diff --git a/package.json b/package.json index 328b6af..f6f8c12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipwhitelist", - "version": "0.1.8", + "version": "0.1.9", "description": "IP WhiteList microservice for cloud provider", "main": "server.js", "scripts": { diff --git a/v2/src/admin/index.js b/v2/src/admin/index.js index 65dbe91..27c6329 100644 --- a/v2/src/admin/index.js +++ b/v2/src/admin/index.js @@ -13,7 +13,7 @@ const express = require('express'); const q = require('../db/queries'); const config = require('../config'); -function createAdminRouter() { +function createAdminRouter({ generateCsrfToken } = {}) { const router = express.Router(); router.use((req, res, next) => { @@ -30,19 +30,29 @@ function createAdminRouter() { router.get('/', async (req, res) => { try { const companies = await q.getAllCompanies(); - res.render('v2/admin', { companies, user: req.adminUser, version: config.version }); + res.render('v2/admin', { + companies, user: req.adminUser, version: config.version, + csrfToken: generateCsrfToken ? generateCsrfToken(req, res) : '', + }); } catch (e) { res.status(500).send('
' + e.message + ''); } }); - // ── GET /audit ────────────────────────────────────────────────────── + // ── GET /audit — журнал операций с пагинацией и фильтром по дате ─── router.get('/audit', async (req, res) => { try { const companyId = req.query.companyId ? parseInt(req.query.companyId) : null; - const audit = await q.getAudit(companyId); + const days = Math.min(Math.max(parseInt(req.query.days) || 365, 1), 365); + const page = Math.max(parseInt(req.query.page) || 1, 1); + const perPage = 100; + const result = await q.getAudit({ companyId, days, page, limit: perPage }); const company = companyId ? await q.getCompanyById(companyId) : null; - res.render('v2/admin-audit', { audit, company, version: config.version }); + res.render('v2/admin-audit', { + audit: result.rows, total: result.total, + page, pages: Math.ceil(result.total / perPage) || 1, + days, company, version: config.version, + }); } catch (e) { res.status(500).send('
' + e.message + ''); } diff --git a/v2/src/config/index.js b/v2/src/config/index.js index 5747417..62e8183 100644 --- a/v2/src/config/index.js +++ b/v2/src/config/index.js @@ -8,7 +8,7 @@ // ═══════════════════════════════════════════════════════════════════════════════ module.exports = { - version: '0.1.8', + version: '0.1.9', // ── IAM ────────────────────────────────────────────────────────────────── iamUrl: process.env.V2_IAM_URL || 'https://auth-api.ngcloud.ru/api/v1/auth/user', diff --git a/v2/src/db/queries.js b/v2/src/db/queries.js index d79494b..dabc32c 100644 --- a/v2/src/db/queries.js +++ b/v2/src/db/queries.js @@ -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 = { diff --git a/views/v2/admin-audit.ejs b/views/v2/admin-audit.ejs index 798dbb4..5e44e67 100644 --- a/views/v2/admin-audit.ejs +++ b/views/v2/admin-audit.ejs @@ -73,9 +73,21 @@