feat: complete memorial website features
- Add user contribution system (memories, timeline entries) - Add AI content moderation with Ollama (bad word detection + qwen3:4b) - Add family photo/video upload with admin approval - Add candle lighting feature - Add timeline and recipe sections - Add QR code page and OG image - Add site authentication (password-protected access) - Add proxy middleware for auth routing - Add admin dashboard for content management - Remove email fields, make name optional (default: Anonym) - Add CI/CD pipeline for Gitea Actions - Add Docker deployment configuration - Optimize Ollama RAM usage (42GB → 2.9GB) - Fix API routes accessibility through proxy middleware Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+37
-37
@@ -47,49 +47,49 @@ const FOLDER_TO_TYPE: Record<string, 'photo' | 'video' | 'music'> = {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
if (!await isAdmin()) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const formData = await req.formData()
|
||||
const file = formData.get('file') as File | null
|
||||
const caption = formData.get('caption') as string | null
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: 'Keine Datei' }, { status: 400 })
|
||||
const files = formData.getAll('files') as File[]
|
||||
const singleFile = formData.get('file') as File | null
|
||||
|
||||
// Support both 'file' (single) and 'files' (multiple)
|
||||
const filesToProcess = files.length > 0 ? files : (singleFile ? [singleFile] : [])
|
||||
|
||||
if (filesToProcess.length === 0) {
|
||||
return NextResponse.json({ error: 'Keine Dateien' }, { status: 400 })
|
||||
}
|
||||
|
||||
let mimeType = file.type?.toLowerCase() || ''
|
||||
const ext = path.extname(file.name).toLowerCase()
|
||||
const uploadedFiles = []
|
||||
|
||||
if (!mimeType && (ext === '.heic' || ext === '.heif')) {
|
||||
mimeType = 'image/heic'
|
||||
for (const file of filesToProcess) {
|
||||
let mimeType = file.type?.toLowerCase() || ''
|
||||
const ext = path.extname(file.name).toLowerCase()
|
||||
|
||||
if (!mimeType && (ext === '.heic' || ext === '.heif')) {
|
||||
mimeType = 'image/heic'
|
||||
}
|
||||
|
||||
const folder = MIME_TO_FOLDER[mimeType]
|
||||
if (!folder) {
|
||||
continue // Skip unsupported files
|
||||
}
|
||||
|
||||
const filename = `${folder}/${randomUUID()}${ext || '.bin'}`
|
||||
const filePath = path.join(DATA_DIR, 'uploads', filename)
|
||||
|
||||
await mkdir(path.dirname(filePath), { recursive: true })
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
await writeFile(filePath, buffer)
|
||||
|
||||
uploadedFiles.push(filename)
|
||||
}
|
||||
|
||||
const folder = MIME_TO_FOLDER[mimeType]
|
||||
if (!folder) {
|
||||
return NextResponse.json(
|
||||
{ error: `Dateityp "${mimeType}" nicht unterstützt` },
|
||||
{ status: 400 }
|
||||
)
|
||||
if (uploadedFiles.length === 0) {
|
||||
return NextResponse.json({ error: 'Keine Dateien konnten verarbeitet werden' }, { status: 400 })
|
||||
}
|
||||
|
||||
const filename = `${folder}/${randomUUID()}${ext || '.bin'}`
|
||||
const filePath = path.join(DATA_DIR, 'uploads', filename)
|
||||
|
||||
await mkdir(path.dirname(filePath), { recursive: true })
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
await writeFile(filePath, buffer)
|
||||
|
||||
const db = getDb()
|
||||
const result = db
|
||||
.prepare(
|
||||
'INSERT INTO media (filename, original_name, type, caption) VALUES (?, ?, ?, ?)'
|
||||
)
|
||||
.run(filename, file.name, FOLDER_TO_TYPE[folder], caption || null)
|
||||
|
||||
const media = db
|
||||
.prepare('SELECT * FROM media WHERE id = ?')
|
||||
.get(result.lastInsertRowid)
|
||||
return NextResponse.json(media, { status: 201 })
|
||||
// Return array of filenames for multi-upload
|
||||
return NextResponse.json({
|
||||
filenames: uploadedFiles,
|
||||
count: uploadedFiles.length
|
||||
}, { status: 201 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user