@atproto/ozone 0.2.8 → 0.2.11
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 +33 -0
- package/package.json +25 -21
- package/bin/migration-create.ts +0 -38
- package/jest.config.cjs +0 -22
- package/src/api/chat/getActorMetadata.ts +0 -23
- package/src/api/chat/getConvo.ts +0 -23
- package/src/api/chat/getConvoMembers.ts +0 -23
- package/src/api/chat/getConvos.ts +0 -23
- package/src/api/chat/getMessageContext.ts +0 -42
- package/src/api/chat/index.ts +0 -16
- package/src/api/communication/createTemplate.ts +0 -51
- package/src/api/communication/deleteTemplate.ts +0 -23
- package/src/api/communication/listTemplates.ts +0 -31
- package/src/api/communication/updateTemplate.ts +0 -51
- package/src/api/health.ts +0 -27
- package/src/api/index.ts +0 -146
- package/src/api/label/fetchLabels.ts +0 -32
- package/src/api/label/queryLabels.ts +0 -57
- package/src/api/label/subscribeLabels.ts +0 -25
- package/src/api/moderation/cancelScheduledActions.ts +0 -72
- package/src/api/moderation/emitEvent.ts +0 -475
- package/src/api/moderation/getAccountTimeline.ts +0 -160
- package/src/api/moderation/getEvent.ts +0 -19
- package/src/api/moderation/getRecord.ts +0 -40
- package/src/api/moderation/getRecords.ts +0 -50
- package/src/api/moderation/getRepo.ts +0 -34
- package/src/api/moderation/getReporterStats.ts +0 -18
- package/src/api/moderation/getRepos.ts +0 -41
- package/src/api/moderation/getSubjects.ts +0 -101
- package/src/api/moderation/listScheduledActions.ts +0 -45
- package/src/api/moderation/queryEvents.ts +0 -72
- package/src/api/moderation/queryStatuses.ts +0 -23
- package/src/api/moderation/scheduleAction.ts +0 -129
- package/src/api/moderation/searchRepos.ts +0 -46
- package/src/api/moderation/util.ts +0 -96
- package/src/api/proxied.ts +0 -327
- package/src/api/queue/assignModerator.ts +0 -31
- package/src/api/queue/createQueue.ts +0 -62
- package/src/api/queue/deleteQueue.ts +0 -56
- package/src/api/queue/getAssignments.ts +0 -19
- package/src/api/queue/listQueues.ts +0 -39
- package/src/api/queue/routeReports.ts +0 -44
- package/src/api/queue/unassignModerator.ts +0 -26
- package/src/api/queue/updateQueue.ts +0 -54
- package/src/api/report/assignModerator.ts +0 -36
- package/src/api/report/createActivity.ts +0 -57
- package/src/api/report/createReport.ts +0 -93
- package/src/api/report/getAssignments.ts +0 -20
- package/src/api/report/getHistoricalStats.ts +0 -41
- package/src/api/report/getLatestReport.ts +0 -44
- package/src/api/report/getLiveStats.ts +0 -26
- package/src/api/report/getReport.ts +0 -55
- package/src/api/report/listActivities.ts +0 -37
- package/src/api/report/queryActivities.ts +0 -64
- package/src/api/report/queryReports.ts +0 -44
- package/src/api/report/reassignQueue.ts +0 -68
- package/src/api/report/refreshStats.ts +0 -27
- package/src/api/report/unassignModerator.ts +0 -21
- package/src/api/safelink/addRule.ts +0 -48
- package/src/api/safelink/queryEvents.ts +0 -32
- package/src/api/safelink/queryRules.ts +0 -58
- package/src/api/safelink/removeRule.ts +0 -42
- package/src/api/safelink/updateRule.ts +0 -48
- package/src/api/server/getConfig.ts +0 -35
- package/src/api/set/addValues.ts +0 -28
- package/src/api/set/deleteSet.ts +0 -34
- package/src/api/set/deleteValues.ts +0 -31
- package/src/api/set/getValues.ts +0 -42
- package/src/api/set/querySets.ts +0 -36
- package/src/api/set/upsertSet.ts +0 -38
- package/src/api/setting/listOptions.ts +0 -44
- package/src/api/setting/removeOptions.ts +0 -64
- package/src/api/setting/upsertOption.ts +0 -156
- package/src/api/team/addMember.ts +0 -51
- package/src/api/team/deleteMember.ts +0 -29
- package/src/api/team/listMembers.ts +0 -20
- package/src/api/team/updateMember.ts +0 -47
- package/src/api/util.ts +0 -265
- package/src/api/verification/grantVerifications.ts +0 -90
- package/src/api/verification/listVerifications.ts +0 -44
- package/src/api/verification/revokeVerifications.ts +0 -43
- package/src/api/well-known.ts +0 -46
- package/src/assignment/index.ts +0 -728
- package/src/auth-verifier.ts +0 -227
- package/src/background.ts +0 -183
- package/src/communication-service/template.ts +0 -110
- package/src/communication-service/util.ts +0 -8
- package/src/config/config.ts +0 -211
- package/src/config/env.ts +0 -95
- package/src/config/index.ts +0 -3
- package/src/config/secrets.ts +0 -17
- package/src/context.ts +0 -399
- package/src/daemon/blob-diverter.ts +0 -186
- package/src/daemon/context.ts +0 -247
- package/src/daemon/event-pusher.ts +0 -363
- package/src/daemon/event-reverser.ts +0 -128
- package/src/daemon/index.ts +0 -33
- package/src/daemon/job-cursor.ts +0 -33
- package/src/daemon/materialized-view-refresher.ts +0 -33
- package/src/daemon/queue-router.ts +0 -101
- package/src/daemon/scheduled-action-processor.ts +0 -304
- package/src/daemon/stats-computer.ts +0 -101
- package/src/daemon/strike-expiry-processor.ts +0 -95
- package/src/daemon/team-profile-synchronizer.ts +0 -15
- package/src/daemon/verification-listener.ts +0 -169
- package/src/db/index.ts +0 -203
- package/src/db/migrations/20231219T205730722Z-init.ts +0 -170
- package/src/db/migrations/20240116T085607200Z-communication-template.ts +0 -23
- package/src/db/migrations/20240201T051104136Z-mod-event-blobs.ts +0 -15
- package/src/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.ts +0 -31
- package/src/db/migrations/20240228T003647759Z-add-label-sigs.ts +0 -25
- package/src/db/migrations/20240408T192432676Z-mute-reporting.ts +0 -15
- package/src/db/migrations/20240506T225055595Z-message-subject.ts +0 -21
- package/src/db/migrations/20240521T211332580Z-member.ts +0 -17
- package/src/db/migrations/20240814T003647759Z-event-created-at-index.ts +0 -13
- package/src/db/migrations/20240903T205730722Z-add-template-lang.ts +0 -12
- package/src/db/migrations/20240904T205730722Z-add-subject-did-index.ts +0 -13
- package/src/db/migrations/20241001T205730722Z-subject-status-review-state-index.ts +0 -15
- package/src/db/migrations/20241008T205730722Z-sets.ts +0 -53
- package/src/db/migrations/20241018T205730722Z-setting.ts +0 -27
- package/src/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.ts +0 -57
- package/src/db/migrations/20241220T144630860Z-stats-materialized-views.ts +0 -215
- package/src/db/migrations/20250204T003647759Z-add-subject-priority-score.ts +0 -22
- package/src/db/migrations/20250211T003647759Z-add-reporter-stats-index.ts +0 -38
- package/src/db/migrations/20250211T132135150Z-moderation-event-message-partial-idx.ts +0 -26
- package/src/db/migrations/20250221T132135150Z-member-details.ts +0 -14
- package/src/db/migrations/20250404T201720309Z-subject-status-sort-idxs.ts +0 -18
- package/src/db/migrations/20250415T201720309Z-verification.ts +0 -34
- package/src/db/migrations/20250417T201720309Z-firehose-cursor.ts +0 -16
- package/src/db/migrations/20250609T110704000Z-safelink.ts +0 -53
- package/src/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.ts +0 -18
- package/src/db/migrations/20250701T000000000Z-add-age-assurance-state.ts +0 -25
- package/src/db/migrations/20250715T000000000Z-add-mod-event-external-id.ts +0 -15
- package/src/db/migrations/20250718T150931000Z-update-appeal-reason-stats.ts +0 -310
- package/src/db/migrations/20250813T000000000Z-mod-tool-batch-id-index.ts +0 -14
- package/src/db/migrations/20250923T000000000Z-scheduled-actions.ts +0 -56
- package/src/db/migrations/20251008T120000000Z-add-strike-system.ts +0 -87
- package/src/db/migrations/20260210T154806448Z-mod-event-created-by-indexes.ts +0 -22
- package/src/db/migrations/20260219T164523000Z-create-report-table.ts +0 -155
- package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +0 -42
- package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +0 -41
- package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +0 -48
- package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +0 -35
- package/src/db/migrations/20260428T000000000Z-add-expiring-tag-table.ts +0 -32
- package/src/db/migrations/20260513T202941104Z-add-subject-convo-id.ts +0 -114
- package/src/db/migrations/20260602T120000000Z-add-report-activity-created-index.ts +0 -17
- package/src/db/migrations/index.ts +0 -44
- package/src/db/migrations/provider.ts +0 -26
- package/src/db/pagination.ts +0 -335
- package/src/db/schema/account_events_stats.ts +0 -16
- package/src/db/schema/account_record_events_stats.ts +0 -15
- package/src/db/schema/account_record_status_stats.ts +0 -15
- package/src/db/schema/account_strike.ts +0 -13
- package/src/db/schema/blob_push_event.ts +0 -21
- package/src/db/schema/communication_template.ts +0 -19
- package/src/db/schema/expiring_tag.ts +0 -18
- package/src/db/schema/firehose_cursor.ts +0 -13
- package/src/db/schema/index.ts +0 -60
- package/src/db/schema/job_cursor.ts +0 -13
- package/src/db/schema/label.ts +0 -22
- package/src/db/schema/member.ts +0 -22
- package/src/db/schema/moderation_event.ts +0 -61
- package/src/db/schema/moderation_subject_status.ts +0 -52
- package/src/db/schema/moderator_assignment.ts +0 -16
- package/src/db/schema/ozone_set.ts +0 -24
- package/src/db/schema/record_events_stats.ts +0 -15
- package/src/db/schema/record_push_event.ts +0 -21
- package/src/db/schema/repo_push_event.ts +0 -19
- package/src/db/schema/report.ts +0 -28
- package/src/db/schema/report_activity.ts +0 -22
- package/src/db/schema/report_queue.ts +0 -21
- package/src/db/schema/report_stat.ts +0 -27
- package/src/db/schema/safelink.ts +0 -39
- package/src/db/schema/scheduled-action.ts +0 -25
- package/src/db/schema/setting.ts +0 -24
- package/src/db/schema/signing_key.ts +0 -10
- package/src/db/schema/verification.ts +0 -21
- package/src/db/types.ts +0 -24
- package/src/error.ts +0 -12
- package/src/image-invalidator.ts +0 -7
- package/src/index.ts +0 -154
- package/src/jetstream/service.ts +0 -107
- package/src/logger.ts +0 -29
- package/src/mod-service/expiring-tags.ts +0 -104
- package/src/mod-service/index.ts +0 -1842
- package/src/mod-service/profile.ts +0 -139
- package/src/mod-service/report.ts +0 -429
- package/src/mod-service/status.ts +0 -549
- package/src/mod-service/strike.ts +0 -96
- package/src/mod-service/subject.ts +0 -311
- package/src/mod-service/types.ts +0 -96
- package/src/mod-service/util.ts +0 -99
- package/src/mod-service/views.ts +0 -912
- package/src/queue/service.ts +0 -603
- package/src/report/activity.ts +0 -281
- package/src/report/handle-report-update.ts +0 -209
- package/src/report/reassign.ts +0 -109
- package/src/report/stats.ts +0 -852
- package/src/report/views.ts +0 -239
- package/src/safelink/service.ts +0 -304
- package/src/scheduled-action/service.ts +0 -281
- package/src/scheduled-action/types.ts +0 -17
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/outbox.ts +0 -123
- package/src/sequencer/sequencer.ts +0 -147
- package/src/set/service.ts +0 -230
- package/src/setting/constants.ts +0 -3
- package/src/setting/service.ts +0 -148
- package/src/setting/types.ts +0 -3
- package/src/setting/validators.ts +0 -333
- package/src/tag-service/content-tagger.ts +0 -30
- package/src/tag-service/embed-tagger.ts +0 -70
- package/src/tag-service/index.ts +0 -70
- package/src/tag-service/language-data.ts +0 -561
- package/src/tag-service/language-tagger.ts +0 -101
- package/src/tag-service/util.ts +0 -13
- package/src/team/index.ts +0 -296
- package/src/util.ts +0 -230
- package/src/verification/issuer.ts +0 -146
- package/src/verification/service.ts +0 -208
- package/src/verification/util.ts +0 -53
- package/test.env +0 -2
- package/tests/3p-labeler.test.ts +0 -288
- package/tests/__snapshots__/account-strikes.test.ts.snap +0 -159
- package/tests/__snapshots__/age-assurance.test.ts.snap +0 -66
- package/tests/__snapshots__/blob-divert.test.ts.snap +0 -219
- package/tests/__snapshots__/get-account-timeline.test.ts.snap +0 -36
- package/tests/__snapshots__/get-record.test.ts.snap +0 -271
- package/tests/__snapshots__/get-records.test.ts.snap +0 -175
- package/tests/__snapshots__/get-repo.test.ts.snap +0 -91
- package/tests/__snapshots__/get-repos.test.ts.snap +0 -127
- package/tests/__snapshots__/get-starter-pack.test.ts.snap +0 -535
- package/tests/__snapshots__/get-subjects.test.ts.snap +0 -529
- package/tests/__snapshots__/moderation-events.test.ts.snap +0 -347
- package/tests/__snapshots__/moderation-statuses.test.ts.snap +0 -276
- package/tests/__snapshots__/moderation.test.ts.snap +0 -85
- package/tests/__snapshots__/report-reason.test.ts.snap +0 -14
- package/tests/__snapshots__/safelink.test.ts.snap +0 -179
- package/tests/__snapshots__/scheduled-action.test.ts.snap +0 -61
- package/tests/__snapshots__/sets.test.ts.snap +0 -46
- package/tests/__snapshots__/settings.test.ts.snap +0 -52
- package/tests/__snapshots__/team.test.ts.snap +0 -374
- package/tests/__snapshots__/verification-listener.test.ts.snap +0 -152
- package/tests/__snapshots__/verification.test.ts.snap +0 -302
- package/tests/_util.ts +0 -242
- package/tests/account-strikes.test.ts +0 -184
- package/tests/ack-all-subjects-of-account.test.ts +0 -177
- package/tests/age-assurance.test.ts +0 -372
- package/tests/blob-divert.test.ts +0 -106
- package/tests/communication-templates.test.ts +0 -149
- package/tests/content-tagger.test.ts +0 -170
- package/tests/db.test.ts +0 -184
- package/tests/expiring-label.test.ts +0 -72
- package/tests/expiring-tags.test.ts +0 -232
- package/tests/get-account-timeline.test.ts +0 -85
- package/tests/get-config.test.ts +0 -55
- package/tests/get-lists.test.ts +0 -111
- package/tests/get-profiles.test.ts +0 -70
- package/tests/get-record.test.ts +0 -130
- package/tests/get-records.test.ts +0 -91
- package/tests/get-repo.test.ts +0 -171
- package/tests/get-report.test.ts +0 -136
- package/tests/get-reporter-stats.test.ts +0 -132
- package/tests/get-repos.test.ts +0 -91
- package/tests/get-starter-pack.test.ts +0 -115
- package/tests/get-subjects.test.ts +0 -81
- package/tests/mod-tool.test.ts +0 -268
- package/tests/moderation-appeals.test.ts +0 -260
- package/tests/moderation-events.test.ts +0 -756
- package/tests/moderation-status-tags.test.ts +0 -140
- package/tests/moderation-statuses.test.ts +0 -495
- package/tests/moderation.test.ts +0 -992
- package/tests/protected-tags.test.ts +0 -218
- package/tests/query-labels.test.ts +0 -238
- package/tests/query-reports.test.ts +0 -608
- package/tests/queue-assignment.test.ts +0 -428
- package/tests/queue-router.test.ts +0 -306
- package/tests/queues.test.ts +0 -690
- package/tests/record-and-account-events.test.ts +0 -197
- package/tests/repo-search.test.ts +0 -136
- package/tests/report-action.test.ts +0 -308
- package/tests/report-activity.test.ts +0 -711
- package/tests/report-assignment.test.ts +0 -517
- package/tests/report-muting.test.ts +0 -100
- package/tests/report-reason.test.ts +0 -154
- package/tests/report-reassign-queue.test.ts +0 -340
- package/tests/report-routing.test.ts +0 -245
- package/tests/report-stats.test.ts +0 -545
- package/tests/revoke-account-credentials.test.ts +0 -54
- package/tests/safelink.test.ts +0 -534
- package/tests/scheduled-action-processor.test.ts +0 -488
- package/tests/scheduled-action.test.ts +0 -334
- package/tests/sequencer.test.ts +0 -227
- package/tests/server.test.ts +0 -62
- package/tests/sets.test.ts +0 -246
- package/tests/settings.test.ts +0 -308
- package/tests/strike-expiry-processor.test.ts +0 -299
- package/tests/subject-priority-score.test.ts +0 -96
- package/tests/takedown.test.ts +0 -105
- package/tests/team.test.ts +0 -216
- package/tests/verification-listener.test.ts +0 -129
- package/tests/verification.test.ts +0 -186
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
import { Kysely, sql } from 'kysely'
|
|
2
|
-
import { OZONE_APPEAL_REASON_TYPE } from '../../api/util.js'
|
|
3
|
-
import { REASONAPPEAL } from '../../lexicon/types/com/atproto/moderation/defs.js'
|
|
4
|
-
import * as modEvent from '../schema/moderation_event.js'
|
|
5
|
-
import * as recordEventsStats from '../schema/record_events_stats.js'
|
|
6
|
-
|
|
7
|
-
export async function up(db: Kysely<any>): Promise<void> {
|
|
8
|
-
// Drop and recreate materialized views to update appeal reason counting
|
|
9
|
-
// to include both REASONAPPEAL and OZONE_APPEAL_REASON_TYPE
|
|
10
|
-
// The primary difference between the old and new query is that we were using = and != operators
|
|
11
|
-
// to match against the meta->>'reportType' field and now we use IN and NOT IN
|
|
12
|
-
|
|
13
|
-
// Drop existing materialized views in reverse dependency order
|
|
14
|
-
await db.schema
|
|
15
|
-
.dropView('account_record_events_stats')
|
|
16
|
-
.materialized()
|
|
17
|
-
.execute()
|
|
18
|
-
await db.schema.dropView('record_events_stats').materialized().execute()
|
|
19
|
-
await db.schema.dropView('account_events_stats').materialized().execute()
|
|
20
|
-
|
|
21
|
-
// Recreate account_events_stats with updated appeal counting
|
|
22
|
-
await db.schema
|
|
23
|
-
.createView('account_events_stats')
|
|
24
|
-
.materialized()
|
|
25
|
-
.as(
|
|
26
|
-
(db as Kysely<modEvent.PartialDB>)
|
|
27
|
-
.selectFrom('moderation_event')
|
|
28
|
-
.where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
|
|
29
|
-
.where('subjectUri', 'is', null)
|
|
30
|
-
.select('subjectDid')
|
|
31
|
-
.select([
|
|
32
|
-
(eb) =>
|
|
33
|
-
sql<number>`COUNT(*) FILTER(
|
|
34
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
35
|
-
AND ${eb.ref('durationInHours')} IS NULL
|
|
36
|
-
)`.as('takedownCount'),
|
|
37
|
-
(eb) =>
|
|
38
|
-
sql<number>`COUNT(*) FILTER(
|
|
39
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
40
|
-
AND ${eb.ref('durationInHours')} IS NOT NULL
|
|
41
|
-
)`.as('suspendCount'),
|
|
42
|
-
(eb) =>
|
|
43
|
-
sql<number>`COUNT(*) FILTER(
|
|
44
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate'
|
|
45
|
-
)`.as('escalateCount'),
|
|
46
|
-
(eb) =>
|
|
47
|
-
sql<number>`COUNT(*) FILTER(
|
|
48
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
|
|
49
|
-
AND ${eb.ref('meta')} ->> 'reportType' NOT IN (${REASONAPPEAL}, ${OZONE_APPEAL_REASON_TYPE})
|
|
50
|
-
)`.as('reportCount'),
|
|
51
|
-
(eb) =>
|
|
52
|
-
sql<number>`COUNT(*) FILTER(
|
|
53
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
|
|
54
|
-
AND ${eb.ref('meta')} ->> 'reportType' IN (${REASONAPPEAL}, ${OZONE_APPEAL_REASON_TYPE})
|
|
55
|
-
)`.as('appealCount'),
|
|
56
|
-
])
|
|
57
|
-
.groupBy('subjectDid'),
|
|
58
|
-
)
|
|
59
|
-
.execute()
|
|
60
|
-
|
|
61
|
-
// Recreate record_events_stats with updated appeal counting
|
|
62
|
-
await db.schema
|
|
63
|
-
.createView('record_events_stats')
|
|
64
|
-
.materialized()
|
|
65
|
-
.as(
|
|
66
|
-
(db as Kysely<modEvent.PartialDB>)
|
|
67
|
-
.selectFrom('moderation_event')
|
|
68
|
-
.select([
|
|
69
|
-
'subjectDid',
|
|
70
|
-
'subjectUri',
|
|
71
|
-
(eb) =>
|
|
72
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate')`.as(
|
|
73
|
-
'escalateCount',
|
|
74
|
-
),
|
|
75
|
-
(eb) =>
|
|
76
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' NOT IN (${REASONAPPEAL}, ${OZONE_APPEAL_REASON_TYPE}))`.as(
|
|
77
|
-
'reportCount',
|
|
78
|
-
),
|
|
79
|
-
(eb) =>
|
|
80
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' IN (${REASONAPPEAL}, ${OZONE_APPEAL_REASON_TYPE}))`.as(
|
|
81
|
-
'appealCount',
|
|
82
|
-
),
|
|
83
|
-
])
|
|
84
|
-
.where('subjectType', '=', 'com.atproto.repo.strongRef')
|
|
85
|
-
.where('subjectUri', 'is not', null)
|
|
86
|
-
.groupBy(['subjectDid', 'subjectUri']),
|
|
87
|
-
)
|
|
88
|
-
.execute()
|
|
89
|
-
|
|
90
|
-
// Recreate account_record_events_stats (unchanged logic, but depends on record_events_stats)
|
|
91
|
-
await db.schema
|
|
92
|
-
.createView('account_record_events_stats')
|
|
93
|
-
.materialized()
|
|
94
|
-
.as(
|
|
95
|
-
(db as Kysely<recordEventsStats.PartialDB>)
|
|
96
|
-
.selectFrom('record_events_stats')
|
|
97
|
-
.select([
|
|
98
|
-
'subjectDid',
|
|
99
|
-
(eb) =>
|
|
100
|
-
sql<number>`SUM(${eb.ref('reportCount')})::bigint`.as(
|
|
101
|
-
'totalReports',
|
|
102
|
-
),
|
|
103
|
-
(eb) =>
|
|
104
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('reportCount')} > 0)`.as(
|
|
105
|
-
'reportedCount',
|
|
106
|
-
),
|
|
107
|
-
(eb) =>
|
|
108
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('escalateCount')} > 0)`.as(
|
|
109
|
-
'escalatedCount',
|
|
110
|
-
),
|
|
111
|
-
(eb) =>
|
|
112
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('appealCount')} > 0)`.as(
|
|
113
|
-
'appealedCount',
|
|
114
|
-
),
|
|
115
|
-
])
|
|
116
|
-
.groupBy('subjectDid'),
|
|
117
|
-
)
|
|
118
|
-
.execute()
|
|
119
|
-
|
|
120
|
-
// Recreate all indexes for the materialized views
|
|
121
|
-
await db.schema
|
|
122
|
-
.createIndex('account_events_stats_did_idx')
|
|
123
|
-
.unique()
|
|
124
|
-
.on('account_events_stats')
|
|
125
|
-
.column('subjectDid')
|
|
126
|
-
.execute()
|
|
127
|
-
|
|
128
|
-
await db.schema
|
|
129
|
-
.createIndex('account_events_stats_suspend_count_idx')
|
|
130
|
-
.on('account_events_stats')
|
|
131
|
-
.expression(sql`"suspendCount" ASC NULLS FIRST`)
|
|
132
|
-
.column('subjectDid')
|
|
133
|
-
.execute()
|
|
134
|
-
|
|
135
|
-
await db.schema
|
|
136
|
-
.createIndex('record_events_stats_uri_idx')
|
|
137
|
-
.unique()
|
|
138
|
-
.on('record_events_stats')
|
|
139
|
-
.column('subjectUri')
|
|
140
|
-
.execute()
|
|
141
|
-
|
|
142
|
-
await db.schema
|
|
143
|
-
.createIndex('record_events_stats_did_idx')
|
|
144
|
-
.on('record_events_stats')
|
|
145
|
-
.column('subjectDid')
|
|
146
|
-
.execute()
|
|
147
|
-
|
|
148
|
-
await db.schema
|
|
149
|
-
.createIndex('account_record_events_stats_did_idx')
|
|
150
|
-
.unique()
|
|
151
|
-
.on('account_record_events_stats')
|
|
152
|
-
.column('subjectDid')
|
|
153
|
-
.execute()
|
|
154
|
-
|
|
155
|
-
await db.schema
|
|
156
|
-
.createIndex('account_record_events_stats_reported_count_idx')
|
|
157
|
-
.on('account_record_events_stats')
|
|
158
|
-
.expression(sql`"reportedCount" ASC NULLS FIRST`)
|
|
159
|
-
.column('subjectDid')
|
|
160
|
-
.execute()
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function down(db: Kysely<any>): Promise<void> {
|
|
164
|
-
// Drop the updated materialized views
|
|
165
|
-
await db.schema
|
|
166
|
-
.dropView('account_record_events_stats')
|
|
167
|
-
.materialized()
|
|
168
|
-
.execute()
|
|
169
|
-
await db.schema.dropView('record_events_stats').materialized().execute()
|
|
170
|
-
await db.schema.dropView('account_events_stats').materialized().execute()
|
|
171
|
-
|
|
172
|
-
// Recreate the original views with single appeal reason type
|
|
173
|
-
await db.schema
|
|
174
|
-
.createView('account_events_stats')
|
|
175
|
-
.materialized()
|
|
176
|
-
.as(
|
|
177
|
-
(db as Kysely<modEvent.PartialDB>)
|
|
178
|
-
.selectFrom('moderation_event')
|
|
179
|
-
.where('subjectType', '=', 'com.atproto.admin.defs#repoRef')
|
|
180
|
-
.where('subjectUri', 'is', null)
|
|
181
|
-
.select('subjectDid')
|
|
182
|
-
.select([
|
|
183
|
-
(eb) =>
|
|
184
|
-
sql<number>`COUNT(*) FILTER(
|
|
185
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
186
|
-
AND ${eb.ref('durationInHours')} IS NULL
|
|
187
|
-
)`.as('takedownCount'),
|
|
188
|
-
(eb) =>
|
|
189
|
-
sql<number>`COUNT(*) FILTER(
|
|
190
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
191
|
-
AND ${eb.ref('durationInHours')} IS NOT NULL
|
|
192
|
-
)`.as('suspendCount'),
|
|
193
|
-
(eb) =>
|
|
194
|
-
sql<number>`COUNT(*) FILTER(
|
|
195
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate'
|
|
196
|
-
)`.as('escalateCount'),
|
|
197
|
-
(eb) =>
|
|
198
|
-
sql<number>`COUNT(*) FILTER(
|
|
199
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
|
|
200
|
-
AND ${eb.ref('meta')} ->> 'reportType' != ${REASONAPPEAL}
|
|
201
|
-
)`.as('reportCount'),
|
|
202
|
-
(eb) =>
|
|
203
|
-
sql<number>`COUNT(*) FILTER(
|
|
204
|
-
WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport'
|
|
205
|
-
AND ${eb.ref('meta')} ->> 'reportType' = ${REASONAPPEAL}
|
|
206
|
-
)`.as('appealCount'),
|
|
207
|
-
])
|
|
208
|
-
.groupBy('subjectDid'),
|
|
209
|
-
)
|
|
210
|
-
.execute()
|
|
211
|
-
|
|
212
|
-
await db.schema
|
|
213
|
-
.createView('record_events_stats')
|
|
214
|
-
.materialized()
|
|
215
|
-
.as(
|
|
216
|
-
(db as Kysely<modEvent.PartialDB>)
|
|
217
|
-
.selectFrom('moderation_event')
|
|
218
|
-
.select([
|
|
219
|
-
'subjectDid',
|
|
220
|
-
'subjectUri',
|
|
221
|
-
(eb) =>
|
|
222
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventEscalate')`.as(
|
|
223
|
-
'escalateCount',
|
|
224
|
-
),
|
|
225
|
-
(eb) =>
|
|
226
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' != 'com.atproto.moderation.defs#reasonAppeal')`.as(
|
|
227
|
-
'reportCount',
|
|
228
|
-
),
|
|
229
|
-
(eb) =>
|
|
230
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('action')} = 'tools.ozone.moderation.defs#modEventReport' AND ${eb.ref('meta')} ->> 'reportType' = 'com.atproto.moderation.defs#reasonAppeal')`.as(
|
|
231
|
-
'appealCount',
|
|
232
|
-
),
|
|
233
|
-
])
|
|
234
|
-
.where('subjectType', '=', 'com.atproto.repo.strongRef')
|
|
235
|
-
.where('subjectUri', 'is not', null)
|
|
236
|
-
.groupBy(['subjectDid', 'subjectUri']),
|
|
237
|
-
)
|
|
238
|
-
.execute()
|
|
239
|
-
|
|
240
|
-
await db.schema
|
|
241
|
-
.createView('account_record_events_stats')
|
|
242
|
-
.materialized()
|
|
243
|
-
.as(
|
|
244
|
-
(db as Kysely<recordEventsStats.PartialDB>)
|
|
245
|
-
.selectFrom('record_events_stats')
|
|
246
|
-
.select([
|
|
247
|
-
'subjectDid',
|
|
248
|
-
(eb) =>
|
|
249
|
-
sql<number>`SUM(${eb.ref('reportCount')})::bigint`.as(
|
|
250
|
-
'totalReports',
|
|
251
|
-
),
|
|
252
|
-
(eb) =>
|
|
253
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('reportCount')} > 0)`.as(
|
|
254
|
-
'reportedCount',
|
|
255
|
-
),
|
|
256
|
-
(eb) =>
|
|
257
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('escalateCount')} > 0)`.as(
|
|
258
|
-
'escalatedCount',
|
|
259
|
-
),
|
|
260
|
-
(eb) =>
|
|
261
|
-
sql<number>`COUNT(*) FILTER (WHERE ${eb.ref('appealCount')} > 0)`.as(
|
|
262
|
-
'appealedCount',
|
|
263
|
-
),
|
|
264
|
-
])
|
|
265
|
-
.groupBy('subjectDid'),
|
|
266
|
-
)
|
|
267
|
-
.execute()
|
|
268
|
-
|
|
269
|
-
// Recreate indexes
|
|
270
|
-
await db.schema
|
|
271
|
-
.createIndex('account_events_stats_did_idx')
|
|
272
|
-
.unique()
|
|
273
|
-
.on('account_events_stats')
|
|
274
|
-
.column('subjectDid')
|
|
275
|
-
.execute()
|
|
276
|
-
|
|
277
|
-
await db.schema
|
|
278
|
-
.createIndex('account_events_stats_suspend_count_idx')
|
|
279
|
-
.on('account_events_stats')
|
|
280
|
-
.expression(sql`"suspendCount" ASC NULLS FIRST`)
|
|
281
|
-
.column('subjectDid')
|
|
282
|
-
.execute()
|
|
283
|
-
|
|
284
|
-
await db.schema
|
|
285
|
-
.createIndex('record_events_stats_uri_idx')
|
|
286
|
-
.unique()
|
|
287
|
-
.on('record_events_stats')
|
|
288
|
-
.column('subjectUri')
|
|
289
|
-
.execute()
|
|
290
|
-
|
|
291
|
-
await db.schema
|
|
292
|
-
.createIndex('record_events_stats_did_idx')
|
|
293
|
-
.on('record_events_stats')
|
|
294
|
-
.column('subjectDid')
|
|
295
|
-
.execute()
|
|
296
|
-
|
|
297
|
-
await db.schema
|
|
298
|
-
.createIndex('account_record_events_stats_did_idx')
|
|
299
|
-
.unique()
|
|
300
|
-
.on('account_record_events_stats')
|
|
301
|
-
.column('subjectDid')
|
|
302
|
-
.execute()
|
|
303
|
-
|
|
304
|
-
await db.schema
|
|
305
|
-
.createIndex('account_record_events_stats_reported_count_idx')
|
|
306
|
-
.on('account_record_events_stats')
|
|
307
|
-
.expression(sql`"reportedCount" ASC NULLS FIRST`)
|
|
308
|
-
.column('subjectDid')
|
|
309
|
-
.execute()
|
|
310
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Kysely, sql } from 'kysely'
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
// Only small percentage of moderation events have a batchId in modTool meta property so we're creating a partial index
|
|
5
|
-
await sql`
|
|
6
|
-
CREATE INDEX moderation_event_mod_tool_batch_id_idx
|
|
7
|
-
ON moderation_event (("modTool" -> 'meta' ->> 'batchId'))
|
|
8
|
-
WHERE "modTool" #> '{meta,batchId}' IS NOT NULL
|
|
9
|
-
`.execute(db)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
13
|
-
await db.schema.dropIndex('moderation_event_mod_tool_batch_id_idx').execute()
|
|
14
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Kysely, sql } from 'kysely'
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
await db.schema
|
|
5
|
-
.createTable('scheduled_action')
|
|
6
|
-
.addColumn('id', 'bigserial', (col) => col.primaryKey())
|
|
7
|
-
.addColumn('action', 'varchar', (col) => col.notNull())
|
|
8
|
-
.addColumn('eventData', 'jsonb')
|
|
9
|
-
.addColumn('did', 'varchar', (col) => col.notNull())
|
|
10
|
-
.addColumn('executeAt', 'varchar')
|
|
11
|
-
.addColumn('executeAfter', 'varchar')
|
|
12
|
-
.addColumn('executeUntil', 'varchar')
|
|
13
|
-
.addColumn('randomizeExecution', 'boolean', (col) =>
|
|
14
|
-
col.notNull().defaultTo(false),
|
|
15
|
-
)
|
|
16
|
-
.addColumn('createdBy', 'varchar', (col) => col.notNull())
|
|
17
|
-
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
|
18
|
-
.addColumn('updatedAt', 'varchar', (col) => col.notNull())
|
|
19
|
-
.addColumn('status', 'varchar', (col) => col.notNull().defaultTo('pending'))
|
|
20
|
-
.addColumn('lastExecutedAt', 'varchar')
|
|
21
|
-
.addColumn('lastFailureReason', 'text')
|
|
22
|
-
.addColumn('executionEventId', 'bigint')
|
|
23
|
-
.execute()
|
|
24
|
-
|
|
25
|
-
// Unique constraint to prevent multiple pending actions for the same subject
|
|
26
|
-
await sql`
|
|
27
|
-
CREATE UNIQUE INDEX scheduled_action_unique_pending_subject
|
|
28
|
-
ON scheduled_action (did, action)
|
|
29
|
-
WHERE status = 'pending'
|
|
30
|
-
`.execute(db)
|
|
31
|
-
|
|
32
|
-
// for task runner to query pending actions efficiently
|
|
33
|
-
await db.schema
|
|
34
|
-
.createIndex('scheduled_action_execute_time_idx')
|
|
35
|
-
.on('scheduled_action')
|
|
36
|
-
.columns(['executeAt', 'executeAfter', 'status'])
|
|
37
|
-
.execute()
|
|
38
|
-
|
|
39
|
-
// for querying actions by subject
|
|
40
|
-
await db.schema
|
|
41
|
-
.createIndex('scheduled_action_did_idx')
|
|
42
|
-
.on('scheduled_action')
|
|
43
|
-
.column('did')
|
|
44
|
-
.execute()
|
|
45
|
-
|
|
46
|
-
// we require status to be always passed when listing scheduled actions and use createdAt for pagination
|
|
47
|
-
await db.schema
|
|
48
|
-
.createIndex('scheduled_action_status_created_at_idx')
|
|
49
|
-
.on('scheduled_action')
|
|
50
|
-
.columns(['status', 'createdAt'])
|
|
51
|
-
.execute()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
55
|
-
await db.schema.dropTable('scheduled_action').execute()
|
|
56
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { Kysely, sql } from 'kysely'
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
await db.schema
|
|
5
|
-
.alterTable('moderation_event')
|
|
6
|
-
.addColumn('severityLevel', 'varchar')
|
|
7
|
-
.execute()
|
|
8
|
-
await db.schema
|
|
9
|
-
.alterTable('moderation_event')
|
|
10
|
-
.addColumn('strikeCount', 'integer')
|
|
11
|
-
.execute()
|
|
12
|
-
await db.schema
|
|
13
|
-
.alterTable('moderation_event')
|
|
14
|
-
.addColumn('strikeExpiresAt', 'varchar')
|
|
15
|
-
.execute()
|
|
16
|
-
|
|
17
|
-
await db.schema
|
|
18
|
-
.createTable('account_strike')
|
|
19
|
-
.addColumn('did', 'text', (col) => col.primaryKey())
|
|
20
|
-
.addColumn('firstStrikeAt', 'varchar')
|
|
21
|
-
.addColumn('lastStrikeAt', 'varchar')
|
|
22
|
-
.addColumn('activeStrikeCount', 'integer', (col) =>
|
|
23
|
-
col.notNull().defaultTo(0),
|
|
24
|
-
)
|
|
25
|
-
.addColumn('totalStrikeCount', 'integer', (col) =>
|
|
26
|
-
col.notNull().defaultTo(0),
|
|
27
|
-
)
|
|
28
|
-
.execute()
|
|
29
|
-
|
|
30
|
-
await db.schema
|
|
31
|
-
.createTable('job_cursor')
|
|
32
|
-
.addColumn('job', 'text', (col) => col.primaryKey())
|
|
33
|
-
.addColumn('cursor', 'text')
|
|
34
|
-
.addColumn('updatedAt', 'text', (col) =>
|
|
35
|
-
col.defaultTo(sql`now()`).notNull(),
|
|
36
|
-
)
|
|
37
|
-
.execute()
|
|
38
|
-
|
|
39
|
-
// This supports fast look up for background job that aggregates strike data per subjectDid
|
|
40
|
-
await db.schema
|
|
41
|
-
.createIndex('moderation_event_subject_did_strike_count_idx')
|
|
42
|
-
.on('moderation_event')
|
|
43
|
-
.columns(['subjectDid', 'strikeCount'])
|
|
44
|
-
.execute()
|
|
45
|
-
|
|
46
|
-
// This supports fast lookup in the background job that needs to find strikes that have expired
|
|
47
|
-
await sql`
|
|
48
|
-
CREATE INDEX moderation_event_strike_expires_at_strike_count_idx
|
|
49
|
-
ON moderation_event ("strikeExpiresAt", "strikeCount")
|
|
50
|
-
WHERE "strikeExpiresAt" IS NOT NULL AND "strikeCount" IS NOT NULL
|
|
51
|
-
`.execute(db)
|
|
52
|
-
|
|
53
|
-
// for sorting and filtering by active strike count
|
|
54
|
-
await db.schema
|
|
55
|
-
.createIndex('account_strike_active_count_idx')
|
|
56
|
-
.on('account_strike')
|
|
57
|
-
.column('activeStrikeCount')
|
|
58
|
-
.execute()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
62
|
-
await db.schema
|
|
63
|
-
.dropIndex('moderation_event_subject_did_strike_count_idx')
|
|
64
|
-
.execute()
|
|
65
|
-
await db.schema
|
|
66
|
-
.dropIndex('moderation_event_strike_expires_at_strike_count_idx')
|
|
67
|
-
.execute()
|
|
68
|
-
await db.schema.dropIndex('account_strike_active_count_idx').execute()
|
|
69
|
-
|
|
70
|
-
await db.schema.dropTable('account_strike').execute()
|
|
71
|
-
await db.schema.dropTable('job_cursor').execute()
|
|
72
|
-
|
|
73
|
-
await db.schema
|
|
74
|
-
.alterTable('moderation_event')
|
|
75
|
-
.dropColumn('severityLevel')
|
|
76
|
-
.execute()
|
|
77
|
-
|
|
78
|
-
await db.schema
|
|
79
|
-
.alterTable('moderation_event')
|
|
80
|
-
.dropColumn('strikeCount')
|
|
81
|
-
.execute()
|
|
82
|
-
|
|
83
|
-
await db.schema
|
|
84
|
-
.alterTable('moderation_event')
|
|
85
|
-
.dropColumn('strikeExpiresAt')
|
|
86
|
-
.execute()
|
|
87
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Kysely } from 'kysely'
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
// @NOTE: These queries should be run with the "CONCURRENTLY" option in
|
|
5
|
-
// production to avoid locking the table. This is not supported by Kysely.
|
|
6
|
-
await db.schema
|
|
7
|
-
.dropIndex('moderation_event_created_by_idx')
|
|
8
|
-
.ifExists()
|
|
9
|
-
.execute()
|
|
10
|
-
await db.schema
|
|
11
|
-
.createIndex('moderation_event_created_by_idx')
|
|
12
|
-
.on('moderation_event')
|
|
13
|
-
.columns(['createdBy', 'createdAt', 'id'])
|
|
14
|
-
.execute()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
18
|
-
await db.schema
|
|
19
|
-
.dropIndex('moderation_event_created_by_idx')
|
|
20
|
-
.ifExists()
|
|
21
|
-
.execute()
|
|
22
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { Kysely, sql } from 'kysely'
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
// Report table - bridges report events to action events
|
|
5
|
-
await db.schema
|
|
6
|
-
.createTable('report')
|
|
7
|
-
.addColumn('id', 'serial', (col) => col.primaryKey())
|
|
8
|
-
|
|
9
|
-
// Core link to report event (display data still comes from moderation_event via JOIN)
|
|
10
|
-
.addColumn('eventId', 'integer', (col) => col.notNull().unique())
|
|
11
|
-
|
|
12
|
-
// Queue assignment (computed by background job in future iteration)
|
|
13
|
-
.addColumn('queueId', 'integer') // NULL = not yet assigned, -1 = no matching queue
|
|
14
|
-
.addColumn('queuedAt', 'varchar')
|
|
15
|
-
|
|
16
|
-
// Action linkage (sorted DESC, most recent first)
|
|
17
|
-
.addColumn('actionEventIds', 'jsonb') // Array of event IDs: [newest_id, ..., oldest_id]
|
|
18
|
-
|
|
19
|
-
// Reporter communication
|
|
20
|
-
.addColumn('actionNote', 'text')
|
|
21
|
-
|
|
22
|
-
// Whether the report is muted (reporter was muted or subject was muted at creation time)
|
|
23
|
-
.addColumn('isMuted', 'boolean', (col) => col.notNull().defaultTo(false))
|
|
24
|
-
|
|
25
|
-
// Status of the ticket/report
|
|
26
|
-
.addColumn('status', 'varchar', (col) => col.notNull().defaultTo('open')) // "open", "closed", "escalated"
|
|
27
|
-
|
|
28
|
-
// Denormalized from moderation_event for filtering without JOIN
|
|
29
|
-
.addColumn('reportType', 'varchar', (col) => col.notNull())
|
|
30
|
-
.addColumn('did', 'varchar', (col) => col.notNull())
|
|
31
|
-
.addColumn('recordPath', 'varchar', (col) => col.notNull().defaultTo('')) // '' = account/message, 'collection/rkey' = record
|
|
32
|
-
.addColumn('subjectMessageId', 'varchar') // NULL for non-message subjects
|
|
33
|
-
|
|
34
|
-
// Timestamps
|
|
35
|
-
.addColumn('createdAt', 'varchar', (col) => col.notNull())
|
|
36
|
-
.addColumn('updatedAt', 'varchar', (col) => col.notNull())
|
|
37
|
-
.addColumn('assignedTo', 'varchar') // DID of permanently assigned moderator
|
|
38
|
-
.addColumn('assignedAt', 'varchar') // When the permanent assignment was created
|
|
39
|
-
.addColumn('closedAt', 'varchar')
|
|
40
|
-
.execute()
|
|
41
|
-
|
|
42
|
-
// ─── Indexes ───
|
|
43
|
-
// Primary JOIN index - critical for every query that fetches display data from moderation_event
|
|
44
|
-
await db.schema
|
|
45
|
-
.createIndex('idx_report_event')
|
|
46
|
-
.on('report')
|
|
47
|
-
.column('eventId')
|
|
48
|
-
.execute()
|
|
49
|
-
|
|
50
|
-
// ─── Hot path: active reports (status != 'closed') ───
|
|
51
|
-
// Partial filter keeps these tight even as closed reports accumulate (~90% of table long-term).
|
|
52
|
-
// No isMuted in key (low cardinality, rarely filtered) and no INCLUDE columns
|
|
53
|
-
// (display data comes from moderation_event JOIN anyway).
|
|
54
|
-
|
|
55
|
-
// queryReports: queueId + status, paginated by createdAt
|
|
56
|
-
await sql`CREATE INDEX idx_report_active_queue_created ON report
|
|
57
|
-
("queueId", status, "createdAt" DESC, id DESC)
|
|
58
|
-
WHERE status != 'closed'`.execute(db)
|
|
59
|
-
|
|
60
|
-
// queryReports: queueId + status, paginated by updatedAt
|
|
61
|
-
await sql`CREATE INDEX idx_report_active_queue_updated ON report
|
|
62
|
-
("queueId", status, "updatedAt" DESC, id DESC)
|
|
63
|
-
WHERE status != 'closed'`.execute(db)
|
|
64
|
-
|
|
65
|
-
// queryReports: status only, paginated by createdAt
|
|
66
|
-
await sql`CREATE INDEX idx_report_active_status_created ON report
|
|
67
|
-
(status, "createdAt" DESC, id DESC)
|
|
68
|
-
WHERE status != 'closed'`.execute(db)
|
|
69
|
-
|
|
70
|
-
// queryReports: status only, paginated by updatedAt
|
|
71
|
-
await sql`CREATE INDEX idx_report_active_status_updated ON report
|
|
72
|
-
(status, "updatedAt" DESC, id DESC)
|
|
73
|
-
WHERE status != 'closed'`.execute(db)
|
|
74
|
-
|
|
75
|
-
// Active reports for a specific account (with optional queueId post-filter)
|
|
76
|
-
await sql`CREATE INDEX idx_report_active_did_created ON report
|
|
77
|
-
(did, status, "createdAt" DESC, id DESC)
|
|
78
|
-
WHERE status != 'closed'`.execute(db)
|
|
79
|
-
|
|
80
|
-
// A moderator's active workload (with optional queueId post-filter)
|
|
81
|
-
await sql`CREATE INDEX idx_report_active_assigned_created ON report
|
|
82
|
-
("assignedTo", status, "createdAt" DESC, id DESC)
|
|
83
|
-
WHERE status != 'closed'`.execute(db)
|
|
84
|
-
|
|
85
|
-
// findReportsForSubject hot path — always filters NOT IN ('closed').
|
|
86
|
-
// did + recordPath identify the subject (account or specific record).
|
|
87
|
-
await sql`CREATE INDEX idx_report_subject_active ON report
|
|
88
|
-
(did, "recordPath", "createdAt" DESC, id DESC)
|
|
89
|
-
WHERE status != 'closed'`.execute(db)
|
|
90
|
-
|
|
91
|
-
// ─── Closed history (status = 'closed') ───
|
|
92
|
-
// Closed reports are terminal; only sort by createdAt.
|
|
93
|
-
|
|
94
|
-
// Closed pagination per queue
|
|
95
|
-
await sql`CREATE INDEX idx_report_closed_queue_created ON report
|
|
96
|
-
("queueId", "createdAt" DESC, id DESC)
|
|
97
|
-
WHERE status = 'closed'`.execute(db)
|
|
98
|
-
|
|
99
|
-
// Closed history for an account
|
|
100
|
-
await sql`CREATE INDEX idx_report_closed_did_created ON report
|
|
101
|
-
(did, "createdAt" DESC, id DESC)
|
|
102
|
-
WHERE status = 'closed'`.execute(db)
|
|
103
|
-
|
|
104
|
-
// Moderator's closed-report history
|
|
105
|
-
await sql`CREATE INDEX idx_report_closed_assigned_created ON report
|
|
106
|
-
("assignedTo", "createdAt" DESC, id DESC)
|
|
107
|
-
WHERE status = 'closed'`.execute(db)
|
|
108
|
-
|
|
109
|
-
// ─── Other access patterns ───
|
|
110
|
-
|
|
111
|
-
// Collection prefix queries: left-anchored LIKE 'app.bsky.feed.post/%' or 'app.bsky.%'
|
|
112
|
-
// text_pattern_ops enables btree-scannable prefix matching (supported since Postgres 8.x)
|
|
113
|
-
await sql`CREATE INDEX idx_report_record_path_pattern ON report
|
|
114
|
-
("recordPath" text_pattern_ops)`.execute(db)
|
|
115
|
-
|
|
116
|
-
// Queue-router covering partial: index-only scan over unrouted, non-closed rows.
|
|
117
|
-
// Selects exactly the columns the router reads, eliminating heap fetches per batch.
|
|
118
|
-
await sql`CREATE INDEX idx_report_unassigned_id ON report (id)
|
|
119
|
-
INCLUDE (status, "reportType", "recordPath", "subjectMessageId")
|
|
120
|
-
WHERE "queueId" IS NULL AND status != 'closed'`.execute(db)
|
|
121
|
-
|
|
122
|
-
// Index for report statistics
|
|
123
|
-
await db.schema
|
|
124
|
-
.createIndex('idx_report_queue_created_id')
|
|
125
|
-
.on('report')
|
|
126
|
-
.columns(['queueId', 'createdAt', 'id'])
|
|
127
|
-
.execute()
|
|
128
|
-
|
|
129
|
-
// aggregate pending count query
|
|
130
|
-
await sql`CREATE INDEX idx_report_pending ON report (id) WHERE status != 'closed'`.execute(
|
|
131
|
-
db,
|
|
132
|
-
)
|
|
133
|
-
// per-queue pending count query
|
|
134
|
-
await sql`CREATE INDEX idx_report_queue_pending ON report ("queueId") WHERE status != 'closed'`.execute(
|
|
135
|
-
db,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
// Queue-router event-source partial: scans new modEventReport rows by id
|
|
139
|
-
// for the daemon that inserts report rows from moderation_event.
|
|
140
|
-
await sql`CREATE INDEX moderation_event_report_id_idx
|
|
141
|
-
ON moderation_event (id)
|
|
142
|
-
WHERE action = 'tools.ozone.moderation.defs#modEventReport'`.execute(db)
|
|
143
|
-
|
|
144
|
-
// Stats windowed queries: aggregate/typeWindow filter by createdAt range and
|
|
145
|
-
// include both open and closed reports, so they cannot use the partial indexes
|
|
146
|
-
// above. (createdAt, reportType) ordering serves the date-range scan and
|
|
147
|
-
// satisfies GROUP BY reportType from the index without a heap fetch.
|
|
148
|
-
await sql`CREATE INDEX idx_report_created_type
|
|
149
|
-
ON report ("createdAt", "reportType")`.execute(db)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
153
|
-
await db.schema.dropIndex('moderation_event_report_id_idx').execute()
|
|
154
|
-
await db.schema.dropTable('report').execute()
|
|
155
|
-
}
|