Compare commits
17 Commits
v0.10.3-et
...
v0.10.3-et
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b925c63171 | ||
![]() |
6faebaf9df | ||
![]() |
c698f57395 | ||
![]() |
9adc13e722 | ||
![]() |
a04b24a5d5 | ||
![]() |
cd1ca7c039 | ||
![]() |
86b4987b7f | ||
![]() |
a7cf647669 | ||
![]() |
80d40d2fb5 | ||
![]() |
b19e961a35 | ||
![]() |
08f5f8ebd2 | ||
![]() |
1edf196049 | ||
![]() |
1f66b4d14a | ||
![]() |
cec5b0af9a | ||
![]() |
2b0e1e7c0e | ||
![]() |
a613a88232 | ||
![]() |
7afce71bef |
67
README.md
67
README.md
@@ -86,6 +86,11 @@ with a proper manifest.json generation on build)
|
|||||||
* [Enable visual customization](https://github.com/etkecc/synapse-admin/pull/81)
|
* [Enable visual customization](https://github.com/etkecc/synapse-admin/pull/81)
|
||||||
* [Fix room state events display](https://github.com/etkecc/synapse-admin/pull/100)
|
* [Fix room state events display](https://github.com/etkecc/synapse-admin/pull/100)
|
||||||
* [Sanitize CSV on import](https://github.com/etkecc/synapse-admin/pull/101)
|
* [Sanitize CSV on import](https://github.com/etkecc/synapse-admin/pull/101)
|
||||||
|
* Allow setting version using `SYNAPSE_ADMIN_VERSION` environment variable on build (if git is not available)
|
||||||
|
* [Add option to control user's experimental features](https://github.com/etkecc/synapse-admin/pull/111)
|
||||||
|
* [Add random password generation on user create/edit form](https://github.com/etkecc/synapse-admin/pull/123)
|
||||||
|
* [Add option to set user's rate limits](https://github.com/etkecc/synapse-admin/pull/125)
|
||||||
|
* [Support configuration via /.well-known/matrix/client](https://github.com/etkecc/synapse-admin/pull/126)
|
||||||
|
|
||||||
_the list will be updated as new changes are added_
|
_the list will be updated as new changes are added_
|
||||||
|
|
||||||
@@ -102,7 +107,11 @@ After that open `http://localhost:5173` in your browser, login using the followi
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
You can use `config.json` file to configure synapse-admin
|
You can use `config.json` file to configure Synapse Admin instance,
|
||||||
|
and `/.well-known/matrix/client` file to provide Synapse Admin configuration specifically for your homeserver.
|
||||||
|
In the latter case, any instance of Synapse Admin will automatically pick up the configuration from the homeserver.
|
||||||
|
Note that configuration inside the `/.well-known/matrix/client` file should go under the `cc.etke.synapse-admin` key,
|
||||||
|
and it will override the configuration from the `config.json` file.
|
||||||
|
|
||||||
The `config.json` can be injected into a Docker container using a bind mount.
|
The `config.json` can be injected into a Docker container using a bind mount.
|
||||||
|
|
||||||
@@ -127,6 +136,16 @@ Edit `config.json` to restrict either to a single homeserver:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
similar for `/.well-known/matrix/client`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cc.etke.synapse-admin": {
|
||||||
|
"restrictBaseUrl": "https://your-matrixs-erver.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
or to a list of homeservers:
|
or to a list of homeservers:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -135,6 +154,16 @@ or to a list of homeservers:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
similar for `/.well-known/matrix/client`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cc.etke.synapse-admin": {
|
||||||
|
"restrictBaseUrl": ["https://your-first-matrix-server.example.com", "https://your-second-matrix-server.example.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Protecting appservice managed users
|
### Protecting appservice managed users
|
||||||
|
|
||||||
To avoid accidental adjustments of appservice-managed users (e.g., puppets created by a bridge) and breaking the bridge,
|
To avoid accidental adjustments of appservice-managed users (e.g., puppets created by a bridge) and breaking the bridge,
|
||||||
@@ -148,6 +177,16 @@ Example for [mautrix-telegram](https://github.com/mautrix/telegram)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
similar for `/.well-known/matrix/client`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cc.etke.synapse-admin": {
|
||||||
|
"asManagedUsers": ["^@telegram_[a-zA-Z0-9]+:example\\.com$"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Adding custom menu items
|
### Adding custom menu items
|
||||||
|
|
||||||
You can add custom menu items to the main menu by providing a `menu` array in the `config.json`.
|
You can add custom menu items to the main menu by providing a `menu` array in the `config.json`.
|
||||||
@@ -164,6 +203,22 @@ You can add custom menu items to the main menu by providing a `menu` array in th
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
similar for `/.well-known/matrix/client`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cc.etke.synapse-admin": {
|
||||||
|
"menu": [
|
||||||
|
{
|
||||||
|
"label": "Contact support",
|
||||||
|
"icon": "SupportAgent",
|
||||||
|
"url": "https://github.com/etkecc/synapse-admin/issues"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
||||||
|
|
||||||
### Providing support URL
|
### Providing support URL
|
||||||
@@ -178,6 +233,16 @@ Where `icon` is one of the [preloaded icons](./src/components/icons.ts)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
similar for `/.well-known/matrix/client`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cc.etke.synapse-admin": {
|
||||||
|
"supportURL": "https://example.com/support"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Supported Synapse
|
### Supported Synapse
|
||||||
|
18
package.json
18
package.json
@@ -13,12 +13,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.13.0",
|
"@eslint/js": "^9.13.0",
|
||||||
"@testing-library/dom": "^10.0.0",
|
"@testing-library/dom": "^10.0.0",
|
||||||
"@testing-library/jest-dom": "^6.6.2",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@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.12",
|
"@types/lodash": "^4.17.13",
|
||||||
"@types/node": "^22.8.1",
|
"@types/node": "^22.8.7",
|
||||||
"@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.11.0",
|
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
"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.6.3",
|
||||||
"typescript-eslint": "^8.11.0",
|
"typescript-eslint": "^8.12.2",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.10",
|
||||||
"vite-plugin-version-mark": "^0.1.2"
|
"vite-plugin-version-mark": "^0.1.2"
|
||||||
},
|
},
|
||||||
@@ -49,15 +49,15 @@
|
|||||||
"@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.5",
|
"@mui/icons-material": "^6.1.5",
|
||||||
"@mui/material": "^6.1.5",
|
"@mui/material": "^6.1.6",
|
||||||
"@mui/utils": "^5.16.6",
|
"@mui/utils": "^5.16.6",
|
||||||
"@tanstack/react-query": "^5.59.16",
|
"@tanstack/react-query": "^5.59.19",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"papaparse": "^5.4.1",
|
"papaparse": "^5.4.1",
|
||||||
"ra-core": "^5.3.1",
|
"ra-core": "^5.3.2",
|
||||||
"ra-i18n-polyglot": "^5.3.0",
|
"ra-i18n-polyglot": "^5.3.2",
|
||||||
"ra-language-english": "^5.3.1",
|
"ra-language-english": "^5.3.2",
|
||||||
"ra-language-farsi": "^5.0.0",
|
"ra-language-farsi": "^5.0.0",
|
||||||
"ra-language-french": "^5.3.1",
|
"ra-language-french": "^5.3.1",
|
||||||
"ra-language-italian": "^3.13.1",
|
"ra-language-italian": "^3.13.1",
|
||||||
|
@@ -1,18 +1,6 @@
|
|||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
|
import { Config } from "./components/config";
|
||||||
interface AppContextType {
|
|
||||||
restrictBaseUrl: string | string[];
|
|
||||||
asManagedUsers: string[];
|
|
||||||
supportURL: string;
|
|
||||||
menu: MenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MenuItem {
|
|
||||||
label: string;
|
|
||||||
icon: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AppContext = createContext({});
|
export const AppContext = createContext({});
|
||||||
|
|
||||||
export const useAppContext = () => useContext(AppContext) as AppContextType;
|
export const useAppContext = () => useContext(AppContext) as Config;
|
||||||
|
@@ -2,6 +2,7 @@ import { AppBar, TitlePortal, InspectorButton, Confirm, Layout, Logout, Menu, us
|
|||||||
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 "./icons";
|
||||||
|
import { ClearConfig } from "./config";
|
||||||
|
|
||||||
const AdminUserMenu = () => {
|
const AdminUserMenu = () => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -21,8 +22,7 @@ const AdminUserMenu = () => {
|
|||||||
|
|
||||||
const handleDialogClose = () => {
|
const handleDialogClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
localStorage.removeItem("access_token");
|
ClearConfig();
|
||||||
localStorage.removeItem("login_type");
|
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
95
src/components/ExperimentalFeatures.tsx
Normal file
95
src/components/ExperimentalFeatures.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { useRecordContext } from "react-admin";
|
||||||
|
import { useNotify } from "react-admin";
|
||||||
|
import { useDataProvider } from "react-admin";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Stack, Switch, Typography } from "@mui/material";
|
||||||
|
import { ExperimentalFeaturesModel, SynapseDataProvider } from "../synapse/dataProvider";
|
||||||
|
|
||||||
|
const experimentalFeaturesMap = {
|
||||||
|
msc3881: "enable remotely toggling push notifications for another client",
|
||||||
|
msc3575: "enable experimental sliding sync support",
|
||||||
|
};
|
||||||
|
const ExperimentalFeatureRow = (props: { featureKey: string, featureValue: boolean, updateFeature: (feature_name: string, feature_value: boolean) => void}) => {
|
||||||
|
const featureKey = props.featureKey;
|
||||||
|
const featureValue = props.featureValue;
|
||||||
|
const featureDescription = experimentalFeaturesMap[featureKey] ?? "";
|
||||||
|
const [checked, setChecked] = useState(featureValue);
|
||||||
|
|
||||||
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setChecked(event.target.checked);
|
||||||
|
props.updateFeature(featureKey, event.target.checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Stack
|
||||||
|
direction="row"
|
||||||
|
spacing={2}
|
||||||
|
alignItems="start"
|
||||||
|
sx={{
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Switch checked={checked} onChange={handleChange} />
|
||||||
|
<Stack>
|
||||||
|
<Typography
|
||||||
|
variant="subtitle1"
|
||||||
|
sx={{
|
||||||
|
fontWeight: "medium",
|
||||||
|
color: "text.primary"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{featureKey}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
color="text.secondary"
|
||||||
|
>
|
||||||
|
{featureDescription}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ExperimentalFeaturesList = () => {
|
||||||
|
const record = useRecordContext();
|
||||||
|
const notify = useNotify();
|
||||||
|
const dataProvider = useDataProvider() as SynapseDataProvider;
|
||||||
|
const [features, setFeatures] = useState({});
|
||||||
|
if (!record) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchFeatures = async () => {
|
||||||
|
const features = await dataProvider.getFeatures(record.id);
|
||||||
|
setFeatures(features);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchFeatures();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateFeature = async (feature_name: string, feature_value: boolean) => {
|
||||||
|
const updatedFeatures = {...features, [feature_name]: feature_value} as ExperimentalFeaturesModel;
|
||||||
|
setFeatures(updatedFeatures);
|
||||||
|
const reponse = await dataProvider.updateFeatures(record.id, updatedFeatures);
|
||||||
|
notify("ra.notification.updated", {
|
||||||
|
messageArgs: { smart_count: 1 },
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
{Object.keys(features).map((featureKey: string) =>
|
||||||
|
<ExperimentalFeatureRow
|
||||||
|
key={featureKey}
|
||||||
|
featureKey={featureKey}
|
||||||
|
featureValue={features[featureKey]}
|
||||||
|
updateFeature={updateFeature}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
}
|
@@ -22,7 +22,7 @@ const Footer = () => {
|
|||||||
borderColor: '#ddd',
|
borderColor: '#ddd',
|
||||||
p: 1,
|
p: 1,
|
||||||
}}>
|
}}>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2" component="div">
|
||||||
<Avatar src="./images/logo.webp" sx={{ width: "1rem", height: "1rem", display: "inline-block", verticalAlign: "sub" }} />
|
<Avatar src="./images/logo.webp" sx={{ width: "1rem", height: "1rem", display: "inline-block", verticalAlign: "sub" }} />
|
||||||
<Link sx={{ color: "#888", textDecoration: 'none' }} href="https://github.com/etkecc/synapse-admin" target="_blank">
|
<Link sx={{ color: "#888", textDecoration: 'none' }} href="https://github.com/etkecc/synapse-admin" target="_blank">
|
||||||
Synapse Admin
|
Synapse Admin
|
||||||
|
90
src/components/UserRateLimits.tsx
Normal file
90
src/components/UserRateLimits.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Stack, Typography } from "@mui/material";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useDataProvider, useNotify, useRecordContext, useTranslate } from "react-admin";
|
||||||
|
import { TextField } from "@mui/material";
|
||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
const RateLimitRow = ({ limit, value, updateRateLimit }: { limit: string, value: number, updateRateLimit: (limit: string, value: number) => void }) => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
|
||||||
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
updateRateLimit(limit, parseInt(event.target.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Stack
|
||||||
|
spacing={1}
|
||||||
|
alignItems="start"
|
||||||
|
sx={{
|
||||||
|
padding: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
id="outlined-number"
|
||||||
|
type="number"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
slotProps={{
|
||||||
|
inputLabel: {
|
||||||
|
shrink: true,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
label={translate(`resources.users.limits.${limit}`)}
|
||||||
|
/>
|
||||||
|
<Stack>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
color="text.secondary"
|
||||||
|
>
|
||||||
|
{translate(`resources.users.limits.${limit}_text`)}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserRateLimits = () => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
const notify = useNotify();
|
||||||
|
const record = useRecordContext();
|
||||||
|
const form = useFormContext();
|
||||||
|
const dataProvider = useDataProvider();
|
||||||
|
const [rateLimits, setRateLimits] = useState({
|
||||||
|
messages_per_second: 0,
|
||||||
|
burst_count: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchRateLimits = async () => {
|
||||||
|
const rateLimits = await dataProvider.getRateLimits(record.id);
|
||||||
|
if (Object.keys(rateLimits).length > 0) {
|
||||||
|
setRateLimits(rateLimits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchRateLimits();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateRateLimit = async (limit: string, value: number) => {
|
||||||
|
let updatedRateLimits = { ...rateLimits, [limit]: value };
|
||||||
|
setRateLimits(updatedRateLimits);
|
||||||
|
form.setValue(`rates.${limit}`, value, { shouldDirty: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
{Object.keys(rateLimits).map((limit: string) =>
|
||||||
|
<RateLimitRow
|
||||||
|
key={limit}
|
||||||
|
limit={limit}
|
||||||
|
value={rateLimits[limit]}
|
||||||
|
updateRateLimit={updateRateLimit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
};
|
87
src/components/config.ts
Normal file
87
src/components/config.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
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");
|
||||||
|
}
|
@@ -55,7 +55,7 @@ const de: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
|
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
details: "Raumdetails",
|
details: "Raumdetails",
|
||||||
@@ -190,7 +190,14 @@ const de: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "Avatar löschen",
|
erase_avatar: "Avatar löschen",
|
||||||
delete_media: "Alle von dem/den Benutzer(n) hochgeladenen Medien löschen",
|
delete_media: "Alle von dem/den Benutzer(n) hochgeladenen Medien löschen",
|
||||||
redact_events: "Schwärzen aller vom Benutzer gesendeten Ereignisse (-s)",
|
redact_events: "Schwärzen aller vom Benutzer gesendeten Ereignisse (-s)",
|
||||||
|
generate_password: "Passwort generieren",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "Nachrichten pro Sekunde",
|
||||||
|
messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können. 0 bedeutet, dass die Rate-Limitierung für diesen Benutzer deaktiviert ist.",
|
||||||
|
burst_count: "Burst-Anzahl",
|
||||||
|
burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Raum |||| Räume",
|
name: "Raum |||| Räume",
|
||||||
|
@@ -25,7 +25,11 @@ const en: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "Localpart of a Matrix user-id without homeserver.",
|
invalid_user_id: "Localpart of a Matrix user-id without homeserver.",
|
||||||
tabs: { sso: "SSO" },
|
tabs: {
|
||||||
|
sso: "SSO",
|
||||||
|
experimental: "Experimental",
|
||||||
|
limits: "Rate Limits",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
details: "Room details",
|
details: "Room details",
|
||||||
@@ -159,7 +163,14 @@ const en: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "Erase avatar",
|
erase_avatar: "Erase avatar",
|
||||||
delete_media: "Delete all media uploaded by the user(-s)",
|
delete_media: "Delete all media uploaded by the user(-s)",
|
||||||
redact_events: "Redact all events sent by the user(-s)",
|
redact_events: "Redact all events sent by the user(-s)",
|
||||||
|
generate_password: "Generate password",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "Messages per second",
|
||||||
|
messages_per_second_text: "The number of actions that can be performed in a second. 0 mean that ratelimiting is disabled for this user",
|
||||||
|
burst_count: "Burst count",
|
||||||
|
burst_count_text: "How many actions that can be performed before being limited.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Room |||| Rooms",
|
name: "Room |||| Rooms",
|
||||||
|
@@ -24,7 +24,7 @@ const fa: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
|
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -155,7 +155,14 @@ const fa: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "محو الصورة الرمزية",
|
erase_avatar: "محو الصورة الرمزية",
|
||||||
delete_media: "حذف جميع الوسائط التي تم تحميلها بواسطة المستخدم (المستخدمين)",
|
delete_media: "حذف جميع الوسائط التي تم تحميلها بواسطة المستخدم (المستخدمين)",
|
||||||
redact_events: "تنقيح جميع الأحداث المرسلة من قبل المستخدم (-s)",
|
redact_events: "تنقيح جميع الأحداث المرسلة من قبل المستخدم (-s)",
|
||||||
|
generate_password: "توليد رمز عبور",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "پیام در ثانیه",
|
||||||
|
messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود. 0 به معنای غیرفعال کردن محدودیت برای این کاربر است.",
|
||||||
|
burst_count: "تعداد پیچیدگی",
|
||||||
|
burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "اتاق |||| اتاق ها",
|
name: "اتاق |||| اتاق ها",
|
||||||
@@ -207,6 +214,11 @@ const fa: SynapseTranslationMessages = {
|
|||||||
title: "حذف اتاق",
|
title: "حذف اتاق",
|
||||||
content:
|
content:
|
||||||
"آیا مطمئن هستید که می خواهید اتاق را حذف کنید؟ این قابل بازگشت نیست. همه پیام ها و رسانه های مشترک در اتاق از سرور حذف می شوند!",
|
"آیا مطمئن هستید که می خواهید اتاق را حذف کنید؟ این قابل بازگشت نیست. همه پیام ها و رسانه های مشترک در اتاق از سرور حذف می شوند!",
|
||||||
|
fields: {
|
||||||
|
block: "حذف",
|
||||||
|
},
|
||||||
|
success: "اتاق با موفقیت حذف شد.",
|
||||||
|
failure: "خطایی رخ داده است.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -24,7 +24,7 @@ const fr: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "Partie locale d'un identifiant utilisateur Matrix sans le nom du serveur d’accueil.",
|
invalid_user_id: "Partie locale d'un identifiant utilisateur Matrix sans le nom du serveur d’accueil.",
|
||||||
tabs: { sso: "Authentification unique" },
|
tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -157,7 +157,14 @@ const fr: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "Effacer l'avatar",
|
erase_avatar: "Effacer l'avatar",
|
||||||
delete_media: "Supprimer tous les médias téléchargés par le(s) utilisateur(s)",
|
delete_media: "Supprimer tous les médias téléchargés par le(s) utilisateur(s)",
|
||||||
redact_events: "Expurger tous les événements envoyés par l'utilisateur(-s)",
|
redact_events: "Expurger tous les événements envoyés par l'utilisateur(-s)",
|
||||||
|
generate_password: "Générer un mot de passe",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "Messages par seconde",
|
||||||
|
messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde. 0 signifie que la limitation est désactivée pour cet utilisateur.",
|
||||||
|
burst_count: "Compteur de pics",
|
||||||
|
burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Salon |||| Salons",
|
name: "Salon |||| Salons",
|
||||||
|
9
src/i18n/index.d.ts
vendored
9
src/i18n/index.d.ts
vendored
@@ -22,7 +22,7 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
};
|
};
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: string;
|
invalid_user_id: string;
|
||||||
tabs: { sso: string };
|
tabs: { sso: string; experimental: string; limits: string; };
|
||||||
};
|
};
|
||||||
rooms: {
|
rooms: {
|
||||||
details?: string; // TODO: fa, fr, it, zh
|
details?: string; // TODO: fa, fr, it, zh
|
||||||
@@ -155,6 +155,13 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
erase_avatar: string;
|
erase_avatar: string;
|
||||||
delete_media: string;
|
delete_media: string;
|
||||||
redact_events: string;
|
redact_events: string;
|
||||||
|
generate_password: string;
|
||||||
|
};
|
||||||
|
limits: {
|
||||||
|
messages_per_second: string;
|
||||||
|
messages_per_second_text: string;
|
||||||
|
burst_count: string;
|
||||||
|
burst_count_text: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@@ -24,7 +24,7 @@ const it: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "ID utente non valido su questo homeserver.",
|
invalid_user_id: "ID utente non valido su questo homeserver.",
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -156,7 +156,14 @@ const it: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "Cancella l'avatar dell'utente",
|
erase_avatar: "Cancella l'avatar dell'utente",
|
||||||
delete_media: "Elimina tutti i media caricati dall'utente(-s)",
|
delete_media: "Elimina tutti i media caricati dall'utente(-s)",
|
||||||
redact_events: "Ridurre tutti gli eventi inviati dall'utente(-s)",
|
redact_events: "Ridurre tutti gli eventi inviati dall'utente(-s)",
|
||||||
|
generate_password: "Genera password",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "Messaggi al secondo",
|
||||||
|
messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo. 0 significa che la limitazione è disabilitata per questo utente.",
|
||||||
|
burst_count: "Burst-conteggio",
|
||||||
|
burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Stanza |||| Stanze",
|
name: "Stanza |||| Stanze",
|
||||||
|
@@ -50,7 +50,7 @@ const ru: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
|
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
details: "Данные комнаты",
|
details: "Данные комнаты",
|
||||||
@@ -193,8 +193,15 @@ const ru: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "Удалить аватар",
|
erase_avatar: "Удалить аватар",
|
||||||
delete_media: "Удаление всех медиафайлов, загруженных пользователем (-ами)",
|
delete_media: "Удаление всех медиафайлов, загруженных пользователем (-ами)",
|
||||||
redact_events: "Удаление всех событий, отправленных пользователем (-ами)",
|
redact_events: "Удаление всех событий, отправленных пользователем (-ами)",
|
||||||
|
generate_password: "Сгенерировать пароль",
|
||||||
},
|
},
|
||||||
},
|
limits: {
|
||||||
|
messages_per_second: "Сообщений в секунду",
|
||||||
|
messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду. 0 означает, что ограничение на количество действий отключено для этого пользователя.",
|
||||||
|
burst_count: "Burst-счётчик",
|
||||||
|
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
|
||||||
|
}
|
||||||
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Комната |||| Комнаты",
|
name: "Комната |||| Комнаты",
|
||||||
fields: {
|
fields: {
|
||||||
|
@@ -52,7 +52,7 @@ const zh: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
|
invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO", experimental: "实验性", limits: "限制" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@@ -180,7 +180,14 @@ const zh: SynapseTranslationMessages = {
|
|||||||
erase_avatar: "抹掉头像",
|
erase_avatar: "抹掉头像",
|
||||||
delete_media: "删除用户上传的所有媒体",
|
delete_media: "删除用户上传的所有媒体",
|
||||||
redact_events: "重新编辑用户(-s)发送的所有事件",
|
redact_events: "重新编辑用户(-s)发送的所有事件",
|
||||||
|
generate_password: "生成密码",
|
||||||
},
|
},
|
||||||
|
limits: {
|
||||||
|
messages_per_second: "每秒消息数",
|
||||||
|
messages_per_second_text: "每秒可以执行的操作数。0 表示禁用此用户的限制。",
|
||||||
|
burst_count: "Burst-计数",
|
||||||
|
burst_count_text: "在限制之前可以执行的操作数。",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "房间",
|
name: "房间",
|
||||||
|
@@ -3,38 +3,42 @@ import React from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { AppContext, MenuItem } from "./AppContext";
|
import { Config, WellKnownKey, LoadConfig } from "./components/config";
|
||||||
|
import { AppContext } from "./AppContext";
|
||||||
import storage from "./storage";
|
import storage from "./storage";
|
||||||
|
|
||||||
fetch("config.json")
|
// load config.json
|
||||||
.then(res => res.json())
|
let props: Config = {};
|
||||||
.then(props => {
|
try {
|
||||||
if (props.asManagedUsers) {
|
const resp = await fetch("config.json");
|
||||||
storage.setItem("as_managed_users", JSON.stringify(props.asManagedUsers));
|
const configJSON = await resp.json();
|
||||||
}
|
console.log("Loaded config.json", configJSON);
|
||||||
|
props = LoadConfig(configJSON as Config);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
let menu: MenuItem[] = [];
|
// if home_server is set, try to load https://home_server/.well-known/matrix/client
|
||||||
if (props.menu) {
|
const homeserver = storage.getItem("home_server");
|
||||||
menu = props.menu;
|
if (homeserver) {
|
||||||
}
|
try {
|
||||||
if (props.supportURL) {
|
const resp = await fetch(`https://${homeserver}/.well-known/matrix/client`);
|
||||||
const migratedSupportURL = {
|
const configWK = await resp.json();
|
||||||
label: "Contact support",
|
if (!configWK[WellKnownKey]) {
|
||||||
icon: "SupportAgent",
|
console.log(`Loaded https://${homeserver}.well-known/matrix/client, but it doesn't contain ${WellKnownKey} key, skipping`, configWK);
|
||||||
url: props.supportURL,
|
} else {
|
||||||
};
|
console.log(`Loaded https://${homeserver}.well-known/matrix/client`, configWK);
|
||||||
console.warn("supportURL config option is deprecated. Please, use the menu option instead. Automatically migrated to the new menu option:", migratedSupportURL);
|
props = LoadConfig(configWK[WellKnownKey] as Config);
|
||||||
menu.push(migratedSupportURL as MenuItem);
|
|
||||||
}
|
|
||||||
if (menu.length > 0) {
|
|
||||||
storage.setItem("menu", JSON.stringify(menu));
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`https://${homeserver}/.well-known/matrix/client not found, skipping`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return createRoot(document.getElementById("root")).render(
|
createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppContext.Provider value={props}>
|
<AppContext.Provider value={props}>
|
||||||
<App />
|
<App />
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
);
|
||||||
});
|
|
||||||
|
@@ -33,7 +33,7 @@ const LoginPage = () => {
|
|||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const { restrictBaseUrl } = useAppContext();
|
const { restrictBaseUrl } = useAppContext();
|
||||||
const allowSingleBaseUrl = typeof restrictBaseUrl === "string";
|
const allowSingleBaseUrl = typeof restrictBaseUrl === "string" && restrictBaseUrl !== "";
|
||||||
const allowMultipleBaseUrls =
|
const allowMultipleBaseUrls =
|
||||||
Array.isArray(restrictBaseUrl) &&
|
Array.isArray(restrictBaseUrl) &&
|
||||||
restrictBaseUrl.length > 0 &&
|
restrictBaseUrl.length > 0 &&
|
||||||
|
@@ -7,9 +7,11 @@ import NotificationsIcon from "@mui/icons-material/Notifications";
|
|||||||
import PermMediaIcon from "@mui/icons-material/PermMedia";
|
import PermMediaIcon from "@mui/icons-material/PermMedia";
|
||||||
import PersonPinIcon from "@mui/icons-material/PersonPin";
|
import PersonPinIcon from "@mui/icons-material/PersonPin";
|
||||||
import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputComponent";
|
import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputComponent";
|
||||||
|
import ScienceIcon from "@mui/icons-material/Science";
|
||||||
|
import LockClockIcon from '@mui/icons-material/LockClock';
|
||||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Alert, ownerDocument } from "@mui/material";
|
import { Alert } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
ArrayInput,
|
ArrayInput,
|
||||||
ArrayField,
|
ArrayField,
|
||||||
@@ -54,10 +56,10 @@ import {
|
|||||||
useNotify,
|
useNotify,
|
||||||
Identifier,
|
Identifier,
|
||||||
ToolbarClasses,
|
ToolbarClasses,
|
||||||
RaRecord,
|
|
||||||
ImageInput,
|
ImageInput,
|
||||||
ImageField,
|
ImageField,
|
||||||
FunctionField,
|
FunctionField,
|
||||||
|
useDataProvider,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
@@ -68,6 +70,10 @@ import { ServerNoticeButton, ServerNoticeBulkButton } from "../components/Server
|
|||||||
import { DATE_FORMAT } from "../components/date";
|
import { DATE_FORMAT } from "../components/date";
|
||||||
import { DeviceRemoveButton } from "../components/devices";
|
import { DeviceRemoveButton } from "../components/devices";
|
||||||
import { MediaIDField, ProtectMediaButton, QuarantineMediaButton } from "../components/media";
|
import { MediaIDField, ProtectMediaButton, QuarantineMediaButton } from "../components/media";
|
||||||
|
import { generateRandomPassword } from "../synapse/synapse";
|
||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
import { ExperimentalFeaturesList } from "../components/ExperimentalFeatures";
|
||||||
|
import { UserRateLimits } from "../components/UserRateLimits";
|
||||||
|
|
||||||
const choices_medium = [
|
const choices_medium = [
|
||||||
{ id: "email", name: "resources.users.email" },
|
{ id: "email", name: "resources.users.email" },
|
||||||
@@ -126,8 +132,6 @@ const UserBulkActionButtons = () => {
|
|||||||
const [asManagedUserIsSelected, setAsManagedUserIsSelected] = useState(false);
|
const [asManagedUserIsSelected, setAsManagedUserIsSelected] = useState(false);
|
||||||
const selectedIds = record.selectedIds;
|
const selectedIds = record.selectedIds;
|
||||||
const ownUserId = localStorage.getItem("user_id");
|
const ownUserId = localStorage.getItem("user_id");
|
||||||
const notify = useNotify();
|
|
||||||
const translate = useTranslate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOwnUserIsSelected(selectedIds.includes(ownUserId));
|
setOwnUserIsSelected(selectedIds.includes(ownUserId));
|
||||||
@@ -238,11 +242,11 @@ export const UserCreate = (props: CreateProps) => (
|
|||||||
|
|
||||||
const UserTitle = () => {
|
const UserTitle = () => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
|
const translate = useTranslate();
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translate = useTranslate();
|
|
||||||
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
|
let username = record ? (record.displayname ? `"${record.displayname}"` : `"${record.name}"`) : ""
|
||||||
if (isASManaged(record?.id)) {
|
if (isASManaged(record?.id)) {
|
||||||
username += " 🤖";
|
username += " 🤖";
|
||||||
@@ -301,12 +305,33 @@ const UserBooleanInput = props => {
|
|||||||
const UserPasswordInput = props => {
|
const UserPasswordInput = props => {
|
||||||
const record = useRecordContext();
|
const record = useRecordContext();
|
||||||
let asManagedUserIsSelected = false;
|
let asManagedUserIsSelected = false;
|
||||||
|
|
||||||
|
// Get form context to update field value
|
||||||
|
const form = useFormContext();
|
||||||
if (record) {
|
if (record) {
|
||||||
asManagedUserIsSelected = isASManaged(record.id);
|
asManagedUserIsSelected = isASManaged(record.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generatePassword = () => {
|
||||||
|
const password = generateRandomPassword();
|
||||||
|
if (record) {
|
||||||
|
form.setValue("password", password, { shouldDirty: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PasswordInput {...props} helperText="resources.users.helper.modify_managed_user_error" disabled={asManagedUserIsSelected} />
|
<>
|
||||||
|
<PasswordInput {...props} helperText="resources.users.helper.modify_managed_user_error"
|
||||||
|
{...(asManagedUserIsSelected ? { disabled: true } : {})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
label="Generate Password"
|
||||||
|
onClick={generatePassword}
|
||||||
|
sx={{ marginBottom: "10px" }}
|
||||||
|
disabled={asManagedUserIsSelected}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -314,7 +339,11 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />} mutationMode="pessimistic">
|
<Edit {...props} title={<UserTitle />} actions={<UserEditActions />} mutationMode="pessimistic" queryOptions={{
|
||||||
|
meta: {
|
||||||
|
include: ["features"] // Tell your dataProvider to include features
|
||||||
|
}
|
||||||
|
}}>
|
||||||
<TabbedForm toolbar={<UserEditToolbar />}>
|
<TabbedForm toolbar={<UserEditToolbar />}>
|
||||||
<FormTab label={translate("resources.users.name", { smart_count: 1 })} icon={<PersonPinIcon />}>
|
<FormTab label={translate("resources.users.name", { smart_count: 1 })} icon={<PersonPinIcon />}>
|
||||||
<AvatarField source="avatar_src" sx={{ height: "120px", width: "120px" }} />
|
<AvatarField source="avatar_src" sx={{ height: "120px", width: "120px" }} />
|
||||||
@@ -448,6 +477,14 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
</Datagrid>
|
</Datagrid>
|
||||||
</ReferenceManyField>
|
</ReferenceManyField>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab label="synapseadmin.users.tabs.experimental" icon={<ScienceIcon />} path="experimental">
|
||||||
|
<ExperimentalFeaturesList />
|
||||||
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab label="synapseadmin.users.tabs.limits" icon={<LockClockIcon />} path="limits">
|
||||||
|
<UserRateLimits />
|
||||||
|
</FormTab>
|
||||||
</TabbedForm>
|
</TabbedForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
@@ -3,6 +3,7 @@ import { AuthProvider, HttpError, Options, fetchUtils } from "react-admin";
|
|||||||
import storage from "../storage";
|
import storage from "../storage";
|
||||||
import { MatrixError, displayError } from "../components/error";
|
import { MatrixError, displayError } from "../components/error";
|
||||||
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
import { fetchAuthenticatedMedia } from "../utils/fetchMedia";
|
||||||
|
import { ClearConfig } from "../components/config";
|
||||||
|
|
||||||
const authProvider: AuthProvider = {
|
const authProvider: AuthProvider = {
|
||||||
// called when the user attempts to log in
|
// called when the user attempts to log in
|
||||||
@@ -154,8 +155,7 @@ const authProvider: AuthProvider = {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Error logging out", err);
|
console.log("Error logging out", err);
|
||||||
} finally {
|
} finally {
|
||||||
storage.removeItem("access_token");
|
ClearConfig();
|
||||||
storage.removeItem("login_type");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -248,9 +248,23 @@ export interface UploadMediaResult {
|
|||||||
content_uri: string;
|
content_uri: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExperimentalFeaturesModel {
|
||||||
|
features: {
|
||||||
|
[key: string]: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RateLimitsModel {
|
||||||
|
messages_per_second?: number;
|
||||||
|
burst_count?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SynapseDataProvider extends DataProvider {
|
export interface SynapseDataProvider extends DataProvider {
|
||||||
deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
|
deleteMedia: (params: DeleteMediaParams) => Promise<DeleteMediaResult>;
|
||||||
uploadMedia: (params: UploadMediaParams) => Promise<UploadMediaResult>;
|
uploadMedia: (params: UploadMediaParams) => Promise<UploadMediaResult>;
|
||||||
|
updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise<void>;
|
||||||
|
getRateLimits: (id: Identifier) => Promise<RateLimitsModel>;
|
||||||
|
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resourceMap = {
|
const resourceMap = {
|
||||||
@@ -798,6 +812,28 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
});
|
});
|
||||||
return json as UploadMediaResult;
|
return json as UploadMediaResult;
|
||||||
},
|
},
|
||||||
|
getFeatures: async (id: Identifier) => {
|
||||||
|
const base_url = storage.getItem("base_url");
|
||||||
|
const endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
|
||||||
|
const { json } = await jsonClient(endpoint_url);
|
||||||
|
return json.features as ExperimentalFeaturesModel;
|
||||||
|
},
|
||||||
|
updateFeatures: async (id: Identifier, features: ExperimentalFeaturesModel) => {
|
||||||
|
const base_url = storage.getItem("base_url");
|
||||||
|
const endpoint_url = `${base_url}/_synapse/admin/v1/experimental_features/${encodeURIComponent(returnMXID(id))}`;
|
||||||
|
await jsonClient(endpoint_url, { method: "PUT", body: JSON.stringify({ features }) });
|
||||||
|
},
|
||||||
|
getRateLimits: async (id: Identifier) => {
|
||||||
|
const base_url = storage.getItem("base_url");
|
||||||
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
|
||||||
|
const { json } = await jsonClient(endpoint_url);
|
||||||
|
return json as RateLimitsModel;
|
||||||
|
},
|
||||||
|
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
|
||||||
|
const base_url = storage.getItem("base_url");
|
||||||
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/override_ratelimit`;
|
||||||
|
await jsonClient(endpoint_url, { method: "POST", body: JSON.stringify(rateLimits) });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const dataProvider = withLifecycleCallbacks(baseDataProvider, [
|
const dataProvider = withLifecycleCallbacks(baseDataProvider, [
|
||||||
@@ -806,6 +842,12 @@ const dataProvider = withLifecycleCallbacks(baseDataProvider, [
|
|||||||
beforeUpdate: async (params: UpdateParams<any>, dataProvider: DataProvider) => {
|
beforeUpdate: async (params: UpdateParams<any>, dataProvider: DataProvider) => {
|
||||||
const avatarFile = params.data.avatar_file?.rawFile;
|
const avatarFile = params.data.avatar_file?.rawFile;
|
||||||
const avatarErase = params.data.avatar_erase;
|
const avatarErase = params.data.avatar_erase;
|
||||||
|
const rates = params.data.rates;
|
||||||
|
|
||||||
|
if (rates) {
|
||||||
|
await dataProvider.setRateLimits(params.id, rates);
|
||||||
|
delete params.data.rates;
|
||||||
|
}
|
||||||
|
|
||||||
if (avatarErase) {
|
if (avatarErase) {
|
||||||
params.data.avatar_url = "";
|
params.data.avatar_url = "";
|
||||||
|
@@ -91,8 +91,8 @@ export function returnMXID(input: string | Identifier): string {
|
|||||||
* Generate a random user password
|
* Generate a random user password
|
||||||
* @returns a new random password as string
|
* @returns a new random password as string
|
||||||
*/
|
*/
|
||||||
export function generateRandomPassword(length = 20): string {
|
export function generateRandomPassword(length = 64): string {
|
||||||
const characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@-#$";
|
const characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~`!@#$%^&*()_-+={[}]|:;'.?/<>,";
|
||||||
return Array.from(crypto.getRandomValues(new Uint32Array(length)))
|
return Array.from(crypto.getRandomValues(new Uint32Array(length)))
|
||||||
.map(x => characters[x % characters.length])
|
.map(x => characters[x % characters.length])
|
||||||
.join("");
|
.join("");
|
||||||
|
@@ -5,11 +5,14 @@ import { defineConfig } from "vite";
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: "./",
|
base: "./",
|
||||||
|
build: {
|
||||||
|
target: "esnext",
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
vitePluginVersionMark({
|
vitePluginVersionMark({
|
||||||
name: "Synapse Admin",
|
name: "Synapse Admin",
|
||||||
command: "git describe --tags || git rev-parse --short HEAD",
|
command: 'git describe --tags || git rev-parse --short HEAD || echo "${SYNAPSE_ADMIN_VERSION:-unknown}"',
|
||||||
ifMeta: false,
|
ifMeta: false,
|
||||||
ifLog: false,
|
ifLog: false,
|
||||||
ifGlobal: true,
|
ifGlobal: true,
|
||||||
|
278
yarn.lock
278
yarn.lock
@@ -332,10 +332,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.25.7"
|
"@babel/helper-plugin-utils" "^7.25.7"
|
||||||
|
|
||||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7":
|
"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7":
|
||||||
version "7.25.9"
|
version "7.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.9.tgz#65884fd6dc255a775402cc1d9811082918f4bf00"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
|
||||||
integrity sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==
|
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
@@ -1001,10 +1001,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.5":
|
"@mui/core-downloads-tracker@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.5.tgz#96fe5068d55fba27d90421b8265b965c203d09e2"
|
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.6.tgz#73d96e75689b2af922a989123149a3497c8a96fa"
|
||||||
integrity sha512-3J96098GrC95XsLw/TpGNMxhUOnoG9NZ/17Pfk1CrJj+4rcuolsF2RdF3XAFTu/3a/A+5ouxlSIykzYz6Ee87g==
|
integrity sha512-nz1SlR9TdBYYPz4qKoNasMPRiGb4PaIHFkzLzhju0YVYS5QSuFF2+n7CsiHMIDcHv3piPu/xDWI53ruhOqvZwQ==
|
||||||
|
|
||||||
"@mui/icons-material@^5.15.20":
|
"@mui/icons-material@^5.15.20":
|
||||||
version "5.16.7"
|
version "5.16.7"
|
||||||
@@ -1038,16 +1038,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.5":
|
"@mui/material@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.1.5.tgz#1ba8deda18564b277f37d957d523a9d2624b4b9a"
|
resolved "https://registry.yarnpkg.com/@mui/material/-/material-6.1.6.tgz#505d7300401f6af38426006d7fb3b8707dc10fbc"
|
||||||
integrity sha512-rhaxC7LnlOG8zIVYv7BycNbWkC5dlm9A/tcDUp0CuwA7Zf9B9JP6M3rr50cNKxI7Z0GIUesAT86ceVm44quwnQ==
|
integrity sha512-1yvejiQ/601l5AK3uIdUlAVElyCxoqKnl7QA+2oFB/2qYPWfRwDgavW/MoywS5Y2gZEslcJKhe0s2F3IthgFgw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.25.7"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/core-downloads-tracker" "^6.1.5"
|
"@mui/core-downloads-tracker" "^6.1.6"
|
||||||
"@mui/system" "^6.1.5"
|
"@mui/system" "^6.1.6"
|
||||||
"@mui/types" "^7.2.18"
|
"@mui/types" "^7.2.19"
|
||||||
"@mui/utils" "^6.1.5"
|
"@mui/utils" "^6.1.6"
|
||||||
"@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"
|
||||||
@@ -1065,13 +1065,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.5":
|
"@mui/private-theming@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.1.5.tgz#634166d5793f6635ee2f815fb30f03342ff4df41"
|
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-6.1.6.tgz#9966bf2eca3d626cddd6e173752f46d344c7d7d1"
|
||||||
integrity sha512-FJqweqEXk0KdtTho9C2h6JEKXsOT7MAVH2Uj3N5oIqs6YKxnwBn2/zL2QuYYEtj5OJ87rEUnCfFic6ldClvzJw==
|
integrity sha512-ioAiFckaD/fJSnTrUMWgjl9HYBWt7ixCh7zZw7gDZ+Tae7NuprNV6QJK95EidDT7K0GetR2rU3kAeIR61Myttw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.25.7"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/utils" "^6.1.5"
|
"@mui/utils" "^6.1.6"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/styled-engine@^5.16.6":
|
"@mui/styled-engine@^5.16.6":
|
||||||
@@ -1084,12 +1084,12 @@
|
|||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/styled-engine@^6.1.5":
|
"@mui/styled-engine@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.1.5.tgz#a21a75799f84446e3553ab191a891ca2192933cc"
|
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-6.1.6.tgz#61996621a0297aac16061e1739a738a899613fd6"
|
||||||
integrity sha512-tiyWzMkHeWlOoE6AqomWvYvdml8Nv5k5T+LDwOiwHEawx8P9Lyja6ZwWPU6xljwPXYYPT2KBp1XvMly7dsK46A==
|
integrity sha512-I+yS1cSuSvHnZDBO7e7VHxTWpj+R7XlSZvTC4lS/OIbUNJOMMSd3UDP6V2sfwzAdmdDNBi7NGCRv2SZ6O9hGDA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.25.7"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@emotion/cache" "^11.13.1"
|
"@emotion/cache" "^11.13.1"
|
||||||
"@emotion/serialize" "^1.3.2"
|
"@emotion/serialize" "^1.3.2"
|
||||||
"@emotion/sheet" "^1.4.0"
|
"@emotion/sheet" "^1.4.0"
|
||||||
@@ -1110,24 +1110,24 @@
|
|||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/system@^6.1.5":
|
"@mui/system@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.1.5.tgz#2124f43be98a7393e08edf89ae0fbc8678607e11"
|
resolved "https://registry.yarnpkg.com/@mui/system/-/system-6.1.6.tgz#d335d6952092f3c758c8b78c2d993aa13ef58175"
|
||||||
integrity sha512-vPM9ocQ8qquRDByTG3XF/wfYTL7IWL/20EiiKqByLDps8wOmbrDG9rVznSE3ZbcjFCFfMRMhtxvN92bwe/63SA==
|
integrity sha512-qOf1VUE9wK8syiB0BBCp82oNBAVPYdj4Trh+G1s+L+ImYiKlubWhhqlnvWt3xqMevR+D2h1CXzA1vhX2FvA+VQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.25.7"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/private-theming" "^6.1.5"
|
"@mui/private-theming" "^6.1.6"
|
||||||
"@mui/styled-engine" "^6.1.5"
|
"@mui/styled-engine" "^6.1.6"
|
||||||
"@mui/types" "^7.2.18"
|
"@mui/types" "^7.2.19"
|
||||||
"@mui/utils" "^6.1.5"
|
"@mui/utils" "^6.1.6"
|
||||||
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"
|
||||||
|
|
||||||
"@mui/types@^7.2.15", "@mui/types@^7.2.18":
|
"@mui/types@^7.2.15", "@mui/types@^7.2.19":
|
||||||
version "7.2.18"
|
version "7.2.19"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.18.tgz#4b6385ed2f7828ef344113cdc339d6fdf8e4bc23"
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.19.tgz#c941954dd24393fdce5f07830d44440cf4ab6c80"
|
||||||
integrity sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==
|
integrity sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==
|
||||||
|
|
||||||
"@mui/utils@^5.16.6":
|
"@mui/utils@^5.16.6":
|
||||||
version "5.16.6"
|
version "5.16.6"
|
||||||
@@ -1141,13 +1141,13 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.3.1"
|
react-is "^18.3.1"
|
||||||
|
|
||||||
"@mui/utils@^6.1.5":
|
"@mui/utils@^6.1.6":
|
||||||
version "6.1.5"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.1.5.tgz#a5c75ac48f9913340670ebeba2907568a6ee8c49"
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.1.6.tgz#4b9fd34da3a1dd4700fe506a20ca7da3933ba48e"
|
||||||
integrity sha512-vp2WfNDY+IbKUIGg+eqX1Ry4t/BilMjzp6p9xO1rfqpYjH1mj8coQxxDfKxcQLzBQkmBJjymjoGOak5VUYwXug==
|
integrity sha512-sBS6D9mJECtELASLM+18WUcXF6RH3zNxBRFeyCRg8wad6NbyNrdxLuwK+Ikvc38sTZwBzAz691HmSofLqHd9sQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.25.7"
|
"@babel/runtime" "^7.26.0"
|
||||||
"@mui/types" "^7.2.18"
|
"@mui/types" "^7.2.19"
|
||||||
"@types/prop-types" "^15.7.13"
|
"@types/prop-types" "^15.7.13"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
@@ -1293,17 +1293,17 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@sinonjs/commons" "^3.0.0"
|
"@sinonjs/commons" "^3.0.0"
|
||||||
|
|
||||||
"@tanstack/query-core@5.59.16":
|
"@tanstack/query-core@5.59.17":
|
||||||
version "5.59.16"
|
version "5.59.17"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.59.16.tgz#aa4616e8a9c12afeef4cfbf3ed0f55f404d66e67"
|
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.59.17.tgz#bda3bb678be48e2f6ee692abd1cfc2db3d455e4b"
|
||||||
integrity sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==
|
integrity sha512-jWdDiif8kaqnRGHNXAa9CnudtxY5v9DUxXhodgqX2Rwzj+1UwStDHEbBd9IA5C7VYAaJ2s+BxFR6PUBs8ERorA==
|
||||||
|
|
||||||
"@tanstack/react-query@^5.59.16", "@tanstack/react-query@^5.8.4":
|
"@tanstack/react-query@^5.59.19", "@tanstack/react-query@^5.8.4":
|
||||||
version "5.59.16"
|
version "5.59.19"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.59.16.tgz#1e701c6e6681965c04aa426df9da54b8edc6db1b"
|
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.59.19.tgz#309ae0d75844e331ea72762bfe0e11fb829a69d3"
|
||||||
integrity sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==
|
integrity sha512-xLRfyFyQOFcLltKCds0LijfC6/HQJrrTTnZB8ciyn74LIkVAm++vZJ6eUVG20RmJtdP8REdy7vSOYW4M3//XLA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/query-core" "5.59.16"
|
"@tanstack/query-core" "5.59.17"
|
||||||
|
|
||||||
"@testing-library/dom@^10.0.0":
|
"@testing-library/dom@^10.0.0":
|
||||||
version "10.4.0"
|
version "10.4.0"
|
||||||
@@ -1319,10 +1319,10 @@
|
|||||||
lz-string "^1.5.0"
|
lz-string "^1.5.0"
|
||||||
pretty-format "^27.0.2"
|
pretty-format "^27.0.2"
|
||||||
|
|
||||||
"@testing-library/jest-dom@^6.6.2":
|
"@testing-library/jest-dom@^6.6.3":
|
||||||
version "6.6.2"
|
version "6.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz#8186aa9a07263adef9cc5a59a4772db8c31f4a5b"
|
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2"
|
||||||
integrity sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==
|
integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@adobe/css-tools" "^4.4.0"
|
"@adobe/css-tools" "^4.4.0"
|
||||||
aria-query "^5.0.0"
|
aria-query "^5.0.0"
|
||||||
@@ -1470,15 +1470,15 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
|
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
|
||||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||||
|
|
||||||
"@types/lodash@^4.17.12":
|
"@types/lodash@^4.17.13":
|
||||||
version "4.17.12"
|
version "4.17.13"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.12.tgz#25d71312bf66512105d71e55d42e22c36bcfc689"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
|
||||||
integrity sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==
|
integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@^22.8.1":
|
"@types/node@*", "@types/node@^22.8.7":
|
||||||
version "22.8.1"
|
version "22.8.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.1.tgz#b39d4b98165e2ae792ce213f610c7c6108ccfa16"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4"
|
||||||
integrity sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==
|
integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.19.8"
|
undici-types "~6.19.8"
|
||||||
|
|
||||||
@@ -1536,62 +1536,62 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@8.11.0", "@typescript-eslint/eslint-plugin@^8.11.0":
|
"@typescript-eslint/eslint-plugin@8.12.2", "@typescript-eslint/eslint-plugin@^8.11.0":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz#c3f087d20715fa94310b30666c08b3349e0ab084"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz#c2ef660bb83fd1432368319312a2581fc92ccac1"
|
||||||
integrity sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==
|
integrity sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp" "^4.10.0"
|
"@eslint-community/regexpp" "^4.10.0"
|
||||||
"@typescript-eslint/scope-manager" "8.11.0"
|
"@typescript-eslint/scope-manager" "8.12.2"
|
||||||
"@typescript-eslint/type-utils" "8.11.0"
|
"@typescript-eslint/type-utils" "8.12.2"
|
||||||
"@typescript-eslint/utils" "8.11.0"
|
"@typescript-eslint/utils" "8.12.2"
|
||||||
"@typescript-eslint/visitor-keys" "8.11.0"
|
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||||
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/parser@8.11.0", "@typescript-eslint/parser@^8.11.0":
|
"@typescript-eslint/parser@8.12.2", "@typescript-eslint/parser@^8.11.0":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.11.0.tgz#2ad1481388dc1c937f50b2d138c9ca57cc6c5cce"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.12.2.tgz#2e8173b34e1685e918b2d571c16c906d3747bad2"
|
||||||
integrity sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==
|
integrity sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "8.11.0"
|
"@typescript-eslint/scope-manager" "8.12.2"
|
||||||
"@typescript-eslint/types" "8.11.0"
|
"@typescript-eslint/types" "8.12.2"
|
||||||
"@typescript-eslint/typescript-estree" "8.11.0"
|
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||||
"@typescript-eslint/visitor-keys" "8.11.0"
|
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@8.11.0":
|
"@typescript-eslint/scope-manager@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz#9d399ce624118966732824878bc9a83593a30405"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz#6db0213745e6392c8e90fe9af5915e6da32eb94a"
|
||||||
integrity sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==
|
integrity sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.11.0"
|
"@typescript-eslint/types" "8.12.2"
|
||||||
"@typescript-eslint/visitor-keys" "8.11.0"
|
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@8.11.0":
|
"@typescript-eslint/type-utils@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz#b7f9e6120c1ddee8a1a07615646642ad85fc91b5"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz#132b0c52d45f6814e6f2e32416c7951ed480b016"
|
||||||
integrity sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==
|
integrity sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree" "8.11.0"
|
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||||
"@typescript-eslint/utils" "8.11.0"
|
"@typescript-eslint/utils" "8.12.2"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
ts-api-utils "^1.3.0"
|
ts-api-utils "^1.3.0"
|
||||||
|
|
||||||
"@typescript-eslint/types@8.11.0":
|
"@typescript-eslint/types@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.11.0.tgz#7c766250502097f49bbc2e651132e6bf489e20b8"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.12.2.tgz#8d70098c0e90442495b53d0296acdca6d0f3f73c"
|
||||||
integrity sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==
|
integrity sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@8.11.0":
|
"@typescript-eslint/typescript-estree@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz#35fe5d3636fc5727c52429393415412e552e222b"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz#206df9b1cbff212aaa9401985ef99f04daa84da5"
|
||||||
integrity sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==
|
integrity sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.11.0"
|
"@typescript-eslint/types" "8.12.2"
|
||||||
"@typescript-eslint/visitor-keys" "8.11.0"
|
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||||
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"
|
||||||
@@ -1599,22 +1599,22 @@
|
|||||||
semver "^7.6.0"
|
semver "^7.6.0"
|
||||||
ts-api-utils "^1.3.0"
|
ts-api-utils "^1.3.0"
|
||||||
|
|
||||||
"@typescript-eslint/utils@8.11.0":
|
"@typescript-eslint/utils@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.11.0.tgz#4480d1e9f2bb18ea3510c79f870a1aefc118103d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.12.2.tgz#726cc9f49f5866605bd15bbc1768ffc15637930e"
|
||||||
integrity sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==
|
integrity sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.4.0"
|
"@eslint-community/eslint-utils" "^4.4.0"
|
||||||
"@typescript-eslint/scope-manager" "8.11.0"
|
"@typescript-eslint/scope-manager" "8.12.2"
|
||||||
"@typescript-eslint/types" "8.11.0"
|
"@typescript-eslint/types" "8.12.2"
|
||||||
"@typescript-eslint/typescript-estree" "8.11.0"
|
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@8.11.0":
|
"@typescript-eslint/visitor-keys@8.12.2":
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz#273de1cbffe63d9f9cd7dfc20b5a5af66310cb92"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz#94d7410f78eb6d134b9fcabaf1eeedb910ba8c38"
|
||||||
integrity sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==
|
integrity sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.11.0"
|
"@typescript-eslint/types" "8.12.2"
|
||||||
eslint-visitor-keys "^3.4.3"
|
eslint-visitor-keys "^3.4.3"
|
||||||
|
|
||||||
"@vitejs/plugin-react@^4.3.3":
|
"@vitejs/plugin-react@^4.3.3":
|
||||||
@@ -4643,10 +4643,10 @@ 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.1:
|
ra-core@^5.3.1, ra-core@^5.3.2:
|
||||||
version "5.3.1"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-5.3.1.tgz#38773ae1cda386064010fc2774c204ccaf6bc325"
|
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-5.3.2.tgz#8d60096f9bd3b88339fa798f42be21791813199e"
|
||||||
integrity sha512-1q/h71iOco6kA9kkGNEmChuw+JgUTr+Y6MLfb/N0VcF75xX+v5SjoNMgHee/Jtn6uObOVeWdhAhIJ/bFwHR5sg==
|
integrity sha512-JDZ44jMCTlAXWWdR8LLFXXjKIc6bNf4L/zjY9/iL9yP5Kcy4yNf4kTBzR46dNWpt5/3k4Z59cMKBf+A9fDjJAQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/react-query" "^5.8.4"
|
"@tanstack/react-query" "^5.8.4"
|
||||||
clsx "^2.1.1"
|
clsx "^2.1.1"
|
||||||
@@ -4659,20 +4659,20 @@ ra-core@^5.3.1:
|
|||||||
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.0, ra-i18n-polyglot@^5.3.1:
|
ra-i18n-polyglot@^5.3.1, ra-i18n-polyglot@^5.3.2:
|
||||||
version "5.3.1"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-5.3.1.tgz#92fa199d3aab77a540e66c719bcac9c01a50a5a5"
|
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-5.3.2.tgz#7da554550fe8e7d2a2028d68280767a9392603cb"
|
||||||
integrity sha512-WcxTBWSYlLy20Hh72BhadyG/El5UHWaJQnglN7JK+M16EDR2Mp8R1kPGHFLtCAbjjMXUaKzZQ50AS51Ct1kVLQ==
|
integrity sha512-jyaDTPKyJOZlMnvLnrw7dqpD/ek01iVFg73FGoI3PAD/xYwKj4AfJBvwWlLVwlc6TOz9fcwd4hTWWVKbOMMFbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-polyglot "^2.2.2"
|
node-polyglot "^2.2.2"
|
||||||
ra-core "^5.3.1"
|
ra-core "^5.3.2"
|
||||||
|
|
||||||
ra-language-english@^5.3.1:
|
ra-language-english@^5.3.1, ra-language-english@^5.3.2:
|
||||||
version "5.3.1"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-5.3.1.tgz#823d25cbbf66ae9ba4098e087ae21110f4ffb030"
|
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-5.3.2.tgz#9d4fc545b05bda945c9ec96d60203ca2d560f632"
|
||||||
integrity sha512-ttEVhRI06VPA0MEFmCkxQNFa3C4ztGHG14vRnfRtpQ3qSF996genzoYCW/AdkI/JzskHU1zAw5o73qezSdqCuA==
|
integrity sha512-otfNWGAyRXuaEBYneYpNaNj96umOLQ+MVtWPJSHvHXq80nRL0y2tEy0RNDrdIayvF6Q9dRYs1Q7a+mV0tkNbAw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ra-core "^5.3.1"
|
ra-core "^5.3.2"
|
||||||
|
|
||||||
ra-language-farsi@^5.0.0:
|
ra-language-farsi@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
@@ -5435,14 +5435,14 @@ 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.11.0:
|
typescript-eslint@^8.12.2:
|
||||||
version "8.11.0"
|
version "8.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.11.0.tgz#74a0551972d675b4141672cec3acc5139b7399c0"
|
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.12.2.tgz#e273d69af30b478b1c410f4159d675ce7925f9a7"
|
||||||
integrity sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==
|
integrity sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/eslint-plugin" "8.11.0"
|
"@typescript-eslint/eslint-plugin" "8.12.2"
|
||||||
"@typescript-eslint/parser" "8.11.0"
|
"@typescript-eslint/parser" "8.12.2"
|
||||||
"@typescript-eslint/utils" "8.11.0"
|
"@typescript-eslint/utils" "8.12.2"
|
||||||
|
|
||||||
typescript@^5.6.3:
|
typescript@^5.6.3:
|
||||||
version "5.6.3"
|
version "5.6.3"
|
||||||
|
Reference in New Issue
Block a user