fix(docker): harden data-dir permissions and surface actionable DB errors
The bind-mount permission fix only takes effect after a rebuild, so a stale image keeps failing with the opaque "unable to open database file". This hardens both sides so any remaining failure is self-explanatory: - docker-entrypoint.sh: fall back to chmod 777 when chown is unsupported (network/9p mounts), and fall back to running as root when su-exec is unavailable, so the data dir is always writable on any filesystem. - server/db.mjs: wrap the DatabaseSync open in a try/catch and, on failure, report the exact path, whether the directory is writable, and the process UID instead of the bare SQLite error. Verified with a real container: a normal bind mount serves /health and creates trxtd.db; a read-only mount now prints the directory-permission diagnostic instead of the raw SQLite error.
This commit is contained in:
parent
1abc08b60f
commit
d400a4a2a0
2 changed files with 41 additions and 7 deletions
|
|
@ -1,11 +1,21 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Ensure the data directory exists and is owned by the node user.
|
# Ensure the data directory exists and is writable by the app user on every
|
||||||
# This fixes permission issues with bind-mounted volumes (e.g. ./data:/app/data)
|
# container start. Bind-mounted host directories (docker-compose ./data,
|
||||||
# where the host directory is owned by root but the container runs as node.
|
# 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
|
mkdir -p /app/data
|
||||||
chown -R node:node /app/data 2>/dev/null || true
|
|
||||||
|
|
||||||
# Drop privileges and run the actual application as node
|
# Prefer chown (sets node:node). On filesystems that do not support chown
|
||||||
exec su-exec node "$@"
|
# (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 "$@"
|
||||||
|
|
@ -9,7 +9,31 @@ if (!fs.existsSync(DATA_DIR)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DB_PATH = path.join(DATA_DIR, 'trxtd.db')
|
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)
|
// Performance optimizations (WAL mode, normal synchronous)
|
||||||
db.exec(`
|
db.exec(`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue