mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
Normalize CORS origins before server wiring
This commit is contained in:
+46
-3
@@ -61,6 +61,42 @@ function resolveRelayFlag(args: string[]): { enabled: boolean; source: RelayFlag
|
|||||||
return { enabled, source }
|
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<string>()
|
||||||
|
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 syncEngine: SyncEngine | null = null
|
||||||
let happyBot: HappyBot | null = null
|
let happyBot: HappyBot | null = null
|
||||||
let webServer: BunServer<WebSocketData> | null = null
|
let webServer: BunServer<WebSocketData> | null = null
|
||||||
@@ -73,9 +109,15 @@ async function main() {
|
|||||||
console.log('HAPI Server starting...')
|
console.log('HAPI Server starting...')
|
||||||
|
|
||||||
// Load configuration (async - loads from env/file with persistence)
|
// Load configuration (async - loads from env/file with persistence)
|
||||||
const config = await createConfiguration()
|
|
||||||
const relayApiDomain = process.env.HAPI_RELAY_API || 'relay.hapi.run'
|
const relayApiDomain = process.env.HAPI_RELAY_API || 'relay.hapi.run'
|
||||||
const relayFlag = resolveRelayFlag(process.argv)
|
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
|
// Display CLI API token information
|
||||||
if (config.cliApiTokenIsNew) {
|
if (config.cliApiTokenIsNew) {
|
||||||
@@ -127,6 +169,7 @@ async function main() {
|
|||||||
const socketServer = createSocketServer({
|
const socketServer = createSocketServer({
|
||||||
store,
|
store,
|
||||||
jwtSecret,
|
jwtSecret,
|
||||||
|
corsOrigins,
|
||||||
getSession: (sessionId) => syncEngine?.getSession(sessionId) ?? store.sessions.getSession(sessionId),
|
getSession: (sessionId) => syncEngine?.getSession(sessionId) ?? store.sessions.getSession(sessionId),
|
||||||
onWebappEvent: (event: SyncEvent) => syncEngine?.handleRealtimeEvent(event),
|
onWebappEvent: (event: SyncEvent) => syncEngine?.handleRealtimeEvent(event),
|
||||||
onSessionAlive: (payload) => syncEngine?.handleSessionAlive(payload),
|
onSessionAlive: (payload) => syncEngine?.handleSessionAlive(payload),
|
||||||
@@ -164,7 +207,8 @@ async function main() {
|
|||||||
jwtSecret,
|
jwtSecret,
|
||||||
store,
|
store,
|
||||||
vapidPublicKey: vapidKeys.publicKey,
|
vapidPublicKey: vapidKeys.publicKey,
|
||||||
socketEngine: socketServer.engine
|
socketEngine: socketServer.engine,
|
||||||
|
corsOrigins
|
||||||
})
|
})
|
||||||
|
|
||||||
// Start the bot if configured
|
// Start the bot if configured
|
||||||
@@ -207,7 +251,6 @@ async function main() {
|
|||||||
console.log('[Web] Public: ' + tunnelUrl)
|
console.log('[Web] Public: ' + tunnelUrl)
|
||||||
|
|
||||||
// Generate direct access link with server and token
|
// Generate direct access link with server and token
|
||||||
const officialWebUrl = process.env.HAPI_OFFICIAL_WEB_URL || 'https://app.hapi.run'
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
server: tunnelUrl,
|
server: tunnelUrl,
|
||||||
token: config.cliApiToken
|
token: config.cliApiToken
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ function resolveEnvNumber(name: string, fallback: number): number {
|
|||||||
export type SocketServerDeps = {
|
export type SocketServerDeps = {
|
||||||
store: Store
|
store: Store
|
||||||
jwtSecret: Uint8Array
|
jwtSecret: Uint8Array
|
||||||
|
corsOrigins?: string[]
|
||||||
getSession?: (sessionId: string) => { active: boolean; namespace: string } | null
|
getSession?: (sessionId: string) => { active: boolean; namespace: string } | null
|
||||||
onWebappEvent?: (event: SyncEvent) => void
|
onWebappEvent?: (event: SyncEvent) => void
|
||||||
onSessionAlive?: (payload: { sid: string; time: number; thinking?: boolean; mode?: 'local' | 'remote' }) => void
|
onSessionAlive?: (payload: { sid: string; time: number; thinking?: boolean; mode?: 'local' | 'remote' }) => void
|
||||||
@@ -45,7 +46,7 @@ export function createSocketServer(deps: SocketServerDeps): {
|
|||||||
engine: Engine
|
engine: Engine
|
||||||
rpcRegistry: RpcRegistry
|
rpcRegistry: RpcRegistry
|
||||||
} {
|
} {
|
||||||
const corsOrigins = configuration.corsOrigins
|
const corsOrigins = deps.corsOrigins ?? configuration.corsOrigins
|
||||||
const allowAllOrigins = corsOrigins.includes('*')
|
const allowAllOrigins = corsOrigins.includes('*')
|
||||||
|
|
||||||
const io = new Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, SocketData>({
|
const io = new Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, SocketData>({
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ function createWebApp(options: {
|
|||||||
jwtSecret: Uint8Array
|
jwtSecret: Uint8Array
|
||||||
store: Store
|
store: Store
|
||||||
vapidPublicKey: string
|
vapidPublicKey: string
|
||||||
|
corsOrigins?: string[]
|
||||||
embeddedAssetMap: Map<string, EmbeddedWebAsset> | null
|
embeddedAssetMap: Map<string, EmbeddedWebAsset> | null
|
||||||
}): Hono<WebAppEnv> {
|
}): Hono<WebAppEnv> {
|
||||||
const app = new Hono<WebAppEnv>()
|
const app = new Hono<WebAppEnv>()
|
||||||
@@ -68,7 +69,7 @@ function createWebApp(options: {
|
|||||||
// Health check endpoint (no auth required)
|
// Health check endpoint (no auth required)
|
||||||
app.get('/health', (c) => c.json({ status: 'ok' }))
|
app.get('/health', (c) => c.json({ status: 'ok' }))
|
||||||
|
|
||||||
const corsOrigins = configuration.corsOrigins
|
const corsOrigins = options.corsOrigins ?? configuration.corsOrigins
|
||||||
const corsOriginOption = corsOrigins.includes('*') ? '*' : corsOrigins
|
const corsOriginOption = corsOrigins.includes('*') ? '*' : corsOrigins
|
||||||
const corsMiddleware = cors({
|
const corsMiddleware = cors({
|
||||||
origin: corsOriginOption,
|
origin: corsOriginOption,
|
||||||
@@ -178,6 +179,7 @@ export async function startWebServer(options: {
|
|||||||
store: Store
|
store: Store
|
||||||
vapidPublicKey: string
|
vapidPublicKey: string
|
||||||
socketEngine: SocketEngine
|
socketEngine: SocketEngine
|
||||||
|
corsOrigins?: string[]
|
||||||
}): Promise<BunServer<WebSocketData>> {
|
}): Promise<BunServer<WebSocketData>> {
|
||||||
const isCompiled = isBunCompiled()
|
const isCompiled = isBunCompiled()
|
||||||
const embeddedAssetMap = isCompiled ? await loadEmbeddedAssetMap() : null
|
const embeddedAssetMap = isCompiled ? await loadEmbeddedAssetMap() : null
|
||||||
@@ -188,6 +190,7 @@ export async function startWebServer(options: {
|
|||||||
jwtSecret: options.jwtSecret,
|
jwtSecret: options.jwtSecret,
|
||||||
store: options.store,
|
store: options.store,
|
||||||
vapidPublicKey: options.vapidPublicKey,
|
vapidPublicKey: options.vapidPublicKey,
|
||||||
|
corsOrigins: options.corsOrigins,
|
||||||
embeddedAssetMap
|
embeddedAssetMap
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user