From e61f90745bc045b3989698f609258e52db9a88de Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 3 Aug 2026 22:22:29 +0200 Subject: [PATCH] v0.8.05-beta - een onbereikbaar pad liet zich gewoon opslaan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oorzaak van de lege appslijst: LIBRARY_DIR stond op /srv/serverup/stacks terwijl alleen /opt/serverup gekoppeld is. Dat pad bestaat binnen de container niet, dus geen apps in de lijst en alle containers onder "handmatig gestart" — want beheerd leest dezelfde map. Nergens stond waarom. paden.bereikbaar() bestond al sinds v0.7.92 maar hing alleen aan het veld Hoofdmap en aan het verhuizen. De drie padvelden schrijven rechtstreeks naar PUT /api/settings, en daar stond geen wacht. - PUT /api/settings weigert nu een pad waar Server Up niet bij kan, met de regel voor .env erbij. Een ongewijzigd pad blokkeert niets, anders kun je met een kapotte instelling niets meer wijzigen - een lege appslijst zegt nu waarom in plaats van "Geen apps gevonden" Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy --- CHANGELOG.md | 24 ++++++++++++++ VERSION | 2 +- server-up/app.py | 20 ++++++++++++ server-up/templates/index.html | 21 ++++++++++-- tests/test_paden.py | 59 ++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c677d1..9bed7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +# v0.8.05-beta — Een onbereikbaar pad liet zich gewoon opslaan + +Dit is de oorzaak van de lege appslijst waar de vorige vier uitgaven omheen +hebben gezocht. + +`LIBRARY_DIR` stond op `/srv/serverup/stacks`, terwijl alleen `/opt/serverup` in +de container gekoppeld is. Dat pad bestaat binnen de container niet eens — dus +geen enkele app in de lijst, en alle containers onder "handmatig gestart", want +`beheerd` leest dezelfde map. Nergens stond waarom. + +De controle daarop bestond al sinds v0.7.92: `paden.bereikbaar()` weet dat de +container alleen bij paden onder `BASE_DIR` kan, en geeft de exacte regel die je +in `.env` moet zetten. Hij hing alleen aan het veld **Hoofdmap** en aan het +**verhuizen**. De drie padvelden eronder schrijven rechtstreeks naar +`PUT /api/settings` — en daar stond geen wacht. Een pad buiten de koppeling liet +zich dus gewoon opslaan, waarna alles stilviel. + +- `PUT /api/settings` weigert nu een `LIBRARY_DIR`, `DATA_DIR` of `BACKUP_DIR` + waar Server Up niet bij kan, met dezelfde uitleg: welke regel in `.env`, en + welk commando erna. Een ongewijzigd pad blokkeert niets, anders kun je met een + al kapotte instelling niets meer wijzigen. +- Is de lijst leeg, dan zegt het scherm nu **waarom**: niet "Geen apps + gevonden", maar dat Server Up er niet bij kan en wat eraan te doen is. + # v0.8.04-beta — `--doctor` vraagt het de app in plaats van het af te leiden Drie keer op rij heb ik de verkeerde oorzaak aangewezen voor een lege diff --git a/VERSION b/VERSION index d909f80..9344553 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.04-beta +0.8.05-beta diff --git a/server-up/app.py b/server-up/app.py index cfd99c8..8c6c357 100644 --- a/server-up/app.py +++ b/server-up/app.py @@ -2937,6 +2937,26 @@ def api_settings_put(): if "NOTIFY_CHANNEL" in updates: if (updates.get("NOTIFY_CHANNEL") or "") not in notify.KANALEN: return jsonify(ok=False, msg="Onbekend meldingskanaal"), 400 + # Kan Server Up bij de mappen die je opgeeft? De container ziet alleen wat + # via BASE_DIR gekoppeld is. Een pad daarbuiten laat zich hier prima + # opslaan, en daarna kijkt de app in een map die binnen de container niet + # bestaat: lege appslijst, alle containers onder "handmatig gestart", en + # nergens een melding. + # + # Deze controle bestond al voor de hoofdmap en voor het verhuizen, maar de + # drie velden schrijven rechtstreeks hierheen — dus liep het langs de wacht. + for sleutel in ("LIBRARY_DIR", "DATA_DIR", "BACKUP_DIR"): + if sleutel not in updates: + continue + nieuw_pad = str(updates[sleutel] or "").strip().rstrip("/") + if not nieuw_pad or nieuw_pad == (current.get(sleutel) or "").rstrip("/"): + continue + controle = paden.bereikbaar(nieuw_pad) + if not controle.get("ok"): + return jsonify(**{**controle, "veld": sleutel, + "msg": _padfout_tekst(controle)}), 400 + updates[sleutel] = nieuw_pad + if "NOTIFY_EVENTS" in updates: ev = updates["NOTIFY_EVENTS"] if not isinstance(ev, list) or any(e not in notify.EVENTS for e in ev): diff --git a/server-up/templates/index.html b/server-up/templates/index.html index 23c7cdc..28ab5f2 100644 --- a/server-up/templates/index.html +++ b/server-up/templates/index.html @@ -480,7 +480,11 @@ tailwind.config = { -

Geen apps gevonden

