From b14ed026700ca02820987cd91e650bd67c85a840 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Mon, 22 Jun 2026 20:23:56 +0200 Subject: [PATCH] =?UTF-8?q?fix(infra):=20plug=20operational=20defaults=20?= =?UTF-8?q?=E2=80=94=20pg=20restart,=20proxy=20timeout,=20env=20template?= =?UTF-8?q?=20(0.87.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three operational gaps tripped a recent review: 1. **Postgres had no `restart:` policy.** tor / docker-socket-proxy / vision-manager all set `unless-stopped`; pg was the lone exception. A transient OOM or panic left backend's `depends_on: service_healthy` blocking every future restart and took the stack offline. Add `unless-stopped` on postgres and on backend/frontend in the base compose so the prod overlay is the same shape. 2. **`BACKEND_PROXY_TIMEOUT_MS` was documented but silently a no-op.** `.env.example` carried it but `docker-compose.yml` never forwarded it into the frontend service env, so the value never reached `hooks.server.ts`. Wire it through. 3. **`.env.example` omitted required env vars.** Operators copying the template got no admin bootstrap, no boot-seed crawler URL, no analysis configuration. Add: `ADMIN_USERNAME`, `ADMIN_PASSWORD`, `PRIVATE_MODE`, `ALLOW_SELF_REGISTER`, `CRAWLER_START_URL`, `CRAWLER_CDN_HOST`, `ANALYSIS_ENABLED`, `ANALYSIS_VISION_URL`, `ANALYSIS_VISION_MODEL`, `ANALYSIS_WORKERS`, `ANALYSIS_JOB_TIMEOUT_SECS`, `ANALYSIS_API_KEY` — each with the comment block that explains seed-vs-runtime semantics. Also adds `frontend/vite.config.ts.timestamp-*.mjs` to `.gitignore` — a crashed dev server leaves these transient files behind otherwise. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.example | 56 +++++++++++++++++++++++++++++++++++++++++++ .gitignore | 4 ++++ backend/Cargo.lock | 2 +- backend/Cargo.toml | 2 +- docker-compose.yml | 10 ++++++++ frontend/package.json | 2 +- 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 9c50ffb..c764a42 100644 --- a/.env.example +++ b/.env.example @@ -69,6 +69,27 @@ CORS_ALLOWED_ORIGINS= # value the browser uses. ADMIN_ALLOWED_ORIGINS= +# ----- Admin bootstrap ----- +# When BOTH are set and non-empty, the backend ensures a user with this +# username exists at startup, promotes it to admin, and (for a brand-new +# row only) sets the password. An existing user's password is NEVER +# overwritten by this — rotate via the dashboard or `/auth/me/password`. +# Leave BOTH empty in a fully provisioned deploy. +ADMIN_USERNAME= +ADMIN_PASSWORD= + +# ----- Site-wide auth gates (env-ONLY) ----- +# PRIVATE_MODE: when `true`, every endpoint except a small public allowlist +# (/health, /auth/config, /auth/login, /auth/logout) demands a valid +# session — anonymous reads return 401. Self-registration is also +# force-disabled regardless of ALLOW_SELF_REGISTER. Default `false`. +PRIVATE_MODE=false +# ALLOW_SELF_REGISTER: when `false`, /auth/register returns 403 and the +# frontend hides its register affordance. Admins can still mint accounts +# via /admin/users. Default `true` (open registration). Forced to `false` +# when PRIVATE_MODE=true. +ALLOW_SELF_REGISTER=true + # ----- Upload limits ----- # Per-request body cap. axum rejects oversized requests with 413 before # our handlers run. Default 200 MiB. @@ -182,6 +203,17 @@ BACKEND_PROXY_TIMEOUT_MS=300000 # analysis ANALYSIS_API_KEY. The important site levers (PRIVATE_MODE, # ALLOW_SELF_REGISTER) also remain env-only by design. # +# Crawler boot seeds (effective only on first boot, then editable from the +# dashboard): +# CRAWLER_START_URL The catalog listing URL the crawler walks. Without +# it, the cron is disabled (no daemon-driven sweeps; +# force-resync from the dashboard still works). +# CRAWLER_CDN_HOST Distinct host the source serves images from. Used to +# set a slower rate limit (CRAWLER_CDN_RATE_MS) and +# seed the download allowlist. +CRAWLER_START_URL= +CRAWLER_CDN_HOST= +# # New analysis knobs (all optional, all seed defaults): # ANALYSIS_TEMPERATURE Sampling temperature. Default 0 (deterministic). # ANALYSIS_SYSTEM_PROMPT Override the single-call vision system prompt. @@ -190,6 +222,30 @@ BACKEND_PROXY_TIMEOUT_MS=300000 # Leave the prompt vars unset to use the built-in defaults (also editable, # with a per-prompt "reset to default", in the dashboard). +# ----- Analysis (vision) — boot seeds + env-only secret ----- +# The page-analysis worker (off by default) POSTs each page image at a +# Chat-Completions-compatible vision endpoint and persists OCR + tags. +# All fields except ANALYSIS_API_KEY are boot seeds for the app_settings +# row and switch to dashboard-editable once persisted. +# +# ANALYSIS_ENABLED Turn the worker on at first boot. Toggleable live in +# the dashboard. Default `false`. +ANALYSIS_ENABLED=false +# ANALYSIS_VISION_URL /v1/chat/completions endpoint. Required when enabled. +# For the bundled vision container, use +# http://mangalord-vision:8000/v1/chat/completions. +ANALYSIS_VISION_URL= +# ANALYSIS_VISION_MODEL Model id the endpoint expects (`model:` field). +ANALYSIS_VISION_MODEL= +# ANALYSIS_WORKERS Concurrent vision dispatches. Default 1. +ANALYSIS_WORKERS=1 +# ANALYSIS_JOB_TIMEOUT_SECS Hard upper bound per vision call before the +# job is acked failed (backoff). Default 600. +ANALYSIS_JOB_TIMEOUT_SECS=600 +# ANALYSIS_API_KEY env-ONLY. Bearer-attached to every vision call; never +# persisted, never logged. +ANALYSIS_API_KEY= + # ----- Vision autoscaling (the `ai` compose profile) ----- # The mangalord-vision (llama.cpp) container pins ~4.4 GiB and has no # idle-unload, so the `vision-manager` sidecar starts it on demand and diff --git a/.gitignore b/.gitignore index a22c07f..bc6f749 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ /frontend/build /frontend/test-results /frontend/playwright-report +# Vite writes these transient files next to vite.config.ts while the dev +# server is running, then deletes them on clean shutdown — but a crashed +# server leaves them behind. Keep them out of `git status` either way. +/frontend/vite.config.ts.timestamp-*.mjs # Local storage volume (manga files). `/data` is the root path the # compose volume mounts at; `/backend/data` is where the dev backend diff --git a/backend/Cargo.lock b/backend/Cargo.lock index cb907d4..0bbae31 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1517,7 +1517,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mangalord" -version = "0.87.0" +version = "0.87.1" dependencies = [ "anyhow", "argon2", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index edc2e71..36cdf93 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mangalord" -version = "0.87.0" +version = "0.87.1" edition = "2021" default-run = "mangalord" diff --git a/docker-compose.yml b/docker-compose.yml index f995b43..6f02d2d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,11 @@ services: interval: 5s timeout: 5s retries: 10 + # Without this, a single Postgres crash leaves backend's + # `depends_on: service_healthy` blocking every future restart and + # takes the whole stack offline. Match the policy already set on + # tor / docker-socket-proxy / vision-manager. + restart: unless-stopped tor: # SOCKS5 proxy for the crawler, plus a control port so the backend @@ -106,6 +111,7 @@ services: # development). expose: - "8080" + restart: unless-stopped frontend: build: ./frontend @@ -115,8 +121,12 @@ services: # SvelteKit's hooks.server.ts proxies /api/* to this URL so the # browser only ever talks to :3000 and cookies stay same-origin. BACKEND_URL: http://backend:8080 + # Per-request wall-clock cap for the /api/* reverse proxy. Defaults + # to 300000 (5 min) in hooks.server.ts; raise/lower via .env. + BACKEND_PROXY_TIMEOUT_MS: ${BACKEND_PROXY_TIMEOUT_MS:-300000} ports: - "3000:3000" + restart: unless-stopped # ----- Vision autoscaling (profile: ai) ----------------------------------- # Two extra containers that idle-stop the mangalord-vision (llama.cpp) diff --git a/frontend/package.json b/frontend/package.json index 06d336f..20a8f02 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mangalord-frontend", - "version": "0.87.0", + "version": "0.87.1", "private": true, "type": "module", "scripts": {