Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9a1c92f
Create a stream to track quarantine state changes
turt2live Mar 12, 2026
c3a0e4a
Insert into quarantined media stream upon changes
turt2live Mar 13, 2026
7386f04
Add admin API to access stream data
turt2live Mar 13, 2026
0f782ce
Add background update to insert existing rows
turt2live Mar 16, 2026
f1a35fa
changelog
turt2live Mar 16, 2026
780f13a
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Mar 16, 2026
a78c03c
fix out of bounds on max
turt2live Mar 16, 2026
7963593
Attempt to fix linting
turt2live Mar 16, 2026
d77a76d
bump for ci
turt2live Mar 16, 2026
2ef406e
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Mar 18, 2026
86a6a15
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Mar 30, 2026
62261e0
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Mar 31, 2026
d743711
Add comments
turt2live Mar 31, 2026
81ce22c
Use current token for stream, requiring writer configuration
turt2live Mar 31, 2026
282e670
Add extra safety
turt2live Mar 31, 2026
616e9c8
Split and expand tests
turt2live Mar 31, 2026
914e252
Attempt to fix linting
turt2live Mar 31, 2026
090e220
bump ci
turt2live Mar 31, 2026
cb71d46
Move schema deltas
turt2live Mar 31, 2026
07ec8f4
Fix tests
turt2live Mar 31, 2026
d288ef5
Apply changes from review comments
turt2live Mar 31, 2026
471b3dc
Attempt to fix linting
turt2live Mar 31, 2026
b5eafbc
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Mar 31, 2026
373ed83
Record quarantine changes in more sites
turt2live Apr 1, 2026
e5791a3
spelling
turt2live Apr 1, 2026
067f659
Apply suggestions from code review
turt2live Apr 1, 2026
99b4bf2
Attempt to fix linting
turt2live Apr 1, 2026
5a4ac32
bump ci
turt2live Apr 1, 2026
5eac826
Use `Token` type
turt2live Apr 1, 2026
b755aae
Add more token stuff
turt2live Apr 1, 2026
c157697
split tests, again
turt2live Apr 1, 2026
dbc3445
Use multi stream tokens to fix types?
turt2live Apr 1, 2026
6688723
Attempt to fix linting
turt2live Apr 1, 2026
237f0a6
Revert "Attempt to fix linting"
turt2live Apr 1, 2026
190bda8
Revert "Use multi stream tokens to fix types?"
turt2live Apr 1, 2026
ef63a72
Partial revert "Add more token stuff"
turt2live Apr 1, 2026
519512c
we do need this though
turt2live Apr 1, 2026
c36311a
Revert "we do need this though"
turt2live Apr 1, 2026
8d6d9c8
Revert "Add more token stuff"
turt2live Apr 1, 2026
2de3744
Move stream wait to servlet I guess
turt2live Apr 1, 2026
2325d9b
Remove excess change from lint fixing action
turt2live Apr 1, 2026
2558b61
API changes
turt2live Apr 2, 2026
7469b49
Change background update
turt2live Apr 2, 2026
8255d7b
Attempt to fix linting
turt2live Apr 2, 2026
455f749
*ahem*
turt2live Apr 2, 2026
cbc6ec3
1
turt2live Apr 2, 2026
79b7a8a
Back out changes which try to capture additional sites
turt2live Apr 6, 2026
6006bc1
Apply suggestions from code review
turt2live Apr 6, 2026
f4f7369
Merge remote-tracking branch 'origin/travis/list-quarantined-media-mk…
turt2live Apr 6, 2026
4a7f8fa
Fix from code review
turt2live Apr 6, 2026
bf589b7
More code review changes
turt2live Apr 6, 2026
61ed17f
Add background update test case
turt2live Apr 6, 2026
1a5d3b8
Attempt to fix linting
turt2live Apr 6, 2026
0851400
incorporate hidden suggestion
turt2live Apr 6, 2026
44ddb62
define store
turt2live Apr 6, 2026
3c1f0ea
remove unused var
turt2live Apr 6, 2026
da290fa
Attempt to fix linting
turt2live Apr 6, 2026
35ac9dc
bump ci
turt2live Apr 6, 2026
b4e3755
fix
turt2live Apr 6, 2026
f4bbc99
Attempt to fix linting
turt2live Apr 6, 2026
d17a8f2
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Apr 6, 2026
af05ff3
Different base I guess
turt2live Apr 6, 2026
8c34804
login
turt2live Apr 6, 2026
78beb96
Add sequence to portdb setup
turt2live Apr 8, 2026
53752b0
docs and test changes
turt2live Apr 8, 2026
0d086a4
Attempt to fix linting
turt2live Apr 8, 2026
22d8370
Merge branch 'develop' into travis/list-quarantined-media-mk2
turt2live Apr 8, 2026
1e94897
E_OFF_BY_1
turt2live Apr 8, 2026
9f03d60
Fix docs
turt2live Apr 8, 2026
045dfec
Apply suggestions from code review
turt2live Apr 8, 2026
af5dd7a
address code review again
turt2live Apr 8, 2026
c78e67f
Attempt to fix linting
turt2live Apr 8, 2026
5269e19
bump ci
turt2live Apr 8, 2026
62c533a
Update synapse/rest/admin/media.py
turt2live Apr 9, 2026
083c00f
Update tests/storage/test_room.py
turt2live Apr 9, 2026
51f9f0e
Give more friendly feedback to callers who try to get future data
turt2live Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/19558.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a ["Listing quarantined media changes" Admin API](https://element-hq.github.io/synapse/latest/admin_api/media_admin_api.html#listing-quarantined-media-changes) for retrieving a paginated record of when media became (un)quarantined.
Comment thread
turt2live marked this conversation as resolved.
Comment thread
MadLittleMods marked this conversation as resolved.
26 changes: 26 additions & 0 deletions docs/admin_api/media_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,32 @@ Response:
{}
```

## Listing quarantined media changes

When media is quarantined or unquarantined, a change record is created in the
database. This API returns those change records.
Comment thread
turt2live marked this conversation as resolved.
Outdated
Comment thread
turt2live marked this conversation as resolved.
Outdated

Request:

```
GET /_synapse/admin/v1/media/quarantine_changes?from=2
Comment thread
turt2live marked this conversation as resolved.
```

Where `from` is the `next_batch` value from a previous request. It is optional.

Response:

```json
{
"next_batch": 4,
"rows": [
{ "origin": "example.org", "media_id": "abcdefg12345...", "quarantined": true },
{ "origin": "example.org", "media_id": "abcdefg12345...", "quarantined": false },
{ "origin": "another.example.org", "media_id": "abcdefg12345...", "quarantined": true }
Comment thread
turt2live marked this conversation as resolved.
]
}
```

# Delete local media
This API deletes the *local* media from the disk of your own server.
This includes any local thumbnails and copies of media downloaded from
Expand Down
1 change: 1 addition & 0 deletions synapse/_scripts/synapse_port_db.py
Comment thread
turt2live marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
"users": ["shadow_banned", "approved", "locked", "suspended"],
"un_partial_stated_event_stream": ["rejection_status_changed"],
"users_who_share_rooms": ["share_private"],
"quarantined_media_changes": ["quarantined"],
}


Expand Down
3 changes: 3 additions & 0 deletions synapse/replication/tcp/streams/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
PresenceStream,
PushersStream,
PushRulesStream,
QuarantinedMediaStream,
ReceiptsStream,
StickyEventsStream,
Stream,
Expand Down Expand Up @@ -73,6 +74,7 @@
ThreadSubscriptionsStream,
UnPartialStatedRoomStream,
UnPartialStatedEventStream,
QuarantinedMediaStream,
)
}

Expand All @@ -96,4 +98,5 @@
"ThreadSubscriptionsStream",
"UnPartialStatedRoomStream",
"UnPartialStatedEventStream",
"QuarantinedMediaStream",
]
44 changes: 44 additions & 0 deletions synapse/replication/tcp/streams/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,3 +808,47 @@ async def _update_function(
return [], to_token, False

return rows, rows[-1][0], len(updates) == limit


@attr.s(slots=True, auto_attribs=True)
class QuarantinedMediaStreamRow:
"""Row for QuarantinedMediaStream"""

origin: str
media_id: str
Comment thread
turt2live marked this conversation as resolved.
quarantined: bool


class QuarantinedMediaStream(_StreamFromIdGen):
"""Stream to track changes to (un)quarantined media."""

NAME = "quarantined_media"
ROW_TYPE = QuarantinedMediaStreamRow

def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastores().main
super().__init__(
hs.get_instance_name(),
self._update_function,
self.store._quarantined_media_changes_id_gen,
)

async def _update_function(
self, instance_name: str, from_token: int, to_token: int, limit: int
Comment thread
turt2live marked this conversation as resolved.
Outdated
) -> StreamUpdateResult:
updates = await self.store.get_quarantined_media_changes(
from_id=from_token, to_id=to_token, limit=limit
)
rows = [
(
update.stream_id,
# Args to `QuarantinedMediaStreamRow`
(update.origin, update.media_id, update.quarantined),
)
for update in updates
]

if not rows:
return [], to_token, False

return rows, rows[-1][0], len(updates) == limit
41 changes: 41 additions & 0 deletions synapse/rest/admin/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,46 @@ async def on_POST(
return HTTPStatus.OK, {}


class ListQuarantineChanges(RestServlet):
"""Lists the quarantine changes to media."""

PATTERNS = admin_patterns("/media/quarantine_changes$")

def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastores().main
self.auth = hs.get_auth()
self.server_name = hs.hostname

async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]:
await assert_requester_is_admin(self.auth, request)

from_id = parse_integer(request, "from", default=0)
Comment thread
MadLittleMods marked this conversation as resolved.
Comment thread
turt2live marked this conversation as resolved.
limit = 100 # arbitrary; not enough to cause problems (hopefully)
Comment thread
MadLittleMods marked this conversation as resolved.
to_id = (
from_id + limit
) # somewhat implied, but makes our call to the store easier
Comment thread
turt2live marked this conversation as resolved.
Outdated

changes = await self.store.get_quarantined_media_changes(
from_id=from_id,
to_id=to_id,
limit=limit,
)

rows = [
{
"origin": c.origin if c.origin is not None else self.server_name,
"media_id": c.media_id,
"quarantined": c.quarantined,
}
for c in changes
]

# `from` is exclusive, so don't +1
next_batch = max(c.stream_id for c in changes) if changes else from_id
Comment thread
turt2live marked this conversation as resolved.
Outdated

return HTTPStatus.OK, {"next_batch": next_batch, "rows": rows}
Comment thread
turt2live marked this conversation as resolved.
Outdated


class ProtectMediaByID(RestServlet):
"""Protect local media from being quarantined."""

Expand Down Expand Up @@ -529,6 +569,7 @@ def register_servlets_for_media_repo(hs: "HomeServer", http_server: HttpServer)
QuarantineMediaByID(hs).register(http_server)
UnquarantineMediaByID(hs).register(http_server)
QuarantineMediaByUser(hs).register(http_server)
ListQuarantineChanges(hs).register(http_server)
ProtectMediaByID(hs).register(http_server)
UnprotectMediaByID(hs).register(http_server)
ListMediaInRoom(hs).register(http_server)
Expand Down
Loading
Loading