163 lines
6.1 KiB
YAML
163 lines
6.1 KiB
YAML
version: "3.9"
|
|
|
|
services:
|
|
# ─── MinIO (object storage for chapter .md files + audio cache) ─────────────
|
|
minio:
|
|
image: minio/minio:latest
|
|
#container_name: libnovel-minio
|
|
restart: unless-stopped
|
|
command: server /data --console-address ":9001"
|
|
environment:
|
|
MINIO_ROOT_USER: "${MINIO_ROOT_USER:-admin}"
|
|
MINIO_ROOT_PASSWORD: "${MINIO_ROOT_PASSWORD:-changeme123}"
|
|
ports:
|
|
- "${MINIO_PORT:-9000}:9000" # S3 API
|
|
- "${MINIO_CONSOLE_PORT:-9001}:9001" # Web console
|
|
volumes:
|
|
- minio_data:/data
|
|
healthcheck:
|
|
test: ["CMD", "mc", "ready", "local"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# ─── MinIO bucket initialisation ─────────────────────────────────────────────
|
|
# Runs once to create the default buckets and then exits.
|
|
minio-init:
|
|
image: minio/mc:latest
|
|
#container_name: libnovel-minio-init
|
|
depends_on:
|
|
minio:
|
|
condition: service_healthy
|
|
entrypoint: >
|
|
/bin/sh -c "
|
|
mc alias set local http://minio:9000 $${MINIO_ROOT_USER:-admin} $${MINIO_ROOT_PASSWORD:-changeme123};
|
|
mc mb --ignore-existing local/libnovel-chapters;
|
|
mc mb --ignore-existing local/libnovel-audio;
|
|
echo 'buckets ready';
|
|
"
|
|
environment:
|
|
MINIO_ROOT_USER: "${MINIO_ROOT_USER:-admin}"
|
|
MINIO_ROOT_PASSWORD: "${MINIO_ROOT_PASSWORD:-changeme123}"
|
|
|
|
# ─── PocketBase (auth + structured data: books, chapters index, ranking, progress) ──
|
|
pocketbase:
|
|
image: ghcr.io/muchobien/pocketbase:latest
|
|
#container_name: libnovel-pocketbase
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${POCKETBASE_PORT:-8090}:8090"
|
|
volumes:
|
|
- pb_data:/pb/pb_data
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:8090/api/health"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# ─── Browserless ────────────────────────────────────────────────────────────
|
|
browserless:
|
|
image: ghcr.io/browserless/chromium:latest
|
|
#container_name: libnovel-browserless
|
|
restart: unless-stopped
|
|
environment:
|
|
# Set a token to lock down the endpoint; the scraper reads it via
|
|
# BROWSERLESS_TOKEN below.
|
|
TOKEN: "${BROWSERLESS_TOKEN:-}"
|
|
# Allow up to 10 concurrent browser sessions.
|
|
CONCURRENT: "${BROWSERLESS_CONCURRENT:-10}"
|
|
# Queue up to 100 requests before returning 429.
|
|
QUEUED: "${BROWSERLESS_QUEUED:-100}"
|
|
# Per-session timeout in ms.
|
|
TIMEOUT: "${BROWSERLESS_TIMEOUT:-60000}"
|
|
# Optional webhook URL for Browserless error alerts.
|
|
ERROR_ALERT_URL: "${ERROR_ALERT_URL:-}"
|
|
ports:
|
|
- "${BROWSERLESS_PORT:-3030}:3000"
|
|
# Shared memory is required for Chrome.
|
|
shm_size: "2gb"
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/json/version"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# ─── Scraper ─────────────────────────────────────────────────────────────────
|
|
scraper:
|
|
build:
|
|
context: ./scraper
|
|
dockerfile: Dockerfile
|
|
#container_name: libnovel-scraper
|
|
restart: unless-stopped
|
|
depends_on:
|
|
pocketbase:
|
|
condition: service_healthy
|
|
minio:
|
|
condition: service_healthy
|
|
environment:
|
|
BROWSERLESS_URL: "http://browserless:3000"
|
|
BROWSERLESS_TOKEN: "${BROWSERLESS_TOKEN:-}"
|
|
# content | scrape | cdp | direct — swap to test different strategies.
|
|
BROWSERLESS_STRATEGY: "${BROWSERLESS_STRATEGY:-direct}"
|
|
# Strategy for URL retrieval (chapter list). Default: content (browserless)
|
|
BROWSERLESS_URL_STRATEGY: "${BROWSERLESS_URL_STRATEGY:-content}"
|
|
# 0 → defaults to NumCPU inside the container.
|
|
SCRAPER_WORKERS: "${SCRAPER_WORKERS:-0}"
|
|
SCRAPER_STATIC_ROOT: "/app/static/books"
|
|
SCRAPER_HTTP_ADDR: ":8080"
|
|
LOG_LEVEL: "debug"
|
|
# Kokoro-FastAPI TTS endpoint.
|
|
KOKORO_URL: "${KOKORO_URL:-https://kokoro.kalekber.cc}"
|
|
KOKORO_VOICE: "${KOKORO_VOICE:-af_bella}"
|
|
# MinIO / S3 object storage
|
|
MINIO_ENDPOINT: "minio:9000"
|
|
MINIO_ACCESS_KEY: "${MINIO_ROOT_USER:-admin}"
|
|
MINIO_SECRET_KEY: "${MINIO_ROOT_PASSWORD:-changeme123}"
|
|
MINIO_USE_SSL: "false"
|
|
MINIO_BUCKET_CHAPTERS: "${MINIO_BUCKET_CHAPTERS:-libnovel-chapters}"
|
|
MINIO_BUCKET_AUDIO: "${MINIO_BUCKET_AUDIO:-libnovel-audio}"
|
|
# PocketBase
|
|
POCKETBASE_URL: "http://pocketbase:8090"
|
|
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL:-admin@libnovel.local}"
|
|
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD:-changeme123}"
|
|
ports:
|
|
- "${SCRAPER_PORT:-8080}:8080"
|
|
volumes:
|
|
- static_books:/app/static/books
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
# ─── SvelteKit UI ────────────────────────────────────────────────────────────
|
|
ui:
|
|
build:
|
|
context: ./ui
|
|
dockerfile: Dockerfile
|
|
container_name: libnovel-ui
|
|
restart: unless-stopped
|
|
depends_on:
|
|
scraper:
|
|
condition: service_healthy
|
|
pocketbase:
|
|
condition: service_healthy
|
|
environment:
|
|
SCRAPER_API_URL: "http://scraper:8080"
|
|
POCKETBASE_URL: "http://pocketbase:8090"
|
|
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL:-admin@libnovel.local}"
|
|
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD:-changeme123}"
|
|
PUBLIC_MINIO_PUBLIC_URL: "${PUBLIC_MINIO_PUBLIC_URL:-http://localhost:9000}"
|
|
ports:
|
|
- "${UI_PORT:-3000}:3000"
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
volumes:
|
|
static_books:
|
|
minio_data:
|
|
pb_data:
|