refactor: шаг 7 — /audit /admin /admin/limit вынесены в src/routes/admin.js
This commit is contained in:
@@ -76,78 +76,15 @@ async function start() {
|
||||
backUrl,
|
||||
}));
|
||||
|
||||
// ── Журнал аудита (только admin) ──────────────────────────────────────────────
|
||||
// ТЗ 4.6: «журнал доступен для просмотра только администратору»
|
||||
app.get('/audit', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
// Если передан ?company=X — показываем только эту компанию
|
||||
const companyId = req.query.company ? parseInt(req.query.company, 10) : null;
|
||||
const companies = await q.getAllCompanies();
|
||||
const rows = await q.getAudit(companyId || null);
|
||||
res.render('audit', {
|
||||
user: req.user,
|
||||
rows,
|
||||
companies,
|
||||
selectedCompanyId: companyId,
|
||||
error: req.query.error || null,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('GET /audit error:', e);
|
||||
res.render('audit', {
|
||||
user: req.user, rows: [], companies: [],
|
||||
selectedCompanyId: null, error: 'Ошибка загрузки: ' + e.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ── Страница управления лимитами (только admin) ───────────────────────────────
|
||||
app.get('/admin', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const companies = await q.getAllCompanies();
|
||||
const defaultLimit = parseInt(process.env.DEFAULT_LIMIT, 10) || 15;
|
||||
res.render('admin', {
|
||||
user: req.user,
|
||||
companies,
|
||||
defaultLimit,
|
||||
message: req.query.message || null,
|
||||
error: req.query.error || null,
|
||||
// CSRF-токен для форм /admin/limit/:id на странице
|
||||
csrfToken: generateCsrfToken(req, res),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('GET /admin error:', e);
|
||||
res.status(500).send('Ошибка: ' + e.message);
|
||||
}
|
||||
});
|
||||
|
||||
// ── Изменение лимита компании (только admin) ──────────────────────────────────
|
||||
// ТЗ 4.5: «администратор может задать индивидуальный лимит»
|
||||
app.post('/admin/limit/:companyId', requireAdmin, mutationLimiter, doubleCsrfProtection, async (req, res) => {
|
||||
const companyId = parseInt(req.params.companyId, 10);
|
||||
if (!Number.isFinite(companyId) || companyId <= 0) {
|
||||
return res.redirect('/admin?error=' + encodeURIComponent('Некорректный ID компании'));
|
||||
}
|
||||
|
||||
try {
|
||||
const raw = req.body.limit;
|
||||
let newLimit;
|
||||
if (raw === '' || raw === null || raw === undefined) {
|
||||
// Пустое поле = сброс на глобальный дефолт
|
||||
newLimit = null;
|
||||
} else {
|
||||
newLimit = parseInt(raw, 10);
|
||||
if (!Number.isFinite(newLimit) || newLimit < 0) {
|
||||
throw new Error('Лимит должен быть целым числом ≥ 0, или пустым для сброса');
|
||||
}
|
||||
}
|
||||
|
||||
await q.setLimit(companyId, newLimit);
|
||||
const label = newLimit === null ? 'сброшен на дефолт' : `установлен: ${newLimit}`;
|
||||
res.redirect('/admin?message=' + encodeURIComponent(`Лимит ${label}`));
|
||||
} catch (e) {
|
||||
res.redirect('/admin?error=' + encodeURIComponent(e.message));
|
||||
}
|
||||
});
|
||||
// GET /audit, GET /admin, POST /admin/limit/:companyId.
|
||||
// src/routes/admin.js содержит логику и комментарии.
|
||||
app.use(require('./src/routes/admin').createRouter({
|
||||
q,
|
||||
requireAdmin,
|
||||
doubleCsrfProtection,
|
||||
mutationLimiter,
|
||||
generateCsrfToken,
|
||||
}));
|
||||
|
||||
// ── CSRF error handler ────────────────────────────────────────────────────────
|
||||
// doubleCsrfProtection вызывает next(err) с кодом 403 при невалидном токене.
|
||||
|
||||
Reference in New Issue
Block a user