mirror of
https://github.com/skoelle/icloud-contacts-sync.git
synced 2026-09-17 15:30:24 +00:00
feature in roadmap
This commit is contained in:
@@ -194,6 +194,12 @@ Unverändert gegenüber v1:
|
||||
- **Mehrere Empfänger je Kontakt, Vorlauf-Erinnerungen**
|
||||
(z. B. "in 3 Tagen") sind funktional einfach nachrüstbar, aktuell
|
||||
aber nicht Teil des Scopes.
|
||||
- **Contact Detail Page: UID statt DB-ID**: Der aktuelle Lookup
|
||||
`WHERE id = %s` nutzt die Auto-Increment-ID, die sich bei Re-Syncs
|
||||
ändern kann. Stabilere Alternative: `WHERE uid = %s AND account = %s`,
|
||||
da die UID aus CardDAV konstant bleibt. Erfordert Änderungen an
|
||||
Routes (`/contacts/{account}/{uid}`), Templates, Mailer-URLs und
|
||||
Schema. Details siehe `feature-contact-uid-lookup.md`.
|
||||
|
||||
|
||||
## 12. Web-Ansicht und API (v3, im selben Repo/Image)
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Feature: Contact Detail Page — UID statt DB-ID
|
||||
|
||||
## Status
|
||||
|
||||
**Geplant (Backlog)** — Kein aktueller Bedarf, aber sinnvolles Zukunftsfuture.
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
Die Contact-Detail-Seite wird aktuell über die interne Datenbank-ID (`id INT AUTO_INCREMENT`) aufgerufen:
|
||||
|
||||
```
|
||||
/contacts/{contact_id} (HTML)
|
||||
/api/contacts/{contact_id} (JSON)
|
||||
```
|
||||
|
||||
Die `id` kann sich bei einem Re-Sync (z.B. nach Datenverlust) ändern. Die **UID** aus CardDAV ist hingegen stabil und bleibt über Synchronisationen hinweg gleich.
|
||||
|
||||
## Problem
|
||||
|
||||
- Die `id` ist kein stabiler Identifier — sie kann sich ändern
|
||||
- Bei einem Re-Sync mit neuem Schema werden alle alten IDs ungültig
|
||||
- Geteilte Links oder Lesezeichen auf `/contacts/42` brechen
|
||||
|
||||
## Lösungsidee
|
||||
|
||||
Den Lookup von `WHERE id = %s` auf `WHERE uid = %s` umstellen.
|
||||
|
||||
### Einschränkung
|
||||
|
||||
UIDs sind pro Account eindeutig (`UNIQUE KEY (account, uid)`), nicht global. Für Admin-Nutzer mit `show_all=True` müsste der Account ebenfalls im Lookup berücksichtigt werden.
|
||||
|
||||
### Mögliche URL-Patterns
|
||||
|
||||
| Ansatz | URL | Vorteil | Nachteil |
|
||||
|--------|-----|---------|----------|
|
||||
| UID only | `/contacts/{uid}` | Einfach | Nur sicher, wenn UID global eindeutig |
|
||||
| Account + UID | `/contacts/{account}/{uid}` | Explizit, immer korrekt | Längere URL, Admin-Modus nötig |
|
||||
| Komposit | `/contacts/{account}--{uid}` | Ein Path-Parameter | Unschön |
|
||||
|
||||
**Empfehlung:** Account + UID (`/contacts/{account}/{uid}`) — sauber und eindeutig.
|
||||
|
||||
## Betroffene Dateien
|
||||
|
||||
### 1. `src/api/main.py` — API-Routen
|
||||
|
||||
**JSON API (Zeile 127-149):**
|
||||
- Route: `GET /api/contacts/{contact_id}` → `GET /api/contacts/{account}/{contact_uid}`
|
||||
- Parameter: `contact_id: int` → `account: str, contact_uid: str`
|
||||
- Query: `WHERE id = %s` → `WHERE uid = %s AND account = %s`
|
||||
|
||||
**HTML Detail (Zeile 578-601):**
|
||||
- Route: `GET /contacts/{contact_id}` → `GET /contacts/{account}/{contact_uid}`
|
||||
- Parameter: `contact_id: int` → `account: str, contact_uid: str`
|
||||
- Query: `WHERE id = %s` → `WHERE uid = %s AND account = %s`
|
||||
|
||||
### 2. `src/api/templates/index.html` — Kontaktliste
|
||||
|
||||
**Zeile 193:**
|
||||
```html
|
||||
<!-- Aktuell -->
|
||||
<a href="/contacts/{{ c.id }}{% if search %}?search={{ search }}{% endif %}">
|
||||
|
||||
<!-- Neu -->
|
||||
<a href="/contacts/{{ c.account }}/{{ c.uid | urlencode }}{% if search %}?search={{ search }}{% endif %}">
|
||||
```
|
||||
|
||||
### 3. `src/api/templates/dashboard.html` — Geburtstagsliste
|
||||
|
||||
**Zeile 309:**
|
||||
```html
|
||||
<!-- Aktuell -->
|
||||
<a href="/contacts/{{ b.id }}" ...>
|
||||
|
||||
<!-- Neu -->
|
||||
<a href="/contacts/{{ b.account }}/{{ b.uid | urlencode }}" ...>
|
||||
```
|
||||
|
||||
### 4. `src/mailer.py` — Birthday-Mails
|
||||
|
||||
**Zeile 25:** SELECT um `uid` erweitern (oder `id` entfernen):
|
||||
```sql
|
||||
SELECT account, uid, full_name, ...
|
||||
```
|
||||
|
||||
**Zeile 84 (Plain Text):**
|
||||
```python
|
||||
# Aktuell
|
||||
line += f" → {Config.WEB_URL.rstrip('/')}/contacts/{b['id']}"
|
||||
# Neu
|
||||
line += f" → {Config.WEB_URL.rstrip('/')}/contacts/{b['account']}/{b['uid']}"
|
||||
```
|
||||
|
||||
**Zeile 95 (HTML):**
|
||||
```python
|
||||
# Aktuell
|
||||
contact_link = f"{Config.WEB_URL.rstrip('/')}/contacts/{b['id']}"
|
||||
# Neu
|
||||
contact_link = f"{Config.WEB_URL.rstrip('/')}/contacts/{b['account']}/{b['uid']}"
|
||||
```
|
||||
|
||||
### 5. `src/api/schemas.py` — Pydantic Models
|
||||
|
||||
- `ContactOut.id` (Zeile 9): Kann bleiben (nützlich für interne Zwecke), aber `uid` wird primärer Lookup-Key
|
||||
- `GroupMemberOut.id` (Zeile 53): Prüfen ob noch benötigt
|
||||
|
||||
### 6. `src/db.py` — DB-Queries
|
||||
|
||||
SELECT-Klauseln enthalten bereits `uid`. `id` kann aus SELECTs entfernt werden, wo es nicht gebraucht wird.
|
||||
|
||||
### 7. Dokumentation
|
||||
|
||||
- `README.md` (Zeilen 166, 244, 247): Endpoint-Pattern aktualisieren
|
||||
- `SPEC.md` (Zeilen 253, 256): Endpoint-Pattern aktualisieren
|
||||
|
||||
## Risiken
|
||||
|
||||
1. **Backward-Kompatibilität:** Bestehende URLs (`/contacts/42`) brechen. Kein eleganter Redirect möglich (alte ID sagt nichts über UID aus).
|
||||
2. **UID-Encoding:** UIDs aus CardDAV können Sonderzeichen enthalten — `urlencode` in Templates ist Pflicht.
|
||||
3. **Admin-Modus:** Bei `show_all=True` ist `account_name` aktuell `None`. Der neue Ansatz mit explizitem Account-Pfad löst das sauber auf.
|
||||
|
||||
## Umsetzungsreihenfolge
|
||||
|
||||
1. `src/api/main.py` — Routen ändern (API + HTML)
|
||||
2. `src/api/templates/index.html` — Links anpassen
|
||||
3. `src/api/templates/dashboard.html` — Links anpassen
|
||||
4. `src/mailer.py` — URL-Bau anpassen
|
||||
5. `src/api/schemas.py` — `GroupMemberOut.id` prüfen
|
||||
6. `README.md` + `SPEC.md` — Dokumentation aktualisieren
|
||||
7. `ruff check src/` — Lint-Check
|
||||
Reference in New Issue
Block a user