mirror of
https://github.com/Joulenap/joulenap.git
synced 2026-08-11 13:21:43 +02:00
The three silent ones first. A 0.9 config that fails to convert used to boot an empty config that looks exactly like a fresh install: the reason now reaches the UI (GET /api/status.config_error), the activity log and an ERROR line, and the .bak parachute is written on the failure branch too, so a later save from the Advanced tab cannot destroy the original. "PBS left powered on" was wrong in both directions -- every successful run against an always-on PBS warned, and a sync route that left its target awake did not. lease.release() returned one False for four situations, only two of which cost power; it now returns a ReleaseOutcome that names the reason, which becomes both the POWEROFF step's detail and RunContext.left_on. The interrupted-run path keeps a step-derived rule, now paired per device and filtered by managed_power. 422 bodies echoed the whole config, secrets included: a config-level validator raises at loc=(), so pydantic attached every token, the secret key, the password hash, the SMTP and bot tokens as the error's input. One helper with include_input=False now serves all three config-shaped 422 sites. Also: a redaction placeholder with nothing to resolve against is rejected instead of silently clearing the credential (a renamed device id, or a create from a copied body); the ad-hoc "Run verify" asks for outdated_after=0, since None meant "only never-verified" and skipped exactly the snapshots the button exists for; the manual power-off holds the single-run lock so it cannot cut a vzdump that started in the check-then-act gap; _current_run_id is cleared when a run ends, so a stop landing between two runs cannot hit the wrong one; and the pre-migration .bak is chmod 0600 like every other secret-bearing file. Tests: 617 passed, 2 skipped. Every finding was reproduced against the real code before the fix, and each new test confirmed failing on the pre-fix code.