95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
/**
|
|
* POST /api/report
|
|
*
|
|
* Signalement d'erreur / proposition de modification sur une fiche
|
|
* Fallback Resend (pas de table NocoDB créée)
|
|
*
|
|
* Body : { fiche_id: number, message: string, email: string }
|
|
* Rate limit : 5/IP/jour
|
|
* Envoi vers jules@trans-former.fr via Resend API
|
|
*/
|
|
|
|
import { checkRateLimitJson } from '~/server/utils/rateLimitJson'
|
|
|
|
const EMAIL_JULES = process.env.EMAIL_JULES || 'jules@trans-former.fr'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
// 1. IP
|
|
const ip =
|
|
getHeader(event, 'x-forwarded-for')?.split(',')[0].trim() ||
|
|
event.node.req.socket?.remoteAddress ||
|
|
'0.0.0.0'
|
|
|
|
// 2. Rate limit 5/IP/jour
|
|
const allowed = checkRateLimitJson(ip, 'report', 5)
|
|
if (!allowed) {
|
|
throw createError({
|
|
statusCode: 429,
|
|
statusMessage: 'Limite de 5 signalements par jour atteinte.',
|
|
})
|
|
}
|
|
|
|
// 3. Lire le body
|
|
const body = await readBody(event)
|
|
const fiche_id: number = Number(body?.fiche_id ?? 0)
|
|
const message: string = (body?.message ?? '').trim()
|
|
const email: string = (body?.email ?? '').trim()
|
|
|
|
// 4. Validation
|
|
if (!fiche_id || fiche_id <= 0) {
|
|
throw createError({ statusCode: 400, statusMessage: 'fiche_id invalide.' })
|
|
}
|
|
if (!message || message.length < 5 || message.length > 500) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Message requis (5-500 caractères).' })
|
|
}
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
if (!email || !emailRegex.test(email)) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Email invalide.' })
|
|
}
|
|
|
|
// 5. Envoi via Resend
|
|
const resendApiKey = process.env.RESEND_API_KEY
|
|
if (!resendApiKey) {
|
|
console.error('[report] RESEND_API_KEY manquante')
|
|
throw createError({ statusCode: 500, statusMessage: 'Configuration email manquante.' })
|
|
}
|
|
|
|
const submittedAt = new Date().toLocaleString('fr-FR', { timeZone: 'Europe/Paris' })
|
|
|
|
try {
|
|
await $fetch('https://api.resend.com/emails', {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${resendApiKey}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
from: 'AEP Signalement <noreply@trans-former.fr>',
|
|
to: EMAIL_JULES,
|
|
subject: `[AEP] Signalement fiche #${fiche_id}`,
|
|
html: `
|
|
<h2>Signalement fiche AEP #${fiche_id}</h2>
|
|
<p><strong>Date :</strong> ${submittedAt}</p>
|
|
<p><strong>Email expéditeur :</strong> ${email}</p>
|
|
<p><strong>Message :</strong></p>
|
|
<blockquote style="border-left:3px solid #ccc;padding-left:12px;color:#555;">
|
|
${message.replace(/\n/g, '<br/>')}
|
|
</blockquote>
|
|
<hr/>
|
|
<p style="font-size:12px;color:#999;">
|
|
Voir la fiche : <a href="https://aep.trans-former.fr/fiche/${fiche_id}">https://aep.trans-former.fr/fiche/${fiche_id}</a>
|
|
</p>
|
|
`,
|
|
}),
|
|
})
|
|
} catch (e: any) {
|
|
console.error('[report] Erreur Resend:', e?.message ?? e)
|
|
throw createError({
|
|
statusCode: 502,
|
|
statusMessage: 'Erreur envoi email — réessaie dans quelques instants.',
|
|
})
|
|
}
|
|
|
|
return { ok: true, message: 'Signalement envoyé, merci !' }
|
|
})
|