Merge branch 'master' into remove_translate
This commit is contained in:
commit
ec4b205600
@ -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.41.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).
|
||||||
|
13
src/App.js
13
src/App.js
@ -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="ra.auth.username"
|
label="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="ra.auth.password"
|
label="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 />
|
||||||
|
132
src/components/RegistrationTokens.js
Normal file
132
src/components/RegistrationTokens.js
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
@ -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",
|
||||||
|
@ -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} />
|
||||||
|
@ -71,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,
|
||||||
@ -180,14 +189,7 @@ export const UserList = props => {
|
|||||||
source="creation_ts"
|
source="creation_ts"
|
||||||
label="resources.users.fields.creation_ts_ms"
|
label="resources.users.fields.creation_ts_ms"
|
||||||
showTime
|
showTime
|
||||||
options={{
|
options={date_format}
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</List>
|
</List>
|
||||||
@ -336,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>
|
||||||
|
|
||||||
@ -404,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 />
|
||||||
@ -439,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
|
||||||
@ -473,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" />
|
||||||
|
@ -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.",
|
||||||
@ -351,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,
|
||||||
@ -371,7 +385,7 @@ const de = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
...germanMessages.ra.notifiaction,
|
...germanMessages.ra.notification,
|
||||||
logged_out: "Abgemeldet",
|
logged_out: "Abgemeldet",
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
|
@ -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.",
|
||||||
@ -173,12 +174,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: {
|
||||||
@ -348,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;
|
||||||
|
@ -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: {
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
@ -296,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;
|
||||||
@ -308,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),
|
||||||
};
|
};
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -5290,9 +5290,9 @@ flush-write-stream@^1.0.0:
|
|||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.14.7"
|
version "1.14.8"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -11457,9 +11457,9 @@ url-loader@4.1.1:
|
|||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
|
|
||||||
url-parse@^1.4.3, url-parse@^1.5.3:
|
url-parse@^1.4.3, url-parse@^1.5.3:
|
||||||
version "1.5.3"
|
version "1.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
|
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.7.tgz#00780f60dbdae90181f51ed85fb24109422c932a"
|
||||||
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
|
integrity sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==
|
||||||
dependencies:
|
dependencies:
|
||||||
querystringify "^2.1.1"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
requires-port "^1.0.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user