- 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
75 lines
2.8 KiB
TypeScript
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.',
|
|
}
|
|
})
|