124 lines
3.5 KiB
JavaScript
124 lines
3.5 KiB
JavaScript
import express from 'express';
|
|
import cors from 'cors';
|
|
import helmet from 'helmet';
|
|
import rateLimit from 'express-rate-limit';
|
|
import dotenv from 'dotenv';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
import { contactRouter } from './routes/contact.js';
|
|
import { errorHandler, notFoundHandler } from './middleware/errorHandlers.js';
|
|
import { requestLogger } from './middleware/logger.js';
|
|
import { initializeEmailService } from './services/emailService.js';
|
|
|
|
// Get the directory of the current module
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
// Load environment variables from the backend directory
|
|
dotenv.config({ path: join(__dirname, '.env') });
|
|
|
|
async function startServer() {
|
|
// Initialize email service after environment variables are loaded
|
|
await initializeEmailService();
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3001;
|
|
|
|
// Trust proxy (required for Railway, Heroku, etc.)
|
|
app.set('trust proxy', 1);
|
|
|
|
// Security middleware
|
|
app.use(
|
|
helmet({
|
|
crossOriginResourcePolicy: { policy: 'cross-origin' },
|
|
})
|
|
);
|
|
|
|
// CORS configuration
|
|
app.use(
|
|
cors({
|
|
origin: [
|
|
'http://localhost:5173',
|
|
'http://localhost:3000',
|
|
'https://portfolio-page-zeta-ten.vercel.app',
|
|
],
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
})
|
|
);
|
|
|
|
// Rate limiting
|
|
const limiter = rateLimit({
|
|
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes
|
|
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 5, // limit each IP to 5 requests per windowMs
|
|
message: {
|
|
error: 'Too many requests from this IP, please try again later.',
|
|
retryAfter: Math.ceil(
|
|
(parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000) / 1000
|
|
),
|
|
},
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
// Add this for better proxy support
|
|
skip: (req) => {
|
|
// Skip rate limiting for health checks
|
|
return req.path === '/health';
|
|
},
|
|
});
|
|
|
|
app.use('/api/', limiter);
|
|
|
|
// Body parsing middleware
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
// Request logging
|
|
app.use(requestLogger);
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (req, res) => {
|
|
res.json({
|
|
status: 'ok',
|
|
timestamp: new Date().toISOString(),
|
|
environment: process.env.NODE_ENV || 'development',
|
|
});
|
|
});
|
|
|
|
// API routes
|
|
app.use('/api/contact', contactRouter);
|
|
|
|
// Error handling middleware
|
|
app.use(notFoundHandler);
|
|
app.use(errorHandler);
|
|
|
|
// Start server
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|
console.log(`🚀 Server is running on port ${PORT}`);
|
|
console.log(
|
|
`📧 Email service: ${process.env.EMAIL_SERVICE || 'Not configured'}`
|
|
);
|
|
console.log(`🌍 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
console.log(
|
|
`🔗 Frontend URL: ${process.env.FRONTEND_URL || 'http://localhost:5173'}`
|
|
);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGTERM', () => {
|
|
console.log('SIGTERM signal received: closing HTTP server');
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on('SIGINT', () => {
|
|
console.log('SIGINT signal received: closing HTTP server');
|
|
process.exit(0);
|
|
});
|
|
}
|
|
|
|
// Start the server
|
|
startServer().catch((error) => {
|
|
console.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
});
|