@atproto/ozone 0.0.16 → 0.0.17-next.0
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/dist/api/moderation/util.d.ts +1 -1
- package/dist/auth-verifier.d.ts +7 -11
- package/dist/config/config.d.ts +1 -0
- package/dist/config/env.d.ts +1 -2
- package/dist/config/secrets.d.ts +0 -2
- package/dist/daemon/event-pusher.d.ts +2 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema/moderation_subject_status.d.ts +2 -2
- package/dist/index.js +821 -1055
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +8 -0
- package/dist/lexicon/lexicons.d.ts +298 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +23 -1
- package/dist/lexicon/types/app/bsky/embed/record.d.ts +2 -1
- package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +41 -0
- package/dist/lexicon/types/app/bsky/labeler/getServices.d.ts +36 -0
- package/dist/lexicon/types/app/bsky/labeler/service.d.ts +14 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -1
- package/dist/lexicon/types/com/atproto/label/defs.d.ts +18 -0
- package/dist/mod-service/index.d.ts +2 -2
- package/package.json +5 -4
- package/src/api/admin/createCommunicationTemplate.ts +1 -1
- package/src/api/admin/deleteCommunicationTemplate.ts +1 -1
- package/src/api/admin/emitModerationEvent.ts +6 -2
- package/src/api/admin/getModerationEvent.ts +1 -1
- package/src/api/admin/getRecord.ts +1 -1
- package/src/api/admin/getRepo.ts +1 -1
- package/src/api/admin/listCommunicationTemplates.ts +1 -1
- package/src/api/admin/queryModerationEvents.ts +1 -1
- package/src/api/admin/queryModerationStatuses.ts +1 -1
- package/src/api/admin/searchRepos.ts +1 -1
- package/src/api/admin/updateCommunicationTemplate.ts +1 -1
- package/src/api/admin/util.ts +1 -1
- package/src/api/moderation/createReport.ts +1 -2
- package/src/api/proxied.ts +8 -8
- package/src/api/temp/fetchLabels.ts +1 -1
- package/src/auth-verifier.ts +19 -29
- package/src/config/config.ts +10 -7
- package/src/config/env.ts +2 -4
- package/src/config/secrets.ts +0 -6
- package/src/context.ts +1 -3
- package/src/daemon/context.ts +2 -2
- package/src/daemon/event-pusher.ts +9 -1
- package/src/db/schema/moderation_subject_status.ts +6 -1
- package/src/lexicon/index.ts +23 -0
- package/src/lexicon/lexicons.ts +327 -1
- package/src/lexicon/types/app/bsky/actor/defs.ts +57 -1
- package/src/lexicon/types/app/bsky/embed/record.ts +2 -0
- package/src/lexicon/types/app/bsky/labeler/defs.ts +93 -0
- package/src/lexicon/types/app/bsky/labeler/getServices.ts +51 -0
- package/src/lexicon/types/app/bsky/labeler/service.ts +31 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +3 -0
- package/src/lexicon/types/com/atproto/label/defs.ts +68 -0
- package/src/mod-service/index.ts +4 -3
- package/src/mod-service/status.ts +42 -26
- package/tests/__snapshots__/get-record.test.ts.snap +4 -4
- package/tests/__snapshots__/get-repo.test.ts.snap +2 -2
- package/tests/communication-templates.test.ts +7 -7
- package/tests/get-record.test.ts +17 -7
- package/tests/get-repo.test.ts +24 -12
- package/tests/moderation-appeals.test.ts +24 -52
- package/tests/moderation-events.test.ts +87 -130
- package/tests/moderation-status-tags.test.ts +16 -31
- package/tests/moderation-statuses.test.ts +125 -58
- package/tests/moderation.test.ts +140 -287
- package/tests/repo-search.test.ts +11 -4
|
@@ -71,3 +71,71 @@ export function isSelfLabel(v: unknown): v is SelfLabel {
|
|
|
71
71
|
export function validateSelfLabel(v: unknown): ValidationResult {
|
|
72
72
|
return lexicons.validate('com.atproto.label.defs#selfLabel', v)
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
/** Declares a label value and its expected interpertations and behaviors. */
|
|
76
|
+
export interface LabelValueDefinition {
|
|
77
|
+
/** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */
|
|
78
|
+
identifier: string
|
|
79
|
+
/** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */
|
|
80
|
+
severity: 'inform' | 'alert' | 'none' | (string & {})
|
|
81
|
+
/** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */
|
|
82
|
+
blurs: 'content' | 'media' | 'none' | (string & {})
|
|
83
|
+
locales: LabelValueDefinitionStrings[]
|
|
84
|
+
[k: string]: unknown
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isLabelValueDefinition(v: unknown): v is LabelValueDefinition {
|
|
88
|
+
return (
|
|
89
|
+
isObj(v) &&
|
|
90
|
+
hasProp(v, '$type') &&
|
|
91
|
+
v.$type === 'com.atproto.label.defs#labelValueDefinition'
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function validateLabelValueDefinition(v: unknown): ValidationResult {
|
|
96
|
+
return lexicons.validate('com.atproto.label.defs#labelValueDefinition', v)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Strings which describe the label in the UI, localized into a specific language. */
|
|
100
|
+
export interface LabelValueDefinitionStrings {
|
|
101
|
+
/** The code of the language these strings are written in. */
|
|
102
|
+
lang: string
|
|
103
|
+
/** A short human-readable name for the label. */
|
|
104
|
+
name: string
|
|
105
|
+
/** A longer description of what the label means and why it might be applied. */
|
|
106
|
+
description: string
|
|
107
|
+
[k: string]: unknown
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function isLabelValueDefinitionStrings(
|
|
111
|
+
v: unknown,
|
|
112
|
+
): v is LabelValueDefinitionStrings {
|
|
113
|
+
return (
|
|
114
|
+
isObj(v) &&
|
|
115
|
+
hasProp(v, '$type') &&
|
|
116
|
+
v.$type === 'com.atproto.label.defs#labelValueDefinitionStrings'
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function validateLabelValueDefinitionStrings(
|
|
121
|
+
v: unknown,
|
|
122
|
+
): ValidationResult {
|
|
123
|
+
return lexicons.validate(
|
|
124
|
+
'com.atproto.label.defs#labelValueDefinitionStrings',
|
|
125
|
+
v,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type LabelValue =
|
|
130
|
+
| '!hide'
|
|
131
|
+
| '!no-promote'
|
|
132
|
+
| '!warn'
|
|
133
|
+
| '!no-unauthenticated'
|
|
134
|
+
| 'dmca-violation'
|
|
135
|
+
| 'doxxing'
|
|
136
|
+
| 'porn'
|
|
137
|
+
| 'sexual'
|
|
138
|
+
| 'nudity'
|
|
139
|
+
| 'nsfl'
|
|
140
|
+
| 'gore'
|
|
141
|
+
| (string & {})
|
package/src/mod-service/index.ts
CHANGED
|
@@ -455,7 +455,8 @@ export class ModerationService {
|
|
|
455
455
|
const takedownRef = `BSKY-${
|
|
456
456
|
isSuspend ? 'SUSPEND' : 'TAKEDOWN'
|
|
457
457
|
}-${takedownId}`
|
|
458
|
-
|
|
458
|
+
|
|
459
|
+
const values = this.eventPusher.takedowns.map((eventType) => ({
|
|
459
460
|
eventType,
|
|
460
461
|
subjectDid: subject.did,
|
|
461
462
|
takedownRef,
|
|
@@ -516,7 +517,7 @@ export class ModerationService {
|
|
|
516
517
|
async takedownRecord(subject: RecordSubject, takedownId: number) {
|
|
517
518
|
this.db.assertTransaction()
|
|
518
519
|
const takedownRef = `BSKY-TAKEDOWN-${takedownId}`
|
|
519
|
-
const values =
|
|
520
|
+
const values = this.eventPusher.takedowns.map((eventType) => ({
|
|
520
521
|
eventType,
|
|
521
522
|
subjectDid: subject.did,
|
|
522
523
|
subjectUri: subject.uri,
|
|
@@ -555,7 +556,7 @@ export class ModerationService {
|
|
|
555
556
|
|
|
556
557
|
if (blobCids && blobCids.length > 0) {
|
|
557
558
|
const blobValues: Insertable<BlobPushEvent>[] = []
|
|
558
|
-
for (const eventType of
|
|
559
|
+
for (const eventType of this.eventPusher.takedowns) {
|
|
559
560
|
for (const cid of blobCids) {
|
|
560
561
|
blobValues.push({
|
|
561
562
|
eventType,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
REVIEWOPEN,
|
|
8
8
|
REVIEWCLOSED,
|
|
9
9
|
REVIEWESCALATED,
|
|
10
|
+
REVIEWNONE,
|
|
10
11
|
} from '../lexicon/types/com/atproto/admin/defs'
|
|
11
12
|
import { ModerationEventRow, ModerationSubjectStatusRow } from './types'
|
|
12
13
|
import { HOUR } from '@atproto/common'
|
|
@@ -14,16 +15,22 @@ import { REASONAPPEAL } from '../lexicon/types/com/atproto/moderation/defs'
|
|
|
14
15
|
import { jsonb } from '../db/types'
|
|
15
16
|
|
|
16
17
|
const getSubjectStatusForModerationEvent = ({
|
|
18
|
+
currentStatus,
|
|
17
19
|
action,
|
|
18
20
|
createdBy,
|
|
19
21
|
createdAt,
|
|
20
22
|
durationInHours,
|
|
21
23
|
}: {
|
|
24
|
+
currentStatus?: ModerationSubjectStatusRow
|
|
22
25
|
action: string
|
|
23
26
|
createdBy: string
|
|
24
27
|
createdAt: string
|
|
25
28
|
durationInHours: number | null
|
|
26
|
-
}): Partial<ModerationSubjectStatusRow>
|
|
29
|
+
}): Partial<ModerationSubjectStatusRow> => {
|
|
30
|
+
const defaultReviewState = currentStatus
|
|
31
|
+
? currentStatus.reviewState
|
|
32
|
+
: REVIEWNONE
|
|
33
|
+
|
|
27
34
|
switch (action) {
|
|
28
35
|
case 'com.atproto.admin.defs#modEventAcknowledge':
|
|
29
36
|
return {
|
|
@@ -54,7 +61,9 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
54
61
|
return {
|
|
55
62
|
lastReviewedBy: createdBy,
|
|
56
63
|
muteUntil: null,
|
|
57
|
-
|
|
64
|
+
// It's not likely to receive an unmute event that does not already have a status row
|
|
65
|
+
// but if it does happen, default to unnecessary
|
|
66
|
+
reviewState: defaultReviewState,
|
|
58
67
|
lastReviewedAt: createdAt,
|
|
59
68
|
}
|
|
60
69
|
case 'com.atproto.admin.defs#modEventTakedown':
|
|
@@ -70,26 +79,29 @@ const getSubjectStatusForModerationEvent = ({
|
|
|
70
79
|
case 'com.atproto.admin.defs#modEventMute':
|
|
71
80
|
return {
|
|
72
81
|
lastReviewedBy: createdBy,
|
|
73
|
-
reviewState: REVIEWOPEN,
|
|
74
82
|
lastReviewedAt: createdAt,
|
|
75
83
|
// By default, mute for 24hrs
|
|
76
84
|
muteUntil: new Date(
|
|
77
85
|
Date.now() + (durationInHours || 24) * HOUR,
|
|
78
86
|
).toISOString(),
|
|
87
|
+
// It's not likely to receive a mute event on a subject that does not already have a status row
|
|
88
|
+
// but if it does happen, default to unnecessary
|
|
89
|
+
reviewState: defaultReviewState,
|
|
79
90
|
}
|
|
80
91
|
case 'com.atproto.admin.defs#modEventComment':
|
|
81
92
|
return {
|
|
82
93
|
lastReviewedBy: createdBy,
|
|
83
94
|
lastReviewedAt: createdAt,
|
|
95
|
+
reviewState: defaultReviewState,
|
|
84
96
|
}
|
|
85
97
|
case 'com.atproto.admin.defs#modEventTag':
|
|
86
|
-
return { tags: [] }
|
|
98
|
+
return { tags: [], reviewState: defaultReviewState }
|
|
87
99
|
case 'com.atproto.admin.defs#modEventResolveAppeal':
|
|
88
100
|
return {
|
|
89
101
|
appealed: false,
|
|
90
102
|
}
|
|
91
103
|
default:
|
|
92
|
-
return
|
|
104
|
+
return {}
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
|
|
@@ -114,23 +126,6 @@ export const adjustModerationSubjectStatus = async (
|
|
|
114
126
|
createdAt,
|
|
115
127
|
} = moderationEvent
|
|
116
128
|
|
|
117
|
-
const isAppealEvent =
|
|
118
|
-
action === 'com.atproto.admin.defs#modEventReport' &&
|
|
119
|
-
meta?.reportType === REASONAPPEAL
|
|
120
|
-
|
|
121
|
-
const subjectStatus = getSubjectStatusForModerationEvent({
|
|
122
|
-
action,
|
|
123
|
-
createdBy,
|
|
124
|
-
createdAt,
|
|
125
|
-
durationInHours: moderationEvent.durationInHours,
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// If there are no subjectStatus that means there are no side-effect of the incoming event
|
|
129
|
-
if (!subjectStatus) {
|
|
130
|
-
return null
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const now = new Date().toISOString()
|
|
134
129
|
// If subjectUri exists, it's not a repoRef so pass along the uri to get identifier back
|
|
135
130
|
const identifier = getStatusIdentifierFromSubject(subjectUri || subjectDid)
|
|
136
131
|
|
|
@@ -140,25 +135,46 @@ export const adjustModerationSubjectStatus = async (
|
|
|
140
135
|
.selectFrom('moderation_subject_status')
|
|
141
136
|
.where('did', '=', identifier.did)
|
|
142
137
|
.where('recordPath', '=', identifier.recordPath)
|
|
138
|
+
// Make sure we respect other updates that may be happening at the same time
|
|
139
|
+
.forUpdate()
|
|
143
140
|
.selectAll()
|
|
144
141
|
.executeTakeFirst()
|
|
145
142
|
|
|
143
|
+
const isAppealEvent =
|
|
144
|
+
action === 'com.atproto.admin.defs#modEventReport' &&
|
|
145
|
+
meta?.reportType === REASONAPPEAL
|
|
146
|
+
|
|
147
|
+
const subjectStatus = getSubjectStatusForModerationEvent({
|
|
148
|
+
currentStatus,
|
|
149
|
+
action,
|
|
150
|
+
createdBy,
|
|
151
|
+
createdAt,
|
|
152
|
+
durationInHours: moderationEvent.durationInHours,
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const now = new Date().toISOString()
|
|
146
156
|
if (
|
|
147
157
|
currentStatus?.reviewState === REVIEWESCALATED &&
|
|
148
|
-
subjectStatus.reviewState
|
|
158
|
+
subjectStatus.reviewState !== REVIEWCLOSED
|
|
149
159
|
) {
|
|
150
|
-
// If the current status is escalated
|
|
151
|
-
//
|
|
160
|
+
// If the current status is escalated only allow incoming events to move the state to
|
|
161
|
+
// reviewClosed because escalated subjects should never move to any other state
|
|
152
162
|
subjectStatus.reviewState = REVIEWESCALATED
|
|
153
163
|
}
|
|
154
164
|
|
|
165
|
+
if (currentStatus && subjectStatus.reviewState === REVIEWNONE) {
|
|
166
|
+
// reviewNone is ONLY allowed when there is no current status
|
|
167
|
+
// If there is a current status, it should not be allowed to move back to reviewNone
|
|
168
|
+
subjectStatus.reviewState = currentStatus.reviewState
|
|
169
|
+
}
|
|
170
|
+
|
|
155
171
|
// Set these because we don't want to override them if they're already set
|
|
156
172
|
const defaultData = {
|
|
157
173
|
comment: null,
|
|
158
174
|
// Defaulting reviewState to open for any event may not be the desired behavior.
|
|
159
175
|
// For instance, if a subject never had any event and we just want to leave a comment to keep an eye on it
|
|
160
176
|
// that shouldn't mean we want to review the subject
|
|
161
|
-
reviewState:
|
|
177
|
+
reviewState: REVIEWNONE,
|
|
162
178
|
recordCid: subjectCid || null,
|
|
163
179
|
}
|
|
164
180
|
const newStatus = {
|
|
@@ -11,7 +11,7 @@ Object {
|
|
|
11
11
|
"cid": "cids(0)",
|
|
12
12
|
"cts": "1970-01-01T00:00:00.000Z",
|
|
13
13
|
"neg": false,
|
|
14
|
-
"src": "user(
|
|
14
|
+
"src": "user(2)",
|
|
15
15
|
"uri": "record(0)",
|
|
16
16
|
"val": "!unspecced-takedown",
|
|
17
17
|
},
|
|
@@ -30,7 +30,7 @@ Object {
|
|
|
30
30
|
"id": 1,
|
|
31
31
|
"lastReportedAt": "1970-01-01T00:00:00.000Z",
|
|
32
32
|
"lastReviewedAt": "1970-01-01T00:00:00.000Z",
|
|
33
|
-
"lastReviewedBy": "
|
|
33
|
+
"lastReviewedBy": "user(1)",
|
|
34
34
|
"reviewState": "com.atproto.admin.defs#reviewClosed",
|
|
35
35
|
"subject": Object {
|
|
36
36
|
"$type": "com.atproto.repo.strongRef",
|
|
@@ -108,7 +108,7 @@ Object {
|
|
|
108
108
|
"cid": "cids(0)",
|
|
109
109
|
"cts": "1970-01-01T00:00:00.000Z",
|
|
110
110
|
"neg": false,
|
|
111
|
-
"src": "user(
|
|
111
|
+
"src": "user(2)",
|
|
112
112
|
"uri": "record(0)",
|
|
113
113
|
"val": "!unspecced-takedown",
|
|
114
114
|
},
|
|
@@ -127,7 +127,7 @@ Object {
|
|
|
127
127
|
"id": 1,
|
|
128
128
|
"lastReportedAt": "1970-01-01T00:00:00.000Z",
|
|
129
129
|
"lastReviewedAt": "1970-01-01T00:00:00.000Z",
|
|
130
|
-
"lastReviewedBy": "
|
|
130
|
+
"lastReviewedBy": "user(1)",
|
|
131
131
|
"reviewState": "com.atproto.admin.defs#reviewClosed",
|
|
132
132
|
"subject": Object {
|
|
133
133
|
"$type": "com.atproto.repo.strongRef",
|
|
@@ -12,7 +12,7 @@ Object {
|
|
|
12
12
|
Object {
|
|
13
13
|
"cts": "1970-01-01T00:00:00.000Z",
|
|
14
14
|
"neg": false,
|
|
15
|
-
"src": "user(
|
|
15
|
+
"src": "user(2)",
|
|
16
16
|
"uri": "user(0)",
|
|
17
17
|
"val": "!unspecced-takedown",
|
|
18
18
|
},
|
|
@@ -23,7 +23,7 @@ Object {
|
|
|
23
23
|
"id": 1,
|
|
24
24
|
"lastReportedAt": "1970-01-01T00:00:00.000Z",
|
|
25
25
|
"lastReviewedAt": "1970-01-01T00:00:00.000Z",
|
|
26
|
-
"lastReviewedBy": "
|
|
26
|
+
"lastReviewedBy": "user(1)",
|
|
27
27
|
"reviewState": "com.atproto.admin.defs#reviewClosed",
|
|
28
28
|
"subject": Object {
|
|
29
29
|
"$type": "com.atproto.admin.defs#repoRef",
|
|
@@ -31,7 +31,7 @@ describe('communication-templates', () => {
|
|
|
31
31
|
await agent.api.com.atproto.admin.listCommunicationTemplates(
|
|
32
32
|
{},
|
|
33
33
|
{
|
|
34
|
-
headers: network.ozone.
|
|
34
|
+
headers: await network.ozone.modHeaders('moderator'),
|
|
35
35
|
},
|
|
36
36
|
)
|
|
37
37
|
return data.communicationTemplates
|
|
@@ -44,7 +44,7 @@ describe('communication-templates', () => {
|
|
|
44
44
|
{ ...templateOne, createdBy: sc.dids.bob },
|
|
45
45
|
{
|
|
46
46
|
encoding: 'application/json',
|
|
47
|
-
headers: network.ozone.
|
|
47
|
+
headers: await network.ozone.modHeaders('moderator'),
|
|
48
48
|
},
|
|
49
49
|
)
|
|
50
50
|
await expect(moderatorReq).rejects.toThrow(
|
|
@@ -55,7 +55,7 @@ describe('communication-templates', () => {
|
|
|
55
55
|
{ ...templateOne, createdBy: sc.dids.bob },
|
|
56
56
|
{
|
|
57
57
|
encoding: 'application/json',
|
|
58
|
-
headers: network.ozone.
|
|
58
|
+
headers: await network.ozone.modHeaders('admin'),
|
|
59
59
|
},
|
|
60
60
|
)
|
|
61
61
|
|
|
@@ -79,7 +79,7 @@ describe('communication-templates', () => {
|
|
|
79
79
|
{ ...templateTwo, createdBy: sc.dids.bob },
|
|
80
80
|
{
|
|
81
81
|
encoding: 'application/json',
|
|
82
|
-
headers: network.ozone.
|
|
82
|
+
headers: await network.ozone.modHeaders('admin'),
|
|
83
83
|
},
|
|
84
84
|
)
|
|
85
85
|
|
|
@@ -95,7 +95,7 @@ describe('communication-templates', () => {
|
|
|
95
95
|
{ id: '1', updatedBy: sc.dids.bob, name: '1 Test template' },
|
|
96
96
|
{
|
|
97
97
|
encoding: 'application/json',
|
|
98
|
-
headers: network.ozone.
|
|
98
|
+
headers: await network.ozone.modHeaders('admin'),
|
|
99
99
|
},
|
|
100
100
|
)
|
|
101
101
|
|
|
@@ -109,7 +109,7 @@ describe('communication-templates', () => {
|
|
|
109
109
|
{ id: '1' },
|
|
110
110
|
{
|
|
111
111
|
encoding: 'application/json',
|
|
112
|
-
headers: network.ozone.
|
|
112
|
+
headers: await network.ozone.modHeaders('moderator'),
|
|
113
113
|
},
|
|
114
114
|
)
|
|
115
115
|
|
|
@@ -121,7 +121,7 @@ describe('communication-templates', () => {
|
|
|
121
121
|
{ id: '1' },
|
|
122
122
|
{
|
|
123
123
|
encoding: 'application/json',
|
|
124
|
-
headers: network.ozone.
|
|
124
|
+
headers: await network.ozone.modHeaders('admin'),
|
|
125
125
|
},
|
|
126
126
|
)
|
|
127
127
|
const list = await listTemplates()
|
package/tests/get-record.test.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SeedClient,
|
|
3
|
+
TestNetwork,
|
|
4
|
+
basicSeed,
|
|
5
|
+
TestOzone,
|
|
6
|
+
ModeratorClient,
|
|
7
|
+
} from '@atproto/dev-env'
|
|
2
8
|
import AtpAgent from '@atproto/api'
|
|
3
9
|
import { AtUri } from '@atproto/syntax'
|
|
4
10
|
import {
|
|
@@ -9,15 +15,19 @@ import { forSnapshot } from './_util'
|
|
|
9
15
|
|
|
10
16
|
describe('admin get record view', () => {
|
|
11
17
|
let network: TestNetwork
|
|
18
|
+
let ozone: TestOzone
|
|
12
19
|
let agent: AtpAgent
|
|
13
20
|
let sc: SeedClient
|
|
21
|
+
let modClient: ModeratorClient
|
|
14
22
|
|
|
15
23
|
beforeAll(async () => {
|
|
16
24
|
network = await TestNetwork.create({
|
|
17
25
|
dbPostgresSchema: 'ozone_admin_get_record',
|
|
18
26
|
})
|
|
19
|
-
|
|
27
|
+
ozone = network.ozone
|
|
28
|
+
agent = ozone.getClient()
|
|
20
29
|
sc = network.getSeedClient()
|
|
30
|
+
modClient = ozone.getModClient()
|
|
21
31
|
await basicSeed(sc)
|
|
22
32
|
await network.processAll()
|
|
23
33
|
})
|
|
@@ -46,7 +56,7 @@ describe('admin get record view', () => {
|
|
|
46
56
|
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
47
57
|
},
|
|
48
58
|
})
|
|
49
|
-
await
|
|
59
|
+
await modClient.emitModerationEvent({
|
|
50
60
|
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
51
61
|
subject: {
|
|
52
62
|
$type: 'com.atproto.repo.strongRef',
|
|
@@ -59,7 +69,7 @@ describe('admin get record view', () => {
|
|
|
59
69
|
it('gets a record by uri, even when taken down.', async () => {
|
|
60
70
|
const result = await agent.api.com.atproto.admin.getRecord(
|
|
61
71
|
{ uri: sc.posts[sc.dids.alice][0].ref.uriStr },
|
|
62
|
-
{ headers:
|
|
72
|
+
{ headers: await ozone.modHeaders() },
|
|
63
73
|
)
|
|
64
74
|
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
65
75
|
})
|
|
@@ -70,7 +80,7 @@ describe('admin get record view', () => {
|
|
|
70
80
|
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
71
81
|
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
72
82
|
},
|
|
73
|
-
{ headers:
|
|
83
|
+
{ headers: await ozone.modHeaders() },
|
|
74
84
|
)
|
|
75
85
|
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
76
86
|
})
|
|
@@ -84,7 +94,7 @@ describe('admin get record view', () => {
|
|
|
84
94
|
'badrkey',
|
|
85
95
|
).toString(),
|
|
86
96
|
},
|
|
87
|
-
{ headers:
|
|
97
|
+
{ headers: await ozone.modHeaders() },
|
|
88
98
|
)
|
|
89
99
|
await expect(promise).rejects.toThrow('Record not found')
|
|
90
100
|
})
|
|
@@ -95,7 +105,7 @@ describe('admin get record view', () => {
|
|
|
95
105
|
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
96
106
|
cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid
|
|
97
107
|
},
|
|
98
|
-
{ headers:
|
|
108
|
+
{ headers: await ozone.modHeaders() },
|
|
99
109
|
)
|
|
100
110
|
await expect(promise).rejects.toThrow('Record not found')
|
|
101
111
|
})
|
package/tests/get-repo.test.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SeedClient,
|
|
3
|
+
TestNetwork,
|
|
4
|
+
TestOzone,
|
|
5
|
+
basicSeed,
|
|
6
|
+
ModeratorClient,
|
|
7
|
+
} from '@atproto/dev-env'
|
|
2
8
|
import AtpAgent from '@atproto/api'
|
|
3
9
|
import {
|
|
4
10
|
REASONOTHER,
|
|
@@ -8,15 +14,21 @@ import { forSnapshot } from './_util'
|
|
|
8
14
|
|
|
9
15
|
describe('admin get repo view', () => {
|
|
10
16
|
let network: TestNetwork
|
|
17
|
+
let ozone: TestOzone
|
|
11
18
|
let agent: AtpAgent
|
|
19
|
+
let pdsAgent: AtpAgent
|
|
12
20
|
let sc: SeedClient
|
|
21
|
+
let modClient: ModeratorClient
|
|
13
22
|
|
|
14
23
|
beforeAll(async () => {
|
|
15
24
|
network = await TestNetwork.create({
|
|
16
25
|
dbPostgresSchema: 'ozone_admin_get_repo',
|
|
17
26
|
})
|
|
18
|
-
|
|
27
|
+
ozone = network.ozone
|
|
28
|
+
agent = ozone.getClient()
|
|
29
|
+
pdsAgent = network.pds.getClient()
|
|
19
30
|
sc = network.getSeedClient()
|
|
31
|
+
modClient = ozone.getModClient()
|
|
20
32
|
await basicSeed(sc)
|
|
21
33
|
await network.processAll()
|
|
22
34
|
})
|
|
@@ -26,7 +38,7 @@ describe('admin get repo view', () => {
|
|
|
26
38
|
})
|
|
27
39
|
|
|
28
40
|
beforeAll(async () => {
|
|
29
|
-
await
|
|
41
|
+
await modClient.emitModerationEvent({
|
|
30
42
|
event: { $type: 'com.atproto.admin.defs#modEventAcknowledge' },
|
|
31
43
|
subject: {
|
|
32
44
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
@@ -50,7 +62,7 @@ describe('admin get repo view', () => {
|
|
|
50
62
|
did: sc.dids.alice,
|
|
51
63
|
},
|
|
52
64
|
})
|
|
53
|
-
await
|
|
65
|
+
await modClient.emitModerationEvent({
|
|
54
66
|
event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
|
|
55
67
|
subject: {
|
|
56
68
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
@@ -62,7 +74,7 @@ describe('admin get repo view', () => {
|
|
|
62
74
|
it('gets a repo by did, even when taken down.', async () => {
|
|
63
75
|
const result = await agent.api.com.atproto.admin.getRepo(
|
|
64
76
|
{ did: sc.dids.alice },
|
|
65
|
-
{ headers:
|
|
77
|
+
{ headers: await ozone.modHeaders() },
|
|
66
78
|
)
|
|
67
79
|
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
68
80
|
})
|
|
@@ -70,15 +82,15 @@ describe('admin get repo view', () => {
|
|
|
70
82
|
it('does not include account emails for triage mods.', async () => {
|
|
71
83
|
const { data: admin } = await agent.api.com.atproto.admin.getRepo(
|
|
72
84
|
{ did: sc.dids.bob },
|
|
73
|
-
{ headers:
|
|
85
|
+
{ headers: await ozone.modHeaders() },
|
|
74
86
|
)
|
|
75
87
|
const { data: moderator } = await agent.api.com.atproto.admin.getRepo(
|
|
76
88
|
{ did: sc.dids.bob },
|
|
77
|
-
{ headers:
|
|
89
|
+
{ headers: await ozone.modHeaders('moderator') },
|
|
78
90
|
)
|
|
79
91
|
const { data: triage } = await agent.api.com.atproto.admin.getRepo(
|
|
80
92
|
{ did: sc.dids.bob },
|
|
81
|
-
{ headers:
|
|
93
|
+
{ headers: await ozone.modHeaders('triage') },
|
|
82
94
|
)
|
|
83
95
|
expect(admin.email).toEqual('bob@test.com')
|
|
84
96
|
expect(moderator.email).toEqual('bob@test.com')
|
|
@@ -90,7 +102,7 @@ describe('admin get repo view', () => {
|
|
|
90
102
|
const { data: beforeEmailVerification } =
|
|
91
103
|
await agent.api.com.atproto.admin.getRepo(
|
|
92
104
|
{ did: sc.dids.bob },
|
|
93
|
-
{ headers:
|
|
105
|
+
{ headers: await ozone.modHeaders() },
|
|
94
106
|
)
|
|
95
107
|
|
|
96
108
|
expect(beforeEmailVerification.emailConfirmedAt).toBeUndefined()
|
|
@@ -101,7 +113,7 @@ describe('admin get repo view', () => {
|
|
|
101
113
|
sc.dids.bob,
|
|
102
114
|
'confirm_email',
|
|
103
115
|
)
|
|
104
|
-
await
|
|
116
|
+
await pdsAgent.api.com.atproto.server.confirmEmail(
|
|
105
117
|
{ email: bobsAccount.email, token: verificationToken },
|
|
106
118
|
{
|
|
107
119
|
encoding: 'application/json',
|
|
@@ -112,7 +124,7 @@ describe('admin get repo view', () => {
|
|
|
112
124
|
const { data: afterEmailVerification } =
|
|
113
125
|
await agent.api.com.atproto.admin.getRepo(
|
|
114
126
|
{ did: sc.dids.bob },
|
|
115
|
-
{ headers:
|
|
127
|
+
{ headers: await ozone.modHeaders() },
|
|
116
128
|
)
|
|
117
129
|
|
|
118
130
|
expect(afterEmailVerification.emailConfirmedAt).toBeTruthy()
|
|
@@ -124,7 +136,7 @@ describe('admin get repo view', () => {
|
|
|
124
136
|
it('fails when repo does not exist.', async () => {
|
|
125
137
|
const promise = agent.api.com.atproto.admin.getRepo(
|
|
126
138
|
{ did: 'did:plc:doesnotexist' },
|
|
127
|
-
{ headers:
|
|
139
|
+
{ headers: await ozone.modHeaders() },
|
|
128
140
|
)
|
|
129
141
|
await expect(promise).rejects.toThrow('Repo not found')
|
|
130
142
|
})
|