Send the ngrok skip-browser-warning header only when the Notification Debug Panel backend override is set.

WebView fetch to free ngrok was blocked by the interstitial (no CORS ACAO). Keep production notify-api requests unchanged.
This commit is contained in:
Jose Olarte III
2026-09-03 21:12:05 +08:00
parent 34a8c51d2f
commit 2afe748292
4 changed files with 80 additions and 1 deletions
@@ -0,0 +1,50 @@
jest.mock("@/constants/app", () => ({
DEFAULT_NOTIFY_API_SERVER: "https://notify-api.timesafari.app",
}));
import {
getNotificationDebugOverrideHeaders,
NGROK_SKIP_BROWSER_WARNING_HEADER,
NGROK_SKIP_BROWSER_WARNING_VALUE,
setBackendBaseUrl,
} from "./NotificationDebugConfig";
const STORAGE_KEY_BACKEND_URL = "notificationDebug.backendBaseUrl";
describe("getNotificationDebugOverrideHeaders", () => {
const memory = new Map<string, string>();
beforeEach(() => {
memory.clear();
Object.defineProperty(globalThis, "localStorage", {
configurable: true,
value: {
getItem: (key: string) => memory.get(key) ?? null,
setItem: (key: string, value: string) => {
memory.set(key, value);
},
removeItem: (key: string) => {
memory.delete(key);
},
},
});
});
it("adds the ngrok skip header when the debug backend override is set", () => {
setBackendBaseUrl("https://detail-frown-machine.ngrok-free.dev");
expect(getNotificationDebugOverrideHeaders()).toEqual({
[NGROK_SKIP_BROWSER_WARNING_HEADER]: NGROK_SKIP_BROWSER_WARNING_VALUE,
});
expect(memory.get(STORAGE_KEY_BACKEND_URL)).toBe(
"https://detail-frown-machine.ngrok-free.dev",
);
});
it("omits the ngrok skip header for the default/production notification API", () => {
setBackendBaseUrl("");
expect(getNotificationDebugOverrideHeaders()).toEqual({});
expect(memory.get(STORAGE_KEY_BACKEND_URL)).toBeUndefined();
});
});
@@ -10,6 +10,10 @@ const STORAGE_KEY_BACKEND_URL = "notificationDebug.backendBaseUrl";
const STORAGE_KEY_TEST_MODE = "notificationDebug.testMode";
const STORAGE_KEY_BYPASS_AUTH = "notificationDebug.bypassAuth";
/** Free-ngrok interstitial bypass; only sent when the debug backend override is set. */
export const NGROK_SKIP_BROWSER_WARNING_HEADER = "ngrok-skip-browser-warning";
export const NGROK_SKIP_BROWSER_WARNING_VALUE = "true";
/** Trim whitespace, drop trailing slash; empty input becomes null. */
export function normalizeNotificationBackendUrl(url: string): string | null {
const trimmed = url.trim();
@@ -113,3 +117,16 @@ export function getNotificationApiBaseUrl(): string {
DEFAULT_NOTIFY_API_SERVER
);
}
/**
* Extra headers for notification API calls when the Debug Panel backend URL
* override is set. Production/default hosts do not get this header.
*/
export function getNotificationDebugOverrideHeaders(): Record<string, string> {
if (!getBackendBaseUrl()) {
return {};
}
return {
[NGROK_SKIP_BROWSER_WARNING_HEADER]: NGROK_SKIP_BROWSER_WARNING_VALUE,
};
}
+3
View File
@@ -17,7 +17,10 @@ export {
getBackendBaseUrl,
getBypassAuth,
getNotificationApiBaseUrl,
getNotificationDebugOverrideHeaders,
getTestMode,
NGROK_SKIP_BROWSER_WARNING_HEADER,
NGROK_SKIP_BROWSER_WARNING_VALUE,
normalizeNotificationBackendUrl,
setBackendBaseUrl,
setBypassAuth,
@@ -7,6 +7,7 @@
import { getHeaders } from "@/libs/endorserServer";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { logger } from "@/utils/logger";
import { getNotificationDebugOverrideHeaders } from "./NotificationDebugConfig";
import { shouldBypassNotificationAuth } from "./notificationApiDebugMode";
import { logNotification } from "./NotificationDebugEvents";
@@ -82,7 +83,14 @@ export async function getNotificationApiHeaders(
if (kind) {
logDebugUnauthenticatedNotificationRequest();
}
return { ok: true, authenticated: false, headers: { ...DEBUG_HEADERS } };
return {
ok: true,
authenticated: false,
headers: {
...DEBUG_HEADERS,
...getNotificationDebugOverrideHeaders(),
},
};
}
const did = await getActiveNotificationDid();
@@ -120,6 +128,7 @@ export async function getNotificationApiHeaders(
headers: {
"Content-Type": headers["Content-Type"],
Authorization: headers.Authorization,
...getNotificationDebugOverrideHeaders(),
},
};
}