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
@@ -33,9 +33,12 @@ const SALT = 'test-salt-0123456789abcdef0123456789abcdef';
|
||||
const ME = 'user-me';
|
||||
const OTHER = 'user-other';
|
||||
const ADMIN = 'user-admin';
|
||||
/** Konto ohne Benutzernamen (alle anderen haben aus `setup` ein Profil): Veröffentlichen geht erst mit Profil. */
|
||||
const NEWBIE = 'user-new';
|
||||
const JWT_ME = 'eyJhbGciOiJI.payload-me.signature-me';
|
||||
const JWT_OTHER = 'eyJhbGciOiJI.payload-other.signature-other';
|
||||
const JWT_ADMIN = 'eyJhbGciOiJI.payload-admin.signature-admin';
|
||||
const JWT_NEW = 'eyJhbGciOiJI.payload-new.signature-new';
|
||||
const MIB5 = 5 * 1024 * 1024;
|
||||
const TAKEN = 'Diese Bilder sind bereits veröffentlicht oder gesperrt.';
|
||||
const NOT_CARD_FORMAT = 'Das Bild hat nicht das Kartenformat.';
|
||||
@@ -54,6 +57,9 @@ async function setup() {
|
||||
fake.addUser(JWT_ME, { id: ME });
|
||||
fake.addUser(JWT_OTHER, { id: OTHER });
|
||||
fake.addUser(JWT_ADMIN, { id: ADMIN, labels: ['admin'] });
|
||||
fake.addUser(JWT_NEW, { id: NEWBIE });
|
||||
// Veröffentlichen verlangt einen Benutzernamen (Tabelle `profiles`): ME, OTHER und ADMIN haben einen, NEWBIE nicht.
|
||||
for (const id of [ME, OTHER, ADMIN]) fake.addProfile(id);
|
||||
|
||||
const logs = [];
|
||||
const clock = { value: 1_700_000_000_000 };
|
||||
@@ -406,6 +412,50 @@ test('Veröffentlichen ohne oder mit ungültigem JWT: 401 und nichts angelegt',
|
||||
assert.equal(fake.calls.filter((c) => c.path === '/v1/account').length, 1, 'fehlendes oder kaputtes JWT erreicht Appwrite gar nicht erst');
|
||||
});
|
||||
|
||||
test('Veröffentlichen ohne Benutzernamen: 403 usernameRequired noch vor jeder anderen Prüfung (auch vor Body, Dateien und Karte), nichts angelegt', async () => {
|
||||
const { post, fake } = await setup();
|
||||
seedUploads(fake, NEWBIE);
|
||||
const publish = (body) => post('/api/veroeffentlichen', body, JWT_NEW);
|
||||
|
||||
expectError(await publish(publishBody()), 403, 'usernameRequired', 'gültiger Body');
|
||||
expectError(await publish({}), 403, 'usernameRequired', 'ungültiger Body (kein Titel, keine Dateien)');
|
||||
expectError(await publish(publishBody({ title: '' })), 403, 'usernameRequired', 'Titel fehlt');
|
||||
expectError(await publish(publishBody({ imageFileId: 'gibts-nicht' })), 403, 'usernameRequired', 'Datei gibt es nicht');
|
||||
expectError(await publish(publishBody({ sourceCardId: 'fremde-karte' })), 403, 'usernameRequired', 'Karte gibt es nicht');
|
||||
expectError(await publish(null), 403, 'usernameRequired', 'kein Body');
|
||||
|
||||
assert.equal(fake.rows('gallery').length, 0);
|
||||
assert.equal(storageCalls(fake).length, 0, 'keine Datei wird angefasst');
|
||||
assert.deepEqual(fake.file('img1').$permissions, ['read("any")', 'delete("user:user-new")'], 'die Dateien bleiben, wie sie waren');
|
||||
// Appwrite sieht je Versuch nur die Prüfung des JWT und die Suche nach dem Profil (Zeilen-ID = Konto-ID)
|
||||
assert.deepEqual(
|
||||
fake.calls.filter((c) => c.path !== '/v1/account').map((c) => `${c.method} ${c.path}`),
|
||||
Array(6).fill('GET /v1/tablesdb/aircard/tables/profiles/rows/user-new'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Veröffentlichen: das Profil gehört dem Konto – ein Profil der anderen Konten hilft nicht, mit eigenem Profil (über /api/profil) geht es', async () => {
|
||||
const { post, fake } = await setup();
|
||||
seedUploads(fake, NEWBIE);
|
||||
expectError(await post('/api/veroeffentlichen', publishBody(), JWT_NEW), 403, 'usernameRequired');
|
||||
|
||||
const named = await post('/api/profil', { username: 'Neuling' }, JWT_NEW);
|
||||
assert.deepEqual(named.json, { username: 'Neuling' });
|
||||
const res = await post('/api/veroeffentlichen', publishBody(), JWT_NEW);
|
||||
assert.equal(res.status, 201);
|
||||
assert.equal(fake.row('gallery', res.json.id).ownerId, NEWBIE);
|
||||
});
|
||||
|
||||
test('Veröffentlichen: lässt sich das Profil nicht prüfen (Appwrite-Fehler), 502 und nichts wird angelegt; der nächste Versuch gelingt', async () => {
|
||||
const { post, fake } = await setup();
|
||||
seedUploads(fake);
|
||||
fake.failNext((c) => c.method === 'GET' && c.path.endsWith('/tables/profiles/rows/user-me'), { status: 500 });
|
||||
expectError(await post('/api/veroeffentlichen', publishBody(), JWT_ME), 502, 'unavailable');
|
||||
assert.equal(fake.rows('gallery').length, 0);
|
||||
assert.equal(storageCalls(fake).length, 0);
|
||||
assert.equal((await post('/api/veroeffentlichen', publishBody(), JWT_ME)).status, 201);
|
||||
});
|
||||
|
||||
test('Veröffentlichen mit fremder Datei: 403 und nichts angelegt', async () => {
|
||||
const { post, fake } = await setup();
|
||||
fake.addFile({ id: 'img1', owner: OTHER });
|
||||
|
||||
Reference in new issue
Block a user