@atproto/bsky 0.0.11 → 0.0.12
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 +13 -0
- 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 +1381 -530
- package/dist/index.js.map +3 -3
- package/dist/lexicon/index.d.ts +8 -0
- package/dist/lexicon/lexicons.d.ts +231 -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 +2 -0
- 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 +30 -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 +13 -14
- 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 +246 -4
- 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 +2 -0
- 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 +44 -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/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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { SeedClient, TestNetwork } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
3
|
+
import { AtUri } from '@atproto/syntax'
|
|
4
|
+
import {
|
|
5
|
+
ACKNOWLEDGE,
|
|
6
|
+
TAKEDOWN,
|
|
7
|
+
} from '@atproto/api/src/client/types/com/atproto/admin/defs'
|
|
8
|
+
import {
|
|
9
|
+
REASONOTHER,
|
|
10
|
+
REASONSPAM,
|
|
11
|
+
} from '../../src/lexicon/types/com/atproto/moderation/defs'
|
|
12
|
+
import { forSnapshot } from '../_util'
|
|
13
|
+
import basicSeed from '../seeds/basic'
|
|
14
|
+
|
|
15
|
+
describe('admin get record view', () => {
|
|
16
|
+
let network: TestNetwork
|
|
17
|
+
let agent: AtpAgent
|
|
18
|
+
let sc: SeedClient
|
|
19
|
+
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
network = await TestNetwork.create({
|
|
22
|
+
dbPostgresSchema: 'views_admin_get_record',
|
|
23
|
+
})
|
|
24
|
+
agent = network.pds.getClient()
|
|
25
|
+
sc = network.getSeedClient()
|
|
26
|
+
await basicSeed(sc)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
await network.close()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
beforeAll(async () => {
|
|
34
|
+
const acknowledge = await sc.takeModerationAction({
|
|
35
|
+
action: ACKNOWLEDGE,
|
|
36
|
+
subject: {
|
|
37
|
+
$type: 'com.atproto.repo.strongRef',
|
|
38
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
39
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
await sc.createReport({
|
|
43
|
+
reportedBy: sc.dids.bob,
|
|
44
|
+
reasonType: REASONSPAM,
|
|
45
|
+
subject: {
|
|
46
|
+
$type: 'com.atproto.repo.strongRef',
|
|
47
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
48
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
await sc.createReport({
|
|
52
|
+
reportedBy: sc.dids.carol,
|
|
53
|
+
reasonType: REASONOTHER,
|
|
54
|
+
reason: 'defamation',
|
|
55
|
+
subject: {
|
|
56
|
+
$type: 'com.atproto.repo.strongRef',
|
|
57
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
58
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
await sc.reverseModerationAction({ id: acknowledge.id })
|
|
62
|
+
await sc.takeModerationAction({
|
|
63
|
+
action: TAKEDOWN,
|
|
64
|
+
subject: {
|
|
65
|
+
$type: 'com.atproto.repo.strongRef',
|
|
66
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
67
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('gets a record by uri, even when taken down.', async () => {
|
|
73
|
+
const result = await agent.api.com.atproto.admin.getRecord(
|
|
74
|
+
{ uri: sc.posts[sc.dids.alice][0].ref.uriStr },
|
|
75
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
76
|
+
)
|
|
77
|
+
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('gets a record by uri and cid.', async () => {
|
|
81
|
+
const result = await agent.api.com.atproto.admin.getRecord(
|
|
82
|
+
{
|
|
83
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
84
|
+
cid: sc.posts[sc.dids.alice][0].ref.cidStr,
|
|
85
|
+
},
|
|
86
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
87
|
+
)
|
|
88
|
+
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('fails when record does not exist.', async () => {
|
|
92
|
+
const promise = agent.api.com.atproto.admin.getRecord(
|
|
93
|
+
{
|
|
94
|
+
uri: AtUri.make(
|
|
95
|
+
sc.dids.alice,
|
|
96
|
+
'app.bsky.feed.post',
|
|
97
|
+
'badrkey',
|
|
98
|
+
).toString(),
|
|
99
|
+
},
|
|
100
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
101
|
+
)
|
|
102
|
+
await expect(promise).rejects.toThrow('Record not found')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('fails when record cid does not exist.', async () => {
|
|
106
|
+
const promise = agent.api.com.atproto.admin.getRecord(
|
|
107
|
+
{
|
|
108
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
109
|
+
cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid
|
|
110
|
+
},
|
|
111
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
112
|
+
)
|
|
113
|
+
await expect(promise).rejects.toThrow('Record not found')
|
|
114
|
+
})
|
|
115
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { SeedClient, TestNetwork } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
3
|
+
import {
|
|
4
|
+
ACKNOWLEDGE,
|
|
5
|
+
TAKEDOWN,
|
|
6
|
+
} from '@atproto/api/src/client/types/com/atproto/admin/defs'
|
|
7
|
+
import {
|
|
8
|
+
REASONOTHER,
|
|
9
|
+
REASONSPAM,
|
|
10
|
+
} from '../../src/lexicon/types/com/atproto/moderation/defs'
|
|
11
|
+
import { forSnapshot } from '../_util'
|
|
12
|
+
import basicSeed from '../seeds/basic'
|
|
13
|
+
|
|
14
|
+
describe('admin get repo view', () => {
|
|
15
|
+
let network: TestNetwork
|
|
16
|
+
let agent: AtpAgent
|
|
17
|
+
let sc: SeedClient
|
|
18
|
+
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
network = await TestNetwork.create({
|
|
21
|
+
dbPostgresSchema: 'views_admin_get_repo',
|
|
22
|
+
})
|
|
23
|
+
agent = network.pds.getClient()
|
|
24
|
+
sc = network.getSeedClient()
|
|
25
|
+
await basicSeed(sc)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
await network.close()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
const acknowledge = await sc.takeModerationAction({
|
|
34
|
+
action: ACKNOWLEDGE,
|
|
35
|
+
subject: {
|
|
36
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
37
|
+
did: sc.dids.alice,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
await sc.createReport({
|
|
41
|
+
reportedBy: sc.dids.bob,
|
|
42
|
+
reasonType: REASONSPAM,
|
|
43
|
+
subject: {
|
|
44
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
45
|
+
did: sc.dids.alice,
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
await sc.createReport({
|
|
49
|
+
reportedBy: sc.dids.carol,
|
|
50
|
+
reasonType: REASONOTHER,
|
|
51
|
+
reason: 'defamation',
|
|
52
|
+
subject: {
|
|
53
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
54
|
+
did: sc.dids.alice,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
await sc.reverseModerationAction({ id: acknowledge.id })
|
|
58
|
+
await sc.takeModerationAction({
|
|
59
|
+
action: TAKEDOWN,
|
|
60
|
+
subject: {
|
|
61
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
62
|
+
did: sc.dids.alice,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('gets a repo by did, even when taken down.', async () => {
|
|
68
|
+
const result = await agent.api.com.atproto.admin.getRepo(
|
|
69
|
+
{ did: sc.dids.alice },
|
|
70
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
71
|
+
)
|
|
72
|
+
expect(forSnapshot(result.data)).toMatchSnapshot()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('does not include account emails for triage mods.', async () => {
|
|
76
|
+
const { data: admin } = await agent.api.com.atproto.admin.getRepo(
|
|
77
|
+
{ did: sc.dids.bob },
|
|
78
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
79
|
+
)
|
|
80
|
+
const { data: moderator } = await agent.api.com.atproto.admin.getRepo(
|
|
81
|
+
{ did: sc.dids.bob },
|
|
82
|
+
{ headers: network.pds.adminAuthHeaders('moderator') },
|
|
83
|
+
)
|
|
84
|
+
const { data: triage } = await agent.api.com.atproto.admin.getRepo(
|
|
85
|
+
{ did: sc.dids.bob },
|
|
86
|
+
{ headers: network.pds.adminAuthHeaders('triage') },
|
|
87
|
+
)
|
|
88
|
+
expect(admin.email).toEqual('bob@test.com')
|
|
89
|
+
expect(moderator.email).toEqual('bob@test.com')
|
|
90
|
+
expect(triage.email).toBeUndefined()
|
|
91
|
+
expect(triage).toEqual({ ...admin, email: undefined })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('fails when repo does not exist.', async () => {
|
|
95
|
+
const promise = agent.api.com.atproto.admin.getRepo(
|
|
96
|
+
{ did: 'did:plc:doesnotexist' },
|
|
97
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
98
|
+
)
|
|
99
|
+
await expect(promise).rejects.toThrow('Repo not found')
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
1
|
+
import { TestNetwork, ImageRef, RecordRef, SeedClient } from '@atproto/dev-env'
|
|
2
2
|
import { TID, cidForCbor } from '@atproto/common'
|
|
3
3
|
import AtpAgent, { ComAtprotoAdminTakeModerationAction } from '@atproto/api'
|
|
4
4
|
import { AtUri } from '@atproto/syntax'
|
|
5
|
-
import { forSnapshot } from '
|
|
6
|
-
import
|
|
7
|
-
import basicSeed from './seeds/basic'
|
|
5
|
+
import { forSnapshot } from '../_util'
|
|
6
|
+
import basicSeed from '../seeds/basic'
|
|
8
7
|
import {
|
|
9
8
|
ACKNOWLEDGE,
|
|
10
9
|
ESCALATE,
|
|
11
10
|
FLAG,
|
|
12
11
|
TAKEDOWN,
|
|
13
|
-
} from '
|
|
12
|
+
} from '../../src/lexicon/types/com/atproto/admin/defs'
|
|
14
13
|
import {
|
|
15
14
|
REASONOTHER,
|
|
16
15
|
REASONSPAM,
|
|
17
|
-
} from '
|
|
18
|
-
import { PeriodicModerationActionReversal } from '
|
|
16
|
+
} from '../../src/lexicon/types/com/atproto/moderation/defs'
|
|
17
|
+
import { PeriodicModerationActionReversal } from '../../src'
|
|
19
18
|
|
|
20
19
|
describe('moderation', () => {
|
|
21
20
|
let network: TestNetwork
|
|
22
21
|
let agent: AtpAgent
|
|
22
|
+
let pdsAgent: AtpAgent
|
|
23
23
|
let sc: SeedClient
|
|
24
24
|
|
|
25
25
|
beforeAll(async () => {
|
|
@@ -27,8 +27,8 @@ describe('moderation', () => {
|
|
|
27
27
|
dbPostgresSchema: 'bsky_moderation',
|
|
28
28
|
})
|
|
29
29
|
agent = network.bsky.getClient()
|
|
30
|
-
|
|
31
|
-
sc =
|
|
30
|
+
pdsAgent = network.pds.getClient()
|
|
31
|
+
sc = network.getSeedClient()
|
|
32
32
|
await basicSeed(sc)
|
|
33
33
|
await network.processAll()
|
|
34
34
|
})
|
|
@@ -962,6 +962,82 @@ describe('moderation', () => {
|
|
|
962
962
|
)
|
|
963
963
|
})
|
|
964
964
|
|
|
965
|
+
it('fans out repo takedowns to pds', async () => {
|
|
966
|
+
const { data: action } =
|
|
967
|
+
await agent.api.com.atproto.admin.takeModerationAction(
|
|
968
|
+
{
|
|
969
|
+
action: TAKEDOWN,
|
|
970
|
+
createdBy: 'did:example:moderator',
|
|
971
|
+
reason: 'Y',
|
|
972
|
+
subject: {
|
|
973
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
974
|
+
did: sc.dids.bob,
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
encoding: 'application/json',
|
|
979
|
+
headers: network.bsky.adminAuthHeaders(),
|
|
980
|
+
},
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
984
|
+
{
|
|
985
|
+
did: sc.dids.bob,
|
|
986
|
+
},
|
|
987
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
988
|
+
)
|
|
989
|
+
expect(res1.data.takedown?.applied).toBe(true)
|
|
990
|
+
|
|
991
|
+
// cleanup
|
|
992
|
+
await reverse(action.id)
|
|
993
|
+
|
|
994
|
+
const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
995
|
+
{
|
|
996
|
+
did: sc.dids.bob,
|
|
997
|
+
},
|
|
998
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
999
|
+
)
|
|
1000
|
+
expect(res2.data.takedown?.applied).toBe(false)
|
|
1001
|
+
})
|
|
1002
|
+
|
|
1003
|
+
it('fans out record takedowns to pds', async () => {
|
|
1004
|
+
const post = sc.posts[sc.dids.bob][0]
|
|
1005
|
+
const uri = post.ref.uriStr
|
|
1006
|
+
const cid = post.ref.cidStr
|
|
1007
|
+
const { data: action } =
|
|
1008
|
+
await agent.api.com.atproto.admin.takeModerationAction(
|
|
1009
|
+
{
|
|
1010
|
+
action: TAKEDOWN,
|
|
1011
|
+
createdBy: 'did:example:moderator',
|
|
1012
|
+
reason: 'Y',
|
|
1013
|
+
subject: {
|
|
1014
|
+
$type: 'com.atproto.repo.strongRef',
|
|
1015
|
+
uri,
|
|
1016
|
+
cid,
|
|
1017
|
+
},
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
encoding: 'application/json',
|
|
1021
|
+
headers: network.bsky.adminAuthHeaders(),
|
|
1022
|
+
},
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
1026
|
+
{ uri },
|
|
1027
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
1028
|
+
)
|
|
1029
|
+
expect(res1.data.takedown?.applied).toBe(true)
|
|
1030
|
+
|
|
1031
|
+
// cleanup
|
|
1032
|
+
await reverse(action.id)
|
|
1033
|
+
|
|
1034
|
+
const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
1035
|
+
{ uri },
|
|
1036
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
1037
|
+
)
|
|
1038
|
+
expect(res2.data.takedown?.applied).toBe(false)
|
|
1039
|
+
})
|
|
1040
|
+
|
|
965
1041
|
it('allows full moderators to takedown.', async () => {
|
|
966
1042
|
const { data: action } =
|
|
967
1043
|
await agent.api.com.atproto.admin.takeModerationAction(
|
|
@@ -1161,6 +1237,17 @@ describe('moderation', () => {
|
|
|
1161
1237
|
expect(await fetchImage.json()).toEqual({ message: 'Image not found' })
|
|
1162
1238
|
})
|
|
1163
1239
|
|
|
1240
|
+
it('fans takedown out to pds', async () => {
|
|
1241
|
+
const res = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
1242
|
+
{
|
|
1243
|
+
did: sc.dids.carol,
|
|
1244
|
+
blob: blob.image.ref.toString(),
|
|
1245
|
+
},
|
|
1246
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
1247
|
+
)
|
|
1248
|
+
expect(res.data.takedown?.applied).toBe(true)
|
|
1249
|
+
})
|
|
1250
|
+
|
|
1164
1251
|
it('restores blob when action is reversed.', async () => {
|
|
1165
1252
|
await agent.api.com.atproto.admin.reverseModerationAction(
|
|
1166
1253
|
{
|
|
@@ -1185,5 +1272,16 @@ describe('moderation', () => {
|
|
|
1185
1272
|
const size = Number(fetchImage.headers.get('content-length'))
|
|
1186
1273
|
expect(size).toBeGreaterThan(9000)
|
|
1187
1274
|
})
|
|
1275
|
+
|
|
1276
|
+
it('fans reversal out to pds', async () => {
|
|
1277
|
+
const res = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
|
|
1278
|
+
{
|
|
1279
|
+
did: sc.dids.carol,
|
|
1280
|
+
blob: blob.image.ref.toString(),
|
|
1281
|
+
},
|
|
1282
|
+
{ headers: network.pds.adminAuthHeaders() },
|
|
1283
|
+
)
|
|
1284
|
+
expect(res.data.takedown?.applied).toBe(false)
|
|
1285
|
+
})
|
|
1188
1286
|
})
|
|
1189
1287
|
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { SeedClient, TestNetwork } from '@atproto/dev-env'
|
|
2
|
+
import AtpAgent from '@atproto/api'
|
|
3
|
+
import { TAKEDOWN } from '@atproto/api/src/client/types/com/atproto/admin/defs'
|
|
4
|
+
import { paginateAll } from '../_util'
|
|
5
|
+
import usersBulkSeed from '../seeds/users-bulk'
|
|
6
|
+
|
|
7
|
+
describe('admin repo search view', () => {
|
|
8
|
+
let network: TestNetwork
|
|
9
|
+
let agent: AtpAgent
|
|
10
|
+
let sc: SeedClient
|
|
11
|
+
let headers: { [s: string]: string }
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
network = await TestNetwork.create({
|
|
15
|
+
dbPostgresSchema: 'views_admin_repo_search',
|
|
16
|
+
})
|
|
17
|
+
agent = network.pds.getClient()
|
|
18
|
+
sc = network.getSeedClient()
|
|
19
|
+
await usersBulkSeed(sc)
|
|
20
|
+
headers = network.pds.adminAuthHeaders()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterAll(async () => {
|
|
24
|
+
await network.close()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
await sc.takeModerationAction({
|
|
29
|
+
action: TAKEDOWN,
|
|
30
|
+
subject: {
|
|
31
|
+
$type: 'com.atproto.admin.defs#repoRef',
|
|
32
|
+
did: sc.dids['cara-wiegand69.test'],
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('gives relevant results', async () => {
|
|
38
|
+
const result = await agent.api.com.atproto.admin.searchRepos(
|
|
39
|
+
{ term: 'car' },
|
|
40
|
+
{ headers },
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const handles = result.data.repos.map((u) => u.handle)
|
|
44
|
+
|
|
45
|
+
const shouldContain = [
|
|
46
|
+
'cara-wiegand69.test', // Present despite repo takedown
|
|
47
|
+
'carlos6.test',
|
|
48
|
+
'carolina-mcdermott77.test',
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
shouldContain.forEach((handle) => expect(handles).toContain(handle))
|
|
52
|
+
|
|
53
|
+
const shouldNotContain = [
|
|
54
|
+
'sven70.test',
|
|
55
|
+
'hilario84.test',
|
|
56
|
+
'santa-hermann78.test',
|
|
57
|
+
'dylan61.test',
|
|
58
|
+
'preston-harris.test',
|
|
59
|
+
'loyce95.test',
|
|
60
|
+
'melyna-zboncak.test',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
shouldNotContain.forEach((handle) => expect(handles).not.toContain(handle))
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('finds repo by did', async () => {
|
|
67
|
+
const term = sc.dids['cara-wiegand69.test']
|
|
68
|
+
const res = await agent.api.com.atproto.admin.searchRepos(
|
|
69
|
+
{ term },
|
|
70
|
+
{ headers },
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
expect(res.data.repos.length).toEqual(1)
|
|
74
|
+
expect(res.data.repos[0].did).toEqual(term)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('paginates with term', async () => {
|
|
78
|
+
const results = (results) => results.flatMap((res) => res.users)
|
|
79
|
+
const paginator = async (cursor?: string) => {
|
|
80
|
+
const res = await agent.api.com.atproto.admin.searchRepos(
|
|
81
|
+
{ term: 'p', cursor, limit: 3 },
|
|
82
|
+
{ headers },
|
|
83
|
+
)
|
|
84
|
+
return res.data
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const paginatedAll = await paginateAll(paginator)
|
|
88
|
+
paginatedAll.forEach((res) =>
|
|
89
|
+
expect(res.repos.length).toBeLessThanOrEqual(3),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const full = await agent.api.com.atproto.admin.searchRepos(
|
|
93
|
+
{ term: 'p' },
|
|
94
|
+
{ headers },
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
expect(full.data.repos.length).toBeGreaterThan(3)
|
|
98
|
+
expect(results(paginatedAll)).toEqual(results([full.data]))
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('paginates without term', async () => {
|
|
102
|
+
const results = (results) => results.flatMap((res) => res.repos)
|
|
103
|
+
const paginator = async (cursor?: string) => {
|
|
104
|
+
const res = await agent.api.com.atproto.admin.searchRepos(
|
|
105
|
+
{ cursor, limit: 3 },
|
|
106
|
+
{ headers },
|
|
107
|
+
)
|
|
108
|
+
return res.data
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const paginatedAll = await paginateAll(paginator, 5)
|
|
112
|
+
paginatedAll.forEach((res) =>
|
|
113
|
+
expect(res.repos.length).toBeLessThanOrEqual(3),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const full = await agent.api.com.atproto.admin.searchRepos(
|
|
117
|
+
{ limit: 15 },
|
|
118
|
+
{ headers },
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
expect(full.data.repos.length).toEqual(15)
|
|
122
|
+
expect(results(paginatedAll)).toEqual(results([full.data]))
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import AtpAgent, { AtUri } from '@atproto/api'
|
|
2
|
-
import { SeedClient } from '
|
|
2
|
+
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
3
3
|
import basicSeed from '../seeds/basic'
|
|
4
4
|
import { makeAlgos } from '../../src'
|
|
5
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
6
5
|
|
|
7
6
|
describe('algo hot-classic', () => {
|
|
8
7
|
let network: TestNetwork
|
|
@@ -26,8 +25,7 @@ describe('algo hot-classic', () => {
|
|
|
26
25
|
bsky: { algos: makeAlgos(feedPublisherDid) },
|
|
27
26
|
})
|
|
28
27
|
agent = new AtpAgent({ service: network.bsky.url })
|
|
29
|
-
|
|
30
|
-
sc = new SeedClient(pdsAgent)
|
|
28
|
+
sc = network.getSeedClient()
|
|
31
29
|
await basicSeed(sc)
|
|
32
30
|
|
|
33
31
|
alice = sc.dids.alice
|
|
@@ -43,7 +41,7 @@ describe('algo hot-classic', () => {
|
|
|
43
41
|
it('returns well liked posts', async () => {
|
|
44
42
|
const img = await sc.uploadFile(
|
|
45
43
|
alice,
|
|
46
|
-
'tests/
|
|
44
|
+
'tests/sample-img/key-landscape-small.jpg',
|
|
47
45
|
'image/jpeg',
|
|
48
46
|
)
|
|
49
47
|
const one = await sc.post(alice, 'first post', undefined, [img])
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { HOUR } from '@atproto/common'
|
|
2
2
|
import AtpAgent, { AtUri } from '@atproto/api'
|
|
3
|
-
import { SeedClient } from '
|
|
3
|
+
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
4
4
|
import basicSeed from '../seeds/basic'
|
|
5
5
|
import { makeAlgos } from '../../src'
|
|
6
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
7
6
|
|
|
8
7
|
describe.skip('algo whats-hot', () => {
|
|
9
8
|
let network: TestNetwork
|
|
@@ -28,8 +27,7 @@ describe.skip('algo whats-hot', () => {
|
|
|
28
27
|
bsky: { algos: makeAlgos(feedPublisherDid) },
|
|
29
28
|
})
|
|
30
29
|
agent = new AtpAgent({ service: network.bsky.url })
|
|
31
|
-
|
|
32
|
-
sc = new SeedClient(pdsAgent)
|
|
30
|
+
sc = network.getSeedClient()
|
|
33
31
|
await basicSeed(sc)
|
|
34
32
|
|
|
35
33
|
alice = sc.dids.alice
|
|
@@ -46,7 +44,7 @@ describe.skip('algo whats-hot', () => {
|
|
|
46
44
|
it('returns well liked posts', async () => {
|
|
47
45
|
const img = await sc.uploadFile(
|
|
48
46
|
alice,
|
|
49
|
-
'tests/
|
|
47
|
+
'tests/sample-img/key-landscape-small.jpg',
|
|
50
48
|
'image/jpeg',
|
|
51
49
|
)
|
|
52
50
|
const one = await sc.post(carol, 'carol is in the chat')
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import AtpAgent, { AtUri } from '@atproto/api'
|
|
2
|
-
import { RecordRef, SeedClient } from '../seeds/client'
|
|
3
2
|
import userSeed from '../seeds/users'
|
|
4
3
|
import { makeAlgos } from '../../src'
|
|
5
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
4
|
+
import { TestNetwork, SeedClient, RecordRef } from '@atproto/dev-env'
|
|
6
5
|
|
|
7
6
|
describe.skip('algo with friends', () => {
|
|
8
7
|
let network: TestNetwork
|
|
@@ -28,8 +27,7 @@ describe.skip('algo with friends', () => {
|
|
|
28
27
|
bsky: { algos: makeAlgos(feedPublisherDid) },
|
|
29
28
|
})
|
|
30
29
|
agent = new AtpAgent({ service: network.bsky.url })
|
|
31
|
-
|
|
32
|
-
sc = new SeedClient(pdsAgent)
|
|
30
|
+
sc = network.getSeedClient()
|
|
33
31
|
await userSeed(sc)
|
|
34
32
|
|
|
35
33
|
alice = sc.dids.alice
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import AtpAgent from '@atproto/api'
|
|
2
|
+
import { SeedClient, TestNetwork } from '@atproto/dev-env'
|
|
3
|
+
import usersSeed from './seeds/users'
|
|
4
|
+
import { createServiceJwt } from '@atproto/xrpc-server'
|
|
5
|
+
import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
|
|
6
|
+
|
|
7
|
+
describe('auth', () => {
|
|
8
|
+
let network: TestNetwork
|
|
9
|
+
let agent: AtpAgent
|
|
10
|
+
let sc: SeedClient
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
network = await TestNetwork.create({
|
|
14
|
+
dbPostgresSchema: 'bsky_auth',
|
|
15
|
+
})
|
|
16
|
+
agent = network.bsky.getClient()
|
|
17
|
+
sc = network.getSeedClient()
|
|
18
|
+
await usersSeed(sc)
|
|
19
|
+
await network.processAll()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
await network.close()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('handles signing key change for service auth.', async () => {
|
|
27
|
+
const issuer = sc.dids.alice
|
|
28
|
+
const attemptWithKey = async (keypair: Keypair) => {
|
|
29
|
+
const jwt = await createServiceJwt({
|
|
30
|
+
iss: issuer,
|
|
31
|
+
aud: network.bsky.ctx.cfg.serverDid,
|
|
32
|
+
keypair,
|
|
33
|
+
})
|
|
34
|
+
return agent.api.app.bsky.actor.getProfile(
|
|
35
|
+
{ actor: sc.dids.carol },
|
|
36
|
+
{ headers: { authorization: `Bearer ${jwt}` } },
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
const origSigningKey = network.pds.ctx.repoSigningKey
|
|
40
|
+
const newSigningKey = await Secp256k1Keypair.create({ exportable: true })
|
|
41
|
+
// confirm original signing key works
|
|
42
|
+
await expect(attemptWithKey(origSigningKey)).resolves.toBeDefined()
|
|
43
|
+
// confirm next signing key doesn't work yet
|
|
44
|
+
await expect(attemptWithKey(newSigningKey)).rejects.toThrow(
|
|
45
|
+
'jwt signature does not match jwt issuer',
|
|
46
|
+
)
|
|
47
|
+
// update to new signing key
|
|
48
|
+
await network.plc
|
|
49
|
+
.getClient()
|
|
50
|
+
.updateAtprotoKey(
|
|
51
|
+
issuer,
|
|
52
|
+
network.pds.ctx.plcRotationKey,
|
|
53
|
+
newSigningKey.did(),
|
|
54
|
+
)
|
|
55
|
+
// old signing key still works due to did doc cache
|
|
56
|
+
await expect(attemptWithKey(origSigningKey)).resolves.toBeDefined()
|
|
57
|
+
// new signing key works
|
|
58
|
+
await expect(attemptWithKey(newSigningKey)).resolves.toBeDefined()
|
|
59
|
+
// old signing key no longer works after cache is updated
|
|
60
|
+
await expect(attemptWithKey(origSigningKey)).rejects.toThrow(
|
|
61
|
+
'jwt signature does not match jwt issuer',
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
1
2
|
import { FuzzyMatcher, encode } from '../../src/auto-moderator/fuzzy-matcher'
|
|
2
|
-
import { TestNetwork } from '@atproto/dev-env'
|
|
3
|
-
import { SeedClient } from '../seeds/client'
|
|
4
3
|
import basicSeed from '../seeds/basic'
|
|
5
4
|
import { AtpAgent } from '@atproto/api'
|
|
6
5
|
import { ImageInvalidator } from '../../src/image/invalidator'
|
|
@@ -25,7 +24,7 @@ describe('fuzzy matcher', () => {
|
|
|
25
24
|
})
|
|
26
25
|
fuzzyMatcher = new FuzzyMatcher(['evil', 'mean', 'bad'], ['baddie'])
|
|
27
26
|
agent = network.pds.getClient()
|
|
28
|
-
sc =
|
|
27
|
+
sc = network.getSeedClient()
|
|
29
28
|
await basicSeed(sc)
|
|
30
29
|
await network.processAll()
|
|
31
30
|
alice = sc.dids.alice
|