const path = require('path'); const express = require('express'); const cookieParser = require('cookie-parser'); const session = require('express-session'); const pgSession = require('connect-pg-simple')(session); const { pool, initSchema, checkConnection } = require('./db'); const { runSeed } = require('./seed'); const { seedAdmin } = require('./seed-admin'); const { seedPromoCodes } = require('./seed-promo'); const { loadUser } = require('./middleware/auth'); const { loadCookieConsent } = require('./middleware/cookieConsent'); const { loadCaptchaLocals, rejectYandexCaptcha } = require('./middleware/captcha'); const healthRoutes = require('./routes/health'); const shopRoutes = require('./routes/shop'); const authRoutes = require('./routes/auth'); const accountRoutes = require('./routes/account'); const adminRoutes = require('./routes/admin'); const cookiesRoutes = require('./routes/cookies'); const passwordResetRoutes = require('./routes/password-reset'); const reservationsRoutes = require('./routes/reservations'); const passkeyRoutes = require('./routes/passkey'); const stockAlertsRoutes = require('./routes/stock-alerts'); const promoRoutes = require('./routes/promo'); const seoRoutes = require('./routes/seo'); const { securityHeaders } = require('./middleware/securityHeaders'); const PORT = process.env.PORT || 3000; const HOST = process.env.HOST || '0.0.0.0'; const isProduction = process.env.NODE_ENV === 'production'; async function start() { await checkConnection(); await initSchema(); await runSeed(); await seedAdmin(); await seedPromoCodes(); const app = express(); if (process.env.TRUST_PROXY === '1' || isProduction) { app.set('trust proxy', 1); } app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); app.use(healthRoutes); app.use(securityHeaders); app.use(seoRoutes); app.use(express.static(path.join(__dirname, 'public'))); app.use(express.urlencoded({ extended: true })); app.use(express.json({ limit: '64kb' })); app.use(cookieParser()); app.use( session({ store: new pgSession({ pool, createTableIfMissing: true, tableName: 'session', }), secret: process.env.SESSION_SECRET || 'dev-secret-change-in-production', resave: false, saveUninitialized: false, cookie: { maxAge: 7 * 24 * 60 * 60 * 1000, httpOnly: true, sameSite: 'lax', secure: isProduction, }, }) ); app.use(loadCookieConsent); app.use(loadCaptchaLocals); app.use(rejectYandexCaptcha); app.use(loadUser); app.use('/cookies', cookiesRoutes); app.use('/', passwordResetRoutes); app.use('/reservations', reservationsRoutes); app.use('/', stockAlertsRoutes); app.use('/', promoRoutes); app.use('/', shopRoutes); app.use('/', authRoutes); app.use('/webauthn', passkeyRoutes); app.use('/account', accountRoutes); app.use('/admin', adminRoutes); app.use((req, res) => { res.status(404).render('error', { title: 'Не найдено', message: 'Страница не найдена', code: 404, }); }); app.use((err, req, res, _next) => { console.error(err); res.status(500).render('error', { title: 'Ошибка', message: 'Внутренняя ошибка сервера', code: 500, }); }); const server = app.listen(PORT, HOST, () => { console.log(`Магазин: http://${HOST}:${PORT} (PostgreSQL)`); }); server.on('error', (err) => { if (err.code === 'EADDRINUSE') { console.error( `Порт ${PORT} уже занят (часто старый «npm start»). Выполните: bash scripts/free-port-3000.sh` ); } else { console.error('Не удалось запустить сервер:', err.message); } process.exit(1); }); } start().catch((err) => { if (err.code === 'ECONNREFUSED' && String(err.message).includes('5432')) { console.error('PostgreSQL недоступен на 127.0.0.1:5432'); console.error(' systemctl start postgresql'); console.error(' bash scripts/setup-postgres-ubuntu.sh'); console.error(' Проверьте DATABASE_URL в .env'); } else if (err.code === 'MODULE_NOT_FOUND') { console.error('Не найден модуль:', err.message); console.error(' cd', path.join(__dirname, '..'), '&& npm install --omit=dev'); } else { console.error('Ошибка запуска:', err.message); if (err.stack) console.error(err.stack); } process.exit(1); });