180 lines
6.0 KiB
JavaScript
180 lines
6.0 KiB
JavaScript
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`);
|
||
});
|
||
})();
|