+
+ +

Geen apps gevonden

+

+

@@ -2528,6 +2532,7 @@ function app() { storeQuery: '', storeTag: '', storeCat: '', filterOpen: false, filterTagQuery: '', stackMenu: '', stackFilterOpen: false, hoofdmap: '', afgeleidePaden: {}, padCheck: {}, padenVoorWijziging: {}, + padWaarschuwing: '', padVelden: [ { sleutel: 'LIBRARY_DIR', label: 'library_dir' }, { sleutel: 'DATA_DIR', label: 'appdata_dir' }, @@ -2869,7 +2874,19 @@ function app() { }, async loadDashboard() { await this.loadDocker(); await this.loadStacks(); }, - async loadStacks() { const d = await this.rpc('/api/stacks'); this.stacks = Array.isArray(d) ? d : (d.stacks || []); }, + async loadStacks() { + const d = await this.rpc('/api/stacks'); + this.stacks = Array.isArray(d) ? d : (d.stacks || []); + // Geen apps? Dan is de vraag of dat klopt of dat Server Up er niet bij + // kan. Een lege lijst met "Geen apps gevonden" liet dat verschil weg, en + // een pad buiten de gekoppelde map ziet er in de instellingen prima uit. + this.padWaarschuwing = ''; + if (!this.stacks.length && this.settings.LIBRARY_DIR) { + const p = await this.rpc('/api/paths/check?path=' + + encodeURIComponent(this.settings.LIBRARY_DIR)); + if (p && !p.ok) this.padWaarschuwing = p.msg || ''; + } + }, async loadCategories() { const d = await this.rpc('/api/categories?lang=' + (localStorage.getItem('lang') || 'nl')); this.categories = d.categories || []; diff --git a/tests/test_paden.py b/tests/test_paden.py index 2cfa6b7..1247d45 100644 --- a/tests/test_paden.py +++ b/tests/test_paden.py @@ -367,3 +367,62 @@ def test_puid_volgt_het_account_van_server_up(client, env): velden = {f["name"]: f for f in d["fields"]} assert velden["puid"]["default"] == os.getuid() assert velden["pgid"]["default"] == os.getgid() + + +# ── Een pad opslaan dat de app niet kan bereiken ───────────────────────────── +# De controle bestond al voor de hoofdmap en voor het verhuizen, maar de drie +# padvelden schrijven rechtstreeks naar PUT /api/settings — dus liep het langs +# de wacht. Sla je daar /srv/... op terwijl alleen /opt/serverup gekoppeld is, +# dan kijkt de app in een map die binnen de container niet bestaat: lege +# appslijst, alle containers onder "handmatig gestart", nergens een melding. + +def test_onbereikbaar_pad_wordt_niet_opgeslagen(client, env, monkeypatch): + from core import paden + csrf = login(client) + # De testomgeving zet LIBRARY_DIR als omgevingsvariabele, en die wint van + # het configuratiebestand — dan zou je niet zien of er iets is opgeslagen. + monkeypatch.delenv("LIBRARY_DIR", raising=False) + monkeypatch.setattr(paden, "_in_container", lambda: True) + monkeypatch.setattr(paden, "_mountpunten", lambda: ["/opt/serverup", "/data"]) + + r = client.put("/api/settings", json={"LIBRARY_DIR": "/srv/serverup/stacks"}, + headers={"X-CSRF-Token": csrf}) + assert r.status_code == 400, r.get_json() + d = r.get_json() + assert d["veld"] == "LIBRARY_DIR" + assert "BASE_DIR=/srv/serverup" in d["msg"], d["msg"] + + import core + assert core.load()["LIBRARY_DIR"] != "/srv/serverup/stacks", \ + "het pad is toch opgeslagen" + + +def test_een_pad_binnen_de_koppeling_mag_gewoon(client, env, monkeypatch, tmp_path): + from core import paden + csrf = login(client) + monkeypatch.delenv("LIBRARY_DIR", raising=False) + monkeypatch.setattr(paden, "_in_container", lambda: True) + monkeypatch.setattr(paden, "_mountpunten", lambda: [str(tmp_path)]) + + doel = tmp_path / "serverup" / "stacks" + r = client.put("/api/settings", json={"LIBRARY_DIR": str(doel)}, + headers={"X-CSRF-Token": csrf}) + assert r.status_code == 200, r.get_json() + import core + assert core.load()["LIBRARY_DIR"] == str(doel) + + +def test_ongewijzigde_paden_blokkeren_het_opslaan_niet(client, env, monkeypatch): + """Anders kun je met een al kapot pad geen enkele instelling meer wijzigen.""" + from core import paden + import core + csrf = login(client) + huidig = core.load()["LIBRARY_DIR"] + monkeypatch.setattr(paden, "_in_container", lambda: True) + monkeypatch.setattr(paden, "_mountpunten", lambda: ["/nergens"]) + + r = client.put("/api/settings", + json={"LIBRARY_DIR": huidig, "TIMEZONE": "Europe/Amsterdam"}, + headers={"X-CSRF-Token": csrf}) + assert r.status_code == 200, r.get_json() + assert core.load()["TIMEZONE"] == "Europe/Amsterdam"