# 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:`). **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).