mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-10-01 11:49:51 -04:00
Move admin API to a new prefix
This commit is contained in:
parent
579b637b6c
commit
8e9ca83537
@ -13,7 +13,7 @@ This API extends the validity of an account by as much time as configured in the
|
|||||||
|
|
||||||
The API is::
|
The API is::
|
||||||
|
|
||||||
POST /_matrix/client/unstable/admin/account_validity/validity
|
POST /_synapse/admin/v1/account_validity/validity
|
||||||
|
|
||||||
with the following body:
|
with the following body:
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ being deleted.
|
|||||||
The API is:
|
The API is:
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /_matrix/client/r0/admin/delete_group/<group_id>
|
POST /_synapse/admin/v1/delete_group/<group_id>
|
||||||
```
|
```
|
||||||
|
|
||||||
including an `access_token` of a server admin.
|
including an `access_token` of a server admin.
|
||||||
|
@ -4,7 +4,7 @@ This API gets a list of known media in a room.
|
|||||||
|
|
||||||
The API is:
|
The API is:
|
||||||
```
|
```
|
||||||
GET /_matrix/client/r0/admin/room/<room_id>/media
|
GET /_synapse/admin/v1/room/<room_id>/media
|
||||||
```
|
```
|
||||||
including an `access_token` of a server admin.
|
including an `access_token` of a server admin.
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ paginate further back in the room from the point being purged from.
|
|||||||
|
|
||||||
The API is:
|
The API is:
|
||||||
|
|
||||||
``POST /_matrix/client/r0/admin/purge_history/<room_id>[/<event_id>]``
|
``POST /_synapse/admin/v1/purge_history/<room_id>[/<event_id>]``
|
||||||
|
|
||||||
including an ``access_token`` of a server admin.
|
including an ``access_token`` of a server admin.
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ Purge status query
|
|||||||
|
|
||||||
It is possible to poll for updates on recent purges with a second API;
|
It is possible to poll for updates on recent purges with a second API;
|
||||||
|
|
||||||
``GET /_matrix/client/r0/admin/purge_history_status/<purge_id>``
|
``GET /_synapse/admin/v1/purge_history_status/<purge_id>``
|
||||||
|
|
||||||
(again, with a suitable ``access_token``). This API returns a JSON body like
|
(again, with a suitable ``access_token``). This API returns a JSON body like
|
||||||
the following:
|
the following:
|
||||||
|
@ -6,7 +6,7 @@ media.
|
|||||||
|
|
||||||
The API is::
|
The API is::
|
||||||
|
|
||||||
POST /_matrix/client/r0/admin/purge_media_cache?before_ts=<unix_timestamp_in_ms>&access_token=<access_token>
|
POST /_synapse/admin/v1/purge_media_cache?before_ts=<unix_timestamp_in_ms>&access_token=<access_token>
|
||||||
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ is not enabled.
|
|||||||
|
|
||||||
To fetch the nonce, you need to request one from the API::
|
To fetch the nonce, you need to request one from the API::
|
||||||
|
|
||||||
> GET /_matrix/client/r0/admin/register
|
> GET /_synapse/admin/v1/register
|
||||||
|
|
||||||
< {"nonce": "thisisanonce"}
|
< {"nonce": "thisisanonce"}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ body containing the nonce, username, password, whether they are an admin
|
|||||||
|
|
||||||
As an example::
|
As an example::
|
||||||
|
|
||||||
> POST /_matrix/client/r0/admin/register
|
> POST /_synapse/admin/v1/register
|
||||||
> {
|
> {
|
||||||
"nonce": "thisisanonce",
|
"nonce": "thisisanonce",
|
||||||
"username": "pepper_roni",
|
"username": "pepper_roni",
|
||||||
|
@ -5,7 +5,7 @@ This API returns information about a specific user account.
|
|||||||
|
|
||||||
The api is::
|
The api is::
|
||||||
|
|
||||||
GET /_matrix/client/r0/admin/whois/<user_id>
|
GET /_synapse/admin/v1/whois/<user_id>
|
||||||
|
|
||||||
including an ``access_token`` of a server admin.
|
including an ``access_token`` of a server admin.
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ references to it).
|
|||||||
|
|
||||||
The api is::
|
The api is::
|
||||||
|
|
||||||
POST /_matrix/client/r0/admin/deactivate/<user_id>
|
POST /_synapse/admin/v1/deactivate/<user_id>
|
||||||
|
|
||||||
with a body of:
|
with a body of:
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ Changes the password of another user.
|
|||||||
|
|
||||||
The api is::
|
The api is::
|
||||||
|
|
||||||
POST /_matrix/client/r0/admin/reset_password/<user_id>
|
POST /_synapse/admin/v1/reset_password/<user_id>
|
||||||
|
|
||||||
with a body of:
|
with a body of:
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ contains Synapse version information).
|
|||||||
|
|
||||||
The api is::
|
The api is::
|
||||||
|
|
||||||
GET /_matrix/client/r0/admin/server_version
|
GET /_synapse/admin/v1/server_version
|
||||||
|
|
||||||
including an ``access_token`` of a server admin.
|
including an ``access_token`` of a server admin.
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ from synapse.python_dependencies import check_requirements
|
|||||||
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
||||||
from synapse.replication.tcp.resource import ReplicationStreamProtocolFactory
|
from synapse.replication.tcp.resource import ReplicationStreamProtocolFactory
|
||||||
from synapse.rest import ClientRestResource
|
from synapse.rest import ClientRestResource
|
||||||
|
from synapse.rest.client.v1.admin import AdminRestResource
|
||||||
from synapse.rest.key.v2 import KeyApiV2Resource
|
from synapse.rest.key.v2 import KeyApiV2Resource
|
||||||
from synapse.rest.media.v0.content_repository import ContentRepoResource
|
from synapse.rest.media.v0.content_repository import ContentRepoResource
|
||||||
from synapse.rest.well_known import WellKnownResource
|
from synapse.rest.well_known import WellKnownResource
|
||||||
@ -180,6 +181,7 @@ class SynapseHomeServer(HomeServer):
|
|||||||
"/_matrix/client/v2_alpha": client_resource,
|
"/_matrix/client/v2_alpha": client_resource,
|
||||||
"/_matrix/client/versions": client_resource,
|
"/_matrix/client/versions": client_resource,
|
||||||
"/.well-known/matrix/client": WellKnownResource(self),
|
"/.well-known/matrix/client": WellKnownResource(self),
|
||||||
|
"/_synapse/admin": AdminRestResource(self),
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.get_config().saml2_enabled:
|
if self.get_config().saml2_enabled:
|
||||||
|
@ -388,8 +388,8 @@ class ServerConfig(Config):
|
|||||||
#
|
#
|
||||||
# Valid resource names are:
|
# Valid resource names are:
|
||||||
#
|
#
|
||||||
# client: the client-server API (/_matrix/client). Also implies 'media' and
|
# client: the client-server API (/_matrix/client), and the synapse admin
|
||||||
# 'static'.
|
# API (/_synapse/admin). Also implies 'media' and 'static'.
|
||||||
#
|
#
|
||||||
# consent: user consent forms (/_matrix/consent). See
|
# consent: user consent forms (/_matrix/consent). See
|
||||||
# docs/consent_tracking.md.
|
# docs/consent_tracking.md.
|
||||||
|
@ -58,8 +58,14 @@ from synapse.rest.client.v2_alpha import (
|
|||||||
|
|
||||||
|
|
||||||
class ClientRestResource(JsonResource):
|
class ClientRestResource(JsonResource):
|
||||||
"""A resource for version 1 of the matrix client API."""
|
"""Matrix Client API REST resource.
|
||||||
|
|
||||||
|
This gets mounted at various points under /_matrix/client, including:
|
||||||
|
* /_matrix/client/r0
|
||||||
|
* /_matrix/client/api/v1
|
||||||
|
* /_matrix/client/unstable
|
||||||
|
* etc
|
||||||
|
"""
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
JsonResource.__init__(self, hs, canonical_json=False)
|
JsonResource.__init__(self, hs, canonical_json=False)
|
||||||
self.register_servlets(self, hs)
|
self.register_servlets(self, hs)
|
||||||
@ -82,7 +88,6 @@ class ClientRestResource(JsonResource):
|
|||||||
presence.register_servlets(hs, client_resource)
|
presence.register_servlets(hs, client_resource)
|
||||||
directory.register_servlets(hs, client_resource)
|
directory.register_servlets(hs, client_resource)
|
||||||
voip.register_servlets(hs, client_resource)
|
voip.register_servlets(hs, client_resource)
|
||||||
admin.register_servlets(hs, client_resource)
|
|
||||||
pusher.register_servlets(hs, client_resource)
|
pusher.register_servlets(hs, client_resource)
|
||||||
push_rule.register_servlets(hs, client_resource)
|
push_rule.register_servlets(hs, client_resource)
|
||||||
logout.register_servlets(hs, client_resource)
|
logout.register_servlets(hs, client_resource)
|
||||||
@ -111,3 +116,6 @@ class ClientRestResource(JsonResource):
|
|||||||
room_upgrade_rest_servlet.register_servlets(hs, client_resource)
|
room_upgrade_rest_servlet.register_servlets(hs, client_resource)
|
||||||
capabilities.register_servlets(hs, client_resource)
|
capabilities.register_servlets(hs, client_resource)
|
||||||
account_validity.register_servlets(hs, client_resource)
|
account_validity.register_servlets(hs, client_resource)
|
||||||
|
|
||||||
|
# moving to /_synapse/admin
|
||||||
|
admin.register_servlets(hs, client_resource)
|
||||||
|
@ -18,6 +18,7 @@ import hashlib
|
|||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
|
import re
|
||||||
|
|
||||||
from six import text_type
|
from six import text_type
|
||||||
from six.moves import http_client
|
from six.moves import http_client
|
||||||
@ -27,6 +28,7 @@ from twisted.internet import defer
|
|||||||
import synapse
|
import synapse
|
||||||
from synapse.api.constants import Membership, UserTypes
|
from synapse.api.constants import Membership, UserTypes
|
||||||
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
||||||
|
from synapse.http.server import JsonResource
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
RestServlet,
|
RestServlet,
|
||||||
assert_params_in_dict,
|
assert_params_in_dict,
|
||||||
@ -37,13 +39,33 @@ from synapse.http.servlet import (
|
|||||||
from synapse.types import UserID, create_requester
|
from synapse.types import UserID, create_requester
|
||||||
from synapse.util.versionstring import get_version_string
|
from synapse.util.versionstring import get_version_string
|
||||||
|
|
||||||
from .base import client_path_patterns
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def historical_admin_path_patterns(path_regex):
|
||||||
|
"""Returns the list of patterns for an admin endpoint, including historical ones
|
||||||
|
|
||||||
|
This is a backwards-compatibility hack. Previously, the Admin API was exposed at
|
||||||
|
various paths under /_matrix/client. This function returns a list of patterns
|
||||||
|
matching those paths (as well as the new one), so that existing scripts which rely
|
||||||
|
on the endpoints being available there are not broken.
|
||||||
|
|
||||||
|
Note that this should only be used for existing endpoints: new ones should just
|
||||||
|
register for the /_synapse/admin path.
|
||||||
|
"""
|
||||||
|
return list(
|
||||||
|
re.compile(prefix + path_regex)
|
||||||
|
for prefix in (
|
||||||
|
"^/_synapse/admin/v1",
|
||||||
|
"^/_matrix/client/api/v1/admin",
|
||||||
|
"^/_matrix/client/unstable/admin",
|
||||||
|
"^/_matrix/client/r0/admin"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UsersRestServlet(RestServlet):
|
class UsersRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/users/(?P<user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/users/(?P<user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
@ -72,7 +94,7 @@ class UsersRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class VersionServlet(RestServlet):
|
class VersionServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/server_version")
|
PATTERNS = historical_admin_path_patterns("/server_version")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.auth = hs.get_auth()
|
self.auth = hs.get_auth()
|
||||||
@ -100,7 +122,7 @@ class UserRegisterServlet(RestServlet):
|
|||||||
nonces (dict[str, int]): The nonces that we will accept. A dict of
|
nonces (dict[str, int]): The nonces that we will accept. A dict of
|
||||||
nonce to the time it was generated, in int seconds.
|
nonce to the time it was generated, in int seconds.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/register")
|
PATTERNS = historical_admin_path_patterns("/register")
|
||||||
NONCE_TIMEOUT = 60
|
NONCE_TIMEOUT = 60
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
@ -231,7 +253,7 @@ class UserRegisterServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class WhoisRestServlet(RestServlet):
|
class WhoisRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/whois/(?P<user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/whois/(?P<user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
@ -257,7 +279,7 @@ class WhoisRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class PurgeMediaCacheRestServlet(RestServlet):
|
class PurgeMediaCacheRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/purge_media_cache")
|
PATTERNS = historical_admin_path_patterns("/purge_media_cache")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.media_repository = hs.get_media_repository()
|
self.media_repository = hs.get_media_repository()
|
||||||
@ -280,8 +302,8 @@ class PurgeMediaCacheRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class PurgeHistoryRestServlet(RestServlet):
|
class PurgeHistoryRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns(
|
PATTERNS = historical_admin_path_patterns(
|
||||||
"/admin/purge_history/(?P<room_id>[^/]*)(/(?P<event_id>[^/]+))?"
|
"/purge_history/(?P<room_id>[^/]*)(/(?P<event_id>[^/]+))?"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
@ -377,8 +399,8 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class PurgeHistoryStatusRestServlet(RestServlet):
|
class PurgeHistoryStatusRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns(
|
PATTERNS = historical_admin_path_patterns(
|
||||||
"/admin/purge_history_status/(?P<purge_id>[^/]+)"
|
"/purge_history_status/(?P<purge_id>[^/]+)"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
@ -406,7 +428,7 @@ class PurgeHistoryStatusRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class DeactivateAccountRestServlet(RestServlet):
|
class DeactivateAccountRestServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/deactivate/(?P<target_user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/deactivate/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self._deactivate_account_handler = hs.get_deactivate_account_handler()
|
self._deactivate_account_handler = hs.get_deactivate_account_handler()
|
||||||
@ -449,7 +471,7 @@ class ShutdownRoomRestServlet(RestServlet):
|
|||||||
to a new room created by `new_room_user_id` and kicked users will be auto
|
to a new room created by `new_room_user_id` and kicked users will be auto
|
||||||
joined to the new room.
|
joined to the new room.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/shutdown_room/(?P<room_id>[^/]+)")
|
PATTERNS = historical_admin_path_patterns("/shutdown_room/(?P<room_id>[^/]+)")
|
||||||
|
|
||||||
DEFAULT_MESSAGE = (
|
DEFAULT_MESSAGE = (
|
||||||
"Sharing illegal content on this server is not permitted and rooms in"
|
"Sharing illegal content on this server is not permitted and rooms in"
|
||||||
@ -574,7 +596,7 @@ class QuarantineMediaInRoom(RestServlet):
|
|||||||
"""Quarantines all media in a room so that no one can download it via
|
"""Quarantines all media in a room so that no one can download it via
|
||||||
this server.
|
this server.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/quarantine_media/(?P<room_id>[^/]+)")
|
PATTERNS = historical_admin_path_patterns("/quarantine_media/(?P<room_id>[^/]+)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
@ -597,7 +619,7 @@ class QuarantineMediaInRoom(RestServlet):
|
|||||||
class ListMediaInRoom(RestServlet):
|
class ListMediaInRoom(RestServlet):
|
||||||
"""Lists all of the media in a given room.
|
"""Lists all of the media in a given room.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/room/(?P<room_id>[^/]+)/media")
|
PATTERNS = historical_admin_path_patterns("/room/(?P<room_id>[^/]+)/media")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
@ -627,7 +649,7 @@ class ResetPasswordRestServlet(RestServlet):
|
|||||||
Returns:
|
Returns:
|
||||||
200 OK with empty object if success otherwise an error.
|
200 OK with empty object if success otherwise an error.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/reset_password/(?P<target_user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/reset_password/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
@ -666,7 +688,7 @@ class GetUsersPaginatedRestServlet(RestServlet):
|
|||||||
Returns:
|
Returns:
|
||||||
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/users_paginate/(?P<target_user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/users_paginate/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
@ -749,7 +771,7 @@ class SearchUsersRestServlet(RestServlet):
|
|||||||
Returns:
|
Returns:
|
||||||
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/search_users/(?P<target_user_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/search_users/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.store = hs.get_datastore()
|
self.store = hs.get_datastore()
|
||||||
@ -789,7 +811,7 @@ class SearchUsersRestServlet(RestServlet):
|
|||||||
class DeleteGroupAdminRestServlet(RestServlet):
|
class DeleteGroupAdminRestServlet(RestServlet):
|
||||||
"""Allows deleting of local groups
|
"""Allows deleting of local groups
|
||||||
"""
|
"""
|
||||||
PATTERNS = client_path_patterns("/admin/delete_group/(?P<group_id>[^/]*)")
|
PATTERNS = historical_admin_path_patterns("/delete_group/(?P<group_id>[^/]*)")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
self.group_server = hs.get_groups_server_handler()
|
self.group_server = hs.get_groups_server_handler()
|
||||||
@ -812,7 +834,7 @@ class DeleteGroupAdminRestServlet(RestServlet):
|
|||||||
|
|
||||||
|
|
||||||
class AccountValidityRenewServlet(RestServlet):
|
class AccountValidityRenewServlet(RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/account_validity/validity$")
|
PATTERNS = historical_admin_path_patterns("/account_validity/validity$")
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
"""
|
"""
|
||||||
@ -847,6 +869,14 @@ class AccountValidityRenewServlet(RestServlet):
|
|||||||
defer.returnValue((200, res))
|
defer.returnValue((200, res))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRestResource(JsonResource):
|
||||||
|
"""The REST resource which gets mounted at /_synapse/admin"""
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
JsonResource.__init__(self, hs, canonical_json=False)
|
||||||
|
register_servlets(hs, self)
|
||||||
|
|
||||||
|
|
||||||
def register_servlets(hs, http_server):
|
def register_servlets(hs, http_server):
|
||||||
WhoisRestServlet(hs).register(http_server)
|
WhoisRestServlet(hs).register(http_server)
|
||||||
PurgeMediaCacheRestServlet(hs).register(http_server)
|
PurgeMediaCacheRestServlet(hs).register(http_server)
|
||||||
|
Loading…
Reference in New Issue
Block a user