2026-09-26 23:24:24 -04:00
|
|
|
/**
|
|
|
|
|
* [Gitea #43] Where the Element Call page is loaded from.
|
|
|
|
|
*
|
|
|
|
|
* By default it's the copy bundled with this app (same origin). With
|
|
|
|
|
* `elementCallUrl` in config.json (e.g.
|
|
|
|
|
* "https://call.chat.lotusguild.org/public/element-call/index.html") the web
|
|
|
|
|
* app loads it from that origin instead, so the call frame can no longer
|
|
|
|
|
* reach this origin's storage (login token, crypto store) or service worker.
|
|
|
|
|
*
|
2026-09-29 00:09:27 -04:00
|
|
|
* Web only: the desktop app keeps its bundled copy (a network copy could
|
|
|
|
|
* drift from the bundle); see resolveDesktopCallPageUrl for how it isolates
|
|
|
|
|
* it. Anything that isn't an absolute https URL (http only on localhost, for
|
2026-09-26 23:24:24 -04:00
|
|
|
* development) is ignored, so a bad value falls
|
|
|
|
|
* back to the bundled page instead of breaking calls.
|
|
|
|
|
*/
|
|
|
|
|
export const resolveCallPageUrl = (value: unknown, desktop: boolean): string | undefined => {
|
|
|
|
|
if (desktop || typeof value !== 'string' || value.trim() === '') return undefined;
|
|
|
|
|
try {
|
|
|
|
|
const url = new URL(value);
|
|
|
|
|
// http only for local development (localhost is a secure context).
|
|
|
|
|
const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1';
|
|
|
|
|
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && local)) return undefined;
|
|
|
|
|
url.search = '';
|
|
|
|
|
url.hash = '';
|
|
|
|
|
return url.href;
|
|
|
|
|
} catch {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-29 00:09:27 -04:00
|
|
|
/**
|
|
|
|
|
* [Gitea #43] Desktop: the bundled call page from a second origin.
|
|
|
|
|
*
|
|
|
|
|
* The desktop app is served by its local server at http://localhost:<port>.
|
|
|
|
|
* The same server answers on http://127.0.0.1:<port>, which is a different
|
|
|
|
|
* origin (and still a secure context), so loading the bundled call page from
|
|
|
|
|
* there cuts the call frame off from the app's storage (login token, crypto
|
|
|
|
|
* store) without a network copy that could drift from the bundle.
|
|
|
|
|
*
|
|
|
|
|
* Only used when cinny-desktop sets `desktopCallOrigin` (it ships the server
|
|
|
|
|
* and CSP changes this needs in the same release), only for a loopback http
|
|
|
|
|
* origin on the SAME port as the app, and only when the app itself runs on
|
|
|
|
|
* http://localhost (release builds). Anything else keeps the same-origin page.
|
|
|
|
|
*/
|
|
|
|
|
export const resolveDesktopCallPageUrl = (
|
|
|
|
|
value: unknown,
|
|
|
|
|
appOrigin: string,
|
|
|
|
|
basePath: string,
|
|
|
|
|
): string | undefined => {
|
|
|
|
|
if (typeof value !== 'string' || value.trim() === '') return undefined;
|
|
|
|
|
try {
|
|
|
|
|
const app = new URL(appOrigin);
|
|
|
|
|
const call = new URL(value);
|
|
|
|
|
if (app.protocol !== 'http:' || app.hostname !== 'localhost' || !app.port) return undefined;
|
|
|
|
|
if (call.protocol !== 'http:' || call.hostname !== '127.0.0.1') return undefined;
|
|
|
|
|
if (call.port !== app.port || call.username || call.password) return undefined;
|
|
|
|
|
if (call.pathname !== '/' || call.search || call.hash) return undefined;
|
|
|
|
|
const base = basePath.replace(/\/+$/, '');
|
|
|
|
|
return `${call.origin}${base}/public/element-call/index.html`;
|
|
|
|
|
} catch {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-26 23:24:24 -04:00
|
|
|
let callPageUrl: string | undefined;
|
|
|
|
|
|
|
|
|
|
export const setCallPageUrl = (url: string | undefined): void => {
|
|
|
|
|
callPageUrl = url;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getCallPageUrl = (): string | undefined => callPageUrl;
|