2016-01-06 23:26:29 -05:00
|
|
|
# Copyright 2014-2016 OpenMarket Ltd
|
2019-05-01 10:32:38 -04:00
|
|
|
# Copyright 2018-2019 New Vector Ltd
|
2021-01-11 16:43:58 -05:00
|
|
|
# Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
|
2014-09-29 09:59:52 -04:00
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
2018-07-09 02:09:20 -04:00
|
|
|
import logging
|
2019-02-28 22:05:47 -05:00
|
|
|
import platform
|
2021-11-29 17:19:45 -05:00
|
|
|
from http import HTTPStatus
|
2021-06-07 10:12:34 -04:00
|
|
|
from typing import TYPE_CHECKING, Optional, Tuple
|
2014-09-29 09:59:52 -04:00
|
|
|
|
2019-02-28 22:05:47 -05:00
|
|
|
import synapse
|
2019-08-13 07:49:28 -04:00
|
|
|
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
2021-06-07 10:12:34 -04:00
|
|
|
from synapse.http.server import HttpServer, JsonResource
|
2019-11-20 06:49:11 -05:00
|
|
|
from synapse.http.servlet import RestServlet, parse_json_object_from_request
|
2021-06-07 10:12:34 -04:00
|
|
|
from synapse.http.site import SynapseRequest
|
2020-11-25 16:26:11 -05:00
|
|
|
from synapse.rest.admin._base import admin_patterns, assert_requester_is_admin
|
2021-11-08 11:08:02 -05:00
|
|
|
from synapse.rest.admin.background_updates import (
|
|
|
|
BackgroundUpdateEnabledRestServlet,
|
|
|
|
BackgroundUpdateRestServlet,
|
2021-11-19 14:39:46 -05:00
|
|
|
BackgroundUpdateStartJobRestServlet,
|
2021-11-08 11:08:02 -05:00
|
|
|
)
|
2020-06-05 08:07:22 -04:00
|
|
|
from synapse.rest.admin.devices import (
|
|
|
|
DeleteDevicesRestServlet,
|
|
|
|
DeviceRestServlet,
|
|
|
|
DevicesRestServlet,
|
|
|
|
)
|
2020-10-26 14:16:37 -04:00
|
|
|
from synapse.rest.admin.event_reports import (
|
|
|
|
EventReportDetailRestServlet,
|
|
|
|
EventReportsRestServlet,
|
|
|
|
)
|
2021-12-06 11:59:50 -05:00
|
|
|
from synapse.rest.admin.federation import (
|
|
|
|
DestinationsRestServlet,
|
|
|
|
ListDestinationsRestServlet,
|
|
|
|
)
|
2019-11-20 06:49:11 -05:00
|
|
|
from synapse.rest.admin.groups import DeleteGroupAdminRestServlet
|
2019-09-03 15:35:20 -04:00
|
|
|
from synapse.rest.admin.media import ListMediaInRoom, register_servlets_for_media_repo
|
2021-08-21 17:14:43 -04:00
|
|
|
from synapse.rest.admin.registration_tokens import (
|
|
|
|
ListRegistrationTokensRestServlet,
|
|
|
|
NewRegistrationTokenRestServlet,
|
|
|
|
RegistrationTokenRestServlet,
|
|
|
|
)
|
2020-03-27 15:16:43 -04:00
|
|
|
from synapse.rest.admin.rooms import (
|
2021-11-18 12:43:49 -05:00
|
|
|
BlockRoomRestServlet,
|
2021-11-12 07:35:31 -05:00
|
|
|
DeleteRoomStatusByDeleteIdRestServlet,
|
|
|
|
DeleteRoomStatusByRoomIdRestServlet,
|
2021-01-07 16:01:59 -05:00
|
|
|
ForwardExtremitiesRestServlet,
|
2020-03-27 15:16:43 -04:00
|
|
|
JoinRoomAliasServlet,
|
|
|
|
ListRoomRestServlet,
|
2020-12-18 10:37:19 -05:00
|
|
|
MakeRoomAdminRestServlet,
|
2021-01-18 09:02:22 -05:00
|
|
|
RoomEventContextServlet,
|
2020-07-16 16:43:23 -04:00
|
|
|
RoomMembersRestServlet,
|
2020-05-07 15:33:07 -04:00
|
|
|
RoomRestServlet,
|
2021-11-12 07:35:31 -05:00
|
|
|
RoomRestV2Servlet,
|
2021-02-02 06:16:29 -05:00
|
|
|
RoomStateRestServlet,
|
2020-03-27 15:16:43 -04:00
|
|
|
)
|
2019-05-02 06:59:16 -04:00
|
|
|
from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet
|
2020-11-05 13:59:12 -05:00
|
|
|
from synapse.rest.admin.statistics import UserMediaStatisticsRestServlet
|
2021-08-17 05:52:38 -04:00
|
|
|
from synapse.rest.admin.username_available import UsernameAvailableRestServlet
|
2019-11-20 06:49:11 -05:00
|
|
|
from synapse.rest.admin.users import (
|
2022-01-05 06:49:06 -05:00
|
|
|
AccountDataRestServlet,
|
2019-11-20 06:49:11 -05:00
|
|
|
AccountValidityRenewServlet,
|
|
|
|
DeactivateAccountRestServlet,
|
2020-10-28 11:02:42 -04:00
|
|
|
PushersRestServlet,
|
2021-04-13 05:26:37 -04:00
|
|
|
RateLimitRestServlet,
|
2019-11-20 06:49:11 -05:00
|
|
|
ResetPasswordRestServlet,
|
|
|
|
SearchUsersRestServlet,
|
2021-01-25 14:49:39 -05:00
|
|
|
ShadowBanRestServlet,
|
2019-11-20 06:49:11 -05:00
|
|
|
UserAdminServlet,
|
2020-09-18 10:26:36 -04:00
|
|
|
UserMembershipRestServlet,
|
2019-11-20 06:49:11 -05:00
|
|
|
UserRegisterServlet,
|
2020-01-09 08:31:00 -05:00
|
|
|
UserRestServletV2,
|
2019-12-05 13:12:23 -05:00
|
|
|
UsersRestServletV2,
|
2020-11-17 05:51:25 -05:00
|
|
|
UserTokenRestServlet,
|
2019-11-20 06:49:11 -05:00
|
|
|
WhoisRestServlet,
|
|
|
|
)
|
2021-06-07 10:12:34 -04:00
|
|
|
from synapse.types import JsonDict, RoomStreamToken
|
2019-02-28 22:05:47 -05:00
|
|
|
from synapse.util.versionstring import get_version_string
|
2015-01-23 06:47:15 -05:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from synapse.server import HomeServer
|
|
|
|
|
2014-09-29 09:59:52 -04:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2019-05-01 05:40:33 -04:00
|
|
|
class VersionServlet(RestServlet):
|
2020-09-17 07:04:15 -04:00
|
|
|
PATTERNS = admin_patterns("/server_version$")
|
2019-02-28 22:05:47 -05:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def __init__(self, hs: "HomeServer"):
|
2019-05-07 04:29:30 -04:00
|
|
|
self.res = {
|
2019-02-28 22:05:47 -05:00
|
|
|
"server_version": get_version_string(synapse),
|
|
|
|
"python_version": platform.python_version(),
|
|
|
|
}
|
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
2021-11-29 17:19:45 -05:00
|
|
|
return HTTPStatus.OK, self.res
|
2019-02-28 22:05:47 -05:00
|
|
|
|
|
|
|
|
2019-05-01 05:40:33 -04:00
|
|
|
class PurgeHistoryRestServlet(RestServlet):
|
2020-11-25 16:26:11 -05:00
|
|
|
PATTERNS = admin_patterns(
|
2021-12-08 11:59:40 -05:00
|
|
|
"/purge_history/(?P<room_id>[^/]*)(/(?P<event_id>[^/]*))?$"
|
2016-07-05 05:28:51 -04:00
|
|
|
)
|
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def __init__(self, hs: "HomeServer"):
|
2018-07-18 10:22:02 -04:00
|
|
|
self.pagination_handler = hs.get_pagination_handler()
|
2018-03-01 18:20:54 -05:00
|
|
|
self.store = hs.get_datastore()
|
2019-05-01 05:40:33 -04:00
|
|
|
self.auth = hs.get_auth()
|
2016-08-12 05:03:19 -04:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
async def on_POST(
|
|
|
|
self, request: SynapseRequest, room_id: str, event_id: Optional[str]
|
|
|
|
) -> Tuple[int, JsonDict]:
|
2019-10-11 07:05:27 -04:00
|
|
|
await assert_requester_is_admin(self.auth, request)
|
2016-07-05 05:28:51 -04:00
|
|
|
|
2018-02-08 13:44:52 -05:00
|
|
|
body = parse_json_object_from_request(request, allow_empty_body=True)
|
|
|
|
|
2018-02-13 08:04:41 -05:00
|
|
|
delete_local_events = bool(body.get("delete_local_events", False))
|
2018-02-08 13:44:52 -05:00
|
|
|
|
2018-03-01 18:20:54 -05:00
|
|
|
# establish the topological ordering we should keep events from. The
|
|
|
|
# user can provide an event_id in the URL or the request body, or can
|
|
|
|
# provide a timestamp in the request body.
|
|
|
|
if event_id is None:
|
|
|
|
event_id = body.get("purge_up_to_event_id")
|
|
|
|
|
|
|
|
if event_id is not None:
|
2019-10-11 07:05:27 -04:00
|
|
|
event = await self.store.get_event(event_id)
|
2018-03-01 18:20:54 -05:00
|
|
|
|
|
|
|
if event.room_id != room_id:
|
2021-11-29 17:19:45 -05:00
|
|
|
raise SynapseError(HTTPStatus.BAD_REQUEST, "Event is for wrong room.")
|
2018-03-01 18:20:54 -05:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
# RoomStreamToken expects [int] not Optional[int]
|
|
|
|
assert event.internal_metadata.stream_ordering is not None
|
2020-10-05 09:43:14 -04:00
|
|
|
room_token = RoomStreamToken(
|
|
|
|
event.depth, event.internal_metadata.stream_ordering
|
|
|
|
)
|
2020-09-30 15:29:19 -04:00
|
|
|
token = await room_token.to_string(self.store)
|
2018-05-15 11:06:30 -04:00
|
|
|
|
|
|
|
logger.info("[purge] purging up to token %s (event_id %s)", token, event_id)
|
2018-03-01 18:20:54 -05:00
|
|
|
elif "purge_up_to_ts" in body:
|
|
|
|
ts = body["purge_up_to_ts"]
|
|
|
|
if not isinstance(ts, int):
|
|
|
|
raise SynapseError(
|
2021-11-29 17:19:45 -05:00
|
|
|
HTTPStatus.BAD_REQUEST,
|
|
|
|
"purge_up_to_ts must be an int",
|
|
|
|
errcode=Codes.BAD_JSON,
|
2018-03-01 18:20:54 -05:00
|
|
|
)
|
|
|
|
|
2019-10-11 07:05:27 -04:00
|
|
|
stream_ordering = await self.store.find_first_stream_ordering_after_ts(ts)
|
2018-03-01 18:20:54 -05:00
|
|
|
|
2020-01-16 15:24:17 -05:00
|
|
|
r = await self.store.get_room_event_before_stream_ordering(
|
2019-10-11 07:05:27 -04:00
|
|
|
room_id, stream_ordering
|
2018-03-01 18:20:54 -05:00
|
|
|
)
|
2018-06-06 19:15:49 -04:00
|
|
|
if not r:
|
2019-10-31 06:23:24 -04:00
|
|
|
logger.warning(
|
2018-04-30 08:29:48 -04:00
|
|
|
"[purge] purging events not possible: No event found "
|
|
|
|
"(received_ts %i => stream_ordering %i)",
|
|
|
|
ts,
|
|
|
|
stream_ordering,
|
|
|
|
)
|
|
|
|
raise SynapseError(
|
2021-11-29 17:19:45 -05:00
|
|
|
HTTPStatus.NOT_FOUND,
|
|
|
|
"there is no event to be purged",
|
|
|
|
errcode=Codes.NOT_FOUND,
|
2018-04-30 08:29:48 -04:00
|
|
|
)
|
2018-06-06 19:15:49 -04:00
|
|
|
(stream, topo, _event_id) = r
|
|
|
|
token = "t%d-%d" % (topo, stream)
|
2018-03-01 18:20:54 -05:00
|
|
|
logger.info(
|
2018-06-06 19:26:38 -04:00
|
|
|
"[purge] purging up to token %s (received_ts %i => "
|
2018-03-01 18:20:54 -05:00
|
|
|
"stream_ordering %i)",
|
2018-05-15 11:06:30 -04:00
|
|
|
token,
|
|
|
|
ts,
|
|
|
|
stream_ordering,
|
2018-03-01 18:20:54 -05:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
raise SynapseError(
|
2021-11-29 17:19:45 -05:00
|
|
|
HTTPStatus.BAD_REQUEST,
|
2018-03-01 18:20:54 -05:00
|
|
|
"must specify purge_up_to_event_id or purge_up_to_ts",
|
|
|
|
errcode=Codes.BAD_JSON,
|
|
|
|
)
|
|
|
|
|
2019-10-11 10:26:09 -04:00
|
|
|
purge_id = self.pagination_handler.start_purge_history(
|
2018-02-08 13:44:52 -05:00
|
|
|
room_id, token, delete_local_events=delete_local_events
|
|
|
|
)
|
2016-07-05 05:28:51 -04:00
|
|
|
|
2021-11-29 17:19:45 -05:00
|
|
|
return HTTPStatus.OK, {"purge_id": purge_id}
|
2018-03-08 06:47:28 -05:00
|
|
|
|
|
|
|
|
2019-05-01 05:40:33 -04:00
|
|
|
class PurgeHistoryStatusRestServlet(RestServlet):
|
2021-12-08 11:59:40 -05:00
|
|
|
PATTERNS = admin_patterns("/purge_history_status/(?P<purge_id>[^/]*)$")
|
2018-03-08 06:47:28 -05:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def __init__(self, hs: "HomeServer"):
|
2018-07-18 10:22:02 -04:00
|
|
|
self.pagination_handler = hs.get_pagination_handler()
|
2019-05-01 05:40:33 -04:00
|
|
|
self.auth = hs.get_auth()
|
2018-03-08 06:47:28 -05:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
async def on_GET(
|
|
|
|
self, request: SynapseRequest, purge_id: str
|
|
|
|
) -> Tuple[int, JsonDict]:
|
2019-10-11 07:05:27 -04:00
|
|
|
await assert_requester_is_admin(self.auth, request)
|
2018-03-08 06:47:28 -05:00
|
|
|
|
2018-07-18 10:22:02 -04:00
|
|
|
purge_status = self.pagination_handler.get_purge_status(purge_id)
|
2018-03-08 06:47:28 -05:00
|
|
|
if purge_status is None:
|
|
|
|
raise NotFoundError("purge id '%s' not found" % purge_id)
|
|
|
|
|
2021-11-29 17:19:45 -05:00
|
|
|
return HTTPStatus.OK, purge_status.asdict()
|
2016-07-05 05:28:51 -04:00
|
|
|
|
|
|
|
|
2019-05-02 06:59:16 -04:00
|
|
|
########################################################################################
|
|
|
|
#
|
|
|
|
# please don't add more servlets here: this file is already long and unwieldy. Put
|
|
|
|
# them in separate files within the 'admin' package.
|
|
|
|
#
|
|
|
|
########################################################################################
|
|
|
|
|
2019-04-16 15:13:59 -04:00
|
|
|
|
2019-05-01 10:18:58 -04:00
|
|
|
class AdminRestResource(JsonResource):
|
|
|
|
"""The REST resource which gets mounted at /_synapse/admin"""
|
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def __init__(self, hs: "HomeServer"):
|
2019-05-01 10:18:58 -04:00
|
|
|
JsonResource.__init__(self, hs, canonical_json=False)
|
2019-05-29 11:47:16 -04:00
|
|
|
register_servlets(hs, self)
|
2019-05-02 06:59:16 -04:00
|
|
|
|
2019-05-29 11:47:16 -04:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
2019-05-29 11:47:16 -04:00
|
|
|
"""
|
|
|
|
Register all the admin servlets.
|
|
|
|
"""
|
|
|
|
register_servlets_for_client_rest_resource(hs, http_server)
|
2021-11-18 12:43:49 -05:00
|
|
|
BlockRoomRestServlet(hs).register(http_server)
|
2020-01-22 08:36:43 -05:00
|
|
|
ListRoomRestServlet(hs).register(http_server)
|
2021-02-02 06:16:29 -05:00
|
|
|
RoomStateRestServlet(hs).register(http_server)
|
2020-05-07 15:33:07 -04:00
|
|
|
RoomRestServlet(hs).register(http_server)
|
2021-11-12 07:35:31 -05:00
|
|
|
RoomRestV2Servlet(hs).register(http_server)
|
2020-07-16 16:43:23 -04:00
|
|
|
RoomMembersRestServlet(hs).register(http_server)
|
2021-11-12 07:35:31 -05:00
|
|
|
DeleteRoomStatusByDeleteIdRestServlet(hs).register(http_server)
|
|
|
|
DeleteRoomStatusByRoomIdRestServlet(hs).register(http_server)
|
2020-03-27 15:16:43 -04:00
|
|
|
JoinRoomAliasServlet(hs).register(http_server)
|
2019-05-29 11:47:16 -04:00
|
|
|
VersionServlet(hs).register(http_server)
|
2019-08-27 05:14:00 -04:00
|
|
|
UserAdminServlet(hs).register(http_server)
|
2020-09-18 10:26:36 -04:00
|
|
|
UserMembershipRestServlet(hs).register(http_server)
|
2020-11-17 05:51:25 -05:00
|
|
|
UserTokenRestServlet(hs).register(http_server)
|
2020-01-09 08:31:00 -05:00
|
|
|
UserRestServletV2(hs).register(http_server)
|
2019-12-05 13:12:23 -05:00
|
|
|
UsersRestServletV2(hs).register(http_server)
|
2020-06-05 08:07:22 -04:00
|
|
|
DeviceRestServlet(hs).register(http_server)
|
|
|
|
DevicesRestServlet(hs).register(http_server)
|
|
|
|
DeleteDevicesRestServlet(hs).register(http_server)
|
2020-11-05 13:59:12 -05:00
|
|
|
UserMediaStatisticsRestServlet(hs).register(http_server)
|
2020-10-26 14:16:37 -04:00
|
|
|
EventReportDetailRestServlet(hs).register(http_server)
|
2020-10-28 11:02:42 -04:00
|
|
|
EventReportsRestServlet(hs).register(http_server)
|
2022-01-05 06:49:06 -05:00
|
|
|
AccountDataRestServlet(hs).register(http_server)
|
2020-10-28 11:02:42 -04:00
|
|
|
PushersRestServlet(hs).register(http_server)
|
2020-12-18 10:37:19 -05:00
|
|
|
MakeRoomAdminRestServlet(hs).register(http_server)
|
2021-01-25 14:49:39 -05:00
|
|
|
ShadowBanRestServlet(hs).register(http_server)
|
2021-01-07 16:01:59 -05:00
|
|
|
ForwardExtremitiesRestServlet(hs).register(http_server)
|
2021-01-18 09:02:22 -05:00
|
|
|
RoomEventContextServlet(hs).register(http_server)
|
2021-04-13 05:26:37 -04:00
|
|
|
RateLimitRestServlet(hs).register(http_server)
|
2021-08-17 05:52:38 -04:00
|
|
|
UsernameAvailableRestServlet(hs).register(http_server)
|
2021-08-21 17:14:43 -04:00
|
|
|
ListRegistrationTokensRestServlet(hs).register(http_server)
|
|
|
|
NewRegistrationTokenRestServlet(hs).register(http_server)
|
|
|
|
RegistrationTokenRestServlet(hs).register(http_server)
|
2021-12-06 11:59:50 -05:00
|
|
|
DestinationsRestServlet(hs).register(http_server)
|
|
|
|
ListDestinationsRestServlet(hs).register(http_server)
|
2019-05-01 10:18:58 -04:00
|
|
|
|
2021-08-27 05:16:40 -04:00
|
|
|
# Some servlets only get registered for the main process.
|
2021-09-13 13:07:12 -04:00
|
|
|
if hs.config.worker.worker_app is None:
|
2021-08-27 05:16:40 -04:00
|
|
|
SendServerNoticeServlet(hs).register(http_server)
|
2021-11-08 11:08:02 -05:00
|
|
|
BackgroundUpdateEnabledRestServlet(hs).register(http_server)
|
|
|
|
BackgroundUpdateRestServlet(hs).register(http_server)
|
2021-11-19 14:39:46 -05:00
|
|
|
BackgroundUpdateStartJobRestServlet(hs).register(http_server)
|
2021-08-27 05:16:40 -04:00
|
|
|
|
2019-05-01 10:18:58 -04:00
|
|
|
|
2021-06-07 10:12:34 -04:00
|
|
|
def register_servlets_for_client_rest_resource(
|
|
|
|
hs: "HomeServer", http_server: HttpServer
|
|
|
|
) -> None:
|
2019-05-02 06:59:16 -04:00
|
|
|
"""Register only the servlets which need to be exposed on /_matrix/client/xxx"""
|
2014-09-29 09:59:52 -04:00
|
|
|
WhoisRestServlet(hs).register(http_server)
|
2018-03-08 06:47:28 -05:00
|
|
|
PurgeHistoryStatusRestServlet(hs).register(http_server)
|
2016-06-30 10:40:58 -04:00
|
|
|
DeactivateAccountRestServlet(hs).register(http_server)
|
2016-07-06 10:56:59 -04:00
|
|
|
PurgeHistoryRestServlet(hs).register(http_server)
|
2017-02-02 08:02:26 -05:00
|
|
|
ResetPasswordRestServlet(hs).register(http_server)
|
|
|
|
SearchUsersRestServlet(hs).register(http_server)
|
2018-07-20 08:41:13 -04:00
|
|
|
UserRegisterServlet(hs).register(http_server)
|
2019-04-03 11:07:34 -04:00
|
|
|
DeleteGroupAdminRestServlet(hs).register(http_server)
|
2019-04-16 15:13:59 -04:00
|
|
|
AccountValidityRenewServlet(hs).register(http_server)
|
2019-08-13 07:49:28 -04:00
|
|
|
|
2019-09-03 15:35:20 -04:00
|
|
|
# Load the media repo ones if we're using them. Otherwise load the servlets which
|
|
|
|
# don't need a media repo (typically readonly admin APIs).
|
2021-09-24 07:25:21 -04:00
|
|
|
if hs.config.media.can_load_media_repo:
|
2019-08-13 07:49:28 -04:00
|
|
|
register_servlets_for_media_repo(hs, http_server)
|
2019-09-03 15:35:20 -04:00
|
|
|
else:
|
|
|
|
ListMediaInRoom(hs).register(http_server)
|
2019-08-13 07:49:28 -04:00
|
|
|
|
2019-05-02 06:59:16 -04:00
|
|
|
# don't add more things here: new servlets should only be exposed on
|
|
|
|
# /_synapse/admin so should not go here. Instead register them in AdminRestResource.
|