fix: photo display, duplicate detection, memory photos
- Remove duplicate FamilyUploadSection from public page - Remove 'Von Anonym' caption from user-uploaded gallery photos - Add SHA-256 duplicate detection in upload route (same file → same path) - Fix timeline photos: use object-contain instead of object-cover (no clipping) - Fix timeline modal photos: remove fixed h-48 height - Add photo display support to MemorySection component - Include media_filenames in memory contribution queries - Add media_filenames to Memory type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { writeFile, mkdir } from 'fs/promises'
|
||||
import { writeFile, mkdir, readdir, readFile } from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { cookies } from 'next/headers'
|
||||
import { createHash, randomUUID } from 'crypto'
|
||||
@@ -19,6 +19,35 @@ async function isAdmin() {
|
||||
|
||||
const DATA_DIR = path.resolve(process.cwd(), process.env.DATA_DIR || 'data')
|
||||
|
||||
// In-memory hash cache (populated on first use)
|
||||
let hashCache: Map<string, string> | null = null
|
||||
|
||||
async function getFileHash(buffer: Buffer): Promise<string> {
|
||||
return createHash('sha256').update(buffer).digest('hex')
|
||||
}
|
||||
|
||||
async function buildHashCache(folder: string): Promise<void> {
|
||||
if (hashCache) return
|
||||
hashCache = new Map()
|
||||
const uploadsDir = path.join(DATA_DIR, 'uploads', folder)
|
||||
try {
|
||||
const files = await readdir(uploadsDir)
|
||||
for (const file of files) {
|
||||
try {
|
||||
const content = await readFile(path.join(uploadsDir, file))
|
||||
const hash = await getFileHash(content)
|
||||
hashCache.set(hash, `${folder}/${file}`)
|
||||
} catch {}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function findDuplicate(buffer: Buffer, folder: string): Promise<string | null> {
|
||||
await buildHashCache(folder)
|
||||
const hash = await getFileHash(buffer)
|
||||
return hashCache?.get(hash) || null
|
||||
}
|
||||
|
||||
const MIME_TO_FOLDER: Record<string, string> = {
|
||||
'image/jpeg': 'photos',
|
||||
'image/jpg': 'photos',
|
||||
@@ -75,11 +104,22 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
const filename = `${folder}/${randomUUID()}${ext || '.bin'}`
|
||||
const filePath = path.join(DATA_DIR, 'uploads', filename)
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
|
||||
// Check for duplicate
|
||||
const existingFile = await findDuplicate(buffer, folder)
|
||||
if (existingFile) {
|
||||
uploadedFiles.push(existingFile)
|
||||
continue
|
||||
}
|
||||
|
||||
await mkdir(path.dirname(filePath), { recursive: true })
|
||||
const buffer = Buffer.from(await file.arrayBuffer())
|
||||
await writeFile(filePath, buffer)
|
||||
|
||||
// Add to hash cache
|
||||
const hash = await getFileHash(buffer)
|
||||
hashCache?.set(hash, filename)
|
||||
|
||||
uploadedFiles.push(filename)
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -41,7 +41,7 @@ export default async function HomePage() {
|
||||
try {
|
||||
userMemories = plain(
|
||||
db.prepare(`
|
||||
SELECT id, name, title, content, created_at
|
||||
SELECT id, name, title, content, media_filenames, created_at
|
||||
FROM contributions
|
||||
WHERE status = 'approved' AND type = 'memory'
|
||||
ORDER BY created_at DESC
|
||||
@@ -58,6 +58,8 @@ export default async function HomePage() {
|
||||
id: m.id,
|
||||
title: m.title || 'Erinnerung',
|
||||
content: m.content,
|
||||
author: m.name || null,
|
||||
media_filenames: m.media_filenames || null,
|
||||
created_at: m.created_at,
|
||||
updated_at: m.created_at,
|
||||
}))
|
||||
@@ -144,7 +146,7 @@ export default async function HomePage() {
|
||||
filename: filename.trim(),
|
||||
original_name: null,
|
||||
type: 'photo' as const,
|
||||
caption: `Von ${c.name || 'Anonym'}`,
|
||||
caption: null,
|
||||
sort_order: 9998,
|
||||
status: 'approved' as const,
|
||||
created_at: c.created_at,
|
||||
|
||||
Reference in New Issue
Block a user