fix: serve uploaded images via API route (public/ is read-only in production)
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 42s

Next.js bakes public/ at build time — dynamically uploaded files were
never served in Docker standalone mode. Store uploads in data/uploads/
and serve via /api/uploads/ with a rewrite for backward compatibility.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 22:56:22 +02:00
parent ae89f8a014
commit 0fbb8aa599
8 changed files with 81 additions and 16 deletions

View File

@@ -10,7 +10,7 @@ import path from 'path'
import { randomUUID } from 'crypto'
import sharp from 'sharp'
const UPLOADS_DIR = 'public/uploads/notes'
const UPLOADS_DIR = 'data/uploads/notes'
const URL_PREFIX = '/uploads/notes'
const MAX_IMAGES_PER_PAGE = 3
const MIN_IMAGE_SIZE = 200 // px -- skip icons, spacers, tracking pixels

View File

@@ -8,7 +8,7 @@ import { promises as fs } from 'fs'
import path from 'path'
import { prisma } from '@/lib/prisma'
const UPLOADS_DIR = 'public/uploads/notes'
const UPLOADS_DIR = 'data/uploads/notes'
/**
* Delete an image file from disk only if no other note references it.
@@ -26,7 +26,7 @@ export async function deleteImageFileSafely(imageUrl: string, excludeNoteId?: st
const otherRefs = notes.filter(n => n.id !== excludeNoteId)
if (otherRefs.length > 0) return // File still referenced elsewhere
const filePath = path.join(process.cwd(), imageUrl)
const filePath = path.join(process.cwd(), 'data', imageUrl)
await fs.unlink(filePath)
} catch {
// File already gone or unreadable -- silently skip