Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6b6e54617 | ||
|
|
8ff0ac913c | ||
|
|
96d2c96740 | ||
|
|
8adab0e927 | ||
|
|
536ffc2fbf | ||
|
|
684c44e470 | ||
|
|
7f92e1a3c0 | ||
|
|
ea59d0dd02 | ||
|
|
706114a382 | ||
|
|
f2a1275673 | ||
|
|
425c210cfc | ||
|
|
b184954ffa | ||
|
|
2f96951c19 | ||
|
|
1706cd3c9d | ||
|
|
eadc04a6a0 | ||
|
|
1432724a64 |
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
This project is built using [react-admin](https://marmelab.com/react-admin/).
|
This project is built using [react-admin](https://marmelab.com/react-admin/).
|
||||||
|
|
||||||
It needs at least Synapse v1.18.0 for all functions to work as expected!
|
It needs at least Synapse v1.23.0 for all functions to work as expected!
|
||||||
|
|
||||||
You get your server version with the request `/_synapse/admin/v1/server_version`.
|
You get your server version with the request `/_synapse/admin/v1/server_version`.
|
||||||
See also [Synapse version API](https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/version_api.rst).
|
See also [Synapse version API](https://github.com/matrix-org/synapse/blob/develop/docs/admin_api/version_api.rst).
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "synapse-admin",
|
"name": "synapse-admin",
|
||||||
"version": "0.2.1",
|
"version": "0.7.0",
|
||||||
"description": "Admin GUI for the Matrix.org server Synapse",
|
"description": "Admin GUI for the Matrix.org server Synapse",
|
||||||
"author": "Awesome Technologies Innovationslabor GmbH",
|
"author": "Awesome Technologies Innovationslabor GmbH",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -22,13 +22,13 @@
|
|||||||
"prettier": "^2.0.0"
|
"prettier": "^2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"papaparse": "^5.2.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"ra-language-german": "^2.1.2",
|
"ra-language-german": "^2.1.2",
|
||||||
"papaparse": "^5.2.0",
|
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-admin": "^3.7.0",
|
"react-admin": "^3.10.0",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.14.0",
|
||||||
"react-scripts": "^3.4.1"
|
"react-scripts": "^3.4.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start",
|
"start": "REACT_APP_VERSION=$(git describe --tags) react-scripts start",
|
||||||
|
|||||||
21
src/App.js
21
src/App.js
@@ -5,9 +5,13 @@ import authProvider from "./synapse/authProvider";
|
|||||||
import dataProvider from "./synapse/dataProvider";
|
import dataProvider from "./synapse/dataProvider";
|
||||||
import { UserList, UserCreate, UserEdit } from "./components/users";
|
import { UserList, UserCreate, UserEdit } from "./components/users";
|
||||||
import { RoomList, RoomShow } from "./components/rooms";
|
import { RoomList, RoomShow } from "./components/rooms";
|
||||||
|
import { ReportList, ReportShow } from "./components/EventReports";
|
||||||
import LoginPage from "./components/LoginPage";
|
import LoginPage from "./components/LoginPage";
|
||||||
import UserIcon from "@material-ui/icons/Group";
|
import UserIcon from "@material-ui/icons/Group";
|
||||||
import { ViewListIcon as RoomIcon } from "@material-ui/icons/ViewList";
|
import EqualizerIcon from "@material-ui/icons/Equalizer";
|
||||||
|
import { UserMediaStatsList } from "./components/statistics";
|
||||||
|
import RoomIcon from "@material-ui/icons/ViewList";
|
||||||
|
import ReportIcon from "@material-ui/icons/Warning";
|
||||||
import { ImportFeature } from "./components/ImportFeature";
|
import { ImportFeature } from "./components/ImportFeature";
|
||||||
import { Route } from "react-router-dom";
|
import { Route } from "react-router-dom";
|
||||||
import germanMessages from "./i18n/de";
|
import germanMessages from "./i18n/de";
|
||||||
@@ -25,6 +29,7 @@ const i18nProvider = polyglotI18nProvider(
|
|||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<Admin
|
<Admin
|
||||||
|
disableTelemetry
|
||||||
loginPage={LoginPage}
|
loginPage={LoginPage}
|
||||||
authProvider={authProvider}
|
authProvider={authProvider}
|
||||||
dataProvider={dataProvider}
|
dataProvider={dataProvider}
|
||||||
@@ -41,9 +46,23 @@ const App = () => (
|
|||||||
icon={UserIcon}
|
icon={UserIcon}
|
||||||
/>
|
/>
|
||||||
<Resource name="rooms" list={RoomList} show={RoomShow} icon={RoomIcon} />
|
<Resource name="rooms" list={RoomList} show={RoomShow} icon={RoomIcon} />
|
||||||
|
<Resource
|
||||||
|
name="user_media_statistics"
|
||||||
|
list={UserMediaStatsList}
|
||||||
|
icon={EqualizerIcon}
|
||||||
|
/>
|
||||||
|
<Resource
|
||||||
|
name="reports"
|
||||||
|
list={ReportList}
|
||||||
|
show={ReportShow}
|
||||||
|
icon={ReportIcon}
|
||||||
|
/>
|
||||||
<Resource name="connections" />
|
<Resource name="connections" />
|
||||||
<Resource name="devices" />
|
<Resource name="devices" />
|
||||||
<Resource name="room_members" />
|
<Resource name="room_members" />
|
||||||
|
<Resource name="users_media" />
|
||||||
|
<Resource name="joined_rooms" />
|
||||||
|
<Resource name="pushers" />
|
||||||
<Resource name="servernotices" />
|
<Resource name="servernotices" />
|
||||||
</Admin>
|
</Admin>
|
||||||
);
|
);
|
||||||
|
|||||||
135
src/components/EventReports.js
Normal file
135
src/components/EventReports.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Datagrid,
|
||||||
|
DateField,
|
||||||
|
List,
|
||||||
|
NumberField,
|
||||||
|
Pagination,
|
||||||
|
ReferenceField,
|
||||||
|
Show,
|
||||||
|
Tab,
|
||||||
|
TabbedShowLayout,
|
||||||
|
TextField,
|
||||||
|
useTranslate,
|
||||||
|
} from "react-admin";
|
||||||
|
import PageviewIcon from "@material-ui/icons/Pageview";
|
||||||
|
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||||
|
|
||||||
|
const ReportPagination = props => (
|
||||||
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ReportShow = props => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
return (
|
||||||
|
<Show {...props}>
|
||||||
|
<TabbedShowLayout>
|
||||||
|
<Tab
|
||||||
|
label={translate("synapseadmin.reports.tabs.basic", {
|
||||||
|
smart_count: 1,
|
||||||
|
})}
|
||||||
|
icon={<ViewListIcon />}
|
||||||
|
>
|
||||||
|
<DateField
|
||||||
|
source="received_ts"
|
||||||
|
showTime
|
||||||
|
options={{
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
}}
|
||||||
|
sortable={true}
|
||||||
|
/>
|
||||||
|
<ReferenceField source="user_id" reference="users">
|
||||||
|
<TextField source="id" />
|
||||||
|
</ReferenceField>
|
||||||
|
<NumberField source="score" />
|
||||||
|
<TextField source="reason" />
|
||||||
|
<TextField source="name" />
|
||||||
|
<TextField
|
||||||
|
source="canonical_alias"
|
||||||
|
label="resources.rooms.fields.canonical_alias"
|
||||||
|
/>
|
||||||
|
<ReferenceField
|
||||||
|
source="room_id"
|
||||||
|
reference="rooms"
|
||||||
|
link="show"
|
||||||
|
label="resources.rooms.fields.room_id"
|
||||||
|
>
|
||||||
|
<TextField source="id" />
|
||||||
|
</ReferenceField>
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab
|
||||||
|
label="synapseadmin.reports.tabs.detail"
|
||||||
|
icon={<PageviewIcon />}
|
||||||
|
path="detail"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<DateField
|
||||||
|
source="event_json.origin_server_ts"
|
||||||
|
showTime
|
||||||
|
options={{
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
}}
|
||||||
|
sortable={true}
|
||||||
|
/>
|
||||||
|
<ReferenceField source="sender" reference="users">
|
||||||
|
<TextField source="id" />
|
||||||
|
</ReferenceField>
|
||||||
|
<TextField source="event_id" />
|
||||||
|
<TextField source="event_json.origin" />
|
||||||
|
<TextField source="event_json.type" />
|
||||||
|
<TextField source="event_json.content.msgtype" />
|
||||||
|
<TextField source="event_json.content.body" />
|
||||||
|
<TextField source="event_json.content.format" />
|
||||||
|
<TextField source="event_json.content.formatted_body" />
|
||||||
|
<TextField source="event_json.content.algorithm" />
|
||||||
|
<TextField
|
||||||
|
source="event_json.content.device_id"
|
||||||
|
label="resources.users.fields.device_id"
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
</TabbedShowLayout>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ReportList = ({ ...props }) => {
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
{...props}
|
||||||
|
pagination={<ReportPagination />}
|
||||||
|
sort={{ field: "received_ts", order: "DESC" }}
|
||||||
|
bulkActionButtons={false}
|
||||||
|
>
|
||||||
|
<Datagrid rowClick="show">
|
||||||
|
<TextField source="id" sortable={false} />
|
||||||
|
<DateField
|
||||||
|
source="received_ts"
|
||||||
|
showTime
|
||||||
|
options={{
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
}}
|
||||||
|
sortable={true}
|
||||||
|
/>
|
||||||
|
<TextField sortable={false} source="user_id" />
|
||||||
|
<TextField sortable={false} source="name" />
|
||||||
|
<TextField sortable={false} source="score" />
|
||||||
|
</Datagrid>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
BooleanField,
|
BooleanField,
|
||||||
BulkDeleteWithConfirmButton,
|
BulkDeleteWithConfirmButton,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
|
DeleteButton,
|
||||||
Filter,
|
Filter,
|
||||||
List,
|
List,
|
||||||
Pagination,
|
Pagination,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
Tab,
|
Tab,
|
||||||
TabbedShowLayout,
|
TabbedShowLayout,
|
||||||
TextField,
|
TextField,
|
||||||
|
TopToolbar,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
@@ -70,10 +72,26 @@ const RoomTitle = ({ record }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RoomShowActions = ({ basePath, data, resource }) => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
return (
|
||||||
|
<TopToolbar>
|
||||||
|
<DeleteButton
|
||||||
|
basePath={basePath}
|
||||||
|
record={data}
|
||||||
|
resource={resource}
|
||||||
|
undoable={false}
|
||||||
|
confirmTitle={translate("synapseadmin.rooms.delete.title")}
|
||||||
|
confirmContent={translate("synapseadmin.rooms.delete.message")}
|
||||||
|
/>
|
||||||
|
</TopToolbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const RoomShow = props => {
|
export const RoomShow = props => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Show {...props} title={<RoomTitle />}>
|
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
||||||
<TabbedShowLayout>
|
<TabbedShowLayout>
|
||||||
<Tab label="synapseadmin.rooms.tabs.basic" icon={<ViewListIcon />}>
|
<Tab label="synapseadmin.rooms.tabs.basic" icon={<ViewListIcon />}>
|
||||||
<TextField source="room_id" />
|
<TextField source="room_id" />
|
||||||
|
|||||||
42
src/components/statistics.js
Normal file
42
src/components/statistics.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Datagrid,
|
||||||
|
Filter,
|
||||||
|
List,
|
||||||
|
NumberField,
|
||||||
|
TextField,
|
||||||
|
SearchInput,
|
||||||
|
Pagination,
|
||||||
|
} from "react-admin";
|
||||||
|
|
||||||
|
const UserMediaStatsPagination = props => (
|
||||||
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const UserMediaStatsFilter = props => (
|
||||||
|
<Filter {...props}>
|
||||||
|
<SearchInput source="search_term" alwaysOn />
|
||||||
|
</Filter>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const UserMediaStatsList = props => {
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
{...props}
|
||||||
|
filters={<UserMediaStatsFilter />}
|
||||||
|
pagination={<UserMediaStatsPagination />}
|
||||||
|
sort={{ field: "media_length", order: "DESC" }}
|
||||||
|
bulkActionButtons={false}
|
||||||
|
>
|
||||||
|
<Datagrid rowClick={(id, basePath, record) => "/users/" + id + "/media"}>
|
||||||
|
<TextField source="user_id" label="resources.users.fields.id" />
|
||||||
|
<TextField
|
||||||
|
source="displayname"
|
||||||
|
label="resources.users.fields.displayname"
|
||||||
|
/>
|
||||||
|
<NumberField source="media_count" />
|
||||||
|
<NumberField source="media_length" />
|
||||||
|
</Datagrid>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -5,6 +5,9 @@ import ContactMailIcon from "@material-ui/icons/ContactMail";
|
|||||||
import DevicesIcon from "@material-ui/icons/Devices";
|
import DevicesIcon from "@material-ui/icons/Devices";
|
||||||
import GetAppIcon from "@material-ui/icons/GetApp";
|
import GetAppIcon from "@material-ui/icons/GetApp";
|
||||||
import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent";
|
import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent";
|
||||||
|
import NotificationsIcon from "@material-ui/icons/Notifications";
|
||||||
|
import PermMediaIcon from "@material-ui/icons/PermMedia";
|
||||||
|
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||||
import {
|
import {
|
||||||
ArrayInput,
|
ArrayInput,
|
||||||
ArrayField,
|
ArrayField,
|
||||||
@@ -40,6 +43,7 @@ import {
|
|||||||
ExportButton,
|
ExportButton,
|
||||||
TopToolbar,
|
TopToolbar,
|
||||||
sanitizeListRestProps,
|
sanitizeListRestProps,
|
||||||
|
NumberField,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
||||||
import { DeviceRemoveButton } from "./devices";
|
import { DeviceRemoveButton } from "./devices";
|
||||||
@@ -312,6 +316,7 @@ export const UserEdit = props => {
|
|||||||
/>
|
/>
|
||||||
<TextField source="consent_version" />
|
<TextField source="consent_version" />
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
<FormTab
|
<FormTab
|
||||||
label="resources.users.threepid"
|
label="resources.users.threepid"
|
||||||
icon={<ContactMailIcon />}
|
icon={<ContactMailIcon />}
|
||||||
@@ -330,6 +335,7 @@ export const UserEdit = props => {
|
|||||||
</SimpleFormIterator>
|
</SimpleFormIterator>
|
||||||
</ArrayInput>
|
</ArrayInput>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
<FormTab
|
<FormTab
|
||||||
label={translate("resources.devices.name", { smart_count: 2 })}
|
label={translate("resources.devices.name", { smart_count: 2 })}
|
||||||
icon={<DevicesIcon />}
|
icon={<DevicesIcon />}
|
||||||
@@ -361,6 +367,7 @@ export const UserEdit = props => {
|
|||||||
</Datagrid>
|
</Datagrid>
|
||||||
</ReferenceManyField>
|
</ReferenceManyField>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
<FormTab
|
<FormTab
|
||||||
label="resources.connections.name"
|
label="resources.connections.name"
|
||||||
icon={<SettingsInputComponentIcon />}
|
icon={<SettingsInputComponentIcon />}
|
||||||
@@ -400,6 +407,111 @@ export const UserEdit = props => {
|
|||||||
</ArrayField>
|
</ArrayField>
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab
|
||||||
|
label={translate("resources.users_media.name", { smart_count: 2 })}
|
||||||
|
icon={<PermMediaIcon />}
|
||||||
|
path="media"
|
||||||
|
>
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="users_media"
|
||||||
|
target="user_id"
|
||||||
|
addLabel={false}
|
||||||
|
pagination={<UserPagination />}
|
||||||
|
perPage={50}
|
||||||
|
>
|
||||||
|
<Datagrid style={{ width: "100%" }}>
|
||||||
|
<DateField
|
||||||
|
source="created_ts"
|
||||||
|
showTime
|
||||||
|
options={{
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
}}
|
||||||
|
sortable={false}
|
||||||
|
/>
|
||||||
|
<DateField
|
||||||
|
source="last_access_ts"
|
||||||
|
showTime
|
||||||
|
options={{
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
}}
|
||||||
|
sortable={false}
|
||||||
|
/>
|
||||||
|
<TextField source="media_id" sortable={false} />
|
||||||
|
<NumberField source="media_length" sortable={false} />
|
||||||
|
<TextField source="media_type" sortable={false} />
|
||||||
|
<TextField source="upload_name" sortable={false} />
|
||||||
|
<TextField source="quarantined_by" sortable={false} />
|
||||||
|
<BooleanField source="safe_from_quarantine" sortable={false} />
|
||||||
|
<DeleteButton undoable={false} redirect={false} />
|
||||||
|
</Datagrid>
|
||||||
|
</ReferenceManyField>
|
||||||
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab
|
||||||
|
label={translate("resources.rooms.name", { smart_count: 2 })}
|
||||||
|
icon={<ViewListIcon />}
|
||||||
|
path="rooms"
|
||||||
|
>
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="joined_rooms"
|
||||||
|
target="user_id"
|
||||||
|
addLabel={false}
|
||||||
|
>
|
||||||
|
<Datagrid
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
source="id"
|
||||||
|
sortable={false}
|
||||||
|
label="resources.rooms.fields.room_id"
|
||||||
|
/>
|
||||||
|
<ReferenceField
|
||||||
|
label="resources.rooms.fields.name"
|
||||||
|
source="id"
|
||||||
|
reference="rooms"
|
||||||
|
sortable={false}
|
||||||
|
link=""
|
||||||
|
>
|
||||||
|
<TextField source="name" sortable={false} />
|
||||||
|
</ReferenceField>
|
||||||
|
</Datagrid>
|
||||||
|
</ReferenceManyField>
|
||||||
|
</FormTab>
|
||||||
|
|
||||||
|
<FormTab
|
||||||
|
label={translate("resources.pushers.name", { smart_count: 2 })}
|
||||||
|
icon={<NotificationsIcon />}
|
||||||
|
path="pushers"
|
||||||
|
>
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="pushers"
|
||||||
|
target="user_id"
|
||||||
|
addLabel={false}
|
||||||
|
>
|
||||||
|
<Datagrid style={{ width: "100%" }}>
|
||||||
|
<TextField source="kind" sortable={false} />
|
||||||
|
<TextField source="app_display_name" sortable={false} />
|
||||||
|
<TextField source="app_id" sortable={false} />
|
||||||
|
<TextField source="data.url" sortable={false} />
|
||||||
|
<TextField source="device_display_name" sortable={false} />
|
||||||
|
<TextField source="lang" sortable={false} />
|
||||||
|
<TextField source="profile_tag" sortable={false} />
|
||||||
|
<TextField source="pushkey" sortable={false} />
|
||||||
|
</Datagrid>
|
||||||
|
</ReferenceManyField>
|
||||||
|
</FormTab>
|
||||||
</TabbedForm>
|
</TabbedForm>
|
||||||
</Edit>
|
</Edit>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export default {
|
|||||||
"Sind Sie sicher dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
"Sind Sie sicher dass Sie den Raum löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden. Alle Nachrichten und Medien, die der Raum beinhaltet werden vom Server gelöscht!",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
reports: { tabs: { basic: "Allgemein", detail: "Details" } },
|
||||||
},
|
},
|
||||||
import_users: {
|
import_users: {
|
||||||
error: {
|
error: {
|
||||||
@@ -126,7 +127,8 @@ export default {
|
|||||||
consent_version: "Zugestimmte Geschäftsbedingungen",
|
consent_version: "Zugestimmte Geschäftsbedingungen",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
deactivate: "Deaktivierte Nutzer können nicht wieder aktiviert werden.",
|
deactivate:
|
||||||
|
"Sie müssen ein Passwort angeben, um ein Konto wieder zu aktivieren.",
|
||||||
erase: "DSGVO konformes Löschen der Benutzerdaten",
|
erase: "DSGVO konformes Löschen der Benutzerdaten",
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
@@ -172,6 +174,30 @@ export default {
|
|||||||
unencrypted: "Nicht verschlüsselt",
|
unencrypted: "Nicht verschlüsselt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
reports: {
|
||||||
|
name: "Ereignisbericht |||| Ereignisberichte",
|
||||||
|
fields: {
|
||||||
|
id: "ID",
|
||||||
|
received_ts: "Meldezeit",
|
||||||
|
user_id: "Meldender",
|
||||||
|
name: "Raumname",
|
||||||
|
score: "Wert",
|
||||||
|
reason: "Grund",
|
||||||
|
event_id: "Event-ID",
|
||||||
|
event_json: {
|
||||||
|
origin: "Ursprungsserver",
|
||||||
|
origin_server_ts: "Sendezeit",
|
||||||
|
type: "Eventtyp",
|
||||||
|
content: {
|
||||||
|
msgtype: "Inhaltstyp",
|
||||||
|
body: "Nachrichteninhalt",
|
||||||
|
format: "Nachrichtenformat",
|
||||||
|
formatted_body: "Formatierter Nachrichteninhalt",
|
||||||
|
algorithm: "Verschlüsselungsalgorithmus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
connections: {
|
connections: {
|
||||||
name: "Verbindungen",
|
name: "Verbindungen",
|
||||||
fields: {
|
fields: {
|
||||||
@@ -197,6 +223,33 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
users_media: {
|
||||||
|
name: "Medien",
|
||||||
|
fields: {
|
||||||
|
media_id: "Medien ID",
|
||||||
|
media_length: "Größe",
|
||||||
|
media_type: "Typ",
|
||||||
|
upload_name: "Dateiname",
|
||||||
|
quarantined_by: "Zur Quarantäne hinzugefügt",
|
||||||
|
safe_from_quarantine: "Geschützt vor Quarantäne",
|
||||||
|
created_ts: "Erstellt",
|
||||||
|
last_access_ts: "Letzter Zugriff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pushers: {
|
||||||
|
name: "Pusher |||| Pushers",
|
||||||
|
fields: {
|
||||||
|
app: "App",
|
||||||
|
app_display_name: "App-Anzeigename",
|
||||||
|
app_id: "App ID",
|
||||||
|
device_display_name: "Geräte-Anzeigename",
|
||||||
|
kind: "Art",
|
||||||
|
lang: "Sprache",
|
||||||
|
profile_tag: "Profil-Tag",
|
||||||
|
pushkey: "Pushkey",
|
||||||
|
data: { url: "URL" },
|
||||||
|
},
|
||||||
|
},
|
||||||
servernotices: {
|
servernotices: {
|
||||||
name: "Serverbenachrichtigungen",
|
name: "Serverbenachrichtigungen",
|
||||||
send: "Servernachricht versenden",
|
send: "Servernachricht versenden",
|
||||||
@@ -213,9 +266,20 @@ export default {
|
|||||||
'Sendet eine Serverbenachrichtigung an die ausgewählten Nutzer. Hierfür muss das Feature "Server Notices" auf dem Server aktiviert sein.',
|
'Sendet eine Serverbenachrichtigung an die ausgewählten Nutzer. Hierfür muss das Feature "Server Notices" auf dem Server aktiviert sein.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
user_media_statistics: {
|
||||||
|
name: "Dateien je Benutzer",
|
||||||
|
fields: {
|
||||||
|
media_count: "Anzahl der Dateien",
|
||||||
|
media_length: "Größe der Dateien",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ra: {
|
ra: {
|
||||||
...germanMessages.ra,
|
...germanMessages.ra,
|
||||||
|
action: {
|
||||||
|
...germanMessages.ra.action,
|
||||||
|
unselect: "Abwählen",
|
||||||
|
},
|
||||||
auth: {
|
auth: {
|
||||||
...germanMessages.ra.auth,
|
...germanMessages.ra.auth,
|
||||||
auth_check_error: "Anmeldung fehlgeschlagen",
|
auth_check_error: "Anmeldung fehlgeschlagen",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export default {
|
|||||||
auth: {
|
auth: {
|
||||||
base_url: "Homeserver URL",
|
base_url: "Homeserver URL",
|
||||||
welcome: "Welcome to Synapse-admin",
|
welcome: "Welcome to Synapse-admin",
|
||||||
|
server_version: "Synapse version",
|
||||||
username_error: "Please enter fully qualified user ID: '@user:domain'",
|
username_error: "Please enter fully qualified user ID: '@user:domain'",
|
||||||
protocol_error: "URL has to start with 'http://' or 'https://'",
|
protocol_error: "URL has to start with 'http://' or 'https://'",
|
||||||
url_error: "Not a valid Matrix server URL",
|
url_error: "Not a valid Matrix server URL",
|
||||||
@@ -27,6 +28,7 @@ export default {
|
|||||||
"Are you sure you want to delete the room? This cannot be undone. All messages and shared media in the room will be deleted from the server!",
|
"Are you sure you want to delete the room? This cannot be undone. All messages and shared media in the room will be deleted from the server!",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
reports: { tabs: { basic: "Basic", detail: "Details" } },
|
||||||
},
|
},
|
||||||
import_users: {
|
import_users: {
|
||||||
error: {
|
error: {
|
||||||
@@ -124,7 +126,7 @@ export default {
|
|||||||
consent_version: "Consent version",
|
consent_version: "Consent version",
|
||||||
},
|
},
|
||||||
helper: {
|
helper: {
|
||||||
deactivate: "Deactivated users cannot be reactivated",
|
deactivate: "You must provide a password to re-activate an account.",
|
||||||
erase: "Mark the user as GDPR-erased",
|
erase: "Mark the user as GDPR-erased",
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
@@ -138,7 +140,7 @@ export default {
|
|||||||
name: "Name",
|
name: "Name",
|
||||||
canonical_alias: "Alias",
|
canonical_alias: "Alias",
|
||||||
joined_members: "Members",
|
joined_members: "Members",
|
||||||
joined_local_members: "local members",
|
joined_local_members: "Local members",
|
||||||
state_events: "State events",
|
state_events: "State events",
|
||||||
version: "Version",
|
version: "Version",
|
||||||
is_encrypted: "Encrypted",
|
is_encrypted: "Encrypted",
|
||||||
@@ -170,6 +172,30 @@ export default {
|
|||||||
unencrypted: "Unencrypted",
|
unencrypted: "Unencrypted",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
reports: {
|
||||||
|
name: "Reported event |||| Reported events",
|
||||||
|
fields: {
|
||||||
|
id: "ID",
|
||||||
|
received_ts: "report time",
|
||||||
|
user_id: "announcer",
|
||||||
|
name: "name of the room",
|
||||||
|
score: "score",
|
||||||
|
reason: "reason",
|
||||||
|
event_id: "event ID",
|
||||||
|
event_json: {
|
||||||
|
origin: "origin server",
|
||||||
|
origin_server_ts: "time of send",
|
||||||
|
type: "event typ",
|
||||||
|
content: {
|
||||||
|
msgtype: "content type",
|
||||||
|
body: "content",
|
||||||
|
format: "format",
|
||||||
|
formatted_body: "formatted content",
|
||||||
|
algorithm: "algorithm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
connections: {
|
connections: {
|
||||||
name: "Connections",
|
name: "Connections",
|
||||||
fields: {
|
fields: {
|
||||||
@@ -195,6 +221,33 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
users_media: {
|
||||||
|
name: "Media",
|
||||||
|
fields: {
|
||||||
|
media_id: "Media ID",
|
||||||
|
media_length: "Lenght",
|
||||||
|
media_type: "Type",
|
||||||
|
upload_name: "File name",
|
||||||
|
quarantined_by: "Quarantined by",
|
||||||
|
safe_from_quarantine: "Safe from quarantine",
|
||||||
|
created_ts: "Created",
|
||||||
|
last_access_ts: "Last access",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pushers: {
|
||||||
|
name: "Pusher |||| Pushers",
|
||||||
|
fields: {
|
||||||
|
app: "App",
|
||||||
|
app_display_name: "App display name",
|
||||||
|
app_id: "App ID",
|
||||||
|
device_display_name: "Device display name",
|
||||||
|
kind: "Kind",
|
||||||
|
lang: "Language",
|
||||||
|
profile_tag: "Profile tag",
|
||||||
|
pushkey: "Pushkey",
|
||||||
|
data: { url: "URL" },
|
||||||
|
},
|
||||||
|
},
|
||||||
servernotices: {
|
servernotices: {
|
||||||
name: "Server Notices",
|
name: "Server Notices",
|
||||||
send: "Send server notices",
|
send: "Send server notices",
|
||||||
@@ -211,5 +264,12 @@ export default {
|
|||||||
'Sends a server notice to the selected users. The feature "Server Notices" has to be activated at the server.',
|
'Sends a server notice to the selected users. The feature "Server Notices" has to be activated at the server.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
user_media_statistics: {
|
||||||
|
name: "Users' media",
|
||||||
|
fields: {
|
||||||
|
media_count: "Media count",
|
||||||
|
media_length: "Media length",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const authProvider = {
|
|||||||
type: "m.login.password",
|
type: "m.login.password",
|
||||||
user: username,
|
user: username,
|
||||||
password: password,
|
password: password,
|
||||||
|
initial_device_display_name: "Synapse Admin",
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,8 +31,26 @@ const authProvider = {
|
|||||||
},
|
},
|
||||||
// called when the user clicks on the logout button
|
// called when the user clicks on the logout button
|
||||||
logout: () => {
|
logout: () => {
|
||||||
console.log("logout ");
|
console.log("logout");
|
||||||
localStorage.removeItem("access_token");
|
|
||||||
|
const logout_api_url =
|
||||||
|
localStorage.getItem("base_url") + "/_matrix/client/r0/logout";
|
||||||
|
const access_token = localStorage.getItem("access_token");
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
authenticated: true,
|
||||||
|
token: `Bearer ${access_token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof access_token === "string") {
|
||||||
|
fetchUtils.fetchJson(logout_api_url, options).then(({ json }) => {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
localStorage.removeItem("device_id");
|
||||||
|
});
|
||||||
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
// called when the API returns an error
|
// called when the API returns an error
|
||||||
@@ -46,7 +65,7 @@ const authProvider = {
|
|||||||
checkAuth: () => {
|
checkAuth: () => {
|
||||||
const access_token = localStorage.getItem("access_token");
|
const access_token = localStorage.getItem("access_token");
|
||||||
console.log("checkAuth " + access_token);
|
console.log("checkAuth " + access_token);
|
||||||
return typeof access_token == "string"
|
return typeof access_token === "string"
|
||||||
? Promise.resolve()
|
? Promise.resolve()
|
||||||
: Promise.reject();
|
: Promise.reject();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,12 +72,24 @@ const resourceMap = {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
reports: {
|
||||||
|
path: "/_synapse/admin/v1/event_reports",
|
||||||
|
map: er => ({
|
||||||
|
...er,
|
||||||
|
id: er.id,
|
||||||
|
}),
|
||||||
|
data: "event_reports",
|
||||||
|
total: json => json.total,
|
||||||
|
},
|
||||||
devices: {
|
devices: {
|
||||||
map: d => ({
|
map: d => ({
|
||||||
...d,
|
...d,
|
||||||
id: d.device_id,
|
id: d.device_id,
|
||||||
}),
|
}),
|
||||||
data: "devices",
|
data: "devices",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
reference: id => ({
|
reference: id => ({
|
||||||
endpoint: `/_synapse/admin/v2/users/${id}/devices`,
|
endpoint: `/_synapse/admin/v2/users/${id}/devices`,
|
||||||
}),
|
}),
|
||||||
@@ -101,6 +113,52 @@ const resourceMap = {
|
|||||||
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
|
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
|
||||||
}),
|
}),
|
||||||
data: "members",
|
data: "members",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pushers: {
|
||||||
|
map: p => ({
|
||||||
|
...p,
|
||||||
|
id: p.pushkey,
|
||||||
|
}),
|
||||||
|
reference: id => ({
|
||||||
|
endpoint: `/_synapse/admin/v1/users/${id}/pushers`,
|
||||||
|
}),
|
||||||
|
data: "pushers",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
joined_rooms: {
|
||||||
|
map: jr => ({
|
||||||
|
id: jr,
|
||||||
|
}),
|
||||||
|
reference: id => ({
|
||||||
|
endpoint: `/_synapse/admin/v1/users/${id}/joined_rooms`,
|
||||||
|
}),
|
||||||
|
data: "joined_rooms",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
users_media: {
|
||||||
|
map: um => ({
|
||||||
|
...um,
|
||||||
|
id: um.media_id,
|
||||||
|
}),
|
||||||
|
reference: id => ({
|
||||||
|
endpoint: `/_synapse/admin/v1/users/${id}/media`,
|
||||||
|
}),
|
||||||
|
data: "media",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
|
delete: params => ({
|
||||||
|
endpoint: `/_synapse/admin/v1/media/${localStorage.getItem(
|
||||||
|
"home_server"
|
||||||
|
)}/${params.id}`,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
servernotices: {
|
servernotices: {
|
||||||
map: n => ({ id: n.event_id }),
|
map: n => ({ id: n.event_id }),
|
||||||
@@ -116,6 +174,17 @@ const resourceMap = {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
user_media_statistics: {
|
||||||
|
path: "/_synapse/admin/v1/statistics/users/media",
|
||||||
|
map: usms => ({
|
||||||
|
...usms,
|
||||||
|
id: usms.user_id,
|
||||||
|
}),
|
||||||
|
data: "users",
|
||||||
|
total: json => {
|
||||||
|
return json.total;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function filterNullValues(key, value) {
|
function filterNullValues(key, value) {
|
||||||
@@ -191,11 +260,18 @@ const dataProvider = {
|
|||||||
params.ids.map(id => jsonClient(`${endpoint_url}/${id}`))
|
params.ids.map(id => jsonClient(`${endpoint_url}/${id}`))
|
||||||
).then(responses => ({
|
).then(responses => ({
|
||||||
data: responses.map(({ json }) => res.map(json)),
|
data: responses.map(({ json }) => res.map(json)),
|
||||||
|
total: responses.length,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
getManyReference: (resource, params) => {
|
getManyReference: (resource, params) => {
|
||||||
console.log("getManyReference " + resource);
|
console.log("getManyReference " + resource);
|
||||||
|
const { page, perPage } = params.pagination;
|
||||||
|
const from = (page - 1) * perPage;
|
||||||
|
const query = {
|
||||||
|
from: from,
|
||||||
|
limit: perPage,
|
||||||
|
};
|
||||||
|
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
||||||
@@ -203,10 +279,11 @@ const dataProvider = {
|
|||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
const ref = res["reference"](params.id);
|
const ref = res["reference"](params.id);
|
||||||
const endpoint_url = homeserver + ref.endpoint;
|
const endpoint_url = `${homeserver}${ref.endpoint}?${stringify(query)}`;
|
||||||
|
|
||||||
return jsonClient(endpoint_url).then(({ headers, json }) => ({
|
return jsonClient(endpoint_url).then(({ headers, json }) => ({
|
||||||
data: json[res.data].map(res.map),
|
data: json[res.data].map(res.map),
|
||||||
|
total: res.total(json, from, perPage),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user