From 38ce58dc2f0128e06075565135282c84876c5c66 Mon Sep 17 00:00:00 2001 From: karim Date: Sat, 30 May 2026 23:16:07 +0200 Subject: [PATCH] feat(provisioning): echtes Modell-A-Provisioning via create_studio_for_user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit studio-adapter ruft jetzt den neuen service_role-RPC (APP 0011): Auth-User anlegen (oder bei 422 bestehenden holen) → create_studio_for_user → Instanz-URL. MOCK-Modus bleibt für lokalen Test ohne Rapport-Stack. Co-Authored-By: Claude Opus 4.8 --- server/provisioning/studio-adapter.js | 31 ++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/server/provisioning/studio-adapter.js b/server/provisioning/studio-adapter.js index de90528..9c14837 100644 --- a/server/provisioning/studio-adapter.js +++ b/server/provisioning/studio-adapter.js @@ -41,31 +41,38 @@ export async function provision({ account, plan }) { "Content-Type": "application/json", }; - // 1. Auth-User anlegen (GoTrue Admin-API), bereits bestätigt. + // 1. Auth-User anlegen (GoTrue Admin-API), bereits bestätigt. Existiert die + // E-Mail schon (422), holen wir den bestehenden User per Listen-Filter. const tempPassword = randomUUID(); const userRes = await fetch(`${base}/auth/v1/admin/users`, { method: "POST", headers, body: JSON.stringify({ email: account.email, password: tempPassword, email_confirm: true }), }); - if (!userRes.ok) throw new Error(`GoTrue admin/users: ${userRes.status} ${await userRes.text()}`); - const user = await userRes.json(); + let user; + if (userRes.ok) { + user = await userRes.json(); + } else if (userRes.status === 422) { + const list = await fetch(`${base}/auth/v1/admin/users?filter=${encodeURIComponent(account.email)}`, { headers }); + const body = list.ok ? await list.json() : null; + user = body?.users?.find((u) => (u.email || "").toLowerCase() === account.email.toLowerCase()); + if (!user) throw new Error(`Auth-User existiert, aber nicht auffindbar: ${account.email}`); + } else { + throw new Error(`GoTrue admin/users: ${userRes.status} ${await userRes.text()}`); + } - // 2.+3. Profil + Studio per RPC. Da create_studio_with_admin auth.uid() nutzt, - // muss der Aufruf im Kontext des neuen Users laufen — hier vereinfacht über - // einen service_role-RPC, der die Ziel-User-ID als Argument nimmt. Diese - // server-seitige Variante (create_studio_for_user) ist im Rapport-Schema noch - // anzulegen; bis dahin schützt der MOCK-Modus den lokalen Test. - const slugForStudio = slug; + // 2. Profil + Studio in einem service_role-RPC (create_studio_for_user, 0011). + // Studio-Name = lokaler Teil der E-Mail als sinnvoller Default. + const studioName = account.email.split("@")[0]; const rpcRes = await fetch(`${base}/rest/v1/rpc/create_studio_for_user`, { method: "POST", headers, - body: JSON.stringify({ p_user_id: user.id, p_name: account.email.split("@")[0], p_slug: slugForStudio }), + body: JSON.stringify({ p_user_id: user.id, p_name: studioName, p_slug: slug }), }); if (!rpcRes.ok) throw new Error(`create_studio_for_user: ${rpcRes.status} ${await rpcRes.text()}`); - const studioId = (await rpcRes.json()); + const studioId = await rpcRes.json(); - return { studioId, slug: slugForStudio, instanceUrl: instanceUrl(slugForStudio) }; + return { studioId, slug, instanceUrl: instanceUrl(slug) }; } export async function deprovision({ instance }) {