Repository navigation
feat(content): ✨ notify scoped moderators about submissions awaiting approval - #907
aXenDeveloper wants to merge 1 commit into
Conversation
…approval Add an `approvalPending` preset to `buildContentNotifications`. When a submission opens, a durable intent is stored in the same transaction; the worker resolves moderators whose `can_approve` covers both the container the queue lists the submission under and the record's current container (own and role grants, subtrees, all resources, root roles, unrestricted entries), reading permissions fresh from the database in batches. - One event per submission (idempotency key), the submitter excluded - Access re-checked on delivery and inbox load (`"approval"` policy) - Decided, superseded and cancelled submissions clean up their items - Forum registers the preset for topics and comments - Docs for moderators and plugin authors; PostgreSQL regression tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CnAiFYAi5Vz6WxSaXS8Z9X
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found 1 new issue in 1 file · 1 warning · score 90 / 100 (Great) · 0 fixed · vs 1 warning
Reviewed by React Doctor for commit |
| if (locked?.status !== "pending") return 0; | ||
|
|
||
| for (const type of types) { | ||
| await c.get("notifications").publish({ |
There was a problem hiding this comment.
React Doctor · react-doctor/async-await-in-loop (warning)
This for…of loop waits before starting the next iteration. If iterations perform independent asynchronous work, consider bounded concurrency; await syntax alone does not establish a speedup.
Fix → Consider concurrent calls only for independent asynchronous work. Shared queues or synchronous work may not benefit. Preserve resource limits, transaction ordering, and failure/cancellation semantics; observe all callback promises.
Description
What?
Adds an
approvalPendingpreset tobuildContentNotifications. When a new record or an edit proposal enters the ModCP approval queue, the moderators who could decide it get one in-app notification. The notification links to the existing submission page at/modcp/approvals/:id. The Forum plugin registers the preset for topics and comments.can_approvefor that content type covers the submission. Grants come from the user's own entry or their roles, scoped to the container, its subtree or all resources. Root roles and unrestricted entries also count. Topic approval does not cover comments. The submitter is never notified.canModerateContentintohasContentModeratorPermission, so the queue, the decision and the notifications apply the same rule.check-staff-permission.tsread permissions straight from the database, not the cache, in three queries per 500 users. At most 1000 candidates are considered per submission, with a warning logged past that.openContentSubmission(c, tx, …)stores the intent in the same transaction that opens the submission. The worker publishes once per submission with the keyapproval:{contentTypeId}:{submissionId}. It publishes only while holding a share lock on the submission row and only if it is still pending."approval"access policy re-checks the pending status, the record and each recipient's permissions on delivery and on every inbox load. The text carries no title or body.approval-notifications.postgres.test.tswith 28 cases. It covers scopes, role plus own grant, permission removal, moves, retries, resubmission, rollback, races with a decision, cleanup, and the 404 after losing access.tests/postgres.tsonly treats apolishsearch configuration as present when it is visible in the test's own schema. Without this, two PostgreSQL test files running in parallel crashed.notifications-api.mdx,notifications.mdx,approvals.mdx,review-approvals.mdx(new "Get notified" section) andscoped-permissions.mdx.No migration is needed: the existing notification tables, queue and idempotency index are enough.
Known limitation, documented: a pending submission keeps the container it was opened in until a moderator decides it, so the queue keeps listing it there. Because of that, content moved before delivery only notifies moderators who cover both the old and the new container. Content moved after delivery leaves the item unavailable for the old container's moderators and does not notify the new container's moderators. The follow-up is to update the pending submission's container when its record moves and store a new intent with a key that includes the container.
Checks:
vitestunit, isolated and dom projects: 9,863 passed.tsc --noEmitandoxfmt --checkare clean on the changed files.oxlint --type-awareis clean on the core package. The forum file shows 3no-unsafe-argumenterrors that also appear on the base commit, because the core package isn't built in that environment.Why?
Moderators had no way to learn that content was waiting for them except by opening the ModCP queue. This tells exactly the moderators who can act on a submission, without revealing it to anyone outside their scope.
Generated by Claude Code