feat: Complete implementation of Tasks 1-5 - CIM Document Processor
Backend Infrastructure: - Complete Express server setup with security middleware (helmet, CORS, rate limiting) - Comprehensive error handling and logging with Winston - Authentication system with JWT tokens and session management - Database models and migrations for Users, Documents, Feedback, and Processing Jobs - API routes structure for authentication and document management - Integration tests for all server components (86 tests passing) Frontend Infrastructure: - React application with TypeScript and Vite - Authentication UI with login form, protected routes, and logout functionality - Authentication context with proper async state management - Component tests with proper async handling (25 tests passing) - Tailwind CSS styling and responsive design Key Features: - User registration, login, and authentication - Protected routes with role-based access control - Comprehensive error handling and user feedback - Database schema with proper relationships - Security middleware and validation - Production-ready build configuration Test Coverage: 111/111 tests passing Tasks Completed: 1-5 (Project setup, Database, Auth system, Frontend UI, Backend infrastructure) Ready for Task 6: File upload backend infrastructure
This commit is contained in:
160
backend/src/config/env.ts
Normal file
160
backend/src/config/env.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import dotenv from 'dotenv';
|
||||
import Joi from 'joi';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
// Environment validation schema
|
||||
const envSchema = Joi.object({
|
||||
NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),
|
||||
PORT: Joi.number().default(5000),
|
||||
|
||||
// Database
|
||||
DATABASE_URL: Joi.string().required(),
|
||||
DB_HOST: Joi.string().default('localhost'),
|
||||
DB_PORT: Joi.number().default(5432),
|
||||
DB_NAME: Joi.string().required(),
|
||||
DB_USER: Joi.string().required(),
|
||||
DB_PASSWORD: Joi.string().required(),
|
||||
|
||||
// Redis
|
||||
REDIS_URL: Joi.string().default('redis://localhost:6379'),
|
||||
REDIS_HOST: Joi.string().default('localhost'),
|
||||
REDIS_PORT: Joi.number().default(6379),
|
||||
|
||||
// JWT
|
||||
JWT_SECRET: Joi.string().required(),
|
||||
JWT_EXPIRES_IN: Joi.string().default('1h'),
|
||||
JWT_REFRESH_SECRET: Joi.string().required(),
|
||||
JWT_REFRESH_EXPIRES_IN: Joi.string().default('7d'),
|
||||
|
||||
// File Upload
|
||||
MAX_FILE_SIZE: Joi.number().default(104857600), // 100MB
|
||||
UPLOAD_DIR: Joi.string().default('uploads'),
|
||||
ALLOWED_FILE_TYPES: Joi.string().default('application/pdf'),
|
||||
|
||||
// LLM
|
||||
LLM_PROVIDER: Joi.string().valid('openai', 'anthropic').default('openai'),
|
||||
OPENAI_API_KEY: Joi.string().when('LLM_PROVIDER', {
|
||||
is: 'openai',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
ANTHROPIC_API_KEY: Joi.string().when('LLM_PROVIDER', {
|
||||
is: 'anthropic',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
LLM_MODEL: Joi.string().default('gpt-4'),
|
||||
LLM_MAX_TOKENS: Joi.number().default(4000),
|
||||
LLM_TEMPERATURE: Joi.number().min(0).max(2).default(0.1),
|
||||
|
||||
// Storage
|
||||
STORAGE_TYPE: Joi.string().valid('local', 's3').default('local'),
|
||||
AWS_ACCESS_KEY_ID: Joi.string().when('STORAGE_TYPE', {
|
||||
is: 's3',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
AWS_SECRET_ACCESS_KEY: Joi.string().when('STORAGE_TYPE', {
|
||||
is: 's3',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
AWS_REGION: Joi.string().when('STORAGE_TYPE', {
|
||||
is: 's3',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
AWS_S3_BUCKET: Joi.string().when('STORAGE_TYPE', {
|
||||
is: 's3',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional()
|
||||
}),
|
||||
|
||||
// Security
|
||||
BCRYPT_ROUNDS: Joi.number().default(12),
|
||||
RATE_LIMIT_WINDOW_MS: Joi.number().default(900000), // 15 minutes
|
||||
RATE_LIMIT_MAX_REQUESTS: Joi.number().default(100),
|
||||
|
||||
// Logging
|
||||
LOG_LEVEL: Joi.string().valid('error', 'warn', 'info', 'debug').default('info'),
|
||||
LOG_FILE: Joi.string().default('logs/app.log'),
|
||||
}).unknown();
|
||||
|
||||
// Validate environment variables
|
||||
const { error, value: envVars } = envSchema.validate(process.env);
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Config validation error: ${error.message}`);
|
||||
}
|
||||
|
||||
// Export validated configuration
|
||||
export const config = {
|
||||
env: envVars.NODE_ENV,
|
||||
nodeEnv: envVars.NODE_ENV,
|
||||
port: envVars.PORT,
|
||||
frontendUrl: process.env['FRONTEND_URL'] || 'http://localhost:3000',
|
||||
|
||||
database: {
|
||||
url: envVars.DATABASE_URL,
|
||||
host: envVars.DB_HOST,
|
||||
port: envVars.DB_PORT,
|
||||
name: envVars.DB_NAME,
|
||||
user: envVars.DB_USER,
|
||||
password: envVars.DB_PASSWORD,
|
||||
},
|
||||
|
||||
redis: {
|
||||
url: envVars.REDIS_URL,
|
||||
host: envVars.REDIS_HOST,
|
||||
port: envVars.REDIS_PORT,
|
||||
},
|
||||
|
||||
jwt: {
|
||||
secret: envVars.JWT_SECRET,
|
||||
expiresIn: envVars.JWT_EXPIRES_IN,
|
||||
refreshSecret: envVars.JWT_REFRESH_SECRET,
|
||||
refreshExpiresIn: envVars.JWT_REFRESH_EXPIRES_IN,
|
||||
},
|
||||
|
||||
upload: {
|
||||
maxFileSize: envVars.MAX_FILE_SIZE,
|
||||
uploadDir: envVars.UPLOAD_DIR,
|
||||
allowedFileTypes: envVars.ALLOWED_FILE_TYPES.split(','),
|
||||
},
|
||||
|
||||
llm: {
|
||||
provider: envVars.LLM_PROVIDER,
|
||||
openaiApiKey: envVars.OPENAI_API_KEY,
|
||||
anthropicApiKey: envVars.ANTHROPIC_API_KEY,
|
||||
model: envVars.LLM_MODEL,
|
||||
maxTokens: envVars.LLM_MAX_TOKENS,
|
||||
temperature: envVars.LLM_TEMPERATURE,
|
||||
},
|
||||
|
||||
storage: {
|
||||
type: envVars.STORAGE_TYPE,
|
||||
aws: {
|
||||
accessKeyId: envVars.AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: envVars.AWS_SECRET_ACCESS_KEY,
|
||||
region: envVars.AWS_REGION,
|
||||
bucket: envVars.AWS_S3_BUCKET,
|
||||
},
|
||||
},
|
||||
|
||||
security: {
|
||||
bcryptRounds: envVars.BCRYPT_ROUNDS,
|
||||
rateLimit: {
|
||||
windowMs: envVars.RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: envVars.RATE_LIMIT_MAX_REQUESTS,
|
||||
},
|
||||
},
|
||||
|
||||
logging: {
|
||||
level: envVars.LOG_LEVEL,
|
||||
file: envVars.LOG_FILE,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user