feat(aep): M1 — formulaire /proposer assoupli (liens + texte libre + chip)
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>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user