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:
2026-09-23 00:59:17 +02:00
parent 305454eeb5
commit cd803a3dfe
28 changed files with 2902 additions and 286 deletions
+22 -4
View File
@@ -20,11 +20,13 @@ Weiter: [CHANGELOG.md](CHANGELOG.md) · [AI_AGENT_BRIEFING.md](AI_AGENT_BRIEFING
- 🔁 **Parallelverarbeitung** mehrerer PDFs (ThreadPool, konfigurierbar)
- ♻️ **Wiederaufnahme aus `working/`** nach einem harten Stopp — keine Datei bleibt liegen
- ✅ **PDF/A-Output** (1, 2 oder 3) optional
- 🛡️ **veraPDF-Validierung** optional
- 🛡️ **veraPDF-Validierung** optional — Binary wird im Preflight geprüft, eine Störung gilt nicht als FAIL
- 🚫 **Überschreibt nie eine gleichnamige Datei** — in `outgoing/`, `error/`, Archiv und Ordner-Upload weicht sie mit Zeitstempel aus
- ☁️ **Upload-Ziele**: lokaler Ordner, Nextcloud (WebDAV via Python), SFTP
- 📧 **E-Mail-Notify** (immer / nur Fehler / nie)
- 🔐 **Service-User-Support** für lokale **und AD-User mit lokaler UID** (SSSD/Winbind)
- ⚙️ Saubere systemd-Integration mit auto-Restart, **Multi-Instanz** über eine Template-Unit
- 👁️ **Toter Verzeichnis-Watch wird erkannt** — der Dienst beendet sich (Exit 3), systemd setzt den Watch neu auf
- 🩺 **`--check-config`** prüft eine Instanz-Config ohne etwas zu verarbeiten
## Schnellstart
@@ -32,6 +34,10 @@ Weiter: [CHANGELOG.md](CHANGELOG.md) · [AI_AGENT_BRIEFING.md](AI_AGENT_BRIEFING
**Voraussetzungen:** Debian 12 oder 13, Python 3.11+, root — und **mindestens
2 GB RAM** (512 MB reichen für 300-dpi-Scans nachweislich nicht, siehe
[Systemanforderungen](docs/INSTALLATION.md#systemanforderungen)).
Dateisystem **ext4, xfs oder zfs**; `incoming/` muss **lokal** liegen — auf
einem CIFS/NFS-Mount liefert inotify keine Events und der Hotfolder bemerkt
neue Dateien nur noch beim Start
([warum](docs/INSTALLATION.md#dateisystem-ext4-xfs-oder-zfs)).
```bash
git clone gitea@gitea.sonith.de:sonith_ug/pdf-ocr-hotfolder.git
@@ -64,6 +70,7 @@ Nach einem Debian-Major-Upgrade: **[docs/OS-UPGRADE.md](docs/OS-UPGRADE.md)**.
| Pfad | Zweck |
|------|-------|
| `/opt/pdf-ocr-hotfolder/` | Code + venv (für alle Instanzen gemeinsam) |
| `/opt/pdf-ocr-hotfolder/lib/common.sh` | gemeinsame Shell-Funktionen für `install.sh`/`update.sh` (mitkopiert) |
| `/etc/pdf-ocr-hotfolder/<instanz>.toml` | Config pro Instanz (640, root:\<service-gruppe\>) |
| `/etc/systemd/system/pdf-ocr-hotfolder@.service` | systemd Template-Unit |
| `/var/lib/pdf-ocr-hotfolder/<instanz>/incoming` | Eingang (Scanner schreibt hier rein) |
@@ -83,7 +90,7 @@ Key-für-Key-Referenz: [docs/INSTALLATION.md](docs/INSTALLATION.md#konfiguration
| Sektion | Zweck |
|---------|-------|
| `[paths]` | `incoming`, `outgoing`, `working`, `error` — **Pflicht** |
| `[paths]` | `incoming`, `outgoing`, `working`, `error` — **Pflicht**, alle **absolut** |
| `[ocr]` | Sprachen, `jobs`, `skip_text`, `pdfa_level`, `deskew`, `max_workers`, `timeout` (Sekunden **pro Seite**) |
| `[output]` | Dateibenennung (`name_mode`/`name_tag`) und Original-Behandlung (`delete`/`archive`) |
| `[verapdf]` | optionale PDF/A-Validierung per CLI |
@@ -104,6 +111,17 @@ cd /opt/pdf-ocr-hotfolder && sudo ./venv/bin/python -m pdf_ocr_hotfolder \
Exit 0 = sauber, 1 = Warnungen, 2 = Fehler. Details:
[docs/UPDATE.md](docs/UPDATE.md#config-prüfung-per---check-config).
### Exit-Codes des Dienstes
| Exit | Bedeutung | Neustart durch systemd |
|------|-----------|------------------------|
| `0` | regulärer Stopp | — |
| `1` | nur bei `--once`: mindestens eine PDF fehlgeschlagen | — |
| `2` | Config- oder Preflight-Fehler | **nein** (`RestartPreventExitStatus=2`) — die Instanz bleibt sichtbar `failed` |
| `3` | Verzeichnis-Watch gestorben, es würden keine Dateien mehr erkannt | **ja**, genau dafür |
Vollständig: [docs/INSTALLATION.md](docs/INSTALLATION.md#exit-codes).
## Service-Verwaltung
```bash
@@ -150,7 +168,7 @@ gelöscht.
## Tests
```bash
pytest # 152 Tests
pytest # 254 Tests
```
`ocrmypdf` muss dafür nicht installiert sein — der Import ist lazy und wird in
@@ -162,5 +180,5 @@ MIT — © Sonith UG
---
**Version:** 0.6.3
**Version:** 0.7.0
**Repo:** https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder