Skip to content

Commit 05ff668

Browse files
authored
Merge pull request #2942 from appwrite/fix/api-endpoint-double-region-subdomain
fix: normalize regional API base URL and prevent invalid multi-region…
2 parents b2c12f8 + 2f23d56 commit 05ff668

File tree

2 files changed

+92
-42
lines changed

2 files changed

+92
-42
lines changed

src/lib/helpers/apiEndpoint.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
REGION_FRA,
3+
REGION_NYC,
4+
REGION_SFO,
5+
REGION_SGP,
6+
REGION_SYD,
7+
REGION_TOR,
8+
SUBDOMAIN_FRA,
9+
SUBDOMAIN_NYC,
10+
SUBDOMAIN_SFO,
11+
SUBDOMAIN_SGP,
12+
SUBDOMAIN_SYD,
13+
SUBDOMAIN_TOR
14+
} from '$lib/constants';
15+
16+
/** Ordered list of region DNS prefixes (e.g. `fra.`) for stripping from API hostnames. */
17+
const REGION_SUBDOMAIN_PREFIXES: readonly string[] = [
18+
SUBDOMAIN_FRA,
19+
SUBDOMAIN_NYC,
20+
SUBDOMAIN_SYD,
21+
SUBDOMAIN_SFO,
22+
SUBDOMAIN_SGP,
23+
SUBDOMAIN_TOR
24+
];
25+
26+
/**
27+
* Removes leading Appwrite Cloud region label(s) from `hostname` (e.g. `fra.`).
28+
* Strips repeatedly so a doubled prefix (`fra.fra.cloud...`) does not become
29+
* `nyc.fra.cloud...` after prepending another region.
30+
*/
31+
export function stripLeadingRegionSubdomain(hostname: string): string {
32+
let host = hostname;
33+
let changed = true;
34+
while (changed) {
35+
changed = false;
36+
for (const prefix of REGION_SUBDOMAIN_PREFIXES) {
37+
if (host.startsWith(prefix)) {
38+
host = host.slice(prefix.length);
39+
changed = true;
40+
break;
41+
}
42+
}
43+
}
44+
return host;
45+
}
46+
47+
/** Region prefix (e.g. `fra.`) used before the API hostname when multi-region is enabled. */
48+
export function getRegionSubdomain(region?: string): string {
49+
switch (region) {
50+
case REGION_FRA:
51+
return SUBDOMAIN_FRA;
52+
case REGION_SYD:
53+
return SUBDOMAIN_SYD;
54+
case REGION_NYC:
55+
return SUBDOMAIN_NYC;
56+
case REGION_SFO:
57+
return SUBDOMAIN_SFO;
58+
case REGION_SGP:
59+
return SUBDOMAIN_SGP;
60+
case REGION_TOR:
61+
return SUBDOMAIN_TOR;
62+
default:
63+
return '';
64+
}
65+
}
66+
67+
/**
68+
* Builds the `/v1` API base URL (protocol + host + `/v1`).
69+
* When `isMultiRegion` is true and a region is selected, strips any known region prefix from the host,
70+
* then prepends that region. If multi-region is on but no region is requested (`region` missing or
71+
* unknown), the hostname is left unchanged so a default region baked into `APPWRITE_ENDPOINT` is kept.
72+
*/
73+
export function buildRegionalV1Endpoint(
74+
protocol: string,
75+
hostname: string,
76+
region: string | undefined,
77+
isMultiRegion: boolean
78+
): string {
79+
if (!isMultiRegion) {
80+
return `${protocol}//${hostname}/v1`;
81+
}
82+
83+
const subdomain = getRegionSubdomain(region);
84+
if (!subdomain) {
85+
return `${protocol}//${hostname}/v1`;
86+
}
87+
88+
const hostWithoutRegion = stripLeadingRegionSubdomain(hostname);
89+
return `${protocol}//${subdomain}${hostWithoutRegion}/v1`;
90+
}

src/lib/stores/sdk.ts

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,59 +28,19 @@ import {
2828
Realtime,
2929
Organizations
3030
} from '@appwrite.io/console';
31+
import { buildRegionalV1Endpoint } from '$lib/helpers/apiEndpoint';
3132
import { Sources } from '$lib/sdk/sources';
32-
import {
33-
REGION_FRA,
34-
REGION_NYC,
35-
REGION_SYD,
36-
REGION_SFO,
37-
REGION_SGP,
38-
REGION_TOR,
39-
SUBDOMAIN_FRA,
40-
SUBDOMAIN_NYC,
41-
SUBDOMAIN_SFO,
42-
SUBDOMAIN_SYD,
43-
SUBDOMAIN_SGP,
44-
SUBDOMAIN_TOR
45-
} from '$lib/constants';
4633
import { building } from '$app/environment';
4734

4835
export function getApiEndpoint(region?: string): string {
4936
if (building) return '';
5037
const url = new URL(
5138
VARS.APPWRITE_ENDPOINT ? VARS.APPWRITE_ENDPOINT : globalThis?.location?.toString()
5239
);
53-
const protocol = url.protocol;
54-
const hostname = url.host; // "hostname:port" (or just "hostname" if no port)
55-
56-
// If instance supports multi-region, add the region subdomain.
57-
let subdomain = isMultiRegionSupported(url) ? getSubdomain(region) : '';
58-
if (subdomain && hostname.startsWith(subdomain)) {
59-
subdomain = '';
60-
}
6140

62-
return `${protocol}//${subdomain}${hostname}/v1`;
41+
return buildRegionalV1Endpoint(url.protocol, url.host, region, isMultiRegionSupported(url));
6342
}
6443

65-
const getSubdomain = (region?: string) => {
66-
switch (region) {
67-
case REGION_FRA:
68-
return SUBDOMAIN_FRA;
69-
case REGION_SYD:
70-
return SUBDOMAIN_SYD;
71-
case REGION_NYC:
72-
return SUBDOMAIN_NYC;
73-
case REGION_SFO:
74-
return SUBDOMAIN_SFO;
75-
case REGION_SGP:
76-
return SUBDOMAIN_SGP;
77-
case REGION_TOR:
78-
return SUBDOMAIN_TOR;
79-
default:
80-
return '';
81-
}
82-
};
83-
8444
function createConsoleSdk(client: Client) {
8545
return {
8646
client,

0 commit comments

Comments
 (0)