@atproto/pds 0.4.59 → 0.4.61
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +48 -0
- package/dist/account-manager/helpers/account.d.ts +1 -0
- package/dist/account-manager/helpers/account.d.ts.map +1 -1
- package/dist/account-manager/helpers/account.js +15 -1
- package/dist/account-manager/helpers/account.js.map +1 -1
- package/dist/account-manager/helpers/invite.d.ts +1 -1
- package/dist/account-manager/helpers/invite.d.ts.map +1 -1
- package/dist/account-manager/helpers/invite.js +20 -9
- package/dist/account-manager/helpers/invite.js.map +1 -1
- package/dist/account-manager/index.d.ts +2 -0
- package/dist/account-manager/index.d.ts.map +1 -1
- package/dist/account-manager/index.js +8 -1
- package/dist/account-manager/index.js.map +1 -1
- package/dist/api/app/bsky/actor/getProfile.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/getProfile.js +2 -9
- package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
- package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/getProfiles.js +2 -6
- package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
- package/dist/api/app/bsky/feed/getActorLikes.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getActorLikes.js +2 -9
- package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.js +2 -9
- package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.js +2 -1
- package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getPostThread.js +12 -14
- package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
- package/dist/api/app/bsky/feed/getTimeline.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getTimeline.js +2 -6
- package/dist/api/app/bsky/feed/getTimeline.js.map +1 -1
- package/dist/api/com/atproto/admin/getAccountInfo.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/getAccountInfo.js +6 -14
- package/dist/api/com/atproto/admin/getAccountInfo.js.map +1 -1
- package/dist/api/com/atproto/admin/getAccountInfos.d.ts +4 -0
- package/dist/api/com/atproto/admin/getAccountInfos.d.ts.map +1 -0
- package/dist/api/com/atproto/admin/getAccountInfos.js +32 -0
- package/dist/api/com/atproto/admin/getAccountInfos.js.map +1 -0
- package/dist/api/com/atproto/admin/index.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/index.js +2 -0
- package/dist/api/com/atproto/admin/index.js.map +1 -1
- package/dist/api/com/atproto/admin/util.d.ts +17 -0
- package/dist/api/com/atproto/admin/util.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/util.js +27 -1
- package/dist/api/com/atproto/admin/util.js.map +1 -1
- package/dist/api/com/atproto/repo/getRecord.d.ts.map +1 -1
- package/dist/api/com/atproto/repo/getRecord.js +2 -2
- package/dist/api/com/atproto/repo/getRecord.js.map +1 -1
- package/dist/api/com/atproto/server/requestPasswordReset.js +1 -1
- package/dist/api/com/atproto/server/requestPasswordReset.js.map +1 -1
- package/dist/config/config.d.ts +17 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +11 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +7 -1
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +9 -1
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +6 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +55 -11
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +4 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +8 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +118 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +135 -3
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/profile.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/actor/profile.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/profile.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +13 -2
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js +21 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts +39 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRecords.js +3 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRecords.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts +39 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRepos.js +3 -0
- package/dist/lexicon/types/tools/ozone/moderation/getRepos.js.map +1 -0
- package/dist/mailer/index.d.ts +1 -1
- package/dist/mailer/index.d.ts.map +1 -1
- package/dist/mailer/index.js.map +1 -1
- package/dist/mailer/templates/confirm-email.js +1 -1
- package/dist/mailer/templates/confirm-email.js.map +2 -2
- package/dist/mailer/templates/delete-account.js +1 -1
- package/dist/mailer/templates/delete-account.js.map +2 -2
- package/dist/mailer/templates/plc-operation.js +1 -1
- package/dist/mailer/templates/plc-operation.js.map +2 -2
- package/dist/mailer/templates/reset-password.js +1 -1
- package/dist/mailer/templates/reset-password.js.map +2 -2
- package/dist/mailer/templates/update-email.js +1 -1
- package/dist/mailer/templates/update-email.js.map +2 -2
- package/dist/pipethrough.d.ts +26 -26
- package/dist/pipethrough.d.ts.map +1 -1
- package/dist/pipethrough.js +360 -228
- package/dist/pipethrough.js.map +1 -1
- package/dist/read-after-write/util.d.ts +13 -5
- package/dist/read-after-write/util.d.ts.map +1 -1
- package/dist/read-after-write/util.js +37 -22
- package/dist/read-after-write/util.js.map +1 -1
- package/package.json +15 -14
- package/src/account-manager/helpers/account.ts +22 -0
- package/src/account-manager/helpers/invite.ts +19 -9
- package/src/account-manager/index.ts +13 -1
- package/src/api/app/bsky/actor/getProfile.ts +3 -17
- package/src/api/app/bsky/actor/getProfiles.ts +3 -15
- package/src/api/app/bsky/feed/getActorLikes.ts +3 -19
- package/src/api/app/bsky/feed/getAuthorFeed.ts +3 -17
- package/src/api/app/bsky/feed/getFeed.ts +3 -1
- package/src/api/app/bsky/feed/getPostThread.ts +16 -23
- package/src/api/app/bsky/feed/getTimeline.ts +3 -14
- package/src/api/com/atproto/admin/getAccountInfo.ts +6 -13
- package/src/api/com/atproto/admin/getAccountInfos.ts +33 -0
- package/src/api/com/atproto/admin/index.ts +2 -0
- package/src/api/com/atproto/admin/util.ts +38 -0
- package/src/api/com/atproto/repo/getRecord.ts +5 -2
- package/src/api/com/atproto/server/requestPasswordReset.ts +1 -1
- package/src/config/config.ts +31 -1
- package/src/config/env.ts +22 -2
- package/src/context.ts +62 -17
- package/src/index.ts +1 -0
- package/src/lexicon/index.ts +24 -0
- package/src/lexicon/lexicons.ts +137 -3
- package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
- package/src/lexicon/types/app/bsky/actor/profile.ts +1 -0
- package/src/lexicon/types/app/bsky/feed/defs.ts +38 -2
- package/src/lexicon/types/app/bsky/feed/getAuthorFeed.ts +1 -0
- package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +2 -0
- package/src/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.ts +2 -0
- package/src/lexicon/types/com/atproto/repo/getRecord.ts +1 -0
- package/src/lexicon/types/tools/ozone/moderation/getRecords.ts +50 -0
- package/src/lexicon/types/tools/ozone/moderation/getRepos.ts +50 -0
- package/src/mailer/index.ts +1 -1
- package/src/mailer/templates/confirm-email.hbs +106 -336
- package/src/mailer/templates/delete-account.hbs +110 -346
- package/src/mailer/templates/plc-operation.hbs +107 -338
- package/src/mailer/templates/reset-password.d.ts +1 -1
- package/src/mailer/templates/reset-password.hbs +108 -344
- package/src/mailer/templates/update-email.hbs +107 -337
- package/src/pipethrough.ts +528 -233
- package/src/read-after-write/util.ts +58 -32
- package/tests/account-deletion.test.ts +1 -1
- package/tests/account.test.ts +2 -2
- package/tests/email-confirmation.test.ts +2 -2
- package/tests/plc-operations.test.ts +1 -1
- package/tests/proxied/proxy-catchall.test.ts +255 -0
- package/tests/proxied/proxy-header.test.ts +31 -1
- package/tests/proxied/read-after-write.test.ts +77 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
import { Server } from '../../../../lexicon'
|
2
|
+
import AppContext from '../../../../context'
|
3
|
+
import { formatAccountInfo } from './util'
|
4
|
+
|
5
|
+
export default function (server: Server, ctx: AppContext) {
|
6
|
+
server.com.atproto.admin.getAccountInfos({
|
7
|
+
auth: ctx.authVerifier.moderator,
|
8
|
+
handler: async ({ params }) => {
|
9
|
+
const [accounts, invites, invitedBy] = await Promise.all([
|
10
|
+
ctx.accountManager.getAccounts(params.dids, {
|
11
|
+
includeDeactivated: true,
|
12
|
+
includeTakenDown: true,
|
13
|
+
}),
|
14
|
+
ctx.accountManager.getAccountsInvitesCodes(params.dids),
|
15
|
+
ctx.accountManager.getInvitedByForAccounts(params.dids),
|
16
|
+
])
|
17
|
+
|
18
|
+
const managesOwnInvites = !ctx.cfg.entryway
|
19
|
+
const infos = Array.from(accounts.values()).map((account) => {
|
20
|
+
return formatAccountInfo(account, {
|
21
|
+
managesOwnInvites,
|
22
|
+
invitedBy,
|
23
|
+
invites,
|
24
|
+
})
|
25
|
+
})
|
26
|
+
|
27
|
+
return {
|
28
|
+
encoding: 'application/json',
|
29
|
+
body: { infos },
|
30
|
+
}
|
31
|
+
},
|
32
|
+
})
|
33
|
+
}
|
@@ -12,11 +12,13 @@ import updateAccountEmail from './updateAccountEmail'
|
|
12
12
|
import updateAccountPassword from './updateAccountPassword'
|
13
13
|
import sendEmail from './sendEmail'
|
14
14
|
import deleteAccount from './deleteAccount'
|
15
|
+
import getAccountInfos from './getAccountInfos'
|
15
16
|
|
16
17
|
export default function (server: Server, ctx: AppContext) {
|
17
18
|
updateSubjectStatus(server, ctx)
|
18
19
|
getSubjectStatus(server, ctx)
|
19
20
|
getAccountInfo(server, ctx)
|
21
|
+
getAccountInfos(server, ctx)
|
20
22
|
enableAccountInvites(server, ctx)
|
21
23
|
disableAccountInvites(server, ctx)
|
22
24
|
disableInviteCodes(server, ctx)
|
@@ -1,4 +1,7 @@
|
|
1
1
|
import express from 'express'
|
2
|
+
import { ActorAccount } from '../../../../account-manager/helpers/account'
|
3
|
+
import { INVALID_HANDLE } from '@atproto/syntax'
|
4
|
+
import { CodeDetail } from '../../../../account-manager/helpers/invite'
|
2
5
|
|
3
6
|
// Output designed to passed as second arg to AtpAgent methods.
|
4
7
|
// The encoding field here is a quirk of the AtpAgent.
|
@@ -22,3 +25,38 @@ export function authPassthru(req: express.Request, withEncoding?: boolean) {
|
|
22
25
|
}
|
23
26
|
}
|
24
27
|
}
|
28
|
+
|
29
|
+
export function formatAccountInfo(
|
30
|
+
account: ActorAccount,
|
31
|
+
{
|
32
|
+
managesOwnInvites,
|
33
|
+
invitedBy,
|
34
|
+
invites,
|
35
|
+
}: {
|
36
|
+
managesOwnInvites: boolean
|
37
|
+
invites: Map<string, CodeDetail[]> | CodeDetail[]
|
38
|
+
invitedBy: Record<string, CodeDetail>
|
39
|
+
},
|
40
|
+
) {
|
41
|
+
let invitesResults: CodeDetail[] | undefined
|
42
|
+
if (managesOwnInvites) {
|
43
|
+
if (Array.isArray(invites)) {
|
44
|
+
invitesResults = invites
|
45
|
+
} else {
|
46
|
+
invitesResults = invites.get(account.did) || []
|
47
|
+
}
|
48
|
+
}
|
49
|
+
return {
|
50
|
+
did: account.did,
|
51
|
+
handle: account.handle ?? INVALID_HANDLE,
|
52
|
+
email: account.email ?? undefined,
|
53
|
+
indexedAt: account.createdAt,
|
54
|
+
emailConfirmedAt: account.emailConfirmedAt ?? undefined,
|
55
|
+
invitedBy: managesOwnInvites ? invitedBy[account.did] : undefined,
|
56
|
+
invites: invitesResults,
|
57
|
+
invitesDisabled: managesOwnInvites
|
58
|
+
? account.invitesDisabled === 1
|
59
|
+
: undefined,
|
60
|
+
deactivatedAt: account.deactivatedAt ?? undefined,
|
61
|
+
}
|
62
|
+
}
|
@@ -16,7 +16,10 @@ export default function (server: Server, ctx: AppContext) {
|
|
16
16
|
store.record.getRecord(uri, cid ?? null),
|
17
17
|
)
|
18
18
|
if (!record || record.takedownRef !== null) {
|
19
|
-
throw new InvalidRequestError(
|
19
|
+
throw new InvalidRequestError(
|
20
|
+
`Could not locate record: ${uri}`,
|
21
|
+
'RecordNotFound',
|
22
|
+
)
|
20
23
|
}
|
21
24
|
return {
|
22
25
|
encoding: 'application/json',
|
@@ -32,6 +35,6 @@ export default function (server: Server, ctx: AppContext) {
|
|
32
35
|
throw new InvalidRequestError(`Could not locate record`)
|
33
36
|
}
|
34
37
|
|
35
|
-
return
|
38
|
+
return pipethrough(ctx, req)
|
36
39
|
})
|
37
40
|
}
|
@@ -40,7 +40,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
40
40
|
'reset_password',
|
41
41
|
)
|
42
42
|
await ctx.mailer.sendResetPassword(
|
43
|
-
{
|
43
|
+
{ handle: account.handle ?? account.email, token },
|
44
44
|
{ to: account.email },
|
45
45
|
)
|
46
46
|
},
|
package/src/config/config.ts
CHANGED
@@ -236,7 +236,17 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
|
|
236
236
|
const crawlersCfg: ServerConfig['crawlers'] = env.crawlers ?? []
|
237
237
|
|
238
238
|
const fetchCfg: ServerConfig['fetch'] = {
|
239
|
-
disableSsrfProtection: env.
|
239
|
+
disableSsrfProtection: env.disableSsrfProtection ?? env.devMode ?? false,
|
240
|
+
maxResponseSize: env.fetchMaxResponseSize ?? 512 * 1024, // 512kb
|
241
|
+
}
|
242
|
+
|
243
|
+
const proxyCfg: ServerConfig['proxy'] = {
|
244
|
+
disableSsrfProtection: env.disableSsrfProtection ?? env.devMode ?? false,
|
245
|
+
allowHTTP2: env.proxyAllowHTTP2 ?? false,
|
246
|
+
headersTimeout: env.proxyHeadersTimeout ?? 10e3,
|
247
|
+
bodyTimeout: env.proxyBodyTimeout ?? 30e3,
|
248
|
+
maxResponseSize: env.proxyMaxResponseSize ?? 10 * 1024 * 1024, // 10mb
|
249
|
+
preferCompressed: env.proxyPreferCompressed ?? false,
|
240
250
|
}
|
241
251
|
|
242
252
|
const oauthCfg: ServerConfig['oauth'] = entrywayCfg
|
@@ -302,6 +312,7 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
|
|
302
312
|
rateLimits: rateLimitsCfg,
|
303
313
|
crawlers: crawlersCfg,
|
304
314
|
fetch: fetchCfg,
|
315
|
+
proxy: proxyCfg,
|
305
316
|
oauth: oauthCfg,
|
306
317
|
}
|
307
318
|
}
|
@@ -324,6 +335,7 @@ export type ServerConfig = {
|
|
324
335
|
rateLimits: RateLimitsConfig
|
325
336
|
crawlers: string[]
|
326
337
|
fetch: FetchConfig
|
338
|
+
proxy: ProxyConfig
|
327
339
|
oauth: OAuthConfig
|
328
340
|
}
|
329
341
|
|
@@ -393,6 +405,24 @@ export type EntrywayConfig = {
|
|
393
405
|
|
394
406
|
export type FetchConfig = {
|
395
407
|
disableSsrfProtection: boolean
|
408
|
+
maxResponseSize: number
|
409
|
+
}
|
410
|
+
|
411
|
+
export type ProxyConfig = {
|
412
|
+
disableSsrfProtection: boolean
|
413
|
+
allowHTTP2: boolean
|
414
|
+
headersTimeout: number
|
415
|
+
bodyTimeout: number
|
416
|
+
maxResponseSize: number
|
417
|
+
|
418
|
+
/**
|
419
|
+
* When proxying requests that might get intercepted (for read-after-write) we
|
420
|
+
* negotiate the encoding based on the client's preferences. We will however
|
421
|
+
* use or own weights in order to be able to better control if the PDS will
|
422
|
+
* need to perform content decoding. This settings allows to prefer compressed
|
423
|
+
* content over uncompressed one.
|
424
|
+
*/
|
425
|
+
preferCompressed: boolean
|
396
426
|
}
|
397
427
|
|
398
428
|
export type OAuthConfig = {
|
package/src/config/env.ts
CHANGED
@@ -117,8 +117,18 @@ export const readEnv = (): ServerEnvironment => {
|
|
117
117
|
'PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX',
|
118
118
|
),
|
119
119
|
|
120
|
+
// user provided url http requests
|
121
|
+
disableSsrfProtection: envBool('PDS_DISABLE_SSRF_PROTECTION'),
|
122
|
+
|
120
123
|
// fetch
|
121
|
-
|
124
|
+
fetchMaxResponseSize: envInt('PDS_FETCH_MAX_RESPONSE_SIZE'),
|
125
|
+
|
126
|
+
// proxy
|
127
|
+
proxyAllowHTTP2: envBool('PDS_PROXY_ALLOW_HTTP2'),
|
128
|
+
proxyHeadersTimeout: envInt('PDS_PROXY_HEADERS_TIMEOUT'),
|
129
|
+
proxyBodyTimeout: envInt('PDS_PROXY_BODY_TIMEOUT'),
|
130
|
+
proxyMaxResponseSize: envInt('PDS_PROXY_MAX_RESPONSE_SIZE'),
|
131
|
+
proxyPreferCompressed: envBool('PDS_PROXY_PREFER_COMPRESSED'),
|
122
132
|
}
|
123
133
|
}
|
124
134
|
|
@@ -233,6 +243,16 @@ export type ServerEnvironment = {
|
|
233
243
|
plcRotationKeyKmsKeyId?: string
|
234
244
|
plcRotationKeyK256PrivateKeyHex?: string
|
235
245
|
|
246
|
+
// user provided url http requests
|
247
|
+
disableSsrfProtection?: boolean
|
248
|
+
|
236
249
|
// fetch
|
237
|
-
|
250
|
+
fetchMaxResponseSize?: number
|
251
|
+
|
252
|
+
// proxy
|
253
|
+
proxyAllowHTTP2?: boolean
|
254
|
+
proxyHeadersTimeout?: number
|
255
|
+
proxyBodyTimeout?: number
|
256
|
+
proxyMaxResponseSize?: number
|
257
|
+
proxyPreferCompressed?: boolean
|
238
258
|
}
|
package/src/context.ts
CHANGED
@@ -1,23 +1,28 @@
|
|
1
1
|
import assert from 'node:assert'
|
2
|
+
import * as undici from 'undici'
|
2
3
|
import * as nodemailer from 'nodemailer'
|
3
4
|
import { Redis } from 'ioredis'
|
4
5
|
import * as plc from '@did-plc/lib'
|
6
|
+
import {
|
7
|
+
Fetch,
|
8
|
+
isUnicastIp,
|
9
|
+
loggedFetch,
|
10
|
+
safeFetchWrap,
|
11
|
+
unicastLookup,
|
12
|
+
} from '@atproto-labs/fetch-node'
|
5
13
|
import * as crypto from '@atproto/crypto'
|
6
14
|
import { IdResolver } from '@atproto/identity'
|
7
15
|
import { AtpAgent } from '@atproto/api'
|
8
16
|
import { KmsKeypair, S3BlobStore } from '@atproto/aws'
|
17
|
+
import { JoseKey, OAuthVerifier } from '@atproto/oauth-provider'
|
18
|
+
import { BlobStore } from '@atproto/repo'
|
9
19
|
import {
|
10
20
|
RateLimiter,
|
11
21
|
RateLimiterCreator,
|
12
22
|
RateLimiterOpts,
|
13
23
|
createServiceAuthHeaders,
|
24
|
+
createServiceJwt,
|
14
25
|
} from '@atproto/xrpc-server'
|
15
|
-
import {
|
16
|
-
JoseKey,
|
17
|
-
Fetch,
|
18
|
-
safeFetchWrap,
|
19
|
-
OAuthVerifier,
|
20
|
-
} from '@atproto/oauth-provider'
|
21
26
|
|
22
27
|
import { ServerConfig, ServerSecrets } from './config'
|
23
28
|
import { PdsOAuthProvider } from './oauth/provider'
|
@@ -29,7 +34,6 @@ import {
|
|
29
34
|
import { fetchLogger } from './logger'
|
30
35
|
import { ServerMailer } from './mailer'
|
31
36
|
import { ModerationMailer } from './mailer/moderation'
|
32
|
-
import { BlobStore } from '@atproto/repo'
|
33
37
|
import { AccountManager } from './account-manager'
|
34
38
|
import { Sequencer } from './sequencer'
|
35
39
|
import { BackgroundQueue } from './background'
|
@@ -59,6 +63,7 @@ export type AppContextOptions = {
|
|
59
63
|
moderationAgent?: AtpAgent
|
60
64
|
reportingAgent?: AtpAgent
|
61
65
|
entrywayAgent?: AtpAgent
|
66
|
+
proxyAgent: undici.Agent
|
62
67
|
safeFetch: Fetch
|
63
68
|
authProvider?: PdsOAuthProvider
|
64
69
|
authVerifier: AuthVerifier
|
@@ -85,6 +90,7 @@ export class AppContext {
|
|
85
90
|
public moderationAgent: AtpAgent | undefined
|
86
91
|
public reportingAgent: AtpAgent | undefined
|
87
92
|
public entrywayAgent: AtpAgent | undefined
|
93
|
+
public proxyAgent: undici.Agent
|
88
94
|
public safeFetch: Fetch
|
89
95
|
public authVerifier: AuthVerifier
|
90
96
|
public authProvider?: PdsOAuthProvider
|
@@ -110,6 +116,7 @@ export class AppContext {
|
|
110
116
|
this.moderationAgent = opts.moderationAgent
|
111
117
|
this.reportingAgent = opts.reportingAgent
|
112
118
|
this.entrywayAgent = opts.entrywayAgent
|
119
|
+
this.proxyAgent = opts.proxyAgent
|
113
120
|
this.safeFetch = opts.safeFetch
|
114
121
|
this.authVerifier = opts.authVerifier
|
115
122
|
this.authProvider = opts.authProvider
|
@@ -256,20 +263,47 @@ export class AppContext {
|
|
256
263
|
appviewCdnUrlPattern: cfg.bskyAppView?.cdnUrlPattern,
|
257
264
|
})
|
258
265
|
|
266
|
+
// An agent for performing HTTP requests based on user provided URLs.
|
267
|
+
const proxyAgent = new undici.Agent({
|
268
|
+
allowH2: cfg.proxy.allowHTTP2, // This is experimental
|
269
|
+
headersTimeout: cfg.proxy.headersTimeout,
|
270
|
+
maxResponseSize: cfg.proxy.maxResponseSize,
|
271
|
+
bodyTimeout: cfg.proxy.bodyTimeout,
|
272
|
+
factory: cfg.proxy.disableSsrfProtection
|
273
|
+
? undefined
|
274
|
+
: (origin, opts) => {
|
275
|
+
const { protocol, hostname } =
|
276
|
+
origin instanceof URL ? origin : new URL(origin)
|
277
|
+
if (protocol !== 'https:') {
|
278
|
+
throw new Error(`Forbidden protocol "${protocol}"`)
|
279
|
+
}
|
280
|
+
if (isUnicastIp(hostname) === false) {
|
281
|
+
throw new Error('Hostname resolved to non-unicast address')
|
282
|
+
}
|
283
|
+
return new undici.Pool(origin, opts)
|
284
|
+
},
|
285
|
+
connect: {
|
286
|
+
lookup: cfg.proxy.disableSsrfProtection ? undefined : unicastLookup,
|
287
|
+
},
|
288
|
+
})
|
289
|
+
|
259
290
|
// A fetch() function that protects against SSRF attacks, large responses &
|
260
291
|
// known bad domains. This function can safely be used to fetch user
|
261
292
|
// provided URLs (unless "disableSsrfProtection" is true, of course).
|
262
|
-
const safeFetch =
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
293
|
+
const safeFetch = loggedFetch({
|
294
|
+
fetch: safeFetchWrap({
|
295
|
+
// Using globalThis.fetch allows safeFetchWrap to use keep-alive. See
|
296
|
+
// unicastFetchWrap().
|
297
|
+
fetch: globalThis.fetch,
|
298
|
+
allowIpHost: false,
|
299
|
+
responseMaxSize: cfg.fetch.maxResponseSize,
|
300
|
+
ssrfProtection: !cfg.fetch.disableSsrfProtection,
|
301
|
+
}),
|
302
|
+
logRequest: ({ method, url }) => {
|
303
|
+
fetchLogger.debug({ method, uri: url }, 'fetch')
|
272
304
|
},
|
305
|
+
logResponse: false,
|
306
|
+
logError: false,
|
273
307
|
})
|
274
308
|
|
275
309
|
const authProvider = cfg.oauth.provider
|
@@ -333,6 +367,7 @@ export class AppContext {
|
|
333
367
|
moderationAgent,
|
334
368
|
reportingAgent,
|
335
369
|
entrywayAgent,
|
370
|
+
proxyAgent,
|
336
371
|
safeFetch,
|
337
372
|
authVerifier,
|
338
373
|
authProvider,
|
@@ -356,6 +391,16 @@ export class AppContext {
|
|
356
391
|
keypair,
|
357
392
|
})
|
358
393
|
}
|
394
|
+
|
395
|
+
async serviceAuthJwt(did: string, aud: string, lxm: string) {
|
396
|
+
const keypair = await this.actorStore.keypair(did)
|
397
|
+
return createServiceJwt({
|
398
|
+
iss: did,
|
399
|
+
aud,
|
400
|
+
lxm,
|
401
|
+
keypair,
|
402
|
+
})
|
403
|
+
}
|
359
404
|
}
|
360
405
|
|
361
406
|
export default AppContext
|
package/src/index.ts
CHANGED
@@ -127,6 +127,7 @@ export class PDS {
|
|
127
127
|
await this.ctx.backgroundQueue.destroy()
|
128
128
|
await this.ctx.accountManager.close()
|
129
129
|
await this.ctx.redisScratch?.quit()
|
130
|
+
await this.ctx.proxyAgent.destroy()
|
130
131
|
clearInterval(this.dbStatsInterval)
|
131
132
|
clearInterval(this.sequencerStatsInterval)
|
132
133
|
}
|
package/src/lexicon/index.ts
CHANGED
@@ -166,7 +166,9 @@ import * as ToolsOzoneCommunicationUpdateTemplate from './types/tools/ozone/comm
|
|
166
166
|
import * as ToolsOzoneModerationEmitEvent from './types/tools/ozone/moderation/emitEvent'
|
167
167
|
import * as ToolsOzoneModerationGetEvent from './types/tools/ozone/moderation/getEvent'
|
168
168
|
import * as ToolsOzoneModerationGetRecord from './types/tools/ozone/moderation/getRecord'
|
169
|
+
import * as ToolsOzoneModerationGetRecords from './types/tools/ozone/moderation/getRecords'
|
169
170
|
import * as ToolsOzoneModerationGetRepo from './types/tools/ozone/moderation/getRepo'
|
171
|
+
import * as ToolsOzoneModerationGetRepos from './types/tools/ozone/moderation/getRepos'
|
170
172
|
import * as ToolsOzoneModerationQueryEvents from './types/tools/ozone/moderation/queryEvents'
|
171
173
|
import * as ToolsOzoneModerationQueryStatuses from './types/tools/ozone/moderation/queryStatuses'
|
172
174
|
import * as ToolsOzoneModerationSearchRepos from './types/tools/ozone/moderation/searchRepos'
|
@@ -2262,6 +2264,17 @@ export class ToolsOzoneModerationNS {
|
|
2262
2264
|
return this._server.xrpc.method(nsid, cfg)
|
2263
2265
|
}
|
2264
2266
|
|
2267
|
+
getRecords<AV extends AuthVerifier>(
|
2268
|
+
cfg: ConfigOf<
|
2269
|
+
AV,
|
2270
|
+
ToolsOzoneModerationGetRecords.Handler<ExtractAuth<AV>>,
|
2271
|
+
ToolsOzoneModerationGetRecords.HandlerReqCtx<ExtractAuth<AV>>
|
2272
|
+
>,
|
2273
|
+
) {
|
2274
|
+
const nsid = 'tools.ozone.moderation.getRecords' // @ts-ignore
|
2275
|
+
return this._server.xrpc.method(nsid, cfg)
|
2276
|
+
}
|
2277
|
+
|
2265
2278
|
getRepo<AV extends AuthVerifier>(
|
2266
2279
|
cfg: ConfigOf<
|
2267
2280
|
AV,
|
@@ -2273,6 +2286,17 @@ export class ToolsOzoneModerationNS {
|
|
2273
2286
|
return this._server.xrpc.method(nsid, cfg)
|
2274
2287
|
}
|
2275
2288
|
|
2289
|
+
getRepos<AV extends AuthVerifier>(
|
2290
|
+
cfg: ConfigOf<
|
2291
|
+
AV,
|
2292
|
+
ToolsOzoneModerationGetRepos.Handler<ExtractAuth<AV>>,
|
2293
|
+
ToolsOzoneModerationGetRepos.HandlerReqCtx<ExtractAuth<AV>>
|
2294
|
+
>,
|
2295
|
+
) {
|
2296
|
+
const nsid = 'tools.ozone.moderation.getRepos' // @ts-ignore
|
2297
|
+
return this._server.xrpc.method(nsid, cfg)
|
2298
|
+
}
|
2299
|
+
|
2276
2300
|
queryEvents<AV extends AuthVerifier>(
|
2277
2301
|
cfg: ConfigOf<
|
2278
2302
|
AV,
|
package/src/lexicon/lexicons.ts
CHANGED
@@ -1686,6 +1686,11 @@ export const schemaDict = {
|
|
1686
1686
|
},
|
1687
1687
|
},
|
1688
1688
|
},
|
1689
|
+
errors: [
|
1690
|
+
{
|
1691
|
+
name: 'RecordNotFound',
|
1692
|
+
},
|
1693
|
+
],
|
1689
1694
|
},
|
1690
1695
|
},
|
1691
1696
|
},
|
@@ -4190,6 +4195,10 @@ export const schemaDict = {
|
|
4190
4195
|
ref: 'lex:com.atproto.label.defs#label',
|
4191
4196
|
},
|
4192
4197
|
},
|
4198
|
+
pinnedPost: {
|
4199
|
+
type: 'ref',
|
4200
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
4201
|
+
},
|
4193
4202
|
},
|
4194
4203
|
},
|
4195
4204
|
profileAssociated: {
|
@@ -4812,6 +4821,10 @@ export const schemaDict = {
|
|
4812
4821
|
type: 'ref',
|
4813
4822
|
ref: 'lex:com.atproto.repo.strongRef',
|
4814
4823
|
},
|
4824
|
+
pinnedPost: {
|
4825
|
+
type: 'ref',
|
4826
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
4827
|
+
},
|
4815
4828
|
createdAt: {
|
4816
4829
|
type: 'string',
|
4817
4830
|
format: 'datetime',
|
@@ -5468,6 +5481,9 @@ export const schemaDict = {
|
|
5468
5481
|
embeddingDisabled: {
|
5469
5482
|
type: 'boolean',
|
5470
5483
|
},
|
5484
|
+
pinned: {
|
5485
|
+
type: 'boolean',
|
5486
|
+
},
|
5471
5487
|
},
|
5472
5488
|
},
|
5473
5489
|
feedViewPost: {
|
@@ -5484,7 +5500,10 @@ export const schemaDict = {
|
|
5484
5500
|
},
|
5485
5501
|
reason: {
|
5486
5502
|
type: 'union',
|
5487
|
-
refs: [
|
5503
|
+
refs: [
|
5504
|
+
'lex:app.bsky.feed.defs#reasonRepost',
|
5505
|
+
'lex:app.bsky.feed.defs#reasonPin',
|
5506
|
+
],
|
5488
5507
|
},
|
5489
5508
|
feedContext: {
|
5490
5509
|
type: 'string',
|
@@ -5536,6 +5555,10 @@ export const schemaDict = {
|
|
5536
5555
|
},
|
5537
5556
|
},
|
5538
5557
|
},
|
5558
|
+
reasonPin: {
|
5559
|
+
type: 'object',
|
5560
|
+
properties: {},
|
5561
|
+
},
|
5539
5562
|
threadViewPost: {
|
5540
5563
|
type: 'object',
|
5541
5564
|
required: ['post'],
|
@@ -5693,7 +5716,10 @@ export const schemaDict = {
|
|
5693
5716
|
},
|
5694
5717
|
reason: {
|
5695
5718
|
type: 'union',
|
5696
|
-
refs: [
|
5719
|
+
refs: [
|
5720
|
+
'lex:app.bsky.feed.defs#skeletonReasonRepost',
|
5721
|
+
'lex:app.bsky.feed.defs#skeletonReasonPin',
|
5722
|
+
],
|
5697
5723
|
},
|
5698
5724
|
feedContext: {
|
5699
5725
|
type: 'string',
|
@@ -5713,6 +5739,10 @@ export const schemaDict = {
|
|
5713
5739
|
},
|
5714
5740
|
},
|
5715
5741
|
},
|
5742
|
+
skeletonReasonPin: {
|
5743
|
+
type: 'object',
|
5744
|
+
properties: {},
|
5745
|
+
},
|
5716
5746
|
threadgateView: {
|
5717
5747
|
type: 'object',
|
5718
5748
|
properties: {
|
@@ -6078,6 +6108,10 @@ export const schemaDict = {
|
|
6078
6108
|
],
|
6079
6109
|
default: 'posts_with_replies',
|
6080
6110
|
},
|
6111
|
+
includePins: {
|
6112
|
+
type: 'boolean',
|
6113
|
+
default: false,
|
6114
|
+
},
|
6081
6115
|
},
|
6082
6116
|
},
|
6083
6117
|
output: {
|
@@ -7162,7 +7196,7 @@ export const schemaDict = {
|
|
7162
7196
|
type: 'record',
|
7163
7197
|
key: 'tid',
|
7164
7198
|
description:
|
7165
|
-
"Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository
|
7199
|
+
"Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository.",
|
7166
7200
|
record: {
|
7167
7201
|
type: 'object',
|
7168
7202
|
required: ['post', 'createdAt'],
|
@@ -8236,6 +8270,12 @@ export const schemaDict = {
|
|
8236
8270
|
ref: 'lex:app.bsky.actor.defs#profileView',
|
8237
8271
|
},
|
8238
8272
|
},
|
8273
|
+
isFallback: {
|
8274
|
+
type: 'boolean',
|
8275
|
+
description:
|
8276
|
+
'If true, response has fallen-back to generic results, and is not scoped using relativeToDid',
|
8277
|
+
default: false,
|
8278
|
+
},
|
8239
8279
|
},
|
8240
8280
|
},
|
8241
8281
|
},
|
@@ -9192,6 +9232,12 @@ export const schemaDict = {
|
|
9192
9232
|
ref: 'lex:app.bsky.unspecced.defs#skeletonSearchActor',
|
9193
9233
|
},
|
9194
9234
|
},
|
9235
|
+
relativeToDid: {
|
9236
|
+
type: 'string',
|
9237
|
+
format: 'did',
|
9238
|
+
description:
|
9239
|
+
'DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer.',
|
9240
|
+
},
|
9195
9241
|
},
|
9196
9242
|
},
|
9197
9243
|
},
|
@@ -11602,6 +11648,49 @@ export const schemaDict = {
|
|
11602
11648
|
},
|
11603
11649
|
},
|
11604
11650
|
},
|
11651
|
+
ToolsOzoneModerationGetRecords: {
|
11652
|
+
lexicon: 1,
|
11653
|
+
id: 'tools.ozone.moderation.getRecords',
|
11654
|
+
defs: {
|
11655
|
+
main: {
|
11656
|
+
type: 'query',
|
11657
|
+
description: 'Get details about some records.',
|
11658
|
+
parameters: {
|
11659
|
+
type: 'params',
|
11660
|
+
required: ['uris'],
|
11661
|
+
properties: {
|
11662
|
+
uris: {
|
11663
|
+
type: 'array',
|
11664
|
+
maxLength: 100,
|
11665
|
+
items: {
|
11666
|
+
type: 'string',
|
11667
|
+
format: 'at-uri',
|
11668
|
+
},
|
11669
|
+
},
|
11670
|
+
},
|
11671
|
+
},
|
11672
|
+
output: {
|
11673
|
+
encoding: 'application/json',
|
11674
|
+
schema: {
|
11675
|
+
type: 'object',
|
11676
|
+
required: ['records'],
|
11677
|
+
properties: {
|
11678
|
+
records: {
|
11679
|
+
type: 'array',
|
11680
|
+
items: {
|
11681
|
+
type: 'union',
|
11682
|
+
refs: [
|
11683
|
+
'lex:tools.ozone.moderation.defs#recordViewDetail',
|
11684
|
+
'lex:tools.ozone.moderation.defs#recordViewNotFound',
|
11685
|
+
],
|
11686
|
+
},
|
11687
|
+
},
|
11688
|
+
},
|
11689
|
+
},
|
11690
|
+
},
|
11691
|
+
},
|
11692
|
+
},
|
11693
|
+
},
|
11605
11694
|
ToolsOzoneModerationGetRepo: {
|
11606
11695
|
lexicon: 1,
|
11607
11696
|
id: 'tools.ozone.moderation.getRepo',
|
@@ -11634,6 +11723,49 @@ export const schemaDict = {
|
|
11634
11723
|
},
|
11635
11724
|
},
|
11636
11725
|
},
|
11726
|
+
ToolsOzoneModerationGetRepos: {
|
11727
|
+
lexicon: 1,
|
11728
|
+
id: 'tools.ozone.moderation.getRepos',
|
11729
|
+
defs: {
|
11730
|
+
main: {
|
11731
|
+
type: 'query',
|
11732
|
+
description: 'Get details about some repositories.',
|
11733
|
+
parameters: {
|
11734
|
+
type: 'params',
|
11735
|
+
required: ['dids'],
|
11736
|
+
properties: {
|
11737
|
+
dids: {
|
11738
|
+
type: 'array',
|
11739
|
+
maxLength: 100,
|
11740
|
+
items: {
|
11741
|
+
type: 'string',
|
11742
|
+
format: 'did',
|
11743
|
+
},
|
11744
|
+
},
|
11745
|
+
},
|
11746
|
+
},
|
11747
|
+
output: {
|
11748
|
+
encoding: 'application/json',
|
11749
|
+
schema: {
|
11750
|
+
type: 'object',
|
11751
|
+
required: ['repos'],
|
11752
|
+
properties: {
|
11753
|
+
repos: {
|
11754
|
+
type: 'array',
|
11755
|
+
items: {
|
11756
|
+
type: 'union',
|
11757
|
+
refs: [
|
11758
|
+
'lex:tools.ozone.moderation.defs#repoViewDetail',
|
11759
|
+
'lex:tools.ozone.moderation.defs#repoViewNotFound',
|
11760
|
+
],
|
11761
|
+
},
|
11762
|
+
},
|
11763
|
+
},
|
11764
|
+
},
|
11765
|
+
},
|
11766
|
+
},
|
11767
|
+
},
|
11768
|
+
},
|
11637
11769
|
ToolsOzoneModerationQueryEvents: {
|
11638
11770
|
lexicon: 1,
|
11639
11771
|
id: 'tools.ozone.moderation.queryEvents',
|
@@ -12450,7 +12582,9 @@ export const ids = {
|
|
12450
12582
|
ToolsOzoneModerationEmitEvent: 'tools.ozone.moderation.emitEvent',
|
12451
12583
|
ToolsOzoneModerationGetEvent: 'tools.ozone.moderation.getEvent',
|
12452
12584
|
ToolsOzoneModerationGetRecord: 'tools.ozone.moderation.getRecord',
|
12585
|
+
ToolsOzoneModerationGetRecords: 'tools.ozone.moderation.getRecords',
|
12453
12586
|
ToolsOzoneModerationGetRepo: 'tools.ozone.moderation.getRepo',
|
12587
|
+
ToolsOzoneModerationGetRepos: 'tools.ozone.moderation.getRepos',
|
12454
12588
|
ToolsOzoneModerationQueryEvents: 'tools.ozone.moderation.queryEvents',
|
12455
12589
|
ToolsOzoneModerationQueryStatuses: 'tools.ozone.moderation.queryStatuses',
|
12456
12590
|
ToolsOzoneModerationSearchRepos: 'tools.ozone.moderation.searchRepos',
|
@@ -7,6 +7,7 @@ import { isObj, hasProp } from '../../../../util'
|
|
7
7
|
import { CID } from 'multiformats/cid'
|
8
8
|
import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'
|
9
9
|
import * as AppBskyGraphDefs from '../graph/defs'
|
10
|
+
import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef'
|
10
11
|
|
11
12
|
export interface ProfileViewBasic {
|
12
13
|
did: string
|
@@ -74,6 +75,7 @@ export interface ProfileViewDetailed {
|
|
74
75
|
createdAt?: string
|
75
76
|
viewer?: ViewerState
|
76
77
|
labels?: ComAtprotoLabelDefs.Label[]
|
78
|
+
pinnedPost?: ComAtprotoRepoStrongRef.Main
|
77
79
|
[k: string]: unknown
|
78
80
|
}
|
79
81
|
|