claude/compassionate-feistel #4
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.git
|
||||||
|
node_modules
|
||||||
|
.next
|
||||||
|
.thumbnails
|
||||||
|
medialore.db
|
||||||
|
medialore.db-shm
|
||||||
|
medialore.db-wal
|
||||||
|
data/
|
||||||
|
.env*
|
||||||
|
docker-compose*.yml
|
||||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Stage 1: Install dependencies (with build tools for native modules)
|
||||||
|
FROM node:22-bookworm-slim AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
python3 make g++ \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Stage 2: Build the Next.js application
|
||||||
|
FROM node:22-bookworm-slim AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 3: Production image
|
||||||
|
FROM node:22-bookworm-slim AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install ffmpeg for video thumbnail generation
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ffmpeg \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=3000
|
||||||
|
# Bind to all interfaces so the container port is reachable
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
# Store libraries.json in /config so it can be bind-mounted as a directory.
|
||||||
|
# Mounting a directory (not a single file) ensures the atomic rename in
|
||||||
|
# writeLibraries() works — both .tmp and the target are on the same filesystem.
|
||||||
|
ENV LIBRARIES_CONFIG_PATH=/config/libraries.json
|
||||||
|
RUN mkdir -p /config
|
||||||
|
|
||||||
|
# Copy standalone Next.js output
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Copy native modules — Next.js standalone's file tracer does not follow
|
||||||
|
# .node binary files, so we copy these manually from the deps stage.
|
||||||
|
# Both are listed in serverExternalPackages and resolved at runtime via require().
|
||||||
|
COPY --from=deps /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
|
||||||
|
COPY --from=deps /app/node_modules/sharp ./node_modules/sharp
|
||||||
|
COPY --from=deps /app/node_modules/@img ./node_modules/@img
|
||||||
|
|
||||||
|
# Create thumbnail cache directory (mounted as a volume in production)
|
||||||
|
RUN mkdir -p /app/.thumbnails
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
14
config/libraries.json
Normal file
14
config/libraries.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "games",
|
||||||
|
"name": "Games",
|
||||||
|
"path": "/data/Games",
|
||||||
|
"type": "games"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "various",
|
||||||
|
"name": "various",
|
||||||
|
"path": "/data/Various Media",
|
||||||
|
"type": "mixed"
|
||||||
|
}
|
||||||
|
]
|
||||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
environment:
|
||||||
|
PORT: 3000
|
||||||
|
NODE_ENV: production
|
||||||
|
volumes:
|
||||||
|
# Runtime data — must map to /app/ since process.cwd() = /app in the container
|
||||||
|
- ./medialore.db:/app/medialore.db
|
||||||
|
- ./.thumbnails:/app/.thumbnails
|
||||||
|
# Library config — mounted as a directory so the atomic rename in the API works.
|
||||||
|
# A single-file bind-mount causes EBUSY on rename() because .tmp and the target
|
||||||
|
# end up on different devices. Initialize before first run:
|
||||||
|
# mkdir -p config && echo '[]' > config/libraries.json
|
||||||
|
- ./config:/config
|
||||||
|
|
||||||
|
# Mount your media folders and reference them in libraries.json using
|
||||||
|
# absolute /data/ paths, e.g. { "path": "/data/Games" }
|
||||||
|
- ./data:/data:ro
|
||||||
|
# - /path/to/your/movies:/data/Movies:ro
|
||||||
|
restart: unless-stopped
|
||||||
@@ -4,11 +4,5 @@
|
|||||||
"name": "Games",
|
"name": "Games",
|
||||||
"path": "./data/Games",
|
"path": "./data/Games",
|
||||||
"type": "games"
|
"type": "games"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "various",
|
|
||||||
"name": "Various",
|
|
||||||
"path": "./data/Various Media",
|
|
||||||
"type": "mixed"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { NextConfig } from 'next'
|
import type { NextConfig } from 'next'
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
serverExternalPackages: ['better-sqlite3', 'sharp'],
|
serverExternalPackages: ['better-sqlite3', 'sharp'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
import { redirect } from 'next/navigation'
|
import { redirect } from 'next/navigation'
|
||||||
import { getLibraries } from '@/lib/libraries'
|
import { getLibraries } from '@/lib/libraries'
|
||||||
import LibraryCard from '@/components/LibraryCard'
|
import LibraryCard from '@/components/LibraryCard'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import path from 'path'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import type { Library, LibraryType } from '@/types'
|
import type { Library, LibraryType } from '@/types'
|
||||||
|
|
||||||
const CONFIG_PATH = path.resolve(process.cwd(), 'libraries.json')
|
const CONFIG_PATH = process.env.LIBRARIES_CONFIG_PATH ?? path.resolve(process.cwd(), 'libraries.json')
|
||||||
const CONFIG_TMP = CONFIG_PATH + '.tmp'
|
const CONFIG_TMP = CONFIG_PATH + '.tmp'
|
||||||
|
|
||||||
export function getLibraries(): Library[] {
|
export function getLibraries(): Library[] {
|
||||||
|
|||||||
Reference in New Issue
Block a user