fix: три бага — CSRF в админке, пагинация аудита, скрыта кнопка выхода
1. Изменение лимитов (Баг 1): добавлен CSRF-токен в форму /v2/admin/limit
- v2/src/admin/index.js: createAdminRouter принимает {generateCsrfToken},
передаёт csrfToken в шаблон admin.ejs
- views/v2/admin.ejs: скрытое поле <input name=_csrf> в форме лимита
2. Журнал операций (Баг 2): пагинация + фильтр по дате вместо хардкод LIMIT 500
- v2/src/db/queries.js: getAudit({companyId,days,page,limit}) —
COUNT(*) OVER() для пагинации, WHERE created_at > NOW() - INTERVAL
- v2/src/admin/index.js: чтение days/page из query string, limit=100
- views/v2/admin-audit.ejs: фильтр 30/90/365 дней, prev/next пагинация
3. Кнопка Выйти (Баг 3): скрыта, т.к. /v2/logout не разлогинивает из Keycloak
- views/v2/user.ejs: кнопка закомментирована с пояснением
v0.1.8 → v0.1.9
This commit is contained in:
+38
-7
@@ -265,16 +265,47 @@ async function logAudit(userEmail, companyId, action, oldValue, newValue, entryI
|
||||
);
|
||||
}
|
||||
|
||||
async function getAudit(companyId = null) {
|
||||
let sql = `
|
||||
SELECT a.*, c.name AS company_name, c.client_id
|
||||
/**
|
||||
* Журнал операций с фильтром по дате и пагинацией.
|
||||
* @param {Object} opts
|
||||
* @param {number|null} opts.companyId — фильтр по компании (null = все)
|
||||
* @param {number} opts.days — глубина в днях (по умолчанию 365)
|
||||
* @param {number} opts.page — страница (с 1)
|
||||
* @param {number} opts.limit — записей на странице
|
||||
* @returns {{ rows: Array, total: number }}
|
||||
*/
|
||||
async function getAudit({ companyId = null, days = 365, page = 1, limit = 100 } = {}) {
|
||||
const offset = (page - 1) * limit;
|
||||
const params = [];
|
||||
|
||||
let where = '';
|
||||
if (companyId) {
|
||||
where += ' WHERE a.company_id = $' + (params.length + 1);
|
||||
params.push(companyId);
|
||||
}
|
||||
const dayParam = '$' + (params.length + 1);
|
||||
where += (where ? ' AND' : ' WHERE') + ' a.created_at > NOW() - (' + dayParam + ' || \' days\')::INTERVAL';
|
||||
params.push(String(days));
|
||||
|
||||
const limitIdx = params.length + 1;
|
||||
const offsetIdx = params.length + 2;
|
||||
|
||||
const sql = `
|
||||
SELECT a.*, c.name AS company_name, c.client_id,
|
||||
COUNT(*) OVER() AS total_count
|
||||
FROM audit_log a
|
||||
JOIN companies c ON c.id = a.company_id
|
||||
${where}
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT $${limitIdx} OFFSET $${offsetIdx}
|
||||
`;
|
||||
const params = [];
|
||||
if (companyId) { sql += ' WHERE a.company_id = $1'; params.push(companyId); }
|
||||
sql += ' ORDER BY a.created_at DESC LIMIT 500';
|
||||
return (await pool.query(sql, params)).rows;
|
||||
params.push(limit, offset);
|
||||
|
||||
const result = await pool.query(sql, params);
|
||||
return {
|
||||
rows: result.rows,
|
||||
total: result.rows.length > 0 ? parseInt(result.rows[0].total_count, 10) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user