v0.5.102: единый интерфейс /v2/app для юзеров и админов
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
# V2 Unified Interface — v0.5.102 (2026-06-15)
|
||||
|
||||
**Что сделано:** объединение юзерского и админского интерфейсов в один `/v2/app`.
|
||||
|
||||
## Изменённые файлы
|
||||
|
||||
### `v2/src/user/index.js` — единый роутер
|
||||
- GET `/` — для юзеров и админов
|
||||
- Админ: dropdown = все компании из БД (`getAllCompanies()`), по умолчанию своя
|
||||
- Юзер: dropdown = свои профили из IAM, по умолчанию активный
|
||||
- `?switchTo=W***` — смена компании: админ → любая, юзер → только свои
|
||||
- CRUD без изменений (add/edit/delete через `req.clientId`)
|
||||
|
||||
### `views/v2/user.ejs` — единый шаблон
|
||||
- Дизайн из оригинала: лого Nubes, CSS-переменные, card-раскладка
|
||||
- Хедер: лого | "IP WhiteList v{VERSION}" | email (админ) | Выйти (реальный юзер)
|
||||
- Dropdown компаний + таблица IP с inline CRUD
|
||||
- Статистика, форма добавления, поддержка удалённых записей
|
||||
|
||||
### `v2/server.js`
|
||||
- `/v2/admin` → 302 на `/v2/app`
|
||||
- `toggle-admin` → всегда редирект на `/v2/app`
|
||||
- Убран импорт `createAdminRouter`
|
||||
|
||||
### Версии
|
||||
- `v2/src/config/index.js`: 0.5.98 → 0.5.99
|
||||
- `package.json`: 0.5.101 → 0.5.102
|
||||
|
||||
## Синтаксис
|
||||
`node -c` — OK на всех изменённых файлах.
|
||||
+3
-5
@@ -28,7 +28,6 @@ const auth = require('./src/auth');
|
||||
const { resolveContext } = require('./src/router');
|
||||
const { enhanceImpersonation } = require('./src/impersonation');
|
||||
const { createUserRouter } = require('./src/user');
|
||||
const { createAdminRouter } = require('./src/admin');
|
||||
const { pool } = require('./src/db');
|
||||
const { ensureSchema } = require('./src/db/schema');
|
||||
|
||||
@@ -53,8 +52,8 @@ function createV2Router() {
|
||||
// ── /v2/app — пользовательский CRUD (только с сессией) ──────────────
|
||||
router.use('/app', enhanceImpersonation, resolveContext, createUserRouter());
|
||||
|
||||
// ── /v2/admin — админка (только для isAdmin=true) ────────────────────
|
||||
router.use('/admin', enhanceImpersonation, resolveContext, createAdminRouter());
|
||||
// ── /v2/admin — редирект на единый интерфейс ───────────────────
|
||||
router.get('/admin', (req, res) => res.redirect('/v2/app'));
|
||||
|
||||
// ── POST /v2/toggle-admin — переключить режим администратора ────
|
||||
router.post('/toggle-admin', (req, res) => {
|
||||
@@ -63,10 +62,9 @@ function createV2Router() {
|
||||
const canAdmin = (u.allClientIds || []).includes('WZ01112') || u.clientId === 'WZ01112' || !!u.isAdmin;
|
||||
if (!canAdmin) return res.redirect('/v2/app');
|
||||
u.adminMode = !u.adminMode;
|
||||
const target = u.adminMode ? '/v2/admin' : '/v2/app';
|
||||
req.session.save(err => {
|
||||
if (err) console.error('[toggle-admin] session save error:', err.message);
|
||||
res.redirect(target);
|
||||
res.redirect('/v2/app');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
module.exports = {
|
||||
version: '0.5.98',
|
||||
version: '0.5.99',
|
||||
|
||||
// ── IAM ──────────────────────────────────────────────────────────────────
|
||||
iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user',
|
||||
|
||||
+65
-16
@@ -1,15 +1,22 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// V2 — Фронтенд пользователя (Express + EJS)
|
||||
// V2 — Единый фронтенд (Express + EJS) — юзеры и админы
|
||||
//
|
||||
// ЭТО: Express-роутер, рендерит EJS-шаблоны.
|
||||
// ЭТО: Express-роутер, рендерит EJS-шаблон views/v2/user.ejs.
|
||||
// НЕ: работа с БД (только через crud/).
|
||||
//
|
||||
// ШАБЛОН: views/v2/user.ejs — данные: entries, limit, used, companies,
|
||||
// activeClientId, user, includeDeleted, version.
|
||||
// ОДИН ИНТЕРФЕЙС для юзеров и админов:
|
||||
// - Юзер: dropdown = свои профили из IAM, CRUD внутри выбранной компании.
|
||||
// - Админ: dropdown = все компании из БД, CRUD внутри выбранной.
|
||||
// - По умолчанию: активный профиль (is_active_profile=true).
|
||||
//
|
||||
// ПЕРЕКЛЮЧЕНИЕ КОМПАНИИ: ?switchTo=W*** → обновляет activeClientId в сессии.
|
||||
// - Юзер: только свои профили.
|
||||
// - Админ: любая компания.
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
const express = require('express');
|
||||
const crud = require('../crud');
|
||||
const q = require('../db/queries');
|
||||
const config = require('../config');
|
||||
|
||||
function createUserRouter() {
|
||||
@@ -18,29 +25,71 @@ function createUserRouter() {
|
||||
// ── GET / — список записей + форма ──────────────────────────────────
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const isAdmin = req.isAdmin;
|
||||
const clId = req.clientId;
|
||||
const allCompanies = req.profiles.length > 0
|
||||
? req.profiles
|
||||
: [{ client_id: clId, company_name: req.companyName || clId }];
|
||||
|
||||
// ── Переключение компании ──────────────────────────────────────
|
||||
if (req.query.switchTo) {
|
||||
const targetId = req.query.switchTo;
|
||||
if (isAdmin) {
|
||||
// Админ может переключиться на любую компанию
|
||||
req.session.user.activeClientId = targetId;
|
||||
req.session.save(() => res.redirect('/v2/app'));
|
||||
return;
|
||||
} else {
|
||||
// Юзер — только свои профили
|
||||
const allowed = (req.profiles || []).find(p => p.client_id === targetId);
|
||||
if (allowed) {
|
||||
req.session.user.activeClientId = targetId;
|
||||
req.session.save(() => res.redirect('/v2/app'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Компании для dropdown ───────────────────────────────────────
|
||||
let companies;
|
||||
if (isAdmin) {
|
||||
const all = await q.getAllCompanies();
|
||||
companies = all.map(c => ({
|
||||
client_id: c.client_id,
|
||||
name: c.name || c.client_id,
|
||||
active_count: c.active_count,
|
||||
}));
|
||||
} else {
|
||||
companies = (req.profiles && req.profiles.length > 0)
|
||||
? req.profiles.map(p => ({
|
||||
client_id: p.client_id,
|
||||
name: p.company_name || p.client_id,
|
||||
is_active: p.is_active_profile,
|
||||
}))
|
||||
: [{ client_id: clId, name: req.companyName || clId, is_active: true }];
|
||||
}
|
||||
|
||||
// ── Записи ──────────────────────────────────────────────────────
|
||||
const includeDeleted = req.query.deleted === '1';
|
||||
const { entries, used, limit } = await crud.list(clId, includeDeleted);
|
||||
|
||||
// Переключение компании
|
||||
if (req.query.switchTo && allCompanies.find(c => c.client_id === req.query.switchTo)) {
|
||||
req.session.user.activeClientId = req.query.switchTo;
|
||||
return res.redirect('/v2/app');
|
||||
}
|
||||
// ── Данные пользователя для шаблона ────────────────────────────
|
||||
const sessionUser = req.session && req.session.user ? req.session.user : {};
|
||||
const templateUser = {
|
||||
email: req.email,
|
||||
fio: sessionUser.fio || null,
|
||||
isAdmin: req.canAdmin || false,
|
||||
adminMode: isAdmin,
|
||||
isImpersonated: req.isImpersonated || false,
|
||||
originalUserEmail: sessionUser.originalUserEmail || '',
|
||||
originalUserFullName: sessionUser.originalUserFullName || '',
|
||||
originalUserCompany: sessionUser.originalUserCompany || '',
|
||||
};
|
||||
|
||||
res.render('v2/user', {
|
||||
entries, limit, used,
|
||||
companies: allCompanies,
|
||||
companies,
|
||||
activeClientId: clId,
|
||||
user: req.user || {},
|
||||
user: templateUser,
|
||||
includeDeleted,
|
||||
version: config.version,
|
||||
canAdmin: req.canAdmin || false,
|
||||
adminMode: req.adminMode || false,
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(500).send('<h2>Ошибка</h2><pre>' + e.message + '</pre><a href="/v2/app">Назад</a>');
|
||||
|
||||
Reference in New Issue
Block a user