fix: auto-approve clean content, fix AI moderation, show all contributions
- Auto-approve: clean submissions now default to 'approved' (not 'pending') - Switch AI model from qwen3:4b to llama3.2 (qwen3 writes to thinking field, response always empty) - AI now correctly flags spam, nonsense, and irrelevant content - Admin: show all contribution types including timeline (was filtering them out) - Add FamilyUploadSection to public page (was imported but never rendered) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1401,8 +1401,8 @@ export default function AdminPage() {
|
||||
<p className="text-warm-brown-light text-sm italic font-lora">Keine Beiträge.</p>
|
||||
) : (
|
||||
(() => {
|
||||
// Filter contributions - exclude timeline type (those go to timeline section)
|
||||
let filtered = timelineContributions.filter(c => c.type !== 'timeline')
|
||||
// Show all contribution types
|
||||
let filtered = [...timelineContributions]
|
||||
if (contributionFilter === 'review') {
|
||||
filtered = filtered.filter(c => c.status === 'pending' || c.status === 'flagged')
|
||||
} else if (contributionFilter !== 'all') {
|
||||
|
||||
@@ -38,46 +38,45 @@ async function moderateWithAI(contributionId: number, content: string) {
|
||||
if (foundBadWord) {
|
||||
console.log(`[AI-Mod] ⚠️ INSTANT FLAG: "${foundBadWord}" detected!`)
|
||||
const db = getDb()
|
||||
const flagResult = db.prepare(`
|
||||
db.prepare(`
|
||||
UPDATE contributions
|
||||
SET status = 'flagged', moderation_reason = ?
|
||||
WHERE id = ?
|
||||
`).run(`Unangemessene Sprache: "${foundBadWord}"`, contributionId)
|
||||
console.log(`[AI-Mod] ✅ FLAGGED ${contributionId} instantly:`, flagResult.changes, 'rows')
|
||||
return // Done, no AI needed
|
||||
console.log(`[AI-Mod] ✅ FLAGGED ${contributionId} instantly`)
|
||||
return
|
||||
}
|
||||
|
||||
// Step 2: AI check for subtle issues
|
||||
// Step 2: AI check for subtle issues (irrelevant content, hidden insults etc.)
|
||||
console.log(`[AI-Mod] No bad words, asking AI...`)
|
||||
|
||||
try {
|
||||
const prompt = `Ist dieser Text unangemessen für eine Gedenkseite?
|
||||
const prompt = `Ist dieser Text angemessen für eine Gedenkseite einer verstorbenen Großmutter?
|
||||
|
||||
"${content}"
|
||||
|
||||
ERLAUBT: Liebe, Vermissen, Trauer
|
||||
VERBOTEN: Beleidigungen, Spam, Hassrede
|
||||
|
||||
Antworte NUR mit JSON (keine Erklärung):
|
||||
{"appropriate": true} oder {"appropriate": false, "reason": "..."}
|
||||
ERLAUBT: Liebe, Vermissen, Trauer, Erinnerungen, persönliche Geschichten, Beileidsbekundungen
|
||||
VERBOTEN: Beleidigungen, Spam, Hassrede, Werbung, völlig zusammenhanglose oder sinnlose Texte ohne Bezug
|
||||
|
||||
Antworte NUR mit JSON:
|
||||
{"appropriate": true} oder {"appropriate": false, "reason": "kurze Begründung"}
|
||||
JSON:`
|
||||
|
||||
const controller = new AbortController()
|
||||
setTimeout(() => controller.abort(), 10000) // 10sec timeout
|
||||
setTimeout(() => controller.abort(), 15000)
|
||||
|
||||
const res = await fetch('http://localhost:11434/api/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({
|
||||
model: 'qwen3:4b',
|
||||
model: 'llama3.2:latest',
|
||||
prompt,
|
||||
stream: false,
|
||||
options: {
|
||||
temperature: 0.1,
|
||||
num_predict: 50,
|
||||
num_ctx: 256
|
||||
num_predict: 60,
|
||||
num_ctx: 512
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -102,22 +101,25 @@ JSON:`
|
||||
console.error(`[AI-Mod] Parse error:`, e)
|
||||
}
|
||||
|
||||
// Update DB if inappropriate
|
||||
// Flag if inappropriate
|
||||
if (result && result.appropriate === false) {
|
||||
const db = getDb()
|
||||
const updateResult = db.prepare(`
|
||||
db.prepare(`
|
||||
UPDATE contributions
|
||||
SET status = 'flagged', moderation_reason = ?
|
||||
WHERE id = ?
|
||||
`).run(result.reason || 'KI-Warnung', contributionId)
|
||||
|
||||
console.log(`[AI-Mod] ✅ FLAGGED ${contributionId}:`, updateResult)
|
||||
console.log(`[AI-Mod] ⚠️ FLAGGED ${contributionId}: ${result.reason}`)
|
||||
} else {
|
||||
console.log(`[AI-Mod] ✅ Passed ${contributionId}`)
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`[AI-Mod] Error for ${contributionId}:`, error.message)
|
||||
if (error.name === 'AbortError') {
|
||||
console.warn(`[AI-Mod] Timeout for ${contributionId}`)
|
||||
} else {
|
||||
console.error(`[AI-Mod] Error for ${contributionId}:`, error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +137,9 @@ export async function POST(request: Request) {
|
||||
|
||||
const db = getDb()
|
||||
|
||||
// 1. Check bad words instantly
|
||||
// 1. Check bad words instantly - clean content is auto-approved
|
||||
const badWordCheck = hasBadWords(content + ' ' + (title || ''))
|
||||
const initialStatus = badWordCheck.flag ? 'flagged' : 'pending'
|
||||
const initialStatus = badWordCheck.flag ? 'flagged' : 'approved'
|
||||
const moderationReason = badWordCheck.flag ? badWordCheck.reason : null
|
||||
|
||||
// 2. Insert contribution
|
||||
|
||||
@@ -218,6 +218,9 @@ export default async function HomePage() {
|
||||
{/* Photo Upload */}
|
||||
<PhotoUploadSection />
|
||||
|
||||
{/* Family Upload */}
|
||||
<FamilyUploadSection />
|
||||
|
||||
{/* Memories */}
|
||||
<section id="erinnerungen">
|
||||
<MemorySection memories={combinedMemories} />
|
||||
|
||||
Reference in New Issue
Block a user