mirror of
https://github.com/UA-Fediland/synapse-admin.git
synced 2024-11-26 00:03:17 +00:00
Add token based registration creation and management (#200)
* Add token based registration creation and management * yarn fix * Apply suggestions from code review Remove empty line * move date to `const date_format`
This commit is contained in:
parent
c891afa611
commit
efed8b2774
6 changed files with 194 additions and 2 deletions
|
@ -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" />
|
||||||
|
|
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>
|
||||||
|
);
|
||||||
|
};
|
|
@ -352,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,
|
||||||
|
|
|
@ -351,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;
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue