import "jsr:@supabase/functions-js/edge-runtime.d.ts"; import nodemailer from "npm:nodemailer@6"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Client-Info, Apikey", }; const MIN_INTERVAL_MS = 60 * 1000; const lastSentMap = new Map(); Deno.serve(async (req: Request) => { if (req.method === "OPTIONS") { return new Response(null, { status: 200, headers: corsHeaders }); } try { const { to, subject, body, html, sendType, smtpConfig } = await req.json(); if (!smtpConfig || !smtpConfig.host || !smtpConfig.port || !smtpConfig.user || !smtpConfig.pass) { return new Response( JSON.stringify({ success: false, error: "SMTP settings are missing. Please configure your SMTP settings first." }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } if (!to || !subject || (!body && !html)) { return new Response( JSON.stringify({ success: false, error: "Missing required fields: to, subject, body or html" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } const recipients = Array.isArray(to) ? to : [to]; const now = Date.now(); const rateLimited: string[] = []; const allowed: string[] = []; for (const email of recipients) { const lastSent = lastSentMap.get(email); if (lastSent && now - lastSent < MIN_INTERVAL_MS) { rateLimited.push(email); } else { allowed.push(email); } } if (allowed.length === 0) { return new Response( JSON.stringify({ success: false, error: "All recipients are rate-limited. Please wait 60 seconds.", rateLimited, }), { status: 429, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } const transportConfig = { host: smtpConfig.host, port: Number(smtpConfig.port), secure: Number(smtpConfig.port) === 465, auth: { user: smtpConfig.user, pass: smtpConfig.pass, }, }; const senderName = smtpConfig.senderName || smtpConfig.user; const senderEmail = smtpConfig.senderEmail || smtpConfig.user; const sender = `"${senderName}" <${senderEmail}>`; const transporter = nodemailer.createTransport(transportConfig); const mailOptions: Record = { from: sender, subject: subject, }; if (html) { mailOptions.html = html; if (body) mailOptions.text = body; } else { mailOptions.text = body; } if (sendType === "bcc") { mailOptions.bcc = allowed.join(", "); } else { mailOptions.to = allowed.join(", "); } const info = await transporter.sendMail(mailOptions); for (const email of allowed) { lastSentMap.set(email, now); } return new Response( JSON.stringify({ success: true, messageId: info.messageId, sent: allowed.length, rateLimited: rateLimited.length, rateLimitedEmails: rateLimited, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error) { return new Response( JSON.stringify({ success: false, error: (error as Error).message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } });