docs: Spec und Plan Benutzernamen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 06:23:08 +00:00
1 parent 13b46da2e0
commit a65c075004
2 files changed
+222

No files matched your search

@@ -0,0 +1,109 @@
# AirCard-IOS – Benutzernamen – Design
Stand: 2026-10-05 · Status: Entwurf im Chat von Kenso freigegeben („ja passt, mach weiter“), Spec + Plan + Umsetzung ohne weitere Rückfrage · baut auf `2026-10-04-aircard-logos-sprache-design.md` auf (live, main 13b46da)
## 1. Ziel und Rahmen
Jedes Konto bekommt einen eindeutigen Benutzernamen. Logos und Community-Karten zeigen „von @name“ statt „Anonym“, damit man Leute wiedererkennt.
| Punkt | Entscheidung (Kenso) |
|---|---|
| Eindeutig | Ja, jeder Name nur einmal; Groß-/Kleinschreibung zählt beim Vergleich nicht. |
| Pflicht | Ja, auch für bestehende Konten. Registrieren nur mit Name; alte Konten werden nach dem Anmelden einmal gefragt; Hochladen und Veröffentlichen gehen erst mit Name. |
| Anzeige | Immer der Benutzername; das Feld „Anzeigename“ fällt weg; gilt auch für vorhandene Einträge des Kontos. |
| Ändern | Ja, im Konto-Menü; danach überall der neue Name, der alte wird frei. |
## 2. Regeln für den Namen
- Muster: `^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$`. Das sind 3–20 Zeichen, das erste ein Buchstabe oder eine Zahl, danach Buchstaben, Zahlen, `.`, `_`, `-`.
- Schlüssel für die Eindeutigkeit: der Name in Kleinbuchstaben (`usernameKey`). Gespeichert und angezeigt wird der Name, wie er eingegeben wurde.
- Gesperrte Schlüssel: `admin`, `administrator`, `root`, `system`, `support`, `moderator`, `mod`, `aircard`, `aircardios`, `aircard-ios`, `aircard.ios`, `aircard_ios`, `webklar`, `anonym`, `anonymous`, `null`, `undefined`, `team`, `staff`, `official`, `offiziell`, `hilfe`, `help`, `kontakt`, `contact`, `info`.
- Anzeige mit `@` davor: „von @name“ bzw. „by @name“.
## 3. Daten (Appwrite, Datenbank `aircard`, Setup-Skript erweitert)
**Tabelle `profiles`** (rowSecurity an, keine Tabellenrechte für Clients):
| Spalte | Typ | Inhalt |
|---|---|---|
| `username` | string 20, Pflicht | Name wie eingegeben |
| `usernameKey` | string 20, Pflicht | Name klein geschrieben |
- Zeilen-ID = Nutzer-ID (eine Zeile pro Konto).
- Index `username_key`: **unique** auf `usernameKey`. Damit kann auch bei Gleichzeitigkeit kein Name doppelt vergeben werden.
- Zeilenrecht genau `['read("any")']`, öffentlich lesbar. Schreiben nur über die API.
## 4. API-Dienst `aircard-api`
Eigenes Modul `deploy/api/profiles.js`, eingebunden wie `logos.js`. Der Betrieb kopiert künftig sechs Dateien: `appwrite.js`, `common.js`, `app.js`, `logos.js`, `profiles.js`, `server.js`.
`POST /api/profil` `{username}`, Nutzer-JWT:
- 401 ohne Anmeldung.
- 400 `usernameInvalid` bei falschem Muster.
- 400 `usernameReserved` bei gesperrtem Schlüssel.
- 409 `usernameTaken`, wenn ein anderes Konto den Schlüssel hat. Das wird vorab per Abfrage geprüft; bei einem Rennen meldet Appwrite 409 auf dem Unique-Index, das wird ebenfalls zu `usernameTaken`.
- Sonst: Hat das Konto schon eine Zeile, wird sie aktualisiert (der alte Name ist damit frei); sonst wird sie mit ID = Nutzer-ID und `read("any")` angelegt. Antwort `200 {username}`.
- Groß-/Kleinschreibung des eigenen Namens ändern („kenso“ → „Kenso“) ist erlaubt.
- Sperre je Konto (`profile:<userId>`).
**Pflicht serverseitig:** `POST /api/logos/hochladen` und `POST /api/veroeffentlichen` antworten **403 `usernameRequired`**, wenn das Konto keine Zeile in `profiles` hat. Die Prüfung läuft nach der Anmeldung und vor allen anderen Prüfungen. Bestehende Logos und Karten bleiben unberührt.
Neue `MSG`-Texte:
- `usernameInvalid`: „Der Benutzername muss 3–20 Zeichen lang sein, mit einem Buchstaben oder einer Zahl beginnen und darf nur Buchstaben, Zahlen, Punkt, Unterstrich und Bindestrich enthalten.“
- `usernameReserved`: „Dieser Benutzername ist nicht erlaubt.“
- `usernameTaken`: „Dieser Benutzername ist schon vergeben.“
- `usernameRequired`: „Bitte wähle zuerst einen Benutzernamen.“
## 5. Oberfläche
- **Registrieren** (Login-Dialog, Reiter „Registrieren“): neues Pflichtfeld „Benutzername“ vor der E-Mail, `autocomplete="username"`, Hinweis „3–20 Zeichen: Buchstaben, Zahlen, . _ -“.
- Vor dem Anlegen des Kontos werden Muster, Sperrliste und Verfügbarkeit geprüft (Abfrage auf `profiles`). Ein Fehler steht am Feld.
- Danach: Konto anlegen → anmelden → `POST /api/profil`.
- Scheitert dieser letzte Schritt (Rennen, Netz), ist man trotzdem angemeldet, und der Dialog „Benutzername wählen“ erscheint.
- **Dialog „Benutzername wählen“** mit Feld, „Speichern“ und „Später“ bzw. „Abbrechen“:
- Nach dem Anmelden oder beim Seitenstart mit Sitzung, wenn das Konto keinen Namen hat. Einmal pro Sitzung (Merker in `sessionStorage` `aircard.usernamePrompted`, Zugriffe in try/catch), mit „Später“.
- Vor dem Hochladen eines Logos und vor dem Veröffentlichen, wenn kein Name da ist. Hier gibt es „Abbrechen“ statt „Später“; ohne Namen geht es nicht weiter.
- Antwortet der Server mit `usernameRequired`, erscheint der Dialog ebenfalls.
- **Konto-Menü**: Statt der E-Mail steht „@name“ oben (die E-Mail darunter klein). Neuer Eintrag „Benutzername ändern“, der den Dialog vorbelegt öffnet. Ohne Namen heißt der Eintrag „Benutzername wählen“.
- **Anzeige „von @name“**: Logo-Kacheln, Logo-Infozeile, Detailseite einer Community-Karte.
- Der Name wird über `ownerId` in `profiles` nachgeschlagen, gesammelt je Seite (`equal('$id', ids)`) und zwischengespeichert.
- Ohne Profil: der früher gespeicherte `displayName` (ohne `@`), sonst „Anonym“.
- Nach einer eigenen Namensänderung aktualisieren Kacheln, Infozeile und Detailseite die eigenen Einträge sofort.
- **Weg fällt** das Feld „Anzeigename“ in den Dialogen „Logo hochladen“ und „Veröffentlichen“ samt `account/display-name.ts` und dem `localStorage`-Schlüssel `aircard.displayName`. Der Client schickt `displayName: ''`; die API nimmt das Feld weiter an (Altbestand).
Alle Texte gibt es auf Deutsch und Englisch.
## 6. Fehlerbehandlung
| Fall | Meldung |
|---|---|
| Muster falsch / gesperrt / vergeben | die drei `MSG`-Texte aus § 4, am Feld |
| Server meldet `usernameRequired` | Dialog „Benutzername wählen“ |
| Netz/API weg beim Speichern | „Der Dienst ist gerade nicht erreichbar.“, Dialog bleibt offen |
| Profil lässt sich nicht lesen (Anzeige) | Fallback `displayName` bzw. „Anonym“, kein Fehlertext |
## 7. Tests
- **node:test:**
- `/api/profil`: anlegen, ändern, Groß/klein am eigenen Namen, vergeben 409, gesperrt 400, Muster 400, ohne JWT 401.
- Rennen mit zwei Konten: genau eines bekommt den Namen.
- Zeilenrechte genau `read("any")`.
- Hochladen und Veröffentlichen ohne Profil → 403 `usernameRequired`.
- Setup-Skript deckt `profiles` mit Unique-Index ab.
- **Vitest:**
- Prüfung des Namens (Muster, Sperrliste, Schlüssel).
- Registrieren mit Vorabprüfung und Fehlern am Feld.
- Dialog: Später, Abbrechen, Speichern, Fehler.
- Einmal-pro-Sitzung-Abfrage.
- Konto-Menü mit „@name“ und Ändern.
- Anzeige „von @name“ mit Fallbacks; sofortige Aktualisierung nach Umbenennen.
- Dialoge ohne „Anzeigename“.
- Hochladen und Veröffentlichen fragen vorher nach dem Namen.
- Gleichlauf der API-Codes, i18n-Abdeckung.
- **Live:** Gast sieht „von …“-Zeilen. Kenso registriert bzw. wird gefragt, ändert den Namen und sieht ihn an seinem Logo.
## 8. Nicht im Umfang
Profilseiten, Suche nach Benutzernamen, Moderation von Namen, Konto löschen, Avatar-Bilder, Benutzername als Anmeldename (angemeldet wird weiter per E-Mail).