const express = require('express'); const mongoose = require('mongoose'); const session = require('express-session'); const MongoStore = require('connect-mongo'); 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'); require('dotenv').config(); const app = express(); // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], fontSrc: ["'self'", "https://fonts.gstatic.com"], scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "ws:", "wss:"] } } })); // 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 mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/smartsoltech', { useNewUrlParser: true, useUnifiedTopology: true, }) .then(() => console.log('✓ MongoDB connected')) .catch(err => console.error('✗ MongoDB connection error:', err)); // Session configuration app.use(session({ secret: process.env.SESSION_SECRET || 'your-secret-key', resave: false, saveUninitialized: false, store: MongoStore.create({ mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost:27017/smartsoltech', touchAfter: 24 * 3600 // lazy session update }), 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')); // 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).json({ success: false, message: process.env.NODE_ENV === 'production' ? 'Something went wrong!' : err.message }); }); // 404 handler app.use((req, res) => { res.status(404).render('404', { title: '404 - Страница не найдена', message: 'Запрашиваемая страница не найдена' }); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`🚀 Server running on port ${PORT}`); console.log(`🌐 Visit: http://localhost:${PORT}`); }); module.exports = app;