Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95de50b925 | |||
| 1a150a10fd | |||
| 158e7dbe98 | |||
| a642f11503 | |||
| 888a3f001b | |||
| 4b0845bee8 | |||
| f449e3277a | |||
| 3303f253b4 | |||
| 0250954ee7 | |||
| efed8b2774 | |||
| c891afa611 | |||
| 38541b8f02 | |||
| 0f4c382c18 | |||
| b90d4ef00f | |||
| 3fb33facc5 | |||
| c4a68ff1d5 | |||
| c4f0fa48ec | |||
| 26ed63d65e | |||
| b9e81b2278 | |||
| f6f437b17a | |||
| 91af8f1c04 | |||
| abc9d5154e | |||
| 8228d7d2c2 | |||
| 4adc20f80d | |||
| a5c7d7dd22 | |||
| dc5c2c1d68 | |||
| 42b3252353 | |||
| 1a17d3e69b | |||
| 79ef38ee6b | |||
| 0ff4b30d71 | |||
| 6c4ff6c791 |
@@ -2,8 +2,14 @@ name: Create docker image(s) and push to docker hub
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
# Sequence of patterns matched against refs/heads
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+'
|
# prettier-ignore
|
||||||
|
branches:
|
||||||
|
# Push events on master branch
|
||||||
|
- master
|
||||||
|
# Sequence of patterns matched against refs/tags
|
||||||
|
tags:
|
||||||
|
- '[0-9]+\.[0-9]+\.[0-9]+' # Push events to 0.X.X tag
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
@@ -21,16 +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 }}"
|
||||||
- name: Update repo description
|
platforms: linux/amd64,linux/arm64
|
||||||
uses: peter-evans/dockerhub-description@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
repository: awesometechnologies/synapse-admin
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -8,6 +8,9 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
+1
-1
@@ -7,5 +7,5 @@
|
|||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"jsxBracketSameLine": false,
|
"jsxBracketSameLine": false,
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Builder
|
# Builder
|
||||||
FROM node:current as builder
|
FROM node:lts as builder
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,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.38.0 for all functions to work as expected!
|
It needs at least Synapse v1.42.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://matrix-org.github.io/synapse/develop/admin_api/version_api.html).
|
See also [Synapse version API](https://matrix-org.github.io/synapse/develop/admin_api/version_api.html).
|
||||||
@@ -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`
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "synapse-admin",
|
"name": "synapse-admin",
|
||||||
"version": "0.8.3",
|
"version": "0.8.5",
|
||||||
"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",
|
||||||
|
|||||||
+13
@@ -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" />
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -59,7 +59,7 @@ export const RoomDirectoryBulkDeleteButton = props => (
|
|||||||
<BulkDeleteButton
|
<BulkDeleteButton
|
||||||
{...props}
|
{...props}
|
||||||
label="resources.room_directory.action.erase"
|
label="resources.room_directory.action.erase"
|
||||||
undoable={false}
|
mutationMode="pessimistic"
|
||||||
confirmTitle="resources.room_directory.action.title"
|
confirmTitle="resources.room_directory.action.title"
|
||||||
confirmContent="resources.room_directory.action.content"
|
confirmContent="resources.room_directory.action.content"
|
||||||
resource="room_directory"
|
resource="room_directory"
|
||||||
@@ -191,7 +191,7 @@ export const FilterableRoomDirectoryList = ({
|
|||||||
filters={<RoomDirectoryFilter />}
|
filters={<RoomDirectoryFilter />}
|
||||||
perPage={100}
|
perPage={100}
|
||||||
>
|
>
|
||||||
<Datagrid>
|
<Datagrid rowClick={(id, basePath, record) => "/rooms/" + id + "/show"}>
|
||||||
<AvatarField
|
<AvatarField
|
||||||
source="avatar_src"
|
source="avatar_src"
|
||||||
sortable={false}
|
sortable={false}
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
|||||||
|
|
||||||
const ServerNoticeToolbar = props => (
|
const ServerNoticeToolbar = props => (
|
||||||
<Toolbar {...props}>
|
<Toolbar {...props}>
|
||||||
<SaveButton label="resources.servernotices.action.send" />
|
<SaveButton
|
||||||
|
label="resources.servernotices.action.send"
|
||||||
|
disabled={props.pristine}
|
||||||
|
/>
|
||||||
<Button label="ra.action.cancel" onClick={onClose}>
|
<Button label="ra.action.cancel" onClick={onClose}>
|
||||||
<IconCancel />
|
<IconCancel />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
+12
-17
@@ -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} />
|
||||||
@@ -315,7 +310,7 @@ const RoomBulkActionButtons = props => (
|
|||||||
{...props}
|
{...props}
|
||||||
confirmTitle="resources.rooms.action.erase.title"
|
confirmTitle="resources.rooms.action.erase.title"
|
||||||
confirmContent="resources.rooms.action.erase.content"
|
confirmContent="resources.rooms.action.erase.content"
|
||||||
undoable={false}
|
mutationMode="pessimistic"
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
+59
-64
@@ -36,7 +36,9 @@ import {
|
|||||||
BulkDeleteButton,
|
BulkDeleteButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
SaveButton,
|
SaveButton,
|
||||||
|
maxLength,
|
||||||
regex,
|
regex,
|
||||||
|
required,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
Pagination,
|
Pagination,
|
||||||
CreateButton,
|
CreateButton,
|
||||||
@@ -69,6 +71,15 @@ const useStyles = makeStyles({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
@@ -142,7 +153,7 @@ const UserBulkActionButtons = props => (
|
|||||||
{...props}
|
{...props}
|
||||||
label="resources.users.action.erase"
|
label="resources.users.action.erase"
|
||||||
confirmTitle="resources.users.helper.erase"
|
confirmTitle="resources.users.helper.erase"
|
||||||
undoable={false}
|
mutationMode="pessimistic"
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
@@ -174,16 +185,28 @@ 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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://matrix.org/docs/spec/appendices#user-identifiers
|
// https://matrix.org/docs/spec/appendices#user-identifiers
|
||||||
const validateUser = regex(
|
// here only local part of user_id
|
||||||
/^@[a-z0-9._=\-/]+:.*/,
|
// maxLength = 255 - "@" - ":" - localStorage.getItem("home_server").length
|
||||||
"synapseadmin.users.invalid_user_id"
|
// localStorage.getItem("home_server").length is not valid here
|
||||||
);
|
const validateUser = [
|
||||||
|
required(),
|
||||||
|
maxLength(253),
|
||||||
|
regex(/^[a-z0-9._=\-/]+$/, "synapseadmin.users.invalid_user_id"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateAddress = [required(), maxLength(255)];
|
||||||
|
|
||||||
export function generateRandomUser() {
|
export function generateRandomUser() {
|
||||||
const homeserver = localStorage.getItem("home_server");
|
const homeserver = localStorage.getItem("home_server");
|
||||||
@@ -230,7 +253,7 @@ const UserEditToolbar = props => {
|
|||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
<Toolbar {...props}>
|
<Toolbar {...props}>
|
||||||
<SaveButton submitOnEnter={true} />
|
<SaveButton submitOnEnter={true} disabled={props.pristine} />
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
label="resources.users.action.erase"
|
label="resources.users.action.erase"
|
||||||
confirmTitle={translate("resources.users.helper.erase", {
|
confirmTitle={translate("resources.users.helper.erase", {
|
||||||
@@ -247,8 +270,12 @@ export const UserCreate = props => (
|
|||||||
<Create {...props}>
|
<Create {...props}>
|
||||||
<SimpleForm>
|
<SimpleForm>
|
||||||
<TextInput source="id" autoComplete="off" validate={validateUser} />
|
<TextInput source="id" autoComplete="off" validate={validateUser} />
|
||||||
<TextInput source="displayname" />
|
<TextInput source="displayname" validate={maxLength(256)} />
|
||||||
<PasswordInput source="password" autoComplete="new-password" />
|
<PasswordInput
|
||||||
|
source="password"
|
||||||
|
autoComplete="new-password"
|
||||||
|
validate={maxLength(512)}
|
||||||
|
/>
|
||||||
<BooleanInput source="admin" />
|
<BooleanInput source="admin" />
|
||||||
<ArrayInput source="threepids">
|
<ArrayInput source="threepids">
|
||||||
<SimpleFormIterator>
|
<SimpleFormIterator>
|
||||||
@@ -258,8 +285,19 @@ export const UserCreate = props => (
|
|||||||
{ id: "email", name: "resources.users.email" },
|
{ id: "email", name: "resources.users.email" },
|
||||||
{ id: "msisdn", name: "resources.users.msisdn" },
|
{ id: "msisdn", name: "resources.users.msisdn" },
|
||||||
]}
|
]}
|
||||||
|
validate={required()}
|
||||||
|
/>
|
||||||
|
<TextInput source="address" validate={validateAddress} />
|
||||||
|
</SimpleFormIterator>
|
||||||
|
</ArrayInput>
|
||||||
|
<ArrayInput source="external_ids" label="synapseadmin.users.tabs.sso">
|
||||||
|
<SimpleFormIterator>
|
||||||
|
<TextInput source="auth_provider" validate={required()} />
|
||||||
|
<TextInput
|
||||||
|
source="external_id"
|
||||||
|
label="resources.users.fields.id"
|
||||||
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
<TextInput source="address" />
|
|
||||||
</SimpleFormIterator>
|
</SimpleFormIterator>
|
||||||
</ArrayInput>
|
</ArrayInput>
|
||||||
</SimpleForm>
|
</SimpleForm>
|
||||||
@@ -300,18 +338,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>
|
||||||
|
|
||||||
@@ -339,16 +366,16 @@ export const UserEdit = props => {
|
|||||||
icon={<AssignmentIndIcon />}
|
icon={<AssignmentIndIcon />}
|
||||||
path="sso"
|
path="sso"
|
||||||
>
|
>
|
||||||
<ArrayField source="external_ids" label={false}>
|
<ArrayInput source="external_ids" label={false}>
|
||||||
<Datagrid style={{ width: "100%" }}>
|
<SimpleFormIterator>
|
||||||
<TextField source="auth_provider" sortable={false} />
|
<TextInput source="auth_provider" validate={required()} />
|
||||||
<TextField
|
<TextInput
|
||||||
source="external_id"
|
source="external_id"
|
||||||
label="resources.users.fields.id"
|
label="resources.users.fields.id"
|
||||||
sortable={false}
|
validate={required()}
|
||||||
/>
|
/>
|
||||||
</Datagrid>
|
</SimpleFormIterator>
|
||||||
</ArrayField>
|
</ArrayInput>
|
||||||
</FormTab>
|
</FormTab>
|
||||||
|
|
||||||
<FormTab
|
<FormTab
|
||||||
@@ -368,14 +395,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 />
|
||||||
@@ -403,14 +423,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
|
||||||
@@ -437,29 +450,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" />
|
||||||
|
|||||||
+16
-4
@@ -10,10 +10,10 @@ 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:
|
invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.",
|
||||||
"Muss eine vollständige Matrix Benutzer-ID sein, z.B. @benutzer_id:homeserver",
|
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
@@ -97,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 +352,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 +385,7 @@ const de = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
...germanMessages.ra.notifiaction,
|
...germanMessages.ra.notification,
|
||||||
logged_out: "Abgemeldet",
|
logged_out: "Abgemeldet",
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
|
|||||||
+21
-7
@@ -10,10 +10,10 @@ 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:
|
invalid_user_id: "Localpart of a Matrix user-id without homeserver.",
|
||||||
"Must be a fully qualified Matrix user-id, e.g. @user_id:homeserver",
|
|
||||||
tabs: { sso: "SSO" },
|
tabs: { sso: "SSO" },
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
@@ -96,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,10 +174,12 @@ const en = {
|
|||||||
},
|
},
|
||||||
unencrypted: "Unencrypted",
|
unencrypted: "Unencrypted",
|
||||||
},
|
},
|
||||||
erase: {
|
action: {
|
||||||
title: "Delete room",
|
erase: {
|
||||||
content:
|
title: "Delete room",
|
||||||
"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!",
|
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!",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reports: {
|
reports: {
|
||||||
@@ -350,5 +351,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;
|
||||||
|
|||||||
+2
-1
@@ -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: "电话",
|
||||||
|
|||||||
@@ -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(
|
||||||
type: "m.login.password",
|
Object.assign(
|
||||||
user: username,
|
{
|
||||||
password: password,
|
device_id: localStorage.getItem("device_id"),
|
||||||
device_id: localStorage.getItem("device_id"),
|
initial_device_display_name: "Synapse Admin",
|
||||||
initial_device_display_name: "Synapse Admin",
|
},
|
||||||
}),
|
loginToken
|
||||||
|
? {
|
||||||
|
type: "m.login.token",
|
||||||
|
token: loginToken,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: "m.login.password",
|
||||||
|
user: username,
|
||||||
|
password: password,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ const resourceMap = {
|
|||||||
data: "users",
|
data: "users",
|
||||||
total: json => json.total,
|
total: json => json.total,
|
||||||
create: data => ({
|
create: data => ({
|
||||||
endpoint: `/_synapse/admin/v2/users/${data.id}`,
|
endpoint: `/_synapse/admin/v2/users/@${data.id}:${localStorage.getItem(
|
||||||
|
"home_server"
|
||||||
|
)}`,
|
||||||
body: data,
|
body: data,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
}),
|
}),
|
||||||
@@ -273,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) {
|
||||||
@@ -294,7 +315,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;
|
||||||
@@ -306,6 +328,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),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user