Files
moeka-project/apps/server/src/utils/server-admin-ui.ts
T

98 lines
3.4 KiB
TypeScript

export const SERVER_ADMIN_UI_BASE_PATH = '/admin'
export const ADMIN_UI_API_SERVER_URL_QUERY_PARAM = 'api_server_url'
export const DEFAULT_ADMIN_UI_URL = 'https://admin.airi.build'
export const SERVER_DEV_API_SERVER_URL = 'https://airi-server-dev.up.railway.app'
export const SERVER_DEV_ADMIN_UI_URL = 'https://server-dev.airi-server-admin.pages.dev'
/**
* Builds an absolute URL inside the externally hosted admin UI.
*
* Use when:
* - Redirecting server-owned admin UI entrypoints to the standalone
* admin UI deployment from the `proj-airi` repository.
* - Preserving dashboard route paths and query parameters.
*
* Expects:
* - `adminUiUrl` is the public admin UI base.
* - `path` is the route path within the admin UI router.
*
* Returns:
* - An absolute URL with the admin UI base path, normalized path, and search.
*/
export function buildAdminUiUrl(adminUiUrl: string, path: string, search = ''): string {
const target = new URL(adminUiUrl)
const basePath = target.pathname.replace(/\/+$/, '')
const routePath = path.startsWith('/') ? path : `/${path}`
target.pathname = `${basePath}${routePath}`
target.search = search
target.hash = ''
return target.toString()
}
/**
* Resolves the standalone admin UI base for the active server environment.
*
* Use when:
* - The server redirects historical `/admin/*` entrypoints to the standalone UI.
* - The server-dev Railway deployment needs the matching Cloudflare Pages
* branch without changing the production admin domain.
*
* Expects:
* - `adminUiUrl` is the configured admin UI base URL.
* - `apiServerUrl` is the configured API server URL.
*
* Returns:
* - The configured admin UI URL, except for the server-dev default pairing where
* the matching Pages branch URL is returned.
*/
export function resolveAdminUiUrl(adminUiUrl: string, apiServerUrl: string): string {
try {
const adminUi = new URL(adminUiUrl)
const defaultAdminUi = new URL(DEFAULT_ADMIN_UI_URL)
const apiServer = new URL(apiServerUrl)
const adminUiBase = `${adminUi.origin}${adminUi.pathname.replace(/\/+$/, '')}`
const defaultAdminUiBase = `${defaultAdminUi.origin}${defaultAdminUi.pathname.replace(/\/+$/, '')}`
if (adminUiBase === defaultAdminUiBase && apiServer.origin === SERVER_DEV_API_SERVER_URL) {
return SERVER_DEV_ADMIN_UI_URL
}
}
catch {
return adminUiUrl
}
return adminUiUrl
}
/**
* Maps a server `/admin/*` request to the standalone admin UI.
*
* Use when:
* - The server keeps owning the historical `/admin/*` entrypoint but no longer
* packages the admin UI bundle.
*
* Expects:
* - `requestUrl` is the incoming server URL.
* - `adminUiUrl` points to the standalone admin UI base.
*
* Returns:
* - The external admin UI URL preserving route suffix and query string.
*/
export function buildAdminUiRedirectUrl(adminUiUrl: string, requestUrl: string, apiServerUrl?: string): string {
const request = new URL(requestUrl)
const suffix = request.pathname === SERVER_ADMIN_UI_BASE_PATH
? '/'
: request.pathname.slice(SERVER_ADMIN_UI_BASE_PATH.length) || '/'
const resolvedAdminUiUrl = apiServerUrl ? resolveAdminUiUrl(adminUiUrl, apiServerUrl) : adminUiUrl
const target = new URL(buildAdminUiUrl(resolvedAdminUiUrl, suffix, request.search))
if (apiServerUrl) {
const apiServer = new URL(apiServerUrl)
target.searchParams.set(ADMIN_UI_API_SERVER_URL_QUERY_PARAM, apiServer.origin)
}
return target.toString()
}