Files
wursor/supabase/migrations/013_design_language_files.sql
T
2026-05-06 18:51:17 +01:00

97 lines
4.4 KiB
PL/PgSQL

-- ═══════════════════════════════════════════════════════════════════════════
-- Migration 013: Phase 6 — Design Language Files table
--
-- Creates the `design_language_files` table used by the DLF token system.
-- Each row is one uploaded version of a workspace's token/constraint file.
-- Only one row per workspace is "active" at a time (enforced by partial index).
--
-- Spec reference: SOURCE-AWARE-CANVAS Phase 6 §9.2
-- All changes use IF NOT EXISTS / DO $$ guards for idempotency.
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS design_language_files (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
name text NOT NULL,
-- Full parsed token + constraint document stored as JSONB.
-- Schema validated in-app before insert via DesignLanguageFileBodySchema.
schema_jsonb jsonb NOT NULL,
version integer NOT NULL DEFAULT 1,
-- Only one row per workspace should be active. The partial unique index below
-- enforces this at the DB level. Use the deactivation step in the POST route
-- to set this to false on all prior rows before inserting a new active one.
is_active boolean NOT NULL DEFAULT false,
created_by text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- ── Indexes ───────────────────────────────────────────────────────────────────
-- Fast lookup of the latest active file for a workspace.
CREATE INDEX IF NOT EXISTS design_language_files_workspace_version_idx
ON design_language_files (workspace_id, version DESC);
-- Enforce at most one active row per workspace.
-- Partial unique index: only rows where is_active = true are checked.
CREATE UNIQUE INDEX IF NOT EXISTS design_language_files_one_active_per_workspace_idx
ON design_language_files (workspace_id)
WHERE (is_active = true);
-- ── Auto-update updated_at ────────────────────────────────────────────────────
CREATE OR REPLACE FUNCTION update_design_language_files_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS trg_design_language_files_updated_at ON design_language_files;
CREATE TRIGGER trg_design_language_files_updated_at
BEFORE UPDATE ON design_language_files
FOR EACH ROW EXECUTE FUNCTION update_design_language_files_updated_at();
-- ── Row Level Security ────────────────────────────────────────────────────────
-- Service-role key (used by API routes) bypasses RLS entirely.
-- Browser clients (anon or authenticated) are restricted to their workspace.
ALTER TABLE design_language_files ENABLE ROW LEVEL SECURITY;
-- Workspace members may read design language files for their workspace.
-- Relies on the `team_members` table introduced in migration 001.
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies
WHERE tablename = 'design_language_files' AND policyname = 'dlf_workspace_select'
) THEN
CREATE POLICY dlf_workspace_select ON design_language_files
FOR SELECT
USING (
workspace_id IN (
SELECT workspace_id FROM team_members
WHERE user_id = auth.uid()::text
)
);
END IF;
END $$;
-- Only workspace owners / designers may insert / update.
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies
WHERE tablename = 'design_language_files' AND policyname = 'dlf_workspace_write'
) THEN
CREATE POLICY dlf_workspace_write ON design_language_files
FOR ALL
USING (
workspace_id IN (
SELECT workspace_id FROM team_members
WHERE user_id = auth.uid()::text
AND role IN ('OWNER', 'DESIGNER')
)
);
END IF;
END $$;