Files
mc_cars_gmbh_infraestructure/supabase/migrations/01-init.sql
T

177 lines
7.7 KiB
PL/PgSQL

-- =============================================================================
-- MC Cars — Postgres bootstrap.
-- Creates the Supabase service roles that GoTrue / PostgREST / Storage expect,
-- installs required extensions, and sets up app schema + RLS + storage policies
-- + admin seed + sample fleet.
-- Runs once on first `docker compose up`.
-- =============================================================================
-- The Postgres connection is authenticated as role `supabase_admin` (POSTGRES_USER),
-- so we need it to be a superuser for the rest of this script to work. That is
-- handled by POSTGRES_USER=supabase_admin in compose.
create extension if not exists pgcrypto;
create extension if not exists "uuid-ossp";
-- Make the password from the shell wrapper available to the DO block below.
select set_config('mccars.pg_password', :'pg_password', false);
-- -----------------------------------------------------------------------------
-- Supabase service roles
-- -----------------------------------------------------------------------------
do $roles$
declare
pw text := current_setting('mccars.pg_password', true);
begin
if pw is null or pw = '' then
raise exception 'mccars.pg_password is not set';
end if;
-- anon (used by PostgREST for unauthenticated requests)
if not exists (select 1 from pg_roles where rolname = 'anon') then
execute 'create role anon nologin noinherit';
end if;
-- authenticated (role the JWT "authenticated" maps to)
if not exists (select 1 from pg_roles where rolname = 'authenticated') then
execute 'create role authenticated nologin noinherit';
end if;
-- service_role (full access; never exposed to the browser)
if not exists (select 1 from pg_roles where rolname = 'service_role') then
execute 'create role service_role nologin noinherit bypassrls';
end if;
-- authenticator (PostgREST logs in as this and switches role per JWT)
if not exists (select 1 from pg_roles where rolname = 'authenticator') then
execute format('create role authenticator login noinherit password %L', pw);
else
execute format('alter role authenticator with login noinherit password %L', pw);
end if;
-- supabase_auth_admin (GoTrue logs in with this, needs schema auth)
if not exists (select 1 from pg_roles where rolname = 'supabase_auth_admin') then
execute format('create role supabase_auth_admin login createrole password %L', pw);
else
execute format('alter role supabase_auth_admin with login createrole password %L', pw);
end if;
-- supabase_storage_admin (Storage service logs in with this)
if not exists (select 1 from pg_roles where rolname = 'supabase_storage_admin') then
execute format('create role supabase_storage_admin login createrole password %L', pw);
else
execute format('alter role supabase_storage_admin with login createrole password %L', pw);
end if;
-- Let authenticator impersonate app roles.
execute 'grant anon, authenticated, service_role to authenticator';
-- Let storage_admin impersonate app roles (needed for RLS on storage.objects).
execute 'grant anon, authenticated, service_role to supabase_storage_admin';
end
$roles$;
-- -----------------------------------------------------------------------------
-- Schemas for GoTrue / Storage (they create their own objects, but own schema)
-- -----------------------------------------------------------------------------
create schema if not exists auth authorization supabase_auth_admin;
create schema if not exists storage authorization supabase_storage_admin;
create schema if not exists _realtime authorization postgres;
grant usage on schema auth to service_role, authenticated, anon;
grant usage on schema storage to service_role, authenticated, anon;
grant all on schema _realtime to postgres;
-- Allow service admins to create/alter objects for their own migrations.
grant create, connect on database postgres to supabase_auth_admin, supabase_storage_admin;
grant all on schema auth to supabase_auth_admin;
grant all on schema storage to supabase_storage_admin;
-- Storage-api's migration process expects to see public schema too.
grant usage, create on schema public to supabase_storage_admin, supabase_auth_admin;
-- -----------------------------------------------------------------------------
-- Application schema: public.vehicles
-- -----------------------------------------------------------------------------
create table if not exists public.vehicles (
id uuid primary key default gen_random_uuid(),
brand text not null,
model text not null,
power_hp integer not null default 0,
top_speed_kmh integer not null default 0,
acceleration text not null default '',
seats integer not null default 2,
daily_price_eur integer not null default 0,
location text not null default 'Steiermark (TBD)',
description_de text not null default '',
description_en text not null default '',
photo_url text not null default '',
photo_path text,
sort_order integer not null default 100,
is_active boolean not null default true,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create index if not exists vehicles_active_sort_idx
on public.vehicles (is_active, sort_order);
create or replace function public.tg_touch_updated_at() returns trigger
language plpgsql as $$
begin
new.updated_at := now();
return new;
end;
$$;
drop trigger if exists vehicles_touch on public.vehicles;
create trigger vehicles_touch
before update on public.vehicles
for each row execute function public.tg_touch_updated_at();
alter table public.vehicles enable row level security;
drop policy if exists "vehicles_public_read" on public.vehicles;
drop policy if exists "vehicles_admin_read_all" on public.vehicles;
drop policy if exists "vehicles_admin_insert" on public.vehicles;
drop policy if exists "vehicles_admin_update" on public.vehicles;
drop policy if exists "vehicles_admin_delete" on public.vehicles;
create policy "vehicles_public_read"
on public.vehicles for select
using (is_active = true);
create policy "vehicles_admin_read_all"
on public.vehicles for select
to authenticated using (true);
create policy "vehicles_admin_insert"
on public.vehicles for insert
to authenticated with check (true);
create policy "vehicles_admin_update"
on public.vehicles for update
to authenticated using (true) with check (true);
create policy "vehicles_admin_delete"
on public.vehicles for delete
to authenticated using (true);
grant select on public.vehicles to anon, authenticated;
grant insert, update, delete on public.vehicles to authenticated;
grant all on public.vehicles to service_role;
-- -----------------------------------------------------------------------------
-- Seed sample fleet (admin can replace any row / photo via the UI)
-- -----------------------------------------------------------------------------
insert into public.vehicles
(brand, model, power_hp, top_speed_kmh, acceleration, seats,
daily_price_eur, location, description_de, description_en, photo_url, sort_order)
values
('Ferrari','296 GTB', 830, 330, '2.9s', 2, 850, 'Steiermark (TBD)',
'V6-Hybrid mit 830 PS, atemberaubendes Design und kompromisslose Performance auf Strasse und Rennstrecke.',
'V6 hybrid with 830 hp, breathtaking design and uncompromising performance on road and track.',
'/images/ferrari-main-car.png',
10)
on conflict do nothing;