Aggiunge newsletter custom con D1, double opt-in e invio campagne.
Attiva il form footer, API subscribe/confirm/unsubscribe e endpoint admin protetti, riusando Resend per le email. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
85
src/pages/api/newsletter/confirm.ts
Normal file
85
src/pages/api/newsletter/confirm.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import {
|
||||
getNewsletterDb,
|
||||
getRuntimeEnv,
|
||||
readEnv,
|
||||
required,
|
||||
sendWithResend,
|
||||
type NewsletterSubscriber,
|
||||
} from '../../../lib/newsletter';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export const GET: APIRoute = async ({ request, locals, url }) => {
|
||||
const runtimeEnv = getRuntimeEnv(locals);
|
||||
const mode = (readEnv('NEWSLETTER_MODE', runtimeEnv) || readEnv('CONTACT_FORM_MODE', runtimeEnv) || 'dev').toLowerCase();
|
||||
const token = required(url.searchParams.get('token'));
|
||||
const okPath = '/newsletter/confermata';
|
||||
const failPath = '/newsletter/conferma-errore';
|
||||
|
||||
if (!token) {
|
||||
return Response.redirect(new URL(failPath, url).toString(), 303);
|
||||
}
|
||||
|
||||
if (mode !== 'live') {
|
||||
console.info('[newsletter:dev] Confirm token received', { token: token.slice(0, 8) + '…' });
|
||||
return Response.redirect(new URL(okPath, url).toString(), 303);
|
||||
}
|
||||
|
||||
const db = getNewsletterDb(runtimeEnv);
|
||||
if (!db) {
|
||||
return new Response('Newsletter database binding NEWSLETTER_DB is not configured.', {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
|
||||
const row = await db
|
||||
.prepare(
|
||||
`SELECT * FROM newsletter_subscribers
|
||||
WHERE confirm_token = ? AND status = 'pending'
|
||||
LIMIT 1`,
|
||||
)
|
||||
.bind(token)
|
||||
.first<NewsletterSubscriber>();
|
||||
|
||||
if (!row) {
|
||||
return Response.redirect(new URL(failPath, url).toString(), 303);
|
||||
}
|
||||
|
||||
await db
|
||||
.prepare(
|
||||
`UPDATE newsletter_subscribers
|
||||
SET status = 'active',
|
||||
confirmed_at = datetime('now'),
|
||||
confirm_token = NULL
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(row.id)
|
||||
.run();
|
||||
|
||||
const resendApiKey =
|
||||
readEnv('NEWSLETTER_RESEND_API_KEY', runtimeEnv) ||
|
||||
readEnv('CONTACT_FORM_RESEND_API_KEY', runtimeEnv);
|
||||
const fromEmail =
|
||||
readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) ||
|
||||
readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv);
|
||||
const notifyTo =
|
||||
readEnv('NEWSLETTER_NOTIFY_TO_EMAIL', runtimeEnv) ||
|
||||
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv);
|
||||
|
||||
if (resendApiKey && fromEmail && notifyTo) {
|
||||
try {
|
||||
await sendWithResend({
|
||||
apiKey: resendApiKey,
|
||||
fromEmail,
|
||||
toEmail: notifyTo,
|
||||
subject: 'Iscrizione newsletter confermata — NexStudio',
|
||||
text: `L’indirizzo ${row.email} ha confermato l’iscrizione alla newsletter.`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('[newsletter] confirm notify failed', error);
|
||||
}
|
||||
}
|
||||
|
||||
return Response.redirect(new URL(okPath, request.url).toString(), 303);
|
||||
};
|
||||
Reference in New Issue
Block a user