const express = require('express'); const path = require('path'); require('dotenv').config(); const { checkConnection } = require('./src/db'); const q = require('./src/queries'); const app = express(); const PORT = process.env.PORT || 3000; const DEV = process.env.DEV_MODE === 'true' && process.env.NODE_ENV !== 'production'; if (DEV) console.warn('⚠ DEV_MODE активен — аутентификация отключена'); app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); app.use(express.static(path.join(__dirname, 'public'))); app.use(express.urlencoded({ extended: true, limit: '32kb' })); // ── Health (выше auth — k8s probe не должна требовать токен) ── app.get('/healthz', (req, res) => res.send('OK')); // ── Auth middleware ── app.use((req, res, next) => { if (DEV) { req.user = { email: 'dev@test.local', clientId: 'WZ01325', companyId: null, companyName: 'DEV' }; return next(); } const auth = req.headers.authorization || ''; try { const payload = JSON.parse(Buffer.from(auth.replace('Bearer ', '').split('.')[1], 'base64').toString()); req.user = { email: payload.email || 'unknown', clientId: payload.ClientID, companyId: payload.company_id, companyName: payload.company_name || payload.ClientID, }; } catch { req.user = {}; } // Без clientId — 401, анонимы не должны делить NULL-компанию if (!req.user.clientId) return res.status(401).send('Unauthorized'); next(); }); // ── Главная ── app.get('/', async (req, res) => { const { clientId, companyName } = req.user; try { const company = await q.getOrCreateCompany(clientId, companyName); const limit = await q.getLimit(company); const entries = await q.listEntries(company.id); res.render('index', { entries, limit, used: entries.length, user: req.user, error: req.query.error || null, message: req.query.message || null, wasNormalized: req.query.wasNormalized === '1', }); } catch (e) { res.render('index', { entries: [], limit: 15, used: 0, user: req.user, error: 'Ошибка загрузки', message: null, wasNormalized: false }); } }); // ── Создать ── app.post('/add', async (req, res) => { const { value, comment } = req.body; const { clientId, companyName, email } = req.user; try { const company = await q.getOrCreateCompany(clientId, companyName); const result = await q.createEntry(company.id, value, comment, email); const message = result.wasNormalized ? `Адрес нормализован в ${result.entry.value_cidr}` : 'Добавлено'; const wasNormalized = result.wasNormalized ? '1' : '0'; res.redirect(`/?message=${encodeURIComponent(message)}&wasNormalized=${wasNormalized}`); } catch (e) { res.redirect('/?error=' + encodeURIComponent(e.message)); } }); // ── Удалить (soft) ── app.post('/delete/:id', async (req, res) => { const { clientId, companyName, email } = req.user; try { const company = await q.getOrCreateCompany(clientId, companyName); await q.deleteEntry(req.params.id, company.id, email); res.redirect('/'); } catch (e) { res.redirect('/?error=' + encodeURIComponent(e.message)); } }); // ── Экспорт (только для своей компании) ── app.get('/export', async (req, res) => { try { const { clientId, companyName } = req.user; const company = await q.getOrCreateCompany(clientId, companyName); const cidrs = await q.getExportCIDRs(company.id); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.send(cidrs.join('\n') + '\n'); } catch (e) { res.status(500).send('Export error'); } }); // ── Старт ── checkConnection() .then(() => console.log('DB connected')) .catch(e => console.error('DB not ready:', e.message)); app.listen(PORT, () => console.log(`Server on port ${PORT}`));