import { createClient } from "https://esm.sh/@supabase/supabase-js@2.99.1"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version", }; const PRODUCTION_ORIGIN = "https://avria.cloud"; Deno.serve(async (req) => { if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders }); try { const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!; const SERVICE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY")!; const authHeader = req.headers.get("Authorization") || ""; if (!authHeader) { return new Response(JSON.stringify({ error: "Missing auth" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } // Verify caller is staff const userClient = createClient(SUPABASE_URL, ANON_KEY, { global: { headers: { Authorization: authHeader } } }); const { data: { user: caller } } = await userClient.auth.getUser(); if (!caller) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } const admin = createClient(SUPABASE_URL, SERVICE_KEY); const { data: roles } = await admin.from("user_roles").select("role").eq("user_id", caller.id); const isStaff = (roles || []).some((r: any) => r.role === "admin" || r.role === "manager"); if (!isStaff) { return new Response(JSON.stringify({ error: "Staff only" }), { status: 403, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } const body = await req.json(); const { rental_id, email, as_owner } = body || {}; const isOwnerInvite = !!as_owner; if (!rental_id || !email) { return new Response(JSON.stringify({ error: "rental_id and email required" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } // Look up rental const { data: rental, error: rentalErr } = await admin .from("rv_boat_lot_rentals") .select("id, renter_name, association_id, user_id") .eq("id", rental_id) .single(); if (rentalErr || !rental) { return new Response(JSON.stringify({ error: "Rental not found" }), { status: 404, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } // Find or create user let userId: string | null = null; const { data: existingList } = await admin.auth.admin.listUsers({ page: 1, perPage: 1, filter: `email.eq.${email}` } as any); // Fallback: paginate-search if filter unsupported if (existingList?.users?.length) { userId = existingList.users[0].id; } else { // Try fetching all (small project) then match const { data: all } = await admin.auth.admin.listUsers({ page: 1, perPage: 1000 }); const found = all?.users?.find((u) => (u.email || "").toLowerCase() === String(email).toLowerCase()); if (found) userId = found.id; } let invited = false; if (!userId) { const { data: created, error: createErr } = await admin.auth.admin.inviteUserByEmail(email, { redirectTo: `${PRODUCTION_ORIGIN}/reset-password?mode=invite`, data: { full_name: rental.renter_name }, }); if (createErr || !created?.user) { return new Response(JSON.stringify({ error: createErr?.message || "Failed to invite" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } userId = created.user.id; invited = true; } else { // Send password recovery so existing users can also access portal await admin.auth.admin.generateLink({ type: "recovery", email, options: { redirectTo: `${PRODUCTION_ORIGIN}/reset-password` } } as any); } // Assign the homeowner-style RV/Boat Lot role, idempotent const role = "rv_boat_lot"; await admin.from("user_roles").upsert( { user_id: userId, role: role as any }, { onConflict: "user_id,role" } as any, ); // Link rental to user (and flag is_owner if this is an owner invite) const updatePayload: Record = { user_id: userId, renter_email: email }; if (isOwnerInvite) updatePayload.is_owner = true; const { error: linkErr } = await admin .from("rv_boat_lot_rentals") .update(updatePayload) .eq("id", rental_id); if (linkErr) { return new Response(JSON.stringify({ error: linkErr.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } return new Response(JSON.stringify({ ok: true, user_id: userId, invited }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } catch (e: any) { return new Response(JSON.stringify({ error: e?.message || "Server error" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } });