2025-12-08 23:42:32 +00:00
|
|
|
|
const express = require('express');
|
|
|
|
|
|
const mongoose = require('mongoose');
|
|
|
|
|
|
const cors = require('cors');
|
|
|
|
|
|
const cookieParser = require('cookie-parser');
|
|
|
|
|
|
const dotenv = require('dotenv');
|
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
const http = require('http');
|
|
|
|
|
|
|
|
|
|
|
|
// Загрузить переменные окружения из корня проекта
|
|
|
|
|
|
const rootEnvPath = path.resolve(__dirname, '../../.env');
|
|
|
|
|
|
dotenv.config({ path: rootEnvPath });
|
|
|
|
|
|
|
|
|
|
|
|
const config = require('../../backend/config');
|
|
|
|
|
|
const { initWebSocket } = require('../../backend/websocket');
|
|
|
|
|
|
|
|
|
|
|
|
// Security middleware
|
|
|
|
|
|
const {
|
|
|
|
|
|
helmetConfig,
|
|
|
|
|
|
sanitizeMongo,
|
|
|
|
|
|
xssProtection,
|
|
|
|
|
|
hppProtection,
|
|
|
|
|
|
ddosProtection
|
|
|
|
|
|
} = require('../../backend/middleware/security');
|
|
|
|
|
|
const { sanitizeInput } = require('../../backend/middleware/validator');
|
|
|
|
|
|
const { requestLogger } = require('../../backend/middleware/logger');
|
|
|
|
|
|
const { errorHandler, notFoundHandler } = require('../../backend/middleware/errorHandler');
|
|
|
|
|
|
const { generalLimiter } = require('../../backend/middleware/rateLimiter');
|
|
|
|
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
|
const server = http.createServer(app);
|
|
|
|
|
|
|
2025-12-09 00:29:13 +00:00
|
|
|
|
// Trust proxy для правильного IP и HTTPS
|
|
|
|
|
|
// В production доверяем прокси (nginx), чтобы правильно определять HTTPS
|
|
|
|
|
|
app.set('trust proxy', config.isProduction() ? 1 : false);
|
2025-12-08 23:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Security headers
|
|
|
|
|
|
app.use(helmetConfig);
|
|
|
|
|
|
|
|
|
|
|
|
// CORS настройки для модераторского сайта
|
|
|
|
|
|
const corsOptions = {
|
|
|
|
|
|
origin: process.env.MODERATION_CORS_ORIGIN || config.frontendUrl || '*',
|
|
|
|
|
|
credentials: true,
|
|
|
|
|
|
optionsSuccessStatus: 200,
|
|
|
|
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
|
|
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'x-telegram-init-data'],
|
|
|
|
|
|
maxAge: 86400
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
app.use(cors(corsOptions));
|
|
|
|
|
|
|
|
|
|
|
|
// Cookie parser для JWT токенов в cookies
|
|
|
|
|
|
app.use(cookieParser());
|
|
|
|
|
|
|
|
|
|
|
|
// Body parsing
|
|
|
|
|
|
app.use(express.json({ limit: '10mb' }));
|
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
|
|
|
|
|
|
|
|
// Security middleware
|
|
|
|
|
|
app.use(sanitizeMongo);
|
|
|
|
|
|
app.use(xssProtection);
|
|
|
|
|
|
app.use(hppProtection);
|
|
|
|
|
|
|
|
|
|
|
|
// Input sanitization
|
|
|
|
|
|
app.use(sanitizeInput);
|
|
|
|
|
|
|
|
|
|
|
|
// Request logging
|
|
|
|
|
|
app.use(requestLogger);
|
|
|
|
|
|
|
|
|
|
|
|
// DDoS protection
|
|
|
|
|
|
app.use(ddosProtection);
|
|
|
|
|
|
|
|
|
|
|
|
// Rate limiting
|
|
|
|
|
|
app.use('/api', generalLimiter);
|
|
|
|
|
|
|
|
|
|
|
|
// Health check
|
|
|
|
|
|
app.get('/health', (req, res) => {
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
status: 'ok',
|
|
|
|
|
|
service: 'moderation',
|
|
|
|
|
|
timestamp: new Date().toISOString()
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// MongoDB подключение (используем ту же БД)
|
|
|
|
|
|
mongoose.connect(config.mongoUri)
|
|
|
|
|
|
.then(async () => {
|
|
|
|
|
|
console.log('✅ MongoDB подключена для модерации');
|
|
|
|
|
|
|
|
|
|
|
|
mongoose.connection.on('error', (err) => {
|
|
|
|
|
|
console.error('❌ MongoDB connection error:', err);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
mongoose.connection.on('disconnected', () => {
|
|
|
|
|
|
console.warn('⚠️ MongoDB отключена');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
mongoose.connection.on('reconnected', () => {
|
|
|
|
|
|
console.log('✅ MongoDB переподключена');
|
|
|
|
|
|
});
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(err => {
|
|
|
|
|
|
console.error('❌ Не удалось подключиться к MongoDB:', err);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Routes - используем те же роуты из основного бэкенда
|
|
|
|
|
|
app.use('/api/mod-app', require('../../backend/routes/modApp'));
|
|
|
|
|
|
app.use('/api/moderation-auth', require('../../backend/routes/moderationAuth'));
|
|
|
|
|
|
|
|
|
|
|
|
// Базовый роут
|
|
|
|
|
|
app.get('/', (req, res) => {
|
|
|
|
|
|
res.json({ message: 'Nakama Moderation API' });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 404 handler
|
|
|
|
|
|
app.use(notFoundHandler);
|
|
|
|
|
|
|
|
|
|
|
|
// Error handler
|
|
|
|
|
|
app.use(errorHandler);
|
|
|
|
|
|
|
|
|
|
|
|
// Инициализировать WebSocket
|
|
|
|
|
|
initWebSocket(server);
|
|
|
|
|
|
|
|
|
|
|
|
// Graceful shutdown
|
|
|
|
|
|
process.on('SIGTERM', () => {
|
|
|
|
|
|
console.log('SIGTERM получен, закрываем сервер модерации...');
|
|
|
|
|
|
server.close(() => {
|
|
|
|
|
|
mongoose.connection.close(false, () => {
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
process.on('SIGINT', () => {
|
|
|
|
|
|
console.log('SIGINT получен, закрываем сервер модерации...');
|
|
|
|
|
|
server.close(() => {
|
|
|
|
|
|
mongoose.connection.close(false, () => {
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const moderationPort = process.env.MODERATION_PORT || 3001;
|
|
|
|
|
|
|
|
|
|
|
|
server.listen(moderationPort, '0.0.0.0', () => {
|
|
|
|
|
|
console.log('\n' + '='.repeat(60));
|
|
|
|
|
|
console.log('✅ Сервер модерации запущен');
|
|
|
|
|
|
console.log(` 🌐 API: http://0.0.0.0:${moderationPort}/api`);
|
|
|
|
|
|
console.log(` 📦 MongoDB: ${config.mongoUri.includes('localhost') ? 'Local' : 'Remote'}`);
|
|
|
|
|
|
console.log('='.repeat(60) + '\n');
|
|
|
|
|
|
});
|
|
|
|
|
|
|