feat: publication pages — design moderne + modération IA + signalement
Page publique (/p/[slug]): - Design éditorial moderne (Source Serif 4 + Inter, couleurs Momento) - KaTeX rendu côté serveur (plus de 3184089 en brut) - Callouts colorés, toggles, colonnes, code blocks rendus correctement - Barre sticky avec logo + bouton Signaler - Temps de lecture estimé - Footer Momento - Responsive Modération: - content-moderation.service.ts — IA classe safe/flagged/blocked - Sera appelée automatiquement à la publication Signalement: - Page /p/[slug]/report — formulaire de signalement public - API /api/notes/report — stocke notification au propriétaire + admins - 8 motifs: illegal, haine, violence, sexuel, harcèlement, copyright, spam, autre i18n: FR/EN
This commit is contained in:
52
memento-note/lib/ai/services/content-moderation.service.ts
Normal file
52
memento-note/lib/ai/services/content-moderation.service.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { getChatProvider } from '../factory'
|
||||
import { getSystemConfig } from '@/lib/config'
|
||||
|
||||
export type ModerationVerdict = 'safe' | 'flagged' | 'blocked'
|
||||
|
||||
export interface ModerationResult {
|
||||
verdict: ModerationVerdict
|
||||
categories: string[]
|
||||
reason: string
|
||||
}
|
||||
|
||||
export class ContentModerationService {
|
||||
async moderate(title: string, content: string): Promise<ModerationResult> {
|
||||
const plainText = content.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 3000)
|
||||
const fullText = `${title}\n\n${plainText}`
|
||||
|
||||
const prompt = `Tu es un modérateur de contenu. Analyse ce texte et classe-le.
|
||||
|
||||
RÈGLES STRICTES :
|
||||
- "blocked" : contenu illégal (pédopornographie, apologie du terrorisme, incitation à la haine violente, menaces crédibles, fraude organisée)
|
||||
- "flagged" : contenu sensible mais légal (violence graphique, drogues, contenu sexuel explicite, discours haineux non violent, désinformation dangereuse)
|
||||
- "safe" : contenu normal (éducation, travail, personnel, technique)
|
||||
|
||||
CATÉGORIES possibles : violence, hate, sexual, harassment, self-harm, illegal, drugs, spam, safe
|
||||
|
||||
TEXTE À ANALYSER :
|
||||
${fullText}
|
||||
|
||||
FORMAT JSON UNIQUEMENT :
|
||||
{"verdict": "safe|flagged|blocked", "categories": ["category1"], "reason": "Explication courte en français"}`
|
||||
|
||||
try {
|
||||
const config = await getSystemConfig()
|
||||
const provider = getChatProvider(config)
|
||||
const raw = await provider.generateText(prompt)
|
||||
|
||||
const jsonMatch = raw.match(/\{[\s\S]+\}/)
|
||||
if (!jsonMatch) return { verdict: 'safe', categories: ['safe'], reason: 'Analysis failed' }
|
||||
|
||||
const parsed = JSON.parse(jsonMatch[0])
|
||||
return {
|
||||
verdict: parsed.verdict === 'blocked' ? 'blocked' : parsed.verdict === 'flagged' ? 'flagged' : 'safe',
|
||||
categories: Array.isArray(parsed.categories) ? parsed.categories : ['safe'],
|
||||
reason: String(parsed.reason || ''),
|
||||
}
|
||||
} catch {
|
||||
return { verdict: 'safe', categories: ['safe'], reason: 'Moderation unavailable' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const contentModerationService = new ContentModerationService()
|
||||
Reference in New Issue
Block a user