66841e8af7
- Add path traversal protection with secureStatic middleware - Implement proper MIME type validation for all file uploads - Add content-based file validation (magic numbers) - Create comprehensive fileSecurityUtils for secure file operations - Update adminPhotos.js with enhanced validation - Update adminSettings.js for secure logo/favicon uploads - Addresses file upload vulnerabilities from security scan 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
175 lines
5.8 KiB
JavaScript
175 lines
5.8 KiB
JavaScript
require('dotenv').config();
|
|
|
|
// Validate critical environment variables before proceeding
|
|
const { validateEnvironment } = require('./src/config/validateEnv');
|
|
validateEnvironment();
|
|
|
|
const express = require('express');
|
|
const helmet = require('helmet');
|
|
const cors = require('cors');
|
|
const rateLimit = require('express-rate-limit');
|
|
const jwt = require('jsonwebtoken');
|
|
const path = require('path');
|
|
const { initializeDatabase } = require('./src/database/db');
|
|
const { startFileWatcher } = require('./src/services/fileWatcher');
|
|
const { startExpirationChecker } = require('./src/services/expirationChecker');
|
|
const { startEmailQueueProcessor } = require('./src/services/emailProcessor');
|
|
const { maintenanceMiddleware } = require('./src/middleware/maintenance');
|
|
const { sessionTimeoutMiddleware } = require('./src/middleware/sessionTimeout');
|
|
const logger = require('./src/utils/logger');
|
|
|
|
// Import routes
|
|
const authRoutes = require('./src/routes/auth-enhanced');
|
|
const eventRoutes = require('./src/routes/events');
|
|
const galleryRoutes = require('./src/routes/gallery');
|
|
const adminRoutes = require('./src/routes/admin');
|
|
const adminAuthRoutes = require('./src/routes/adminAuth');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Security middleware
|
|
app.use(helmet());
|
|
|
|
// CORS configuration
|
|
const corsOptions = {
|
|
origin: function (origin, callback) {
|
|
const allowedOrigins = [
|
|
process.env.FRONTEND_URL || 'http://localhost:3005',
|
|
process.env.ADMIN_URL || 'http://localhost:3005',
|
|
'http://localhost:5173', // Vite dev server
|
|
'http://localhost:3002', // Backend server
|
|
'http://localhost:3001', // For API testing
|
|
'http://localhost:3000' // Direct backend access
|
|
];
|
|
|
|
// Allow requests with no origin (like mobile apps or curl)
|
|
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
|
|
callback(null, true);
|
|
} else {
|
|
callback(new Error('Not allowed by CORS'));
|
|
}
|
|
},
|
|
credentials: true
|
|
};
|
|
|
|
app.use(cors(corsOptions));
|
|
|
|
// Rate limiting with admin bypass
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: process.env.NODE_ENV === 'development' ? 1000 : 100, // More lenient in development
|
|
skip: (req) => {
|
|
// Skip rate limiting for authenticated admin users
|
|
if (req.path.startsWith('/api/admin/') && req.headers.authorization) {
|
|
const token = req.headers.authorization.replace('Bearer ', '');
|
|
try {
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
return decoded.type === 'admin';
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
// Also skip rate limiting for public settings endpoint in development
|
|
if (process.env.NODE_ENV === 'development' && req.path === '/api/public/settings') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
const authLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 5 // limit auth attempts
|
|
});
|
|
|
|
// Apply rate limiting - admin routes check will skip for valid admin tokens
|
|
app.use('/api/', limiter);
|
|
app.use('/api/auth', authLimiter);
|
|
|
|
// Body parsing middleware
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// Maintenance mode middleware - add after body parsing but before routes
|
|
app.use(maintenanceMiddleware);
|
|
|
|
// Session timeout middleware for admin routes
|
|
app.use('/api/admin', sessionTimeoutMiddleware);
|
|
|
|
// Middleware to set CORS headers for static files
|
|
const setCorsHeaders = (req, res, next) => {
|
|
res.header('Access-Control-Allow-Origin', req.headers.origin || '*');
|
|
res.header('Access-Control-Allow-Credentials', 'true');
|
|
res.header('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
next();
|
|
};
|
|
|
|
// Import secure static middleware
|
|
const secureStatic = require('./src/middleware/secureStatic');
|
|
|
|
// Static file serving for photos (protected)
|
|
app.use('/photos', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(__dirname, 'storage/events/active')));
|
|
|
|
// Static file serving for thumbnails (protected)
|
|
app.use('/thumbnails', require('./src/middleware/photoAuth'), setCorsHeaders, secureStatic(path.join(__dirname, 'storage/thumbnails')));
|
|
|
|
// Static file serving for uploads (public - logos, favicons)
|
|
app.use('/uploads', setCorsHeaders, secureStatic(path.join(__dirname, 'storage/uploads')));
|
|
|
|
// Health check endpoint
|
|
app.get('/api/health', (req, res) => {
|
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
});
|
|
|
|
// Routes
|
|
app.use('/api/auth', authRoutes);
|
|
app.use('/api/events', eventRoutes);
|
|
app.use('/api/gallery', galleryRoutes);
|
|
app.use('/api/admin', adminRoutes);
|
|
app.use('/api/admin/auth', adminAuthRoutes);
|
|
app.use('/api/admin/system', require('./src/routes/adminSystem'));
|
|
app.use('/api/public/settings', require('./src/routes/publicSettings'));
|
|
app.use('/api/public', require('./src/routes/publicCMS'));
|
|
app.use('/api/images', require('./src/routes/protectedImages'));
|
|
|
|
// Error handling middleware
|
|
app.use((err, req, res, next) => {
|
|
logger.error(err.stack);
|
|
res.status(500).json({ error: 'Something went wrong!' });
|
|
});
|
|
|
|
// Initialize services
|
|
async function startServer() {
|
|
try {
|
|
// Initialize database
|
|
await initializeDatabase();
|
|
|
|
// Initialize auth security cleanup job
|
|
const { initializeCleanupJob } = require('./src/utils/authSecurity');
|
|
initializeCleanupJob();
|
|
|
|
// Start file watcher
|
|
startFileWatcher();
|
|
|
|
// Start expiration checker
|
|
startExpirationChecker();
|
|
|
|
// Start email queue processor
|
|
startEmailQueueProcessor();
|
|
|
|
app.listen(PORT, () => {
|
|
logger.info(`Server running on port ${PORT}`);
|
|
logger.info(`Admin interface: ${process.env.ADMIN_URL || 'http://localhost:3000'}`);
|
|
logger.info(`Frontend: ${process.env.FRONTEND_URL || 'http://localhost:3001'}`);
|
|
});
|
|
} catch (error) {
|
|
logger.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
startServer();
|
|
|
|
module.exports = app; // For testing
|