From 606c0ed0dab74b3ae662f905719cf81e16abadb9 Mon Sep 17 00:00:00 2001 From: "Patrick Asmus (scriptos)" Date: Mon, 30 Mar 2026 00:41:27 +0200 Subject: [PATCH] docs: Dokumentation auf Feature-Stand von master aktualisiert --- README.md | 12 ++++++++++-- docs/README.md | 6 ++++-- docs/funktionsweise.md | 42 +++++++++++++++++++++++++++++++++++++++-- docs/schnellstart.md | 14 ++++++++++++-- docs/troubleshooting.md | 24 +++++++++++++++-------- 5 files changed, 82 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 00b7dfe..86034de 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,17 @@ Ein einfacher Daemon, der automatisch einen Geburtstagskalender für jede Mailco ## Kurzübersicht -- Liest Geburtstage aus allen CardDAV-Adressbüchern jeder Mailbox +- Liest Geburtstage und Jahrestage aus allen CardDAV-Adressbüchern jeder Mailbox - Erstellt und synchronisiert automatisch einen Geburtstagskalender pro Benutzer -- Synchronisation alle **15 Minuten** +- Konfigurierbarer Kalendername (`CALENDAR_NAME`) und Kalenderfarbe (`CALENDAR_COLOR`) +- Optionale Benachrichtigungen (VALARM) zur konfigurierbaren Uhrzeit +- Konfigurierbares Sync-Intervall (`SYNC_INTERVAL`) und Event-Horizont (`EVENT_YEARS`) +- Mailboxen einzeln ausschließbar via `MAILBOX_EXCLUDE` +- Automatische App-Passwort-Verwaltung (persistent im State-File) +- Integrierter Cleanup-Befehl bei doppelten Kalendern nach Umbenennung +- Startup-Connectivity-Check, Graceful Shutdown und Docker-Healthcheck +- Hairpin-NAT-Lösung via `MAILCOW_RESOLVE_HOST` für Docker-Netze +- Strukturiertes Logging mit konfigurierbarem Log-Level - Läuft als Docker-Container direkt im Mailcow-Stack ## Schnellstart diff --git a/docs/README.md b/docs/README.md index b00a3cd..249b532 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,9 @@ Willkommen in der Dokumentation des **Mailcow Birthday Daemon** 🎂 ## Kurzübersicht - Automatischer Geburtstagskalender für jede Mailcow-Mailbox -- Liest Geburtstage aus allen CardDAV-Adressbüchern +- Liest Geburtstage und Jahrestage aus allen CardDAV-Adressbüchern - Optionale Benachrichtigungen (VALARM) zur konfigurierbaren Uhrzeit +- Konfigurierbares Sync-Intervall, Event-Horizont und Kalenderfarbe +- Mailboxen einzeln ausschließbar, strukturiertes Logging mit Log-Level +- Docker-Healthcheck, Graceful Shutdown und Startup-Connectivity-Check - Läuft als Docker-Container im Mailcow-Stack via `docker-compose.override.yml` -- Synchronisation alle 15 Minuten, vollständig automatisch diff --git a/docs/funktionsweise.md b/docs/funktionsweise.md index b183174..6e90ac4 100644 --- a/docs/funktionsweise.md +++ b/docs/funktionsweise.md @@ -4,6 +4,10 @@ Der Mailcow Birthday Daemon synchronisiert automatisch Geburtstagskalender für jede aktive Mailbox. Der gesamte Prozess läuft ohne Benutzereingriff ab. +## Startup-Connectivity-Check + +Beim Start prüft der Daemon aktiv, ob die Mailcow-API und SOGo erreichbar sind, bevor die erste Synchronisation beginnt. Die Prüfung nutzt exponentielles Backoff (2 s → 4 s → … → max 30 s) und wiederholt sich, bis beide Dienste antworten. Im Log wird klar angegeben, welcher Dienst noch nicht bereit ist. Ein Shutdown-Signal bricht den Check sofort ab. + ## App-Passwörter - Über die Mailcow-API wird für jeden aktiven Benutzer ein App-Passwort mit Zugriff auf CardDAV und CalDAV erzeugt. @@ -12,10 +16,20 @@ Der Mailcow Birthday Daemon synchronisiert automatisch Geburtstagskalender für ## Kontakte und Geburtstage - Alle Kontakte aus sämtlichen Adressbüchern werden abgerufen und die Geburtstagsinformationen je Benutzer extrahiert. +- Zusätzlich zu Geburtstagen werden auch Jahrestage (`ANNIVERSARY`-Feld nach vCard 4.0 / RFC 6350) ausgelesen. Dieses Feld wird von allen gängigen Clients unterstützt (Android, iOS, Thunderbird). +- Geburtstags-Events erhalten das Präfix 🎂, Jahrestags-Events das Präfix 💍 – so sind beide Typen im Kalender sofort unterscheidbar. - Die daraus resultierenden Kalendereinträge werden im Voraus berechnet. - - Aktuell fest eingestellt: 1 Jahr in der Vergangenheit, 10 Jahre in der Zukunft. + +> **Hinweis zu Jahrestagen:** Die SOGo-Weboberfläche bietet kein Feld zum Anzeigen oder Bearbeiten von Jahrestagen. Das `ANNIVERSARY`-Feld muss über einen externen Client (z. B. Thunderbird, Android- oder iOS-Kontakte-App) gepflegt und per CardDAV synchronisiert werden. Das SOGo-CardDAV-Backend speichert und liefert das Feld korrekt – es fehlt lediglich die Unterstützung in der Web-UI. + - Aktuell standardmäßig: 1 Jahr in der Vergangenheit, 10 Jahre in der Zukunft (konfigurierbar über `EVENT_YEARS`). - Selbstverständlich pro Mailbox isoliert – ein Benutzer sieht nur die Geburtstage seiner eigenen Kontakte. +## Mailbox-Filter + +Standardmäßig erhalten alle aktiven Mailboxen einen Geburtstagskalender. Über die Umgebungsvariable `MAILBOX_EXCLUDE` können einzelne Mailboxen von der Synchronisation ausgeschlossen werden (z. B. Service-Accounts oder Shared Mailboxen). Ausgeschlossene Mailboxen werden beim Sync-Zyklus übersprungen. Bereits vorhandene Kalender in diesen Mailboxen werden dabei nicht automatisch entfernt – dafür kann der `cleanup`-Befehl verwendet werden (siehe [Troubleshooting](troubleshooting.md#doppelte-kalender-nach-umbenennung-von-calendar_name)). + +Die Variable kann jederzeit – auch bei bestehenden Installationen – per Update hinzugefügt oder geändert werden. + ## Kalendersynchronisation - Die berechneten Ereignisse werden in einen Kalender synchronisiert, dessen Name über `CALENDAR_NAME` konfigurierbar ist (Standard: „Birthdays"). Der Anzeigename kann vom Benutzer in SOGo zusätzlich umbenannt werden. @@ -23,6 +37,13 @@ Der Mailcow Birthday Daemon synchronisiert automatisch Geburtstagskalender für > **Wichtig (betrifft nur Updates von vor v0.2.0):** Damit die Umbenennung korrekt erkannt wird, muss der Daemon **mindestens einmal** mit dem neuen Code (ab v0.2.0) und dem **alten** Kalendernamen gelaufen sein, damit der Name im State-File gespeichert wird. Erst danach `CALENDAR_NAME` ändern und erneut starten. Wird der Name geändert, bevor der State aktualisiert wurde, kann der alte Kalender nicht automatisch entfernt werden. In diesem Fall kann der integrierte Cleanup-Befehl verwendet werden (siehe [Troubleshooting](troubleshooting.md#doppelte-kalender-nach-umbenennung-von-calendar_name)). +## Kalenderfarbe + +- Der Daemon setzt automatisch die CalDAV-Property `calendar-color` (Apple-Namespace) auf dem Geburtstagskalender. Dadurch hebt sich der Kalender im Client farblich sofort ab. +- Die Farbe ist über `CALENDAR_COLOR` konfigurierbar (Standard: `#D01818`). Das Format ist `#RRGGBB`. +- Hat ein Benutzer die Farbe seines Kalenders manuell geändert (z. B. in SOGo oder einem anderen Client), wird diese Anpassung respektiert und nicht überschrieben. +- Bei einer Änderung von `CALENDAR_COLOR` werden alle Kalender aktualisiert, deren Farbe nicht manuell angepasst wurde. + ## Benachrichtigungen - Wenn `NOTIFICATION_ENABLED=true` gesetzt ist, erhält jedes Geburtstags-Event einen **VALARM** (iCal-Alarm). Kalender-Clients (SOGo, iOS, Android, Thunderbird) zeigen dann zur konfigurierten Uhrzeit eine Benachrichtigung an. Das Event bleibt weiterhin ein Ganztags-Event. @@ -31,4 +52,21 @@ Der Mailcow Birthday Daemon synchronisiert automatisch Geburtstagskalender für ## Synchronisationsintervall -Der Synchronisationszyklus läuft alle **15 Minuten** automatisch. +Der Synchronisationszyklus läuft standardmäßig alle **15 Minuten** automatisch. Das Intervall kann über die Umgebungsvariable `SYNC_INTERVAL` angepasst werden (z. B. `SYNC_INTERVAL=30m`). Details zu den möglichen Werten finden sich in der [Umgebungsvariablen-Tabelle](schnellstart.md#umgebungsvariablen). + +## Graceful Shutdown + +Der Daemon reagiert auf `SIGTERM` und `SIGINT` (z. B. durch `docker stop`) und beendet sich sauber: + +1. Der aktuelle Synchronisationszyklus wird noch vollständig abgeschlossen. +2. Ungespeicherte App-Passwörter werden in die Zustandsdatei geschrieben. +3. Erst danach beendet sich der Prozess. + +Dadurch wird sichergestellt, dass das State-File konsistent bleibt und keine Daten verloren gehen. Auch der Startup-Connectivity-Check wird bei einem Shutdown-Signal sofort abgebrochen. + +## Healthcheck + +- Das Dockerfile enthält eine `HEALTHCHECK`-Anweisung, die den eingebauten Subcommand `healthcheck` nutzt – es werden keine externen Tools wie `curl` oder `wget` benötigt und kein Port wird geöffnet. +- Nach jedem Sync-Lauf schreibt der Daemon eine kleine Statusdatei (`health.json`) neben das State-File. Der `healthcheck`-Subcommand liest diese Datei und prüft, ob der letzte Sync aktuell und fehlerfrei war. +- Docker zeigt den Status in `docker ps` als `(healthy)` oder `(unhealthy)` an. +- Der Healthcheck meldet **unhealthy**, wenn der letzte Sync-Lauf fehlgeschlagen ist oder länger als das konfigurierte Sync-Intervall plus 5 Minuten Toleranz zurückliegt. Während der Startphase (bevor der erste Sync abgeschlossen ist) gilt der Daemon als healthy. diff --git a/docs/schnellstart.md b/docs/schnellstart.md index 23a4e6d..dda2f9f 100644 --- a/docs/schnellstart.md +++ b/docs/schnellstart.md @@ -24,9 +24,14 @@ services: - MAILCOW_BASE=https://mail.example.com - MAILCOW_APIKEY=DEIN-APIKEY-HIER - CALENDAR_NAME=Birthdays + # - CALENDAR_COLOR=#D01818 # - MAILCOW_RESOLVE_HOST=nginx-mailcow # - NOTIFICATION_ENABLED=true # - NOTIFICATION_TIME=08:00 + # - SYNC_INTERVAL=15m + # - EVENT_YEARS=10 + # - MAILBOX_EXCLUDE=user1@example.com,user2@example.com + # - LOG_LEVEL=info volumes: - birthdaydaemon:/data @@ -49,7 +54,7 @@ cd /opt/mailcow-dockerized docker compose pull birthdaydaemon && docker compose up -d --no-deps birthdaydaemon ``` -> **Hinweis:** Nach dem Start wartet der Daemon zunächst **15 Sekunden**, bevor die erste Synchronisation beginnt. Diese Verzögerung stellt sicher, dass abhängige Dienste (z. B. Nginx, SOGo) vollständig bereit sind. +> **Hinweis:** Nach dem Start prüft der Daemon aktiv die Erreichbarkeit der Mailcow-API und SOGo, bevor die erste Synchronisation beginnt. Die Prüfung wiederholt sich mit steigendem Intervall (2 s → 4 s → … → max 30 s), bis beide Dienste antworten. Im Log wird klar angezeigt, welcher Dienst noch nicht bereit ist. ## Umgebungsvariablen @@ -59,9 +64,14 @@ docker compose pull birthdaydaemon && docker compose up -d --no-deps birthdaydae | `MAILCOW_APIKEY` | **Ja** | – | API-Key mit Lese-/Schreibzugriff aus dem Mailcow-Admin-Panel | | `MAILCOW_RESOLVE_HOST` | Nein | – | Interner Hostname für TCP-Verbindungen (z. B. `nginx-mailcow`). Löst Hairpin-NAT-Probleme in Docker-Netzen. TLS nutzt weiterhin den Hostnamen aus `MAILCOW_BASE`. | | `CALENDAR_NAME` | Nein | `Birthdays` | Name des Geburtstagskalenders, der in jeder Mailbox erstellt wird | +| `CALENDAR_COLOR` | Nein | `#D01818` | Farbe des Geburtstagskalenders im Hex-Format (z. B. `#FF6600`). Wird nur gesetzt, wenn der Benutzer die Farbe nicht manuell geändert hat. | | `NOTIFICATION_ENABLED` | Nein | `false` | Aktiviert Kalender-Benachrichtigungen (VALARM) für Geburtstags-Events (`true`/`false`) | | `NOTIFICATION_TIME` | Nein | `08:00` | Uhrzeit der Benachrichtigung im Format `HH:MM` (nur wirksam wenn `NOTIFICATION_ENABLED=true`) | | `STATEFILE` | Nein | `state.json` (im Container: `/data/state.json`) | Pfad zur Zustandsdatei, in der App-Passwörter und der aktuelle Kalendername gespeichert werden | +| `SYNC_INTERVAL` | Nein | `15m` | Intervall zwischen den Synchronisationsläufen im Go-Duration-Format (z. B. `10m`, `30m`, `1h`). Mindestwert: `1m`. | +| `EVENT_YEARS` | Nein | `10` | Anzahl der Jahre, für die Geburtstags-Events im Voraus erzeugt werden. Gültige Werte: `1`–`30`. Kleinere Werte können die Performance in Kalender-Clients verbessern. | +| `MAILBOX_EXCLUDE` | Nein | – | Kommagetrennte Liste von Mailadressen, die von der Synchronisation ausgeschlossen werden (z. B. `admin@example.com,noreply@example.com`). Standardmäßig erhalten alle aktiven Mailboxen einen Geburtstagskalender. | +| `LOG_LEVEL` | Nein | `info` | Log-Verbosity: `debug`, `info`, `warn` oder `error`. Im `debug`-Modus werden einzelne Kalendereinträge (hinzugefügt/entfernt) und Kontaktdetails geloggt. | ## API-Key erstellen @@ -78,7 +88,7 @@ cd /opt/mailcow-dockerized docker compose logs -f birthdaydaemon ``` -Nach dem Start synchronisiert der Daemon automatisch alle 15 Minuten die Geburtstagskalender für jede Mailbox. +Nach dem Start synchronisiert der Daemon automatisch alle 15 Minuten (konfigurierbar über `SYNC_INTERVAL`) die Geburtstagskalender für jede Mailbox. > **Hinweis für bestehende Installationen:** Falls der Daemon die Mailcow-API wegen Hairpin-NAT nicht erreichen kann, muss lediglich `MAILCOW_RESOLVE_HOST=nginx-mailcow` als Umgebungsvariable ergänzt werden. Details siehe [Installationsabschnitt](#installation). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index f027c1f..7eea469 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -3,8 +3,9 @@ ## Container startet, aber keine Kalender werden erstellt 1. Logs prüfen: `docker compose logs -f birthdaydaemon` -2. Sicherstellen, dass der API-Key **Lese- und Schreibzugriff** hat (nicht nur Lesezugriff). -3. Prüfen, ob die Mailbox aktiv ist – inaktive Mailboxen werden übersprungen. +2. Für detaillierte Ausgaben `LOG_LEVEL=debug` als Umgebungsvariable setzen – damit werden einzelne Kontakte und Kalendereinträge sichtbar. +3. Sicherstellen, dass der API-Key **Lese- und Schreibzugriff** hat (nicht nur Lesezugriff). +4. Prüfen, ob die Mailbox aktiv ist – inaktive Mailboxen werden übersprungen. ## Verbindungsfehler / Timeout @@ -33,12 +34,12 @@ docker compose exec birthdaydaemon /mailcow-birthday-daemon cleanup Birthdays **Beispielausgabe:** ``` -2026/03/28 23:46:22 INFO starting calendar cleanup calendarName=Birthdays -2026/03/28 23:46:22 INFO using internal resolve host for connections resolveHost=nginx-mailcow -2026/03/28 23:46:22 INFO removed old birthday calendar user=user1@example.com calendar=Birthdays -2026/03/28 23:46:23 INFO removed old birthday calendar user=user2@example.com calendar=Birthdays -2026/03/28 23:46:23 INFO removed old birthday calendar user=user3@example.com calendar=Birthdays -2026/03/28 23:46:24 INFO cleanup finished processed=5 skipped=0 +2026/03/28 23:46:22 [INFO] starting calendar cleanup calendarName=Birthdays +2026/03/28 23:46:22 [INFO] using internal resolve host for connections resolveHost=nginx-mailcow +2026/03/28 23:46:22 [INFO] removed old birthday calendar user=user1@example.com calendar=Birthdays +2026/03/28 23:46:23 [INFO] removed old birthday calendar user=user2@example.com calendar=Birthdays +2026/03/28 23:46:23 [INFO] removed old birthday calendar user=user3@example.com calendar=Birthdays +2026/03/28 23:46:24 [INFO] cleanup finished processed=5 skipped=0 ``` > **Hinweis:** Der Daemon muss vorher mindestens einmal gelaufen sein, damit App-Passwörter im State-File vorhanden sind. Benutzer ohne gespeichertes Passwort werden übersprungen. @@ -46,3 +47,10 @@ docker compose exec birthdaydaemon /mailcow-birthday-daemon cleanup Birthdays ## Kalender erscheint nicht in SOGo SOGo zeigt neue Kalender manchmal erst nach einem Neuladen der Seite (Strg+Shift+R) oder nach dem nächsten Login an. Der Kalender wird unter dem Namen erstellt, der in `CALENDAR_NAME` konfiguriert ist (Standard: `Birthdays`). + +## Healthcheck meldet `unhealthy` + +1. Logs prüfen: `docker compose logs -f birthdaydaemon` +2. Status manuell abfragen: `docker compose exec birthdaydaemon /mailcow-birthday-daemon healthcheck` +3. Der Healthcheck meldet `unhealthy`, wenn der letzte Sync-Lauf fehlgeschlagen ist oder länger als 20 Minuten zurückliegt. +4. Falls der Container gerade erst gestartet wurde, kann es bis zu 2 Minuten dauern, bis der erste Sync abgeschlossen und der Status `healthy` ist. -- 2.54.0