diff --git a/src/app/api/contributions/route.ts b/src/app/api/contributions/route.ts index 4df1491..d4e0df6 100644 --- a/src/app/api/contributions/route.ts +++ b/src/app/api/contributions/route.ts @@ -126,11 +126,25 @@ JSON:` export async function POST(request: Request) { try { const body = await request.json() - const { name, type, title, content, photoUrl, date, category } = body + const { name, type, title, content, year, month, day, location, media_filenames } = body - if (!content || !type) { + if (!type) { return NextResponse.json( - { error: 'Content and type are required' }, + { error: 'Type is required' }, + { status: 400 } + ) + } + + // Require content for memory type, title for timeline + if (type === 'memory' && !content) { + return NextResponse.json( + { error: 'Content is required for memories' }, + { status: 400 } + ) + } + if (type === 'timeline' && !title) { + return NextResponse.json( + { error: 'Title is required for timeline entries' }, { status: 400 } ) } @@ -138,30 +152,35 @@ export async function POST(request: Request) { const db = getDb() // 1. Check bad words instantly - clean content is auto-approved - const badWordCheck = hasBadWords(content + ' ' + (title || '')) + const textToCheck = [content, title].filter(Boolean).join(' ') + const badWordCheck = textToCheck ? hasBadWords(textToCheck) : { flag: false } const initialStatus = badWordCheck.flag ? 'flagged' : 'approved' - const moderationReason = badWordCheck.flag ? badWordCheck.reason : null + const moderationReason = badWordCheck.flag ? (badWordCheck as any).reason : null - // 2. Insert contribution + // 2. Insert contribution with all fields const result = db.prepare(` - INSERT INTO contributions (name, type, title, content, status, moderation_reason) - VALUES (?, ?, ?, ?, ?, ?) + INSERT INTO contributions (name, type, title, content, year, month, day, location, media_filenames, status, moderation_reason) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( name || 'Anonym', type, title || null, - content, + content || null, + year || null, + month || null, + day || null, + location || null, + media_filenames || null, initialStatus, moderationReason || null ) const contributionId = Number(result.lastInsertRowid) - console.log(`[API] Created contribution ${contributionId}, status: ${initialStatus}`) + console.log(`[API] Created contribution ${contributionId}, type: ${type}, status: ${initialStatus}`) - // 3. If not already flagged, run AI check in background - if (!badWordCheck.flag) { - // Fire and forget - don't await - moderateWithAI(contributionId, content).catch(e => + // 3. If not already flagged and has text, run AI check in background + if (!badWordCheck.flag && textToCheck) { + moderateWithAI(contributionId, textToCheck).catch(e => console.error('[AI-Mod] Background error:', e) ) } @@ -169,7 +188,7 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, id: contributionId, - message: 'Beitrag wurde gespeichert und wird geprüft' + message: 'Beitrag wurde gespeichert' }) } catch (error) { diff --git a/src/app/page.tsx b/src/app/page.tsx index beeedf0..75e8a18 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,7 +11,6 @@ import TimelineSection from '@/components/TimelineSection' import TimelineUploadSection from '@/components/TimelineUploadSection' import MemoryUploadSection from '@/components/MemoryUploadSection' import PhotoUploadSection from '@/components/PhotoUploadSection' -import FamilyUploadSection from '@/components/FamilyUploadSection' import RecipeSection from '@/components/RecipeSection' import RecipeUploadSection from '@/components/RecipeUploadSection' @@ -123,7 +122,7 @@ export default async function HomePage() { // Create virtual MediaItem entries for timeline photos const timelinePhotos: MediaItem[] = Array.from(timelinePhotoFilenames).map((filename, i) => ({ - id: 999000 + i, // High ID to avoid conflicts + id: 999000 + i, filename, original_name: null, type: 'photo' as const, @@ -133,8 +132,28 @@ export default async function HomePage() { created_at: new Date().toISOString(), })) - // Merge with existing photos - const allPhotos = [...photos, ...timelinePhotos] + // Fetch approved media contributions (user-uploaded photos) + let mediaContribPhotos: MediaItem[] = [] + try { + const mediaContribs = plain( + db.prepare("SELECT id, media_filenames, name, created_at FROM contributions WHERE status = 'approved' AND media_filenames IS NOT NULL AND media_filenames != ''").all() + ) + mediaContribPhotos = mediaContribs.flatMap((c: any) => + c.media_filenames.split(',').filter(Boolean).map((filename: string, i: number) => ({ + id: 998000 + c.id * 10 + i, + filename: filename.trim(), + original_name: null, + type: 'photo' as const, + caption: `Von ${c.name || 'Anonym'}`, + sort_order: 9998, + status: 'approved' as const, + created_at: c.created_at, + })) + ) + } catch {} + + // Merge all photos + const allPhotos = [...photos, ...timelinePhotos, ...mediaContribPhotos] const recipes = plain( db.prepare('SELECT * FROM recipes ORDER BY sort_order, title').all() @@ -218,9 +237,6 @@ export default async function HomePage() { {/* Photo Upload */} - {/* Family Upload */} - - {/* Memories */}
diff --git a/src/components/PhotoUploadSection.tsx b/src/components/PhotoUploadSection.tsx index 38e87fc..bc00481 100644 --- a/src/components/PhotoUploadSection.tsx +++ b/src/components/PhotoUploadSection.tsx @@ -41,6 +41,7 @@ export default function PhotoUploadSection() { body: JSON.stringify({ name: 'Anonym', type: 'media', + title: 'Foto-Upload', media_filenames: uploadedFilenames.join(','), }), })