import { serve } from "https://deno.land/std@0.190.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", }; interface SendOTPRequest { user_id: string; email: string; } // Generate 6-digit OTP code function generateOTP(): string { return Math.floor(100000 + Math.random() * 900000).toString(); } serve(async (req: Request) => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const { user_id, email }: SendOTPRequest = await req.json(); // Validate required fields if (!user_id || !email) { return new Response( JSON.stringify({ success: false, message: "Missing required fields: user_id, email" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } // Basic email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return new Response( JSON.stringify({ success: false, message: "Invalid email format" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } // Initialize Supabase client with service role const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; const supabase = createClient(supabaseUrl, supabaseServiceKey); // Generate OTP code const otpCode = generateOTP(); const expiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes from now console.log(`Generating OTP for user ${user_id}, email ${email}`); // Store OTP in database const { error: otpError } = await supabase .from('auth_otps') .insert({ user_id, email, otp_code: otpCode, expires_at: expiresAt.toISOString(), }); if (otpError) { console.error('Error storing OTP:', otpError); throw new Error(`Failed to store OTP: ${otpError.message}`); } // Get notification settings const { data: settings, error: settingsError } = await supabase .from('notification_settings') .select('*') .single(); if (settingsError || !settings) { console.error('Error fetching notification settings:', settingsError); throw new Error('Notification settings not configured'); } // Get email template const { data: template, error: templateError } = await supabase .from('notification_templates') .select('*') .eq('template_key', 'auth_email_verification') .single(); if (templateError || !template) { console.error('Error fetching email template:', templateError); throw new Error('Email template not found. Please create template with key: auth_email_verification'); } // Get user data from auth.users const { data: { user }, error: userError } = await supabase.auth.admin.getUserById(user_id); if (userError || !user) { console.error('Error fetching user:', userError); throw new Error('User not found'); } // Prepare template variables const templateVars = { platform_name: settings.platform_name || 'Platform', nama: user.user_metadata?.name || user.email || 'Pengguna', email: email, otp_code: otpCode, expiry_minutes: '15', confirmation_link: '', // Not used for OTP year: new Date().getFullYear().toString(), }; // Process shortcodes in subject let subject = template.subject; Object.entries(templateVars).forEach(([key, value]) => { subject = subject.replace(new RegExp(`{${key}}`, 'g'), value); }); // Process shortcodes in HTML body let htmlBody = template.html_content; Object.entries(templateVars).forEach(([key, value]) => { htmlBody = htmlBody.replace(new RegExp(`{${key}}`, 'g'), value); }); // Send email via send-email-v2 console.log(`Sending OTP email to ${email}`); const emailResponse = await fetch(`${supabaseUrl}/functions/v1/send-email-v2`, { method: 'POST', headers: { 'Authorization': `Bearer ${supabaseServiceKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ to: email, api_token: settings.mailketing_api_token, from_name: settings.from_name, from_email: settings.from_email, subject: subject, html_body: htmlBody, }), }); if (!emailResponse.ok) { const errorText = await emailResponse.text(); console.error('Email send error:', emailResponse.status, errorText); throw new Error(`Failed to send email: ${emailResponse.status} ${errorText}`); } const emailResult = await emailResponse.json(); console.log('Email sent successfully:', emailResult); // Log notification await supabase .from('notification_logs') .insert({ user_id, email: email, notification_type: 'auth_email_verification', status: 'sent', provider: 'mailketing', error_message: null, }); return new Response( JSON.stringify({ success: true, message: 'OTP sent successfully' }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error: any) { console.error("Error sending OTP:", error); // Try to log error notification try { const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; const supabase = createClient(supabaseUrl, supabaseServiceKey); await supabase .from('notification_logs') .insert({ user_id: null, email: null, notification_type: 'auth_email_verification', status: 'failed', provider: 'mailketing', error_message: error.message || 'Unknown error', }); } catch (logError) { console.error('Failed to log error:', logError); } return new Response( JSON.stringify({ success: false, message: error.message || "Failed to send OTP" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } });