Merge pull request #798 from vector-im/dbkr/move_to_config_file

Move default homeserver to config file
This commit is contained in:
David Baker 2022-12-21 17:57:16 +00:00 committed by GitHub
commit 51be754ad8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 97 additions and 83 deletions

View file

@ -3,8 +3,6 @@ on:
pull_request: {}
push:
branches: [main]
env:
VITE_DEFAULT_HOMESERVER: "https://call.ems.host"
jobs:
build:
name: Build

View file

@ -1,4 +1,10 @@
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://call.ems.host",
"server_name": "call.ems.host"
}
},
"posthog": {
"api_key": "phc_MhClVy9DiV20vazSYIiedFkM5Xi3z1LPBwrdn9PYZQQ",
"api_host": "https://app.posthog.com"

View file

@ -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<Props> = ({ 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<Props> = ({ children }) => {
try {
const client = await initClient(
{
baseUrl: defaultHomeserver,
baseUrl: Config.defaultHomeserverUrl(),
accessToken: access_token,
userId: user_id,
deviceId: device_id,

View file

@ -114,8 +114,8 @@ export class PosthogAnalytics {
constructor(private readonly posthog: PostHog) {
const posthogConfig: PosthogSettings = {
project_api_key: Config.instance.config.posthog?.api_key,
api_host: Config.instance.config.posthog?.api_host,
project_api_key: Config.get().posthog?.api_key,
api_host: Config.get().posthog?.api_host,
};
if (posthogConfig.project_api_key && posthogConfig.api_host) {

View file

@ -14,14 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {
FC,
FormEvent,
useCallback,
useRef,
useState,
useMemo,
} from "react";
import React, { FC, FormEvent, useCallback, useRef, useState } from "react";
import { useHistory, useLocation, Link } from "react-router-dom";
import { Trans, useTranslation } from "react-i18next";
@ -29,11 +22,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 +34,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<HTMLInputElement>();
const passwordRef = useRef<HTMLInputElement>();
const history = useHistory();
@ -75,14 +68,6 @@ export const LoginPage: FC = () => {
[login, location, history, homeserver, setClient]
);
const homeserverHost = useMemo(() => {
try {
return new URL(homeserver).host;
} catch (error) {
return defaultHomeserverHost;
}
}, [homeserver]);
return (
<>
<div className={styles.container}>
@ -102,7 +87,7 @@ export const LoginPage: FC = () => {
autoCorrect="off"
autoCapitalize="none"
prefix="@"
suffix={`:${homeserverHost}`}
suffix={`:${Config.defaultServerName()}`}
/>
</FieldRow>
<FieldRow>

View file

@ -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()}`}
/>
</FieldRow>
<FieldRow>

View file

@ -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,

View file

@ -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<MatrixClient>();
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,

View file

@ -22,13 +22,15 @@ import {
export class Config {
private static internalInstance: Config;
public static get instance(): Config {
if (!this.internalInstance)
public static get(): ConfigOptions {
if (!this.internalInstance?.config)
throw new Error("Config instance read before config got initialized");
return this.internalInstance;
return this.internalInstance.config;
}
public static init(): Promise<void> {
if (Config?.internalInstance?.initPromise) {
if (Config.internalInstance?.initPromise) {
return Config.internalInstance.initPromise;
}
Config.internalInstance = new Config();
@ -41,8 +43,17 @@ export class Config {
return Config.internalInstance.initPromise;
}
public config: ResolvedConfigOptions;
private initPromise: Promise<void>;
// Convenience accessors
public static defaultHomeserverUrl(): string | undefined {
return Config.get().default_server_config["m.homeserver"].base_url;
}
public static defaultServerName(): string | undefined {
return Config.get().default_server_config["m.homeserver"].server_name;
}
public config?: ResolvedConfigOptions;
private initPromise?: Promise<void>;
}
async function downloadConfig(
@ -59,7 +70,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();

View file

@ -19,21 +19,33 @@ export interface ConfigOptions {
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;
};
};
}
// Overrides members from ConfigOptions that are always provided by the
// default config and are therefore non-optional.
export interface ResolvedConfigOptions extends ConfigOptions {
sentry: {
DSN: string;
environment: string;
};
rageshake: {
submit_url: string;
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",
default_server_config: {
["m.homeserver"]: {
base_url: "http://localhost:8008",
server_name: "localhost",
},
},
};

View file

@ -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,
@ -192,19 +191,21 @@ export class Initializer {
this.loadStates.sentry === LoadState.None &&
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,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation:
Sentry.reactRouterV5Instrumentation(history),
}),
],
tracesSampleRate: 1.0,
});
if (Config.get().sentry?.DSN && Config.get().sentry?.environment) {
Sentry.init({
dsn: Config.get().sentry?.DSN,
environment: Config.get().sentry?.environment,
integrations: [
new Integrations.BrowserTracing({
routingInstrumentation:
Sentry.reactRouterV5Instrumentation(history),
}),
],
tracesSampleRate: 1.0,
});
}
// Sentry is now 'loadeed' (even if we actually skipped starting
// it due to to not being configured)
this.loadStates.sentry = LoadState.Loaded;
}

View file

@ -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}`;

View file

@ -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()}`);

View file

@ -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))

View file

@ -25,7 +25,6 @@ import { useClient } from "../ClientContext";
import { InspectorContext } from "../room/GroupCallInspector";
import { useModalTriggerState } from "../Modal";
import { Config } from "../config/Config";
import { DEFAULT_CONFIG } from "../config/ConfigOptions";
interface RageShakeSubmitOptions {
sendLogs: boolean;
@ -54,6 +53,10 @@ export function useSubmitRageshake(): {
const submitRageshake = useCallback(
async (opts) => {
if (!Config.get().rageshake?.submit_url) {
throw new Error("No rageshake URL is configured");
}
if (sending) {
return;
}
@ -258,14 +261,10 @@ export function useSubmitRageshake(): {
);
}
await fetch(
Config.instance.config.rageshake?.submit_url ??
DEFAULT_CONFIG.rageshake.submit_url,
{
method: "POST",
body,
}
);
await fetch(Config.get().rageshake?.submit_url, {
method: "POST",
body,
});
setState({ sending: false, sent: true, error: null });
} catch (error) {