Add admin API for destinations (#213)
* Add admin API for destinations * Add rooms and connections to federation/destinations
This commit is contained in:
		
							parent
							
								
									6d30af9976
								
							
						
					
					
						commit
						8501f19a03
					
				@ -13,7 +13,7 @@ This project is built using [react-admin](https://marmelab.com/react-admin/).
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### Supported Synapse
 | 
					### Supported Synapse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It needs at least [Synapse](https://github.com/matrix-org/synapse) v1.48.0 for all functions to work as expected!
 | 
					It needs at least [Synapse](https://github.com/matrix-org/synapse) v1.52.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).
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/App.js
									
									
									
									
									
								
							@ -7,13 +7,15 @@ import { UserList, UserCreate, UserEdit } from "./components/users";
 | 
				
			|||||||
import { RoomList, RoomShow } from "./components/rooms";
 | 
					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 ConfirmationNumberIcon from "@material-ui/icons/ConfirmationNumber";
 | 
					import ConfirmationNumberIcon from "@material-ui/icons/ConfirmationNumber";
 | 
				
			||||||
 | 
					import CloudQueueIcon from "@material-ui/icons/CloudQueue";
 | 
				
			||||||
import EqualizerIcon from "@material-ui/icons/Equalizer";
 | 
					import EqualizerIcon from "@material-ui/icons/Equalizer";
 | 
				
			||||||
 | 
					import UserIcon from "@material-ui/icons/Group";
 | 
				
			||||||
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 { DestinationList, DestinationShow } from "./components/destinations";
 | 
				
			||||||
import { ImportFeature } from "./components/ImportFeature";
 | 
					import { ImportFeature } from "./components/ImportFeature";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  RegistrationTokenCreate,
 | 
					  RegistrationTokenCreate,
 | 
				
			||||||
@ -72,6 +74,12 @@ const App = () => (
 | 
				
			|||||||
      list={RoomDirectoryList}
 | 
					      list={RoomDirectoryList}
 | 
				
			||||||
      icon={FolderSharedIcon}
 | 
					      icon={FolderSharedIcon}
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
 | 
					    <Resource
 | 
				
			||||||
 | 
					      name="destinations"
 | 
				
			||||||
 | 
					      list={DestinationList}
 | 
				
			||||||
 | 
					      show={DestinationShow}
 | 
				
			||||||
 | 
					      icon={CloudQueueIcon}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
    <Resource
 | 
					    <Resource
 | 
				
			||||||
      name="registration_tokens"
 | 
					      name="registration_tokens"
 | 
				
			||||||
      list={RegistrationTokenList}
 | 
					      list={RegistrationTokenList}
 | 
				
			||||||
@ -88,6 +96,7 @@ const App = () => (
 | 
				
			|||||||
    <Resource name="servernotices" />
 | 
					    <Resource name="servernotices" />
 | 
				
			||||||
    <Resource name="forward_extremities" />
 | 
					    <Resource name="forward_extremities" />
 | 
				
			||||||
    <Resource name="room_state" />
 | 
					    <Resource name="room_state" />
 | 
				
			||||||
 | 
					    <Resource name="destination_rooms" />
 | 
				
			||||||
  </Admin>
 | 
					  </Admin>
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										185
									
								
								src/components/destinations.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/components/destinations.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,185 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  Datagrid,
 | 
				
			||||||
 | 
					  DateField,
 | 
				
			||||||
 | 
					  Filter,
 | 
				
			||||||
 | 
					  List,
 | 
				
			||||||
 | 
					  Pagination,
 | 
				
			||||||
 | 
					  ReferenceField,
 | 
				
			||||||
 | 
					  ReferenceManyField,
 | 
				
			||||||
 | 
					  SearchInput,
 | 
				
			||||||
 | 
					  Show,
 | 
				
			||||||
 | 
					  Tab,
 | 
				
			||||||
 | 
					  TabbedShowLayout,
 | 
				
			||||||
 | 
					  TextField,
 | 
				
			||||||
 | 
					  TopToolbar,
 | 
				
			||||||
 | 
					  useRecordContext,
 | 
				
			||||||
 | 
					  useDelete,
 | 
				
			||||||
 | 
					  useNotify,
 | 
				
			||||||
 | 
					  useRefresh,
 | 
				
			||||||
 | 
					  useTranslate,
 | 
				
			||||||
 | 
					} from "react-admin";
 | 
				
			||||||
 | 
					import AutorenewIcon from "@material-ui/icons/Autorenew";
 | 
				
			||||||
 | 
					import FolderSharedIcon from "@material-ui/icons/FolderShared";
 | 
				
			||||||
 | 
					import ViewListIcon from "@material-ui/icons/ViewList";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DestinationPagination = props => (
 | 
				
			||||||
 | 
					  <Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const date_format = {
 | 
				
			||||||
 | 
					  year: "numeric",
 | 
				
			||||||
 | 
					  month: "2-digit",
 | 
				
			||||||
 | 
					  day: "2-digit",
 | 
				
			||||||
 | 
					  hour: "2-digit",
 | 
				
			||||||
 | 
					  minute: "2-digit",
 | 
				
			||||||
 | 
					  second: "2-digit",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const destinationRowStyle = (record, index) => ({
 | 
				
			||||||
 | 
					  backgroundColor: record.retry_last_ts > 0 ? "#ffcccc" : "white",
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DestinationFilter = ({ ...props }) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Filter {...props}>
 | 
				
			||||||
 | 
					      <SearchInput source="destination" alwaysOn />
 | 
				
			||||||
 | 
					    </Filter>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DestinationReconnectButton = props => {
 | 
				
			||||||
 | 
					  const record = useRecordContext();
 | 
				
			||||||
 | 
					  const refresh = useRefresh();
 | 
				
			||||||
 | 
					  const notify = useNotify();
 | 
				
			||||||
 | 
					  const [handleReconnect, { isLoading }] = useDelete("destinations");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Reconnect is not required if no error has occurred. (`failure_ts`)
 | 
				
			||||||
 | 
					  if (!record || !record.failure_ts) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleClick = e => {
 | 
				
			||||||
 | 
					    // Prevents redirection to the detail page when clicking in the list
 | 
				
			||||||
 | 
					    e.stopPropagation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    handleReconnect(
 | 
				
			||||||
 | 
					      { payload: { id: record.id } },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        onSuccess: () => {
 | 
				
			||||||
 | 
					          notify("ra.notification.updated", {
 | 
				
			||||||
 | 
					            messageArgs: { smart_count: 1 },
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          refresh();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        onFailure: () => {
 | 
				
			||||||
 | 
					          notify("ra.message.error", { type: "error" });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					      label="resources.destinations.action.reconnect"
 | 
				
			||||||
 | 
					      onClick={handleClick}
 | 
				
			||||||
 | 
					      disabled={isLoading}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <AutorenewIcon />
 | 
				
			||||||
 | 
					    </Button>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DestinationShowActions = props => (
 | 
				
			||||||
 | 
					  <TopToolbar>
 | 
				
			||||||
 | 
					    <DestinationReconnectButton />
 | 
				
			||||||
 | 
					  </TopToolbar>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DestinationTitle = props => {
 | 
				
			||||||
 | 
					  const record = useRecordContext();
 | 
				
			||||||
 | 
					  const translate = useTranslate();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <span>
 | 
				
			||||||
 | 
					      {translate("resources.destinations.name", 1)} {record.destination}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DestinationList = props => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <List
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					      filters={<DestinationFilter />}
 | 
				
			||||||
 | 
					      pagination={<DestinationPagination />}
 | 
				
			||||||
 | 
					      sort={{ field: "destination", order: "ASC" }}
 | 
				
			||||||
 | 
					      bulkActionButtons={false}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Datagrid
 | 
				
			||||||
 | 
					        rowStyle={destinationRowStyle}
 | 
				
			||||||
 | 
					        rowClick={(id, basePath, record) => `${basePath}/${id}/show/rooms`}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <TextField source="destination" />
 | 
				
			||||||
 | 
					        <DateField source="failure_ts" showTime options={date_format} />
 | 
				
			||||||
 | 
					        <DateField source="retry_last_ts" showTime options={date_format} />
 | 
				
			||||||
 | 
					        <TextField source="retry_interval" />
 | 
				
			||||||
 | 
					        <TextField source="last_successful_stream_ordering" />
 | 
				
			||||||
 | 
					        <DestinationReconnectButton />
 | 
				
			||||||
 | 
					      </Datagrid>
 | 
				
			||||||
 | 
					    </List>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DestinationShow = props => {
 | 
				
			||||||
 | 
					  const translate = useTranslate();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Show
 | 
				
			||||||
 | 
					      actions={<DestinationShowActions />}
 | 
				
			||||||
 | 
					      title={<DestinationTitle />}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <TabbedShowLayout>
 | 
				
			||||||
 | 
					        <Tab label="status" icon={<ViewListIcon />}>
 | 
				
			||||||
 | 
					          <TextField source="destination" />
 | 
				
			||||||
 | 
					          <DateField source="failure_ts" showTime options={date_format} />
 | 
				
			||||||
 | 
					          <DateField source="retry_last_ts" showTime options={date_format} />
 | 
				
			||||||
 | 
					          <TextField source="retry_interval" />
 | 
				
			||||||
 | 
					          <TextField source="last_successful_stream_ordering" />
 | 
				
			||||||
 | 
					        </Tab>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Tab
 | 
				
			||||||
 | 
					          label={translate("resources.rooms.name", { smart_count: 2 })}
 | 
				
			||||||
 | 
					          icon={<FolderSharedIcon />}
 | 
				
			||||||
 | 
					          path="rooms"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <ReferenceManyField
 | 
				
			||||||
 | 
					            reference="destination_rooms"
 | 
				
			||||||
 | 
					            target="destination"
 | 
				
			||||||
 | 
					            addLabel={false}
 | 
				
			||||||
 | 
					            pagination={<DestinationPagination />}
 | 
				
			||||||
 | 
					            perPage={50}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Datagrid
 | 
				
			||||||
 | 
					              style={{ width: "100%" }}
 | 
				
			||||||
 | 
					              rowClick={(id, basePath, record) => `/rooms/${id}/show`}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <TextField
 | 
				
			||||||
 | 
					                source="room_id"
 | 
				
			||||||
 | 
					                label="resources.rooms.fields.room_id"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <TextField source="stream_ordering" sortable={false} />
 | 
				
			||||||
 | 
					              <ReferenceField
 | 
				
			||||||
 | 
					                label="resources.rooms.fields.name"
 | 
				
			||||||
 | 
					                source="id"
 | 
				
			||||||
 | 
					                reference="rooms"
 | 
				
			||||||
 | 
					                sortable={false}
 | 
				
			||||||
 | 
					                link=""
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <TextField source="name" sortable={false} />
 | 
				
			||||||
 | 
					              </ReferenceField>
 | 
				
			||||||
 | 
					            </Datagrid>
 | 
				
			||||||
 | 
					          </ReferenceManyField>
 | 
				
			||||||
 | 
					        </Tab>
 | 
				
			||||||
 | 
					      </TabbedShowLayout>
 | 
				
			||||||
 | 
					    </Show>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -355,6 +355,18 @@ const de = {
 | 
				
			|||||||
        send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
 | 
					        send_failure: "Beim Entfernen ist ein Fehler aufgetreten.",
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    destinations: {
 | 
				
			||||||
 | 
					      name: "Föderation",
 | 
				
			||||||
 | 
					      fields: {
 | 
				
			||||||
 | 
					        destination: "Ziel",
 | 
				
			||||||
 | 
					        failure_ts: "Fehlerzeitpunkt",
 | 
				
			||||||
 | 
					        retry_last_ts: "Letzter Wiederholungsversuch",
 | 
				
			||||||
 | 
					        retry_interval: "Wiederholungsintervall",
 | 
				
			||||||
 | 
					        last_successful_stream_ordering: "letzte erfogreicher Stream",
 | 
				
			||||||
 | 
					        stream_ordering: "Stream",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      action: { reconnect: "Neu verbinden" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    registration_tokens: {
 | 
					    registration_tokens: {
 | 
				
			||||||
      name: "Registrierungstoken",
 | 
					      name: "Registrierungstoken",
 | 
				
			||||||
      fields: {
 | 
					      fields: {
 | 
				
			||||||
 | 
				
			|||||||
@ -352,6 +352,18 @@ const en = {
 | 
				
			|||||||
        send_failure: "An error has occurred.",
 | 
					        send_failure: "An error has occurred.",
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    destinations: {
 | 
				
			||||||
 | 
					      name: "Federation",
 | 
				
			||||||
 | 
					      fields: {
 | 
				
			||||||
 | 
					        destination: "Destination",
 | 
				
			||||||
 | 
					        failure_ts: "Failure timestamp",
 | 
				
			||||||
 | 
					        retry_last_ts: "Last retry timestamp",
 | 
				
			||||||
 | 
					        retry_interval: "Retry interval",
 | 
				
			||||||
 | 
					        last_successful_stream_ordering: "Last successful stream",
 | 
				
			||||||
 | 
					        stream_ordering: "Stream",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      action: { reconnect: "Reconnect" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  registration_tokens: {
 | 
					  registration_tokens: {
 | 
				
			||||||
    name: "Registration tokens",
 | 
					    name: "Registration tokens",
 | 
				
			||||||
 | 
				
			|||||||
@ -281,6 +281,34 @@ const resourceMap = {
 | 
				
			|||||||
      method: "PUT",
 | 
					      method: "PUT",
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  destinations: {
 | 
				
			||||||
 | 
					    path: "/_synapse/admin/v1/federation/destinations",
 | 
				
			||||||
 | 
					    map: dst => ({
 | 
				
			||||||
 | 
					      ...dst,
 | 
				
			||||||
 | 
					      id: dst.destination,
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    data: "destinations",
 | 
				
			||||||
 | 
					    total: json => {
 | 
				
			||||||
 | 
					      return json.total;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    delete: params => ({
 | 
				
			||||||
 | 
					      endpoint: `/_synapse/admin/v1/federation/destinations/${params.id}/reset_connection`,
 | 
				
			||||||
 | 
					      method: "POST",
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  destination_rooms: {
 | 
				
			||||||
 | 
					    map: dstroom => ({
 | 
				
			||||||
 | 
					      ...dstroom,
 | 
				
			||||||
 | 
					      id: dstroom.room_id,
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    reference: id => ({
 | 
				
			||||||
 | 
					      endpoint: `/_synapse/admin/v1/federation/destinations/${id}/rooms`,
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    data: "rooms",
 | 
				
			||||||
 | 
					    total: json => {
 | 
				
			||||||
 | 
					      return json.total;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  registration_tokens: {
 | 
					  registration_tokens: {
 | 
				
			||||||
    path: "/_synapse/admin/v1/registration_tokens",
 | 
					    path: "/_synapse/admin/v1/registration_tokens",
 | 
				
			||||||
    map: rt => ({
 | 
					    map: rt => ({
 | 
				
			||||||
@ -322,8 +350,15 @@ 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, valid } =
 | 
					    const {
 | 
				
			||||||
      params.filter;
 | 
					      user_id,
 | 
				
			||||||
 | 
					      name,
 | 
				
			||||||
 | 
					      guests,
 | 
				
			||||||
 | 
					      deactivated,
 | 
				
			||||||
 | 
					      search_term,
 | 
				
			||||||
 | 
					      destination,
 | 
				
			||||||
 | 
					      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;
 | 
				
			||||||
@ -333,6 +368,7 @@ const dataProvider = {
 | 
				
			|||||||
      user_id: user_id,
 | 
					      user_id: user_id,
 | 
				
			||||||
      search_term: search_term,
 | 
					      search_term: search_term,
 | 
				
			||||||
      name: name,
 | 
					      name: name,
 | 
				
			||||||
 | 
					      destination: destination,
 | 
				
			||||||
      guests: guests,
 | 
					      guests: guests,
 | 
				
			||||||
      deactivated: deactivated,
 | 
					      deactivated: deactivated,
 | 
				
			||||||
      valid: valid,
 | 
					      valid: valid,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user