From 94a2c98252331f9c2713d5cddcdc90b2ff5ba803 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 18 Jul 2026 14:44:38 +0200 Subject: [PATCH 1/2] bootstrap-admin.mjs: Zugangsdaten nicht mehr verlieren, wenn ZUGANGSDATEN.md nicht beschreibbar ist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Admin-Benutzer wird immer in der Datenbank angelegt, bevor versucht wird, die generierten Zugangsdaten in ZUGANGSDATEN.md zu schreiben. Schlug dieser Schreibvorgang fehl (z. B. EACCES, weil die per Bind-Mount eingebundene Host-Datei root gehört und der App-Container als nicht-root läuft), flog bisher eine ungefangene Exception – das Skript brach ab, BEVOR das Passwort irgendwo protokolliert wurde. Der Admin existierte dann in der DB, aber niemand kannte sein Passwort mehr. appendFileSync() läuft jetzt in einem try/catch; schlägt der Dateizugriff fehl, werden E-Mail und Passwort stattdessen direkt ins Log geschrieben, und das Skript beendet sich weiterhin mit Exitcode 0 (die eigentlich kritische Operation – der DB-Insert – ist ja bereits erfolgreich). Lokal verifiziert: Skript bricht bei einer nicht beschreibbaren ZUGANGSDATEN.md nicht mehr ab, Zugangsdaten landen im Log, zweiter Lauf bleibt idempotent. DEPLOYMENT.md: Schritt 2 ergänzt um "chmod 666 ZUGANGSDATEN.md" (der eigentliche Auslöser), neue Fehlerbehebung-Einträge für ECONNREFUSED-Startreihenfolge und diesen EACCES-Fall inkl. Recovery-Anleitung für einen bereits verwaisten Admin-Account. --- DEPLOYMENT.md | 40 ++++++++++++++++++++++++++++++++++++- scripts/bootstrap-admin.mjs | 25 +++++++++++++++++++---- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index b5c6a2b..3d76501 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -51,10 +51,12 @@ Docker-Compose-Start ignoriert. Zusätzlich eine leere Datei für die automatisch generierten Admin-Zugangsdaten anlegen, da sie per Bind-Mount in den Container -eingebunden wird: +eingebunden wird. Der App-Container läuft als nicht-root-Nutzer – +damit er die Datei beschreiben darf, muss sie für alle schreibbar sein: ```bash touch ZUGANGSDATEN.md +chmod 666 ZUGANGSDATEN.md ``` ## 3. App starten @@ -176,6 +178,42 @@ docker compose exec db psql -U postgres -c "ALTER USER postgres WITH PASSWORD '< docker compose --env-file .env.local up -d ``` +### `Error: connect ECONNREFUSED ...:5432` beim Start + +Normal beim allerersten Start: Der App-Container versucht sich zu +verbinden, bevor PostgreSQL im `db`-Container fertig hochgefahren ist. +Dank `restart: unless-stopped` startet der App-Container automatisch +neu und verbindet sich, sobald die Datenbank bereit ist (kann ein paar +Sekunden bis wenige Minuten dauern). Mit `docker compose logs app -f` +verfolgen – taucht der Fehler dauerhaft (mehrere Minuten) weiter auf, +`docker compose ps` prüfen, ob der `db`-Container tatsächlich dauerhaft +läuft. + +### `EACCES: permission denied, open '/app/ZUGANGSDATEN.md'` + +Die Datei wurde ohne Schreibrecht für alle angelegt (Schritt 2 oben, +`chmod 666` fehlte). Der Admin-Benutzer wurde in diesem Fall trotzdem +in der Datenbank angelegt, nur die Zugangsdaten konnten nicht in die +Datei geschrieben werden – sie stehen dann stattdessen im Log +(`docker compose logs app` nach `bootstrap-admin – Admin-Passwort:` +durchsuchen). Für zukünftige Deployments zusätzlich die Berechtigung +korrigieren: + +```bash +chmod 666 ZUGANGSDATEN.md +``` + +Falls das Passwort auch im Log nicht mehr auffindbar ist (z. B. weil +Logs schon rotiert wurden), Admin-Konto neu anlegen lassen: + +```bash +docker compose exec db psql -U postgres -d canteen_menu -c "delete from users;" +chmod 666 ZUGANGSDATEN.md +docker compose restart app +docker compose logs app --tail 20 +cat ZUGANGSDATEN.md +``` + ## Updates einspielen ```bash diff --git a/scripts/bootstrap-admin.mjs b/scripts/bootstrap-admin.mjs index a0ffdc7..032614e 100644 --- a/scripts/bootstrap-admin.mjs +++ b/scripts/bootstrap-admin.mjs @@ -58,19 +58,36 @@ async function main() { [email, passwordHash] ); + // Der Admin-Datensatz steht ab hier in der Datenbank – das darf durch + // ein Problem beim Ablegen der Zugangsdaten nicht mehr verloren gehen + // (sonst gibt es einen Benutzer, dessen Passwort niemand kennt). + // appendFileSync() also bewusst nicht mehr ungefangen werfen lassen, + // sondern im Fehlerfall die Zugangsdaten direkt ins Log schreiben. const zugangsdatenPath = join( dirname(dirname(fileURLToPath(import.meta.url))), "ZUGANGSDATEN.md" ); const entry = `\n## Admin-Zugang (automatisch angelegt, ${new Date().toISOString().slice(0, 10)})\n\n- E-Mail: \`${email}\`\n- Passwort: \`${password}\`\n\nBitte nach dem ersten Login ändern.\n`; - if (existsSync(zugangsdatenPath)) { - appendFileSync(zugangsdatenPath, entry); + let wroteFile = false; + try { + if (existsSync(zugangsdatenPath)) { + appendFileSync(zugangsdatenPath, entry); + wroteFile = true; + } + } catch (err) { + console.warn(`bootstrap-admin – Konnte ${zugangsdatenPath} nicht beschreiben: ${err.message}`); + } + + if (wroteFile) { + console.log(`bootstrap-admin – Admin angelegt: ${email} (Passwort siehe ZUGANGSDATEN.md)`); } else { console.warn( - `bootstrap-admin – ${zugangsdatenPath} existiert nicht, Zugangsdaten werden nur geloggt:` + `bootstrap-admin – ${zugangsdatenPath} existiert nicht oder ist nicht beschreibbar. ` + + `Zugangsdaten NUR HIER im Log, bitte sofort notieren:` ); + console.log(`bootstrap-admin – Admin-E-Mail: ${email}`); + console.log(`bootstrap-admin – Admin-Passwort: ${password}`); } - console.log(`bootstrap-admin – Admin angelegt: ${email} (Passwort siehe ZUGANGSDATEN.md)`); } finally { await client.end(); } From b63822060d0976ad532ad558cd5f9d5e3a788380 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 18 Jul 2026 14:51:29 +0200 Subject: [PATCH 2/2] Version 1.6.2 --- CHANGELOG.md | 18 ++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e315b7d..e3a9c4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert. Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionierung nach [Semantic Versioning](https://semver.org/lang/de/). +## [1.6.2] – 2026-07-18 + +### Behoben +- `scripts/bootstrap-admin.mjs`: Konnte `ZUGANGSDATEN.md` nicht + geschrieben werden (z. B. `EACCES`, weil die per Bind-Mount + eingebundene Host-Datei root gehört und der App-Container als + nicht-root läuft), brach das Skript ab, nachdem der Admin-Benutzer + bereits in der Datenbank angelegt war – das generierte Passwort war + dann nirgends mehr zu finden. Schreibt jetzt bei einem Fehler + stattdessen E-Mail/Passwort ins Log und beendet sich weiterhin + erfolgreich. +- DEPLOYMENT.md: Schritt 2 fehlte `chmod 666 ZUGANGSDATEN.md` (der + eigentliche Auslöser des Bugs oben). Neue Fehlerbehebung-Einträge für + `ECONNREFUSED` beim Start (harmlose Start-Reihenfolge, löst sich + durch den automatischen Neustart von selbst) und für diesen + `EACCES`-Fall inkl. Recovery-Anleitung für einen bereits verwaisten + Admin-Account. + ## [1.6.1] – 2026-07-18 ### Geändert diff --git a/package-lock.json b/package-lock.json index 285d45b..e1e97a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "canteen-menu", - "version": "1.6.1", + "version": "1.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "canteen-menu", - "version": "1.6.1", + "version": "1.6.2", "dependencies": { "@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/server": "^13.3.2", diff --git a/package.json b/package.json index 976eebf..c289eed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "canteen-menu", - "version": "1.6.1", + "version": "1.6.2", "private": true, "scripts": { "predev": "node --env-file-if-exists=.env.local db/migrate.mjs && node --env-file-if-exists=.env.local scripts/bootstrap-admin.mjs",