Move default homeserver to config file
This commit is contained in:
parent
282a4853cf
commit
96de515e56
13 changed files with 69 additions and 54 deletions
|
@ -32,7 +32,6 @@ import { useTranslation } from "react-i18next";
|
||||||
import { ErrorView } from "./FullScreenView";
|
import { ErrorView } from "./FullScreenView";
|
||||||
import {
|
import {
|
||||||
initClient,
|
initClient,
|
||||||
defaultHomeserver,
|
|
||||||
CryptoStoreIntegrityError,
|
CryptoStoreIntegrityError,
|
||||||
fallbackICEServerAllowed,
|
fallbackICEServerAllowed,
|
||||||
} from "./matrix-utils";
|
} from "./matrix-utils";
|
||||||
|
@ -40,6 +39,7 @@ import { widget } from "./widget";
|
||||||
import { PosthogAnalytics, RegistrationType } from "./PosthogAnalytics";
|
import { PosthogAnalytics, RegistrationType } from "./PosthogAnalytics";
|
||||||
import { translatedError } from "./TranslatedError";
|
import { translatedError } from "./TranslatedError";
|
||||||
import { useEventTarget } from "./useEvents";
|
import { useEventTarget } from "./useEvents";
|
||||||
|
import { Config } from "./config/Config";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -139,7 +139,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
return {
|
return {
|
||||||
client: await initClient(
|
client: await initClient(
|
||||||
{
|
{
|
||||||
baseUrl: defaultHomeserver,
|
baseUrl: Config.defaultHomeserverUrl(),
|
||||||
accessToken: access_token,
|
accessToken: access_token,
|
||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
|
@ -155,7 +155,7 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
try {
|
try {
|
||||||
const client = await initClient(
|
const client = await initClient(
|
||||||
{
|
{
|
||||||
baseUrl: defaultHomeserver,
|
baseUrl: Config.defaultHomeserverUrl(),
|
||||||
accessToken: access_token,
|
accessToken: access_token,
|
||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
|
|
|
@ -29,11 +29,11 @@ import { ReactComponent as Logo } from "../icons/LogoLarge.svg";
|
||||||
import { useClient } from "../ClientContext";
|
import { useClient } from "../ClientContext";
|
||||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||||
import { Button } from "../button";
|
import { Button } from "../button";
|
||||||
import { defaultHomeserver, defaultHomeserverHost } from "../matrix-utils";
|
|
||||||
import styles from "./LoginPage.module.css";
|
import styles from "./LoginPage.module.css";
|
||||||
import { useInteractiveLogin } from "./useInteractiveLogin";
|
import { useInteractiveLogin } from "./useInteractiveLogin";
|
||||||
import { usePageTitle } from "../usePageTitle";
|
import { usePageTitle } from "../usePageTitle";
|
||||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||||
|
import { Config } from "../config/Config";
|
||||||
|
|
||||||
export const LoginPage: FC = () => {
|
export const LoginPage: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -41,7 +41,7 @@ export const LoginPage: FC = () => {
|
||||||
|
|
||||||
const { setClient } = useClient();
|
const { setClient } = useClient();
|
||||||
const login = useInteractiveLogin();
|
const login = useInteractiveLogin();
|
||||||
const homeserver = defaultHomeserver; // TODO: Make this configurable
|
const homeserver = Config.defaultHomeserverUrl(); // TODO: Make this configurable
|
||||||
const usernameRef = useRef<HTMLInputElement>();
|
const usernameRef = useRef<HTMLInputElement>();
|
||||||
const passwordRef = useRef<HTMLInputElement>();
|
const passwordRef = useRef<HTMLInputElement>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -76,11 +76,9 @@ export const LoginPage: FC = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const homeserverHost = useMemo(() => {
|
const homeserverHost = useMemo(() => {
|
||||||
try {
|
// 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;
|
return new URL(homeserver).host;
|
||||||
} catch (error) {
|
|
||||||
return defaultHomeserverHost;
|
|
||||||
}
|
|
||||||
}, [homeserver]);
|
}, [homeserver]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -31,7 +31,6 @@ import { Trans, useTranslation } from "react-i18next";
|
||||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||||
import { Button } from "../button";
|
import { Button } from "../button";
|
||||||
import { useClient } from "../ClientContext";
|
import { useClient } from "../ClientContext";
|
||||||
import { defaultHomeserverHost } from "../matrix-utils";
|
|
||||||
import { useInteractiveRegistration } from "./useInteractiveRegistration";
|
import { useInteractiveRegistration } from "./useInteractiveRegistration";
|
||||||
import styles from "./LoginPage.module.css";
|
import styles from "./LoginPage.module.css";
|
||||||
import { ReactComponent as Logo } from "../icons/LogoLarge.svg";
|
import { ReactComponent as Logo } from "../icons/LogoLarge.svg";
|
||||||
|
@ -40,6 +39,7 @@ import { useRecaptcha } from "./useRecaptcha";
|
||||||
import { Caption, Link } from "../typography/Typography";
|
import { Caption, Link } from "../typography/Typography";
|
||||||
import { usePageTitle } from "../usePageTitle";
|
import { usePageTitle } from "../usePageTitle";
|
||||||
import { PosthogAnalytics } from "../PosthogAnalytics";
|
import { PosthogAnalytics } from "../PosthogAnalytics";
|
||||||
|
import { Config } from "../config/Config";
|
||||||
|
|
||||||
export const RegisterPage: FC = () => {
|
export const RegisterPage: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -165,7 +165,7 @@ export const RegisterPage: FC = () => {
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
prefix="@"
|
prefix="@"
|
||||||
suffix={`:${defaultHomeserverHost}`}
|
suffix={`:${Config.defaultServerName()}`}
|
||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { useCallback } from "react";
|
||||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
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 { Session } from "../ClientContext";
|
||||||
|
|
||||||
export const useInteractiveLogin = () =>
|
export const useInteractiveLogin = () =>
|
||||||
|
@ -59,7 +59,7 @@ export const useInteractiveLogin = () =>
|
||||||
|
|
||||||
const client = await initClient(
|
const client = await initClient(
|
||||||
{
|
{
|
||||||
baseUrl: defaultHomeserver,
|
baseUrl: homeserver,
|
||||||
accessToken: access_token,
|
accessToken: access_token,
|
||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
|
|
|
@ -18,8 +18,9 @@ import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
|
||||||
import { createClient, MatrixClient } from "matrix-js-sdk/src/matrix";
|
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 { Session } from "../ClientContext";
|
||||||
|
import { Config } from "../config/Config";
|
||||||
|
|
||||||
export const useInteractiveRegistration = (): [
|
export const useInteractiveRegistration = (): [
|
||||||
string,
|
string,
|
||||||
|
@ -37,7 +38,9 @@ export const useInteractiveRegistration = (): [
|
||||||
|
|
||||||
const authClient = useRef<MatrixClient>();
|
const authClient = useRef<MatrixClient>();
|
||||||
if (!authClient.current) {
|
if (!authClient.current) {
|
||||||
authClient.current = createClient({ baseUrl: defaultHomeserver });
|
authClient.current = createClient({
|
||||||
|
baseUrl: Config.defaultHomeserverUrl(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -92,7 +95,7 @@ export const useInteractiveRegistration = (): [
|
||||||
|
|
||||||
const client = await initClient(
|
const client = await initClient(
|
||||||
{
|
{
|
||||||
baseUrl: defaultHomeserver,
|
baseUrl: Config.defaultHomeserverUrl(),
|
||||||
accessToken: access_token,
|
accessToken: access_token,
|
||||||
userId: user_id,
|
userId: user_id,
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
|
|
|
@ -14,11 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { DEFAULT_CONFIG, ConfigOptions } from "./ConfigOptions";
|
||||||
DEFAULT_CONFIG,
|
|
||||||
ConfigOptions,
|
|
||||||
ResolvedConfigOptions,
|
|
||||||
} from "./ConfigOptions";
|
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private static internalInstance: Config;
|
private static internalInstance: Config;
|
||||||
|
@ -41,7 +37,26 @@ export class Config {
|
||||||
return Config.internalInstance.initPromise;
|
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<void>;
|
private initPromise: Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +74,7 @@ async function downloadConfig(
|
||||||
// Lack of a config isn't an error, we should just use the defaults.
|
// 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:
|
// 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.
|
// 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();
|
return res.json();
|
||||||
|
|
|
@ -19,21 +19,24 @@ export interface ConfigOptions {
|
||||||
rageshake?: {
|
rageshake?: {
|
||||||
submit_url: string;
|
submit_url: string;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResolvedConfigOptions extends ConfigOptions {
|
// Describes the default homeserver to use. The same format as Element Web
|
||||||
sentry: {
|
// (without identity servers as we don't use them).
|
||||||
DSN: string;
|
default_server_config: {
|
||||||
environment: string;
|
["m.homeserver"]: {
|
||||||
|
base_url: string;
|
||||||
|
server_name: string;
|
||||||
};
|
};
|
||||||
rageshake: {
|
|
||||||
submit_url: string;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
export const DEFAULT_CONFIG: ConfigOptions = {
|
||||||
sentry: { DSN: "", environment: "production" },
|
default_server_config: {
|
||||||
rageshake: {
|
["m.homeserver"]: {
|
||||||
submit_url: "https://element.io/bugreports/submit",
|
// 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,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,6 @@ import * as Sentry from "@sentry/react";
|
||||||
|
|
||||||
import { getUrlParams } from "./UrlParams";
|
import { getUrlParams } from "./UrlParams";
|
||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
import { DEFAULT_CONFIG } from "./config/ConfigOptions";
|
|
||||||
|
|
||||||
enum LoadState {
|
enum LoadState {
|
||||||
None,
|
None,
|
||||||
|
@ -193,10 +192,8 @@ export class Initializer {
|
||||||
this.loadStates.config === LoadState.Loaded
|
this.loadStates.config === LoadState.Loaded
|
||||||
) {
|
) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: Config.instance.config.sentry?.DSN ?? DEFAULT_CONFIG.sentry.DSN,
|
dsn: Config.instance.config.sentry?.DSN,
|
||||||
environment:
|
environment: Config.instance.config.sentry?.environment,
|
||||||
Config.instance.config.sentry.environment ??
|
|
||||||
DEFAULT_CONFIG.sentry.environment,
|
|
||||||
integrations: [
|
integrations: [
|
||||||
new Integrations.BrowserTracing({
|
new Integrations.BrowserTracing({
|
||||||
routingInstrumentation:
|
routingInstrumentation:
|
||||||
|
|
|
@ -19,15 +19,11 @@ import type { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import IndexedDBWorker from "./IndexedDBWorker?worker";
|
import IndexedDBWorker from "./IndexedDBWorker?worker";
|
||||||
import { getUrlParams } from "./UrlParams";
|
import { getUrlParams } from "./UrlParams";
|
||||||
import { loadOlm } from "./olm";
|
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 =
|
export const fallbackICEServerAllowed =
|
||||||
import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true";
|
import.meta.env.VITE_FALLBACK_STUN_ALLOWED === "true";
|
||||||
|
|
||||||
export const defaultHomeserverHost = new URL(defaultHomeserver).host;
|
|
||||||
|
|
||||||
export class CryptoStoreIntegrityError extends Error {
|
export class CryptoStoreIntegrityError extends Error {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Crypto store data was expected, but none was found");
|
super("Crypto store data was expected, but none was found");
|
||||||
|
@ -206,7 +202,7 @@ export function roomNameFromRoomId(roomId: string): string {
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isLocalRoomId(roomId: string): boolean {
|
export function isLocalRoomId(roomId: string, client: MatrixClient): boolean {
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -217,7 +213,7 @@ export function isLocalRoomId(roomId: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts[1] === defaultHomeserverHost;
|
return parts[1] === client.getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createRoom(
|
export async function createRoom(
|
||||||
|
@ -291,11 +287,12 @@ export async function createRoom(
|
||||||
return [fullAliasFromRoomName(name, client), result.room_id];
|
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 {
|
export function getRoomUrl(roomIdOrAlias: string): string {
|
||||||
if (roomIdOrAlias.startsWith("#")) {
|
if (roomIdOrAlias.startsWith("#")) {
|
||||||
const [localPart, host] = roomIdOrAlias.replace("#", "").split(":");
|
const [localPart, host] = roomIdOrAlias.replace("#", "").split(":");
|
||||||
|
|
||||||
if (host !== defaultHomeserverHost) {
|
if (host !== Config.defaultServerName()) {
|
||||||
return `${window.location.protocol}//${window.location.host}/room/${roomIdOrAlias}`;
|
return `${window.location.protocol}//${window.location.host}/room/${roomIdOrAlias}`;
|
||||||
} else {
|
} else {
|
||||||
return `${window.location.protocol}//${window.location.host}/${localPart}`;
|
return `${window.location.protocol}//${window.location.host}/${localPart}`;
|
||||||
|
|
|
@ -17,9 +17,11 @@ limitations under the License.
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useLocation, useHistory } from "react-router-dom";
|
import { useLocation, useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import { defaultHomeserverHost } from "../matrix-utils";
|
import { Config } from "../config/Config";
|
||||||
import { LoadingView } from "../FullScreenView";
|
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() {
|
export function RoomRedirect() {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -32,7 +34,7 @@ export function RoomRedirect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!roomId.startsWith("#") && !roomId.startsWith("!")) {
|
if (!roomId.startsWith("#") && !roomId.startsWith("!")) {
|
||||||
roomId = `#${roomId}:${defaultHomeserverHost}`;
|
roomId = `#${roomId}:${Config.defaultServerName()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.replace(`/room/${roomId.toLowerCase()}`);
|
history.replace(`/room/${roomId.toLowerCase()}`);
|
||||||
|
|
|
@ -61,7 +61,7 @@ export const useLoadGroupCall = (
|
||||||
return room;
|
return room;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
isLocalRoomId(roomIdOrAlias) &&
|
isLocalRoomId(roomIdOrAlias, client) &&
|
||||||
(error.errcode === "M_NOT_FOUND" ||
|
(error.errcode === "M_NOT_FOUND" ||
|
||||||
(error.message &&
|
(error.message &&
|
||||||
error.message.indexOf("Failed to fetch alias") !== -1))
|
error.message.indexOf("Failed to fetch alias") !== -1))
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { useEffect } from "react";
|
||||||
|
|
||||||
export function usePageTitle(title: string): void {
|
export function usePageTitle(title: string): void {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const productName = import.meta.env.VITE_PRODUCT_NAME || "Element Call";
|
const productName = "Element Call";
|
||||||
document.title = title ? `${productName} | ${title}` : productName;
|
document.title = title ? `${productName} | ${title}` : productName;
|
||||||
}, [title]);
|
}, [title]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default defineConfig(({ mode }) => {
|
||||||
svgrPlugin(),
|
svgrPlugin(),
|
||||||
htmlTemplate.default({
|
htmlTemplate.default({
|
||||||
data: {
|
data: {
|
||||||
title: env.VITE_PRODUCT_NAME || "Element Call",
|
title: "Element Call",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue