@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,32 +0,0 @@
|
|
|
1
|
-
import { AppContext } from '../../context.js'
|
|
2
|
-
import { Server } from '../../lexicon/index.js'
|
|
3
|
-
|
|
4
|
-
export default function (server: Server, ctx: AppContext) {
|
|
5
|
-
server.com.atproto.temp.fetchLabels({
|
|
6
|
-
auth: ctx.authVerifier.standardOptionalOrAdminToken,
|
|
7
|
-
handler: async ({ params }) => {
|
|
8
|
-
const { limit } = params
|
|
9
|
-
const since =
|
|
10
|
-
params.since !== undefined ? new Date(params.since).toISOString() : ''
|
|
11
|
-
const labelRes = await ctx.db.db
|
|
12
|
-
.selectFrom('label')
|
|
13
|
-
.selectAll()
|
|
14
|
-
.orderBy('label.cts', 'asc')
|
|
15
|
-
.where('cts', '>', since)
|
|
16
|
-
.limit(limit)
|
|
17
|
-
.execute()
|
|
18
|
-
|
|
19
|
-
const modSrvc = ctx.modService(ctx.db)
|
|
20
|
-
const labels = await Promise.all(
|
|
21
|
-
labelRes.map((l) => modSrvc.views.formatLabelAndEnsureSig(l)),
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
encoding: 'application/json',
|
|
26
|
-
body: {
|
|
27
|
-
labels,
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
})
|
|
32
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { AppContext } from '../../context.js'
|
|
3
|
-
import { Server } from '../../lexicon/index.js'
|
|
4
|
-
|
|
5
|
-
export default function (server: Server, ctx: AppContext) {
|
|
6
|
-
server.com.atproto.label.queryLabels(async ({ params }) => {
|
|
7
|
-
const { uriPatterns, sources, limit, cursor } = params
|
|
8
|
-
let builder = ctx.db.db.selectFrom('label').selectAll().limit(limit)
|
|
9
|
-
// if includes '*', then we don't need a where clause
|
|
10
|
-
if (!uriPatterns.includes('*')) {
|
|
11
|
-
builder = builder.where((eb) =>
|
|
12
|
-
eb.or(
|
|
13
|
-
uriPatterns.map((pattern) => {
|
|
14
|
-
// if no '*', then we're looking for an exact match
|
|
15
|
-
if (!pattern.includes('*')) {
|
|
16
|
-
return eb('uri', '=', pattern)
|
|
17
|
-
}
|
|
18
|
-
if (pattern.indexOf('*') < pattern.length - 1) {
|
|
19
|
-
throw new InvalidRequestError(`invalid pattern: ${pattern}`)
|
|
20
|
-
}
|
|
21
|
-
const searchPattern = pattern
|
|
22
|
-
.slice(0, -1)
|
|
23
|
-
.replaceAll('%', '') // sanitize search pattern
|
|
24
|
-
.replaceAll('_', '\\_') // escape any underscores
|
|
25
|
-
return eb('uri', 'like', `${searchPattern}%`)
|
|
26
|
-
}),
|
|
27
|
-
),
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
if (sources && sources.length > 0) {
|
|
31
|
-
builder = builder.where('src', 'in', sources)
|
|
32
|
-
}
|
|
33
|
-
if (cursor) {
|
|
34
|
-
const cursorId = parseInt(cursor, 10)
|
|
35
|
-
if (isNaN(cursorId)) {
|
|
36
|
-
throw new InvalidRequestError('invalid cursor')
|
|
37
|
-
}
|
|
38
|
-
builder = builder.where('id', '>', cursorId)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const res = await builder.execute()
|
|
42
|
-
|
|
43
|
-
const modSrvc = ctx.modService(ctx.db)
|
|
44
|
-
const labels = await Promise.all(
|
|
45
|
-
res.map((l) => modSrvc.views.formatLabelAndEnsureSig(l)),
|
|
46
|
-
)
|
|
47
|
-
const resCursor = res.at(-1)?.id.toString(10)
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
encoding: 'application/json',
|
|
51
|
-
body: {
|
|
52
|
-
cursor: resCursor,
|
|
53
|
-
labels,
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from '@atproto/xrpc-server'
|
|
2
|
-
import { AppContext } from '../../context.js'
|
|
3
|
-
import { Server } from '../../lexicon/index.js'
|
|
4
|
-
import { Outbox } from '../../sequencer/outbox.js'
|
|
5
|
-
|
|
6
|
-
export default function (server: Server, ctx: AppContext) {
|
|
7
|
-
server.com.atproto.label.subscribeLabels(async function* ({
|
|
8
|
-
params,
|
|
9
|
-
signal,
|
|
10
|
-
}) {
|
|
11
|
-
const { cursor } = params
|
|
12
|
-
const outbox = new Outbox(ctx.sequencer)
|
|
13
|
-
|
|
14
|
-
if (cursor !== undefined) {
|
|
15
|
-
const curr = await ctx.sequencer.curr()
|
|
16
|
-
if (cursor > (curr ?? 0)) {
|
|
17
|
-
throw new InvalidRequestError('Cursor in the future.', 'FutureCursor')
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
for await (const evt of outbox.events(cursor, signal)) {
|
|
22
|
-
yield { $type: '#labels', ...evt }
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { AuthRequiredError } from '@atproto/xrpc-server'
|
|
2
|
-
import { AppContext } from '../../context.js'
|
|
3
|
-
import { Server } from '../../lexicon/index.js'
|
|
4
|
-
import { subjectFromInput } from '../../mod-service/subject.js'
|
|
5
|
-
import { ScheduledTakedownTag } from './util.js'
|
|
6
|
-
|
|
7
|
-
export default function (server: Server, ctx: AppContext) {
|
|
8
|
-
server.tools.ozone.moderation.cancelScheduledActions({
|
|
9
|
-
auth: ctx.authVerifier.modOrAdminToken,
|
|
10
|
-
handler: async ({ input, auth }) => {
|
|
11
|
-
const access = auth.credentials
|
|
12
|
-
const db = ctx.db
|
|
13
|
-
const { subjects, comment } = input.body
|
|
14
|
-
|
|
15
|
-
if (!access.isModerator) {
|
|
16
|
-
throw new AuthRequiredError(
|
|
17
|
-
'Must be a moderator to cancel scheduled actions',
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const createdBy =
|
|
22
|
-
access.type === 'admin_token' ? ctx.cfg.service.did : access.iss
|
|
23
|
-
const now = new Date()
|
|
24
|
-
|
|
25
|
-
const result = await db.transaction(async (tx) => {
|
|
26
|
-
const scheduledActionService = ctx.scheduledActionService(tx)
|
|
27
|
-
const modService = ctx.modService(tx)
|
|
28
|
-
|
|
29
|
-
const cancellations =
|
|
30
|
-
await scheduledActionService.cancelScheduledActions(subjects)
|
|
31
|
-
|
|
32
|
-
for (const subject of cancellations.succeeded) {
|
|
33
|
-
await modService.logEvent({
|
|
34
|
-
event: {
|
|
35
|
-
$type: 'tools.ozone.moderation.defs#cancelScheduledTakedownEvent',
|
|
36
|
-
comment,
|
|
37
|
-
},
|
|
38
|
-
subject: subjectFromInput({
|
|
39
|
-
did: subject,
|
|
40
|
-
$type: 'com.atproto.admin.defs#repoRef',
|
|
41
|
-
}),
|
|
42
|
-
createdBy,
|
|
43
|
-
createdAt: now,
|
|
44
|
-
})
|
|
45
|
-
await modService.logEvent({
|
|
46
|
-
event: {
|
|
47
|
-
$type: 'tools.ozone.moderation.defs#modEventTag',
|
|
48
|
-
remove: [ScheduledTakedownTag],
|
|
49
|
-
add: [],
|
|
50
|
-
},
|
|
51
|
-
subject: subjectFromInput({
|
|
52
|
-
did: subject,
|
|
53
|
-
$type: 'com.atproto.admin.defs#repoRef',
|
|
54
|
-
}),
|
|
55
|
-
createdBy,
|
|
56
|
-
createdAt: now,
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return cancellations
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
encoding: 'application/json',
|
|
65
|
-
body: {
|
|
66
|
-
succeeded: result.succeeded,
|
|
67
|
-
failed: result.failed,
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
}
|
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AuthRequiredError,
|
|
3
|
-
ForbiddenError,
|
|
4
|
-
InvalidRequestError,
|
|
5
|
-
} from '@atproto/xrpc-server'
|
|
6
|
-
import { AdminTokenOutput, ModeratorOutput } from '../../auth-verifier.js'
|
|
7
|
-
import { AppContext } from '../../context.js'
|
|
8
|
-
import { Server } from '../../lexicon/index.js'
|
|
9
|
-
import { ids } from '../../lexicon/lexicons.js'
|
|
10
|
-
import {
|
|
11
|
-
ModEventTag,
|
|
12
|
-
isAgeAssuranceEvent,
|
|
13
|
-
isAgeAssuranceOverrideEvent,
|
|
14
|
-
isAgeAssurancePurgeEvent,
|
|
15
|
-
isModEventAcknowledge,
|
|
16
|
-
isModEventDivert,
|
|
17
|
-
isModEventEmail,
|
|
18
|
-
isModEventLabel,
|
|
19
|
-
isModEventMuteReporter,
|
|
20
|
-
isModEventReport,
|
|
21
|
-
isModEventReverseTakedown,
|
|
22
|
-
isModEventTag,
|
|
23
|
-
isModEventTakedown,
|
|
24
|
-
isModEventUnmuteReporter,
|
|
25
|
-
isRevokeAccountCredentialsEvent,
|
|
26
|
-
} from '../../lexicon/types/tools/ozone/moderation/defs.js'
|
|
27
|
-
import { HandlerInput } from '../../lexicon/types/tools/ozone/moderation/emitEvent.js'
|
|
28
|
-
import { httpLogger } from '../../logger.js'
|
|
29
|
-
import { processReportAction } from '../../mod-service/report.js'
|
|
30
|
-
import { subjectFromInput } from '../../mod-service/subject.js'
|
|
31
|
-
import { SettingService } from '../../setting/service.js'
|
|
32
|
-
import { TagService } from '../../tag-service/index.js'
|
|
33
|
-
import { getTagForReport } from '../../tag-service/util.js'
|
|
34
|
-
import { retryHttp } from '../../util.js'
|
|
35
|
-
import { getEventType } from '../util.js'
|
|
36
|
-
import { assertProtectedTagAction, getProtectedTags } from './util.js'
|
|
37
|
-
|
|
38
|
-
const handleModerationEvent = async ({
|
|
39
|
-
ctx,
|
|
40
|
-
input,
|
|
41
|
-
auth,
|
|
42
|
-
}: {
|
|
43
|
-
ctx: AppContext
|
|
44
|
-
input: HandlerInput
|
|
45
|
-
auth: ModeratorOutput | AdminTokenOutput
|
|
46
|
-
}) => {
|
|
47
|
-
const access = auth.credentials
|
|
48
|
-
const createdBy =
|
|
49
|
-
auth.credentials.type === 'moderator'
|
|
50
|
-
? auth.credentials.iss
|
|
51
|
-
: input.body.createdBy
|
|
52
|
-
const db = ctx.db
|
|
53
|
-
const moderationService = ctx.modService(db)
|
|
54
|
-
const settingService = ctx.settingService(db)
|
|
55
|
-
const { event, externalId } = input.body
|
|
56
|
-
const isAcknowledgeEvent = isModEventAcknowledge(event)
|
|
57
|
-
const isTakedownEvent = isModEventTakedown(event)
|
|
58
|
-
const isReverseTakedownEvent = isModEventReverseTakedown(event)
|
|
59
|
-
const isLabelEvent = isModEventLabel(event)
|
|
60
|
-
const subject = subjectFromInput(
|
|
61
|
-
input.body.subject,
|
|
62
|
-
input.body.subjectBlobCids,
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
if (isAgeAssuranceEvent(event) && !subject.isRepo()) {
|
|
66
|
-
throw new InvalidRequestError('Invalid subject type')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (isAgeAssuranceOverrideEvent(event)) {
|
|
70
|
-
if (!subject.isRepo()) {
|
|
71
|
-
throw new InvalidRequestError('Invalid subject type')
|
|
72
|
-
}
|
|
73
|
-
if (!auth.credentials.isModerator) {
|
|
74
|
-
throw new AuthRequiredError(
|
|
75
|
-
'Must be a full moderator to override age assurance',
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (isAgeAssurancePurgeEvent(event)) {
|
|
81
|
-
if (!subject.isRepo()) {
|
|
82
|
-
throw new InvalidRequestError('Invalid subject type')
|
|
83
|
-
}
|
|
84
|
-
if (!auth.credentials.isModerator) {
|
|
85
|
-
throw new ForbiddenError(
|
|
86
|
-
'Must be a moderator to purge age assurance events',
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (isRevokeAccountCredentialsEvent(event)) {
|
|
92
|
-
if (!subject.isRepo()) {
|
|
93
|
-
throw new InvalidRequestError('Invalid subject type')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!auth.credentials.isAdmin) {
|
|
97
|
-
throw new AuthRequiredError(
|
|
98
|
-
'Must be an admin to revoke account credentials',
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!ctx.pdsAgent) {
|
|
103
|
-
throw new InvalidRequestError('PDS not configured')
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
await ctx.pdsAgent.com.atproto.temp.revokeAccountCredentials(
|
|
107
|
-
{ account: subject.did },
|
|
108
|
-
await ctx.pdsAuth(ids.ComAtprotoTempRevokeAccountCredentials),
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// if less than moderator access then can only take ack and escalation actions
|
|
113
|
-
if (isTakedownEvent || isReverseTakedownEvent) {
|
|
114
|
-
if (!access.isModerator) {
|
|
115
|
-
throw new AuthRequiredError(
|
|
116
|
-
'Must be a full moderator to take this type of action',
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Non admins should not be able to take down feed generators
|
|
121
|
-
if (
|
|
122
|
-
!access.isAdmin &&
|
|
123
|
-
subject.recordPath?.includes('app.bsky.feed.generator/')
|
|
124
|
-
) {
|
|
125
|
-
throw new AuthRequiredError(
|
|
126
|
-
'Must be a full admin to take this type of action on feed generators',
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// if less than moderator access then can not apply labels
|
|
131
|
-
if (!access.isModerator && isLabelEvent) {
|
|
132
|
-
throw new AuthRequiredError('Must be a full moderator to label content')
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (isLabelEvent) {
|
|
136
|
-
validateLabels([
|
|
137
|
-
...(event.createLabelVals ?? []),
|
|
138
|
-
...(event.negateLabelVals ?? []),
|
|
139
|
-
])
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const isTakedownOrReverseTakedownEvent =
|
|
143
|
-
isTakedownEvent || isReverseTakedownEvent
|
|
144
|
-
if (isTakedownOrReverseTakedownEvent || isLabelEvent) {
|
|
145
|
-
const status = await moderationService.getStatus(subject)
|
|
146
|
-
|
|
147
|
-
if (status?.takendown && isTakedownEvent) {
|
|
148
|
-
throw new InvalidRequestError(`Subject is already taken down`)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (!status?.takendown && isReverseTakedownEvent) {
|
|
152
|
-
throw new InvalidRequestError(`Subject is not taken down`)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (status?.tags?.length) {
|
|
156
|
-
const protectedTags = await getProtectedTags(
|
|
157
|
-
settingService,
|
|
158
|
-
ctx.cfg.service.did,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if (protectedTags) {
|
|
162
|
-
assertProtectedTagAction({
|
|
163
|
-
protectedTags,
|
|
164
|
-
subjectTags: status.tags,
|
|
165
|
-
actionAuthor: createdBy,
|
|
166
|
-
isAdmin: auth.credentials.isAdmin,
|
|
167
|
-
isModerator: auth.credentials.isModerator,
|
|
168
|
-
isTriage: auth.credentials.isTriage,
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (status?.takendown && isReverseTakedownEvent && subject.isRecord()) {
|
|
174
|
-
// due to the way blob status is modeled, we should reverse takedown on all
|
|
175
|
-
// blobs for the record being restored, which aren't taken down on another record.
|
|
176
|
-
subject.blobCids = status.blobCids ?? []
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (isModEventEmail(event) && event.content) {
|
|
181
|
-
// sending email prior to logging the event to avoid a long transaction below
|
|
182
|
-
if (!subject.isRepo()) {
|
|
183
|
-
throw new InvalidRequestError('Email can only be sent to a repo subject')
|
|
184
|
-
}
|
|
185
|
-
const { content, subjectLine } = event
|
|
186
|
-
// on error, don't fail the whole event. instead, log the event data with isDelivered false
|
|
187
|
-
try {
|
|
188
|
-
await retryHttp(() =>
|
|
189
|
-
ctx.modService(db).sendEmail({
|
|
190
|
-
subject: subjectLine,
|
|
191
|
-
content,
|
|
192
|
-
recipientDid: subject.did,
|
|
193
|
-
}),
|
|
194
|
-
)
|
|
195
|
-
event.isDelivered = true
|
|
196
|
-
} catch (err) {
|
|
197
|
-
event.isDelivered = false
|
|
198
|
-
httpLogger.error({ err, event }, 'failed to send mod event email')
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (isModEventDivert(event) && subject.isRecord()) {
|
|
203
|
-
if (!ctx.blobDiverter) {
|
|
204
|
-
throw new InvalidRequestError(
|
|
205
|
-
'BlobDiverter not configured for this service',
|
|
206
|
-
)
|
|
207
|
-
}
|
|
208
|
-
await ctx.blobDiverter.uploadBlobOnService(subject.info())
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
(isModEventMuteReporter(event) || isModEventUnmuteReporter(event)) &&
|
|
213
|
-
!subject.isRepo()
|
|
214
|
-
) {
|
|
215
|
-
throw new InvalidRequestError('Subject must be a repo when muting reporter')
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (isModEventTag(event)) {
|
|
219
|
-
await assertTagAuth(settingService, ctx.cfg.service.did, event, auth)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (isModEventReport(event)) {
|
|
223
|
-
await ctx.moderationServiceProfile().validateReasonType(event.reportType)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const moderationEvent = await db.transaction(async (dbTxn) => {
|
|
227
|
-
const moderationTxn = ctx.modService(dbTxn)
|
|
228
|
-
|
|
229
|
-
if (externalId) {
|
|
230
|
-
const existingEvent = await moderationTxn.getEventByExternalId(
|
|
231
|
-
getEventType(event.$type),
|
|
232
|
-
externalId,
|
|
233
|
-
subject,
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
if (existingEvent) {
|
|
237
|
-
throw new InvalidRequestError(
|
|
238
|
-
`An event with the same external ID already exists for the subject.`,
|
|
239
|
-
'DuplicateExternalId',
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Validate reportAction if provided (actual processing happens after event is logged)
|
|
245
|
-
if (input.body.reportAction) {
|
|
246
|
-
// Validate that at least one targeting criteria is provided
|
|
247
|
-
const { reportAction } = input.body
|
|
248
|
-
if (
|
|
249
|
-
!reportAction.ids?.length &&
|
|
250
|
-
!reportAction.types?.length &&
|
|
251
|
-
!reportAction.all
|
|
252
|
-
) {
|
|
253
|
-
throw new InvalidRequestError(
|
|
254
|
-
'reportAction must specify ids, types, or all',
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const result = await moderationTxn.logEvent({
|
|
260
|
-
event,
|
|
261
|
-
subject,
|
|
262
|
-
createdBy,
|
|
263
|
-
modTool: input.body.modTool,
|
|
264
|
-
externalId,
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
// Update reports if reportAction was provided
|
|
268
|
-
if (input.body.reportAction) {
|
|
269
|
-
const subjectUri = subject.isRecord() ? subject.uri : null
|
|
270
|
-
try {
|
|
271
|
-
await processReportAction({
|
|
272
|
-
db: dbTxn,
|
|
273
|
-
reportAction: input.body.reportAction,
|
|
274
|
-
subjectDid: subject.did,
|
|
275
|
-
subjectUri,
|
|
276
|
-
eventId: result.event.id,
|
|
277
|
-
eventType: event.$type,
|
|
278
|
-
createdBy,
|
|
279
|
-
})
|
|
280
|
-
} catch (err) {
|
|
281
|
-
throw new InvalidRequestError(
|
|
282
|
-
err instanceof Error
|
|
283
|
-
? err.message
|
|
284
|
-
: 'Failed to process report action',
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const tagService = new TagService(
|
|
290
|
-
subject,
|
|
291
|
-
result.subjectStatus,
|
|
292
|
-
ctx.cfg.service.did,
|
|
293
|
-
moderationTxn,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
const initialTags = isModEventReport(event)
|
|
297
|
-
? [getTagForReport(event.reportType)]
|
|
298
|
-
: undefined
|
|
299
|
-
await tagService.evaluateForSubject(initialTags)
|
|
300
|
-
|
|
301
|
-
if (subject.isRepo()) {
|
|
302
|
-
if (isTakedownEvent) {
|
|
303
|
-
const isSuspend = !!result.event.durationInHours
|
|
304
|
-
await moderationTxn.takedownRepo(
|
|
305
|
-
subject,
|
|
306
|
-
result.event.id,
|
|
307
|
-
new Set(
|
|
308
|
-
result.event.meta?.targetServices
|
|
309
|
-
? `${result.event.meta.targetServices}`.split(',')
|
|
310
|
-
: undefined,
|
|
311
|
-
),
|
|
312
|
-
isSuspend,
|
|
313
|
-
)
|
|
314
|
-
} else if (isReverseTakedownEvent) {
|
|
315
|
-
await moderationTxn.reverseTakedownRepo(subject)
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (subject.isRecord()) {
|
|
320
|
-
if (isTakedownEvent) {
|
|
321
|
-
await moderationTxn.takedownRecord(
|
|
322
|
-
subject,
|
|
323
|
-
result.event.id,
|
|
324
|
-
new Set(
|
|
325
|
-
result.event.meta?.targetServices
|
|
326
|
-
? `${result.event.meta.targetServices}`.split(',')
|
|
327
|
-
: undefined,
|
|
328
|
-
),
|
|
329
|
-
)
|
|
330
|
-
} else if (isReverseTakedownEvent) {
|
|
331
|
-
await moderationTxn.reverseTakedownRecord(subject)
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
(isTakedownEvent || isAcknowledgeEvent) &&
|
|
337
|
-
result.event.meta?.acknowledgeAccountSubjects
|
|
338
|
-
) {
|
|
339
|
-
await moderationTxn.resolveSubjectsForAccount(
|
|
340
|
-
subject.did,
|
|
341
|
-
createdBy,
|
|
342
|
-
result.event,
|
|
343
|
-
)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (isLabelEvent) {
|
|
347
|
-
await moderationTxn.formatAndCreateLabels(
|
|
348
|
-
result.event.subjectUri ?? result.event.subjectDid,
|
|
349
|
-
result.event.subjectCid,
|
|
350
|
-
{
|
|
351
|
-
create: result.event.createLabelVals?.length
|
|
352
|
-
? result.event.createLabelVals.split(' ')
|
|
353
|
-
: undefined,
|
|
354
|
-
negate: result.event.negateLabelVals?.length
|
|
355
|
-
? result.event.negateLabelVals.split(' ')
|
|
356
|
-
: undefined,
|
|
357
|
-
},
|
|
358
|
-
result.event.durationInHours ?? undefined,
|
|
359
|
-
)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return result.event
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
return moderationService.views.formatEvent(moderationEvent)
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
export default function (server: Server, ctx: AppContext) {
|
|
369
|
-
server.tools.ozone.moderation.emitEvent({
|
|
370
|
-
auth: ctx.authVerifier.modOrAdminToken,
|
|
371
|
-
handler: async ({ input, auth }) => {
|
|
372
|
-
try {
|
|
373
|
-
const moderationEvent = await handleModerationEvent({
|
|
374
|
-
input,
|
|
375
|
-
auth,
|
|
376
|
-
ctx,
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
// On divert events, we need to automatically take down the blobs
|
|
380
|
-
if (isModEventDivert(input.body.event)) {
|
|
381
|
-
await handleModerationEvent({
|
|
382
|
-
auth,
|
|
383
|
-
ctx,
|
|
384
|
-
input: {
|
|
385
|
-
...input,
|
|
386
|
-
body: {
|
|
387
|
-
...input.body,
|
|
388
|
-
event: {
|
|
389
|
-
...input.body.event,
|
|
390
|
-
$type: 'tools.ozone.moderation.defs#modEventTakedown',
|
|
391
|
-
comment:
|
|
392
|
-
'[DIVERT_SIDE_EFFECT]: Automatically taking down after divert event',
|
|
393
|
-
},
|
|
394
|
-
modTool: input.body.modTool,
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
})
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return {
|
|
401
|
-
encoding: 'application/json',
|
|
402
|
-
body: moderationEvent,
|
|
403
|
-
}
|
|
404
|
-
} catch (err) {
|
|
405
|
-
httpLogger.error(
|
|
406
|
-
{ err, body: input.body },
|
|
407
|
-
'failed to emit moderation event',
|
|
408
|
-
)
|
|
409
|
-
throw err
|
|
410
|
-
}
|
|
411
|
-
},
|
|
412
|
-
})
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const assertTagAuth = async (
|
|
416
|
-
settingService: SettingService,
|
|
417
|
-
serviceDid: string,
|
|
418
|
-
event: ModEventTag,
|
|
419
|
-
auth: ModeratorOutput | AdminTokenOutput,
|
|
420
|
-
) => {
|
|
421
|
-
// admins can add/remove any tag
|
|
422
|
-
if (auth.credentials.isAdmin) return
|
|
423
|
-
|
|
424
|
-
const protectedTags = await getProtectedTags(settingService, serviceDid)
|
|
425
|
-
|
|
426
|
-
if (!protectedTags) {
|
|
427
|
-
return
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
for (const tag of Object.keys(protectedTags)) {
|
|
431
|
-
if (event.add.includes(tag) || event.remove.includes(tag)) {
|
|
432
|
-
// if specific moderators are configured to manage this tag but the current user
|
|
433
|
-
// is not one of them, then throw an error
|
|
434
|
-
const configuredModerators = protectedTags[tag]?.['moderators']
|
|
435
|
-
if (
|
|
436
|
-
configuredModerators &&
|
|
437
|
-
!configuredModerators.includes(auth.credentials.iss)
|
|
438
|
-
) {
|
|
439
|
-
throw new InvalidRequestError(`Not allowed to manage tag: ${tag}`)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const configuredRoles = protectedTags[tag]?.['roles']
|
|
443
|
-
if (configuredRoles) {
|
|
444
|
-
// admins can already do everything so we only check for moderator and triage role config
|
|
445
|
-
if (
|
|
446
|
-
auth.credentials.isModerator &&
|
|
447
|
-
!configuredRoles.includes('tools.ozone.team.defs#roleModerator')
|
|
448
|
-
) {
|
|
449
|
-
throw new InvalidRequestError(
|
|
450
|
-
`Can not manage tag ${tag} with moderator role`,
|
|
451
|
-
)
|
|
452
|
-
} else if (
|
|
453
|
-
auth.credentials.isTriage &&
|
|
454
|
-
!configuredRoles.includes('tools.ozone.team.defs#roleTriage')
|
|
455
|
-
) {
|
|
456
|
-
throw new InvalidRequestError(
|
|
457
|
-
`Can not manage tag ${tag} with triage role`,
|
|
458
|
-
)
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const validateLabels = (labels: string[]) => {
|
|
466
|
-
for (const label of labels) {
|
|
467
|
-
for (const char of badChars) {
|
|
468
|
-
if (label.includes(char)) {
|
|
469
|
-
throw new InvalidRequestError(`Invalid label: ${label}`)
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const badChars = [' ', ',', ';', `'`, `"`]
|