From 9ca11a8c6db2907548adfd8f1ac1773cae71f324 Mon Sep 17 00:00:00 2001 From: tigeren Date: Sat, 30 Aug 2025 19:39:51 +0000 Subject: [PATCH] feat: add thumbnail API and update Docker configuration - Implemented a new API route for serving thumbnails, allowing dynamic retrieval of image files. - Updated Dockerfile to install FFmpeg for media processing. - Enhanced docker-compose.yml to mount the thumbnails directory for easier access. - Added public/thumbnails to .dockerignore and .gitignore to prevent unnecessary file inclusion. - Created a new media database file for managing media assets. --- .dockerignore | 2 ++ .gitignore | 2 ++ Dockerfile | 7 +++-- data/media.db | Bin 0 -> 122880 bytes docker/data/media.db | Bin 118784 -> 122880 bytes docker/docker-compose.yml | 1 + next.config.ts | 8 +++++ src/app/api/thumbnails/[filename]/route.ts | 34 +++++++++++++++++++++ 8 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 data/media.db create mode 100644 src/app/api/thumbnails/[filename]/route.ts diff --git a/.dockerignore b/.dockerignore index db5a59e..5621cea 100644 --- a/.dockerignore +++ b/.dockerignore @@ -64,3 +64,5 @@ coverage # Temporary folders tmp temp + +public/thumbnails \ No newline at end of file diff --git a/.gitignore b/.gitignore index df36e21..a63b2a6 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ yarn-error.log* next-env.d.ts public/thumbnails +docker/public/thumbnails + screenshots \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 88f208a..1946fc2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,7 +44,10 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 -# No additional packages needed for production +# Install FFmpeg for media file analysis +RUN apt-get update && apt-get install -y \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* RUN groupadd --system --gid 1001 nodejs RUN useradd --system --uid 1001 --gid nodejs nextjs @@ -56,7 +59,7 @@ RUN mkdir -p /app/data /app/media RUN chown -R nextjs:nodejs /app/data /app/media # Copy built application -COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Copy node_modules to ensure native bindings are available diff --git a/data/media.db b/data/media.db new file mode 100644 index 0000000000000000000000000000000000000000..3c45987f04646f30c010d81e183f8b2530bafae5 GIT binary patch literal 122880 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 Y(-(^FJvA-`yr1U8H6bxpzQQE_e-uNnvj6}9 literal 0 HcmV?d00001 diff --git a/docker/data/media.db b/docker/data/media.db index 647789c352b7a9e1362465914b110acf0d0e2f65..45e8f6637475675101b110b03be7a9ff064b1261 100644 GIT binary patch delta 12924 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 BT^;}c diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 21ef6d5..4c0324f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -10,6 +10,7 @@ services: volumes: - ${DB_PATH:-./data}:/app/data - ${MEDIA_PATH:-./media}:/app/media + - ${THUMBNAILS_PATH:-./public/thumbnails}:/app/public/thumbnails - /mnt/data1:/mnt/data1 command: node server.js environment: diff --git a/next.config.ts b/next.config.ts index 225e495..41cbf2e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,14 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: 'standalone', + async rewrites() { + return [ + { + source: '/thumbnails/:filename*', + destination: '/api/thumbnails/:filename*', + }, + ]; + }, }; export default nextConfig; diff --git a/src/app/api/thumbnails/[filename]/route.ts b/src/app/api/thumbnails/[filename]/route.ts new file mode 100644 index 0000000..e7bde96 --- /dev/null +++ b/src/app/api/thumbnails/[filename]/route.ts @@ -0,0 +1,34 @@ +import { NextRequest, NextResponse } from 'next/server'; +import fs from 'fs'; +import path from 'path'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ filename: string }> } +) { + try { + const { filename } = await params; + + // Construct the path to the thumbnail file + const thumbnailPath = path.join(process.cwd(), 'public', 'thumbnails', filename); + + // Check if the file exists + if (!fs.existsSync(thumbnailPath)) { + return NextResponse.json({ error: 'Thumbnail not found' }, { status: 404 }); + } + + // Read the file + const fileBuffer = fs.readFileSync(thumbnailPath); + + // Return the image with appropriate headers + return new NextResponse(fileBuffer, { + headers: { + 'Content-Type': 'image/png', + 'Cache-Control': 'public, max-age=31536000, immutable', + }, + }); + } catch (error) { + console.error('Error serving thumbnail:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +}