@@ -350,26 +350,47 @@ export const make = Effect.gen(function* () {
350350 readonly body ?: string ;
351351 } ) {
352352 const base = new URL ( `${ input . baseUrl } /api/v1/` ) ;
353- const url = new URL ( input . path , base ) ;
354- if (
355- url . origin !== base . origin ||
356- ! url . pathname . startsWith ( base . pathname ) ||
357- url . username ||
358- url . password
359- )
353+ const apiUrl = ( path : string , from : string ) => {
354+ const url = URL . parse ( path , from ) ;
355+ return url &&
356+ url . origin === base . origin &&
357+ url . pathname . startsWith ( base . pathname ) &&
358+ ! url . username &&
359+ ! url . password
360+ ? url
361+ : undefined ;
362+ } ;
363+ const requested = apiUrl ( input . path , base . href ) ;
364+ if ( ! requested )
360365 return yield * new ForgejoCliError ( {
361366 command : "fj" ,
362367 cwd : input . cwd ,
363368 detail : "Invalid Forgejo API path." ,
364369 } ) ;
365- let request = HttpClientRequest . make ( input . method ?? "GET" ) ( url . toString ( ) ) . pipe (
366- HttpClientRequest . setHeader ( "Authorization" , `token ${ input . token } ` ) ,
367- ) ;
368- if ( input . body !== undefined )
369- request = request . pipe ( HttpClientRequest . bodyText ( input . body , "application/json" ) ) ;
370- const response = yield * httpClient
371- . execute ( request )
372- . pipe ( Effect . provideService ( FetchHttpClient . RequestInit , { redirect : "manual" } ) ) ;
370+ let url = requested ;
371+ const send = ( target : URL ) => {
372+ let request = HttpClientRequest . make ( input . method ?? "GET" ) ( target . toString ( ) ) . pipe (
373+ HttpClientRequest . setHeader ( "Authorization" , `token ${ input . token } ` ) ,
374+ ) ;
375+ if ( input . body !== undefined )
376+ request = request . pipe ( HttpClientRequest . bodyText ( input . body , "application/json" ) ) ;
377+ return httpClient
378+ . execute ( request )
379+ . pipe ( Effect . provideService ( FetchHttpClient . RequestInit , { redirect : "manual" } ) ) ;
380+ } ;
381+ let response = yield * send ( url ) ;
382+ // Forgejo redirects a renamed repository's API paths before running the handler,
383+ // so the same method and body are re-sent. The token never leaves the API base.
384+ for ( let hops = 0 ; hops < 5 ; hops ++ ) {
385+ const location = response . headers . location ;
386+ const next =
387+ location && [ 301 , 302 , 307 , 308 ] . includes ( response . status )
388+ ? apiUrl ( location , url . href )
389+ : undefined ;
390+ if ( ! next ) break ;
391+ url = next ;
392+ response = yield * send ( url ) ;
393+ }
373394 const status = response . status ;
374395 const body =
375396 status === 204 || status === 205
0 commit comments