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();