@atproto/ozone 0.0.17-next.0 → 0.0.17-next.1
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/util.d.ts +10 -0
- package/dist/auth-verifier.d.ts +2 -2
- package/dist/communication-service/template.d.ts +2 -2
- package/dist/config/config.d.ts +5 -0
- package/dist/config/env.d.ts +2 -0
- package/dist/context.d.ts +6 -0
- package/dist/daemon/blob-diverter.d.ts +26 -0
- package/dist/daemon/event-pusher.d.ts +4 -0
- package/dist/daemon/index.d.ts +1 -0
- package/dist/db/index.js +21 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20240228T003647759Z-add-label-sigs.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/schema/index.d.ts +2 -1
- package/dist/db/schema/label.d.ts +4 -0
- package/dist/db/schema/moderation_event.d.ts +1 -1
- package/dist/db/schema/moderation_subject_status.d.ts +1 -1
- package/dist/db/schema/signing_key.d.ts +9 -0
- package/dist/index.js +10949 -10628
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +48 -28
- package/dist/lexicon/lexicons.d.ts +4641 -4650
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +7 -7
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +3 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +0 -305
- package/dist/lexicon/types/com/atproto/label/defs.d.ts +5 -0
- package/dist/lexicon/types/{com/atproto/admin/createCommunicationTemplate.d.ts → tools/ozone/communication/createTemplate.d.ts} +2 -2
- package/dist/lexicon/types/tools/ozone/communication/defs.d.ts +14 -0
- package/dist/lexicon/types/{com/atproto/admin/listCommunicationTemplates.d.ts → tools/ozone/communication/listTemplates.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.d.ts → tools/ozone/communication/updateTemplate.d.ts} +2 -2
- package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +269 -0
- package/dist/lexicon/types/{com/atproto/admin/emitModerationEvent.d.ts → tools/ozone/moderation/emitEvent.d.ts} +5 -4
- package/dist/lexicon/types/{com/atproto/admin/getModerationEvent.d.ts → tools/ozone/moderation/getEvent.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.d.ts +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.d.ts +2 -2
- package/dist/lexicon/types/{com/atproto/admin/queryModerationEvents.d.ts → tools/ozone/moderation/queryEvents.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin/queryModerationStatuses.d.ts → tools/ozone/moderation/queryStatuses.d.ts} +2 -2
- package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.d.ts +2 -2
- package/dist/mod-service/index.d.ts +16 -15
- package/dist/mod-service/subject.d.ts +1 -1
- package/dist/mod-service/types.d.ts +2 -2
- package/dist/mod-service/util.d.ts +6 -0
- package/dist/mod-service/views.d.ts +9 -3
- package/dist/sequencer/sequencer.d.ts +6 -4
- package/dist/util.d.ts +2 -0
- package/package.json +7 -7
- package/src/api/{admin/createCommunicationTemplate.ts → communication/createTemplate.ts} +1 -1
- package/src/api/{admin/deleteCommunicationTemplate.ts → communication/deleteTemplate.ts} +1 -1
- package/src/api/{admin/listCommunicationTemplates.ts → communication/listTemplates.ts} +1 -1
- package/src/api/{admin/updateCommunicationTemplate.ts → communication/updateTemplate.ts} +1 -1
- package/src/api/index.ts +21 -21
- package/src/api/{temp → label}/fetchLabels.ts +4 -2
- package/src/api/label/queryLabels.ts +4 -2
- package/src/api/moderation/emitEvent.ts +218 -0
- package/src/api/{admin/getModerationEvent.ts → moderation/getEvent.ts} +1 -1
- package/src/api/{admin → moderation}/getRecord.ts +2 -2
- package/src/api/{admin → moderation}/getRepo.ts +2 -2
- package/src/api/{admin/queryModerationEvents.ts → moderation/queryEvents.ts} +2 -2
- package/src/api/{admin/queryModerationStatuses.ts → moderation/queryStatuses.ts} +2 -2
- package/src/api/{admin → moderation}/searchRepos.ts +1 -1
- package/src/api/{moderation → report}/createReport.ts +1 -1
- package/src/api/util.ts +119 -0
- package/src/auth-verifier.ts +2 -2
- package/src/communication-service/template.ts +2 -2
- package/src/config/config.ts +14 -0
- package/src/config/env.ts +4 -0
- package/src/context.ts +35 -9
- package/src/daemon/blob-diverter.ts +150 -0
- package/src/daemon/context.ts +9 -5
- package/src/daemon/event-pusher.ts +49 -14
- package/src/daemon/index.ts +1 -0
- package/src/db/migrations/20240228T003647759Z-add-label-sigs.ts +25 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/index.ts +2 -0
- package/src/db/schema/label.ts +3 -0
- package/src/db/schema/moderation_event.ts +11 -11
- package/src/db/schema/moderation_subject_status.ts +1 -1
- package/src/db/schema/signing_key.ts +10 -0
- package/src/lexicon/index.ts +178 -138
- package/src/lexicon/lexicons.ts +6078 -6106
- package/src/lexicon/types/app/bsky/actor/defs.ts +11 -11
- package/src/lexicon/types/app/bsky/feed/defs.ts +1 -0
- package/src/lexicon/types/app/bsky/graph/defs.ts +3 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +0 -697
- package/src/lexicon/types/com/atproto/label/defs.ts +10 -0
- package/src/lexicon/types/{com/atproto/admin/createCommunicationTemplate.ts → tools/ozone/communication/createTemplate.ts} +2 -2
- package/src/lexicon/types/tools/ozone/communication/defs.ts +35 -0
- package/src/lexicon/types/{com/atproto/admin/listCommunicationTemplates.ts → tools/ozone/communication/listTemplates.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.ts → tools/ozone/communication/updateTemplate.ts} +2 -2
- package/src/lexicon/types/tools/ozone/moderation/defs.ts +641 -0
- package/src/lexicon/types/{com/atproto/admin/emitModerationEvent.ts → tools/ozone/moderation/emitEvent.ts} +15 -14
- package/src/lexicon/types/{com/atproto/admin/getModerationEvent.ts → tools/ozone/moderation/getEvent.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.ts +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.ts +2 -2
- package/src/lexicon/types/{com/atproto/admin/queryModerationEvents.ts → tools/ozone/moderation/queryEvents.ts} +3 -3
- package/src/lexicon/types/{com/atproto/admin/queryModerationStatuses.ts → tools/ozone/moderation/queryStatuses.ts} +2 -2
- package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.ts +2 -2
- package/src/mod-service/index.ts +42 -47
- package/src/mod-service/lang.ts +1 -1
- package/src/mod-service/status.ts +19 -16
- package/src/mod-service/subject.ts +1 -1
- package/src/mod-service/types.ts +10 -10
- package/src/mod-service/util.ts +49 -5
- package/src/mod-service/views.ts +45 -18
- package/src/sequencer/sequencer.ts +12 -11
- package/src/util.ts +21 -0
- package/tests/__snapshots__/blob-divert.test.ts.snap +22 -0
- package/tests/__snapshots__/get-record.test.ts.snap +10 -2
- package/tests/__snapshots__/get-repo.test.ts.snap +5 -1
- package/tests/__snapshots__/moderation-events.test.ts.snap +8 -8
- package/tests/__snapshots__/moderation-statuses.test.ts.snap +6 -6
- package/tests/_util.ts +5 -0
- package/tests/blob-divert.test.ts +87 -0
- package/tests/communication-templates.test.ts +30 -34
- package/tests/db.test.ts +6 -6
- package/tests/get-record.test.ts +6 -6
- package/tests/get-repo.test.ts +11 -11
- package/tests/moderation-appeals.test.ts +28 -28
- package/tests/moderation-events.test.ts +44 -44
- package/tests/moderation-status-tags.test.ts +8 -10
- package/tests/moderation-statuses.test.ts +27 -27
- package/tests/moderation.test.ts +50 -57
- package/tests/query-labels.test.ts +86 -10
- package/tests/repo-search.test.ts +8 -8
- package/tests/sequencer.test.ts +6 -3
- package/dist/api/admin/util.d.ts +0 -5
- package/dist/api/moderation/util.d.ts +0 -4
- package/src/api/admin/emitModerationEvent.ts +0 -174
- package/src/api/admin/util.ts +0 -54
- package/src/api/moderation/util.ts +0 -67
- /package/dist/api/{admin/createCommunicationTemplate.d.ts → communication/createTemplate.d.ts} +0 -0
- /package/dist/api/{admin/deleteCommunicationTemplate.d.ts → communication/deleteTemplate.d.ts} +0 -0
- /package/dist/api/{admin/emitModerationEvent.d.ts → communication/listTemplates.d.ts} +0 -0
- /package/dist/api/{admin/getModerationEvent.d.ts → communication/updateTemplate.d.ts} +0 -0
- /package/dist/api/{temp → label}/fetchLabels.d.ts +0 -0
- /package/dist/api/{admin/getRecord.d.ts → moderation/emitEvent.d.ts} +0 -0
- /package/dist/api/{admin/getRepo.d.ts → moderation/getEvent.d.ts} +0 -0
- /package/dist/api/{admin/listCommunicationTemplates.d.ts → moderation/getRecord.d.ts} +0 -0
- /package/dist/api/{admin/queryModerationEvents.d.ts → moderation/getRepo.d.ts} +0 -0
- /package/dist/api/{admin/queryModerationStatuses.d.ts → moderation/queryEvents.d.ts} +0 -0
- /package/dist/api/{admin/searchRepos.d.ts → moderation/queryStatuses.d.ts} +0 -0
- /package/dist/api/{admin/updateCommunicationTemplate.d.ts → moderation/searchRepos.d.ts} +0 -0
- /package/dist/api/{moderation → report}/createReport.d.ts +0 -0
- /package/dist/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.d.ts → tools/ozone/communication/deleteTemplate.d.ts} +0 -0
- /package/src/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.ts → tools/ozone/communication/deleteTemplate.ts} +0 -0
package/src/context.ts
CHANGED
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
CommunicationTemplateService,
|
|
15
15
|
CommunicationTemplateServiceCreator,
|
|
16
16
|
} from './communication-service/template'
|
|
17
|
+
import { BlobDiverter } from './daemon/blob-diverter'
|
|
17
18
|
import { AuthVerifier } from './auth-verifier'
|
|
18
19
|
import { ImageInvalidator } from './image-invalidator'
|
|
20
|
+
import { getSigningKeyId } from './util'
|
|
19
21
|
|
|
20
22
|
export type AppContextOptions = {
|
|
21
23
|
db: Database
|
|
@@ -24,7 +26,9 @@ export type AppContextOptions = {
|
|
|
24
26
|
communicationTemplateService: CommunicationTemplateServiceCreator
|
|
25
27
|
appviewAgent: AtpAgent
|
|
26
28
|
pdsAgent: AtpAgent | undefined
|
|
29
|
+
blobDiverter?: BlobDiverter
|
|
27
30
|
signingKey: Keypair
|
|
31
|
+
signingKeyId: number
|
|
28
32
|
idResolver: IdResolver
|
|
29
33
|
imgInvalidator?: ImageInvalidator
|
|
30
34
|
backgroundQueue: BackgroundQueue
|
|
@@ -48,11 +52,16 @@ export class AppContext {
|
|
|
48
52
|
poolIdleTimeoutMs: cfg.db.poolIdleTimeoutMs,
|
|
49
53
|
})
|
|
50
54
|
const signingKey = await Secp256k1Keypair.import(secrets.signingKeyHex)
|
|
55
|
+
const signingKeyId = await getSigningKeyId(db, signingKey.did())
|
|
51
56
|
const appviewAgent = new AtpAgent({ service: cfg.appview.url })
|
|
52
57
|
const pdsAgent = cfg.pds
|
|
53
58
|
? new AtpAgent({ service: cfg.pds.url })
|
|
54
59
|
: undefined
|
|
55
60
|
|
|
61
|
+
const idResolver = new IdResolver({
|
|
62
|
+
plcUrl: cfg.identity.plcUrl,
|
|
63
|
+
})
|
|
64
|
+
|
|
56
65
|
const createAuthHeaders = (aud: string) =>
|
|
57
66
|
createServiceAuthHeaders({
|
|
58
67
|
iss: `${cfg.service.did}#atproto_labeler`,
|
|
@@ -61,30 +70,31 @@ export class AppContext {
|
|
|
61
70
|
})
|
|
62
71
|
|
|
63
72
|
const backgroundQueue = new BackgroundQueue(db)
|
|
73
|
+
const blobDiverter = cfg.blobDivert
|
|
74
|
+
? new BlobDiverter(db, {
|
|
75
|
+
idResolver,
|
|
76
|
+
serviceConfig: cfg.blobDivert,
|
|
77
|
+
})
|
|
78
|
+
: undefined
|
|
64
79
|
const eventPusher = new EventPusher(db, createAuthHeaders, {
|
|
65
80
|
appview: cfg.appview.pushEvents ? cfg.appview : undefined,
|
|
66
81
|
pds: cfg.pds ?? undefined,
|
|
67
82
|
})
|
|
68
|
-
|
|
69
|
-
const idResolver = new IdResolver({
|
|
70
|
-
plcUrl: cfg.identity.plcUrl,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
83
|
const modService = ModerationService.creator(
|
|
84
|
+
signingKey,
|
|
85
|
+
signingKeyId,
|
|
74
86
|
cfg,
|
|
75
87
|
backgroundQueue,
|
|
76
88
|
idResolver,
|
|
77
89
|
eventPusher,
|
|
78
90
|
appviewAgent,
|
|
79
91
|
createAuthHeaders,
|
|
80
|
-
cfg.service.did,
|
|
81
92
|
overrides?.imgInvalidator,
|
|
82
|
-
cfg.cdn.paths,
|
|
83
93
|
)
|
|
84
94
|
|
|
85
95
|
const communicationTemplateService = CommunicationTemplateService.creator()
|
|
86
96
|
|
|
87
|
-
const sequencer = new Sequencer(db)
|
|
97
|
+
const sequencer = new Sequencer(modService(db))
|
|
88
98
|
|
|
89
99
|
const authVerifier = new AuthVerifier(idResolver, {
|
|
90
100
|
serviceDid: cfg.service.did,
|
|
@@ -103,10 +113,12 @@ export class AppContext {
|
|
|
103
113
|
appviewAgent,
|
|
104
114
|
pdsAgent,
|
|
105
115
|
signingKey,
|
|
116
|
+
signingKeyId,
|
|
106
117
|
idResolver,
|
|
107
118
|
backgroundQueue,
|
|
108
119
|
sequencer,
|
|
109
120
|
authVerifier,
|
|
121
|
+
blobDiverter,
|
|
110
122
|
...(overrides ?? {}),
|
|
111
123
|
},
|
|
112
124
|
secrets,
|
|
@@ -133,6 +145,10 @@ export class AppContext {
|
|
|
133
145
|
return this.opts.modService
|
|
134
146
|
}
|
|
135
147
|
|
|
148
|
+
get blobDiverter(): BlobDiverter | undefined {
|
|
149
|
+
return this.opts.blobDiverter
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
get communicationTemplateService(): CommunicationTemplateServiceCreator {
|
|
137
153
|
return this.opts.communicationTemplateService
|
|
138
154
|
}
|
|
@@ -149,6 +165,10 @@ export class AppContext {
|
|
|
149
165
|
return this.opts.signingKey
|
|
150
166
|
}
|
|
151
167
|
|
|
168
|
+
get signingKeyId(): number {
|
|
169
|
+
return this.opts.signingKeyId
|
|
170
|
+
}
|
|
171
|
+
|
|
152
172
|
get plcClient(): plc.Client {
|
|
153
173
|
return new plc.Client(this.cfg.identity.plcUrl)
|
|
154
174
|
}
|
|
@@ -188,6 +208,12 @@ export class AppContext {
|
|
|
188
208
|
async appviewAuth() {
|
|
189
209
|
return this.serviceAuthHeaders(this.cfg.appview.did)
|
|
190
210
|
}
|
|
191
|
-
}
|
|
192
211
|
|
|
212
|
+
devOverride(overrides: Partial<AppContextOptions>) {
|
|
213
|
+
this.opts = {
|
|
214
|
+
...this.opts,
|
|
215
|
+
...overrides,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
193
219
|
export default AppContext
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VerifyCidTransform,
|
|
3
|
+
forwardStreamErrors,
|
|
4
|
+
getPdsEndpoint,
|
|
5
|
+
} from '@atproto/common'
|
|
6
|
+
import { IdResolver } from '@atproto/identity'
|
|
7
|
+
import axios from 'axios'
|
|
8
|
+
import { Readable } from 'stream'
|
|
9
|
+
import { CID } from 'multiformats/cid'
|
|
10
|
+
|
|
11
|
+
import Database from '../db'
|
|
12
|
+
import { retryHttp } from '../util'
|
|
13
|
+
import { BlobDivertConfig } from '../config'
|
|
14
|
+
|
|
15
|
+
export class BlobDiverter {
|
|
16
|
+
serviceConfig: BlobDivertConfig
|
|
17
|
+
idResolver: IdResolver
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
public db: Database,
|
|
21
|
+
services: {
|
|
22
|
+
idResolver: IdResolver
|
|
23
|
+
serviceConfig: BlobDivertConfig
|
|
24
|
+
},
|
|
25
|
+
) {
|
|
26
|
+
this.serviceConfig = services.serviceConfig
|
|
27
|
+
this.idResolver = services.idResolver
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async getBlob({
|
|
31
|
+
pds,
|
|
32
|
+
did,
|
|
33
|
+
cid,
|
|
34
|
+
}: {
|
|
35
|
+
pds: string
|
|
36
|
+
did: string
|
|
37
|
+
cid: string
|
|
38
|
+
}) {
|
|
39
|
+
const blobResponse = await axios.get(
|
|
40
|
+
`${pds}/xrpc/com.atproto.sync.getBlob`,
|
|
41
|
+
{
|
|
42
|
+
params: { did, cid },
|
|
43
|
+
decompress: true,
|
|
44
|
+
responseType: 'stream',
|
|
45
|
+
timeout: 5000, // 5sec of inactivity on the connection
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
const imageStream: Readable = blobResponse.data
|
|
49
|
+
const verifyCid = new VerifyCidTransform(CID.parse(cid))
|
|
50
|
+
forwardStreamErrors(imageStream, verifyCid)
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
contentType:
|
|
54
|
+
blobResponse.headers['content-type'] || 'application/octet-stream',
|
|
55
|
+
imageStream: imageStream.pipe(verifyCid),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async sendImage({
|
|
60
|
+
url,
|
|
61
|
+
imageStream,
|
|
62
|
+
contentType,
|
|
63
|
+
}: {
|
|
64
|
+
url: string
|
|
65
|
+
imageStream: Readable
|
|
66
|
+
contentType: string
|
|
67
|
+
}) {
|
|
68
|
+
const result = await axios(url, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
data: imageStream,
|
|
71
|
+
headers: {
|
|
72
|
+
Authorization: basicAuth('admin', this.serviceConfig.adminPassword),
|
|
73
|
+
'Content-Type': contentType,
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return result.status === 200
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async uploadBlob(
|
|
81
|
+
{
|
|
82
|
+
imageStream,
|
|
83
|
+
contentType,
|
|
84
|
+
}: { imageStream: Readable; contentType: string },
|
|
85
|
+
{
|
|
86
|
+
subjectDid,
|
|
87
|
+
subjectUri,
|
|
88
|
+
}: { subjectDid: string; subjectUri: string | null },
|
|
89
|
+
) {
|
|
90
|
+
const url = new URL(this.serviceConfig.url)
|
|
91
|
+
url.searchParams.set('did', subjectDid)
|
|
92
|
+
if (subjectUri) url.searchParams.set('uri', subjectUri)
|
|
93
|
+
const result = await this.sendImage({
|
|
94
|
+
url: url.toString(),
|
|
95
|
+
imageStream,
|
|
96
|
+
contentType,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async uploadBlobOnService({
|
|
103
|
+
subjectDid,
|
|
104
|
+
subjectUri,
|
|
105
|
+
subjectBlobCids,
|
|
106
|
+
}: {
|
|
107
|
+
subjectDid: string
|
|
108
|
+
subjectUri: string
|
|
109
|
+
subjectBlobCids: string[]
|
|
110
|
+
}): Promise<boolean> {
|
|
111
|
+
const didDoc = await this.idResolver.did.resolve(subjectDid)
|
|
112
|
+
|
|
113
|
+
if (!didDoc) {
|
|
114
|
+
throw new Error('Error resolving DID')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const pds = getPdsEndpoint(didDoc)
|
|
118
|
+
|
|
119
|
+
if (!pds) {
|
|
120
|
+
throw new Error('Error resolving PDS')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// attempt to download and upload within the same retry block since the imageStream is not reusable
|
|
124
|
+
const uploadResult = await Promise.all(
|
|
125
|
+
subjectBlobCids.map((cid) =>
|
|
126
|
+
retryHttp(async () => {
|
|
127
|
+
const { imageStream, contentType } = await this.getBlob({
|
|
128
|
+
pds,
|
|
129
|
+
cid,
|
|
130
|
+
did: subjectDid,
|
|
131
|
+
})
|
|
132
|
+
return this.uploadBlob(
|
|
133
|
+
{ imageStream, contentType },
|
|
134
|
+
{ subjectDid, subjectUri },
|
|
135
|
+
)
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if (uploadResult.includes(false)) {
|
|
141
|
+
throw new Error(`Error uploading blob ${subjectUri}`)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const basicAuth = (username: string, password: string) => {
|
|
149
|
+
return 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
|
|
150
|
+
}
|
package/src/daemon/context.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
|
|
2
2
|
import { createServiceAuthHeaders } from '@atproto/xrpc-server'
|
|
3
|
+
import { IdResolver } from '@atproto/identity'
|
|
3
4
|
import AtpAgent from '@atproto/api'
|
|
4
5
|
import { OzoneConfig, OzoneSecrets } from '../config'
|
|
5
6
|
import { Database } from '../db'
|
|
@@ -7,7 +8,7 @@ import { EventPusher } from './event-pusher'
|
|
|
7
8
|
import { EventReverser } from './event-reverser'
|
|
8
9
|
import { ModerationService, ModerationServiceCreator } from '../mod-service'
|
|
9
10
|
import { BackgroundQueue } from '../background'
|
|
10
|
-
import {
|
|
11
|
+
import { getSigningKeyId } from '../util'
|
|
11
12
|
|
|
12
13
|
export type DaemonContextOptions = {
|
|
13
14
|
db: Database
|
|
@@ -31,6 +32,11 @@ export class DaemonContext {
|
|
|
31
32
|
schema: cfg.db.postgresSchema,
|
|
32
33
|
})
|
|
33
34
|
const signingKey = await Secp256k1Keypair.import(secrets.signingKeyHex)
|
|
35
|
+
const signingKeyId = await getSigningKeyId(db, signingKey.did())
|
|
36
|
+
|
|
37
|
+
const idResolver = new IdResolver({
|
|
38
|
+
plcUrl: cfg.identity.plcUrl,
|
|
39
|
+
})
|
|
34
40
|
|
|
35
41
|
const appviewAgent = new AtpAgent({ service: cfg.appview.url })
|
|
36
42
|
const createAuthHeaders = (aud: string) =>
|
|
@@ -46,18 +52,16 @@ export class DaemonContext {
|
|
|
46
52
|
})
|
|
47
53
|
|
|
48
54
|
const backgroundQueue = new BackgroundQueue(db)
|
|
49
|
-
const idResolver = new IdResolver({
|
|
50
|
-
plcUrl: cfg.identity.plcUrl,
|
|
51
|
-
})
|
|
52
55
|
|
|
53
56
|
const modService = ModerationService.creator(
|
|
57
|
+
signingKey,
|
|
58
|
+
signingKeyId,
|
|
54
59
|
cfg,
|
|
55
60
|
backgroundQueue,
|
|
56
61
|
idResolver,
|
|
57
62
|
eventPusher,
|
|
58
63
|
appviewAgent,
|
|
59
64
|
createAuthHeaders,
|
|
60
|
-
cfg.service.did,
|
|
61
65
|
)
|
|
62
66
|
|
|
63
67
|
const eventReverser = new EventReverser(db, modService)
|
|
@@ -6,6 +6,8 @@ import { RepoPushEventType } from '../db/schema/repo_push_event'
|
|
|
6
6
|
import { retryHttp } from '../util'
|
|
7
7
|
import { dbLogger } from '../logger'
|
|
8
8
|
import { InputSchema } from '../lexicon/types/com/atproto/admin/updateSubjectStatus'
|
|
9
|
+
import { BlobPushEvent } from '../db/schema/blob_push_event'
|
|
10
|
+
import { Insertable, Selectable } from 'kysely'
|
|
9
11
|
|
|
10
12
|
type EventSubject = InputSchema['subject']
|
|
11
13
|
|
|
@@ -285,20 +287,53 @@ export class EventPusher {
|
|
|
285
287
|
subject,
|
|
286
288
|
evt.takedownRef,
|
|
287
289
|
)
|
|
288
|
-
await dbTxn
|
|
289
|
-
.updateTable('blob_push_event')
|
|
290
|
-
.set(
|
|
291
|
-
succeeded
|
|
292
|
-
? { confirmedAt: new Date() }
|
|
293
|
-
: {
|
|
294
|
-
lastAttempted: new Date(),
|
|
295
|
-
attempts: (evt.attempts ?? 0) + 1,
|
|
296
|
-
},
|
|
297
|
-
)
|
|
298
|
-
.where('subjectDid', '=', evt.subjectDid)
|
|
299
|
-
.where('subjectBlobCid', '=', evt.subjectBlobCid)
|
|
300
|
-
.where('eventType', '=', evt.eventType)
|
|
301
|
-
.execute()
|
|
290
|
+
await this.markBlobEventAttempt(dbTxn, evt, succeeded)
|
|
302
291
|
})
|
|
303
292
|
}
|
|
293
|
+
|
|
294
|
+
async markBlobEventAttempt(
|
|
295
|
+
dbTxn: Database,
|
|
296
|
+
event: Selectable<BlobPushEvent>,
|
|
297
|
+
succeeded: boolean,
|
|
298
|
+
) {
|
|
299
|
+
await dbTxn.db
|
|
300
|
+
.updateTable('blob_push_event')
|
|
301
|
+
.set(
|
|
302
|
+
succeeded
|
|
303
|
+
? { confirmedAt: new Date() }
|
|
304
|
+
: {
|
|
305
|
+
lastAttempted: new Date(),
|
|
306
|
+
attempts: (event.attempts ?? 0) + 1,
|
|
307
|
+
},
|
|
308
|
+
)
|
|
309
|
+
.where('subjectDid', '=', event.subjectDid)
|
|
310
|
+
.where('subjectBlobCid', '=', event.subjectBlobCid)
|
|
311
|
+
.where('eventType', '=', event.eventType)
|
|
312
|
+
.execute()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async logBlobPushEvent(
|
|
316
|
+
blobValues: Insertable<BlobPushEvent>[],
|
|
317
|
+
takedownRef?: string | null,
|
|
318
|
+
) {
|
|
319
|
+
return this.db.db
|
|
320
|
+
.insertInto('blob_push_event')
|
|
321
|
+
.values(blobValues)
|
|
322
|
+
.onConflict((oc) =>
|
|
323
|
+
oc.columns(['subjectDid', 'subjectBlobCid', 'eventType']).doUpdateSet({
|
|
324
|
+
takedownRef,
|
|
325
|
+
confirmedAt: null,
|
|
326
|
+
attempts: 0,
|
|
327
|
+
lastAttempted: null,
|
|
328
|
+
}),
|
|
329
|
+
)
|
|
330
|
+
.returning([
|
|
331
|
+
'id',
|
|
332
|
+
'subjectDid',
|
|
333
|
+
'subjectUri',
|
|
334
|
+
'subjectBlobCid',
|
|
335
|
+
'eventType',
|
|
336
|
+
])
|
|
337
|
+
.execute()
|
|
338
|
+
}
|
|
304
339
|
}
|
package/src/daemon/index.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Kysely, sql } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema.alterTable('label').addColumn('exp', 'varchar').execute()
|
|
5
|
+
await db.schema
|
|
6
|
+
.alterTable('label')
|
|
7
|
+
.addColumn('sig', sql`bytea`)
|
|
8
|
+
.execute()
|
|
9
|
+
await db.schema
|
|
10
|
+
.alterTable('label')
|
|
11
|
+
.addColumn('signingKeyId', 'integer')
|
|
12
|
+
.execute()
|
|
13
|
+
await db.schema
|
|
14
|
+
.createTable('signing_key')
|
|
15
|
+
.addColumn('id', 'serial', (col) => col.primaryKey())
|
|
16
|
+
.addColumn('key', 'varchar', (col) => col.notNull().unique())
|
|
17
|
+
.execute()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
21
|
+
await db.schema.dropTable('signing_key')
|
|
22
|
+
await db.schema.alterTable('label').dropColumn('exp').execute()
|
|
23
|
+
await db.schema.alterTable('label').dropColumn('sig').execute()
|
|
24
|
+
await db.schema.alterTable('label').dropColumn('signingKey').execute()
|
|
25
|
+
}
|
|
@@ -6,3 +6,4 @@ export * as _20231219T205730722Z from './20231219T205730722Z-init'
|
|
|
6
6
|
export * as _20240116T085607200Z from './20240116T085607200Z-communication-template'
|
|
7
7
|
export * as _20240201T051104136Z from './20240201T051104136Z-mod-event-blobs'
|
|
8
8
|
export * as _20240208T213404429Z from './20240208T213404429Z-add-tags-column-to-moderation-subject'
|
|
9
|
+
export * as _20240228T003647759Z from './20240228T003647759Z-add-label-sigs'
|
package/src/db/schema/index.ts
CHANGED
|
@@ -5,11 +5,13 @@ import * as repoPushEvent from './repo_push_event'
|
|
|
5
5
|
import * as recordPushEvent from './record_push_event'
|
|
6
6
|
import * as blobPushEvent from './blob_push_event'
|
|
7
7
|
import * as label from './label'
|
|
8
|
+
import * as signingKey from './signing_key'
|
|
8
9
|
import * as communicationTemplate from './communication_template'
|
|
9
10
|
|
|
10
11
|
export type DatabaseSchemaType = modEvent.PartialDB &
|
|
11
12
|
modSubjectStatus.PartialDB &
|
|
12
13
|
label.PartialDB &
|
|
14
|
+
signingKey.PartialDB &
|
|
13
15
|
repoPushEvent.PartialDB &
|
|
14
16
|
recordPushEvent.PartialDB &
|
|
15
17
|
blobPushEvent.PartialDB &
|
package/src/db/schema/label.ts
CHANGED
|
@@ -5,17 +5,17 @@ export const eventTableName = 'moderation_event'
|
|
|
5
5
|
export interface ModerationEvent {
|
|
6
6
|
id: Generated<number>
|
|
7
7
|
action:
|
|
8
|
-
| '
|
|
9
|
-
| '
|
|
10
|
-
| '
|
|
11
|
-
| '
|
|
12
|
-
| '
|
|
13
|
-
| '
|
|
14
|
-
| '
|
|
15
|
-
| '
|
|
16
|
-
| '
|
|
17
|
-
| '
|
|
18
|
-
| '
|
|
8
|
+
| 'tools.ozone.moderation.defs#modEventTakedown'
|
|
9
|
+
| 'tools.ozone.moderation.defs#modEventAcknowledge'
|
|
10
|
+
| 'tools.ozone.moderation.defs#modEventEscalate'
|
|
11
|
+
| 'tools.ozone.moderation.defs#modEventComment'
|
|
12
|
+
| 'tools.ozone.moderation.defs#modEventLabel'
|
|
13
|
+
| 'tools.ozone.moderation.defs#modEventReport'
|
|
14
|
+
| 'tools.ozone.moderation.defs#modEventMute'
|
|
15
|
+
| 'tools.ozone.moderation.defs#modEventReverseTakedown'
|
|
16
|
+
| 'tools.ozone.moderation.defs#modEventEmail'
|
|
17
|
+
| 'tools.ozone.moderation.defs#modEventResolveAppeal'
|
|
18
|
+
| 'tools.ozone.moderation.defs#modEventTag'
|
|
19
19
|
subjectType: 'com.atproto.admin.defs#repoRef' | 'com.atproto.repo.strongRef'
|
|
20
20
|
subjectDid: string
|
|
21
21
|
subjectUri: string | null
|