db: tenants-db schema — per-brand operational tables

This commit is contained in:
2026-05-06 15:51:02 +00:00
parent 676f395ac9
commit eaf61b4c75
@@ -0,0 +1,135 @@
-- manar-tenants-db: per-brand operational schemas
-- Creates brand_alsalama and brand_asktam with identical table structure
DO $$
DECLARE
schemas TEXT[] := ARRAY['brand_alsalama', 'brand_asktam'];
s TEXT;
BEGIN
FOREACH s IN ARRAY schemas LOOP
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.ad_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
platform_slug TEXT NOT NULL,
external_id TEXT NOT NULL,
name TEXT,
currency TEXT DEFAULT ''SAR'',
status TEXT DEFAULT ''active'',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (platform_slug, external_id)
)', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.ad_credentials (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ad_account_id UUID REFERENCES %I.ad_accounts(id) ON DELETE CASCADE,
platform_slug TEXT NOT NULL,
access_token TEXT,
refresh_token TEXT,
token_expiry TIMESTAMPTZ,
developer_token TEXT,
login_customer_id TEXT,
credential_type TEXT DEFAULT ''oauth'',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s, s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.recommendations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ad_account_id UUID REFERENCES %I.ad_accounts(id),
type TEXT NOT NULL,
priority TEXT DEFAULT ''medium'',
title TEXT NOT NULL,
description TEXT,
payload JSONB,
estimated_impact JSONB,
status TEXT DEFAULT ''pending'',
trust_level INT DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
actioned_at TIMESTAMPTZ,
actioned_by UUID
)', s, s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.approvals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
recommendation_id UUID REFERENCES %I.recommendations(id),
decision TEXT NOT NULL,
notes TEXT,
decided_by UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s, s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.audit_log (
id BIGSERIAL PRIMARY KEY,
user_id UUID,
action TEXT NOT NULL,
entity_type TEXT,
entity_id TEXT,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.brand_context (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key TEXT UNIQUE NOT NULL,
value JSONB NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.kpi_targets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ad_account_id UUID REFERENCES %I.ad_accounts(id),
metric TEXT NOT NULL,
target_value NUMERIC,
period TEXT,
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s, s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.alert_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
metric TEXT NOT NULL,
condition TEXT NOT NULL,
threshold NUMERIC,
window_mins INT DEFAULT 60,
severity TEXT DEFAULT ''warning'',
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.alert_history (
id BIGSERIAL PRIMARY KEY,
rule_id UUID REFERENCES %I.alert_rules(id),
triggered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
value NUMERIC,
resolved_at TIMESTAMPTZ,
payload JSONB
)', s, s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.search_term_decisions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
search_term TEXT NOT NULL,
decision TEXT NOT NULL,
match_type TEXT,
ad_group_id TEXT,
campaign_id TEXT,
decided_by TEXT DEFAULT ''ai'',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)', s);
EXECUTE format('CREATE TABLE IF NOT EXISTS %I.ai_memory (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
scope TEXT NOT NULL,
scope_id TEXT,
key TEXT NOT NULL,
value JSONB NOT NULL,
ttl TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (scope, scope_id, key)
)', s);
RAISE NOTICE ''Schema % initialized'', s;
END LOOP;
END $$;