diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 031685b..92acbb1 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -4,7 +4,7 @@ on: branches: [ "main" ] env: upstream_version: v0.10.3 - etke_version: etke8 + etke_version: etke9 bunny_version: v0.1.0 base_path: ./ permissions: diff --git a/README.md b/README.md index fa06ee3..8adf2eb 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ The following changes are already implemented: * [Fix footer overlapping content](https://github.com/Awesome-Technologies/synapse-admin/issues/574) * Switch from nginx to [SWS](https://static-web-server.net/) for serving the app, reducing the size of the Docker image * [Fix redirect URL after user creation](https://github.com/etkecc/synapse-admin/pull/16) +* [Display actual Synapse errors](https://github.com/etkecc/synapse-admin/pull/17) _the list will be updated as new changes are added_ diff --git a/src/components/error.ts b/src/components/error.ts new file mode 100644 index 0000000..5b09232 --- /dev/null +++ b/src/components/error.ts @@ -0,0 +1,6 @@ +export type MatrixError = { + errcode: string; + error: string; +} + +export const displayError = (errcode: string, status: number, message: string) => `${errcode} (${status}): ${message}`; \ No newline at end of file diff --git a/src/synapse/authProvider.ts b/src/synapse/authProvider.ts index 2134ee2..d221021 100644 --- a/src/synapse/authProvider.ts +++ b/src/synapse/authProvider.ts @@ -1,6 +1,7 @@ -import { AuthProvider, Options, fetchUtils } from "react-admin"; +import { AuthProvider, HttpError, Options, fetchUtils, useTranslate } from "react-admin"; import storage from "../storage"; +import { MatrixError, displayError } from "../components/error"; const authProvider: AuthProvider = { // called when the user attempts to log in @@ -44,13 +45,36 @@ const authProvider: AuthProvider = { // use the base_url from login instead of the well_known entry from the // server, since the admin might want to access the admin API via some // private address + if (!base_url) { + // there is some kind of bug with base_url being present in the form, but not submitted + // ref: https://github.com/etkecc/synapse-admin/issues/14 + localStorage.removeItem("base_url") + throw new Error("Homeserver URL is required."); + } base_url = base_url.replace(/\/+$/g, ""); storage.setItem("base_url", base_url); const decoded_base_url = window.decodeURIComponent(base_url); const login_api_url = decoded_base_url + "/_matrix/client/r0/login"; - const { json } = await fetchUtils.fetchJson(login_api_url, options); + let response; + try { + response = await fetchUtils.fetchJson(login_api_url, options); + } catch(err) { + const error = err as HttpError; + const errorStatus = error.status; + const errorBody = error.body as MatrixError; + const errMsg = !!errorBody?.errcode ? displayError(errorBody.errcode, errorStatus, errorBody.error) : displayError("M_INVALID", errorStatus, error.message); + + return Promise.reject( + new HttpError( + errMsg, + errorStatus, + ) + ); + } + + const json = response.json; storage.setItem("home_server", json.home_server); storage.setItem("user_id", json.user_id); storage.setItem("access_token", json.access_token); @@ -77,10 +101,12 @@ const authProvider: AuthProvider = { } }, // called when the API returns an error - checkError: ({ status }: { status: number }) => { - console.log("checkError " + status); + checkError: (err: HttpError) => { + const errorBody = err.body as MatrixError; + const status = err.status; + if (status === 401 || status === 403) { - return Promise.reject(); + return Promise.reject({message: displayError(errorBody.errcode, status, errorBody.error)}); } return Promise.resolve(); }, diff --git a/src/synapse/dataProvider.ts b/src/synapse/dataProvider.ts index 41b05dc..05adec0 100644 --- a/src/synapse/dataProvider.ts +++ b/src/synapse/dataProvider.ts @@ -1,11 +1,12 @@ import { stringify } from "query-string"; -import { DataProvider, DeleteParams, Identifier, Options, RaRecord, fetchUtils } from "react-admin"; +import { DataProvider, DeleteParams, HttpError, Identifier, Options, RaRecord, fetchUtils } from "react-admin"; import storage from "../storage"; +import { MatrixError, displayError } from "../components/error"; // Adds the access token to all requests -const jsonClient = (url: string, options: Options = {}) => { +const jsonClient = async (url: string, options: Options = {}) => { const token = storage.getItem("access_token"); console.log("httpClient " + url); if (token != null) { @@ -14,7 +15,17 @@ const jsonClient = (url: string, options: Options = {}) => { token: `Bearer ${token}`, }; } - return fetchUtils.fetchJson(url, options); + try { + const response = await fetchUtils.fetchJson(url, options); + return response; + } catch (err: any) { + const error = err as HttpError; + const errorStatus = error.status; + const errorBody = error.body as MatrixError; + const errMsg = !!errorBody?.errcode ? displayError(errorBody.errcode, errorStatus, errorBody.error) : displayError("M_INVALID", errorStatus, error.message); + + return Promise.reject(new HttpError(errMsg, errorStatus, errorBody)); + } }; const mxcUrlToHttp = (mxcUrl: string) => {