feat(api): Benutzernamen (Profil-Tabelle, /api/profil, Pflicht beim Hochladen und Veröffentlichen)
- Schema: Tabelle `profiles` (Zeilen-ID = Konto-ID, `username` und `usernameKey`
je 20 Zeichen, Unique-Index `username_key` auf `usernameKey`, rowSecurity an,
keine Tabellenrechte) im Setup-Skript.
- Neues Modul `deploy/api/profiles.js`: `POST /api/profil` legt das Profil an
oder ändert es (Zeilenrecht genau read("any"), Sperre je Konto). Muster,
Sperrliste und Schlüssel (`usernameKey`) stehen in `common.js`; vergeben ->
409 `usernameTaken`, auch wenn erst der Unique-Index bei einem Rennen
entscheidet. Dazu die MSG-Texte `usernameInvalid`, `usernameReserved`,
`usernameTaken`, `usernameRequired`.
- `POST /api/logos/hochladen` und `POST /api/veroeffentlichen` antworten 403
`usernameRequired`, wenn das Konto kein Profil hat (`hasProfile`, nach der
Anmeldung und vor allen anderen Prüfungen).
- Fake-Appwrite: Tabelle `profiles` mit Unique-Index (409), `addProfile`,
`holdNext`. API-Dienst besteht jetzt aus sechs Dateien (READMEs).
- Client: die vier API-Texte in `API_MESSAGES` und `en.ts` (Gleichlauf mit MSG).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a65c075004
commit
19c8151c7e
15 files changed
+980
-53
No files matched your search
+13
-5
@@ -2,12 +2,13 @@
|
||||
|
||||
/**
|
||||
* Routen und Logik des API-Dienstes `aircard-api` (Node 22, nur Bordmittel). Alle Schreibzugriffe auf die
|
||||
* Community-Galerie und die Community-Logos laufen hier über den Appwrite-Server-Key (siehe appwrite.js).
|
||||
* Gemeinsame Helfer, Fehlertexte und -codes: common.js. Die Logo-Endpunkte stehen in logos.js; dieses Modul gibt ihnen
|
||||
* Anmeldung, Sperre je Schlüssel, das (gemeinsame) Rate-Limit und das Log.
|
||||
* Community-Galerie, die Community-Logos und die Benutzernamen laufen hier über den Appwrite-Server-Key (siehe appwrite.js).
|
||||
* Gemeinsame Helfer, Fehlertexte und -codes: common.js. Die Logo-Endpunkte stehen in logos.js, der Endpunkt der Benutzernamen in
|
||||
* profiles.js; dieses Modul gibt ihnen Anmeldung, Sperre je Schlüssel, das (gemeinsame) Rate-Limit und das Log.
|
||||
*
|
||||
* POST /api/veroeffentlichen Nutzer-JWT {title, issuer?, tags?, displayName?, imageFileId, previewFileId, sourceCardId}
|
||||
* 401 kein/ungültiges JWT · 400 Validierung oder Datei fehlt/falsches Format/falsche Bildmaße · 403 Datei nicht meine /
|
||||
* 401 kein/ungültiges JWT · 403 `usernameRequired` (das Konto hat noch keinen Benutzernamen; vor allen anderen Prüfungen) ·
|
||||
* 400 Validierung oder Datei fehlt/falsches Format/falsche Bildmaße · 403 Datei nicht meine /
|
||||
* falscher Bucket / Datei gesperrt (kein read("any")) oder von einer Karte des Autors schon verwendet / Karte nicht
|
||||
* meine (`cards`) · 409 schon veröffentlicht · 429 schon 20 Veröffentlichungen · 201 {id}
|
||||
* POST /api/zurueckziehen Nutzer-JWT (Autor oder Admin) {id}
|
||||
@@ -18,12 +19,14 @@
|
||||
* 401 · 403 kein Admin · 400 · 404 · 200 {ok:true}
|
||||
* POST /api/logos/hochladen · /api/logos/herz · /api/logos/melden · /api/logos/loeschen · /api/admin/logo-sichtbarkeit
|
||||
* siehe logos.js
|
||||
* POST /api/profil siehe profiles.js
|
||||
* GET /api/gesundheit {ok:true}
|
||||
* alles andere 404; Body höchstens 8 KB (413), nur application/json (415)
|
||||
*
|
||||
* Das Nutzer-JWT kommt im Header X-Aircard-Jwt und wird bei Appwrite (GET /account) geprüft; Admin = Label `admin`.
|
||||
* Der Appwrite-Key braucht die Console-Scopes documents.read/documents.write (= rows.* der API) und files.read/files.write.
|
||||
* Reihenfolge je Anfrage: Content-Type (415) -> Body lesen (413) -> JWT (401) -> JSON und Felder (400) -> Logik.
|
||||
* Reihenfolge je Anfrage: Content-Type (415) -> Body lesen (413) -> JWT (401) -> Benutzername (403, nur Veröffentlichen und
|
||||
* Logo hochladen) -> JSON und Felder (400) -> Logik.
|
||||
* Fehlerantworten sind immer {error: <deutscher Text>, code: <Schlüssel in MSG>} (siehe common.js).
|
||||
* Appwrite-Fehler: 502 (keine Antwort, 429, 5xx; code `unavailable`) bzw. 500 (alle anderen, z. B. falscher Key oder
|
||||
* fehlende Tabelle; code `internal`).
|
||||
@@ -60,6 +63,7 @@ const {
|
||||
checkFileOwnership,
|
||||
} = require('./common.js');
|
||||
const { createLogoRoutes } = require('./logos.js');
|
||||
const { createProfileRoutes, hasProfile } = require('./profiles.js');
|
||||
|
||||
const TABLE_GALLERY = 'gallery';
|
||||
const TABLE_REPORTS = 'reports';
|
||||
@@ -417,6 +421,8 @@ function createApp({ appwrite, salt, now = Date.now, log = console.log }) {
|
||||
// ---- Endpunkte
|
||||
async function publish({ req, raw }) {
|
||||
const user = await requireUser(req);
|
||||
// Ohne Benutzernamen geht nichts weiter: vor allen anderen Prüfungen, es wird nichts angelegt oder angefasst.
|
||||
if (!(await hasProfile(appwrite, user.id))) throw new HttpError(403, MSG.usernameRequired);
|
||||
const validation = validatePublish(parseJsonObject(raw));
|
||||
if (!validation.ok) throw new HttpError(400, validation.message);
|
||||
const data = validation.data;
|
||||
@@ -572,6 +578,8 @@ function createApp({ appwrite, salt, now = Date.now, log = console.log }) {
|
||||
['POST /api/admin/sichtbarkeit', { name: 'admin/sichtbarkeit', json: true, run: setVisibility }],
|
||||
// Logos (logos.js): bekommen Anmeldung, Sperre je Schlüssel, das gemeinsame Rate-Limit und das Log von hier.
|
||||
...createLogoRoutes({ appwrite, salt, say, withLock, rateLimit, requireUser }),
|
||||
// Benutzernamen (profiles.js): bekommen Anmeldung, Sperre je Schlüssel und das Log von hier.
|
||||
...createProfileRoutes({ appwrite, say, withLock, requireUser }),
|
||||
]);
|
||||
|
||||
async function handle(req, res) {
|
||||
|
||||
Reference in new issue
Block a user