// ═══════════════════════════════════════════════════════════════════════════════ // V2 — CRUD API-слой // // ЭТО: чистые функции — API между фронтендами и БД. // НЕ: Express, HTTP, req/res, сессии. // // ЗАЧЕМ: // 1. Фронтенды (user/, admin/, test/) не знают про SQL, транзакции, companyId. // 2. Валидация вызывается ОДИН раз здесь, не дублируется в каждом фронтенде. // 3. clientId (W-номер) → companyId (внутренний ID БД) резолвится здесь. // 4. Каждую функцию можно тестировать изолированно, без Express. // // КОНТРАКТ: // list(clientId) → { entries, used, limit } // add(clientId, cidr, ...) → { entry, wasNormalized } // edit(entryId, clientId, ...) → { entry, wasNormalized } // remove(entryId, clientId, ...) → void // // Вход: clientId — W-номер компании (строка вида "WZ10001"). // email — почта юзера (для аудита). // impBy — почта админа при имперсонации (null если нет). // // Ошибки: throw — валидация, лимит, дубликат, пересечение. // ═══════════════════════════════════════════════════════════════════════════════ const q = require('../db/queries'); const { validate } = require('../validators'); // ── resolve(clientId) → company (внутренний хелпер) ────────────────────────── // Атомарный upsert: если компании с таким W-номером нет — создаст. // Возвращает { id, client_id, name, custom_limit }. // Вызывается из list/add/edit/remove перед каждой операцией. async function resolve(clientId) { return q.getOrCreateCompany(clientId, clientId); } // ── list(clientId, includeDeleted?) → { entries, used, limit } ────────────── // Всегда возвращает лимит и счётчик активных записей. // used — только неудалённые (deleted_at IS NULL). async function list(clientId, includeDeleted = false) { const co = await resolve(clientId); const entries = await q.listEntries(co.id, includeDeleted); const limit = await q.getLimit(co); const used = entries.filter(e => !e.deleted_at).length; return { entries, used, limit }; } // ── add(clientId, cidr, comment, email, impBy) → { entry, wasNormalized } ─── // ПОРЯДОК: // 1. validate(rawCidr) — проверка ТЗ (маска, диапазоны, нормализация). // 2. resolve(clientId) — получить/создать компанию. // 3. db.createEntry() — транзакция: лимит, дубликаты, INSERT + аудит. // Если validate() бросает — до БД не доходим. async function add(clientId, cidr, comment, email, impBy) { const { cidr: validated, wasNormalized } = validate(cidr); const co = await resolve(clientId); return q.createEntry(co.id, validated, comment, email, impBy, wasNormalized); } // ── edit(entryId, clientId, cidr, comment, email, impBy) → { entry, wasNormalized } // Аналогично add, но для существующей записи. // entryId — id из v2_entries (число). async function edit(entryId, clientId, cidr, comment, email, impBy) { const { cidr: validated, wasNormalized } = validate(cidr); const co = await resolve(clientId); return q.updateEntry(entryId, co.id, validated, comment, email, impBy, wasNormalized); } // ── remove(entryId, clientId, email, impBy) → void ────────────────────────── // Soft delete: ставит deleted_at, записывает аудит. // Запись остаётся в БД, но не показывается в list(). async function remove(entryId, clientId, email, impBy) { const co = await resolve(clientId); return q.deleteEntry(entryId, co.id, email, impBy); } // ── restore(entryId, clientId, email, impBy) → void ───────────────────────── // Восстановление удалённой записи. async function restore(entryId, clientId, email, impBy) { const co = await resolve(clientId); return q.restoreEntry(entryId, co.id, email, impBy); } module.exports = { list, add, edit, remove, restore };