Implemented sync functionality with sheets and email sending
This commit is contained in:
161
src/routes/private/api/gmail/+server.ts
Normal file
161
src/routes/private/api/gmail/+server.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
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(participant: Participant, eventId: string): Promise<string> {
|
||||
const qrCodeData = JSON.stringify({
|
||||
participantId: participant.id,
|
||||
eventId: eventId,
|
||||
name: participant.name,
|
||||
surname: participant.surname
|
||||
});
|
||||
|
||||
const qrCodeBase64 = await QRCode.toDataURL(qrCodeData, {
|
||||
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, eventId);
|
||||
|
||||
// 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 });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user