@atproto/ozone 0.1.108 → 0.1.109
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 +17 -0
- package/dist/api/health.js +1 -1
- package/dist/api/health.js.map +1 -1
- package/dist/api/verification/grantVerifications.d.ts.map +1 -1
- package/dist/api/verification/grantVerifications.js +9 -1
- package/dist/api/verification/grantVerifications.js.map +1 -1
- package/dist/background.js +1 -1
- package/dist/background.js.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/jetstream/service.d.ts +1 -5
- package/dist/jetstream/service.d.ts.map +1 -1
- package/dist/jetstream/service.js +1 -1
- package/dist/jetstream/service.js.map +1 -1
- package/dist/lexicon/index.d.ts +3 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -1
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +160 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +81 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +21 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js +9 -0
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/status.d.ts +23 -0
- package/dist/lexicon/types/app/bsky/actor/status.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/actor/status.js +19 -0
- package/dist/lexicon/types/app/bsky/actor/status.js.map +1 -0
- package/dist/mod-service/index.js +1 -1
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js +2 -0
- package/dist/mod-service/views.js.map +1 -1
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +2 -2
- package/dist/team/index.js.map +1 -1
- package/package.json +9 -9
- package/src/api/health.ts +1 -1
- package/src/api/verification/grantVerifications.ts +17 -1
- package/src/background.ts +1 -1
- package/src/error.ts +1 -1
- package/src/jetstream/service.ts +2 -8
- package/src/lexicon/index.ts +3 -0
- package/src/lexicon/lexicons.ts +85 -0
- package/src/lexicon/types/app/bsky/actor/defs.ts +26 -0
- package/src/lexicon/types/app/bsky/actor/status.ts +40 -0
- package/src/mod-service/index.ts +1 -1
- package/src/mod-service/views.ts +4 -0
- package/src/team/index.ts +2 -5
- package/tests/expiring-label.test.ts +72 -0
- package/tests/verification.test.ts +30 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/ozone",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backend service for moderating the Bluesky network.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"uint8arrays": "3.0.0",
|
|
36
36
|
"undici": "^6.14.1",
|
|
37
37
|
"ws": "^8.12.0",
|
|
38
|
-
"@atproto/api": "^0.15.
|
|
39
|
-
"@atproto/common": "^0.4.
|
|
38
|
+
"@atproto/api": "^0.15.6",
|
|
39
|
+
"@atproto/common": "^0.4.11",
|
|
40
40
|
"@atproto/crypto": "^0.4.4",
|
|
41
|
-
"@atproto/identity": "^0.4.
|
|
42
|
-
"@atproto/lexicon": "^0.4.
|
|
41
|
+
"@atproto/identity": "^0.4.8",
|
|
42
|
+
"@atproto/lexicon": "^0.4.11",
|
|
43
43
|
"@atproto/syntax": "^0.4.0",
|
|
44
|
-
"@atproto/xrpc": "^0.
|
|
45
|
-
"@atproto/xrpc-server": "^0.7.
|
|
44
|
+
"@atproto/xrpc": "^0.7.0",
|
|
45
|
+
"@atproto/xrpc-server": "^0.7.18"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@did-plc/server": "^0.0.1",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"jest": "^28.1.2",
|
|
55
55
|
"ts-node": "^10.8.2",
|
|
56
56
|
"typescript": "^5.6.3",
|
|
57
|
-
"@atproto/lex-cli": "^0.8.
|
|
58
|
-
"@atproto/pds": "^0.4.
|
|
57
|
+
"@atproto/lex-cli": "^0.8.1",
|
|
58
|
+
"@atproto/pds": "^0.4.135"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/tools/ozone/*/*",
|
package/src/api/health.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const createRouter = (ctx: AppContext): Router => {
|
|
|
17
17
|
try {
|
|
18
18
|
await sql`select 1`.execute(ctx.db.db)
|
|
19
19
|
} catch (err) {
|
|
20
|
-
req.log.error(err, 'failed health check')
|
|
20
|
+
req.log.error({ err }, 'failed health check')
|
|
21
21
|
return res.status(503).send({ version, error: 'Service Unavailable' })
|
|
22
22
|
}
|
|
23
23
|
res.send({ version })
|
|
@@ -20,10 +20,26 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const modViews = ctx.modService(ctx.db).views
|
|
23
|
+
const profilesBefore = await modViews.getProfiles(
|
|
24
|
+
input.body.verifications.map((v) => v.subject),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// Filter out any subject for which, the current issuer already has a valid verification record indexed
|
|
28
|
+
const verificationsToBeGranted = input.body.verifications.filter(
|
|
29
|
+
(verificationInput) => {
|
|
30
|
+
const hasValidVerification = profilesBefore
|
|
31
|
+
.get(verificationInput.subject)
|
|
32
|
+
?.verification?.verifications.find(
|
|
33
|
+
(v) => v.issuer === ctx.cfg.verifier?.did && v.isValid,
|
|
34
|
+
)
|
|
35
|
+
return !hasValidVerification
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
|
|
23
39
|
const verificationIssuer = ctx.verificationIssuer(ctx.cfg.verifier)
|
|
24
40
|
const verificationService = ctx.verificationService(ctx.db)
|
|
25
41
|
const { grantedVerifications, failedVerifications } =
|
|
26
|
-
await verificationIssuer.verify(
|
|
42
|
+
await verificationIssuer.verify(verificationsToBeGranted)
|
|
27
43
|
|
|
28
44
|
if (!grantedVerifications.length) {
|
|
29
45
|
return {
|
package/src/background.ts
CHANGED
|
@@ -63,7 +63,7 @@ export class BackgroundQueue {
|
|
|
63
63
|
await task(this.db, abortController.signal)
|
|
64
64
|
} catch (err) {
|
|
65
65
|
if (!isCausedBySignal(err, abortController.signal)) {
|
|
66
|
-
dbLogger.error(err, 'background queue task failed')
|
|
66
|
+
dbLogger.error({ err }, 'background queue task failed')
|
|
67
67
|
}
|
|
68
68
|
} finally {
|
|
69
69
|
abortController.abort()
|
package/src/error.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { XRPCError } from '@atproto/xrpc-server'
|
|
|
3
3
|
import { httpLogger as log } from './logger'
|
|
4
4
|
|
|
5
5
|
export const handler: ErrorRequestHandler = (err, _req, res, next) => {
|
|
6
|
-
log.error(err, 'unexpected internal server error')
|
|
6
|
+
log.error({ err }, 'unexpected internal server error')
|
|
7
7
|
if (res.headersSent) {
|
|
8
8
|
return next(err)
|
|
9
9
|
}
|
package/src/jetstream/service.ts
CHANGED
|
@@ -6,11 +6,7 @@ type OnCreateCallback<T extends JetstreamRecord> = (
|
|
|
6
6
|
) => Promise<void>
|
|
7
7
|
|
|
8
8
|
export type JetstreamOptions = {
|
|
9
|
-
|
|
10
|
-
* The full subscription endpoint to connect to.
|
|
11
|
-
* @default "wss://jetstream1.us-east.bsky.network/subscribe"
|
|
12
|
-
*/
|
|
13
|
-
endpoint?: string
|
|
9
|
+
endpoint: string
|
|
14
10
|
/**
|
|
15
11
|
* The record collections that you want to receive updates for.
|
|
16
12
|
* Leave this empty to receive updates for all record collections.
|
|
@@ -61,9 +57,7 @@ export class Jetstream {
|
|
|
61
57
|
public cursor?: number
|
|
62
58
|
|
|
63
59
|
constructor(opts: JetstreamOptions) {
|
|
64
|
-
this.url = new URL(
|
|
65
|
-
opts.endpoint ?? 'wss://jetstream1.us-east.bsky.network/subscribe',
|
|
66
|
-
)
|
|
60
|
+
this.url = new URL(opts.endpoint)
|
|
67
61
|
opts.wantedCollections?.forEach((collection) => {
|
|
68
62
|
this.url.searchParams.append('wantedCollections', collection)
|
|
69
63
|
})
|
package/src/lexicon/index.ts
CHANGED
|
@@ -230,6 +230,9 @@ export const COM_ATPROTO_MODERATION = {
|
|
|
230
230
|
DefsReasonOther: 'com.atproto.moderation.defs#reasonOther',
|
|
231
231
|
DefsReasonAppeal: 'com.atproto.moderation.defs#reasonAppeal',
|
|
232
232
|
}
|
|
233
|
+
export const APP_BSKY_ACTOR = {
|
|
234
|
+
StatusLive: 'app.bsky.actor.status#live',
|
|
235
|
+
}
|
|
233
236
|
export const APP_BSKY_FEED = {
|
|
234
237
|
DefsRequestLess: 'app.bsky.feed.defs#requestLess',
|
|
235
238
|
DefsRequestMore: 'app.bsky.feed.defs#requestMore',
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -4568,6 +4568,10 @@ export const schemaDict = {
|
|
|
4568
4568
|
type: 'ref',
|
|
4569
4569
|
ref: 'lex:app.bsky.actor.defs#verificationState',
|
|
4570
4570
|
},
|
|
4571
|
+
status: {
|
|
4572
|
+
type: 'ref',
|
|
4573
|
+
ref: 'lex:app.bsky.actor.defs#statusView',
|
|
4574
|
+
},
|
|
4571
4575
|
},
|
|
4572
4576
|
},
|
|
4573
4577
|
profileView: {
|
|
@@ -4623,6 +4627,10 @@ export const schemaDict = {
|
|
|
4623
4627
|
type: 'ref',
|
|
4624
4628
|
ref: 'lex:app.bsky.actor.defs#verificationState',
|
|
4625
4629
|
},
|
|
4630
|
+
status: {
|
|
4631
|
+
type: 'ref',
|
|
4632
|
+
ref: 'lex:app.bsky.actor.defs#statusView',
|
|
4633
|
+
},
|
|
4626
4634
|
},
|
|
4627
4635
|
},
|
|
4628
4636
|
profileViewDetailed: {
|
|
@@ -4699,6 +4707,10 @@ export const schemaDict = {
|
|
|
4699
4707
|
type: 'ref',
|
|
4700
4708
|
ref: 'lex:app.bsky.actor.defs#verificationState',
|
|
4701
4709
|
},
|
|
4710
|
+
status: {
|
|
4711
|
+
type: 'ref',
|
|
4712
|
+
ref: 'lex:app.bsky.actor.defs#statusView',
|
|
4713
|
+
},
|
|
4702
4714
|
},
|
|
4703
4715
|
},
|
|
4704
4716
|
profileAssociated: {
|
|
@@ -5240,6 +5252,36 @@ export const schemaDict = {
|
|
|
5240
5252
|
},
|
|
5241
5253
|
},
|
|
5242
5254
|
},
|
|
5255
|
+
statusView: {
|
|
5256
|
+
type: 'object',
|
|
5257
|
+
required: ['status', 'record'],
|
|
5258
|
+
properties: {
|
|
5259
|
+
status: {
|
|
5260
|
+
type: 'string',
|
|
5261
|
+
description: 'The status for the account.',
|
|
5262
|
+
knownValues: ['app.bsky.actor.status#live'],
|
|
5263
|
+
},
|
|
5264
|
+
record: {
|
|
5265
|
+
type: 'unknown',
|
|
5266
|
+
},
|
|
5267
|
+
embed: {
|
|
5268
|
+
type: 'union',
|
|
5269
|
+
description: 'An optional embed associated with the status.',
|
|
5270
|
+
refs: ['lex:app.bsky.embed.external#view'],
|
|
5271
|
+
},
|
|
5272
|
+
expiresAt: {
|
|
5273
|
+
type: 'string',
|
|
5274
|
+
description:
|
|
5275
|
+
'The date when this status will expire. The application might choose to no longer return the status after expiration.',
|
|
5276
|
+
format: 'datetime',
|
|
5277
|
+
},
|
|
5278
|
+
isActive: {
|
|
5279
|
+
type: 'boolean',
|
|
5280
|
+
description:
|
|
5281
|
+
'True if the status is not expired, false if it is expired. Only present if expiration was set.',
|
|
5282
|
+
},
|
|
5283
|
+
},
|
|
5284
|
+
},
|
|
5243
5285
|
},
|
|
5244
5286
|
},
|
|
5245
5287
|
AppBskyActorGetPreferences: {
|
|
@@ -5569,6 +5611,48 @@ export const schemaDict = {
|
|
|
5569
5611
|
},
|
|
5570
5612
|
},
|
|
5571
5613
|
},
|
|
5614
|
+
AppBskyActorStatus: {
|
|
5615
|
+
lexicon: 1,
|
|
5616
|
+
id: 'app.bsky.actor.status',
|
|
5617
|
+
defs: {
|
|
5618
|
+
main: {
|
|
5619
|
+
type: 'record',
|
|
5620
|
+
description: 'A declaration of a Bluesky account status.',
|
|
5621
|
+
key: 'literal:self',
|
|
5622
|
+
record: {
|
|
5623
|
+
type: 'object',
|
|
5624
|
+
required: ['status', 'createdAt'],
|
|
5625
|
+
properties: {
|
|
5626
|
+
status: {
|
|
5627
|
+
type: 'string',
|
|
5628
|
+
description: 'The status for the account.',
|
|
5629
|
+
knownValues: ['app.bsky.actor.status#live'],
|
|
5630
|
+
},
|
|
5631
|
+
embed: {
|
|
5632
|
+
type: 'union',
|
|
5633
|
+
description: 'An optional embed associated with the status.',
|
|
5634
|
+
refs: ['lex:app.bsky.embed.external'],
|
|
5635
|
+
},
|
|
5636
|
+
durationMinutes: {
|
|
5637
|
+
type: 'integer',
|
|
5638
|
+
description:
|
|
5639
|
+
'The duration of the status in minutes. Applications can choose to impose minimum and maximum limits.',
|
|
5640
|
+
minimum: 1,
|
|
5641
|
+
},
|
|
5642
|
+
createdAt: {
|
|
5643
|
+
type: 'string',
|
|
5644
|
+
format: 'datetime',
|
|
5645
|
+
},
|
|
5646
|
+
},
|
|
5647
|
+
},
|
|
5648
|
+
},
|
|
5649
|
+
live: {
|
|
5650
|
+
type: 'token',
|
|
5651
|
+
description:
|
|
5652
|
+
'Advertises an account as currently offering live content.',
|
|
5653
|
+
},
|
|
5654
|
+
},
|
|
5655
|
+
},
|
|
5572
5656
|
AppBskyEmbedDefs: {
|
|
5573
5657
|
lexicon: 1,
|
|
5574
5658
|
id: 'app.bsky.embed.defs',
|
|
@@ -16113,6 +16197,7 @@ export const ids = {
|
|
|
16113
16197
|
AppBskyActorPutPreferences: 'app.bsky.actor.putPreferences',
|
|
16114
16198
|
AppBskyActorSearchActors: 'app.bsky.actor.searchActors',
|
|
16115
16199
|
AppBskyActorSearchActorsTypeahead: 'app.bsky.actor.searchActorsTypeahead',
|
|
16200
|
+
AppBskyActorStatus: 'app.bsky.actor.status',
|
|
16116
16201
|
AppBskyEmbedDefs: 'app.bsky.embed.defs',
|
|
16117
16202
|
AppBskyEmbedExternal: 'app.bsky.embed.external',
|
|
16118
16203
|
AppBskyEmbedImages: 'app.bsky.embed.images',
|
|
@@ -14,6 +14,7 @@ import type * as AppBskyGraphDefs from '../graph/defs.js'
|
|
|
14
14
|
import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef.js'
|
|
15
15
|
import type * as AppBskyFeedThreadgate from '../feed/threadgate.js'
|
|
16
16
|
import type * as AppBskyFeedPostgate from '../feed/postgate.js'
|
|
17
|
+
import type * as AppBskyEmbedExternal from '../embed/external.js'
|
|
17
18
|
|
|
18
19
|
const is$typed = _is$typed,
|
|
19
20
|
validate = _validate
|
|
@@ -30,6 +31,7 @@ export interface ProfileViewBasic {
|
|
|
30
31
|
labels?: ComAtprotoLabelDefs.Label[]
|
|
31
32
|
createdAt?: string
|
|
32
33
|
verification?: VerificationState
|
|
34
|
+
status?: StatusView
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
const hashProfileViewBasic = 'profileViewBasic'
|
|
@@ -55,6 +57,7 @@ export interface ProfileView {
|
|
|
55
57
|
viewer?: ViewerState
|
|
56
58
|
labels?: ComAtprotoLabelDefs.Label[]
|
|
57
59
|
verification?: VerificationState
|
|
60
|
+
status?: StatusView
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
const hashProfileView = 'profileView'
|
|
@@ -86,6 +89,7 @@ export interface ProfileViewDetailed {
|
|
|
86
89
|
labels?: ComAtprotoLabelDefs.Label[]
|
|
87
90
|
pinnedPost?: ComAtprotoRepoStrongRef.Main
|
|
88
91
|
verification?: VerificationState
|
|
92
|
+
status?: StatusView
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
const hashProfileViewDetailed = 'profileViewDetailed'
|
|
@@ -592,3 +596,25 @@ export function validatePostInteractionSettingsPref<V>(v: V) {
|
|
|
592
596
|
hashPostInteractionSettingsPref,
|
|
593
597
|
)
|
|
594
598
|
}
|
|
599
|
+
|
|
600
|
+
export interface StatusView {
|
|
601
|
+
$type?: 'app.bsky.actor.defs#statusView'
|
|
602
|
+
/** The status for the account. */
|
|
603
|
+
status: 'app.bsky.actor.status#live' | (string & {})
|
|
604
|
+
record: { [_ in string]: unknown }
|
|
605
|
+
embed?: $Typed<AppBskyEmbedExternal.View> | { $type: string }
|
|
606
|
+
/** The date when this status will expire. The application might choose to no longer return the status after expiration. */
|
|
607
|
+
expiresAt?: string
|
|
608
|
+
/** True if the status is not expired, false if it is expired. Only present if expiration was set. */
|
|
609
|
+
isActive?: boolean
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const hashStatusView = 'statusView'
|
|
613
|
+
|
|
614
|
+
export function isStatusView<V>(v: V) {
|
|
615
|
+
return is$typed(v, id, hashStatusView)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export function validateStatusView<V>(v: V) {
|
|
619
|
+
return validate<StatusView & V>(v, id, hashStatusView)
|
|
620
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
|
3
|
+
*/
|
|
4
|
+
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
|
|
5
|
+
import { CID } from 'multiformats/cid'
|
|
6
|
+
import { validate as _validate } from '../../../../lexicons'
|
|
7
|
+
import {
|
|
8
|
+
type $Typed,
|
|
9
|
+
is$typed as _is$typed,
|
|
10
|
+
type OmitKey,
|
|
11
|
+
} from '../../../../util'
|
|
12
|
+
import type * as AppBskyEmbedExternal from '../embed/external.js'
|
|
13
|
+
|
|
14
|
+
const is$typed = _is$typed,
|
|
15
|
+
validate = _validate
|
|
16
|
+
const id = 'app.bsky.actor.status'
|
|
17
|
+
|
|
18
|
+
export interface Record {
|
|
19
|
+
$type: 'app.bsky.actor.status'
|
|
20
|
+
/** The status for the account. */
|
|
21
|
+
status: 'app.bsky.actor.status#live' | (string & {})
|
|
22
|
+
embed?: $Typed<AppBskyEmbedExternal.Main> | { $type: string }
|
|
23
|
+
/** The duration of the status in minutes. Applications can choose to impose minimum and maximum limits. */
|
|
24
|
+
durationMinutes?: number
|
|
25
|
+
createdAt: string
|
|
26
|
+
[k: string]: unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hashRecord = 'main'
|
|
30
|
+
|
|
31
|
+
export function isRecord<V>(v: V) {
|
|
32
|
+
return is$typed(v, id, hashRecord)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function validateRecord<V>(v: V) {
|
|
36
|
+
return validate<Record & V>(v, id, hashRecord, true)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Advertises an account as currently offering live content. */
|
|
40
|
+
export const LIVE = `${id}#live`
|
package/src/mod-service/index.ts
CHANGED
package/src/mod-service/views.ts
CHANGED
|
@@ -544,10 +544,14 @@ export class ModerationViews {
|
|
|
544
544
|
subjects: string[],
|
|
545
545
|
includeNeg?: boolean,
|
|
546
546
|
): Promise<Map<string, Label[]>> {
|
|
547
|
+
const now = new Date().toISOString()
|
|
547
548
|
const labels = new Map<string, Label[]>()
|
|
548
549
|
const res = await this.db.db
|
|
549
550
|
.selectFrom('label')
|
|
550
551
|
.where('label.uri', 'in', subjects)
|
|
552
|
+
.where((qb) =>
|
|
553
|
+
qb.where('label.exp', 'is', null).orWhere('label.exp', '>', now),
|
|
554
|
+
)
|
|
551
555
|
.if(!includeNeg, (qb) => qb.where('neg', '=', false))
|
|
552
556
|
.selectAll()
|
|
553
557
|
.execute()
|
package/src/team/index.ts
CHANGED
|
@@ -229,11 +229,8 @@ export class TeamService {
|
|
|
229
229
|
profiles.set(profile.did, profile)
|
|
230
230
|
})
|
|
231
231
|
}
|
|
232
|
-
} catch (
|
|
233
|
-
httpLogger.error(
|
|
234
|
-
{ error, dids },
|
|
235
|
-
'Failed to get profiles for team members',
|
|
236
|
-
)
|
|
232
|
+
} catch (err) {
|
|
233
|
+
httpLogger.error({ err, dids }, 'Failed to get profiles for team members')
|
|
237
234
|
}
|
|
238
235
|
|
|
239
236
|
return profiles
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import AtpAgent from '@atproto/api'
|
|
2
|
+
import {
|
|
3
|
+
ModeratorClient,
|
|
4
|
+
SeedClient,
|
|
5
|
+
TestNetwork,
|
|
6
|
+
basicSeed,
|
|
7
|
+
} from '@atproto/dev-env'
|
|
8
|
+
import { ids } from '../src/lexicon/lexicons'
|
|
9
|
+
|
|
10
|
+
describe('expiring label', () => {
|
|
11
|
+
let network: TestNetwork
|
|
12
|
+
let sc: SeedClient
|
|
13
|
+
let modClient: ModeratorClient
|
|
14
|
+
let agent: AtpAgent
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
network = await TestNetwork.create({
|
|
18
|
+
dbPostgresSchema: 'ozone_expiring_label_test',
|
|
19
|
+
})
|
|
20
|
+
sc = network.getSeedClient()
|
|
21
|
+
agent = network.ozone.getClient()
|
|
22
|
+
modClient = network.ozone.getModClient()
|
|
23
|
+
await basicSeed(sc)
|
|
24
|
+
await network.processAll()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
afterAll(async () => {
|
|
28
|
+
await network.close()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const emitExpiringLabel = async (did: string) =>
|
|
32
|
+
modClient.emitEvent(
|
|
33
|
+
{
|
|
34
|
+
subject: { $type: 'com.atproto.admin.defs#repoRef', did },
|
|
35
|
+
event: {
|
|
36
|
+
$type: 'tools.ozone.moderation.defs#modEventLabel',
|
|
37
|
+
comment: 'Testing expiring label',
|
|
38
|
+
createLabelVals: ['expiring'],
|
|
39
|
+
negateLabelVals: [],
|
|
40
|
+
durationInHours: 1,
|
|
41
|
+
},
|
|
42
|
+
createdBy: sc.dids.alice,
|
|
43
|
+
},
|
|
44
|
+
'moderator',
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
it('Returns expiring label only within expiration period', async () => {
|
|
48
|
+
const getRepo = async (did: string) =>
|
|
49
|
+
agent.tools.ozone.moderation.getRepo(
|
|
50
|
+
{ did },
|
|
51
|
+
{
|
|
52
|
+
headers: await network.ozone.modHeaders(
|
|
53
|
+
ids.ToolsOzoneModerationGetRepo,
|
|
54
|
+
),
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const now = new Date().toISOString()
|
|
59
|
+
await emitExpiringLabel(sc.dids.carol)
|
|
60
|
+
const { data: repoWithExpiringLabel } = await getRepo(sc.dids.carol)
|
|
61
|
+
expect(repoWithExpiringLabel.labels?.[0].val).toEqual('expiring')
|
|
62
|
+
// Manually expire the label in db
|
|
63
|
+
await network.ozone.ctx.db.db
|
|
64
|
+
.updateTable('label')
|
|
65
|
+
.set({ exp: now })
|
|
66
|
+
.where('uri', '=', sc.dids.carol)
|
|
67
|
+
.execute()
|
|
68
|
+
|
|
69
|
+
const { data: repoAfterExpiringLabel } = await getRepo(sc.dids.carol)
|
|
70
|
+
expect(repoAfterExpiringLabel.labels?.length).toEqual(0)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -133,4 +133,34 @@ describe('verification', () => {
|
|
|
133
133
|
)
|
|
134
134
|
})
|
|
135
135
|
})
|
|
136
|
+
|
|
137
|
+
it('does not publish record if a valid one already exists', async () => {
|
|
138
|
+
const { data: beforePublish } =
|
|
139
|
+
await adminAgent.tools.ozone.verification.listVerifications({
|
|
140
|
+
subjects: [sc.dids.bob],
|
|
141
|
+
})
|
|
142
|
+
const {
|
|
143
|
+
data: { verifications },
|
|
144
|
+
} = await adminAgent.tools.ozone.verification.grantVerifications({
|
|
145
|
+
verifications: [
|
|
146
|
+
{
|
|
147
|
+
subject: sc.dids.bob,
|
|
148
|
+
handle: sc.accounts[sc.dids.bob].handle,
|
|
149
|
+
displayName: 'bobby',
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const { data: afterPublish } =
|
|
155
|
+
await adminAgent.tools.ozone.verification.listVerifications({
|
|
156
|
+
subjects: [sc.dids.bob],
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// assert that the response does not contain any new verification
|
|
160
|
+
expect(verifications.length).toEqual(0)
|
|
161
|
+
// assert that the list of verifications in db hasn't changed
|
|
162
|
+
expect(afterPublish.verifications.length).toEqual(
|
|
163
|
+
beforePublish.verifications.length,
|
|
164
|
+
)
|
|
165
|
+
})
|
|
136
166
|
})
|