'use strict'; /** * ui/index.js — UI-слой: SSR через EJS, данные из /api/v1/*. * * createUiRouter({ auth, MOCK_USERS, authLimiter }) → Express Router. * * Монтируется в server.js ПОСЛЕ /api/v1/. * * Middleware: * 1. requireToken — если нет session.token → redirect /login * 2. resolveUser — декодирует payload из токена → req.user * (без проверки подписи; API валидирует при каждом запросе) * * Если API возвращает 401 → сессия уничтожается → redirect /login. */ 'use strict'; const { Router } = require('express'); const jwt = require('jsonwebtoken'); const { createRouter: createAuthRouter } = require('./routes/auth'); const { createRouter: createEntriesRouter } = require('./routes/entries'); const { createRouter: createAdminRouter } = require('./routes/admin'); const { createRouter: createExportRouter } = require('./routes/export'); const api = require('./api-client'); const ADMIN_CLIENT_ID = process.env.ADMIN_CLIENT_ID || 'WZ01112'; function createUiRouter({ auth, MOCK_USERS, authLimiter }) { const router = Router(); // ── Публичные маршруты: /login, /login-token, /logout ──────────────────── router.use('/', createAuthRouter({ auth, MOCK_USERS, authLimiter })); // ── requireToken: все маршруты ниже требуют session.token ──────────────── router.use((req, res, next) => { const token = api.token(req); if (!token) { return res.redirect('/login?returnTo=' + encodeURIComponent(req.originalUrl)); } next(); }); // ── resolveUser: payload → req.user ────────────────────────────────────── // Подпись не проверяем здесь — API проверяет при каждом запросе. router.use((req, res, next) => { if (req.user) return next(); try { const payload = jwt.decode(api.token(req)); if (!payload) throw new Error('empty payload'); const rawClientId = payload.ClientID || payload.clientId || payload.sub || ''; const allClientIds = rawClientId.split(',').map(s => s.trim()).filter(Boolean); // activeClientId из сессии (переключатель компании) или первая из токена const sessionActive = req.session && req.session.user && req.session.user.activeClientId; const activeClientId = (sessionActive && allClientIds.includes(sessionActive)) ? sessionActive : (allClientIds[0] || rawClientId); req.user = { clientId: rawClientId, allClientIds, activeClientId, companyId: payload.company_id || payload.companyId || null, companyName: payload.company_name || payload.companyName || activeClientId, email: payload.email || activeClientId + '@unknown', isAdmin: activeClientId === ADMIN_CLIENT_ID, }; } catch (e) { req.session.destroy(() => res.redirect('/login?error=' + encodeURIComponent('Токен недействителен, войдите заново')) ); return; } next(); }); // ── Основные маршруты (все за requireToken + resolveUser) ───────────────── router.use('/', createEntriesRouter()); router.use('/', createExportRouter()); router.use('/', createAdminRouter()); // ── 404 ─────────────────────────────────────────────────────────────────── router.use((req, res) => { res.status(404).render('error', { status: 404, message: 'Страница не найдена' }); }); // ── Общий обработчик ошибок ─────────────────────────────────────────────── // eslint-disable-next-line no-unused-vars router.use((err, req, res, next) => { console.error('[UI] Error:', err); res.status(500).render('error', { status: 500, message: 'Внутренняя ошибка сервера' }); }); return router; } module.exports = { createUiRouter };