From 96de515e56fe0e7826717eaa360b58e4d2f3b068 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 20 Dec 2022 17:26:45 +0000 Subject: [PATCH] Move default homeserver to config file --- src/ClientContext.tsx | 6 +++--- src/auth/LoginPage.tsx | 12 +++++------ src/auth/RegisterPage.tsx | 4 ++-- src/auth/useInteractiveLogin.ts | 4 ++-- src/auth/useInteractiveRegistration.ts | 9 +++++--- src/config/Config.ts | 29 +++++++++++++++++++------- src/config/ConfigOptions.ts | 27 +++++++++++++----------- src/initializer.tsx | 7 ++----- src/matrix-utils.ts | 13 +++++------- src/room/RoomRedirect.tsx | 6 ++++-- src/room/useLoadGroupCall.ts | 2 +- src/usePageTitle.ts | 2 +- vite.config.js | 2 +- 13 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index a48101f..fa83f5d 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -32,7 +32,6 @@ import { useTranslation } from "react-i18next"; import { ErrorView } from "./FullScreenView"; import { initClient, - defaultHomeserver, CryptoStoreIntegrityError, fallbackICEServerAllowed, } from "./matrix-utils"; @@ -40,6 +39,7 @@ import { widget } from "./widget"; import { PosthogAnalytics, RegistrationType } from "./PosthogAnalytics"; import { translatedError } from "./TranslatedError"; import { useEventTarget } from "./useEvents"; +import { Config } from "./config/Config"; declare global { interface Window { @@ -139,7 +139,7 @@ export const ClientProvider: FC = ({ children }) => { return { client: await initClient( { - baseUrl: defaultHomeserver, + baseUrl: Config.defaultHomeserverUrl(), accessToken: access_token, userId: user_id, deviceId: device_id, @@ -155,7 +155,7 @@ export const ClientProvider: FC = ({ children }) => { try { const client = await initClient( { - baseUrl: defaultHomeserver, + baseUrl: Config.defaultHomeserverUrl(), accessToken: access_token, userId: user_id, deviceId: device_id, diff --git a/src/auth/LoginPage.tsx b/src/auth/LoginPage.tsx index 3dc4840..a6b0af9 100644 --- a/src/auth/LoginPage.tsx +++ b/src/auth/LoginPage.tsx @@ -29,11 +29,11 @@ import { ReactComponent as Logo } from "../icons/LogoLarge.svg"; import { useClient } from "../ClientContext"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { Button } from "../button"; -import { defaultHomeserver, defaultHomeserverHost } from "../matrix-utils"; import styles from "./LoginPage.module.css"; import { useInteractiveLogin } from "./useInteractiveLogin"; import { usePageTitle } from "../usePageTitle"; import { PosthogAnalytics } from "../PosthogAnalytics"; +import { Config } from "../config/Config"; export const LoginPage: FC = () => { const { t } = useTranslation(); @@ -41,7 +41,7 @@ export const LoginPage: FC = () => { const { setClient } = useClient(); const login = useInteractiveLogin(); - const homeserver = defaultHomeserver; // TODO: Make this configurable + const homeserver = Config.defaultHomeserverUrl(); // TODO: Make this configurable const usernameRef = useRef(); const passwordRef = useRef(); const history = useHistory(); @@ -76,11 +76,9 @@ export const LoginPage: FC = () => { ); const homeserverHost = useMemo(() => { - try { - return new URL(homeserver).host; - } catch (error) { - return defaultHomeserverHost; - } + // XXX: This isn't really correct: the server name of an HS may not + // be the same as the hostname of the client API endpoint. + return new URL(homeserver).host; }, [homeserver]); return ( diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index 9da1d32..f5025b8 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -31,7 +31,6 @@ import { Trans, useTranslation } from "react-i18next"; import { FieldRow, InputField, ErrorMessage } from "../input/Input"; import { Button } from "../button"; import { useClient } from "../ClientContext"; -import { defaultHomeserverHost } from "../matrix-utils"; import { useInteractiveRegistration } from "./useInteractiveRegistration"; import styles from "./LoginPage.module.css"; import { ReactComponent as Logo } from "../icons/LogoLarge.svg"; @@ -40,6 +39,7 @@ import { useRecaptcha } from "./useRecaptcha"; import { Caption, Link } from "../typography/Typography"; import { usePageTitle } from "../usePageTitle"; import { PosthogAnalytics } from "../PosthogAnalytics"; +import { Config } from "../config/Config"; export const RegisterPage: FC = () => { const { t } = useTranslation(); @@ -165,7 +165,7 @@ export const RegisterPage: FC = () => { autoCorrect="off" autoCapitalize="none" prefix="@" - suffix={`:${defaultHomeserverHost}`} + suffix={`:${Config.defaultServerName()}`} /> diff --git a/src/auth/useInteractiveLogin.ts b/src/auth/useInteractiveLogin.ts index ae0c855..4db466e 100644 --- a/src/auth/useInteractiveLogin.ts +++ b/src/auth/useInteractiveLogin.ts @@ -18,7 +18,7 @@ import { useCallback } from "react"; import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth"; import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; -import { initClient, defaultHomeserver } from "../matrix-utils"; +import { initClient } from "../matrix-utils"; import { Session } from "../ClientContext"; export const useInteractiveLogin = () => @@ -59,7 +59,7 @@ export const useInteractiveLogin = () => const client = await initClient( { - baseUrl: defaultHomeserver, + baseUrl: homeserver, accessToken: access_token, userId: user_id, deviceId: device_id, diff --git a/src/auth/useInteractiveRegistration.ts b/src/auth/useInteractiveRegistration.ts index 6b25c29..bfc7b2a 100644 --- a/src/auth/useInteractiveRegistration.ts +++ b/src/auth/useInteractiveRegistration.ts @@ -18,8 +18,9 @@ import { useState, useEffect, useCallback, useRef } from "react"; import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth"; import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix"; -import { initClient, defaultHomeserver } from "../matrix-utils"; +import { initClient } from "../matrix-utils"; import { Session } from "../ClientContext"; +import { Config } from "../config/Config"; export const useInteractiveRegistration = (): [ string, @@ -37,7 +38,9 @@ export const useInteractiveRegistration = (): [ const authClient = useRef(); if (!authClient.current) { - authClient.current = createClient({ baseUrl: defaultHomeserver }); + authClient.current = createClient({ + baseUrl: Config.defaultHomeserverUrl(), + }); } useEffect(() => { @@ -92,7 +95,7 @@ export const useInteractiveRegistration = (): [ const client = await initClient( { - baseUrl: defaultHomeserver, + baseUrl: Config.defaultHomeserverUrl(), accessToken: access_token, userId: user_id, deviceId: device_id, diff --git a/src/config/Config.ts b/src/config/Config.ts index 2806a5b..af6b8b5 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -14,11 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - DEFAULT_CONFIG, - ConfigOptions, - ResolvedConfigOptions, -} from "./ConfigOptions"; +import { DEFAULT_CONFIG, ConfigOptions } from "./ConfigOptions"; export class Config { private static internalInstance: Config; @@ -41,7 +37,26 @@ export class Config { return Config.internalInstance.initPromise; } - public config: ResolvedConfigOptions; + // Convenience accessors + public static defaultHomeserverUrl(): string | undefined { + const defaultServerConfig = Config.instance.config.default_server_config; + if (!defaultServerConfig) { + return undefined; + } + + return defaultServerConfig["m.homeserver"]?.base_url; + } + + public static defaultServerName(): string | undefined { + const defaultServerConfig = Config.instance.config.default_server_config; + if (!defaultServerConfig) { + return undefined; + } + + return defaultServerConfig["m.homeserver"]?.server_name; + } + + public config: ConfigOptions; private initPromise: Promise; } @@ -59,7 +74,7 @@ async function downloadConfig( // Lack of a config isn't an error, we should just use the defaults. // Also treat a blank config as no config, assuming the status code is 0, because we don't get 404s from file: // URIs so this is the only way we can not fail if the file doesn't exist when loading from a file:// URI. - return {}; + return DEFAULT_CONFIG; } return res.json(); diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts index 92dd74f..bdfbe58 100644 --- a/src/config/ConfigOptions.ts +++ b/src/config/ConfigOptions.ts @@ -19,21 +19,24 @@ export interface ConfigOptions { rageshake?: { submit_url: string; }; -} -export interface ResolvedConfigOptions extends ConfigOptions { - sentry: { - DSN: string; - environment: string; - }; - rageshake: { - submit_url: string; + // Describes the default homeserver to use. The same format as Element Web + // (without identity servers as we don't use them). + default_server_config: { + ["m.homeserver"]: { + base_url: string; + server_name: string; + }; }; } -export const DEFAULT_CONFIG: ResolvedConfigOptions = { - sentry: { DSN: "", environment: "production" }, - rageshake: { - submit_url: "https://element.io/bugreports/submit", +export const DEFAULT_CONFIG: ConfigOptions = { + default_server_config: { + ["m.homeserver"]: { + // These are probably poor guesses - we may want to just not work without + // a config file. + base_url: `${window.location.protocol}//${window.location.host}`, + server_name: window.location.host, + }, }, }; diff --git a/src/initializer.tsx b/src/initializer.tsx index 6290764..bdde17e 100644 --- a/src/initializer.tsx +++ b/src/initializer.tsx @@ -23,7 +23,6 @@ import * as Sentry from "@sentry/react"; import { getUrlParams } from "./UrlParams"; import { Config } from "./config/Config"; -import { DEFAULT_CONFIG } from "./config/ConfigOptions"; enum LoadState { None, @@ -193,10 +192,8 @@ export class Initializer { this.loadStates.config === LoadState.Loaded ) { Sentry.init({ - dsn: Config.instance.config.sentry?.DSN ?? DEFAULT_CONFIG.sentry.DSN, - environment: - Config.instance.config.sentry.environment ?? - DEFAULT_CONFIG.sentry.environment, + dsn: Config.instance.config.sentry?.DSN, + environment: Config.instance.config.sentry?.environment, integrations: [ new Integrations.BrowserTracing({ routingInstrumentation: diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts index fdb5938..2d124a0 100644 --- a/src/matrix-utils.ts +++ b/src/matrix-utils.ts @@ -19,15 +19,11 @@ import type { Room } from "matrix-js-sdk/src/models/room"; import IndexedDBWorker from "./IndexedDBWorker?worker"; import { getUrlParams } from "./UrlParams"; import { loadOlm } from "./olm"; +import { Config } from "./config/Config"; -export const defaultHomeserver = - (import.meta.env.VITE_DEFAULT_HOMESERVER as string) ?? - `${window.location.protocol}//${window.location.host}`; export const fallbackICEServerAllowed = import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true"; -export const defaultHomeserverHost = new URL(defaultHomeserver).host; - export class CryptoStoreIntegrityError extends Error { constructor() { super("Crypto store data was expected, but none was found"); @@ -206,7 +202,7 @@ export function roomNameFromRoomId(roomId: string): string { .toLowerCase(); } -export function isLocalRoomId(roomId: string): boolean { +export function isLocalRoomId(roomId: string, client: MatrixClient): boolean { if (!roomId) { return false; } @@ -217,7 +213,7 @@ export function isLocalRoomId(roomId: string): boolean { return false; } - return parts[1] === defaultHomeserverHost; + return parts[1] === client.getDomain(); } export async function createRoom( @@ -291,11 +287,12 @@ export async function createRoom( return [fullAliasFromRoomName(name, client), result.room_id]; } +// Returns a URL to that will load Element Call with the given room export function getRoomUrl(roomIdOrAlias: string): string { if (roomIdOrAlias.startsWith("#")) { const [localPart, host] = roomIdOrAlias.replace("#", "").split(":"); - if (host !== defaultHomeserverHost) { + if (host !== Config.defaultServerName()) { return `${window.location.protocol}//${window.location.host}/room/${roomIdOrAlias}`; } else { return `${window.location.protocol}//${window.location.host}/${localPart}`; diff --git a/src/room/RoomRedirect.tsx b/src/room/RoomRedirect.tsx index eeef083..f52abf6 100644 --- a/src/room/RoomRedirect.tsx +++ b/src/room/RoomRedirect.tsx @@ -17,9 +17,11 @@ limitations under the License. import React, { useEffect } from "react"; import { useLocation, useHistory } from "react-router-dom"; -import { defaultHomeserverHost } from "../matrix-utils"; +import { Config } from "../config/Config"; import { LoadingView } from "../FullScreenView"; +// A component that, when loaded, redirects the client to a full room URL +// based on the current URL being an abbreviated room URL export function RoomRedirect() { const { pathname } = useLocation(); const history = useHistory(); @@ -32,7 +34,7 @@ export function RoomRedirect() { } if (!roomId.startsWith("#") && !roomId.startsWith("!")) { - roomId = `#${roomId}:${defaultHomeserverHost}`; + roomId = `#${roomId}:${Config.defaultServerName()}`; } history.replace(`/room/${roomId.toLowerCase()}`); diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts index eae23b9..5b2a976 100644 --- a/src/room/useLoadGroupCall.ts +++ b/src/room/useLoadGroupCall.ts @@ -61,7 +61,7 @@ export const useLoadGroupCall = ( return room; } catch (error) { if ( - isLocalRoomId(roomIdOrAlias) && + isLocalRoomId(roomIdOrAlias, client) && (error.errcode === "M_NOT_FOUND" || (error.message && error.message.indexOf("Failed to fetch alias") !== -1)) diff --git a/src/usePageTitle.ts b/src/usePageTitle.ts index 0e1b1a0..f77fee9 100644 --- a/src/usePageTitle.ts +++ b/src/usePageTitle.ts @@ -18,7 +18,7 @@ import { useEffect } from "react"; export function usePageTitle(title: string): void { useEffect(() => { - const productName = import.meta.env.VITE_PRODUCT_NAME || "Element Call"; + const productName = "Element Call"; document.title = title ? `${productName} | ${title}` : productName; }, [title]); } diff --git a/vite.config.js b/vite.config.js index 772bce6..b18ab74 100644 --- a/vite.config.js +++ b/vite.config.js @@ -31,7 +31,7 @@ export default defineConfig(({ mode }) => { svgrPlugin(), htmlTemplate.default({ data: { - title: env.VITE_PRODUCT_NAME || "Element Call", + title: "Element Call", }, }), ],