@atproto/bsky 0.0.170 → 0.0.171
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 +9 -0
- package/dist/api/app/bsky/unspecced/getAgeAssuranceState.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/getAgeAssuranceState.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/getAgeAssuranceState.js +36 -0
- package/dist/api/app/bsky/unspecced/getAgeAssuranceState.js.map +1 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js +59 -0
- package/dist/api/app/bsky/unspecced/initAgeAssurance.js.map +1 -0
- package/dist/api/external.d.ts +4 -0
- package/dist/api/external.d.ts.map +1 -0
- package/dist/api/external.js +47 -0
- package/dist/api/external.js.map +1 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +6 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/kws/api.d.ts +4 -0
- package/dist/api/kws/api.d.ts.map +1 -0
- package/dist/api/kws/api.js +60 -0
- package/dist/api/kws/api.js.map +1 -0
- package/dist/api/kws/index.d.ts +4 -0
- package/dist/api/kws/index.d.ts.map +1 -0
- package/dist/api/kws/index.js +21 -0
- package/dist/api/kws/index.js.map +1 -0
- package/dist/api/kws/types.d.ts +100 -0
- package/dist/api/kws/types.d.ts.map +1 -0
- package/dist/api/kws/types.js +29 -0
- package/dist/api/kws/types.js.map +1 -0
- package/dist/api/kws/util.d.ts +21 -0
- package/dist/api/kws/util.d.ts.map +1 -0
- package/dist/api/kws/util.js +78 -0
- package/dist/api/kws/util.js.map +1 -0
- package/dist/api/kws/webhook.d.ts +5 -0
- package/dist/api/kws/webhook.d.ts.map +1 -0
- package/dist/api/kws/webhook.js +80 -0
- package/dist/api/kws/webhook.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -0
- package/dist/context.js.map +1 -1
- package/dist/data-plane/bsync/index.d.ts.map +1 -1
- package/dist/data-plane/bsync/index.js +52 -33
- package/dist/data-plane/bsync/index.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.js +22 -0
- package/dist/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -0
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/tables/actor.d.ts +2 -0
- package/dist/data-plane/server/db/tables/actor.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/actor.js.map +1 -1
- package/dist/data-plane/server/routes/profile.d.ts.map +1 -1
- package/dist/data-plane/server/routes/profile.js +14 -1
- package/dist/data-plane/server/routes/profile.js.map +1 -1
- package/dist/feature-gates.d.ts +2 -1
- package/dist/feature-gates.d.ts.map +1 -1
- package/dist/feature-gates.js +1 -0
- package/dist/feature-gates.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/kws.d.ts +16 -0
- package/dist/kws.d.ts.map +1 -0
- package/dist/kws.js +86 -0
- package/dist/kws.js.map +1 -0
- package/dist/lexicon/index.d.ts +6 -2
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +12 -4
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +308 -82
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +157 -42
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts +32 -0
- package/dist/lexicon/types/app/bsky/unspecced/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/defs.js +18 -0
- package/dist/lexicon/types/app/bsky/unspecced/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getAgeAssuranceState.d.ts +18 -0
- package/dist/lexicon/types/app/bsky/unspecced/getAgeAssuranceState.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/getAgeAssuranceState.js +7 -0
- package/dist/lexicon/types/app/bsky/unspecced/getAgeAssuranceState.js.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.d.ts +28 -0
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.js +7 -0
- package/dist/lexicon/types/app/bsky/unspecced/initAgeAssurance.js.map +1 -0
- package/dist/proto/bsky_pb.d.ts +29 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +97 -4
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/stash.d.ts +1 -0
- package/dist/stash.d.ts.map +1 -1
- package/dist/stash.js +1 -0
- package/dist/stash.js.map +1 -1
- package/package.json +7 -4
- package/proto/bsky.proto +7 -0
- package/src/api/app/bsky/unspecced/getAgeAssuranceState.ts +46 -0
- package/src/api/app/bsky/unspecced/initAgeAssurance.ts +71 -0
- package/src/api/external.ts +13 -0
- package/src/api/index.ts +6 -0
- package/src/api/kws/api.ts +92 -0
- package/src/api/kws/index.ts +23 -0
- package/src/api/kws/types.ts +67 -0
- package/src/api/kws/util.ts +111 -0
- package/src/api/kws/webhook.ts +107 -0
- package/src/config.ts +59 -0
- package/src/context.ts +6 -0
- package/src/data-plane/bsync/index.ts +69 -33
- package/src/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.ts +22 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/actor.ts +2 -0
- package/src/data-plane/server/routes/profile.ts +16 -1
- package/src/feature-gates.ts +1 -0
- package/src/index.ts +7 -1
- package/src/kws.ts +108 -0
- package/src/lexicon/index.ts +37 -11
- package/src/lexicon/lexicons.ts +166 -43
- package/src/lexicon/types/app/bsky/unspecced/defs.ts +50 -0
- package/src/lexicon/types/app/bsky/unspecced/getAgeAssuranceState.ts +34 -0
- package/src/lexicon/types/app/bsky/unspecced/initAgeAssurance.ts +47 -0
- package/src/proto/bsky_pb.ts +79 -0
- package/src/stash.ts +3 -0
- package/tests/views/age-assurance.test.ts +425 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/src/config.ts
CHANGED
|
@@ -6,6 +6,17 @@ type LiveNowConfig = {
|
|
|
6
6
|
domains: string[]
|
|
7
7
|
}[]
|
|
8
8
|
|
|
9
|
+
export interface KwsConfig {
|
|
10
|
+
apiKey: string
|
|
11
|
+
apiOrigin: string
|
|
12
|
+
authOrigin: string
|
|
13
|
+
clientId: string
|
|
14
|
+
redirectUrl: string
|
|
15
|
+
userAgent: string
|
|
16
|
+
verificationSecret: string
|
|
17
|
+
webhookSecret: string
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
export interface ServerConfigValues {
|
|
10
21
|
// service
|
|
11
22
|
version?: string
|
|
@@ -72,6 +83,7 @@ export interface ServerConfigValues {
|
|
|
72
83
|
proxyMaxResponseSize?: number
|
|
73
84
|
proxyMaxRetries?: number
|
|
74
85
|
proxyPreferCompressed?: boolean
|
|
86
|
+
kws?: KwsConfig
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
export class ServerConfig {
|
|
@@ -222,6 +234,48 @@ export class ServerConfig {
|
|
|
222
234
|
const proxyPreferCompressed =
|
|
223
235
|
process.env.BSKY_PROXY_PREFER_COMPRESSED === 'true'
|
|
224
236
|
|
|
237
|
+
let kws: KwsConfig | undefined
|
|
238
|
+
const kwsApiKey = process.env.BSKY_KWS_API_KEY
|
|
239
|
+
const kwsApiOrigin = process.env.BSKY_KWS_API_ORIGIN
|
|
240
|
+
const kwsAuthOrigin = process.env.BSKY_KWS_AUTH_ORIGIN
|
|
241
|
+
const kwsClientId = process.env.BSKY_KWS_CLIENT_ID
|
|
242
|
+
const kwsRedirectUrl = process.env.BSKY_KWS_REDIRECT_URL
|
|
243
|
+
const kwsUserAgent = process.env.BSKY_KWS_USER_AGENT
|
|
244
|
+
const kwsVerificationSecret = process.env.BSKY_KWS_VERIFICATION_SECRET
|
|
245
|
+
const kwsWebhookSecret = process.env.BSKY_KWS_WEBHOOK_SIGNING_KEY
|
|
246
|
+
if (
|
|
247
|
+
kwsApiKey ||
|
|
248
|
+
kwsApiOrigin ||
|
|
249
|
+
kwsAuthOrigin ||
|
|
250
|
+
kwsClientId ||
|
|
251
|
+
kwsRedirectUrl ||
|
|
252
|
+
kwsUserAgent ||
|
|
253
|
+
kwsVerificationSecret ||
|
|
254
|
+
kwsWebhookSecret
|
|
255
|
+
) {
|
|
256
|
+
assert(
|
|
257
|
+
kwsApiOrigin &&
|
|
258
|
+
kwsAuthOrigin &&
|
|
259
|
+
kwsClientId &&
|
|
260
|
+
kwsRedirectUrl &&
|
|
261
|
+
kwsUserAgent &&
|
|
262
|
+
kwsVerificationSecret &&
|
|
263
|
+
kwsWebhookSecret &&
|
|
264
|
+
kwsApiKey,
|
|
265
|
+
'all KWS environment variables must be set if any are set',
|
|
266
|
+
)
|
|
267
|
+
kws = {
|
|
268
|
+
apiKey: kwsApiKey,
|
|
269
|
+
apiOrigin: kwsApiOrigin,
|
|
270
|
+
authOrigin: kwsAuthOrigin,
|
|
271
|
+
clientId: kwsClientId,
|
|
272
|
+
redirectUrl: kwsRedirectUrl,
|
|
273
|
+
userAgent: kwsUserAgent,
|
|
274
|
+
verificationSecret: kwsVerificationSecret,
|
|
275
|
+
webhookSecret: kwsWebhookSecret,
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
225
279
|
return new ServerConfig({
|
|
226
280
|
version,
|
|
227
281
|
debugMode,
|
|
@@ -279,6 +333,7 @@ export class ServerConfig {
|
|
|
279
333
|
proxyMaxResponseSize,
|
|
280
334
|
proxyMaxRetries,
|
|
281
335
|
proxyPreferCompressed,
|
|
336
|
+
kws,
|
|
282
337
|
...stripUndefineds(overrides ?? {}),
|
|
283
338
|
})
|
|
284
339
|
}
|
|
@@ -513,6 +568,10 @@ export class ServerConfig {
|
|
|
513
568
|
get proxyPreferCompressed(): boolean {
|
|
514
569
|
return this.cfg.proxyPreferCompressed ?? true
|
|
515
570
|
}
|
|
571
|
+
|
|
572
|
+
get kws() {
|
|
573
|
+
return this.cfg.kws
|
|
574
|
+
}
|
|
516
575
|
}
|
|
517
576
|
|
|
518
577
|
function stripUndefineds(
|
package/src/context.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { CourierClient } from './courier'
|
|
|
12
12
|
import { DataPlaneClient, HostList } from './data-plane/client'
|
|
13
13
|
import { FeatureGates } from './feature-gates'
|
|
14
14
|
import { Hydrator } from './hydration/hydrator'
|
|
15
|
+
import { KwsClient } from './kws'
|
|
15
16
|
import { httpLogger as log } from './logger'
|
|
16
17
|
import { StashClient } from './stash'
|
|
17
18
|
import {
|
|
@@ -41,6 +42,7 @@ export class AppContext {
|
|
|
41
42
|
authVerifier: AuthVerifier
|
|
42
43
|
featureGates: FeatureGates
|
|
43
44
|
blobDispatcher: Dispatcher
|
|
45
|
+
kwsClient: KwsClient | undefined
|
|
44
46
|
},
|
|
45
47
|
) {}
|
|
46
48
|
|
|
@@ -116,6 +118,10 @@ export class AppContext {
|
|
|
116
118
|
return this.opts.blobDispatcher
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
get kwsClient(): KwsClient | undefined {
|
|
122
|
+
return this.opts.kwsClient
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
reqLabelers(req: express.Request): ParsedLabelers {
|
|
120
126
|
const val = req.header('atproto-accept-labelers')
|
|
121
127
|
let parsed: ParsedLabelers | null
|
|
@@ -9,6 +9,7 @@ import { jsonStringToLex } from '@atproto/lexicon'
|
|
|
9
9
|
import { AtUri } from '@atproto/syntax'
|
|
10
10
|
import { ids } from '../../lexicon/lexicons'
|
|
11
11
|
import { SubjectActivitySubscription } from '../../lexicon/types/app/bsky/notification/defs'
|
|
12
|
+
import { AgeAssuranceEvent } from '../../lexicon/types/app/bsky/unspecced/defs'
|
|
12
13
|
import { httpLogger } from '../../logger'
|
|
13
14
|
import { Service } from '../../proto/bsync_connect'
|
|
14
15
|
import {
|
|
@@ -159,10 +160,10 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
159
160
|
|
|
160
161
|
const now = new Date().toISOString()
|
|
161
162
|
|
|
162
|
-
//
|
|
163
|
+
// Index all items into private_data.
|
|
163
164
|
await handleGenericOperation(db, req, now)
|
|
164
165
|
|
|
165
|
-
//
|
|
166
|
+
// Maintain bespoke indexes for certain namespaces.
|
|
166
167
|
if (
|
|
167
168
|
namespace ===
|
|
168
169
|
Namespaces.AppBskyNotificationDefsSubjectActivitySubscription
|
|
@@ -174,6 +175,16 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
174
175
|
'mock bsync put operation failed',
|
|
175
176
|
),
|
|
176
177
|
)
|
|
178
|
+
} else if (
|
|
179
|
+
namespace === Namespaces.AppBskyUnspeccedDefsAgeAssuranceEvent
|
|
180
|
+
) {
|
|
181
|
+
await handleAgeAssuranceEventOperation(db, req, now).catch(
|
|
182
|
+
(err: unknown) =>
|
|
183
|
+
httpLogger.warn(
|
|
184
|
+
{ err, namespace },
|
|
185
|
+
'mock bsync put operation failed',
|
|
186
|
+
),
|
|
187
|
+
)
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
return {
|
|
@@ -197,6 +208,43 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
197
208
|
},
|
|
198
209
|
})
|
|
199
210
|
|
|
211
|
+
// upsert into or remove from private_data
|
|
212
|
+
const handleGenericOperation = async (
|
|
213
|
+
db: Database,
|
|
214
|
+
req: PutOperationRequest,
|
|
215
|
+
now: string,
|
|
216
|
+
) => {
|
|
217
|
+
const { actorDid, namespace, key, method, payload } = req
|
|
218
|
+
if (method === Method.CREATE || method === Method.UPDATE) {
|
|
219
|
+
await db.db
|
|
220
|
+
.insertInto('private_data')
|
|
221
|
+
.values({
|
|
222
|
+
actorDid,
|
|
223
|
+
namespace,
|
|
224
|
+
key,
|
|
225
|
+
payload: Buffer.from(payload).toString('utf8'),
|
|
226
|
+
indexedAt: now,
|
|
227
|
+
updatedAt: now,
|
|
228
|
+
})
|
|
229
|
+
.onConflict((oc) =>
|
|
230
|
+
oc.columns(['actorDid', 'namespace', 'key']).doUpdateSet({
|
|
231
|
+
payload: excluded(db.db, 'payload'),
|
|
232
|
+
updatedAt: excluded(db.db, 'updatedAt'),
|
|
233
|
+
}),
|
|
234
|
+
)
|
|
235
|
+
.execute()
|
|
236
|
+
} else if (method === Method.DELETE) {
|
|
237
|
+
await db.db
|
|
238
|
+
.deleteFrom('private_data')
|
|
239
|
+
.where('actorDid', '=', actorDid)
|
|
240
|
+
.where('namespace', '=', namespace)
|
|
241
|
+
.where('key', '=', key)
|
|
242
|
+
.execute()
|
|
243
|
+
} else {
|
|
244
|
+
assert.fail(`unexpected method ${method}`)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
200
248
|
const handleSubjectActivitySubscriptionOperation = async (
|
|
201
249
|
db: Database,
|
|
202
250
|
req: PutOperationRequest,
|
|
@@ -246,39 +294,27 @@ const handleSubjectActivitySubscriptionOperation = async (
|
|
|
246
294
|
.execute()
|
|
247
295
|
}
|
|
248
296
|
|
|
249
|
-
|
|
250
|
-
const handleGenericOperation = async (
|
|
297
|
+
const handleAgeAssuranceEventOperation = async (
|
|
251
298
|
db: Database,
|
|
252
299
|
req: PutOperationRequest,
|
|
253
|
-
|
|
300
|
+
_now: string,
|
|
254
301
|
) => {
|
|
255
|
-
const { actorDid,
|
|
256
|
-
if (method
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
})
|
|
267
|
-
.onConflict((oc) =>
|
|
268
|
-
oc.columns(['actorDid', 'namespace', 'key']).doUpdateSet({
|
|
269
|
-
payload: excluded(db.db, 'payload'),
|
|
270
|
-
updatedAt: excluded(db.db, 'updatedAt'),
|
|
271
|
-
}),
|
|
272
|
-
)
|
|
273
|
-
.execute()
|
|
274
|
-
} else if (method === Method.DELETE) {
|
|
275
|
-
await db.db
|
|
276
|
-
.deleteFrom('private_data')
|
|
277
|
-
.where('actorDid', '=', actorDid)
|
|
278
|
-
.where('namespace', '=', namespace)
|
|
279
|
-
.where('key', '=', key)
|
|
280
|
-
.execute()
|
|
281
|
-
} else {
|
|
282
|
-
assert.fail(`unexpected method ${method}`)
|
|
302
|
+
const { actorDid, method, payload } = req
|
|
303
|
+
if (method !== Method.CREATE) return
|
|
304
|
+
|
|
305
|
+
const parsed = jsonStringToLex(
|
|
306
|
+
Buffer.from(payload).toString('utf8'),
|
|
307
|
+
) as AgeAssuranceEvent
|
|
308
|
+
const { status, createdAt } = parsed
|
|
309
|
+
|
|
310
|
+
const update = {
|
|
311
|
+
ageAssuranceStatus: status,
|
|
312
|
+
ageAssuranceLastInitiatedAt: status === 'pending' ? createdAt : undefined,
|
|
283
313
|
}
|
|
314
|
+
|
|
315
|
+
return db.db
|
|
316
|
+
.updateTable('actor')
|
|
317
|
+
.set(update)
|
|
318
|
+
.where('did', '=', actorDid)
|
|
319
|
+
.execute()
|
|
284
320
|
}
|
package/src/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.alterTable('actor')
|
|
6
|
+
.addColumn('ageAssuranceStatus', 'text')
|
|
7
|
+
.execute()
|
|
8
|
+
|
|
9
|
+
await db.schema
|
|
10
|
+
.alterTable('actor')
|
|
11
|
+
.addColumn('ageAssuranceLastInitiatedAt', 'varchar')
|
|
12
|
+
.execute()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
16
|
+
await db.schema.alterTable('actor').dropColumn('ageAssuranceStatus').execute()
|
|
17
|
+
|
|
18
|
+
await db.schema
|
|
19
|
+
.alterTable('actor')
|
|
20
|
+
.dropColumn('ageAssuranceLastInitiatedAt')
|
|
21
|
+
.execute()
|
|
22
|
+
}
|
|
@@ -52,3 +52,4 @@ export * as _20250526T023712742Z from './20250526T023712742Z-like-repost-via'
|
|
|
52
52
|
export * as _20250528T221913281Z from './20250528T221913281Z-add-record-tags'
|
|
53
53
|
export * as _20250602T190357447Z from './20250602T190357447Z-add-private-data'
|
|
54
54
|
export * as _20250611T140649895Z from './20250611T140649895Z-add-activity-subscription'
|
|
55
|
+
export * as _20250627T025331240Z from './20250627T025331240Z-add-actor-age-assurance-columns'
|
|
@@ -22,7 +22,7 @@ type VerifiedBy = {
|
|
|
22
22
|
|
|
23
23
|
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
24
24
|
async getActors(req) {
|
|
25
|
-
const { dids } = req
|
|
25
|
+
const { dids, returnAgeAssuranceForDids } = req
|
|
26
26
|
if (dids.length === 0) {
|
|
27
27
|
return { actors: [] }
|
|
28
28
|
}
|
|
@@ -105,6 +105,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
105
105
|
}
|
|
106
106
|
return acc
|
|
107
107
|
}, {} as VerifiedBy)
|
|
108
|
+
const ageAssuranceForDids = new Set(returnAgeAssuranceForDids)
|
|
108
109
|
|
|
109
110
|
const activitySubscription = () => {
|
|
110
111
|
const record = parseRecordBytes<AppBskyNotificationDeclaration.Record>(
|
|
@@ -128,6 +129,19 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
const ageAssuranceStatus = () => {
|
|
133
|
+
if (!ageAssuranceForDids.has(did)) {
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
status: row?.ageAssuranceStatus ?? 'unknown',
|
|
139
|
+
lastInitiatedAt: row?.ageAssuranceLastInitiatedAt
|
|
140
|
+
? Timestamp.fromDate(new Date(row?.ageAssuranceLastInitiatedAt))
|
|
141
|
+
: undefined,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
131
145
|
return {
|
|
132
146
|
exists: !!row,
|
|
133
147
|
handle: row?.handle ?? undefined,
|
|
@@ -149,6 +163,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
149
163
|
tags: [],
|
|
150
164
|
profileTags: [],
|
|
151
165
|
allowActivitySubscriptionsFrom: activitySubscription(),
|
|
166
|
+
ageAssuranceStatus: ageAssuranceStatus(),
|
|
152
167
|
}
|
|
153
168
|
})
|
|
154
169
|
return { actors }
|
package/src/feature-gates.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { AtpAgent } from '@atproto/api'
|
|
|
10
10
|
import { DAY, SECOND } from '@atproto/common'
|
|
11
11
|
import { Keypair } from '@atproto/crypto'
|
|
12
12
|
import { IdResolver } from '@atproto/identity'
|
|
13
|
-
import API, { blobResolver, health, wellKnown } from './api'
|
|
13
|
+
import API, { blobResolver, external, health, wellKnown } from './api'
|
|
14
14
|
import { createBlobDispatcher } from './api/blob-dispatcher'
|
|
15
15
|
import { AuthVerifier, createPublicKeyObject } from './auth-verifier'
|
|
16
16
|
import { authWithApiKey as bsyncAuth, createBsyncClient } from './bsync'
|
|
@@ -27,6 +27,7 @@ import { FeatureGates } from './feature-gates'
|
|
|
27
27
|
import { Hydrator } from './hydration/hydrator'
|
|
28
28
|
import * as imageServer from './image/server'
|
|
29
29
|
import { ImageUriBuilder } from './image/uri'
|
|
30
|
+
import { createKwsClient } from './kws'
|
|
30
31
|
import { createServer } from './lexicon'
|
|
31
32
|
import { loggerMiddleware } from './logger'
|
|
32
33
|
import { createStashClient } from './stash'
|
|
@@ -58,6 +59,7 @@ export class BskyAppView {
|
|
|
58
59
|
}): BskyAppView {
|
|
59
60
|
const { config, signingKey } = opts
|
|
60
61
|
const app = express()
|
|
62
|
+
app.set('trust proxy', true)
|
|
61
63
|
app.use(cors({ maxAge: DAY / SECOND }))
|
|
62
64
|
app.use(loggerMiddleware)
|
|
63
65
|
app.use(compression())
|
|
@@ -150,6 +152,8 @@ export class BskyAppView {
|
|
|
150
152
|
})
|
|
151
153
|
: undefined
|
|
152
154
|
|
|
155
|
+
const kwsClient = config.kws ? createKwsClient(config.kws) : undefined
|
|
156
|
+
|
|
153
157
|
const entrywayJwtPublicKey = config.entrywayJwtPublicKeyHex
|
|
154
158
|
? createPublicKeyObject(config.entrywayJwtPublicKeyHex)
|
|
155
159
|
: undefined
|
|
@@ -186,6 +190,7 @@ export class BskyAppView {
|
|
|
186
190
|
authVerifier,
|
|
187
191
|
featureGates,
|
|
188
192
|
blobDispatcher,
|
|
193
|
+
kwsClient,
|
|
189
194
|
})
|
|
190
195
|
|
|
191
196
|
let server = createServer({
|
|
@@ -205,6 +210,7 @@ export class BskyAppView {
|
|
|
205
210
|
app.use(imageServer.createMiddleware(ctx, { prefix: '/img/' }))
|
|
206
211
|
app.use(server.xrpc.router)
|
|
207
212
|
app.use(error.handler)
|
|
213
|
+
app.use('/external', external.createRouter(ctx))
|
|
208
214
|
|
|
209
215
|
return new BskyAppView({ ctx, app })
|
|
210
216
|
}
|
package/src/kws.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { KwsExternalPayload } from './api/kws/types'
|
|
3
|
+
import { serializeExternalPayload } from './api/kws/util'
|
|
4
|
+
import { buildBasicAuth } from './auth-verifier'
|
|
5
|
+
import { KwsConfig } from './config'
|
|
6
|
+
import { httpLogger as log } from './logger'
|
|
7
|
+
|
|
8
|
+
export const createKwsClient = (cfg: KwsConfig): KwsClient => {
|
|
9
|
+
return new KwsClient(cfg)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Not `.strict()` to avoid breaking if KWS adds fields.
|
|
13
|
+
const authResponseSchema = z.object({
|
|
14
|
+
access_token: z.string(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export class KwsClient {
|
|
18
|
+
constructor(public cfg: KwsConfig) {}
|
|
19
|
+
|
|
20
|
+
private async auth() {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(
|
|
23
|
+
`${this.cfg.authOrigin}/auth/realms/kws/protocol/openid-connect/token`,
|
|
24
|
+
{
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
Accept: 'application/json',
|
|
28
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
29
|
+
Authorization: buildBasicAuth(this.cfg.clientId, this.cfg.apiKey),
|
|
30
|
+
},
|
|
31
|
+
body: new URLSearchParams({
|
|
32
|
+
grant_type: 'client_credentials',
|
|
33
|
+
scope: 'verification',
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
const errorText = await res.text()
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Failed to fetch age assurance access token: status: ${res.status}, statusText: ${res.statusText}, errorText: ${errorText}`,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const auth = await res.json()
|
|
45
|
+
const authResponse = authResponseSchema.parse(auth)
|
|
46
|
+
return authResponse.access_token
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.error({ err }, 'Failed to authenticate with KWS')
|
|
49
|
+
throw err
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async fetchWithAuth(
|
|
54
|
+
url: string,
|
|
55
|
+
init: RequestInit,
|
|
56
|
+
): Promise<Response> {
|
|
57
|
+
const accessToken = await this.auth()
|
|
58
|
+
|
|
59
|
+
return fetch(url, {
|
|
60
|
+
...init,
|
|
61
|
+
headers: {
|
|
62
|
+
...(init.headers ?? {}),
|
|
63
|
+
Authorization: `Bearer ${accessToken}`,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async sendEmail({
|
|
69
|
+
countryCode,
|
|
70
|
+
email,
|
|
71
|
+
externalPayload,
|
|
72
|
+
language,
|
|
73
|
+
}: {
|
|
74
|
+
countryCode: string
|
|
75
|
+
email: string
|
|
76
|
+
externalPayload: KwsExternalPayload
|
|
77
|
+
language: string
|
|
78
|
+
}) {
|
|
79
|
+
const res = await this.fetchWithAuth(
|
|
80
|
+
`${this.cfg.apiOrigin}/v1/verifications/send-email`,
|
|
81
|
+
{
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
'User-Agent': this.cfg.userAgent,
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
email,
|
|
89
|
+
externalPayload: serializeExternalPayload(externalPayload),
|
|
90
|
+
language,
|
|
91
|
+
location: countryCode,
|
|
92
|
+
userContext: 'adult',
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
const errorText = await res.text()
|
|
99
|
+
log.error(
|
|
100
|
+
{ status: res.status, statusText: res.statusText, errorText },
|
|
101
|
+
'Failed to send age assurance email',
|
|
102
|
+
)
|
|
103
|
+
throw new Error('Failed to send age assurance email')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return res.json()
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/lexicon/index.ts
CHANGED
|
@@ -109,8 +109,8 @@ import * as AppBskyFeedGetFeedGenerators from './types/app/bsky/feed/getFeedGene
|
|
|
109
109
|
import * as AppBskyFeedGetFeedSkeleton from './types/app/bsky/feed/getFeedSkeleton.js'
|
|
110
110
|
import * as AppBskyFeedGetLikes from './types/app/bsky/feed/getLikes.js'
|
|
111
111
|
import * as AppBskyFeedGetListFeed from './types/app/bsky/feed/getListFeed.js'
|
|
112
|
-
import * as AppBskyFeedGetPosts from './types/app/bsky/feed/getPosts.js'
|
|
113
112
|
import * as AppBskyFeedGetPostThread from './types/app/bsky/feed/getPostThread.js'
|
|
113
|
+
import * as AppBskyFeedGetPosts from './types/app/bsky/feed/getPosts.js'
|
|
114
114
|
import * as AppBskyFeedGetQuotes from './types/app/bsky/feed/getQuotes.js'
|
|
115
115
|
import * as AppBskyFeedGetRepostedBy from './types/app/bsky/feed/getRepostedBy.js'
|
|
116
116
|
import * as AppBskyFeedGetSuggestedFeeds from './types/app/bsky/feed/getSuggestedFeeds.js'
|
|
@@ -148,6 +148,7 @@ import * as AppBskyNotificationPutPreferences from './types/app/bsky/notificatio
|
|
|
148
148
|
import * as AppBskyNotificationPutPreferencesV2 from './types/app/bsky/notification/putPreferencesV2.js'
|
|
149
149
|
import * as AppBskyNotificationRegisterPush from './types/app/bsky/notification/registerPush.js'
|
|
150
150
|
import * as AppBskyNotificationUpdateSeen from './types/app/bsky/notification/updateSeen.js'
|
|
151
|
+
import * as AppBskyUnspeccedGetAgeAssuranceState from './types/app/bsky/unspecced/getAgeAssuranceState.js'
|
|
151
152
|
import * as AppBskyUnspeccedGetConfig from './types/app/bsky/unspecced/getConfig.js'
|
|
152
153
|
import * as AppBskyUnspeccedGetPopularFeedGenerators from './types/app/bsky/unspecced/getPopularFeedGenerators.js'
|
|
153
154
|
import * as AppBskyUnspeccedGetPostThreadOtherV2 from './types/app/bsky/unspecced/getPostThreadOtherV2.js'
|
|
@@ -163,6 +164,7 @@ import * as AppBskyUnspeccedGetTaggedSuggestions from './types/app/bsky/unspecce
|
|
|
163
164
|
import * as AppBskyUnspeccedGetTrendingTopics from './types/app/bsky/unspecced/getTrendingTopics.js'
|
|
164
165
|
import * as AppBskyUnspeccedGetTrends from './types/app/bsky/unspecced/getTrends.js'
|
|
165
166
|
import * as AppBskyUnspeccedGetTrendsSkeleton from './types/app/bsky/unspecced/getTrendsSkeleton.js'
|
|
167
|
+
import * as AppBskyUnspeccedInitAgeAssurance from './types/app/bsky/unspecced/initAgeAssurance.js'
|
|
166
168
|
import * as AppBskyUnspeccedSearchActorsSkeleton from './types/app/bsky/unspecced/searchActorsSkeleton.js'
|
|
167
169
|
import * as AppBskyUnspeccedSearchPostsSkeleton from './types/app/bsky/unspecced/searchPostsSkeleton.js'
|
|
168
170
|
import * as AppBskyUnspeccedSearchStarterPacksSkeleton from './types/app/bsky/unspecced/searchStarterPacksSkeleton.js'
|
|
@@ -1597,27 +1599,27 @@ export class AppBskyFeedNS {
|
|
|
1597
1599
|
return this._server.xrpc.method(nsid, cfg)
|
|
1598
1600
|
}
|
|
1599
1601
|
|
|
1600
|
-
|
|
1602
|
+
getPostThread<A extends Auth = void>(
|
|
1601
1603
|
cfg: MethodConfigOrHandler<
|
|
1602
1604
|
A,
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1605
|
+
AppBskyFeedGetPostThread.QueryParams,
|
|
1606
|
+
AppBskyFeedGetPostThread.HandlerInput,
|
|
1607
|
+
AppBskyFeedGetPostThread.HandlerOutput
|
|
1606
1608
|
>,
|
|
1607
1609
|
) {
|
|
1608
|
-
const nsid = 'app.bsky.feed.
|
|
1610
|
+
const nsid = 'app.bsky.feed.getPostThread' // @ts-ignore
|
|
1609
1611
|
return this._server.xrpc.method(nsid, cfg)
|
|
1610
1612
|
}
|
|
1611
1613
|
|
|
1612
|
-
|
|
1614
|
+
getPosts<A extends Auth = void>(
|
|
1613
1615
|
cfg: MethodConfigOrHandler<
|
|
1614
1616
|
A,
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1617
|
+
AppBskyFeedGetPosts.QueryParams,
|
|
1618
|
+
AppBskyFeedGetPosts.HandlerInput,
|
|
1619
|
+
AppBskyFeedGetPosts.HandlerOutput
|
|
1618
1620
|
>,
|
|
1619
1621
|
) {
|
|
1620
|
-
const nsid = 'app.bsky.feed.
|
|
1622
|
+
const nsid = 'app.bsky.feed.getPosts' // @ts-ignore
|
|
1621
1623
|
return this._server.xrpc.method(nsid, cfg)
|
|
1622
1624
|
}
|
|
1623
1625
|
|
|
@@ -2105,6 +2107,18 @@ export class AppBskyUnspeccedNS {
|
|
|
2105
2107
|
this._server = server
|
|
2106
2108
|
}
|
|
2107
2109
|
|
|
2110
|
+
getAgeAssuranceState<A extends Auth = void>(
|
|
2111
|
+
cfg: MethodConfigOrHandler<
|
|
2112
|
+
A,
|
|
2113
|
+
AppBskyUnspeccedGetAgeAssuranceState.QueryParams,
|
|
2114
|
+
AppBskyUnspeccedGetAgeAssuranceState.HandlerInput,
|
|
2115
|
+
AppBskyUnspeccedGetAgeAssuranceState.HandlerOutput
|
|
2116
|
+
>,
|
|
2117
|
+
) {
|
|
2118
|
+
const nsid = 'app.bsky.unspecced.getAgeAssuranceState' // @ts-ignore
|
|
2119
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2108
2122
|
getConfig<A extends Auth = void>(
|
|
2109
2123
|
cfg: MethodConfigOrHandler<
|
|
2110
2124
|
A,
|
|
@@ -2285,6 +2299,18 @@ export class AppBskyUnspeccedNS {
|
|
|
2285
2299
|
return this._server.xrpc.method(nsid, cfg)
|
|
2286
2300
|
}
|
|
2287
2301
|
|
|
2302
|
+
initAgeAssurance<A extends Auth = void>(
|
|
2303
|
+
cfg: MethodConfigOrHandler<
|
|
2304
|
+
A,
|
|
2305
|
+
AppBskyUnspeccedInitAgeAssurance.QueryParams,
|
|
2306
|
+
AppBskyUnspeccedInitAgeAssurance.HandlerInput,
|
|
2307
|
+
AppBskyUnspeccedInitAgeAssurance.HandlerOutput
|
|
2308
|
+
>,
|
|
2309
|
+
) {
|
|
2310
|
+
const nsid = 'app.bsky.unspecced.initAgeAssurance' // @ts-ignore
|
|
2311
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2288
2314
|
searchActorsSkeleton<A extends Auth = void>(
|
|
2289
2315
|
cfg: MethodConfigOrHandler<
|
|
2290
2316
|
A,
|