diff --git a/packages/synchronizer/src/handlers/apiHandler.ts b/packages/synchronizer/src/handlers/apiHandler.ts index 4c3c0e3c6..a94a08037 100644 --- a/packages/synchronizer/src/handlers/apiHandler.ts +++ b/packages/synchronizer/src/handlers/apiHandler.ts @@ -3,6 +3,7 @@ import fetch from 'node-fetch'; import {SuppressionStatus} from '@monokle/types'; import {DEFAULT_API_URL} from '../constants.js'; import type {TokenInfo} from './storageHandlerAuth.js'; +import { OriginConfig } from '../utils/fetcher.js'; const getUserQuery = ` query getUser { @@ -176,8 +177,21 @@ export type ApiRepoIdData = { }; export class ApiHandler { - constructor(private _apiUrl: string = DEFAULT_API_URL) { - if ((_apiUrl || '').length === 0) { + private _apiUrl: string; + private _originConfig?: OriginConfig; + + constructor(_apiUrl: string); + constructor(_originConfig: OriginConfig); + constructor(_apiUrlOrOriginConfig: string | OriginConfig = DEFAULT_API_URL) { + + if (typeof _apiUrlOrOriginConfig === 'string') { + this._apiUrl = _apiUrlOrOriginConfig; + } else { + this._originConfig = _apiUrlOrOriginConfig; + this._apiUrl = _apiUrlOrOriginConfig.apiOrigin; + } + + if ((this._apiUrl || '').length === 0) { this._apiUrl = DEFAULT_API_URL; } } @@ -207,15 +221,19 @@ export class ApiHandler { } generateDeepLink(path: string) { - if (this.apiUrl.includes('staging.monokle.com')) { - return normalizeUrl(`https://app.staging.monokle.com/${path}`); - } else if (this.apiUrl.includes('.monokle.com')) { - return normalizeUrl(`https://app.monokle.com/${path}`); + let appUrl = this._originConfig?.origin; + + if (!appUrl) { + if (this.apiUrl.includes('staging.monokle.com')) { + appUrl = 'https://app.staging.monokle.com/'; + } else if (this.apiUrl.includes('.monokle.com')) { + appUrl = 'https://app.monokle.com/'; + } else { + appUrl = this.apiUrl; + } } - // For any custom base urls we just append the path. - // @TODO this might need adjustment in the future for self-hosted solutions. - return normalizeUrl(`${this.apiUrl}/${path}`); + return normalizeUrl(`${appUrl}/${path}`); } async queryApi(query: string, tokenInfo: TokenInfo, variables = {}): Promise { diff --git a/packages/synchronizer/src/utils/fetcher.ts b/packages/synchronizer/src/utils/fetcher.ts index e674a5a6a..3829b2dd4 100644 --- a/packages/synchronizer/src/utils/fetcher.ts +++ b/packages/synchronizer/src/utils/fetcher.ts @@ -1,3 +1,5 @@ +import normalizeUrl from 'normalize-url'; +import fetch from 'node-fetch'; import {EventEmitter} from 'events'; import {ApiHandler} from '../handlers/apiHandler.js'; import {TokenInfo} from '../handlers/storageHandlerAuth.js'; @@ -13,6 +15,14 @@ export type QueryResult = { error?: string; }; +export type OriginConfig = { + origin: string; + apiOrigin: string; + authOrigin: string; + authClientId: string; + [key: string]: string; +}; + export class Fetcher extends EventEmitter { private _token: TokenInfo | undefined; @@ -20,6 +30,32 @@ export class Fetcher extends EventEmitter { super(); } + static async getOriginConfig(origin: string): Promise { + try { + const configUrl = normalizeUrl(`${origin}/config.js`); + const response = await fetch(configUrl); + const responseText = await response.text(); + + const values = responseText.match(/([A-Z_]+)\s*:\s*"(.*?)"/gm)?.reduce((acc: Record, match) => { + if (match[1] && match[2]) { + acc[match[1]] = match[2]; + } + return acc; + }, {}); + + if (values) { + values.origin = origin; + values.apiOrigin = values.API_ORIGIN; + values.authOrigin = values.OIDC_DISCOVERY_URL; + values.authClientId = values.CLIENT_ID; + } + + return values as OriginConfig; + } catch (error: any) { + return undefined; + } + } + useBearerToken(token: string) { this._token = { accessToken: token,