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", }; serve(async (req) => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const supabase = createClient(supabaseUrl, supabaseKey); const { election_id, base_url } = await req.json(); if (!election_id || !base_url) { return new Response(JSON.stringify({ error: "election_id and base_url are required" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // Fetch election const { data: election, error: elErr } = await supabase .from("elections") .select("*, association:association_id(id, name)") .eq("id", election_id) .single(); if (elErr || !election) { return new Response(JSON.stringify({ error: "Election not found" }), { status: 404, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // Fetch eligible voters who have consent and a vote_token const { data: voters, error: vErr } = await supabase .from("election_eligible_voters") .select("*, owner:owner_id(id, first_name, last_name, email)") .eq("election_id", election_id) .eq("has_consent", true); if (vErr) throw vErr; const sent: string[] = []; const failed: string[] = []; for (const voter of voters || []) { const email = voter.owner?.email; if (!email || !voter.vote_token) { failed.push(voter.owner?.email || "unknown"); continue; } const votingUrl = `${base_url}/vote/${election_id}?token=${voter.vote_token}`; const ownerName = `${voter.owner?.first_name || ""} ${voter.owner?.last_name || ""}`.trim() || "Owner"; // Use Supabase's built-in email (or you can integrate with any SMTP) // For now, we'll use the auth admin to send a simple email try { const deadline = election.voting_end ? new Date(election.voting_end).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit" }) : ""; const { error: invokeErr } = await supabase.functions.invoke("send-transactional-email", { body: { templateName: "election-invite", recipientEmail: email, idempotencyKey: `election-${election_id}-${voter.owner?.id || email}`, templateData: { ownerName, electionTitle: election.title, associationName: election.association?.name || "", deadline, votingUrl, }, }, }); if (invokeErr) failed.push(email); else sent.push(email); } catch { failed.push(email); } // Log the send await supabase.from("election_audit_log").insert({ election_id, voter_description: `Invite sent to ${ownerName} (${email})`, action: "invite_sent", }); } return new Response( JSON.stringify({ sent: sent.length, failed: failed.length, details: { sent, failed } }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (err) { console.error("Error sending election invites:", err); return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } });