From 1994ed5810aa0111afee5e907718de883950b5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= Date: Sat, 18 Oct 2025 20:05:11 +0200 Subject: [PATCH] feat: update file upload paths and add dynamic file retrieval endpoint --- .gitignore | 1 + Dockerfile | 2 +- app/api/upload/route.ts | 4 ++-- app/api/uploads/[filename]/route.ts | 31 +++++++++++++++++++++++++++++ docker-compose.yml | 2 +- package.json | 1 + pnpm-lock.yaml | 17 ++++++++++++++++ 7 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 app/api/uploads/[filename]/route.ts diff --git a/.gitignore b/.gitignore index af2c33d..14fac98 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ prisma/dev.db uploads/ uploads/* +!app/api/uploads data/ data/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 972bff9..7da9885 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,4 +9,4 @@ ENV NEXTAUTH_SECRET=your_super_secret_key_here_change_in_production RUN npx prisma generate RUN pnpm run build EXPOSE 3000 -CMD ["sh", "-c", "mkdir -p /app/prisma/data && npx prisma db push && pnpm start"] +CMD ["sh", "-c", "mkdir -p /app/prisma/data && mkdir -p /app/uploads && npx prisma db push && pnpm start"] diff --git a/app/api/upload/route.ts b/app/api/upload/route.ts index 3b5adbc..199c6b7 100644 --- a/app/api/upload/route.ts +++ b/app/api/upload/route.ts @@ -27,10 +27,10 @@ export async function POST(request: NextRequest) { const buffer = Buffer.from(bytes); const filename = `${Date.now()}-${file.name}`; - const filepath = path.join(process.cwd(), 'public', 'uploads', filename); + const filepath = path.join(process.cwd(), 'uploads', filename); await mkdir(path.dirname(filepath), { recursive: true }); await writeFile(filepath, buffer); - return NextResponse.json({ path: `/uploads/${filename}` }); + return NextResponse.json({ path: `/api/uploads/${filename}` }); } diff --git a/app/api/uploads/[filename]/route.ts b/app/api/uploads/[filename]/route.ts new file mode 100644 index 0000000..03e292e --- /dev/null +++ b/app/api/uploads/[filename]/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { readFile } from 'fs/promises'; +import path from 'path'; +import mime from 'mime-types'; + +export const dynamic = 'force-dynamic'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ filename: string }> }, +) { + const { filename } = await params; + + const filepath = path.join(process.cwd(), 'uploads', filename); + + try { + const file = await readFile(filepath); + const mimeType = mime.lookup(filename) || 'application/octet-stream'; + + const uint8Array = new Uint8Array(file); + + return new NextResponse(uint8Array as any, { + headers: { + 'Content-Type': mimeType, + 'Cache-Control': 'public, max-age=31536000', + }, + }); + } catch (error) { + return NextResponse.json({ error: 'File not found' }, { status: 404 }); + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 8bf5fd8..c04e032 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: - '3000:3000' volumes: - ./data:/app/prisma/data - - ./uploads:/app/public/uploads + - ./uploads:/app/uploads environment: - NEXTAUTH_SECRET=your_secret_key - NEXTAUTH_URL=http://localhost:3000 diff --git a/package.json b/package.json index 242be12..f092d91 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "date-fns": "^4.1.0", "exceljs": "^4.4.0", "lucide-react": "^0.546.0", + "mime-types": "^3.0.1", "next": "15.5.6", "next-auth": "^4.24.11", "next-themes": "^0.4.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 769704d..aaf97a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: lucide-react: specifier: ^0.546.0 version: 0.546.0(react@19.1.0) + mime-types: + specifier: ^3.0.1 + version: 3.0.1 next: specifier: 15.5.6 version: 15.5.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1518,10 +1521,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3465,10 +3476,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} minimatch@3.1.2: