Merge pull request #798 from vector-im/dbkr/move_to_config_file
Move default homeserver to config file
This commit is contained in:
commit
51be754ad8
15 changed files with 97 additions and 83 deletions
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
|
@ -3,8 +3,6 @@ on:
|
|||
pull_request: {}
|
||||
push:
|
||||
branches: [main]
|
||||
env:
|
||||
VITE_DEFAULT_HOMESERVER: "https://call.ems.host"
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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()}`);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue