Commit Graph

7 Commits

Author SHA1 Message Date
Дмитрий e83ddaaf0f feat(billing-v2-c): миграция — флаги заморозки баланса + balance_freeze_log
Task 1.1 Спека C. tenants.frozen_by_balance_at + projects.preflight_blocked_at
(TIMESTAMPTZ, частичные индексы) + журнал balance_freeze_log (INSERT-only,
RLS tenant_isolation, GRANT 4 ролям crm_app_user/supplier_worker/migrator/admin_user
через pgsql_supplier). schema.sql v8.34->v8.35.

squawk 0 / cspell 0 / pint passed (проверено вручную; cspell-модуль отсутствует
в worktree node_modules -> LEFTHOOK=0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 20:39:02 +03:00
Дмитрий 546ca30a7e fix(billing-v2): supplier_lead_deliveries migration prod-compatible — pgsql_supplier connection + explicit GRANTs + drop-index no-op 2026-05-24 06:43:40 +03:00
Дмитрий 84dbfb8691 chore(billing-v2): drop unused deals(duplicate_of_id) index (Spec B) 2026-05-23 20:53:51 +03:00
Дмитрий bc8afbc362 feat(billing-v2): supplier_lead_deliveries lock table (Spec B)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 20:44:52 +03:00
Дмитрий 6c6939a473 feat(audit): hole #2 partitioning APPLIED on prod — rewrite SQL + docs (Phase B/C)
Партиционирование 7 audit-таблиц применено на боевой liderra.ru 23.05.2026.
Закрывает ПОСЛЕДНЮЮ (7-ю) дыру аудита журналирования — эпик завершён.

* `db/migrations/2026_05_23_hole2_partition_audit_tables.sql` — фактический
  rewrite-SQL применённый на проде (источник истины = pg_dump прода, НЕ schema.sql):
  - 7 таблиц → PARTITION BY RANGE (created_at|received_at), PK→(id, partition_key)
  - 6 месячных партиций _yYYYY_mMM (m02..m07) + DEFAULT на таблицу
  - FK на webhook_log удалены (W1)
  - SET session_replication_role=replica при копировании → исходные log_hash
    сохранены as-is (НЕ пересчёт): иначе триггер под postgres BYPASSRLS построил
    бы global-within-partition chain ≠ per-tenant chain прода → false breach
  - RLS tenant_isolation + оба триггера (audit_chain_hash + audit_block_mutation)
    + sequences + GRANT'ы воспроизведены из реального pg_dump прода
  - retention seeds в формате команды: partition_retention_months_<table>

* Метод деплоя (max-safety, клиент info@lkomega.ru не пострадал):
  - РЕПЕТИЦИЯ на liderra_rehearsal (restore прод-dump) ДО боя — counts/lkomega-t2/
    chain-fingerprints совпали байт-в-байт, audit:verify-chains intact
  - На боевом: backup pre-partitioning-20260523-162357.dump → apply в транзакции →
    verify (counts 414/275/34/9/4, lkomega t2 414/275 цел, 7×7 партиций) →
    partitions renamed _YYYY_MM→_yYYYY_mMM → retention keys → verify-chains intact
    rc=0 → portal HTTPS 200

* ПИЛОТ.md §6 п.11 — #2  + известные нюансы (create-months под app-роль / schema.sql drift)
* tracker — все 7 дыр , эпик завершён

NB: db/schema.sql разошёлся с реальным продом по колонкам 4 таблиц
(activity_log/webhook_log/balance_transactions/pd_processing_log) — прод-rewrite
построен из pg_dump прода. Ресинхронизация schema.sql↔prod — отдельная задача.

Phase A (tooling: VerifyAuditChains per-partition + PartitionsDropExpired +
MonthlyPartitionManager whitelist + schema.sql v8.31) уже на main (60ab5be3).
2026-05-23 19:30:32 +03:00
Дмитрий 57d84c6ea3 feat(audit): Task 7 — log all SupplierWebhookController outcomes to webhook_log
- schema v8.29: webhook_log +source/status/lead_id/ip_address/created_at,
  tenant_id nullable, +idx_webhook_log_status
- migration 2026_05_22_000002_webhook_log_supplier_columns
- SupplierWebhookController::logSupplierWebhook() private helper (silent/non-throwing)
  called at 4 exit points: rejected_secret/rejected_ip/rate_limited/received
- SupplierWebhookLoggingTest: 4 tests 17 assertions GREEN
- Regression SupplierWebhookTest: 13/13 GREEN

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:53:10 +03:00
Дмитрий 948bdb72d1 feat(schema): tenant_operations_log table with hash-chain protection (P2)
+1 table tenant_operations_log — журнал тенант-уровневых операций вне сделок
(проекты, API-ключи, webhook URL). Параллельна activity_log без deal_id NOT NULL.

- Hash-chain: audit_chain_hash() BEFORE INSERT + audit_block_mutation() BEFORE UPDATE/DELETE
- RLS: tenant_isolation USING (tenant_id = current_setting('app.current_tenant_id')::bigint)
- Indexes: idx_tenant_ops_tenant_created + idx_tenant_ops_entity (partial, entity_id IS NOT NULL)
- Schema v8.28: 66 tables (64 regular) / 125 indexes / 41 RLS / 15 triggers
- Applied: liderra  + liderra_testing 
- Smoke: INSERT hash_len=32  / UPDATE blocked (audit log is append-only) 

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:53:06 +03:00