feat: Add Supabase configuration and migrations for MC Cars application
- Create Kong declarative configuration for routing and authentication. - Implement initialization script to set up the database. - Add SQL migration for initializing roles, schemas, and seeding vehicle data. - Create leads and customers tables with appropriate policies and functions for CRM. - Seed admin user and configure storage bucket with RLS policies.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
-- =============================================================================
|
||||
-- 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';
|
||||
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
|
||||
('Porsche','911 GT3', 510, 318, '3.4s', 2, 890, 'Steiermark (TBD)',
|
||||
'Puristischer Hochdrehzahl-Saugmotor und kompromissloser Motorsport-Charakter.',
|
||||
'Pure high-revving naturally aspirated engine with uncompromising motorsport character.',
|
||||
'https://images.unsplash.com/photo-1611821064430-0d40291d0f0b?auto=format&fit=crop&w=1400&q=80',
|
||||
10),
|
||||
('Lamborghini','Huracan EVO', 640, 325, '2.9s', 2, 990, 'Steiermark (TBD)',
|
||||
'V10 mit 640 PS, scharfes Design und kompromisslose Performance auf Strasse und Rennstrecke.',
|
||||
'V10 with 640 hp, sharp design and uncompromising performance on road and track.',
|
||||
'https://images.unsplash.com/photo-1544636331-e26879cd4d9b?auto=format&fit=crop&w=1400&q=80',
|
||||
20),
|
||||
('Audi','RS6 Performance', 630, 305, '3.4s', 5, 540, 'Steiermark (TBD)',
|
||||
'Alltagstauglicher Kombi mit brutaler V8-Biturbo-Power und Allradantrieb.',
|
||||
'Everyday-ready estate with brutal twin-turbo V8 power and quattro AWD.',
|
||||
'https://images.unsplash.com/photo-1606664515524-ed2f786a0bd6?auto=format&fit=crop&w=1400&q=80',
|
||||
30),
|
||||
('BMW','M4 Competition', 530, 290, '3.5s', 4, 430, 'Steiermark (TBD)',
|
||||
'Reihensechszylinder-Biturbo mit praeziser Lenkung und sportlichem Fahrwerk.',
|
||||
'Twin-turbo inline-six with precise steering and sporty chassis.',
|
||||
'https://images.unsplash.com/photo-1555215695-3004980ad54e?auto=format&fit=crop&w=1400&q=80',
|
||||
40),
|
||||
('Nissan','GT-R R35', 570, 315, '2.8s', 4, 510, 'Steiermark (TBD)',
|
||||
'Ikonischer Allrad-Supersportler mit Twin-Turbo V6 und brutalem Antritt.',
|
||||
'Iconic AWD supercar with twin-turbo V6 and ferocious launch.',
|
||||
'https://images.unsplash.com/photo-1626668893632-6f3a4466d22f?auto=format&fit=crop&w=1400&q=80',
|
||||
50),
|
||||
('Mercedes-AMG','G63', 585, 220, '4.5s', 5, 620, 'Steiermark (TBD)',
|
||||
'Legendaere G-Klasse mit V8-Biturbo-Performance und unverkennbarem Design.',
|
||||
'Legendary G-Class with V8 biturbo performance and unmistakable design.',
|
||||
'https://images.unsplash.com/photo-1606611013016-969c19ba27bb?auto=format&fit=crop&w=1400&q=80',
|
||||
60)
|
||||
on conflict do nothing;
|
||||
Reference in New Issue
Block a user