db: core-db schema — brands, users, memberships, audit
This commit is contained in:
@@ -0,0 +1,105 @@
|
|||||||
|
-- manar-core-db: Registry layer
|
||||||
|
-- Shared across all brands. No tenant data here.
|
||||||
|
|
||||||
|
-- Extensions
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
|
||||||
|
-- ── Brands ───────────────────────────────────────────────
|
||||||
|
CREATE TABLE brands (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
slug TEXT UNIQUE NOT NULL, -- e.g. 'alsalama', 'asktam'
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
logo_url TEXT,
|
||||||
|
timezone TEXT NOT NULL DEFAULT 'Asia/Riyadh',
|
||||||
|
trust_level SMALLINT NOT NULL DEFAULT 0 CHECK (trust_level BETWEEN 0 AND 3),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Users ────────────────────────────────────────────────
|
||||||
|
CREATE TABLE users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
email TEXT UNIQUE NOT NULL,
|
||||||
|
name TEXT,
|
||||||
|
hashed_password TEXT,
|
||||||
|
is_super_admin BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
last_login_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Brand Memberships ────────────────────────────────────
|
||||||
|
CREATE TABLE brand_memberships (
|
||||||
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
brand_id UUID NOT NULL REFERENCES brands(id) ON DELETE CASCADE,
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('viewer', 'analyst', 'manager', 'admin')),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
UNIQUE(brand_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Platforms ────────────────────────────────────────────
|
||||||
|
CREATE TABLE platforms (
|
||||||
|
id SMALLSERIAL PRIMARY KEY,
|
||||||
|
slug TEXT UNIQUE NOT NULL, -- 'google_ads', 'meta', 'tiktok'
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
is_live BOOLEAN NOT NULL DEFAULT false
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO platforms (slug, name, is_live) VALUES
|
||||||
|
('google_ads', 'Google Ads', true),
|
||||||
|
('meta', 'Meta Ads', false),
|
||||||
|
('tiktok', 'TikTok Ads', false),
|
||||||
|
('snapchat', 'Snapchat Ads', false),
|
||||||
|
('twitter', 'X Ads', false),
|
||||||
|
('linkedin', 'LinkedIn Ads', false);
|
||||||
|
|
||||||
|
-- ── Audit Log (global) ───────────────────────────────────
|
||||||
|
CREATE TABLE audit_log_global (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id UUID REFERENCES users(id),
|
||||||
|
brand_id UUID REFERENCES brands(id),
|
||||||
|
action TEXT NOT NULL,
|
||||||
|
entity_type TEXT,
|
||||||
|
entity_id TEXT,
|
||||||
|
payload JSONB,
|
||||||
|
ip_address INET,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_audit_global_brand ON audit_log_global(brand_id);
|
||||||
|
CREATE INDEX idx_audit_global_created ON audit_log_global(created_at DESC);
|
||||||
|
|
||||||
|
-- ── Super Admin Actions ──────────────────────────────────
|
||||||
|
CREATE TABLE super_admin_actions (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id),
|
||||||
|
action TEXT NOT NULL,
|
||||||
|
target_type TEXT,
|
||||||
|
target_id TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
payload JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Seed: Brands ─────────────────────────────────────────
|
||||||
|
INSERT INTO brands (slug, name, timezone, trust_level) VALUES
|
||||||
|
('alsalama', 'Al Salama Medical', 'Asia/Riyadh', 0),
|
||||||
|
('asktam', 'Ask Tam', 'Asia/Riyadh', 0);
|
||||||
|
|
||||||
|
-- ── Seed: Super Admin ────────────────────────────────────
|
||||||
|
INSERT INTO users (email, name, is_super_admin) VALUES
|
||||||
|
('tam@asktam.net', 'Tam', true);
|
||||||
|
|
||||||
|
-- Updated_at trigger
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER trg_brands_updated BEFORE UPDATE ON brands
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||||
|
CREATE TRIGGER trg_users_updated BEFORE UPDATE ON users
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||||
Reference in New Issue
Block a user