@atproto/bsky 0.0.15 → 0.0.17
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 +18 -0
- package/dist/api/com/atproto/moderation/util.d.ts +4 -3
- package/dist/cache/read-through.d.ts +30 -0
- package/dist/config.d.ts +18 -0
- package/dist/context.d.ts +21 -6
- package/dist/daemon/config.d.ts +15 -0
- package/dist/daemon/context.d.ts +15 -0
- package/dist/daemon/index.d.ts +23 -0
- package/dist/daemon/logger.d.ts +3 -0
- package/dist/daemon/notifications.d.ts +18 -0
- package/dist/daemon/services.d.ts +11 -0
- package/dist/db/database-schema.d.ts +1 -2
- package/dist/db/index.js +41 -1
- package/dist/db/index.js.map +3 -3
- package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
- package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/pagination.d.ts +2 -1
- package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
- package/dist/db/tables/moderation.d.ts +24 -34
- package/dist/did-cache.d.ts +10 -7
- package/dist/feed-gen/types.d.ts +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4370 -2758
- package/dist/index.js.map +3 -3
- package/dist/indexer/context.d.ts +2 -0
- package/dist/indexer/index.d.ts +1 -0
- package/dist/lexicon/index.d.ts +23 -18
- package/dist/lexicon/lexicons.d.ts +561 -412
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/admin/defs.d.ts +114 -48
- package/dist/lexicon/types/com/atproto/admin/{resolveModerationReports.d.ts → deleteAccount.d.ts} +2 -13
- package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
- package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
- package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
- package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/{admin/getModerationReport.d.ts → temp/importRepo.d.ts} +10 -7
- package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
- package/dist/lexicon/types/com/atproto/{admin/reverseModerationAction.d.ts → temp/transferAccount.d.ts} +11 -5
- package/dist/logger.d.ts +1 -0
- package/dist/migrate-moderation-data.d.ts +1 -0
- package/dist/redis.d.ts +10 -1
- package/dist/services/actor/index.d.ts +18 -4
- package/dist/services/actor/views.d.ts +6 -8
- package/dist/services/feed/index.d.ts +7 -4
- package/dist/services/feed/util.d.ts +9 -1
- package/dist/services/feed/views.d.ts +11 -21
- package/dist/services/graph/index.d.ts +5 -29
- package/dist/services/graph/types.d.ts +1 -0
- package/dist/services/index.d.ts +3 -7
- package/dist/services/label/index.d.ts +10 -4
- package/dist/services/moderation/index.d.ts +134 -72
- package/dist/services/moderation/pagination.d.ts +36 -0
- package/dist/services/moderation/status.d.ts +13 -0
- package/dist/services/moderation/types.d.ts +35 -0
- package/dist/services/moderation/views.d.ts +18 -14
- package/dist/services/types.d.ts +3 -0
- package/dist/services/util/notification.d.ts +5 -0
- package/dist/services/util/post.d.ts +6 -6
- package/dist/util/debug.d.ts +1 -1
- package/dist/util/retry.d.ts +1 -6
- package/package.json +11 -11
- package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
- package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
- package/src/api/app/bsky/feed/getFeed.ts +9 -9
- package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
- package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
- package/src/api/app/bsky/feed/getListFeed.ts +1 -3
- package/src/api/app/bsky/feed/getPostThread.ts +15 -54
- package/src/api/app/bsky/feed/getPosts.ts +21 -18
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
- package/src/api/app/bsky/feed/getTimeline.ts +1 -3
- package/src/api/app/bsky/feed/searchPosts.ts +20 -17
- package/src/api/app/bsky/graph/getList.ts +6 -3
- package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
- package/src/api/app/bsky/graph/getListMutes.ts +2 -1
- package/src/api/app/bsky/graph/getLists.ts +2 -1
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
- package/src/api/blob-resolver.ts +6 -11
- package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
- package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
- package/src/api/com/atproto/admin/getRecord.ts +1 -0
- package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
- package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
- package/src/api/com/atproto/moderation/createReport.ts +9 -7
- package/src/api/com/atproto/moderation/util.ts +38 -20
- package/src/api/index.ts +8 -14
- package/src/auth.ts +29 -21
- package/src/auto-moderator/index.ts +26 -19
- package/src/cache/read-through.ts +151 -0
- package/src/config.ts +90 -1
- package/src/context.ts +11 -7
- package/src/daemon/config.ts +60 -0
- package/src/daemon/context.ts +27 -0
- package/src/daemon/index.ts +78 -0
- package/src/daemon/logger.ts +6 -0
- package/src/daemon/notifications.ts +54 -0
- package/src/daemon/services.ts +22 -0
- package/src/db/database-schema.ts +0 -2
- package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
- package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
- package/src/db/migrations/index.ts +2 -0
- package/src/db/pagination.ts +26 -3
- package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +50 -46
- package/src/db/tables/moderation.ts +35 -52
- package/src/did-cache.ts +33 -56
- package/src/feed-gen/bsky-team.ts +1 -1
- package/src/feed-gen/hot-classic.ts +1 -1
- package/src/feed-gen/index.ts +0 -4
- package/src/feed-gen/mutuals.ts +6 -2
- package/src/feed-gen/types.ts +1 -1
- package/src/index.ts +57 -17
- package/src/indexer/context.ts +5 -0
- package/src/indexer/index.ts +10 -7
- package/src/lexicon/index.ts +80 -67
- package/src/lexicon/lexicons.ts +698 -507
- package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
- package/src/lexicon/types/app/bsky/graph/defs.ts +1 -0
- package/src/lexicon/types/com/atproto/admin/defs.ts +276 -84
- package/src/lexicon/types/com/atproto/admin/{resolveModerationReports.ts → deleteAccount.ts} +2 -13
- package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
- package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationActions.ts → queryModerationEvents.ts} +8 -1
- package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationStatuses.ts} +21 -14
- package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
- package/src/lexicon/types/com/atproto/{admin/getModerationAction.ts → temp/importRepo.ts} +11 -7
- package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
- package/src/lexicon/types/com/atproto/{admin/reverseModerationAction.ts → temp/transferAccount.ts} +18 -5
- package/src/logger.ts +2 -0
- package/src/migrate-moderation-data.ts +414 -0
- package/src/redis.ts +43 -3
- package/src/services/actor/index.ts +55 -7
- package/src/services/actor/views.ts +18 -21
- package/src/services/feed/index.ts +52 -19
- package/src/services/feed/util.ts +47 -19
- package/src/services/feed/views.ts +87 -13
- package/src/services/graph/index.ts +21 -3
- package/src/services/graph/types.ts +1 -0
- package/src/services/index.ts +14 -14
- package/src/services/indexing/index.ts +7 -10
- package/src/services/indexing/plugins/block.ts +2 -3
- package/src/services/indexing/plugins/feed-generator.ts +2 -3
- package/src/services/indexing/plugins/follow.ts +2 -3
- package/src/services/indexing/plugins/like.ts +2 -3
- package/src/services/indexing/plugins/list-block.ts +2 -3
- package/src/services/indexing/plugins/list-item.ts +2 -3
- package/src/services/indexing/plugins/list.ts +2 -3
- package/src/services/indexing/plugins/post.ts +16 -4
- package/src/services/indexing/plugins/repost.ts +2 -3
- package/src/services/indexing/plugins/thread-gate.ts +2 -3
- package/src/services/label/index.ts +68 -25
- package/src/services/moderation/index.ts +380 -395
- package/src/services/moderation/pagination.ts +96 -0
- package/src/services/moderation/status.ts +241 -0
- package/src/services/moderation/types.ts +49 -0
- package/src/services/moderation/views.ts +278 -329
- package/src/services/types.ts +4 -0
- package/src/services/util/notification.ts +70 -0
- package/src/util/debug.ts +2 -2
- package/src/util/retry.ts +1 -44
- package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
- package/tests/__snapshots__/indexing.test.ts.snap +0 -6
- package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
- package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
- package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
- package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
- package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
- package/tests/admin/get-record.test.ts +5 -9
- package/tests/admin/get-repo.test.ts +10 -12
- package/tests/admin/moderation-events.test.ts +221 -0
- package/tests/admin/moderation-statuses.test.ts +145 -0
- package/tests/admin/moderation.test.ts +512 -860
- package/tests/admin/repo-search.test.ts +3 -3
- package/tests/algos/hot-classic.test.ts +1 -2
- package/tests/auth.test.ts +1 -1
- package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
- package/tests/auto-moderator/labeler.test.ts +19 -20
- package/tests/auto-moderator/takedowns.test.ts +61 -28
- package/tests/blob-resolver.test.ts +4 -2
- package/tests/daemon.test.ts +191 -0
- package/tests/did-cache.test.ts +20 -5
- package/tests/feed-generation.test.ts +57 -9
- package/tests/handle-invalidation.test.ts +1 -5
- package/tests/indexing.test.ts +20 -13
- package/tests/redis-cache.test.ts +231 -0
- package/tests/seeds/basic.ts +3 -0
- package/tests/subscription/repo.test.ts +4 -7
- package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
- package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
- package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
- package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
- package/tests/views/actor-search.test.ts +2 -3
- package/tests/views/author-feed.test.ts +42 -36
- package/tests/views/follows.test.ts +40 -35
- package/tests/views/list-feed.test.ts +17 -9
- package/tests/views/notifications.test.ts +13 -9
- package/tests/views/profile.test.ts +20 -19
- package/tests/views/thread.test.ts +117 -94
- package/tests/views/threadgating.test.ts +89 -19
- package/tests/views/timeline.test.ts +21 -13
- package/dist/api/com/atproto/admin/resolveModerationReports.d.ts +0 -3
- package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
- package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
- package/dist/db/tables/did-cache.d.ts +0 -10
- package/dist/feed-gen/best-of-follows.d.ts +0 -29
- package/dist/feed-gen/whats-hot.d.ts +0 -29
- package/dist/feed-gen/with-friends.d.ts +0 -3
- package/dist/label-cache.d.ts +0 -19
- package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
- package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
- package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
- package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
- package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
- package/src/db/tables/did-cache.ts +0 -13
- package/src/feed-gen/best-of-follows.ts +0 -74
- package/src/feed-gen/whats-hot.ts +0 -101
- package/src/feed-gen/with-friends.ts +0 -39
- package/src/label-cache.ts +0 -90
- package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
- package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
- package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
- package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
- package/tests/admin/get-moderation-action.test.ts +0 -100
- package/tests/admin/get-moderation-actions.test.ts +0 -164
- package/tests/admin/get-moderation-report.test.ts +0 -100
- package/tests/admin/get-moderation-reports.test.ts +0 -332
- package/tests/algos/whats-hot.test.ts +0 -118
- package/tests/algos/with-friends.test.ts +0 -145
- /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
- /package/dist/api/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +0 -0
|
@@ -1,76 +1,59 @@
|
|
|
1
1
|
import { Generated } from 'kysely'
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ESCALATE,
|
|
3
|
+
REVIEWCLOSED,
|
|
4
|
+
REVIEWOPEN,
|
|
5
|
+
REVIEWESCALATED,
|
|
7
6
|
} from '../../lexicon/types/com/atproto/admin/defs'
|
|
8
|
-
import {
|
|
9
|
-
REASONOTHER,
|
|
10
|
-
REASONSPAM,
|
|
11
|
-
REASONMISLEADING,
|
|
12
|
-
REASONRUDE,
|
|
13
|
-
REASONSEXUAL,
|
|
14
|
-
REASONVIOLATION,
|
|
15
|
-
} from '../../lexicon/types/com/atproto/moderation/defs'
|
|
16
7
|
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
19
|
-
export const reportTableName = 'moderation_report'
|
|
20
|
-
export const reportResolutionTableName = 'moderation_report_resolution'
|
|
8
|
+
export const eventTableName = 'moderation_event'
|
|
9
|
+
export const subjectStatusTableName = 'moderation_subject_status'
|
|
21
10
|
|
|
22
|
-
export interface
|
|
11
|
+
export interface ModerationEvent {
|
|
23
12
|
id: Generated<number>
|
|
24
|
-
action:
|
|
13
|
+
action:
|
|
14
|
+
| 'com.atproto.admin.defs#modEventTakedown'
|
|
15
|
+
| 'com.atproto.admin.defs#modEventAcknowledge'
|
|
16
|
+
| 'com.atproto.admin.defs#modEventEscalate'
|
|
17
|
+
| 'com.atproto.admin.defs#modEventComment'
|
|
18
|
+
| 'com.atproto.admin.defs#modEventLabel'
|
|
19
|
+
| 'com.atproto.admin.defs#modEventReport'
|
|
20
|
+
| 'com.atproto.admin.defs#modEventMute'
|
|
21
|
+
| 'com.atproto.admin.defs#modEventReverseTakedown'
|
|
22
|
+
| 'com.atproto.admin.defs#modEventEmail'
|
|
25
23
|
subjectType: 'com.atproto.admin.defs#repoRef' | 'com.atproto.repo.strongRef'
|
|
26
24
|
subjectDid: string
|
|
27
25
|
subjectUri: string | null
|
|
28
26
|
subjectCid: string | null
|
|
29
27
|
createLabelVals: string | null
|
|
30
28
|
negateLabelVals: string | null
|
|
31
|
-
|
|
29
|
+
comment: string | null
|
|
32
30
|
createdAt: string
|
|
33
31
|
createdBy: string
|
|
34
|
-
reversedAt: string | null
|
|
35
|
-
reversedBy: string | null
|
|
36
|
-
reversedReason: string | null
|
|
37
32
|
durationInHours: number | null
|
|
38
33
|
expiresAt: string | null
|
|
34
|
+
meta: Record<string, string | boolean> | null
|
|
35
|
+
legacyRefId: number | null
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
export interface
|
|
42
|
-
actionId: number
|
|
43
|
-
cid: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface ModerationReport {
|
|
38
|
+
export interface ModerationSubjectStatus {
|
|
47
39
|
id: Generated<number>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| typeof REASONSPAM
|
|
54
|
-
| typeof REASONOTHER
|
|
55
|
-
| typeof REASONMISLEADING
|
|
56
|
-
| typeof REASONRUDE
|
|
57
|
-
| typeof REASONSEXUAL
|
|
58
|
-
| typeof REASONVIOLATION
|
|
59
|
-
reason: string | null
|
|
60
|
-
reportedByDid: string
|
|
40
|
+
did: string
|
|
41
|
+
recordPath: string
|
|
42
|
+
recordCid: string | null
|
|
43
|
+
blobCids: string[] | null
|
|
44
|
+
reviewState: typeof REVIEWCLOSED | typeof REVIEWOPEN | typeof REVIEWESCALATED
|
|
61
45
|
createdAt: string
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
46
|
+
updatedAt: string
|
|
47
|
+
lastReviewedBy: string | null
|
|
48
|
+
lastReviewedAt: string | null
|
|
49
|
+
lastReportedAt: string | null
|
|
50
|
+
muteUntil: string | null
|
|
51
|
+
suspendUntil: string | null
|
|
52
|
+
takendown: boolean
|
|
53
|
+
comment: string | null
|
|
69
54
|
}
|
|
70
55
|
|
|
71
56
|
export type PartialDB = {
|
|
72
|
-
[
|
|
73
|
-
[
|
|
74
|
-
[reportTableName]: ModerationReport
|
|
75
|
-
[reportResolutionTableName]: ModerationReportResolution
|
|
57
|
+
[eventTableName]: ModerationEvent
|
|
58
|
+
[subjectStatusTableName]: ModerationSubjectStatus
|
|
76
59
|
}
|
package/src/did-cache.ts
CHANGED
|
@@ -1,81 +1,61 @@
|
|
|
1
1
|
import PQueue from 'p-queue'
|
|
2
2
|
import { CacheResult, DidCache, DidDocument } from '@atproto/identity'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { dbLogger } from './logger'
|
|
3
|
+
import { cacheLogger as log } from './logger'
|
|
4
|
+
import { Redis } from './redis'
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
type CacheOptions = {
|
|
7
|
+
staleTTL: number
|
|
8
|
+
maxTTL: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class DidRedisCache implements DidCache {
|
|
12
|
+
public pQueue: PQueue | null // null during teardown
|
|
9
13
|
|
|
10
|
-
constructor(
|
|
11
|
-
// @TODO perhaps could use both primary and non-primary. not high enough
|
|
12
|
-
// throughput to matter right now. also may just move this over to redis before long!
|
|
13
|
-
public db: PrimaryDatabase,
|
|
14
|
-
public staleTTL: number,
|
|
15
|
-
public maxTTL: number,
|
|
16
|
-
) {
|
|
14
|
+
constructor(public redis: Redis, public opts: CacheOptions) {
|
|
17
15
|
this.pQueue = new PQueue()
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
async cacheDid(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
}
|
|
18
|
+
async cacheDid(did: string, doc: DidDocument): Promise<void> {
|
|
19
|
+
const item = JSON.stringify({
|
|
20
|
+
doc,
|
|
21
|
+
updatedAt: Date.now(),
|
|
22
|
+
})
|
|
23
|
+
await this.redis.set(did, item, this.opts.maxTTL)
|
|
44
24
|
}
|
|
45
25
|
|
|
46
26
|
async refreshCache(
|
|
47
27
|
did: string,
|
|
48
28
|
getDoc: () => Promise<DidDocument | null>,
|
|
49
|
-
prevResult?: CacheResult,
|
|
50
29
|
): Promise<void> {
|
|
51
30
|
this.pQueue?.add(async () => {
|
|
52
31
|
try {
|
|
53
32
|
const doc = await getDoc()
|
|
54
33
|
if (doc) {
|
|
55
|
-
await this.cacheDid(did, doc
|
|
34
|
+
await this.cacheDid(did, doc)
|
|
56
35
|
} else {
|
|
57
36
|
await this.clearEntry(did)
|
|
58
37
|
}
|
|
59
38
|
} catch (err) {
|
|
60
|
-
|
|
39
|
+
log.error({ did, err }, 'refreshing did cache failed')
|
|
61
40
|
}
|
|
62
41
|
})
|
|
63
42
|
}
|
|
64
43
|
|
|
65
44
|
async checkCache(did: string): Promise<CacheResult | null> {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
45
|
+
let got: string | null
|
|
46
|
+
try {
|
|
47
|
+
got = await this.redis.get(did)
|
|
48
|
+
} catch (err) {
|
|
49
|
+
got = null
|
|
50
|
+
log.error({ did, err }, 'error fetching did from cache')
|
|
51
|
+
}
|
|
52
|
+
if (!got) return null
|
|
53
|
+
const { doc, updatedAt } = JSON.parse(got) as CacheResult
|
|
73
54
|
const now = Date.now()
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const stale = now > updatedAt + this.staleTTL
|
|
55
|
+
const expired = now > updatedAt + this.opts.maxTTL
|
|
56
|
+
const stale = now > updatedAt + this.opts.staleTTL
|
|
77
57
|
return {
|
|
78
|
-
doc
|
|
58
|
+
doc,
|
|
79
59
|
updatedAt,
|
|
80
60
|
did,
|
|
81
61
|
stale,
|
|
@@ -84,14 +64,11 @@ export class DidSqlCache implements DidCache {
|
|
|
84
64
|
}
|
|
85
65
|
|
|
86
66
|
async clearEntry(did: string): Promise<void> {
|
|
87
|
-
await this.
|
|
88
|
-
.deleteFrom('did_cache')
|
|
89
|
-
.where('did', '=', did)
|
|
90
|
-
.executeTakeFirst()
|
|
67
|
+
await this.redis.del(did)
|
|
91
68
|
}
|
|
92
69
|
|
|
93
70
|
async clear(): Promise<void> {
|
|
94
|
-
|
|
71
|
+
throw new Error('Not implemented for redis cache')
|
|
95
72
|
}
|
|
96
73
|
|
|
97
74
|
async processAll() {
|
|
@@ -107,4 +84,4 @@ export class DidSqlCache implements DidCache {
|
|
|
107
84
|
}
|
|
108
85
|
}
|
|
109
86
|
|
|
110
|
-
export default
|
|
87
|
+
export default DidRedisCache
|
|
@@ -14,7 +14,7 @@ const BSKY_TEAM: NotEmptyArray<string> = [
|
|
|
14
14
|
const handler: AlgoHandler = async (
|
|
15
15
|
ctx: AppContext,
|
|
16
16
|
params: SkeletonParams,
|
|
17
|
-
_viewer: string,
|
|
17
|
+
_viewer: string | null,
|
|
18
18
|
): Promise<AlgoResponse> => {
|
|
19
19
|
const { limit = 50, cursor } = params
|
|
20
20
|
const db = ctx.db.getReplica('feed')
|
|
@@ -11,7 +11,7 @@ const NO_WHATS_HOT_LABELS: NotEmptyArray<string> = ['!no-promote']
|
|
|
11
11
|
const handler: AlgoHandler = async (
|
|
12
12
|
ctx: AppContext,
|
|
13
13
|
params: SkeletonParams,
|
|
14
|
-
_viewer: string,
|
|
14
|
+
_viewer: string | null,
|
|
15
15
|
): Promise<AlgoResponse> => {
|
|
16
16
|
const { limit = 50, cursor } = params
|
|
17
17
|
const db = ctx.db.getReplica('feed')
|
package/src/feed-gen/index.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { AtUri } from '@atproto/syntax'
|
|
2
2
|
import { ids } from '../lexicon/lexicons'
|
|
3
|
-
import withFriends from './with-friends'
|
|
4
3
|
import bskyTeam from './bsky-team'
|
|
5
4
|
import hotClassic from './hot-classic'
|
|
6
|
-
import bestOfFollows from './best-of-follows'
|
|
7
5
|
import mutuals from './mutuals'
|
|
8
6
|
import { MountedAlgos } from './types'
|
|
9
7
|
|
|
@@ -13,9 +11,7 @@ const feedgenUri = (did, name) =>
|
|
|
13
11
|
// These are custom algorithms that will be mounted directly onto an AppView
|
|
14
12
|
// Feel free to remove, update to your own, or serve the following logic at a record that you control
|
|
15
13
|
export const makeAlgos = (did: string): MountedAlgos => ({
|
|
16
|
-
[feedgenUri(did, 'with-friends')]: withFriends,
|
|
17
14
|
[feedgenUri(did, 'bsky-team')]: bskyTeam,
|
|
18
15
|
[feedgenUri(did, 'hot-classic')]: hotClassic,
|
|
19
|
-
[feedgenUri(did, 'best-of-follows')]: bestOfFollows,
|
|
20
16
|
[feedgenUri(did, 'mutuals')]: mutuals,
|
|
21
17
|
})
|
package/src/feed-gen/mutuals.ts
CHANGED
|
@@ -3,16 +3,20 @@ import AppContext from '../context'
|
|
|
3
3
|
import { paginate } from '../db/pagination'
|
|
4
4
|
import { AlgoHandler, AlgoResponse } from './types'
|
|
5
5
|
import { FeedKeyset, getFeedDateThreshold } from '../api/app/bsky/util/feed'
|
|
6
|
+
import { AuthRequiredError } from '@atproto/xrpc-server'
|
|
6
7
|
|
|
7
8
|
const handler: AlgoHandler = async (
|
|
8
9
|
ctx: AppContext,
|
|
9
10
|
params: SkeletonParams,
|
|
10
|
-
viewer: string,
|
|
11
|
+
viewer: string | null,
|
|
11
12
|
): Promise<AlgoResponse> => {
|
|
13
|
+
if (!viewer) {
|
|
14
|
+
throw new AuthRequiredError('This feed requires being logged-in')
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
const { limit = 50, cursor } = params
|
|
13
18
|
const db = ctx.db.getReplica('feed')
|
|
14
19
|
const feedService = ctx.services.feed(db)
|
|
15
|
-
|
|
16
20
|
const { ref } = db.db.dynamic
|
|
17
21
|
|
|
18
22
|
const mutualsSubquery = db.db
|
package/src/feed-gen/types.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -6,6 +6,12 @@ import { createHttpTerminator, HttpTerminator } from 'http-terminator'
|
|
|
6
6
|
import cors from 'cors'
|
|
7
7
|
import compression from 'compression'
|
|
8
8
|
import { IdResolver } from '@atproto/identity'
|
|
9
|
+
import {
|
|
10
|
+
RateLimiter,
|
|
11
|
+
RateLimiterOpts,
|
|
12
|
+
Options as XrpcServerOptions,
|
|
13
|
+
} from '@atproto/xrpc-server'
|
|
14
|
+
import { MINUTE } from '@atproto/common'
|
|
9
15
|
import API, { health, wellKnown, blobResolver } from './api'
|
|
10
16
|
import { DatabaseCoordinator } from './db'
|
|
11
17
|
import * as error from './error'
|
|
@@ -16,29 +22,31 @@ import { ImageUriBuilder } from './image/uri'
|
|
|
16
22
|
import { BlobDiskCache, ImageProcessingServer } from './image/server'
|
|
17
23
|
import { createServices } from './services'
|
|
18
24
|
import AppContext from './context'
|
|
19
|
-
import
|
|
25
|
+
import DidRedisCache from './did-cache'
|
|
20
26
|
import {
|
|
21
27
|
ImageInvalidator,
|
|
22
28
|
ImageProcessingServerInvalidator,
|
|
23
29
|
} from './image/invalidator'
|
|
24
30
|
import { BackgroundQueue } from './background'
|
|
25
31
|
import { MountedAlgos } from './feed-gen/types'
|
|
26
|
-
import { LabelCache } from './label-cache'
|
|
27
32
|
import { NotificationServer } from './notifications'
|
|
28
33
|
import { AtpAgent } from '@atproto/api'
|
|
29
34
|
import { Keypair } from '@atproto/crypto'
|
|
35
|
+
import { Redis } from './redis'
|
|
30
36
|
|
|
31
37
|
export type { ServerConfigValues } from './config'
|
|
32
38
|
export type { MountedAlgos } from './feed-gen/types'
|
|
33
39
|
export { ServerConfig } from './config'
|
|
34
40
|
export { Database, PrimaryDatabase, DatabaseCoordinator } from './db'
|
|
35
|
-
export {
|
|
41
|
+
export { PeriodicModerationEventReversal } from './db/periodic-moderation-event-reversal'
|
|
36
42
|
export { Redis } from './redis'
|
|
37
43
|
export { ViewMaintainer } from './db/views'
|
|
38
44
|
export { AppContext } from './context'
|
|
39
45
|
export { makeAlgos } from './feed-gen'
|
|
46
|
+
export * from './daemon'
|
|
40
47
|
export * from './indexer'
|
|
41
48
|
export * from './ingester'
|
|
49
|
+
export { MigrateModerationData } from './migrate-moderation-data'
|
|
42
50
|
|
|
43
51
|
export class BskyAppView {
|
|
44
52
|
public ctx: AppContext
|
|
@@ -54,23 +62,25 @@ export class BskyAppView {
|
|
|
54
62
|
|
|
55
63
|
static create(opts: {
|
|
56
64
|
db: DatabaseCoordinator
|
|
65
|
+
redis: Redis
|
|
57
66
|
config: ServerConfig
|
|
58
67
|
signingKey: Keypair
|
|
59
68
|
imgInvalidator?: ImageInvalidator
|
|
60
69
|
algos?: MountedAlgos
|
|
61
70
|
}): BskyAppView {
|
|
62
|
-
const { db, config, signingKey, algos = {} } = opts
|
|
71
|
+
const { db, redis, config, signingKey, algos = {} } = opts
|
|
63
72
|
let maybeImgInvalidator = opts.imgInvalidator
|
|
64
73
|
const app = express()
|
|
74
|
+
app.set('trust proxy', true)
|
|
65
75
|
app.use(cors())
|
|
66
76
|
app.use(loggerMiddleware)
|
|
67
77
|
app.use(compression())
|
|
68
78
|
|
|
69
|
-
const didCache = new
|
|
70
|
-
|
|
71
|
-
config.
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
const didCache = new DidRedisCache(redis.withNamespace('did-doc'), {
|
|
80
|
+
staleTTL: config.didCacheStaleTTL,
|
|
81
|
+
maxTTL: config.didCacheMaxTTL,
|
|
82
|
+
})
|
|
83
|
+
|
|
74
84
|
const idResolver = new IdResolver({
|
|
75
85
|
plcUrl: config.didPlcUrl,
|
|
76
86
|
didCache,
|
|
@@ -101,7 +111,7 @@ export class BskyAppView {
|
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
const backgroundQueue = new BackgroundQueue(db.getPrimary())
|
|
104
|
-
|
|
114
|
+
|
|
105
115
|
const notifServer = new NotificationServer(db.getPrimary())
|
|
106
116
|
const searchAgent = config.searchEndpoint
|
|
107
117
|
? new AtpAgent({ service: config.searchEndpoint })
|
|
@@ -110,7 +120,11 @@ export class BskyAppView {
|
|
|
110
120
|
const services = createServices({
|
|
111
121
|
imgUriBuilder,
|
|
112
122
|
imgInvalidator,
|
|
113
|
-
|
|
123
|
+
labelCacheOpts: {
|
|
124
|
+
redis: redis.withNamespace('label'),
|
|
125
|
+
staleTTL: config.labelCacheStaleTTL,
|
|
126
|
+
maxTTL: config.labelCacheMaxTTL,
|
|
127
|
+
},
|
|
114
128
|
})
|
|
115
129
|
|
|
116
130
|
const ctx = new AppContext({
|
|
@@ -121,21 +135,48 @@ export class BskyAppView {
|
|
|
121
135
|
signingKey,
|
|
122
136
|
idResolver,
|
|
123
137
|
didCache,
|
|
124
|
-
|
|
138
|
+
redis,
|
|
125
139
|
backgroundQueue,
|
|
126
140
|
searchAgent,
|
|
127
141
|
algos,
|
|
128
142
|
notifServer,
|
|
129
143
|
})
|
|
130
144
|
|
|
131
|
-
|
|
145
|
+
const xrpcOpts: XrpcServerOptions = {
|
|
132
146
|
validateResponse: config.debugMode,
|
|
133
147
|
payload: {
|
|
134
148
|
jsonLimit: 100 * 1024, // 100kb
|
|
135
149
|
textLimit: 100 * 1024, // 100kb
|
|
136
150
|
blobLimit: 5 * 1024 * 1024, // 5mb
|
|
137
151
|
},
|
|
138
|
-
}
|
|
152
|
+
}
|
|
153
|
+
if (config.rateLimitsEnabled) {
|
|
154
|
+
const rlCreator = (opts: RateLimiterOpts) =>
|
|
155
|
+
RateLimiter.redis(redis.driver, {
|
|
156
|
+
bypassSecret: config.rateLimitBypassKey,
|
|
157
|
+
bypassIps: config.rateLimitBypassIps,
|
|
158
|
+
...opts,
|
|
159
|
+
})
|
|
160
|
+
xrpcOpts['rateLimits'] = {
|
|
161
|
+
creator: rlCreator,
|
|
162
|
+
global: [
|
|
163
|
+
{
|
|
164
|
+
name: 'global-unauthed-ip',
|
|
165
|
+
durationMs: 5 * MINUTE,
|
|
166
|
+
points: 3000,
|
|
167
|
+
calcKey: (ctx) => (ctx.auth ? null : ctx.req.ip),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'global-authed-did',
|
|
171
|
+
durationMs: 5 * MINUTE,
|
|
172
|
+
points: 3000,
|
|
173
|
+
calcKey: (ctx) => ctx.auth?.credentials?.did ?? null,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let server = createServer(xrpcOpts)
|
|
139
180
|
|
|
140
181
|
server = API(server, ctx)
|
|
141
182
|
|
|
@@ -184,7 +225,6 @@ export class BskyAppView {
|
|
|
184
225
|
'background queue stats',
|
|
185
226
|
)
|
|
186
227
|
}, 10000)
|
|
187
|
-
this.ctx.labelCache.start()
|
|
188
228
|
const server = this.app.listen(this.ctx.cfg.port)
|
|
189
229
|
this.server = server
|
|
190
230
|
server.keepAliveTimeout = 90000
|
|
@@ -195,11 +235,11 @@ export class BskyAppView {
|
|
|
195
235
|
return server
|
|
196
236
|
}
|
|
197
237
|
|
|
198
|
-
async destroy(opts?: { skipDb: boolean }): Promise<void> {
|
|
199
|
-
this.ctx.labelCache.stop()
|
|
238
|
+
async destroy(opts?: { skipDb: boolean; skipRedis: boolean }): Promise<void> {
|
|
200
239
|
await this.ctx.didCache.destroy()
|
|
201
240
|
await this.terminator?.terminate()
|
|
202
241
|
await this.ctx.backgroundQueue.destroy()
|
|
242
|
+
if (!opts?.skipRedis) await this.ctx.redis.destroy()
|
|
203
243
|
if (!opts?.skipDb) await this.ctx.db.close()
|
|
204
244
|
clearInterval(this.dbStatsInterval)
|
|
205
245
|
}
|
package/src/indexer/context.ts
CHANGED
|
@@ -12,6 +12,7 @@ export class IndexerContext {
|
|
|
12
12
|
private opts: {
|
|
13
13
|
db: PrimaryDatabase
|
|
14
14
|
redis: Redis
|
|
15
|
+
redisCache: Redis
|
|
15
16
|
cfg: IndexerConfig
|
|
16
17
|
services: Services
|
|
17
18
|
idResolver: IdResolver
|
|
@@ -29,6 +30,10 @@ export class IndexerContext {
|
|
|
29
30
|
return this.opts.redis
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
get redisCache(): Redis {
|
|
34
|
+
return this.opts.redisCache
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
get cfg(): IndexerConfig {
|
|
33
38
|
return this.opts.cfg
|
|
34
39
|
}
|
package/src/indexer/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import express from 'express'
|
|
|
2
2
|
import { IdResolver } from '@atproto/identity'
|
|
3
3
|
import { BackgroundQueue } from '../background'
|
|
4
4
|
import { PrimaryDatabase } from '../db'
|
|
5
|
-
import
|
|
5
|
+
import DidRedisCache from '../did-cache'
|
|
6
6
|
import log from './logger'
|
|
7
7
|
import { dbLogger } from '../logger'
|
|
8
8
|
import { IndexerConfig } from './config'
|
|
@@ -40,15 +40,15 @@ export class BskyIndexer {
|
|
|
40
40
|
static create(opts: {
|
|
41
41
|
db: PrimaryDatabase
|
|
42
42
|
redis: Redis
|
|
43
|
+
redisCache: Redis
|
|
43
44
|
cfg: IndexerConfig
|
|
44
45
|
imgInvalidator?: ImageInvalidator
|
|
45
46
|
}): BskyIndexer {
|
|
46
|
-
const { db, redis, cfg } = opts
|
|
47
|
-
const didCache = new
|
|
48
|
-
|
|
49
|
-
cfg.
|
|
50
|
-
|
|
51
|
-
)
|
|
47
|
+
const { db, redis, redisCache, cfg } = opts
|
|
48
|
+
const didCache = new DidRedisCache(redisCache.withNamespace('did-doc'), {
|
|
49
|
+
staleTTL: cfg.didCacheStaleTTL,
|
|
50
|
+
maxTTL: cfg.didCacheMaxTTL,
|
|
51
|
+
})
|
|
52
52
|
const idResolver = new IdResolver({
|
|
53
53
|
plcUrl: cfg.didPlcUrl,
|
|
54
54
|
didCache,
|
|
@@ -81,6 +81,7 @@ export class BskyIndexer {
|
|
|
81
81
|
const ctx = new IndexerContext({
|
|
82
82
|
db,
|
|
83
83
|
redis,
|
|
84
|
+
redisCache,
|
|
84
85
|
cfg,
|
|
85
86
|
services,
|
|
86
87
|
idResolver,
|
|
@@ -139,7 +140,9 @@ export class BskyIndexer {
|
|
|
139
140
|
if (this.closeServer) await this.closeServer()
|
|
140
141
|
await this.sub.destroy()
|
|
141
142
|
clearInterval(this.subStatsInterval)
|
|
143
|
+
await this.ctx.didCache.destroy()
|
|
142
144
|
if (!opts?.skipRedis) await this.ctx.redis.destroy()
|
|
145
|
+
if (!opts?.skipRedis) await this.ctx.redisCache.destroy()
|
|
143
146
|
if (!opts?.skipDb) await this.ctx.db.close()
|
|
144
147
|
clearInterval(this.dbStatsInterval)
|
|
145
148
|
}
|