diff --git a/servers/media-core/bookorbit/README.md b/servers/media-core/bookorbit/README.md new file mode 100644 index 0000000..5c27f79 --- /dev/null +++ b/servers/media-core/bookorbit/README.md @@ -0,0 +1,14 @@ +# BookOrbit on media-core + +- Web: `http://192.168.50.46:3000` +- Book library: `/mnt/truenas/multimedia/books` -> `/books` +- Book Dock: `/mnt/truenas/multimedia/curator/staging/bookorbit` -> `/book-dock` +- App data: `/opt/media-core/appdata/bookorbit/app` +- PostgreSQL: `/opt/media-core/appdata/bookorbit/postgres` +- Secrets: `/opt/media-core/appdata/bookorbit/secrets` (`0600`, not in Git) + +Create the first library with container path `/books`. Do not later widen or replace that root without a database backup and a tested migration: BookOrbit currently has an open issue where root-folder changes can duplicate books or lose metadata and reading progress. + +The existing Curator and BookOrbit share the final book files, but they do not share databases. During the initial evaluation, keep BookOrbit automatic file rename and metadata write-back disabled. Use Book Dock for reviewed imports and retain Curator as the control ledger until BookOrbit integration is explicitly designed. + +Before upgrades, create a PostgreSQL dump and back up `/opt/media-core/appdata/bookorbit/app`. Image tags are descriptive and digests are authoritative. diff --git a/servers/media-core/bookorbit/compose.yaml b/servers/media-core/bookorbit/compose.yaml new file mode 100644 index 0000000..3cb28fb --- /dev/null +++ b/servers/media-core/bookorbit/compose.yaml @@ -0,0 +1,145 @@ +x-arcane: + icon: bookorbit + urls: + - https://bookorbit.app/ + +services: + app: + image: ghcr.io/bookorbit/bookorbit:3.1.0@sha256:78b6eac18306eb7eba0dbf6592ecdee01b15f24de9a941cb853db4a0232d7999 + container_name: bookorbit-app + restart: unless-stopped + init: true + depends_on: + postgres: + condition: service_healthy + ports: + - "3000:3000" + environment: + NODE_ENV: production + PORT: "3000" + APP_URL: http://192.168.50.46:3000 + CLIENT_URL: http://192.168.50.46:3000 + POSTGRES_HOST: postgres + POSTGRES_PORT: "5432" + POSTGRES_USER: bookorbit + POSTGRES_DB: bookorbit + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + JWT_SECRET_FILE: /run/secrets/jwt_secret + PODCAST_ENCRYPTION_KEY_FILE: /run/secrets/podcast_encryption_key + SETUP_BOOTSTRAP_TOKEN_FILE: /run/secrets/setup_bootstrap_token + EMAIL_ENCRYPTION_KEY_FILE: /run/secrets/email_encryption_key + MIGRATION_ENCRYPTION_KEY_FILE: /run/secrets/migration_encryption_key + BOOK_REQUEST_ENCRYPTION_KEY_FILE: /run/secrets/book_request_encryption_key + TZ: Asia/Shanghai + PUID: "1000" + PGID: "1000" + NODE_MAX_OLD_SPACE_SIZE: "2048" + BOOK_DOCK_PATH: /book-dock + LIBRARY_BROWSE_ROOT: /books + DISABLE_LOCAL_AUTH: "false" + volumes: + - type: bind + source: /mnt/truenas/multimedia/books + target: /books + bind: + create_host_path: false + - type: bind + source: /mnt/truenas/multimedia/curator/staging/bookorbit + target: /book-dock + bind: + create_host_path: false + - type: bind + source: /opt/media-core/appdata/bookorbit/app + target: /data + bind: + create_host_path: false + secrets: + - postgres_password + - jwt_secret + - podcast_encryption_key + - setup_bootstrap_token + - email_encryption_key + - migration_encryption_key + - book_request_encryption_key + read_only: true + tmpfs: + - /tmp + cap_drop: + - ALL + cap_add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + security_opt: + - no-new-privileges:true + healthcheck: + test: + - CMD + - node + - -e + - const p=process.env.PORT||3000;fetch('http://127.0.0.1:'+p+'/api/v1/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1)) + interval: 30s + timeout: 5s + retries: 5 + start_period: 60s + stop_grace_period: 30s + labels: + com.getarcaneapp.arcane.icon: bookorbit + logging: + driver: json-file + options: + max-size: 10m + max-file: "3" + + postgres: + image: pgvector/pgvector:pg18@sha256:2ba9ca5f2e7daa0f0e7723cba1ee9167bab54efd3640516a44ac1a928dd67e7a + container_name: bookorbit-db + restart: unless-stopped + environment: + POSTGRES_USER: bookorbit + POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password + POSTGRES_DB: bookorbit + PGDATA: /var/lib/postgresql/data/pgdata + TZ: Asia/Shanghai + volumes: + - type: bind + source: /opt/media-core/appdata/bookorbit/postgres + target: /var/lib/postgresql/data + bind: + create_host_path: false + secrets: + - postgres_password + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + security_opt: + - no-new-privileges:true + stop_grace_period: 60s + labels: + com.getarcaneapp.arcane.icon: postgres + logging: + driver: json-file + options: + max-size: 10m + max-file: "3" + +secrets: + postgres_password: + file: /opt/media-core/appdata/bookorbit/secrets/postgres_password + jwt_secret: + file: /opt/media-core/appdata/bookorbit/secrets/jwt_secret + podcast_encryption_key: + file: /opt/media-core/appdata/bookorbit/secrets/podcast_encryption_key + setup_bootstrap_token: + file: /opt/media-core/appdata/bookorbit/secrets/setup_bootstrap_token + email_encryption_key: + file: /opt/media-core/appdata/bookorbit/secrets/email_encryption_key + migration_encryption_key: + file: /opt/media-core/appdata/bookorbit/secrets/migration_encryption_key + book_request_encryption_key: + file: /opt/media-core/appdata/bookorbit/secrets/book_request_encryption_key