import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.45.0' const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', } const PUBLIC_BASE_URL = 'https://avria.cloud' Deno.serve(async (req) => { if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders }) try { const { vendor_id } = await req.json() if (!vendor_id) { return new Response(JSON.stringify({ error: 'vendor_id required' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } const supabaseUrl = Deno.env.get('SUPABASE_URL')! const serviceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! const anonKey = Deno.env.get('SUPABASE_ANON_KEY')! const authHeader = req.headers.get('Authorization') || '' // Auth client to validate caller const authClient = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } }, }) const { data: userRes } = await authClient.auth.getUser() if (!userRes?.user) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } const userId = userRes.user.id const admin = createClient(supabaseUrl, serviceKey) // Verify caller is admin or manager const { data: roleRows } = await admin .from('user_roles').select('role').eq('user_id', userId) const roles = (roleRows || []).map((r: any) => r.role) if (!roles.includes('admin') && !roles.includes('manager')) { return new Response(JSON.stringify({ error: 'Forbidden' }), { status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } // Load vendor const { data: vendor, error: vErr } = await admin .from('vendors').select('id, name, email').eq('id', vendor_id).single() if (vErr || !vendor) { return new Response(JSON.stringify({ error: 'Vendor not found' }), { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } if (!vendor.email) { return new Response(JSON.stringify({ error: 'Vendor has no email on file' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } // Get profile name for requester (best-effort) const { data: profile } = await admin .from('profiles').select('full_name').eq('user_id', userId).maybeSingle() // Create request token const { data: reqRow, error: reqErr } = await admin .from('vendor_insurance_requests') .insert({ vendor_id, sent_to_email: vendor.email, created_by: userId, }) .select('id, token, expires_at') .single() if (reqErr || !reqRow) { return new Response(JSON.stringify({ error: 'Failed to create request', detail: reqErr?.message }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } const link = `${PUBLIC_BASE_URL}/vendor-insurance/${reqRow.token}` const expiresAt = new Date(reqRow.expires_at).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric', }) // Send email via shared transactional sender using the caller's JWT. // Some deployed environments still enforce gateway JWT verification even // when local config says verify_jwt=false, so forwarding the staff session // avoids the nested call being rejected before it reaches the sender. let emailErr: any = null try { const emailRes = await fetch(`${supabaseUrl}/functions/v1/send-transactional-email`, { method: 'POST', headers: { 'apikey': anonKey, ...(authHeader ? { Authorization: authHeader } : {}), 'Content-Type': 'application/json', }, body: JSON.stringify({ templateName: 'vendor-insurance-request', recipientEmail: vendor.email, idempotencyKey: `vendor-insurance-${reqRow.id}`, templateData: { vendorName: vendor.name, requesterName: profile?.full_name || 'Avria Community Management', link, expiresAt, }, }), }) if (!emailRes.ok) { const text = await emailRes.text() emailErr = { message: `status ${emailRes.status}: ${text}` } } } catch (e) { emailErr = e } if (emailErr) { console.error('Email send failed:', emailErr) return new Response(JSON.stringify({ ok: false, request_id: reqRow.id, link, error: 'Email failed to send. Share the link manually.', }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }) } return new Response(JSON.stringify({ ok: true, request_id: reqRow.id, link, sent_to: vendor.email, }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }) } catch (e) { console.error('send-vendor-insurance-request error:', e) return new Response(JSON.stringify({ error: String(e) }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } })