Files
nav-carte/server/api/submit/reference.post.ts
Jules Neny c860dabdc0 feat(proposer): interface 5 onglets + soumission Références vers NocoDB
- Page /proposer avec 5 onglets (Écosystème/Réseau/Jobs/Outils/Références RAG)
- 5 form components + form-styles.css partagé
- Submit multi-type avec submission_type vers NocoDB
- Server route references.post.ts réécrite pour NocoDB (table ressources_references)
- redirect 301 /contribuer → /proposer
- Suppression references-pending.json
2026-06-23 12:13:33 +02:00

75 lines
2.8 KiB
TypeScript

import { z } from 'zod'
import { checkRateLimit } from '~/server/utils/rateLimit'
const ReferenceSchema = z.object({
titre: z.string().min(3, 'Minimum 3 caractères').max(200, 'Maximum 200 caractères').trim(),
auteur: z.string().min(2, 'Minimum 2 caractères').max(200, 'Maximum 200 caractères').trim(),
url: z.string().url('URL invalide').optional().or(z.literal('')).transform(v => v || undefined),
description: z.string().min(50, 'Minimum 50 caractères').max(1000, 'Maximum 1000 caractères').trim(),
pertinence_rag: z.string().max(500, 'Maximum 500 caractères').optional().transform(v => v?.trim() || undefined),
hashtags: z.string().max(300, 'Maximum 300 caractères').optional().transform(v => v?.trim() || undefined),
submitted_by_email: z.string().email('Email invalide').optional().or(z.literal('')).transform(v => v || undefined),
})
export type ReferenceInput = z.infer<typeof ReferenceSchema>
export default defineEventHandler(async (event) => {
const ip = getHeader(event, 'x-forwarded-for')?.split(',')[0].trim() || event.node.req.socket?.remoteAddress || '0.0.0.0'
const allowed = await checkRateLimit(ip, 'submit-reference', 3)
if (!allowed) {
throw createError({ statusCode: 429, statusMessage: 'Trop de soumissions. Réessaie demain.' })
}
const body = await readBody(event)
const parsed = ReferenceSchema.safeParse(body)
if (!parsed.success) {
throw createError({
statusCode: 422,
statusMessage: 'Validation échouée',
data: parsed.error.flatten().fieldErrors,
})
}
const data = parsed.data
const payload: Record<string, unknown> = {
titre: data.titre,
auteur: data.auteur,
url: data.url || null,
description: data.description,
pertinence_rag: data.pertinence_rag || null,
hashtags: data.hashtags ? data.hashtags.split(',').map(h => h.trim()).filter(Boolean).join(',') : null,
submitted_by_email: data.submitted_by_email || null,
moderation_status: 'pending',
submitted_at: new Date().toISOString(),
}
const config = useRuntimeConfig()
const insertUrl = `${config.nocodbUrl}/api/v1/db/data/noco/${config.nocodbBase}/${config.referencesTableId}`
let insertedRecord: any
try {
insertedRecord = await $fetch(insertUrl, {
method: 'POST',
headers: {
'xc-token': config.nocodbToken,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
} catch (e: any) {
console.error('[reference] Erreur NocoDB insert:', e?.message ?? e)
throw createError({ statusCode: 502, statusMessage: 'Erreur serveur — réessaie dans quelques instants.' })
}
const referenceId = insertedRecord?.Id ?? insertedRecord?.id ?? null
return {
status: 201,
referenceId,
message: 'Ta référence est en attente de modération.',
}
})