Files
braidsbymira/supabase/migrations/20260808000001_initial_schema.sql
belviskhoremk 8c1522a2b4 chore: deploy Phase 2A backend to live Supabase project (Task 9)
Substitutes the real admin UID everywhere the placeholder appeared,
points the Edge Functions base URL at the deployed project, and adds
production JWT verification: trigger functions now send the anon key
as a bearer token so verify_jwt can stay enabled instead of leaving
the functions open to unauthenticated calls from the public internet.

Also adds scripts/create-admin.ts (one-off helper used to create
Mira's admin auth user), documents the seed script's non-VITE env
vars in .env.example, and closes a gitignore gap where plain .env
wasn't excluded alongside .env.local.

Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
2026-08-12 09:59:33 +02:00

129 lines
5.1 KiB
SQL

-- Products, services, availability schedule, bookings, inquiries, and the
-- public booked_slots mirror. See docs/superpowers/specs/2026-08-08-phase2-backend-foundation-supabase-design.md.
create table public.products (
id text primary key,
name text not null,
category text not null check (category in ('hair-care', 'extensions', 'tools', 'accessories')),
price numeric not null,
image text not null,
description text not null,
in_stock boolean not null default true
);
create table public.services (
id text primary key,
name text not null,
duration_minutes integer not null,
price_from numeric not null,
description text not null,
image text not null
);
create table public.schedule_template (
id text primary key default 'default',
working_days integer[] not null,
start_time time not null,
end_time time not null,
slot_minutes integer not null
);
create table public.schedule_exceptions (
date date primary key,
closed boolean not null,
note text
);
create table public.bookings (
id uuid primary key default gen_random_uuid(),
service_id text not null references public.services(id),
date date not null,
time text not null,
contact_name text not null,
contact_phone text not null,
contact_email text not null,
contact_notes text not null default '',
status text not null default 'confirmed' check (status in ('confirmed', 'cancelled')),
created_at timestamptz not null default now()
);
create index bookings_service_id_idx on public.bookings (service_id);
create table public.booked_slots (
date date not null,
time text not null,
primary key (date, time)
);
create table public.inquiries (
id uuid primary key default gen_random_uuid(),
product_id text not null references public.products(id),
contact_name text not null,
contact_email text not null,
contact_notes text not null default '',
status text not null default 'new' check (status in ('new', 'read')),
created_at timestamptz not null default now()
);
create index inquiries_product_id_idx on public.inquiries (product_id);
-- Row Level Security
alter table public.products enable row level security;
alter table public.services enable row level security;
alter table public.schedule_template enable row level security;
alter table public.schedule_exceptions enable row level security;
alter table public.bookings enable row level security;
alter table public.booked_slots enable row level security;
alter table public.inquiries enable row level security;
-- products: public read, admin write
create policy "products_public_read" on public.products
for select using (true);
create policy "products_admin_write" on public.products
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- services: public read, admin write
create policy "services_public_read" on public.services
for select using (true);
create policy "services_admin_write" on public.services
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- schedule_template: public read, admin write
create policy "schedule_template_public_read" on public.schedule_template
for select using (true);
create policy "schedule_template_admin_write" on public.schedule_template
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- schedule_exceptions: public read, admin write
create policy "schedule_exceptions_public_read" on public.schedule_exceptions
for select using (true);
create policy "schedule_exceptions_admin_write" on public.schedule_exceptions
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- bookings: anyone can insert (submit a booking); only admin can read/update/delete
create policy "bookings_public_insert" on public.bookings
for insert with check (true);
create policy "bookings_admin_all" on public.bookings
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- booked_slots: public read; only admin policy exists for writes (in practice only
-- the security-definer trigger in the next migration ever writes here)
create policy "booked_slots_public_read" on public.booked_slots
for select using (true);
create policy "booked_slots_admin_write" on public.booked_slots
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);
-- inquiries: anyone can insert; only admin can read/update/delete
create policy "inquiries_public_insert" on public.inquiries
for insert with check (true);
create policy "inquiries_admin_all" on public.inquiries
for all using ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid)
with check ((select auth.uid()) = '21c90224-e0b2-412e-a431-8e7f097c1cef'::uuid);