110 lines
7.4 KiB
Markdown
110 lines
7.4 KiB
Markdown
# 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).
|