deleteLogo löschte zuerst die Datei und zuletzt die Zeile. Brach es dazwischen ab, blieb eine öffentliche Zeile mit gelöschter Datei stehen: unter dieser Datei-ID kann jedes Konto im Bucket eine neue Datei anlegen, und alle gespeicherten Karten mit diesem Logo zeigten bis zur Wiederholung deren Bild. - logos.js: Vor dem Löschen der Datei wird das Logo ausgeblendet wie bei der Admin-Sichtbarkeit (hideLogo: Datei, dann Zeile auf genau die ausgeblendeten Rechte mit hidden: true; die Rechte der Zeile bleiben die des Hochladers, auch wenn ein Admin löscht). Ein schon ausgeblendetes Logo überspringt das. Danach wie bisher Datei, Herzen, Meldungen, die Zeile zuletzt. Wer nicht löschen darf (403), ändert nichts. Das Zurückziehen von Karten bleibt unverändert. - Tests: Reihenfolge (Datei und Zeile ausblenden, Datei, Herzen, Meldungen, Zeile); Abbruch nach dem Löschen der Datei bei „Herzen lesen“, „Meldungen lesen“ und „Zeile löschen“ (Hochlader und Admin): die Zeile bleibt mit genau den ausgeblendeten Rechten, kein Gast liest sie, die Wiederholung vollendet das Löschen ohne erneutes Ausblenden; scheitert schon das Ausblenden, ist nichts gelöscht; Fremde und schon ausgeblendete Logos lösen kein Ausblenden aus. - README und Spec: Reihenfolge beim Löschen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
22 KiB
AirCard-IOS – Community-Logos, Englisch und GitHub-Link – Design
Stand: 2026-10-04 · Status: Entwurf im Chat von Kenso freigegeben („ja“), Spec + Plan + Umsetzung ohne weitere Rückfrage · baut auf 2026-10-04-aircard-website-design.md und 2026-10-04-aircard-community-design.md auf (live, main 8fb28eb)
1. Ziel und Rahmen
AirCard-IOS soll in die internationale Community der App AirCard-iOS, ohne dass Kenso fremde Marken-Logos selbst anbietet. Dafür:
- Keine eingebauten Marken-Logos mehr. Stattdessen laden Nutzer Logos hoch (Community-Logos), alle können sie benutzen, Herzen zeigen die beliebtesten.
- Die Seite gibt es auf Deutsch und Englisch.
- GitHub-Link zum Original-Repo der App, daneben der Sprachknopf.
| Punkt | Entscheidung (Kenso) |
|---|---|
| Eingebaute Logos | Alle 18 Netzwerk-Logos (Visa ×4, Mastercard ×5 inkl. „Klassisch“, UnionPay ×4, American Express ×3, JCB, Discover) und das Apple-Logo entfallen, auch ihre SVG-Dateien. |
| „Klassik kann so bleiben“ | Gemeint ist die Vorlage „Klassisch“ (Chip, Kontaktlos-Zeichen, Nummer, PLATINUM). Sie bleibt unverändert. |
| Logo oben links | Bleibt: AirCard-IOS Symbol, AirCard-IOS Text, Kein Logo (ohne Apple). |
| Platz der Community-Logos | Unten rechts, wo bisher das Kartennetzwerk saß. |
| Hochladen | Nur angemeldet; sofort öffentlich; Melden, Auto-Ausblenden ab 3 Meldern, Admin. |
| Herzen | Nur angemeldet, eins pro Konto und Logo, wieder entfernbar; Sortierung „Beliebt“ zuerst. |
| Sprache | Automatisch nach Browsersprache, Umschalter merkt sich die Wahl. |
| GitHub | Link https://github.com/Mak5er/AirCard-iOS in der Kopfzeile, Sprachknopf direkt daneben. |
Live-Stand beim Schreiben: 0 veröffentlichte Community-Karten, 1 gespeicherte Testkarte. Es gibt keinen Altbestand mit Marken-Logos, der aufgeräumt werden müsste.
2. Karte und Konfiguration
CardConfig: Das Feldnetworkentfällt. Neu istlogo: string | null, die ID eines Community-Logos (MusterID_PATTERN).brandist nur noch'aircards-symbol' | 'aircards-text' | 'none'.vbleibt1.parseConfig: Ein altesnetworkwird ignoriert. Ein fehlendes oder ungültigeslogoergibtnull.brand: 'apple'und andere Unbekannte ergeben den Standard (aircards-symbol).serializeConfigschreibt{v, template, logo, details, brand, crop}.DEFAULT_CONFIG.logo = null, eine neue Karte hat also kein Logo unten rechts.- In den Vorlagen-Layouts heißt
networkjetztlogo: „Nur Bild“ ohne, „Minimalistisch“, „Klassisch“ und „Signature“ mit Logo. - Das Logo sitzt wie bisher das Netzwerk-Logo: rechte Kante 1480, Unterkante 913, höchstens 360 px breit und 125 px hoch (Seitenverhältnis des Bildes bleibt).
- Einfarbige Logos (
mono = true) werden beim Zeichnen in der Details-Farbe gefüllt (Hell#FFFFFF, Dunkel#111111). Die Form kommt aus dem Alphakanal des PNG. Mehrfarbige Logos erscheinen unverändert. - Gelöscht werden 19 SVGs in
web/src/assets/marks/(alle außeraircards-symbol.svgundaircards-text.svg) sowie alles zu Netzwerk-Marken inmarks.tsundconfig.ts. - Fehlt ein Logo (gelöscht oder ausgeblendet), zeichnet der Editor die Karte ohne Logo und zeigt den Hinweis „Das gewählte Logo ist nicht mehr verfügbar.“. Die Auswahl bleibt, bis der Nutzer ein anderes Logo wählt. Beim Veröffentlichen aus „Meine Karten“ führt ein fehlendes Logo zum Abbruch mit „Das Logo dieser Karte ist nicht mehr verfügbar. Öffne die Karte im Editor und wähle ein anderes.“.
3. Editor: Bereich „Logo unten rechts“
Die rechte Spalte „Individuell gestalten“ zeigt von oben nach unten: Logo unten rechts, Details (Hell/Dunkel), Logo oben links (bisher „Logo“).
Der Bereich „Logo unten rechts“ besteht aus:
- Kopf: Überschrift „Logo unten rechts“, Plakette „Community“ und der Knopf „+ Hochladen“. Darunter ein kleiner Satz: „Logos werden von der Community hochgeladen.“
- Sortierung: Umschalter „Beliebt“ (Standard: meiste Herzen, bei Gleichstand neueste zuerst) und „Neu“ (neueste zuerst). Admins sehen zusätzlich „Gemeldet“ und „Ausgeblendet“.
- Raster (Desktop 2 Spalten):
- Erste Kachel „Kein Logo“.
- Jede Logo-Kachel zeigt das Logo so, wie es auf der Karte erscheint. Einfarbige Logos stehen wie die bisherigen Kacheln in Weiß auf dunklem Grund.
- Darunter folgen der Name (eine Zeile, gekürzt) und „von “ bzw. „von Anonym“ (klein, gedämpft).
- Der Herz-Knopf „♥ “ ist ein eigener Knopf, nicht in den Auswahlknopf verschachtelt.
aria-pressedzeigt, ob ich das Logo mit einem Herz markiert habe. - Ein Klick auf die Kachel setzt
config.logo. Karte und Vorlagen-Miniaturen zeigen das Logo sofort. - Ist das gewählte Logo nicht in der geladenen Liste (z. B. nach dem Öffnen einer gespeicherten Karte), steht es als zweite Kachel direkt nach „Kein Logo“.
- 24 Logos je Seite, darunter „Mehr laden“ (Cursor).
- Infozeile zum gewählten Logo unter dem Raster: „ · von · ♥ “ und der Knopf „Melden“.
- Wer das Logo hochgeladen hat, sieht zusätzlich „Löschen“ (mit Rückfrage).
- Admins sehen „ Meldungen“ mit den Gründen sowie „Ausblenden“/„Einblenden“ und „Löschen“.
- Leer: „Noch keine Community-Logos – lade das erste hoch.“
- Ladefehler: „Die Logos konnten nicht geladen werden.“ mit „Nochmal versuchen“.
- Herz ohne Anmeldung: Ein Klick öffnet den Login-Dialog, danach wird das Herz gesetzt. Das Herz schaltet sofort um (optimistisch). Scheitert die Anfrage, springt es zurück, und die Infozeile zeigt die Fehlermeldung.
- Handy (unter 900 px): Das Raster nutzt die volle Breite (
auto-fill, mindestens 140 px je Kachel).
4. Logo hochladen
Der Dialog „Logo hochladen“ (eigenes <dialog> wie die bisherigen Dialoge, alle Texte als Text, nie als HTML) enthält:
- Datei: PNG, JPG, WebP oder SVG, höchstens 5 MB und 40 Megapixel.
- Vorschau: das fertig verarbeitete Logo, einmal auf dunklem und einmal auf hellem Grund. Bei „Einfarbig“ ist es in Weiß bzw. Schwarz gefüllt.
- Name: Pflicht, höchstens 40 Zeichen.
- Anzeigename: optional, höchstens 30 Zeichen. Leer bedeutet „Anonym“, die E-Mail erscheint nie. Vorbelegt mit dem zuletzt benutzten Namen aus
localStorage(aircard.displayName; Zugriffe in try/catch). Der Veröffentlichen-Dialog liest und schreibt denselben Schlüssel. - Häkchen „Einfarbig – passt sich Hell/Dunkel an“: nur wählbar, wenn das Logo transparente Bereiche hat.
- Pflicht-Häkchen: „Ich habe die Rechte an diesem Logo oder die Erlaubnis des Inhabers, und es darf öffentlich geteilt werden.“
- Knöpfe: „Hochladen“ und „Abbrechen“.
Verarbeitung im Browser (der Server prüft trotzdem):
- Datei dekodieren. SVG wird als Bild geladen, also ohne Skripte. Hat ein SVG keine eigene Größe, gilt 1000 × 1000.
- Transparente Ränder abschneiden (Alpha ≤ 8 gilt als leer).
- Auf höchstens 720 × 250 px verkleinern, nie vergrößern. SVG wird direkt in der Zielgröße gerastert.
- Als PNG ausgeben.
- Abbruch mit „Das Logo ist leer.“, wenn nichts Sichtbares bleibt, und mit „Das Logo ist zu klein (mindestens 8 × 8 Pixel).“ unter 8 × 8 px.
Ablauf:
- Das PNG landet im Bucket
logo-imagesmit den Rechtenread("any")unddelete("user:<ich>"). POST /api/logos/hochladenwird aufgerufen.- Scheitert der Aufruf eindeutig, löscht der Client die Datei wieder (gleiche Regel wie beim Veröffentlichen). Bleibt der Ausgang offen, bleibt die Datei liegen.
- Danach ist das Logo für alle sichtbar, wird gewählt (
config.logo) und steht als erste Logo-Kachel.
Pro Konto sind höchstens 30 Logos erlaubt, ausgeblendete zählen mit. Darüber hinaus erscheint: „Du hast schon 30 Logos hochgeladen. Lösche eins, um ein neues hochzuladen.“
5. Herzen
- Die Tabelle
logo_likeshat eine Zeile je Konto und Logo (feste Zeilen-ID aus Logo und Nutzer). Der ZählerlikeCountinlogoswird nur von der API gesetzt, und zwar immer als Anzahl der Zeilen (neu gezählt, nicht hoch- oder runtergezählt). - Der Client lädt den Herz-Zustand für die angezeigten Logos mit
equal('userId', me)undequal('logoId', [ids]). Beim An- oder Abmelden wird er neu geladen bzw. geleert. - Für ausgeblendete oder gelöschte Logos gibt es kein Herz (404 „Dieses Logo ist nicht (mehr) verfügbar.“).
6. Melden, Löschen, Admin
- Melden funktioniert wie bei Karten (gleicher Dialog, Text „dieses Logo“).
- Gründe für Logos: „Unangemessen“, „Urheberrecht/Marke“, „Spam“, „Sonstiges“.
- Ein Melder ist ein IP-Netz (IPv4 /24, IPv6 /48), gespeichert nur als Hash.
- Rate-Limit: 10 Meldungen pro Stunde je Anschluss, gemeinsam mit den Karten-Meldungen.
- Ab 3 verschiedenen Meldern wird das Logo ausgeblendet. Zeile und Datei sind dann nur noch für den Hochlader und Admins lesbar.
- Löschen (Hochlader oder Admin) blendet das Logo zuerst aus (Datei und Zeile), damit nach einem Abbruch keine öffentliche Zeile mit gelöschter Datei zurückbleibt, und entfernt dann Datei, Herzen, Meldungen und zuletzt die Zeile. Gespeicherte Karten zeigen das Logo danach nicht mehr (Hinweis siehe § 2). Bereits veröffentlichte Kartenbilder bleiben unverändert, weil das Logo im Bild steckt.
- Admin (Label
admin):- Filter „Gemeldet“ (
reportCount > 0) und „Ausgeblendet“. - Ein- und Ausblenden. Einblenden löscht die Meldungen und setzt
reportCountauf 0. - Löschen.
- Filter „Gemeldet“ (
7. Sprache und Kopfzeile
Kopfzeile, rechte Seite: GitHub · Sprache · ☕ Unterstützen · Hell/Dunkel · Menü.
- GitHub: GitHub-Zeichen und der Text „GitHub“.
- Ziel ist
https://github.com/Mak5er/AirCard-iOSmittarget="_blank" rel="noopener noreferrer". Der Title lautet „AirCard-iOS auf GitHub“ bzw. „AirCard-iOS on GitHub“. - Zwischen 600 und 1179 px zeigt der Link nur das Zeichen (wie der Spenden-Knopf).
- Unter 600 px steht der Link im aufgeklappten Menü statt in der Kopfzeile.
- Ziel ist
- Sprachknopf: Globus-Zeichen plus das Kürzel der Zielsprache, also „EN“ auf der deutschen und „DE“ auf der englischen Seite.
aria-labelundtitlestehen in der Zielsprache („Switch to English“ bzw. „Auf Deutsch umschalten“), dazu ein passendeslangam Knopf.- Der Knopf bleibt bei jeder Breite in der Kopfzeile.
Startsprache: zuerst localStorage aircard.lang (de oder en). Sonst Deutsch, wenn die erste bevorzugte Browsersprache (navigator.languages[0], ersatzweise navigator.language) mit de beginnt, andernfalls Englisch. <html lang> folgt der Sprache und wird wie das Theme vor dem ersten Paint per Inline-Skript gesetzt.
Umschalten geht ohne Neuladen:
- Kopfzeile, Spenden-Banner, Konto-Menü, Fußzeile und die aktuelle Seite werden neu aufgebaut. Der Editor-Entwurf bleibt erhalten, weil er im Store liegt.
document.titlewird neu gesetzt.- Der Appwrite-Client bekommt die Sprache (
client.setLocale), damit Passwort-Mails in dieser Sprache kommen.
Übersetzt wird alles Sichtbare: Texte, aria-label, Platzhalter, title, Seitentitel, Fehlermeldungen (Client, Appwrite, API) und Datumsangaben (Deutsch 04.10.2026, Englisch Oct 4, 2026).
Nicht übersetzt werden:
- Nutzerinhalte: Titel, Herausgeber, Tags, Logo-Namen, Anzeigenamen.
- Routen: Sie bleiben
/gestalten,/karten,/community/<id>,/passwort-neu. - Download-Dateinamen, README und Dokumentation.
- Die Werte der Melde-Gründe in der API.
Technik (gettext-Stil):
t('Deutscher Text', { name })übersetzt beim Aufruf. Platzhalter stehen als{name}im Text.msg('…')markiert Texte, die vorab in Konstanten stehen. Sie gehen erst beim Anzeigen durcht().- Das Wörterbuch
web/src/i18n/en.tsordnet Deutsch → Englisch zu. Fehlt ein Eintrag, bleibt der deutsche Text. - Ein Test sammelt alle Literale aus
t()/msg()inweb/src/**(ohne Tests). Er prüft, dass jedes eine englische Übersetzung hat, dassen.tskeine ungenutzten Einträge enthält und dass keint()mit${…}aufgerufen wird. - In Tests ist die Sprache Deutsch (Vitest-Setup).
- Englisch: kurz, freundlich, US-Schreibweise. „AirCard-IOS“ bleibt der Name der Seite, „AirCard-iOS“ der Name der App.
Fehler der API: Antworten tragen zusätzlich code, den Schlüssel aus MSG, z. B. {"error":"…","code":"alreadyPublished"}. Der Client übersetzt über den Code. Ohne bekannten Code zeigt er auf Deutsch den Servertext und auf Englisch eine allgemeine Meldung je Status (400 „Invalid request.“, 403 „You are not allowed to do that.“, 404 „Not found.“, sonst „Something went wrong. Please try again.“).
Statisches index.html: <title>AirCard-IOS – Card designs for Apple Wallet</title> und <meta name="description" content="Discover community card designs for AirCard-iOS, create your own and download it as PNG.">, damit Link-Vorschauen englisch sind. Der Router setzt danach den Titel in der gewählten Sprache.
Fußzeile:
- Deutsch: „Inoffizielle Community-Seite für die App AirCard-iOS – kein Teil des Original-Projekts und nicht mit Apple verbunden. Logos und Motive stammen von Nutzern; alle Marken gehören ihren Inhabern.“
- Englisch: „Unofficial community site for the AirCard-iOS app – not part of the original project and not affiliated with Apple. Logos and artwork are uploaded by users; all trademarks belong to their owners.“
8. Daten (Appwrite, Projekt 6ac2130a00289343d6d5, Datenbank aircard, Setup-Skript erweitert)
Tabelle logos (rowSecurity an, keine Tabellenrechte für Clients):
| Spalte | Typ | Inhalt |
|---|---|---|
name |
string 40, Pflicht | Name des Logos |
displayName |
string 30 | Anzeigename oder leer |
fileId |
string 36, Pflicht | PNG im Bucket logo-images |
ownerId |
string 36, Pflicht | Nutzer-ID des Hochladers |
mono |
boolean, Standard false | einfarbig |
likeCount |
integer, Standard 0, min 0 | Anzahl Herzen |
hidden |
boolean, Standard false | ausgeblendet |
reportCount |
integer, Standard 0, min 0 | Anzahl verschiedener Melder |
Indizes: owner (key, ownerId), hidden (key, hidden), likes (key, likeCount).
Zeilen-ID = hash32("<ownerId>|<fileId>") (seit 2026-10-05; zuerst war sie die Datei-ID, die Altlogos behalten sie). Dieselbe Datei desselben Kontos ergibt dieselbe ID, ein zweiter Versuch also 409. Die ID eines gelöschten Logos steht öffentlich in der Liste und in gespeicherten Karten und lässt sich so nicht mit einer Datei gleicher ID neu belegen. Dass eine Datei nie zwei Logos trägt, sichert der Besitz: nach dem Anlegen hat sie kein delete(owner) mehr.
Zeilenrechte (setzt nur die API): sichtbar ['read("any")'], ausgeblendet ['read("user:<owner>")','read("label:admin")'].
Tabelle logo_likes (rowSecurity an, keine Tabellenrechte):
- Spalten:
logoIdstring 36 Pflicht,userIdstring 36 Pflicht. - Zeilen-ID =
hash32("<logoId>|<userId>"). - Zeilenrecht
['read("user:<userId>")']. - Indizes:
logo(key,logoId),user_logo(key,userId,logoId).
Tabelle logo_reports (rowSecurity aus, Tabellenrecht read("label:admin"), Schreiben nur API):
- Spalten:
logoIdstring 36 Pflicht,reasonenum (unangemessen, kartendaten, urheberrecht, spam, sonstiges) Pflicht,reporterHashstring 64 Pflicht. - Zeilen-ID =
hash32("logo|<logoId>|<reporterHash>"). - Index
logo(key,logoId).
Bucket logo-images:
- Bucket-Recht
create("users"), fileSecurity an, höchstens 1 MiB, Endung nurpng, ohne Kompression und ohne Virenscan. - Dateirechte beim Hochladen:
read("any"),delete("user:<owner>"). - Nach dem Anlegen setzt die API genau
read("any"). Der Hochlader verliert damitdelete, sonst könnte er das Bild unter derselben ID tauschen. - Ausgeblendet:
read("user:<owner>"),read("label:admin").
Bestehende Tabellen und Buckets bleiben unverändert.
9. API-Dienst aircard-api (Erweiterung)
Die neuen Endpunkte stehen in einem eigenen Modul deploy/api/logos.js. Reine gemeinsame Helfer (Meldetexte, Prüfungen, Rechte, Melder-Hash) wandern nach deploy/api/common.js. app.js reicht dem Logo-Modul seine laufenden Helfer (Anmeldung, Sperre je Schlüssel, Rate-Limit, Log) und hängt dessen Routen an. Der Betrieb kopiert künftig fünf Dateien (appwrite.js, common.js, app.js, logos.js, server.js). appwrite.js bekommt countRows(table, queries), das total einer Abfrage mit limit(1) liefert.
| Endpunkt | Auth | Wirkung |
|---|---|---|
POST /api/logos/hochladen {name, displayName?, mono, fileId} |
Nutzer-JWT | Prüft Felder; die Datei existiert, liegt in logo-images, trägt delete("user:<ich>") und read("any"), ist PNG, höchstens 1 MiB, Maße zwischen 8 × 8 und 720 × 250 (aus dem PNG-Kopf); höchstens 30 Logos je Konto. Legt die Zeile an (sichtbar), setzt die Datei auf read("any") und nimmt die Zeile zurück, wenn das scheitert. → 201 {id}; 409 bei schon verwendeter Datei; 429 bei Limit. |
POST /api/logos/herz {id, herz} |
Nutzer-JWT | 404 bei fehlendem oder ausgeblendetem Logo. herz: true legt die Like-Zeile an (409 = schon da, kein Fehler), false löscht sie (404 = schon weg, kein Fehler). Danach likeCount = countRows. → 200 {herz, likeCount} |
POST /api/logos/melden {id, reason} |
keine | Wie /api/melden: Rate-Limit (gemeinsam), Melder-Hash, Duplikat → 200 {schonGemeldet:true}, reportCount = Zeilen, ab 3 ausblenden. 404 bei fehlendem oder ausgeblendetem Logo. |
POST /api/logos/loeschen {id} |
Nutzer-JWT (Hochlader oder Admin) | Blendet das Logo zuerst aus, löscht dann Datei, Herzen, Meldungen und zuletzt die Zeile. 403 bei fremdem Logo, 404 bei fehlendem. → 200 {ok:true} |
POST /api/admin/logo-sichtbarkeit {id, sichtbar} |
Admin-JWT | Wie /api/admin/sichtbarkeit für Logos (Datei- und Zeilenrechte, hidden; Einblenden löscht Meldungen, reportCount = 0). |
Alle Fehlerantworten (auch die bestehenden) haben die Form {error: <deutscher Text>, code: <MSG-Schlüssel>}. Dynamische Prüftexte ohne festen Schlüssel bekommen code: 'badRequest'.
Body höchstens 8 KB, nur application/json, sonst unverändert wie in der Community-Spec § 8.
10. Was Kenso tun muss
Nichts Pflicht. Der vorhandene Provisioning-Key (AIRCARD_APPWRITE_API_KEY) legt das Schema an. Der API-Key aircard-api hat die nötigen Scopes (Zeilen und Dateien). Fürs Testen der Admin-Knöpfe braucht Kensos Konto weiterhin das Label admin (steht noch aus der Community-Runde offen).
11. Fehlerbehandlung
| Fall | Meldung |
|---|---|
| Datei falscher Typ / zu groß / zu viele Pixel | „Bitte ein PNG-, JPG-, WebP- oder SVG-Bild wählen.“ / „Die Datei ist größer als 5 MB.“ / „Das Bild ist zu groß (max. 40 Megapixel).“ |
| Datei nicht lesbar | „Das Logo konnte nicht gelesen werden.“ |
| Logo leer / zu klein | siehe § 4 |
| Name fehlt / zu lang | „Bitte gib einen Namen an.“ / „Der Name ist zu lang (höchstens 40 Zeichen).“ |
| Häkchen fehlt | „Bitte bestätige, dass du das Logo teilen darfst.“ |
| Limit 30 | siehe § 4 |
| Hochladen gescheitert | „Hochladen fehlgeschlagen – “, Datei wird bei eindeutigem Fehler gelöscht |
| Herz/Melden für weg/ausgeblendet | „Dieses Logo ist nicht (mehr) verfügbar.“ |
| Melden doppelt / Rate-Limit | „Danke, du hast dieses Logo schon gemeldet.“ / „Zu viele Meldungen – bitte später nochmal.“ |
| API nicht erreichbar | „Der Dienst ist gerade nicht erreichbar.“ |
| Nicht angemeldet (Hochladen, Herz) | Login-Dialog (withLogin/requireLogin), Abbruch → nichts passiert |
Alle Texte gibt es auf Deutsch und Englisch.
12. Tests
- Vitest:
- i18n: Startsprache, Umschalten, Platzhalter, Abdeckung aller
t()/msg()-Texte, Datum. - Konfiguration: alte Konfiguration mit
network/apple,logo. - Renderer: Logo-Box, einfarbig, ohne Logo.
- Logo-Verarbeitung: Rand-Zuschnitt, Skalierung, leer, zu klein, Transparenz-Erkennung.
- Logo-Abfragen: Beliebt/Neu/Admin-Filter, Cursor, eigene Herzen.
- Hochladen-Ablauf mit Fakes: Aufräumen nur bei eindeutigem Fehler.
- Logo-Bereich: Kacheln, Auswahl, Herz optimistisch und Rücksprung, Login-Pflicht, angeheftete Auswahl, Infozeile je Rolle.
- Kopfzeile: GitHub-Link, Sprachknopf.
- Bestehende Tests bleiben grün (Sprache im Test = Deutsch).
- i18n: Startsprache, Umschalten, Platzhalter, Abdeckung aller
- node:test (
aircard-api):- Hochladen: fremde Datei 403, falsches Format/Maße 400, Limit 429, doppelt 409, Rechte nachher genau
read("any"), Rücknahme bei Fehler. - Herz: an/aus idempotent, Zähler = Zeilen, ausgeblendet 404.
- Melden: Dedup, gemeinsames Rate-Limit, ab 3 ausgeblendet.
- Löschen: Hochlader/Admin/fremd.
- Admin-Sichtbarkeit.
codein allen Fehlerantworten.
- Hochladen: fremde Datei 403, falsches Format/Maße 400, Limit 429, doppelt 409, Rechte nachher genau
- Setup-Skript:
--checkdecktlogos,logo_likes,logo_reportsundlogo-imagesinkl. Indizes ab. - Live (aircard-ios.org):
- Hochladen (PNG transparent, einfarbig; SVG; JPG ohne Transparenz). Das Logo ist für Gäste sichtbar und sitzt auf der Karte; Download-PNG enthält es.
- Herz an/aus (Zahl, Sortierung).
- Melden über 3 Netze → ausgeblendet; Gast sieht es nicht mehr.
- Löschen.
- Sprachwechsel mit offenem Editor-Entwurf (bleibt erhalten).
- Englisch nach Browsersprache; GitHub-Link.
- Breiten 1440 und 390 px, Hell/Dunkel.
13. Nicht im Umfang
Logo-Suche, Community-Logos oben links, Herzen für Community-Karten, Zähler „wie oft benutzt“, Logos bearbeiten (nur löschen und neu hochladen), englische Routen, weitere Sprachen, Mail an Kenso bei Meldungen, SVG-Speicherung (gespeichert wird immer PNG).
14. Risiken
- Marken über Nutzer: Nutzer können Visa & Co. selbst hochladen. Dagegen stehen das Pflicht-Häkchen, der Melde-Grund „Urheberrecht/Marke“, das Auto-Ausblenden und der Admin. Kenso haftet ab Kenntnis, sollte also gemeldete Logos regelmäßig ansehen. Ohne Kontaktadresse/Impressum fehlt ein Beschwerdeweg außerhalb von „Melden“ (Kenso-Entscheidung aus der Website-Runde, bekannt).
- Herzen: Konten sind kostenlos, viele Konten ergeben viele Herzen (akzeptiert). Auth-Limits in der Console bleiben Kensos offener Schritt.
- Zählung:
countRowsnutzttotalvon Appwrite (zählt bis 5000). Das reicht. - Übersetzungslücken: Der Abdeckungstest fängt fehlende Einträge. Server-Prüftexte ohne Code erscheinen auf Englisch als allgemeine Meldung.