f502b0058d
Backend multi-tenant фундамент развёрнут на dev-БД liderra: 68 таблиц (52 обычных + 16 партиций) + 36 RLS-policies + 5 функций + 13 триггеров. `php artisan migrate:fresh` за 870 ms через одну raw-SQL миграцию `load_initial_schema.php` (DB::unprepared с db/schema.sql). Первый реальный запуск schema.sql на pristine PG 16 поймал 2 несовместимости v8.5, исправлены архитектурно (CTO-17): 1. CREATE UNIQUE INDEX на партиционированной deals (schema:1263) PG требует partition key (received_at) в UNIQUE; включить нельзя — ломает идемпотентность webhook'ов. Решение: новая таблица webhook_dedup_keys (не партиционированная, PK (tenant_id, source_crm_id) → deal_id, composite FK на deals(id, received_at) ON DELETE CASCADE, RLS tenant_isolation USING+WITH CHECK). UNIQUE INDEX в deals заменён на обычный. Webhook handler — двустадийная UPSERT. 2. GENERATED ALWAYS AS на pd_subject_requests.deadline_at (schema:1999) `+ INTERVAL '30 days'` не immutable. Решение: обычная TIMESTAMPTZ NOT NULL + триггер trg_pd_subject_requests_deadline + функция set_pd_subject_request_deadline(). Изменения: - db/schema.sql: v8.5 → v8.6 (заголовок, 1 новая таблица, 1 RLS-policy, 1 функция, 1 триггер, замена UNIQUE на обычный INDEX, замена GENERATED на TIMESTAMPTZ NOT NULL) - db/CHANGELOG_schema.md: новая запись §X v8.5→v8.6 - db/00_create_roles.sql (NEW): deployment-скрипт 4 ролей PG для production (crm_app_user, crm_admin_user BYPASSRLS, crm_migrator BYPASSRLS+CREATEDB, crm_audit_writer). На dev — postgres superuser (schema §13 разрешает) - db/02_grants.sql (NEW): GRANT/REVOKE из закомментированных секций §13 schema. REVOKE на 6 saas-таблицах для crm_app_user (defense-in-depth поверх RLS, OPEN-И-14). REVOKE DELETE на 4 финансовых таблицах для crm_admin_user (только soft markers) - app/database/migrations: удалены 3 default Laravel (users/cache/jobs дублировались с нашей schema), создан 0001_01_01_000000_load_initial_schema.php - .squawk.toml: + excluded_paths для db/00_create_roles.sql (psql client-side variables :'name' не парсятся libpg_query) - docs/Открытые_вопросы_v8_3.md: v1.18 → v1.19, CTO-17 закрыт фиксом, 70 ✅ / 5 🟦 / 4 ⏸. Техдолг: ТЗ §15-16 webhook handler нужно обновить под двустадийную dedup-логику - CLAUDE.md: v1.9 → v1.10 (§0 ссылки на schema v8.6 + реестр v1.19; §2 метрики БД 54→55/91→92/35→36/12→13/4→5; §6 фундамент развёрнут) - cspell-words.txt: +9 новых терминов Smoke-test через Boost MCP database-query: - 68 таблиц (включая webhook_dedup_keys + 16 партиций) - 36 RLS-policies - 35 RLS-enabled (relkind='r'; +2 partitioned 'p' = 37 total) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
3.0 KiB
TOML
45 lines
3.0 KiB
TOML
# =============================================================================
|
||
# .squawk.toml — конфигурация squawk (Прил. Н #15) для Лидерры
|
||
# =============================================================================
|
||
# Документация: https://squawkhq.com/docs/configuration
|
||
# Инструмент: линтер миграций PostgreSQL
|
||
# Применяется автоматически при запуске `squawk` без явного --config
|
||
# =============================================================================
|
||
#
|
||
# Стратегия проекта (Прил. Н v1.6, §10.3):
|
||
#
|
||
# 1. Этот конфиг тихо обрабатывает `db/schema.sql` (initial bootstrap, не миграция).
|
||
# Поэтому отключены:
|
||
# • правила безопасной миграции (require-timeout-settings, prefer-robust-stmts,
|
||
# require-concurrent-index-creation, constraint-missing-not-valid,
|
||
# adding-foreign-key-constraint) — нерелевантны для initial bootstrap
|
||
# (заливается на пустую БД при `db:reset`).
|
||
# • дизайнные предпочтения (prefer-identity, prefer-text-field,
|
||
# prefer-bigint-over-int, prefer-bigint-over-smallint) —
|
||
# в проекте сознательно используются BIGSERIAL и VARCHAR(N) с длинами
|
||
# для UI-валидации; SMALLINT для денормализованных status_code.
|
||
#
|
||
# 2. Для будущих **Laravel-миграций** в `app/database/migrations/*.sql`
|
||
# создавать локальный `.squawk.toml` рядом или запускать squawk без
|
||
# `--exclude`, чтобы получить полную проверку лайв-миграции.
|
||
# =============================================================================
|
||
|
||
excluded_paths = [
|
||
"db/00_create_roles.sql", # psql client-side variables (:'name') не парсятся libpg_query
|
||
]
|
||
|
||
excluded_rules = [
|
||
# --- Bootstrap-неприменимые ---------------------------------------------
|
||
"require-timeout-settings", # initial setup, не run-time миграция
|
||
"prefer-robust-stmts", # IF NOT EXISTS не нужен на пустой БД
|
||
"require-concurrent-index-creation", # нет конкурирующих подключений
|
||
"constraint-missing-not-valid", # initial CREATE, не ADD CONSTRAINT
|
||
"adding-foreign-key-constraint", # initial CREATE с FK inline
|
||
|
||
# --- Дизайнные решения проекта ------------------------------------------
|
||
"prefer-identity", # BIGSERIAL по консистентности (см. §6.4)
|
||
"prefer-text-field", # VARCHAR(N) для UI-валидации длин
|
||
"prefer-bigint-over-int", # INT для счётчиков с лимитом
|
||
"prefer-bigint-over-smallint", # SMALLINT для status_code (денорм.)
|
||
]
|