Compare commits
25 Commits
v0.10.3-et
...
v0.10.3-et
Author | SHA1 | Date | |
---|---|---|---|
![]() |
392fec3186 | ||
![]() |
ea0c7a73fd | ||
![]() |
21c2fdb5a6 | ||
![]() |
0a55fc6182 | ||
![]() |
64c4bd760b | ||
![]() |
e0c8fef365 | ||
![]() |
29b757ac75 | ||
![]() |
6eb0aaea88 | ||
![]() |
b42a5c3b69 | ||
![]() |
66447ac025 | ||
![]() |
747818af6a | ||
![]() |
a86fa93608 | ||
![]() |
f948318919 | ||
![]() |
5fea8de023 | ||
![]() |
20417a67b9 | ||
![]() |
cfd8238edc | ||
![]() |
eae00a6ccd | ||
![]() |
3731410486 | ||
![]() |
099e349a90 | ||
![]() |
de125d0d66 | ||
![]() |
eb8e8f80a9 | ||
![]() |
1229266546 | ||
![]() |
6620942928 | ||
![]() |
998d3817c6 | ||
![]() |
abca842eb9 |
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -4,16 +4,16 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 30
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 30
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 30
|
||||||
|
@@ -98,6 +98,8 @@ The following changes are already implemented:
|
|||||||
* 🔍 [Allow providing login form details via GET params](https://github.com/etkecc/synapse-admin/pull/140)
|
* 🔍 [Allow providing login form details via GET params](https://github.com/etkecc/synapse-admin/pull/140)
|
||||||
* 🎨 [Add preferred theme colors to login page and footer](https://github.com/etkecc/synapse-admin/pull/155)
|
* 🎨 [Add preferred theme colors to login page and footer](https://github.com/etkecc/synapse-admin/pull/155)
|
||||||
* 🔰 [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156)
|
* 🔰 [Add "Assign Admin" button to the rooms](https://github.com/etkecc/synapse-admin/pull/156)
|
||||||
|
* 🖼️ [Add rooms' avatars](https://github.com/etkecc/synapse-admin/pull/158)
|
||||||
|
* 🤖 [User Badges](https://github.com/etkecc/synapse-admin/pull/160)
|
||||||
|
|
||||||
_the list will be updated as new changes are added_
|
_the list will be updated as new changes are added_
|
||||||
|
|
||||||
|
@@ -35,7 +35,7 @@ In this case, you could provide the configuration in the `/.well-known/matrix/cl
|
|||||||
* `menu` - add custom menu items to the main menu (sidebar) by providing a `menu` array in the config.
|
* `menu` - add custom menu items to the main menu (sidebar) by providing a `menu` array in the config.
|
||||||
Each `menu` item can contain the following fields:
|
Each `menu` item can contain the following fields:
|
||||||
* `label` (required): The text to display in the menu.
|
* `label` (required): The text to display in the menu.
|
||||||
* `icon` (optional): The icon to display next to the label, one of the [../src/components/icons.ts] icons, otherwise a default icon will be used.
|
* `icon` (optional): The icon to display next to the label, one of the [src/utils/icons.ts](../src/utils/icons.ts) icons, otherwise a default icon will be used.
|
||||||
* `url` (required): The URL to navigate to when the menu item is clicked.
|
* `url` (required): The URL to navigate to when the menu item is clicked.
|
||||||
[More details](custom-menu.md)
|
[More details](custom-menu.md)
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ The examples below contain the configuration settings to add a link to the [Syna
|
|||||||
Each `menu` item can contain the following fields:
|
Each `menu` item can contain the following fields:
|
||||||
|
|
||||||
* `label` (required): The text to display in the menu.
|
* `label` (required): The text to display in the menu.
|
||||||
* `icon` (optional): The icon to display next to the label, one of the [../src/components/icons.ts] icons, otherwise a
|
* `icon` (optional): The icon to display next to the label, one of the [src/utils/icons.ts](../src/utils/icons.ts) icons, otherwise a
|
||||||
default icon will be used.
|
default icon will be used.
|
||||||
* `url` (required): The URL to navigate to when the menu item is clicked.
|
* `url` (required): The URL to navigate to when the menu item is clicked.
|
||||||
|
|
||||||
|
36
docs/user-badges.md
Normal file
36
docs/user-badges.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# User Badges
|
||||||
|
|
||||||
|
To help with identifying users with certain roles or permissions, we have implemented a badge system.
|
||||||
|
These badges are displayed on the user's avatar and have a handy tooltip that explains what the badge means.
|
||||||
|
|
||||||
|
## Available Badges
|
||||||
|
|
||||||
|
### 🧙 You
|
||||||
|
|
||||||
|
This badge is displayed on your user's avatar.
|
||||||
|
Tooltip for this badge will contain additional information, e.g.: `You (Admin)`.
|
||||||
|
|
||||||
|
### 👑 Admin
|
||||||
|
|
||||||
|
This badge is displayed on homeserver admins' avatars.
|
||||||
|
Tooltip for this badge is `Admin`.
|
||||||
|
|
||||||
|
### 🛡️ Appservice/System-managed
|
||||||
|
|
||||||
|
This badge is displayed on users that are managed by an appservices (or system), [more details](./system-users.md).
|
||||||
|
Tooltip for this badge will contain additional information, e.g.: `System-managed (Bot)`.
|
||||||
|
|
||||||
|
### 🤖 Bot
|
||||||
|
|
||||||
|
This badge is displayed on bots' avatars (users with the `user_type` set to `bot`).
|
||||||
|
Tooltip for this badge is `Bot`.
|
||||||
|
|
||||||
|
### 📞 Support
|
||||||
|
|
||||||
|
This badge is displayed on users that are part of the support team (users with the `user_type` set to `support`).
|
||||||
|
Tooltip for this badge is `Support`.
|
||||||
|
|
||||||
|
### 👤 Regular User
|
||||||
|
|
||||||
|
This badge is displayed on regular users' avatars.
|
||||||
|
Tooltip for this badge is `Regular User`.
|
24
package.json
24
package.json
@@ -18,11 +18,11 @@
|
|||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/lodash": "^4.17.13",
|
"@types/lodash": "^4.17.13",
|
||||||
"@types/node": "^22.8.7",
|
"@types/node": "^22.9.3",
|
||||||
"@types/papaparse": "^5.3.15",
|
"@types/papaparse": "^5.3.15",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.14.0",
|
"@typescript-eslint/eslint-plugin": "^8.14.0",
|
||||||
"@typescript-eslint/parser": "^8.13.0",
|
"@typescript-eslint/parser": "^8.15.0",
|
||||||
"@vitejs/plugin-react": "^4.3.3",
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
"eslint": "^9.15.0",
|
"eslint": "^9.15.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
@@ -38,20 +38,20 @@
|
|||||||
"react-test-renderer": "^18.3.1",
|
"react-test-renderer": "^18.3.1",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.7.2",
|
||||||
"typescript-eslint": "^8.13.0",
|
"typescript-eslint": "^8.15.0",
|
||||||
"vite": "^5.4.11",
|
"vite": "^5.4.11",
|
||||||
"vite-plugin-version-mark": "^0.1.2"
|
"vite-plugin-version-mark": "^0.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.0",
|
"@emotion/react": "^11.13.5",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.5",
|
||||||
"@haleos/ra-language-german": "^1.0.0",
|
"@haleos/ra-language-german": "^1.0.0",
|
||||||
"@haxqer/ra-language-chinese": "^4.16.2",
|
"@haxqer/ra-language-chinese": "^4.16.2",
|
||||||
"@mui/icons-material": "^6.1.7",
|
"@mui/icons-material": "^6.1.8",
|
||||||
"@mui/material": "^6.1.7",
|
"@mui/material": "^6.1.8",
|
||||||
"@mui/utils": "^5.16.6",
|
"@mui/utils": "^5.16.6",
|
||||||
"@tanstack/react-query": "^5.60.5",
|
"@tanstack/react-query": "^5.61.3",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
@@ -59,13 +59,13 @@
|
|||||||
"ra-i18n-polyglot": "^5.3.4",
|
"ra-i18n-polyglot": "^5.3.4",
|
||||||
"ra-language-english": "^5.3.4",
|
"ra-language-english": "^5.3.4",
|
||||||
"ra-language-farsi": "^5.0.0",
|
"ra-language-farsi": "^5.0.0",
|
||||||
"ra-language-french": "^5.3.4",
|
"ra-language-french": "^5.4.0",
|
||||||
"ra-language-italian": "^3.13.1",
|
"ra-language-italian": "^3.13.1",
|
||||||
"ra-language-russian": "^4.14.2",
|
"ra-language-russian": "^4.14.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-admin": "^5.3.3",
|
"react-admin": "^5.4.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.1",
|
"react-hook-form": "^7.53.2",
|
||||||
"react-is": "^18.3.1",
|
"react-is": "^18.3.1",
|
||||||
"react-router": "^6.26.2",
|
"react-router": "^6.26.2",
|
||||||
"react-router-dom": "^6.28.0"
|
"react-router-dom": "^6.28.0"
|
||||||
|
14
src/App.tsx
14
src/App.tsx
@@ -2,10 +2,11 @@ import { merge } from "lodash";
|
|||||||
import polyglotI18nProvider from "ra-i18n-polyglot";
|
import polyglotI18nProvider from "ra-i18n-polyglot";
|
||||||
|
|
||||||
import { Admin, CustomRoutes, Resource, resolveBrowserLocale } from "react-admin";
|
import { Admin, CustomRoutes, Resource, resolveBrowserLocale } from "react-admin";
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
import { Route } from "react-router-dom";
|
import { Route } from "react-router-dom";
|
||||||
|
|
||||||
import { AdminLayout } from "./components/AdminLayout";
|
import AdminLayout from "./components/AdminLayout";
|
||||||
import { ImportFeature } from "./components/ImportFeature";
|
import UserImport from "./components/UserImport";
|
||||||
import germanMessages from "./i18n/de";
|
import germanMessages from "./i18n/de";
|
||||||
import englishMessages from "./i18n/en";
|
import englishMessages from "./i18n/en";
|
||||||
import frenchMessages from "./i18n/fr";
|
import frenchMessages from "./i18n/fr";
|
||||||
@@ -23,6 +24,7 @@ import users from "./resources/users";
|
|||||||
import authProvider from "./synapse/authProvider";
|
import authProvider from "./synapse/authProvider";
|
||||||
import dataProvider from "./synapse/dataProvider";
|
import dataProvider from "./synapse/dataProvider";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { Config } from "./utils/config";
|
||||||
|
|
||||||
// TODO: Can we use lazy loading together with browser locale?
|
// TODO: Can we use lazy loading together with browser locale?
|
||||||
const messages = {
|
const messages = {
|
||||||
@@ -49,7 +51,7 @@ const i18nProvider = polyglotI18nProvider(
|
|||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const App = () => (
|
export const App = () => (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<Admin
|
<Admin
|
||||||
disableTelemetry
|
disableTelemetry
|
||||||
@@ -61,7 +63,7 @@ const App = () => (
|
|||||||
i18nProvider={i18nProvider}
|
i18nProvider={i18nProvider}
|
||||||
>
|
>
|
||||||
<CustomRoutes>
|
<CustomRoutes>
|
||||||
<Route path="/import_users" element={<ImportFeature />} />
|
<Route path="/import_users" element={<UserImport />} />
|
||||||
</CustomRoutes>
|
</CustomRoutes>
|
||||||
<Resource {...users} />
|
<Resource {...users} />
|
||||||
<Resource {...rooms} />
|
<Resource {...rooms} />
|
||||||
@@ -84,4 +86,8 @@ const App = () => (
|
|||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const AppContext = createContext({});
|
||||||
|
|
||||||
|
export const useAppContext = () => useContext(AppContext) as Config;
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
import { createContext, useContext } from "react";
|
|
||||||
import { Config } from "./components/config";
|
|
||||||
|
|
||||||
export const AppContext = createContext({});
|
|
||||||
|
|
||||||
export const useAppContext = () => useContext(AppContext) as Config;
|
|
@@ -1,8 +1,8 @@
|
|||||||
import { CheckForApplicationUpdate, AppBar, TitlePortal, InspectorButton, Confirm, Layout, Logout, Menu, useLogout, UserMenu } from "react-admin";
|
import { CheckForApplicationUpdate, AppBar, TitlePortal, InspectorButton, Confirm, Layout, Logout, Menu, useLogout, UserMenu } from "react-admin";
|
||||||
import { LoginMethod } from "../pages/LoginPage";
|
import { LoginMethod } from "../pages/LoginPage";
|
||||||
import { useEffect, useState, Suspense } from "react";
|
import { useEffect, useState, Suspense } from "react";
|
||||||
import { Icons, DefaultIcon } from "./icons";
|
import { Icons, DefaultIcon } from "../utils/icons";
|
||||||
import { ClearConfig } from "./config";
|
import { MenuItem, GetConfig, ClearConfig } from "../utils/config";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
|
|
||||||
const AdminUserMenu = () => {
|
const AdminUserMenu = () => {
|
||||||
@@ -53,18 +53,8 @@ const AdminAppBar = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AdminMenu = (props) => {
|
const AdminMenu = (props) => {
|
||||||
const [menu, setMenu] = useState([]);
|
const [menu, setMenu] = useState([] as MenuItem[]);
|
||||||
|
useEffect(() => setMenu(GetConfig().menu), []);
|
||||||
useEffect(() => {
|
|
||||||
const menuConfig = localStorage.getItem('menu');
|
|
||||||
if (menuConfig) {
|
|
||||||
try {
|
|
||||||
setMenu(JSON.parse(menuConfig));
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing menu configuration', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu {...props}>
|
<Menu {...props}>
|
||||||
@@ -106,3 +96,5 @@ export const AdminLayout = ({ children }) => {
|
|||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default AdminLayout;
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import { get } from "lodash";
|
import { get } from "lodash";
|
||||||
import { Avatar, AvatarProps } from "@mui/material";
|
import { Avatar, AvatarProps, Badge, Tooltip } from "@mui/material";
|
||||||
import { FieldProps, useRecordContext } from "react-admin";
|
import { FieldProps, useRecordContext, useTranslate } from "react-admin";
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||||
|
import { isMXID, isASManaged } from "../utils/mxid";
|
||||||
|
|
||||||
const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
|
const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
|
||||||
const { alt, classes, sizes, sx, variant } = rest;
|
const { alt, classes, sizes, sx, variant } = rest;
|
||||||
@@ -31,7 +32,76 @@ const AvatarField = ({ source, ...rest }: AvatarProps & FieldProps) => {
|
|||||||
};
|
};
|
||||||
}, [mxcURL, fetchAvatar]);
|
}, [mxcURL, fetchAvatar]);
|
||||||
|
|
||||||
return <Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant} />;
|
// a hacky way to handle both users and rooms,
|
||||||
|
// where users have an ID, may have a name, and may have a displayname
|
||||||
|
// and rooms have an ID and may have a name
|
||||||
|
let letter = "";
|
||||||
|
if (record?.id) {
|
||||||
|
letter = record.id[0].toUpperCase();
|
||||||
|
}
|
||||||
|
if (record?.name) {
|
||||||
|
letter = record.name[0].toUpperCase();
|
||||||
|
}
|
||||||
|
if (record?.displayname) {
|
||||||
|
letter = record.displayname[0].toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// hacky way to determine the user type
|
||||||
|
let badge = "";
|
||||||
|
let tooltip = "";
|
||||||
|
if (isMXID(record?.id)) {
|
||||||
|
const translate = useTranslate();
|
||||||
|
switch (record?.user_type) {
|
||||||
|
case "bot":
|
||||||
|
badge = "🤖";
|
||||||
|
tooltip = translate("resources.users.badge.bot");
|
||||||
|
break;
|
||||||
|
case "support":
|
||||||
|
badge = "📞";
|
||||||
|
tooltip = translate("resources.users.badge.support");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
badge = "👤";
|
||||||
|
tooltip = translate("resources.users.badge.regular");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (record?.admin) {
|
||||||
|
badge = "👑";
|
||||||
|
tooltip = translate("resources.users.badge.admin");
|
||||||
|
}
|
||||||
|
if (isASManaged(record?.name)) {
|
||||||
|
badge = "🛡️";
|
||||||
|
tooltip = `${translate("resources.users.badge.system_managed")} (${tooltip})`;
|
||||||
|
}
|
||||||
|
if (localStorage.getItem("user_id") === record?.id) {
|
||||||
|
badge = "🧙";
|
||||||
|
tooltip = `${translate("resources.users.badge.you")} (${tooltip})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a badge, wrap the Avatar in a Badge and a Tooltip
|
||||||
|
if (badge) {
|
||||||
|
return (
|
||||||
|
<Tooltip title={tooltip}>
|
||||||
|
<Badge
|
||||||
|
badgeContent={badge}
|
||||||
|
overlap="circular"
|
||||||
|
sx={{ "& .MuiBadge-badge": { width: "10px" } }} // we deliberately set a very small width here, to make the badge actually circular
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant}>
|
||||||
|
{letter}
|
||||||
|
</Avatar>
|
||||||
|
</Badge>
|
||||||
|
</Tooltip>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<Avatar alt={alt} classes={classes} sizes={sizes} src={src} sx={sx} variant={variant}>
|
||||||
|
{letter}
|
||||||
|
</Avatar>);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AvatarField;
|
export default AvatarField;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { DeleteWithConfirmButton, DeleteWithConfirmButtonProps, useRecordContext } from "react-admin";
|
import { DeleteWithConfirmButton, DeleteWithConfirmButtonProps, useRecordContext } from "react-admin";
|
||||||
import { isASManaged } from "./mxid";
|
import { isASManaged } from "../utils/mxid";
|
||||||
|
|
||||||
export const DeviceRemoveButton = (props: DeleteWithConfirmButtonProps) => {
|
export const DeviceRemoveButton = (props: DeleteWithConfirmButtonProps) => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
@@ -26,3 +26,5 @@ export const DeviceRemoveButton = (props: DeleteWithConfirmButtonProps) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default DeviceRemoveButton;
|
@@ -93,3 +93,5 @@ export const ExperimentalFeaturesList = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default ExperimentalFeaturesList;
|
||||||
|
@@ -15,7 +15,8 @@ import {
|
|||||||
import { DataProvider, useTranslate } from "ra-core";
|
import { DataProvider, useTranslate } from "ra-core";
|
||||||
import { useDataProvider, useNotify, RaRecord, Title } from "react-admin";
|
import { useDataProvider, useNotify, RaRecord, Title } from "react-admin";
|
||||||
|
|
||||||
import { generateRandomMxId, generateRandomPassword, returnMXID } from "../synapse/synapse";
|
import { generateRandomMXID, returnMXID } from "../utils/mxid";
|
||||||
|
import { generateRandomPassword } from "../utils/password";
|
||||||
|
|
||||||
const LOGGING = true;
|
const LOGGING = true;
|
||||||
|
|
||||||
@@ -274,7 +275,7 @@ const FilePicker = () => {
|
|||||||
// No need to do a bunch of cryptographic random number getting if
|
// No need to do a bunch of cryptographic random number getting if
|
||||||
// we are using neither a generated password nor a generated user id.
|
// we are using neither a generated password nor a generated user id.
|
||||||
if (useridMode === "ignore" || userRecord.id === undefined || userRecord.id === "") {
|
if (useridMode === "ignore" || userRecord.id === undefined || userRecord.id === "") {
|
||||||
userRecord.id = generateRandomMxId();
|
userRecord.id = generateRandomMXID();
|
||||||
}
|
}
|
||||||
if (passwordMode === false || entry.password === undefined || entry.password === "") {
|
if (passwordMode === false || entry.password === undefined || entry.password === "") {
|
||||||
userRecord.password = generateRandomPassword();
|
userRecord.password = generateRandomPassword();
|
||||||
@@ -325,7 +326,7 @@ const FilePicker = () => {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const newRecordData = Object.assign({}, recordData, {
|
const newRecordData = Object.assign({}, recordData, {
|
||||||
id: generateRandomMxId(),
|
id: generateRandomMXID(),
|
||||||
});
|
});
|
||||||
retries++;
|
retries++;
|
||||||
if (retries > 512) {
|
if (retries > 512) {
|
||||||
@@ -570,4 +571,5 @@ const FilePicker = () => {
|
|||||||
return [<Title defaultTitle={translate("import_users.title")} />, cardContainer];
|
return [<Title defaultTitle={translate("import_users.title")} />, cardContainer];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ImportFeature = FilePicker;
|
export const UserImport = FilePicker;
|
||||||
|
export default UserImport;
|
@@ -46,7 +46,7 @@ const RateLimitRow = ({ limit, value, updateRateLimit }: { limit: string, value:
|
|||||||
</Stack>
|
</Stack>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserRateLimits = () => {
|
const UserRateLimits = () => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
@@ -93,3 +93,5 @@ export const UserRateLimits = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default UserRateLimits;
|
||||||
|
@@ -1,87 +0,0 @@
|
|||||||
import storage from "../storage";
|
|
||||||
|
|
||||||
export interface Config {
|
|
||||||
restrictBaseUrl: string | string[];
|
|
||||||
asManagedUsers: string[];
|
|
||||||
supportURL: string;
|
|
||||||
menu: MenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuItem {
|
|
||||||
label: string;
|
|
||||||
icon: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const WellKnownKey = "cc.etke.synapse-admin";
|
|
||||||
|
|
||||||
export const LoadConfig = (context: Config): Config => {
|
|
||||||
if (context.restrictBaseUrl) {
|
|
||||||
storage.setItem("restrict_base_url", JSON.stringify(context.restrictBaseUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.asManagedUsers) {
|
|
||||||
storage.setItem("as_managed_users", JSON.stringify(context.asManagedUsers));
|
|
||||||
}
|
|
||||||
|
|
||||||
let menu: MenuItem[] = [];
|
|
||||||
if (context.menu) {
|
|
||||||
menu = context.menu;
|
|
||||||
}
|
|
||||||
if (context.supportURL) {
|
|
||||||
const migratedSupportURL = {
|
|
||||||
label: "Contact support",
|
|
||||||
icon: "SupportAgent",
|
|
||||||
url: context.supportURL,
|
|
||||||
};
|
|
||||||
console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL);
|
|
||||||
menu.push(migratedSupportURL as MenuItem);
|
|
||||||
}
|
|
||||||
if (menu.length > 0) {
|
|
||||||
storage.setItem("menu", JSON.stringify(menu));
|
|
||||||
}
|
|
||||||
|
|
||||||
// below we try to calculate "final" config, which will contain values from context and already set values in storage
|
|
||||||
// because LoadConfig could be called multiple times to get config from different sources
|
|
||||||
let finalRestrictBaseUrl: string | string[] = "";
|
|
||||||
try {
|
|
||||||
finalRestrictBaseUrl = JSON.parse(storage.getItem("restrict_base_url") || "");
|
|
||||||
if (Array.isArray(finalRestrictBaseUrl) && finalRestrictBaseUrl.length == 1) {
|
|
||||||
finalRestrictBaseUrl = finalRestrictBaseUrl[0];
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
let finalAsManagedUsers: string[] = [];
|
|
||||||
try {
|
|
||||||
finalAsManagedUsers = JSON.parse(storage.getItem("as_managed_users") || "");
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
let finalMenu: MenuItem[] = [];
|
|
||||||
try {
|
|
||||||
finalMenu = JSON.parse(storage.getItem("menu") || "");
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
return {
|
|
||||||
restrictBaseUrl: finalRestrictBaseUrl,
|
|
||||||
asManagedUsers: finalAsManagedUsers,
|
|
||||||
supportURL: storage.getItem("support_url") || "",
|
|
||||||
menu: finalMenu,
|
|
||||||
} as Config;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const ClearConfig = () => {
|
|
||||||
// config.json
|
|
||||||
storage.removeItem("restrict_base_url");
|
|
||||||
storage.removeItem("as_managed_users");
|
|
||||||
storage.removeItem("support_url");
|
|
||||||
storage.removeItem("menu");
|
|
||||||
|
|
||||||
// session
|
|
||||||
storage.removeItem("home_server");
|
|
||||||
storage.removeItem("base_url");
|
|
||||||
storage.removeItem("user_id");
|
|
||||||
storage.removeItem("device_id");
|
|
||||||
storage.removeItem("access_token");
|
|
||||||
storage.removeItem("login_type");
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
import { lazy } from "react";
|
|
||||||
|
|
||||||
export const Icons = {
|
|
||||||
Announcement: lazy(() => import('@mui/icons-material/Announcement')),
|
|
||||||
Engineering: lazy(() => import('@mui/icons-material/Engineering')),
|
|
||||||
HelpCenter: lazy(() => import('@mui/icons-material/HelpCenter')),
|
|
||||||
SupportAgent: lazy(() => import('@mui/icons-material/SupportAgent')),
|
|
||||||
Default: lazy(() => import('@mui/icons-material/OpenInNew')),
|
|
||||||
// Add more icons as needed
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DefaultIcon = Icons.Default;
|
|
@@ -32,9 +32,8 @@ import {
|
|||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { dateParser } from "./date";
|
import { dateParser } from "../utils/date";
|
||||||
import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider";
|
import { DeleteMediaParams, SynapseDataProvider } from "../synapse/dataProvider";
|
||||||
import storage from "../storage";
|
|
||||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||||
|
|
||||||
const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
|
const DeleteMediaDialog = ({ open, onClose, onSubmit }) => {
|
||||||
@@ -385,7 +384,7 @@ export const MediaIDField = ({ source }) => {
|
|||||||
if (!record) {
|
if (!record) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const homeserver = storage.getItem("home_server");
|
const homeserver = localStorage.getItem("home_server");
|
||||||
|
|
||||||
const mediaID = get(record, source)?.toString();
|
const mediaID = get(record, source)?.toString();
|
||||||
if (!mediaID) {
|
if (!mediaID) {
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
import { Identifier } from "ra-core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a user is managed by an application service
|
|
||||||
* @param id The user ID to check
|
|
||||||
* @returns Whether the user is managed by an application service
|
|
||||||
*/
|
|
||||||
export const isASManaged = (id: string | Identifier): boolean => {
|
|
||||||
const managedUsersString = localStorage.getItem("as_managed_users") || '';
|
|
||||||
try {
|
|
||||||
const asManagedUsers = JSON.parse(managedUsersString).map(regex => new RegExp(regex));
|
|
||||||
return asManagedUsers.some(regex => regex.test(id));
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
@@ -25,7 +25,7 @@ const fixedGermanMessages = {
|
|||||||
message: {
|
message: {
|
||||||
...formalGermanMessages.ra.message,
|
...formalGermanMessages.ra.message,
|
||||||
access_denied:
|
access_denied:
|
||||||
"Sie haben nicht die richtigen Berechtigungen um auf diese Seite zuzugreifen.",
|
"Sie haben nicht die erforderlichen Berechtigungen um auf diese Seite zuzugreifen.",
|
||||||
authentication_error:
|
authentication_error:
|
||||||
"Der Authentifizierungsserver hat einen Fehler zurückgegeben und Ihre Anmeldedaten konnten nicht überprüft werden.",
|
"Der Authentifizierungsserver hat einen Fehler zurückgegeben und Ihre Anmeldedaten konnten nicht überprüft werden.",
|
||||||
},
|
},
|
||||||
@@ -179,6 +179,7 @@ const de: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
password: "Durch die Änderung des Passworts wird der Benutzer von allen Sitzungen abgemeldet.",
|
password: "Durch die Änderung des Passworts wird der Benutzer von allen Sitzungen abgemeldet.",
|
||||||
|
create_password: "Generiere ein starkes und sicheres Passwort mit dem Button unten.",
|
||||||
deactivate: "Sie müssen ein Passwort angeben, um ein Konto wieder zu aktivieren.",
|
deactivate: "Sie müssen ein Passwort angeben, um ein Konto wieder zu aktivieren.",
|
||||||
erase: "DSGVO konformes Löschen der Benutzerdaten.",
|
erase: "DSGVO konformes Löschen der Benutzerdaten.",
|
||||||
erase_text: "Das bedeutet, dass die von dem/den Benutzer(n) gesendeten Nachrichten für alle, die zum Zeitpunkt des Sendens im Raum waren, sichtbar bleiben, aber für Benutzer, die dem Raum später beitreten, nicht sichtbar sind.",
|
erase_text: "Das bedeutet, dass die von dem/den Benutzer(n) gesendeten Nachrichten für alle, die zum Zeitpunkt des Sendens im Raum waren, sichtbar bleiben, aber für Benutzer, die dem Raum später beitreten, nicht sichtbar sind.",
|
||||||
@@ -186,6 +187,14 @@ const de: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "Das Ändern eines vom System verwalteten Benutzers ist nicht zulässig.",
|
modify_managed_user_error: "Das Ändern eines vom System verwalteten Benutzers ist nicht zulässig.",
|
||||||
username_available: "Benutzername verfügbar",
|
username_available: "Benutzername verfügbar",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "Sie",
|
||||||
|
bot: "Bot",
|
||||||
|
admin: "Administrator",
|
||||||
|
support: "Unterstützung",
|
||||||
|
regular: "Normaler Benutzer",
|
||||||
|
system_managed: "Systemverwalteter Benutzer",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "Lösche Benutzerdaten",
|
erase: "Lösche Benutzerdaten",
|
||||||
erase_avatar: "Avatar löschen",
|
erase_avatar: "Avatar löschen",
|
||||||
@@ -225,10 +234,11 @@ const de: SynapseTranslationMessages = {
|
|||||||
history_visibility: "Historie-Sichtbarkeit",
|
history_visibility: "Historie-Sichtbarkeit",
|
||||||
topic: "Thema",
|
topic: "Thema",
|
||||||
avatar: "Avatar",
|
avatar: "Avatar",
|
||||||
|
actions: "Aktionen",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
forward_extremities:
|
forward_extremities:
|
||||||
"Forward extremities are the leaf events at the end of a Directed acyclic graph (DAG) in a room, aka events that have no children. The more exist in a room, the more state resolution that Synapse needs to perform (hint: it's an expensive operation). While Synapse has code to prevent too many of these existing at one time in a room, bugs can sometimes make them crop up again. If a room has >10 forward extremities, it's worth checking which room is the culprit and potentially removing them using the SQL queries mentioned in #1760.",
|
"Vorderextremitäten sind Blatt-Ereignisse am Ende eines gerichteten azyklischen Graphens (DAG) in einem Raum, auch bekannt als Ereignisse ohne Nachkommen. Je mehr in einem Raum existieren, umso mehr Zustandsauflösungen muss Synapse absolvieren (Hinweis: dies ist eine sehr aufwendige Operation). Obwohl Synapse Code hat um zu verhindern, dass zuviele davon gleichzeitig in einem Raum existieren, können Bugs manchmal dafür sorgen, dass sie sich ansammeln. Wenn ein Raum >10 Vorderextremitäten hat ist es sinnvoll zu überprüfen um welchen Raum es sich handelt und sie gegebenenfalls, wie in #1769 beschrieben, mittels SQL-Queries zu entfernen.",
|
||||||
},
|
},
|
||||||
enums: {
|
enums: {
|
||||||
join_rules: {
|
join_rules: {
|
||||||
@@ -253,9 +263,9 @@ const de: SynapseTranslationMessages = {
|
|||||||
erase: {
|
erase: {
|
||||||
title: "Raum löschen",
|
title: "Raum löschen",
|
||||||
content:
|
content:
|
||||||
"Sind Sie sicher dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
"Sind Sie sicher, dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
||||||
fields: {
|
fields: {
|
||||||
block: "Benutzer blockieren und daran hindern, dem Raum beizutreten",
|
block: "Blockieren und Benutzer daran hindern, dem Raum beizutreten",
|
||||||
},
|
},
|
||||||
success: "Raum/Räume erfolgreich gelöscht.",
|
success: "Raum/Räume erfolgreich gelöscht.",
|
||||||
failure: "Der/die Raum/Räume konnten nicht gelöscht werden.",
|
failure: "Der/die Raum/Räume konnten nicht gelöscht werden.",
|
||||||
@@ -264,7 +274,7 @@ const de: SynapseTranslationMessages = {
|
|||||||
assign_admin: "Raumadministrator zuweisen",
|
assign_admin: "Raumadministrator zuweisen",
|
||||||
title: "Raumadministrator zu %{roomName} zuweisen",
|
title: "Raumadministrator zu %{roomName} zuweisen",
|
||||||
confirm: "Raumadministrator zuweisen",
|
confirm: "Raumadministrator zuweisen",
|
||||||
content: "Geben Sie die vollständige MXID des Benutzers an, der als Administrator gesetzt werden soll.\nWarnung: Damit dies funktioniert, muss der Raum mindestens einen lokalen Mitglied als Administrator haben.",
|
content: "Geben Sie die vollständige MXID des Benutzers an, der als Administrator gesetzt werden soll.\nWarnung: Damit dies funktioniert, muss der Raum mindestens ein lokales Mitglied als Administrator haben.",
|
||||||
success: "Der/die Benutzer wurde/n als Raumadministrator gesetzt.",
|
success: "Der/die Benutzer wurde/n als Raumadministrator gesetzt.",
|
||||||
failure: "Der/die Benutzer konnte/n nicht als Raumadministrator gesetzt werden. %{errMsg}",
|
failure: "Der/die Benutzer konnte/n nicht als Raumadministrator gesetzt werden. %{errMsg}",
|
||||||
}
|
}
|
||||||
@@ -301,7 +311,7 @@ const de: SynapseTranslationMessages = {
|
|||||||
erase: {
|
erase: {
|
||||||
title: "Gemeldetes Event löschen",
|
title: "Gemeldetes Event löschen",
|
||||||
content:
|
content:
|
||||||
"Sind Sie sicher dass Sie das gemeldete Event löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
|
"Sind Sie sicher, dass Sie das gemeldete Event löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -348,8 +358,8 @@ const de: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
protect_media: {
|
protect_media: {
|
||||||
action: {
|
action: {
|
||||||
create: "Ungeschützt, Schutz erstellen",
|
create: "Ungeschützt, Schutz aktivieren",
|
||||||
delete: "Geschützt, Schutz aufheben",
|
delete: "Geschützt, Schutz deaktivieren",
|
||||||
none: "In Quarantäne",
|
none: "In Quarantäne",
|
||||||
send_success: "Erfolgreich den Schutz-Status geändert.",
|
send_success: "Erfolgreich den Schutz-Status geändert.",
|
||||||
send_failure: "Beim Versenden ist ein Fehler aufgetreten.",
|
send_failure: "Beim Versenden ist ein Fehler aufgetreten.",
|
||||||
@@ -429,8 +439,8 @@ const de: SynapseTranslationMessages = {
|
|||||||
title: "Raum aus Verzeichnis löschen |||| %{smart_count} Räume aus Verzeichnis löschen",
|
title: "Raum aus Verzeichnis löschen |||| %{smart_count} Räume aus Verzeichnis löschen",
|
||||||
content:
|
content:
|
||||||
"Möchten Sie den Raum wirklich aus dem Raumverzeichnis löschen? |||| Möchten Sie die %{smart_count} Räume wirklich aus dem Raumverzeichnis löschen?",
|
"Möchten Sie den Raum wirklich aus dem Raumverzeichnis löschen? |||| Möchten Sie die %{smart_count} Räume wirklich aus dem Raumverzeichnis löschen?",
|
||||||
erase: "Lösche aus Verzeichnis",
|
erase: "Aus Verzeichnis löschen",
|
||||||
create: "Eintragen ins Verzeichnis",
|
create: "Ins Verzeichnis eintragen",
|
||||||
send_success: "Raum erfolgreich eingetragen.",
|
send_success: "Raum erfolgreich eingetragen.",
|
||||||
send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
|
send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
|
||||||
},
|
},
|
||||||
@@ -452,7 +462,7 @@ const de: SynapseTranslationMessages = {
|
|||||||
fields: {
|
fields: {
|
||||||
token: "Token",
|
token: "Token",
|
||||||
valid: "Gültige Token",
|
valid: "Gültige Token",
|
||||||
uses_allowed: "Anzahl",
|
uses_allowed: "Verwendungen erlaubt",
|
||||||
pending: "Ausstehend",
|
pending: "Ausstehend",
|
||||||
completed: "Abgeschlossen",
|
completed: "Abgeschlossen",
|
||||||
expiry_time: "Ablaufzeit",
|
expiry_time: "Ablaufzeit",
|
||||||
|
@@ -152,6 +152,7 @@ const en: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
password: "Changing password will log user out of all sessions.",
|
password: "Changing password will log user out of all sessions.",
|
||||||
|
create_password: "Generate a strong and secure password using the button below.",
|
||||||
deactivate: "You must provide a password to re-activate an account.",
|
deactivate: "You must provide a password to re-activate an account.",
|
||||||
erase: "Mark the user as GDPR-erased",
|
erase: "Mark the user as GDPR-erased",
|
||||||
erase_text: "This means messages sent by the user(-s) will still be visible by anyone who was in the room when these messages were sent, but hidden from users joining the room afterward.",
|
erase_text: "This means messages sent by the user(-s) will still be visible by anyone who was in the room when these messages were sent, but hidden from users joining the room afterward.",
|
||||||
@@ -170,6 +171,14 @@ const en: SynapseTranslationMessages = {
|
|||||||
overwrite_cancel: "Cancel",
|
overwrite_cancel: "Cancel",
|
||||||
overwrite_confirm: "Overwrite",
|
overwrite_confirm: "Overwrite",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "You",
|
||||||
|
bot: "Bot",
|
||||||
|
admin: "Admin",
|
||||||
|
support: "Support",
|
||||||
|
regular: "Regular User",
|
||||||
|
system_managed: "System-managed",
|
||||||
|
},
|
||||||
limits: {
|
limits: {
|
||||||
messages_per_second: "Messages per second",
|
messages_per_second: "Messages per second",
|
||||||
messages_per_second_text: "The number of actions that can be performed in a second.",
|
messages_per_second_text: "The number of actions that can be performed in a second.",
|
||||||
@@ -198,6 +207,7 @@ const en: SynapseTranslationMessages = {
|
|||||||
history_visibility: "History visibility",
|
history_visibility: "History visibility",
|
||||||
topic: "Topic",
|
topic: "Topic",
|
||||||
avatar: "Avatar",
|
avatar: "Avatar",
|
||||||
|
actions: "Actions",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
forward_extremities:
|
forward_extremities:
|
||||||
|
@@ -144,6 +144,7 @@ const fa: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
password: "با تغییر رمز عبور کاربر از تمام دستگاه ها خارج می شود.",
|
password: "با تغییر رمز عبور کاربر از تمام دستگاه ها خارج می شود.",
|
||||||
|
create_password: "رمز عبور قوی و امنی را با استفاده از دکمه زیر ایجاد کنید.",
|
||||||
deactivate: "برای فعالسازی مجدد حساب باید رمز عبور وارد کنید.",
|
deactivate: "برای فعالسازی مجدد حساب باید رمز عبور وارد کنید.",
|
||||||
erase: "کاربر را به عنوان GDPR پاک شده علامت گذاری کنید",
|
erase: "کاربر را به عنوان GDPR پاک شده علامت گذاری کنید",
|
||||||
erase_text: "وهذا يعني أن الرسائل المرسلة من قبل المستخدم (المستخدمين) ستظل مرئية من قبل أي شخص كان في الغرفة عند إرسال هذه الرسائل، ولكنها مخفية عن المستخدمين الذين ينضمون إلى الغرفة بعد ذلك.",
|
erase_text: "وهذا يعني أن الرسائل المرسلة من قبل المستخدم (المستخدمين) ستظل مرئية من قبل أي شخص كان في الغرفة عند إرسال هذه الرسائل، ولكنها مخفية عن المستخدمين الذين ينضمون إلى الغرفة بعد ذلك.",
|
||||||
@@ -151,6 +152,14 @@ const fa: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "لا يُسمح بتغيير المستخدم الذي يديره النظام.",
|
modify_managed_user_error: "لا يُسمح بتغيير المستخدم الذي يديره النظام.",
|
||||||
username_available: "نام کاربری موجود",
|
username_available: "نام کاربری موجود",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "شما",
|
||||||
|
bot: "ربات",
|
||||||
|
admin: "مدیر",
|
||||||
|
support: "پشتیبان",
|
||||||
|
regular: "کاربر عادی",
|
||||||
|
system_managed: "مدیریت سیستم",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "پاک کردن اطلاعات کاربر",
|
erase: "پاک کردن اطلاعات کاربر",
|
||||||
erase_avatar: "محو الصورة الرمزية",
|
erase_avatar: "محو الصورة الرمزية",
|
||||||
@@ -190,6 +199,7 @@ const fa: SynapseTranslationMessages = {
|
|||||||
history_visibility: "مشاهده تاریخچه",
|
history_visibility: "مشاهده تاریخچه",
|
||||||
topic: "موضوع",
|
topic: "موضوع",
|
||||||
avatar: "آواتار",
|
avatar: "آواتار",
|
||||||
|
actions: "عملیات",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
forward_extremities:
|
forward_extremities:
|
||||||
|
@@ -146,6 +146,8 @@ const fr: SynapseTranslationMessages = {
|
|||||||
auth_provider: "Fournisseur d'identité",
|
auth_provider: "Fournisseur d'identité",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
|
password: "Changer le mot de passe déconnectera l'utilisateur de toutes les sessions.",
|
||||||
|
create_password: "Générer un mot de passe fort et sécurisé en utilisant le bouton ci-dessous.",
|
||||||
deactivate: "Vous devrez fournir un mot de passe pour réactiver le compte.",
|
deactivate: "Vous devrez fournir un mot de passe pour réactiver le compte.",
|
||||||
erase: "Marquer l'utilisateur comme effacé conformément au RGPD",
|
erase: "Marquer l'utilisateur comme effacé conformément au RGPD",
|
||||||
erase_text: "Cela signifie que les messages envoyés par le(s) utilisateur(s) seront toujours visibles par toute personne qui se trouvait dans la salle au moment où ces messages ont été envoyés, mais qu'ils seront cachés aux utilisateurs qui rejoindront la salle par la suite.",
|
erase_text: "Cela signifie que les messages envoyés par le(s) utilisateur(s) seront toujours visibles par toute personne qui se trouvait dans la salle au moment où ces messages ont été envoyés, mais qu'ils seront cachés aux utilisateurs qui rejoindront la salle par la suite.",
|
||||||
@@ -153,6 +155,14 @@ const fr: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "La modification d'un utilisateur géré par le système n'est pas autorisée.",
|
modify_managed_user_error: "La modification d'un utilisateur géré par le système n'est pas autorisée.",
|
||||||
username_available: "Nom d'utilisateur disponible",
|
username_available: "Nom d'utilisateur disponible",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "Vous",
|
||||||
|
bot: "Bot",
|
||||||
|
admin: "Admin",
|
||||||
|
support: "Support",
|
||||||
|
regular: "Utilisateur régulier",
|
||||||
|
system_managed: "Géré par le système",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "Effacer les données de l'utilisateur",
|
erase: "Effacer les données de l'utilisateur",
|
||||||
erase_avatar: "Effacer l'avatar",
|
erase_avatar: "Effacer l'avatar",
|
||||||
@@ -192,6 +202,7 @@ const fr: SynapseTranslationMessages = {
|
|||||||
history_visibility: "Visibilité de l'historique",
|
history_visibility: "Visibilité de l'historique",
|
||||||
topic: "Sujet",
|
topic: "Sujet",
|
||||||
avatar: "Avatar",
|
avatar: "Avatar",
|
||||||
|
actions: "Actions",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
forward_extremities:
|
forward_extremities:
|
||||||
|
12
src/i18n/index.d.ts
vendored
12
src/i18n/index.d.ts
vendored
@@ -143,7 +143,8 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
user_type?: string;
|
user_type?: string;
|
||||||
};
|
};
|
||||||
helper: {
|
helper: {
|
||||||
password?: string;
|
password: string;
|
||||||
|
create_password: string;
|
||||||
deactivate: string;
|
deactivate: string;
|
||||||
erase: string;
|
erase: string;
|
||||||
erase_text: string;
|
erase_text: string;
|
||||||
@@ -162,6 +163,14 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
overwrite_cancel: string;
|
overwrite_cancel: string;
|
||||||
overwrite_confirm: string;
|
overwrite_confirm: string;
|
||||||
};
|
};
|
||||||
|
badge: {
|
||||||
|
you: string;
|
||||||
|
bot: string;
|
||||||
|
admin: string;
|
||||||
|
support: string;
|
||||||
|
regular: string;
|
||||||
|
system_managed: string;
|
||||||
|
}
|
||||||
limits: {
|
limits: {
|
||||||
messages_per_second: string;
|
messages_per_second: string;
|
||||||
messages_per_second_text: string;
|
messages_per_second_text: string;
|
||||||
@@ -190,6 +199,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
history_visibility: string;
|
history_visibility: string;
|
||||||
topic?: string;
|
topic?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
actions: string;
|
||||||
};
|
};
|
||||||
helper?: {
|
helper?: {
|
||||||
forward_extremities: string;
|
forward_extremities: string;
|
||||||
|
@@ -145,6 +145,7 @@ const it: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
password: "Cambiando la password l'utente verrà disconnesso da tutte le sessioni attive.",
|
password: "Cambiando la password l'utente verrà disconnesso da tutte le sessioni attive.",
|
||||||
|
create_password: "Genera una password forte e sicura utilizzando il pulsante sottostante.",
|
||||||
deactivate: "Devi fornire una password per riattivare l'account.",
|
deactivate: "Devi fornire una password per riattivare l'account.",
|
||||||
erase: "Constrassegna l'utente come cancellato dal GDPR",
|
erase: "Constrassegna l'utente come cancellato dal GDPR",
|
||||||
erase_text: "Ciò significa che i messaggi inviati dall'utente (o dagli utenti) saranno ancora visibili da chiunque si trovasse nella stanza al momento dell'invio, ma saranno nascosti agli utenti che si uniranno alla stanza in seguito.",
|
erase_text: "Ciò significa che i messaggi inviati dall'utente (o dagli utenti) saranno ancora visibili da chiunque si trovasse nella stanza al momento dell'invio, ma saranno nascosti agli utenti che si uniranno alla stanza in seguito.",
|
||||||
@@ -152,6 +153,14 @@ const it: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "La modifica di un utente gestito dal sistema non è consentita.",
|
modify_managed_user_error: "La modifica di un utente gestito dal sistema non è consentita.",
|
||||||
username_available: "Nome utente disponibile",
|
username_available: "Nome utente disponibile",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "Tu",
|
||||||
|
bot: "Bot",
|
||||||
|
admin: "Amministratore",
|
||||||
|
support: "Supporto",
|
||||||
|
regular: "Utente normale",
|
||||||
|
system_managed: "Gestito dal sistema",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "Cancella i dati dell'utente",
|
erase: "Cancella i dati dell'utente",
|
||||||
erase_avatar: "Cancella l'avatar dell'utente",
|
erase_avatar: "Cancella l'avatar dell'utente",
|
||||||
@@ -191,6 +200,7 @@ const it: SynapseTranslationMessages = {
|
|||||||
history_visibility: "Visibilità temporale",
|
history_visibility: "Visibilità temporale",
|
||||||
topic: "Topic",
|
topic: "Topic",
|
||||||
avatar: "Avatar",
|
avatar: "Avatar",
|
||||||
|
actions: "Azioni",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
/* forward_extremities:
|
/* forward_extremities:
|
||||||
|
516
src/i18n/ru.ts
516
src/i18n/ru.ts
@@ -147,10 +147,10 @@ const ru: SynapseTranslationMessages = {
|
|||||||
helper: {
|
helper: {
|
||||||
send: "Это API удаляет локальные файлы с вашего собственного сервера, включая локальные миниатюры и копии скачанных файлов. \
|
send: "Это API удаляет локальные файлы с вашего собственного сервера, включая локальные миниатюры и копии скачанных файлов. \
|
||||||
Данный API не затрагивает файлы, загруженные во внешние хранилища.",
|
Данный API не затрагивает файлы, загруженные во внешние хранилища.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
users: {
|
users: {
|
||||||
name: "Пользователь |||| Пользователи",
|
name: "Пользователь |||| Пользователи",
|
||||||
email: "Почта",
|
email: "Почта",
|
||||||
msisdn: "Телефон",
|
msisdn: "Телефон",
|
||||||
@@ -182,6 +182,7 @@ const ru: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
password: "Смена пароля завершит все сессии пользователя.",
|
password: "Смена пароля завершит все сессии пользователя.",
|
||||||
|
create_password: "Сгенерировать надёжный и безопасный пароль, используя кнопку ниже.",
|
||||||
deactivate: "Вы должны предоставить пароль для реактивации учётной записи.",
|
deactivate: "Вы должны предоставить пароль для реактивации учётной записи.",
|
||||||
erase: "Пометить пользователя как удалённого в соответствии с GDPR",
|
erase: "Пометить пользователя как удалённого в соответствии с GDPR",
|
||||||
erase_text: "Это означает, что сообщения, отправленные пользователем (-ами), будут по-прежнему видны всем, кто находился в комнате в момент их отправки, но будут скрыты от пользователей, присоединившихся к комнате после этого.",
|
erase_text: "Это означает, что сообщения, отправленные пользователем (-ами), будут по-прежнему видны всем, кто находился в комнате в момент их отправки, но будут скрыты от пользователей, присоединившихся к комнате после этого.",
|
||||||
@@ -189,6 +190,14 @@ const ru: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "Изменение пользователя, управляемого системой, не допускается.",
|
modify_managed_user_error: "Изменение пользователя, управляемого системой, не допускается.",
|
||||||
username_available: "Имя пользователя доступно",
|
username_available: "Имя пользователя доступно",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "Вы",
|
||||||
|
bot: "Бот",
|
||||||
|
admin: "Админ",
|
||||||
|
support: "Поддержка",
|
||||||
|
regular: "Обычный пользователь",
|
||||||
|
system_managed: "Управляемый системой",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "Удалить данные пользователя",
|
erase: "Удалить данные пользователя",
|
||||||
erase_avatar: "Удалить аватар",
|
erase_avatar: "Удалить аватар",
|
||||||
@@ -207,265 +216,266 @@ const ru: SynapseTranslationMessages = {
|
|||||||
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
|
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Комната |||| Комнаты",
|
name: "Комната |||| Комнаты",
|
||||||
fields: {
|
fields: {
|
||||||
room_id: "ID комнаты",
|
room_id: "ID комнаты",
|
||||||
name: "Название",
|
name: "Название",
|
||||||
canonical_alias: "Псевдоним",
|
canonical_alias: "Псевдоним",
|
||||||
joined_members: "Участники",
|
joined_members: "Участники",
|
||||||
joined_local_members: "Локальные участники",
|
joined_local_members: "Локальные участники",
|
||||||
joined_local_devices: "Локальные устройства",
|
joined_local_devices: "Локальные устройства",
|
||||||
state_events: "События состояния / Сложность",
|
state_events: "События состояния / Сложность",
|
||||||
version: "Версия",
|
version: "Версия",
|
||||||
is_encrypted: "Зашифровано",
|
is_encrypted: "Зашифровано",
|
||||||
encryption: "Шифрование",
|
encryption: "Шифрование",
|
||||||
federatable: "Федерация",
|
federatable: "Федерация",
|
||||||
public: "Отображается в каталоге комнат",
|
public: "Отображается в каталоге комнат",
|
||||||
creator: "Создатель",
|
creator: "Создатель",
|
||||||
join_rules: "Правила входа",
|
join_rules: "Правила входа",
|
||||||
guest_access: "Гостевой доступ",
|
guest_access: "Гостевой доступ",
|
||||||
history_visibility: "Видимость истории",
|
history_visibility: "Видимость истории",
|
||||||
topic: "Тема",
|
topic: "Тема",
|
||||||
avatar: "Аватар",
|
avatar: "Аватар",
|
||||||
},
|
actions: "Действия",
|
||||||
helper: {
|
|
||||||
forward_extremities:
|
|
||||||
"Оконечности — это события-листья в конце ориентированного ациклического графа (DAG) в комнате, т.е. события без дочерних элементов. \
|
|
||||||
Чем больше их в комнате, тем больше Synapse работает над разрешением состояния (это дорогостоящая операция). \
|
|
||||||
Хотя Synapse старается не допускать существования слишком большого числа таких событий в комнате, из-за ошибок они иногда снова появляются. \
|
|
||||||
Если в комнате >10 оконечностей, стоит найти комнату-виновника и попробовать удалить их с помощью SQL-запросов из #1760.",
|
|
||||||
},
|
|
||||||
enums: {
|
|
||||||
join_rules: {
|
|
||||||
public: "Для всех",
|
|
||||||
knock: "Надо постучать",
|
|
||||||
invite: "По приглашению",
|
|
||||||
private: "Приватная",
|
|
||||||
},
|
|
||||||
guest_access: {
|
|
||||||
can_join: "Гости могут войти",
|
|
||||||
forbidden: "Гости не могут войти",
|
|
||||||
},
|
|
||||||
history_visibility: {
|
|
||||||
invited: "С момента приглашения",
|
|
||||||
joined: "С момента входа",
|
|
||||||
shared: "С момента открытия доступа",
|
|
||||||
world_readable: "Для всех",
|
|
||||||
},
|
|
||||||
unencrypted: "Без шифрования",
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
erase: {
|
|
||||||
title: "Удалить комнату",
|
|
||||||
content:
|
|
||||||
"Действительно удалить эту комнату? Это действие будет невозможно отменить. Все сообщения и файлы в комнате будут удалены с сервера!",
|
|
||||||
fields: {
|
|
||||||
block: "Заблокировать и запретить пользователям присоединяться к комнате",
|
|
||||||
},
|
|
||||||
success: "Комната/ы успешно удалены",
|
|
||||||
failure: "Комната/ы не могут быть удалены.",
|
|
||||||
},
|
|
||||||
make_admin: {
|
|
||||||
assign_admin: "Назначить администратора",
|
|
||||||
title: "Назначить администратора комнате %{roomName}",
|
|
||||||
confirm: "Назначить администратора",
|
|
||||||
content: "Введите полную MXID пользователя, которого нужно назначить администратором.\nПредупреждение: для этого должен быть назначен хотя бы один локальный участник в качестве администратора.",
|
|
||||||
success: "Пользователь назначен администратором комнаты.",
|
|
||||||
failure: "Пользователь не может быть назначен администратором комнаты. %{errMsg}",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
reports: {
|
helper: {
|
||||||
name: "Жалоба |||| Жалобы",
|
forward_extremities:
|
||||||
fields: {
|
"Оконечности — это события-листья в конце ориентированного ациклического графа (DAG) в комнате, т.е. события без дочерних элементов. \
|
||||||
id: "ID",
|
Чем больше их в комнате, тем больше Synapse работает над разрешением состояния (это дорогостоящая операция). \
|
||||||
received_ts: "Дата и время жалобы",
|
Хотя Synapse старается не допускать существования слишком большого числа таких событий в комнате, из-за ошибок они иногда снова появляются. \
|
||||||
user_id: "Автор жалобы",
|
Если в комнате >10 оконечностей, стоит найти комнату-виновника и попробовать удалить их с помощью SQL-запросов из #1760.",
|
||||||
name: "Название комнаты",
|
|
||||||
score: "Баллы",
|
|
||||||
reason: "Причина",
|
|
||||||
event_id: "ID события",
|
|
||||||
event_json: {
|
|
||||||
origin: "Исходнный сервер",
|
|
||||||
origin_server_ts: "Дата и время отправки",
|
|
||||||
type: "Тип события",
|
|
||||||
content: {
|
|
||||||
msgtype: "Тип содержимого",
|
|
||||||
body: "Содержимое",
|
|
||||||
format: "Формат",
|
|
||||||
formatted_body: "Форматированное содержимое",
|
|
||||||
algorithm: "Алгоритм",
|
|
||||||
url: "Ссылка",
|
|
||||||
info: {
|
|
||||||
mimetype: "Тип",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
erase: {
|
|
||||||
title: "Удалить жалобу",
|
|
||||||
content: "Действительно удалить жалобу? Это действие будет невозможно отменить.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
connections: {
|
enums: {
|
||||||
name: "Подключения",
|
join_rules: {
|
||||||
fields: {
|
public: "Для всех",
|
||||||
last_seen: "Дата",
|
knock: "Надо постучать",
|
||||||
ip: "IP адрес",
|
invite: "По приглашению",
|
||||||
user_agent: "Юзер-агент",
|
private: "Приватная",
|
||||||
},
|
},
|
||||||
|
guest_access: {
|
||||||
|
can_join: "Гости могут войти",
|
||||||
|
forbidden: "Гости не могут войти",
|
||||||
|
},
|
||||||
|
history_visibility: {
|
||||||
|
invited: "С момента приглашения",
|
||||||
|
joined: "С момента входа",
|
||||||
|
shared: "С момента открытия доступа",
|
||||||
|
world_readable: "Для всех",
|
||||||
|
},
|
||||||
|
unencrypted: "Без шифрования",
|
||||||
},
|
},
|
||||||
devices: {
|
action: {
|
||||||
name: "Устройство |||| Устройства",
|
erase: {
|
||||||
fields: {
|
title: "Удалить комнату",
|
||||||
device_id: "ID устройства",
|
|
||||||
display_name: "Название",
|
|
||||||
last_seen_ts: "Дата и время",
|
|
||||||
last_seen_ip: "IP адрес",
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
erase: {
|
|
||||||
title: "Удаление %{id}",
|
|
||||||
content: 'Действительно удалить устройство "%{name}"?',
|
|
||||||
success: "Устройство успешно удалено.",
|
|
||||||
failure: "Произошла ошибка.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
users_media: {
|
|
||||||
name: "Файлы",
|
|
||||||
fields: {
|
|
||||||
media_id: "ID файла",
|
|
||||||
media_length: "Размер файла (в байтах)",
|
|
||||||
media_type: "Тип",
|
|
||||||
upload_name: "Имя файла",
|
|
||||||
quarantined_by: "На карантине",
|
|
||||||
safe_from_quarantine: "Защитить от карантина",
|
|
||||||
created_ts: "Создано",
|
|
||||||
last_access_ts: "Последний доступ",
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
open: "Открыть файл в новом окне",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
protect_media: {
|
|
||||||
action: {
|
|
||||||
create: "Не защищён, установить защиту",
|
|
||||||
delete: "Защищён, снять защиту",
|
|
||||||
none: "На карантине",
|
|
||||||
send_success: "Статус защиты успешно изменён.",
|
|
||||||
send_failure: "Произошла ошибка.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
quarantine_media: {
|
|
||||||
action: {
|
|
||||||
name: "Карантин",
|
|
||||||
create: "Поместить на карантин",
|
|
||||||
delete: "На карантине, снять карантин",
|
|
||||||
none: "Защищено от карантина",
|
|
||||||
send_success: "Статус карантина успешно изменён.",
|
|
||||||
send_failure: "Произошла ошибка.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pushers: {
|
|
||||||
name: "Пушер |||| Пушеры",
|
|
||||||
fields: {
|
|
||||||
app: "Приложение",
|
|
||||||
app_display_name: "Название приложения",
|
|
||||||
app_id: "ID приложения",
|
|
||||||
device_display_name: "Название устройства",
|
|
||||||
kind: "Вид",
|
|
||||||
lang: "Язык",
|
|
||||||
profile_tag: "Тег профиля",
|
|
||||||
pushkey: "Ключ",
|
|
||||||
data: { url: "URL" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
servernotices: {
|
|
||||||
name: "Серверные уведомления",
|
|
||||||
send: "Отправить серверные уведомления",
|
|
||||||
fields: {
|
|
||||||
body: "Сообщение",
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
send: "Отправить",
|
|
||||||
send_success: "Серверное уведомление успешно отправлено.",
|
|
||||||
send_failure: "Произошла ошибка.",
|
|
||||||
},
|
|
||||||
helper: {
|
|
||||||
send: 'Отправить серверное уведомление выбранным пользователям. На сервере должна быть активна функция "Server Notices".',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user_media_statistics: {
|
|
||||||
name: "Файлы пользователей",
|
|
||||||
fields: {
|
|
||||||
media_count: "Количество файлов",
|
|
||||||
media_length: "Размер файлов",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
forward_extremities: {
|
|
||||||
name: "Оконечности",
|
|
||||||
fields: {
|
|
||||||
id: "ID события",
|
|
||||||
received_ts: "Дата и время",
|
|
||||||
depth: "Глубина",
|
|
||||||
state_group: "Группа состояния",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
room_state: {
|
|
||||||
name: "События состояния",
|
|
||||||
fields: {
|
|
||||||
type: "Тип",
|
|
||||||
content: "Содержимое",
|
|
||||||
origin_server_ts: "Дата отправки",
|
|
||||||
sender: "Отправитель",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
room_directory: {
|
|
||||||
name: "Каталог комнат",
|
|
||||||
fields: {
|
|
||||||
world_readable: "Гости могут просматривать без входа",
|
|
||||||
guest_can_join: "Гости могут войти",
|
|
||||||
},
|
|
||||||
action: {
|
|
||||||
title:
|
|
||||||
"Удалить комнату из каталога |||| Удалить %{smart_count} комнаты из каталога |||| Удалить %{smart_count} комнат из каталога",
|
|
||||||
content:
|
content:
|
||||||
"Действительно удалить комнату из каталога? |||| Действительно удалить %{smart_count} комнаты из каталога? |||| Действительно удалить %{smart_count} комнат из каталога?",
|
"Действительно удалить эту комнату? Это действие будет невозможно отменить. Все сообщения и файлы в комнате будут удалены с сервера!",
|
||||||
erase: "Удалить из каталога комнат",
|
fields: {
|
||||||
create: "Опубликовать в каталоге комнат",
|
block: "Заблокировать и запретить пользователям присоединяться к комнате",
|
||||||
send_success: "Комната успешно опубликована.",
|
},
|
||||||
send_failure: "Произошла ошибка.",
|
success: "Комната/ы успешно удалены",
|
||||||
|
failure: "Комната/ы не могут быть удалены.",
|
||||||
|
},
|
||||||
|
make_admin: {
|
||||||
|
assign_admin: "Назначить администратора",
|
||||||
|
title: "Назначить администратора комнате %{roomName}",
|
||||||
|
confirm: "Назначить администратора",
|
||||||
|
content: "Введите полную MXID пользователя, которого нужно назначить администратором.\nПредупреждение: для этого должен быть назначен хотя бы один локальный участник в качестве администратора.",
|
||||||
|
success: "Пользователь назначен администратором комнаты.",
|
||||||
|
failure: "Пользователь не может быть назначен администратором комнаты. %{errMsg}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reports: {
|
||||||
|
name: "Жалоба |||| Жалобы",
|
||||||
|
fields: {
|
||||||
|
id: "ID",
|
||||||
|
received_ts: "Дата и время жалобы",
|
||||||
|
user_id: "Автор жалобы",
|
||||||
|
name: "Название комнаты",
|
||||||
|
score: "Баллы",
|
||||||
|
reason: "Причина",
|
||||||
|
event_id: "ID события",
|
||||||
|
event_json: {
|
||||||
|
origin: "Исходнный сервер",
|
||||||
|
origin_server_ts: "Дата и время отправки",
|
||||||
|
type: "Тип события",
|
||||||
|
content: {
|
||||||
|
msgtype: "Тип содержимого",
|
||||||
|
body: "Содержимое",
|
||||||
|
format: "Формат",
|
||||||
|
formatted_body: "Форматированное содержимое",
|
||||||
|
algorithm: "Алгоритм",
|
||||||
|
url: "Ссылка",
|
||||||
|
info: {
|
||||||
|
mimetype: "Тип",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
destinations: {
|
action: {
|
||||||
name: "Федерация",
|
erase: {
|
||||||
fields: {
|
title: "Удалить жалобу",
|
||||||
destination: "Назначение",
|
content: "Действительно удалить жалобу? Это действие будет невозможно отменить.",
|
||||||
failure_ts: "Дата и время ошибки",
|
|
||||||
retry_last_ts: "Дата и время последней попытки",
|
|
||||||
retry_interval: "Интервал между попытками",
|
|
||||||
last_successful_stream_ordering: "Последний успешный поток",
|
|
||||||
stream_ordering: "Поток",
|
|
||||||
},
|
},
|
||||||
action: { reconnect: "Переподключиться" },
|
|
||||||
},
|
},
|
||||||
registration_tokens: {
|
},
|
||||||
name: "Токены регистрации",
|
connections: {
|
||||||
fields: {
|
name: "Подключения",
|
||||||
token: "Токен",
|
fields: {
|
||||||
valid: "Рабочий токен",
|
last_seen: "Дата",
|
||||||
uses_allowed: "Количество использований",
|
ip: "IP адрес",
|
||||||
pending: "Ожидает",
|
user_agent: "Юзер-агент",
|
||||||
completed: "Завершено",
|
},
|
||||||
expiry_time: "Дата окончания",
|
},
|
||||||
length: "Длина",
|
devices: {
|
||||||
|
name: "Устройство |||| Устройства",
|
||||||
|
fields: {
|
||||||
|
device_id: "ID устройства",
|
||||||
|
display_name: "Название",
|
||||||
|
last_seen_ts: "Дата и время",
|
||||||
|
last_seen_ip: "IP адрес",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
erase: {
|
||||||
|
title: "Удаление %{id}",
|
||||||
|
content: 'Действительно удалить устройство "%{name}"?',
|
||||||
|
success: "Устройство успешно удалено.",
|
||||||
|
failure: "Произошла ошибка.",
|
||||||
},
|
},
|
||||||
helper: { length: "Длина токена, если токен не задан." },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
users_media: {
|
||||||
|
name: "Файлы",
|
||||||
|
fields: {
|
||||||
|
media_id: "ID файла",
|
||||||
|
media_length: "Размер файла (в байтах)",
|
||||||
|
media_type: "Тип",
|
||||||
|
upload_name: "Имя файла",
|
||||||
|
quarantined_by: "На карантине",
|
||||||
|
safe_from_quarantine: "Защитить от карантина",
|
||||||
|
created_ts: "Создано",
|
||||||
|
last_access_ts: "Последний доступ",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
open: "Открыть файл в новом окне",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
protect_media: {
|
||||||
|
action: {
|
||||||
|
create: "Не защищён, установить защиту",
|
||||||
|
delete: "Защищён, снять защиту",
|
||||||
|
none: "На карантине",
|
||||||
|
send_success: "Статус защиты успешно изменён.",
|
||||||
|
send_failure: "Произошла ошибка.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
quarantine_media: {
|
||||||
|
action: {
|
||||||
|
name: "Карантин",
|
||||||
|
create: "Поместить на карантин",
|
||||||
|
delete: "На карантине, снять карантин",
|
||||||
|
none: "Защищено от карантина",
|
||||||
|
send_success: "Статус карантина успешно изменён.",
|
||||||
|
send_failure: "Произошла ошибка.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pushers: {
|
||||||
|
name: "Пушер |||| Пушеры",
|
||||||
|
fields: {
|
||||||
|
app: "Приложение",
|
||||||
|
app_display_name: "Название приложения",
|
||||||
|
app_id: "ID приложения",
|
||||||
|
device_display_name: "Название устройства",
|
||||||
|
kind: "Вид",
|
||||||
|
lang: "Язык",
|
||||||
|
profile_tag: "Тег профиля",
|
||||||
|
pushkey: "Ключ",
|
||||||
|
data: { url: "URL" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
servernotices: {
|
||||||
|
name: "Серверные уведомления",
|
||||||
|
send: "Отправить серверные уведомления",
|
||||||
|
fields: {
|
||||||
|
body: "Сообщение",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
send: "Отправить",
|
||||||
|
send_success: "Серверное уведомление успешно отправлено.",
|
||||||
|
send_failure: "Произошла ошибка.",
|
||||||
|
},
|
||||||
|
helper: {
|
||||||
|
send: 'Отправить серверное уведомление выбранным пользователям. На сервере должна быть активна функция "Server Notices".',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user_media_statistics: {
|
||||||
|
name: "Файлы пользователей",
|
||||||
|
fields: {
|
||||||
|
media_count: "Количество файлов",
|
||||||
|
media_length: "Размер файлов",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
forward_extremities: {
|
||||||
|
name: "Оконечности",
|
||||||
|
fields: {
|
||||||
|
id: "ID события",
|
||||||
|
received_ts: "Дата и время",
|
||||||
|
depth: "Глубина",
|
||||||
|
state_group: "Группа состояния",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
room_state: {
|
||||||
|
name: "События состояния",
|
||||||
|
fields: {
|
||||||
|
type: "Тип",
|
||||||
|
content: "Содержимое",
|
||||||
|
origin_server_ts: "Дата отправки",
|
||||||
|
sender: "Отправитель",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
room_directory: {
|
||||||
|
name: "Каталог комнат",
|
||||||
|
fields: {
|
||||||
|
world_readable: "Гости могут просматривать без входа",
|
||||||
|
guest_can_join: "Гости могут войти",
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
title:
|
||||||
|
"Удалить комнату из каталога |||| Удалить %{smart_count} комнаты из каталога |||| Удалить %{smart_count} комнат из каталога",
|
||||||
|
content:
|
||||||
|
"Действительно удалить комнату из каталога? |||| Действительно удалить %{smart_count} комнаты из каталога? |||| Действительно удалить %{smart_count} комнат из каталога?",
|
||||||
|
erase: "Удалить из каталога комнат",
|
||||||
|
create: "Опубликовать в каталоге комнат",
|
||||||
|
send_success: "Комната успешно опубликована.",
|
||||||
|
send_failure: "Произошла ошибка.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
destinations: {
|
||||||
|
name: "Федерация",
|
||||||
|
fields: {
|
||||||
|
destination: "Назначение",
|
||||||
|
failure_ts: "Дата и время ошибки",
|
||||||
|
retry_last_ts: "Дата и время последней попытки",
|
||||||
|
retry_interval: "Интервал между попытками",
|
||||||
|
last_successful_stream_ordering: "Последний успешный поток",
|
||||||
|
stream_ordering: "Поток",
|
||||||
|
},
|
||||||
|
action: { reconnect: "Переподключиться" },
|
||||||
|
},
|
||||||
|
registration_tokens: {
|
||||||
|
name: "Токены регистрации",
|
||||||
|
fields: {
|
||||||
|
token: "Токен",
|
||||||
|
valid: "Рабочий токен",
|
||||||
|
uses_allowed: "Количество использований",
|
||||||
|
pending: "Ожидает",
|
||||||
|
completed: "Завершено",
|
||||||
|
expiry_time: "Дата окончания",
|
||||||
|
length: "Длина",
|
||||||
|
},
|
||||||
|
helper: { length: "Длина токена, если токен не задан." },
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export default ru;
|
export default ru;
|
||||||
|
@@ -169,6 +169,8 @@ const zh: SynapseTranslationMessages = {
|
|||||||
consent_version: "协议版本",
|
consent_version: "协议版本",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
|
password: "更改密码会使用户注销所有会话。",
|
||||||
|
create_password: "使用下面的按钮生成一个强大和安全的密码。",
|
||||||
deactivate: "您必须提供一串密码来激活账户。",
|
deactivate: "您必须提供一串密码来激活账户。",
|
||||||
erase: "将用户标记为根据 GDPR 的要求抹除了",
|
erase: "将用户标记为根据 GDPR 的要求抹除了",
|
||||||
erase_text: "这意味着用户发送的信息对于发送信息时在房间内的任何人来说都是可见的,但对于之后加入房间的用户来说则是隐藏的。",
|
erase_text: "这意味着用户发送的信息对于发送信息时在房间内的任何人来说都是可见的,但对于之后加入房间的用户来说则是隐藏的。",
|
||||||
@@ -176,6 +178,14 @@ const zh: SynapseTranslationMessages = {
|
|||||||
modify_managed_user_error: "不允许修改系统管理的用户。",
|
modify_managed_user_error: "不允许修改系统管理的用户。",
|
||||||
username_available: "用户名可用",
|
username_available: "用户名可用",
|
||||||
},
|
},
|
||||||
|
badge: {
|
||||||
|
you: "您",
|
||||||
|
bot: "机器人",
|
||||||
|
admin: "管理员",
|
||||||
|
support: "支持",
|
||||||
|
regular: "普通用户",
|
||||||
|
system_managed: "系统管理",
|
||||||
|
},
|
||||||
action: {
|
action: {
|
||||||
erase: "抹除用户信息",
|
erase: "抹除用户信息",
|
||||||
erase_avatar: "抹掉头像",
|
erase_avatar: "抹掉头像",
|
||||||
@@ -212,6 +222,9 @@ const zh: SynapseTranslationMessages = {
|
|||||||
join_rules: "加入规则",
|
join_rules: "加入规则",
|
||||||
guest_access: "访客访问",
|
guest_access: "访客访问",
|
||||||
history_visibility: "历史可见性",
|
history_visibility: "历史可见性",
|
||||||
|
topic: "主题",
|
||||||
|
avatar: "头像",
|
||||||
|
actions: "操作",
|
||||||
},
|
},
|
||||||
enums: {
|
enums: {
|
||||||
join_rules: {
|
join_rules: {
|
||||||
|
@@ -2,47 +2,14 @@ import React from "react";
|
|||||||
|
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import App from "./App";
|
import {App, AppContext } from "./App";
|
||||||
import { Config, WellKnownKey, LoadConfig } from "./components/config";
|
import { FetchConfig, GetConfig } from "./utils/config";
|
||||||
import { AppContext } from "./AppContext";
|
|
||||||
import storage from "./storage";
|
|
||||||
|
|
||||||
// load config.json
|
await FetchConfig();
|
||||||
let props: Config = {
|
|
||||||
restrictBaseUrl: [],
|
|
||||||
asManagedUsers: [],
|
|
||||||
supportURL: "",
|
|
||||||
menu: [],
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
const resp = await fetch("config.json");
|
|
||||||
const configJSON = await resp.json();
|
|
||||||
console.log("Loaded config.json", configJSON);
|
|
||||||
props = LoadConfig(configJSON as Config);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if home_server is set, try to load https://home_server/.well-known/matrix/client
|
|
||||||
const homeserver = storage.getItem("home_server");
|
|
||||||
if (homeserver) {
|
|
||||||
try {
|
|
||||||
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
|
|
||||||
const configWK = await resp.json();
|
|
||||||
if (!configWK[WellKnownKey]) {
|
|
||||||
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
|
|
||||||
} else {
|
|
||||||
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
|
|
||||||
props = LoadConfig(configWK[WellKnownKey] as Config);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")).render(
|
createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppContext.Provider value={props}>
|
<AppContext.Provider value={GetConfig()}>
|
||||||
<App />
|
<App />
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
@@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react";
|
|||||||
import { AdminContext } from "react-admin";
|
import { AdminContext } from "react-admin";
|
||||||
|
|
||||||
import LoginPage from "./LoginPage";
|
import LoginPage from "./LoginPage";
|
||||||
import { AppContext } from "../AppContext";
|
import { AppContext } from "../App";
|
||||||
import englishMessages from "../i18n/en";
|
import englishMessages from "../i18n/en";
|
||||||
|
|
||||||
const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]);
|
const i18nProvider = polyglotI18nProvider(() => englishMessages, "en", [{ locale: "en", name: "English" }]);
|
||||||
|
@@ -16,7 +16,7 @@ import {
|
|||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import LoginFormBox from "../components/LoginFormBox";
|
import LoginFormBox from "../components/LoginFormBox";
|
||||||
import { useAppContext } from "../AppContext";
|
import { useAppContext } from "../App";
|
||||||
import {
|
import {
|
||||||
getServerVersion,
|
getServerVersion,
|
||||||
getSupportedFeatures,
|
getSupportedFeatures,
|
||||||
@@ -24,8 +24,7 @@ import {
|
|||||||
getWellKnownUrl,
|
getWellKnownUrl,
|
||||||
isValidBaseUrl,
|
isValidBaseUrl,
|
||||||
splitMxid,
|
splitMxid,
|
||||||
} from "../synapse/synapse";
|
} from "../synapse/matrix";
|
||||||
import storage from "../storage";
|
|
||||||
import Footer from "../components/Footer";
|
import Footer from "../components/Footer";
|
||||||
|
|
||||||
export type LoginMethod = "credentials" | "accessToken";
|
export type LoginMethod = "credentials" | "accessToken";
|
||||||
@@ -46,7 +45,7 @@ const LoginPage = () => {
|
|||||||
const [locale, setLocale] = useLocaleState();
|
const [locale, setLocale] = useLocaleState();
|
||||||
const locales = useLocales();
|
const locales = useLocales();
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
const base_url = allowSingleBaseUrl ? restrictBaseUrl : storage.getItem("base_url");
|
const base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorage.getItem("base_url");
|
||||||
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
|
||||||
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href);
|
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href);
|
||||||
const [loginMethod, setLoginMethod] = useState<LoginMethod>("credentials");
|
const [loginMethod, setLoginMethod] = useState<LoginMethod>("credentials");
|
||||||
@@ -60,8 +59,8 @@ const LoginPage = () => {
|
|||||||
console.log("SSO token is", ssoToken);
|
console.log("SSO token is", ssoToken);
|
||||||
// Prevent further requests
|
// Prevent further requests
|
||||||
window.history.replaceState({}, "", window.location.href.replace(loginToken[0], "#").split("#")[0]);
|
window.history.replaceState({}, "", window.location.href.replace(loginToken[0], "#").split("#")[0]);
|
||||||
const baseUrl = storage.getItem("sso_base_url");
|
const baseUrl = localStorage.getItem("sso_base_url");
|
||||||
storage.removeItem("sso_base_url");
|
localStorage.removeItem("sso_base_url");
|
||||||
if (baseUrl) {
|
if (baseUrl) {
|
||||||
const auth = {
|
const auth = {
|
||||||
base_url: baseUrl,
|
base_url: baseUrl,
|
||||||
@@ -114,7 +113,7 @@ const LoginPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSSO = () => {
|
const handleSSO = () => {
|
||||||
storage.setItem("sso_base_url", ssoBaseUrl);
|
localStorage.setItem("sso_base_url", ssoBaseUrl);
|
||||||
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(
|
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(
|
||||||
window.location.href
|
window.location.href
|
||||||
)}`;
|
)}`;
|
||||||
|
@@ -33,7 +33,7 @@ import {
|
|||||||
DateFieldProps,
|
DateFieldProps,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
|
||||||
import { DATE_FORMAT } from "../components/date";
|
import { DATE_FORMAT } from "../utils/date";
|
||||||
import { get } from "lodash";
|
import { get } from "lodash";
|
||||||
|
|
||||||
const DestinationPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
|
const DestinationPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
|
||||||
@@ -125,11 +125,11 @@ export const DestinationList = (props: ListProps) => {
|
|||||||
sort={{ field: "destination", order: "ASC" }}
|
sort={{ field: "destination", order: "ASC" }}
|
||||||
>
|
>
|
||||||
<DatagridConfigurable rowClick={id => `${id}/show/rooms`} bulkActionButtons={false}>
|
<DatagridConfigurable rowClick={id => `${id}/show/rooms`} bulkActionButtons={false}>
|
||||||
<FunctionField source="destination" render={destinationFieldRender} />
|
<FunctionField source="destination" render={destinationFieldRender} label="resources.destinations.fields.destination" />
|
||||||
<DateField source="failure_ts" showTime options={DATE_FORMAT} />
|
<DateField source="failure_ts" showTime options={DATE_FORMAT} label="resources.destinations.fields.failure_ts" />
|
||||||
<RetryDateField source="retry_last_ts" showTime options={DATE_FORMAT} />
|
<RetryDateField source="retry_last_ts" showTime options={DATE_FORMAT} label="resources.destinations.fields.retry_last_ts" />
|
||||||
<TextField source="retry_interval" />
|
<TextField source="retry_interval" label="resources.destinations.fields.retry_interval" />
|
||||||
<TextField source="last_successful_stream_ordering" />
|
<TextField source="last_successful_stream_ordering" label="resources.destinations.fields.last_successful_stream_ordering" />
|
||||||
<DestinationReconnectButton />
|
<DestinationReconnectButton />
|
||||||
</DatagridConfigurable>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
|
@@ -24,7 +24,7 @@ import {
|
|||||||
Toolbar,
|
Toolbar,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
|
||||||
import { DATE_FORMAT, dateFormatter, dateParser } from "../components/date";
|
import { DATE_FORMAT, dateFormatter, dateParser } from "../utils/date";
|
||||||
|
|
||||||
const validateToken = [regex(/^[A-Za-z0-9._~-]{0,64}$/)];
|
const validateToken = [regex(/^[A-Za-z0-9._~-]{0,64}$/)];
|
||||||
const validateUsesAllowed = [number()];
|
const validateUsesAllowed = [number()];
|
||||||
@@ -41,11 +41,11 @@ export const RegistrationTokenList = (props: ListProps) => (
|
|||||||
perPage={500}
|
perPage={500}
|
||||||
>
|
>
|
||||||
<DatagridConfigurable rowClick="edit">
|
<DatagridConfigurable rowClick="edit">
|
||||||
<TextField source="token" sortable={false} />
|
<TextField source="token" sortable={false} label="resources.registration_tokens.fields.token"/>
|
||||||
<NumberField source="uses_allowed" sortable={false} />
|
<NumberField source="uses_allowed" sortable={false} label="resources.registration_tokens.fields.uses_allowed" />
|
||||||
<NumberField source="pending" sortable={false} />
|
<NumberField source="pending" sortable={false} label="resources.registration_tokens.fields.pending" />
|
||||||
<NumberField source="completed" sortable={false} />
|
<NumberField source="completed" sortable={false} label="resources.registration_tokens.fields.completed" />
|
||||||
<DateField source="expiry_time" showTime options={DATE_FORMAT} sortable={false} />
|
<DateField source="expiry_time" showTime options={DATE_FORMAT} sortable={false} label="resources.registration_tokens.fields.expiry_time" />
|
||||||
</DatagridConfigurable>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
@@ -22,7 +22,7 @@ import {
|
|||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
|
||||||
import { DATE_FORMAT } from "../components/date";
|
import { DATE_FORMAT } from "../utils/date";
|
||||||
import { ReportMediaContent } from "../components/media";
|
import { ReportMediaContent } from "../components/media";
|
||||||
|
|
||||||
const ReportPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
|
const ReportPagination = () => <Pagination rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />;
|
||||||
@@ -92,11 +92,11 @@ const ReportShowActions = () => {
|
|||||||
export const ReportList = (props: ListProps) => (
|
export const ReportList = (props: ListProps) => (
|
||||||
<List {...props} pagination={<ReportPagination />} sort={{ field: "received_ts", order: "DESC" }}>
|
<List {...props} pagination={<ReportPagination />} sort={{ field: "received_ts", order: "DESC" }}>
|
||||||
<DatagridConfigurable rowClick="show" bulkActionButtons={false}>
|
<DatagridConfigurable rowClick="show" bulkActionButtons={false}>
|
||||||
<TextField source="id" sortable={false} />
|
<TextField source="id" sortable={false} label="resources.reports.fields.id" />
|
||||||
<DateField source="received_ts" showTime options={DATE_FORMAT} sortable={true} />
|
<DateField source="received_ts" showTime options={DATE_FORMAT} sortable={true} label="resources.reports.fields.received_ts" />
|
||||||
<TextField sortable={false} source="user_id" />
|
<TextField sortable={false} source="user_id" label="resources.reports.fields.user_id" />
|
||||||
<TextField sortable={false} source="name" />
|
<TextField sortable={false} source="name" label="resources.reports.fields.name" />
|
||||||
<TextField sortable={false} source="score" />
|
<TextField sortable={false} source="score" label="resources.reports.fields.score" />
|
||||||
</DatagridConfigurable>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
@@ -47,7 +47,7 @@ import {
|
|||||||
RoomDirectoryUnpublishButton,
|
RoomDirectoryUnpublishButton,
|
||||||
RoomDirectoryPublishButton,
|
RoomDirectoryPublishButton,
|
||||||
} from "./room_directory";
|
} from "./room_directory";
|
||||||
import { DATE_FORMAT } from "../components/date";
|
import { DATE_FORMAT } from "../utils/date";
|
||||||
import DeleteRoomButton from "../components/DeleteRoomButton";
|
import DeleteRoomButton from "../components/DeleteRoomButton";
|
||||||
import AvatarField from "../components/AvatarField";
|
import AvatarField from "../components/AvatarField";
|
||||||
import { Room } from "../synapse/dataProvider";
|
import { Room } from "../synapse/dataProvider";
|
||||||
@@ -216,7 +216,7 @@ export const RoomShow = (props: ShowProps) => {
|
|||||||
<Tab label="synapseadmin.rooms.tabs.members" icon={<UserIcon />} path="members">
|
<Tab label="synapseadmin.rooms.tabs.members" icon={<UserIcon />} path="members">
|
||||||
<MakeAdminBtn />
|
<MakeAdminBtn />
|
||||||
<ReferenceManyField reference="room_members" target="room_id" label={false}>
|
<ReferenceManyField reference="room_members" target="room_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} rowClick={id => "/users/" + id} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} rowClick={id => "/users/" + id} bulkActionButtons={false}>
|
||||||
<RaTextField source="id" sortable={false} label="resources.users.fields.id" />
|
<RaTextField source="id" sortable={false} label="resources.users.fields.id" />
|
||||||
<ReferenceField
|
<ReferenceField
|
||||||
label="resources.users.fields.displayname"
|
label="resources.users.fields.displayname"
|
||||||
@@ -284,7 +284,7 @@ export const RoomShow = (props: ShowProps) => {
|
|||||||
|
|
||||||
<Tab label={translate("resources.room_state.name", { smart_count: 2 })} icon={<EventIcon />} path="state">
|
<Tab label={translate("resources.room_state.name", { smart_count: 2 })} icon={<EventIcon />} path="state">
|
||||||
<ReferenceManyField reference="room_state" target="room_id" label={false}>
|
<ReferenceManyField reference="room_state" target="room_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<RaTextField source="type" sortable={false} />
|
<RaTextField source="type" sortable={false} />
|
||||||
<DateField source="origin_server_ts" showTime options={DATE_FORMAT} sortable={false} />
|
<DateField source="origin_server_ts" showTime options={DATE_FORMAT} sortable={false} />
|
||||||
<FunctionField source="content" sortable={false} render={record => `${JSON.stringify(record.content, null, 2)}`} />
|
<FunctionField source="content" sortable={false} render={record => `${JSON.stringify(record.content, null, 2)}`} />
|
||||||
@@ -305,7 +305,7 @@ export const RoomShow = (props: ShowProps) => {
|
|||||||
{translate("resources.rooms.helper.forward_extremities")}
|
{translate("resources.rooms.helper.forward_extremities")}
|
||||||
</Box>
|
</Box>
|
||||||
<ReferenceManyField reference="forward_extremities" target="room_id" label={false}>
|
<ReferenceManyField reference="forward_extremities" target="room_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<RaTextField source="id" sortable={false} />
|
<RaTextField source="id" sortable={false} />
|
||||||
<DateField source="received_ts" showTime options={DATE_FORMAT} sortable={false} />
|
<DateField source="received_ts" showTime options={DATE_FORMAT} sortable={false} />
|
||||||
<NumberField source="depth" sortable={false} />
|
<NumberField source="depth" sortable={false} />
|
||||||
@@ -358,25 +358,32 @@ export const RoomList = (props: ListProps) => {
|
|||||||
bulkActionButtons={<RoomBulkActionButtons />}
|
bulkActionButtons={<RoomBulkActionButtons />}
|
||||||
omit={["joined_local_members", "state_events", "version", "federatable"]}
|
omit={["joined_local_members", "state_events", "version", "federatable"]}
|
||||||
>
|
>
|
||||||
<BooleanField
|
<ReferenceField reference="rooms" source="id" label="resources.users.fields.avatar" link={false} sortable={false}>
|
||||||
source="is_encrypted"
|
<AvatarField source="avatar" sx={{ height: "40px", width: "40px" }} />
|
||||||
sortBy="encryption"
|
</ReferenceField>
|
||||||
TrueIcon={HttpsIcon}
|
<WrapperField source="encryption" sortBy="is_encrypted" label="resources.rooms.fields.encryption">
|
||||||
FalseIcon={NoEncryptionIcon}
|
<BooleanField
|
||||||
label={<HttpsIcon />}
|
source="is_encrypted"
|
||||||
sx={{
|
sortBy="encryption"
|
||||||
[`& [data-testid="true"]`]: { color: theme.palette.success.main },
|
TrueIcon={HttpsIcon}
|
||||||
[`& [data-testid="false"]`]: { color: theme.palette.error.main },
|
FalseIcon={NoEncryptionIcon}
|
||||||
}}
|
label={<HttpsIcon />}
|
||||||
/>
|
sx={{
|
||||||
<FunctionField source="name" render={record => record["name"] || record["canonical_alias"] || record["id"]} />
|
[`& [data-testid="true"]`]: { color: theme.palette.success.main },
|
||||||
<RaTextField source="joined_members" />
|
[`& [data-testid="false"]`]: { color: theme.palette.error.main },
|
||||||
<RaTextField source="joined_local_members" />
|
}}
|
||||||
<RaTextField source="state_events" />
|
/>
|
||||||
<RaTextField source="version" />
|
</WrapperField>
|
||||||
<BooleanField source="federatable" />
|
<FunctionField source="name" render={record => record["name"] || record["canonical_alias"] || record["id"]} label="resources.rooms.fields.name" />
|
||||||
<BooleanField source="public" />
|
<RaTextField source="joined_members" label="resources.rooms.fields.joined_members" />
|
||||||
<MakeAdminBtn />
|
<RaTextField source="joined_local_members" label="resources.rooms.fields.joined_local_members" />
|
||||||
|
<RaTextField source="state_events" label="resources.rooms.fields.state_events" />
|
||||||
|
<RaTextField source="version" label="resources.rooms.fields.version" />
|
||||||
|
<BooleanField source="federatable" label="resources.rooms.fields.federatable" />
|
||||||
|
<BooleanField source="public" label="resources.rooms.fields.public" />
|
||||||
|
<WrapperField label="resources.rooms.fields.actions">
|
||||||
|
<MakeAdminBtn />
|
||||||
|
</WrapperField>
|
||||||
</DatagridConfigurable>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
@@ -69,15 +69,15 @@ import { Link } from "react-router-dom";
|
|||||||
|
|
||||||
import AvatarField from "../components/AvatarField";
|
import AvatarField from "../components/AvatarField";
|
||||||
import DeleteUserButton from "../components/DeleteUserButton";
|
import DeleteUserButton from "../components/DeleteUserButton";
|
||||||
import { isASManaged } from "../components/mxid";
|
import { isASManaged } from "../utils/mxid";
|
||||||
import { ServerNoticeButton, ServerNoticeBulkButton } from "../components/ServerNotices";
|
import { ServerNoticeButton, ServerNoticeBulkButton } from "../components/ServerNotices";
|
||||||
import { DATE_FORMAT } from "../components/date";
|
import { DATE_FORMAT } from "../utils/date";
|
||||||
import { DeviceRemoveButton } from "../components/devices";
|
import DeviceRemoveButton from "../components/DeviceRemoveButton";
|
||||||
import { MediaIDField, ProtectMediaButton, QuarantineMediaButton } from "../components/media";
|
import { MediaIDField, ProtectMediaButton, QuarantineMediaButton } from "../components/media";
|
||||||
import { generateRandomPassword } from "../synapse/synapse";
|
import { generateRandomPassword } from "../utils/password";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { ExperimentalFeaturesList } from "../components/ExperimentalFeatures";
|
import ExperimentalFeaturesList from "../components/ExperimentalFeatures";
|
||||||
import { UserRateLimits } from "../components/UserRateLimits";
|
import UserRateLimits from "../components/UserRateLimits";
|
||||||
import { User, UsernameAvailabilityResult } from "../synapse/dataProvider";
|
import { User, UsernameAvailabilityResult } from "../synapse/dataProvider";
|
||||||
import { MakeAdminBtn } from "./rooms";
|
import { MakeAdminBtn } from "./rooms";
|
||||||
|
|
||||||
@@ -171,14 +171,14 @@ export const UserList = (props: ListProps) => (
|
|||||||
rowClick={(id: Identifier, resource: string) => `/${resource}/${id}`}
|
rowClick={(id: Identifier, resource: string) => `/${resource}/${id}`}
|
||||||
bulkActionButtons={<UserBulkActionButtons />}
|
bulkActionButtons={<UserBulkActionButtons />}
|
||||||
>
|
>
|
||||||
<AvatarField source="avatar_src" sx={{ height: "40px", width: "40px" }} sortBy="avatar_url" />
|
<AvatarField source="avatar_src" sx={{ height: "40px", width: "40px" }} sortBy="avatar_url" label="resources.users.fields.avatar" />
|
||||||
<TextField source="id" sortBy="name" />
|
<TextField source="id" sortBy="name" label="resources.users.fields.id" />
|
||||||
<TextField source="displayname" />
|
<TextField source="displayname" label="resources.users.fields.displayname" />
|
||||||
<BooleanField source="is_guest" />
|
<BooleanField source="is_guest" label="resources.users.fields.is_guest" />
|
||||||
<BooleanField source="admin" />
|
<BooleanField source="admin" label="resources.users.fields.admin" />
|
||||||
<BooleanField source="deactivated" />
|
<BooleanField source="deactivated" label="resources.users.fields.deactivated" />
|
||||||
<BooleanField source="locked" />
|
<BooleanField source="locked" label="resources.users.fields.locked" />
|
||||||
<BooleanField source="erased" sortable={false} />
|
<BooleanField source="erased" sortable={false} label="resources.users.fields.erased" />
|
||||||
<DateField source="creation_ts" label="resources.users.fields.creation_ts_ms" showTime options={DATE_FORMAT} />
|
<DateField source="creation_ts" label="resources.users.fields.creation_ts_ms" showTime options={DATE_FORMAT} />
|
||||||
</DatagridConfigurable>
|
</DatagridConfigurable>
|
||||||
</List>
|
</List>
|
||||||
@@ -284,7 +284,7 @@ export const UserCreate = (props: CreateProps) => {
|
|||||||
>
|
>
|
||||||
<TextInput source="id" autoComplete="off" validate={validateUser} onBlur={checkAvailability} helperText={userAvailabilityEl}/>
|
<TextInput source="id" autoComplete="off" validate={validateUser} onBlur={checkAvailability} helperText={userAvailabilityEl}/>
|
||||||
<TextInput source="displayname" validate={maxLength(256)} />
|
<TextInput source="displayname" validate={maxLength(256)} />
|
||||||
<PasswordInput source="password" autoComplete="new-password" validate={maxLength(512)} />
|
<UserPasswordInput source="password" autoComplete="new-password" helperText="resources.users.helper.password" />
|
||||||
<SelectInput source="user_type" choices={choices_type} translateChoice={false} resettable />
|
<SelectInput source="user_type" choices={choices_type} translateChoice={false} resettable />
|
||||||
<BooleanInput source="admin" />
|
<BooleanInput source="admin" />
|
||||||
<ArrayInput source="threepids">
|
<ArrayInput source="threepids">
|
||||||
@@ -320,9 +320,6 @@ const UserTitle = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
|
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
|
||||||
if (isASManaged(record?.id)) {
|
|
||||||
username += " 🤖";
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{translate("resources.users.name", {
|
{translate("resources.users.name", {
|
||||||
@@ -386,19 +383,15 @@ const UserPasswordInput = props => {
|
|||||||
|
|
||||||
const generatePassword = () => {
|
const generatePassword = () => {
|
||||||
const password = generateRandomPassword();
|
const password = generateRandomPassword();
|
||||||
if (record) {
|
form.setValue("password", password, { shouldDirty: true });
|
||||||
form.setValue("password", password, { shouldDirty: true });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PasswordInput {...props} helperText="resources.users.helper.modify_managed_user_error"
|
<PasswordInput {...props} helperText={asManagedUserIsSelected ? "resources.users.helper.modify_managed_user_error" : (record ? "resources.users.helper.password" : "resources.users.helper.create_password")} disabled={asManagedUserIsSelected} />
|
||||||
{...(asManagedUserIsSelected ? { disabled: true } : {})}
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
label="Generate Password"
|
label="resources.users.action.generate_password"
|
||||||
onClick={generatePassword}
|
onClick={generatePassword}
|
||||||
sx={{ marginBottom: "10px" }}
|
sx={{ marginBottom: "10px" }}
|
||||||
disabled={asManagedUserIsSelected}
|
disabled={asManagedUserIsSelected}
|
||||||
@@ -464,7 +457,7 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
|
|
||||||
<FormTab label={translate("resources.devices.name", { smart_count: 2 })} icon={<DevicesIcon />} path="devices">
|
<FormTab label={translate("resources.devices.name", { smart_count: 2 })} icon={<DevicesIcon />} path="devices">
|
||||||
<ReferenceManyField reference="devices" target="user_id" label={false}>
|
<ReferenceManyField reference="devices" target="user_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="device_id" sortable={false} />
|
<TextField source="device_id" sortable={false} />
|
||||||
<TextField source="display_name" sortable={false} />
|
<TextField source="display_name" sortable={false} />
|
||||||
<TextField source="last_seen_ip" sortable={false} />
|
<TextField source="last_seen_ip" sortable={false} />
|
||||||
@@ -477,7 +470,7 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
<FormTab label="resources.connections.name" icon={<SettingsInputComponentIcon />} path="connections">
|
<FormTab label="resources.connections.name" icon={<SettingsInputComponentIcon />} path="connections">
|
||||||
<ReferenceField reference="connections" source="id" label={false} link={false}>
|
<ReferenceField reference="connections" source="id" label={false} link={false}>
|
||||||
<ArrayField source="devices[].sessions[0].connections" label="resources.connections.name">
|
<ArrayField source="devices[].sessions[0].connections" label="resources.connections.name">
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="ip" sortable={false} />
|
<TextField source="ip" sortable={false} />
|
||||||
<DateField source="last_seen" showTime options={DATE_FORMAT} sortable={false} />
|
<DateField source="last_seen" showTime options={DATE_FORMAT} sortable={false} />
|
||||||
<TextField source="user_agent" sortable={false} style={{ width: "100%" }} />
|
<TextField source="user_agent" sortable={false} style={{ width: "100%" }} />
|
||||||
@@ -499,7 +492,7 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
perPage={50}
|
perPage={50}
|
||||||
sort={{ field: "created_ts", order: "DESC" }}
|
sort={{ field: "created_ts", order: "DESC" }}
|
||||||
>
|
>
|
||||||
<Datagrid style={{ width: "100%" }}>
|
<Datagrid sx={{ width: "100%" }}>
|
||||||
<MediaIDField source="media_id" />
|
<MediaIDField source="media_id" />
|
||||||
<DateField source="created_ts" showTime options={DATE_FORMAT} />
|
<DateField source="created_ts" showTime options={DATE_FORMAT} />
|
||||||
<DateField source="last_access_ts" showTime options={DATE_FORMAT} />
|
<DateField source="last_access_ts" showTime options={DATE_FORMAT} />
|
||||||
@@ -516,19 +509,19 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
|
|
||||||
<FormTab label={translate("resources.rooms.name", { smart_count: 2 })} icon={<ViewListIcon />} path="rooms">
|
<FormTab label={translate("resources.rooms.name", { smart_count: 2 })} icon={<ViewListIcon />} path="rooms">
|
||||||
<ReferenceManyField reference="joined_rooms" target="user_id" label={false}>
|
<ReferenceManyField reference="joined_rooms" target="user_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} rowClick={id => "/rooms/" + id + "/show"} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} rowClick={id => "/rooms/" + id + "/show"} bulkActionButtons={false}>
|
||||||
<TextField source="id" sortable={false} label="resources.rooms.fields.room_id" />
|
<ReferenceField reference="rooms" source="id" label={false} link={false} sortable={false}>
|
||||||
<ReferenceField
|
<AvatarField source="avatar" sx={{ height: "40px", width: "40px" }} />
|
||||||
label="resources.rooms.fields.name"
|
</ReferenceField>
|
||||||
source="id"
|
<TextField source="id" label="resources.rooms.fields.room_id" sortable={false}/>
|
||||||
reference="rooms"
|
<ReferenceField reference="rooms" source="id" label="resources.rooms.fields.name" link={false} sortable={false}>
|
||||||
sortable={false}
|
<TextField source="name" />
|
||||||
link=""
|
</ReferenceField>
|
||||||
>
|
<ReferenceField reference="rooms" source="id" label="resources.rooms.fields.joined_members" link={false} sortable={false}>
|
||||||
<TextField source="name" sortable={false} />
|
<TextField source="joined_members" sortable={false} />
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
<MakeAdminBtn />
|
<MakeAdminBtn />
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</ReferenceManyField>
|
</ReferenceManyField>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
@@ -538,7 +531,7 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
path="pushers"
|
path="pushers"
|
||||||
>
|
>
|
||||||
<ReferenceManyField reference="pushers" target="user_id" label={false}>
|
<ReferenceManyField reference="pushers" target="user_id" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }} bulkActionButtons={false}>
|
<Datagrid sx={{ width: "100%" }} bulkActionButtons={false}>
|
||||||
<TextField source="kind" sortable={false} />
|
<TextField source="kind" sortable={false} />
|
||||||
<TextField source="app_display_name" sortable={false} />
|
<TextField source="app_display_name" sortable={false} />
|
||||||
<TextField source="app_id" sortable={false} />
|
<TextField source="app_id" sortable={false} />
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
const storage = localStorage;
|
|
||||||
|
|
||||||
export default storage;
|
|
@@ -1,7 +1,6 @@
|
|||||||
import fetchMock from "jest-fetch-mock";
|
import fetchMock from "jest-fetch-mock";
|
||||||
|
|
||||||
import authProvider from "./authProvider";
|
import authProvider from "./authProvider";
|
||||||
import storage from "../storage";
|
|
||||||
import { HttpError } from "ra-core";
|
import { HttpError } from "ra-core";
|
||||||
|
|
||||||
fetchMock.enableMocks();
|
fetchMock.enableMocks();
|
||||||
@@ -9,7 +8,7 @@ fetchMock.enableMocks();
|
|||||||
describe("authProvider", () => {
|
describe("authProvider", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fetchMock.resetMocks();
|
fetchMock.resetMocks();
|
||||||
storage.clear();
|
localStorage.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("login", () => {
|
describe("login", () => {
|
||||||
@@ -38,10 +37,10 @@ describe("authProvider", () => {
|
|||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
expect(storage.getItem("base_url")).toEqual("http://example.com");
|
expect(localStorage.getItem("base_url")).toEqual("http://example.com");
|
||||||
expect(storage.getItem("user_id")).toEqual("@user:example.com");
|
expect(localStorage.getItem("user_id")).toEqual("@user:example.com");
|
||||||
expect(storage.getItem("access_token")).toEqual("foobar");
|
expect(localStorage.getItem("access_token")).toEqual("foobar");
|
||||||
expect(storage.getItem("device_id")).toEqual("some_device");
|
expect(localStorage.getItem("device_id")).toEqual("some_device");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -69,16 +68,16 @@ describe("authProvider", () => {
|
|||||||
}),
|
}),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
expect(storage.getItem("base_url")).toEqual("https://example.com");
|
expect(localStorage.getItem("base_url")).toEqual("https://example.com");
|
||||||
expect(storage.getItem("user_id")).toEqual("@user:example.com");
|
expect(localStorage.getItem("user_id")).toEqual("@user:example.com");
|
||||||
expect(storage.getItem("access_token")).toEqual("foobar");
|
expect(localStorage.getItem("access_token")).toEqual("foobar");
|
||||||
expect(storage.getItem("device_id")).toEqual("some_device");
|
expect(localStorage.getItem("device_id")).toEqual("some_device");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("logout", () => {
|
describe("logout", () => {
|
||||||
it("should remove the access_token from storage", async () => {
|
it("should remove the access_token from storage", async () => {
|
||||||
storage.setItem("base_url", "example.com");
|
localStorage.setItem("base_url", "example.com");
|
||||||
storage.setItem("access_token", "foo");
|
localStorage.setItem("access_token", "foo");
|
||||||
fetchMock.mockResponse(JSON.stringify({}));
|
fetchMock.mockResponse(JSON.stringify({}));
|
||||||
|
|
||||||
await authProvider.logout(null);
|
await authProvider.logout(null);
|
||||||
@@ -91,7 +90,7 @@ describe("authProvider", () => {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
user: { authenticated: true, token: "Bearer foo" },
|
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 () => {
|
it("should resolve when logged in", async () => {
|
||||||
storage.setItem("access_token", "foobar");
|
localStorage.setItem("access_token", "foobar");
|
||||||
|
|
||||||
await expect(authProvider.checkAuth({})).resolves.toBeUndefined();
|
await expect(authProvider.checkAuth({})).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
|
import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
|
||||||
|
|
||||||
import storage from "../storage";
|
import { MatrixError, displayError } from "../utils/error";
|
||||||
import { MatrixError, displayError } from "../components/error";
|
|
||||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||||
import { ClearConfig } from "../components/config";
|
import { FetchConfig, ClearConfig } from "../utils/config";
|
||||||
|
|
||||||
const authProvider: AuthProvider = {
|
const authProvider: AuthProvider = {
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
@@ -26,7 +25,7 @@ const authProvider: AuthProvider = {
|
|||||||
body: JSON.stringify(
|
body: JSON.stringify(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{
|
{
|
||||||
device_id: storage.getItem("device_id"),
|
device_id: localStorage.getItem("device_id"),
|
||||||
initial_device_display_name: "Synapse Admin",
|
initial_device_display_name: "Synapse Admin",
|
||||||
},
|
},
|
||||||
loginToken
|
loginToken
|
||||||
@@ -52,11 +51,11 @@ const authProvider: AuthProvider = {
|
|||||||
if (!base_url) {
|
if (!base_url) {
|
||||||
// there is some kind of bug with base_url being present in the form, but not submitted
|
// 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
|
// ref: https://github.com/etkecc/synapse-admin/issues/14
|
||||||
storage.removeItem("base_url")
|
localStorage.removeItem("base_url")
|
||||||
throw new Error("Homeserver URL is required.");
|
throw new Error("Homeserver URL is required.");
|
||||||
}
|
}
|
||||||
base_url = base_url.replace(/\/+$/g, "");
|
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);
|
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");
|
let login_api_url = decoded_base_url + (accessToken ? "/_matrix/client/v3/account/whoami" : "/_matrix/client/v3/login");
|
||||||
@@ -76,11 +75,16 @@ const authProvider: AuthProvider = {
|
|||||||
|
|
||||||
response = await fetchUtils.fetchJson(login_api_url, options);
|
response = await fetchUtils.fetchJson(login_api_url, options);
|
||||||
const json = response.json;
|
const json = response.json;
|
||||||
storage.setItem("home_server", accessToken ? json.user_id.split(":")[1] : json.home_server);
|
localStorage.setItem("home_server", accessToken ? json.user_id.split(":")[1] : json.home_server);
|
||||||
storage.setItem("user_id", json.user_id);
|
localStorage.setItem("user_id", json.user_id);
|
||||||
storage.setItem("access_token", accessToken ? accessToken : json.access_token);
|
localStorage.setItem("access_token", accessToken ? accessToken : json.access_token);
|
||||||
storage.setItem("device_id", json.device_id);
|
localStorage.setItem("device_id", json.device_id);
|
||||||
storage.setItem("login_type", accessToken ? "accessToken" : "credentials");
|
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) {
|
||||||
|
await FetchConfig();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve({redirectTo: "/"});
|
return Promise.resolve({redirectTo: "/"});
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
@@ -98,9 +102,9 @@ const authProvider: AuthProvider = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getIdentity: async () => {
|
getIdentity: async () => {
|
||||||
const access_token = storage.getItem("access_token");
|
const access_token = localStorage.getItem("access_token");
|
||||||
const user_id = storage.getItem("user_id");
|
const user_id = localStorage.getItem("user_id");
|
||||||
const base_url = storage.getItem("base_url");
|
const base_url = localStorage.getItem("base_url");
|
||||||
|
|
||||||
if (typeof access_token !== "string" || typeof user_id !== "string" || typeof base_url !== "string") {
|
if (typeof access_token !== "string" || typeof user_id !== "string" || typeof base_url !== "string") {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
@@ -138,8 +142,8 @@ const authProvider: AuthProvider = {
|
|||||||
logout: async () => {
|
logout: async () => {
|
||||||
console.log("logout");
|
console.log("logout");
|
||||||
|
|
||||||
const logout_api_url = storage.getItem("base_url") + "/_matrix/client/v3/logout";
|
const logout_api_url = localStorage.getItem("base_url") + "/_matrix/client/v3/logout";
|
||||||
const access_token = storage.getItem("access_token");
|
const access_token = localStorage.getItem("access_token");
|
||||||
|
|
||||||
const options: Options = {
|
const options: Options = {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -171,8 +175,7 @@ const authProvider: AuthProvider = {
|
|||||||
},
|
},
|
||||||
// called when the user navigates to a new location, to check for authentication
|
// called when the user navigates to a new location, to check for authentication
|
||||||
checkAuth: () => {
|
checkAuth: () => {
|
||||||
const access_token = storage.getItem("access_token");
|
const access_token = localStorage.getItem("access_token");
|
||||||
console.log("checkAuth " + access_token);
|
|
||||||
return typeof access_token === "string" ? Promise.resolve() : Promise.reject();
|
return typeof access_token === "string" ? Promise.resolve() : Promise.reject();
|
||||||
},
|
},
|
||||||
// called when the user navigates to a new location, to check for permissions / roles
|
// called when the user navigates to a new location, to check for permissions / roles
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import fetchMock from "jest-fetch-mock";
|
import fetchMock from "jest-fetch-mock";
|
||||||
|
|
||||||
import dataProvider from "./dataProvider";
|
import dataProvider from "./dataProvider";
|
||||||
import storage from "../storage";
|
|
||||||
|
|
||||||
fetchMock.enableMocks();
|
fetchMock.enableMocks();
|
||||||
|
|
||||||
@@ -10,8 +9,8 @@ beforeEach(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("dataProvider", () => {
|
describe("dataProvider", () => {
|
||||||
storage.setItem("base_url", "http://localhost");
|
localStorage.setItem("base_url", "http://localhost");
|
||||||
storage.setItem("access_token", "access_token");
|
localStorage.setItem("access_token", "access_token");
|
||||||
|
|
||||||
it("fetches all users", async () => {
|
it("fetches all users", async () => {
|
||||||
fetchMock.mockResponseOnce(
|
fetchMock.mockResponseOnce(
|
||||||
|
@@ -13,13 +13,12 @@ import {
|
|||||||
withLifecycleCallbacks,
|
withLifecycleCallbacks,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
|
||||||
import storage from "../storage";
|
import { returnMXID } from "../utils/mxid";
|
||||||
import { returnMXID } from "./synapse";
|
import { MatrixError, displayError } from "../utils/error";
|
||||||
import { MatrixError, displayError } from "../components/error";
|
|
||||||
|
|
||||||
// Adds the access token to all requests
|
// Adds the access token to all requests
|
||||||
const jsonClient = async (url: string, options: Options = {}) => {
|
const jsonClient = async (url: string, options: Options = {}) => {
|
||||||
const token = storage.getItem("access_token");
|
const token = localStorage.getItem("access_token");
|
||||||
console.log("httpClient " + url);
|
console.log("httpClient " + url);
|
||||||
if (token !== null) {
|
if (token !== null) {
|
||||||
options.user = {
|
options.user = {
|
||||||
@@ -401,7 +400,7 @@ const resourceMap = {
|
|||||||
data: "media",
|
data: "media",
|
||||||
total: json => json.total,
|
total: json => json.total,
|
||||||
delete: (params: DeleteParams) => ({
|
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: {
|
protect_media: {
|
||||||
@@ -418,11 +417,11 @@ const resourceMap = {
|
|||||||
quarantine_media: {
|
quarantine_media: {
|
||||||
map: (qm: UserMedia) => ({ id: qm.media_id }),
|
map: (qm: UserMedia) => ({ id: qm.media_id }),
|
||||||
create: (params: UserMedia) => ({
|
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",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
delete: (params: DeleteParams) => ({
|
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",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -567,7 +566,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
order_by: field,
|
order_by: field,
|
||||||
dir: getSearchOrder(order),
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -586,7 +585,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
getOne: async (resource, params) => {
|
getOne: async (resource, params) => {
|
||||||
console.log("getOne " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -598,8 +597,8 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
getMany: async (resource, params) => {
|
getMany: async (resource, params) => {
|
||||||
console.log("getMany " + resource);
|
console.log("getMany " + resource);
|
||||||
const base_url = storage.getItem("base_url");
|
const base_url = localStorage.getItem("base_url");
|
||||||
const homeserver = storage.getItem("home_server");
|
const homeserver = localStorage.getItem("home_server");
|
||||||
if (!base_url || !(resource in resourceMap)) throw Error("base_url not set");
|
if (!base_url || !(resource in resourceMap)) throw Error("base_url not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -638,7 +637,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
dir: getSearchOrder(order),
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -655,7 +654,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
update: async (resource, params) => {
|
update: async (resource, params) => {
|
||||||
console.log("update " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -670,7 +669,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
updateMany: async (resource, params) => {
|
updateMany: async (resource, params) => {
|
||||||
console.log("updateMany " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -687,7 +686,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
create: async (resource, params) => {
|
create: async (resource, params) => {
|
||||||
console.log("create " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -704,7 +703,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
createMany: async (resource: string, params: { ids: Identifier[]; data: RaRecord }) => {
|
createMany: async (resource: string, params: { ids: Identifier[]; data: RaRecord }) => {
|
||||||
console.log("createMany " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -726,7 +725,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
delete: async (resource, params) => {
|
delete: async (resource, params) => {
|
||||||
console.log("delete " + resource);
|
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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -751,7 +750,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
|
|
||||||
deleteMany: async (resource, params) => {
|
deleteMany: async (resource, params) => {
|
||||||
console.log("deleteMany " + resource, "params", 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");
|
if (!homeserver || !(resource in resourceMap)) throw Error("Homeserver not set");
|
||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
@@ -798,17 +797,17 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
deleteMedia: async ({ before_ts, size_gt = 0, keep_profiles = true }) => {
|
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 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 endpoint_url = base_url + endpoint;
|
||||||
const { json } = await jsonClient(endpoint_url, { method: "POST" });
|
const { json } = await jsonClient(endpoint_url, { method: "POST" });
|
||||||
return json as DeleteMediaResult;
|
return json as DeleteMediaResult;
|
||||||
},
|
},
|
||||||
|
|
||||||
uploadMedia: async ({ file, filename, content_type }: UploadMediaParams) => {
|
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 uploadMediaURL = `${base_url}/_matrix/media/v3/upload`;
|
||||||
|
|
||||||
const { json } = await jsonClient(`${uploadMediaURL}?filename=${filename}`, {
|
const { json } = await jsonClient(`${uploadMediaURL}?filename=${filename}`, {
|
||||||
@@ -822,18 +821,18 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
return json as UploadMediaResult;
|
return json as UploadMediaResult;
|
||||||
},
|
},
|
||||||
getFeatures: async (id: Identifier) => {
|
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 endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
|
||||||
const { json } = await jsonClient(endpoint_url);
|
const { json } = await jsonClient(endpoint_url);
|
||||||
return json.features as ExperimentalFeaturesModel;
|
return json.features as ExperimentalFeaturesModel;
|
||||||
},
|
},
|
||||||
updateFeatures: async (id: Identifier, features: 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))}`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
|
||||||
await jsonClient(endpoint_url, { method: "PUT", body: JSON.stringify({ features }) });
|
await jsonClient(endpoint_url, { method: "PUT", body: JSON.stringify({ features }) });
|
||||||
},
|
},
|
||||||
getRateLimits: async (id: Identifier) => {
|
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 endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
|
||||||
const { json } = await jsonClient(endpoint_url);
|
const { json } = await jsonClient(endpoint_url);
|
||||||
return json as RateLimitsModel;
|
return json as RateLimitsModel;
|
||||||
@@ -846,7 +845,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
return obj;
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
|
||||||
if (Object.keys(filtered).length === 0) {
|
if (Object.keys(filtered).length === 0) {
|
||||||
await jsonClient(endpoint_url, { method: "DELETE" });
|
await jsonClient(endpoint_url, { method: "DELETE" });
|
||||||
@@ -856,7 +855,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify(filtered) });
|
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify(filtered) });
|
||||||
},
|
},
|
||||||
checkUsernameAvailability: async (username: string) => {
|
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)}`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/username_available?username=${encodeURIComponent(username)}`;
|
||||||
try {
|
try {
|
||||||
const { json } = await jsonClient(endpoint_url);
|
const { json } = await jsonClient(endpoint_url);
|
||||||
@@ -869,7 +868,7 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
makeRoomAdmin: async (room_id: string, user_id: string) => {
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/rooms/${encodeURIComponent(room_id)}/make_room_admin`;
|
||||||
try {
|
try {
|
||||||
@@ -914,13 +913,13 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
|
|||||||
},
|
},
|
||||||
beforeDelete: async (params: DeleteParams<any>, dataProvider: DataProvider) => {
|
beforeDelete: async (params: DeleteParams<any>, dataProvider: DataProvider) => {
|
||||||
if (params.meta?.deleteMedia) {
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(params.id))}/media`;
|
||||||
await jsonClient(endpoint_url, { method: "DELETE" });
|
await jsonClient(endpoint_url, { method: "DELETE" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.meta?.redactEvents) {
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/user/${encodeURIComponent(returnMXID(params.id))}/redact`;
|
||||||
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
|
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
|
||||||
}
|
}
|
||||||
@@ -931,13 +930,13 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
params.ids.map(async id => {
|
params.ids.map(async id => {
|
||||||
if (params.meta?.deleteMedia) {
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/media`;
|
||||||
await jsonClient(endpoint_url, { method: "DELETE" });
|
await jsonClient(endpoint_url, { method: "DELETE" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.meta?.redactEvents) {
|
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`;
|
const endpoint_url = `${base_url}/_synapse/admin/v1/user/${encodeURIComponent(returnMXID(id))}/redact`;
|
||||||
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
|
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify({ rooms: [] }) });
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { isValidBaseUrl, splitMxid } from "./synapse";
|
import { isValidBaseUrl, splitMxid } from "./matrix";
|
||||||
|
|
||||||
describe("splitMxid", () => {
|
describe("splitMxid", () => {
|
||||||
it("splits valid MXIDs", () =>
|
it("splits valid MXIDs", () =>
|
@@ -1,6 +1,6 @@
|
|||||||
import { Identifier, fetchUtils } from "react-admin";
|
import { Identifier, fetchUtils } from "react-admin";
|
||||||
|
|
||||||
import storage from "../storage";
|
import { isMXID } from "../utils/mxid";
|
||||||
|
|
||||||
export const splitMxid = mxid => {
|
export const splitMxid = mxid => {
|
||||||
const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/;
|
const re = /^@(?<name>[a-zA-Z0-9._=\-/]+):(?<domain>[a-zA-Z0-9\-.]+\.[a-zA-Z]+)$/;
|
||||||
@@ -49,51 +49,7 @@ export const getSupportedFeatures = async baseUrl => {
|
|||||||
* @returns array of supported login flows
|
* @returns array of supported login flows
|
||||||
*/
|
*/
|
||||||
export const getSupportedLoginFlows = async baseUrl => {
|
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" });
|
const response = await fetchUtils.fetchJson(loginFlowsUrl, { method: "GET" });
|
||||||
return response.json.flows;
|
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 (typeof input === 'string' && mxidPattern.test(input)) {
|
|
||||||
return input; // 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("");
|
|
||||||
}
|
|
82
src/utils/config.ts
Normal file
82
src/utils/config.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
export interface Config {
|
||||||
|
restrictBaseUrl: string | string[];
|
||||||
|
asManagedUsers: RegExp[];
|
||||||
|
menu: MenuItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MenuItem {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WellKnownKey = "cc.etke.synapse-admin";
|
||||||
|
|
||||||
|
// current configuration
|
||||||
|
let config: Config = {
|
||||||
|
restrictBaseUrl: "",
|
||||||
|
asManagedUsers: [],
|
||||||
|
menu: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FetchConfig = async () => {
|
||||||
|
try {
|
||||||
|
const resp = await fetch("config.json");
|
||||||
|
const configJSON = await resp.json();
|
||||||
|
console.log("Loaded config.json", configJSON);
|
||||||
|
LoadConfig(configJSON);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if home_server is set, try to load https://home_server/.well-known/matrix/client
|
||||||
|
const homeserver = localStorage.getItem("home_server");
|
||||||
|
if (homeserver) {
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
|
||||||
|
const configWK = await resp.json();
|
||||||
|
if (!configWK[WellKnownKey]) {
|
||||||
|
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
|
||||||
|
} else {
|
||||||
|
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
|
||||||
|
LoadConfig(configWK[WellKnownKey]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// load config from context
|
||||||
|
export const LoadConfig = (context: any) => {
|
||||||
|
if (context?.restrictBaseUrl) {
|
||||||
|
config.restrictBaseUrl = context.restrictBaseUrl as string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context?.asManagedUsers) {
|
||||||
|
config.asManagedUsers = context.asManagedUsers.map((regex: string) => new RegExp(regex));
|
||||||
|
}
|
||||||
|
|
||||||
|
let menu: MenuItem[] = [];
|
||||||
|
if (context?.menu) {
|
||||||
|
menu = context.menu as MenuItem[];
|
||||||
|
}
|
||||||
|
if (menu.length > 0) {
|
||||||
|
config.menu = menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get config
|
||||||
|
export const GetConfig = (): Config => {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// clear config
|
||||||
|
export const ClearConfig = () => {
|
||||||
|
// config.json
|
||||||
|
config = {} as Config;
|
||||||
|
// session
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
@@ -1,9 +1,6 @@
|
|||||||
import storage from "../storage";
|
|
||||||
|
|
||||||
export const getServerAndMediaIdFromMxcUrl = (mxcUrl: string): { serverName: string, mediaId: string } => {
|
export const getServerAndMediaIdFromMxcUrl = (mxcUrl: string): { serverName: string, mediaId: string } => {
|
||||||
const re = /^mxc:\/\/([^/]+)\/(\w+)/;
|
const re = /^mxc:\/\/([^/]+)\/(\w+)/;
|
||||||
const ret = re.exec(mxcUrl);
|
const ret = re.exec(mxcUrl);
|
||||||
console.log("mxcClient " + ret);
|
|
||||||
if (ret == null) {
|
if (ret == null) {
|
||||||
throw new Error("Invalid mxcUrl");
|
throw new Error("Invalid mxcUrl");
|
||||||
}
|
}
|
||||||
@@ -15,8 +12,8 @@ export const getServerAndMediaIdFromMxcUrl = (mxcUrl: string): { serverName: str
|
|||||||
export type MediaType = "thumbnail" | "original";
|
export type MediaType = "thumbnail" | "original";
|
||||||
|
|
||||||
export const fetchAuthenticatedMedia = async (mxcUrl: string, type: MediaType): Promise<Response> => {
|
export const fetchAuthenticatedMedia = async (mxcUrl: string, type: MediaType): Promise<Response> => {
|
||||||
const homeserver = storage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
const accessToken = storage.getItem("access_token");
|
const accessToken = localStorage.getItem("access_token");
|
||||||
|
|
||||||
const { serverName, mediaId } = getServerAndMediaIdFromMxcUrl(mxcUrl);
|
const { serverName, mediaId } = getServerAndMediaIdFromMxcUrl(mxcUrl);
|
||||||
if (!serverName || !mediaId) {
|
if (!serverName || !mediaId) {
|
||||||
|
16
src/utils/icons.ts
Normal file
16
src/utils/icons.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import AnnouncementIcon from '@mui/icons-material/Announcement';
|
||||||
|
import EngineeringIcon from '@mui/icons-material/Engineering';
|
||||||
|
import HelpCenterIcon from '@mui/icons-material/HelpCenter';
|
||||||
|
import SupportAgentIcon from '@mui/icons-material/SupportAgent';
|
||||||
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||||
|
|
||||||
|
export const Icons = {
|
||||||
|
Announcement: AnnouncementIcon,
|
||||||
|
Engineering: EngineeringIcon,
|
||||||
|
HelpCenter: HelpCenterIcon,
|
||||||
|
SupportAgent: SupportAgentIcon,
|
||||||
|
Default: OpenInNewIcon,
|
||||||
|
// Add more icons as needed
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultIcon = Icons.Default;
|
52
src/utils/mxid.ts
Normal file
52
src/utils/mxid.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Identifier } from "ra-core";
|
||||||
|
import { GetConfig } from "../utils/config";
|
||||||
|
|
||||||
|
const mxidPattern = /^@[^@:]+:[^@:]+$/;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if id is a valid Matrix ID (user)
|
||||||
|
* @param id The ID to check
|
||||||
|
* @returns Whether the ID is a valid Matrix ID
|
||||||
|
*/
|
||||||
|
export const isMXID = (id: string | Identifier): boolean => mxidPattern.test(id as string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a user is managed by an application service
|
||||||
|
* @param id The user ID to check
|
||||||
|
* @returns Whether the user is managed by an application service
|
||||||
|
*/
|
||||||
|
export const isASManaged = (id: string | Identifier): boolean => {
|
||||||
|
return GetConfig().asManagedUsers.some(regex => regex.test(id as string));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random MXID for current homeserver
|
||||||
|
* @returns full MXID as string
|
||||||
|
*/
|
||||||
|
export function generateRandomMXID(): string {
|
||||||
|
const homeserver = localStorage.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 = localStorage.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}`;
|
||||||
|
}
|
10
src/utils/password.ts
Normal file
10
src/utils/password.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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("");
|
||||||
|
}
|
452
yarn.lock
452
yarn.lock
@@ -413,16 +413,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/trace-mapping" "0.3.9"
|
"@jridgewell/trace-mapping" "0.3.9"
|
||||||
|
|
||||||
"@emotion/babel-plugin@^11.12.0":
|
"@emotion/babel-plugin@^11.13.5":
|
||||||
version "11.12.0"
|
version "11.13.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0"
|
||||||
integrity sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==
|
integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-module-imports" "^7.16.7"
|
"@babel/helper-module-imports" "^7.16.7"
|
||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
"@emotion/hash" "^0.9.2"
|
"@emotion/hash" "^0.9.2"
|
||||||
"@emotion/memoize" "^0.9.0"
|
"@emotion/memoize" "^0.9.0"
|
||||||
"@emotion/serialize" "^1.2.0"
|
"@emotion/serialize" "^1.3.3"
|
||||||
babel-plugin-macros "^3.1.0"
|
babel-plugin-macros "^3.1.0"
|
||||||
convert-source-map "^1.5.0"
|
convert-source-map "^1.5.0"
|
||||||
escape-string-regexp "^4.0.0"
|
escape-string-regexp "^4.0.0"
|
||||||
@@ -430,14 +430,14 @@
|
|||||||
source-map "^0.5.7"
|
source-map "^0.5.7"
|
||||||
stylis "4.2.0"
|
stylis "4.2.0"
|
||||||
|
|
||||||
"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.13.1":
|
"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.1", "@emotion/cache@^11.13.5":
|
||||||
version "11.13.1"
|
version "11.13.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.5.tgz#e78dad0489e1ed7572507ba8ed9d2130529e4266"
|
||||||
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
|
integrity sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/memoize" "^0.9.0"
|
"@emotion/memoize" "^0.9.0"
|
||||||
"@emotion/sheet" "^1.4.0"
|
"@emotion/sheet" "^1.4.0"
|
||||||
"@emotion/utils" "^1.4.0"
|
"@emotion/utils" "^1.4.2"
|
||||||
"@emotion/weak-memoize" "^0.4.0"
|
"@emotion/weak-memoize" "^0.4.0"
|
||||||
stylis "4.2.0"
|
stylis "4.2.0"
|
||||||
|
|
||||||
@@ -458,40 +458,29 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz"
|
||||||
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
|
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
|
||||||
|
|
||||||
"@emotion/react@^11.13.0", "@emotion/react@^11.4.1":
|
"@emotion/react@^11.13.5", "@emotion/react@^11.4.1":
|
||||||
version "11.13.3"
|
version "11.13.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.5.tgz#fc818ff5b13424f86501ba4d0740f343ae20b8d9"
|
||||||
integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==
|
integrity sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
"@emotion/babel-plugin" "^11.12.0"
|
"@emotion/babel-plugin" "^11.13.5"
|
||||||
"@emotion/cache" "^11.13.0"
|
"@emotion/cache" "^11.13.5"
|
||||||
"@emotion/serialize" "^1.3.1"
|
"@emotion/serialize" "^1.3.3"
|
||||||
"@emotion/use-insertion-effect-with-fallbacks" "^1.1.0"
|
"@emotion/use-insertion-effect-with-fallbacks" "^1.1.0"
|
||||||
"@emotion/utils" "^1.4.0"
|
"@emotion/utils" "^1.4.2"
|
||||||
"@emotion/weak-memoize" "^0.4.0"
|
"@emotion/weak-memoize" "^0.4.0"
|
||||||
hoist-non-react-statics "^3.3.1"
|
hoist-non-react-statics "^3.3.1"
|
||||||
|
|
||||||
"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0", "@emotion/serialize@^1.3.1":
|
"@emotion/serialize@^1.3.2", "@emotion/serialize@^1.3.3":
|
||||||
version "1.3.1"
|
version "1.3.3"
|
||||||
resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8"
|
||||||
integrity sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==
|
integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/hash" "^0.9.2"
|
"@emotion/hash" "^0.9.2"
|
||||||
"@emotion/memoize" "^0.9.0"
|
"@emotion/memoize" "^0.9.0"
|
||||||
"@emotion/unitless" "^0.10.0"
|
"@emotion/unitless" "^0.10.0"
|
||||||
"@emotion/utils" "^1.4.0"
|
"@emotion/utils" "^1.4.2"
|
||||||
csstype "^3.0.2"
|
|
||||||
|
|
||||||
"@emotion/serialize@^1.3.2":
|
|
||||||
version "1.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.2.tgz#e1c1a2e90708d5d85d81ccaee2dfeb3cc0cccf7a"
|
|
||||||
integrity sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==
|
|
||||||
dependencies:
|
|
||||||
"@emotion/hash" "^0.9.2"
|
|
||||||
"@emotion/memoize" "^0.9.0"
|
|
||||||
"@emotion/unitless" "^0.10.0"
|
|
||||||
"@emotion/utils" "^1.4.1"
|
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
"@emotion/sheet@^1.4.0":
|
"@emotion/sheet@^1.4.0":
|
||||||
@@ -499,17 +488,17 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz"
|
||||||
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
|
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
|
||||||
|
|
||||||
"@emotion/styled@^11.13.0", "@emotion/styled@^11.3.0":
|
"@emotion/styled@^11.13.5", "@emotion/styled@^11.3.0":
|
||||||
version "11.13.0"
|
version "11.13.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.13.5.tgz#0fa6602227414c5e42cf267506e3c35bae655df9"
|
||||||
integrity sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==
|
integrity sha512-gnOQ+nGLPvDXgIx119JqGalys64lhMdnNQA9TMxhDA4K0Hq5+++OE20Zs5GxiCV9r814xQ2K5WmtofSpHVW6BQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.18.3"
|
"@babel/runtime" "^7.18.3"
|
||||||
"@emotion/babel-plugin" "^11.12.0"
|
"@emotion/babel-plugin" "^11.13.5"
|
||||||
"@emotion/is-prop-valid" "^1.3.0"
|
"@emotion/is-prop-valid" "^1.3.0"
|
||||||
"@emotion/serialize" "^1.3.0"
|
"@emotion/serialize" "^1.3.3"
|
||||||
"@emotion/use-insertion-effect-with-fallbacks" "^1.1.0"
|
"@emotion/use-insertion-effect-with-fallbacks" "^1.1.0"
|
||||||
"@emotion/utils" "^1.4.0"
|
"@emotion/utils" "^1.4.2"
|
||||||
|
|
||||||
"@emotion/unitless@^0.10.0":
|
"@emotion/unitless@^0.10.0":
|
||||||
version "0.10.0"
|
version "0.10.0"
|
||||||
@@ -521,15 +510,10 @@
|
|||||||
resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz"
|
||||||
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
|
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
|
||||||
|
|
||||||
"@emotion/utils@^1.4.0":
|
"@emotion/utils@^1.4.2":
|
||||||
version "1.4.0"
|
version "1.4.2"
|
||||||
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz"
|
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52"
|
||||||
integrity sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==
|
integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==
|
||||||
|
|
||||||
"@emotion/utils@^1.4.1":
|
|
||||||
version "1.4.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.1.tgz#b3adbb43de12ee2149541c4f1337d2eb7774f0ad"
|
|
||||||
integrity sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==
|
|
||||||
|
|
||||||
"@emotion/weak-memoize@^0.4.0":
|
"@emotion/weak-memoize@^0.4.0":
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
@@ -1006,10 +990,10 @@
|
|||||||
resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz"
|
resolved "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz"
|
||||||
integrity sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==
|
integrity sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==
|
||||||
|
|
||||||
"@mui/core-downloads-tracker@^6.1.7":
|
"@mui/core-downloads-tracker@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.7.tgz#80f4327804ee97b6af7b11980f87e29dc2231226"
|
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.8.tgz#4fccd4eb9960ffa17ebf0533a6e9cfd2787cba57"
|
||||||
integrity sha512-POuIBi80BZBogQkG4PQKIGwy4QFwB+kOr+OI4k7Znh7LqMAIhwB9OC00l6M+w1GrZJYj3T8R5WX8G6QAIvoVEw==
|
integrity sha512-TGAvzwUg9hybDacwfIGFjI2bXYXrIqky+vMfaeay8rvT56/PNAlvIDUJ54kpT5KRc9AWAihOvtDI7/LJOThOmQ==
|
||||||
|
|
||||||
"@mui/icons-material@^5.15.20":
|
"@mui/icons-material@^5.15.20":
|
||||||
version "5.16.7"
|
version "5.16.7"
|
||||||
@@ -1018,10 +1002,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.23.9"
|
"@babel/runtime" "^7.23.9"
|
||||||
|
|
||||||
"@mui/icons-material@^6.1.7":
|
"@mui/icons-material@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-6.1.7.tgz#79c0fa7087acbcba2fe81f89d9dda15cb65c6742"
|
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-6.1.8.tgz#696fe487c16a6caba5b10cf72c5954dce4d33401"
|
||||||
integrity sha512-RGzkeHNArIVy5ZQ12bq/8VYNeICEyngngsFskTJ/2hYKhIeIII3iRGtaZaSvLpXh7h3Fg3VKTulT+QU0w5K4XQ==
|
integrity sha512-6frsXcf1TcJKWevWwRup6V4L8lzI33cbHcAjT83YLgKw0vYRZKY0kjMI9fhrJZdRWXgFFgKKvEv3GjoxbqFF7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
|
|
||||||
@@ -1043,16 +1027,16 @@
|
|||||||
react-is "^18.3.1"
|
react-is "^18.3.1"
|
||||||
react-transition-group "^4.4.5"
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
"@mui/material@^6.1.7":
|
"@mui/material@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.1.7.tgz#a30dcfdae6e3cbb184669813d6b3a8addd4540ee"
|
resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.1.8.tgz#67cc5796aaa3f81ac94783fd79c85c80062e4a99"
|
||||||
integrity sha512-KsjujQL/A2hLd1PV3QboF+W6SSL5QqH6ZlSuQoeYz9r69+TnyBFIevbYLxdjJcJmGBjigL5pfpn7hTGop+vhSg==
|
integrity sha512-QZdQFnXct+7NXIzHgT3qt+sQiO7HYGZU2vymP9Xl9tUMXEOA/S1mZMMb7+WGZrk5TzNlU/kP/85K0da5V1jXoQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/core-downloads-tracker" "^6.1.7"
|
"@mui/core-downloads-tracker" "^6.1.8"
|
||||||
"@mui/system" "^6.1.7"
|
"@mui/system" "^6.1.8"
|
||||||
"@mui/types" "^7.2.19"
|
"@mui/types" "^7.2.19"
|
||||||
"@mui/utils" "^6.1.7"
|
"@mui/utils" "^6.1.8"
|
||||||
"@popperjs/core" "^2.11.8"
|
"@popperjs/core" "^2.11.8"
|
||||||
"@types/react-transition-group" "^4.4.11"
|
"@types/react-transition-group" "^4.4.11"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
@@ -1070,13 +1054,13 @@
|
|||||||
"@mui/utils" "^5.16.6"
|
"@mui/utils" "^5.16.6"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/private-theming@^6.1.7":
|
"@mui/private-theming@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.1.7.tgz#9415c32eb567194b713941243cf18fde09981ba1"
|
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.1.8.tgz#3ac194689bd4a4bb79816508e9fcc00708753d12"
|
||||||
integrity sha512-uLbfUSsug5K0LVkv0PI6Flste3le8+6WSL2omdTiYde93P89Qr7pKr8TA6d2yXfr+Bm+SvD8/fGnkaRwFkryuQ==
|
integrity sha512-TuKl7msynCNCVvhX3c0ef1sF0Qb3VHcPs8XOGB/8bdOGBr/ynmIG1yTMjZeiFQXk8yN9fzK/FDEKMFxILNn3wg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/utils" "^6.1.7"
|
"@mui/utils" "^6.1.8"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/styled-engine@^5.16.6":
|
"@mui/styled-engine@^5.16.6":
|
||||||
@@ -1089,10 +1073,10 @@
|
|||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/styled-engine@^6.1.7":
|
"@mui/styled-engine@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.1.7.tgz#2e38642325afe473dd14349c95a56e947640b154"
|
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.1.8.tgz#8e99ff036d171b811eff0f0421c7aae8b0b9ac35"
|
||||||
integrity sha512-Ou4CxN7MQmwrfG1Pu6EYjPgPChQXxPDJrwgizLXlRPOad5qAq4gYXRuzrGQ2DfGjjwmJhjI8T6A0SeapAZPGig==
|
integrity sha512-ZvEoT0U2nPLSLI+B4by4cVjaZnPT2f20f4JUPkyHdwLv65ZzuoHiTlwyhqX1Ch63p8bcJzKTHQVGisEoMK6PGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@emotion/cache" "^11.13.1"
|
"@emotion/cache" "^11.13.1"
|
||||||
@@ -1115,16 +1099,16 @@
|
|||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/system@^6.1.7":
|
"@mui/system@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.1.7.tgz#041d450b842d5c2fb1608d4027360ad2ba25bd9a"
|
resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.1.8.tgz#8ec7eabbb40a6888ebc526fd919b8f3a9d8eeece"
|
||||||
integrity sha512-qbMGgcC/FodpuRSfjXlEDdbNQaW++eATh0vNBcPUv2/YXSpReoOpoT9FhogxEBNks+aQViDXBRZKh6HX2fVmwg==
|
integrity sha512-i1kLfQoWxzFpXTBQIuPoA3xKnAnP3en4I2T8xIolovSolGQX5k8vGjw1JaydQS40td++cFsgCdEU458HDNTGUA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/private-theming" "^6.1.7"
|
"@mui/private-theming" "^6.1.8"
|
||||||
"@mui/styled-engine" "^6.1.7"
|
"@mui/styled-engine" "^6.1.8"
|
||||||
"@mui/types" "^7.2.19"
|
"@mui/types" "^7.2.19"
|
||||||
"@mui/utils" "^6.1.7"
|
"@mui/utils" "^6.1.8"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
@@ -1146,10 +1130,10 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.3.1"
|
react-is "^18.3.1"
|
||||||
|
|
||||||
"@mui/utils@^6.1.7":
|
"@mui/utils@^6.1.8":
|
||||||
version "6.1.7"
|
version "6.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.1.7.tgz#0959d9772ae13c6ceac984a493e06aebb9087e71"
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.1.8.tgz#ae07cad4f6099eeb43dbc71267b4a96304ba3982"
|
||||||
integrity sha512-Gr7cRZxBoZ0BIa3Xqf/2YaUrBLyNPJvXPQH3OsD9WMZukI/TutibbQBVqLYpgqJn8pKSjbD50Yq2auG0wI1xOw==
|
integrity sha512-O2DWb1kz8hiANVcR7Z4gOB3SvPPsSQGUmStpyBDzde6dJIfBzgV9PbEQOBZd3EBsd1pB+Uv1z5LAJAbymmawrA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.26.0"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/types" "^7.2.19"
|
"@mui/types" "^7.2.19"
|
||||||
@@ -1298,17 +1282,17 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@sinonjs/commons" "^3.0.0"
|
"@sinonjs/commons" "^3.0.0"
|
||||||
|
|
||||||
"@tanstack/query-core@5.60.5":
|
"@tanstack/query-core@5.60.6":
|
||||||
version "5.60.5"
|
version "5.60.6"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.60.5.tgz#37b7c5ab7e6894cea9ef341299a7a3febc2ea361"
|
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.60.6.tgz#0dd33fe231b0d18bf66d0c615b29899738300658"
|
||||||
integrity sha512-jiS1aC3XI3BJp83ZiTuDLerTmn9P3U95r6p+6/SNauLJaYxfIC4dMuWygwnBHIZxjn2zJqEpj3nysmPieoxfPQ==
|
integrity sha512-tI+k0KyCo1EBJ54vxK1kY24LWj673ujTydCZmzEZKAew4NqZzTaVQJEuaG1qKj2M03kUHN46rchLRd+TxVq/zQ==
|
||||||
|
|
||||||
"@tanstack/react-query@^5.60.5", "@tanstack/react-query@^5.8.4":
|
"@tanstack/react-query@^5.21.7", "@tanstack/react-query@^5.61.3":
|
||||||
version "5.60.5"
|
version "5.61.3"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.60.5.tgz#3194c390f7eff20542b321c3042880dc3f1a81e2"
|
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.61.3.tgz#0187b73b87adaeaed09f3d9717e35b507175fe23"
|
||||||
integrity sha512-M77bOsPwj1wYE56gk7iJvxGAr4IC12NWdIDhT+Eo8ldkWRHMvIR8I/rufIvT1OXoV/bl7EECwuRuMlxxWtvW2Q==
|
integrity sha512-c3Oz9KaCBapGkRewu7AJLhxE9BVqpMcHsd3KtFxSd7FSCu2qGwqfIN37zbSGoyk6Ix9LGZBNHQDPI6GpWABnmA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-core" "5.60.5"
|
"@tanstack/query-core" "5.60.6"
|
||||||
|
|
||||||
"@testing-library/dom@^10.0.0":
|
"@testing-library/dom@^10.0.0":
|
||||||
version "10.4.0"
|
version "10.4.0"
|
||||||
@@ -1480,10 +1464,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
|
||||||
integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
|
integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@^22.8.7":
|
"@types/node@*", "@types/node@^22.9.3":
|
||||||
version "22.8.7"
|
version "22.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.3.tgz#08f3d64b3bc6d74b162d36f60213e8a6704ef2b4"
|
||||||
integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==
|
integrity sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.19.8"
|
undici-types "~6.19.8"
|
||||||
|
|
||||||
@@ -1541,100 +1525,62 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@8.13.0":
|
"@typescript-eslint/eslint-plugin@8.15.0", "@typescript-eslint/eslint-plugin@^8.14.0":
|
||||||
version "8.13.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz#650c50b8c795b5d092189f139f6d00535b5b0f3d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz#c95c6521e70c8b095a684d884d96c0c1c63747d2"
|
||||||
integrity sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==
|
integrity sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp" "^4.10.0"
|
"@eslint-community/regexpp" "^4.10.0"
|
||||||
"@typescript-eslint/scope-manager" "8.13.0"
|
"@typescript-eslint/scope-manager" "8.15.0"
|
||||||
"@typescript-eslint/type-utils" "8.13.0"
|
"@typescript-eslint/type-utils" "8.15.0"
|
||||||
"@typescript-eslint/utils" "8.13.0"
|
"@typescript-eslint/utils" "8.15.0"
|
||||||
"@typescript-eslint/visitor-keys" "8.13.0"
|
"@typescript-eslint/visitor-keys" "8.15.0"
|
||||||
graphemer "^1.4.0"
|
graphemer "^1.4.0"
|
||||||
ignore "^5.3.1"
|
ignore "^5.3.1"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
ts-api-utils "^1.3.0"
|
ts-api-utils "^1.3.0"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^8.14.0":
|
"@typescript-eslint/parser@8.15.0", "@typescript-eslint/parser@^8.15.0":
|
||||||
version "8.14.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz#7dc0e419c87beadc8f554bf5a42e5009ed3748dc"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.15.0.tgz#92610da2b3af702cfbc02a46e2a2daa6260a9045"
|
||||||
integrity sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==
|
integrity sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp" "^4.10.0"
|
"@typescript-eslint/scope-manager" "8.15.0"
|
||||||
"@typescript-eslint/scope-manager" "8.14.0"
|
"@typescript-eslint/types" "8.15.0"
|
||||||
"@typescript-eslint/type-utils" "8.14.0"
|
"@typescript-eslint/typescript-estree" "8.15.0"
|
||||||
"@typescript-eslint/utils" "8.14.0"
|
"@typescript-eslint/visitor-keys" "8.15.0"
|
||||||
"@typescript-eslint/visitor-keys" "8.14.0"
|
|
||||||
graphemer "^1.4.0"
|
|
||||||
ignore "^5.3.1"
|
|
||||||
natural-compare "^1.4.0"
|
|
||||||
ts-api-utils "^1.3.0"
|
|
||||||
|
|
||||||
"@typescript-eslint/parser@8.13.0", "@typescript-eslint/parser@^8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.13.0.tgz#ef76203b7cac515aa3ccc4f7ce5320dd61c46b29"
|
|
||||||
integrity sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==
|
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/scope-manager" "8.13.0"
|
|
||||||
"@typescript-eslint/types" "8.13.0"
|
|
||||||
"@typescript-eslint/typescript-estree" "8.13.0"
|
|
||||||
"@typescript-eslint/visitor-keys" "8.13.0"
|
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@8.13.0":
|
"@typescript-eslint/scope-manager@8.15.0":
|
||||||
version "8.13.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz#2f4aed0b87d72360e64e4ea194b1fde14a59082e"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6"
|
||||||
integrity sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==
|
integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.13.0"
|
"@typescript-eslint/types" "8.15.0"
|
||||||
"@typescript-eslint/visitor-keys" "8.13.0"
|
"@typescript-eslint/visitor-keys" "8.15.0"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@8.14.0":
|
"@typescript-eslint/type-utils@8.15.0":
|
||||||
version "8.14.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz#01f37c147a735cd78f0ff355e033b9457da1f373"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz#a6da0f93aef879a68cc66c73fe42256cb7426c72"
|
||||||
integrity sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==
|
integrity sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.14.0"
|
"@typescript-eslint/typescript-estree" "8.15.0"
|
||||||
"@typescript-eslint/visitor-keys" "8.14.0"
|
"@typescript-eslint/utils" "8.15.0"
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz#8c8fa68490dcb9ae1687ffc7de8fbe23c26417bd"
|
|
||||||
integrity sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==
|
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/typescript-estree" "8.13.0"
|
|
||||||
"@typescript-eslint/utils" "8.13.0"
|
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
ts-api-utils "^1.3.0"
|
ts-api-utils "^1.3.0"
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@8.14.0":
|
"@typescript-eslint/types@8.15.0":
|
||||||
version "8.14.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz#455c6af30c336b24a1af28bc4f81b8dd5d74d94d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.15.0.tgz#4958edf3d83e97f77005f794452e595aaf6430fc"
|
||||||
integrity sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==
|
integrity sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree@8.15.0":
|
||||||
|
version "8.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz#915c94e387892b114a2a2cc0df2d7f19412c8ba7"
|
||||||
|
integrity sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree" "8.14.0"
|
"@typescript-eslint/types" "8.15.0"
|
||||||
"@typescript-eslint/utils" "8.14.0"
|
"@typescript-eslint/visitor-keys" "8.15.0"
|
||||||
debug "^4.3.4"
|
|
||||||
ts-api-utils "^1.3.0"
|
|
||||||
|
|
||||||
"@typescript-eslint/types@8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.13.0.tgz#3f35dead2b2491a04339370dcbcd17bbdfc204d8"
|
|
||||||
integrity sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==
|
|
||||||
|
|
||||||
"@typescript-eslint/types@8.14.0":
|
|
||||||
version "8.14.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.14.0.tgz#0d33d8d0b08479c424e7d654855fddf2c71e4021"
|
|
||||||
integrity sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz#db8c93dd5437ca3ce417a255fb35ddc3c12c3e95"
|
|
||||||
integrity sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==
|
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/types" "8.13.0"
|
|
||||||
"@typescript-eslint/visitor-keys" "8.13.0"
|
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
fast-glob "^3.3.2"
|
fast-glob "^3.3.2"
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
@@ -1642,55 +1588,23 @@
|
|||||||
semver "^7.6.0"
|
semver "^7.6.0"
|
||||||
ts-api-utils "^1.3.0"
|
ts-api-utils "^1.3.0"
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@8.14.0":
|
"@typescript-eslint/utils@8.15.0":
|
||||||
version "8.14.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz#a7a3a5a53a6c09313e12fb4531d4ff582ee3c312"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.15.0.tgz#ac04679ad19252776b38b81954b8e5a65567cef6"
|
||||||
integrity sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==
|
integrity sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/types" "8.14.0"
|
|
||||||
"@typescript-eslint/visitor-keys" "8.14.0"
|
|
||||||
debug "^4.3.4"
|
|
||||||
fast-glob "^3.3.2"
|
|
||||||
is-glob "^4.0.3"
|
|
||||||
minimatch "^9.0.4"
|
|
||||||
semver "^7.6.0"
|
|
||||||
ts-api-utils "^1.3.0"
|
|
||||||
|
|
||||||
"@typescript-eslint/utils@8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.13.0.tgz#f6d40e8b5053dcaeabbd2e26463857abf27d62c0"
|
|
||||||
integrity sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.4.0"
|
"@eslint-community/eslint-utils" "^4.4.0"
|
||||||
"@typescript-eslint/scope-manager" "8.13.0"
|
"@typescript-eslint/scope-manager" "8.15.0"
|
||||||
"@typescript-eslint/types" "8.13.0"
|
"@typescript-eslint/types" "8.15.0"
|
||||||
"@typescript-eslint/typescript-estree" "8.13.0"
|
"@typescript-eslint/typescript-estree" "8.15.0"
|
||||||
|
|
||||||
"@typescript-eslint/utils@8.14.0":
|
"@typescript-eslint/visitor-keys@8.15.0":
|
||||||
version "8.14.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.14.0.tgz#ac2506875e03aba24e602364e43b2dfa45529dbd"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz#9ea5a85eb25401d2aa74ec8a478af4e97899ea12"
|
||||||
integrity sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==
|
integrity sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.4.0"
|
"@typescript-eslint/types" "8.15.0"
|
||||||
"@typescript-eslint/scope-manager" "8.14.0"
|
eslint-visitor-keys "^4.2.0"
|
||||||
"@typescript-eslint/types" "8.14.0"
|
|
||||||
"@typescript-eslint/typescript-estree" "8.14.0"
|
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@8.13.0":
|
|
||||||
version "8.13.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz#e97b0d92b266ef38a1faf40a74da289b66683a5b"
|
|
||||||
integrity sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==
|
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/types" "8.13.0"
|
|
||||||
eslint-visitor-keys "^3.4.3"
|
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@8.14.0":
|
|
||||||
version "8.14.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz#2418d5a54669af9658986ade4e6cfb7767d815ad"
|
|
||||||
integrity sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==
|
|
||||||
dependencies:
|
|
||||||
"@typescript-eslint/types" "8.14.0"
|
|
||||||
eslint-visitor-keys "^3.4.3"
|
|
||||||
|
|
||||||
"@vitejs/plugin-react@^4.3.3":
|
"@vitejs/plugin-react@^4.3.3":
|
||||||
version "4.3.3"
|
version "4.3.3"
|
||||||
@@ -2839,7 +2753,7 @@ eslint-scope@^8.2.0:
|
|||||||
esrecurse "^4.3.0"
|
esrecurse "^4.3.0"
|
||||||
estraverse "^5.2.0"
|
estraverse "^5.2.0"
|
||||||
|
|
||||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
|
eslint-visitor-keys@^3.3.0:
|
||||||
version "3.4.3"
|
version "3.4.3"
|
||||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||||
@@ -4717,12 +4631,12 @@ ra-core@^4.11.2, ra-core@^4.16.2:
|
|||||||
react-is "^17.0.2"
|
react-is "^17.0.2"
|
||||||
react-query "^3.32.1"
|
react-query "^3.32.1"
|
||||||
|
|
||||||
ra-core@^5.3.3, ra-core@^5.3.4:
|
ra-core@^5.3.4, ra-core@^5.4.0:
|
||||||
version "5.3.4"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-5.3.4.tgz#849f6da3460b896f295f5a88ea2b8a1487c7111d"
|
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-5.4.0.tgz#db05e39959c6836a72236517e344ae361d6273c9"
|
||||||
integrity sha512-BC6pO8jaUjJESfO0FS6J2CMgeRreJ2kpdPkkySBODM0M5S4ZRC0rOFh1jBXFnjSUcNoPWaVc/WaZPwfAg00Wmw==
|
integrity sha512-Gvp7FiItL0fYq0r9C5jU0Gm56096c2FfOY/UaCDnOvshs3UGTLd5cknRZ30lo9Qr+SoZPP8PH8Ef6LblEXkFBA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/react-query" "^5.8.4"
|
"@tanstack/react-query" "^5.21.7"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
date-fns "^3.6.0"
|
date-fns "^3.6.0"
|
||||||
eventemitter3 "^5.0.1"
|
eventemitter3 "^5.0.1"
|
||||||
@@ -4733,32 +4647,32 @@ ra-core@^5.3.3, ra-core@^5.3.4:
|
|||||||
react-error-boundary "^4.0.13"
|
react-error-boundary "^4.0.13"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
ra-i18n-polyglot@^5.3.3, ra-i18n-polyglot@^5.3.4:
|
ra-i18n-polyglot@^5.3.4, ra-i18n-polyglot@^5.4.0:
|
||||||
version "5.3.4"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-5.3.4.tgz#f4653d6d6a9bcf1f52d65993e2a6e5acf2b7e000"
|
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-5.4.0.tgz#0c8f71e0c03874a5b87ef2c4eaacee497f23b036"
|
||||||
integrity sha512-9BtWoCHE5LxIN4+VJGOrxL29lDhCTgBjIDkTb/i228vr/CD3+ozjyMiBG8NGeLN5wCqtHpsKDwUNJOcoPtwkiQ==
|
integrity sha512-2bRIa/y1PgZFjMir3bLEo1DcLEkWr7L7jrZuTXc0gXL5E8O6tGMaWkURxJyp1iIeZ0pUiPEahr6rePWwHVeZhA==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-polyglot "^2.2.2"
|
node-polyglot "^2.2.2"
|
||||||
ra-core "^5.3.4"
|
ra-core "^5.4.0"
|
||||||
|
|
||||||
ra-language-english@^5.3.3, ra-language-english@^5.3.4:
|
ra-language-english@^5.3.4, ra-language-english@^5.4.0:
|
||||||
version "5.3.4"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-5.3.4.tgz#a1ffcd848ec1b080f4dc3728332e0ceedb1f6783"
|
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-5.4.0.tgz#e1f8dc76ba46faec64ddd40f5c10cfa6d919274f"
|
||||||
integrity sha512-QR0ej0JKQaGmtGSh1W+O4HnoaTv/9SQBtpztAkTitPVjlCGJeNBr/IjriAxsp0ezhhCr+uZrwrCfoWjKPkjpDg==
|
integrity sha512-RUV6wo8EIFOjC4UA6d+FbfVslnvnX8f+UhElzuxRvDhXZqmEyRKxsFCLTbeI9DF/IN8Gb6pvKxCoe3jzfUcm5Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
ra-core "^5.3.4"
|
ra-core "^5.4.0"
|
||||||
|
|
||||||
ra-language-farsi@^5.0.0:
|
ra-language-farsi@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.npmjs.org/ra-language-farsi/-/ra-language-farsi-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/ra-language-farsi/-/ra-language-farsi-5.0.0.tgz"
|
||||||
integrity sha512-wdveom01mdPY3KiFBWj4cc1f1YhWWi4pqNIuY79jgbJ43Kk72qOQIr3byWipy/Nt7dYzrAP3IvXmylWLLMmgnw==
|
integrity sha512-wdveom01mdPY3KiFBWj4cc1f1YhWWi4pqNIuY79jgbJ43Kk72qOQIr3byWipy/Nt7dYzrAP3IvXmylWLLMmgnw==
|
||||||
|
|
||||||
ra-language-french@^5.3.4:
|
ra-language-french@^5.4.0:
|
||||||
version "5.3.4"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ra-language-french/-/ra-language-french-5.3.4.tgz#11c16d9fcefcf1e9f82ac291fe7f6beb083a1a22"
|
resolved "https://registry.yarnpkg.com/ra-language-french/-/ra-language-french-5.4.0.tgz#b7d9f117f57a57f11e5d2ed09d0a2620cd916740"
|
||||||
integrity sha512-7w+Z7Dpvo+hSa3MWcM3O6HnbGgrhi9RnDxDygDCOlV01oDgYBz+sgZE+aZUzOHkmVdvCT1hpaytGJYqiT06Sqg==
|
integrity sha512-Hdi9W1CLAh8oq8DvUzQ6gk4m9l4vdnGXdy5ZUGBmAo9x5QKfdZlWGkim/0HvzucYpnNmWFRVQaT9vMdGq11jBA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ra-core "^5.3.4"
|
ra-core "^5.4.0"
|
||||||
|
|
||||||
ra-language-italian@^3.13.1:
|
ra-language-italian@^3.13.1:
|
||||||
version "3.13.1"
|
version "3.13.1"
|
||||||
@@ -4772,12 +4686,12 @@ ra-language-russian@^4.14.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ra-core "^4.16.2"
|
ra-core "^4.16.2"
|
||||||
|
|
||||||
ra-ui-materialui@^5.3.3:
|
ra-ui-materialui@^5.4.0:
|
||||||
version "5.3.3"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-5.3.3.tgz#2f928e668c0242f2e103afc96016a108256dc23e"
|
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-5.4.0.tgz#92511c3274026243ac9ebe027d6aa9eef6b60f7f"
|
||||||
integrity sha512-zaODfIoN+uc/9wuzlgFA0CHWZPpHs8kxtSi4i2HwBEFY42x/UoPsCqsRgLYGGJofBqxlD1UqzFRuVAKTEM/1bg==
|
integrity sha512-FwBIboQrUXyB2ztZH150zcq5v6qMiGn2aNc9OoTcft0hoWuVBS1yIE8B67JfyZyH0azgNW20ImPbC5lYDs1l1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/react-query" "^5.8.4"
|
"@tanstack/react-query" "^5.21.7"
|
||||||
autosuggest-highlight "^3.1.1"
|
autosuggest-highlight "^3.1.1"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
css-mediaquery "^0.1.2"
|
css-mediaquery "^0.1.2"
|
||||||
@@ -4790,19 +4704,19 @@ ra-ui-materialui@^5.3.3:
|
|||||||
react-error-boundary "^4.0.13"
|
react-error-boundary "^4.0.13"
|
||||||
react-transition-group "^4.4.5"
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
react-admin@^5.3.3:
|
react-admin@^5.4.0:
|
||||||
version "5.3.3"
|
version "5.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-5.3.3.tgz#0db4d69db7eca3e0bfcb42858e069b6710c8bed8"
|
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-5.4.0.tgz#463208d8d03da0ad5ee21f818a236a37c4aec13b"
|
||||||
integrity sha512-TAN3G7/JHQZpR1CieREjYFepSkQQdS4b70k060UVkeUL4RRU1c9whDfnSsUVuO56mnxTBKpQx0e/VHablSmBwA==
|
integrity sha512-tvGyMUSXKzfqkQKf/uRowTpVhNM03Lo9iZq7e3BgO1nTOTFXqK/QcPbECpRH6IsLgH47uSDQoeqViYkB3ut9cA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/react" "^11.4.1"
|
"@emotion/react" "^11.4.1"
|
||||||
"@emotion/styled" "^11.3.0"
|
"@emotion/styled" "^11.3.0"
|
||||||
"@mui/icons-material" "^5.15.20"
|
"@mui/icons-material" "^5.15.20"
|
||||||
"@mui/material" "^5.15.20"
|
"@mui/material" "^5.15.20"
|
||||||
ra-core "^5.3.3"
|
ra-core "^5.4.0"
|
||||||
ra-i18n-polyglot "^5.3.3"
|
ra-i18n-polyglot "^5.4.0"
|
||||||
ra-language-english "^5.3.3"
|
ra-language-english "^5.4.0"
|
||||||
ra-ui-materialui "^5.3.3"
|
ra-ui-materialui "^5.4.0"
|
||||||
react-hook-form "^7.53.0"
|
react-hook-form "^7.53.0"
|
||||||
react-router "^6.22.0"
|
react-router "^6.22.0"
|
||||||
react-router-dom "^6.22.0"
|
react-router-dom "^6.22.0"
|
||||||
@@ -4831,10 +4745,10 @@ react-error-boundary@^4.0.13:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
react-hook-form@^7.53.0, react-hook-form@^7.53.1:
|
react-hook-form@^7.53.0, react-hook-form@^7.53.2:
|
||||||
version "7.53.1"
|
version "7.53.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.1.tgz#3f2cd1ed2b3af99416a4ac674da2d526625add67"
|
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.53.2.tgz#6fa37ae27330af81089baadd7f322cc987b8e2ac"
|
||||||
integrity sha512-6aiQeBda4zjcuaugWvim9WsGqisoUk+etmFEsSUMm451/Ic8L/UAb7sRtMj3V+Hdzm6mMjU1VhiSzYUZeBm0Vg==
|
integrity sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==
|
||||||
|
|
||||||
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0, react-is@^18.3.1:
|
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0, react-is@^18.3.1:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
@@ -5504,19 +5418,19 @@ typed-array-length@^1.0.6:
|
|||||||
is-typed-array "^1.1.13"
|
is-typed-array "^1.1.13"
|
||||||
possible-typed-array-names "^1.0.0"
|
possible-typed-array-names "^1.0.0"
|
||||||
|
|
||||||
typescript-eslint@^8.13.0:
|
typescript-eslint@^8.15.0:
|
||||||
version "8.13.0"
|
version "8.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.13.0.tgz#c7d92cc06188176c7d0e3825e10305b9c22fb102"
|
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.15.0.tgz#c8a2a0d183c3eb48ae176aa078c1b9daa584cf9d"
|
||||||
integrity sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==
|
integrity sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/eslint-plugin" "8.13.0"
|
"@typescript-eslint/eslint-plugin" "8.15.0"
|
||||||
"@typescript-eslint/parser" "8.13.0"
|
"@typescript-eslint/parser" "8.15.0"
|
||||||
"@typescript-eslint/utils" "8.13.0"
|
"@typescript-eslint/utils" "8.15.0"
|
||||||
|
|
||||||
typescript@^5.6.3:
|
typescript@^5.7.2:
|
||||||
version "5.6.3"
|
version "5.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||||
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
|
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||||
|
|
||||||
unbox-primitive@^1.0.2:
|
unbox-primitive@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
Reference in New Issue
Block a user