diff --git a/src/pages/api/aep-perso.ts b/src/pages/api/aep-perso.ts new file mode 100644 index 0000000..d127226 --- /dev/null +++ b/src/pages/api/aep-perso.ts @@ -0,0 +1,196 @@ +import type { APIRoute } from 'astro' + +export const prerender = false + +const KIT_API_BASE = 'https://api.kit.com/v4' +const TAG_NAME = 'AEP - sans perso' + +function htmlPage(title: string, heading: string, message: string): string { + return ` + + + + + ${title} — trans-former.fr + + + +
+

${heading}

+

${message}

+ + ← Retour sur trans-former.fr + +
+ +` +} + +async function getSubscriberByEmail(email: string, apiKey: string): Promise { + const url = `${KIT_API_BASE}/subscribers?email_address=${encodeURIComponent(email)}` + const res = await fetch(url, { + headers: { 'X-Kit-Api-Key': apiKey }, + signal: AbortSignal.timeout(10000), + }) + if (!res.ok) return null + const data = await res.json() + if (!data?.subscribers?.length) return null + return String(data.subscribers[0].id) +} + +async function getTagId(apiKey: string): Promise { + const url = `${KIT_API_BASE}/tags` + const res = await fetch(url, { + headers: { 'X-Kit-Api-Key': apiKey }, + signal: AbortSignal.timeout(10000), + }) + if (!res.ok) return null + const data = await res.json() + const tag = data?.tags?.find((t: any) => t.name === TAG_NAME) + return tag?.id ?? null +} + +async function tagSubscriber(subscriberId: string, tagId: number, apiKey: string): Promise { + const url = `${KIT_API_BASE}/tags/${tagId}/subscribers` + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Kit-Api-Key': apiKey, + }, + body: JSON.stringify({ id: subscriberId }), + signal: AbortSignal.timeout(10000), + }) + return res.ok || res.status === 409 +} + +async function untagSubscriber(subscriberId: string, tagId: number, apiKey: string): Promise { + const url = `${KIT_API_BASE}/tags/${tagId}/subscribers/${subscriberId}` + const res = await fetch(url, { + method: 'DELETE', + headers: { 'X-Kit-Api-Key': apiKey }, + signal: AbortSignal.timeout(10000), + }) + return res.ok || res.status === 404 +} + +export const GET: APIRoute = async ({ url }) => { + const KIT_API_KEY = import.meta.env.KIT_API_SECRET_V4 + if (!KIT_API_KEY) { + return new Response(htmlPage( + 'Erreur', + 'Configuration manquante', + 'Le service de désinscription n\'est pas configuré. Contacte-moi à julesneny8@gmail.com.' + ), { + status: 500, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + const email = (url.searchParams.get('email') || '').trim().toLowerCase() + const action = url.searchParams.get('action') + + if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + return new Response(htmlPage( + 'Lien invalide', + 'Lien invalide', + 'Le lien que tu as suivi est incomplet ou mal formé. Essaye de copier-coller l\'URL complète depuis l\'email.' + ), { + status: 400, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + if (action !== 'unsubscribe' && action !== 'resubscribe') { + return new Response(htmlPage( + 'Action inconnue', + 'Action inconnue', + 'Le lien que tu as suivi ne correspond à aucune action connue.' + ), { + status: 400, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + try { + const subscriberId = await getSubscriberByEmail(email, KIT_API_KEY) + if (!subscriberId) { + return new Response(htmlPage( + 'Email inconnu', + 'Email inconnu', + 'Cet email n\'est pas abonné à la newsletter. Si tu penses qu\'il s\'agit d\'une erreur, écris-moi à julesneny8@gmail.com.' + ), { + status: 404, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + const tagId = await getTagId(KIT_API_KEY) + if (!tagId) { + return new Response(htmlPage( + 'Erreur', + 'Configuration manquante', + 'Le tag de désinscription est introuvable. Contacte-moi à julesneny8@gmail.com.' + ), { + status: 500, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + if (action === 'unsubscribe') { + const result = await tagSubscriber(subscriberId, tagId, KIT_API_KEY) + if (!result) { + return new Response(htmlPage( + 'Erreur', + 'Erreur technique', + 'Une erreur est survenue lors de la désinscription. Réessaie ou écris-moi à julesneny8@gmail.com.' + ), { + status: 500, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + return new Response(htmlPage( + 'Désinscription réussie', + 'Tu es désinscrit·e', + 'Tu ne recevras plus les lettres « perso » de l\'infolettre Architecture d\'Écologie Politique. Tu continues à recevoir les textes de fond.

Si tu changes d\'avis, un lien de réabonnement est disponible dans chaque email de fond.' + ), { + status: 200, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + if (action === 'resubscribe') { + const result = await untagSubscriber(subscriberId, tagId, KIT_API_KEY) + if (!result) { + return new Response(htmlPage( + 'Erreur', + 'Erreur technique', + 'Une erreur est survenue lors du réabonnement. Réessaie ou écris-moi à julesneny8@gmail.com.' + ), { + status: 500, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + return new Response(htmlPage( + 'Réabonnement réussi', + 'Tu es de nouveau abonné·e', + 'Tu recevras à nouveau les lettres « perso » de l\'infolettre Architecture d\'Écologie Politique. Merci de ton intérêt !' + ), { + status: 200, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } + + return new Response(null, { status: 400 }) + } catch (e) { + return new Response(htmlPage( + 'Erreur', + 'Erreur technique', + 'Une erreur inattendue est survenue. Réessaie ou écris-moi à julesneny8@gmail.com.' + ), { + status: 500, + headers: { 'Content-Type': 'text/html; charset=utf-8' }, + }) + } +}