Fix: Push-Registrierung griff nicht bei bereits eingeloggtem Nutzer

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 <noreply@anthropic.com>
This commit is contained in:
cschlaefke 2026-07-19 15:39:02 +02:00
parent 0ce4b2cb19
commit 4bceb8747b
4 changed files with 82 additions and 7 deletions

68
functions/src/debug.ts Normal file
View file

@ -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<string, unknown> = {
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});
}
);

View file

@ -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.

View file

@ -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,

View file

@ -71,3 +71,13 @@ final pushRegistrationServiceProvider = Provider<PushRegistrationService>((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<void>((ref) {
final user = ref.watch(authStateProvider).value;
if (user != null) {
ref.read(pushRegistrationServiceProvider).registerDevice(user);
}
});