import { json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { sendGmail } from '$lib/google/gmail/server.js'; import QRCode from 'qrcode'; interface Participant { id: string; name: string; surname: string; email: string; } interface EmailResult { participant: Participant; success: boolean; error?: string; } /** * Replaces template variables in a string with participant data * Currently supports {name} and {surname} placeholders */ function replaceTemplateVariables(template: string, participant: Participant): string { return template .replace(/{name}/gi, participant.name || '') .replace(/{surname}/gi, participant.surname || ''); } async function generateQRCode(participantId: string): Promise { const qrCodeBase64 = await QRCode.toDataURL(participantId, { type: 'image/png', margin: 2, scale: 8 }); // Remove the data URL prefix to get just the base64 string return qrCodeBase64.replace(/^data:image\/png;base64,/, ''); } async function sendEmailToParticipant( participant: Participant, subject: string, text: string, eventId: string, refreshToken: string, supabase: any ): Promise { try { const qrCodeBase64Data = await generateQRCode(participant.id); // Replace template variables in subject and body const personalizedSubject = replaceTemplateVariables(subject, participant); const personalizedText = replaceTemplateVariables(text, participant); // Send email with QR code await sendGmail(refreshToken, { to: participant.email, subject: personalizedSubject, text: personalizedText, qr_code: qrCodeBase64Data }); // Call the participant_emailed RPC function try { await supabase.rpc('participant_emailed', { p_participant_id: participant.id }); } catch (dbError) { console.error('Failed to call participant_emailed RPC:', dbError); // Don't fail the entire operation if the RPC call fails } return { participant: participant, success: true }; } catch (error) { console.error('Failed to send email to participant:', participant.email, error); return { participant: participant, success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } function validateRequest(participants: unknown, subject: unknown, text: unknown, eventId: unknown, refreshToken: unknown) { if (!participants || !Array.isArray(participants)) { return { error: 'Invalid participants array', status: 400 }; } if (!subject || !text) { return { error: 'Subject and text are required', status: 400 }; } if (!eventId) { return { error: 'Event ID is required', status: 400 }; } if (!refreshToken || typeof refreshToken !== 'string') { return { error: 'Refresh token is required', status: 401 }; } return null; } export const POST: RequestHandler = async ({ request, locals }) => { try { const { participants, subject, text, eventId, refreshToken } = await request.json(); const validationError = validateRequest(participants, subject, text, eventId, refreshToken); if (validationError) { return json({ error: validationError.error }, { status: validationError.status }); } const results: EmailResult[] = []; let successCount = 0; let errorCount = 0; // Send emails to each participant for (const participant of participants as Participant[]) { const result = await sendEmailToParticipant( participant, subject as string, text as string, eventId as string, refreshToken as string, locals.supabase ); results.push(result); if (result.success) { successCount++; } else { errorCount++; } } return json({ success: true, results, summary: { total: participants.length, success: successCount, errors: errorCount } }); } catch (error) { console.error('Email sending error:', error); // Handle specific Gmail API errors if (error instanceof Error) { if (error.message.includes('Invalid Credentials') || error.message.includes('unauthorized')) { return json({ error: 'Invalid or expired Google credentials' }, { status: 401 }); } if (error.message.includes('quota')) { return json({ error: 'Gmail API quota exceeded' }, { status: 429 }); } if (error.message.includes('rate')) { return json({ error: 'Rate limit exceeded' }, { status: 429 }); } } return json({ error: 'Failed to send emails' }, { status: 500 }); } };