Merge branch 'master' into add_user_type

This commit is contained in:
Dirk Klimpel 2022-02-17 22:12:25 +01:00 committed by GitHub
commit ebc78cd608
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2042 additions and 1839 deletions

View File

@ -2,8 +2,14 @@ name: Create docker image(s) and push to docker hub
on: on:
push: push:
# Sequence of patterns matched against refs/heads
# prettier-ignore
branches:
# Push events on master branch
- master
# Sequence of patterns matched against refs/tags
tags: tags:
- '[0-9]+\.[0-9]+\.[0-9]+' - '[0-9]+\.[0-9]+\.[0-9]+' # Push events to 0.X.X tag
jobs: jobs:
docker: docker:
@ -21,10 +27,25 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push - name: Calculate docker image tag
id: set-tag
run: |
case "${GITHUB_REF}" in
refs/heads/master|refs/heads/main)
tag=latest
;;
refs/tags/*)
tag=${GITHUB_REF#refs/tags/}
;;
*)
tag=${GITHUB_SHA}
;;
esac
echo "::set-output name=tag::$tag"
- name: Build and Push Tag
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64
push: true push: true
tags: awesometechnologies/synapse-admin:latest tags: "awesometechnologies/synapse-admin:${{ steps.set-tag.outputs.tag }}"
platforms: linux/amd64,linux/arm64

26
.github/workflows/edge_ghpage.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Build and Deploy Edge version to GH Pages
on:
workflow_dispatch:
push:
branches:
- main
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2.3.1
- uses: actions/setup-node@v2
with:
node-version: "14"
- name: Install and Build 🔧
run: |
yarn install
yarn build
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@4.1.5
with:
branch: gh-pages
folder: build

View File

@ -7,5 +7,5 @@
"trailingComma": "es5", "trailingComma": "es5",
"bracketSpacing": true, "bracketSpacing": true,
"jsxBracketSameLine": false, "jsxBracketSameLine": false,
"arrowParens": "avoid", "arrowParens": "avoid"
} }

View File

@ -19,15 +19,15 @@ You need access to the following endpoints:
See also [Synapse administration endpoints](https://matrix-org.github.io/synapse/develop/reverse_proxy.html#synapse-administration-endpoints) See also [Synapse administration endpoints](https://matrix-org.github.io/synapse/develop/reverse_proxy.html#synapse-administration-endpoints)
## Step-By-Step install: ## Step-By-Step install
You have three options: You have three options:
1. Download the tarball and serve with any webserver 1. [Download the tarball and serve with any webserver](#steps-for-1)
2. Download the source code from github and run using nodejs 2. [Download the source code from github and run using nodejs](#steps-for-2)
3. Run the Docker container 3. [Run the Docker container](#steps-for-3)
Steps for 1): ### Steps for 1)
- make sure you have a webserver installed that can serve static files (any webserver like nginx or apache will do) - make sure you have a webserver installed that can serve static files (any webserver like nginx or apache will do)
- configure a vhost for synapse admin on your webserver - configure a vhost for synapse admin on your webserver
@ -36,7 +36,7 @@ Steps for 1):
- move or symlink the `synapse-admin-x.x.x` into your vhosts root dir - move or symlink the `synapse-admin-x.x.x` into your vhosts root dir
- open the url of the vhost in your browser - open the url of the vhost in your browser
Steps for 2): ### Steps for 2)
- make sure you have installed the following: git, yarn, nodejs - make sure you have installed the following: git, yarn, nodejs
- download the source code: `git clone https://github.com/Awesome-Technologies/synapse-admin.git` - download the source code: `git clone https://github.com/Awesome-Technologies/synapse-admin.git`
@ -49,7 +49,7 @@ Either you define it at startup (e.g. `REACT_APP_SERVER=https://yourmatrixserver
or by editing it in the [.env](.env) file. See also the or by editing it in the [.env](.env) file. See also the
[documentation](https://create-react-app.dev/docs/adding-custom-environment-variables/). [documentation](https://create-react-app.dev/docs/adding-custom-environment-variables/).
Steps for 3): ### Steps for 3)
- run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the [docker-compose.yml](docker-compose.yml): `docker-compose up -d` - run the Docker container from the public docker registry: `docker run -p 8080:80 awesometechnologies/synapse-admin` or use the [docker-compose.yml](docker-compose.yml): `docker-compose up -d`

View File

@ -1,6 +1,6 @@
{ {
"name": "synapse-admin", "name": "synapse-admin",
"version": "0.8.3", "version": "0.8.4",
"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",

View File

@ -8,12 +8,18 @@ import { RoomList, RoomShow } from "./components/rooms";
import { ReportList, ReportShow } from "./components/EventReports"; 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 ConfirmationNumberIcon from "@material-ui/icons/ConfirmationNumber";
import EqualizerIcon from "@material-ui/icons/Equalizer"; import EqualizerIcon from "@material-ui/icons/Equalizer";
import { UserMediaStatsList } from "./components/statistics"; import { UserMediaStatsList } from "./components/statistics";
import RoomIcon from "@material-ui/icons/ViewList"; import RoomIcon from "@material-ui/icons/ViewList";
import ReportIcon from "@material-ui/icons/Warning"; import ReportIcon from "@material-ui/icons/Warning";
import FolderSharedIcon from "@material-ui/icons/FolderShared"; import FolderSharedIcon from "@material-ui/icons/FolderShared";
import { ImportFeature } from "./components/ImportFeature"; import { ImportFeature } from "./components/ImportFeature";
import {
RegistrationTokenCreate,
RegistrationTokenEdit,
RegistrationTokenList,
} from "./components/RegistrationTokens";
import { RoomDirectoryList } from "./components/RoomDirectory"; import { RoomDirectoryList } from "./components/RoomDirectory";
import { Route } from "react-router-dom"; import { Route } from "react-router-dom";
import germanMessages from "./i18n/de"; import germanMessages from "./i18n/de";
@ -66,6 +72,13 @@ const App = () => (
list={RoomDirectoryList} list={RoomDirectoryList}
icon={FolderSharedIcon} icon={FolderSharedIcon}
/> />
<Resource
name="registration_tokens"
list={RegistrationTokenList}
create={RegistrationTokenCreate}
edit={RegistrationTokenEdit}
icon={ConfirmationNumberIcon}
/>
<Resource name="connections" /> <Resource name="connections" />
<Resource name="devices" /> <Resource name="devices" />
<Resource name="room_members" /> <Resource name="room_members" />

View File

@ -15,6 +15,15 @@ import {
import PageviewIcon from "@material-ui/icons/Pageview"; import PageviewIcon from "@material-ui/icons/Pageview";
import ViewListIcon from "@material-ui/icons/ViewList"; import ViewListIcon from "@material-ui/icons/ViewList";
const date_format = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
};
const ReportPagination = props => ( const ReportPagination = props => (
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} /> <Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
); );
@ -33,14 +42,7 @@ export const ReportShow = props => {
<DateField <DateField
source="received_ts" source="received_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={true} sortable={true}
/> />
<ReferenceField source="user_id" reference="users"> <ReferenceField source="user_id" reference="users">
@ -72,14 +74,7 @@ export const ReportShow = props => {
<DateField <DateField
source="event_json.origin_server_ts" source="event_json.origin_server_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={true} sortable={true}
/> />
<ReferenceField source="sender" reference="users"> <ReferenceField source="sender" reference="users">
@ -116,14 +111,7 @@ export const ReportList = ({ ...props }) => {
<DateField <DateField
source="received_ts" source="received_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={true} sortable={true}
/> />
<TextField sortable={false} source="user_id" /> <TextField sortable={false} source="user_id" />

View File

@ -78,11 +78,48 @@ const LoginPage = ({ theme }) => {
const login = useLogin(); const login = useLogin();
const notify = useNotify(); const notify = useNotify();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [supportPassAuth, setSupportPassAuth] = useState(true);
var locale = useLocale(); var locale = useLocale();
const setLocale = useSetLocale(); const setLocale = useSetLocale();
const translate = useTranslate(); const translate = useTranslate();
const base_url = localStorage.getItem("base_url"); const base_url = localStorage.getItem("base_url");
const cfg_base_url = process.env.REACT_APP_SERVER; const cfg_base_url = process.env.REACT_APP_SERVER;
const [ssoBaseUrl, setSSOBaseUrl] = useState("");
const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href);
if (loginToken) {
const ssoToken = loginToken[1];
console.log("SSO token is", ssoToken);
// Prevent further requests
window.history.replaceState(
{},
"",
window.location.href.replace(loginToken[0], "#").split("#")[0]
);
const baseUrl = localStorage.getItem("sso_base_url");
localStorage.removeItem("sso_base_url");
if (baseUrl) {
const auth = {
base_url: baseUrl,
username: null,
password: null,
loginToken: ssoToken,
};
console.log("Base URL is:", baseUrl);
console.log("SSO Token is:", ssoToken);
console.log("Let's try token login...");
login(auth).catch(error => {
alert(
typeof error === "string"
? error
: typeof error === "undefined" || !error.message
? "ra.auth.sign_in_error"
: error.message
);
console.error(error);
});
}
}
const renderInput = ({ const renderInput = ({
meta: { touched, error } = {}, meta: { touched, error } = {},
@ -137,6 +174,14 @@ const LoginPage = ({ theme }) => {
}); });
}; };
const handleSSO = () => {
localStorage.setItem("sso_base_url", ssoBaseUrl);
const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(
window.location.href
)}`;
window.location.href = ssoFullUrl;
};
const extractHomeServer = username => { const extractHomeServer = username => {
const usernameRegex = /@[a-zA-Z0-9._=\-/]+:([a-zA-Z0-9\-.]+\.[a-zA-Z]+)/; const usernameRegex = /@[a-zA-Z0-9._=\-/]+:([a-zA-Z0-9\-.]+\.[a-zA-Z]+)/;
if (!username) return null; if (!username) return null;
@ -188,6 +233,31 @@ const LoginPage = ({ theme }) => {
.catch(_ => { .catch(_ => {
setServerVersion(""); setServerVersion("");
}); });
// Set SSO Url
const authMethodUrl = `${formData.base_url}/_matrix/client/r0/login`;
let supportPass = false,
supportSSO = false;
fetchUtils
.fetchJson(authMethodUrl, { method: "GET" })
.then(({ json }) => {
json.flows.forEach(f => {
if (f.type === "m.login.password") {
supportPass = true;
} else if (f.type === "m.login.sso") {
supportSSO = true;
}
});
setSupportPassAuth(supportPass);
if (supportSSO) {
setSSOBaseUrl(formData.base_url);
} else {
setSSOBaseUrl("");
}
})
.catch(_ => {
setSSOBaseUrl("");
});
}, },
[formData.base_url] [formData.base_url]
); );
@ -200,7 +270,7 @@ const LoginPage = ({ theme }) => {
name="username" name="username"
component={renderInput} component={renderInput}
label={translate("ra.auth.username")} label={translate("ra.auth.username")}
disabled={loading} disabled={loading || !supportPassAuth}
onBlur={handleUsernameChange} onBlur={handleUsernameChange}
resettable resettable
fullWidth fullWidth
@ -212,7 +282,7 @@ const LoginPage = ({ theme }) => {
component={renderInput} component={renderInput}
label={translate("ra.auth.password")} label={translate("ra.auth.password")}
type="password" type="password"
disabled={loading} disabled={loading || !supportPassAuth}
resettable resettable
fullWidth fullWidth
/> />
@ -273,13 +343,24 @@ const LoginPage = ({ theme }) => {
variant="contained" variant="contained"
type="submit" type="submit"
color="primary" color="primary"
disabled={loading} disabled={loading || !supportPassAuth}
className={classes.button} className={classes.button}
fullWidth fullWidth
> >
{loading && <CircularProgress size={25} thickness={2} />} {loading && <CircularProgress size={25} thickness={2} />}
{translate("ra.auth.sign_in")} {translate("ra.auth.sign_in")}
</Button> </Button>
<Button
variant="contained"
color="secondary"
onClick={handleSSO}
disabled={loading || ssoBaseUrl === ""}
className={classes.button}
fullWidth
>
{loading && <CircularProgress size={25} thickness={2} />}
{translate("synapseadmin.auth.sso_sign_in")}
</Button>
</CardActions> </CardActions>
</Card> </Card>
<Notification /> <Notification />

View File

@ -0,0 +1,132 @@
import React from "react";
import {
BooleanInput,
Create,
Datagrid,
DateField,
DateTimeInput,
Edit,
Filter,
List,
maxValue,
number,
NumberField,
NumberInput,
regex,
SimpleForm,
TextInput,
TextField,
Toolbar,
} from "react-admin";
const date_format = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
};
const validateToken = [regex(/^[A-Za-z0-9._~-]{0,64}$/)];
const validateUsesAllowed = [number()];
const validateLength = [number(), maxValue(64)];
const dateParser = v => {
const d = new Date(v);
if (isNaN(d)) return 0;
return d.getTime();
};
const dateFormatter = v => {
if (v === undefined || v === null) return;
const d = new Date(v);
const pad = "00";
const year = d.getFullYear().toString();
const month = (pad + (d.getMonth() + 1).toString()).slice(-2);
const day = (pad + d.getDate().toString()).slice(-2);
const hour = (pad + d.getHours().toString()).slice(-2);
const minute = (pad + d.getMinutes().toString()).slice(-2);
// target format yyyy-MM-ddThh:mm
return `${year}-${month}-${day}T${hour}:${minute}`;
};
const RegistrationTokenFilter = props => (
<Filter {...props}>
<BooleanInput source="valid" alwaysOn />
</Filter>
);
export const RegistrationTokenList = props => {
return (
<List
{...props}
filters={<RegistrationTokenFilter />}
filterDefaultValues={{ valid: true }}
pagination={false}
perPage={500}
>
<Datagrid rowClick="edit">
<TextField source="token" sortable={false} />
<NumberField source="uses_allowed" sortable={false} />
<NumberField source="pending" sortable={false} />
<NumberField source="completed" sortable={false} />
<DateField
source="expiry_time"
showTime
options={date_format}
sortable={false}
/>
</Datagrid>
</List>
);
};
export const RegistrationTokenCreate = props => (
<Create {...props}>
<SimpleForm redirect="list" toolbar={<Toolbar alwaysEnableSaveButton />}>
<TextInput
source="token"
autoComplete="off"
validate={validateToken}
resettable
/>
<NumberInput
source="length"
validate={validateLength}
helperText="resources.registration_tokens.helper.length"
step={1}
/>
<NumberInput
source="uses_allowed"
validate={validateUsesAllowed}
step={1}
/>
<DateTimeInput source="expiry_time" parse={dateParser} />
</SimpleForm>
</Create>
);
export const RegistrationTokenEdit = props => {
return (
<Edit {...props}>
<SimpleForm>
<TextInput source="token" disabled />
<NumberInput source="pending" disabled />
<NumberInput source="completed" disabled />
<NumberInput
source="uses_allowed"
validate={validateUsesAllowed}
step={1}
/>
<DateTimeInput
source="expiry_time"
parse={dateParser}
format={dateFormatter}
/>
</SimpleForm>
</Edit>
);
};

View File

@ -8,7 +8,7 @@ import {
} from "react-admin"; } from "react-admin";
import ActionDelete from "@material-ui/icons/Delete"; import ActionDelete from "@material-ui/icons/Delete";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { fade } from "@material-ui/core/styles/colorManipulator"; import { alpha } from "@material-ui/core/styles/colorManipulator";
import classnames from "classnames"; import classnames from "classnames";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -16,7 +16,7 @@ const useStyles = makeStyles(
deleteButton: { deleteButton: {
color: theme.palette.error.main, color: theme.palette.error.main,
"&:hover": { "&:hover": {
backgroundColor: fade(theme.palette.error.main, 0.12), backgroundColor: alpha(theme.palette.error.main, 0.12),
// Reset on mouse devices // Reset on mouse devices
"@media (hover: none)": { "@media (hover: none)": {
backgroundColor: "transparent", backgroundColor: "transparent",

View File

@ -1,6 +1,6 @@
import React, { Fragment, useState } from "react"; import React, { Fragment, useState } from "react";
import classnames from "classnames"; import classnames from "classnames";
import { fade } from "@material-ui/core/styles/colorManipulator"; import { alpha } from "@material-ui/core/styles/colorManipulator";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { Tooltip } from "@material-ui/core"; import { Tooltip } from "@material-ui/core";
import { import {
@ -33,7 +33,7 @@ const useStyles = makeStyles(
deleteButton: { deleteButton: {
color: theme.palette.error.main, color: theme.palette.error.main,
"&:hover": { "&:hover": {
backgroundColor: fade(theme.palette.error.main, 0.12), backgroundColor: alpha(theme.palette.error.main, 0.12),
// Reset on mouse devices // Reset on mouse devices
"@media (hover: none)": { "@media (hover: none)": {
backgroundColor: "transparent", backgroundColor: "transparent",

View File

@ -41,6 +41,15 @@ import {
RoomDirectorySaveButton, RoomDirectorySaveButton,
} from "./RoomDirectory"; } from "./RoomDirectory";
const date_format = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
};
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
helper_forward_extremities: { helper_forward_extremities: {
fontFamily: "Roboto, Helvetica, Arial, sans-serif", fontFamily: "Roboto, Helvetica, Arial, sans-serif",
@ -247,14 +256,7 @@ export const RoomShow = props => {
<DateField <DateField
source="origin_server_ts" source="origin_server_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={false} sortable={false}
/> />
<TextField source="content" sortable={false} /> <TextField source="content" sortable={false} />
@ -287,14 +289,7 @@ export const RoomShow = props => {
<DateField <DateField
source="received_ts" source="received_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={false} sortable={false}
/> />
<NumberField source="depth" sortable={false} /> <NumberField source="depth" sortable={false} />

View File

@ -81,6 +81,15 @@ const choices_type = [
{ id: "support", name: "support" }, { id: "support", name: "support" },
]; ];
const date_format = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
};
const UserListActions = ({ const UserListActions = ({
currentSort, currentSort,
className, className,
@ -186,6 +195,12 @@ export const UserList = props => {
<BooleanField source="is_guest" /> <BooleanField source="is_guest" />
<BooleanField source="admin" /> <BooleanField source="admin" />
<BooleanField source="deactivated" /> <BooleanField source="deactivated" />
<DateField
source="creation_ts"
label="resources.users.fields.creation_ts_ms"
showTime
options={date_format}
/>
</Datagrid> </Datagrid>
</List> </List>
); );
@ -345,18 +360,7 @@ export const UserEdit = props => {
source="deactivated" source="deactivated"
helperText="resources.users.helper.deactivate" helperText="resources.users.helper.deactivate"
/> />
<DateField <DateField source="creation_ts_ms" showTime options={date_format} />
source="creation_ts_ms"
showTime
options={{
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
/>
<TextField source="consent_version" /> <TextField source="consent_version" />
</FormTab> </FormTab>
@ -407,14 +411,7 @@ export const UserEdit = props => {
<DateField <DateField
source="last_seen_ts" source="last_seen_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={false} sortable={false}
/> />
<DeviceRemoveButton /> <DeviceRemoveButton />
@ -442,14 +439,7 @@ export const UserEdit = props => {
<DateField <DateField
source="last_seen" source="last_seen"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
sortable={false} sortable={false}
/> />
<TextField <TextField
@ -476,29 +466,11 @@ export const UserEdit = props => {
sort={{ field: "created_ts", order: "DESC" }} sort={{ field: "created_ts", order: "DESC" }}
> >
<Datagrid style={{ width: "100%" }}> <Datagrid style={{ width: "100%" }}>
<DateField <DateField source="created_ts" showTime options={date_format} />
source="created_ts"
showTime
options={{
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
/>
<DateField <DateField
source="last_access_ts" source="last_access_ts"
showTime showTime
options={{ options={date_format}
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}}
/> />
<TextField source="media_id" /> <TextField source="media_id" />
<NumberField source="media_length" /> <NumberField source="media_length" />

View File

@ -10,6 +10,7 @@ const de = {
username_error: "Bitte vollständigen Nutzernamen angeben: '@user:domain'", username_error: "Bitte vollständigen Nutzernamen angeben: '@user:domain'",
protocol_error: "Die URL muss mit 'http://' oder 'https://' beginnen", protocol_error: "Die URL muss mit 'http://' oder 'https://' beginnen",
url_error: "Keine gültige Matrix Server URL", url_error: "Keine gültige Matrix Server URL",
sso_sign_in: "Anmeldung mit SSO",
}, },
users: { users: {
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.", invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
@ -96,7 +97,6 @@ const de = {
}, },
resources: { resources: {
users: { users: {
backtolist: "Zurück zur Liste",
name: "Benutzer", name: "Benutzer",
email: "E-Mail", email: "E-Mail",
msisdn: "Telefon", msisdn: "Telefon",
@ -353,6 +353,19 @@ const de = {
send_failure: "Beim Entfernen ist ein Fehler aufgetreten.", send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
}, },
}, },
registration_tokens: {
name: "Registrierungstoken",
fields: {
token: "Token",
valid: "Gültige Token",
uses_allowed: "Anzahl",
pending: "Ausstehend",
completed: "Abgeschlossen",
expiry_time: "Ablaufzeit",
length: "Länge",
},
helper: { length: "Länge des Tokens, wenn kein Token vorgegeben wird." },
},
}, },
ra: { ra: {
...germanMessages.ra, ...germanMessages.ra,
@ -373,7 +386,7 @@ const de = {
}, },
}, },
notification: { notification: {
...germanMessages.ra.notifiaction, ...germanMessages.ra.notification,
logged_out: "Abgemeldet", logged_out: "Abgemeldet",
}, },
page: { page: {

View File

@ -10,6 +10,7 @@ const en = {
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",
sso_sign_in: "Sign in with SSO",
}, },
users: { users: {
invalid_user_id: "Localpart of a Matrix user-id without homeserver.", invalid_user_id: "Localpart of a Matrix user-id without homeserver.",
@ -95,7 +96,6 @@ const en = {
}, },
resources: { resources: {
users: { users: {
backtolist: "Back to list",
name: "User |||| Users", name: "User |||| Users",
email: "Email", email: "Email",
msisdn: "Phone", msisdn: "Phone",
@ -175,12 +175,14 @@ const en = {
}, },
unencrypted: "Unencrypted", unencrypted: "Unencrypted",
}, },
action: {
erase: { erase: {
title: "Delete room", title: "Delete room",
content: content:
"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: { reports: {
name: "Reported event |||| Reported events", name: "Reported event |||| Reported events",
fields: { fields: {
@ -350,5 +352,18 @@ const en = {
}, },
}, },
}, },
registration_tokens: {
name: "Registration tokens",
fields: {
token: "Token",
valid: "Valid token",
uses_allowed: "Uses allowed",
pending: "Pending",
completed: "Completed",
expiry_time: "Expiry time",
length: "Length",
},
helper: { length: "Length of the token if no token is given." },
},
}; };
export default en; export default en;

View File

@ -10,10 +10,12 @@ const zh = {
username_error: "请输入完整有效的用户 ID: '@user:domain'", username_error: "请输入完整有效的用户 ID: '@user:domain'",
protocol_error: "URL 需要以'http://'或'https://'作为起始", protocol_error: "URL 需要以'http://'或'https://'作为起始",
url_error: "不是一个有效的 Matrix 服务器地址", url_error: "不是一个有效的 Matrix 服务器地址",
sso_sign_in: "使用 SSO 登录",
}, },
users: { users: {
invalid_user_id: invalid_user_id:
"必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver", "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver",
tabs: { sso: "SSO" },
}, },
rooms: { rooms: {
tabs: { tabs: {
@ -98,7 +100,6 @@ const zh = {
}, },
resources: { resources: {
users: { users: {
backtolist: "回到列表",
name: "用户", name: "用户",
email: "邮箱", email: "邮箱",
msisdn: "电话", msisdn: "电话",

View File

@ -2,20 +2,31 @@ import { fetchUtils } from "react-admin";
const authProvider = { const authProvider = {
// called when the user attempts to log in // called when the user attempts to log in
login: ({ base_url, username, password }) => { login: ({ base_url, username, password, loginToken }) => {
// force homeserver for protection in case the form is manipulated // force homeserver for protection in case the form is manipulated
base_url = process.env.REACT_APP_SERVER || base_url; base_url = process.env.REACT_APP_SERVER || base_url;
console.log("login "); console.log("login ");
const options = { const options = {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify(
Object.assign(
{
device_id: localStorage.getItem("device_id"),
initial_device_display_name: "Synapse Admin",
},
loginToken
? {
type: "m.login.token",
token: loginToken,
}
: {
type: "m.login.password", type: "m.login.password",
user: username, user: username,
password: password, password: password,
device_id: localStorage.getItem("device_id"), }
initial_device_display_name: "Synapse Admin", )
}), ),
}; };
// use the base_url from login instead of the well_known entry from the // use the base_url from login instead of the well_known entry from the

View File

@ -275,6 +275,25 @@ const resourceMap = {
method: "PUT", method: "PUT",
}), }),
}, },
registration_tokens: {
path: "/_synapse/admin/v1/registration_tokens",
map: rt => ({
...rt,
id: rt.token,
}),
data: "registration_tokens",
total: json => {
return json.registration_tokens.length;
},
create: params => ({
endpoint: "/_synapse/admin/v1/registration_tokens/new",
body: params,
method: "POST",
}),
delete: params => ({
endpoint: `/_synapse/admin/v1/registration_tokens/${params.id}`,
}),
},
}; };
function filterNullValues(key, value) { function filterNullValues(key, value) {
@ -297,7 +316,8 @@ function getSearchOrder(order) {
const dataProvider = { const dataProvider = {
getList: (resource, params) => { getList: (resource, params) => {
console.log("getList " + resource); console.log("getList " + resource);
const { user_id, name, guests, deactivated, search_term } = params.filter; const { user_id, name, guests, deactivated, search_term, valid } =
params.filter;
const { page, perPage } = params.pagination; const { page, perPage } = params.pagination;
const { field, order } = params.sort; const { field, order } = params.sort;
const from = (page - 1) * perPage; const from = (page - 1) * perPage;
@ -309,6 +329,7 @@ const dataProvider = {
name: name, name: name,
guests: guests, guests: guests,
deactivated: deactivated, deactivated: deactivated,
valid: valid,
order_by: field, order_by: field,
dir: getSearchOrder(order), dir: getSearchOrder(order),
}; };

3340
yarn.lock

File diff suppressed because it is too large Load Diff