forked from trent_larson/crowd-funder-for-time-pwa
feat(dev): test full WAKEUP_PING pipeline from debug panel
Add Send Real WAKEUP_PING via /debug/send-wakeup and rename the local refresh shortcut to Simulate WAKEUP_PING (Local).
This commit is contained in:
@@ -62,11 +62,35 @@
|
||||
:class="{ 'opacity-50 cursor-not-allowed': busy }"
|
||||
@click="onSimulateWakeupRefresh"
|
||||
>
|
||||
Simulate WAKEUP_PING
|
||||
Simulate WAKEUP_PING (Local)
|
||||
</button>
|
||||
<p class="text-xs text-slate-500">
|
||||
Local simulation only — calls the refresh API directly (no FCM push).
|
||||
</p>
|
||||
<button
|
||||
class="w-full text-md bg-gradient-to-b from-amber-400 to-amber-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
||||
:disabled="busy"
|
||||
:class="{ 'opacity-50 cursor-not-allowed': busy }"
|
||||
@click="onSendRealWakeupPing"
|
||||
>
|
||||
Send Real WAKEUP_PING
|
||||
</button>
|
||||
<p
|
||||
v-if="realWakeupStatus"
|
||||
class="text-xs rounded px-3 py-2 border"
|
||||
:class="
|
||||
realWakeupStatus.ok
|
||||
? 'text-emerald-900 bg-emerald-50 border-emerald-200'
|
||||
: 'text-rose-900 bg-rose-50 border-rose-200'
|
||||
"
|
||||
role="status"
|
||||
>
|
||||
{{ realWakeupStatus.message }}
|
||||
</p>
|
||||
<p v-else class="text-xs text-slate-500">
|
||||
Full pipeline — backend `/debug/send-wakeup` → FCM → WAKEUP_PING
|
||||
handler.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
@@ -306,6 +330,7 @@ const testModeEnabled = ref(NotificationDebugService.isTestModeEnabled());
|
||||
const fcmToken = ref<string | null>(NotificationDebugService.getFcmToken());
|
||||
|
||||
const activeBackendUrl = ref(NotificationDebugService.getActiveBackendUrl());
|
||||
const realWakeupStatus = ref<{ ok: boolean; message: string } | null>(null);
|
||||
|
||||
const truncatedFcmToken = computed(() => {
|
||||
const t = fcmToken.value?.trim() ?? "";
|
||||
@@ -420,6 +445,43 @@ async function onSimulateWakeupRefresh(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function formatRealWakeupStatusMessage(
|
||||
result: Awaited<
|
||||
ReturnType<typeof NotificationDebugService.sendRealWakeupPing>
|
||||
>,
|
||||
): string {
|
||||
if (result.ok) {
|
||||
const body =
|
||||
typeof result.responseBody === "object" && result.responseBody !== null
|
||||
? (result.responseBody as Record<string, unknown>)
|
||||
: null;
|
||||
const parts = ["Real WAKEUP_PING sent via backend."];
|
||||
if (typeof body?.message === "string" && body.message.trim()) {
|
||||
parts.push(body.message.trim());
|
||||
}
|
||||
if (typeof body?.tokenSuffix === "string" && body.tokenSuffix.trim()) {
|
||||
parts.push(`token …${body.tokenSuffix.trim()}`);
|
||||
}
|
||||
return parts.join(" ");
|
||||
}
|
||||
const parts = [`Real WAKEUP_PING failed: ${result.errorMessage}`];
|
||||
if (result.status != null) {
|
||||
parts.push(`(HTTP ${result.status})`);
|
||||
}
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
async function onSendRealWakeupPing(): Promise<void> {
|
||||
realWakeupStatus.value = null;
|
||||
await withBusy(async () => {
|
||||
const result = await NotificationDebugService.sendRealWakeupPing();
|
||||
realWakeupStatus.value = {
|
||||
ok: result.ok,
|
||||
message: formatRealWakeupStatusMessage(result),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function onCopyFcmToken(): Promise<void> {
|
||||
const token = fcmToken.value?.trim();
|
||||
if (!token) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { Capacitor } from "@capacitor/core";
|
||||
import type { PushNotificationSchema } from "@capacitor/push-notifications";
|
||||
import { logger } from "@/utils/logger";
|
||||
import { getOrCreateDeviceId } from "./deviceId";
|
||||
import {
|
||||
clearNotificationDebugLogs,
|
||||
logNotification,
|
||||
@@ -26,12 +27,17 @@ import {
|
||||
getLastKnownFcmToken,
|
||||
reregisterFcmTokenNow,
|
||||
} from "./firebaseMessagingClient";
|
||||
import {
|
||||
getNotificationApiHeaders,
|
||||
httpAuthErrorMessage,
|
||||
} from "./notificationApiAuth";
|
||||
import {
|
||||
applyNotificationRefreshPayload,
|
||||
handleCapacitorPushNotificationReceived,
|
||||
refreshNotificationsWithDiagnostics,
|
||||
type NotificationRefreshPayload,
|
||||
} from "./NativeNotificationService";
|
||||
import { truncateFcmTokenForLog } from "./notificationLog";
|
||||
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
|
||||
import { NotificationInspector } from "@/plugins/NotificationInspectorPlugin";
|
||||
|
||||
@@ -49,6 +55,52 @@ export type PendingNotificationsResult = {
|
||||
inspectorUnavailableMessage?: string;
|
||||
};
|
||||
|
||||
export type SendRealWakeupPingResult =
|
||||
| { ok: true; responseBody?: unknown }
|
||||
| {
|
||||
ok: false;
|
||||
errorMessage: string;
|
||||
status?: number;
|
||||
responseBody?: unknown;
|
||||
};
|
||||
|
||||
function wakeupPingResponseDetail(body: unknown): Record<string, unknown> {
|
||||
if (typeof body !== "object" || body === null) {
|
||||
return {};
|
||||
}
|
||||
const record = body as Record<string, unknown>;
|
||||
const detail: Record<string, unknown> = {};
|
||||
for (const key of [
|
||||
"success",
|
||||
"message",
|
||||
"reason",
|
||||
"error",
|
||||
"tokenSuffix",
|
||||
"deviceId",
|
||||
] as const) {
|
||||
if (record[key] !== undefined) {
|
||||
detail[key] = record[key];
|
||||
}
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
function wakeupPingFailureMessage(status: number, body: unknown): string {
|
||||
if (typeof body === "object" && body !== null) {
|
||||
const record = body as Record<string, unknown>;
|
||||
for (const key of ["message", "reason", "error"] as const) {
|
||||
const value = record[key];
|
||||
if (typeof value === "string" && value.trim()) {
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status === 401 || status === 403) {
|
||||
return httpAuthErrorMessage(status);
|
||||
}
|
||||
return `HTTP ${status}`;
|
||||
}
|
||||
|
||||
function isUnimplementedError(e: unknown): boolean {
|
||||
return (
|
||||
typeof e === "object" &&
|
||||
@@ -112,6 +164,75 @@ export const NotificationDebugService = {
|
||||
});
|
||||
},
|
||||
|
||||
/** Full pipeline: backend `/debug/send-wakeup` → FCM → native WAKEUP_PING handler. */
|
||||
async sendRealWakeupPing(): Promise<SendRealWakeupPingResult> {
|
||||
logNotification("Real WAKEUP_PING requested");
|
||||
|
||||
const fcmToken = getLastKnownFcmToken()?.trim() ?? "";
|
||||
if (!fcmToken) {
|
||||
const errorMessage = "no FCM token (register first)";
|
||||
logNotification(`Real WAKEUP_PING failed: ${errorMessage}`);
|
||||
return { ok: false, errorMessage };
|
||||
}
|
||||
|
||||
try {
|
||||
const auth = await getNotificationApiHeaders();
|
||||
if (!auth.ok) {
|
||||
logNotification(`Real WAKEUP_PING failed: ${auth.message}`);
|
||||
return { ok: false, errorMessage: auth.message };
|
||||
}
|
||||
|
||||
const deviceId = await getOrCreateDeviceId();
|
||||
const baseUrl = getNotificationApiBaseUrl();
|
||||
const res = await fetch(`${baseUrl}/debug/send-wakeup`, {
|
||||
method: "POST",
|
||||
headers: auth.headers,
|
||||
body: JSON.stringify({
|
||||
deviceId,
|
||||
fcmToken,
|
||||
platform: Capacitor.getPlatform(),
|
||||
testMode: getTestMode(),
|
||||
}),
|
||||
});
|
||||
|
||||
let responseBody: unknown;
|
||||
try {
|
||||
responseBody = await res.json();
|
||||
} catch {
|
||||
responseBody = undefined;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const errorMessage = wakeupPingFailureMessage(res.status, responseBody);
|
||||
logNotification(`Real WAKEUP_PING failed: ${errorMessage}`, {
|
||||
status: res.status,
|
||||
token: truncateFcmTokenForLog(fcmToken),
|
||||
...wakeupPingResponseDetail(responseBody),
|
||||
});
|
||||
return {
|
||||
ok: false,
|
||||
errorMessage,
|
||||
status: res.status,
|
||||
responseBody,
|
||||
};
|
||||
}
|
||||
|
||||
logNotification("Real WAKEUP_PING success", {
|
||||
token: truncateFcmTokenForLog(fcmToken),
|
||||
deviceId,
|
||||
...wakeupPingResponseDetail(responseBody),
|
||||
});
|
||||
return { ok: true, responseBody };
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
logNotification(`Real WAKEUP_PING failed: ${errorMessage}`, {
|
||||
token: truncateFcmTokenForLog(fcmToken),
|
||||
});
|
||||
logger.warn(`${LOG} sendRealWakeupPing failed`, err);
|
||||
return { ok: false, errorMessage };
|
||||
}
|
||||
},
|
||||
|
||||
generateMockNotifications(
|
||||
intervalMs: number = 60_000,
|
||||
): NotificationRefreshPayload {
|
||||
|
||||
Reference in New Issue
Block a user