feat: update main migration script and add sync migration for existing deployments
Main migration now canonical for fresh deployments; added sync migration for legacy DBs. Co-authored-by: Moheeb Zara <[email protected]>
This commit is contained in:
+19
-12
@@ -4,29 +4,36 @@
|
|||||||
|
|
||||||
### Fresh deployment
|
### Fresh deployment
|
||||||
|
|
||||||
Run **one file**:
|
Run **one file** in Supabase SQL Editor:
|
||||||
|
|
||||||
```
|
```
|
||||||
scripts/schema.sql
|
scripts/schema.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
Paste it in Supabase → **SQL Editor → New query** and hit Run.
|
This creates the entire schema from scratch — all tables, enums, indexes, RLS policies, functions, and triggers. Idempotent, can be run multiple times safely.
|
||||||
That is the complete, idempotent schema — all tables, enums, indexes, RLS policies, functions, and triggers.
|
|
||||||
|
|
||||||
### Existing deployment
|
### Existing deployment migration
|
||||||
|
|
||||||
The numbered scripts (`001_*` → `011_*`) are the **migration history** of the live database.
|
If you have a legacy Hackerspace database and want to sync it to the canonical schema:
|
||||||
Do not re-run them. They are kept for reference only.
|
|
||||||
|
```
|
||||||
|
scripts/012_canonical_sync.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
This migration adds all missing columns, renames `full_name` → `display_name`, creates missing constraints, fixes the auth trigger, and updates all other tables to match the canonical schema. Also idempotent — safe to run multiple times.
|
||||||
|
|
||||||
|
### Migration history (reference only)
|
||||||
|
|
||||||
|
Scripts `001_*` through `011_*` represent the evolution of the live database. **Do not re-run them.** They exist for historical reference.
|
||||||
|
|
||||||
### What `schema.sql` contains
|
### What `schema.sql` contains
|
||||||
|
|
||||||
| Section | Contents |
|
| Section | Contents |
|
||||||
|---------|----------|
|
|---------|----------|
|
||||||
| Extensions | `uuid-ossp` |
|
| Extensions | `uuid-ossp` |
|
||||||
| Enums | 14 custom types (`member_role`, `task_status`, etc.) |
|
| Enums | 14 custom types (`member_role`, `task_status`, `payment_platform`, etc.) |
|
||||||
| Helper functions | `get_user_space_ids`, `user_has_role_in_space` |
|
| Helper functions | `get_user_space_ids`, `user_has_role_in_space` |
|
||||||
| Tables | `spaces`, `space_members`, `tasks`, `projects`, `payments`, `contacts`, `knowledge_base`, `secrets`, `area_leads`, `integrations`, `comms_channels`, `comms_messages`, `activity_log` |
|
| Tables | 13 tables: `spaces`, `space_members`, `tasks`, `projects`, `payments`, `contacts`, `knowledge_base`, `secrets`, `area_leads`, `integrations`, `comms_channels`, `comms_messages`, `activity_log` |
|
||||||
| Indexes | 22 performance indexes |
|
| Indexes | 22 performance indexes on foreign keys and common filters |
|
||||||
| RLS policies | Full row-level security on every table |
|
| RLS policies | Full row-level security on all tables (access gated by space membership) |
|
||||||
| Triggers | `on_space_created` (auto-creates 4 default channels) |
|
| Triggers | `on_space_created` (auto-creates 4 default channels), `on_auth_user_created` (auth signup hook) |
|
||||||
| Auth hook | `on_auth_user_created` (handles create-space and join-space signup flows) |
|
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
-- Migration 012: Sync existing databases to canonical schema
|
||||||
|
-- Run this on any existing Hackerspace deployment to align with the canonical schema.sql
|
||||||
|
-- Safe to run multiple times (idempotent).
|
||||||
|
--
|
||||||
|
-- Changes:
|
||||||
|
-- • Rename space_members.full_name → display_name (to match app code everywhere)
|
||||||
|
-- • Drop redundant tasks.type text column (canonical column is task_type enum)
|
||||||
|
-- • Add missing columns to all tables that the app code expects
|
||||||
|
-- • Create missing unique constraints for upserts
|
||||||
|
-- • Update handle_space_signup trigger to match canonical version
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- RENAME COLUMNS
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.space_members RENAME COLUMN full_name TO display_name;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS handle text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS bio text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS avatar_url text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS dues_paid_until timestamptz;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS stripe_customer_id text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS has_card_access boolean NOT NULL DEFAULT false;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS payment_status text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS payment_note text;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS last_payment_at timestamptz;
|
||||||
|
ALTER TABLE public.space_members ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now();
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- TASKS TABLE FIXES
|
||||||
|
-- ============================================================
|
||||||
|
-- Drop the redundant text 'type' column if it exists (canonical column is task_type enum)
|
||||||
|
ALTER TABLE public.tasks DROP COLUMN IF EXISTS type CASCADE;
|
||||||
|
|
||||||
|
-- Ensure all canonical task columns exist
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS priority text NOT NULL DEFAULT 'medium';
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS requested_by uuid REFERENCES auth.users(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS requested_by_name text;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS claimed_by_name text;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS assigned_to_name text;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS completed_at timestamptz;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS last_done_at timestamptz;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS subtask_total integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS subtask_completed integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS progress integer NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- SPACES TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS city text;
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS address text;
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS timezone text DEFAULT 'America/Phoenix';
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS invite_code text;
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS require_approval boolean NOT NULL DEFAULT true;
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS public_member_directory boolean NOT NULL DEFAULT false;
|
||||||
|
ALTER TABLE public.spaces ADD COLUMN IF NOT EXISTS webhook_secret text;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- PAYMENTS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.payments ADD COLUMN IF NOT EXISTS from_identifier text;
|
||||||
|
ALTER TABLE public.payments ADD COLUMN IF NOT EXISTS from_note text;
|
||||||
|
ALTER TABLE public.payments ADD COLUMN IF NOT EXISTS member_name text;
|
||||||
|
ALTER TABLE public.payments ADD COLUMN IF NOT EXISTS link_status public.payment_link_status NOT NULL DEFAULT 'unlinked';
|
||||||
|
ALTER TABLE public.payments ADD COLUMN IF NOT EXISTS transaction_date timestamptz DEFAULT now();
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- CONTACTS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.contacts ADD COLUMN IF NOT EXISTS code text;
|
||||||
|
ALTER TABLE public.contacts ADD COLUMN IF NOT EXISTS details text;
|
||||||
|
ALTER TABLE public.contacts ADD COLUMN IF NOT EXISTS note text;
|
||||||
|
ALTER TABLE public.contacts ADD COLUMN IF NOT EXISTS group_label text;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- KNOWLEDGE BASE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS icon text;
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS access_level text DEFAULT 'all_members';
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS area text;
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT false;
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS updated_by_id uuid REFERENCES public.space_members(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS updated_by_name text;
|
||||||
|
ALTER TABLE public.knowledge_base ADD COLUMN IF NOT EXISTS updated_by text;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- SECRETS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.secrets ADD COLUMN IF NOT EXISTS icon text;
|
||||||
|
ALTER TABLE public.secrets ADD COLUMN IF NOT EXISTS description text;
|
||||||
|
ALTER TABLE public.secrets ADD COLUMN IF NOT EXISTS area text;
|
||||||
|
ALTER TABLE public.secrets ADD COLUMN IF NOT EXISTS title text;
|
||||||
|
-- Backfill title from label where label exists and title is null
|
||||||
|
UPDATE public.secrets SET title = label WHERE title IS NULL AND label IS NOT NULL;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- AREA LEADS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.area_leads ADD COLUMN IF NOT EXISTS area_code text;
|
||||||
|
ALTER TABLE public.area_leads ADD COLUMN IF NOT EXISTS lead_handle text;
|
||||||
|
|
||||||
|
-- Add unique constraint on (space_id, area_code) if not already present
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM pg_constraint
|
||||||
|
WHERE conname = 'area_leads_space_area_code_key'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.area_leads ADD CONSTRAINT area_leads_space_area_code_key
|
||||||
|
UNIQUE (space_id, area_code) DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- INTEGRATIONS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.integrations ADD COLUMN IF NOT EXISTS name text;
|
||||||
|
ALTER TABLE public.integrations ADD COLUMN IF NOT EXISTS description text;
|
||||||
|
ALTER TABLE public.integrations ADD COLUMN IF NOT EXISTS config jsonb;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- COMMS CHANNELS
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.comms_channels ADD COLUMN IF NOT EXISTS icon text;
|
||||||
|
ALTER TABLE public.comms_channels ADD COLUMN IF NOT EXISTS area_reference text;
|
||||||
|
ALTER TABLE public.comms_channels ADD COLUMN IF NOT EXISTS member_count integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE public.comms_channels ADD COLUMN IF NOT EXISTS unread_count integer NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- COMMS MESSAGES
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.comms_messages ADD COLUMN IF NOT EXISTS display_name text;
|
||||||
|
ALTER TABLE public.comms_messages ADD COLUMN IF NOT EXISTS handle text;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- ACTIVITY LOG
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.activity_log ADD COLUMN IF NOT EXISTS display_name text;
|
||||||
|
ALTER TABLE public.activity_log ADD COLUMN IF NOT EXISTS details text;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- PROJECTS TABLE
|
||||||
|
-- ============================================================
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS name text;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS area text;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS category text;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS assignees text;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS assignee_names text[];
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS task_count integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS tasks_completed integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE public.projects ADD COLUMN IF NOT EXISTS progress integer NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- UNIQUE INDEXES / CONSTRAINTS
|
||||||
|
-- ============================================================
|
||||||
|
-- space_members: unique index on (space_id, email) for upsert in importMembers
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_space_members_space_email
|
||||||
|
ON public.space_members (space_id, email)
|
||||||
|
WHERE email IS NOT NULL;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- UPDATE TRIGGERS
|
||||||
|
-- ============================================================
|
||||||
|
-- Fix handle_space_signup to match canonical version
|
||||||
|
CREATE OR REPLACE FUNCTION public.handle_space_signup()
|
||||||
|
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER
|
||||||
|
SET search_path = public
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
v_space_id uuid;
|
||||||
|
v_role public.member_role;
|
||||||
|
v_display_name text;
|
||||||
|
BEGIN
|
||||||
|
v_space_id := (NEW.raw_user_meta_data->>'space_id')::uuid;
|
||||||
|
v_role := COALESCE(
|
||||||
|
NULLIF((NEW.raw_user_meta_data->>'role'), '')::public.member_role,
|
||||||
|
'member'
|
||||||
|
);
|
||||||
|
v_display_name := COALESCE(
|
||||||
|
NULLIF(NEW.raw_user_meta_data->>'display_name', ''),
|
||||||
|
NULLIF(NEW.raw_user_meta_data->>'full_name', ''),
|
||||||
|
NEW.email
|
||||||
|
);
|
||||||
|
|
||||||
|
IF v_space_id IS NULL THEN
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
INSERT INTO public.space_members (space_id, user_id, role, status, display_name, email, approved)
|
||||||
|
VALUES (v_space_id, NEW.id, v_role, 'unverified', v_display_name, NEW.email, true)
|
||||||
|
ON CONFLICT (space_id, user_id) DO NOTHING;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
Reference in New Issue
Block a user