The page hotlinked ~100 AniList CDN covers per load and some failed at random. The reader now downloads each catalog cover once (sequential, ~1/s, stops on 401/403/429, images up to 2 MB) into data/greatest-covers/ and serves it from /api/greatest-cover with a year-long browser cache; until a copy exists the route redirects to the catalog URL. The page also accepts the server's relative cover URLs, so owned titles show their library cover again. GREATEST_COVER_WARM=0 turns the downloads off (tests, offline installs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
147 lines
4.1 KiB
YAML
147 lines
4.1 KiB
YAML
name: mangacircus
|
|
|
|
x-app: &app
|
|
image: ${APP_IMAGE:-mangacircus:local}
|
|
build:
|
|
context: .
|
|
args:
|
|
VCS_REF: ${VCS_REF:-development}
|
|
init: true
|
|
restart: unless-stopped
|
|
stop_grace_period: 90s
|
|
read_only: true
|
|
tmpfs: [/tmp]
|
|
security_opt: [no-new-privileges:true]
|
|
cap_drop: [ALL]
|
|
secrets: [scraper_token]
|
|
logging:
|
|
driver: local
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
services:
|
|
reader:
|
|
<<: *app
|
|
image: ${READER_IMAGE:-${APP_IMAGE:-mangacircus:local}}
|
|
command: [node, runtime/launch.js, reader]
|
|
ports: ["${READER_BIND:-127.0.0.1}:${READER_PORT:-8080}:8080"]
|
|
volumes:
|
|
- type: bind
|
|
source: ${LIBRARY_PATH:-./library}
|
|
target: /library
|
|
read_only: true
|
|
bind: {create_host_path: false}
|
|
- type: bind
|
|
source: ${DATA_PATH:-./data}
|
|
target: /data
|
|
bind: {create_host_path: false}
|
|
# Built-in backups (docs/backup.md). The folder must exist and be writable by
|
|
# uid 1000; backups stay off unless BACKUP_PATH is set.
|
|
- type: bind
|
|
source: ${BACKUP_PATH:-./backup}
|
|
target: /backup
|
|
bind: {create_host_path: false}
|
|
environment:
|
|
PORT: "8080"
|
|
DATA: /data
|
|
LIBRARY: /library
|
|
BACKUP: ${BACKUP_PATH:+/backup}
|
|
TZ: ${TZ:-UTC}
|
|
SIGNUP: closed
|
|
SCAN_INTERVAL: "10"
|
|
SCAN_CONCURRENCY: ${SCAN_CONCURRENCY:-8}
|
|
COOKIE_SECURE: ${COOKIE_SECURE:-0}
|
|
READING_WINDOWS_JSON: ${READING_WINDOWS_JSON:-}
|
|
READING_TIMEZONE: ${READING_TIMEZONE:-UTC}
|
|
READING_QUIZ_UNLOCK_MINUTES: ${READING_QUIZ_UNLOCK_MINUTES:-60}
|
|
READING_QUIZ_CATALOG_PATH: ${READING_QUIZ_CATALOG_PATH:-}
|
|
SCRAPER_URL: http://scraper:8091
|
|
SCRAPER_TOKEN_FILE: /run/secrets/scraper_token
|
|
DISCOVERY_HARVEST_AT: ${DISCOVERY_HARVEST_AT:-03:30}
|
|
DISCOVERY_ANILIST_URL: ${DISCOVERY_ANILIST_URL:-}
|
|
GREATEST_COVER_WARM: ${GREATEST_COVER_WARM:-1}
|
|
networks: [backend, egress]
|
|
healthcheck:
|
|
test: [CMD, node, tools/healthcheck.js, reader]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 60s
|
|
retries: 3
|
|
|
|
scraper:
|
|
<<: *app
|
|
command: [node, runtime/launch.js, scraper]
|
|
volumes:
|
|
- type: bind
|
|
source: ${LIBRARY_PATH:-./library}
|
|
target: /library
|
|
bind: {create_host_path: false}
|
|
- type: bind
|
|
source: ${SCRAPER_DATA_PATH:-./data/scraper}
|
|
target: /data/scraper
|
|
bind: {create_host_path: false}
|
|
- type: bind
|
|
source: ${PEER_DATA_PATH:-./data/peering}
|
|
target: /data/peering
|
|
read_only: true
|
|
environment:
|
|
DATA: /data
|
|
LIBRARY: /library
|
|
SCRAPER_DATA: /data/scraper
|
|
METADATA_CACHE: /data/scraper/metadata/anilist
|
|
TZ: ${TZ:-UTC}
|
|
SCRAPER_HOST: 0.0.0.0
|
|
SCRAPER_PORT: "8091"
|
|
SCRAPER_TOKEN_FILE: /run/secrets/scraper_token
|
|
PEER_DATA: /data/peering
|
|
networks: [backend, egress]
|
|
healthcheck:
|
|
test: [CMD, node, tools/healthcheck.js, scraper]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 60s
|
|
retries: 3
|
|
|
|
peer-gateway:
|
|
<<: *app
|
|
profiles: [peering]
|
|
command: [node, peering/gateway.js]
|
|
ports: ["${PEER_BIND:-127.0.0.1}:${PEER_PORT:-8093}:8093"]
|
|
volumes:
|
|
- type: bind
|
|
source: ${LIBRARY_PATH:-./library}
|
|
target: /library
|
|
read_only: true
|
|
bind: {create_host_path: false}
|
|
- type: bind
|
|
source: ${PEER_DATA_PATH:-./data/peering}
|
|
target: /data/peering
|
|
read_only: true
|
|
- type: bind
|
|
source: ${PEER_USAGE_PATH:-./data/peering-usage}
|
|
target: /data/peering-usage
|
|
environment:
|
|
LIBRARY: /library
|
|
PEER_DATA: /data/peering
|
|
PEER_USAGE_FILE: /data/peering-usage/usage.json
|
|
PEER_HOST: 0.0.0.0
|
|
PEER_PORT: "8093"
|
|
# Docker (Desktop) does not publish host ports from an internal-only network.
|
|
networks: [backend, egress]
|
|
healthcheck:
|
|
test: [CMD, node, tools/healthcheck-peer.js]
|
|
interval: 30s
|
|
timeout: 5s
|
|
start_period: 30s
|
|
retries: 3
|
|
|
|
networks:
|
|
backend:
|
|
internal: true
|
|
egress: {}
|
|
|
|
secrets:
|
|
scraper_token:
|
|
file: ${SCRAPER_TOKEN_PATH:-./.secrets/scraper_token}
|