@@ -2,7 +2,8 @@ import LinkHeader from 'http-link-header';
22
33import { Logger , ExposableError } from '@gitbook/runtime' ;
44
5- import type { GitLabSpaceConfiguration } from './types' ;
5+ import type { GitLabRuntimeContext , GitLabSpaceConfiguration } from './types' ;
6+ import { signResponse } from './utils' ;
67
78const logger = Logger ( 'gitlab:api' ) ;
89
@@ -38,8 +39,11 @@ interface GLFetchOptions {
3839/**
3940 * Fetch the current GitLab user. It will use the access token from the environment.
4041 */
41- export async function getCurrentUser ( config : GitLabSpaceConfiguration ) {
42- const user = await gitlabAPI < GLUser > ( config , {
42+ export async function getCurrentUser (
43+ context : GitLabRuntimeContext ,
44+ config : GitLabSpaceConfiguration ,
45+ ) {
46+ const user = await gitlabAPI < GLUser > ( context , config , {
4347 path : '/user' ,
4448 } ) ;
4549
@@ -51,10 +55,11 @@ export async function getCurrentUser(config: GitLabSpaceConfiguration) {
5155 * the access token from the environment.
5256 */
5357export async function fetchProjects (
58+ context : GitLabRuntimeContext ,
5459 config : GitLabSpaceConfiguration ,
5560 options : GLFetchOptions = { } ,
5661) {
57- const projects = await gitlabAPI < Array < GLProject > > ( config , {
62+ const projects = await gitlabAPI < Array < GLProject > > ( context , config , {
5863 path : '/projects' ,
5964 params : {
6065 membership : true ,
@@ -71,11 +76,12 @@ export async function fetchProjects(
7176 * Search currently authenticated user projects for a given query.
7277 */
7378export async function searchUserProjects (
79+ context : GitLabRuntimeContext ,
7480 config : GitLabSpaceConfiguration ,
7581 search : string ,
7682 options : GLFetchOptions = { } ,
7783) {
78- const projects = await gitlabAPI < Array < GLProject > > ( config , {
84+ const projects = await gitlabAPI < Array < GLProject > > ( context , config , {
7985 path : `/users/${ config . userId } /projects` ,
8086 params : {
8187 search,
@@ -91,8 +97,12 @@ export async function searchUserProjects(
9197/**
9298 * Fetch a GitLab project by its ID.
9399 */
94- export async function fetchProject ( config : GitLabSpaceConfiguration , projectId : number ) {
95- const project = await gitlabAPI < GLProject > ( config , {
100+ export async function fetchProject (
101+ context : GitLabRuntimeContext ,
102+ config : GitLabSpaceConfiguration ,
103+ projectId : number ,
104+ ) {
105+ const project = await gitlabAPI < GLProject > ( context , config , {
96106 path : `/projects/${ projectId } ` ,
97107 } ) ;
98108
@@ -102,8 +112,12 @@ export async function fetchProject(config: GitLabSpaceConfiguration, projectId:
102112/**
103113 * Fetch all branches for a given project repository.
104114 */
105- export async function fetchProjectBranches ( config : GitLabSpaceConfiguration , projectId : number ) {
106- const branches = await gitlabAPI < Array < GLBranch > > ( config , {
115+ export async function fetchProjectBranches (
116+ context : GitLabRuntimeContext ,
117+ config : GitLabSpaceConfiguration ,
118+ projectId : number ,
119+ ) {
120+ const branches = await gitlabAPI < Array < GLBranch > > ( context , config , {
107121 path : `/projects/${ projectId } /repository/branches` ,
108122 params : {
109123 per_page : 100 ,
@@ -118,12 +132,13 @@ export async function fetchProjectBranches(config: GitLabSpaceConfiguration, pro
118132 * Configure a GitLab webhook for a given project.
119133 */
120134export async function addProjectWebhook (
135+ context : GitLabRuntimeContext ,
121136 config : GitLabSpaceConfiguration ,
122137 projectId : number ,
123138 webhookUrl : string ,
124139 webhookToken : string ,
125140) {
126- const { id } = await gitlabAPI < { id : number } > ( config , {
141+ const { id } = await gitlabAPI < { id : number } > ( context , config , {
127142 method : 'POST' ,
128143 path : `/projects/${ projectId } /hooks` ,
129144 body : {
@@ -141,11 +156,12 @@ export async function addProjectWebhook(
141156 * Delete a GitLab webhook for a given project.
142157 */
143158export async function deleteProjectWebhook (
159+ context : GitLabRuntimeContext ,
144160 config : GitLabSpaceConfiguration ,
145161 projectId : number ,
146162 webhookId : number ,
147163) {
148- await gitlabAPI ( config , {
164+ await gitlabAPI ( context , config , {
149165 method : 'DELETE' ,
150166 path : `/projects/${ projectId } /hooks/${ webhookId } ` ,
151167 } ) ;
@@ -155,12 +171,13 @@ export async function deleteProjectWebhook(
155171 * Create a commit status for a commit SHA.
156172 */
157173export async function editCommitStatus (
174+ context : GitLabRuntimeContext ,
158175 config : GitLabSpaceConfiguration ,
159176 projectId : number ,
160177 sha : string ,
161178 status : object ,
162179) : Promise < void > {
163- await gitlabAPI ( config , {
180+ await gitlabAPI ( context , config , {
164181 method : 'POST' ,
165182 path : `/projects/${ projectId } /statuses/${ sha } ` ,
166183 body : status ,
@@ -171,6 +188,7 @@ export async function editCommitStatus(
171188 * Execute a GitLab API request.
172189 */
173190export async function gitlabAPI < T > (
191+ context : GitLabRuntimeContext ,
174192 config : GitLabSpaceConfiguration ,
175193 request : {
176194 method ?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' ;
@@ -209,7 +227,7 @@ export async function gitlabAPI<T>(
209227 body : body ? JSON . stringify ( body ) : undefined ,
210228 } ;
211229
212- const response = await requestGitLab ( token , url , options ) ;
230+ const response = await requestGitLab ( context , token , url , options ) ;
213231
214232 const isJSONResponse = response . headers . get ( 'Content-Type' ) ?. includes ( 'application/json' ) ;
215233 if ( ! isJSONResponse ) {
@@ -234,7 +252,7 @@ export async function gitlabAPI<T>(
234252 const nextURLSearchParams = Object . fromEntries ( nextURL . searchParams ) ;
235253 if ( nextURLSearchParams . page ) {
236254 url . searchParams . set ( 'page' , nextURLSearchParams . page as string ) ;
237- const nextResponse = await requestGitLab ( token , url , options ) ;
255+ const nextResponse = await requestGitLab ( context , token , url , options ) ;
238256 const nextData = await nextResponse . json ( ) ;
239257 // @ts -ignore
240258 data = [ ...data , ...( paginatedListProperty ? nextData [ listProperty ] : nextData ) ] ;
@@ -253,21 +271,35 @@ export async function gitlabAPI<T>(
253271 * It will throw an error if the response is not ok.
254272 */
255273async function requestGitLab (
274+ context : GitLabRuntimeContext ,
256275 token : string ,
257276 url : URL ,
258277 options : RequestInit = { } ,
259278) : Promise < Response > {
260279 logger . debug ( `GitLab API -> [${ options . method } ] ${ url . toString ( ) } ` ) ;
261- const response = await fetch ( url . toString ( ) , {
262- ...options ,
263- headers : {
264- ...options . headers ,
265- ...( options . body ? { 'Content-Type' : 'application/json' } : { } ) ,
266- Accept : 'application/json' ,
267- Authorization : `Bearer ${ token } ` ,
268- 'User-Agent' : 'GitLab-Integration-Worker' ,
269- } ,
270- } ) ;
280+ // Hardcoded test org, will need to switch to use Reflag for that.
281+ const useProxy = await shouldUseProxy ( context ) ;
282+ const response = useProxy
283+ ? await proxyRequest ( context , url . toString ( ) , {
284+ ...options ,
285+ headers : {
286+ ...options . headers ,
287+ ...( options . body ? { 'Content-Type' : 'application/json' } : { } ) ,
288+ Accept : 'application/json' ,
289+ Authorization : `Bearer ${ token } ` ,
290+ 'User-Agent' : 'GitLab-Integration-Worker' ,
291+ } ,
292+ } )
293+ : await fetch ( url . toString ( ) , {
294+ ...options ,
295+ headers : {
296+ ...options . headers ,
297+ ...( options . body ? { 'Content-Type' : 'application/json' } : { } ) ,
298+ Accept : 'application/json' ,
299+ Authorization : `Bearer ${ token } ` ,
300+ 'User-Agent' : 'GitLab-Integration-Worker' ,
301+ } ,
302+ } ) ;
271303
272304 if ( ! response . ok ) {
273305 const text = await response . text ( ) ;
@@ -303,3 +335,51 @@ export function getAccessTokenOrThrow(config: GitLabSpaceConfiguration): string
303335
304336 return accessToken ;
305337}
338+
339+ export async function proxyRequest (
340+ context : GitLabRuntimeContext ,
341+ url : string ,
342+ options : RequestInit = { } ,
343+ ) : Promise < Response > {
344+ const signature = await signResponse ( url , context . environment . secrets . PROXY_SECRET ) ;
345+ const proxyUrl = new URL ( context . environment . secrets . PROXY_URL ) ;
346+
347+ proxyUrl . searchParams . set ( 'target' , url ) ;
348+
349+ return fetch ( proxyUrl . toString ( ) , {
350+ ...options ,
351+ headers : {
352+ ...options . headers ,
353+ 'X-Gitbook-Proxy-Signature' : signature ,
354+ } ,
355+ } ) ;
356+ }
357+
358+ export async function shouldUseProxy ( context : GitLabRuntimeContext ) : Promise < boolean > {
359+ const companyId = context . environment . installation ?. target . organization ;
360+ if ( ! companyId ) {
361+ return false ;
362+ }
363+ try {
364+ const response = await fetch (
365+ `https://front.reflag.com/features/enabled?context.company.id=${ companyId } &key=GIT_SYNC_STATIC_IP` ,
366+ {
367+ method : 'GET' ,
368+ headers : {
369+ Authorization : `Bearer ${ context . environment . secrets . REFLAG_SECRET_KEY } ` ,
370+ 'Content-Type' : 'application/json' ,
371+ } ,
372+ } ,
373+ ) ;
374+
375+ const json = ( await response . json ( ) ) as {
376+ features : { GIT_SYNC_STATIC_IP : { isEnabled : boolean } } ;
377+ } ;
378+ const flag = json . features . GIT_SYNC_STATIC_IP ;
379+
380+ return flag . isEnabled ;
381+ } catch ( e ) {
382+ logger . error ( 'Error checking Reflag feature flag:' , e ) ;
383+ return false ;
384+ }
385+ }
0 commit comments