From 906a0041a4af1531aed567bcb7b4900ba04f0f1b Mon Sep 17 00:00:00 2001 From: Krao Hasanee Date: Mon, 30 Mar 2026 09:27:19 -0400 Subject: [PATCH] Add team-only user creation, remove public signup - Add + New User form on Companies page (name, email, password, company) - Create-user Supabase edge function with role verification - Remove public signup route and login page link Co-Authored-By: Claude Sonnet 4.6 --- src/App.jsx | 4 -- src/pages/Login.jsx | 3 +- supabase/functions/create-user/index.ts | 65 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 supabase/functions/create-user/index.ts diff --git a/src/App.jsx b/src/App.jsx index 55ba702..470ec2e 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,8 +3,6 @@ import { AuthProvider } from './context/AuthContext'; import ProtectedRoute from './components/ProtectedRoute'; import Login from './pages/Login'; -import Signup from './pages/Signup'; -import SignupConfirmation from './pages/SignupConfirmation'; import Dashboard from './pages/team/Dashboard'; import Companies from './pages/team/Companies'; @@ -32,8 +30,6 @@ export default function App() { } /> - } /> - } /> } /> } /> diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 512a083..13639d4 100755 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -67,8 +67,7 @@ export default function Login() {

- New client?{' '} - Create an account + Contact Fourge Branding to get access.

diff --git a/supabase/functions/create-user/index.ts b/supabase/functions/create-user/index.ts new file mode 100644 index 0000000..98ccef8 --- /dev/null +++ b/supabase/functions/create-user/index.ts @@ -0,0 +1,65 @@ +// deno-lint-ignore-file +import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +} + +const ok = (body: Record) => new Response(JSON.stringify(body), { + status: 200, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, +}) + +serve(async (req) => { + if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders }) + + try { + const authHeader = req.headers.get('Authorization') ?? '' + if (!authHeader) return ok({ error: 'No authorization header' }) + + const callerClient = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { global: { headers: { Authorization: authHeader } } } + ) + + const { data: userData, error: userError } = await callerClient.auth.getUser() + if (userError || !userData?.user) return ok({ error: `Auth failed: ${userError?.message ?? 'no user'}` }) + + const { data: profile, error: profileError } = await callerClient + .from('profiles').select('role').eq('id', userData.user.id).single() + if (profileError) return ok({ error: `Profile error: ${profileError.message}` }) + if (profile?.role !== 'team') return ok({ error: 'Forbidden: team only' }) + + const body = await req.json() + const { name, email, password, company_id } = body + if (!name || !email || !password) return ok({ error: 'name, email and password are required' }) + + const admin = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' + ) + + const { data: created, error: createError } = await admin.auth.admin.createUser({ + email, + password, + email_confirm: true, + user_metadata: { name, role: 'client' }, + }) + + if (createError) return ok({ error: `Create user failed: ${createError.message}` }) + + if (company_id && created?.user) { + const { error: assignError } = await admin + .from('profiles').update({ company_id }).eq('id', created.user.id) + if (assignError) return ok({ error: `User created but company assign failed: ${assignError.message}` }) + } + + return ok({ success: true }) + + } catch (err) { + return ok({ error: `Unexpected: ${String(err)}` }) + } +})