@atproto/ozone 0.1.107 → 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.
Files changed (198) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +6 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/server/getConfig.d.ts.map +1 -1
  6. package/dist/api/server/getConfig.js +1 -0
  7. package/dist/api/server/getConfig.js.map +1 -1
  8. package/dist/api/setting/removeOptions.d.ts.map +1 -1
  9. package/dist/api/setting/removeOptions.js +1 -0
  10. package/dist/api/setting/removeOptions.js.map +1 -1
  11. package/dist/api/setting/upsertOption.js +7 -0
  12. package/dist/api/setting/upsertOption.js.map +1 -1
  13. package/dist/api/util.d.ts +1 -1
  14. package/dist/api/util.d.ts.map +1 -1
  15. package/dist/api/util.js +6 -1
  16. package/dist/api/util.js.map +1 -1
  17. package/dist/api/verification/grantVerifications.d.ts +4 -0
  18. package/dist/api/verification/grantVerifications.d.ts.map +1 -0
  19. package/dist/api/verification/grantVerifications.js +52 -0
  20. package/dist/api/verification/grantVerifications.js.map +1 -0
  21. package/dist/api/verification/listVerifications.d.ts +4 -0
  22. package/dist/api/verification/listVerifications.d.ts.map +1 -0
  23. package/dist/api/verification/listVerifications.js +32 -0
  24. package/dist/api/verification/listVerifications.js.map +1 -0
  25. package/dist/api/verification/revokeVerifications.d.ts +4 -0
  26. package/dist/api/verification/revokeVerifications.d.ts.map +1 -0
  27. package/dist/api/verification/revokeVerifications.js +36 -0
  28. package/dist/api/verification/revokeVerifications.js.map +1 -0
  29. package/dist/auth-verifier.d.ts +4 -1
  30. package/dist/auth-verifier.d.ts.map +1 -1
  31. package/dist/auth-verifier.js +4 -3
  32. package/dist/auth-verifier.js.map +1 -1
  33. package/dist/background.d.ts +3 -1
  34. package/dist/background.d.ts.map +1 -1
  35. package/dist/background.js +3 -2
  36. package/dist/background.js.map +1 -1
  37. package/dist/config/config.d.ts +9 -0
  38. package/dist/config/config.d.ts.map +1 -1
  39. package/dist/config/config.js +10 -0
  40. package/dist/config/config.js.map +1 -1
  41. package/dist/config/env.d.ts +5 -0
  42. package/dist/config/env.d.ts.map +1 -1
  43. package/dist/config/env.js +5 -0
  44. package/dist/config/env.js.map +1 -1
  45. package/dist/context.d.ts +6 -0
  46. package/dist/context.d.ts.map +1 -1
  47. package/dist/context.js +12 -0
  48. package/dist/context.js.map +1 -1
  49. package/dist/daemon/context.d.ts +3 -0
  50. package/dist/daemon/context.d.ts.map +1 -1
  51. package/dist/daemon/context.js +11 -0
  52. package/dist/daemon/context.js.map +1 -1
  53. package/dist/daemon/verification-listener.d.ts +29 -0
  54. package/dist/daemon/verification-listener.d.ts.map +1 -0
  55. package/dist/daemon/verification-listener.js +171 -0
  56. package/dist/daemon/verification-listener.js.map +1 -0
  57. package/dist/db/migrations/20250415T201720309Z-verification.d.ts +4 -0
  58. package/dist/db/migrations/20250415T201720309Z-verification.d.ts.map +1 -0
  59. package/dist/db/migrations/20250415T201720309Z-verification.js +35 -0
  60. package/dist/db/migrations/20250415T201720309Z-verification.js.map +1 -0
  61. package/dist/db/migrations/20250417T201720309Z-firehose-cursor.d.ts +4 -0
  62. package/dist/db/migrations/20250417T201720309Z-firehose-cursor.d.ts.map +1 -0
  63. package/dist/db/migrations/20250417T201720309Z-firehose-cursor.js +17 -0
  64. package/dist/db/migrations/20250417T201720309Z-firehose-cursor.js.map +1 -0
  65. package/dist/db/migrations/index.d.ts +2 -0
  66. package/dist/db/migrations/index.d.ts.map +1 -1
  67. package/dist/db/migrations/index.js +3 -1
  68. package/dist/db/migrations/index.js.map +1 -1
  69. package/dist/db/pagination.d.ts +15 -0
  70. package/dist/db/pagination.d.ts.map +1 -1
  71. package/dist/db/pagination.js +23 -1
  72. package/dist/db/pagination.js.map +1 -1
  73. package/dist/db/schema/firehose_cursor.d.ts +11 -0
  74. package/dist/db/schema/firehose_cursor.d.ts.map +1 -0
  75. package/dist/db/schema/firehose_cursor.js +5 -0
  76. package/dist/db/schema/firehose_cursor.js.map +1 -0
  77. package/dist/db/schema/index.d.ts +3 -1
  78. package/dist/db/schema/index.d.ts.map +1 -1
  79. package/dist/db/schema/member.d.ts +1 -1
  80. package/dist/db/schema/member.d.ts.map +1 -1
  81. package/dist/db/schema/verification.d.ts +19 -0
  82. package/dist/db/schema/verification.d.ts.map +1 -0
  83. package/dist/db/schema/verification.js +5 -0
  84. package/dist/db/schema/verification.js.map +1 -0
  85. package/dist/jetstream/service.d.ts +64 -0
  86. package/dist/jetstream/service.d.ts.map +1 -0
  87. package/dist/jetstream/service.js +65 -0
  88. package/dist/jetstream/service.js.map +1 -0
  89. package/dist/lexicon/index.d.ts +12 -0
  90. package/dist/lexicon/index.d.ts.map +1 -1
  91. package/dist/lexicon/index.js +33 -1
  92. package/dist/lexicon/index.js.map +1 -1
  93. package/dist/lexicon/lexicons.d.ts +672 -12
  94. package/dist/lexicon/lexicons.d.ts.map +1 -1
  95. package/dist/lexicon/lexicons.js +353 -0
  96. package/dist/lexicon/lexicons.js.map +1 -1
  97. package/dist/lexicon/types/tools/ozone/server/getConfig.d.ts +3 -1
  98. package/dist/lexicon/types/tools/ozone/server/getConfig.d.ts.map +1 -1
  99. package/dist/lexicon/types/tools/ozone/server/getConfig.js.map +1 -1
  100. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +1 -1
  101. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -1
  102. package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -1
  103. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +1 -1
  104. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -1
  105. package/dist/lexicon/types/tools/ozone/team/addMember.d.ts +1 -1
  106. package/dist/lexicon/types/tools/ozone/team/addMember.d.ts.map +1 -1
  107. package/dist/lexicon/types/tools/ozone/team/defs.d.ts +3 -1
  108. package/dist/lexicon/types/tools/ozone/team/defs.d.ts.map +1 -1
  109. package/dist/lexicon/types/tools/ozone/team/defs.js +3 -1
  110. package/dist/lexicon/types/tools/ozone/team/defs.js.map +1 -1
  111. package/dist/lexicon/types/tools/ozone/team/updateMember.d.ts +1 -1
  112. package/dist/lexicon/types/tools/ozone/team/updateMember.d.ts.map +1 -1
  113. package/dist/lexicon/types/tools/ozone/verification/defs.d.ts +43 -0
  114. package/dist/lexicon/types/tools/ozone/verification/defs.d.ts.map +1 -0
  115. package/dist/lexicon/types/tools/ozone/verification/defs.js +16 -0
  116. package/dist/lexicon/types/tools/ozone/verification/defs.js.map +1 -0
  117. package/dist/lexicon/types/tools/ozone/verification/grantVerifications.d.ts +66 -0
  118. package/dist/lexicon/types/tools/ozone/verification/grantVerifications.d.ts.map +1 -0
  119. package/dist/lexicon/types/tools/ozone/verification/grantVerifications.js +25 -0
  120. package/dist/lexicon/types/tools/ozone/verification/grantVerifications.js.map +1 -0
  121. package/dist/lexicon/types/tools/ozone/verification/listVerifications.d.ts +52 -0
  122. package/dist/lexicon/types/tools/ozone/verification/listVerifications.d.ts.map +1 -0
  123. package/dist/lexicon/types/tools/ozone/verification/listVerifications.js +7 -0
  124. package/dist/lexicon/types/tools/ozone/verification/listVerifications.js.map +1 -0
  125. package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.d.ts +56 -0
  126. package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.d.ts.map +1 -0
  127. package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.js +16 -0
  128. package/dist/lexicon/types/tools/ozone/verification/revokeVerifications.js.map +1 -0
  129. package/dist/logger.d.ts +1 -0
  130. package/dist/logger.d.ts.map +1 -1
  131. package/dist/logger.js +2 -1
  132. package/dist/logger.js.map +1 -1
  133. package/dist/mod-service/status.d.ts +6 -0
  134. package/dist/mod-service/status.d.ts.map +1 -1
  135. package/dist/team/index.d.ts +1 -0
  136. package/dist/team/index.d.ts.map +1 -1
  137. package/dist/team/index.js +3 -0
  138. package/dist/team/index.js.map +1 -1
  139. package/dist/verification/issuer.d.ts +37 -0
  140. package/dist/verification/issuer.d.ts.map +1 -0
  141. package/dist/verification/issuer.js +119 -0
  142. package/dist/verification/issuer.js.map +1 -0
  143. package/dist/verification/service.d.ts +47 -0
  144. package/dist/verification/service.d.ts.map +1 -0
  145. package/dist/verification/service.js +141 -0
  146. package/dist/verification/service.js.map +1 -0
  147. package/dist/verification/util.d.ts +6 -0
  148. package/dist/verification/util.d.ts.map +1 -0
  149. package/dist/verification/util.js +32 -0
  150. package/dist/verification/util.js.map +1 -0
  151. package/package.json +5 -4
  152. package/src/api/index.ts +6 -0
  153. package/src/api/server/getConfig.ts +1 -0
  154. package/src/api/setting/removeOptions.ts +1 -0
  155. package/src/api/setting/upsertOption.ts +7 -0
  156. package/src/api/util.ts +7 -1
  157. package/src/api/verification/grantVerifications.ts +74 -0
  158. package/src/api/verification/listVerifications.ts +44 -0
  159. package/src/api/verification/revokeVerifications.ts +43 -0
  160. package/src/auth-verifier.ts +8 -4
  161. package/src/background.ts +7 -2
  162. package/src/config/config.ts +21 -0
  163. package/src/config/env.ts +10 -0
  164. package/src/context.ts +22 -0
  165. package/src/daemon/context.ts +19 -0
  166. package/src/daemon/verification-listener.ts +164 -0
  167. package/src/db/migrations/20250415T201720309Z-verification.ts +34 -0
  168. package/src/db/migrations/20250417T201720309Z-firehose-cursor.ts +16 -0
  169. package/src/db/migrations/index.ts +2 -0
  170. package/src/db/pagination.ts +31 -0
  171. package/src/db/schema/firehose_cursor.ts +13 -0
  172. package/src/db/schema/index.ts +5 -1
  173. package/src/db/schema/member.ts +1 -0
  174. package/src/db/schema/verification.ts +21 -0
  175. package/src/jetstream/service.ts +110 -0
  176. package/src/lexicon/index.ts +47 -0
  177. package/src/lexicon/lexicons.ts +372 -0
  178. package/src/lexicon/types/tools/ozone/server/getConfig.ts +3 -0
  179. package/src/lexicon/types/tools/ozone/setting/defs.ts +1 -0
  180. package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +1 -0
  181. package/src/lexicon/types/tools/ozone/team/addMember.ts +1 -0
  182. package/src/lexicon/types/tools/ozone/team/defs.ts +3 -0
  183. package/src/lexicon/types/tools/ozone/team/updateMember.ts +1 -0
  184. package/src/lexicon/types/tools/ozone/verification/defs.ts +59 -0
  185. package/src/lexicon/types/tools/ozone/verification/grantVerifications.ts +100 -0
  186. package/src/lexicon/types/tools/ozone/verification/listVerifications.ts +70 -0
  187. package/src/lexicon/types/tools/ozone/verification/revokeVerifications.ts +81 -0
  188. package/src/logger.ts +2 -0
  189. package/src/team/index.ts +4 -0
  190. package/src/verification/issuer.ts +135 -0
  191. package/src/verification/service.ts +208 -0
  192. package/src/verification/util.ts +50 -0
  193. package/tests/__snapshots__/verification-listener.test.ts.snap +146 -0
  194. package/tests/__snapshots__/verification.test.ts.snap +288 -0
  195. package/tests/verification-listener.test.ts +102 -0
  196. package/tests/verification.test.ts +136 -0
  197. package/tsconfig.build.tsbuildinfo +1 -1
  198. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { type ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { CID } from 'multiformats/cid'
7
+ import { validate as _validate } from '../../../../lexicons'
8
+ import {
9
+ type $Typed,
10
+ is$typed as _is$typed,
11
+ type OmitKey,
12
+ } from '../../../../util'
13
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
14
+ import type * as ToolsOzoneVerificationDefs from './defs.js'
15
+
16
+ const is$typed = _is$typed,
17
+ validate = _validate
18
+ const id = 'tools.ozone.verification.listVerifications'
19
+
20
+ export interface QueryParams {
21
+ /** Pagination cursor */
22
+ cursor?: string
23
+ /** Maximum number of results to return */
24
+ limit: number
25
+ /** Filter to verifications created after this timestamp */
26
+ createdAfter?: string
27
+ /** Filter to verifications created before this timestamp */
28
+ createdBefore?: string
29
+ /** Filter to verifications from specific issuers */
30
+ issuers?: string[]
31
+ /** Filter to specific verified DIDs */
32
+ subjects?: string[]
33
+ /** Sort direction for creation date */
34
+ sortDirection: 'asc' | 'desc'
35
+ /** Filter to verifications that are revoked or not. By default, includes both. */
36
+ isRevoked?: boolean
37
+ }
38
+
39
+ export type InputSchema = undefined
40
+
41
+ export interface OutputSchema {
42
+ cursor?: string
43
+ verifications: ToolsOzoneVerificationDefs.VerificationView[]
44
+ }
45
+
46
+ export type HandlerInput = undefined
47
+
48
+ export interface HandlerSuccess {
49
+ encoding: 'application/json'
50
+ body: OutputSchema
51
+ headers?: { [key: string]: string }
52
+ }
53
+
54
+ export interface HandlerError {
55
+ status: number
56
+ message?: string
57
+ }
58
+
59
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
60
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
61
+ auth: HA
62
+ params: QueryParams
63
+ input: HandlerInput
64
+ req: express.Request
65
+ res: express.Response
66
+ resetRouteRateLimits: () => Promise<void>
67
+ }
68
+ export type Handler<HA extends HandlerAuth = never> = (
69
+ ctx: HandlerReqCtx<HA>,
70
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,81 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { type ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { CID } from 'multiformats/cid'
7
+ import { validate as _validate } from '../../../../lexicons'
8
+ import {
9
+ type $Typed,
10
+ is$typed as _is$typed,
11
+ type OmitKey,
12
+ } from '../../../../util'
13
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
14
+
15
+ const is$typed = _is$typed,
16
+ validate = _validate
17
+ const id = 'tools.ozone.verification.revokeVerifications'
18
+
19
+ export interface QueryParams {}
20
+
21
+ export interface InputSchema {
22
+ /** Array of verification record uris to revoke */
23
+ uris: string[]
24
+ /** Reason for revoking the verification. This is optional and can be omitted if not needed. */
25
+ revokeReason?: string
26
+ }
27
+
28
+ export interface OutputSchema {
29
+ /** List of verification uris successfully revoked */
30
+ revokedVerifications: string[]
31
+ /** List of verification uris that couldn't be revoked, including failure reasons */
32
+ failedRevocations: RevokeError[]
33
+ }
34
+
35
+ export interface HandlerInput {
36
+ encoding: 'application/json'
37
+ body: InputSchema
38
+ }
39
+
40
+ export interface HandlerSuccess {
41
+ encoding: 'application/json'
42
+ body: OutputSchema
43
+ headers?: { [key: string]: string }
44
+ }
45
+
46
+ export interface HandlerError {
47
+ status: number
48
+ message?: string
49
+ }
50
+
51
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
52
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
53
+ auth: HA
54
+ params: QueryParams
55
+ input: HandlerInput
56
+ req: express.Request
57
+ res: express.Response
58
+ resetRouteRateLimits: () => Promise<void>
59
+ }
60
+ export type Handler<HA extends HandlerAuth = never> = (
61
+ ctx: HandlerReqCtx<HA>,
62
+ ) => Promise<HandlerOutput> | HandlerOutput
63
+
64
+ /** Error object for failed revocations */
65
+ export interface RevokeError {
66
+ $type?: 'tools.ozone.verification.revokeVerifications#revokeError'
67
+ /** The AT-URI of the verification record that failed to revoke. */
68
+ uri: string
69
+ /** Description of the error that occurred during revocation. */
70
+ error: string
71
+ }
72
+
73
+ const hashRevokeError = 'revokeError'
74
+
75
+ export function isRevokeError<V>(v: V) {
76
+ return is$typed(v, id, hashRevokeError)
77
+ }
78
+
79
+ export function validateRevokeError<V>(v: V) {
80
+ return validate<RevokeError & V>(v, id, hashRevokeError)
81
+ }
package/src/logger.ts CHANGED
@@ -10,6 +10,8 @@ export const httpLogger: ReturnType<typeof subsystemLogger> =
10
10
  subsystemLogger('ozone')
11
11
  export const langLogger: ReturnType<typeof subsystemLogger> =
12
12
  subsystemLogger('ozone:lang')
13
+ export const verificationLogger: ReturnType<typeof subsystemLogger> =
14
+ subsystemLogger('ozone:verification')
13
15
 
14
16
  export const loggerMiddleware = pinoHttp({
15
17
  logger: httpLogger,
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
+ }