@atproto/ozone 0.1.106 → 0.1.108
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 +20 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +6 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/proxied.d.ts.map +1 -1
- package/dist/api/proxied.js +29 -16
- package/dist/api/proxied.js.map +1 -1
- package/dist/api/server/getConfig.d.ts.map +1 -1
- package/dist/api/server/getConfig.js +1 -0
- package/dist/api/server/getConfig.js.map +1 -1
- package/dist/api/setting/removeOptions.d.ts.map +1 -1
- package/dist/api/setting/removeOptions.js +1 -0
- package/dist/api/setting/removeOptions.js.map +1 -1
- package/dist/api/setting/upsertOption.js +7 -0
- package/dist/api/setting/upsertOption.js.map +1 -1
- package/dist/api/util.d.ts +1 -1
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +6 -1
- package/dist/api/util.js.map +1 -1
- package/dist/api/verification/grantVerifications.d.ts +4 -0
- package/dist/api/verification/grantVerifications.d.ts.map +1 -0
- package/dist/api/verification/grantVerifications.js +52 -0
- package/dist/api/verification/grantVerifications.js.map +1 -0
- package/dist/api/verification/listVerifications.d.ts +4 -0
- package/dist/api/verification/listVerifications.d.ts.map +1 -0
- package/dist/api/verification/listVerifications.js +32 -0
- package/dist/api/verification/listVerifications.js.map +1 -0
- package/dist/api/verification/revokeVerifications.d.ts +4 -0
- package/dist/api/verification/revokeVerifications.d.ts.map +1 -0
- package/dist/api/verification/revokeVerifications.js +36 -0
- package/dist/api/verification/revokeVerifications.js.map +1 -0
- package/dist/auth-verifier.d.ts +4 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +4 -3
- package/dist/auth-verifier.js.map +1 -1
- package/dist/background.d.ts +3 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +3 -2
- package/dist/background.js.map +1 -1
- package/dist/config/config.d.ts +9 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +10 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +5 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +5 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +6 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +12 -0
- package/dist/context.js.map +1 -1
- package/dist/daemon/context.d.ts +3 -0
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js +11 -0
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/verification-listener.d.ts +29 -0
- package/dist/daemon/verification-listener.d.ts.map +1 -0
- package/dist/daemon/verification-listener.js +171 -0
- package/dist/daemon/verification-listener.js.map +1 -0
- package/dist/db/migrations/20250415T201720309Z-verification.d.ts +4 -0
- package/dist/db/migrations/20250415T201720309Z-verification.d.ts.map +1 -0
- package/dist/db/migrations/20250415T201720309Z-verification.js +35 -0
- package/dist/db/migrations/20250415T201720309Z-verification.js.map +1 -0
- package/dist/db/migrations/20250417T201720309Z-firehose-cursor.d.ts +4 -0
- package/dist/db/migrations/20250417T201720309Z-firehose-cursor.d.ts.map +1 -0
- package/dist/db/migrations/20250417T201720309Z-firehose-cursor.js +17 -0
- package/dist/db/migrations/20250417T201720309Z-firehose-cursor.js.map +1 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +3 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/pagination.d.ts +15 -0
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js +23 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/firehose_cursor.d.ts +11 -0
- package/dist/db/schema/firehose_cursor.d.ts.map +1 -0
- package/dist/db/schema/firehose_cursor.js +5 -0
- package/dist/db/schema/firehose_cursor.js.map +1 -0
- package/dist/db/schema/index.d.ts +3 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/member.d.ts +1 -1
- package/dist/db/schema/member.d.ts.map +1 -1
- package/dist/db/schema/verification.d.ts +19 -0
- package/dist/db/schema/verification.d.ts.map +1 -0
- package/dist/db/schema/verification.js +5 -0
- package/dist/db/schema/verification.js.map +1 -0
- package/dist/jetstream/service.d.ts +64 -0
- package/dist/jetstream/service.d.ts.map +1 -0
- package/dist/jetstream/service.js +65 -0
- package/dist/jetstream/service.js.map +1 -0
- package/dist/lexicon/index.d.ts +19 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +56 -1
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +976 -80
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +485 -1
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/hosting/getAccountHistory.d.ts +81 -0
- package/dist/lexicon/types/tools/ozone/hosting/getAccountHistory.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/hosting/getAccountHistory.js +61 -0
- package/dist/lexicon/types/tools/ozone/hosting/getAccountHistory.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/server/getConfig.d.ts +3 -1
- package/dist/lexicon/types/tools/ozone/server/getConfig.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/server/getConfig.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/team/addMember.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/team/addMember.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/team/defs.d.ts +3 -1
- package/dist/lexicon/types/tools/ozone/team/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/team/defs.js +3 -1
- package/dist/lexicon/types/tools/ozone/team/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/team/updateMember.d.ts +1 -1
- package/dist/lexicon/types/tools/ozone/team/updateMember.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/verification/defs.d.ts +43 -0
- package/dist/lexicon/types/tools/ozone/verification/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/defs.js +16 -0
- package/dist/lexicon/types/tools/ozone/verification/defs.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/grantVerifications.d.ts +66 -0
- package/dist/lexicon/types/tools/ozone/verification/grantVerifications.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/grantVerifications.js +25 -0
- package/dist/lexicon/types/tools/ozone/verification/grantVerifications.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/listVerifications.d.ts +52 -0
- package/dist/lexicon/types/tools/ozone/verification/listVerifications.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/listVerifications.js +7 -0
- package/dist/lexicon/types/tools/ozone/verification/listVerifications.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.d.ts +56 -0
- package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.js +16 -0
- package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.js.map +1 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +58 -21
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/status.d.ts +6 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +3 -0
- package/dist/team/index.js.map +1 -1
- package/dist/verification/issuer.d.ts +37 -0
- package/dist/verification/issuer.d.ts.map +1 -0
- package/dist/verification/issuer.js +119 -0
- package/dist/verification/issuer.js.map +1 -0
- package/dist/verification/service.d.ts +47 -0
- package/dist/verification/service.d.ts.map +1 -0
- package/dist/verification/service.js +141 -0
- package/dist/verification/service.js.map +1 -0
- package/dist/verification/util.d.ts +6 -0
- package/dist/verification/util.d.ts.map +1 -0
- package/dist/verification/util.js +32 -0
- package/dist/verification/util.js.map +1 -0
- package/package.json +5 -4
- package/src/api/index.ts +6 -0
- package/src/api/proxied.ts +39 -24
- package/src/api/server/getConfig.ts +1 -0
- package/src/api/setting/removeOptions.ts +1 -0
- package/src/api/setting/upsertOption.ts +7 -0
- package/src/api/util.ts +7 -1
- package/src/api/verification/grantVerifications.ts +74 -0
- package/src/api/verification/listVerifications.ts +44 -0
- package/src/api/verification/revokeVerifications.ts +43 -0
- package/src/auth-verifier.ts +8 -4
- package/src/background.ts +7 -2
- package/src/config/config.ts +21 -0
- package/src/config/env.ts +10 -0
- package/src/context.ts +22 -0
- package/src/daemon/context.ts +19 -0
- package/src/daemon/verification-listener.ts +164 -0
- package/src/db/migrations/20250415T201720309Z-verification.ts +34 -0
- package/src/db/migrations/20250417T201720309Z-firehose-cursor.ts +16 -0
- package/src/db/migrations/index.ts +2 -0
- package/src/db/pagination.ts +31 -0
- package/src/db/schema/firehose_cursor.ts +13 -0
- package/src/db/schema/index.ts +5 -1
- package/src/db/schema/member.ts +1 -0
- package/src/db/schema/verification.ts +21 -0
- package/src/jetstream/service.ts +110 -0
- package/src/lexicon/index.ts +69 -0
- package/src/lexicon/lexicons.ts +505 -1
- package/src/lexicon/types/chat/bsky/actor/defs.ts +1 -1
- package/src/lexicon/types/tools/ozone/hosting/getAccountHistory.ts +161 -0
- package/src/lexicon/types/tools/ozone/server/getConfig.ts +3 -0
- package/src/lexicon/types/tools/ozone/setting/defs.ts +1 -0
- package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +1 -0
- package/src/lexicon/types/tools/ozone/team/addMember.ts +1 -0
- package/src/lexicon/types/tools/ozone/team/defs.ts +3 -0
- package/src/lexicon/types/tools/ozone/team/updateMember.ts +1 -0
- package/src/lexicon/types/tools/ozone/verification/defs.ts +59 -0
- package/src/lexicon/types/tools/ozone/verification/grantVerifications.ts +100 -0
- package/src/lexicon/types/tools/ozone/verification/listVerifications.ts +70 -0
- package/src/lexicon/types/tools/ozone/verification/revokeVerifications.ts +81 -0
- package/src/logger.ts +2 -0
- package/src/mod-service/index.ts +83 -30
- package/src/team/index.ts +4 -0
- package/src/verification/issuer.ts +135 -0
- package/src/verification/service.ts +208 -0
- package/src/verification/util.ts +50 -0
- package/tests/__snapshots__/verification-listener.test.ts.snap +146 -0
- package/tests/__snapshots__/verification.test.ts.snap +288 -0
- package/tests/get-reporter-stats.test.ts +24 -9
- package/tests/verification-listener.test.ts +102 -0
- package/tests/verification.test.ts +136 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/src/mod-service/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import net from 'node:net'
|
|
2
|
-
import { Insertable, sql } from 'kysely'
|
|
2
|
+
import { Insertable, RawBuilder, sql } from 'kysely'
|
|
3
3
|
import { CID } from 'multiformats/cid'
|
|
4
4
|
import { AtpAgent } from '@atproto/api'
|
|
5
5
|
import { addHoursToDate, chunkArray } from '@atproto/common'
|
|
@@ -1275,17 +1275,35 @@ export class ModerationService {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
}
|
|
1277
1277
|
|
|
1278
|
-
buildModerationQuery(
|
|
1278
|
+
async buildModerationQuery(
|
|
1279
1279
|
subjectType: 'account' | 'record',
|
|
1280
1280
|
createdByDids: string[],
|
|
1281
1281
|
isActionQuery: boolean,
|
|
1282
1282
|
): Promise<(Partial<ReporterStatsResult> & { did: string })[]> {
|
|
1283
|
-
|
|
1283
|
+
if (!createdByDids.length) return []
|
|
1284
|
+
|
|
1284
1285
|
const actionTypes = [
|
|
1285
1286
|
'tools.ozone.moderation.defs#modEventTakedown',
|
|
1286
1287
|
'tools.ozone.moderation.defs#modEventLabel',
|
|
1287
1288
|
] as const
|
|
1288
1289
|
|
|
1290
|
+
const countAll = () => {
|
|
1291
|
+
return sql<number>`COUNT(*)`
|
|
1292
|
+
}
|
|
1293
|
+
const countAllDistinctBy = (ref: RawBuilder) => {
|
|
1294
|
+
return sql<number>`COUNT(DISTINCT ${ref})`
|
|
1295
|
+
}
|
|
1296
|
+
const countTakedownsDistinctBy = (ref: RawBuilder) => {
|
|
1297
|
+
return sql<number>`COUNT(DISTINCT ${ref}) FILTER (
|
|
1298
|
+
WHERE actions."action" = 'tools.ozone.moderation.defs#modEventTakedown'
|
|
1299
|
+
)`
|
|
1300
|
+
}
|
|
1301
|
+
const countLabelsDistinctBy = (ref: RawBuilder) => {
|
|
1302
|
+
return sql<number>`COUNT(DISTINCT ${ref}) FILTER (
|
|
1303
|
+
WHERE actions."action" = 'tools.ozone.moderation.defs#modEventLabel'
|
|
1304
|
+
)`
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1289
1307
|
const query = this.db.db
|
|
1290
1308
|
.selectFrom('moderation_event as reports')
|
|
1291
1309
|
.where(
|
|
@@ -1293,48 +1311,83 @@ export class ModerationService {
|
|
|
1293
1311
|
'=',
|
|
1294
1312
|
'tools.ozone.moderation.defs#modEventReport',
|
|
1295
1313
|
)
|
|
1296
|
-
.where(
|
|
1314
|
+
.where(
|
|
1315
|
+
'reports.subjectUri',
|
|
1316
|
+
subjectType === 'account' ? 'is' : 'is not',
|
|
1317
|
+
null,
|
|
1318
|
+
)
|
|
1297
1319
|
.where('reports.createdBy', 'in', createdByDids)
|
|
1298
1320
|
.select(['reports.createdBy as did'])
|
|
1299
1321
|
|
|
1300
|
-
if (isActionQuery) {
|
|
1322
|
+
if (!isActionQuery) {
|
|
1323
|
+
if (subjectType === 'account') {
|
|
1324
|
+
return query
|
|
1325
|
+
.select([
|
|
1326
|
+
() => countAll().as('accountReportCount'),
|
|
1327
|
+
(eb) =>
|
|
1328
|
+
countAllDistinctBy(eb.ref('reports.subjectDid')).as(
|
|
1329
|
+
'reportedAccountCount',
|
|
1330
|
+
),
|
|
1331
|
+
])
|
|
1332
|
+
.groupBy('reports.createdBy')
|
|
1333
|
+
.execute()
|
|
1334
|
+
} else {
|
|
1335
|
+
return query
|
|
1336
|
+
.select([
|
|
1337
|
+
() => countAll().as('recordReportCount'),
|
|
1338
|
+
(eb) =>
|
|
1339
|
+
countAllDistinctBy(eb.ref('reports.subjectUri')).as(
|
|
1340
|
+
'reportedRecordCount',
|
|
1341
|
+
),
|
|
1342
|
+
])
|
|
1343
|
+
.groupBy('reports.createdBy')
|
|
1344
|
+
.execute()
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
if (subjectType === 'account') {
|
|
1349
|
+
return query
|
|
1350
|
+
.leftJoin('moderation_event as actions', (join) =>
|
|
1351
|
+
join
|
|
1352
|
+
.onRef('actions.subjectDid', '=', 'reports.subjectDid')
|
|
1353
|
+
.on('actions.subjectUri', 'is', null)
|
|
1354
|
+
.onRef('actions.createdAt', '>', 'reports.createdAt')
|
|
1355
|
+
.on('actions.action', 'in', actionTypes),
|
|
1356
|
+
)
|
|
1357
|
+
.select([
|
|
1358
|
+
(eb) =>
|
|
1359
|
+
countTakedownsDistinctBy(eb.ref('actions.subjectDid')).as(
|
|
1360
|
+
'takendownAccountCount',
|
|
1361
|
+
),
|
|
1362
|
+
(eb) =>
|
|
1363
|
+
countLabelsDistinctBy(eb.ref('actions.subjectDid')).as(
|
|
1364
|
+
'labeledAccountCount',
|
|
1365
|
+
),
|
|
1366
|
+
])
|
|
1367
|
+
.groupBy('reports.createdBy')
|
|
1368
|
+
.execute()
|
|
1369
|
+
} else {
|
|
1301
1370
|
return query
|
|
1302
1371
|
.leftJoin('moderation_event as actions', (join) =>
|
|
1303
1372
|
join
|
|
1304
1373
|
.onRef('actions.subjectDid', '=', 'reports.subjectDid')
|
|
1305
|
-
.
|
|
1374
|
+
.onRef('actions.subjectUri', '=', 'reports.subjectUri')
|
|
1306
1375
|
.onRef('actions.createdAt', '>', 'reports.createdAt')
|
|
1307
1376
|
.on('actions.action', 'in', actionTypes),
|
|
1308
1377
|
)
|
|
1309
1378
|
.select([
|
|
1310
|
-
() =>
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
)
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
)`.as(`labeled${isAccount ? 'Account' : 'Record'}Count`),
|
|
1379
|
+
(eb) =>
|
|
1380
|
+
countTakedownsDistinctBy(eb.ref('actions.subjectUri')).as(
|
|
1381
|
+
'takendownRecordCount',
|
|
1382
|
+
),
|
|
1383
|
+
(eb) =>
|
|
1384
|
+
countLabelsDistinctBy(eb.ref('actions.subjectUri')).as(
|
|
1385
|
+
'labeledRecordCount',
|
|
1386
|
+
),
|
|
1319
1387
|
])
|
|
1320
1388
|
.groupBy('reports.createdBy')
|
|
1321
1389
|
.execute()
|
|
1322
1390
|
}
|
|
1323
|
-
|
|
1324
|
-
return query
|
|
1325
|
-
.select([
|
|
1326
|
-
(eb) =>
|
|
1327
|
-
eb.fn.count<number>('reports.id').as(`${subjectType}ReportCount`),
|
|
1328
|
-
(eb) =>
|
|
1329
|
-
eb.fn
|
|
1330
|
-
.count<number>(
|
|
1331
|
-
isAccount ? 'reports.subjectDid' : 'reports.subjectUri',
|
|
1332
|
-
)
|
|
1333
|
-
.distinct()
|
|
1334
|
-
.as(`reported${isAccount ? 'Account' : 'Record'}Count`),
|
|
1335
|
-
])
|
|
1336
|
-
.groupBy('reports.createdBy')
|
|
1337
|
-
.execute()
|
|
1338
1391
|
}
|
|
1339
1392
|
|
|
1340
1393
|
async getReporterStats(dids: string[]) {
|
package/src/team/index.ts
CHANGED
|
@@ -54,6 +54,7 @@ export class TeamService {
|
|
|
54
54
|
(r) =>
|
|
55
55
|
r === 'tools.ozone.team.defs#roleAdmin' ||
|
|
56
56
|
r === 'tools.ozone.team.defs#roleModerator' ||
|
|
57
|
+
r === 'tools.ozone.team.defs#roleVerifier' ||
|
|
57
58
|
r === 'tools.ozone.team.defs#roleTriage',
|
|
58
59
|
)
|
|
59
60
|
|
|
@@ -197,11 +198,14 @@ export class TeamService {
|
|
|
197
198
|
isAdmin || member?.role === 'tools.ozone.team.defs#roleModerator'
|
|
198
199
|
const isTriage =
|
|
199
200
|
isModerator || member?.role === 'tools.ozone.team.defs#roleTriage'
|
|
201
|
+
const isVerifier =
|
|
202
|
+
isAdmin || member?.role === 'tools.ozone.team.defs#roleVerifier'
|
|
200
203
|
|
|
201
204
|
return {
|
|
202
205
|
isModerator,
|
|
203
206
|
isAdmin,
|
|
204
207
|
isTriage,
|
|
208
|
+
isVerifier,
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Selectable } from 'kysely'
|
|
2
|
+
import { Agent, AtUri, CredentialSession } from '@atproto/api'
|
|
3
|
+
import { VerifierConfig } from '../config'
|
|
4
|
+
import { Verification } from '../db/schema/verification'
|
|
5
|
+
|
|
6
|
+
export type VerificationInput = {
|
|
7
|
+
displayName: string
|
|
8
|
+
handle: string
|
|
9
|
+
subject: string
|
|
10
|
+
createdAt?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type VerificationIssuerCreator = (
|
|
14
|
+
verifierConfig: VerifierConfig,
|
|
15
|
+
) => VerificationIssuer
|
|
16
|
+
|
|
17
|
+
export class VerificationIssuer {
|
|
18
|
+
private session = new CredentialSession(new URL(this.verifierConfig.url))
|
|
19
|
+
private agent = new Agent(this.session)
|
|
20
|
+
constructor(private verifierConfig: VerifierConfig) {}
|
|
21
|
+
|
|
22
|
+
static creator() {
|
|
23
|
+
return (verifierConfig: VerifierConfig) =>
|
|
24
|
+
new VerificationIssuer(verifierConfig)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getAgent() {
|
|
28
|
+
if (!this.session.hasSession) {
|
|
29
|
+
await this.session.login({
|
|
30
|
+
identifier: this.verifierConfig.did,
|
|
31
|
+
password: this.verifierConfig.password,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Trigger a test request to check if the session is still valid, if not, we will login again
|
|
36
|
+
try {
|
|
37
|
+
await this.agent.com.atproto.server.getSession()
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if ((err as any).status === 401) {
|
|
40
|
+
await this.session.login({
|
|
41
|
+
identifier: this.verifierConfig.did,
|
|
42
|
+
password: this.verifierConfig.password,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.agent
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async verify(verifications: VerificationInput[]) {
|
|
51
|
+
const grantedVerifications: Selectable<Verification>[] = []
|
|
52
|
+
const failedVerifications: {
|
|
53
|
+
$type: 'tools.ozone.verification.grantVerifications#grantError'
|
|
54
|
+
subject: string
|
|
55
|
+
error: string
|
|
56
|
+
}[] = []
|
|
57
|
+
const now = new Date().toISOString()
|
|
58
|
+
const agent = await this.getAgent()
|
|
59
|
+
await Promise.allSettled(
|
|
60
|
+
verifications.map(async ({ displayName, handle, subject, createdAt }) => {
|
|
61
|
+
try {
|
|
62
|
+
const verificationRecord = {
|
|
63
|
+
createdAt: createdAt || now,
|
|
64
|
+
issuer: this.verifierConfig.did,
|
|
65
|
+
displayName,
|
|
66
|
+
handle,
|
|
67
|
+
subject,
|
|
68
|
+
}
|
|
69
|
+
const {
|
|
70
|
+
data: { uri, cid },
|
|
71
|
+
} = await agent.com.atproto.repo.createRecord({
|
|
72
|
+
repo: this.verifierConfig.did,
|
|
73
|
+
record: verificationRecord,
|
|
74
|
+
collection: 'app.bsky.graph.verification',
|
|
75
|
+
})
|
|
76
|
+
grantedVerifications.push({
|
|
77
|
+
...verificationRecord,
|
|
78
|
+
uri,
|
|
79
|
+
cid,
|
|
80
|
+
revokedAt: null,
|
|
81
|
+
updatedAt: now,
|
|
82
|
+
revokedBy: null,
|
|
83
|
+
revokeReason: null,
|
|
84
|
+
})
|
|
85
|
+
} catch (err) {
|
|
86
|
+
failedVerifications.push({
|
|
87
|
+
$type: 'tools.ozone.verification.grantVerifications#grantError',
|
|
88
|
+
error: (err as Error).message,
|
|
89
|
+
subject,
|
|
90
|
+
})
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return { grantedVerifications, failedVerifications }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async revoke({ uris }: { uris: string[] }) {
|
|
100
|
+
const revokedVerifications: string[] = []
|
|
101
|
+
const failedRevocations: Array<{ uri: string; error: string }> = []
|
|
102
|
+
|
|
103
|
+
const agent = await this.getAgent()
|
|
104
|
+
|
|
105
|
+
await Promise.allSettled(
|
|
106
|
+
uris.map(async (uri) => {
|
|
107
|
+
try {
|
|
108
|
+
const atUri = new AtUri(uri)
|
|
109
|
+
|
|
110
|
+
if (atUri.collection !== 'app.bsky.graph.verification') {
|
|
111
|
+
throw new Error(`Only verification records can be revoked`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (atUri.host !== this.verifierConfig.did) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Cannot revoke verification record ${uri} not issued by ${this.verifierConfig.did}`,
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await agent.com.atproto.repo.deleteRecord({
|
|
121
|
+
collection: atUri.collection,
|
|
122
|
+
repo: this.verifierConfig.did,
|
|
123
|
+
rkey: atUri.rkey,
|
|
124
|
+
})
|
|
125
|
+
revokedVerifications.push(uri)
|
|
126
|
+
} catch (err) {
|
|
127
|
+
failedRevocations.push({ uri, error: (err as Error).message })
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return { revokedVerifications, failedRevocations }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Selectable } from 'kysely'
|
|
2
|
+
import {
|
|
3
|
+
$Typed,
|
|
4
|
+
AppBskyActorDefs,
|
|
5
|
+
AtUri,
|
|
6
|
+
ToolsOzoneModerationDefs,
|
|
7
|
+
ToolsOzoneVerificationDefs,
|
|
8
|
+
} from '@atproto/api'
|
|
9
|
+
import { Database } from '../db'
|
|
10
|
+
import { CreatedAtUriKeyset, paginate } from '../db/pagination'
|
|
11
|
+
import { Verification } from '../db/schema/verification'
|
|
12
|
+
|
|
13
|
+
export type VerificationServiceCreator = (db: Database) => VerificationService
|
|
14
|
+
|
|
15
|
+
export class VerificationService {
|
|
16
|
+
constructor(public db: Database) {}
|
|
17
|
+
|
|
18
|
+
static creator() {
|
|
19
|
+
return (db: Database) => new VerificationService(db)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async create(
|
|
23
|
+
verifications: Pick<
|
|
24
|
+
Verification,
|
|
25
|
+
| 'uri'
|
|
26
|
+
| 'issuer'
|
|
27
|
+
| 'subject'
|
|
28
|
+
| 'handle'
|
|
29
|
+
| 'displayName'
|
|
30
|
+
| 'createdAt'
|
|
31
|
+
| 'cid'
|
|
32
|
+
>[],
|
|
33
|
+
) {
|
|
34
|
+
return this.db.transaction(async (tx) => {
|
|
35
|
+
return tx.db
|
|
36
|
+
.insertInto('verification')
|
|
37
|
+
.values(verifications)
|
|
38
|
+
.onConflict((oc) => oc.doNothing())
|
|
39
|
+
.returningAll()
|
|
40
|
+
.execute()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async markRevoked({
|
|
45
|
+
uris,
|
|
46
|
+
revokedBy,
|
|
47
|
+
revokedAt,
|
|
48
|
+
revokeReason,
|
|
49
|
+
}: {
|
|
50
|
+
uris: string[]
|
|
51
|
+
revokedBy?: string
|
|
52
|
+
revokedAt?: string
|
|
53
|
+
revokeReason?: string
|
|
54
|
+
}) {
|
|
55
|
+
const now = new Date().toISOString()
|
|
56
|
+
return this.db.transaction(async (tx) => {
|
|
57
|
+
for (const uri of uris) {
|
|
58
|
+
return tx.db
|
|
59
|
+
.updateTable('verification')
|
|
60
|
+
.set({
|
|
61
|
+
revokeReason,
|
|
62
|
+
updatedAt: now,
|
|
63
|
+
revokedAt: revokedAt || now,
|
|
64
|
+
// Allow setting revokedBy to a moderator/verifier DID and if it isn't set, default to the author of the verification record
|
|
65
|
+
revokedBy: revokedBy || new AtUri(uri).host,
|
|
66
|
+
})
|
|
67
|
+
.where('uri', '=', uri)
|
|
68
|
+
.where('revokedAt', 'is', null)
|
|
69
|
+
.execute()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async list({
|
|
75
|
+
sortDirection,
|
|
76
|
+
cursor,
|
|
77
|
+
createdAfter,
|
|
78
|
+
createdBefore,
|
|
79
|
+
issuers = [],
|
|
80
|
+
subjects = [],
|
|
81
|
+
isRevoked,
|
|
82
|
+
limit = 100,
|
|
83
|
+
}: {
|
|
84
|
+
sortDirection?: 'asc' | 'desc'
|
|
85
|
+
cursor?: string
|
|
86
|
+
createdAfter?: string
|
|
87
|
+
createdBefore?: string
|
|
88
|
+
issuers?: string[]
|
|
89
|
+
subjects?: string[]
|
|
90
|
+
isRevoked?: boolean
|
|
91
|
+
limit?: number
|
|
92
|
+
}) {
|
|
93
|
+
const { ref } = this.db.db.dynamic
|
|
94
|
+
|
|
95
|
+
let qb = this.db.db.selectFrom('verification').selectAll()
|
|
96
|
+
|
|
97
|
+
if (issuers.length) {
|
|
98
|
+
qb = qb.where('issuer', 'in', issuers)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (isRevoked !== undefined) {
|
|
102
|
+
qb = qb.where('revokedAt', isRevoked ? 'is not' : 'is', null)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (subjects.length) {
|
|
106
|
+
qb = qb.where('subject', 'in', subjects)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (createdAfter) {
|
|
110
|
+
qb = qb.where('createdAt', '>=', createdAfter)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (createdBefore) {
|
|
114
|
+
qb = qb.where('createdAt', '<=', createdBefore)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const keyset = new CreatedAtUriKeyset(ref(`createdAt`), ref('uri'))
|
|
118
|
+
const paginatedBuilder = paginate(qb, {
|
|
119
|
+
limit,
|
|
120
|
+
cursor,
|
|
121
|
+
keyset,
|
|
122
|
+
tryIndex: true,
|
|
123
|
+
direction: sortDirection === 'desc' ? 'desc' : 'asc',
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const result = await paginatedBuilder.execute()
|
|
127
|
+
return { verifications: result, cursor: keyset.packFromResult(result) }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
view(
|
|
131
|
+
verifications: Selectable<Verification>[],
|
|
132
|
+
repos: Map<
|
|
133
|
+
string,
|
|
134
|
+
| $Typed<ToolsOzoneModerationDefs.RepoViewDetail>
|
|
135
|
+
| $Typed<ToolsOzoneModerationDefs.RepoViewNotFound>
|
|
136
|
+
>,
|
|
137
|
+
profiles: Map<string, AppBskyActorDefs.ProfileViewDetailed>,
|
|
138
|
+
): $Typed<ToolsOzoneVerificationDefs.VerificationView>[] {
|
|
139
|
+
return verifications.map((verification) => {
|
|
140
|
+
const issuerRepo = repos.get(verification.issuer)
|
|
141
|
+
const subjectRepo = repos.get(verification.subject)
|
|
142
|
+
const subjectProfile = profiles.get(verification.subject)
|
|
143
|
+
const issuerProfile = profiles.get(verification.issuer)
|
|
144
|
+
return {
|
|
145
|
+
$type: 'tools.ozone.verification.defs#verificationView',
|
|
146
|
+
uri: verification.uri,
|
|
147
|
+
issuer: verification.issuer,
|
|
148
|
+
subject: verification.subject,
|
|
149
|
+
createdAt: verification.createdAt,
|
|
150
|
+
displayName: verification.displayName,
|
|
151
|
+
handle: verification.handle,
|
|
152
|
+
updatedAt: verification.updatedAt || undefined,
|
|
153
|
+
revokedAt: verification.revokedAt || undefined,
|
|
154
|
+
revokedBy: verification.revokedBy || undefined,
|
|
155
|
+
revokeReason: verification.revokeReason || undefined,
|
|
156
|
+
issuerRepo,
|
|
157
|
+
subjectRepo,
|
|
158
|
+
subjectProfile: subjectProfile
|
|
159
|
+
? {
|
|
160
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed',
|
|
161
|
+
...subjectProfile,
|
|
162
|
+
}
|
|
163
|
+
: undefined,
|
|
164
|
+
issuerProfile: issuerProfile
|
|
165
|
+
? {
|
|
166
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed',
|
|
167
|
+
...issuerProfile,
|
|
168
|
+
}
|
|
169
|
+
: undefined,
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async getFirehoseCursor() {
|
|
175
|
+
const entry = await this.db.db
|
|
176
|
+
.selectFrom('firehose_cursor')
|
|
177
|
+
.select('cursor')
|
|
178
|
+
.where('service', '=', 'verification')
|
|
179
|
+
.executeTakeFirst()
|
|
180
|
+
|
|
181
|
+
return entry?.cursor || null
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
createFirehoseCursor() {
|
|
185
|
+
return this.db.db
|
|
186
|
+
.insertInto('firehose_cursor')
|
|
187
|
+
.values({
|
|
188
|
+
service: 'verification',
|
|
189
|
+
cursor: null,
|
|
190
|
+
})
|
|
191
|
+
.onConflict((oc) => oc.doNothing())
|
|
192
|
+
.execute()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async updateFirehoseCursor(cursor: number) {
|
|
196
|
+
const updated = await this.db.db
|
|
197
|
+
.updateTable('firehose_cursor')
|
|
198
|
+
.set({ cursor })
|
|
199
|
+
.where('service', '=', 'verification')
|
|
200
|
+
.where((qb) =>
|
|
201
|
+
qb.where('cursor', '<', cursor).orWhere('cursor', 'is', null),
|
|
202
|
+
)
|
|
203
|
+
.returningAll()
|
|
204
|
+
.executeTakeFirst()
|
|
205
|
+
|
|
206
|
+
return updated?.cursor
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { $Typed, ToolsOzoneModerationDefs } from '@atproto/api'
|
|
2
|
+
import { addAccountInfoToRepoViewDetail, getPdsAccountInfos } from '../api/util'
|
|
3
|
+
import { AppContext } from '../context'
|
|
4
|
+
import { ModerationService } from '../mod-service'
|
|
5
|
+
import { ParsedLabelers } from '../util'
|
|
6
|
+
|
|
7
|
+
export const getReposForVerifications = async (
|
|
8
|
+
ctx: AppContext,
|
|
9
|
+
labelers: ParsedLabelers,
|
|
10
|
+
modService: ModerationService,
|
|
11
|
+
dids: string[],
|
|
12
|
+
isModerator: boolean,
|
|
13
|
+
) => {
|
|
14
|
+
const [partialRepos, accountInfo] = await Promise.all([
|
|
15
|
+
modService.views.repoDetails(dids, labelers),
|
|
16
|
+
getPdsAccountInfos(ctx, dids),
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
const repos = new Map<
|
|
20
|
+
string,
|
|
21
|
+
| $Typed<ToolsOzoneModerationDefs.RepoViewDetail>
|
|
22
|
+
| $Typed<ToolsOzoneModerationDefs.RepoViewNotFound>
|
|
23
|
+
>(
|
|
24
|
+
dids.map((did) => {
|
|
25
|
+
const partialRepo = partialRepos.get(did)
|
|
26
|
+
if (!partialRepo) {
|
|
27
|
+
return [
|
|
28
|
+
did,
|
|
29
|
+
{
|
|
30
|
+
did,
|
|
31
|
+
$type: 'tools.ozone.moderation.defs#repoViewNotFound',
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
did,
|
|
37
|
+
{
|
|
38
|
+
...addAccountInfoToRepoViewDetail(
|
|
39
|
+
partialRepo,
|
|
40
|
+
accountInfo.get(did) || null,
|
|
41
|
+
isModerator,
|
|
42
|
+
),
|
|
43
|
+
$type: 'tools.ozone.moderation.defs#repoViewDetail',
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return repos
|
|
50
|
+
}
|