diff --git a/dankserver/audiobookshelf/quadlet/audiobookshelf.container b/dankserver/audiobookshelf/quadlet/audiobookshelf.container index e694cbd..872443a 100644 --- a/dankserver/audiobookshelf/quadlet/audiobookshelf.container +++ b/dankserver/audiobookshelf/quadlet/audiobookshelf.container @@ -33,9 +33,16 @@ Network=pasta # README is the npm package's naming and is INERT here: with ABS_PORT=8000 the # server listened on :80 under base path /audiobookshelf, so the unit sat # `active (running)` while every probe to :8000 was refused (measured 2026-11-02, -# first deploy). ROUTER_BASE_PATH=/ is what makes the URL the plain host:port. +# first deploy). +# ROUTER_BASE_PATH MUST be /audiobookshelf: the SPA in /app/client/dist/index.html is +# built with `routerBasePath:"/audiobookshelf"` baked in, so it asks for its assets under +# that prefix no matter what the server is mounted at. With `/` the server answers the +# page but every script request falls through to the SPA fallback and comes back as +# text/html — Chrome refuses to execute it and the UI never leaves "Loading…" +# (measured 2026-11-03; the API and /healthcheck were fine, which is why an API-only +# verification missed it). Environment=PORT=8000 -Environment=ROUTER_BASE_PATH=/ +Environment=ROUTER_BASE_PATH=/audiobookshelf Environment=CONFIG_PATH=/config Environment=METADATA_PATH=/metadata Environment=TZ=America/Vancouver @@ -45,6 +52,8 @@ Volume=/home/dank/Containers/audiobookshelf/metadata:/metadata Volume=/mnt/entropy/Audiobooks:/audiobooks:ro # The image is Alpine: it ships wget and no curl, so the health check must name # the binary that exists or the container is unhealthy while serving perfectly. +# /healthcheck answers `OK` at the root AND under the base path (measured on both +# base paths), so the probe below is correct whatever ROUTER_BASE_PATH says. HealthCmd=wget -q -O /dev/null http://127.0.0.1:8000/healthcheck HealthInterval=30s HealthTimeout=10s diff --git a/scripts/deploy-remote.sh b/scripts/deploy-remote.sh index 5160157..7e90e4b 100755 --- a/scripts/deploy-remote.sh +++ b/scripts/deploy-remote.sh @@ -1315,7 +1315,8 @@ deploy_audiobookshelf() { mkdir -p /home/dank/Containers/audiobookshelf/config \ /home/dank/Containers/audiobookshelf/metadata systemctl --user restart audiobookshelf.service - # /healthcheck answers once the HTTP server is bound; the library scan that + # /healthcheck answers once the HTTP server is bound, at the root whatever + # ROUTER_BASE_PATH says (measured on both base paths); the library scan that # follows runs in the background and must not gate the deploy. wait_for_health audiobookshelf http://127.0.0.1:8000/healthcheck 90 # A server with no root user and no library is "healthy" and useless, so the @@ -1323,7 +1324,12 @@ deploy_audiobookshelf() { # it creates the account (credential in the mode-600 host secret file), the # Audiobooks library over the /audiobooks bind, and fires the scan. Every # step is idempotent, so a re-deploy costs one incremental scan. - scripts/audiobookshelf-init.sh + # The bootstrapper has to speak the same base path the unit mounts (the API, the + # login route and the OIDC redirect subfolder all hang off it), so the URL is read + # out of the unit file here instead of being restated in the script's default. + abs_base=$(awk -F= '/^Environment=ROUTER_BASE_PATH=/{print $3}' \ + dankserver/audiobookshelf/quadlet/audiobookshelf.container) + ABS_URL="http://127.0.0.1:8000${abs_base}" scripts/audiobookshelf-init.sh } # The content server's single admin account and the desktop's basic-auth diff --git a/tests/dankserver_quadlet.bats b/tests/dankserver_quadlet.bats index ae4b34e..9b66f9f 100755 --- a/tests/dankserver_quadlet.bats +++ b/tests/dankserver_quadlet.bats @@ -182,8 +182,13 @@ load helpers # under base path /audiobookshelf and the deploy gate timed out beside a unit # that was active (running). PORT and ROUTER_BASE_PATH are what the image's own # ENV block and the server source actually use. + # The base path is not a free choice either: /app/client/dist/index.html inside the + # pinned image carries routerBasePath:"/audiobookshelf", so a server mounted at / hands + # out a page whose every script request falls through to the SPA fallback and comes back + # text/html — the UI never leaves "Loading…" while the API stays healthy (measured + # 2026-11-02, which is exactly how it hid behind an API-only verification). unit="$REPO_ROOT/dankserver/audiobookshelf/quadlet/audiobookshelf.container" grep -q '^Environment=PORT=8000$' "$unit" || return 1 - grep -q '^Environment=ROUTER_BASE_PATH=/$' "$unit" || return 1 + grep -q '^Environment=ROUTER_BASE_PATH=/audiobookshelf$' "$unit" || return 1 ! grep -qE '^Environment=ABS_' "$unit" || return 1 }