Files
sts-avto-v2/server.js
kalugin66 5fa29fd33d
All checks were successful
Deploy sts / deploy-sber (push) Successful in 1m43s
www
2026-08-04 15:57:56 +05:00

180 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const express = require('express');
const path = require('path');
const fs = require('fs');
const sqlite3 = require('sqlite3').verbose();
const sharp = require('sharp');
require('dotenv').config();
const app = express();
app.use((req, res, next) => {
const proto = req.headers['x-forwarded-proto'];
if (proto && proto !== 'https') {
return res.redirect(301, 'https://' + req.headers.host + req.url);
}
next();
});
app.use((req, res, next) => {
const host = req.hostname;
if (host.startsWith('www.')) {
const target = host.replace(/^www\./, '');
return res.redirect(301, 'https://' + target + req.originalUrl);
}
next();
});
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
next();
});
const PORT = process.env.PORT || 3000;
const API_KEY = process.env.HOTEL777KEY;
if (!API_KEY) {
console.error('FATAL: HOTEL777KEY environment variable not set');
process.exit(1);
}
const imgDir = path.join(__dirname, 'public', 'img');
const supportedFormats = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff'];
async function convertImagesToWebp() {
if (!fs.existsSync(imgDir)) {
console.log('Папка img не найдена, пропускаем конвертацию');
return;
}
const files = fs.readdirSync(imgDir);
const toConvert = files.filter(f => {
const ext = path.extname(f).toLowerCase();
return supportedFormats.includes(ext) && !f.endsWith('.webp');
});
if (toConvert.length === 0) {
console.log('Все изображения уже в формате WebP');
return;
}
console.log(`Конвертация ${toConvert.length} изображений в WebP...`);
for (const file of toConvert) {
const inputPath = path.join(imgDir, file);
const outputPath = path.join(imgDir, path.basename(file, path.extname(file)) + '.webp');
try {
await sharp(inputPath).webp({ quality: 85 }).toFile(outputPath);
fs.unlinkSync(inputPath);
console.log(` ${file} -> ${path.basename(outputPath)}`);
} catch (err) {
console.error(` Ошибка конвертации ${file}:`, err.message);
}
}
console.log('Конвертация завершена');
}
app.use(express.json());
app.get('/robots.txt', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'robots.txt'));
});
app.get('/sitemap.xml', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'sitemap.xml'));
});
app.use(express.static(path.join(__dirname, 'public'), { index: false }));
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir);
[
'equipment_images', 'partner_images', 'license_images',
'activity_images', 'hero_images'
].forEach(dir => {
const p = path.join(dataDir, dir);
if (!fs.existsSync(p)) fs.mkdirSync(p);
app.use('/data/' + dir, express.static(p));
});
const dbPath = path.join(dataDir, 'bookings.db');
const db = new sqlite3.Database(dbPath);
const dbModule = require('./modules/database');
const crudModule = require('./modules/database/crud');
const authModule = require('./modules/auth');
const heroModule = require('./modules/hero');
const aboutModule = require('./modules/about');
const servicesModule = require('./modules/services');
const workSchemesModule = require('./modules/workSchemes');
const processStepsModule = require('./modules/processSteps');
const equipmentModule = require('./modules/equipment');
const geographyModule = require('./modules/geography');
const licensesModule = require('./modules/licenses');
const activitiesModule = require('./modules/activities');
const partnersModule = require('./modules/partners');
const settingsModule = require('./modules/settings');
const usersModule = require('./modules/users');
const leadsModule = require('./modules/leads');
const siteDataModule = require('./modules/siteData');
const ssrRenderer = require('./modules/ssrRenderer');
[
crudModule, authModule, heroModule, aboutModule,
servicesModule, workSchemesModule, processStepsModule,
equipmentModule, geographyModule, licensesModule,
activitiesModule, partnersModule, settingsModule,
usersModule, leadsModule, siteDataModule
].forEach(m => m.init(db));
const auth = {
authenticateToken: authModule.authenticateToken,
requireAdmin: authModule.requireAdmin
};
authModule.setupRoutes(app);
heroModule.setupRoutes(app, auth);
aboutModule.setupRoutes(app, auth);
servicesModule.setupRoutes(app, auth);
workSchemesModule.setupRoutes(app, auth);
processStepsModule.setupRoutes(app, auth);
equipmentModule.setupRoutes(app, auth);
geographyModule.setupRoutes(app, auth);
licensesModule.setupRoutes(app, auth);
activitiesModule.setupRoutes(app, auth);
partnersModule.setupRoutes(app, auth);
settingsModule.setupRoutes(app, auth);
usersModule.setupRoutes(app, auth);
leadsModule.setupRoutes(app, auth);
siteDataModule.setupRoutes(app);
app.get('/admin', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'admin.html'));
});
app.get('/', async (req, res) => {
const htmlPath = path.join(__dirname, 'public', 'index.html');
let html = fs.readFileSync(htmlPath, 'utf8');
try {
const siteData = await siteDataModule.loadSiteData();
const ssrHtml = ssrRenderer.renderAll(siteData);
for (const [containerId, content] of Object.entries(ssrHtml)) {
const placeholder = 'id="' + containerId + '">';
html = html.replace(placeholder, placeholder + content);
}
} catch (err) {
console.error('SSR render error:', err.message);
}
res.send(html);
});
convertImagesToWebp().then(() => console.log('Images ready'));
(async () => {
await dbModule.init(db);
await authModule.syncAdmin();
console.log('Database initialized, admin synced');
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log(`Admin panel: http://localhost:${PORT}/admin`);
});
})();