Merge pull request #2942 from appwrite/fix/api-endpoint-double-region-subdomain

fix: normalize regional API base URL and prevent invalid multi-region…
This commit is contained in:
Harsh Mahajan
2026-04-16 18:35:01 +05:30
committed by GitHub
2 changed files with 92 additions and 42 deletions
+90
View File
@@ -0,0 +1,90 @@
import {
REGION_FRA,
REGION_NYC,
REGION_SFO,
REGION_SGP,
REGION_SYD,
REGION_TOR,
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SFO,
SUBDOMAIN_SGP,
SUBDOMAIN_SYD,
SUBDOMAIN_TOR
} from '$lib/constants';
/** Ordered list of region DNS prefixes (e.g. `fra.`) for stripping from API hostnames. */
const REGION_SUBDOMAIN_PREFIXES: readonly string[] = [
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SYD,
SUBDOMAIN_SFO,
SUBDOMAIN_SGP,
SUBDOMAIN_TOR
];
/**
* Removes leading Appwrite Cloud region label(s) from `hostname` (e.g. `fra.`).
* Strips repeatedly so a doubled prefix (`fra.fra.cloud...`) does not become
* `nyc.fra.cloud...` after prepending another region.
*/
export function stripLeadingRegionSubdomain(hostname: string): string {
let host = hostname;
let changed = true;
while (changed) {
changed = false;
for (const prefix of REGION_SUBDOMAIN_PREFIXES) {
if (host.startsWith(prefix)) {
host = host.slice(prefix.length);
changed = true;
break;
}
}
}
return host;
}
/** Region prefix (e.g. `fra.`) used before the API hostname when multi-region is enabled. */
export function getRegionSubdomain(region?: string): string {
switch (region) {
case REGION_FRA:
return SUBDOMAIN_FRA;
case REGION_SYD:
return SUBDOMAIN_SYD;
case REGION_NYC:
return SUBDOMAIN_NYC;
case REGION_SFO:
return SUBDOMAIN_SFO;
case REGION_SGP:
return SUBDOMAIN_SGP;
case REGION_TOR:
return SUBDOMAIN_TOR;
default:
return '';
}
}
/**
* Builds the `/v1` API base URL (protocol + host + `/v1`).
* When `isMultiRegion` is true and a region is selected, strips any known region prefix from the host,
* then prepends that region. If multi-region is on but no region is requested (`region` missing or
* unknown), the hostname is left unchanged so a default region baked into `APPWRITE_ENDPOINT` is kept.
*/
export function buildRegionalV1Endpoint(
protocol: string,
hostname: string,
region: string | undefined,
isMultiRegion: boolean
): string {
if (!isMultiRegion) {
return `${protocol}//${hostname}/v1`;
}
const subdomain = getRegionSubdomain(region);
if (!subdomain) {
return `${protocol}//${hostname}/v1`;
}
const hostWithoutRegion = stripLeadingRegionSubdomain(hostname);
return `${protocol}//${subdomain}${hostWithoutRegion}/v1`;
}
+2 -42
View File
@@ -28,21 +28,8 @@ import {
Realtime,
Organizations
} from '@appwrite.io/console';
import { buildRegionalV1Endpoint } from '$lib/helpers/apiEndpoint';
import { Sources } from '$lib/sdk/sources';
import {
REGION_FRA,
REGION_NYC,
REGION_SYD,
REGION_SFO,
REGION_SGP,
REGION_TOR,
SUBDOMAIN_FRA,
SUBDOMAIN_NYC,
SUBDOMAIN_SFO,
SUBDOMAIN_SYD,
SUBDOMAIN_SGP,
SUBDOMAIN_TOR
} from '$lib/constants';
import { building } from '$app/environment';
export function getApiEndpoint(region?: string): string {
@@ -50,37 +37,10 @@ export function getApiEndpoint(region?: string): string {
const url = new URL(
VARS.APPWRITE_ENDPOINT ? VARS.APPWRITE_ENDPOINT : globalThis?.location?.toString()
);
const protocol = url.protocol;
const hostname = url.host; // "hostname:port" (or just "hostname" if no port)
// If instance supports multi-region, add the region subdomain.
let subdomain = isMultiRegionSupported(url) ? getSubdomain(region) : '';
if (subdomain && hostname.startsWith(subdomain)) {
subdomain = '';
}
return `${protocol}//${subdomain}${hostname}/v1`;
return buildRegionalV1Endpoint(url.protocol, url.host, region, isMultiRegionSupported(url));
}
const getSubdomain = (region?: string) => {
switch (region) {
case REGION_FRA:
return SUBDOMAIN_FRA;
case REGION_SYD:
return SUBDOMAIN_SYD;
case REGION_NYC:
return SUBDOMAIN_NYC;
case REGION_SFO:
return SUBDOMAIN_SFO;
case REGION_SGP:
return SUBDOMAIN_SGP;
case REGION_TOR:
return SUBDOMAIN_TOR;
default:
return '';
}
};
function createConsoleSdk(client: Client) {
return {
client,