@atproto/bsky 0.0.11 → 0.0.13
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 +29 -0
- package/LICENSE.txt +7 -0
- package/README.md +6 -1
- package/dist/api/com/atproto/admin/util.d.ts +5 -0
- package/dist/context.d.ts +6 -1
- package/dist/db/index.js +51 -2
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20230929T192920807Z-record-cursor-indexes.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/did-cache.d.ts +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1630 -755
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +8 -0
- package/dist/lexicon/lexicons.d.ts +243 -3
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +28 -0
- package/dist/lexicon/types/com/atproto/admin/getAccountInfo.d.ts +29 -0
- package/dist/lexicon/types/com/atproto/admin/getSubjectStatus.d.ts +39 -0
- package/dist/lexicon/types/com/atproto/admin/searchRepos.d.ts +0 -1
- package/dist/lexicon/types/com/atproto/admin/updateSubjectStatus.d.ts +46 -0
- package/dist/lexicon/types/com/atproto/server/createAccount.d.ts +4 -2
- package/dist/lexicon/types/com/atproto/server/createSession.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/server/refreshSession.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/server/reserveSigningKey.d.ts +36 -0
- package/dist/lexicon/types/com/atproto/sync/listRepos.d.ts +1 -0
- package/dist/services/actor/types.d.ts +1 -0
- package/dist/services/graph/index.d.ts +2 -0
- package/dist/services/moderation/index.d.ts +13 -3
- package/package.json +14 -15
- package/src/api/app/bsky/feed/getAuthorFeed.ts +2 -2
- package/src/api/app/bsky/feed/getPostThread.ts +2 -2
- package/src/api/app/bsky/notification/listNotifications.ts +33 -22
- package/src/api/com/atproto/admin/getModerationAction.ts +28 -2
- package/src/api/com/atproto/admin/getModerationReport.ts +27 -2
- package/src/api/com/atproto/admin/getRecord.ts +14 -2
- package/src/api/com/atproto/admin/getRepo.ts +13 -2
- package/src/api/com/atproto/admin/reverseModerationAction.ts +31 -5
- package/src/api/com/atproto/admin/searchRepos.ts +2 -5
- package/src/api/com/atproto/admin/takeModerationAction.ts +41 -7
- package/src/api/com/atproto/admin/util.ts +50 -0
- package/src/api/well-known.ts +8 -0
- package/src/auth.ts +12 -5
- package/src/auto-moderator/index.ts +1 -0
- package/src/context.ts +25 -1
- package/src/db/migrations/20230929T192920807Z-record-cursor-indexes.ts +40 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/did-cache.ts +29 -14
- package/src/feed-gen/with-friends.ts +2 -2
- package/src/index.ts +4 -1
- package/src/indexer/subscription.ts +1 -21
- package/src/lexicon/index.ts +48 -0
- package/src/lexicon/lexicons.ts +259 -5
- package/src/lexicon/types/app/bsky/actor/defs.ts +1 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +61 -0
- package/src/lexicon/types/com/atproto/admin/getAccountInfo.ts +41 -0
- package/src/lexicon/types/com/atproto/admin/getSubjectStatus.ts +54 -0
- package/src/lexicon/types/com/atproto/admin/searchRepos.ts +0 -1
- package/src/lexicon/types/com/atproto/admin/updateSubjectStatus.ts +61 -0
- package/src/lexicon/types/com/atproto/server/createAccount.ts +4 -2
- package/src/lexicon/types/com/atproto/server/createSession.ts +1 -0
- package/src/lexicon/types/com/atproto/server/refreshSession.ts +1 -0
- package/src/lexicon/types/com/atproto/server/reserveSigningKey.ts +51 -0
- package/src/lexicon/types/com/atproto/sync/listRepos.ts +1 -0
- package/src/logger.ts +8 -0
- package/src/services/actor/index.ts +7 -1
- package/src/services/actor/types.ts +1 -0
- package/src/services/actor/views.ts +26 -8
- package/src/services/graph/index.ts +26 -7
- package/src/services/indexing/index.ts +15 -17
- package/src/services/moderation/index.ts +94 -14
- package/src/services/moderation/views.ts +1 -0
- package/tests/__snapshots__/feed-generation.test.ts.snap +12 -12
- package/tests/__snapshots__/indexing.test.ts.snap +4 -4
- package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +172 -0
- package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +178 -0
- package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +177 -0
- package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +307 -0
- package/tests/admin/__snapshots__/get-record.test.ts.snap +275 -0
- package/tests/admin/__snapshots__/get-repo.test.ts.snap +103 -0
- package/tests/admin/get-moderation-action.test.ts +100 -0
- package/tests/admin/get-moderation-actions.test.ts +164 -0
- package/tests/admin/get-moderation-report.test.ts +100 -0
- package/tests/admin/get-moderation-reports.test.ts +332 -0
- package/tests/admin/get-record.test.ts +115 -0
- package/tests/admin/get-repo.test.ts +101 -0
- package/tests/{moderation.test.ts → admin/moderation.test.ts} +107 -9
- package/tests/admin/repo-search.test.ts +124 -0
- package/tests/algos/hot-classic.test.ts +3 -5
- package/tests/algos/whats-hot.test.ts +3 -5
- package/tests/algos/with-friends.test.ts +2 -4
- package/tests/auth.test.ts +64 -0
- package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -3
- package/tests/auto-moderator/labeler.test.ts +5 -7
- package/tests/auto-moderator/takedowns.test.ts +11 -12
- package/tests/blob-resolver.test.ts +1 -3
- package/tests/did-cache.test.ts +2 -5
- package/tests/feed-generation.test.ts +8 -6
- package/tests/handle-invalidation.test.ts +2 -3
- package/tests/image/server.test.ts +1 -4
- package/tests/image/sharp.test.ts +1 -1
- package/tests/indexing.test.ts +4 -4
- package/tests/notification-server.test.ts +2 -3
- package/tests/pipeline/backpressure.test.ts +2 -3
- package/tests/pipeline/reingest.test.ts +7 -4
- package/tests/pipeline/repartition.test.ts +2 -3
- package/tests/reprocessing.test.ts +2 -6
- package/tests/seeds/basic.ts +4 -4
- package/tests/seeds/follows.ts +1 -1
- package/tests/seeds/likes.ts +1 -1
- package/tests/seeds/reposts.ts +1 -1
- package/tests/seeds/users-bulk.ts +1 -1
- package/tests/seeds/users.ts +1 -1
- package/tests/server.test.ts +1 -3
- package/tests/subscription/repo.test.ts +2 -4
- package/tests/views/__snapshots__/author-feed.test.ts.snap +24 -24
- package/tests/views/__snapshots__/block-lists.test.ts.snap +42 -7
- package/tests/views/__snapshots__/blocks.test.ts.snap +2 -2
- package/tests/views/__snapshots__/list-feed.test.ts.snap +6 -6
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +15 -4
- package/tests/views/__snapshots__/mutes.test.ts.snap +2 -2
- package/tests/views/__snapshots__/notifications.test.ts.snap +2 -2
- package/tests/views/__snapshots__/posts.test.ts.snap +8 -8
- package/tests/views/__snapshots__/thread.test.ts.snap +10 -10
- package/tests/views/__snapshots__/timeline.test.ts.snap +58 -58
- package/tests/views/actor-likes.test.ts +2 -3
- package/tests/views/actor-search.test.ts +5 -5
- package/tests/views/admin/repo-search.test.ts +2 -4
- package/tests/views/author-feed.test.ts +2 -4
- package/tests/views/block-lists.test.ts +34 -7
- package/tests/views/blocks.test.ts +6 -3
- package/tests/views/follows.test.ts +2 -4
- package/tests/views/likes.test.ts +2 -5
- package/tests/views/list-feed.test.ts +2 -4
- package/tests/views/mute-lists.test.ts +23 -5
- package/tests/views/mutes.test.ts +2 -5
- package/tests/views/notifications.test.ts +2 -4
- package/tests/views/posts.test.ts +2 -5
- package/tests/views/profile.test.ts +4 -5
- package/tests/views/reposts.test.ts +2 -4
- package/tests/views/suggested-follows.test.ts +2 -3
- package/tests/views/suggestions.test.ts +2 -4
- package/tests/views/thread.test.ts +2 -4
- package/tests/views/threadgating.test.ts +2 -3
- package/tests/views/timeline.test.ts +2 -4
- package/LICENSE +0 -21
- package/dist/env.d.ts +0 -1
- package/example.dev.env +0 -5
- package/src/env.ts +0 -9
- package/tests/seeds/client.ts +0 -466
- /package/tests/{__snapshots__ → admin/__snapshots__}/moderation.test.ts.snap +0 -0
- /package/tests/{image/fixtures → sample-img}/at.png +0 -0
- /package/tests/{image/fixtures → sample-img}/hd-key.jpg +0 -0
- /package/tests/{image/fixtures → sample-img}/key-alt.jpg +0 -0
- /package/tests/{image/fixtures → sample-img}/key-landscape-large.jpg +0 -0
- /package/tests/{image/fixtures → sample-img}/key-landscape-small.jpg +0 -0
- /package/tests/{image/fixtures → sample-img}/key-portrait-large.jpg +0 -0
- /package/tests/{image/fixtures → sample-img}/key-portrait-small.jpg +0 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
2
|
import { AtUri } from '@atproto/syntax'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AuthRequiredError,
|
|
5
|
+
InvalidRequestError,
|
|
6
|
+
UpstreamFailureError,
|
|
7
|
+
} from '@atproto/xrpc-server'
|
|
4
8
|
import { Server } from '../../../../lexicon'
|
|
5
9
|
import AppContext from '../../../../context'
|
|
6
10
|
import {
|
|
@@ -9,6 +13,8 @@ import {
|
|
|
9
13
|
TAKEDOWN,
|
|
10
14
|
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
11
15
|
import { getSubject, getAction } from '../moderation/util'
|
|
16
|
+
import { TakedownSubjects } from '../../../../services/moderation'
|
|
17
|
+
import { retryHttp } from '../../../../util/retry'
|
|
12
18
|
|
|
13
19
|
export default function (server: Server, ctx: AppContext) {
|
|
14
20
|
server.com.atproto.admin.takeModerationAction({
|
|
@@ -52,7 +58,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
52
58
|
|
|
53
59
|
validateLabels([...(createLabelVals ?? []), ...(negateLabelVals ?? [])])
|
|
54
60
|
|
|
55
|
-
const
|
|
61
|
+
const { result, takenDown } = await db.transaction(async (dbTxn) => {
|
|
56
62
|
const moderationTxn = ctx.services.moderation(dbTxn)
|
|
57
63
|
const labelTxn = ctx.services.label(dbTxn)
|
|
58
64
|
|
|
@@ -67,13 +73,15 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
67
73
|
durationInHours,
|
|
68
74
|
})
|
|
69
75
|
|
|
76
|
+
let takenDown: TakedownSubjects | undefined
|
|
77
|
+
|
|
70
78
|
if (
|
|
71
79
|
result.action === TAKEDOWN &&
|
|
72
80
|
result.subjectType === 'com.atproto.admin.defs#repoRef' &&
|
|
73
81
|
result.subjectDid
|
|
74
82
|
) {
|
|
75
83
|
// No credentials to revoke on appview
|
|
76
|
-
await moderationTxn.takedownRepo({
|
|
84
|
+
takenDown = await moderationTxn.takedownRepo({
|
|
77
85
|
takedownId: result.id,
|
|
78
86
|
did: result.subjectDid,
|
|
79
87
|
})
|
|
@@ -82,11 +90,13 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
82
90
|
if (
|
|
83
91
|
result.action === TAKEDOWN &&
|
|
84
92
|
result.subjectType === 'com.atproto.repo.strongRef' &&
|
|
85
|
-
result.subjectUri
|
|
93
|
+
result.subjectUri &&
|
|
94
|
+
result.subjectCid
|
|
86
95
|
) {
|
|
87
|
-
await moderationTxn.takedownRecord({
|
|
96
|
+
takenDown = await moderationTxn.takedownRecord({
|
|
88
97
|
takedownId: result.id,
|
|
89
98
|
uri: new AtUri(result.subjectUri),
|
|
99
|
+
cid: CID.parse(result.subjectCid),
|
|
90
100
|
blobCids: subjectBlobCids?.map((cid) => CID.parse(cid)) ?? [],
|
|
91
101
|
})
|
|
92
102
|
}
|
|
@@ -98,12 +108,36 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
98
108
|
{ create: createLabelVals, negate: negateLabelVals },
|
|
99
109
|
)
|
|
100
110
|
|
|
101
|
-
return result
|
|
111
|
+
return { result, takenDown }
|
|
102
112
|
})
|
|
103
113
|
|
|
114
|
+
if (takenDown) {
|
|
115
|
+
const { did, subjects } = takenDown
|
|
116
|
+
if (did && subjects.length > 0) {
|
|
117
|
+
const agent = await ctx.pdsAdminAgent(did)
|
|
118
|
+
const results = await Promise.allSettled(
|
|
119
|
+
subjects.map((subject) =>
|
|
120
|
+
retryHttp(() =>
|
|
121
|
+
agent.api.com.atproto.admin.updateSubjectStatus({
|
|
122
|
+
subject,
|
|
123
|
+
takedown: {
|
|
124
|
+
applied: true,
|
|
125
|
+
ref: result.id.toString(),
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
const hadFailure = results.some((r) => r.status === 'rejected')
|
|
132
|
+
if (hadFailure) {
|
|
133
|
+
throw new UpstreamFailureError('failed to apply action on PDS')
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
104
138
|
return {
|
|
105
139
|
encoding: 'application/json',
|
|
106
|
-
body: await moderationService.views.action(
|
|
140
|
+
body: await moderationService.views.action(result),
|
|
107
141
|
}
|
|
108
142
|
},
|
|
109
143
|
})
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import AppContext from '../../../../context'
|
|
2
|
+
import {
|
|
3
|
+
RepoView,
|
|
4
|
+
RepoViewDetail,
|
|
5
|
+
AccountView,
|
|
6
|
+
} from '../../../../lexicon/types/com/atproto/admin/defs'
|
|
7
|
+
|
|
8
|
+
export const getPdsAccountInfo = async (
|
|
9
|
+
ctx: AppContext,
|
|
10
|
+
did: string,
|
|
11
|
+
): Promise<AccountView | null> => {
|
|
12
|
+
try {
|
|
13
|
+
const agent = await ctx.pdsAdminAgent(did)
|
|
14
|
+
const res = await agent.api.com.atproto.admin.getAccountInfo({ did })
|
|
15
|
+
return res.data
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const addAccountInfoToRepoViewDetail = (
|
|
22
|
+
repoView: RepoViewDetail,
|
|
23
|
+
accountInfo: AccountView | null,
|
|
24
|
+
includeEmail = false,
|
|
25
|
+
): RepoViewDetail => {
|
|
26
|
+
if (!accountInfo) return repoView
|
|
27
|
+
return {
|
|
28
|
+
...repoView,
|
|
29
|
+
email: includeEmail ? accountInfo.email : undefined,
|
|
30
|
+
invitedBy: accountInfo.invitedBy,
|
|
31
|
+
invitesDisabled: accountInfo.invitesDisabled,
|
|
32
|
+
inviteNote: accountInfo.inviteNote,
|
|
33
|
+
invites: accountInfo.invites,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const addAccountInfoToRepoView = (
|
|
38
|
+
repoView: RepoView,
|
|
39
|
+
accountInfo: AccountView | null,
|
|
40
|
+
includeEmail = false,
|
|
41
|
+
): RepoView => {
|
|
42
|
+
if (!accountInfo) return repoView
|
|
43
|
+
return {
|
|
44
|
+
...repoView,
|
|
45
|
+
email: includeEmail ? accountInfo.email : undefined,
|
|
46
|
+
invitedBy: accountInfo.invitedBy,
|
|
47
|
+
invitesDisabled: accountInfo.invitesDisabled,
|
|
48
|
+
inviteNote: accountInfo.inviteNote,
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/api/well-known.ts
CHANGED
|
@@ -12,6 +12,14 @@ export const createRouter = (ctx: AppContext): express.Router => {
|
|
|
12
12
|
res.json({
|
|
13
13
|
'@context': ['https://www.w3.org/ns/did/v1'],
|
|
14
14
|
id: ctx.cfg.serverDid,
|
|
15
|
+
verificationMethod: [
|
|
16
|
+
{
|
|
17
|
+
id: `${ctx.cfg.serverDid}#atproto`,
|
|
18
|
+
type: 'Multikey',
|
|
19
|
+
controller: ctx.cfg.serverDid,
|
|
20
|
+
publicKeyMultibase: ctx.signingKey.did().replace('did:key:', ''),
|
|
21
|
+
},
|
|
22
|
+
],
|
|
15
23
|
service: [
|
|
16
24
|
{
|
|
17
25
|
id: '#bsky_notif',
|
package/src/auth.ts
CHANGED
|
@@ -14,11 +14,18 @@ export const authVerifier =
|
|
|
14
14
|
if (!jwtStr) {
|
|
15
15
|
throw new AuthRequiredError('missing jwt', 'MissingJwt')
|
|
16
16
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const payload = await verifyJwt(
|
|
18
|
+
jwtStr,
|
|
19
|
+
opts.aud,
|
|
20
|
+
async (did, forceRefresh) => {
|
|
21
|
+
const atprotoData = await idResolver.did.resolveAtprotoData(
|
|
22
|
+
did,
|
|
23
|
+
forceRefresh,
|
|
24
|
+
)
|
|
25
|
+
return atprotoData.signingKey
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
return { credentials: { did: payload.iss }, artifacts: { aud: opts.aud } }
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
export const authOptionalVerifier =
|
package/src/context.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as plc from '@did-plc/lib'
|
|
2
2
|
import { IdResolver } from '@atproto/identity'
|
|
3
|
+
import { AtpAgent } from '@atproto/api'
|
|
4
|
+
import { Keypair } from '@atproto/crypto'
|
|
5
|
+
import { createServiceJwt } from '@atproto/xrpc-server'
|
|
3
6
|
import { DatabaseCoordinator } from './db'
|
|
4
7
|
import { ServerConfig } from './config'
|
|
5
8
|
import { ImageUriBuilder } from './image/uri'
|
|
@@ -10,7 +13,6 @@ import { BackgroundQueue } from './background'
|
|
|
10
13
|
import { MountedAlgos } from './feed-gen/types'
|
|
11
14
|
import { LabelCache } from './label-cache'
|
|
12
15
|
import { NotificationServer } from './notifications'
|
|
13
|
-
import { AtpAgent } from '@atproto/api'
|
|
14
16
|
|
|
15
17
|
export class AppContext {
|
|
16
18
|
constructor(
|
|
@@ -19,6 +21,7 @@ export class AppContext {
|
|
|
19
21
|
imgUriBuilder: ImageUriBuilder
|
|
20
22
|
cfg: ServerConfig
|
|
21
23
|
services: Services
|
|
24
|
+
signingKey: Keypair
|
|
22
25
|
idResolver: IdResolver
|
|
23
26
|
didCache: DidSqlCache
|
|
24
27
|
labelCache: LabelCache
|
|
@@ -45,6 +48,10 @@ export class AppContext {
|
|
|
45
48
|
return this.opts.services
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
get signingKey(): Keypair {
|
|
52
|
+
return this.opts.signingKey
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
get plcClient(): plc.Client {
|
|
49
56
|
return new plc.Client(this.cfg.didPlcUrl)
|
|
50
57
|
}
|
|
@@ -91,6 +98,23 @@ export class AppContext {
|
|
|
91
98
|
return auth.roleVerifier(this.cfg)
|
|
92
99
|
}
|
|
93
100
|
|
|
101
|
+
async serviceAuthJwt(aud: string) {
|
|
102
|
+
const iss = this.cfg.serverDid
|
|
103
|
+
return createServiceJwt({
|
|
104
|
+
iss,
|
|
105
|
+
aud,
|
|
106
|
+
keypair: this.signingKey,
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async pdsAdminAgent(did: string): Promise<AtpAgent> {
|
|
111
|
+
const data = await this.idResolver.did.resolveAtprotoData(did)
|
|
112
|
+
const agent = new AtpAgent({ service: data.pds })
|
|
113
|
+
const jwt = await this.serviceAuthJwt(did)
|
|
114
|
+
agent.api.setHeader('authorization', `Bearer ${jwt}`)
|
|
115
|
+
return agent
|
|
116
|
+
}
|
|
117
|
+
|
|
94
118
|
get backgroundQueue(): BackgroundQueue {
|
|
95
119
|
return this.opts.backgroundQueue
|
|
96
120
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createIndex('like_creator_cursor_idx')
|
|
6
|
+
.on('like')
|
|
7
|
+
.columns(['creator', 'sortAt', 'cid'])
|
|
8
|
+
.execute()
|
|
9
|
+
await db.schema
|
|
10
|
+
.createIndex('follow_creator_cursor_idx')
|
|
11
|
+
.on('follow')
|
|
12
|
+
.columns(['creator', 'sortAt', 'cid'])
|
|
13
|
+
.execute()
|
|
14
|
+
await db.schema
|
|
15
|
+
.createIndex('follow_subject_cursor_idx')
|
|
16
|
+
.on('follow')
|
|
17
|
+
.columns(['subjectDid', 'sortAt', 'cid'])
|
|
18
|
+
.execute()
|
|
19
|
+
|
|
20
|
+
// drop old indices that are superceded by these
|
|
21
|
+
await db.schema.dropIndex('like_creator_idx').execute()
|
|
22
|
+
await db.schema.dropIndex('follow_subjectdid_idx').execute()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
26
|
+
await db.schema
|
|
27
|
+
.createIndex('like_creator_idx')
|
|
28
|
+
.on('like')
|
|
29
|
+
.column('creator')
|
|
30
|
+
.execute()
|
|
31
|
+
await db.schema
|
|
32
|
+
.createIndex('follow_subjectdid_idx')
|
|
33
|
+
.on('follow')
|
|
34
|
+
.column('subjectDid')
|
|
35
|
+
.execute()
|
|
36
|
+
|
|
37
|
+
await db.schema.dropIndex('like_creator_cursor_idx').execute()
|
|
38
|
+
await db.schema.dropIndex('follow_creator_cursor_idx').execute()
|
|
39
|
+
await db.schema.dropIndex('follow_subject_cursor_idx').execute()
|
|
40
|
+
}
|
|
@@ -29,3 +29,4 @@ export * as _20230830T205507322Z from './20230830T205507322Z-suggested-feeds'
|
|
|
29
29
|
export * as _20230904T211011773Z from './20230904T211011773Z-block-lists'
|
|
30
30
|
export * as _20230906T222220386Z from './20230906T222220386Z-thread-gating'
|
|
31
31
|
export * as _20230920T213858047Z from './20230920T213858047Z-add-tags-to-post'
|
|
32
|
+
export * as _20230929T192920807Z from './20230929T192920807Z-record-cursor-indexes'
|
package/src/did-cache.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import PQueue from 'p-queue'
|
|
2
2
|
import { CacheResult, DidCache, DidDocument } from '@atproto/identity'
|
|
3
3
|
import { PrimaryDatabase } from './db'
|
|
4
|
+
import { excluded } from './db/util'
|
|
4
5
|
import { dbLogger } from './logger'
|
|
5
6
|
|
|
6
7
|
export class DidSqlCache implements DidCache {
|
|
@@ -16,25 +17,42 @@ export class DidSqlCache implements DidCache {
|
|
|
16
17
|
this.pQueue = new PQueue()
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
async cacheDid(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
async cacheDid(
|
|
21
|
+
did: string,
|
|
22
|
+
doc: DidDocument,
|
|
23
|
+
prevResult?: CacheResult,
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
if (prevResult) {
|
|
26
|
+
await this.db.db
|
|
27
|
+
.updateTable('did_cache')
|
|
28
|
+
.set({ doc, updatedAt: Date.now() })
|
|
29
|
+
.where('did', '=', did)
|
|
30
|
+
.where('updatedAt', '=', prevResult.updatedAt)
|
|
31
|
+
.execute()
|
|
32
|
+
} else {
|
|
33
|
+
await this.db.db
|
|
34
|
+
.insertInto('did_cache')
|
|
35
|
+
.values({ did, doc, updatedAt: Date.now() })
|
|
36
|
+
.onConflict((oc) =>
|
|
37
|
+
oc.column('did').doUpdateSet({
|
|
38
|
+
doc: excluded(this.db.db, 'doc'),
|
|
39
|
+
updatedAt: excluded(this.db.db, 'updatedAt'),
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
42
|
+
.executeTakeFirst()
|
|
43
|
+
}
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
async refreshCache(
|
|
30
47
|
did: string,
|
|
31
48
|
getDoc: () => Promise<DidDocument | null>,
|
|
49
|
+
prevResult?: CacheResult,
|
|
32
50
|
): Promise<void> {
|
|
33
51
|
this.pQueue?.add(async () => {
|
|
34
52
|
try {
|
|
35
53
|
const doc = await getDoc()
|
|
36
54
|
if (doc) {
|
|
37
|
-
await this.cacheDid(did, doc)
|
|
55
|
+
await this.cacheDid(did, doc, prevResult)
|
|
38
56
|
} else {
|
|
39
57
|
await this.clearEntry(did)
|
|
40
58
|
}
|
|
@@ -51,20 +69,17 @@ export class DidSqlCache implements DidCache {
|
|
|
51
69
|
.selectAll()
|
|
52
70
|
.executeTakeFirst()
|
|
53
71
|
if (!res) return null
|
|
72
|
+
|
|
54
73
|
const now = Date.now()
|
|
55
74
|
const updatedAt = new Date(res.updatedAt).getTime()
|
|
56
|
-
|
|
57
75
|
const expired = now > updatedAt + this.maxTTL
|
|
58
|
-
if (expired) {
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
76
|
const stale = now > updatedAt + this.staleTTL
|
|
63
77
|
return {
|
|
64
78
|
doc: res.doc,
|
|
65
79
|
updatedAt,
|
|
66
80
|
did,
|
|
67
81
|
stale,
|
|
82
|
+
expired,
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
|
|
@@ -15,7 +15,7 @@ const handler: AlgoHandler = async (
|
|
|
15
15
|
|
|
16
16
|
const { ref } = db.db.dynamic
|
|
17
17
|
|
|
18
|
-
const keyset = new FeedKeyset(ref('post.
|
|
18
|
+
const keyset = new FeedKeyset(ref('post.sortAt'), ref('post.cid'))
|
|
19
19
|
const sortFrom = keyset.unpack(cursor)?.primary
|
|
20
20
|
|
|
21
21
|
let postsQb = feedService
|
|
@@ -24,7 +24,7 @@ const handler: AlgoHandler = async (
|
|
|
24
24
|
.innerJoin('post_agg', 'post_agg.uri', 'post.uri')
|
|
25
25
|
.where('post_agg.likeCount', '>=', 5)
|
|
26
26
|
.where('follow.creator', '=', requester)
|
|
27
|
-
.where('post.
|
|
27
|
+
.where('post.sortAt', '>', getFeedDateThreshold(sortFrom))
|
|
28
28
|
|
|
29
29
|
postsQb = paginate(postsQb, { limit, cursor, keyset, tryIndex: true })
|
|
30
30
|
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { MountedAlgos } from './feed-gen/types'
|
|
|
26
26
|
import { LabelCache } from './label-cache'
|
|
27
27
|
import { NotificationServer } from './notifications'
|
|
28
28
|
import { AtpAgent } from '@atproto/api'
|
|
29
|
+
import { Keypair } from '@atproto/crypto'
|
|
29
30
|
|
|
30
31
|
export type { ServerConfigValues } from './config'
|
|
31
32
|
export type { MountedAlgos } from './feed-gen/types'
|
|
@@ -54,10 +55,11 @@ export class BskyAppView {
|
|
|
54
55
|
static create(opts: {
|
|
55
56
|
db: DatabaseCoordinator
|
|
56
57
|
config: ServerConfig
|
|
58
|
+
signingKey: Keypair
|
|
57
59
|
imgInvalidator?: ImageInvalidator
|
|
58
60
|
algos?: MountedAlgos
|
|
59
61
|
}): BskyAppView {
|
|
60
|
-
const { db, config, algos = {} } = opts
|
|
62
|
+
const { db, config, signingKey, algos = {} } = opts
|
|
61
63
|
let maybeImgInvalidator = opts.imgInvalidator
|
|
62
64
|
const app = express()
|
|
63
65
|
app.use(cors())
|
|
@@ -116,6 +118,7 @@ export class BskyAppView {
|
|
|
116
118
|
cfg: config,
|
|
117
119
|
services,
|
|
118
120
|
imgUriBuilder,
|
|
121
|
+
signingKey,
|
|
119
122
|
idResolver,
|
|
120
123
|
didCache,
|
|
121
124
|
labelCache,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import { CID } from 'multiformats/cid'
|
|
3
3
|
import { AtUri } from '@atproto/syntax'
|
|
4
|
-
import { cborDecode, wait } from '@atproto/common'
|
|
4
|
+
import { cborDecode, wait, handleAllSettledErrors } from '@atproto/common'
|
|
5
5
|
import { DisconnectError } from '@atproto/xrpc-server'
|
|
6
6
|
import {
|
|
7
7
|
WriteOpAction,
|
|
@@ -343,23 +343,3 @@ type PreparedDelete = {
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
type PreparedWrite = PreparedCreate | PreparedUpdate | PreparedDelete
|
|
346
|
-
|
|
347
|
-
function handleAllSettledErrors(results: PromiseSettledResult<unknown>[]) {
|
|
348
|
-
const errors = results.filter(isRejected).map((res) => res.reason)
|
|
349
|
-
if (errors.length === 0) {
|
|
350
|
-
return
|
|
351
|
-
}
|
|
352
|
-
if (errors.length === 1) {
|
|
353
|
-
throw errors[0]
|
|
354
|
-
}
|
|
355
|
-
throw new AggregateError(
|
|
356
|
-
errors,
|
|
357
|
-
'Multiple errors: ' + errors.map((err) => err?.message).join('\n'),
|
|
358
|
-
)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function isRejected(
|
|
362
|
-
result: PromiseSettledResult<unknown>,
|
|
363
|
-
): result is PromiseRejectedResult {
|
|
364
|
-
return result.status === 'rejected'
|
|
365
|
-
}
|
package/src/lexicon/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { schemas } from './lexicons'
|
|
|
12
12
|
import * as ComAtprotoAdminDisableAccountInvites from './types/com/atproto/admin/disableAccountInvites'
|
|
13
13
|
import * as ComAtprotoAdminDisableInviteCodes from './types/com/atproto/admin/disableInviteCodes'
|
|
14
14
|
import * as ComAtprotoAdminEnableAccountInvites from './types/com/atproto/admin/enableAccountInvites'
|
|
15
|
+
import * as ComAtprotoAdminGetAccountInfo from './types/com/atproto/admin/getAccountInfo'
|
|
15
16
|
import * as ComAtprotoAdminGetInviteCodes from './types/com/atproto/admin/getInviteCodes'
|
|
16
17
|
import * as ComAtprotoAdminGetModerationAction from './types/com/atproto/admin/getModerationAction'
|
|
17
18
|
import * as ComAtprotoAdminGetModerationActions from './types/com/atproto/admin/getModerationActions'
|
|
@@ -19,6 +20,7 @@ import * as ComAtprotoAdminGetModerationReport from './types/com/atproto/admin/g
|
|
|
19
20
|
import * as ComAtprotoAdminGetModerationReports from './types/com/atproto/admin/getModerationReports'
|
|
20
21
|
import * as ComAtprotoAdminGetRecord from './types/com/atproto/admin/getRecord'
|
|
21
22
|
import * as ComAtprotoAdminGetRepo from './types/com/atproto/admin/getRepo'
|
|
23
|
+
import * as ComAtprotoAdminGetSubjectStatus from './types/com/atproto/admin/getSubjectStatus'
|
|
22
24
|
import * as ComAtprotoAdminResolveModerationReports from './types/com/atproto/admin/resolveModerationReports'
|
|
23
25
|
import * as ComAtprotoAdminReverseModerationAction from './types/com/atproto/admin/reverseModerationAction'
|
|
24
26
|
import * as ComAtprotoAdminSearchRepos from './types/com/atproto/admin/searchRepos'
|
|
@@ -26,6 +28,7 @@ import * as ComAtprotoAdminSendEmail from './types/com/atproto/admin/sendEmail'
|
|
|
26
28
|
import * as ComAtprotoAdminTakeModerationAction from './types/com/atproto/admin/takeModerationAction'
|
|
27
29
|
import * as ComAtprotoAdminUpdateAccountEmail from './types/com/atproto/admin/updateAccountEmail'
|
|
28
30
|
import * as ComAtprotoAdminUpdateAccountHandle from './types/com/atproto/admin/updateAccountHandle'
|
|
31
|
+
import * as ComAtprotoAdminUpdateSubjectStatus from './types/com/atproto/admin/updateSubjectStatus'
|
|
29
32
|
import * as ComAtprotoIdentityResolveHandle from './types/com/atproto/identity/resolveHandle'
|
|
30
33
|
import * as ComAtprotoIdentityUpdateHandle from './types/com/atproto/identity/updateHandle'
|
|
31
34
|
import * as ComAtprotoLabelQueryLabels from './types/com/atproto/label/queryLabels'
|
|
@@ -56,6 +59,7 @@ import * as ComAtprotoServerRequestAccountDelete from './types/com/atproto/serve
|
|
|
56
59
|
import * as ComAtprotoServerRequestEmailConfirmation from './types/com/atproto/server/requestEmailConfirmation'
|
|
57
60
|
import * as ComAtprotoServerRequestEmailUpdate from './types/com/atproto/server/requestEmailUpdate'
|
|
58
61
|
import * as ComAtprotoServerRequestPasswordReset from './types/com/atproto/server/requestPasswordReset'
|
|
62
|
+
import * as ComAtprotoServerReserveSigningKey from './types/com/atproto/server/reserveSigningKey'
|
|
59
63
|
import * as ComAtprotoServerResetPassword from './types/com/atproto/server/resetPassword'
|
|
60
64
|
import * as ComAtprotoServerRevokeAppPassword from './types/com/atproto/server/revokeAppPassword'
|
|
61
65
|
import * as ComAtprotoServerUpdateEmail from './types/com/atproto/server/updateEmail'
|
|
@@ -224,6 +228,17 @@ export class AdminNS {
|
|
|
224
228
|
return this._server.xrpc.method(nsid, cfg)
|
|
225
229
|
}
|
|
226
230
|
|
|
231
|
+
getAccountInfo<AV extends AuthVerifier>(
|
|
232
|
+
cfg: ConfigOf<
|
|
233
|
+
AV,
|
|
234
|
+
ComAtprotoAdminGetAccountInfo.Handler<ExtractAuth<AV>>,
|
|
235
|
+
ComAtprotoAdminGetAccountInfo.HandlerReqCtx<ExtractAuth<AV>>
|
|
236
|
+
>,
|
|
237
|
+
) {
|
|
238
|
+
const nsid = 'com.atproto.admin.getAccountInfo' // @ts-ignore
|
|
239
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
240
|
+
}
|
|
241
|
+
|
|
227
242
|
getInviteCodes<AV extends AuthVerifier>(
|
|
228
243
|
cfg: ConfigOf<
|
|
229
244
|
AV,
|
|
@@ -301,6 +316,17 @@ export class AdminNS {
|
|
|
301
316
|
return this._server.xrpc.method(nsid, cfg)
|
|
302
317
|
}
|
|
303
318
|
|
|
319
|
+
getSubjectStatus<AV extends AuthVerifier>(
|
|
320
|
+
cfg: ConfigOf<
|
|
321
|
+
AV,
|
|
322
|
+
ComAtprotoAdminGetSubjectStatus.Handler<ExtractAuth<AV>>,
|
|
323
|
+
ComAtprotoAdminGetSubjectStatus.HandlerReqCtx<ExtractAuth<AV>>
|
|
324
|
+
>,
|
|
325
|
+
) {
|
|
326
|
+
const nsid = 'com.atproto.admin.getSubjectStatus' // @ts-ignore
|
|
327
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
328
|
+
}
|
|
329
|
+
|
|
304
330
|
resolveModerationReports<AV extends AuthVerifier>(
|
|
305
331
|
cfg: ConfigOf<
|
|
306
332
|
AV,
|
|
@@ -377,6 +403,17 @@ export class AdminNS {
|
|
|
377
403
|
const nsid = 'com.atproto.admin.updateAccountHandle' // @ts-ignore
|
|
378
404
|
return this._server.xrpc.method(nsid, cfg)
|
|
379
405
|
}
|
|
406
|
+
|
|
407
|
+
updateSubjectStatus<AV extends AuthVerifier>(
|
|
408
|
+
cfg: ConfigOf<
|
|
409
|
+
AV,
|
|
410
|
+
ComAtprotoAdminUpdateSubjectStatus.Handler<ExtractAuth<AV>>,
|
|
411
|
+
ComAtprotoAdminUpdateSubjectStatus.HandlerReqCtx<ExtractAuth<AV>>
|
|
412
|
+
>,
|
|
413
|
+
) {
|
|
414
|
+
const nsid = 'com.atproto.admin.updateSubjectStatus' // @ts-ignore
|
|
415
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
416
|
+
}
|
|
380
417
|
}
|
|
381
418
|
|
|
382
419
|
export class IdentityNS {
|
|
@@ -748,6 +785,17 @@ export class ServerNS {
|
|
|
748
785
|
return this._server.xrpc.method(nsid, cfg)
|
|
749
786
|
}
|
|
750
787
|
|
|
788
|
+
reserveSigningKey<AV extends AuthVerifier>(
|
|
789
|
+
cfg: ConfigOf<
|
|
790
|
+
AV,
|
|
791
|
+
ComAtprotoServerReserveSigningKey.Handler<ExtractAuth<AV>>,
|
|
792
|
+
ComAtprotoServerReserveSigningKey.HandlerReqCtx<ExtractAuth<AV>>
|
|
793
|
+
>,
|
|
794
|
+
) {
|
|
795
|
+
const nsid = 'com.atproto.server.reserveSigningKey' // @ts-ignore
|
|
796
|
+
return this._server.xrpc.method(nsid, cfg)
|
|
797
|
+
}
|
|
798
|
+
|
|
751
799
|
resetPassword<AV extends AuthVerifier>(
|
|
752
800
|
cfg: ConfigOf<
|
|
753
801
|
AV,
|