155 lines
3.8 KiB
TypeScript
155 lines
3.8 KiB
TypeScript
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;
|
|
}
|
|
|
|
async function generateQRCode(participantId: string): Promise<string> {
|
|
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<EmailResult> {
|
|
try {
|
|
const qrCodeBase64Data = await generateQRCode(participant.id);
|
|
|
|
// Send email with QR code
|
|
await sendGmail(refreshToken, {
|
|
to: participant.email,
|
|
subject: subject,
|
|
text: text,
|
|
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 });
|
|
}
|
|
};
|