import { createServiceClient } from "../_shared/chatbot.ts"; import { getClientIp, getCorsHeaders, hashText, jsonResponse, preflightResponse, readJsonBody, requireRateLimit, } from "../_shared/security.ts"; const MAX_BODY_BYTES = 8 * 1024; const isValidEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim()); Deno.serve(async (request) => { if (request.method === "OPTIONS") { return preflightResponse(request, "public"); } if (request.method !== "POST") { return jsonResponse({ ok: false, error: "Method not allowed" }, 405); } const corsHeaders = getCorsHeaders(request, "public"); if (!corsHeaders) { return jsonResponse({ ok: false, error: "Origin not allowed" }, 403); } try { const { body } = await readJsonBody<{ email?: string; otp?: string }>(request, { maxBytes: MAX_BODY_BYTES, }); const email = String(body.email || "").trim().toLowerCase(); const otp = String(body.otp || "").trim(); if (!email || !isValidEmail(email)) { return jsonResponse({ ok: false, error: "Valid email is required" }, 400, corsHeaders); } if (!otp || otp.length < 4 || otp.length > 12) { return jsonResponse({ ok: false, error: "Valid OTP is required" }, 400, corsHeaders); } const supabase = createServiceClient(); const emailHash = await hashText(email); const ipHash = await hashText(getClientIp(request)); await requireRateLimit(supabase, { scope: "otp-verify", key: `${ipHash}:${emailHash}`, maxCount: 5, windowSeconds: 600, blockSeconds: 1800, }); const { data, error } = await supabase.auth.verifyOtp({ email, token: otp, type: "email", }); if (error) { return jsonResponse({ ok: false, error: error.message }, 400, corsHeaders); } return jsonResponse( { ok: true, session: data.session || null, user: data.session?.user || null, }, 200, corsHeaders, ); } catch (error) { if (error instanceof Error && "status" in error) { const httpError = error as { status: number; message: string }; return jsonResponse({ ok: false, error: httpError.message }, httpError.status, corsHeaders); } return jsonResponse( { ok: false, error: error instanceof Error ? error.message : "Unexpected error", }, 500, corsHeaders, ); } });