feat(schema): create_studio_for_user RPC (service_role) für HOST-Provisioning
RAPPORT-HOST provisioniert serverseitig (kein auth.uid()), daher braucht es eine service_role-Variante von create_studio_with_admin mit expliziter User-ID. Legt zusätzlich das Profil an (profiles.username/display_name sind NOT NULL, fürs erste Instanz-Login nötig). NUR an service_role gegrantet — nie an authenticated, sonst könnte jeder User sich zum Admin machen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- RAPPORT — Studio für einen bestimmten User anlegen (Server-/Hosting-Pfad)
|
||||||
|
-- ============================================================================
|
||||||
|
-- `create_studio_with_admin` nutzt auth.uid() und läuft nur im Kontext eines
|
||||||
|
-- eingeloggten Users (Frontend). RAPPORT-HOST provisioniert aber serverseitig
|
||||||
|
-- mit service_role und kennt keinen auth.uid() — es übergibt die Ziel-User-ID
|
||||||
|
-- explizit.
|
||||||
|
--
|
||||||
|
-- `create_studio_for_user` ist die service_role-Variante: identische Wirkung
|
||||||
|
-- (Studio + Admin-Membership + Settings), aber die User-ID ist ein Parameter.
|
||||||
|
-- Bewusst NICHT an `authenticated` gegrantet — nur service_role darf das, sonst
|
||||||
|
-- könnte ein User sich selbst zum Admin beliebiger Studios machen.
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
create or replace function create_studio_for_user(
|
||||||
|
p_user_id uuid,
|
||||||
|
p_name text,
|
||||||
|
p_slug text,
|
||||||
|
p_username text default null,
|
||||||
|
p_display_name text default null
|
||||||
|
)
|
||||||
|
returns uuid
|
||||||
|
language plpgsql
|
||||||
|
security definer
|
||||||
|
as $$
|
||||||
|
declare
|
||||||
|
v_studio_id uuid;
|
||||||
|
v_email text;
|
||||||
|
v_username text;
|
||||||
|
v_display text;
|
||||||
|
begin
|
||||||
|
if p_user_id is null then
|
||||||
|
raise exception 'p_user_id required';
|
||||||
|
end if;
|
||||||
|
select email into v_email from auth.users where id = p_user_id;
|
||||||
|
if v_email is null then
|
||||||
|
raise exception 'user % does not exist', p_user_id;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- Profil sicherstellen (profiles.username/display_name sind NOT NULL; das
|
||||||
|
-- Frontend braucht sie beim ersten Login in die Instanz). Aus E-Mail
|
||||||
|
-- abgeleitet, falls nicht explizit übergeben.
|
||||||
|
v_username := coalesce(nullif(p_username, ''), split_part(v_email, '@', 1));
|
||||||
|
v_display := coalesce(nullif(p_display_name, ''), v_username);
|
||||||
|
insert into profiles (id, username, display_name)
|
||||||
|
values (p_user_id, v_username, v_display)
|
||||||
|
on conflict (id) do nothing;
|
||||||
|
|
||||||
|
insert into studios (name, slug) values (p_name, p_slug) returning id into v_studio_id;
|
||||||
|
insert into studio_members (studio_id, user_id, app_role_id)
|
||||||
|
values (v_studio_id, p_user_id, 'r-admin');
|
||||||
|
|
||||||
|
-- Studio-Name + setup_completed in die settings übernehmen (Seed-Trigger hat
|
||||||
|
-- die Zeile mit Defaults bereits angelegt) — analog create_studio_with_admin.
|
||||||
|
update studio_settings
|
||||||
|
set name = p_name, setup_completed = true
|
||||||
|
where studio_id = v_studio_id;
|
||||||
|
|
||||||
|
return v_studio_id;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- Nur service_role (RAPPORT-HOST). KEIN Grant an anon/authenticated.
|
||||||
|
revoke all on function create_studio_for_user(uuid, text, text, text, text) from public;
|
||||||
|
grant execute on function create_studio_for_user(uuid, text, text, text, text) to service_role;
|
||||||
Reference in New Issue
Block a user