refactoring (#178)

* unify components import

* refactor config and app context

* refactor icons

* refactor date, error, mxid and storage

* refactor synapse utils
This commit is contained in:
Aine
2024-11-25 12:51:05 +02:00
committed by GitHub
parent ea0c7a73fd
commit 392fec3186
35 changed files with 200 additions and 206 deletions

View File

@@ -1,7 +1,6 @@
import fetchMock from "jest-fetch-mock";
import authProvider from "./authProvider";
import storage from "../storage";
import { HttpError } from "ra-core";
fetchMock.enableMocks();
@@ -9,7 +8,7 @@ fetchMock.enableMocks();
describe("authProvider", () => {
beforeEach(() => {
fetchMock.resetMocks();
storage.clear();
localStorage.clear();
});
describe("login", () => {
@@ -38,10 +37,10 @@ describe("authProvider", () => {
}),
method: "POST",
});
expect(storage.getItem("base_url")).toEqual("http://example.com");
expect(storage.getItem("user_id")).toEqual("@user:example.com");
expect(storage.getItem("access_token")).toEqual("foobar");
expect(storage.getItem("device_id")).toEqual("some_device");
expect(localStorage.getItem("base_url")).toEqual("http://example.com");
expect(localStorage.getItem("user_id")).toEqual("@user:example.com");
expect(localStorage.getItem("access_token")).toEqual("foobar");
expect(localStorage.getItem("device_id")).toEqual("some_device");
});
});
@@ -69,16 +68,16 @@ describe("authProvider", () => {
}),
method: "POST",
});
expect(storage.getItem("base_url")).toEqual("https://example.com");
expect(storage.getItem("user_id")).toEqual("@user:example.com");
expect(storage.getItem("access_token")).toEqual("foobar");
expect(storage.getItem("device_id")).toEqual("some_device");
expect(localStorage.getItem("base_url")).toEqual("https://example.com");
expect(localStorage.getItem("user_id")).toEqual("@user:example.com");
expect(localStorage.getItem("access_token")).toEqual("foobar");
expect(localStorage.getItem("device_id")).toEqual("some_device");
});
describe("logout", () => {
it("should remove the access_token from storage", async () => {
storage.setItem("base_url", "example.com");
storage.setItem("access_token", "foo");
localStorage.setItem("base_url", "example.com");
localStorage.setItem("access_token", "foo");
fetchMock.mockResponse(JSON.stringify({}));
await authProvider.logout(null);
@@ -91,7 +90,7 @@ describe("authProvider", () => {
method: "POST",
user: { authenticated: true, token: "Bearer foo" },
});
expect(storage.getItem("access_token")).toBeNull();
expect(localStorage.getItem("access_token")).toBeNull();
});
});
@@ -115,7 +114,7 @@ describe("authProvider", () => {
});
it("should resolve when logged in", async () => {
storage.setItem("access_token", "foobar");
localStorage.setItem("access_token", "foobar");
await expect(authProvider.checkAuth({})).resolves.toBeUndefined();
});

View File

@@ -1,9 +1,8 @@
import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
import storage from "../storage";
import { MatrixError, displayError } from "../components/error";
import { MatrixError, displayError } from "../utils/error";
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
import { FetchConfig, ClearConfig } from "../components/config";
import { FetchConfig, ClearConfig } from "../utils/config";
const authProvider: AuthProvider = {
// called when the user attempts to log in
@@ -26,7 +25,7 @@ const authProvider: AuthProvider = {
body: JSON.stringify(
Object.assign(
{
device_id: storage.getItem("device_id"),
device_id: localStorage.getItem("device_id"),
initial_device_display_name: "Synapse Admin",
},
loginToken
@@ -52,11 +51,11 @@ const authProvider: AuthProvider = {
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
storage.removeItem("base_url")
localStorage.removeItem("base_url")
throw new Error("Homeserver URL is required.");
}
base_url = base_url.replace(/\/+$/g, "");
storage.setItem("base_url", base_url);
localStorage.setItem("base_url", base_url);
const decoded_base_url = window.decodeURIComponent(base_url);
let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/v3/login");
@@ -76,11 +75,11 @@ const authProvider: AuthProvider = {
response = await fetchUtils.fetchJson(login_api_url, options);
const json = response.json;
storage.setItem("home_server", accessToken ? json.user_id.split(":")[1] : json.home_server);
storage.setItem("user_id", json.user_id);
storage.setItem("access_token", accessToken ? accessToken : json.access_token);
storage.setItem("device_id", json.device_id);
storage.setItem("login_type", accessToken ? "accessToken" : "credentials");
localStorage.setItem("home_server", accessToken ? json.user_id.split(":")[1] : json.home_server);
localStorage.setItem("user_id", json.user_id);
localStorage.setItem("access_token", accessToken ? accessToken : json.access_token);
localStorage.setItem("device_id", json.device_id);
localStorage.setItem("login_type", accessToken ? "accessToken" : "credentials");
// when doing access token auth, config is not fetched, so we need to do it here
if (accessToken) {
@@ -103,9 +102,9 @@ const authProvider: AuthProvider = {
}
},
getIdentity: async () => {
const access_token = storage.getItem("access_token");
const user_id = storage.getItem("user_id");
const base_url = storage.getItem("base_url");
const access_token = localStorage.getItem("access_token");
const user_id = localStorage.getItem("user_id");
const base_url = localStorage.getItem("base_url");
if (typeof access_token !== "string" || typeof user_id !== "string" || typeof base_url !== "string") {
return Promise.reject();
@@ -143,8 +142,8 @@ const authProvider: AuthProvider = {
logout: async () => {
console.log("logout");
const logout_api_url = storage.getItem("base_url") + "/_matrix/client/v3/logout";
const access_token = storage.getItem("access_token");
const logout_api_url = localStorage.getItem("base_url") + "/_matrix/client/v3/logout";
const access_token = localStorage.getItem("access_token");
const options: Options = {
method: "POST",
@@ -176,7 +175,7 @@ const authProvider: AuthProvider = {
},
// called when the user navigates to a new location, to check for authentication
checkAuth: () => {
const access_token = storage.getItem("access_token");
const access_token = localStorage.getItem("access_token");
return typeof access_token === "string" ? Promise.resolve() : Promise.reject();
},
// called when the user navigates to a new location, to check for permissions / roles

View File

@@ -1,7 +1,6 @@
import fetchMock from "jest-fetch-mock";
import dataProvider from "./dataProvider";
import storage from "../storage";
fetchMock.enableMocks();
@@ -10,8 +9,8 @@ beforeEach(() => {
});
describe("dataProvider", () => {
storage.setItem("base_url", "http://localhost");
storage.setItem("access_token", "access_token");
localStorage.setItem("base_url", "http://localhost");
localStorage.setItem("access_token", "access_token");
it("fetches all users", async () => {
fetchMock.mockResponseOnce(

View File

@@ -13,13 +13,12 @@ import {
withLifecycleCallbacks,
} from "react-admin";
import storage from "../storage";
import { returnMXID } from "./synapse";
import { MatrixError, displayError } from "../components/error";
import { returnMXID } from "../utils/mxid";
import { MatrixError, displayError } from "../utils/error";
// Adds the access token to all requests
const jsonClient = async (url: string, options: Options = {}) => {
const token = storage.getItem("access_token");
const token = localStorage.getItem("access_token");
console.log("httpClient " + url);
if (token !== null) {
options.user = {
@@ -401,7 +400,7 @@ const resourceMap = {
data: "media",
total: json => json.total,
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/${storage.getItem("home_server")}/${params.id}`,
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem("home_server")}/${params.id}`,
}),
},
protect_media: {
@@ -418,11 +417,11 @@ const resourceMap = {
quarantine_media: {
map: (qm: UserMedia) => ({ id: qm.media_id }),
create: (params: UserMedia) => ({
endpoint: `/_synapse/admin/v1/media/quarantine/${storage.getItem("home_server")}/${params.media_id}`,
endpoint: `/_synapse/admin/v1/media/quarantine/${localStorage.getItem("home_server")}/${params.media_id}`,
method: "POST",
}),
delete: (params: DeleteParams) => ({
endpoint: `/_synapse/admin/v1/media/unquarantine/${storage.getItem("home_server")}/${params.id}`,
endpoint: `/_synapse/admin/v1/media/unquarantine/${localStorage.getItem("home_server")}/${params.id}`,
method: "POST",
}),
},
@@ -567,7 +566,7 @@ const baseDataProvider: SynapseDataProvider = {
order_by: field,
dir: getSearchOrder(order),
};
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -586,7 +585,7 @@ const baseDataProvider: SynapseDataProvider = {
getOne: async (resource, params) => {
console.log("getOne " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -598,8 +597,8 @@ const baseDataProvider: SynapseDataProvider = {
getMany: async (resource, params) => {
console.log("getMany " + resource);
const base_url = storage.getItem("base_url");
const homeserver = storage.getItem("home_server");
const base_url = localStorage.getItem("base_url");
const homeserver = localStorage.getItem("home_server");
if (!base_url || !(resource in resourceMap)) throw Error("base_url not set");
const res = resourceMap[resource];
@@ -638,7 +637,7 @@ const baseDataProvider: SynapseDataProvider = {
dir: getSearchOrder(order),
};
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -655,7 +654,7 @@ const baseDataProvider: SynapseDataProvider = {
update: async (resource, params) => {
console.log("update " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -670,7 +669,7 @@ const baseDataProvider: SynapseDataProvider = {
updateMany: async (resource, params) => {
console.log("updateMany " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -687,7 +686,7 @@ const baseDataProvider: SynapseDataProvider = {
create: async (resource, params) => {
console.log("create " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -704,7 +703,7 @@ const baseDataProvider: SynapseDataProvider = {
createMany: async (resource: string, params: { ids: Identifier[]; data: RaRecord }) => {
console.log("createMany " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -726,7 +725,7 @@ const baseDataProvider: SynapseDataProvider = {
delete: async (resource, params) => {
console.log("delete " + resource);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -751,7 +750,7 @@ const baseDataProvider: SynapseDataProvider = {
deleteMany: async (resource, params) => {
console.log("deleteMany " + resource, "params", params);
const homeserver = storage.getItem("base_url");
const homeserver = localStorage.getItem("base_url");
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
const res = resourceMap[resource];
@@ -798,17 +797,17 @@ const baseDataProvider: SynapseDataProvider = {
* @returns
*/
deleteMedia: async ({ before_ts, size_gt = 0, keep_profiles = true }) => {
const homeserver = storage.getItem("home_server"); // TODO only required for synapse < 1.78.0
const homeserver = localStorage.getItem("home_server"); // TODO only required for synapse < 1.78.0
const endpoint = `/_synapse/admin/v1/media/${homeserver}/delete?before_ts=${before_ts}&size_gt=${size_gt}&keep_profiles=${keep_profiles}`;
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = base_url + endpoint;
const { json } = await jsonClient(endpoint_url, { method: "POST" });
return json as DeleteMediaResult;
},
uploadMedia: async ({ file, filename, content_type }: UploadMediaParams) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const uploadMediaURL = `${base_url}/_matrix/media/v3/upload`;
const { json } = await jsonClient(`${uploadMediaURL}?filename=${filename}`, {
@@ -822,18 +821,18 @@ const baseDataProvider: SynapseDataProvider = {
return json as UploadMediaResult;
},
getFeatures: async (id: Identifier) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
const { json } = await jsonClient(endpoint_url);
return json.features as ExperimentalFeaturesModel;
},
updateFeatures: async (id: Identifier, features: ExperimentalFeaturesModel) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
await jsonClient(endpoint_url, { method: "PUT", body: JSON.stringify({ features }) });
},
getRateLimits: async (id: Identifier) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
const { json } = await jsonClient(endpoint_url);
return json as RateLimitsModel;
@@ -846,7 +845,7 @@ const baseDataProvider: SynapseDataProvider = {
return obj;
}, {});
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
if (Object.keys(filtered).length === 0) {
await jsonClient(endpoint_url, { method: "DELETE" });
@@ -856,7 +855,7 @@ const baseDataProvider: SynapseDataProvider = {
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify(filtered) });
},
checkUsernameAvailability: async (username: string) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/username_available?username=${encodeURIComponent(username)}`;
try {
const { json } = await jsonClient(endpoint_url);
@@ -869,7 +868,7 @@ const baseDataProvider: SynapseDataProvider = {
}
},
makeRoomAdmin: async (room_id: string, user_id: string) => {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/rooms/${encodeURIComponent(room_id)}/make_room_admin`;
try {
@@ -914,13 +913,13 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
},
beforeDelete: async (params: DeleteParams<any>, dataProvider: DataProvider) => {
if (params.meta?.deleteMedia) {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(params.id))}/media`;
await jsonClient(endpoint_url, { method: "DELETE" });
}
if (params.meta?.redactEvents) {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/user/${encodeURIComponent(returnMXID(params.id))}/redact`;
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
}
@@ -931,13 +930,13 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
await Promise.all(
params.ids.map(async id => {
if (params.meta?.deleteMedia) {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/media`;
await jsonClient(endpoint_url, { method: "DELETE" });
}
if (params.meta?.redactEvents) {
const base_url = storage.getItem("base_url");
const base_url = localStorage.getItem("base_url");
const endpoint_url = `${base_url}/_synapse/admin/v1/user/${encodeURIComponent(returnMXID(id))}/redact`;
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
}

View File

@@ -1,4 +1,4 @@
import { isValidBaseUrl, splitMxid } from "./synapse";
import { isValidBaseUrl, splitMxid } from "./matrix";
describe("splitMxid", () => {
it("splits valid MXIDs", () =>

View File

@@ -1,7 +1,6 @@
import { Identifier, fetchUtils } from "react-admin";
import storage from "../storage";
import { isMXID } from "../components/mxid";
import { isMXID } from "../utils/mxid";
export const splitMxid = mxid => {
const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/;
@@ -50,51 +49,7 @@ export const getSupportedFeatures = async baseUrl => {
* @returns array of supported login flows
*/
export const getSupportedLoginFlows = async baseUrl => {
const loginFlowsUrl = `${baseUrl}/_matrix/client/r0/login`;
const loginFlowsUrl = `${baseUrl}/_matrix/client/v3/login`;
const response = await fetchUtils.fetchJson(loginFlowsUrl, { method: "GET" });
return response.json.flows;
};
/**
* Generate a random MXID for current homeserver
* @returns full MXID as string
*/
export function generateRandomMxId(): string {
const homeserver = storage.getItem("home_server");
const characters = "0123456789abcdefghijklmnopqrstuvwxyz";
const localpart = Array.from(crypto.getRandomValues(new Uint32Array(8)))
.map(x => characters[x % characters.length])
.join("");
return `@${localpart}:${homeserver}`;
}
/**
* Return the full MXID from an arbitrary input
* @param input the input string
* @returns full MXID as string
*/
export function returnMXID(input: string | Identifier): string {
const homeserver = storage.getItem("home_server");
// Check if the input already looks like a valid MXID (i.e., starts with "@" and contains ":")
const mxidPattern = /^@[^@:]+:[^@:]+$/;
if (isMXID(input)) {
return input as string; // Already a valid MXID
}
// If input is not a valid MXID, assume it's a localpart and construct the MXID
const localpart = typeof input === 'string' && input.startsWith('@') ? input.slice(1) : input;
return `@${localpart}:${homeserver}`;
}
/**
* Generate a random user password
* @returns a new random password as string
*/
export function generateRandomPassword(length = 64): string {
const characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~`!@#$%^&*()_-+={[}]|:;'.?/<>,";
return Array.from(crypto.getRandomValues(new Uint32Array(length)))
.map(x => characters[x % characters.length])
.join("");
}