@atproto/ozone 0.1.172 → 0.1.174
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +40 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +31 -0
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/api/queue/assignModerator.d.ts +4 -0
- package/dist/api/queue/assignModerator.d.ts.map +1 -0
- package/dist/api/queue/assignModerator.js +28 -0
- package/dist/api/queue/assignModerator.js.map +1 -0
- package/dist/api/queue/createQueue.d.ts +4 -0
- package/dist/api/queue/createQueue.d.ts.map +1 -0
- package/dist/api/queue/createQueue.js +44 -0
- package/dist/api/queue/createQueue.js.map +1 -0
- package/dist/api/queue/deleteQueue.d.ts +4 -0
- package/dist/api/queue/deleteQueue.d.ts.map +1 -0
- package/dist/api/queue/deleteQueue.js +40 -0
- package/dist/api/queue/deleteQueue.js.map +1 -0
- package/dist/api/queue/getAssignments.d.ts +4 -0
- package/dist/api/queue/getAssignments.d.ts.map +1 -0
- package/dist/api/queue/getAssignments.js +19 -0
- package/dist/api/queue/getAssignments.js.map +1 -0
- package/dist/api/queue/listQueues.d.ts +4 -0
- package/dist/api/queue/listQueues.d.ts.map +1 -0
- package/dist/api/queue/listQueues.js +29 -0
- package/dist/api/queue/listQueues.js.map +1 -0
- package/dist/api/queue/routeReports.d.ts +4 -0
- package/dist/api/queue/routeReports.d.ts.map +1 -0
- package/dist/api/queue/routeReports.js +33 -0
- package/dist/api/queue/routeReports.js.map +1 -0
- package/dist/api/queue/unassignModerator.d.ts +4 -0
- package/dist/api/queue/unassignModerator.d.ts.map +1 -0
- package/dist/api/queue/unassignModerator.js +24 -0
- package/dist/api/queue/unassignModerator.js.map +1 -0
- package/dist/api/queue/updateQueue.d.ts +4 -0
- package/dist/api/queue/updateQueue.d.ts.map +1 -0
- package/dist/api/queue/updateQueue.js +39 -0
- package/dist/api/queue/updateQueue.js.map +1 -0
- package/dist/api/report/assignModerator.d.ts +4 -0
- package/dist/api/report/assignModerator.d.ts.map +1 -0
- package/dist/api/report/assignModerator.js +33 -0
- package/dist/api/report/assignModerator.js.map +1 -0
- package/dist/api/report/createActivity.d.ts +4 -0
- package/dist/api/report/createActivity.d.ts.map +1 -0
- package/dist/api/report/createActivity.js +44 -0
- package/dist/api/report/createActivity.js.map +1 -0
- package/dist/api/report/getAssignments.d.ts +4 -0
- package/dist/api/report/getAssignments.d.ts.map +1 -0
- package/dist/api/report/getAssignments.js +19 -0
- package/dist/api/report/getAssignments.js.map +1 -0
- package/dist/api/report/getHistoricalStats.d.ts +4 -0
- package/dist/api/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/api/report/getHistoricalStats.js +32 -0
- package/dist/api/report/getHistoricalStats.js.map +1 -0
- package/dist/api/report/getLatestReport.d.ts +4 -0
- package/dist/api/report/getLatestReport.d.ts.map +1 -0
- package/dist/api/report/getLatestReport.js +31 -0
- package/dist/api/report/getLatestReport.js.map +1 -0
- package/dist/api/report/getLiveStats.d.ts +4 -0
- package/dist/api/report/getLiveStats.d.ts.map +1 -0
- package/dist/api/report/getLiveStats.js +25 -0
- package/dist/api/report/getLiveStats.js.map +1 -0
- package/dist/api/report/getReport.d.ts +4 -0
- package/dist/api/report/getReport.d.ts.map +1 -0
- package/dist/api/report/getReport.js +35 -0
- package/dist/api/report/getReport.js.map +1 -0
- package/dist/api/report/listActivities.d.ts +4 -0
- package/dist/api/report/listActivities.d.ts.map +1 -0
- package/dist/api/report/listActivities.js +25 -0
- package/dist/api/report/listActivities.js.map +1 -0
- package/dist/api/report/queryReports.d.ts +4 -0
- package/dist/api/report/queryReports.d.ts.map +1 -0
- package/dist/api/report/queryReports.js +29 -0
- package/dist/api/report/queryReports.js.map +1 -0
- package/dist/api/report/reassignQueue.d.ts +4 -0
- package/dist/api/report/reassignQueue.d.ts.map +1 -0
- package/dist/api/report/reassignQueue.js +45 -0
- package/dist/api/report/reassignQueue.js.map +1 -0
- package/dist/api/report/refreshStats.d.ts +4 -0
- package/dist/api/report/refreshStats.d.ts.map +1 -0
- package/dist/api/report/refreshStats.js +26 -0
- package/dist/api/report/refreshStats.js.map +1 -0
- package/dist/api/report/unassignModerator.d.ts +4 -0
- package/dist/api/report/unassignModerator.d.ts.map +1 -0
- package/dist/api/report/unassignModerator.js +21 -0
- package/dist/api/report/unassignModerator.js.map +1 -0
- package/dist/api/util.d.ts +2 -0
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +9 -1
- package/dist/api/util.js.map +1 -1
- package/dist/assignment/index.d.ts +89 -0
- package/dist/assignment/index.d.ts.map +1 -0
- package/dist/assignment/index.js +537 -0
- package/dist/assignment/index.js.map +1 -0
- package/dist/config/config.d.ts +14 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +9 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +3 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +3 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +31 -10
- package/dist/context.js.map +1 -1
- package/dist/daemon/context.d.ts +6 -0
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js +28 -4
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/event-reverser.d.ts +1 -0
- package/dist/daemon/event-reverser.d.ts.map +1 -1
- package/dist/daemon/event-reverser.js +42 -1
- package/dist/daemon/event-reverser.js.map +1 -1
- package/dist/daemon/job-cursor.d.ts +5 -0
- package/dist/daemon/job-cursor.d.ts.map +1 -0
- package/dist/daemon/job-cursor.js +28 -0
- package/dist/daemon/job-cursor.js.map +1 -0
- package/dist/daemon/queue-router.d.ts +17 -0
- package/dist/daemon/queue-router.d.ts.map +1 -0
- package/dist/daemon/queue-router.js +114 -0
- package/dist/daemon/queue-router.js.map +1 -0
- package/dist/daemon/stats-computer.d.ts +51 -0
- package/dist/daemon/stats-computer.d.ts.map +1 -0
- package/dist/daemon/stats-computer.js +117 -0
- package/dist/daemon/stats-computer.js.map +1 -0
- package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
- package/dist/daemon/strike-expiry-processor.js +4 -19
- package/dist/daemon/strike-expiry-processor.js.map +1 -1
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts +4 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts.map +1 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js +126 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts +4 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js +35 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts +4 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js +36 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts +4 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js +39 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts +4 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js +34 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js.map +1 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts +4 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts.map +1 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js +32 -0
- package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js.map +1 -0
- package/dist/db/migrations/index.d.ts +6 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +7 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/pagination.d.ts +31 -0
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js +74 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/expiring_tag.d.ts +15 -0
- package/dist/db/schema/expiring_tag.d.ts.map +1 -0
- package/dist/db/schema/expiring_tag.js +5 -0
- package/dist/db/schema/expiring_tag.js.map +1 -0
- package/dist/db/schema/index.d.ts +7 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/index.js.map +1 -1
- package/dist/db/schema/moderator_assignment.d.ts +14 -0
- package/dist/db/schema/moderator_assignment.d.ts.map +1 -0
- package/dist/db/schema/moderator_assignment.js +5 -0
- package/dist/db/schema/moderator_assignment.js.map +1 -0
- package/dist/db/schema/report.d.ts +25 -0
- package/dist/db/schema/report.d.ts.map +1 -0
- package/dist/db/schema/report.js +5 -0
- package/dist/db/schema/report.js.map +1 -0
- package/dist/db/schema/report_activity.d.ts +18 -0
- package/dist/db/schema/report_activity.d.ts.map +1 -0
- package/dist/db/schema/report_activity.js +5 -0
- package/dist/db/schema/report_activity.js.map +1 -0
- package/dist/db/schema/report_queue.d.ts +19 -0
- package/dist/db/schema/report_queue.d.ts.map +1 -0
- package/dist/db/schema/report_queue.js +5 -0
- package/dist/db/schema/report_queue.js.map +1 -0
- package/dist/db/schema/report_stat.d.ts +20 -0
- package/dist/db/schema/report_stat.d.ts.map +1 -0
- package/dist/db/schema/report_stat.js +5 -0
- package/dist/db/schema/report_stat.js.map +1 -0
- package/dist/lexicon/index.d.ts +50 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +120 -2
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +11255 -7885
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +1900 -120
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/embed/external.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts +8 -2
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.js +9 -0
- package/dist/lexicon/types/chat/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +37 -10
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.js +9 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts +3 -0
- package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getMessages.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +2 -0
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +19 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js +9 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts +35 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts +62 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js +34 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts +29 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts +18 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts +37 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts +185 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/defs.js +108 -0
- package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts +36 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts +21 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts +22 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts +26 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts +48 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts +28 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts +25 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js.map +1 -0
- package/dist/mod-service/expiring-tags.d.ts +27 -0
- package/dist/mod-service/expiring-tags.d.ts.map +1 -0
- package/dist/mod-service/expiring-tags.js +62 -0
- package/dist/mod-service/expiring-tags.js.map +1 -0
- package/dist/mod-service/index.d.ts +3 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +61 -2
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/report.d.ts +64 -0
- package/dist/mod-service/report.d.ts.map +1 -0
- package/dist/mod-service/report.js +282 -0
- package/dist/mod-service/report.js.map +1 -0
- package/dist/mod-service/status.d.ts +24 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/queue/service.d.ts +86 -0
- package/dist/queue/service.d.ts.map +1 -0
- package/dist/queue/service.js +430 -0
- package/dist/queue/service.js.map +1 -0
- package/dist/report/activity.d.ts +77 -0
- package/dist/report/activity.d.ts.map +1 -0
- package/dist/report/activity.js +141 -0
- package/dist/report/activity.js.map +1 -0
- package/dist/report/handle-report-update.d.ts +47 -0
- package/dist/report/handle-report-update.d.ts.map +1 -0
- package/dist/report/handle-report-update.js +178 -0
- package/dist/report/handle-report-update.js.map +1 -0
- package/dist/report/reassign.d.ts +10 -0
- package/dist/report/reassign.d.ts.map +1 -0
- package/dist/report/reassign.js +75 -0
- package/dist/report/reassign.js.map +1 -0
- package/dist/report/stats.d.ts +105 -0
- package/dist/report/stats.d.ts.map +1 -0
- package/dist/report/stats.js +619 -0
- package/dist/report/stats.js.map +1 -0
- package/dist/report/views.d.ts +111 -0
- package/dist/report/views.d.ts.map +1 -0
- package/dist/report/views.js +156 -0
- package/dist/report/views.js.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +11 -0
- package/dist/team/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/index.ts +40 -0
- package/src/api/moderation/emitEvent.ts +38 -0
- package/src/api/queue/assignModerator.ts +31 -0
- package/src/api/queue/createQueue.ts +62 -0
- package/src/api/queue/deleteQueue.ts +56 -0
- package/src/api/queue/getAssignments.ts +19 -0
- package/src/api/queue/listQueues.ts +39 -0
- package/src/api/queue/routeReports.ts +44 -0
- package/src/api/queue/unassignModerator.ts +26 -0
- package/src/api/queue/updateQueue.ts +54 -0
- package/src/api/report/assignModerator.ts +36 -0
- package/src/api/report/createActivity.ts +57 -0
- package/src/api/report/getAssignments.ts +20 -0
- package/src/api/report/getHistoricalStats.ts +41 -0
- package/src/api/report/getLatestReport.ts +44 -0
- package/src/api/report/getLiveStats.ts +26 -0
- package/src/api/report/getReport.ts +55 -0
- package/src/api/report/listActivities.ts +34 -0
- package/src/api/report/queryReports.ts +44 -0
- package/src/api/report/reassignQueue.ts +68 -0
- package/src/api/report/refreshStats.ts +27 -0
- package/src/api/report/unassignModerator.ts +21 -0
- package/src/api/util.ts +12 -0
- package/src/assignment/index.ts +731 -0
- package/src/config/config.ts +27 -0
- package/src/config/env.ts +8 -0
- package/src/context.ts +31 -0
- package/src/daemon/context.ts +34 -0
- package/src/daemon/event-reverser.ts +50 -1
- package/src/daemon/job-cursor.ts +33 -0
- package/src/daemon/queue-router.ts +101 -0
- package/src/daemon/stats-computer.ts +101 -0
- package/src/daemon/strike-expiry-processor.ts +4 -20
- package/src/db/migrations/20260219T164523000Z-create-report-table.ts +155 -0
- package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +42 -0
- package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +41 -0
- package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +48 -0
- package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +35 -0
- package/src/db/migrations/20260428T000000000Z-add-expiring-tag-table.ts +32 -0
- package/src/db/migrations/index.ts +6 -0
- package/src/db/pagination.ts +85 -0
- package/src/db/schema/expiring_tag.ts +17 -0
- package/src/db/schema/index.ts +13 -1
- package/src/db/schema/moderator_assignment.ts +16 -0
- package/src/db/schema/report.ts +27 -0
- package/src/db/schema/report_activity.ts +22 -0
- package/src/db/schema/report_queue.ts +21 -0
- package/src/db/schema/report_stat.ts +27 -0
- package/src/lexicon/index.ts +280 -0
- package/src/lexicon/lexicons.ts +2083 -214
- package/src/lexicon/types/app/bsky/embed/external.ts +2 -0
- package/src/lexicon/types/chat/bsky/actor/defs.ts +17 -1
- package/src/lexicon/types/chat/bsky/convo/defs.ts +50 -10
- package/src/lexicon/types/chat/bsky/convo/getMessages.ts +3 -0
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +24 -0
- package/src/lexicon/types/tools/ozone/queue/assignModerator.ts +46 -0
- package/src/lexicon/types/tools/ozone/queue/createQueue.ts +54 -0
- package/src/lexicon/types/tools/ozone/queue/defs.ts +99 -0
- package/src/lexicon/types/tools/ozone/queue/deleteQueue.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/listQueues.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/routeReports.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/unassignModerator.ts +37 -0
- package/src/lexicon/types/tools/ozone/queue/updateQueue.ts +51 -0
- package/src/lexicon/types/tools/ozone/report/assignModerator.ts +50 -0
- package/src/lexicon/types/tools/ozone/report/createActivity.ts +60 -0
- package/src/lexicon/types/tools/ozone/report/defs.ts +327 -0
- package/src/lexicon/types/tools/ozone/report/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/report/getHistoricalStats.ts +54 -0
- package/src/lexicon/types/tools/ozone/report/getLatestReport.ts +39 -0
- package/src/lexicon/types/tools/ozone/report/getLiveStats.ts +45 -0
- package/src/lexicon/types/tools/ozone/report/getReport.ts +38 -0
- package/src/lexicon/types/tools/ozone/report/listActivities.ts +44 -0
- package/src/lexicon/types/tools/ozone/report/queryReports.ts +72 -0
- package/src/lexicon/types/tools/ozone/report/reassignQueue.ts +55 -0
- package/src/lexicon/types/tools/ozone/report/refreshStats.ts +46 -0
- package/src/lexicon/types/tools/ozone/report/unassignModerator.ts +44 -0
- package/src/mod-service/expiring-tags.ts +98 -0
- package/src/mod-service/index.ts +71 -3
- package/src/mod-service/report.ts +408 -0
- package/src/queue/service.ts +599 -0
- package/src/report/activity.ts +234 -0
- package/src/report/handle-report-update.ts +209 -0
- package/src/report/reassign.ts +109 -0
- package/src/report/stats.ts +850 -0
- package/src/report/views.ts +241 -0
- package/src/team/index.ts +11 -0
- package/tests/expiring-tags.test.ts +231 -0
- package/tests/get-report.test.ts +136 -0
- package/tests/query-reports.test.ts +608 -0
- package/tests/queue-assignment.test.ts +428 -0
- package/tests/queue-router.test.ts +306 -0
- package/tests/queues.test.ts +690 -0
- package/tests/report-action.test.ts +308 -0
- package/tests/report-activity.test.ts +567 -0
- package/tests/report-assignment.test.ts +517 -0
- package/tests/report-reassign-queue.test.ts +340 -0
- package/tests/report-routing.test.ts +245 -0
- package/tests/report-stats.test.ts +545 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EventReverser = void 0;
|
|
4
4
|
const common_1 = require("@atproto/common");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
+
const expiring_tags_1 = require("../mod-service/expiring-tags");
|
|
7
|
+
const subject_1 = require("../mod-service/subject");
|
|
6
8
|
class EventReverser {
|
|
7
9
|
constructor(db, modService) {
|
|
8
10
|
Object.defineProperty(this, "db", {
|
|
@@ -76,7 +78,46 @@ class EventReverser {
|
|
|
76
78
|
const subjectsDueForReversal = await moderationService.getSubjectsDueForReversal();
|
|
77
79
|
// We shouldn't have too many actions due for reversal at any given time, so running in parallel is probably fine
|
|
78
80
|
// Internally, each reversal runs within its own transaction
|
|
79
|
-
await Promise.all(
|
|
81
|
+
await Promise.all([
|
|
82
|
+
...subjectsDueForReversal.map(this.revertState.bind(this)),
|
|
83
|
+
this.findAndRevertExpiredTags(),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
async findAndRevertExpiredTags() {
|
|
87
|
+
const groups = await (0, expiring_tags_1.getExpiredTags)(this.db);
|
|
88
|
+
if (!groups.length)
|
|
89
|
+
return;
|
|
90
|
+
for (const group of groups) {
|
|
91
|
+
await this.db.transaction(async (dbTxn) => {
|
|
92
|
+
// Check which tags are still present on the subject
|
|
93
|
+
const status = await dbTxn.db
|
|
94
|
+
.selectFrom('moderation_subject_status')
|
|
95
|
+
.where('did', '=', group.did)
|
|
96
|
+
.where('recordPath', '=', group.recordPath)
|
|
97
|
+
.selectAll()
|
|
98
|
+
.executeTakeFirst();
|
|
99
|
+
const currentTags = status?.tags ?? [];
|
|
100
|
+
const tagsToRemove = group.tags.filter((t) => currentTags.includes(t));
|
|
101
|
+
// Delete the expiring_tag rows regardless
|
|
102
|
+
await (0, expiring_tags_1.deleteExpiringTagsByIds)(dbTxn, group.ids);
|
|
103
|
+
// Only emit removal event if there are tags still present to remove
|
|
104
|
+
if (tagsToRemove.length > 0 && status) {
|
|
105
|
+
const subject = (0, subject_1.subjectFromStatusRow)(status);
|
|
106
|
+
const moderationTxn = this.modService(dbTxn);
|
|
107
|
+
await moderationTxn.logEvent({
|
|
108
|
+
event: {
|
|
109
|
+
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
110
|
+
add: [],
|
|
111
|
+
remove: tagsToRemove,
|
|
112
|
+
comment: '[SCHEDULED_REVERSAL] Reverting temporary tags as originally scheduled',
|
|
113
|
+
},
|
|
114
|
+
createdBy: group.createdBy,
|
|
115
|
+
subject,
|
|
116
|
+
createdAt: new Date(),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
80
121
|
}
|
|
81
122
|
}
|
|
82
123
|
exports.EventReverser = EventReverser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-reverser.js","sourceRoot":"","sources":["../../src/daemon/event-reverser.ts"],"names":[],"mappings":";;;AAAA,4CAAwC;AAExC,sCAAoC;
|
|
1
|
+
{"version":3,"file":"event-reverser.js","sourceRoot":"","sources":["../../src/daemon/event-reverser.ts"],"names":[],"mappings":"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","sourcesContent":["import { MINUTE } from '@atproto/common'\nimport { Database } from '../db'\nimport { dbLogger } from '../logger'\nimport { ModerationServiceCreator, ReversalSubject } from '../mod-service'\nimport {\n deleteExpiringTagsByIds,\n getExpiredTags,\n} from '../mod-service/expiring-tags'\nimport { subjectFromStatusRow } from '../mod-service/subject'\n\nexport class EventReverser {\n destroyed = false\n reversalPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private modService: ModerationServiceCreator,\n ) {}\n\n start() {\n this.poll()\n }\n\n poll() {\n if (this.destroyed) return\n this.reversalPromise = this.findAndRevertDueActions()\n .catch((err) =>\n dbLogger.error({ err }, 'moderation action reversal errored'),\n )\n .finally(() => {\n this.timer = setTimeout(() => this.poll(), getInterval())\n })\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.reversalPromise\n }\n\n async revertState(subject: ReversalSubject) {\n await this.db.transaction(async (dbTxn) => {\n const moderationTxn = this.modService(dbTxn)\n const originalEvent =\n await moderationTxn.getLastReversibleEventForSubject(subject)\n if (originalEvent) {\n await moderationTxn.revertState({\n action: originalEvent.action,\n createdBy: originalEvent.createdBy,\n comment:\n '[SCHEDULED_REVERSAL] Reverting action as originally scheduled',\n subject: subject.subject,\n createdAt: new Date(),\n })\n }\n })\n }\n\n async findAndRevertDueActions() {\n const moderationService = this.modService(this.db)\n const subjectsDueForReversal =\n await moderationService.getSubjectsDueForReversal()\n\n // We shouldn't have too many actions due for reversal at any given time, so running in parallel is probably fine\n // Internally, each reversal runs within its own transaction\n await Promise.all([\n ...subjectsDueForReversal.map(this.revertState.bind(this)),\n this.findAndRevertExpiredTags(),\n ])\n }\n\n async findAndRevertExpiredTags() {\n const groups = await getExpiredTags(this.db)\n if (!groups.length) return\n\n for (const group of groups) {\n await this.db.transaction(async (dbTxn) => {\n // Check which tags are still present on the subject\n const status = await dbTxn.db\n .selectFrom('moderation_subject_status')\n .where('did', '=', group.did)\n .where('recordPath', '=', group.recordPath)\n .selectAll()\n .executeTakeFirst()\n\n const currentTags: string[] = status?.tags ?? []\n const tagsToRemove = group.tags.filter((t) => currentTags.includes(t))\n\n // Delete the expiring_tag rows regardless\n await deleteExpiringTagsByIds(dbTxn, group.ids)\n\n // Only emit removal event if there are tags still present to remove\n if (tagsToRemove.length > 0 && status) {\n const subject = subjectFromStatusRow(status)\n const moderationTxn = this.modService(dbTxn)\n await moderationTxn.logEvent({\n event: {\n $type: 'tools.ozone.moderation.defs#modEventTag',\n add: [],\n remove: tagsToRemove,\n comment:\n '[SCHEDULED_REVERSAL] Reverting temporary tags as originally scheduled',\n },\n createdBy: group.createdBy,\n subject,\n createdAt: new Date(),\n })\n }\n })\n }\n }\n}\n\nconst getInterval = (): number => {\n // super basic synchronization by agreeing when the intervals land relative to unix timestamp\n const now = Date.now()\n const intervalMs = MINUTE\n const nextIteration = Math.ceil(now / intervalMs)\n return nextIteration * intervalMs - now\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Database } from '../db';
|
|
2
|
+
export declare function initJobCursor(db: Database, job: string): Promise<void>;
|
|
3
|
+
export declare function getJobCursor(db: Database, job: string): Promise<string | null>;
|
|
4
|
+
export declare function updateJobCursor(db: Database, job: string, cursor: string): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=job-cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-cursor.d.ts","sourceRoot":"","sources":["../../src/daemon/job-cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,wBAAsB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM5E;AAED,wBAAsB,YAAY,CAChC,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initJobCursor = initJobCursor;
|
|
4
|
+
exports.getJobCursor = getJobCursor;
|
|
5
|
+
exports.updateJobCursor = updateJobCursor;
|
|
6
|
+
async function initJobCursor(db, job) {
|
|
7
|
+
await db.db
|
|
8
|
+
.insertInto('job_cursor')
|
|
9
|
+
.values({ job, cursor: null })
|
|
10
|
+
.onConflict((oc) => oc.doNothing())
|
|
11
|
+
.execute();
|
|
12
|
+
}
|
|
13
|
+
async function getJobCursor(db, job) {
|
|
14
|
+
const entry = await db.db
|
|
15
|
+
.selectFrom('job_cursor')
|
|
16
|
+
.select('cursor')
|
|
17
|
+
.where('job', '=', job)
|
|
18
|
+
.executeTakeFirst();
|
|
19
|
+
return entry?.cursor ?? null;
|
|
20
|
+
}
|
|
21
|
+
async function updateJobCursor(db, job, cursor) {
|
|
22
|
+
await db.db
|
|
23
|
+
.updateTable('job_cursor')
|
|
24
|
+
.set({ cursor })
|
|
25
|
+
.where('job', '=', job)
|
|
26
|
+
.execute();
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=job-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-cursor.js","sourceRoot":"","sources":["../../src/daemon/job-cursor.ts"],"names":[],"mappings":";;AAEA,sCAMC;AAED,oCAUC;AAED,0CAUC;AA9BM,KAAK,UAAU,aAAa,CAAC,EAAY,EAAE,GAAW;IAC3D,MAAM,EAAE,CAAC,EAAE;SACR,UAAU,CAAC,YAAY,CAAC;SACxB,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SAC7B,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC;SAClC,OAAO,EAAE,CAAA;AACd,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,EAAY,EACZ,GAAW;IAEX,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,EAAE;SACtB,UAAU,CAAC,YAAY,CAAC;SACxB,MAAM,CAAC,QAAQ,CAAC;SAChB,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC;SACtB,gBAAgB,EAAE,CAAA;IACrB,OAAO,KAAK,EAAE,MAAM,IAAI,IAAI,CAAA;AAC9B,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,EAAY,EACZ,GAAW,EACX,MAAc;IAEd,MAAM,EAAE,CAAC,EAAE;SACR,WAAW,CAAC,YAAY,CAAC;SACzB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;SACf,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC;SACtB,OAAO,EAAE,CAAA;AACd,CAAC","sourcesContent":["import { Database } from '../db'\n\nexport async function initJobCursor(db: Database, job: string): Promise<void> {\n await db.db\n .insertInto('job_cursor')\n .values({ job, cursor: null })\n .onConflict((oc) => oc.doNothing())\n .execute()\n}\n\nexport async function getJobCursor(\n db: Database,\n job: string,\n): Promise<string | null> {\n const entry = await db.db\n .selectFrom('job_cursor')\n .select('cursor')\n .where('job', '=', job)\n .executeTakeFirst()\n return entry?.cursor ?? null\n}\n\nexport async function updateJobCursor(\n db: Database,\n job: string,\n cursor: string,\n): Promise<void> {\n await db.db\n .updateTable('job_cursor')\n .set({ cursor })\n .where('job', '=', job)\n .execute()\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Database } from '../db';
|
|
2
|
+
import { QueueServiceCreator } from '../queue/service';
|
|
3
|
+
export declare class QueueRouter {
|
|
4
|
+
private db;
|
|
5
|
+
private queueServiceCreator;
|
|
6
|
+
destroyed: boolean;
|
|
7
|
+
processingPromise: Promise<void>;
|
|
8
|
+
timer?: NodeJS.Timeout;
|
|
9
|
+
constructor(db: Database, queueServiceCreator: QueueServiceCreator);
|
|
10
|
+
start(): void;
|
|
11
|
+
poll(): void;
|
|
12
|
+
destroy(): Promise<void>;
|
|
13
|
+
initializeCursor(): Promise<void>;
|
|
14
|
+
getCursor(): Promise<number | null>;
|
|
15
|
+
routeReports(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=queue-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-router.d.ts","sourceRoot":"","sources":["../../src/daemon/queue-router.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAMtD,qBAAa,WAAW;IAMpB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,mBAAmB;IAN7B,SAAS,UAAQ;IACjB,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAoB;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGZ,EAAE,EAAE,QAAQ,EACZ,mBAAmB,EAAE,mBAAmB;IAGlD,KAAK;IAIL,IAAI;IASE,OAAO;IASP,gBAAgB;IAIhB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASnC,YAAY;CA2CnB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueueRouter = void 0;
|
|
4
|
+
const common_1 = require("@atproto/common");
|
|
5
|
+
const logger_1 = require("../logger");
|
|
6
|
+
const job_cursor_1 = require("./job-cursor");
|
|
7
|
+
const JOB_NAME = 'queue_router';
|
|
8
|
+
const BATCH_SIZE = 100;
|
|
9
|
+
class QueueRouter {
|
|
10
|
+
constructor(db, queueServiceCreator) {
|
|
11
|
+
Object.defineProperty(this, "db", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: db
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "queueServiceCreator", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: queueServiceCreator
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "destroyed", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: false
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "processingPromise", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: Promise.resolve()
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "timer", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: void 0
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
start() {
|
|
43
|
+
this.initializeCursor().then(() => this.poll());
|
|
44
|
+
}
|
|
45
|
+
poll() {
|
|
46
|
+
if (this.destroyed)
|
|
47
|
+
return;
|
|
48
|
+
this.processingPromise = this.routeReports()
|
|
49
|
+
.catch((err) => logger_1.dbLogger.error({ err }, 'queue routing errored'))
|
|
50
|
+
.finally(() => {
|
|
51
|
+
this.timer = setTimeout(() => this.poll(), getInterval());
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async destroy() {
|
|
55
|
+
this.destroyed = true;
|
|
56
|
+
if (this.timer) {
|
|
57
|
+
clearTimeout(this.timer);
|
|
58
|
+
this.timer = undefined;
|
|
59
|
+
}
|
|
60
|
+
await this.processingPromise;
|
|
61
|
+
}
|
|
62
|
+
async initializeCursor() {
|
|
63
|
+
await (0, job_cursor_1.initJobCursor)(this.db, JOB_NAME);
|
|
64
|
+
}
|
|
65
|
+
async getCursor() {
|
|
66
|
+
const row = await this.db.db
|
|
67
|
+
.selectFrom('job_cursor')
|
|
68
|
+
.select('cursor')
|
|
69
|
+
.where('job', '=', JOB_NAME)
|
|
70
|
+
.executeTakeFirst();
|
|
71
|
+
return row?.cursor ? parseInt(row.cursor, 10) : null;
|
|
72
|
+
}
|
|
73
|
+
async routeReports() {
|
|
74
|
+
await this.db.transaction(async (txn) => {
|
|
75
|
+
// Acquire row lock on the job_cursor row. A second daemon instance
|
|
76
|
+
// hitting this same query blocks here until the first transaction
|
|
77
|
+
// commits, then reads the now-advanced cursor and processes the next
|
|
78
|
+
// range. The lock is held for the whole batch (~50–200ms).
|
|
79
|
+
const row = await txn.db
|
|
80
|
+
.selectFrom('job_cursor')
|
|
81
|
+
.selectAll()
|
|
82
|
+
.where('job', '=', JOB_NAME)
|
|
83
|
+
.forUpdate()
|
|
84
|
+
.executeTakeFirst();
|
|
85
|
+
if (!row)
|
|
86
|
+
return;
|
|
87
|
+
const cursor = row.cursor ? parseInt(row.cursor, 10) : null;
|
|
88
|
+
const queueService = this.queueServiceCreator(txn);
|
|
89
|
+
const result = await queueService.insertReportsFromEvents({
|
|
90
|
+
cursor,
|
|
91
|
+
limit: BATCH_SIZE,
|
|
92
|
+
});
|
|
93
|
+
if (result.processed === 0) {
|
|
94
|
+
logger_1.dbLogger.info('no new report events to route');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
await txn.db
|
|
98
|
+
.updateTable('job_cursor')
|
|
99
|
+
.set({ cursor: String(result.maxEventId) })
|
|
100
|
+
.where('job', '=', JOB_NAME)
|
|
101
|
+
.execute();
|
|
102
|
+
logger_1.dbLogger.info({
|
|
103
|
+
processed: result.processed,
|
|
104
|
+
assigned: result.assigned,
|
|
105
|
+
unmatched: result.unmatched,
|
|
106
|
+
maxEventId: result.maxEventId,
|
|
107
|
+
}, 'queue routing completed');
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.QueueRouter = QueueRouter;
|
|
112
|
+
// Poll every 1 minute
|
|
113
|
+
const getInterval = () => 1 * common_1.MINUTE;
|
|
114
|
+
//# sourceMappingURL=queue-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-router.js","sourceRoot":"","sources":["../../src/daemon/queue-router.ts"],"names":[],"mappings":"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","sourcesContent":["import { MINUTE } from '@atproto/common'\nimport { Database } from '../db'\nimport { dbLogger } from '../logger'\nimport { QueueServiceCreator } from '../queue/service'\nimport { initJobCursor } from './job-cursor'\n\nconst JOB_NAME = 'queue_router'\nconst BATCH_SIZE = 100\n\nexport class QueueRouter {\n destroyed = false\n processingPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private queueServiceCreator: QueueServiceCreator,\n ) {}\n\n start() {\n this.initializeCursor().then(() => this.poll())\n }\n\n poll() {\n if (this.destroyed) return\n this.processingPromise = this.routeReports()\n .catch((err) => dbLogger.error({ err }, 'queue routing errored'))\n .finally(() => {\n this.timer = setTimeout(() => this.poll(), getInterval())\n })\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.processingPromise\n }\n\n async initializeCursor() {\n await initJobCursor(this.db, JOB_NAME)\n }\n\n async getCursor(): Promise<number | null> {\n const row = await this.db.db\n .selectFrom('job_cursor')\n .select('cursor')\n .where('job', '=', JOB_NAME)\n .executeTakeFirst()\n return row?.cursor ? parseInt(row.cursor, 10) : null\n }\n\n async routeReports() {\n await this.db.transaction(async (txn) => {\n // Acquire row lock on the job_cursor row. A second daemon instance\n // hitting this same query blocks here until the first transaction\n // commits, then reads the now-advanced cursor and processes the next\n // range. The lock is held for the whole batch (~50–200ms).\n const row = await txn.db\n .selectFrom('job_cursor')\n .selectAll()\n .where('job', '=', JOB_NAME)\n .forUpdate()\n .executeTakeFirst()\n if (!row) return\n const cursor = row.cursor ? parseInt(row.cursor, 10) : null\n\n const queueService = this.queueServiceCreator(txn)\n const result = await queueService.insertReportsFromEvents({\n cursor,\n limit: BATCH_SIZE,\n })\n\n if (result.processed === 0) {\n dbLogger.info('no new report events to route')\n return\n }\n\n await txn.db\n .updateTable('job_cursor')\n .set({ cursor: String(result.maxEventId) })\n .where('job', '=', JOB_NAME)\n .execute()\n\n dbLogger.info(\n {\n processed: result.processed,\n assigned: result.assigned,\n unmatched: result.unmatched,\n maxEventId: result.maxEventId,\n },\n 'queue routing completed',\n )\n })\n }\n}\n\n// Poll every 1 minute\nconst getInterval = (): number => 1 * MINUTE\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Database } from '../db';
|
|
2
|
+
import { ReportStatsServiceCreator } from '../report/stats';
|
|
3
|
+
/**
|
|
4
|
+
* Background daemon that materializes report statistics on an interval (default is 15 minutes).
|
|
5
|
+
*
|
|
6
|
+
* Each cycle computes calendar-day snapshots: today's stats are recomputed (in-progress day),
|
|
7
|
+
* and yesterday's snapshot is finalized if it wasn't already. Historical snapshots (completed
|
|
8
|
+
* days) are write-once and never recomputed unless explicitly refreshed via the API.
|
|
9
|
+
*
|
|
10
|
+
* Query profile per cycle (assuming ~10K reports/day, 10 queues, 20 moderators, 9 type groups):
|
|
11
|
+
* - 7 batched GROUP BY queries against the report table for today's date window
|
|
12
|
+
* (+ 7 more for yesterday if finalization is needed).
|
|
13
|
+
* Day-window queries scan ~10K rows. Pending-count queries use partial indexes
|
|
14
|
+
* (WHERE status != 'closed') so only scan open reports, not the full table.
|
|
15
|
+
* Expected: ~10-50ms per query, ~100-350ms total report-table time.
|
|
16
|
+
* - ~40 lightweight reads against report_stat for freshness checks (small indexed table).
|
|
17
|
+
* - ~40 lightweight writes to report_stat for upserts.
|
|
18
|
+
*
|
|
19
|
+
* Locking: Uses pg_try_advisory_lock to ensure only one instance materializes at a time
|
|
20
|
+
* when running multiple containers. Advisory locks are cooperative, session-level locks —
|
|
21
|
+
* they do NOT block any table reads, writes, row locks, or transactions from other sessions.
|
|
22
|
+
* Normal application queries (report creation, moderation actions, API reads) are completely
|
|
23
|
+
* unaffected. If another instance already holds the lock, this instance skips the cycle
|
|
24
|
+
* immediately without blocking.
|
|
25
|
+
*/
|
|
26
|
+
export declare class StatsComputer {
|
|
27
|
+
private db;
|
|
28
|
+
private reportStatsServiceCreator;
|
|
29
|
+
/**
|
|
30
|
+
* Minutes between stats computer cycles.
|
|
31
|
+
* Defaults to 15. Minimum is 1.
|
|
32
|
+
* Set to -1 to disable the stats computer.
|
|
33
|
+
*/
|
|
34
|
+
private intervalMinutes;
|
|
35
|
+
destroyed: boolean;
|
|
36
|
+
processingPromise: Promise<void>;
|
|
37
|
+
timer?: NodeJS.Timeout;
|
|
38
|
+
constructor(db: Database, reportStatsServiceCreator: ReportStatsServiceCreator,
|
|
39
|
+
/**
|
|
40
|
+
* Minutes between stats computer cycles.
|
|
41
|
+
* Defaults to 15. Minimum is 1.
|
|
42
|
+
* Set to -1 to disable the stats computer.
|
|
43
|
+
*/
|
|
44
|
+
intervalMinutes: number);
|
|
45
|
+
get disabled(): boolean;
|
|
46
|
+
start(): void;
|
|
47
|
+
poll(): void;
|
|
48
|
+
private materializeStats;
|
|
49
|
+
destroy(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=stats-computer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-computer.d.ts","sourceRoot":"","sources":["../../src/daemon/stats-computer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAK3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IAMtB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,yBAAyB;IACjC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAZzB,SAAS,UAAQ;IACjB,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAoB;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGZ,EAAE,EAAE,QAAQ,EACZ,yBAAyB,EAAE,yBAAyB;IAC5D;;;;OAIG;IACK,eAAe,EAAE,MAAM;IAGjC,IAAI,QAAQ,YAEX;IAED,KAAK;IAIL,IAAI;YAYU,gBAAgB;IAwBxB,OAAO;CAQd"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StatsComputer = void 0;
|
|
4
|
+
const kysely_1 = require("kysely");
|
|
5
|
+
const common_1 = require("@atproto/common");
|
|
6
|
+
const logger_1 = require("../logger");
|
|
7
|
+
// Stable lock ID for pg_try_advisory_lock across all instances
|
|
8
|
+
const ADVISORY_LOCK_ID = 7239401;
|
|
9
|
+
/**
|
|
10
|
+
* Background daemon that materializes report statistics on an interval (default is 15 minutes).
|
|
11
|
+
*
|
|
12
|
+
* Each cycle computes calendar-day snapshots: today's stats are recomputed (in-progress day),
|
|
13
|
+
* and yesterday's snapshot is finalized if it wasn't already. Historical snapshots (completed
|
|
14
|
+
* days) are write-once and never recomputed unless explicitly refreshed via the API.
|
|
15
|
+
*
|
|
16
|
+
* Query profile per cycle (assuming ~10K reports/day, 10 queues, 20 moderators, 9 type groups):
|
|
17
|
+
* - 7 batched GROUP BY queries against the report table for today's date window
|
|
18
|
+
* (+ 7 more for yesterday if finalization is needed).
|
|
19
|
+
* Day-window queries scan ~10K rows. Pending-count queries use partial indexes
|
|
20
|
+
* (WHERE status != 'closed') so only scan open reports, not the full table.
|
|
21
|
+
* Expected: ~10-50ms per query, ~100-350ms total report-table time.
|
|
22
|
+
* - ~40 lightweight reads against report_stat for freshness checks (small indexed table).
|
|
23
|
+
* - ~40 lightweight writes to report_stat for upserts.
|
|
24
|
+
*
|
|
25
|
+
* Locking: Uses pg_try_advisory_lock to ensure only one instance materializes at a time
|
|
26
|
+
* when running multiple containers. Advisory locks are cooperative, session-level locks —
|
|
27
|
+
* they do NOT block any table reads, writes, row locks, or transactions from other sessions.
|
|
28
|
+
* Normal application queries (report creation, moderation actions, API reads) are completely
|
|
29
|
+
* unaffected. If another instance already holds the lock, this instance skips the cycle
|
|
30
|
+
* immediately without blocking.
|
|
31
|
+
*/
|
|
32
|
+
class StatsComputer {
|
|
33
|
+
constructor(db, reportStatsServiceCreator,
|
|
34
|
+
/**
|
|
35
|
+
* Minutes between stats computer cycles.
|
|
36
|
+
* Defaults to 15. Minimum is 1.
|
|
37
|
+
* Set to -1 to disable the stats computer.
|
|
38
|
+
*/
|
|
39
|
+
intervalMinutes) {
|
|
40
|
+
Object.defineProperty(this, "db", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: db
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(this, "reportStatsServiceCreator", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: reportStatsServiceCreator
|
|
51
|
+
});
|
|
52
|
+
Object.defineProperty(this, "intervalMinutes", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
56
|
+
value: intervalMinutes
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(this, "destroyed", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
writable: true,
|
|
62
|
+
value: false
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(this, "processingPromise", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: Promise.resolve()
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "timer", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: void 0
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
get disabled() {
|
|
78
|
+
return this.intervalMinutes < 1;
|
|
79
|
+
}
|
|
80
|
+
start() {
|
|
81
|
+
this.poll();
|
|
82
|
+
}
|
|
83
|
+
poll() {
|
|
84
|
+
if (this.destroyed || this.disabled)
|
|
85
|
+
return;
|
|
86
|
+
this.processingPromise = this.materializeStats()
|
|
87
|
+
.catch((err) => logger_1.dbLogger.error({ err }, 'stats materialization errored'))
|
|
88
|
+
.finally(() => {
|
|
89
|
+
this.timer = setTimeout(() => this.poll(), this.intervalMinutes * common_1.MINUTE);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async materializeStats() {
|
|
93
|
+
const lockResult = await (0, kysely_1.sql) `SELECT pg_try_advisory_lock(${ADVISORY_LOCK_ID}) as locked`.execute(this.db.db);
|
|
94
|
+
const acquired = lockResult.rows[0]?.locked === true;
|
|
95
|
+
if (!acquired) {
|
|
96
|
+
logger_1.dbLogger.info('stats materialization skipped, another instance holds lock');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const statsService = this.reportStatsServiceCreator(this.db);
|
|
101
|
+
await statsService.materializeAll();
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
await (0, kysely_1.sql) `SELECT pg_advisory_unlock(${ADVISORY_LOCK_ID})`.execute(this.db.db);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async destroy() {
|
|
108
|
+
this.destroyed = true;
|
|
109
|
+
if (this.timer) {
|
|
110
|
+
clearTimeout(this.timer);
|
|
111
|
+
this.timer = undefined;
|
|
112
|
+
}
|
|
113
|
+
await this.processingPromise;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.StatsComputer = StatsComputer;
|
|
117
|
+
//# sourceMappingURL=stats-computer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-computer.js","sourceRoot":"","sources":["../../src/daemon/stats-computer.ts"],"names":[],"mappings":";;;AAAA,mCAA4B;AAC5B,4CAAwC;AAExC,sCAAoC;AAGpC,+DAA+D;AAC/D,MAAM,gBAAgB,GAAG,OAAS,CAAA;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,aAAa;IAKxB,YACU,EAAY,EACZ,yBAAoD;IAC5D;;;;OAIG;IACK,eAAuB;QAP/B;;;;mBAAQ,EAAE;WAAU;QACpB;;;;mBAAQ,yBAAyB;WAA2B;QAM5D;;;;mBAAQ,eAAe;WAAQ;QAZjC;;;;mBAAY,KAAK;WAAA;QACjB;;;;mBAAmC,OAAO,CAAC,OAAO,EAAE;WAAA;QACpD;;;;;WAAsB;IAWnB,CAAC;IAEJ,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,eAAe,GAAG,CAAC,CAAA;IACjC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAM;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE;aAC7C,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,+BAA+B,CAAC,CAAC;aACxE,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,KAAK,GAAG,UAAU,CACrB,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EACjB,IAAI,CAAC,eAAe,GAAG,eAAM,CAC9B,CAAA;QACH,CAAC,CAAC,CAAA;IACN,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,MAAM,UAAU,GAAG,MAAM,IAAA,YAAG,EAE1B,+BAA+B,gBAAgB,aAAa,CAAC,OAAO,CACpE,IAAI,CAAC,EAAE,CAAC,EAAE,CACX,CAAA;QACD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,iBAAQ,CAAC,IAAI,CACX,4DAA4D,CAC7D,CAAA;YACD,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC5D,MAAM,YAAY,CAAC,cAAc,EAAE,CAAA;QACrC,CAAC;gBAAS,CAAC;YACT,MAAM,IAAA,YAAG,EAAA,6BAA6B,gBAAgB,GAAG,CAAC,OAAO,CAC/D,IAAI,CAAC,EAAE,CAAC,EAAE,CACX,CAAA;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACxB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACxB,CAAC;QACD,MAAM,IAAI,CAAC,iBAAiB,CAAA;IAC9B,CAAC;CACF;AApED,sCAoEC","sourcesContent":["import { sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { Database } from '../db'\nimport { dbLogger } from '../logger'\nimport { ReportStatsServiceCreator } from '../report/stats'\n\n// Stable lock ID for pg_try_advisory_lock across all instances\nconst ADVISORY_LOCK_ID = 7_239_401\n\n/**\n * Background daemon that materializes report statistics on an interval (default is 15 minutes).\n *\n * Each cycle computes calendar-day snapshots: today's stats are recomputed (in-progress day),\n * and yesterday's snapshot is finalized if it wasn't already. Historical snapshots (completed\n * days) are write-once and never recomputed unless explicitly refreshed via the API.\n *\n * Query profile per cycle (assuming ~10K reports/day, 10 queues, 20 moderators, 9 type groups):\n * - 7 batched GROUP BY queries against the report table for today's date window\n * (+ 7 more for yesterday if finalization is needed).\n * Day-window queries scan ~10K rows. Pending-count queries use partial indexes\n * (WHERE status != 'closed') so only scan open reports, not the full table.\n * Expected: ~10-50ms per query, ~100-350ms total report-table time.\n * - ~40 lightweight reads against report_stat for freshness checks (small indexed table).\n * - ~40 lightweight writes to report_stat for upserts.\n *\n * Locking: Uses pg_try_advisory_lock to ensure only one instance materializes at a time\n * when running multiple containers. Advisory locks are cooperative, session-level locks —\n * they do NOT block any table reads, writes, row locks, or transactions from other sessions.\n * Normal application queries (report creation, moderation actions, API reads) are completely\n * unaffected. If another instance already holds the lock, this instance skips the cycle\n * immediately without blocking.\n */\nexport class StatsComputer {\n destroyed = false\n processingPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private reportStatsServiceCreator: ReportStatsServiceCreator,\n /**\n * Minutes between stats computer cycles.\n * Defaults to 15. Minimum is 1.\n * Set to -1 to disable the stats computer.\n */\n private intervalMinutes: number,\n ) {}\n\n get disabled() {\n return this.intervalMinutes < 1\n }\n\n start() {\n this.poll()\n }\n\n poll() {\n if (this.destroyed || this.disabled) return\n this.processingPromise = this.materializeStats()\n .catch((err) => dbLogger.error({ err }, 'stats materialization errored'))\n .finally(() => {\n this.timer = setTimeout(\n () => this.poll(),\n this.intervalMinutes * MINUTE,\n )\n })\n }\n\n private async materializeStats() {\n const lockResult = await sql<{\n locked: boolean\n }>`SELECT pg_try_advisory_lock(${ADVISORY_LOCK_ID}) as locked`.execute(\n this.db.db,\n )\n const acquired = lockResult.rows[0]?.locked === true\n if (!acquired) {\n dbLogger.info(\n 'stats materialization skipped, another instance holds lock',\n )\n return\n }\n\n try {\n const statsService = this.reportStatsServiceCreator(this.db)\n await statsService.materializeAll()\n } finally {\n await sql`SELECT pg_advisory_unlock(${ADVISORY_LOCK_ID})`.execute(\n this.db.db,\n )\n }\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.processingPromise\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strike-expiry-processor.d.ts","sourceRoot":"","sources":["../../src/daemon/strike-expiry-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"strike-expiry-processor.d.ts","sourceRoot":"","sources":["../../src/daemon/strike-expiry-processor.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAK5D,qBAAa,qBAAqB;IAM9B,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,oBAAoB;IAN9B,SAAS,UAAQ;IACjB,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAoB;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAA;gBAGZ,EAAE,EAAE,QAAQ,EACZ,oBAAoB,EAAE,oBAAoB;IAGpD,KAAK;IAIL,IAAI;IAWE,OAAO;IASP,gBAAgB;IAIhB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAInC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,qBAAqB;CAgC5B"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.StrikeExpiryProcessor = void 0;
|
|
4
4
|
const common_1 = require("@atproto/common");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
+
const job_cursor_1 = require("./job-cursor");
|
|
6
7
|
const JOB_NAME = 'strike_expiry';
|
|
7
8
|
class StrikeExpiryProcessor {
|
|
8
9
|
constructor(db, strikeServiceCreator) {
|
|
@@ -58,29 +59,13 @@ class StrikeExpiryProcessor {
|
|
|
58
59
|
await this.processingPromise;
|
|
59
60
|
}
|
|
60
61
|
async initializeCursor() {
|
|
61
|
-
await this.db
|
|
62
|
-
.insertInto('job_cursor')
|
|
63
|
-
.values({
|
|
64
|
-
job: JOB_NAME,
|
|
65
|
-
cursor: null,
|
|
66
|
-
})
|
|
67
|
-
.onConflict((oc) => oc.doNothing())
|
|
68
|
-
.execute();
|
|
62
|
+
await (0, job_cursor_1.initJobCursor)(this.db, JOB_NAME);
|
|
69
63
|
}
|
|
70
64
|
async getCursor() {
|
|
71
|
-
|
|
72
|
-
.selectFrom('job_cursor')
|
|
73
|
-
.select('cursor')
|
|
74
|
-
.where('job', '=', JOB_NAME)
|
|
75
|
-
.executeTakeFirst();
|
|
76
|
-
return entry?.cursor || null;
|
|
65
|
+
return (0, job_cursor_1.getJobCursor)(this.db, JOB_NAME);
|
|
77
66
|
}
|
|
78
67
|
async updateCursor(cursor) {
|
|
79
|
-
await this.db
|
|
80
|
-
.updateTable('job_cursor')
|
|
81
|
-
.set({ cursor })
|
|
82
|
-
.where('job', '=', JOB_NAME)
|
|
83
|
-
.execute();
|
|
68
|
+
await (0, job_cursor_1.updateJobCursor)(this.db, JOB_NAME, cursor);
|
|
84
69
|
}
|
|
85
70
|
async processExpiredStrikes() {
|
|
86
71
|
const now = new Date();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strike-expiry-processor.js","sourceRoot":"","sources":["../../src/daemon/strike-expiry-processor.ts"],"names":[],"mappings":";;;AAAA,4CAAsC;AAEtC,sCAAoC;
|
|
1
|
+
{"version":3,"file":"strike-expiry-processor.js","sourceRoot":"","sources":["../../src/daemon/strike-expiry-processor.ts"],"names":[],"mappings":"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","sourcesContent":["import { HOUR } from '@atproto/common'\nimport { Database } from '../db'\nimport { dbLogger } from '../logger'\nimport { StrikeServiceCreator } from '../mod-service/strike'\nimport { getJobCursor, initJobCursor, updateJobCursor } from './job-cursor'\n\nconst JOB_NAME = 'strike_expiry'\n\nexport class StrikeExpiryProcessor {\n destroyed = false\n processingPromise: Promise<void> = Promise.resolve()\n timer?: NodeJS.Timeout\n\n constructor(\n private db: Database,\n private strikeServiceCreator: StrikeServiceCreator,\n ) {}\n\n start() {\n this.initializeCursor().then(() => this.poll())\n }\n\n poll() {\n if (this.destroyed) return\n this.processingPromise = this.processExpiredStrikes()\n .catch((err) =>\n dbLogger.error({ err }, 'strike expiry processing errored'),\n )\n .finally(() => {\n this.timer = setTimeout(() => this.poll(), getInterval())\n })\n }\n\n async destroy() {\n this.destroyed = true\n if (this.timer) {\n clearTimeout(this.timer)\n this.timer = undefined\n }\n await this.processingPromise\n }\n\n async initializeCursor() {\n await initJobCursor(this.db, JOB_NAME)\n }\n\n async getCursor(): Promise<string | null> {\n return getJobCursor(this.db, JOB_NAME)\n }\n\n async updateCursor(cursor: string): Promise<void> {\n await updateJobCursor(this.db, JOB_NAME, cursor)\n }\n\n async processExpiredStrikes() {\n const now = new Date()\n const strikeService = this.strikeServiceCreator(this.db)\n const lastProcessedAt = await this.getCursor()\n const affectedSubjects = await strikeService.getExpiredStrikeSubjects(\n lastProcessedAt || undefined,\n )\n\n if (!affectedSubjects.length) {\n dbLogger.info('no expired strikes to process')\n await this.updateCursor(now.toISOString())\n return\n }\n\n dbLogger.info(\n { count: affectedSubjects.length },\n 'processing subjects with expired strikes',\n )\n\n await Promise.all(\n affectedSubjects.map(({ subjectDid }) => {\n return strikeService.updateSubjectStrikeCount(subjectDid)\n }),\n )\n\n await this.updateCursor(now.toISOString())\n\n dbLogger.info(\n { processed: affectedSubjects.length },\n 'strike expiry processing completed',\n )\n }\n}\n\nconst getInterval = (): number => {\n // Run every hour, synchronized to the hour boundary\n const now = Date.now()\n const intervalMs = HOUR\n const nextIteration = Math.ceil(now / intervalMs)\n return nextIteration * intervalMs - now\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260219T164523000Z-create-report-table.d.ts","sourceRoot":"","sources":["../../../src/db/migrations/20260219T164523000Z-create-report-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAO,MAAM,QAAQ,CAAA;AAEpC,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmJ3D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7D"}
|