176 lines
22 KiB
Markdown
176 lines
22 KiB
Markdown
# Firebase
|
||
|
||
Das Firebase-Projekt heißt **MetalCircle**. Android ist dauerhaft als `de.pinguholic.concerts` registriert. Manuelle Testnachrichten funktionieren bereits. Automatischer Versand für Freundschaftsanfragen, Direktnachrichten und Veranstaltungseinladungen ist implementiert und benötigt einen separaten serverseitigen Zugang sowie `PUSH_ENABLED=true`.
|
||
|
||
## Unterschiedliche Konfigurationsdateien
|
||
|
||
- `android/android/app/google-services.json`: lokale, Git-ignorierte Android-Client-Konfiguration. Projekt-/App-Kennungen und der Client-API-Key werden vom Build in die APK übernommen; sie sind kein Backend-Privatschlüssel.
|
||
- **Service-Account-JSON**: privater Schlüssel für das Backend. Niemals in Git, APK, Docker-Image, Webassets, Chat, Wiki oder Logs aufnehmen. Die Android-Datei ersetzt diesen Zugang nicht.
|
||
|
||
## Lokale Entwicklungs-/Testumgebung
|
||
|
||
1. In Firebase **MetalCircle** auswählen und die tatsächliche **Projekt-ID** notieren; sie kann vom Anzeigenamen abweichen.
|
||
2. In der zugehörigen Google Cloud Console die **Firebase Cloud Messaging API (HTTP v1)** prüfen/aktivieren.
|
||
3. Der lokale Test verwendet den dedizierten Service Account `metalcircle-push-local@metalcircle-30d9b.iam.gserviceaccount.com`. Für dessen Versand ist die Projektrolle **Firebase Cloud Messaging API Admin**, Rollen-ID `roles/firebasecloudmessaging.admin`, erforderlich. Diese genaue Rolle ist diesem lokalen Test-Service-Account auf Projektebene im Projekt `metalcircle-30d9b` zugewiesen. Sie enthält `cloudmessaging.messages.create`. Nicht mit ähnlich benannten Firebase-Administrationsrollen verwechseln; OAuth-Tokenbezug allein beweist keine Versandberechtigung. Keine persönlichen oder Gitea-Zugänge verwenden. Siehe [Firebase IAM](https://firebase.google.com/docs/projects/iam/permissions) und [FCM-Rollen](https://docs.cloud.google.com/iam/docs/roles-permissions/firebasecloudmessaging).
|
||
4. Den privaten JSON-Schlüssel geschützt **außerhalb des Repositories und Docker-Buildkontexts** speichern. Der Betreiber verwaltet die Datei. [Firebase Admin Setup](https://firebase.google.com/docs/admin/setup) beschreibt Service-Account-Dateien.
|
||
5. Dateirechte einschränken, beispielsweise `chmod 600 /absoluter/pfad/firebase-service-account.json`. Keine Inhalte ausgeben.
|
||
6. In der lokalen `.env` die folgenden Werte selbst eintragen:
|
||
|
||
```dotenv
|
||
PUSH_ENABLED=true
|
||
FIREBASE_PROJECT_ID=<tatsaechliche-test-projekt-id>
|
||
FIREBASE_SERVICE_ACCOUNT_FILE=/absoluter/pfad/firebase-service-account.json
|
||
```
|
||
|
||
7. In der eigenen lokalen HTTP-Testumgebung starten:
|
||
|
||
```bash
|
||
COOKIE_SECURE=false docker compose -f compose.yml -f compose.push.yml up -d --build web
|
||
```
|
||
|
||
`compose.push.yml` liest `FIREBASE_SERVICE_ACCOUNT_FILE` als absoluten Host-Pfad, bindet diese Datei **read-only** unter `/run/secrets/firebase-service-account.json` in den `web`-Container ein und setzt `GOOGLE_APPLICATION_CREDENTIALS=/run/secrets/firebase-service-account.json`. Die Quelldatei muss existieren. Der Sender prüft, dass `FIREBASE_PROJECT_ID` zur Projekt-ID des Credentials passt. Die Android-App muss dasselbe Firebase-Projekt verwenden. Schlüsseldatei, `.env` und Token gehören weder ins Repository noch in ein Image.
|
||
|
||
Cloud-Staging richtet der Betreiber separat ein; dort hinter HTTPS `COOKIE_SECURE=true` lassen. Codex auf PinguCore greift nicht automatisch darauf zu. Produktion bekommt später eigene Credentials, keine kopierten Testschlüssel.
|
||
|
||
## Verifizierter lokaler E2E-Stand
|
||
|
||
Am **15.09.2026** wurde der vollständige lokale Weg mit dem Android-Testgerät und dem lokalen Backend erfolgreich geprüft. Nach Zuweisung von `roles/firebasecloudmessaging.admin` verschwand der vorherige HTTP-403-Fehler `PERMISSION_DENIED`. Das Service-Account-Credential stimmte mit `FIREBASE_PROJECT_ID=metalcircle-30d9b` überein; der Firebase Admin SDK Versand wurde vom FCM HTTP-v1-Endpunkt angenommen.
|
||
|
||
Freundschaftsanfrage und Veranstaltungseinladung wurden im Android Notification Manager nachgewiesen. Die Direktnachrichten-Benachrichtigung wurde auf dem Gerät gesehen; Antippen öffnete den zugehörigen Chat. Alle drei Benachrichtigungen verwenden generische Vorschautexte ohne Nachrichtentext oder private Veranstaltungsdetails. Die installierte Test-App war `1.1.0-debug`, Package ID `de.pinguholic.concerts`.
|
||
|
||
Für eine Wiederholung lokale App und Backend verwenden; mit `adb reverse tcp:8080 tcp:8080` wird der Android-Testbuild an den lokalen Port 8080 weitergeleitet. Keine Cloud-Staging- oder Produktionsumgebung verwenden. In der Datenbank bedeutet `push_notifications.state='sent'`, dass der Firebase-Sendeaufruf angenommen wurde; für einen vollständigen E2E-PASS zusätzlich den tatsächlichen Android-Empfang über Notification Manager oder gleichwertige Gerätebeobachtung prüfen.
|
||
|
||
Automatisierte Backend-Tests simulieren Firebase und belegen nicht die Cloud-IAM-Berechtigung. Die lokale Suite mit 53 Tests und der Android-Debug-Build waren erfolgreich; der oben beschriebene Gerätetest hat zusätzlich den echten FCM-Versand und Empfang bestätigt. Bei erneutem `configuration`-/403-Fehler Projekt-ID, aktiven Service Account, dessen `roles/firebasecloudmessaging.admin`-Zuweisung im richtigen Firebase-Projekt, API-Aktivierung und Secret-Mount prüfen. Logs enthalten absichtlich keine vollständigen Firebase-Fehlerantworten oder Secrets. Keine Legacy-Server-Keys einsetzen.
|
||
|
||
## Pre-Production Deployment
|
||
|
||
### Bestand und Grenzen
|
||
|
||
Im Repository gibt es `compose.yml` für Web/DB, die optionale Secret-Einbindung `compose.push.yml` und eine lokale, ignorierte `compose.dev.yml` mit Reload/Quellcode-Mount. Die vom Betreiber bestätigte aktuelle Pre-Production-Adresse ist **https://konzerte.pinguholic.de/**; das Repository bleibt **`kai/pingu-concerts`**. Der Server-Checkout liegt unter `/opt/pingu-concerts`; die aktive Konfiguration liegt außerhalb unter `/home/kai/.config/metalcircle/preprod.env`. Die folgenden Befehle werden **vom Betreiber im bestehenden Pre-Production-Checkout** ausgeführt. Den vorhandenen Compose-Projektnamen und gegebenenfalls serverseitige Overrides beibehalten, damit dieselbe Pre-Production-Datenbank und dieselben Upload-Volumes verwendet werden. Keine zweite Installation mit denselben festen Containernamen auf demselben Docker-Host starten.
|
||
|
||
Neu ist `compose.preprod.yml`: Es übernimmt per `extends` die Einbindung aus `compose.push.yml`, aktiviert Push und erzwingt sichere Cookies. Start mit `compose.yml` + `compose.preprod.yml`; die dritte Datei muss nicht zusätzlich angegeben werden. Pre-Production benötigt HTTPS am vorhandenen Reverse Proxy. Die Basisdatei behält `PUSH_ENABLED=false` als Standard; lokale Konfiguration und spätere Produktion werden nicht automatisch aktiviert.
|
||
|
||
### Einmalig in Google Cloud: eigener Zugang
|
||
|
||
1. Projekt **`metalcircle-30d9b`** auswählen. Unter **APIs & Services → Enabled APIs** prüfen, dass **Firebase Cloud Messaging API** (`fcm.googleapis.com`, HTTP v1) aktiviert ist; nur falls nötig aktivieren.
|
||
2. Unter **IAM & Admin → Service Accounts → Create service account** den Namen und die ID **`metalcircle-push-preprod`** anlegen. Erwartete Adresse: `metalcircle-push-preprod@metalcircle-30d9b.iam.gserviceaccount.com`.
|
||
3. Diesem Service Account im genannten Projekt **Firebase Cloud Messaging API Admin**, exakt **`roles/firebasecloudmessaging.admin`**, zuweisen. Der Versand benötigt `cloudmessaging.messages.create`; die vordefinierte FCM-API-Rolle enthält diese Berechtigung. Keine Editor-/Owner-/Firebase-Admin-Rolle und keine zusätzliche Service Account Token Creator-Rolle für die hier verwendete JSON-Schlüssel-Authentifizierung vergeben. Nicht die ähnlich benannte Rolle `roles/firebasenotifications.admin` auswählen. [Offizielle Rollenübersicht](https://docs.cloud.google.com/iam/docs/roles-permissions/firebasecloudmessaging), [FCM HTTP v1](https://firebase.google.com/docs/cloud-messaging/send/v1-api).
|
||
4. Beim **neuen** Service Account unter **Keys → Add key → Create new key → JSON** einen Schlüssel erzeugen und über einen sicheren Übertragungsweg auf den Pre-Production-Host bringen. Der Betreiber führt diesen Schritt aus. Nicht die Admin-SDK-Schaltfläche für einen allgemeinen Standard-Service-Account verwenden und keine Organisationsrichtlinie für verbotene Schlüssel umgehen.
|
||
|
||
Der lokale Account `metalcircle-push-local@metalcircle-30d9b.iam.gserviceaccount.com` und sein Schlüssel bleiben ausschließlich lokal. Produktion bekommt später einen weiteren eigenen Account/Schlüssel. Unterschiedliche Service Accounts im selben Firebase-Projekt trennen Credentials, aber nicht die FCM-Token-Zielgruppe: Die separate Backend-Datenbank und frische Registrierung sind deshalb zwingend.
|
||
|
||
### A. Einmalig auf dem Pre-Production-Host
|
||
|
||
Verbindlicher Host-Pfad: `/home/kai/.secrets/metalcircle/firebase-push-preprod.json`. Der Key gehört dem Deployment-Benutzer kai und hat Modus 600, das Verzeichnis Modus 700. Der bestehende read-only Mount bleibt `/run/secrets/firebase-service-account.json`; `create_host_path: false` verhindert Verzeichnisse anstelle fehlender Dateien. Bei Rootless-Docker oder abweichender Container-UID muss der Betreiber die UID-Abbildung prüfen; nicht auf weltweite Leserechte ausweichen.
|
||
|
||
Die gesamte aktive Konfiguration liegt in `/home/kai/.config/metalcircle/preprod.env` (Eigentümer kai, Modus 600). Vorlage: `config/preprod.env.example`. Neben vollständigen DB-/Admin-/Gitea-Werten benötigt Firebase:
|
||
|
||
```dotenv
|
||
PUSH_ENABLED=true
|
||
FIREBASE_PROJECT_ID=metalcircle-30d9b
|
||
FIREBASE_SERVICE_ACCOUNT_FILE=/home/kai/.secrets/metalcircle/firebase-push-preprod.json
|
||
COOKIE_SECURE=true
|
||
```
|
||
|
||
`GOOGLE_APPLICATION_CREDENTIALS` setzt Compose intern. Kein `~` als Pfad und keine Credential-Inhalte in diese Anleitung übernehmen. Die alte Checkout-`.env` wird vom Deployment nicht mehr gelesen und nicht automatisch gelöscht. Die vollständige [Migrationsanleitung](Deployment.md#migration-der-bisherigen-checkout-env) beschreibt Übernahme, Prüfung, Deployment und das spätere manuelle Archivieren.
|
||
|
||
### B. Deployment im Pre-Production-Checkout
|
||
|
||
```bash
|
||
cd /opt/pingu-concerts
|
||
./scripts/deploy-preprod.sh --check
|
||
./scripts/deploy-preprod.sh
|
||
```
|
||
|
||
Das Script verlangt einen sauberen `main`-Checkout und verwendet durchgehend `--env-file /home/kai/.config/metalcircle/preprod.env` mit `compose.yml` + `compose.preprod.yml`. Pflichtwerte, Host-Dateirechte und externer Firebase-Key werden vor dem Build geprüft. Vor dem Containerwechsel müssen anschließend **Firebase und Gitea** in temporären Containern PASS liefern. Danach wird die Env-Datei außerhalb des Checkouts gesichert (700/600, letzte zehn Kopien) und nur `web` mit `up -d --no-deps web` aktualisiert. Beide Preflights laufen danach erneut im tatsächlichen Webcontainer. [Ablauf, Abbruchverhalten und manueller Fallback](Deployment.md).
|
||
|
||
Der bestehende Firebase-Preflight bleibt offline: Push aktiv, Containerpfad, reguläre Datei, restriktive Rechte, read-only Mount, gültiges Credential, Projekt und exakter Pre-Production-Service-Account. PASS belegt **kein** IAM und keine FCM-Zustellung. Gitea wird zusätzlich read-only als `metalcircle-bot` auf Erreichbarkeit geprüft; keine Test-Issues oder Attachments werden erstellt.
|
||
|
||
Vor Aktivierung bestätigen: Die vorhandene DB ist die Pre-Production-DB, keine lokalen Daten/Sessions wurden importiert. Frisch in Pre-Production anmelden und das Android-Gerät dort registrieren. Keine Local-Datenbank kopieren.
|
||
|
||
Eine bewusste **Notabschaltung** des Pushversands erfolgt außerhalb des normalen Deployment-Scripts: In der externen `preprod.env` `PUSH_ENABLED=false` setzen und den Webcontainer ausdrücklich mit `--env-file /home/kai/.config/metalcircle/preprod.env`, `compose.yml` + `compose.push.yml` aktualisieren. Das reguläre Pre-Production-Override erzwingt Push und ist für diese Ausnahme wegzulassen; `COOKIE_SECURE=true` in der externen Env beibehalten. Kein `down`, kein DB-Recreate. Für den nächsten normalen Deployment-Lauf Push wieder aktivieren und beide Preflights bestehen lassen.
|
||
|
||
Schlüsselrotation: neue Pre-Production-Datei geschützt am Host ersetzen und den Webcontainer über den normalen Deployment-Weg neu erstellen, damit Mount und SDK-Credential erneuert werden. Während eines laufenden Deployments Env/Key nicht bearbeiten.
|
||
|
||
### Android mit Pre-Production verbinden
|
||
|
||
`android/capacitor.config.ts` verwendet `METALCIRCLE_SERVER_URL`. Die Standardadresse **https://konzerte.pinguholic.de/** ist bereits eure bestätigte aktuelle Pre-Production. Eine App mit dieser Zieladresse benötigt für den neuen Backend-Zugang keine neue APK. Nur einen noch auf localhost zeigenden Testbuild oder eine App mit abweichender Adresse neu synchronisieren und bauen. Die Zieladresse wird beim Sync in die APK übernommen, nicht durch eine Backend-`.env` geändert:
|
||
|
||
```bash
|
||
# Im Verzeichnis android/ auf dem Build-Rechner:
|
||
METALCIRCLE_SERVER_URL=https://konzerte.pinguholic.de/ METALCIRCLE_LOCAL_TEST=0 npm run sync
|
||
npm run build
|
||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
||
```
|
||
|
||
Vor dem Wechsel am alten Backend abmelden. Package ID bleibt **`de.pinguholic.concerts`**; die APK ersetzt bei passender Signatur die bestehende Installation. Bei Signaturkonflikt nicht ungefragt App-Daten löschen. Kein `adb reverse` für das HTTPS-Pre-Production-Backend verwenden. Danach auf Pre-Production als Empfänger B anmelden, Benachrichtigungen erlauben und unter „Mein Profil“ die Registrierung bestätigen lassen. Für weitere Local-/Prod-Builds den gewünschten Ziel-URL erneut ausdrücklich setzen.
|
||
|
||
`native-push.js` verwendet ausschließlich relative `/api/push/...`-URLs und dieselbe authentifizierte Origin wie die geladene Web-App. `/api/push/session` liefert die aktuelle Session-Bindung, `POST /api/push/devices` registriert/aktualisiert nur den eingeloggten Benutzer. Geräte-ID und Token sind eindeutig; dieselbe Geräte-ID aktualisiert den Token. Ein Sitzungs-/Umgebungswechsel löscht im nativen Plugin den alten Token, leert eigene Notifications und registriert neu. Logout entfernt die serverseitige Session samt Geräte-/Outbox-Verknüpfungen; Kategorie-Abwahl und verweigerte Android-Berechtigung sind zusätzlich berücksichtigt. Datenbanken und aktive Geräte-/Session-Tabellen zwischen Umgebungen nicht kopieren.
|
||
|
||
### Smoke-Test: Backend bis zum Antippen
|
||
|
||
Zwei **Pre-Production-Testkonten** A/B verwenden. B ist auf dem Android-Testgerät angemeldet, alle drei Kategorien sind aktiv. Vor jedem Ereignis die App mit Home in den Hintergrund bringen (nicht per „Stopp erzwingen“ beenden). Den Browser von B ebenfalls nicht im Chat oder in Nachrichten/Einladungen offen lassen. Uhrzeit und IDs der Testereignisse notieren. Erst nach technischer Erfassung die Benachrichtigung antippen.
|
||
|
||
| Test | Aktion A / Voraussetzungen | Backend-Nachweis | Android-Vorschau und Ziel |
|
||
|---|---|---|---|
|
||
| 1 | Neue Freundschaftsanfrage an B, noch keine offene/bestätigte Verbindung | `friendships` mit `status='pending'`, Outbox `friend_request` | „Neue Freundschaftsanfrage“ / „New friend request“; Tap führt zu A's Profil |
|
||
| 2 | B zu einer privaten Testveranstaltung einladen | `event_invitations` mit B und `viewed_at IS NULL`, Outbox `event_invitation` | „Neue Veranstaltungseinladung“ / „New event invitation“; Tap öffnet Veranstaltung |
|
||
| 3 | Nach Annahme der Freundschaft eine neue DM an B senden | `direct_messages` mit B und `read_at IS NULL`, Outbox `direct_message` | „Neue Nachricht – Du hast eine neue Nachricht.“ / „New message – You have a new message.“; Tap öffnet Chat mit A |
|
||
|
||
Für die Backend-Prüfung können selektive SQL-Abfragen benutzt werden (IDs/Zeitpunkt eingrenzen; kein `SELECT *`):
|
||
|
||
```bash
|
||
sudo docker compose --env-file /home/kai/.config/metalcircle/preprod.env -f compose.yml -f compose.preprod.yml exec -T db \
|
||
sh -c 'exec psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"' <<'SQL'
|
||
SELECT user_id, platform, app_version, created_at, last_seen_at FROM push_devices ORDER BY last_seen_at DESC LIMIT 10;
|
||
SELECT user_id, language, friend_request, direct_message, event_invitation FROM notification_preferences ORDER BY user_id;
|
||
SELECT id, kind, object_id, state, attempts, created_at FROM push_notifications ORDER BY created_at DESC LIMIT 15;
|
||
SELECT id, requester_id, addressee_id, status FROM friendships ORDER BY id DESC LIMIT 5;
|
||
SELECT concert_id, user_id, viewed_at FROM event_invitations ORDER BY concert_id DESC LIMIT 5;
|
||
SELECT id, sender_id, recipient_id, read_at FROM direct_messages ORDER BY id DESC LIMIT 5;
|
||
SQL
|
||
```
|
||
|
||
Keiner dieser Befehle gibt Token, Session-Bindung, Schlüssel oder private Nachrichtentexte aus. `state='sent'` wird erst gesetzt, wenn `firebase_admin.messaging.send` erfolgreich zurückgekehrt ist. Dies belegt die FCM-Annahme, **nicht** die Zustellung. Der aktuelle Sender speichert die zurückgegebene Firebase-Message-ID nicht; stattdessen ist `push_notifications.id` als Android-Notification-Tag korrelierbar. Keine neuen SDK-Debuglogs einschalten.
|
||
|
||
Für einen technischen Nachweis direkt vor dem Tap auf dem ADB-Rechner die zur Outbox passende UUID im aktiven Notification-Manager-Bestand erfassen. Möglich ist die folgende Ausgabe mit ausschließlich UUID-Tags; keine vollständigen Notification-Dumps speichern oder teilen:
|
||
|
||
```bash
|
||
adb shell dumpsys notification | python3 -c '
|
||
import re, sys
|
||
active = False
|
||
for line in sys.stdin:
|
||
if line.strip() == "Notification List:":
|
||
active = True
|
||
continue
|
||
if active and line.strip() and len(line) - len(line.lstrip()) <= 2:
|
||
active = False
|
||
if active and "NotificationRecord(" in line and "pkg=de.pinguholic.concerts " in line:
|
||
tag = re.search(r"\btag=([a-f0-9-]{36})\b", line)
|
||
if tag:
|
||
print("active MetalCircle notification_id=" + tag.group(1))
|
||
'
|
||
```
|
||
|
||
Hersteller/Android-Versionen können den Dump anders gliedern. Leere Ausgabe ist deshalb kein alleiniger Nachweis fehlender Zustellung. Alternativ über die verbundene WebView nur `notifications.map(n => ({tag: n.tag, id: n.id}))` des Capacitor-Aufrufs `PushNotifications.getDeliveredNotifications()` betrachten, **nie** dessen komplettes Objekt (`data` enthält Session-Metadaten). Notification-Tag mit der Outbox-ID vergleichen, generische Vorschau am Gerät prüfen, dann Tap/Ziel prüfen. Ohne belastbaren Gerätebeleg bleibt der Android-Empfang **nicht verifiziert**.
|
||
|
||
Alle drei Tests zusätzlich mit B in EN wiederholen; ursprüngliche Sprache/Präferenzen danach wiederherstellen. Eine Kategorie deaktivieren: Ereignis muss entstehen, ein neuer Versandauftrag darf nicht entstehen. Im Vordergrund separat prüfen: lokale Banner-Aktion und native Anzeige; Verhalten siehe [Push Notifications](Push-Notifications.md). Testveranstaltung/DM/Anfragen anschließend gezielt entfernen bzw. ursprüngliche Verbindung wiederherstellen; keine fremden Daten pauschal löschen.
|
||
|
||
### Troubleshooting
|
||
|
||
- **Preflight abgebrochen:** `credential_service_account_mismatch` bedeutet falscher Account (z. B. Local-Key), `credential_project_mismatch` ein anderes Projekt. `credential_permissions_too_broad` verlangt engere Host-Rechte; `credential_mount_not_read_only` eine korrigierte Compose-Einbindung. Bei `credential_missing_unreadable_or_invalid` Dateiexistenz, Leserechte/UID-Mapping und Gültigkeit des neu erzeugten Schlüssels prüfen, keinen Inhalt ausgeben.
|
||
- **`configuration`/403 nach Preflight-PASS:** Preflight prüft kein IAM. Exakte Account-Zuweisung von `roles/firebasecloudmessaging.admin` auf `metalcircle-30d9b`, API-Aktivierung, nicht widerrufenen Schlüssel und Netzwerkzugang zu Google prüfen. IAM-Propagation abwarten. Der Worker loggt bewusst nur Fehlerkategorien, keine volle Google-Antwort. Für die drei aktuellen Push-Arten nicht `roles/firebasenotifications.admin` mit der HTTP-v1-Rolle verwechseln.
|
||
- **Kein Outbox-Eintrag:** `PUSH_ENABLED`, korrektes Backend in der APK, frische Registrierung von B, Kategorien, Selbstbenachrichtigung und tatsächliches neues Ereignis prüfen. Ein reines Backend-Update ändert die APK-Ziel-URL nicht.
|
||
- **`dropped`:** Auftrag kann gelesen, abgelaufen, abgewählt, blockiert oder durch Session-/Tokenwechsel ungültig sein. Direktnachrichten vor dem Versand nicht öffnen; der Chat-GET setzt `read_at`. Bereits erledigte Ziele führen beim Tap absichtlich zur Übersicht.
|
||
- **`sent`, aber nichts sichtbar:** FCM-Annahme getrennt vom Android-Empfang bewerten. Vor dem Tap erfassen; Android-App-/Kanalberechtigung, Benachrichtigungskanal, Energiesparen/Netzwerk, Force-Stop und Gerätewechsel prüfen. Identische Wiederholungs-Tags ersetzen dieselbe Meldung; neue Outbox-IDs sind verschieden. Die App löscht eigene Notifications bei Session-Wechsel.
|
||
- **Chat-Push im Vordergrund:** Capacitor 6.0.5 erhält den Payload im nativen Callback; `presentationOptions: ['sound', 'alert']` erlaubt eine native Anzeige, zusätzlich kommt der Web-Banner. Systemanzeige hängt weiterhin vom Android-Kanal ab. Es gibt keinen DM-spezifischen Kanal und keinen belegten DM-Payload-Fehler.
|
||
- **Keine Freigabe für Pre-Production ableiten:** Lokale historische Gerätetests und simulierte Tests ersetzen die drei echten Pre-Production-Smoke-Tests nicht. Aktuell liegt noch kein solcher Pre-Production-Nachweis vor.
|
||
|
||
### Lokal geprüfte Pre-Production-Vorbereitung
|
||
|
||
Die ursprüngliche Push-Vorbereitung wurde mit 64 erfolgreichen Backend-Tests (isolierte lokale PostgreSQL-Schemas, simuliertes Firebase) und 11 JavaScript-Tests für Push-Registrierung/-Navigation und Android-Zurück-Verhalten geprüft. Diese Prüfungen decken insbesondere falsche Service Accounts, Projekt-/Mount-/Dateirechte, relative authentifizierte Registrierungs-URLs, die gemeinsamen Payload-Eigenschaften aller drei Push-Arten und den Lesestatus von Direktnachrichten ab.
|
||
|
||
Die anschließende Absicherung des Deployments mit externer Environment-Datei wurde mit inzwischen **84 Backend-Tests, 22 Host-/Deployment-Tests und 11 JavaScript-Tests** geprüft. Dazu gehören fehlende Gitea-Werte, Bot-Identität, API-Fehler, Rechte, fehlende Dateien, sichere Backup-Rotation und Abbruch vor dem Containerwechsel. Die Compose-Interpolation wurde mit synthetischen Werten auch gegen den echten lokalen Compose-Parser geprüft. Keine Pre-Production-Secrets und kein Serverdeployment waren dafür nötig.
|
||
|
||
Compose-Konfigurationen für Basis, Local-Push und Pre-Production wurden mit synthetischen Konfigurationswerten geprüft, einschließlich Abbruch bei fehlendem Projekt/Secret-Pfad. Das Backend-Image wurde lokal gebaut und auf Credential-Dateien/Schlüssel geprüft; der einzige Schlüssel-Marker war ein kurzer synthetischer Text im bestehenden Gitea-Redaktionstest. `.env`, Android-Firebase-Konfiguration und die vorgesehenen Schlüsseldateinamen sind Git-ignoriert. Diese Prüfungen verwenden keinen Pre-Production-Key und belegen weder Server-IAM noch Zustellung auf dem Pre-Production-Gerät. Native Android-Dateien wurden nicht geändert; ein neuer APK-Build war dafür nicht erforderlich.
|