@@ -135,6 +135,7 @@ function isAdminRequest(request: FastifyRequest): boolean {
135135}
136136
137137function shouldRequireAuth ( request : FastifyRequest ) : boolean {
138+ if ( request . method === "OPTIONS" ) return false ;
138139 if ( request . method === "GET" && request . url . startsWith ( "/admin/config" ) ) return false ;
139140 if ( request . method === "GET" && request . url . startsWith ( "/admin/commandcode/credentials" ) ) {
140141 return false ;
@@ -144,12 +145,28 @@ function shouldRequireAuth(request: FastifyRequest): boolean {
144145
145146function isPublicAdminRequest ( request : FastifyRequest ) : boolean {
146147 return (
147- request . method === "GET" &&
148- ( request . url . startsWith ( "/admin/config" ) ||
149- request . url . startsWith ( "/admin/commandcode/credentials" ) )
148+ request . method === "OPTIONS" ||
149+ ( request . method === "GET" &&
150+ ( request . url . startsWith ( "/admin/config" ) ||
151+ request . url . startsWith ( "/admin/commandcode/credentials" ) ) )
150152 ) ;
151153}
152154
155+ function sameHostnameOrigin ( request : FastifyRequest ) : string | undefined {
156+ const origin = request . headers . origin ;
157+ if ( ! origin ) return undefined ;
158+ const host = request . headers . host ;
159+ if ( ! host ) return undefined ;
160+ try {
161+ const originUrl = new URL ( origin ) ;
162+ const hostName = host . split ( ":" ) [ 0 ] ;
163+ if ( originUrl . protocol === "http:" && originUrl . hostname === hostName ) return origin ;
164+ } catch {
165+ return undefined ;
166+ }
167+ return undefined ;
168+ }
169+
153170function asOpenAIRequest (
154171 value : z . infer < typeof chatCompletionRequestSchema > ,
155172) : OpenAIChatCompletionRequest {
@@ -320,8 +337,22 @@ export async function createApp(options: CreateAppOptions = {}): Promise<Fastify
320337 } ,
321338 } ,
322339 } ) ;
323- await app . register ( rateLimit , { max : config . rateLimitMax , timeWindow : config . rateLimitWindow } ) ;
324340 if ( config . corsOrigin ) await app . register ( cors , { origin : config . corsOrigin } ) ;
341+ app . addHook ( "onRequest" , async ( request , reply ) => {
342+ if ( config . corsOrigin ) return ;
343+ const origin = sameHostnameOrigin ( request ) ;
344+ if ( ! origin ) return ;
345+ reply . header ( "access-control-allow-origin" , origin ) ;
346+ reply . header ( "vary" , "origin" ) ;
347+ reply . header ( "access-control-allow-methods" , "GET,PUT,POST,OPTIONS" ) ;
348+ reply . header ( "access-control-allow-headers" , "authorization,content-type,x-api-key" ) ;
349+ } ) ;
350+ app . options ( "*" , async ( request , reply ) => {
351+ const origin = config . corsOrigin ? request . headers . origin : sameHostnameOrigin ( request ) ;
352+ if ( ! origin && ! config . corsOrigin ) return reply . code ( 404 ) . send ( ) ;
353+ return reply . code ( 204 ) . send ( ) ;
354+ } ) ;
355+ await app . register ( rateLimit , { max : config . rateLimitMax , timeWindow : config . rateLimitWindow } ) ;
325356
326357 let balanceAlertTimer : NodeJS . Timeout | undefined ;
327358 if ( balanceAlertManager ) {
0 commit comments