@atproto/pds 0.4.28 → 0.4.29
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +7 -0
- package/dist/account-manager/helpers/account.d.ts +7 -0
- package/dist/account-manager/helpers/account.d.ts.map +1 -1
- package/dist/account-manager/helpers/account.js +9 -1
- package/dist/account-manager/helpers/account.js.map +1 -1
- package/dist/account-manager/index.d.ts +3 -1
- package/dist/account-manager/index.d.ts.map +1 -1
- package/dist/account-manager/index.js +22 -6
- package/dist/account-manager/index.js.map +1 -1
- package/dist/api/com/atproto/admin/deleteAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/deleteAccount.js +4 -2
- package/dist/api/com/atproto/admin/deleteAccount.js.map +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.js +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.js.map +1 -1
- package/dist/api/com/atproto/admin/updateSubjectStatus.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/updateSubjectStatus.js +2 -0
- package/dist/api/com/atproto/admin/updateSubjectStatus.js.map +1 -1
- package/dist/api/com/atproto/identity/submitPlcOperation.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/submitPlcOperation.js.map +1 -1
- package/dist/api/com/atproto/identity/updateHandle.js +1 -1
- package/dist/api/com/atproto/identity/updateHandle.js.map +1 -1
- package/dist/api/com/atproto/repo/describeRepo.d.ts.map +1 -1
- package/dist/api/com/atproto/repo/describeRepo.js +2 -4
- package/dist/api/com/atproto/repo/describeRepo.js.map +1 -1
- package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/activateAccount.js +2 -1
- package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
- package/dist/api/com/atproto/server/createAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/createAccount.js +4 -2
- package/dist/api/com/atproto/server/createAccount.js.map +1 -1
- package/dist/api/com/atproto/server/deactivateAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/deactivateAccount.js +2 -0
- package/dist/api/com/atproto/server/deactivateAccount.js.map +1 -1
- package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/deleteAccount.js +4 -3
- package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
- package/dist/api/com/atproto/server/util.d.ts +1 -0
- package/dist/api/com/atproto/server/util.d.ts.map +1 -1
- package/dist/api/com/atproto/server/util.js +9 -6
- package/dist/api/com/atproto/server/util.js.map +1 -1
- package/dist/api/com/atproto/sync/deprecated/getCheckout.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/deprecated/getCheckout.js +2 -8
- package/dist/api/com/atproto/sync/deprecated/getCheckout.js.map +1 -1
- package/dist/api/com/atproto/sync/deprecated/getHead.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/deprecated/getHead.js +2 -7
- package/dist/api/com/atproto/sync/deprecated/getHead.js.map +1 -1
- package/dist/api/com/atproto/sync/getBlob.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getBlob.js +3 -6
- package/dist/api/com/atproto/sync/getBlob.js.map +1 -1
- package/dist/api/com/atproto/sync/getBlocks.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getBlocks.js +2 -7
- package/dist/api/com/atproto/sync/getBlocks.js.map +1 -1
- package/dist/api/com/atproto/sync/getLatestCommit.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getLatestCommit.js +2 -7
- package/dist/api/com/atproto/sync/getLatestCommit.js.map +1 -1
- package/dist/api/com/atproto/sync/getRecord.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getRecord.js +2 -7
- package/dist/api/com/atproto/sync/getRecord.js.map +1 -1
- package/dist/api/com/atproto/sync/getRepo.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getRepo.js +2 -7
- package/dist/api/com/atproto/sync/getRepo.js.map +1 -1
- package/dist/api/com/atproto/sync/getRepoStatus.d.ts +4 -0
- package/dist/api/com/atproto/sync/getRepoStatus.d.ts.map +1 -0
- package/dist/api/com/atproto/sync/getRepoStatus.js +28 -0
- package/dist/api/com/atproto/sync/getRepoStatus.js.map +1 -0
- package/dist/api/com/atproto/sync/index.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/index.js +2 -0
- package/dist/api/com/atproto/sync/index.js.map +1 -1
- package/dist/api/com/atproto/sync/listBlobs.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/listBlobs.js +2 -8
- package/dist/api/com/atproto/sync/listBlobs.js.map +1 -1
- package/dist/api/com/atproto/sync/listRepos.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/listRepos.js +13 -8
- package/dist/api/com/atproto/sync/listRepos.js.map +1 -1
- package/dist/api/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/subscribeRepos.js +8 -0
- package/dist/api/com/atproto/sync/subscribeRepos.js.map +1 -1
- package/dist/api/com/atproto/sync/util.d.ts +11 -0
- package/dist/api/com/atproto/sync/util.d.ts.map +1 -0
- package/dist/api/com/atproto/sync/util.js +41 -0
- package/dist/api/com/atproto/sync/util.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +105 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +185 -4
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +1 -1
- package/dist/lexicon/types/com/atproto/sync/getBlob.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/getBlob.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getBlocks.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/getBlocks.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getLatestCommit.d.ts +1 -1
- package/dist/lexicon/types/com/atproto/sync/getLatestCommit.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getRecord.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/getRecord.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getRepo.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/getRepo.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getRepoStatus.d.ts +42 -0
- package/dist/lexicon/types/com/atproto/sync/getRepoStatus.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/getRepoStatus.js +3 -0
- package/dist/lexicon/types/com/atproto/sync/getRepoStatus.js.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/listBlobs.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/listBlobs.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/listRepos.d.ts +3 -0
- package/dist/lexicon/types/com/atproto/sync/listRepos.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/listRepos.js.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts +19 -4
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js +11 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js.map +1 -1
- package/dist/scripts/index.d.ts +4 -0
- package/dist/scripts/index.d.ts.map +1 -0
- package/dist/scripts/index.js +8 -0
- package/dist/scripts/index.js.map +1 -0
- package/dist/scripts/rebuild-repo.d.ts +3 -0
- package/dist/scripts/rebuild-repo.d.ts.map +1 -0
- package/dist/scripts/rebuild-repo.js +121 -0
- package/dist/scripts/rebuild-repo.js.map +1 -0
- package/dist/sequencer/db/schema.d.ts +1 -1
- package/dist/sequencer/db/schema.d.ts.map +1 -1
- package/dist/sequencer/events.d.ts +27 -2
- package/dist/sequencer/events.d.ts.map +1 -1
- package/dist/sequencer/events.js +35 -2
- package/dist/sequencer/events.js.map +1 -1
- package/dist/sequencer/sequencer.d.ts +8 -6
- package/dist/sequencer/sequencer.d.ts.map +1 -1
- package/dist/sequencer/sequencer.js +22 -9
- package/dist/sequencer/sequencer.js.map +1 -1
- package/package.json +4 -4
- package/src/account-manager/helpers/account.ts +8 -0
- package/src/account-manager/index.ts +19 -6
- package/src/api/com/atproto/admin/deleteAccount.ts +7 -2
- package/src/api/com/atproto/admin/updateAccountHandle.ts +1 -1
- package/src/api/com/atproto/admin/updateSubjectStatus.ts +2 -0
- package/src/api/com/atproto/identity/submitPlcOperation.ts +1 -0
- package/src/api/com/atproto/identity/updateHandle.ts +1 -1
- package/src/api/com/atproto/repo/describeRepo.ts +2 -4
- package/src/api/com/atproto/server/activateAccount.ts +2 -1
- package/src/api/com/atproto/server/createAccount.ts +6 -3
- package/src/api/com/atproto/server/deactivateAccount.ts +2 -0
- package/src/api/com/atproto/server/deleteAccount.ts +7 -3
- package/src/api/com/atproto/server/util.ts +12 -5
- package/src/api/com/atproto/sync/deprecated/getCheckout.ts +6 -8
- package/src/api/com/atproto/sync/deprecated/getHead.ts +7 -10
- package/src/api/com/atproto/sync/getBlob.ts +8 -6
- package/src/api/com/atproto/sync/getBlocks.ts +6 -7
- package/src/api/com/atproto/sync/getLatestCommit.ts +7 -10
- package/src/api/com/atproto/sync/getRecord.ts +7 -7
- package/src/api/com/atproto/sync/getRepo.ts +6 -7
- package/src/api/com/atproto/sync/getRepoStatus.ts +31 -0
- package/src/api/com/atproto/sync/index.ts +2 -0
- package/src/api/com/atproto/sync/listBlobs.ts +6 -8
- package/src/api/com/atproto/sync/listRepos.ts +13 -8
- package/src/api/com/atproto/sync/subscribeRepos.ts +7 -0
- package/src/api/com/atproto/sync/util.ts +59 -0
- package/src/index.ts +1 -0
- package/src/lexicon/index.ts +12 -0
- package/src/lexicon/lexicons.ts +193 -7
- package/src/lexicon/types/chat/bsky/convo/defs.ts +1 -1
- package/src/lexicon/types/com/atproto/sync/getBlob.ts +6 -0
- package/src/lexicon/types/com/atproto/sync/getBlocks.ts +6 -0
- package/src/lexicon/types/com/atproto/sync/getLatestCommit.ts +1 -1
- package/src/lexicon/types/com/atproto/sync/getRecord.ts +6 -0
- package/src/lexicon/types/com/atproto/sync/getRepo.ts +1 -0
- package/src/lexicon/types/com/atproto/sync/getRepoStatus.ts +52 -0
- package/src/lexicon/types/com/atproto/sync/listBlobs.ts +1 -0
- package/src/lexicon/types/com/atproto/sync/listRepos.ts +3 -0
- package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +30 -3
- package/src/scripts/index.ts +5 -0
- package/src/scripts/rebuild-repo.ts +143 -0
- package/src/sequencer/db/schema.ts +1 -0
- package/src/sequencer/events.ts +47 -0
- package/src/sequencer/sequencer.ts +35 -14
- package/tests/account-deactivation.test.ts +36 -10
- package/tests/account-deletion.test.ts +10 -2
- package/tests/moderation.test.ts +2 -2
- package/tests/proxied/notif.test.ts +1 -0
- package/tests/sequencer.test.ts +2 -2
- package/tests/sync/list.test.ts +1 -0
- package/tests/sync/subscribe-repos.test.ts +224 -40
- package/tests/sync/sync.test.ts +48 -4
@@ -0,0 +1,52 @@
|
|
1
|
+
/**
|
2
|
+
* GENERATED CODE - DO NOT MODIFY
|
3
|
+
*/
|
4
|
+
import express from 'express'
|
5
|
+
import { ValidationResult, BlobRef } from '@atproto/lexicon'
|
6
|
+
import { lexicons } from '../../../../lexicons'
|
7
|
+
import { isObj, hasProp } from '../../../../util'
|
8
|
+
import { CID } from 'multiformats/cid'
|
9
|
+
import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
|
10
|
+
|
11
|
+
export interface QueryParams {
|
12
|
+
/** The DID of the repo. */
|
13
|
+
did: string
|
14
|
+
}
|
15
|
+
|
16
|
+
export type InputSchema = undefined
|
17
|
+
|
18
|
+
export interface OutputSchema {
|
19
|
+
did: string
|
20
|
+
active: boolean
|
21
|
+
/** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */
|
22
|
+
status?: 'takendown' | 'suspended' | 'deactivated' | (string & {})
|
23
|
+
/** Optional field, the current rev of the repo, if active=true */
|
24
|
+
rev?: string
|
25
|
+
[k: string]: unknown
|
26
|
+
}
|
27
|
+
|
28
|
+
export type HandlerInput = undefined
|
29
|
+
|
30
|
+
export interface HandlerSuccess {
|
31
|
+
encoding: 'application/json'
|
32
|
+
body: OutputSchema
|
33
|
+
headers?: { [key: string]: string }
|
34
|
+
}
|
35
|
+
|
36
|
+
export interface HandlerError {
|
37
|
+
status: number
|
38
|
+
message?: string
|
39
|
+
error?: 'RepoNotFound'
|
40
|
+
}
|
41
|
+
|
42
|
+
export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
|
43
|
+
export type HandlerReqCtx<HA extends HandlerAuth = never> = {
|
44
|
+
auth: HA
|
45
|
+
params: QueryParams
|
46
|
+
input: HandlerInput
|
47
|
+
req: express.Request
|
48
|
+
res: express.Response
|
49
|
+
}
|
50
|
+
export type Handler<HA extends HandlerAuth = never> = (
|
51
|
+
ctx: HandlerReqCtx<HA>,
|
52
|
+
) => Promise<HandlerOutput> | HandlerOutput
|
@@ -36,6 +36,7 @@ export interface HandlerSuccess {
|
|
36
36
|
export interface HandlerError {
|
37
37
|
status: number
|
38
38
|
message?: string
|
39
|
+
error?: 'RepoNotFound' | 'RepoTakendown' | 'RepoSuspended' | 'RepoDeactivated'
|
39
40
|
}
|
40
41
|
|
41
42
|
export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
|
@@ -51,6 +51,9 @@ export interface Repo {
|
|
51
51
|
/** Current repo commit CID */
|
52
52
|
head: string
|
53
53
|
rev: string
|
54
|
+
active?: boolean
|
55
|
+
/** If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. */
|
56
|
+
status?: 'takendown' | 'suspended' | 'deactivated' | (string & {})
|
54
57
|
[k: string]: unknown
|
55
58
|
}
|
56
59
|
|
@@ -16,6 +16,7 @@ export interface QueryParams {
|
|
16
16
|
export type OutputSchema =
|
17
17
|
| Commit
|
18
18
|
| Identity
|
19
|
+
| Account
|
19
20
|
| Handle
|
20
21
|
| Migrate
|
21
22
|
| Tombstone
|
@@ -77,6 +78,8 @@ export interface Identity {
|
|
77
78
|
seq: number
|
78
79
|
did: string
|
79
80
|
time: string
|
81
|
+
/** The current handle for the account, or 'handle.invalid' if validation fails. This field is optional, might have been validated or passed-through from an upstream source. Semantics and behaviors for PDS vs Relay may evolve in the future; see atproto specs for more details. */
|
82
|
+
handle?: string
|
80
83
|
[k: string]: unknown
|
81
84
|
}
|
82
85
|
|
@@ -92,7 +95,31 @@ export function validateIdentity(v: unknown): ValidationResult {
|
|
92
95
|
return lexicons.validate('com.atproto.sync.subscribeRepos#identity', v)
|
93
96
|
}
|
94
97
|
|
95
|
-
/** Represents
|
98
|
+
/** Represents a change to an account's status on a host (eg, PDS or Relay). The semantics of this event are that the status is at the host which emitted the event, not necessarily that at the currently active PDS. Eg, a Relay takedown would emit a takedown with active=false, even if the PDS is still active. */
|
99
|
+
export interface Account {
|
100
|
+
seq: number
|
101
|
+
did: string
|
102
|
+
time: string
|
103
|
+
/** Indicates that the account has a repository which can be fetched from the host that emitted this event. */
|
104
|
+
active: boolean
|
105
|
+
/** If active=false, this optional field indicates a reason for why the account is not active. */
|
106
|
+
status?: 'takendown' | 'suspended' | 'deleted' | 'deactivated' | (string & {})
|
107
|
+
[k: string]: unknown
|
108
|
+
}
|
109
|
+
|
110
|
+
export function isAccount(v: unknown): v is Account {
|
111
|
+
return (
|
112
|
+
isObj(v) &&
|
113
|
+
hasProp(v, '$type') &&
|
114
|
+
v.$type === 'com.atproto.sync.subscribeRepos#account'
|
115
|
+
)
|
116
|
+
}
|
117
|
+
|
118
|
+
export function validateAccount(v: unknown): ValidationResult {
|
119
|
+
return lexicons.validate('com.atproto.sync.subscribeRepos#account', v)
|
120
|
+
}
|
121
|
+
|
122
|
+
/** DEPRECATED -- Use #identity event instead */
|
96
123
|
export interface Handle {
|
97
124
|
seq: number
|
98
125
|
did: string
|
@@ -113,7 +140,7 @@ export function validateHandle(v: unknown): ValidationResult {
|
|
113
140
|
return lexicons.validate('com.atproto.sync.subscribeRepos#handle', v)
|
114
141
|
}
|
115
142
|
|
116
|
-
/**
|
143
|
+
/** DEPRECATED -- Use #account event instead */
|
117
144
|
export interface Migrate {
|
118
145
|
seq: number
|
119
146
|
did: string
|
@@ -134,7 +161,7 @@ export function validateMigrate(v: unknown): ValidationResult {
|
|
134
161
|
return lexicons.validate('com.atproto.sync.subscribeRepos#migrate', v)
|
135
162
|
}
|
136
163
|
|
137
|
-
/**
|
164
|
+
/** DEPRECATED -- Use #account event instead */
|
138
165
|
export interface Tombstone {
|
139
166
|
seq: number
|
140
167
|
did: string
|
@@ -0,0 +1,143 @@
|
|
1
|
+
import readline from 'node:readline/promises'
|
2
|
+
import { CID } from 'multiformats/cid'
|
3
|
+
import {
|
4
|
+
BlockMap,
|
5
|
+
CidSet,
|
6
|
+
MST,
|
7
|
+
MemoryBlockstore,
|
8
|
+
formatDataKey,
|
9
|
+
signCommit,
|
10
|
+
} from '@atproto/repo'
|
11
|
+
import { AtUri } from '@atproto/syntax'
|
12
|
+
import { TID } from '@atproto/common'
|
13
|
+
import { ActorStoreTransactor } from '../actor-store'
|
14
|
+
import AppContext from '../context'
|
15
|
+
|
16
|
+
export const rebuildRepo = async (ctx: AppContext, args: string[]) => {
|
17
|
+
const did = args[0]
|
18
|
+
if (!did || !did.startsWith('did:')) {
|
19
|
+
throw new Error('Expected DID as argument')
|
20
|
+
}
|
21
|
+
|
22
|
+
const memoryStore = new MemoryBlockstore()
|
23
|
+
const rev = TID.nextStr()
|
24
|
+
const commit = await ctx.actorStore.transact(did, async (store) => {
|
25
|
+
const [records, existingCids] = await Promise.all([
|
26
|
+
listAllRecords(store),
|
27
|
+
listExistingBlocks(store),
|
28
|
+
])
|
29
|
+
let mst = await MST.create(memoryStore)
|
30
|
+
for (const record of records) {
|
31
|
+
mst = await mst.add(record.path, record.cid)
|
32
|
+
}
|
33
|
+
const newBlocks = new BlockMap()
|
34
|
+
for await (const node of mst.walk()) {
|
35
|
+
if (node.isTree()) {
|
36
|
+
const pointer = await node.getPointer()
|
37
|
+
if (!existingCids.has(pointer)) {
|
38
|
+
const serialized = await node.serialize()
|
39
|
+
newBlocks.set(serialized.cid, serialized.bytes)
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
const mstCids = await mst.allCids()
|
44
|
+
const toDelete = new CidSet(existingCids.toList()).subtractSet(mstCids)
|
45
|
+
const newCommit = await signCommit(
|
46
|
+
{
|
47
|
+
did,
|
48
|
+
version: 3,
|
49
|
+
rev,
|
50
|
+
prev: null,
|
51
|
+
data: await mst.getPointer(),
|
52
|
+
},
|
53
|
+
store.repo.signingKey,
|
54
|
+
)
|
55
|
+
const commitCid = await newBlocks.add(newCommit)
|
56
|
+
|
57
|
+
console.log('Record count: ', records.length)
|
58
|
+
console.log('Existing blocks: ', existingCids.toList().length)
|
59
|
+
console.log('Deleting blocks:', toDelete.toList().length)
|
60
|
+
console.log('Adding blocks: ', newBlocks.size)
|
61
|
+
|
62
|
+
const shouldContinue = await promptContinue()
|
63
|
+
if (!shouldContinue) {
|
64
|
+
throw new Error('Aborted')
|
65
|
+
}
|
66
|
+
|
67
|
+
await store.repo.storage.deleteMany(toDelete.toList())
|
68
|
+
await store.repo.storage.putMany(newBlocks, rev)
|
69
|
+
await store.repo.storage.updateRoot(commitCid, rev)
|
70
|
+
return {
|
71
|
+
cid: commitCid,
|
72
|
+
rev,
|
73
|
+
since: null,
|
74
|
+
prev: null,
|
75
|
+
newBlocks,
|
76
|
+
removedCids: toDelete,
|
77
|
+
}
|
78
|
+
})
|
79
|
+
await ctx.accountManager.updateRepoRoot(did, commit.cid, rev)
|
80
|
+
await ctx.sequencer.sequenceCommit(did, commit, [])
|
81
|
+
}
|
82
|
+
|
83
|
+
const listExistingBlocks = async (
|
84
|
+
store: ActorStoreTransactor,
|
85
|
+
): Promise<CidSet> => {
|
86
|
+
const cids = new CidSet()
|
87
|
+
let cursor: string | undefined = ''
|
88
|
+
while (cursor !== undefined) {
|
89
|
+
const res = await store.db.db
|
90
|
+
.selectFrom('repo_block')
|
91
|
+
.select('cid')
|
92
|
+
.where('cid', '>', cursor)
|
93
|
+
.orderBy('cid', 'asc')
|
94
|
+
.limit(1000)
|
95
|
+
.execute()
|
96
|
+
for (const row of res) {
|
97
|
+
cids.add(CID.parse(row.cid))
|
98
|
+
}
|
99
|
+
cursor = res.at(-1)?.cid
|
100
|
+
}
|
101
|
+
return cids
|
102
|
+
}
|
103
|
+
|
104
|
+
const listAllRecords = async (
|
105
|
+
store: ActorStoreTransactor,
|
106
|
+
): Promise<RecordDescript[]> => {
|
107
|
+
const records: RecordDescript[] = []
|
108
|
+
let cursor: string | undefined = ''
|
109
|
+
while (cursor !== undefined) {
|
110
|
+
const res = await store.db.db
|
111
|
+
.selectFrom('record')
|
112
|
+
.select(['uri', 'cid'])
|
113
|
+
.where('uri', '>', cursor)
|
114
|
+
.orderBy('uri', 'asc')
|
115
|
+
.limit(1000)
|
116
|
+
.execute()
|
117
|
+
for (const row of res) {
|
118
|
+
const parsed = new AtUri(row.uri)
|
119
|
+
records.push({
|
120
|
+
uri: row.uri,
|
121
|
+
path: formatDataKey(parsed.collection, parsed.rkey),
|
122
|
+
cid: CID.parse(row.cid),
|
123
|
+
})
|
124
|
+
}
|
125
|
+
cursor = res.at(-1)?.uri
|
126
|
+
}
|
127
|
+
return records
|
128
|
+
}
|
129
|
+
|
130
|
+
const promptContinue = async (): Promise<boolean> => {
|
131
|
+
const rl = readline.createInterface({
|
132
|
+
input: process.stdin,
|
133
|
+
output: process.stdout,
|
134
|
+
})
|
135
|
+
const answer = await rl.question('Continue? y/n ')
|
136
|
+
return answer === ''
|
137
|
+
}
|
138
|
+
|
139
|
+
type RecordDescript = {
|
140
|
+
uri: string
|
141
|
+
path: string
|
142
|
+
cid: CID
|
143
|
+
}
|
package/src/sequencer/events.ts
CHANGED
@@ -10,6 +10,7 @@ import {
|
|
10
10
|
import { PreparedWrite } from '../repo'
|
11
11
|
import { CID } from 'multiformats/cid'
|
12
12
|
import { RepoSeqInsert } from './db'
|
13
|
+
import { AccountStatus } from '../account-manager'
|
13
14
|
|
14
15
|
export const formatSeqCommit = async (
|
15
16
|
did: string,
|
@@ -83,10 +84,14 @@ export const formatSeqHandleUpdate = async (
|
|
83
84
|
|
84
85
|
export const formatSeqIdentityEvt = async (
|
85
86
|
did: string,
|
87
|
+
handle?: string,
|
86
88
|
): Promise<RepoSeqInsert> => {
|
87
89
|
const evt: IdentityEvt = {
|
88
90
|
did,
|
89
91
|
}
|
92
|
+
if (handle) {
|
93
|
+
evt.handle = handle
|
94
|
+
}
|
90
95
|
return {
|
91
96
|
did,
|
92
97
|
eventType: 'identity',
|
@@ -95,6 +100,26 @@ export const formatSeqIdentityEvt = async (
|
|
95
100
|
}
|
96
101
|
}
|
97
102
|
|
103
|
+
export const formatSeqAccountEvt = async (
|
104
|
+
did: string,
|
105
|
+
status: AccountStatus,
|
106
|
+
): Promise<RepoSeqInsert> => {
|
107
|
+
const evt: AccountEvt = {
|
108
|
+
did,
|
109
|
+
active: status === 'active',
|
110
|
+
}
|
111
|
+
if (status !== AccountStatus.Active) {
|
112
|
+
evt.status = status
|
113
|
+
}
|
114
|
+
|
115
|
+
return {
|
116
|
+
did,
|
117
|
+
eventType: 'account',
|
118
|
+
event: cborEncode(evt),
|
119
|
+
sequencedAt: new Date().toISOString(),
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
98
123
|
export const formatSeqTombstone = async (
|
99
124
|
did: string,
|
100
125
|
): Promise<RepoSeqInsert> => {
|
@@ -142,9 +167,24 @@ export type HandleEvt = z.infer<typeof handleEvt>
|
|
142
167
|
|
143
168
|
export const identityEvt = z.object({
|
144
169
|
did: z.string(),
|
170
|
+
handle: z.string().optional(),
|
145
171
|
})
|
146
172
|
export type IdentityEvt = z.infer<typeof identityEvt>
|
147
173
|
|
174
|
+
export const accountEvt = z.object({
|
175
|
+
did: z.string(),
|
176
|
+
active: z.boolean(),
|
177
|
+
status: z
|
178
|
+
.enum([
|
179
|
+
AccountStatus.Takendown,
|
180
|
+
AccountStatus.Suspended,
|
181
|
+
AccountStatus.Deleted,
|
182
|
+
AccountStatus.Deactivated,
|
183
|
+
])
|
184
|
+
.optional(),
|
185
|
+
})
|
186
|
+
export type AccountEvt = z.infer<typeof accountEvt>
|
187
|
+
|
148
188
|
export const tombstoneEvt = z.object({
|
149
189
|
did: z.string(),
|
150
190
|
})
|
@@ -168,6 +208,12 @@ type TypedIdentityEvt = {
|
|
168
208
|
time: string
|
169
209
|
evt: IdentityEvt
|
170
210
|
}
|
211
|
+
type TypedAccountEvt = {
|
212
|
+
type: 'account'
|
213
|
+
seq: number
|
214
|
+
time: string
|
215
|
+
evt: AccountEvt
|
216
|
+
}
|
171
217
|
type TypedTombstoneEvt = {
|
172
218
|
type: 'tombstone'
|
173
219
|
seq: number
|
@@ -178,4 +224,5 @@ export type SeqEvt =
|
|
178
224
|
| TypedCommitEvt
|
179
225
|
| TypedHandleEvt
|
180
226
|
| TypedIdentityEvt
|
227
|
+
| TypedAccountEvt
|
181
228
|
| TypedTombstoneEvt
|
@@ -4,11 +4,13 @@ import { seqLogger as log } from '../logger'
|
|
4
4
|
import { SECOND, cborDecode, wait } from '@atproto/common'
|
5
5
|
import { CommitData } from '@atproto/repo'
|
6
6
|
import {
|
7
|
+
AccountEvt,
|
7
8
|
CommitEvt,
|
8
9
|
HandleEvt,
|
9
10
|
IdentityEvt,
|
10
11
|
SeqEvt,
|
11
12
|
TombstoneEvt,
|
13
|
+
formatSeqAccountEvt,
|
12
14
|
formatSeqCommit,
|
13
15
|
formatSeqHandleUpdate,
|
14
16
|
formatSeqIdentityEvt,
|
@@ -23,6 +25,7 @@ import {
|
|
23
25
|
} from './db'
|
24
26
|
import { PreparedWrite } from '../repo'
|
25
27
|
import { Crawlers } from '../crawlers'
|
28
|
+
import { AccountStatus } from '../account-manager/helpers/account'
|
26
29
|
|
27
30
|
export * from './events'
|
28
31
|
|
@@ -154,6 +157,13 @@ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) {
|
|
154
157
|
time: row.sequencedAt,
|
155
158
|
evt: evt as IdentityEvt,
|
156
159
|
})
|
160
|
+
} else if (row.eventType === 'account') {
|
161
|
+
seqEvts.push({
|
162
|
+
type: 'account',
|
163
|
+
seq: row.seq,
|
164
|
+
time: row.sequencedAt,
|
165
|
+
evt: evt as AccountEvt,
|
166
|
+
})
|
157
167
|
} else if (row.eventType === 'tombstone') {
|
158
168
|
seqEvts.push({
|
159
169
|
type: 'tombstone',
|
@@ -197,43 +207,54 @@ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) {
|
|
197
207
|
await wait(waitTime)
|
198
208
|
}
|
199
209
|
|
200
|
-
async sequenceEvt(evt: RepoSeqInsert) {
|
201
|
-
await this.db.executeWithRetry(
|
202
|
-
this.db.db.insertInto('repo_seq').values(evt),
|
210
|
+
async sequenceEvt(evt: RepoSeqInsert): Promise<number> {
|
211
|
+
const res = await this.db.executeWithRetry(
|
212
|
+
this.db.db.insertInto('repo_seq').values(evt).returningAll(),
|
203
213
|
)
|
204
214
|
this.crawlers.notifyOfUpdate()
|
215
|
+
return res[0].seq
|
205
216
|
}
|
206
217
|
|
207
218
|
async sequenceCommit(
|
208
219
|
did: string,
|
209
220
|
commitData: CommitData,
|
210
221
|
writes: PreparedWrite[],
|
211
|
-
) {
|
222
|
+
): Promise<number> {
|
212
223
|
const evt = await formatSeqCommit(did, commitData, writes)
|
213
|
-
await this.sequenceEvt(evt)
|
224
|
+
return await this.sequenceEvt(evt)
|
214
225
|
}
|
215
226
|
|
216
|
-
async sequenceHandleUpdate(did: string, handle: string) {
|
227
|
+
async sequenceHandleUpdate(did: string, handle: string): Promise<number> {
|
217
228
|
const evt = await formatSeqHandleUpdate(did, handle)
|
218
|
-
await this.sequenceEvt(evt)
|
229
|
+
return await this.sequenceEvt(evt)
|
230
|
+
}
|
231
|
+
|
232
|
+
async sequenceIdentityEvt(did: string, handle?: string): Promise<number> {
|
233
|
+
const evt = await formatSeqIdentityEvt(did, handle)
|
234
|
+
return await this.sequenceEvt(evt)
|
219
235
|
}
|
220
236
|
|
221
|
-
async
|
222
|
-
|
223
|
-
|
237
|
+
async sequenceAccountEvt(
|
238
|
+
did: string,
|
239
|
+
status: AccountStatus,
|
240
|
+
): Promise<number> {
|
241
|
+
const evt = await formatSeqAccountEvt(did, status)
|
242
|
+
return await this.sequenceEvt(evt)
|
224
243
|
}
|
225
244
|
|
226
|
-
async sequenceTombstone(did: string) {
|
245
|
+
async sequenceTombstone(did: string): Promise<number> {
|
227
246
|
const evt = await formatSeqTombstone(did)
|
228
|
-
await this.sequenceEvt(evt)
|
247
|
+
return await this.sequenceEvt(evt)
|
229
248
|
}
|
230
249
|
|
231
|
-
async deleteAllForUser(did: string) {
|
250
|
+
async deleteAllForUser(did: string, excludingSeqs: number[] = []) {
|
232
251
|
await this.db.executeWithRetry(
|
233
252
|
this.db.db
|
234
253
|
.deleteFrom('repo_seq')
|
235
254
|
.where('did', '=', did)
|
236
|
-
.
|
255
|
+
.if(excludingSeqs.length > 0, (qb) =>
|
256
|
+
qb.where('seq', 'not in', excludingSeqs),
|
257
|
+
),
|
237
258
|
)
|
238
259
|
}
|
239
260
|
}
|
@@ -1,5 +1,10 @@
|
|
1
1
|
import AtpAgent from '@atproto/api'
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
ImageRef,
|
4
|
+
SeedClient,
|
5
|
+
TestNetworkNoAppView,
|
6
|
+
basicSeed,
|
7
|
+
} from '@atproto/dev-env'
|
3
8
|
|
4
9
|
describe('account deactivation', () => {
|
5
10
|
let network: TestNetworkNoAppView
|
@@ -8,6 +13,7 @@ describe('account deactivation', () => {
|
|
8
13
|
let agent: AtpAgent
|
9
14
|
|
10
15
|
let alice: string
|
16
|
+
let aliceAvatar: ImageRef
|
11
17
|
|
12
18
|
beforeAll(async () => {
|
13
19
|
network = await TestNetworkNoAppView.create({
|
@@ -19,6 +25,16 @@ describe('account deactivation', () => {
|
|
19
25
|
|
20
26
|
await basicSeed(sc)
|
21
27
|
alice = sc.dids.alice
|
28
|
+
|
29
|
+
aliceAvatar = await sc.uploadFile(
|
30
|
+
alice,
|
31
|
+
'../dev-env/src/seed/img/key-portrait-small.jpg',
|
32
|
+
'image/jpeg',
|
33
|
+
)
|
34
|
+
await sc.updateProfile(alice, {
|
35
|
+
avatar: aliceAvatar.image,
|
36
|
+
})
|
37
|
+
|
22
38
|
await network.processAll()
|
23
39
|
})
|
24
40
|
|
@@ -33,16 +49,25 @@ describe('account deactivation', () => {
|
|
33
49
|
)
|
34
50
|
})
|
35
51
|
|
52
|
+
it('returns deactivated status', async () => {
|
53
|
+
const res = await agent.com.atproto.sync.getRepoStatus({ did: alice })
|
54
|
+
expect(res.data).toEqual({
|
55
|
+
did: alice,
|
56
|
+
active: false,
|
57
|
+
status: 'deactivated',
|
58
|
+
})
|
59
|
+
})
|
60
|
+
|
36
61
|
it('no longer serves repo data', async () => {
|
37
62
|
await expect(
|
38
63
|
agent.com.atproto.sync.getRepo({ did: alice }),
|
39
|
-
).rejects.toThrow()
|
64
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
40
65
|
await expect(
|
41
66
|
agent.com.atproto.sync.getLatestCommit({ did: alice }),
|
42
|
-
).rejects.toThrow()
|
67
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
43
68
|
await expect(
|
44
69
|
agent.com.atproto.sync.listBlobs({ did: alice }),
|
45
|
-
).rejects.toThrow()
|
70
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
46
71
|
const recordUri = sc.posts[alice][0].ref.uri
|
47
72
|
await expect(
|
48
73
|
agent.com.atproto.sync.getRecord({
|
@@ -50,7 +75,7 @@ describe('account deactivation', () => {
|
|
50
75
|
collection: recordUri.collection,
|
51
76
|
rkey: recordUri.rkey,
|
52
77
|
}),
|
53
|
-
).rejects.toThrow()
|
78
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
54
79
|
await expect(
|
55
80
|
agent.com.atproto.repo.getRecord({
|
56
81
|
repo: alice,
|
@@ -62,17 +87,18 @@ describe('account deactivation', () => {
|
|
62
87
|
agent.com.atproto.repo.describeRepo({
|
63
88
|
repo: alice,
|
64
89
|
}),
|
65
|
-
).rejects.toThrow()
|
90
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
66
91
|
|
67
|
-
const blobCid = sc.profiles[alice].avatar.cid
|
68
92
|
await expect(
|
69
93
|
agent.com.atproto.sync.getBlob({
|
70
94
|
did: alice,
|
71
|
-
cid:
|
95
|
+
cid: aliceAvatar.image.ref.toString(),
|
72
96
|
}),
|
73
|
-
).rejects.toThrow()
|
97
|
+
).rejects.toThrow(/Repo has been deactivated/)
|
74
98
|
const listedRepos = await agent.com.atproto.sync.listRepos()
|
75
|
-
|
99
|
+
const listedAlice = listedRepos.data.repos.find((r) => r.did === alice)
|
100
|
+
expect(listedAlice?.active).toBe(false)
|
101
|
+
expect(listedAlice?.status).toBe('deactivated')
|
76
102
|
})
|
77
103
|
|
78
104
|
it('no longer resolves handle', async () => {
|
@@ -143,10 +143,18 @@ describe('account deletion', () => {
|
|
143
143
|
expect(updatedDbContents.userAccounts).toEqual(
|
144
144
|
initialDbContents.userAccounts.filter((row) => row.did !== carol.did),
|
145
145
|
)
|
146
|
-
// check
|
146
|
+
// check we didn't touch other user seqs
|
147
147
|
expect(
|
148
|
-
updatedDbContents.repoSeqs.filter((row) => row.
|
148
|
+
updatedDbContents.repoSeqs.filter((row) => row.did !== carol.did),
|
149
149
|
).toEqual(initialDbContents.repoSeqs.filter((row) => row.did !== carol.did))
|
150
|
+
// check all seqs for this did are gone, except for the tombstone & account events
|
151
|
+
expect(
|
152
|
+
updatedDbContents.repoSeqs
|
153
|
+
.filter((row) => row.did === carol.did)
|
154
|
+
.every(
|
155
|
+
(row) => row.eventType === 'tombstone' || row.eventType === 'account',
|
156
|
+
),
|
157
|
+
).toBe(true)
|
150
158
|
// check we do have a tombstone for this did
|
151
159
|
expect(
|
152
160
|
updatedDbContents.repoSeqs.filter(
|
package/tests/moderation.test.ts
CHANGED
@@ -238,12 +238,12 @@ describe('moderation', () => {
|
|
238
238
|
}
|
239
239
|
// public, disallow
|
240
240
|
const attempt1 = agent.api.com.atproto.sync.getBlob(blobParams)
|
241
|
-
await expect(attempt1).rejects.toThrow(
|
241
|
+
await expect(attempt1).rejects.toThrow(/Repo has been takendown/)
|
242
242
|
// logged-in, disallow
|
243
243
|
const attempt2 = agent.api.com.atproto.sync.getBlob(blobParams, {
|
244
244
|
headers: sc.getHeaders(sc.dids.bob),
|
245
245
|
})
|
246
|
-
await expect(attempt2).rejects.toThrow(
|
246
|
+
await expect(attempt2).rejects.toThrow(/Repo has been takendown/)
|
247
247
|
// logged-in as account, allow
|
248
248
|
const res1 = await agent.api.com.atproto.sync.getBlob(blobParams, {
|
249
249
|
headers: sc.getHeaders(sc.dids.carol),
|
package/tests/sequencer.test.ts
CHANGED