gendesign/_wt-cadval/data/sql/144_audit_log.sql
bot-backend 542ff1c9ca docs(tradein/domclick): два комментария описывали пул, которого нет с миграции 253
Оба места утверждали, что у Домклика один выделенный резидентный прокси и
пула нет. Это перестало быть правдой ещё в #2800 (миграция 253 сняла
резервацию узла), но текст остался — и именно на него опирался тикет #3189,
поставленный под «калибровку» ограничения, которого не существует.
Свип к тому же ходит через пул давно (serp.py:359), а бэкфилл подключён к
нему в PR #3222.

Заодно докстринг называл не тот ограничитель: свип кладёт не счётчик
провалов lease, а break по первому DomClickBlockedError (#2854) — до
ротации дело не доходит ни при каком счётчике, отсюда buckets_completed=0.

Только комментарии, поведение не меняется. Миграция 175 уже применена, а
_schema_migrations трекает по имени файла без checksum — правка текста
её не перезапустит.
2026-08-29 18:57:25 +03:00

65 lines
3.6 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 144_audit_log.sql
-- #962 (EPIC18, ТЗ §19): аудит-лог чувствительных действий аналитика/пользователей.
--
-- Пишется best-effort FastAPI-middleware (app/core/audit_middleware.py) ПОСЛЕ
-- ответа на запрос для ключевых действий:
-- POST /api/v1/parcels/{cad}/analyze → action='analyze'
-- GET /api/v1/parcels/{cad}/forecast → action='forecast'
-- GET /api/v1/parcels/{cad}/forecast/export → action='export'
-- Прочие matched-нестандартные пути → action='other'. /health, static,
-- non-matched пути НЕ аудируются.
--
-- Best-effort: сбой записи в audit_log НИКОГДА не ломает основной запрос
-- (middleware оборачивает write в try/except).
--
-- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз NN=144).
-- Idempotent: CREATE TABLE/INDEX IF NOT EXISTS + guarded DO-блок для CHECK.
BEGIN;
-- ── 1. Таблица audit_log ──────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS audit_log (
id BIGSERIAL PRIMARY KEY,
username TEXT NOT NULL, -- из X-Authenticated-User
role TEXT, -- get_role(username) на момент запроса
action TEXT NOT NULL, -- analyze | forecast | export | other
method TEXT NOT NULL, -- HTTP-метод (GET/POST/...)
path TEXT NOT NULL, -- request.url.path
cad_num TEXT, -- кадастровый номер из пути (NULL если нет)
status_code INTEGER, -- HTTP-статус ответа
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
detail JSONB -- произвольный контекст (резерв)
);
COMMENT ON TABLE audit_log IS
'Аудит чувствительных действий (#962, ТЗ §19). Пишется best-effort '
'middleware app/core/audit_middleware.py ПОСЛЕ ответа на запрос. '
'Сбой записи не влияет на основной запрос.';
COMMENT ON COLUMN audit_log.action IS
'analyze | forecast | export | other — выводится из request path в middleware.';
COMMENT ON COLUMN audit_log.cad_num IS
'Кадастровый номер из /parcels/{cad}/... (напр. 66:41:0204016:10), NULL если нет.';
COMMENT ON COLUMN audit_log.detail IS
'Резервное JSONB-поле под доп. контекст (пока не заполняется).';
-- ── 2. CHECK на допустимые action (guarded — idempotent при ре-apply) ────────
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'ck_audit_log_action'
) THEN
ALTER TABLE audit_log
ADD CONSTRAINT ck_audit_log_action
CHECK (action IN ('analyze', 'forecast', 'export', 'other'));
END IF;
END
$$;
-- ── 3. Индексы: lookup по юзеру во времени + по кадастру ──────────────────────
CREATE INDEX IF NOT EXISTS idx_audit_log_username_created
ON audit_log (username, created_at);
CREATE INDEX IF NOT EXISTS idx_audit_log_cad_num
ON audit_log (cad_num);
COMMIT;