Files
ipwhitelist-app/ui/index.js
T
naeel 4af34616f9 fix: canAdminMode по email ntazetdinov@nubes.ru вместо isAdmin
- ui/index.js: email берётся из сессии (IAM/Keycloak)
- canAdminMode: (isAdmin AND WZ01112) OR (ntazetdinov@nubes.ru AND WZ01112)
- Временно для тестов, убрать после настройки IAM isAdmin
2026-06-11 13:41:35 +04:00

112 lines
5.2 KiB
JavaScript

'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);
// Email из сессии (IAM или Keycloak) или из JWT
const sessionEmail = req.session && req.session.user && req.session.user.email;
const email = sessionEmail || payload.email || payload.login || activeClientId + '@unknown';
// 🔮 isAdmin через IAM
const iamAdmin = req.session && req.session.user && req.session.user.isAdmin;
// ВРЕМЕННО: ntazetdinov@nubes.ru без isAdmin тоже видит toggle (для тестов)
const canAdminMode = !!(iamAdmin && activeClientId === ADMIN_CLIENT_ID)
|| (email === 'ntazetdinov@nubes.ru' && activeClientId === ADMIN_CLIENT_ID); // ← убрать после тестов
req.user = {
clientId: rawClientId,
allClientIds,
activeClientId,
companyId: payload.company_id || payload.companyId || null,
companyName: payload.company_name || payload.companyName || activeClientId,
email,
isAdmin: iamAdmin,
canAdminMode,
adminMode: !!(req.session && req.session.adminMode),
};
} 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) => {
const msg = err instanceof Error ? (err.message || err.stack || String(err)) : JSON.stringify(err);
console.error('[UI] Error:', msg);
res.status(500).render('error', { status: 500, message: 'Ошибка: ' + msg.slice(0, 500) });
});
return router;
}
module.exports = { createUiRouter };