const express = require('express'); const { sequelize, testConnection } = require('./config/database'); const session = require('express-session'); const SequelizeStore = require('connect-session-sequelize')(session.Store); const path = require('path'); const helmet = require('helmet'); const compression = require('compression'); const cors = require('cors'); const morgan = require('morgan'); const rateLimit = require('express-rate-limit'); const i18n = require('i18n'); require('dotenv').config(); const app = express(); // Настройка i18n i18n.configure({ locales: ['ko', 'en', 'ru', 'kk'], defaultLocale: 'ru', directory: path.join(__dirname, 'locales'), objectNotation: true, updateFiles: false, syncFiles: false }); // i18n middleware app.use(i18n.init); // Middleware для передачи переменных в шаблоны app.use((req, res, next) => { const currentLang = req.session?.language || req.getLocale() || 'ko'; req.setLocale(currentLang); res.locals.locale = currentLang; res.locals.__ = res.__; res.locals.theme = req.session?.theme || 'light'; res.locals.currentLanguage = currentLang; res.locals.currentPage = req.path.split('/')[1] || 'home'; next(); }); // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] } } })); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs }); app.use('/api/', limiter); // Middleware app.use(compression()); app.use(cors()); app.use(morgan('combined')); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Static files app.use(express.static(path.join(__dirname, 'public'))); app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); // View engine app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // Database connection and testing testConnection(); // Session store configuration const sessionStore = new SequelizeStore({ db: sequelize, tableName: 'sessions', checkExpirationInterval: 15 * 60 * 1000, // 15 minutes expiration: 7 * 24 * 60 * 60 * 1000 // 7 days }); // Session configuration app.use(session({ secret: process.env.SESSION_SECRET || 'your-secret-key', resave: false, saveUninitialized: false, store: sessionStore, cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days } })); // Routes app.use('/', require('./routes/index')); app.use('/api/auth', require('./routes/auth')); app.use('/api/portfolio', require('./routes/portfolio')); app.use('/api/services', require('./routes/services')); app.use('/api/calculator', require('./routes/calculator')); app.use('/api/contact', require('./routes/contact')); app.use('/api/media', require('./routes/media')); app.use('/admin', require('./routes/admin')); // Language switching routes app.get('/lang/:language', (req, res) => { const { language } = req.params; const supportedLanguages = ['ko', 'en', 'ru', 'kk']; if (supportedLanguages.includes(language)) { req.setLocale(language); req.session.language = language; } const referer = req.get('Referer') || '/'; res.redirect(referer); }); // Theme switching routes app.get('/theme/:theme', (req, res) => { const { theme } = req.params; if (['light', 'dark'].includes(theme)) { req.session.theme = theme; } res.json({ success: true, theme: req.session.theme }); }); // PWA Service Worker app.get('/sw.js', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'sw.js')); }); // PWA Manifest app.get('/manifest.json', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'manifest.json')); }); // Error handling middleware app.use((err, req, res, next) => { console.error(err.stack); res.status(500).render('error', { title: 'Error', settings: {}, message: process.env.NODE_ENV === 'production' ? 'Something went wrong!' : err.message, currentPage: 'error' }); }); // 404 handler app.use((req, res) => { res.status(404).render('error', { title: '404 - 페이지를 찾을 수 없습니다', settings: {}, message: '요청하신 페이지를 찾을 수 없습니다', currentPage: 'error' }); }); const PORT = process.env.PORT || 3000; // Sync database and start server async function startServer() { try { // Sync all models with database await sequelize.sync({ force: false }); console.log('✓ Database synchronized'); // Create session table await sessionStore.sync(); console.log('✓ Session store synchronized'); app.listen(PORT, () => { console.log(`🚀 Server running on port ${PORT}`); console.log(`🌐 Visit: http://localhost:${PORT}`); }); } catch (error) { console.error('✗ Failed to start server:', error); process.exit(1); } } startServer();