fix(billing-v2): supplier_lead_deliveries migration prod-compatible — pgsql_supplier connection + explicit GRANTs + drop-index no-op

This commit is contained in:
Дмитрий
2026-05-24 06:43:40 +03:00
parent 84dbfb8691
commit 546ca30a7e
4 changed files with 62 additions and 9 deletions
@@ -16,11 +16,14 @@ return new class extends Migration
if ($sql === false) {
throw new RuntimeException('Migration SQL file not found.');
}
DB::unprepared($sql);
// Prod: crm_app_user (default pgsql) не имеет CREATE на schema public.
// Используем pgsql_supplier (crm_supplier_worker, BYPASSRLS, имеет CREATE).
// На dev pgsql_supplier тоже = postgres superuser → работает идентично.
DB::connection('pgsql_supplier')->unprepared($sql);
}
public function down(): void
{
DB::unprepared('DROP TABLE IF EXISTS supplier_lead_deliveries CASCADE;');
DB::connection('pgsql_supplier')->unprepared('DROP TABLE IF EXISTS supplier_lead_deliveries CASCADE;');
}
};
@@ -1,21 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* No-op миграция-маркер (Billing v2 Spec B): телефонный дедуп удалён,
* индекс deals_duplicate_of_id_idx становится неиспользуемым (колонка
* deals.duplicate_of_id оставлена спящей drop отдельной DBA-задачей,
* mirrors Spec A balance_leads two-phase).
*
* Почему миграция no-op: индекс владельца crm_migrator, DROP требует прав
* owner; .env не имеет crm_migrator credentials, а pgsql_supplier
* (crm_supplier_worker) не владеет индексами на partitioned deals. Запуск
* DROP отложен выполняется напрямую psql под postgres-superuser отдельно.
* Эта миграция только маркер «обработано», чтобы migrate --force не падал.
*
* На dev (postgres-superuser) индекс уже отсутствует из schema.sql v8.34,
* поэтому ничего не делаем тоже корректно.
*/
public function up(): void
{
$sql = file_get_contents(base_path('../db/migrations/2026_05_23_201_drop_deals_duplicate_of_id_index.sql'));
if ($sql === false) {
throw new RuntimeException('Migration SQL file not found.');
}
DB::unprepared($sql);
// Intentionally empty.
}
public function down(): void
{
// No-op: index has been deemed unused (telephone dedup removed). Recreation is unnecessary.
// No-op: recreation is unnecessary (concept removed).
}
};
@@ -16,3 +16,23 @@ CREATE TABLE supplier_lead_deliveries (
ALTER TABLE supplier_lead_deliveries ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON supplier_lead_deliveries
USING (tenant_id = current_setting('app.current_tenant_id')::bigint);
-- Явные GRANT'ы для 4 ролей: на prod таблица создаётся crm_supplier_worker
-- (default privileges от postgres-superuser не наследуются на чужие creator-role).
-- Mirror webhook_dedup_keys grant pattern. DO block — idempotent + dev-safe
-- (на dev ролей нет → silent skip).
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_app_user') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_app_user;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_admin_user') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_admin_user;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_supplier_worker') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_supplier_worker;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_migrator') THEN
GRANT ALL PRIVILEGES ON supplier_lead_deliveries TO crm_migrator;
END IF;
END $$;
+20
View File
@@ -2062,6 +2062,26 @@ ALTER TABLE supplier_lead_deliveries ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON supplier_lead_deliveries
USING (tenant_id = current_setting('app.current_tenant_id')::bigint);
-- Явные GRANT'ы для 4 ролей (mirror webhook_dedup_keys): на prod таблица
-- создаётся crm_supplier_worker, default privileges не наследуются от
-- postgres-superuser на чужие creator-role. DO block — idempotent + dev-safe
-- (на dev ролей нет → silent skip).
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_app_user') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_app_user;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_admin_user') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_admin_user;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_supplier_worker') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON supplier_lead_deliveries TO crm_supplier_worker;
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'crm_migrator') THEN
GRANT ALL PRIVILEGES ON supplier_lead_deliveries TO crm_migrator;
END IF;
END $$;
-- =============================================================================
-- 7. БИЛЛИНГ (SAAS-уровень)