import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; type SenderConfig = { host: string; port?: number; username: string; password: string; use_tls?: boolean; use_ssl?: boolean; from: string; fromEmail?: string; fromName?: string; envelopeFrom?: string; signature_html?: string; }; 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 FALLBACK_PUBLIC_SUPABASE_URL = "https://yqdefzjapnzabowsgoyd.supabase.co"; function getPublicFunctionBaseUrl() { const explicitUrl = Deno.env.get("PUBLIC_SUPABASE_URL") || Deno.env.get("PUBLIC_FUNCTION_BASE_URL") || Deno.env.get("VITE_SUPABASE_URL"); if (explicitUrl) return explicitUrl.replace(/\/$/, ""); const internalUrl = (Deno.env.get("SUPABASE_URL") || "").replace(/\/$/, ""); if (/^https:\/\/[^/]+\.supabase\.co$/i.test(internalUrl)) return internalUrl; const projectRef = Deno.env.get("SUPABASE_PROJECT_REF") || Deno.env.get("SB_PROJECT_REF"); if (projectRef) return `https://${projectRef}.supabase.co`; return FALLBACK_PUBLIC_SUPABASE_URL; } const ALLOWED_ROLES = ["admin", "manager", "staff", "employee", "board_member", "arc_member", "fining_member"] as const; function jsonResponse(body: unknown, status = 200) { return new Response(JSON.stringify(body), { status, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } async function getAuthorizedCaller(req: Request) { const authHeader = req.headers.get("authorization") || ""; if (!authHeader.startsWith("Bearer ")) { return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) }; } const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!; const callerClient = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } }, }); const token = authHeader.replace("Bearer ", ""); const { data: claimsData, error: claimsError } = await callerClient.auth.getClaims(token); const callerId = claimsData?.claims?.sub; if (claimsError || !callerId) { console.error("[send-smtp-email] Auth failed - claimsError:", claimsError?.message, "callerId:", callerId); return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) }; } console.log("[send-smtp-email] Authenticated user:", callerId); for (const role of ALLOWED_ROLES) { const { data: hasRole } = await callerClient.rpc("has_role", { _user_id: callerId, _role: role, }); if (hasRole) { console.log("[send-smtp-email] User has role:", role); return { callerId, authHeader }; } } console.error("[send-smtp-email] No matching role found for user:", callerId); return { error: jsonResponse({ success: false, error: "Insufficient permissions" }, 403) }; } async function getAuthenticatedCaller(req: Request) { const authHeader = req.headers.get("authorization") || ""; if (!authHeader.startsWith("Bearer ")) { return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) }; } const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!; const callerClient = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } }, }); const token = authHeader.replace("Bearer ", ""); const { data: claimsData, error: claimsError } = await callerClient.auth.getClaims(token); const callerId = claimsData?.claims?.sub; if (claimsError || !callerId) { return { error: jsonResponse({ success: false, error: "Unauthorized" }, 401) }; } return { callerId }; } function escapeHtml(value: string) { return String(value ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function mapSenderRowToConfig(sender: any): SenderConfig { const port = Number(sender.smtp_port ?? 587); const isImplicitSslPort = port === 465; const isStartTlsPort = port === 587; const envelopeFrom = sender.smtp_username || sender.email_address; return { host: sender.smtp_host, port, username: sender.smtp_username, password: sender.smtp_password, use_ssl: isImplicitSslPort ? true : sender.use_ssl ?? false, use_tls: isImplicitSslPort ? false : sender.use_tls ?? isStartTlsPort, from: sender.sender_name ? `${sender.sender_name} <${sender.email_address}>` : sender.email_address, fromEmail: sender.email_address, fromName: sender.sender_name, envelopeFrom, signature_html: sender.signature_html || "", }; } function ensureHtmlDocument(htmlContent: string) { const trimmed = String(htmlContent ?? "").trim(); if (!trimmed) return "
"; if (/]/i.test(trimmed)) return trimmed; return `${trimmed}`; } function toPlainText(htmlContent: string) { return htmlContent .replace(/<\s*br\s*\/?>/gi, "\n") .replace(/<\/(p|div|h1|h2|h3|h4|h5|h6|li|tr|section)\s*>/gi, "\n") .replace(/