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:
Jose Olarte III
2026-06-11 17:01:28 +08:00
parent cd32895281
commit 3d6ac2ab53
2 changed files with 184 additions and 1 deletions

View File

@@ -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) {

View File

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