diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index dc93ab7..d6f36c7 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,11 +1,21 @@ #!/bin/sh set -e -# Ensure the data directory exists and is owned by the node user. -# This fixes permission issues with bind-mounted volumes (e.g. ./data:/app/data) -# where the host directory is owned by root but the container runs as node. +# Ensure the data directory exists and is writable by the app user on every +# container start. Bind-mounted host directories (docker-compose ./data, +# Unraid appdata, etc.) are often owned by root or "nobody", which would +# otherwise make the unprivileged "node" user unable to create trxtd.db. mkdir -p /app/data -chown -R node:node /app/data 2>/dev/null || true -# Drop privileges and run the actual application as node -exec su-exec node "$@" \ No newline at end of file +# Prefer chown (sets node:node). On filesystems that do not support chown +# (some network/9p mounts), fall back to making the directory world-writable. +if ! chown -R node:node /app/data 2>/dev/null; then + chmod 777 /app/data 2>/dev/null || true +fi + +# Drop privileges to the unprivileged node user when su-exec is available, +# otherwise run as root (still fully functional). +if command -v su-exec >/dev/null 2>&1; then + exec su-exec node "$@" +fi +exec "$@" \ No newline at end of file diff --git a/server/db.mjs b/server/db.mjs index 4e63bb4..90cda15 100644 --- a/server/db.mjs +++ b/server/db.mjs @@ -9,7 +9,31 @@ if (!fs.existsSync(DATA_DIR)) { } const DB_PATH = path.join(DATA_DIR, 'trxtd.db') -const db = new DatabaseSync(DB_PATH) + +// Fail fast with an actionable message when the database file cannot be +// opened (most commonly a bind-mounted volume the process user cannot +// write to). The default node:sqlite error only says "unable to open +// database file" and hides the path and permission problem. +let db +try { + db = new DatabaseSync(DB_PATH) +} catch (err) { + let writable = false + try { + fs.accessSync(DATA_DIR, fs.constants.W_OK) + writable = true + } catch { + /* directory not writable */ + } + const uid = typeof process.getuid === 'function' ? process.getuid() : 'unbekannt' + console.error( + `[db] Datenbank konnte nicht geöffnet werden: ${DB_PATH}\n` + + ` Verzeichnis ${DATA_DIR} ist ${writable ? 'beschreibbar' : 'NICHT beschreibbar'} ` + + `(Prozess läuft als UID ${uid}).\n` + + ` Bitte prüfen, ob das Volume auf ${DATA_DIR} gemountet und beschreibbar ist.`, + ) + throw err +} // Performance optimizations (WAL mode, normal synchronous) db.exec(`