feat(insights): manual-entry Insight entity CRUD + §19 audit (#948 part A) #1164

Merged
bot-backend merged 1 commit from feat/insight-entity-948a into main 2026-06-08 07:42:16 +00:00
Collaborator

Что

#948 Part A — сущность «Инсайт» (ручная, ACL-гейт, §7.13/§8.9). Аналитик пишет free-form intel про локацию/участок с пометкой «непублично». Часть #944. (Part B — «Локация» как first-class entity §8.2 — отдельный follow-up, это больший district-рефактор.)

Разблокировано #962 (циклическая зависимость EPIC18↔EPIC4 разорвана — roles теперь есть).

Содержимое

  • data/sql/145_insight.sql — таблица insight (idempotent; geom GENERATED из lon/lat; индексы). Аддитивно расширяет CHECK audit_log.action += insight_write (тот же ck_audit_log_action, to_regclass-guarded, superset — не ломает #962-аудит, не правит applied 144).
  • api/v1/insights.py + services/insights.py (raw-SQL, зеркало custom_pois) + schemas/insight.py (Pydantic v2). CRUD: POST/GET(list+filters+pagination)/GET{id}/PUT/DELETE.
  • audit_middleware.classify_path (#962, моё): method-aware → insight_write для POST/PUT/DELETE (GET не аудируется), обратно совместимо.
  • ACL: created_by из X-Authenticated-User (не из тела — спуфинг автора невозможен). Backend rbac_guard hard-блокирует только /api/v1/admin/*; pilot отсекается frontend RouteGuard+Caddy (как parcels/custom_pois). is_confidential — пометка без per-record backend-гейта (analyst видит, per #962).

Качество

  • code-reviewer: APPROVE — оба гейта: cross-migration CHECK ALTER корректен (правильное имя constraint, idempotent, guarded, superset → #962-аудит цел) + zero SQL-injection (всё bind, CAST not ::, pagination 1-200). Author-integrity airtight. Docstrings исправлены на точную ACL-формулировку. 86 тестов (28 insight + 5 audit), ruff, без новых deps. parcels.py не тронут (NON-colliding).

Deploy/verify

145 применяется до нового кода (стандартный _schema_migrations order). #962+#948A backend-деплой пока в очереди раннера — проверю обе на проде когда backend пересоберётся.

Refs #948 (Part A; Part B Location — follow-up).

## Что #948 **Part A — сущность «Инсайт»** (ручная, ACL-гейт, §7.13/§8.9). Аналитик пишет free-form intel про локацию/участок с пометкой «непублично». Часть #944. (Part B — «Локация» как first-class entity §8.2 — отдельный follow-up, это больший district-рефактор.) Разблокировано #962 (циклическая зависимость EPIC18↔EPIC4 разорвана — roles теперь есть). ## Содержимое - `data/sql/145_insight.sql` — таблица `insight` (idempotent; geom GENERATED из lon/lat; индексы). Аддитивно расширяет CHECK `audit_log.action += insight_write` (тот же `ck_audit_log_action`, to_regclass-guarded, superset — **не ломает #962-аудит, не правит applied 144**). - `api/v1/insights.py` + `services/insights.py` (raw-SQL, зеркало custom_pois) + `schemas/insight.py` (Pydantic v2). CRUD: POST/GET(list+filters+pagination)/GET{id}/PUT/DELETE. - `audit_middleware.classify_path` (#962, моё): method-aware → `insight_write` для POST/PUT/DELETE (GET не аудируется), обратно совместимо. - **ACL:** `created_by` из `X-Authenticated-User` (не из тела — спуфинг автора невозможен). Backend rbac_guard hard-блокирует только /api/v1/admin/*; pilot отсекается frontend RouteGuard+Caddy (как parcels/custom_pois). `is_confidential` — пометка без per-record backend-гейта (analyst видит, per #962). ## Качество - `code-reviewer`: ✅ APPROVE — оба гейта: cross-migration CHECK ALTER корректен (правильное имя constraint, idempotent, guarded, superset → #962-аудит цел) + zero SQL-injection (всё bind, CAST not ::, pagination 1-200). Author-integrity airtight. Docstrings исправлены на точную ACL-формулировку. **86 тестов** (28 insight + 5 audit), ruff, без новых deps. `parcels.py` не тронут (NON-colliding). ## Deploy/verify 145 применяется до нового кода (стандартный `_schema_migrations` order). #962+#948A backend-деплой пока в очереди раннера — проверю обе на проде когда backend пересоберётся. Refs #948 (Part A; Part B Location — follow-up).
bot-backend added the
scope/backend
GG-форсайт
site-finder
labels 2026-06-08 07:42:14 +00:00
bot-backend added 1 commit 2026-06-08 07:42:14 +00:00
feat(insights): manual-entry Insight entity CRUD + §19 audit (#948 part A)
All checks were successful
CI / changes (push) Successful in 7s
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Has been skipped
Deploy / changes (push) Successful in 7s
Deploy / build-backend (push) Successful in 1m41s
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Successful in 2m50s
Deploy / deploy (push) Successful in 1m22s
CI / backend-tests (push) Successful in 6m35s
CI / backend-tests (pull_request) Successful in 6m32s
6400ebde24
Insight = аналитик пишет free-form intel про локацию/участок с пометкой
«непублично» (is_confidential, §7.13/§8.9). CRUD под /api/v1/insights;
created_by из X-Authenticated-User (не из тела — спуфинг автора невозможен).
Зеркало custom_pois: raw-SQL service + Pydantic v2 schemas, sync def handlers
(threadpool, off event loop).

- data/sql/145_insight.sql: table insight (idempotent, geom GENERATED из lon/lat,
  индексы district/cad_num/is_confidential/created_by/GIST); аддитивно расширяет
  CHECK audit_log.action += insight_write (тот же constraint ck_audit_log_action,
  to_regclass-guarded, superset — не ломает #962-аудит; НЕ правит applied 144).
- audit_middleware.classify_path: method-aware insight_write для POST/PUT/DELETE
  /api/v1/insights (GET-чтения не аудируются); обратно совместимо с parcels.
- ACL: backend rbac_guard hard-блокирует только /api/v1/admin/*; pilot отсекается
  frontend RouteGuard + Caddy (как parcels/custom_pois). is_confidential — стораемая
  пометка без per-record backend-гейта (analyst видит, per #962-политике).
- tests: 28 insight (CRUD+filters+required+#261 commit) + 5 audit. 86 зелёных, ruff.

Part B (Location first-class, §8.2) — отдельный follow-up.

Refs #948.
bot-backend merged commit 6400ebde24 into main 2026-06-08 07:42:16 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1164
No description provided.