hackdex-website/supabase/migrations/20251224083033_add_hack_features.sql
Jared Schoeny c220bbf98e Add db migration for more hack features
(like completion status, rejection, and patch groups)
2025-12-23 23:09:43 -10:00

144 lines
4.8 KiB
PL/PgSQL

create type public."Completion Status" as enum ('Complete', 'Demo', 'Alpha', 'Beta');
alter table if exists public.hacks
add column if not exists is_archive boolean not null default false;
alter table if exists public.hacks
add column if not exists rejected boolean not null default false;
alter table if exists public.hacks
add column if not exists rejected_at timestamp with time zone;
alter table if exists public.hacks
add column if not exists rejected_reason text;
alter table if exists public.hacks
add column if not exists rejected_by uuid;
alter table if exists public.hacks
add column if not exists completion_status "Completion Status";
-- Foreign key for rejected_by -> auth.users(id)
alter table "public"."hacks" add constraint "hacks_rejected_by_fkey" FOREIGN KEY (rejected_by) REFERENCES auth.users(id) ON DELETE SET NULL not valid;
alter table "public"."hacks" validate constraint "hacks_rejected_by_fkey";
CREATE OR REPLACE FUNCTION public.is_archive_hack_for_archiver(hack_slug text)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
AS $$
SELECT EXISTS (
SELECT 1
FROM public.hacks h
WHERE h.slug = hack_slug
AND h.is_archive = true
);
$$;
create table if not exists public.patch_groups (
"id" bigint generated by default as identity not null,
"created_at" timestamp with time zone not null default now(),
"updated_at" timestamp with time zone not null default now(),
"name" text not null,
"hack_slug" text not null,
"patch_id" bigint not null,
"order" integer not null
);
alter table public.patch_groups owner to "postgres";
-- Primary key
CREATE UNIQUE INDEX patch_groups_pkey ON public.patch_groups USING btree (id);
alter table "public"."patch_groups" add constraint "patch_groups_pkey" PRIMARY KEY using index "patch_groups_pkey";
-- Foreign keys
alter table "public"."patch_groups" add constraint "patch_groups_patch_id_fkey" FOREIGN KEY (patch_id) REFERENCES patches(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
alter table "public"."patch_groups" validate constraint "patch_groups_patch_id_fkey";
alter table "public"."patch_groups" add constraint "patch_groups_hack_slug_fkey" FOREIGN KEY (hack_slug) REFERENCES hacks(slug) ON UPDATE CASCADE ON DELETE CASCADE not valid;
alter table "public"."patch_groups" validate constraint "patch_groups_hack_slug_fkey";
-- Unique constraints
-- A patch can only appear once per group
CREATE UNIQUE INDEX patch_groups_hack_slug_name_patch_id_key ON public.patch_groups USING btree (hack_slug, name, patch_id);
alter table "public"."patch_groups" add constraint "patch_groups_hack_slug_name_patch_id_key" UNIQUE using index "patch_groups_hack_slug_name_patch_id_key";
-- Performance indexes
CREATE INDEX patch_groups_hack_slug_idx ON public.patch_groups USING btree (hack_slug);
CREATE INDEX patch_groups_patch_id_idx ON public.patch_groups USING btree (patch_id);
CREATE INDEX patch_groups_name_idx ON public.patch_groups USING btree (name);
-- Enable RLS
alter table "public"."patch_groups" enable row level security;
-- RLS Policies
-- Public read access (assuming patches are public)
create policy "Patch groups are viewable by everyone"
on "public"."patch_groups"
for select
using (true);
-- Allow authenticated users to insert patch groups for their own hacks
create policy "Users can insert patch groups for own hacks"
on "public"."patch_groups"
for insert
with check (
public.is_admin() OR
(public.is_archiver() AND public.is_archive_hack_for_archiver(patch_groups.hack_slug)) OR
EXISTS (
SELECT 1
FROM public.hacks h
WHERE h.slug = patch_groups.hack_slug
AND h.created_by = auth.uid()
)
);
-- Allow authenticated users to update patch groups for their own hacks
create policy "Users can update patch groups for own hacks"
on "public"."patch_groups"
for update
using (
public.is_admin() OR
(public.is_archiver() AND public.is_archive_hack_for_archiver(patch_groups.hack_slug)) OR
EXISTS (
SELECT 1
FROM public.hacks h
WHERE h.slug = patch_groups.hack_slug
AND h.created_by = auth.uid()
)
)
with check (
public.is_admin() OR
(public.is_archiver() AND public.is_archive_hack_for_archiver(patch_groups.hack_slug)) OR
EXISTS (
SELECT 1
FROM public.hacks h
WHERE h.slug = patch_groups.hack_slug
AND h.created_by = auth.uid()
)
);
-- Allow authenticated users to delete patch groups for their own hacks
create policy "Users can delete patch groups for own hacks"
on "public"."patch_groups"
for delete
using (
public.is_admin() OR
(public.is_archiver() AND public.is_archive_hack_for_archiver(patch_groups.hack_slug)) OR
EXISTS (
SELECT 1
FROM public.hacks h
WHERE h.slug = patch_groups.hack_slug
AND h.created_by = auth.uid()
)
);
-- Trigger for updated_at
CREATE OR REPLACE TRIGGER set_patch_groups_updated_at
BEFORE UPDATE ON public.patch_groups
FOR EACH ROW
EXECUTE FUNCTION public.set_updated_at();