[COOP-1050] Stream recent decisions from server
Context & Requests for Reviewers
The Recent Decisions CSV used to be built in the browser, which capped it at 10,000 rows. This moves the export to the server. The new GET /api/v1/moderation_activity/export route takes the same filters as the page, pages through the activity feed, and streams the CSV to the browser.
Some notes:
- There's no row cap
- A start date is now required, and the range is capped at 400 days
- The CSV columns haven't changed
Tests
- Server: route tests cover a missing session, multi-page streaming, filter passthrough, input that should get a 400, cross-site requests, and a failure mid-stream. Also unit tests for stream backpressure, CSV formatting, and paging in
iterateAll. - Client: tests for the export URL builder, the date-range check, and the Download button.
- Manual: on Recent Decisions, set filters and a date range, click Download, and check that the file matches the table. Then clear the start date and check that Download is disabled.
Checklist
Only check items that apply to this PR; leave the rest unchecked.
-
If you changed anything user-facing (i.e. user interface or APIs):
Did you update related docs? -
If the change is notable (refer to Keep a Changelog conventions):
Did you update CHANGELOG.md? -
If you changed
db/src/scripts/**and usedCREATE TABLE,ADD COLUMN, orALTER COLUMN:
Are as many columns markedNOT NULLas possible? If some columns can sometimes be null depending on other columns, are thereCHECKconstraints capturing those relationships, and are these also reflected using unions in the associated Kysely types? -
If you added a new signal in
server/services/signalsService/signals/**:
Did you classify every error case as a permanent error (SignalPermanentError, no retry) or a normal error (retryable)? Any case where the signal can't determine a score should be aSignalPermanentError.
Summary by cubic
Streams the Recent Decisions CSV export from a new GET /api/v1/moderation_activity/export route instead of building it in the browser, so downloads are no longer capped at 10,000 rows.
- The route applies the page's filters, pages the feed 100 rows at a time (each page stays below the store's row cap), and streams the CSV; the Download button now just links to it. A start date is required, the range is capped at 400 days, and the route requires the
VIEW_MRT_DATApermission, rejects requests started from outside Coop (Sec-Fetch-Site), and returns 400 for unknown decision types. The CSV columns are unchanged, and the Download button is disabled until a valid range is set.