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/).
|
||||
|
||||
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`.
|
||||
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",
|
||||
"version": "0.2.1",
|
||||
"version": "0.7.0",
|
||||
"description": "Admin GUI for the Matrix.org server Synapse",
|
||||
"author": "Awesome Technologies Innovationslabor GmbH",
|
||||
"license": "Apache-2.0",
|
||||
@@ -22,13 +22,13 @@
|
||||
"prettier": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"papaparse": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"ra-language-german": "^2.1.2",
|
||||
"papaparse": "^5.2.0",
|
||||
"react": "^16.13.1",
|
||||
"react-admin": "^3.7.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "^3.4.1"
|
||||
"react-admin": "^3.10.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-scripts": "^3.4.4"
|
||||
},
|
||||
"scripts": {
|
||||
"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 { UserList, UserCreate, UserEdit } from "./components/users";
|
||||
import { RoomList, RoomShow } from "./components/rooms";
|
||||
import { ReportList, ReportShow } from "./components/EventReports";
|
||||
import LoginPage from "./components/LoginPage";
|
||||
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 { Route } from "react-router-dom";
|
||||
import germanMessages from "./i18n/de";
|
||||
@@ -25,6 +29,7 @@ const i18nProvider = polyglotI18nProvider(
|
||||
|
||||
const App = () => (
|
||||
<Admin
|
||||
disableTelemetry
|
||||
loginPage={LoginPage}
|
||||
authProvider={authProvider}
|
||||
dataProvider={dataProvider}
|
||||
@@ -41,9 +46,23 @@ const App = () => (
|
||||
icon={UserIcon}
|
||||
/>
|
||||
<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="devices" />
|
||||
<Resource name="room_members" />
|
||||
<Resource name="users_media" />
|
||||
<Resource name="joined_rooms" />
|
||||
<Resource name="pushers" />
|
||||
<Resource name="servernotices" />
|
||||
</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,
|
||||
BulkDeleteWithConfirmButton,
|
||||
Datagrid,
|
||||
DeleteButton,
|
||||
Filter,
|
||||
List,
|
||||
Pagination,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
Tab,
|
||||
TabbedShowLayout,
|
||||
TextField,
|
||||
TopToolbar,
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
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 => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<Show {...props} title={<RoomTitle />}>
|
||||
<Show {...props} actions={<RoomShowActions />} title={<RoomTitle />}>
|
||||
<TabbedShowLayout>
|
||||
<Tab label="synapseadmin.rooms.tabs.basic" icon={<ViewListIcon />}>
|
||||
<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 GetAppIcon from "@material-ui/icons/GetApp";
|
||||
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 {
|
||||
ArrayInput,
|
||||
ArrayField,
|
||||
@@ -40,6 +43,7 @@ import {
|
||||
ExportButton,
|
||||
TopToolbar,
|
||||
sanitizeListRestProps,
|
||||
NumberField,
|
||||
} from "react-admin";
|
||||
import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices";
|
||||
import { DeviceRemoveButton } from "./devices";
|
||||
@@ -312,6 +316,7 @@ export const UserEdit = props => {
|
||||
/>
|
||||
<TextField source="consent_version" />
|
||||
</FormTab>
|
||||
|
||||
<FormTab
|
||||
label="resources.users.threepid"
|
||||
icon={<ContactMailIcon />}
|
||||
@@ -330,6 +335,7 @@ export const UserEdit = props => {
|
||||
</SimpleFormIterator>
|
||||
</ArrayInput>
|
||||
</FormTab>
|
||||
|
||||
<FormTab
|
||||
label={translate("resources.devices.name", { smart_count: 2 })}
|
||||
icon={<DevicesIcon />}
|
||||
@@ -361,6 +367,7 @@ export const UserEdit = props => {
|
||||
</Datagrid>
|
||||
</ReferenceManyField>
|
||||
</FormTab>
|
||||
|
||||
<FormTab
|
||||
label="resources.connections.name"
|
||||
icon={<SettingsInputComponentIcon />}
|
||||
@@ -400,6 +407,111 @@ export const UserEdit = props => {
|
||||
</ArrayField>
|
||||
</ReferenceField>
|
||||
</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>
|
||||
</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!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "Allgemein", detail: "Details" } },
|
||||
},
|
||||
import_users: {
|
||||
error: {
|
||||
@@ -126,7 +127,8 @@ export default {
|
||||
consent_version: "Zugestimmte Geschäftsbedingungen",
|
||||
},
|
||||
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",
|
||||
},
|
||||
action: {
|
||||
@@ -172,6 +174,30 @@ export default {
|
||||
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: {
|
||||
name: "Verbindungen",
|
||||
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: {
|
||||
name: "Serverbenachrichtigungen",
|
||||
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.',
|
||||
},
|
||||
},
|
||||
user_media_statistics: {
|
||||
name: "Dateien je Benutzer",
|
||||
fields: {
|
||||
media_count: "Anzahl der Dateien",
|
||||
media_length: "Größe der Dateien",
|
||||
},
|
||||
},
|
||||
},
|
||||
ra: {
|
||||
...germanMessages.ra,
|
||||
action: {
|
||||
...germanMessages.ra.action,
|
||||
unselect: "Abwählen",
|
||||
},
|
||||
auth: {
|
||||
...germanMessages.ra.auth,
|
||||
auth_check_error: "Anmeldung fehlgeschlagen",
|
||||
|
||||
@@ -6,6 +6,7 @@ export default {
|
||||
auth: {
|
||||
base_url: "Homeserver URL",
|
||||
welcome: "Welcome to Synapse-admin",
|
||||
server_version: "Synapse version",
|
||||
username_error: "Please enter fully qualified user ID: '@user:domain'",
|
||||
protocol_error: "URL has to start with 'http://' or 'https://'",
|
||||
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!",
|
||||
},
|
||||
},
|
||||
reports: { tabs: { basic: "Basic", detail: "Details" } },
|
||||
},
|
||||
import_users: {
|
||||
error: {
|
||||
@@ -124,7 +126,7 @@ export default {
|
||||
consent_version: "Consent version",
|
||||
},
|
||||
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",
|
||||
},
|
||||
action: {
|
||||
@@ -138,7 +140,7 @@ export default {
|
||||
name: "Name",
|
||||
canonical_alias: "Alias",
|
||||
joined_members: "Members",
|
||||
joined_local_members: "local members",
|
||||
joined_local_members: "Local members",
|
||||
state_events: "State events",
|
||||
version: "Version",
|
||||
is_encrypted: "Encrypted",
|
||||
@@ -170,6 +172,30 @@ export default {
|
||||
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: {
|
||||
name: "Connections",
|
||||
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: {
|
||||
name: "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.',
|
||||
},
|
||||
},
|
||||
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",
|
||||
user: username,
|
||||
password: password,
|
||||
initial_device_display_name: "Synapse Admin",
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -30,8 +31,26 @@ const authProvider = {
|
||||
},
|
||||
// called when the user clicks on the logout button
|
||||
logout: () => {
|
||||
console.log("logout ");
|
||||
localStorage.removeItem("access_token");
|
||||
console.log("logout");
|
||||
|
||||
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();
|
||||
},
|
||||
// called when the API returns an error
|
||||
@@ -46,7 +65,7 @@ const authProvider = {
|
||||
checkAuth: () => {
|
||||
const access_token = localStorage.getItem("access_token");
|
||||
console.log("checkAuth " + access_token);
|
||||
return typeof access_token == "string"
|
||||
return typeof access_token === "string"
|
||||
? Promise.resolve()
|
||||
: Promise.reject();
|
||||
},
|
||||
|
||||
@@ -72,12 +72,24 @@ const resourceMap = {
|
||||
method: "POST",
|
||||
}),
|
||||
},
|
||||
reports: {
|
||||
path: "/_synapse/admin/v1/event_reports",
|
||||
map: er => ({
|
||||
...er,
|
||||
id: er.id,
|
||||
}),
|
||||
data: "event_reports",
|
||||
total: json => json.total,
|
||||
},
|
||||
devices: {
|
||||
map: d => ({
|
||||
...d,
|
||||
id: d.device_id,
|
||||
}),
|
||||
data: "devices",
|
||||
total: json => {
|
||||
return json.total;
|
||||
},
|
||||
reference: id => ({
|
||||
endpoint: `/_synapse/admin/v2/users/${id}/devices`,
|
||||
}),
|
||||
@@ -101,6 +113,52 @@ const resourceMap = {
|
||||
endpoint: `/_synapse/admin/v1/rooms/${id}/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: {
|
||||
map: n => ({ id: n.event_id }),
|
||||
@@ -116,6 +174,17 @@ const resourceMap = {
|
||||
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) {
|
||||
@@ -191,11 +260,18 @@ const dataProvider = {
|
||||
params.ids.map(id => jsonClient(`${endpoint_url}/${id}`))
|
||||
).then(responses => ({
|
||||
data: responses.map(({ json }) => res.map(json)),
|
||||
total: responses.length,
|
||||
}));
|
||||
},
|
||||
|
||||
getManyReference: (resource, params) => {
|
||||
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");
|
||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
||||
@@ -203,10 +279,11 @@ const dataProvider = {
|
||||
const res = resourceMap[resource];
|
||||
|
||||
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 }) => ({
|
||||
data: json[res.data].map(res.map),
|
||||
total: res.total(json, from, perPage),
|
||||
}));
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user