feat: stille Datenverlust-Pfade geschlossen, gemeinsame Shell-Lib (v0.7.0)
Vor dem Rollout durchgesehen und die verbliebenen Stellen geschlossen, an denen etwas schiefgehen konnte, ohne dass es irgendwo sichtbar wurde. Datenverlust: - veraPDF: das in [verapdf].binary konfigurierte Programm wird im Preflight geprueft. Bisher galt bei falschem Pfad JEDE Datei als "nicht konform" — Ergebnis nach error/, Original geloescht (Default delete). run_verapdf() trennt jetzt ausserdem ein echtes FAIL-Urteil von einer Stoerung (VeraPdfUnavailable: nicht startbar, abgestuerzt, kein PASS/FAIL in der Ausgabe). Bei Stoerung wandern Original UND Ergebnis nach error/, das Original wird nicht entsorgt. - Gleichnamige Dateien wurden in outgoing/, error/ und beim Ordner-Upload mit abweichendem target kommentarlos ueberschrieben. Jetzt Zeitstempel daneben, mit Warnung; ProcessResult.output traegt den echten Pfad. Robustheit: - Kaputtes oder nicht lesbares TOML beim Start: Exit 2 statt Traceback. - RestartPreventExitStatus=2 in der Unit — Exit 2 (Config/Preflight) laeuft nicht mehr endlos neu, die Instanz bleibt sichtbar failed stehen. - Toter watchdog-Observer wird erkannt: Exit 3, systemd setzt den Watch neu auf. Vorher blieb die Unit "active" und verarbeitete nichts mehr. - Relative Pfade in [paths]/archive_dir/target sind ein Config-Fehler statt still unter /opt zu landen. - Fehler beim Archivieren entwertet den Durchlauf nicht mehr: Upload und Mail laufen, Sichtbarkeit ueber log.error + "OK mit Warnung"-Mail. - Nicht-PDFs in incoming/ werden beim Start-Scan gesammelt gemeldet. - Logging explizit nach stdout (die Doku versprach das schon). Struktur: - Neue lib/common.sh, von install.sh und update.sh gesourct. Die doppelte venv_is_healthy() gibt es nur noch einmal, in der gruendlichen Fassung — die schlanke in install.sh haette eine nach einem Distro-Sprung kaputte venv als gesund durchgewunken (nachgewiesen). - install.sh warnt in Containern, wenn systemd-journald nicht laeuft. Doku: Dateisystem-Festlegung (ext4/xfs/zfs, kein CIFS/NFS wegen inotify), Debian 13 in LXC auf Proxmox scheitert an journald (243/CREDENTIALS, AppArmor blockiert sd-mkdcreds) inkl. Abhilfe, echte Speicher-Messwerte, Exit-Code-Tabelle. 254 Tests gruen (vorher 152). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,29 @@ def _section(data: dict[str, Any], *keys: str) -> dict[str, Any]:
|
||||
return cur if isinstance(cur, dict) else {}
|
||||
|
||||
|
||||
def _require_absolute(value: str, label: str, cfg_path: Path,
|
||||
beispiel: str) -> None:
|
||||
"""Weist relative Pfadangaben zurück.
|
||||
|
||||
Ein relativer Pfad wird gegen das Arbeitsverzeichnis des Prozesses
|
||||
aufgelöst — bei der systemd-Unit also gegen `WorkingDirectory`
|
||||
(/opt/pdf-ocr-hotfolder). `incoming = "in"` legte damit stillschweigend
|
||||
/opt/pdf-ocr-hotfolder/in an: der Scanner schreibt woanders hin als der
|
||||
Dienst schaut, und niemand sieht einen Fehler. Absolute Pfade sind die
|
||||
einzige sinnvolle Angabe; install.sh erzeugt ohnehin nur solche.
|
||||
"""
|
||||
if not value or Path(value).is_absolute():
|
||||
return
|
||||
raise ConfigError(
|
||||
f"{cfg_path}: {label} = {value!r} ist ein relativer Pfad. Hier sind "
|
||||
f"nur absolute Pfade zulässig — ein relativer würde gegen das "
|
||||
f"Arbeitsverzeichnis des Dienstes aufgelöst "
|
||||
f"(WorkingDirectory, also z.B. /opt/pdf-ocr-hotfolder/{value}) und "
|
||||
f"nicht gegen das Verzeichnis, in dem die Config liegt. "
|
||||
f'Bitte absolut angeben, z.B. "{beispiel}".'
|
||||
)
|
||||
|
||||
|
||||
def _require_path(p: dict[str, Any], key: str, cfg_path: Path) -> Path:
|
||||
"""Holt einen Pflicht-Pfad aus der [paths]-Sektion.
|
||||
|
||||
@@ -144,7 +167,10 @@ def _require_path(p: dict[str, Any], key: str, cfg_path: Path) -> Path:
|
||||
f'{key} = "/var/lib/pdf-ocr-hotfolder/{key}" '
|
||||
f"— siehe config.example.toml."
|
||||
)
|
||||
return Path(str(value))
|
||||
value = str(value)
|
||||
_require_absolute(value, f"In der Sektion [paths] der Eintrag '{key}'",
|
||||
cfg_path, f"/var/lib/pdf-ocr-hotfolder/{key}")
|
||||
return Path(value)
|
||||
|
||||
|
||||
def _unknown_in(data: dict[str, Any], keys: tuple[str, ...],
|
||||
@@ -218,6 +244,13 @@ def load_config(path: str | Path) -> Config:
|
||||
email = EmailNotify(**{k: v for k, v in _section(data, "notify", "email").items()
|
||||
if k in EmailNotify.__annotations__})
|
||||
|
||||
# Dieselbe Regel wie für [paths]: beides sind Verzeichnisse, in die der
|
||||
# Dienst schreibt, und beide wären relativ aufgelöst schlicht falsch.
|
||||
_require_absolute(str(output.archive_dir), "[output].archive_dir", path,
|
||||
"/var/lib/pdf-ocr-hotfolder/archive")
|
||||
_require_absolute(str(folder.target), "[upload.folder].target", path,
|
||||
"/srv/scans/fertig")
|
||||
|
||||
log_level = _section(data, "logging").get("level", "INFO")
|
||||
|
||||
return Config(
|
||||
|
||||
Reference in New Issue
Block a user