From 794b48f0d7f87c9a7dac518cfc099ee487c13ef7 Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Mon, 18 May 2026 15:06:52 +0800 Subject: [PATCH] feat(notifications): add localStorage debug config for notification API base URL Introduce NotificationDebugConfig so register/refresh use getNotificationApiBaseUrl() (APP_SERVER by default, optional LAN/ngrok override) and configurable testMode without rebuilds. --- .../NativeNotificationService.ts | 9 +- .../notifications/NotificationDebugConfig.ts | 96 +++++++++++++++++++ .../notifications/NotificationService.ts | 9 +- src/services/notifications/index.ts | 8 ++ 4 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/services/notifications/NotificationDebugConfig.ts diff --git a/src/services/notifications/NativeNotificationService.ts b/src/services/notifications/NativeNotificationService.ts index 1da11b3b..17bd24f6 100644 --- a/src/services/notifications/NativeNotificationService.ts +++ b/src/services/notifications/NativeNotificationService.ts @@ -16,6 +16,10 @@ import type { PushNotificationSchema } from "@capacitor/push-notifications"; import { DailyNotification } from "@/plugins/DailyNotificationPlugin"; import { REMINDER_ID_DAILY_REMINDER } from "./reminderIds"; import { configureNativeFetcherIfReady } from "./nativeFetcherConfig"; +import { + getNotificationApiBaseUrl, + getTestMode, +} from "./NotificationDebugConfig"; /** * Extended type for DailyNotification that includes the actual Swift implementation @@ -555,12 +559,13 @@ export async function refreshNotifications(): Promise { } try { - const res = await fetch("/notifications/refresh", { + const baseUrl = getNotificationApiBaseUrl(); + const res = await fetch(`${baseUrl}/notifications/refresh`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ platform: Capacitor.getPlatform(), - testMode: true, + testMode: getTestMode(), }), }); diff --git a/src/services/notifications/NotificationDebugConfig.ts b/src/services/notifications/NotificationDebugConfig.ts new file mode 100644 index 00000000..22c607fe --- /dev/null +++ b/src/services/notifications/NotificationDebugConfig.ts @@ -0,0 +1,96 @@ +/** + * Lightweight debug configuration for notification backend testing. + * Persists overrides in localStorage; production defaults apply when unset. + */ + +import { APP_SERVER } from "@/constants/app"; + +const LOG = "[NotificationDebug]"; +const STORAGE_KEY_BACKEND_URL = "notificationDebug.backendBaseUrl"; +const STORAGE_KEY_TEST_MODE = "notificationDebug.testMode"; + +/** Trim whitespace, drop trailing slash; empty input becomes null. */ +export function normalizeNotificationBackendUrl(url: string): string | null { + const trimmed = url.trim(); + if (!trimmed) { + return null; + } + return trimmed.replace(/\/$/, ""); +} + +function readStorage(key: string): string | null { + if (typeof localStorage === "undefined") { + return null; + } + try { + return localStorage.getItem(key); + } catch { + return null; + } +} + +function writeStorage(key: string, value: string | null): void { + if (typeof localStorage === "undefined") { + return; + } + try { + if (value === null) { + localStorage.removeItem(key); + } else { + localStorage.setItem(key, value); + } + } catch { + // Quota / privacy mode — ignore + } +} + +/** Backend URL override, or null when using the default app server. */ +export function getBackendBaseUrl(): string | null { + const raw = readStorage(STORAGE_KEY_BACKEND_URL); + if (raw === null) { + return null; + } + return normalizeNotificationBackendUrl(raw); +} + +export function setBackendBaseUrl(url: string): void { + const normalized = normalizeNotificationBackendUrl(url); + if (normalized === null) { + writeStorage(STORAGE_KEY_BACKEND_URL, null); + // eslint-disable-next-line no-console + console.log(`${LOG} backend URL cleared (using default)`); + return; + } + writeStorage(STORAGE_KEY_BACKEND_URL, normalized); + // eslint-disable-next-line no-console + console.log(`${LOG} backend URL set to ${normalized}`); +} + +/** + * When never configured via debug UI/console, matches prior hardcoded `testMode: true`. + */ +export function getTestMode(): boolean { + const raw = readStorage(STORAGE_KEY_TEST_MODE); + if (raw === null) { + return true; + } + return raw === "true"; +} + +export function setTestMode(enabled: boolean): void { + writeStorage(STORAGE_KEY_TEST_MODE, enabled ? "true" : "false"); + // eslint-disable-next-line no-console + console.log(`${LOG} test mode ${enabled ? "enabled" : "disabled"}`); +} + +/** + * Base URL for `/notifications/*` API calls. + * Uses debug override when set; otherwise the built-in app server (production default). + */ +export function getNotificationApiBaseUrl(): string { + const override = getBackendBaseUrl(); + if (override) { + return override; + } + return normalizeNotificationBackendUrl(APP_SERVER) ?? APP_SERVER; +} diff --git a/src/services/notifications/NotificationService.ts b/src/services/notifications/NotificationService.ts index 379b60ea..1d6fa2aa 100644 --- a/src/services/notifications/NotificationService.ts +++ b/src/services/notifications/NotificationService.ts @@ -16,6 +16,10 @@ import { Capacitor } from "@capacitor/core"; import { logger } from "@/utils/logger"; import { getOrCreateDeviceId } from "./deviceId"; +import { + getNotificationApiBaseUrl, + getTestMode, +} from "./NotificationDebugConfig"; import { NativeNotificationService } from "./NativeNotificationService"; import { WebPushNotificationService } from "./WebPushNotificationService"; @@ -24,14 +28,15 @@ import { WebPushNotificationService } from "./WebPushNotificationService"; */ export async function registerToken(fcmToken: string): Promise { const deviceId = await getOrCreateDeviceId(); - const res = await fetch("/notifications/register", { + const baseUrl = getNotificationApiBaseUrl(); + const res = await fetch(`${baseUrl}/notifications/register`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ deviceId, fcmToken, platform: Capacitor.getPlatform(), - testMode: true, + testMode: getTestMode(), }), }); if (!res.ok) { diff --git a/src/services/notifications/index.ts b/src/services/notifications/index.ts index 7d2b8f51..dd2176fa 100644 --- a/src/services/notifications/index.ts +++ b/src/services/notifications/index.ts @@ -13,6 +13,14 @@ * ``` */ +export { + getBackendBaseUrl, + getNotificationApiBaseUrl, + getTestMode, + normalizeNotificationBackendUrl, + setBackendBaseUrl, + setTestMode, +} from "./NotificationDebugConfig"; export { NotificationService, registerToken } from "./NotificationService"; export { NativeNotificationService } from "./NativeNotificationService"; export { WebPushNotificationService } from "./WebPushNotificationService";