mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
feat: add Cloudflare edge caching infrastructure for api.worldmonitor.app (#471)
Route web production RPC traffic through api.worldmonitor.app via fetch interceptor (installWebApiRedirect). Add default Cache-Control headers (s-maxage=300, stale-while-revalidate=60) on GET 200 responses, with no-store override for real-time endpoints (vessel snapshot). Update CORS to allow GET method. Skip Vercel bot middleware for API subdomain using hostname check (non-spoofable, replacing CF-Ray header approach). Update desktop cloud fallback to route through api.worldmonitor.app.
This commit is contained in:
@@ -142,6 +142,18 @@ export default async function handler(request: Request): Promise<Response> {
|
||||
mergedHeaders.set(key, value);
|
||||
}
|
||||
|
||||
if (response.status === 200 && request.method === 'GET' && !mergedHeaders.has('Cache-Control')) {
|
||||
const url = new URL(request.url);
|
||||
const noStoreEndpoints = new Set([
|
||||
'/api/maritime/v1/get-vessel-snapshot',
|
||||
]);
|
||||
if (noStoreEndpoints.has(url.pathname)) {
|
||||
mergedHeaders.set('Cache-Control', 'no-store');
|
||||
} else {
|
||||
mergedHeaders.set('Cache-Control', 'public, s-maxage=300, stale-while-revalidate=60');
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
|
||||
@@ -20,8 +20,13 @@ const SOCIAL_IMAGE_UA =
|
||||
/Slack-ImgProxy|Slackbot|twitterbot|facebookexternalhit|linkedinbot|telegrambot|whatsapp|discordbot|redditbot/i;
|
||||
|
||||
export default function middleware(request: Request) {
|
||||
const ua = request.headers.get('user-agent') ?? '';
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.hostname === 'api.worldmonitor.app') {
|
||||
return;
|
||||
}
|
||||
|
||||
const ua = request.headers.get('user-agent') ?? '';
|
||||
const path = url.pathname;
|
||||
|
||||
// Allow social preview/image bots on OG image assets (bypasses Vercel Attack Challenge)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* CORS header generation -- TypeScript port of api/_cors.js.
|
||||
*
|
||||
* Identical ALLOWED_ORIGIN_PATTERNS and logic, with methods hardcoded
|
||||
* to 'POST, OPTIONS' (all sebuf routes are POST).
|
||||
* Identical ALLOWED_ORIGIN_PATTERNS and logic, with methods set
|
||||
* to 'GET, POST, OPTIONS' (sebuf routes support GET and POST).
|
||||
*/
|
||||
|
||||
declare const process: { env: Record<string, string | undefined> };
|
||||
@@ -35,7 +35,7 @@ export function getCorsHeaders(req: Request): Record<string, string> {
|
||||
const allowOrigin = isAllowedOrigin(origin) ? origin : 'https://worldmonitor.app';
|
||||
return {
|
||||
'Access-Control-Allow-Origin': allowOrigin,
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-WorldMonitor-Key',
|
||||
'Access-Control-Max-Age': '86400',
|
||||
'Vary': 'Origin',
|
||||
|
||||
@@ -142,7 +142,7 @@ window.addEventListener('unhandledrejection', (e) => {
|
||||
|
||||
import { debugInjectTestEvents, debugGetCells, getCellCount } from '@/services/geo-convergence';
|
||||
import { initMetaTags } from '@/services/meta-tags';
|
||||
import { installRuntimeFetchPatch } from '@/services/runtime';
|
||||
import { installRuntimeFetchPatch, installWebApiRedirect } from '@/services/runtime';
|
||||
import { loadDesktopSecrets } from '@/services/runtime-config';
|
||||
import { initAnalytics, trackApiKeysSnapshot } from '@/services/analytics';
|
||||
import { applyStoredTheme } from '@/utils/theme-manager';
|
||||
@@ -163,6 +163,8 @@ initMetaTags();
|
||||
|
||||
// In desktop mode, route /api/* calls to the local Tauri sidecar backend.
|
||||
installRuntimeFetchPatch();
|
||||
// In web production, route RPC calls through api.worldmonitor.app (Cloudflare edge).
|
||||
installWebApiRedirect();
|
||||
loadDesktopSecrets().then(async () => {
|
||||
await initAnalytics();
|
||||
trackApiKeysSnapshot();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const DEFAULT_REMOTE_HOSTS: Record<string, string> = {
|
||||
tech: 'https://tech.worldmonitor.app',
|
||||
full: 'https://worldmonitor.app',
|
||||
world: 'https://worldmonitor.app',
|
||||
tech: 'https://api.worldmonitor.app',
|
||||
full: 'https://api.worldmonitor.app',
|
||||
world: 'https://api.worldmonitor.app',
|
||||
};
|
||||
|
||||
const DEFAULT_LOCAL_API_PORT = 46123;
|
||||
@@ -130,6 +130,7 @@ const APP_HOSTS = new Set([
|
||||
'worldmonitor.app',
|
||||
'www.worldmonitor.app',
|
||||
'tech.worldmonitor.app',
|
||||
'api.worldmonitor.app',
|
||||
'localhost',
|
||||
'127.0.0.1',
|
||||
]);
|
||||
@@ -357,3 +358,35 @@ export function installRuntimeFetchPatch(): void {
|
||||
|
||||
(window as unknown as Record<string, unknown>).__wmFetchPatched = true;
|
||||
}
|
||||
|
||||
const WEB_RPC_PATTERN = /^\/api\/[^/]+\/v1\//;
|
||||
|
||||
export function installWebApiRedirect(): void {
|
||||
if (isDesktopRuntime() || typeof window === 'undefined') return;
|
||||
if ((window as unknown as Record<string, unknown>).__wmWebRedirectPatched) return;
|
||||
|
||||
const host = window.location?.hostname ?? '';
|
||||
const isProduction = host === 'worldmonitor.app' || host === 'www.worldmonitor.app' || host === 'tech.worldmonitor.app';
|
||||
if (!isProduction) return;
|
||||
|
||||
const nativeFetch = window.fetch.bind(window);
|
||||
const API_BASE = 'https://api.worldmonitor.app';
|
||||
|
||||
window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
||||
if (typeof input === 'string' && WEB_RPC_PATTERN.test(input)) {
|
||||
return nativeFetch(`${API_BASE}${input}`, init);
|
||||
}
|
||||
if (input instanceof URL && input.origin === window.location.origin && WEB_RPC_PATTERN.test(input.pathname)) {
|
||||
return nativeFetch(new URL(`${API_BASE}${input.pathname}${input.search}`), init);
|
||||
}
|
||||
if (input instanceof Request) {
|
||||
const u = new URL(input.url);
|
||||
if (u.origin === window.location.origin && WEB_RPC_PATTERN.test(u.pathname)) {
|
||||
return nativeFetch(new Request(`${API_BASE}${u.pathname}${u.search}`, input), init);
|
||||
}
|
||||
}
|
||||
return nativeFetch(input, init);
|
||||
};
|
||||
|
||||
(window as unknown as Record<string, unknown>).__wmWebRedirectPatched = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user