diff --git a/ROADMAP.md b/ROADMAP.md index d094a1cf..d912eefc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -23,6 +23,7 @@ This document tracks the development status and planned features for JMAP Webmai - [x] Session establishment and keep-alive - [x] Connection error handling and retries - [x] CORS error detection with actionable user guidance +- [x] Session URL origin rewriting (fixes Docker/reverse proxy deployments where server returns internal hostname) - [x] Storage quota display - [x] Server capability detection - [x] Shared folders support (multi-account access) diff --git a/lib/jmap/client.ts b/lib/jmap/client.ts index 9dd34c0c..5ddaa42c 100644 --- a/lib/jmap/client.ts +++ b/lib/jmap/client.ts @@ -102,6 +102,8 @@ export class JMAPClient { const session = await sessionResponse.json(); + this.rewriteSessionUrls(session); + // Store the full session for reference this.session = session; @@ -208,6 +210,28 @@ export class JMAPClient { this.capabilities = {}; } + private rewriteSessionUrl(url: string): string { + try { + const urlOrigin = new URL(url).origin; + const serverOrigin = new URL(this.serverUrl).origin; + if (urlOrigin === serverOrigin) return url; + return serverOrigin + url.slice(urlOrigin.length); + } catch { + return url; + } + } + + private rewriteSessionUrls(session: JMAPSession): void { + session.apiUrl = this.rewriteSessionUrl(session.apiUrl); + session.downloadUrl = this.rewriteSessionUrl(session.downloadUrl); + if (session.uploadUrl) { + session.uploadUrl = this.rewriteSessionUrl(session.uploadUrl); + } + if (session.eventSourceUrl) { + session.eventSourceUrl = this.rewriteSessionUrl(session.eventSourceUrl); + } + } + private async request(methodCalls: JMAPMethodCall[], using?: string[]): Promise { if (!this.apiUrl) { throw new Error('Not connected. Call connect() first.');