feat: iCloud-Kontaktgruppen synchronisieren und API-Endpunkte hinzufügen

Erkennt iCloud-Gruppen (vCards mit X-ADDRESSBOOKSERVER-KIND:group) beim
CardDAV-Sync und speichert sie separat in den neuen Tabellen groups und
group_members statt als Kontakte in die contacts-Tabelle.

Änderungen:
- sql/schema.sql: Neue Tabellen groups + group_members (FK CASCADE)
- src/vcard_parser.py: is_group_vcard() + parse_group() Funktionen
- src/db.py: DB-Funktionen für Gruppen (upsert, delete, replace, get)
- src/sync.py: _classify_vcards() trennt Gruppen von Kontakten
- src/api/schemas.py: Pydantic-Modelle für Gruppen-Responses
- src/api/main.py: 3 neue Endpunkte (/api/groups, /{id}, /{id}/members)
- src/api/templates/contact.html: Gruppen-Section im Kontakt-Detail
- README.md + SPEC.md: Dokumentation aktualisiert
This commit is contained in:
2026-08-07 15:18:44 +02:00
parent 38b03a2c8a
commit 94a279f207
9 changed files with 369 additions and 38 deletions
+9 -1
View File
@@ -92,6 +92,11 @@ Siehe `sql/schema.sql`. Wichtigste Änderungen gegenüber v1:
- Neue Tabelle `birthday_mail_log`: ein Datensatz pro Tag, an dem
erfolgreich eine Geburtstagsmail versendet wurde, verhindert
Doppelversand bei mehrfachem Container-Neustart am selben Tag.
- Neue Tabellen `groups` und `group_members`: Speichert
iCloud-Kontaktgruppen (vCards mit `X-ADDRESSBOOKSERVER-KIND:group`)
und deren Mitgliedschaften. Gruppen werden beim Sync erkannt und
nicht als Kontakte in die `contacts`-Tabelle geschrieben.
`group_members` referenziert `groups(id)` mit `ON DELETE CASCADE`.
## 6. Geburtstags-Mailer
@@ -247,10 +252,13 @@ geteilt wird. Getrennt ist nur die **Rolle**, in der der Container läuft.
| `GET /contacts/{id}` | HTML-Detailseite eines einzelnen Kontakts (Jinja2-Template) |
| `GET /api/health` | Health-Check ohne Auth-Anforderung |
| `GET /api/contacts` | Kontaktliste, Filter `q` (Freitext), Pagination `limit`/`offset` |
| `GET /api/contacts/{id}` | Einzelner Kontakt (JSON) |
| `GET /api/contacts/{id}` | Einzelner Kontakt (JSON), inklusive `groups`-Feld mit zugehörigen Gruppennamen |
| `GET /api/contacts/count` | Anzahl der Kontakte des zugeordneten Accounts |
| `GET /api/contacts/birthdays/today` | Heutige Geburtstage (kontospezifisch bzw. global für Admins) |
| `GET /api/contacts/birthdays/upcoming` | Geburtstage der nächsten N Tage (Parameter `days`, Default 7) |
| `GET /api/groups` | Gruppenliste mit `member_count`, Pagination `limit`/`offset` |
| `GET /api/groups/{id}` | Einzelne Gruppe mit aufgelösten Members (Name + UID) |
| `GET /api/groups/{id}/members` | Nur Members einer Gruppe (Kontaktdaten aufgelöst) |
| `GET /api/sync-runs` | Sync-Historie (kontospezifisch bzw. global für Admins) |
### 12.5 Netzwerkkontext