feat(api): aircard-api für Veröffentlichen, Melden und Admin

Neuer abhängigkeitsfreier Node-22-Dienst in deploy/api/ (appwrite.js, app.js,
server.js), der alle Galerie-Schreibzugriffe mit dem Appwrite-Server-Key
ausführt: POST /api/veroeffentlichen, /zurueckziehen, /melden (Rate-Limit
10/h je Melder, IPv6 auf /64, Auto-Ausblenden ab 3), /admin/sichtbarkeit und
GET /api/gesundheit.

- JWT-Prüfung über GET /account (Header X-Aircard-Jwt), Admin = Label admin
- IDs per Muster geprüft, Pfadsegmente kodiert, "." und ".." abgelehnt
- deterministische Zeilen-IDs (Galerie, Meldung): Appwrite-409 ersetzt jede
  Doppelt-Prüfung; Sperre je Karte gegen verlorene Zählerstände
- Dateiprüfung: Bucket, delete("user:<ich>"), PNG/WebP, höchstens 5 MiB
- Appwrite-Aufrufe mit 10-s-Zeitlimit, Logs ohne Key, Salz, JWT und IPs
- Tests gegen einen Fake-Appwrite im Speicher (70 Tests), Compose-Service
  aircard-api (Router aircard-gallery-api) und README ergänzt

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 14:09:30 +00:00
1 parent 9e4f4ffacc
commit dd1597862a
11 files changed
+2659 -13

No files matched your search

+41 -3
View File
@@ -3,10 +3,12 @@
# URL: aircard-ios.org
# Repository: https://git.webklar.com/knso/AirCardIOS
# Einfügen unter `services:` direkt nach dem eship-Block (siehe deploy/README.md).
# Starten NUR mit: docker compose up -d aircard-build aircard
# Starten NUR mit: docker compose up -d aircard-build aircard aircard-api
# (ein nacktes `up -d` würde andere Dienste wieder anlegen)
# Die Appwrite-Route liegt als Datei in traefik/dynamic/aircard.yml.
# AIRCARD_APPWRITE_PROJECT_ID kommt aus /home/webklar/apps/.env.
# Die Appwrite-Route liegt als Datei in traefik/dynamic/aircard.yml (Router `aircard-api@file`, Pfad /v1).
# Der API-Dienst `aircard-api` hängt per Label am Pfad /api (Router `aircard-gallery-api`, ein anderer Name,
# damit er nicht mit der Datei-Route kollidiert).
# AIRCARD_APPWRITE_PROJECT_ID, AIRCARD_API_KEY und AIRCARD_API_SALT kommen aus /home/webklar/apps/.env.
# ==============================================
aircard-build:
image: node:22-alpine
@@ -80,3 +82,39 @@
# - "traefik.http.middlewares.aircard-www.redirectregex.regex=^https?://www\\.aircard-ios\\.org(.*)"
# - "traefik.http.middlewares.aircard-www.redirectregex.replacement=https://aircard-ios.org$${1}"
# - "traefik.http.middlewares.aircard-www.redirectregex.permanent=true"
# --- API-Dienst der Community-Galerie (deploy/api/, siehe deploy/api/README.md) ---
# Ohne AIRCARD_API_KEY oder AIRCARD_API_SALT in der .env beendet sich der Dienst mit Exit-Code 1.
# Kein `ports:`: erreichbar nur über Traefik (der Dienst vertraut dem Header X-Real-Ip).
aircard-api:
image: node:22-alpine
container_name: webklar-aircard-api
restart: unless-stopped
init: true
mem_limit: 128m
user: node
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
working_dir: /srv
environment:
- AIRCARD_API_PORT=8088
- AIRCARD_APPWRITE_ENDPOINT=http://webklar-appwrite/v1
- AIRCARD_APPWRITE_PROJECT_ID=${AIRCARD_APPWRITE_PROJECT_ID}
- AIRCARD_API_KEY=${AIRCARD_API_KEY}
- AIRCARD_API_SALT=${AIRCARD_API_SALT}
- NODE_OPTIONS=--max-old-space-size=64
volumes:
- ./aircard-ios.org/api:/srv:ro
networks:
- webklar-network
command: node /srv/server.js
labels:
- "traefik.enable=true"
- "traefik.http.routers.aircard-gallery-api.rule=Host(`aircard-ios.org`) && PathPrefix(`/api`)"
- "traefik.http.routers.aircard-gallery-api.entrypoints=websecure"
- "traefik.http.routers.aircard-gallery-api.tls.certresolver=letsencrypt"
- "traefik.http.routers.aircard-gallery-api.priority=200"
- "traefik.http.services.aircard-gallery-api.loadbalancer.server.port=8088"