/
hitech
/
crmai
Обзор
Документация
Войти
/
hitech
/
crmai
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
server.js
116 строк
4 KB
hitech
create: .htaccess, package.json, README.md, server.js, test.js
09 май 2026, 17:37
Верифицирован
09 май 2026, 17:37
08a8ed0
Код
Авторство
О чём код?
// server.js — cPanel/Passenger safe версия import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import dotenv from 'dotenv'; import path from 'path'; import { fileURLToPath } from 'url'; import jwt from 'jsonwebtoken'; // Настройка логирования const LOG_LEVELS = { 'error': 0, 'warn': 1, 'info': 2, 'debug': 3 }; const LOG_LEVEL = LOG_LEVELS[process.env.LOG_LEVEL?.toLowerCase() || 'info'] || LOG_LEVELS.info; const log = (level, message, ...args) => { const levelNum = LOG_LEVELS[level]; if (levelNum <= LOG_LEVEL) { const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19); console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`, ...args); } }; // Импорт модулей (статические, без await!) import { pool } from './db/pool.js'; import authRoutes from './routes/auth.js'; import crmRoutes from './routes/crm.js'; import webhookRoutes from './routes/webhooks.js'; import paymentRoutes from './routes/payments.js'; import uploadRoutes from './routes/upload.js'; // Настройка .env с абсолютным путём const __dirname = path.dirname(fileURLToPath(import.meta.url)); dotenv.config({ path: path.join(__dirname, '.env') }); const app = express(); // === HELMET: настройка для shared-хостинга === app.use(helmet({ contentSecurityPolicy: false, // ✅ Отключаем строгий CSP (разрешает инлайн-скрипты) crossOriginEmbedderPolicy: false, crossOriginOpenerPolicy: false, })); app.use(cors({ origin: process.env.BASE_URL || '*', credentials: true })); app.use(express.json({ limit: '1mb' })); app.use(express.urlencoded({ extended: true })); // === СТАТИКА: ДОЛЖНА БЫТЬ ПЕРЕД МАРШРУТАМИ! === const publicDir = path.join(__dirname, 'public'); app.use(express.static(publicDir, { maxAge: '1d', // Кэширование статики setHeaders: (res, filepath) => { // Правильные MIME-типы для CSS/JS if (filepath.endsWith('.css')) res.setHeader('Content-Type', 'text/css'); if (filepath.endsWith('.js')) res.setHeader('Content-Type', 'application/javascript'); } })); // === API эндпоинты === app.get('/health', (req, res) => res.json({ status: 'ok', node: process.version })); app.get('/test-db', async (req, res) => { try { const [rows] = await pool.query('SELECT 1 AS ok, VERSION() AS v'); res.json({ status: 'ok', db: process.env.DB_NAME, version: rows[0].v }); } catch (e) { res.status(500).json({ error: e.message }); } }); // Регистрация роутов app.use('/auth', authRoutes); app.use('/crm', crmRoutes); app.use('/webhooks', webhookRoutes); app.use('/payments', paymentRoutes); app.use('/upload', uploadRoutes); // === ФРОНТЕНД: корневой редирект === app.get('/', (req, res) => { res.sendFile(path.join(publicDir, 'index.html')); }); // === ДЕМО-ТОКЕН (только development) === if (process.env.NODE_ENV === 'development') { app.get('/debug/token', (req, res) => { const token = jwt.sign( { id: 1, role: 'admin', email: 'demo@consult.tj' }, process.env.JWT_SECRET || 'change_this_to_random_string_64chars_min', { expiresIn: '24h' } ); res.json({ token, hint: 'Authorization: Bearer ' + token }); }); // Health check с информацией о среде app.get('/health', (req, res) => { res.json({ status: 'ok', node: process.version, env: process.env.NODE_ENV, timestamp: new Date().toISOString() }); }); } // === 404 обработчик (должен быть ПОСЛЕ всех маршрутов) === app.use((req, res) => { res.status(404).sendFile(path.join(publicDir, '404.html'), (err) => { if (err) res.status(404).json({ error: 'Not found' }); }); }); // === Запуск === const PORT = process.env.PORT || 3000; app.listen(PORT, () => log('info', `CRM started on port ${PORT}`)); process.on('uncaughtException', e => log('error', 'Uncaught exception:', e.message, e.stack)); process.on('unhandledRejection', (e, promise) => log('error', 'Unhandled rejection:', e.message, e.stack));