diff --git a/webapp/app.py b/webapp/app.py index 0bbf747..c17ab03 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -15,6 +15,7 @@ This file is the route surface; most logic lives in ``services/``: import json import os import shutil +import tempfile from datetime import datetime from pathlib import Path @@ -506,13 +507,29 @@ def imaging_set_laps(serial): if not isinstance(pw, str): return {"ok": False, "error": "password must be string"}, 400 if pw == "": - # Clear by direct read-modify-write since update_session skips empty values. - state = imaging_status.get_session(serial) or {} - if "laps_password" in state: - state.pop("laps_password", None) - # Re-feed everything (minus laps_password) through update_session. - state["serial"] = serial - imaging_status.update_session(state) + # Clear by direct file write. update_session() merges payload INTO + # existing state and skips empty values, so it cannot remove a key. + # Pop the laps_password key directly from the session JSON and + # write the result atomically. + path = imaging_status._path_for(serial) + if os.path.isfile(path): + try: + with open(path, "r") as f: + state = json.load(f) + except (json.JSONDecodeError, OSError): + state = {} + if "laps_password" in state: + state.pop("laps_password", None) + state["last_updated"] = imaging_status._now_iso() + fd, tmp = tempfile.mkstemp(dir=config.IMAGING_DIR, prefix=".tmp-", suffix=".json") + try: + with os.fdopen(fd, "w") as f: + json.dump(state, f, indent=2) + os.replace(tmp, path) + except Exception: + try: os.unlink(tmp) + except OSError: pass + raise return {"ok": True, "cleared": True} imaging_status.update_session({"serial": serial, "laps_password": pw}) return {"ok": True}