fix: rewrite JMAP session URLs to match user-configured server origin
Fixes Docker/reverse proxy deployments where the JMAP server returns its internal hostname in session URLs, causing all requests after login to fail.
This commit is contained in:
@@ -23,6 +23,7 @@ This document tracks the development status and planned features for JMAP Webmai
|
|||||||
- [x] Session establishment and keep-alive
|
- [x] Session establishment and keep-alive
|
||||||
- [x] Connection error handling and retries
|
- [x] Connection error handling and retries
|
||||||
- [x] CORS error detection with actionable user guidance
|
- [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] Storage quota display
|
||||||
- [x] Server capability detection
|
- [x] Server capability detection
|
||||||
- [x] Shared folders support (multi-account access)
|
- [x] Shared folders support (multi-account access)
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ export class JMAPClient {
|
|||||||
|
|
||||||
const session = await sessionResponse.json();
|
const session = await sessionResponse.json();
|
||||||
|
|
||||||
|
this.rewriteSessionUrls(session);
|
||||||
|
|
||||||
// Store the full session for reference
|
// Store the full session for reference
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
|
||||||
@@ -208,6 +210,28 @@ export class JMAPClient {
|
|||||||
this.capabilities = {};
|
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<JMAPResponse> {
|
private async request(methodCalls: JMAPMethodCall[], using?: string[]): Promise<JMAPResponse> {
|
||||||
if (!this.apiUrl) {
|
if (!this.apiUrl) {
|
||||||
throw new Error('Not connected. Call connect() first.');
|
throw new Error('Not connected. Call connect() first.');
|
||||||
|
|||||||
Reference in New Issue
Block a user