fix: onthoud gesloten startwizard
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 6m7s

This commit is contained in:
Ramon 2026-08-04 22:16:11 +02:00
parent 6c84def759
commit 75eb24741f
6 changed files with 60 additions and 8 deletions

View file

@ -1,3 +1,18 @@
# v0.8.16-beta — De startwizard blijft gesloten
**De knop Sluiten verborg de optionele startwizard alleen tijdelijk.** De
server kreeg daardoor nooit door dat de gebruiker de wizard had overgeslagen en
opende hem bij iedere nieuwe paginalading opnieuw.
- **Sluiten wordt blijvend onthouden.** De knop markeert de wizard nu via de API
als afgerond en sluit pas nadat de server dit heeft opgeslagen.
- **Een ontbrekende appmap reset de wizard niet meer.** Een nog niet gemounte of
tijdelijk onbereikbare library wordt als padprobleem behandeld en overschrijft
niet langer een expliciete keuze van de gebruiker.
- **Bewust opnieuw starten blijft mogelijk.** De knop “Opnieuw uitvoeren” in
Instellingen wist de voltooiingsstatus en opent de wizard weer vanaf het
begin.
# v0.8.15-beta — Apps verwijderen en mobiele schermen werken weer # v0.8.15-beta — Apps verwijderen en mobiele schermen werken weer
**De verwijderknop stopte containers en verwijderde volumes, maar liet de **De verwijderknop stopte containers en verwijderde volumes, maar liet de

View file

@ -1 +1 @@
0.8.15-beta 0.8.16-beta

View file

@ -325,9 +325,6 @@ def load() -> dict:
val = cfg.get(key, "").rstrip("/") val = cfg.get(key, "").rstrip("/")
if val in _unsafe or not val.startswith("/"): if val in _unsafe or not val.startswith("/"):
cfg[key] = DEFAULTS[key] cfg[key] = DEFAULTS[key]
# Auto-reset wizard als LIBRARY_DIR niet bestaat (verse installatie met oud volume)
if cfg.get("WIZARD_DONE") and not Path(cfg["LIBRARY_DIR"]).exists():
cfg["WIZARD_DONE"] = False
# Env-overrides gelden alleen voor eenvoudige waarden; lijsten en objecten # Env-overrides gelden alleen voor eenvoudige waarden; lijsten en objecten
# (APP_REPOS, AUTH, NETWORKS, …) zouden anders een kale string worden. # (APP_REPOS, AUTH, NETWORKS, …) zouden anders een kale string worden.
for k, default in DEFAULTS.items(): for k, default in DEFAULTS.items():

View file

@ -2477,7 +2477,7 @@ tailwind.config = {
<div x-show="wizardStapId() === 'klaar'"><p class="text-sm">We synchroniseren nu de repos en zijn klaar voor gebruik.</p></div> <div x-show="wizardStapId() === 'klaar'"><p class="text-sm">We synchroniseren nu de repos en zijn klaar voor gebruik.</p></div>
</div> </div>
<div class="modal-foot !px-5 sm:!px-7"> <div class="modal-foot !px-5 sm:!px-7">
<button class="btn" @click="wizard.open = false" x-text="t('close')">Sluiten</button> <button class="btn" @click="dismissWizard()" x-text="t('close')">Sluiten</button>
<button class="btn" @click="wizard.step = Math.max(0, wizard.step-1)" :disabled="wizard.step===0" x-text="t('prev')">Terug</button> <button class="btn" @click="wizard.step = Math.max(0, wizard.step-1)" :disabled="wizard.step===0" x-text="t('prev')">Terug</button>
<button class="btn btn-primary" @click="wizardNext()"><span x-text="wizard.step < wizard.steps.length-1 ? 'Volgende' : 'Klaar'"></span></button> <button class="btn btn-primary" @click="wizardNext()"><span x-text="wizard.step < wizard.steps.length-1 ? 'Volgende' : 'Klaar'"></span></button>
</div> </div>
@ -4236,6 +4236,17 @@ function app() {
this.wizard.BACKUP_DIR = d.defaults?.BACKUP_DIR || '/opt/serverup/backups'; this.wizard.BACKUP_DIR = d.defaults?.BACKUP_DIR || '/opt/serverup/backups';
this.wizard.open = true; this.wizard.open = true;
}, },
// De wizard is volledig optioneel. Ook voortijdig sluiten wordt op
// de server onthouden, zodat hij niet na elke paginalading terugkomt.
async dismissWizard() {
const d = await this.rpc('/api/wizard/complete', {});
if (!d.ok) {
this.toast(d.msg || d.error || this.t('error'), 'er');
return false;
}
this.wizard.open = false;
return true;
},
// Opnieuw uitvoeren: markeer als niet-afgerond en open hem. // Opnieuw uitvoeren: markeer als niet-afgerond en open hem.
async resetWizard() { async resetWizard() {
await this.rpc('/api/wizard/reset', {}); await this.rpc('/api/wizard/reset', {});
@ -4296,9 +4307,7 @@ function app() {
} else { } else {
const d = await this.rpc('/api/wizard/sync', {}); const d = await this.rpc('/api/wizard/sync', {});
if (d.job_id) this.pollJob(d.job_id, async () => { if (d.job_id) this.pollJob(d.job_id, async () => {
await this.rpc('/api/wizard/complete', {}); if (await this.dismissWizard()) this.loadDashboard();
this.wizard.open = false;
this.loadDashboard();
}); });
} }
}, },

View file

@ -349,6 +349,19 @@ check('balk staat halverwege', c.job.progress.pct === 50);
// De poortcontrole praat met de backend, dus die checks zijn asynchroon. // De poortcontrole praat met de backend, dus die checks zijn asynchroon.
// Top-level await kan niet in een CommonJS-script; vandaar deze afsluiting. // Top-level await kan niet in een CommonJS-script; vandaar deze afsluiting.
(async () => { (async () => {
// De wizard is optioneel. Ook "Sluiten" moet daarom blijvend onthouden dat
// de gebruiker hem niet bij iedere paginalading opnieuw wil zien.
let wizardVerzoek = null;
c.wizard.open = true;
c.rpc = async (url, body) => {
wizardVerzoek = {url, body};
return {ok: true};
};
await c.dismissWizard();
check('wizard sluiten wordt op de server bewaard',
wizardVerzoek.url === '/api/wizard/complete' &&
c.wizard.open === false);
// ── App verwijderen ─────────────────────────────────────────────────────── // ── App verwijderen ───────────────────────────────────────────────────────
// De veilige keuze hoort de appconfiguratie weg te halen en data te bewaren. // De veilige keuze hoort de appconfiguratie weg te halen en data te bewaren.
let verwijderVerzoek = null; let verwijderVerzoek = null;

18
tests/test_wizard.py Normal file
View file

@ -0,0 +1,18 @@
"""De startwizard blijft gesloten nadat de gebruiker hem heeft afgerond."""
from conftest import login
def test_voltooide_wizard_blijft_voltooid_als_library_niet_bestaat(
client, env):
login(client)
ontbrekend = env["tmp"] / "nog-niet-gemounte-library"
env["core"].patch({
"LIBRARY_DIR": str(ontbrekend),
"WIZARD_DONE": True,
})
antwoord = client.get("/api/wizard")
assert antwoord.status_code == 200
assert antwoord.get_json()["done"] is True