Add Users' Account Data tab (#276)
* Add Account Data tab in User edit * update readme
This commit is contained in:
parent
44d801a2f5
commit
2a5b59002e
@ -109,6 +109,7 @@ The following changes are already implemented:
|
|||||||
* 🛑 [Add support for Account Suspension (MSC3823)](https://github.com/etkecc/synapse-admin/pull/195)
|
* 🛑 [Add support for Account Suspension (MSC3823)](https://github.com/etkecc/synapse-admin/pull/195)
|
||||||
* 🗑️ [Add "Purge Remote Media" button](https://github.com/etkecc/synapse-admin/pull/237)
|
* 🗑️ [Add "Purge Remote Media" button](https://github.com/etkecc/synapse-admin/pull/237)
|
||||||
* [Respect base url (`BASE_PATH` / `vite build --base`) when loading `config.json`](https://github.com/etkecc/synapse-admin/pull/274)
|
* [Respect base url (`BASE_PATH` / `vite build --base`) when loading `config.json`](https://github.com/etkecc/synapse-admin/pull/274)
|
||||||
|
* [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276)
|
||||||
|
|
||||||
#### exclusive for [etke.cc](https://etke.cc) customers
|
#### exclusive for [etke.cc](https://etke.cc) customers
|
||||||
|
|
||||||
|
65
src/components/UserAccountData.tsx
Normal file
65
src/components/UserAccountData.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { useDataProvider, useRecordContext, useTranslate } from "react-admin";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Typography, Box, Stack, Accordion, AccordionSummary, AccordionDetails } from "@mui/material";
|
||||||
|
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||||
|
import { SynapseDataProvider } from "../synapse/dataProvider";
|
||||||
|
|
||||||
|
const UserAccountData = () => {
|
||||||
|
const dataProvider = useDataProvider() as SynapseDataProvider;
|
||||||
|
const record = useRecordContext();
|
||||||
|
const translate = useTranslate();
|
||||||
|
const [globalAccountData, setGlobalAccountData] = useState({});
|
||||||
|
const [roomsAccountData, setRoomsAccountData] = useState({});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAccountData = async () => {
|
||||||
|
const accountData = await dataProvider.getAccountData(record.id);
|
||||||
|
setGlobalAccountData(accountData.account_data.global);
|
||||||
|
setRoomsAccountData(accountData.account_data.rooms);
|
||||||
|
};
|
||||||
|
fetchAccountData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (Object.keys(globalAccountData).length === 0 && Object.keys(roomsAccountData).length === 0) {
|
||||||
|
return <Typography variant="body2">{translate('ra.navigation.no_results', {
|
||||||
|
resource: 'Account Data',
|
||||||
|
_: 'No results found.',
|
||||||
|
})}</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
spacing={2}
|
||||||
|
width="100%"
|
||||||
|
>
|
||||||
|
<Typography variant="h6">{translate('resources.users.account_data.title')}</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
<Box>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary expandIcon={<ArrowDownwardIcon />}>
|
||||||
|
<Typography variant="h6">{translate('resources.users.account_data.global')}</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Box sx={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(globalAccountData, null, 4)}</Box>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary expandIcon={<ArrowDownwardIcon />}>
|
||||||
|
<Typography variant="h6">{translate('resources.users.account_data.rooms')}</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Box sx={{ whiteSpace: "pre-wrap" }}>{JSON.stringify(roomsAccountData, null, 4)}</Box>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserAccountData;
|
@ -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", experimental: "Experimentell", limits: "Rate Limits" },
|
tabs: { sso: "SSO", experimental: "Experimentell", limits: "Rate Limits", account_data: "Kontodaten" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
details: "Raumdetails",
|
details: "Raumdetails",
|
||||||
@ -229,6 +229,11 @@ const de: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können.",
|
messages_per_second_text: "Die Anzahl der Aktionen, die in einer Sekunde durchgeführt werden können.",
|
||||||
burst_count: "Burst-Anzahl",
|
burst_count: "Burst-Anzahl",
|
||||||
burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.",
|
burst_count_text: "Die Anzahl der Aktionen, die vor der Begrenzung durchgeführt werden können.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "Kontodaten",
|
||||||
|
global: "Globale",
|
||||||
|
rooms: "Räume",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -29,6 +29,7 @@ const en: SynapseTranslationMessages = {
|
|||||||
sso: "SSO",
|
sso: "SSO",
|
||||||
experimental: "Experimental",
|
experimental: "Experimental",
|
||||||
limits: "Rate Limits",
|
limits: "Rate Limits",
|
||||||
|
account_data: "Account Data",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
@ -202,6 +203,11 @@ const en: SynapseTranslationMessages = {
|
|||||||
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.",
|
||||||
burst_count: "Burst count",
|
burst_count: "Burst count",
|
||||||
burst_count_text: "How many actions that can be performed before being limited.",
|
burst_count_text: "How many actions that can be performed before being limited.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "Account Data",
|
||||||
|
global: "Global",
|
||||||
|
rooms: "Rooms",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -24,7 +24,7 @@ const fa: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
|
invalid_user_id: "بخش محلی یک شناسه کاربری ماتریکس بدون سرور خانگی.",
|
||||||
tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها" },
|
tabs: { sso: "SSO", experimental: "تجربی", limits: "محدودیت ها", account_data: "داده های کاربر" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@ -195,6 +195,11 @@ const fa: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود.",
|
messages_per_second_text: "تعداد عملیاتی که می تواند در یک ثانیه انجام شود.",
|
||||||
burst_count: "تعداد پیچیدگی",
|
burst_count: "تعداد پیچیدگی",
|
||||||
burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.",
|
burst_count_text: "تعداد عملیاتی که می تواند قبل از محدودیت انجام شود.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "داده های کاربر",
|
||||||
|
global: "عمومی",
|
||||||
|
rooms: "اتاق ها",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -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", experimental: "Expérimental", limits: "Limites" },
|
tabs: { sso: "Authentification unique", experimental: "Expérimental", limits: "Limites", account_data: "Données du compte" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@ -197,6 +197,11 @@ const fr: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde.",
|
messages_per_second_text: "Le nombre d'actions que l'utilisateur peut effectuer par seconde.",
|
||||||
burst_count: "Compteur de pics",
|
burst_count: "Compteur de pics",
|
||||||
burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.",
|
burst_count_text: "Le nombre d'actions que l'utilisateur peut effectuer avant d'être limité.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "Données du compte",
|
||||||
|
global: "Globales",
|
||||||
|
rooms: "Salons",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
7
src/i18n/index.d.ts
vendored
7
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; experimental: string; limits: string; };
|
tabs: { sso: string; experimental: string; limits: string; account_data: string; };
|
||||||
};
|
};
|
||||||
rooms: {
|
rooms: {
|
||||||
details?: string; // TODO: fa, fr, it, zh
|
details?: string; // TODO: fa, fr, it, zh
|
||||||
@ -195,6 +195,11 @@ interface SynapseTranslationMessages extends TranslationMessages {
|
|||||||
burst_count: string;
|
burst_count: string;
|
||||||
burst_count_text: string;
|
burst_count_text: string;
|
||||||
};
|
};
|
||||||
|
account_data: {
|
||||||
|
title: string;
|
||||||
|
global: string;
|
||||||
|
rooms: string;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
rooms: {
|
rooms: {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -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", experimental: "Sperimentale", limits: "Limiti" },
|
tabs: { sso: "SSO", experimental: "Sperimentale", limits: "Limiti", account_data: "Dati del profilo" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@ -195,6 +195,11 @@ const it: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo.",
|
messages_per_second_text: "Il numero di azioni che l'utente può eseguire al secondo.",
|
||||||
burst_count: "Burst-conteggio",
|
burst_count: "Burst-conteggio",
|
||||||
burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.",
|
burst_count_text: "Il numero di azioni che l'utente può eseguire prima di essere limitato.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "Dati del profilo",
|
||||||
|
global: "Globale",
|
||||||
|
rooms: "Stanza",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -50,7 +50,7 @@ const ru: SynapseTranslationMessages = {
|
|||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
|
invalid_user_id: "Локальная часть ID пользователя Matrix без адреса домашнего сервера.",
|
||||||
tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения" },
|
tabs: { sso: "SSO", experimental: "Экспериментальные", limits: "Ограничения", account_data: "Данные пользователя" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
details: "Данные комнаты",
|
details: "Данные комнаты",
|
||||||
@ -232,6 +232,11 @@ const ru: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду.",
|
messages_per_second_text: "Количество действий, которые могут быть выполнены в секунду.",
|
||||||
burst_count: "Burst-счётчик",
|
burst_count: "Burst-счётчик",
|
||||||
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
|
burst_count_text: "Количество действий, которые могут быть выполнены до ограничения.",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "Данные пользователя",
|
||||||
|
global: "Глобальные",
|
||||||
|
rooms: "Комнаты",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -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", experimental: "实验性", limits: "限制" },
|
tabs: { sso: "SSO", experimental: "实验性", limits: "限制", account_data: "账户数据" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
tabs: {
|
tabs: {
|
||||||
@ -221,6 +221,11 @@ const zh: SynapseTranslationMessages = {
|
|||||||
messages_per_second_text: "每秒可以执行的操作数。",
|
messages_per_second_text: "每秒可以执行的操作数。",
|
||||||
burst_count: "Burst-计数",
|
burst_count: "Burst-计数",
|
||||||
burst_count_text: "在限制之前可以执行的操作数。",
|
burst_count_text: "在限制之前可以执行的操作数。",
|
||||||
|
},
|
||||||
|
account_data: {
|
||||||
|
title: "账户数据",
|
||||||
|
global: "全局",
|
||||||
|
rooms: "房间",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
|
@ -10,6 +10,7 @@ import SettingsInputComponentIcon from "@mui/icons-material/SettingsInputCompone
|
|||||||
import ScienceIcon from "@mui/icons-material/Science";
|
import ScienceIcon from "@mui/icons-material/Science";
|
||||||
import LockClockIcon from '@mui/icons-material/LockClock';
|
import LockClockIcon from '@mui/icons-material/LockClock';
|
||||||
import ViewListIcon from "@mui/icons-material/ViewList";
|
import ViewListIcon from "@mui/icons-material/ViewList";
|
||||||
|
import DocumentScannerIcon from "@mui/icons-material/DocumentScanner";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Alert, Typography } from "@mui/material";
|
import { Alert, Typography } from "@mui/material";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { useTheme } from "@mui/material/styles";
|
||||||
@ -81,6 +82,7 @@ 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";
|
||||||
|
import UserAccountData from "../components/UserAccountData";
|
||||||
|
|
||||||
const choices_medium = [
|
const choices_medium = [
|
||||||
{ id: "email", name: "resources.users.email" },
|
{ id: "email", name: "resources.users.email" },
|
||||||
@ -556,6 +558,10 @@ export const UserEdit = (props: EditProps) => {
|
|||||||
<FormTab label="synapseadmin.users.tabs.limits" icon={<LockClockIcon />} path="limits">
|
<FormTab label="synapseadmin.users.tabs.limits" icon={<LockClockIcon />} path="limits">
|
||||||
<UserRateLimits />
|
<UserRateLimits />
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab label="synapseadmin.users.tabs.account_data" icon={<DocumentScannerIcon />} path="accountdata">
|
||||||
|
<UserAccountData />
|
||||||
|
</FormTab>
|
||||||
</TabbedForm>
|
</TabbedForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
@ -260,6 +260,17 @@ export interface RateLimitsModel {
|
|||||||
burst_count?: number;
|
burst_count?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AccountDataModel {
|
||||||
|
account_data: {
|
||||||
|
global: {
|
||||||
|
[key: string]: object;
|
||||||
|
},
|
||||||
|
rooms: {
|
||||||
|
[key: string]: object;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface UsernameAvailabilityResult {
|
export interface UsernameAvailabilityResult {
|
||||||
available?: boolean;
|
available?: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
@ -309,6 +320,7 @@ export interface SynapseDataProvider extends DataProvider {
|
|||||||
updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise<void>;
|
updateFeatures: (id: Identifier, features: ExperimentalFeaturesModel) => Promise<void>;
|
||||||
getRateLimits: (id: Identifier) => Promise<RateLimitsModel>;
|
getRateLimits: (id: Identifier) => Promise<RateLimitsModel>;
|
||||||
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>;
|
setRateLimits: (id: Identifier, rateLimits: RateLimitsModel) => Promise<void>;
|
||||||
|
getAccountData: (id: Identifier) => Promise<AccountDataModel>;
|
||||||
checkUsernameAvailability: (username: string) => Promise<UsernameAvailabilityResult>;
|
checkUsernameAvailability: (username: string) => Promise<UsernameAvailabilityResult>;
|
||||||
makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>;
|
makeRoomAdmin: (room_id: string, user_id: string) => Promise<{ success: boolean; error?: string; errcode?: string }>;
|
||||||
getServerRunningProcess: (etkeAdminUrl: string) => Promise<ServerProcessResponse>;
|
getServerRunningProcess: (etkeAdminUrl: string) => Promise<ServerProcessResponse>;
|
||||||
@ -911,6 +923,12 @@ const baseDataProvider: SynapseDataProvider = {
|
|||||||
const { json } = await jsonClient(endpoint_url);
|
const { json } = await jsonClient(endpoint_url);
|
||||||
return json as RateLimitsModel;
|
return json as RateLimitsModel;
|
||||||
},
|
},
|
||||||
|
getAccountData: async (id: Identifier) => {
|
||||||
|
const base_url = localStorage.getItem("base_url");
|
||||||
|
const endpoint_url = `${base_url}/_synapse/admin/v1/users/${encodeURIComponent(returnMXID(id))}/accountdata`;
|
||||||
|
const { json } = await jsonClient(endpoint_url);
|
||||||
|
return json as AccountDataModel;
|
||||||
|
},
|
||||||
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
|
setRateLimits: async (id: Identifier, rateLimits: RateLimitsModel) => {
|
||||||
const filtered = Object.entries(rateLimits).
|
const filtered = Object.entries(rateLimits).
|
||||||
filter(([key, value]) => value !== null && value !== undefined).
|
filter(([key, value]) => value !== null && value !== undefined).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user