@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
package/src/report/activity.ts
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { Database } from '../db/index.js'
|
|
3
|
-
import { TimeIdKeyset, paginate } from '../db/pagination.js'
|
|
4
|
-
import { ReportView } from '../lexicon/types/tools/ozone/report/defs.js'
|
|
5
|
-
import { QueryParams as QueryActivitiesParams } from '../lexicon/types/tools/ozone/report/queryActivities.js'
|
|
6
|
-
import { Member } from '../lexicon/types/tools/ozone/team/defs.js'
|
|
7
|
-
import {
|
|
8
|
-
AlreadyInTargetState,
|
|
9
|
-
InvalidStateTransition,
|
|
10
|
-
handleReportUpdate,
|
|
11
|
-
} from './handle-report-update.js'
|
|
12
|
-
|
|
13
|
-
export type ActivityType =
|
|
14
|
-
| 'queueActivity'
|
|
15
|
-
| 'assignmentActivity'
|
|
16
|
-
| 'escalationActivity'
|
|
17
|
-
| 'closeActivity'
|
|
18
|
-
| 'reopenActivity'
|
|
19
|
-
| 'noteActivity'
|
|
20
|
-
|
|
21
|
-
export type CreateActivityParams = {
|
|
22
|
-
reportId: number
|
|
23
|
-
activityType: ActivityType
|
|
24
|
-
internalNote?: string
|
|
25
|
-
publicNote?: string
|
|
26
|
-
meta?: Record<string, unknown>
|
|
27
|
-
/** Set true for activities created by automated processes (e.g. queue router). */
|
|
28
|
-
isAutomated?: boolean
|
|
29
|
-
createdBy: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function createReportActivity(
|
|
33
|
-
db: Database,
|
|
34
|
-
params: CreateActivityParams,
|
|
35
|
-
) {
|
|
36
|
-
const {
|
|
37
|
-
reportId,
|
|
38
|
-
activityType,
|
|
39
|
-
internalNote,
|
|
40
|
-
publicNote,
|
|
41
|
-
meta,
|
|
42
|
-
isAutomated = false,
|
|
43
|
-
createdBy,
|
|
44
|
-
} = params
|
|
45
|
-
|
|
46
|
-
return db.transaction(async (dbTxn) => {
|
|
47
|
-
// Lock the report row for the duration of the transaction to prevent
|
|
48
|
-
// concurrent writes from racing on status validation + update.
|
|
49
|
-
const report = await dbTxn.db
|
|
50
|
-
.selectFrom('report')
|
|
51
|
-
.select(['id', 'status'])
|
|
52
|
-
.where('id', '=', reportId)
|
|
53
|
-
.forUpdate()
|
|
54
|
-
.executeTakeFirst()
|
|
55
|
-
|
|
56
|
-
if (!report) {
|
|
57
|
-
throw new InvalidRequestError(
|
|
58
|
-
`Report ${reportId} not found`,
|
|
59
|
-
'ReportNotFound',
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let result
|
|
64
|
-
try {
|
|
65
|
-
result = handleReportUpdate(report.status, {
|
|
66
|
-
type: 'activity',
|
|
67
|
-
activityType,
|
|
68
|
-
})
|
|
69
|
-
} catch (err) {
|
|
70
|
-
if (err instanceof AlreadyInTargetState) {
|
|
71
|
-
throw new InvalidRequestError(err.message, 'AlreadyInTargetState')
|
|
72
|
-
}
|
|
73
|
-
if (err instanceof InvalidStateTransition) {
|
|
74
|
-
throw new InvalidRequestError(err.message, 'InvalidStateTransition')
|
|
75
|
-
}
|
|
76
|
-
throw err
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const now = new Date().toISOString()
|
|
80
|
-
|
|
81
|
-
if (result.nextStatus !== null) {
|
|
82
|
-
const updateSet: Record<string, string | null> = {
|
|
83
|
-
status: result.nextStatus,
|
|
84
|
-
updatedAt: now,
|
|
85
|
-
}
|
|
86
|
-
if (result.nextStatus === 'closed') {
|
|
87
|
-
updateSet.closedAt = now
|
|
88
|
-
} else if (result.nextStatus === 'open') {
|
|
89
|
-
updateSet.closedAt = null
|
|
90
|
-
}
|
|
91
|
-
await dbTxn.db
|
|
92
|
-
.updateTable('report')
|
|
93
|
-
.set(updateSet)
|
|
94
|
-
.where('id', '=', reportId)
|
|
95
|
-
.execute()
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const [activity] = await dbTxn.db
|
|
99
|
-
.insertInto('report_activity')
|
|
100
|
-
.values({
|
|
101
|
-
reportId,
|
|
102
|
-
activityType,
|
|
103
|
-
previousStatus: result.activity?.previousStatus ?? null,
|
|
104
|
-
internalNote: internalNote ?? null,
|
|
105
|
-
publicNote: publicNote ?? null,
|
|
106
|
-
meta: meta ?? null,
|
|
107
|
-
isAutomated,
|
|
108
|
-
createdBy,
|
|
109
|
-
createdAt: now,
|
|
110
|
-
})
|
|
111
|
-
.returningAll()
|
|
112
|
-
.execute()
|
|
113
|
-
|
|
114
|
-
return activity
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export type BulkActivityInsert = {
|
|
119
|
-
reportId: number
|
|
120
|
-
activityType: string
|
|
121
|
-
previousStatus: string | null
|
|
122
|
-
internalNote?: string
|
|
123
|
-
publicNote?: string
|
|
124
|
-
meta?: unknown
|
|
125
|
-
isAutomated: boolean
|
|
126
|
-
createdBy: string
|
|
127
|
-
createdAt: string
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Insert multiple activity rows in a single query. No validation — caller is
|
|
132
|
-
* responsible for correctness and for being inside an appropriate transaction.
|
|
133
|
-
*/
|
|
134
|
-
export async function bulkInsertReportActivities(
|
|
135
|
-
db: Database,
|
|
136
|
-
activities: BulkActivityInsert[],
|
|
137
|
-
) {
|
|
138
|
-
if (!activities.length) return
|
|
139
|
-
await db.db
|
|
140
|
-
.insertInto('report_activity')
|
|
141
|
-
.values(
|
|
142
|
-
activities.map((a) => ({
|
|
143
|
-
reportId: a.reportId,
|
|
144
|
-
activityType: a.activityType,
|
|
145
|
-
previousStatus: a.previousStatus,
|
|
146
|
-
internalNote: a.internalNote ?? null,
|
|
147
|
-
publicNote: a.publicNote ?? null,
|
|
148
|
-
meta: a.meta ?? null,
|
|
149
|
-
isAutomated: a.isAutomated,
|
|
150
|
-
createdBy: a.createdBy,
|
|
151
|
-
createdAt: a.createdAt,
|
|
152
|
-
})),
|
|
153
|
-
)
|
|
154
|
-
.execute()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export type ListActivitiesParams = {
|
|
158
|
-
reportId: number
|
|
159
|
-
limit?: number
|
|
160
|
-
cursor?: string
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function listReportActivities(
|
|
164
|
-
db: Database,
|
|
165
|
-
params: ListActivitiesParams,
|
|
166
|
-
) {
|
|
167
|
-
const { reportId, limit = 50, cursor } = params
|
|
168
|
-
|
|
169
|
-
let builder = db.db
|
|
170
|
-
.selectFrom('report_activity')
|
|
171
|
-
.selectAll()
|
|
172
|
-
.where('reportId', '=', reportId)
|
|
173
|
-
.orderBy('createdAt', 'desc')
|
|
174
|
-
.orderBy('id', 'desc')
|
|
175
|
-
.limit(limit + 1)
|
|
176
|
-
|
|
177
|
-
if (cursor) {
|
|
178
|
-
const cursorId = parseInt(cursor, 10)
|
|
179
|
-
if (!isNaN(cursorId)) {
|
|
180
|
-
builder = builder.where('id', '<', cursorId)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const rows = await builder.execute()
|
|
185
|
-
const hasMore = rows.length > limit
|
|
186
|
-
const activities = hasMore ? rows.slice(0, limit) : rows
|
|
187
|
-
|
|
188
|
-
const nextCursor =
|
|
189
|
-
hasMore && activities.length > 0
|
|
190
|
-
? String(activities[activities.length - 1].id)
|
|
191
|
-
: undefined
|
|
192
|
-
|
|
193
|
-
return { activities, cursor: nextCursor }
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export async function queryReportActivities(
|
|
197
|
-
db: Database,
|
|
198
|
-
params: QueryActivitiesParams,
|
|
199
|
-
) {
|
|
200
|
-
const {
|
|
201
|
-
activityTypes,
|
|
202
|
-
createdAfter,
|
|
203
|
-
createdBefore,
|
|
204
|
-
sortDirection,
|
|
205
|
-
limit,
|
|
206
|
-
cursor,
|
|
207
|
-
} = params
|
|
208
|
-
const { ref } = db.db.dynamic
|
|
209
|
-
|
|
210
|
-
let builder = db.db.selectFrom('report_activity').selectAll()
|
|
211
|
-
|
|
212
|
-
if (activityTypes && activityTypes.length > 0) {
|
|
213
|
-
builder = builder.where('activityType', 'in', activityTypes)
|
|
214
|
-
}
|
|
215
|
-
if (createdAfter) {
|
|
216
|
-
builder = builder.where('createdAt', '>=', createdAfter)
|
|
217
|
-
}
|
|
218
|
-
if (createdBefore) {
|
|
219
|
-
builder = builder.where('createdAt', '<=', createdBefore)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const keyset = new TimeIdKeyset(
|
|
223
|
-
ref('report_activity.createdAt'),
|
|
224
|
-
ref('report_activity.id'),
|
|
225
|
-
)
|
|
226
|
-
const paginatedBuilder = paginate(builder, {
|
|
227
|
-
limit,
|
|
228
|
-
cursor,
|
|
229
|
-
keyset,
|
|
230
|
-
direction: sortDirection,
|
|
231
|
-
tryIndex: true,
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
const activities = await paginatedBuilder.execute()
|
|
235
|
-
return { activities, cursor: keyset.packFromResult(activities) }
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function buildActivityObject(
|
|
239
|
-
activityType: string,
|
|
240
|
-
previousStatus: string | null,
|
|
241
|
-
): { $type: string; [k: string]: unknown } {
|
|
242
|
-
const $type = `tools.ozone.report.defs#${activityType}`
|
|
243
|
-
if (previousStatus !== null) {
|
|
244
|
-
return { $type, previousStatus }
|
|
245
|
-
}
|
|
246
|
-
return { $type }
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export function formatActivityView(
|
|
250
|
-
activity: {
|
|
251
|
-
id: number
|
|
252
|
-
reportId: number
|
|
253
|
-
activityType: string
|
|
254
|
-
previousStatus: string | null
|
|
255
|
-
internalNote: string | null
|
|
256
|
-
publicNote: string | null
|
|
257
|
-
meta: unknown
|
|
258
|
-
isAutomated: boolean
|
|
259
|
-
createdBy: string
|
|
260
|
-
createdAt: string
|
|
261
|
-
},
|
|
262
|
-
memberViews?: Map<string, Member>,
|
|
263
|
-
reportViews?: Map<number, ReportView>,
|
|
264
|
-
) {
|
|
265
|
-
return {
|
|
266
|
-
id: activity.id,
|
|
267
|
-
reportId: activity.reportId,
|
|
268
|
-
activity: buildActivityObject(
|
|
269
|
-
activity.activityType,
|
|
270
|
-
activity.previousStatus,
|
|
271
|
-
),
|
|
272
|
-
internalNote: activity.internalNote ?? undefined,
|
|
273
|
-
publicNote: activity.publicNote ?? undefined,
|
|
274
|
-
meta: (activity.meta as Record<string, unknown>) ?? undefined,
|
|
275
|
-
isAutomated: activity.isAutomated,
|
|
276
|
-
createdBy: activity.createdBy,
|
|
277
|
-
moderator: memberViews?.get(activity.createdBy),
|
|
278
|
-
report: reportViews?.get(activity.reportId),
|
|
279
|
-
createdAt: activity.createdAt,
|
|
280
|
-
}
|
|
281
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure, synchronous state-transition logic for reports.
|
|
3
|
-
*
|
|
4
|
-
* Every code path that changes a report's status or creates a report activity
|
|
5
|
-
* should call `handleReportUpdate` to determine the next status and the
|
|
6
|
-
* activity record to insert. This keeps the state machine in one place and
|
|
7
|
-
* decouples it from DB operations so it works for both single-row transactions
|
|
8
|
-
* and bulk updates.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Error types — callers decide how to surface these (throw, skip, etc.)
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
export class AlreadyInTargetState extends Error {
|
|
16
|
-
constructor(
|
|
17
|
-
public currentStatus: string,
|
|
18
|
-
public targetStatus: string,
|
|
19
|
-
) {
|
|
20
|
-
super(`Report is already in '${targetStatus}' status`)
|
|
21
|
-
this.name = 'AlreadyInTargetState'
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class InvalidStateTransition extends Error {
|
|
26
|
-
constructor(
|
|
27
|
-
public fromStatus: string,
|
|
28
|
-
public toStatus: string,
|
|
29
|
-
) {
|
|
30
|
-
super(`Cannot transition report from '${fromStatus}' to '${toStatus}'`)
|
|
31
|
-
this.name = 'InvalidStateTransition'
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// State machine tables
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
|
|
39
|
-
/** Valid state transitions: key = fromState, value = allowed toStates */
|
|
40
|
-
export const VALID_TRANSITIONS: Record<string, string[]> = {
|
|
41
|
-
open: ['closed', 'escalated', 'queued', 'assigned'],
|
|
42
|
-
closed: ['open'],
|
|
43
|
-
escalated: ['open', 'closed'],
|
|
44
|
-
queued: ['assigned', 'open'],
|
|
45
|
-
assigned: ['open', 'closed', 'escalated', 'queued'],
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Activity types that map to a specific target status */
|
|
49
|
-
const ACTIVITY_TO_STATE: Record<string, string> = {
|
|
50
|
-
queueActivity: 'queued',
|
|
51
|
-
assignmentActivity: 'assigned',
|
|
52
|
-
escalationActivity: 'escalated',
|
|
53
|
-
closeActivity: 'closed',
|
|
54
|
-
reopenActivity: 'open',
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Activity types that are only valid from specific source states */
|
|
58
|
-
const ACTIVITY_VALID_FROM_STATES: Record<string, string[]> = {
|
|
59
|
-
reopenActivity: ['closed'],
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Moderation event types → target status (+ activity type) */
|
|
63
|
-
const EVENT_TYPE_MAP: Record<string, { status: string; activityType: string }> =
|
|
64
|
-
{
|
|
65
|
-
'tools.ozone.moderation.defs#modEventAcknowledge': {
|
|
66
|
-
status: 'closed',
|
|
67
|
-
activityType: 'closeActivity',
|
|
68
|
-
},
|
|
69
|
-
'tools.ozone.moderation.defs#modEventTakedown': {
|
|
70
|
-
status: 'closed',
|
|
71
|
-
activityType: 'closeActivity',
|
|
72
|
-
},
|
|
73
|
-
'tools.ozone.moderation.defs#modEventLabel': {
|
|
74
|
-
status: 'closed',
|
|
75
|
-
activityType: 'closeActivity',
|
|
76
|
-
},
|
|
77
|
-
'tools.ozone.moderation.defs#modEventComment': {
|
|
78
|
-
status: 'closed',
|
|
79
|
-
activityType: 'closeActivity',
|
|
80
|
-
},
|
|
81
|
-
'tools.ozone.moderation.defs#modEventEscalate': {
|
|
82
|
-
status: 'escalated',
|
|
83
|
-
activityType: 'escalationActivity',
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// Action types — the three ways a report's status can change
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
|
|
91
|
-
export type ReportUpdateAction =
|
|
92
|
-
| { type: 'activity'; activityType: string }
|
|
93
|
-
| { type: 'event'; eventType: string }
|
|
94
|
-
| { type: 'queue' }
|
|
95
|
-
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
// Result type
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
|
|
100
|
-
export type ActivityRecord = {
|
|
101
|
-
activityType: string
|
|
102
|
-
previousStatus: string
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export type ReportUpdateResult = {
|
|
106
|
-
nextStatus: string | null
|
|
107
|
-
activity: ActivityRecord | null
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
// Core function
|
|
112
|
-
// ---------------------------------------------------------------------------
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Determines the next status and activity record for a report update.
|
|
116
|
-
*
|
|
117
|
-
* @throws AlreadyInTargetState if the report is already in the target status
|
|
118
|
-
* @throws InvalidStateTransition if the transition is not allowed
|
|
119
|
-
* @returns nextStatus (null = no change) and activity (null = nothing to record)
|
|
120
|
-
*/
|
|
121
|
-
export function handleReportUpdate(
|
|
122
|
-
currentStatus: string,
|
|
123
|
-
action: ReportUpdateAction,
|
|
124
|
-
): ReportUpdateResult {
|
|
125
|
-
switch (action.type) {
|
|
126
|
-
case 'activity':
|
|
127
|
-
return handleActivityAction(currentStatus, action.activityType)
|
|
128
|
-
case 'event':
|
|
129
|
-
return handleEventAction(currentStatus, action.eventType)
|
|
130
|
-
case 'queue':
|
|
131
|
-
return handleQueueAction(currentStatus)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
// Action handlers
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
|
|
139
|
-
function handleActivityAction(
|
|
140
|
-
currentStatus: string,
|
|
141
|
-
activityType: string,
|
|
142
|
-
): ReportUpdateResult {
|
|
143
|
-
const toState = ACTIVITY_TO_STATE[activityType] ?? null
|
|
144
|
-
|
|
145
|
-
// Note-type activities — no state change, but still produce an activity record
|
|
146
|
-
if (toState === null) {
|
|
147
|
-
return { nextStatus: null, activity: null }
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Check activity-specific source-state constraints
|
|
151
|
-
const validFromStates = ACTIVITY_VALID_FROM_STATES[activityType]
|
|
152
|
-
if (validFromStates && !validFromStates.includes(currentStatus)) {
|
|
153
|
-
throw new InvalidStateTransition(currentStatus, toState)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
validateTransition(currentStatus, toState)
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
nextStatus: toState,
|
|
160
|
-
activity: { activityType, previousStatus: currentStatus },
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function handleEventAction(
|
|
165
|
-
currentStatus: string,
|
|
166
|
-
eventType: string,
|
|
167
|
-
): ReportUpdateResult {
|
|
168
|
-
const mapping = EVENT_TYPE_MAP[eventType]
|
|
169
|
-
if (!mapping) {
|
|
170
|
-
// Event type doesn't affect report status
|
|
171
|
-
return { nextStatus: null, activity: null }
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
validateTransition(currentStatus, mapping.status)
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
nextStatus: mapping.status,
|
|
178
|
-
activity: {
|
|
179
|
-
activityType: mapping.activityType,
|
|
180
|
-
previousStatus: currentStatus,
|
|
181
|
-
},
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function handleQueueAction(currentStatus: string): ReportUpdateResult {
|
|
186
|
-
// Queue routing only transitions open → queued
|
|
187
|
-
if (currentStatus !== 'open') {
|
|
188
|
-
return { nextStatus: null, activity: null }
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
nextStatus: 'queued',
|
|
193
|
-
activity: { activityType: 'queueActivity', previousStatus: currentStatus },
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
// Shared validation
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
|
-
|
|
201
|
-
function validateTransition(fromStatus: string, toStatus: string): void {
|
|
202
|
-
if (fromStatus === toStatus) {
|
|
203
|
-
throw new AlreadyInTargetState(fromStatus, toStatus)
|
|
204
|
-
}
|
|
205
|
-
const allowed = VALID_TRANSITIONS[fromStatus] ?? []
|
|
206
|
-
if (!allowed.includes(toStatus)) {
|
|
207
|
-
throw new InvalidStateTransition(fromStatus, toStatus)
|
|
208
|
-
}
|
|
209
|
-
}
|
package/src/report/reassign.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { Database } from '../db/index.js'
|
|
3
|
-
import { QueueService } from '../queue/service.js'
|
|
4
|
-
|
|
5
|
-
export type ReassignReportQueueParams = {
|
|
6
|
-
reportId: number
|
|
7
|
-
toQueueId: number
|
|
8
|
-
comment?: string
|
|
9
|
-
createdBy: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function reassignReportQueue(
|
|
13
|
-
db: Database,
|
|
14
|
-
queueService: QueueService,
|
|
15
|
-
params: ReassignReportQueueParams,
|
|
16
|
-
): Promise<void> {
|
|
17
|
-
const { reportId, toQueueId, comment, createdBy } = params
|
|
18
|
-
|
|
19
|
-
if (toQueueId !== -1) {
|
|
20
|
-
const queue = await queueService.getById(toQueueId)
|
|
21
|
-
if (!queue) {
|
|
22
|
-
throw new InvalidRequestError(
|
|
23
|
-
`Queue ${toQueueId} not found`,
|
|
24
|
-
'QueueNotFound',
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
if (!queue.enabled) {
|
|
28
|
-
throw new InvalidRequestError(
|
|
29
|
-
`Queue ${toQueueId} is disabled`,
|
|
30
|
-
'QueueDisabled',
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
await db.transaction(async (dbTxn) => {
|
|
36
|
-
const report = await dbTxn.db
|
|
37
|
-
.selectFrom('report')
|
|
38
|
-
.select(['id', 'status', 'queueId'])
|
|
39
|
-
.where('id', '=', reportId)
|
|
40
|
-
.forUpdate()
|
|
41
|
-
.executeTakeFirst()
|
|
42
|
-
|
|
43
|
-
if (!report) {
|
|
44
|
-
throw new InvalidRequestError(
|
|
45
|
-
`Report ${reportId} not found`,
|
|
46
|
-
'ReportNotFound',
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (report.status === 'closed') {
|
|
51
|
-
throw new InvalidRequestError(
|
|
52
|
-
`Report ${reportId} is closed and cannot be reassigned`,
|
|
53
|
-
'ReportClosed',
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// NULL and -1 both mean "unassigned" for equivalence purposes.
|
|
58
|
-
const currentQueueId = report.queueId ?? -1
|
|
59
|
-
if (currentQueueId === toQueueId) {
|
|
60
|
-
throw new InvalidRequestError(
|
|
61
|
-
`Report ${reportId} is already in queue ${toQueueId}`,
|
|
62
|
-
'AlreadyInTargetQueue',
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const previousStatus = report.status
|
|
67
|
-
let nextStatus: string = previousStatus
|
|
68
|
-
if (toQueueId !== -1 && previousStatus === 'open') {
|
|
69
|
-
nextStatus = 'queued'
|
|
70
|
-
} else if (toQueueId === -1 && previousStatus === 'queued') {
|
|
71
|
-
nextStatus = 'open'
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const now = new Date().toISOString()
|
|
75
|
-
|
|
76
|
-
const reportUpdate: Record<string, string | number | null> = {
|
|
77
|
-
queueId: toQueueId,
|
|
78
|
-
queuedAt: toQueueId === -1 ? null : now,
|
|
79
|
-
updatedAt: now,
|
|
80
|
-
}
|
|
81
|
-
if (nextStatus !== previousStatus) {
|
|
82
|
-
reportUpdate.status = nextStatus
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
await dbTxn.db
|
|
86
|
-
.updateTable('report')
|
|
87
|
-
.set(reportUpdate)
|
|
88
|
-
.where('id', '=', reportId)
|
|
89
|
-
.execute()
|
|
90
|
-
|
|
91
|
-
await dbTxn.db
|
|
92
|
-
.insertInto('report_activity')
|
|
93
|
-
.values({
|
|
94
|
-
reportId,
|
|
95
|
-
activityType: 'queueActivity',
|
|
96
|
-
previousStatus,
|
|
97
|
-
internalNote: comment ?? null,
|
|
98
|
-
publicNote: null,
|
|
99
|
-
meta: {
|
|
100
|
-
fromQueueId: report.queueId ?? null,
|
|
101
|
-
toQueueId,
|
|
102
|
-
},
|
|
103
|
-
isAutomated: false,
|
|
104
|
-
createdBy,
|
|
105
|
-
createdAt: now,
|
|
106
|
-
})
|
|
107
|
-
.execute()
|
|
108
|
-
})
|
|
109
|
-
}
|