fix(region): route projects in any cloud region to their subdomain (DISCORD-15265968)#3119
fix(region): route projects in any cloud region to their subdomain (DISCORD-15265968)#3119claudear wants to merge 1 commit into
Conversation
…5265968) Clicking a project in the organization view got stuck for projects hosted in an Appwrite Cloud region that was not part of the hardcoded region list. getRegionSubdomain() resolved a project's regional subdomain via a switch that only knew fra/syd/nyc/sfo/sgp/tor. Any other region fell through to the empty default, so buildRegionalV1Endpoint() left the host unchanged and the SDK issued requests to the apex host (or the region baked into APPWRITE_ENDPOINT) instead of the project's real regional host (e.g. blr.cloud.appwrite.io). Those cross-origin requests failed CORS and the navigation hung. The impersonation tooling targets the correct regional endpoint directly, which is why the same action worked there. Derive the subdomain directly from the region id so any current or future region is routed correctly, keeping the `default` placeholder (and empty values) mapped to the apex domain. stripLeadingRegionSubdomain() now also strips the region about to be prepended, avoiding a doubled subdomain when the console itself is served from that region. Adds unit tests covering the regression and the edge cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Greptile SummaryThis PR fixes project navigation getting stuck (with CORS errors) when clicking a project in any Appwrite Cloud region not covered by the old hardcoded
Confidence Score: 4/5Safe to merge for the primary reported scenario; a narrow gap remains for consoles served from unlisted regions navigating to a different region. The main fix is correct and well-tested: any region id now maps directly to its subdomain, resolving the CORS hang for all new regions. The remaining gap — the extraPrefix mechanism only prevents doubling when the hostname already carries the same unlisted region as the target, so navigating from an unlisted-region console host to a different region produces a compound hostname — is a corner case that would only surface when APPWRITE_ENDPOINT is unset and the console is opened from a new-region URL. Both changed files are worth a second look: apiEndpoint.ts for the stripping logic gap, and apiEndpoint.test.ts for the missing cross-region test case. Important Files Changed
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
src/lib/helpers/apiEndpoint.ts:34-37
**Unlisted source-region prefix not stripped when navigating to a different region**
`extraPrefix` is the subdomain being *prepended* (the target region), so it only prevents doubling when the hostname already carries the same unlisted region as the destination. If the console is served from an unlisted region (e.g. `blr.cloud.appwrite.io`) and the user navigates to a *different* region (e.g. `nyc`), `extraPrefix = 'nyc.'` is already in `REGION_SUBDOMAIN_PREFIXES`, so the original list is used unchanged and `blr.` is never stripped — the result is `nyc.blr.cloud.appwrite.io/v1` instead of `nyc.cloud.appwrite.io/v1`. This scenario is real when `APPWRITE_ENDPOINT` is unset and `getApiEndpoint` in `sdk.ts` falls back to `globalThis.location`, picking up the console's own host, which can itself carry a new-region prefix.
### Issue 2 of 2
src/lib/helpers/apiEndpoint.test.ts:53-67
**Missing test for unlisted source region with a different target region**
The existing `buildRegionalV1Endpoint` tests cover `fra → blr` and `blr → blr`, but not `blr → nyc` (unlisted source region, different listed target region). Adding a case like `buildRegionalV1Endpoint('https:', 'blr.cloud.appwrite.io', 'nyc', true)` and asserting it returns `https://nyc.cloud.appwrite.io/v1` would pin down the behaviour described above and catch any future regression in the stripping logic.
Reviews (1): Last reviewed commit: "fix: route projects in any cloud region ..." | Re-trigger Greptile |
| const prefixes = | ||
| extraPrefix && !REGION_SUBDOMAIN_PREFIXES.includes(extraPrefix) | ||
| ? [extraPrefix, ...REGION_SUBDOMAIN_PREFIXES] | ||
| : REGION_SUBDOMAIN_PREFIXES; |
There was a problem hiding this comment.
Unlisted source-region prefix not stripped when navigating to a different region
extraPrefix is the subdomain being prepended (the target region), so it only prevents doubling when the hostname already carries the same unlisted region as the destination. If the console is served from an unlisted region (e.g. blr.cloud.appwrite.io) and the user navigates to a different region (e.g. nyc), extraPrefix = 'nyc.' is already in REGION_SUBDOMAIN_PREFIXES, so the original list is used unchanged and blr. is never stripped — the result is nyc.blr.cloud.appwrite.io/v1 instead of nyc.cloud.appwrite.io/v1. This scenario is real when APPWRITE_ENDPOINT is unset and getApiEndpoint in sdk.ts falls back to globalThis.location, picking up the console's own host, which can itself carry a new-region prefix.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/helpers/apiEndpoint.ts
Line: 34-37
Comment:
**Unlisted source-region prefix not stripped when navigating to a different region**
`extraPrefix` is the subdomain being *prepended* (the target region), so it only prevents doubling when the hostname already carries the same unlisted region as the destination. If the console is served from an unlisted region (e.g. `blr.cloud.appwrite.io`) and the user navigates to a *different* region (e.g. `nyc`), `extraPrefix = 'nyc.'` is already in `REGION_SUBDOMAIN_PREFIXES`, so the original list is used unchanged and `blr.` is never stripped — the result is `nyc.blr.cloud.appwrite.io/v1` instead of `nyc.cloud.appwrite.io/v1`. This scenario is real when `APPWRITE_ENDPOINT` is unset and `getApiEndpoint` in `sdk.ts` falls back to `globalThis.location`, picking up the console's own host, which can itself carry a new-region prefix.
How can I resolve this? If you propose a fix, please make it concise.| it('prepends the regional subdomain for a region outside the hardcoded set', () => { | ||
| // apex host | ||
| expect(buildRegionalV1Endpoint('https:', 'cloud.appwrite.io', 'blr', true)).toBe( | ||
| 'https://blr.cloud.appwrite.io/v1' | ||
| ); | ||
| // host that already carries a known region label | ||
| expect(buildRegionalV1Endpoint('https:', 'fra.cloud.appwrite.io', 'blr', true)).toBe( | ||
| 'https://blr.cloud.appwrite.io/v1' | ||
| ); | ||
| }); | ||
|
|
||
| it('does not double the subdomain when the host already carries the target region', () => { | ||
| expect(buildRegionalV1Endpoint('https:', 'blr.cloud.appwrite.io', 'blr', true)).toBe( | ||
| 'https://blr.cloud.appwrite.io/v1' | ||
| ); |
There was a problem hiding this comment.
Missing test for unlisted source region with a different target region
The existing buildRegionalV1Endpoint tests cover fra → blr and blr → blr, but not blr → nyc (unlisted source region, different listed target region). Adding a case like buildRegionalV1Endpoint('https:', 'blr.cloud.appwrite.io', 'nyc', true) and asserting it returns https://nyc.cloud.appwrite.io/v1 would pin down the behaviour described above and catch any future regression in the stripping logic.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/helpers/apiEndpoint.test.ts
Line: 53-67
Comment:
**Missing test for unlisted source region with a different target region**
The existing `buildRegionalV1Endpoint` tests cover `fra → blr` and `blr → blr`, but not `blr → nyc` (unlisted source region, different listed target region). Adding a case like `buildRegionalV1Endpoint('https:', 'blr.cloud.appwrite.io', 'nyc', true)` and asserting it returns `https://nyc.cloud.appwrite.io/v1` would pin down the behaviour described above and catch any future regression in the stripping logic.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Fix Confidence: 80/100The mechanism is well-supported: a hardcoded region→subdomain switch means projects in newer/unlisted regions route to the apex domain, exactly matching the reported CORS-and-stuck symptom that works in impersonation (which targets the correct regional endpoint). The fix is minimal, backward-compatible, and CI's build job (which runs bun test:unit + type-check + lint + build) passed, so the new tests pass. Confidence is not higher because I could not reproduce the exact failing region in a live environment (sandbox can't install deps / no confirmed region id from the reporter), and the issue text left open that the true fault could be in cloud/appwrite backend CORS config rather than the console. |
| @@ -1,10 +1,4 @@ | |||
| import { | |||
There was a problem hiding this comment.
@claudear very bad way of fixing it here. No requests were made out of these regions. So error is somewhere else
What / Why
Reported via Discord (DISCORD-15265968): clicking a project in the organization view gets stuck, while the same action works from the internal impersonation tooling. Another user saw the browser throw CORS for the same flow.
Root cause
getRegionSubdomain()insrc/lib/helpers/apiEndpoint.tsresolved a project's regional subdomain through a hardcodedswitchthat only knewfra/syd/nyc/sfo/sgp/tor. Any project hosted in a region outside that list fell through to the empty default, sobuildRegionalV1Endpoint()left the hostname unchanged and the SDK issued requests to the apex host (or whatever region is baked intoAPPWRITE_ENDPOINT) instead of the project's real regional host, e.g.blr.cloud.appwrite.io.Those cross-origin requests failed the CORS preflight and the navigation hung. The impersonation tooling opens the project against its correct regional endpoint directly, which is why it was unaffected.
The set of Appwrite Cloud regions is dynamic and grows over time, so a hardcoded list is inherently fragile — every new region silently breaks project navigation until the switch is updated.
Fix
`${region}.`) so any current or future region routes correctly. Thedefaultplaceholder (and empty/undefinedvalues) still resolve to the apex domain.stripLeadingRegionSubdomain()now also strips the region that is about to be prepended, preventing a doubled subdomain (blr.blr.cloud…) when the console itself is served from that region.Tests
Added
src/lib/helpers/apiEndpoint.test.tscovering:default/empty/undefined→ apex,The new tests fail against the previous logic (unlisted region → apex → CORS) and pass with this change.
🤖 Generated with Claude Code