Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
1b454bf090
commit
fbf23c1c2f
@@ -18,29 +18,47 @@ interface SendBody {
|
||||
bcc?: string | string[];
|
||||
replyTo?: string;
|
||||
test?: boolean;
|
||||
context?: string;
|
||||
case_id?: string;
|
||||
}
|
||||
|
||||
function toArray(v: string | string[] | undefined | null): string[] {
|
||||
if (!v) return [];
|
||||
if (Array.isArray(v)) return v.map((s) => s.trim()).filter(Boolean);
|
||||
return v
|
||||
.split(/[,;]/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });
|
||||
|
||||
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
||||
const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
|
||||
const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||
const admin = createClient(supabaseUrl, serviceKey);
|
||||
|
||||
let userId: string | null = null;
|
||||
let body: SendBody | null = null;
|
||||
let fromAddr = "";
|
||||
let toList: string[] = [];
|
||||
let ccList: string[] = [];
|
||||
let bccList: string[] = [];
|
||||
|
||||
try {
|
||||
const authHeader = req.headers.get("Authorization");
|
||||
if (!authHeader) {
|
||||
return json({ error: "Missing authorization" }, 401);
|
||||
}
|
||||
|
||||
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
||||
const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
|
||||
const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
||||
|
||||
// Verify caller is authenticated
|
||||
const userClient = createClient(supabaseUrl, anonKey, {
|
||||
global: { headers: { Authorization: authHeader } },
|
||||
});
|
||||
const { data: userData, error: userErr } = await userClient.auth.getUser();
|
||||
if (userErr || !userData.user) return json({ error: "Unauthorized" }, 401);
|
||||
|
||||
const admin = createClient(supabaseUrl, serviceKey);
|
||||
userId = userData.user.id;
|
||||
|
||||
// Load latest enabled SMTP settings
|
||||
const { data: settings, error: setErr } = await admin
|
||||
@@ -57,7 +75,7 @@ Deno.serve(async (req) => {
|
||||
const password = Deno.env.get("SMTP_PASSWORD") ?? "";
|
||||
if (!password) return json({ error: "SMTP_PASSWORD secret not set" }, 400);
|
||||
|
||||
const body = (await req.json()) as SendBody;
|
||||
body = (await req.json()) as SendBody;
|
||||
if (!body.to || !body.subject) {
|
||||
return json({ error: "to and subject are required" }, 400);
|
||||
}
|
||||
@@ -69,13 +87,6 @@ Deno.serve(async (req) => {
|
||||
if (settings.port === 465) useTls = true;
|
||||
else if (settings.port === 587 || settings.port === 25) useTls = false;
|
||||
|
||||
console.log("SMTP connecting", {
|
||||
host: settings.host,
|
||||
port: settings.port,
|
||||
tls: useTls,
|
||||
hasAuth: !!settings.username,
|
||||
});
|
||||
|
||||
const client = new SMTPClient({
|
||||
connection: {
|
||||
hostname: settings.host,
|
||||
@@ -87,15 +98,25 @@ Deno.serve(async (req) => {
|
||||
},
|
||||
});
|
||||
|
||||
const fromAddr = settings.from_name
|
||||
fromAddr = settings.from_name
|
||||
? `${settings.from_name} <${settings.from_email}>`
|
||||
: settings.from_email;
|
||||
|
||||
toList = toArray(body.to);
|
||||
ccList = toArray(body.cc);
|
||||
// Merge per-message BCC with default BCC from settings
|
||||
bccList = [
|
||||
...toArray(body.bcc),
|
||||
...toArray(settings.default_bcc ?? null),
|
||||
];
|
||||
// dedupe
|
||||
bccList = Array.from(new Set(bccList));
|
||||
|
||||
await client.send({
|
||||
from: fromAddr,
|
||||
to: body.to,
|
||||
cc: body.cc,
|
||||
bcc: body.bcc,
|
||||
to: toList,
|
||||
cc: ccList.length ? ccList : undefined,
|
||||
bcc: bccList.length ? bccList : undefined,
|
||||
replyTo: body.replyTo ?? settings.reply_to ?? undefined,
|
||||
subject: body.subject,
|
||||
content: body.text ?? "auto",
|
||||
@@ -103,10 +124,47 @@ Deno.serve(async (req) => {
|
||||
});
|
||||
await client.close();
|
||||
|
||||
// Log success
|
||||
await admin.from("email_logs").insert({
|
||||
sent_by: userId,
|
||||
to_addresses: toList,
|
||||
cc_addresses: ccList,
|
||||
bcc_addresses: bccList,
|
||||
reply_to: body.replyTo ?? settings.reply_to ?? null,
|
||||
from_address: fromAddr,
|
||||
subject: body.subject,
|
||||
body_html: body.html ?? null,
|
||||
body_text: body.text ?? null,
|
||||
status: "sent",
|
||||
context: body.context ?? (body.test ? "test" : null),
|
||||
case_id: body.case_id ?? null,
|
||||
});
|
||||
|
||||
return json({ ok: true });
|
||||
} catch (e) {
|
||||
console.error("send-smtp-email error", e);
|
||||
return json({ error: e instanceof Error ? e.message : String(e) }, 500);
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
console.error("send-smtp-email error", message);
|
||||
// Best-effort failure log
|
||||
try {
|
||||
await admin.from("email_logs").insert({
|
||||
sent_by: userId,
|
||||
to_addresses: toList,
|
||||
cc_addresses: ccList,
|
||||
bcc_addresses: bccList,
|
||||
reply_to: body?.replyTo ?? null,
|
||||
from_address: fromAddr || null,
|
||||
subject: body?.subject ?? "(unknown)",
|
||||
body_html: body?.html ?? null,
|
||||
body_text: body?.text ?? null,
|
||||
status: "failed",
|
||||
error_message: message,
|
||||
context: body?.context ?? (body?.test ? "test" : null),
|
||||
case_id: body?.case_id ?? null,
|
||||
});
|
||||
} catch (_) {
|
||||
// ignore log failure
|
||||
}
|
||||
return json({ error: message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user