add changes

This commit is contained in:
AD2025
2025-11-12 23:06:27 +02:00
parent c664d0a341
commit ec6534fcc2
42 changed files with 11854 additions and 299 deletions

View File

@@ -1,11 +1,19 @@
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');
const swaggerUi = require('swagger-ui-express');
const swaggerSpec = require('./config/swagger');
const logger = require('./config/logger');
const { errorHandler, notFoundHandler } = require('./middleware/errorHandler');
const { testConnection, getDatabaseStats } = require('./config/db');
const { validateEnvironment } = require('./validate-env');
const { isRedisConnected } = require('./config/redis');
// Security middleware
const { helmetConfig, customSecurityHeaders, getCorsOptions } = require('./middleware/security');
const { sanitizeAll } = require('./middleware/sanitization');
const { apiLimiter, docsLimiter } = require('./middleware/rateLimiter');
// Validate environment configuration on startup
console.log('\n🔧 Validating environment configuration...');
@@ -23,33 +31,59 @@ const PORT = config.server.port;
const API_PREFIX = config.server.apiPrefix;
const NODE_ENV = config.server.nodeEnv;
// Security middleware
app.use(helmet());
// Trust proxy - important for rate limiting and getting real client IP
app.set('trust proxy', 1);
// CORS configuration
app.use(cors(config.cors));
// Security middleware - order matters!
// 1. Helmet for security headers
app.use(helmetConfig);
// Body parser middleware
// 2. Custom security headers
app.use(customSecurityHeaders);
// 3. CORS configuration
app.use(cors(getCorsOptions()));
// 4. Body parser middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Logging middleware
// 5. Input sanitization (NoSQL injection, XSS, HPP)
app.use(sanitizeAll);
// 6. Logging middleware
if (NODE_ENV === 'development') {
app.use(morgan('dev'));
app.use(morgan('dev', { stream: logger.stream }));
} else {
app.use(morgan('combined'));
app.use(morgan('combined', { stream: logger.stream }));
}
// Rate limiting
const limiter = rateLimit({
windowMs: config.rateLimit.windowMs,
max: config.rateLimit.maxRequests,
message: config.rateLimit.message,
standardHeaders: true,
legacyHeaders: false,
});
// 7. Log all requests in development
if (NODE_ENV === 'development') {
app.use((req, res, next) => {
logger.info(`${req.method} ${req.originalUrl}`, {
ip: req.ip,
userAgent: req.get('user-agent')
});
next();
});
}
app.use(API_PREFIX, limiter);
// 8. Global rate limiting for all API routes
app.use(API_PREFIX, apiLimiter);
// API Documentation - with rate limiting
app.use('/api-docs', docsLimiter, swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: 'Interview Quiz API Documentation',
customfavIcon: '/favicon.ico'
}));
// Swagger JSON endpoint - with rate limiting
app.get('/api-docs.json', docsLimiter, (req, res) => {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
});
// Health check endpoint
app.get('/health', async (req, res) => {
@@ -90,31 +124,18 @@ app.get('/', (req, res) => {
});
});
// 404 handler
app.use((req, res, next) => {
res.status(404).json({
success: false,
message: 'Route not found',
path: req.originalUrl
});
});
// 404 handler - must be after all routes
app.use(notFoundHandler);
// Global error handler
app.use((err, req, res, next) => {
console.error('Error:', err);
const statusCode = err.statusCode || 500;
const message = err.message || 'Internal Server Error';
res.status(statusCode).json({
success: false,
message: message,
...(NODE_ENV === 'development' && { stack: err.stack })
});
});
// Global error handler - must be last
app.use(errorHandler);
// Start server
app.listen(PORT, async () => {
logger.info('Server starting up...');
const redisStatus = isRedisConnected() ? '✅ Connected' : '⚠️ Not Connected (Optional)';
console.log(`
╔════════════════════════════════════════╗
║ Interview Quiz API - MySQL Edition ║
@@ -124,14 +145,28 @@ app.listen(PORT, async () => {
🌍 Environment: ${NODE_ENV}
🔗 API Endpoint: http://localhost:${PORT}${API_PREFIX}
📊 Health Check: http://localhost:${PORT}/health
📚 API Docs: http://localhost:${PORT}/api-docs
📝 Logs: backend/logs/
💾 Cache (Redis): ${redisStatus}
`);
logger.info(`Server started successfully on port ${PORT}`);
// Test database connection on startup
console.log('🔌 Testing database connection...');
const connected = await testConnection();
if (!connected) {
console.warn('⚠️ Warning: Database connection failed. Server is running but database operations will fail.');
}
// Log Redis status
if (isRedisConnected()) {
console.log('💾 Redis cache connected and ready');
logger.info('Redis cache connected');
} else {
console.log('⚠️ Redis not connected - caching disabled (optional feature)');
logger.warn('Redis not connected - caching disabled');
}
});
// Handle unhandled promise rejections