From 4a2e30ee28bc653d05da58c579f6ae636efca817 Mon Sep 17 00:00:00 2001 From: weishu Date: Wed, 14 Jan 2026 09:23:45 +0800 Subject: [PATCH] Normalize CORS origins before server wiring --- server/src/index.ts | 49 ++++++++++++++++++++++++++++++++++--- server/src/socket/server.ts | 3 ++- server/src/web/server.ts | 5 +++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/server/src/index.ts b/server/src/index.ts index f067835b..94a599d4 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -61,6 +61,42 @@ function resolveRelayFlag(args: string[]): { enabled: boolean; source: RelayFlag return { enabled, source } } +function normalizeOrigin(value: string): string { + const trimmed = value.trim() + if (!trimmed) { + return '' + } + try { + return new URL(trimmed).origin + } catch { + return trimmed + } +} + +function normalizeOrigins(origins: string[]): string[] { + const normalized = origins + .map(normalizeOrigin) + .filter(Boolean) + if (normalized.includes('*')) { + return ['*'] + } + return Array.from(new Set(normalized)) +} + +function mergeCorsOrigins(base: string[], extra: string[]): string[] { + if (base.includes('*') || extra.includes('*')) { + return ['*'] + } + const merged = new Set() + for (const origin of base) { + merged.add(origin) + } + for (const origin of extra) { + merged.add(origin) + } + return Array.from(merged) +} + let syncEngine: SyncEngine | null = null let happyBot: HappyBot | null = null let webServer: BunServer | null = null @@ -73,9 +109,15 @@ async function main() { console.log('HAPI Server starting...') // Load configuration (async - loads from env/file with persistence) - const config = await createConfiguration() const relayApiDomain = process.env.HAPI_RELAY_API || 'relay.hapi.run' const relayFlag = resolveRelayFlag(process.argv) + const officialWebUrl = process.env.HAPI_OFFICIAL_WEB_URL || 'https://app.hapi.run' + const config = await createConfiguration() + const baseCorsOrigins = normalizeOrigins(config.corsOrigins) + const relayCorsOrigin = normalizeOrigin(officialWebUrl) + const corsOrigins = relayFlag.enabled + ? mergeCorsOrigins(baseCorsOrigins, relayCorsOrigin ? [relayCorsOrigin] : []) + : baseCorsOrigins // Display CLI API token information if (config.cliApiTokenIsNew) { @@ -127,6 +169,7 @@ async function main() { const socketServer = createSocketServer({ store, jwtSecret, + corsOrigins, getSession: (sessionId) => syncEngine?.getSession(sessionId) ?? store.sessions.getSession(sessionId), onWebappEvent: (event: SyncEvent) => syncEngine?.handleRealtimeEvent(event), onSessionAlive: (payload) => syncEngine?.handleSessionAlive(payload), @@ -164,7 +207,8 @@ async function main() { jwtSecret, store, vapidPublicKey: vapidKeys.publicKey, - socketEngine: socketServer.engine + socketEngine: socketServer.engine, + corsOrigins }) // Start the bot if configured @@ -207,7 +251,6 @@ async function main() { console.log('[Web] Public: ' + tunnelUrl) // Generate direct access link with server and token - const officialWebUrl = process.env.HAPI_OFFICIAL_WEB_URL || 'https://app.hapi.run' const params = new URLSearchParams({ server: tunnelUrl, token: config.cliApiToken diff --git a/server/src/socket/server.ts b/server/src/socket/server.ts index 07c34af7..a2d08398 100644 --- a/server/src/socket/server.ts +++ b/server/src/socket/server.ts @@ -33,6 +33,7 @@ function resolveEnvNumber(name: string, fallback: number): number { export type SocketServerDeps = { store: Store jwtSecret: Uint8Array + corsOrigins?: string[] getSession?: (sessionId: string) => { active: boolean; namespace: string } | null onWebappEvent?: (event: SyncEvent) => void onSessionAlive?: (payload: { sid: string; time: number; thinking?: boolean; mode?: 'local' | 'remote' }) => void @@ -45,7 +46,7 @@ export function createSocketServer(deps: SocketServerDeps): { engine: Engine rpcRegistry: RpcRegistry } { - const corsOrigins = configuration.corsOrigins + const corsOrigins = deps.corsOrigins ?? configuration.corsOrigins const allowAllOrigins = corsOrigins.includes('*') const io = new Server({ diff --git a/server/src/web/server.ts b/server/src/web/server.ts index 4224a2cd..3bbc48f1 100644 --- a/server/src/web/server.ts +++ b/server/src/web/server.ts @@ -59,6 +59,7 @@ function createWebApp(options: { jwtSecret: Uint8Array store: Store vapidPublicKey: string + corsOrigins?: string[] embeddedAssetMap: Map | null }): Hono { const app = new Hono() @@ -68,7 +69,7 @@ function createWebApp(options: { // Health check endpoint (no auth required) app.get('/health', (c) => c.json({ status: 'ok' })) - const corsOrigins = configuration.corsOrigins + const corsOrigins = options.corsOrigins ?? configuration.corsOrigins const corsOriginOption = corsOrigins.includes('*') ? '*' : corsOrigins const corsMiddleware = cors({ origin: corsOriginOption, @@ -178,6 +179,7 @@ export async function startWebServer(options: { store: Store vapidPublicKey: string socketEngine: SocketEngine + corsOrigins?: string[] }): Promise> { const isCompiled = isBunCompiled() const embeddedAssetMap = isCompiled ? await loadEmbeddedAssetMap() : null @@ -188,6 +190,7 @@ export async function startWebServer(options: { jwtSecret: options.jwtSecret, store: options.store, vapidPublicKey: options.vapidPublicKey, + corsOrigins: options.corsOrigins, embeddedAssetMap })