From 4cf94bb56ccef6477c40bad676a2f9582e30d7ba Mon Sep 17 00:00:00 2001 From: tsshadow Date: Fri, 12 Sep 2025 19:44:45 +0200 Subject: [PATCH 1/5] chore: optimize production docker image --- Dockerfile | 56 ++++++++++++++++++++++++++++++++------------------- api/server.py | 4 ++++ entrypoint.sh | 15 ++------------ 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1a7decf..efba019 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,14 @@ # syntax=docker/dockerfile:1 -FROM python:3.13-slim-trixie -# --- Base tooling + Node + pnpm (unchanged, but without apt ffmpeg) --- +# ----------------------------------------------------- +# Build stage: install tooling, dependencies and build +# ----------------------------------------------------- +FROM python:3.13-slim-trixie AS builder + +# Base tooling + Node + pnpm + build deps RUN apt-get update && apt-get install -y \ gcc wget curl gnupg bash git ca-certificates \ unzip p7zip-full \ - # build deps for ffmpeg from source build-essential yasm nasm pkg-config \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y nodejs \ @@ -14,44 +17,55 @@ RUN apt-get update && apt-get install -y \ WORKDIR /app -# --- Build and install FFmpeg from source --- -# Pin to a release for reproducibility; change version if you need newer. +# Build and install FFmpeg from source ARG FFMPEG_VERSION=6.1.2 RUN set -eux; \ curl -L "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz" -o /tmp/ffmpeg.tar.xz; \ tar -xJf /tmp/ffmpeg.tar.xz -C /tmp; \ cd "/tmp/ffmpeg-${FFMPEG_VERSION}"; \ - # Minimal config: no docs, no ffplay; native AAC encoder is included by default ./configure --prefix=/usr/local --disable-debug --disable-doc --disable-ffplay; \ make -j"$(nproc)"; \ make install; \ - # Make sure binaries are trivially discoverable - ln -sf /usr/local/bin/ffmpeg /usr/bin/ffmpeg; \ - ln -sf /usr/local/bin/ffprobe /usr/bin/ffprobe; \ - # Clean build artifacts - rm -rf /tmp/ffmpeg*; + rm -rf /tmp/ffmpeg* -# Python deps first for better caching +# Python dependencies COPY requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt -# Frontend deps (cache pnpm install) +# Frontend dependencies and build COPY frontend/package.json frontend/pnpm-lock.yaml frontend/ -RUN cd frontend && pnpm install +RUN cd frontend && pnpm install && pnpm build + +# Copy application source +COPY . . + +# Copy default env if present +RUN cp .env.linux .env || true && rm -f .env.linux + -# App source +# ----------------------------------------------------- +# Final stage: minimal runtime image +# ----------------------------------------------------- +FROM python:3.13-slim-trixie + +WORKDIR /app + +# Bring in Python deps and FFmpeg +COPY --from=builder /usr/local /usr/local + +# Application source (node_modules are ignored via .dockerignore) COPY . . -# Optional env copy (your original step) +# Include built frontend +COPY --from=builder /app/frontend/dist frontend/dist + +# Copy default env again for runtime RUN cp .env.linux .env || true && rm -f .env.linux -# Vite dev server env -ENV VITE_API_BASE=/api -EXPOSE 5173 +EXPOSE 8001 -# Entrypoint COPY entrypoint.sh ./entrypoint.sh RUN chmod +x entrypoint.sh -# Use bash so wait -n works if you rely on it CMD ["bash", "./entrypoint.sh"] + diff --git a/api/server.py b/api/server.py index b904992..02bc82f 100644 --- a/api/server.py +++ b/api/server.py @@ -17,6 +17,7 @@ ) from fastapi.middleware.cors import CORSMiddleware from fastapi.concurrency import run_in_threadpool +from fastapi.staticfiles import StaticFiles from .steps import step_map from .db_init import ensure_tables_exist @@ -293,3 +294,6 @@ async def jobs_ws(ws: WebSocket): pass finally: clients.discard(ws) + +# Serve the pre-built frontend as static files +app.mount("/", StaticFiles(directory="frontend/dist", html=True), name="frontend") diff --git a/entrypoint.sh b/entrypoint.sh index c1a53f5..59b3a55 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,16 +2,5 @@ set -e export PATH="/usr/local/bin:/usr/bin:/bin:${PATH}" command -v ffmpeg || echo "ffmpeg missing from PATH at runtime" -# Start backend -uvicorn api.server:app --host 0.0.0.0 --port 8001 & -BACK_PID=$! - -# Start frontend -cd frontend -pnpm dev:docker --host 0.0.0.0 --port 5173 & -FRONT_PID=$! - -# Wait for both processes -wait -n $BACK_PID $FRONT_PID -kill -TERM $BACK_PID $FRONT_PID 2>/dev/null || true -wait \ No newline at end of file +# Start backend only (frontend assets are pre-built) +uvicorn api.server:app --host 0.0.0.0 --port 8001 From db93c96acb8874aeba076cdb5fc96ec615ce5ff2 Mon Sep 17 00:00:00 2001 From: tsshadow Date: Sun, 14 Sep 2025 08:38:30 +0200 Subject: [PATCH 2/5] fix: include frontend sources in docker build --- Dockerfile | 6 ++++-- frontend/package.json | 1 + frontend/svelte.config.js | 11 ++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index efba019..716001b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,8 +33,10 @@ COPY requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt # Frontend dependencies and build -COPY frontend/package.json frontend/pnpm-lock.yaml frontend/ -RUN cd frontend && pnpm install && pnpm build +COPY frontend/pnpm-lock.yaml frontend/package.json frontend/ +RUN cd frontend && pnpm install +COPY frontend/ frontend/ +RUN cd frontend && pnpm build # Copy application source COPY . . diff --git a/frontend/package.json b/frontend/package.json index dc5d483..327f920 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/vite": "^4.0.0", diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 1295460..33b51b2 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,4 +1,4 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ @@ -7,12 +7,9 @@ const config = { // for more information about preprocessors preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. - // If your environment is not supported, or you settled on a specific environment, switch out the adapter. - // See https://svelte.dev/docs/kit/adapters for more information about adapters. - adapter: adapter() - } + kit: { + adapter: adapter({ pages: 'dist', assets: 'dist', fallback: 'index.html' }) + } }; export default config; From 3319a3c111573855ea5c9541426b198f9357f781 Mon Sep 17 00:00:00 2001 From: tsshadow Date: Sun, 14 Sep 2025 09:44:02 +0200 Subject: [PATCH 3/5] feat: allow configurable runtime port --- Dockerfile | 4 +++- docker-compose.yml | 3 +-- entrypoint.sh | 3 ++- frontend/package.json | 4 ++-- frontend/vite.config.ts | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 716001b..9236b7a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,7 +64,9 @@ COPY --from=builder /app/frontend/dist frontend/dist # Copy default env again for runtime RUN cp .env.linux .env || true && rm -f .env.linux -EXPOSE 8001 +ARG PORT=8001 +ENV PORT=${PORT} +EXPOSE ${PORT} COPY entrypoint.sh ./entrypoint.sh RUN chmod +x entrypoint.sh diff --git a/docker-compose.yml b/docker-compose.yml index a7996fe..f2d4cc0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,8 +36,7 @@ services: DB_PASS: music-importer DB_DB: music-importer ports: - - "8001:8001" - - "5173:5173" + - "${PORT:-8001}:${PORT:-8001}" volumes: - .:/app volumes: diff --git a/entrypoint.sh b/entrypoint.sh index 59b3a55..e0b3c9f 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,4 +3,5 @@ set -e export PATH="/usr/local/bin:/usr/bin:/bin:${PATH}" command -v ffmpeg || echo "ffmpeg missing from PATH at runtime" # Start backend only (frontend assets are pre-built) -uvicorn api.server:app --host 0.0.0.0 --port 8001 +PORT="${PORT:-8001}" +uvicorn api.server:app --host 0.0.0.0 --port "$PORT" diff --git a/frontend/package.json b/frontend/package.json index 327f920..9b73e99 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,8 +4,8 @@ "version": "0.0.1", "type": "module", "scripts": { - "dev": "VITE_API_BASE=/api VITE_DEV_API_TARGET=http://192.168.1.178:8001 vite dev", - "dev:docker": "VITE_API_BASE=/api VITE_DEV_API_TARGET=http://192.168.1.27:8001 vite dev", + "dev": "VITE_API_BASE=/api VITE_DEV_API_TARGET=${VITE_DEV_API_TARGET:-http://localhost:8001} vite dev", + "dev:docker": "VITE_API_BASE=/api VITE_DEV_API_TARGET=${VITE_DEV_API_TARGET:-http://localhost:8001} vite dev", "build": "vite build", "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index b45ffce..1346dd2 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,7 +3,7 @@ import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; -const devProxyTarget = process.env.VITE_DEV_API_TARGET ?? 'http://192.168.1.178:8001'; +const devProxyTarget = process.env.VITE_DEV_API_TARGET ?? 'http://localhost:8001'; export default defineConfig({ plugins: [tailwindcss(), sveltekit()], From 10368ba9db36d3c7ad1a4425d40a052bda937173 Mon Sep 17 00:00:00 2001 From: tsshadow Date: Sun, 14 Sep 2025 10:02:26 +0200 Subject: [PATCH 4/5] docs: add docker usage and env vars --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/README.md b/README.md index 3d2196b..2644f12 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,70 @@ key via the `X-API-Key` header (REST) or `api_key` query parameter (WebSocket). ## 👨‍💻 Who Is It For? * Personal project, specifically tailored for my needs + +## ⚙️ Configuration + +The application is configured through environment variables. Frequently used settings +include: + +| Variable | Default | Description | +|----------|---------|-------------| +| `PORT` | `8001` | HTTP port for the API and static frontend | +| `DB_HOST` | – | Database host name | +| `DB_PORT` | `3306` | Database port | +| `DB_USER` | – | Database user | +| `DB_PASS` | – | Database password | +| `DB_DB` | – | Database name | +| `API_KEY` | – | Optional shared secret required by clients | +| `CORS_ORIGINS` | `*` | Comma‑separated list of allowed origins | + +Other optional variables exist for specific downloaders such as Discogs, Spotify or +Telegram; consult the source if you need those integrations. + +## 🐳 Running with Docker + +Build the production image and expose it on the desired port: + +```bash +docker build -t music-importer . +docker run -p 8001:8001 \ + -e DB_HOST=db -e DB_PORT=3306 -e DB_USER=music-importer \ + -e DB_PASS=music-importer -e DB_DB=music-importer \ + music-importer +``` + +### docker-compose example + +```yaml +version: "3.9" +services: + db: + image: mariadb:11 + environment: + MARIADB_ROOT_PASSWORD: rootpass + MARIADB_DATABASE: music-importer + MARIADB_USER: music-importer + MARIADB_PASSWORD: music-importer + volumes: + - db_data:/var/lib/mysql + + music-importer: + build: . + depends_on: + - db + environment: + DB_HOST: db + DB_PORT: 3306 + DB_USER: music-importer + DB_PASS: music-importer + DB_DB: music-importer + # API_KEY: choose-a-secret + ports: + - "${PORT:-8001}:${PORT:-8001}" + +volumes: + db_data: +``` + +Use the `PORT` variable to run multiple instances side-by-side and override any other +variables as needed for your setup. From d82e5731bc737249b96d4e65135bc05e1d40a1e1 Mon Sep 17 00:00:00 2001 From: tsshadow Date: Sun, 14 Sep 2025 14:57:33 +0200 Subject: [PATCH 5/5] Refactor Dockerfile and expand README with configuration steps Streamlined Dockerfile to minimize image size by excluding dev files and clarified build/run steps. Enhanced README with detailed environment variable settings, Docker usage instructions, and a docker-compose example for quick setup. --- Dockerfile | 30 ++++++++++++------------ README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index a2d0f84..179ac72 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,18 +32,24 @@ RUN set -eux; \ COPY requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt -# Copy application source -COPY . . - -# Optional env copy (your original step) -RUN [ -f .env.linux ] && cp -f .env.linux .env && rm -f .env.linux || true - # Frontend dependencies and build COPY frontend/pnpm-lock.yaml frontend/package.json frontend/ RUN cd frontend && pnpm install COPY frontend/ frontend/ RUN cd frontend && pnpm build +# Copy application source +COPY . . + +# Remove dev files to shrink final image +RUN rm -rf tests \ + frontend/node_modules frontend/.svelte-kit frontend/.vite \ + frontend/src frontend/static frontend/tsconfig.json \ + frontend/vite.config.ts frontend/svelte.config.js \ + frontend/eslint.config.js frontend/package.json \ + frontend/pnpm-lock.yaml frontend/README.md \ + frontend/vitest-setup-client.ts + # ----------------------------------------------------- # Final stage: minimal runtime image @@ -55,20 +61,14 @@ WORKDIR /app # Bring in Python deps and FFmpeg COPY --from=builder /usr/local /usr/local -# Application source (node_modules are ignored via .dockerignore) -COPY . . - -# Include built frontend -COPY --from=builder /app/frontend/dist frontend/dist - -# Copy default env again for runtime -RUN cp .env.linux .env || true && rm -f .env.linux +# Bring in application code and built frontend +COPY --from=builder /app /app ARG PORT=8001 ENV PORT=${PORT} EXPOSE ${PORT} -COPY entrypoint.sh ./entrypoint.sh +# Ensure entrypoint is executable RUN chmod +x entrypoint.sh CMD ["bash", "./entrypoint.sh"] diff --git a/README.md b/README.md index 2644f12..8f7a891 100644 --- a/README.md +++ b/README.md @@ -155,3 +155,70 @@ volumes: Use the `PORT` variable to run multiple instances side-by-side and override any other variables as needed for your setup. + +## ⚙️ Configuration + +The application is configured through environment variables. Frequently used settings +include: + +| Variable | Default | Description | +|----------|---------|-------------| +| `PORT` | `8001` | HTTP port for the API and static frontend | +| `DB_HOST` | – | Database host name | +| `DB_PORT` | `3306` | Database port | +| `DB_USER` | – | Database user | +| `DB_PASS` | – | Database password | +| `DB_DB` | – | Database name | +| `API_KEY` | – | Optional shared secret required by clients | +| `CORS_ORIGINS` | `*` | Comma‑separated list of allowed origins | + +Other optional variables exist for specific downloaders such as Discogs, Spotify or +Telegram; consult the source if you need those integrations. + +## 🐳 Running with Docker + +Build the production image and expose it on the desired port: + +```bash +docker build -t music-importer . +docker run -p 8001:8001 \ + -e DB_HOST=db -e DB_PORT=3306 -e DB_USER=music-importer \ + -e DB_PASS=music-importer -e DB_DB=music-importer \ + music-importer +``` + +### docker-compose example + +```yaml +version: "3.9" +services: + db: + image: mariadb:11 + environment: + MARIADB_ROOT_PASSWORD: rootpass + MARIADB_DATABASE: music-importer + MARIADB_USER: music-importer + MARIADB_PASSWORD: music-importer + volumes: + - db_data:/var/lib/mysql + + music-importer: + build: . + depends_on: + - db + environment: + DB_HOST: db + DB_PORT: 3306 + DB_USER: music-importer + DB_PASS: music-importer + DB_DB: music-importer + # API_KEY: choose-a-secret + ports: + - "${PORT:-8001}:${PORT:-8001}" + +volumes: + db_data: +``` + +Use the `PORT` variable to run multiple instances side-by-side and override any other +variables as needed for your setup.