Refactor CookieBanner and Contact API for improved functionality and security

- Removed localStorage fallback from CookieBanner, simplifying consent management.
- Refactored manual review email handling in the Contact API to utilize HTML templates for better structure and security.
- Enhanced email content generation by escaping HTML special characters and using template files for dynamic data insertion.
This commit is contained in:
2025-06-26 22:54:02 +02:00
parent cb64f7f76c
commit 49fabddc96
7 changed files with 226 additions and 235 deletions

View File

@@ -311,22 +311,4 @@ export const POST: APIRoute = async ({ request, clientAddress }) => {
}
};
export const manualReviewPOST: APIRoute = async ({ request }) => {
const { token, email: submittedEmail, justification } = await request.json();
try {
const payload = jwt.verify(token, MANUAL_REVIEW_SECRET) as { email: string, message: string };
if (payload.email !== submittedEmail) {
return new Response(JSON.stringify({ error: 'Email does not match original submission.' }), { status: 403 });
}
// Send to manual review mailbox
await sendEmail(
MANUAL_REVIEW_EMAIL,
'Manual Review Requested: Contact Form Submission',
`<p><strong>Email:</strong> ${submittedEmail}</p><p><strong>Message:</strong> ${payload.message}</p><p><strong>Justification:</strong> ${justification || 'None provided'}</p>`,
`Email: ${submittedEmail}\nMessage: ${payload.message}\nJustification: ${justification || 'None provided'}`
);
return new Response(JSON.stringify({ success: true }));
} catch (_err) {
return new Response(JSON.stringify({ error: 'Invalid or expired token.' }), { status: 400 });
}
};

View File

@@ -1,18 +1,19 @@
import type { APIRoute } from 'astro';
import jwt from 'jsonwebtoken';
import { sendEmail } from '../../../utils/email-handler';
import { sendEmail, escapeHtml } from '../../../utils/email-handler';
import fs from 'fs/promises';
import path from 'path';
const MANUAL_REVIEW_SECRET = process.env.MANUAL_REVIEW_SECRET;
const MANUAL_REVIEW_EMAIL = 'manual-review@365devnet.eu';
// Utility to escape HTML special characters
function escapeHtml(str: string): string {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
async function getTemplate(templateName: string, data: Record<string, string>): Promise<string> {
const templatePath = path.join(process.cwd(), 'src', 'templates', 'email', `${templateName}.html`);
let template = await fs.readFile(templatePath, 'utf-8');
for (const key in data) {
template = template.replace(new RegExp(`{{${key}}}`, 'g'), data[key]);
}
return template;
}
export const POST: APIRoute = async ({ request }) => {
@@ -23,10 +24,15 @@ export const POST: APIRoute = async ({ request }) => {
return new Response(JSON.stringify({ error: 'Email does not match original submission.' }), { status: 403 });
}
// Send to manual review mailbox
const html = await getTemplate('manual-review', {
email: escapeHtml(submittedEmail),
message: escapeHtml(payload.message),
justification: escapeHtml(justification || 'None provided'),
});
await sendEmail(
MANUAL_REVIEW_EMAIL,
'Manual Review Requested: Contact Form Submission',
`<p><strong>Email:</strong> ${escapeHtml(submittedEmail)}</p><p><strong>Message:</strong> ${escapeHtml(payload.message)}</p><p><strong>Justification:</strong> ${escapeHtml(justification || 'None provided')}</p>`,
html,
`Email: ${submittedEmail}\nMessage: ${payload.message}\nJustification: ${justification || 'None provided'}`
);
return new Response(JSON.stringify({ success: true }));
@@ -34,4 +40,6 @@ export const POST: APIRoute = async ({ request }) => {
return new Response(JSON.stringify({ error: 'Invalid or expired token.' }), { status: 400 });
}
};