From 50689c46e5d665c73d88cc8318fe45c91a1de11d Mon Sep 17 00:00:00 2001 From: grabowski Date: Thu, 9 Apr 2026 14:08:00 +0700 Subject: [PATCH] Fix image upload 500 in production: use UPLOAD_DIR env for absolute path In production with adapter-node, the relative path 'static/uploads' doesn't resolve correctly. Now uses UPLOAD_DIR env variable (absolute path) for both saving and serving uploaded files. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/server/uploads.ts | 10 ++++++---- src/routes/uploads/[...path]/+server.ts | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/server/uploads.ts b/src/lib/server/uploads.ts index 5775b91..0acb16e 100644 --- a/src/lib/server/uploads.ts +++ b/src/lib/server/uploads.ts @@ -1,9 +1,10 @@ import { randomUUID } from 'crypto'; import { writeFile, unlink, mkdir } from 'fs/promises'; -import { join, extname } from 'path'; +import { join, extname, resolve } from 'path'; import sharp from 'sharp'; +import { env } from '$env/dynamic/private'; -const UPLOAD_BASE = 'static/uploads'; +const UPLOAD_BASE = resolve(env.UPLOAD_DIR || 'static/uploads'); const THUMBNAIL_WIDTH = 300; const ALLOWED_IMAGE_TYPES = [ @@ -67,8 +68,9 @@ export async function saveDocument(file: File): Promise<{ filePath: string; orig export async function deleteFile(filePath: string): Promise { try { - // filePath is like /uploads/devices/xxx.jpg, need to prepend static/ - const fullPath = join('static', filePath); + // filePath is like /uploads/devices/xxx.jpg — strip /uploads/ prefix and join with UPLOAD_BASE + const relativePath = filePath.replace(/^\/uploads\//, ''); + const fullPath = join(UPLOAD_BASE, relativePath); await unlink(fullPath); } catch { // File may already be deleted diff --git a/src/routes/uploads/[...path]/+server.ts b/src/routes/uploads/[...path]/+server.ts index 01a4062..77b8ae0 100644 --- a/src/routes/uploads/[...path]/+server.ts +++ b/src/routes/uploads/[...path]/+server.ts @@ -1,7 +1,8 @@ import type { RequestHandler } from './$types'; import { readFile } from 'fs/promises'; -import { join, extname } from 'path'; +import { join, extname, resolve } from 'path'; import { error } from '@sveltejs/kit'; +import { env } from '$env/dynamic/private'; const MIME_TYPES: Record = { '.jpg': 'image/jpeg', @@ -21,7 +22,8 @@ export const GET: RequestHandler = async ({ params }) => { // Prevent directory traversal if (filePath.includes('..')) error(400, 'Invalid path'); - const fullPath = join('static', 'uploads', filePath); + const uploadBase = resolve(env.UPLOAD_DIR || 'static/uploads'); + const fullPath = join(uploadBase, filePath); try { const data = await readFile(fullPath);