From 4bceb8747b0a5957cd8c316ec0aecc09ef9112e2 Mon Sep 17 00:00:00 2001 From: cschlaefke Date: Sun, 19 Jul 2026 15:39:02 +0200 Subject: [PATCH] Fix: Push-Registrierung griff nicht bei bereits eingeloggtem Nutzer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit registerDevice lief nur bei Auth-Änderungen (ref.listen) – bestand der Login schon beim App-Start, wurde nie ein FCM-Token gespeichert. Jetzt reaktiv über pushSetupProvider (ref.watch). Zusätzlich: temporäre debugReminders-Function zur Diagnose der Push-Strecke. Co-Authored-By: Claude Fable 5 --- functions/src/debug.ts | 68 +++++++++++++++++++ functions/src/index.ts | 1 + lib/app.dart | 10 +-- .../data/push_registration_service.dart | 10 +++ 4 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 functions/src/debug.ts diff --git a/functions/src/debug.ts b/functions/src/debug.ts new file mode 100644 index 0000000..207de58 --- /dev/null +++ b/functions/src/debug.ts @@ -0,0 +1,68 @@ +import {onRequest} from "firebase-functions/v2/https"; +import * as admin from "firebase-admin"; + +/** + * TEMPORÄRE Diagnose-Funktion für die Push-Strecke. Wird nach dem Debugging + * wieder entfernt. Zugriff nur mit zufälligem Token (kein Erraten möglich); + * gibt keine sensiblen Inhalte zurück. + * + * ?mode=diagnose → Zustand aller Nutzer (Tokens, Zeiten, Slot-Rechnung) + * ?mode=send → schickt sofort eine Test-Push an alle Nutzer-Tokens + */ +const DEBUG_TOKEN = "lftm-dbg-xK92mQ7pW4nT"; + +export const debugReminders = onRequest( + {region: "europe-west3"}, + async (req, res) => { + if (req.query.token !== DEBUG_TOKEN) { + res.status(403).send("forbidden"); + return; + } + const db = admin.firestore(); + const now = new Date(); + const users = await db.collection("users").get(); + const report: unknown[] = []; + + for (const userDoc of users.docs) { + const user = userDoc.data(); + const tokens: string[] = user.fcmTokens ?? []; + const timezone: string = user.timezone ?? "Europe/Berlin"; + const reminderTime: string = user.reminderTime ?? "09:00"; + + const local = new Intl.DateTimeFormat("en-GB", { + timeZone: timezone, + hour: "2-digit", + minute: "2-digit", + hour12: false, + }).format(now); + + const entry: Record = { + uid: userDoc.id, + tokenCount: tokens.length, + tokenPreviews: tokens.map((t) => t.slice(0, 12) + "…"), + reminderTime, + timezone, + localNow: local, + householdId: user.householdId ?? null, + lastReminderSentOn: user.lastReminderSentOn ?? null, + }; + + if (req.query.mode === "send" && tokens.length > 0) { + const response = await admin.messaging().sendEachForMulticast({ + tokens, + notification: { + title: "Test-Push von LeafItToMe 🌱", + body: "Wenn du das liest, funktioniert die Push-Strecke.", + }, + apns: {payload: {aps: {sound: "default"}}}, + android: {notification: {defaultSound: true}, priority: "high"}, + }); + entry.sendResults = response.responses.map((r) => + r.success ? "ok" : `${r.error?.code}: ${r.error?.message}` + ); + } + report.push(entry); + } + res.json({now: now.toISOString(), users: report}); + } +); diff --git a/functions/src/index.ts b/functions/src/index.ts index 052a94e..4d60b8e 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -5,6 +5,7 @@ import * as admin from "firebase-admin"; admin.initializeApp(); export {sendDailyReminders} from "./reminders"; +export {debugReminders} from "./debug"; // Secrets liegen im Google Secret Manager (firebase functions:secrets:set), // niemals im Code oder in der App. diff --git a/lib/app.dart b/lib/app.dart index 7df1f80..500e6e1 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -16,13 +16,9 @@ class LeafItToMeApp extends ConsumerWidget { final settings = ref.watch(settingsProvider); final router = ref.watch(appRouterProvider); - // Nach jedem Login das Gerät für Push-Erinnerungen registrieren. - ref.listen(authStateProvider, (previous, next) { - final user = next.value; - if (user != null) { - ref.read(pushRegistrationServiceProvider).registerDevice(user); - } - }); + // Hält die Push-Registrierung aktiv: registriert das Gerät, sobald ein + // Nutzer angemeldet ist (auch wenn der Login vor dem App-Start bestand). + ref.watch(pushSetupProvider); return MaterialApp.router( routerConfig: router, diff --git a/lib/features/notifications/data/push_registration_service.dart b/lib/features/notifications/data/push_registration_service.dart index d98435e..208330f 100644 --- a/lib/features/notifications/data/push_registration_service.dart +++ b/lib/features/notifications/data/push_registration_service.dart @@ -71,3 +71,13 @@ final pushRegistrationServiceProvider = Provider((ref) ref.watch(firebaseAuthProvider), ); }); + +/// Sorgt dafür, dass das Gerät registriert ist, sobald (und solange) ein +/// Nutzer angemeldet ist — auch wenn der Login schon vor dem App-Start +/// bestand. Wird in app.dart per ref.watch aktiv gehalten. +final pushSetupProvider = Provider((ref) { + final user = ref.watch(authStateProvider).value; + if (user != null) { + ref.read(pushRegistrationServiceProvider).registerDevice(user); + } +});