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