2c86d7973f
- Navidrome password is now passed to curl via stdin (--data-urlencode "p@-") in navidrome-scan.sh and pipeline-status.sh, so it no longer appears in ps/proc. Verified the query sent is identical and a live scan still triggers. - MIN_ARTIST_DIRS (the share-health gate) is now a setting, threaded through to the pipeline env, so a user with a small library can lower it instead of the scan/sync being permanently blocked by the hardcoded 500. - /auth/logout is now POST-only (with a nav form + aria-label), so a drive-by GET can't log the user out; enforced allowed_email already landed separately. - view_log now confirms the run's log_path resolves under the logs dir before serving it (defense in depth). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.8 KiB
Bash
Executable File
63 lines
2.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# navidrome-scan.sh — trigger a Navidrome full scan ONLY if the music library
|
|
# share is healthy.
|
|
#
|
|
# Why this exists: with ND_SCANNER_PURGEMISSING=always (which we keep, so
|
|
# on-disk deletions propagate to playlists and out to Traktor), a full scan
|
|
# that runs while the QNAP NFS share is unavailable makes Navidrome see the
|
|
# entire library as "missing" and purge it — wiping every playlist. That is
|
|
# exactly what happened on 2026-06-01 and lost the manual dj-* playlists.
|
|
#
|
|
# This wrapper refuses to trigger the scan unless the share is mounted, a
|
|
# canary file is readable, and the library has a sane number of artist dirs.
|
|
# Skipping a scan is harmless (the next healthy run picks up changes); running
|
|
# one against a dead mount is catastrophic.
|
|
set -euo pipefail
|
|
|
|
: "${MUSIC_DATA_DIR:=/data/music}"
|
|
: "${ALEMBIC_CONFIG_DIR:=/config}"
|
|
NAVIDROME_ENV="$ALEMBIC_CONFIG_DIR/pipeline/navidrome/admin.env"
|
|
[ -f "$NAVIDROME_ENV" ] && source "$NAVIDROME_ENV"
|
|
|
|
LIB="$MUSIC_DATA_DIR/Library"
|
|
CANARY="$LIB/.navidrome-canary"
|
|
MIN_ARTIST_DIRS="${MIN_ARTIST_DIRS:-500}" # override via env for a small library
|
|
ND="${ND_BASE:-http://navidrome:4533}"
|
|
ND_USER="${ND_USER:-}"
|
|
ND_PASS="${ND_PASS:-}"
|
|
|
|
log() { echo "[$(date -Iseconds)] navidrome-scan: $*"; }
|
|
|
|
# `mountpoint -q` on the host detected an unmounted/dead NFS share directly.
|
|
# Running inside a container against a bind-mounted view, that check doesn't
|
|
# apply (a bind mount has no distinct device number to detect) — the canary
|
|
# file + artist-dir-count checks below are what actually carry the
|
|
# 2026-06-01 protection now: an unhealthy/empty share fails both of them.
|
|
# (Confirmed as an intentional adaptation, not a silent drop — see alembic
|
|
# migration plan / MIGRATION.md.)
|
|
if [ ! -r "$CANARY" ]; then
|
|
log "ABORT: canary $CANARY missing/unreadable — share degraded — skipping scan"
|
|
exit 0
|
|
fi
|
|
n=$(find "$LIB" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l)
|
|
if [ "$n" -lt "$MIN_ARTIST_DIRS" ]; then
|
|
log "ABORT: only $n artist dirs under $LIB (< $MIN_ARTIST_DIRS) — library looks truncated — skipping scan"
|
|
exit 0
|
|
fi
|
|
|
|
log "library healthy ($n artist dirs) — triggering full scan"
|
|
# Pass the password via stdin (--data-urlencode "p@-"), not on the command
|
|
# line, so it isn't visible in ps/proc. printf %s avoids a trailing newline.
|
|
resp=$(printf '%s' "$ND_PASS" | curl -s -G "$ND/rest/startScan.view" \
|
|
--data-urlencode "u=$ND_USER" --data-urlencode "p@-" \
|
|
--data-urlencode 'v=1.16.0' --data-urlencode 'c=cron' \
|
|
--data-urlencode 'f=json' --data-urlencode 'fullScan=true' || true)
|
|
# Don't log "scan triggered" on faith: a rotated password or Navidrome error
|
|
# would otherwise report success forever while no scan ever runs.
|
|
if echo "$resp" | grep -q '"status":"ok"'; then
|
|
log "scan triggered"
|
|
else
|
|
log "ERROR: scan trigger failed — response: ${resp:-<empty/no response>}"
|
|
exit 1
|
|
fi
|