Le contributeur colle un lien (ou plusieurs) et explique en texte libre pourquoi c'est pertinent, sans template ni minimum de caractères. Le type devient une suggestion optionnelle (chips). Les 5 formulaires typés restent disponibles derrière « Mode détaillé » (replié par défaut). - utils/submitLibre.ts : schéma Zod partagé client/serveur + mapping vers les colonnes NocoDB existantes (aucun changement de schéma) — table orgas par défaut, table ressources_references si le chip « Références » est choisi. - components/FormLibre.vue : liens dynamiques (1-10), texte libre, chips, email optionnel, honeypot. - server/api/submit/index.post.ts : branche mode=libre en amont du schéma typé existant ; email Jules remplacé par ntfy (server/utils/notify.ts) — jamais l'email ni le texte du contributeur dans la notif. - scripts/test-submit-libre.mjs : 6 cas contre le vrai schéma Zod (via jiti). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
/**
|
|
* Notification ntfy.sh — remplace Resend (abandonné le 15/07, domaine non vérifié).
|
|
* POST https://ntfy.sh/${NTFY_TOPIC}
|
|
*
|
|
* RÈGLE : un topic ntfy est une URL, pas un coffre — le message ne contient
|
|
* JAMAIS l'email du contributeur ni le texte libre soumis. Seulement des
|
|
* métadonnées de modération (id NocoDB, nom suggéré, type, nb de liens).
|
|
*
|
|
* Si NTFY_TOPIC est absent : log et continue (pas d'erreur bloquante).
|
|
*/
|
|
|
|
export interface NtfyNotifyInput {
|
|
title: string
|
|
message: string
|
|
/** Priorité ntfy 1-5 (3 = défaut) */
|
|
priority?: number
|
|
tags?: string[]
|
|
/** Lien cliqué depuis la notif (ex : NocoDB) */
|
|
clickUrl?: string
|
|
}
|
|
|
|
export async function notifyNtfy(topic: string | undefined, input: NtfyNotifyInput): Promise<void> {
|
|
if (!topic) {
|
|
console.warn('[notify] NTFY_TOPIC absent — notification skippée:', input.title)
|
|
return
|
|
}
|
|
|
|
try {
|
|
const headers: Record<string, string> = {
|
|
Title: encodeHeaderValue(input.title),
|
|
Priority: String(input.priority ?? 3),
|
|
}
|
|
if (input.tags?.length) headers.Tags = input.tags.join(',')
|
|
if (input.clickUrl) headers.Click = input.clickUrl
|
|
|
|
const res = await fetch(`https://ntfy.sh/${topic}`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: input.message,
|
|
})
|
|
|
|
if (!res.ok) {
|
|
console.warn('[notify] ntfy erreur HTTP:', res.status, await res.text())
|
|
}
|
|
} catch (e: any) {
|
|
console.warn('[notify] ntfy exception:', e?.message ?? e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Construit le message de notification pour une nouvelle soumission.
|
|
* N'inclut jamais l'email ni le texte libre du contributeur.
|
|
*/
|
|
export function buildSubmissionNotif(input: {
|
|
submissionId: number | string | null
|
|
nomSuggere: string
|
|
typeSuggere: string
|
|
nbLiens: number
|
|
nocodbAdminUrl?: string
|
|
}): NtfyNotifyInput {
|
|
const { submissionId, nomSuggere, typeSuggere, nbLiens, nocodbAdminUrl } = input
|
|
return {
|
|
title: '[AEP] Nouvelle fiche soumise',
|
|
message: [
|
|
`Id NocoDB : ${submissionId ?? '—'}`,
|
|
`Nom suggéré : ${nomSuggere}`,
|
|
`Type suggéré : ${typeSuggere}`,
|
|
`Liens : ${nbLiens}`,
|
|
].join('\n'),
|
|
tags: ['inbox_tray'],
|
|
clickUrl: nocodbAdminUrl,
|
|
}
|
|
}
|
|
|
|
function encodeHeaderValue(v: string): string {
|
|
// ntfy accepte l'UTF-8 dans les headers mais certains proxies sont capricieux
|
|
// avec les accents en header brut — on reste simple, ntfy gère nativement.
|
|
return v
|
|
}
|