mirror of
https://github.com/vector-im/element-call.git
synced 2026-07-18 18:59:23 +00:00
For discussion
This commit is contained in:
@@ -72,8 +72,12 @@
|
|||||||
},
|
},
|
||||||
"disconnected_banner": "Connectivity to the server has been lost.",
|
"disconnected_banner": "Connectivity to the server has been lost.",
|
||||||
"error": {
|
"error": {
|
||||||
"auth_connection_failed_details": "<0>The application could not reach the call authentication service at <2>{{url}}</2>. If you are the server admin, check the network logs and make sure <5>lk-jwt-service</5> is listening at that address.</0>",
|
"configuration_error": "Configuration error",
|
||||||
"auth_connection_rejected_details": "<0>The application connected to the call authentication service at <2>{{url}}</2>, but it responded with status code {{status}} ({{response}}). If you are the server admin, make sure <10>lk-jwt-service</10> is listening at that address and check the logs for more information.</0>",
|
"configuration_error_description": "There is a configuration issues with the system. Please contact your administrator.",
|
||||||
|
"server_error": "Server error",
|
||||||
|
"server_error_description": "There is a server issue with the system. Please try again and contact your administrator if the problem persists.",
|
||||||
|
"network_error": "Network error",
|
||||||
|
"network_error_description": "There is a network issue with the system. Please check your network connection and try again. Alternatively try a different network if available.",
|
||||||
"call_not_found": "Call not found",
|
"call_not_found": "Call not found",
|
||||||
"call_not_found_description": "<0>That link doesn't appear to belong to any existing call. Check that you have the right link, or <1>create a new one</1>.</0>",
|
"call_not_found_description": "<0>That link doesn't appear to belong to any existing call. Check that you have the right link, or <1>create a new one</1>.</0>",
|
||||||
"connection_failed": "Connection failed",
|
"connection_failed": "Connection failed",
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
ErrorIcon,
|
||||||
OfflineIcon,
|
OfflineIcon,
|
||||||
PopOutIcon,
|
PopOutIcon,
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import { Button, Link } from "@vector-im/compound-web";
|
import { Button } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { ErrorView } from "./ErrorView";
|
import { ErrorView } from "./ErrorView";
|
||||||
|
|
||||||
@@ -58,40 +59,22 @@ export class OpenElsewhereError extends RichError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthConnectionFailedProps {
|
interface ConfigurationErrorViewProps {
|
||||||
livekitServiceUrl: string;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthConnectionFailed: FC<AuthConnectionFailedProps> = ({
|
const ConfigurationErrorView: FC<ConfigurationErrorViewProps> = ({
|
||||||
livekitServiceUrl,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
const onShowDetailsClick = useCallback(() => setShowDetails(true), []);
|
const onShowDetailsClick = useCallback(() => setShowDetails(true), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorView Icon={OfflineIcon} title={t("error.connection_failed")}>
|
<ErrorView Icon={ErrorIcon} title={t("error.configuration_error")}>
|
||||||
<p>{t("error.connection_failed_description")}</p>
|
<p>{t("error.configuration_error_description")}</p>
|
||||||
{showDetails ? (
|
{showDetails ? (
|
||||||
<Trans
|
children
|
||||||
i18nKey="error.auth_connection_failed_details"
|
|
||||||
url={livekitServiceUrl}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
The application could not reach the call authentication service at{" "}
|
|
||||||
<Link href={livekitServiceUrl} target="_blank">
|
|
||||||
{{ url: livekitServiceUrl } as unknown as ReactElement}
|
|
||||||
</Link>
|
|
||||||
. If you are the server admin, check the network logs and make sure{" "}
|
|
||||||
<Link
|
|
||||||
href="https://github.com/element-hq/lk-jwt-service/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
lk-jwt-service
|
|
||||||
</Link>{" "}
|
|
||||||
is listening at that address.
|
|
||||||
</p>
|
|
||||||
</Trans>
|
|
||||||
) : (
|
) : (
|
||||||
<Button kind="tertiary" onClick={onShowDetailsClick}>
|
<Button kind="tertiary" onClick={onShowDetailsClick}>
|
||||||
{t("error.show_details")}
|
{t("error.show_details")}
|
||||||
@@ -101,82 +84,186 @@ const AuthConnectionFailed: FC<AuthConnectionFailedProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class AuthConnectionFailedError extends RichError {
|
interface NetworkErrorViewProps {
|
||||||
public constructor(livekitServiceUrl: string, cause?: unknown) {
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NetworkErrorView: FC<NetworkErrorViewProps> = ({ children }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
const onShowDetailsClick = useCallback(() => setShowDetails(true), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorView Icon={OfflineIcon} title={t("error.network_error")}>
|
||||||
|
<p>{t("error.network_error_description")}</p>
|
||||||
|
{showDetails ? (
|
||||||
|
children
|
||||||
|
) : (
|
||||||
|
<Button kind="tertiary" onClick={onShowDetailsClick}>
|
||||||
|
{t("error.show_details")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ErrorView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ServerErrorViewProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServerErrorView: FC<ServerErrorViewProps> = ({ children }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
const onShowDetailsClick = useCallback(() => setShowDetails(true), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorView Icon={ErrorIcon} title={t("error.server_error")}>
|
||||||
|
<p>{t("error.server_error_description")}</p>
|
||||||
|
{showDetails ? (
|
||||||
|
children
|
||||||
|
) : (
|
||||||
|
<Button kind="tertiary" onClick={onShowDetailsClick}>
|
||||||
|
{t("error.show_details")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ErrorView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ConfigurationError extends RichError {
|
||||||
|
public constructor(message: string, richMessage: ReactNode, cause?: unknown) {
|
||||||
super(
|
super(
|
||||||
`Failed to connect to ${livekitServiceUrl}`,
|
message,
|
||||||
<AuthConnectionFailed livekitServiceUrl={livekitServiceUrl} />,
|
<ConfigurationErrorView>{richMessage}</ConfigurationErrorView>,
|
||||||
cause,
|
cause,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AuthConnectionRejectedProps {
|
export class NetworkError extends RichError {
|
||||||
livekitServiceUrl: string;
|
public constructor(message: string, richMessage: ReactNode, cause?: unknown) {
|
||||||
status: number;
|
super(message, <NetworkErrorView>{richMessage}</NetworkErrorView>, cause);
|
||||||
response: string;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthConnectionRejected: FC<AuthConnectionRejectedProps> = ({
|
export class ServerError extends RichError {
|
||||||
livekitServiceUrl,
|
public constructor(message: string, richMessage: ReactNode, cause?: unknown) {
|
||||||
status,
|
super(message, <ServerErrorView>{richMessage}</ServerErrorView>, cause);
|
||||||
response,
|
}
|
||||||
}) => {
|
}
|
||||||
const { t } = useTranslation();
|
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
|
||||||
const onShowDetailsClick = useCallback(() => setShowDetails(true), []);
|
|
||||||
|
|
||||||
return (
|
export class URLBuildingConfigurationError extends ConfigurationError {
|
||||||
<ErrorView Icon={OfflineIcon} title={t("error.connection_failed")}>
|
public constructor(baseUrl: string, cause?: unknown) {
|
||||||
<p>{t("error.connection_rejected_description")}</p>
|
let message: string;
|
||||||
{showDetails ? (
|
if (cause instanceof Error) {
|
||||||
<Trans
|
message = cause.message;
|
||||||
i18nKey="error.auth_connection_rejected_details"
|
} else {
|
||||||
url={livekitServiceUrl}
|
message = "Unknown error";
|
||||||
status={status}
|
}
|
||||||
response={response}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
The application connected to the call authentication service at{" "}
|
|
||||||
<Link href={livekitServiceUrl} target="_blank">
|
|
||||||
{{ url: livekitServiceUrl } as unknown as ReactElement}
|
|
||||||
</Link>
|
|
||||||
, but it responded with status code{" "}
|
|
||||||
{{ status } as unknown as ReactElement} (
|
|
||||||
{{ response } as unknown as ReactElement}). If you are the server
|
|
||||||
admin, make sure{" "}
|
|
||||||
<Link
|
|
||||||
href="https://github.com/element-hq/lk-jwt-service/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
lk-jwt-service
|
|
||||||
</Link>{" "}
|
|
||||||
is listening at that address and check the logs for more
|
|
||||||
information.
|
|
||||||
</p>
|
|
||||||
</Trans>
|
|
||||||
) : (
|
|
||||||
<Button kind="tertiary" onClick={onShowDetailsClick}>
|
|
||||||
{t("error.show_details")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</ErrorView>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export class AuthConnectionRejectedError extends RichError {
|
|
||||||
public constructor(
|
|
||||||
livekitServiceUrl: string,
|
|
||||||
status: number,
|
|
||||||
response: string,
|
|
||||||
) {
|
|
||||||
super(
|
super(
|
||||||
`Failed to connect to ${livekitServiceUrl} (status ${status})`,
|
`Unable to build URL based on: ${baseUrl}`,
|
||||||
<AuthConnectionRejected
|
<Trans
|
||||||
livekitServiceUrl={livekitServiceUrl}
|
i18nKey="error.invalid_url_details"
|
||||||
status={status}
|
baseUrl={baseUrl}
|
||||||
response={response}
|
message={message}
|
||||||
/>,
|
>
|
||||||
|
<p>
|
||||||
|
The URL derived from{" "}
|
||||||
|
<code>{{ baseUrl } as unknown as ReactElement}</code> is not valid:{" "}
|
||||||
|
<pre>{{ message } as unknown as ReactElement}</pre>
|
||||||
|
</p>
|
||||||
|
</Trans>,
|
||||||
|
cause,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ResourceNotFoundConfigurationError extends ConfigurationError {
|
||||||
|
public constructor(url: URL) {
|
||||||
|
super(
|
||||||
|
`The server returned a 404 response for: ${url.href}`,
|
||||||
|
<Trans i18nKey="error.resource_not_found_details" url={url.href}>
|
||||||
|
<p>
|
||||||
|
The request to{" "}
|
||||||
|
<code>{{ url: url.href } as unknown as ReactElement}</code> returned a{" "}
|
||||||
|
<code>404</code> response.
|
||||||
|
</p>
|
||||||
|
</Trans>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnexpectedResponseCodeError extends ServerError {
|
||||||
|
public constructor(url: URL, status: number, response: string) {
|
||||||
|
super(
|
||||||
|
`Received unexpected response code from ${url.href}: ${status}`,
|
||||||
|
<Trans
|
||||||
|
i18nKey="error.unexpected_response_code_details"
|
||||||
|
url={url.href}
|
||||||
|
status={status}
|
||||||
|
response={response}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
The application received an unexpected response from{" "}
|
||||||
|
<code>{{ url } as unknown as ReactElement}</code>. It received status
|
||||||
|
code <code>{{ status } as unknown as ReactElement}</code>:{" "}
|
||||||
|
<pre>{{ response } as unknown as ReactElement}</pre>.
|
||||||
|
</p>
|
||||||
|
</Trans>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FetchError extends ServerError {
|
||||||
|
public constructor(url: URL, cause: unknown) {
|
||||||
|
let message: string;
|
||||||
|
if (cause instanceof Error) {
|
||||||
|
message = cause.message;
|
||||||
|
} else {
|
||||||
|
message = "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
super(
|
||||||
|
`Failed to connect to ${url.href}: ${message}`,
|
||||||
|
<Trans
|
||||||
|
i18nKey="error.fetch_error_details"
|
||||||
|
url={url.href}
|
||||||
|
message={message}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
The application received an unexpected response from{" "}
|
||||||
|
<code>{{ url: url.href } as unknown as ReactElement}</code>. It
|
||||||
|
received status code{" "}
|
||||||
|
<code>{{ message } as unknown as ReactElement}</code>.
|
||||||
|
</p>
|
||||||
|
</Trans>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidServerResponseError extends ServerError {
|
||||||
|
public constructor(url: URL, cause: unknown) {
|
||||||
|
let message: string;
|
||||||
|
if (cause instanceof Error) {
|
||||||
|
message = cause.message;
|
||||||
|
} else {
|
||||||
|
message = "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
super(
|
||||||
|
`Invalid response received from ${url.href}: ${message}`,
|
||||||
|
<Trans
|
||||||
|
i18nKey="error.invalid_server_response_error_details"
|
||||||
|
url={url.href}
|
||||||
|
message={message}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
The server at{" "}
|
||||||
|
<code>{{ url: url.href } as unknown as ReactElement}</code> returned
|
||||||
|
an invalid response:{" "}
|
||||||
|
<pre>{{ message } as unknown as ReactElement}</pre>
|
||||||
|
</p>
|
||||||
|
</Trans>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ import { type LivekitFocus } from "matrix-js-sdk/src/matrixrtc/LivekitFocus";
|
|||||||
|
|
||||||
import { useActiveLivekitFocus } from "../room/useActiveFocus";
|
import { useActiveLivekitFocus } from "../room/useActiveFocus";
|
||||||
import {
|
import {
|
||||||
AuthConnectionFailedError,
|
FetchError,
|
||||||
AuthConnectionRejectedError,
|
InvalidServerResponseError,
|
||||||
|
ResourceNotFoundConfigurationError,
|
||||||
|
UnexpectedResponseCodeError,
|
||||||
|
URLBuildingConfigurationError,
|
||||||
} from "../RichError";
|
} from "../RichError";
|
||||||
|
|
||||||
export interface SFUConfig {
|
export interface SFUConfig {
|
||||||
@@ -87,9 +90,18 @@ async function getLiveKitJWT(
|
|||||||
roomName: string,
|
roomName: string,
|
||||||
openIDToken: IOpenIDToken,
|
openIDToken: IOpenIDToken,
|
||||||
): Promise<SFUConfig> {
|
): Promise<SFUConfig> {
|
||||||
|
let url: URL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: check that relative URLs are handled as expected by this
|
||||||
|
url = new URL("sfu/get", livekitServiceURL);
|
||||||
|
} catch (e) {
|
||||||
|
throw new URLBuildingConfigurationError(livekitServiceURL, e);
|
||||||
|
}
|
||||||
|
|
||||||
let res: Response;
|
let res: Response;
|
||||||
try {
|
try {
|
||||||
res = await fetch(livekitServiceURL + "/sfu/get", {
|
res = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -101,16 +113,17 @@ async function getLiveKitJWT(
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new AuthConnectionFailedError(livekitServiceURL, e);
|
throw new FetchError(url, e);
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw res.status === 404
|
throw res.status === 404
|
||||||
? new AuthConnectionFailedError(livekitServiceURL)
|
? new ResourceNotFoundConfigurationError(url)
|
||||||
: new AuthConnectionRejectedError(
|
: new UnexpectedResponseCodeError(url, res.status, await res.text());
|
||||||
livekitServiceURL,
|
}
|
||||||
res.status,
|
|
||||||
await res.text(),
|
try {
|
||||||
);
|
return await res.json();
|
||||||
|
} catch (e) {
|
||||||
|
throw new InvalidServerResponseError(url, e);
|
||||||
}
|
}
|
||||||
return await res.json();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user