Merge branch 'release-v1.41' of github.com:matrix-org/synapse into matrix-org-hotfixes
commit
2f4fe530a1
|
@ -1,7 +1,7 @@
|
||||||
Upgrading Synapse
|
Upgrading Synapse
|
||||||
=================
|
=================
|
||||||
|
|
||||||
This document has moved to the `Synapse documentation website <https://matrix-org.github.io/synapse/latest/upgrading>`_.
|
This document has moved to the `Synapse documentation website <https://matrix-org.github.io/synapse/latest/upgrade>`_.
|
||||||
Please update your links.
|
Please update your links.
|
||||||
|
|
||||||
The markdown source is available in `docs/upgrade.md <docs/upgrade.md>`_.
|
The markdown source is available in `docs/upgrade.md <docs/upgrade.md>`_.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add support for routing `/createRoom` to workers.
|
|
@ -0,0 +1 @@
|
||||||
|
Admin API to delete several media for a specific user. Contributed by @dklimpel.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix a broken link to the upgrade notes.
|
|
@ -0,0 +1 @@
|
||||||
|
Add option to allow modules to run periodic tasks on all instances, rather than just the one configured to run background tasks.
|
|
@ -123,6 +123,12 @@ for more information and examples.
|
||||||
|
|
||||||
We plan to remove support for these settings in October 2021.
|
We plan to remove support for these settings in October 2021.
|
||||||
|
|
||||||
|
## `/_synapse/admin/v1/users/{userId}/media` must be handled by media workers
|
||||||
|
|
||||||
|
The [media repository worker documentation](https://matrix-org.github.io/synapse/latest/workers.html#synapseappmedia_repository)
|
||||||
|
has been updated to reflect that calls to `/_synapse/admin/v1/users/{userId}/media`
|
||||||
|
must now be handled by media repository workers. This is due to the new `DELETE` method
|
||||||
|
of this endpoint modifying the media store.
|
||||||
|
|
||||||
# Upgrading to v1.39.0
|
# Upgrading to v1.39.0
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,7 @@ expressions:
|
||||||
^/_matrix/federation/v1/send/
|
^/_matrix/federation/v1/send/
|
||||||
|
|
||||||
# Client API requests
|
# Client API requests
|
||||||
|
^/_matrix/client/(api/v1|r0|unstable)/createRoom$
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/publicRooms$
|
^/_matrix/client/(api/v1|r0|unstable)/publicRooms$
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$
|
||||||
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*$
|
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*$
|
||||||
|
@ -425,10 +426,12 @@ Handles the media repository. It can handle all endpoints starting with:
|
||||||
^/_synapse/admin/v1/user/.*/media.*$
|
^/_synapse/admin/v1/user/.*/media.*$
|
||||||
^/_synapse/admin/v1/media/.*$
|
^/_synapse/admin/v1/media/.*$
|
||||||
^/_synapse/admin/v1/quarantine_media/.*$
|
^/_synapse/admin/v1/quarantine_media/.*$
|
||||||
|
^/_synapse/admin/v1/users/.*/media$
|
||||||
|
|
||||||
You should also set `enable_media_repo: False` in the shared configuration
|
You should also set `enable_media_repo: False` in the shared configuration
|
||||||
file to stop the main synapse running background jobs related to managing the
|
file to stop the main synapse running background jobs related to managing the
|
||||||
media repository.
|
media repository. Note that doing so will prevent the main process from being
|
||||||
|
able to handle the above endpoints.
|
||||||
|
|
||||||
In the `media_repository` worker configuration file, configure the http listener to
|
In the `media_repository` worker configuration file, configure the http listener to
|
||||||
expose the `media` resource. For example:
|
expose the `media` resource. For example:
|
||||||
|
|
|
@ -604,10 +604,15 @@ class ModuleApi:
|
||||||
msec: float,
|
msec: float,
|
||||||
*args,
|
*args,
|
||||||
desc: Optional[str] = None,
|
desc: Optional[str] = None,
|
||||||
|
run_on_all_instances: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Wraps a function as a background process and calls it repeatedly.
|
"""Wraps a function as a background process and calls it repeatedly.
|
||||||
|
|
||||||
|
NOTE: Will only run on the instance that is configured to run
|
||||||
|
background processes (which is the main process by default), unless
|
||||||
|
`run_on_all_workers` is set.
|
||||||
|
|
||||||
Waits `msec` initially before calling `f` for the first time.
|
Waits `msec` initially before calling `f` for the first time.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -618,12 +623,14 @@ class ModuleApi:
|
||||||
msec: How long to wait between calls in milliseconds.
|
msec: How long to wait between calls in milliseconds.
|
||||||
*args: Positional arguments to pass to function.
|
*args: Positional arguments to pass to function.
|
||||||
desc: The background task's description. Default to the function's name.
|
desc: The background task's description. Default to the function's name.
|
||||||
|
run_on_all_instances: Whether to run this on all instances, rather
|
||||||
|
than just the instance configured to run background tasks.
|
||||||
**kwargs: Key arguments to pass to function.
|
**kwargs: Key arguments to pass to function.
|
||||||
"""
|
"""
|
||||||
if desc is None:
|
if desc is None:
|
||||||
desc = f.__name__
|
desc = f.__name__
|
||||||
|
|
||||||
if self._hs.config.run_background_tasks:
|
if self._hs.config.run_background_tasks or run_on_all_instances:
|
||||||
self._clock.looping_call(
|
self._clock.looping_call(
|
||||||
run_as_background_process,
|
run_as_background_process,
|
||||||
msec,
|
msec,
|
||||||
|
|
|
@ -61,7 +61,6 @@ from synapse.rest.admin.users import (
|
||||||
SearchUsersRestServlet,
|
SearchUsersRestServlet,
|
||||||
ShadowBanRestServlet,
|
ShadowBanRestServlet,
|
||||||
UserAdminServlet,
|
UserAdminServlet,
|
||||||
UserMediaRestServlet,
|
|
||||||
UserMembershipRestServlet,
|
UserMembershipRestServlet,
|
||||||
UserRegisterServlet,
|
UserRegisterServlet,
|
||||||
UserRestServletV2,
|
UserRestServletV2,
|
||||||
|
@ -225,7 +224,6 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||||
SendServerNoticeServlet(hs).register(http_server)
|
SendServerNoticeServlet(hs).register(http_server)
|
||||||
VersionServlet(hs).register(http_server)
|
VersionServlet(hs).register(http_server)
|
||||||
UserAdminServlet(hs).register(http_server)
|
UserAdminServlet(hs).register(http_server)
|
||||||
UserMediaRestServlet(hs).register(http_server)
|
|
||||||
UserMembershipRestServlet(hs).register(http_server)
|
UserMembershipRestServlet(hs).register(http_server)
|
||||||
UserTokenRestServlet(hs).register(http_server)
|
UserTokenRestServlet(hs).register(http_server)
|
||||||
UserRestServletV2(hs).register(http_server)
|
UserRestServletV2(hs).register(http_server)
|
||||||
|
|
|
@ -18,14 +18,15 @@ from typing import TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
||||||
from synapse.http.server import HttpServer
|
from synapse.http.server import HttpServer
|
||||||
from synapse.http.servlet import RestServlet, parse_boolean, parse_integer
|
from synapse.http.servlet import RestServlet, parse_boolean, parse_integer, parse_string
|
||||||
from synapse.http.site import SynapseRequest
|
from synapse.http.site import SynapseRequest
|
||||||
from synapse.rest.admin._base import (
|
from synapse.rest.admin._base import (
|
||||||
admin_patterns,
|
admin_patterns,
|
||||||
assert_requester_is_admin,
|
assert_requester_is_admin,
|
||||||
assert_user_is_admin,
|
assert_user_is_admin,
|
||||||
)
|
)
|
||||||
from synapse.types import JsonDict
|
from synapse.storage.databases.main.media_repository import MediaSortOrder
|
||||||
|
from synapse.types import JsonDict, UserID
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
@ -314,6 +315,165 @@ class DeleteMediaByDateSize(RestServlet):
|
||||||
return 200, {"deleted_media": deleted_media, "total": total}
|
return 200, {"deleted_media": deleted_media, "total": total}
|
||||||
|
|
||||||
|
|
||||||
|
class UserMediaRestServlet(RestServlet):
|
||||||
|
"""
|
||||||
|
Gets information about all uploaded local media for a specific `user_id`.
|
||||||
|
With DELETE request you can delete all this media.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
http://localhost:8008/_synapse/admin/v1/users/@user:server/media
|
||||||
|
|
||||||
|
Args:
|
||||||
|
The parameters `from` and `limit` are required for pagination.
|
||||||
|
By default, a `limit` of 100 is used.
|
||||||
|
Returns:
|
||||||
|
A list of media and an integer representing the total number of
|
||||||
|
media that exist given for this user
|
||||||
|
"""
|
||||||
|
|
||||||
|
PATTERNS = admin_patterns("/users/(?P<user_id>[^/]+)/media$")
|
||||||
|
|
||||||
|
def __init__(self, hs: "HomeServer"):
|
||||||
|
self.is_mine = hs.is_mine
|
||||||
|
self.auth = hs.get_auth()
|
||||||
|
self.store = hs.get_datastore()
|
||||||
|
self.media_repository = hs.get_media_repository()
|
||||||
|
|
||||||
|
async def on_GET(
|
||||||
|
self, request: SynapseRequest, user_id: str
|
||||||
|
) -> Tuple[int, JsonDict]:
|
||||||
|
# This will always be set by the time Twisted calls us.
|
||||||
|
assert request.args is not None
|
||||||
|
|
||||||
|
await assert_requester_is_admin(self.auth, request)
|
||||||
|
|
||||||
|
if not self.is_mine(UserID.from_string(user_id)):
|
||||||
|
raise SynapseError(400, "Can only look up local users")
|
||||||
|
|
||||||
|
user = await self.store.get_user_by_id(user_id)
|
||||||
|
if user is None:
|
||||||
|
raise NotFoundError("Unknown user")
|
||||||
|
|
||||||
|
start = parse_integer(request, "from", default=0)
|
||||||
|
limit = parse_integer(request, "limit", default=100)
|
||||||
|
|
||||||
|
if start < 0:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Query parameter from must be a string representing a positive integer.",
|
||||||
|
errcode=Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
|
|
||||||
|
if limit < 0:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Query parameter limit must be a string representing a positive integer.",
|
||||||
|
errcode=Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If neither `order_by` nor `dir` is set, set the default order
|
||||||
|
# to newest media is on top for backward compatibility.
|
||||||
|
if b"order_by" not in request.args and b"dir" not in request.args:
|
||||||
|
order_by = MediaSortOrder.CREATED_TS.value
|
||||||
|
direction = "b"
|
||||||
|
else:
|
||||||
|
order_by = parse_string(
|
||||||
|
request,
|
||||||
|
"order_by",
|
||||||
|
default=MediaSortOrder.CREATED_TS.value,
|
||||||
|
allowed_values=(
|
||||||
|
MediaSortOrder.MEDIA_ID.value,
|
||||||
|
MediaSortOrder.UPLOAD_NAME.value,
|
||||||
|
MediaSortOrder.CREATED_TS.value,
|
||||||
|
MediaSortOrder.LAST_ACCESS_TS.value,
|
||||||
|
MediaSortOrder.MEDIA_LENGTH.value,
|
||||||
|
MediaSortOrder.MEDIA_TYPE.value,
|
||||||
|
MediaSortOrder.QUARANTINED_BY.value,
|
||||||
|
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
direction = parse_string(
|
||||||
|
request, "dir", default="f", allowed_values=("f", "b")
|
||||||
|
)
|
||||||
|
|
||||||
|
media, total = await self.store.get_local_media_by_user_paginate(
|
||||||
|
start, limit, user_id, order_by, direction
|
||||||
|
)
|
||||||
|
|
||||||
|
ret = {"media": media, "total": total}
|
||||||
|
if (start + limit) < total:
|
||||||
|
ret["next_token"] = start + len(media)
|
||||||
|
|
||||||
|
return 200, ret
|
||||||
|
|
||||||
|
async def on_DELETE(
|
||||||
|
self, request: SynapseRequest, user_id: str
|
||||||
|
) -> Tuple[int, JsonDict]:
|
||||||
|
# This will always be set by the time Twisted calls us.
|
||||||
|
assert request.args is not None
|
||||||
|
|
||||||
|
await assert_requester_is_admin(self.auth, request)
|
||||||
|
|
||||||
|
if not self.is_mine(UserID.from_string(user_id)):
|
||||||
|
raise SynapseError(400, "Can only look up local users")
|
||||||
|
|
||||||
|
user = await self.store.get_user_by_id(user_id)
|
||||||
|
if user is None:
|
||||||
|
raise NotFoundError("Unknown user")
|
||||||
|
|
||||||
|
start = parse_integer(request, "from", default=0)
|
||||||
|
limit = parse_integer(request, "limit", default=100)
|
||||||
|
|
||||||
|
if start < 0:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Query parameter from must be a string representing a positive integer.",
|
||||||
|
errcode=Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
|
|
||||||
|
if limit < 0:
|
||||||
|
raise SynapseError(
|
||||||
|
400,
|
||||||
|
"Query parameter limit must be a string representing a positive integer.",
|
||||||
|
errcode=Codes.INVALID_PARAM,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If neither `order_by` nor `dir` is set, set the default order
|
||||||
|
# to newest media is on top for backward compatibility.
|
||||||
|
if b"order_by" not in request.args and b"dir" not in request.args:
|
||||||
|
order_by = MediaSortOrder.CREATED_TS.value
|
||||||
|
direction = "b"
|
||||||
|
else:
|
||||||
|
order_by = parse_string(
|
||||||
|
request,
|
||||||
|
"order_by",
|
||||||
|
default=MediaSortOrder.CREATED_TS.value,
|
||||||
|
allowed_values=(
|
||||||
|
MediaSortOrder.MEDIA_ID.value,
|
||||||
|
MediaSortOrder.UPLOAD_NAME.value,
|
||||||
|
MediaSortOrder.CREATED_TS.value,
|
||||||
|
MediaSortOrder.LAST_ACCESS_TS.value,
|
||||||
|
MediaSortOrder.MEDIA_LENGTH.value,
|
||||||
|
MediaSortOrder.MEDIA_TYPE.value,
|
||||||
|
MediaSortOrder.QUARANTINED_BY.value,
|
||||||
|
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
direction = parse_string(
|
||||||
|
request, "dir", default="f", allowed_values=("f", "b")
|
||||||
|
)
|
||||||
|
|
||||||
|
media, _ = await self.store.get_local_media_by_user_paginate(
|
||||||
|
start, limit, user_id, order_by, direction
|
||||||
|
)
|
||||||
|
|
||||||
|
deleted_media, total = await self.media_repository.delete_local_media_ids(
|
||||||
|
([row["media_id"] for row in media])
|
||||||
|
)
|
||||||
|
|
||||||
|
return 200, {"deleted_media": deleted_media, "total": total}
|
||||||
|
|
||||||
|
|
||||||
def register_servlets_for_media_repo(hs: "HomeServer", http_server: HttpServer) -> None:
|
def register_servlets_for_media_repo(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||||
"""
|
"""
|
||||||
Media repo specific APIs.
|
Media repo specific APIs.
|
||||||
|
@ -328,3 +488,4 @@ def register_servlets_for_media_repo(hs: "HomeServer", http_server: HttpServer)
|
||||||
ListMediaInRoom(hs).register(http_server)
|
ListMediaInRoom(hs).register(http_server)
|
||||||
DeleteMediaByID(hs).register(http_server)
|
DeleteMediaByID(hs).register(http_server)
|
||||||
DeleteMediaByDateSize(hs).register(http_server)
|
DeleteMediaByDateSize(hs).register(http_server)
|
||||||
|
UserMediaRestServlet(hs).register(http_server)
|
||||||
|
|
|
@ -35,7 +35,6 @@ from synapse.rest.admin._base import (
|
||||||
assert_user_is_admin,
|
assert_user_is_admin,
|
||||||
)
|
)
|
||||||
from synapse.rest.client._base import client_patterns
|
from synapse.rest.client._base import client_patterns
|
||||||
from synapse.storage.databases.main.media_repository import MediaSortOrder
|
|
||||||
from synapse.storage.databases.main.stats import UserSortOrder
|
from synapse.storage.databases.main.stats import UserSortOrder
|
||||||
from synapse.types import JsonDict, UserID
|
from synapse.types import JsonDict, UserID
|
||||||
|
|
||||||
|
@ -851,165 +850,6 @@ class PushersRestServlet(RestServlet):
|
||||||
return 200, {"pushers": filtered_pushers, "total": len(filtered_pushers)}
|
return 200, {"pushers": filtered_pushers, "total": len(filtered_pushers)}
|
||||||
|
|
||||||
|
|
||||||
class UserMediaRestServlet(RestServlet):
|
|
||||||
"""
|
|
||||||
Gets information about all uploaded local media for a specific `user_id`.
|
|
||||||
With DELETE request you can delete all this media.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
http://localhost:8008/_synapse/admin/v1/users/@user:server/media
|
|
||||||
|
|
||||||
Args:
|
|
||||||
The parameters `from` and `limit` are required for pagination.
|
|
||||||
By default, a `limit` of 100 is used.
|
|
||||||
Returns:
|
|
||||||
A list of media and an integer representing the total number of
|
|
||||||
media that exist given for this user
|
|
||||||
"""
|
|
||||||
|
|
||||||
PATTERNS = admin_patterns("/users/(?P<user_id>[^/]+)/media$")
|
|
||||||
|
|
||||||
def __init__(self, hs: "HomeServer"):
|
|
||||||
self.is_mine = hs.is_mine
|
|
||||||
self.auth = hs.get_auth()
|
|
||||||
self.store = hs.get_datastore()
|
|
||||||
self.media_repository = hs.get_media_repository()
|
|
||||||
|
|
||||||
async def on_GET(
|
|
||||||
self, request: SynapseRequest, user_id: str
|
|
||||||
) -> Tuple[int, JsonDict]:
|
|
||||||
# This will always be set by the time Twisted calls us.
|
|
||||||
assert request.args is not None
|
|
||||||
|
|
||||||
await assert_requester_is_admin(self.auth, request)
|
|
||||||
|
|
||||||
if not self.is_mine(UserID.from_string(user_id)):
|
|
||||||
raise SynapseError(400, "Can only look up local users")
|
|
||||||
|
|
||||||
user = await self.store.get_user_by_id(user_id)
|
|
||||||
if user is None:
|
|
||||||
raise NotFoundError("Unknown user")
|
|
||||||
|
|
||||||
start = parse_integer(request, "from", default=0)
|
|
||||||
limit = parse_integer(request, "limit", default=100)
|
|
||||||
|
|
||||||
if start < 0:
|
|
||||||
raise SynapseError(
|
|
||||||
400,
|
|
||||||
"Query parameter from must be a string representing a positive integer.",
|
|
||||||
errcode=Codes.INVALID_PARAM,
|
|
||||||
)
|
|
||||||
|
|
||||||
if limit < 0:
|
|
||||||
raise SynapseError(
|
|
||||||
400,
|
|
||||||
"Query parameter limit must be a string representing a positive integer.",
|
|
||||||
errcode=Codes.INVALID_PARAM,
|
|
||||||
)
|
|
||||||
|
|
||||||
# If neither `order_by` nor `dir` is set, set the default order
|
|
||||||
# to newest media is on top for backward compatibility.
|
|
||||||
if b"order_by" not in request.args and b"dir" not in request.args:
|
|
||||||
order_by = MediaSortOrder.CREATED_TS.value
|
|
||||||
direction = "b"
|
|
||||||
else:
|
|
||||||
order_by = parse_string(
|
|
||||||
request,
|
|
||||||
"order_by",
|
|
||||||
default=MediaSortOrder.CREATED_TS.value,
|
|
||||||
allowed_values=(
|
|
||||||
MediaSortOrder.MEDIA_ID.value,
|
|
||||||
MediaSortOrder.UPLOAD_NAME.value,
|
|
||||||
MediaSortOrder.CREATED_TS.value,
|
|
||||||
MediaSortOrder.LAST_ACCESS_TS.value,
|
|
||||||
MediaSortOrder.MEDIA_LENGTH.value,
|
|
||||||
MediaSortOrder.MEDIA_TYPE.value,
|
|
||||||
MediaSortOrder.QUARANTINED_BY.value,
|
|
||||||
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
direction = parse_string(
|
|
||||||
request, "dir", default="f", allowed_values=("f", "b")
|
|
||||||
)
|
|
||||||
|
|
||||||
media, total = await self.store.get_local_media_by_user_paginate(
|
|
||||||
start, limit, user_id, order_by, direction
|
|
||||||
)
|
|
||||||
|
|
||||||
ret = {"media": media, "total": total}
|
|
||||||
if (start + limit) < total:
|
|
||||||
ret["next_token"] = start + len(media)
|
|
||||||
|
|
||||||
return 200, ret
|
|
||||||
|
|
||||||
async def on_DELETE(
|
|
||||||
self, request: SynapseRequest, user_id: str
|
|
||||||
) -> Tuple[int, JsonDict]:
|
|
||||||
# This will always be set by the time Twisted calls us.
|
|
||||||
assert request.args is not None
|
|
||||||
|
|
||||||
await assert_requester_is_admin(self.auth, request)
|
|
||||||
|
|
||||||
if not self.is_mine(UserID.from_string(user_id)):
|
|
||||||
raise SynapseError(400, "Can only look up local users")
|
|
||||||
|
|
||||||
user = await self.store.get_user_by_id(user_id)
|
|
||||||
if user is None:
|
|
||||||
raise NotFoundError("Unknown user")
|
|
||||||
|
|
||||||
start = parse_integer(request, "from", default=0)
|
|
||||||
limit = parse_integer(request, "limit", default=100)
|
|
||||||
|
|
||||||
if start < 0:
|
|
||||||
raise SynapseError(
|
|
||||||
400,
|
|
||||||
"Query parameter from must be a string representing a positive integer.",
|
|
||||||
errcode=Codes.INVALID_PARAM,
|
|
||||||
)
|
|
||||||
|
|
||||||
if limit < 0:
|
|
||||||
raise SynapseError(
|
|
||||||
400,
|
|
||||||
"Query parameter limit must be a string representing a positive integer.",
|
|
||||||
errcode=Codes.INVALID_PARAM,
|
|
||||||
)
|
|
||||||
|
|
||||||
# If neither `order_by` nor `dir` is set, set the default order
|
|
||||||
# to newest media is on top for backward compatibility.
|
|
||||||
if b"order_by" not in request.args and b"dir" not in request.args:
|
|
||||||
order_by = MediaSortOrder.CREATED_TS.value
|
|
||||||
direction = "b"
|
|
||||||
else:
|
|
||||||
order_by = parse_string(
|
|
||||||
request,
|
|
||||||
"order_by",
|
|
||||||
default=MediaSortOrder.CREATED_TS.value,
|
|
||||||
allowed_values=(
|
|
||||||
MediaSortOrder.MEDIA_ID.value,
|
|
||||||
MediaSortOrder.UPLOAD_NAME.value,
|
|
||||||
MediaSortOrder.CREATED_TS.value,
|
|
||||||
MediaSortOrder.LAST_ACCESS_TS.value,
|
|
||||||
MediaSortOrder.MEDIA_LENGTH.value,
|
|
||||||
MediaSortOrder.MEDIA_TYPE.value,
|
|
||||||
MediaSortOrder.QUARANTINED_BY.value,
|
|
||||||
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
direction = parse_string(
|
|
||||||
request, "dir", default="f", allowed_values=("f", "b")
|
|
||||||
)
|
|
||||||
|
|
||||||
media, _ = await self.store.get_local_media_by_user_paginate(
|
|
||||||
start, limit, user_id, order_by, direction
|
|
||||||
)
|
|
||||||
|
|
||||||
deleted_media, total = await self.media_repository.delete_local_media_ids(
|
|
||||||
([row["media_id"] for row in media])
|
|
||||||
)
|
|
||||||
|
|
||||||
return 200, {"deleted_media": deleted_media, "total": total}
|
|
||||||
|
|
||||||
|
|
||||||
class UserTokenRestServlet(RestServlet):
|
class UserTokenRestServlet(RestServlet):
|
||||||
"""An admin API for logging in as a user.
|
"""An admin API for logging in as a user.
|
||||||
|
|
||||||
|
|
|
@ -1141,10 +1141,10 @@ def register_servlets(hs: "HomeServer", http_server, is_worker=False):
|
||||||
JoinedRoomsRestServlet(hs).register(http_server)
|
JoinedRoomsRestServlet(hs).register(http_server)
|
||||||
RoomAliasListServlet(hs).register(http_server)
|
RoomAliasListServlet(hs).register(http_server)
|
||||||
SearchRestServlet(hs).register(http_server)
|
SearchRestServlet(hs).register(http_server)
|
||||||
|
RoomCreateRestServlet(hs).register(http_server)
|
||||||
|
|
||||||
# Some servlets only get registered for the main process.
|
# Some servlets only get registered for the main process.
|
||||||
if not is_worker:
|
if not is_worker:
|
||||||
RoomCreateRestServlet(hs).register(http_server)
|
|
||||||
RoomForgetRestServlet(hs).register(http_server)
|
RoomForgetRestServlet(hs).register(http_server)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,40 @@ class RoomWorkerStore(SQLBaseStore):
|
||||||
|
|
||||||
self.config = hs.config
|
self.config = hs.config
|
||||||
|
|
||||||
|
async def store_room(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
room_creator_user_id: str,
|
||||||
|
is_public: bool,
|
||||||
|
room_version: RoomVersion,
|
||||||
|
):
|
||||||
|
"""Stores a room.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id: The desired room ID, can be None.
|
||||||
|
room_creator_user_id: The user ID of the room creator.
|
||||||
|
is_public: True to indicate that this room should appear in
|
||||||
|
public room lists.
|
||||||
|
room_version: The version of the room
|
||||||
|
Raises:
|
||||||
|
StoreError if the room could not be stored.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
await self.db_pool.simple_insert(
|
||||||
|
"rooms",
|
||||||
|
{
|
||||||
|
"room_id": room_id,
|
||||||
|
"creator": room_creator_user_id,
|
||||||
|
"is_public": is_public,
|
||||||
|
"room_version": room_version.identifier,
|
||||||
|
"has_auth_chain_index": True,
|
||||||
|
},
|
||||||
|
desc="store_room",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("store_room with room_id=%s failed: %s", room_id, e)
|
||||||
|
raise StoreError(500, "Problem creating room.")
|
||||||
|
|
||||||
async def get_room(self, room_id: str) -> dict:
|
async def get_room(self, room_id: str) -> dict:
|
||||||
"""Retrieve a room.
|
"""Retrieve a room.
|
||||||
|
|
||||||
|
@ -1342,40 +1376,6 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
|
||||||
lock=False,
|
lock=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def store_room(
|
|
||||||
self,
|
|
||||||
room_id: str,
|
|
||||||
room_creator_user_id: str,
|
|
||||||
is_public: bool,
|
|
||||||
room_version: RoomVersion,
|
|
||||||
):
|
|
||||||
"""Stores a room.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
room_id: The desired room ID, can be None.
|
|
||||||
room_creator_user_id: The user ID of the room creator.
|
|
||||||
is_public: True to indicate that this room should appear in
|
|
||||||
public room lists.
|
|
||||||
room_version: The version of the room
|
|
||||||
Raises:
|
|
||||||
StoreError if the room could not be stored.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
await self.db_pool.simple_insert(
|
|
||||||
"rooms",
|
|
||||||
{
|
|
||||||
"room_id": room_id,
|
|
||||||
"creator": room_creator_user_id,
|
|
||||||
"is_public": is_public,
|
|
||||||
"room_version": room_version.identifier,
|
|
||||||
"has_auth_chain_index": True,
|
|
||||||
},
|
|
||||||
desc="store_room",
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("store_room with room_id=%s failed: %s", room_id, e)
|
|
||||||
raise StoreError(500, "Problem creating room.")
|
|
||||||
|
|
||||||
async def maybe_store_room_on_outlier_membership(
|
async def maybe_store_room_on_outlier_membership(
|
||||||
self, room_id: str, room_version: RoomVersion
|
self, room_id: str, room_version: RoomVersion
|
||||||
):
|
):
|
||||||
|
|
Loading…
Reference in New Issue