@atproto/bsky 0.0.75 → 0.0.77

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.
Files changed (268) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
  3. package/dist/api/app/bsky/actor/getProfiles.js +9 -1
  4. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  5. package/dist/api/app/bsky/actor/searchActorsTypeahead.js +10 -2
  6. package/dist/api/app/bsky/actor/searchActorsTypeahead.js.map +1 -1
  7. package/dist/api/app/bsky/feed/getActorLikes.js +2 -2
  8. package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
  9. package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
  10. package/dist/api/app/bsky/feed/getFeed.js +8 -1
  11. package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
  12. package/dist/api/app/bsky/feed/getLikes.js +6 -6
  13. package/dist/api/app/bsky/feed/getLikes.js.map +1 -1
  14. package/dist/api/app/bsky/feed/getPosts.d.ts.map +1 -1
  15. package/dist/api/app/bsky/feed/getPosts.js +12 -5
  16. package/dist/api/app/bsky/feed/getPosts.js.map +1 -1
  17. package/dist/api/app/bsky/feed/getQuotes.d.ts +4 -0
  18. package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -0
  19. package/dist/api/app/bsky/feed/getQuotes.js +67 -0
  20. package/dist/api/app/bsky/feed/getQuotes.js.map +1 -0
  21. package/dist/api/app/bsky/feed/getRepostedBy.js +6 -6
  22. package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
  23. package/dist/api/app/bsky/feed/searchPosts.js +4 -4
  24. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  25. package/dist/api/app/bsky/graph/getFollowers.js +8 -8
  26. package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
  27. package/dist/api/app/bsky/graph/getList.d.ts.map +1 -1
  28. package/dist/api/app/bsky/graph/getList.js +32 -2
  29. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  30. package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
  31. package/dist/api/app/bsky/notification/listNotifications.js +29 -8
  32. package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
  33. package/dist/api/index.d.ts.map +1 -1
  34. package/dist/api/index.js +2 -0
  35. package/dist/api/index.js.map +1 -1
  36. package/dist/auth-verifier.d.ts +8 -3
  37. package/dist/auth-verifier.d.ts.map +1 -1
  38. package/dist/auth-verifier.js +43 -29
  39. package/dist/auth-verifier.js.map +1 -1
  40. package/dist/data-plane/server/db/database-schema.d.ts +4 -2
  41. package/dist/data-plane/server/db/database-schema.d.ts.map +1 -1
  42. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts +4 -0
  43. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.d.ts.map +1 -0
  44. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js +15 -0
  45. package/dist/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.js.map +1 -0
  46. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts +4 -0
  47. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.d.ts.map +1 -0
  48. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js +30 -0
  49. package/dist/data-plane/server/db/migrations/20240723T220703655Z-quotes.js.map +1 -0
  50. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts +4 -0
  51. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.d.ts.map +1 -0
  52. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js +20 -0
  53. package/dist/data-plane/server/db/migrations/20240801T193939827Z-post-gate.js.map +1 -0
  54. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts +4 -0
  55. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.d.ts.map +1 -0
  56. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js +28 -0
  57. package/dist/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.js.map +1 -0
  58. package/dist/data-plane/server/db/migrations/index.d.ts +4 -0
  59. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  60. package/dist/data-plane/server/db/migrations/index.js +5 -1
  61. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  62. package/dist/data-plane/server/db/tables/post-agg.d.ts +1 -0
  63. package/dist/data-plane/server/db/tables/post-agg.d.ts.map +1 -1
  64. package/dist/data-plane/server/db/tables/post-gate.d.ts +14 -0
  65. package/dist/data-plane/server/db/tables/post-gate.d.ts.map +1 -0
  66. package/dist/data-plane/server/db/tables/post-gate.js +4 -0
  67. package/dist/data-plane/server/db/tables/post-gate.js.map +1 -0
  68. package/dist/data-plane/server/db/tables/post.d.ts +3 -0
  69. package/dist/data-plane/server/db/tables/post.d.ts.map +1 -1
  70. package/dist/data-plane/server/db/tables/quote.d.ts +16 -0
  71. package/dist/data-plane/server/db/tables/quote.d.ts.map +1 -0
  72. package/dist/data-plane/server/db/tables/quote.js +4 -0
  73. package/dist/data-plane/server/db/tables/quote.js.map +1 -0
  74. package/dist/data-plane/server/indexing/index.d.ts +2 -0
  75. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  76. package/dist/data-plane/server/indexing/index.js +6 -0
  77. package/dist/data-plane/server/indexing/index.js.map +1 -1
  78. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts +10 -0
  79. package/dist/data-plane/server/indexing/plugins/post-gate.d.ts.map +1 -0
  80. package/dist/data-plane/server/indexing/plugins/post-gate.js +101 -0
  81. package/dist/data-plane/server/indexing/plugins/post-gate.js.map +1 -0
  82. package/dist/data-plane/server/indexing/plugins/post.d.ts +2 -0
  83. package/dist/data-plane/server/indexing/plugins/post.d.ts.map +1 -1
  84. package/dist/data-plane/server/indexing/plugins/post.js +122 -15
  85. package/dist/data-plane/server/indexing/plugins/post.js.map +1 -1
  86. package/dist/data-plane/server/indexing/plugins/thread-gate.d.ts.map +1 -1
  87. package/dist/data-plane/server/indexing/plugins/thread-gate.js +12 -0
  88. package/dist/data-plane/server/indexing/plugins/thread-gate.js.map +1 -1
  89. package/dist/data-plane/server/routes/index.d.ts.map +1 -1
  90. package/dist/data-plane/server/routes/index.js +2 -0
  91. package/dist/data-plane/server/routes/index.js.map +1 -1
  92. package/dist/data-plane/server/routes/interactions.d.ts.map +1 -1
  93. package/dist/data-plane/server/routes/interactions.js +2 -1
  94. package/dist/data-plane/server/routes/interactions.js.map +1 -1
  95. package/dist/data-plane/server/routes/quotes.d.ts +6 -0
  96. package/dist/data-plane/server/routes/quotes.d.ts.map +1 -0
  97. package/dist/data-plane/server/routes/quotes.js +27 -0
  98. package/dist/data-plane/server/routes/quotes.js.map +1 -0
  99. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  100. package/dist/data-plane/server/routes/records.js +11 -1
  101. package/dist/data-plane/server/routes/records.js.map +1 -1
  102. package/dist/data-plane/server/util.d.ts +6 -7
  103. package/dist/data-plane/server/util.d.ts.map +1 -1
  104. package/dist/data-plane/server/util.js +1 -9
  105. package/dist/data-plane/server/util.js.map +1 -1
  106. package/dist/hydration/feed.d.ts +10 -0
  107. package/dist/hydration/feed.d.ts.map +1 -1
  108. package/dist/hydration/feed.js +31 -7
  109. package/dist/hydration/feed.js.map +1 -1
  110. package/dist/hydration/hydrator.d.ts +5 -2
  111. package/dist/hydration/hydrator.d.ts.map +1 -1
  112. package/dist/hydration/hydrator.js +102 -38
  113. package/dist/hydration/hydrator.js.map +1 -1
  114. package/dist/hydration/util.d.ts +0 -1
  115. package/dist/hydration/util.d.ts.map +1 -1
  116. package/dist/hydration/util.js +1 -5
  117. package/dist/hydration/util.js.map +1 -1
  118. package/dist/lexicon/index.d.ts +2 -0
  119. package/dist/lexicon/index.d.ts.map +1 -1
  120. package/dist/lexicon/index.js +4 -0
  121. package/dist/lexicon/index.js.map +1 -1
  122. package/dist/lexicon/lexicons.d.ts +141 -0
  123. package/dist/lexicon/lexicons.d.ts.map +1 -1
  124. package/dist/lexicon/lexicons.js +142 -0
  125. package/dist/lexicon/lexicons.js.map +1 -1
  126. package/dist/lexicon/types/app/bsky/embed/record.d.ts +8 -1
  127. package/dist/lexicon/types/app/bsky/embed/record.d.ts.map +1 -1
  128. package/dist/lexicon/types/app/bsky/embed/record.js +11 -1
  129. package/dist/lexicon/types/app/bsky/embed/record.js.map +1 -1
  130. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +2 -0
  131. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  132. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  133. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts +44 -0
  134. package/dist/lexicon/types/app/bsky/feed/getQuotes.d.ts.map +1 -0
  135. package/dist/lexicon/types/app/bsky/feed/getQuotes.js +3 -0
  136. package/dist/lexicon/types/app/bsky/feed/getQuotes.js.map +1 -0
  137. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts +25 -0
  138. package/dist/lexicon/types/app/bsky/feed/postgate.d.ts.map +1 -0
  139. package/dist/lexicon/types/app/bsky/feed/postgate.js +27 -0
  140. package/dist/lexicon/types/app/bsky/feed/postgate.js.map +1 -0
  141. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts +2 -0
  142. package/dist/lexicon/types/app/bsky/feed/threadgate.d.ts.map +1 -1
  143. package/dist/lexicon/types/app/bsky/feed/threadgate.js.map +1 -1
  144. package/dist/proto/bsky_connect.d.ts +21 -1
  145. package/dist/proto/bsky_connect.d.ts.map +1 -1
  146. package/dist/proto/bsky_connect.js +20 -0
  147. package/dist/proto/bsky_connect.js.map +1 -1
  148. package/dist/proto/bsky_pb.d.ts +96 -0
  149. package/dist/proto/bsky_pb.d.ts.map +1 -1
  150. package/dist/proto/bsky_pb.js +306 -4
  151. package/dist/proto/bsky_pb.js.map +1 -1
  152. package/dist/util/uris.d.ts +12 -0
  153. package/dist/util/uris.d.ts.map +1 -0
  154. package/dist/util/uris.js +34 -0
  155. package/dist/util/uris.js.map +1 -0
  156. package/dist/views/index.d.ts +8 -2
  157. package/dist/views/index.d.ts.map +1 -1
  158. package/dist/views/index.js +83 -39
  159. package/dist/views/index.js.map +1 -1
  160. package/dist/views/types.d.ts +1 -1
  161. package/dist/views/types.d.ts.map +1 -1
  162. package/dist/views/types.js.map +1 -1
  163. package/dist/views/util.d.ts +11 -1
  164. package/dist/views/util.d.ts.map +1 -1
  165. package/dist/views/util.js +19 -8
  166. package/dist/views/util.js.map +1 -1
  167. package/package.json +6 -6
  168. package/proto/bsky.proto +33 -0
  169. package/src/api/app/bsky/actor/getProfiles.ts +10 -1
  170. package/src/api/app/bsky/actor/searchActorsTypeahead.ts +9 -4
  171. package/src/api/app/bsky/feed/getActorLikes.ts +1 -1
  172. package/src/api/app/bsky/feed/getFeed.ts +12 -1
  173. package/src/api/app/bsky/feed/getLikes.ts +1 -1
  174. package/src/api/app/bsky/feed/getPosts.ts +10 -2
  175. package/src/api/app/bsky/feed/getQuotes.ts +105 -0
  176. package/src/api/app/bsky/feed/getRepostedBy.ts +1 -1
  177. package/src/api/app/bsky/feed/searchPosts.ts +1 -1
  178. package/src/api/app/bsky/graph/getFollowers.ts +1 -1
  179. package/src/api/app/bsky/graph/getList.ts +47 -4
  180. package/src/api/app/bsky/notification/listNotifications.ts +32 -6
  181. package/src/api/index.ts +2 -0
  182. package/src/auth-verifier.ts +78 -51
  183. package/src/data-plane/server/db/database-schema.ts +7 -3
  184. package/src/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.ts +12 -0
  185. package/src/data-plane/server/db/migrations/20240723T220703655Z-quotes.ts +28 -0
  186. package/src/data-plane/server/db/migrations/20240801T193939827Z-post-gate.ts +17 -0
  187. package/src/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.ts +25 -0
  188. package/src/data-plane/server/db/migrations/index.ts +4 -0
  189. package/src/data-plane/server/db/tables/post-agg.ts +1 -0
  190. package/src/data-plane/server/db/tables/post-gate.ts +12 -0
  191. package/src/data-plane/server/db/tables/post.ts +3 -0
  192. package/src/data-plane/server/db/tables/quote.ts +15 -0
  193. package/src/data-plane/server/indexing/index.ts +7 -0
  194. package/src/data-plane/server/indexing/plugins/post-gate.ts +104 -0
  195. package/src/data-plane/server/indexing/plugins/post.ts +151 -16
  196. package/src/data-plane/server/indexing/plugins/thread-gate.ts +12 -0
  197. package/src/data-plane/server/routes/index.ts +2 -0
  198. package/src/data-plane/server/routes/interactions.ts +2 -1
  199. package/src/data-plane/server/routes/quotes.ts +32 -0
  200. package/src/data-plane/server/routes/records.ts +11 -1
  201. package/src/data-plane/server/util.ts +0 -8
  202. package/src/hydration/feed.ts +58 -12
  203. package/src/hydration/hydrator.ts +112 -24
  204. package/src/hydration/util.ts +0 -4
  205. package/src/lexicon/index.ts +12 -0
  206. package/src/lexicon/lexicons.ts +145 -0
  207. package/src/lexicon/types/app/bsky/embed/record.ts +19 -0
  208. package/src/lexicon/types/app/bsky/feed/defs.ts +2 -0
  209. package/src/lexicon/types/app/bsky/feed/getQuotes.ts +54 -0
  210. package/src/lexicon/types/app/bsky/feed/postgate.ts +47 -0
  211. package/src/lexicon/types/app/bsky/feed/threadgate.ts +2 -0
  212. package/src/proto/bsky_connect.ts +24 -0
  213. package/src/proto/bsky_pb.ts +289 -0
  214. package/src/util/uris.ts +31 -0
  215. package/src/views/index.ts +90 -35
  216. package/src/views/types.ts +1 -0
  217. package/src/views/util.ts +37 -7
  218. package/tests/__snapshots__/feed-generation.test.ts.snap +37 -0
  219. package/tests/admin/admin-auth.test.ts +15 -8
  220. package/tests/auth.test.ts +2 -1
  221. package/tests/data-plane/__snapshots__/indexing.test.ts.snap +18 -0
  222. package/tests/data-plane/handle-invalidation.test.ts +31 -5
  223. package/tests/data-plane/indexing.test.ts +139 -23
  224. package/tests/data-plane/thread-mutes.test.ts +41 -9
  225. package/tests/feed-generation.test.ts +150 -32
  226. package/tests/postgates.test.ts +186 -0
  227. package/tests/seed/feed-hidden-replies.ts +62 -0
  228. package/tests/seed/postgates.ts +56 -0
  229. package/tests/server.test.ts +1 -1
  230. package/tests/views/__snapshots__/author-feed.test.ts.snap +56 -0
  231. package/tests/views/__snapshots__/block-lists.test.ts.snap +6 -0
  232. package/tests/views/__snapshots__/blocks.test.ts.snap +10 -0
  233. package/tests/views/__snapshots__/list-feed.test.ts.snap +22 -0
  234. package/tests/views/__snapshots__/lists.test.ts.snap +145 -26
  235. package/tests/views/__snapshots__/mute-lists.test.ts.snap +8 -0
  236. package/tests/views/__snapshots__/mutes.test.ts.snap +6 -0
  237. package/tests/views/__snapshots__/posts.test.ts.snap +12 -0
  238. package/tests/views/__snapshots__/quotes.test.ts.snap +399 -0
  239. package/tests/views/__snapshots__/starter-packs.test.ts.snap +245 -4
  240. package/tests/views/__snapshots__/thread.test.ts.snap +50 -0
  241. package/tests/views/__snapshots__/timeline.test.ts.snap +170 -0
  242. package/tests/views/account-deactivation.test.ts +8 -2
  243. package/tests/views/actor-likes.test.ts +27 -6
  244. package/tests/views/actor-search.test.ts +5 -1
  245. package/tests/views/author-feed.test.ts +76 -21
  246. package/tests/views/block-lists.test.ts +201 -40
  247. package/tests/views/blocks.test.ts +245 -46
  248. package/tests/views/feed-hidden-replies.test.ts +246 -0
  249. package/tests/views/feed-view-post.test.ts +501 -0
  250. package/tests/views/follows.test.ts +133 -22
  251. package/tests/views/known-followers.test.ts +43 -7
  252. package/tests/views/labeler-service.test.ts +36 -6
  253. package/tests/views/likes.test.ts +8 -5
  254. package/tests/views/list-feed.test.ts +25 -4
  255. package/tests/views/lists.test.ts +73 -31
  256. package/tests/views/mute-lists.test.ts +101 -29
  257. package/tests/views/mutes.test.ts +77 -17
  258. package/tests/views/notifications.test.ts +141 -25
  259. package/tests/views/posts.test.ts +13 -2
  260. package/tests/views/profile.test.ts +37 -11
  261. package/tests/views/quotes.test.ts +105 -0
  262. package/tests/views/reposts.test.ts +31 -5
  263. package/tests/views/starter-packs.test.ts +83 -3
  264. package/tests/views/suggested-follows.test.ts +31 -5
  265. package/tests/views/suggestions.test.ts +37 -6
  266. package/tests/views/thread.test.ts +121 -20
  267. package/tests/views/threadgating.test.ts +128 -22
  268. package/tests/views/timeline.test.ts +67 -14
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AuthRequiredError,
3
+ parseReqNsid,
3
4
  verifyJwt as verifyServiceJwt,
4
5
  } from '@atproto/xrpc-server'
5
6
  import * as ui8 from 'uint8arrays'
@@ -17,6 +18,11 @@ type ReqCtx = {
17
18
  req: express.Request
18
19
  }
19
20
 
21
+ type StandardAuthOpts = {
22
+ skipAudCheck?: boolean
23
+ lxmCheck?: (method?: string) => boolean
24
+ }
25
+
20
26
  export enum RoleStatus {
21
27
  Valid,
22
28
  Invalid,
@@ -80,61 +86,55 @@ export class AuthVerifier {
80
86
  }
81
87
 
82
88
  // verifiers (arrow fns to preserve scope)
83
-
84
- standard = async (ctx: ReqCtx): Promise<StandardOutput> => {
85
- // @TODO remove! basic auth + did supported just for testing.
86
- if (isBasicToken(ctx.req)) {
87
- const aud = this.ownDid
88
- const iss = ctx.req.headers['appview-as-did']
89
- if (typeof iss !== 'string' || !iss.startsWith('did:')) {
90
- throw new AuthRequiredError('bad issuer')
91
- }
92
- if (!this.parseRoleCreds(ctx.req).admin) {
93
- throw new AuthRequiredError('bad credentials')
94
- }
95
- return {
96
- credentials: { type: 'standard', iss, aud },
89
+ standardOptionalParameterized =
90
+ (opts: StandardAuthOpts) =>
91
+ async (ctx: ReqCtx): Promise<StandardOutput | NullOutput> => {
92
+ // @TODO remove! basic auth + did supported just for testing.
93
+ if (isBasicToken(ctx.req)) {
94
+ const aud = this.ownDid
95
+ const iss = ctx.req.headers['appview-as-did']
96
+ if (typeof iss !== 'string' || !iss.startsWith('did:')) {
97
+ throw new AuthRequiredError('bad issuer')
98
+ }
99
+ if (!this.parseRoleCreds(ctx.req).admin) {
100
+ throw new AuthRequiredError('bad credentials')
101
+ }
102
+ return {
103
+ credentials: { type: 'standard', iss, aud },
104
+ }
105
+ } else if (isBearerToken(ctx.req)) {
106
+ const { iss, aud } = await this.verifyServiceJwt(ctx, {
107
+ lxmCheck: opts.lxmCheck,
108
+ iss: null,
109
+ aud: null,
110
+ })
111
+ if (!opts.skipAudCheck && !this.standardAudienceDids.has(aud)) {
112
+ throw new AuthRequiredError(
113
+ 'jwt audience does not match service did',
114
+ 'BadJwtAudience',
115
+ )
116
+ }
117
+ return {
118
+ credentials: {
119
+ type: 'standard',
120
+ iss,
121
+ aud,
122
+ },
123
+ }
124
+ } else {
125
+ return this.nullCreds()
97
126
  }
98
127
  }
99
- const { iss, aud } = await this.verifyServiceJwt(ctx, {
100
- aud: null,
101
- iss: null,
102
- })
103
- if (!this.standardAudienceDids.has(aud)) {
104
- throw new AuthRequiredError(
105
- 'jwt audience does not match service did',
106
- 'BadJwtAudience',
107
- )
108
- }
109
- return {
110
- credentials: {
111
- type: 'standard',
112
- iss,
113
- aud,
114
- },
115
- }
116
- }
117
128
 
118
- standardOptional = async (
119
- ctx: ReqCtx,
120
- ): Promise<StandardOutput | NullOutput> => {
121
- if (isBearerToken(ctx.req) || isBasicToken(ctx.req)) {
122
- return this.standard(ctx)
123
- }
124
- return this.nullCreds()
125
- }
129
+ standardOptional: (ctx: ReqCtx) => Promise<StandardOutput | NullOutput> =
130
+ this.standardOptionalParameterized({})
126
131
 
127
- standardOptionalAnyAud = async (
128
- ctx: ReqCtx,
129
- ): Promise<StandardOutput | NullOutput> => {
130
- if (!isBearerToken(ctx.req)) {
131
- return this.nullCreds()
132
+ standard = async (ctx: ReqCtx): Promise<StandardOutput> => {
133
+ const output = await this.standardOptional(ctx)
134
+ if (output.credentials.type === 'none') {
135
+ throw new AuthRequiredError(undefined, 'AuthMissing')
132
136
  }
133
- const { iss, aud } = await this.verifyServiceJwt(ctx, {
134
- aud: null,
135
- iss: null,
136
- })
137
- return { credentials: { type: 'standard', iss, aud } }
137
+ return output as StandardOutput
138
138
  }
139
139
 
140
140
  role = (ctx: ReqCtx): RoleOutput => {
@@ -215,7 +215,11 @@ export class AuthVerifier {
215
215
 
216
216
  async verifyServiceJwt(
217
217
  reqCtx: ReqCtx,
218
- opts: { aud: string | null; iss: string[] | null },
218
+ opts: {
219
+ iss: string[] | null
220
+ aud: string | null
221
+ lxmCheck?: (method?: string) => boolean
222
+ },
219
223
  ) {
220
224
  const getSigningKey = async (
221
225
  iss: string,
@@ -243,17 +247,40 @@ export class AuthVerifier {
243
247
  }
244
248
  return didKey
245
249
  }
250
+ const assertLxmCheck = () => {
251
+ const lxm = parseReqNsid(reqCtx.req)
252
+ if (
253
+ (opts.lxmCheck && !opts.lxmCheck(payload.lxm)) ||
254
+ (!opts.lxmCheck && payload.lxm !== lxm)
255
+ ) {
256
+ throw new AuthRequiredError(
257
+ payload.lxm !== undefined
258
+ ? `bad jwt lexicon method ("lxm"). must match: ${lxm}`
259
+ : `missing jwt lexicon method ("lxm"). must match: ${lxm}`,
260
+ 'BadJwtLexiconMethod',
261
+ )
262
+ }
263
+ }
246
264
 
247
265
  const jwtStr = bearerTokenFromReq(reqCtx.req)
248
266
  if (!jwtStr) {
249
267
  throw new AuthRequiredError('missing jwt', 'MissingJwt')
250
268
  }
269
+ // if validating additional scopes, skip scope check in initial validation & follow up afterwards
251
270
  const payload = await verifyServiceJwt(
252
271
  jwtStr,
253
272
  opts.aud,
254
273
  null,
255
274
  getSigningKey,
256
275
  )
276
+ if (
277
+ !payload.iss.endsWith('#atproto_labeler') ||
278
+ payload.lxm !== undefined
279
+ ) {
280
+ // @TODO currently permissive of labelers who dont set lxm yet.
281
+ // we'll allow ozone self-hosters to upgrade before removing this condition.
282
+ assertLxmCheck()
283
+ }
257
284
  return { iss: payload.iss, aud: payload.aud }
258
285
  }
259
286
 
@@ -6,7 +6,8 @@ import * as post from './tables/post'
6
6
  import * as postEmbed from './tables/post-embed'
7
7
  import * as postAgg from './tables/post-agg'
8
8
  import * as repost from './tables/repost'
9
- import * as threadGate from './tables/thread-gate'
9
+ import * as threadgate from './tables/thread-gate'
10
+ import * as postgate from './tables/post-gate'
10
11
  import * as feedItem from './tables/feed-item'
11
12
  import * as follow from './tables/follow'
12
13
  import * as like from './tables/like'
@@ -35,6 +36,7 @@ import * as taggedSuggestion from './tables/tagged-suggestion'
35
36
  import * as blobTakedown from './tables/blob-takedown'
36
37
  import * as labeler from './tables/labeler'
37
38
  import * as starterPack from './tables/starter-pack'
39
+ import * as quote from './tables/quote'
38
40
 
39
41
  export type DatabaseSchemaType = duplicateRecord.PartialDB &
40
42
  profile.PartialDB &
@@ -43,7 +45,8 @@ export type DatabaseSchemaType = duplicateRecord.PartialDB &
43
45
  postEmbed.PartialDB &
44
46
  postAgg.PartialDB &
45
47
  repost.PartialDB &
46
- threadGate.PartialDB &
48
+ threadgate.PartialDB &
49
+ postgate.PartialDB &
47
50
  feedItem.PartialDB &
48
51
  follow.PartialDB &
49
52
  like.PartialDB &
@@ -71,7 +74,8 @@ export type DatabaseSchemaType = duplicateRecord.PartialDB &
71
74
  blobTakedown.PartialDB &
72
75
  labeler.PartialDB &
73
76
  starterPack.PartialDB &
74
- taggedSuggestion.PartialDB
77
+ taggedSuggestion.PartialDB &
78
+ quote.PartialDB
75
79
 
76
80
  export type DatabaseSchema = Kysely<DatabaseSchemaType>
77
81
 
@@ -0,0 +1,12 @@
1
+ import { Kysely } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .alterTable('post_agg')
6
+ .addColumn('quoteCount', 'bigint', (col) => col.notNull().defaultTo(0))
7
+ .execute()
8
+ }
9
+
10
+ export async function down(db: Kysely<unknown>): Promise<void> {
11
+ await db.schema.alterTable('post_agg').dropColumn('quoteCount').execute()
12
+ }
@@ -0,0 +1,28 @@
1
+ import { Kysely, sql } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .createTable('quote')
6
+ .addColumn('uri', 'varchar', (col) => col.primaryKey())
7
+ .addColumn('cid', 'varchar', (col) => col.notNull())
8
+ .addColumn('subject', 'varchar', (col) => col.notNull())
9
+ .addColumn('subjectCid', 'varchar', (col) => col.notNull())
10
+ .addColumn('createdAt', 'varchar', (col) => col.notNull())
11
+ .addColumn('indexedAt', 'varchar', (col) => col.notNull())
12
+ .addColumn('sortAt', 'varchar', (col) =>
13
+ col
14
+ .generatedAlwaysAs(sql`least("createdAt", "indexedAt")`)
15
+ .stored()
16
+ .notNull(),
17
+ )
18
+ .execute()
19
+ await db.schema
20
+ .createIndex('quote_subject_cursor_idx')
21
+ .on('quote')
22
+ .columns(['subject', 'sortAt', 'cid'])
23
+ .execute()
24
+ }
25
+
26
+ export async function down(db: Kysely<unknown>): Promise<void> {
27
+ await db.schema.dropTable('quote').execute()
28
+ }
@@ -0,0 +1,17 @@
1
+ import { Kysely } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .createTable('post_gate')
6
+ .addColumn('uri', 'varchar', (col) => col.primaryKey())
7
+ .addColumn('cid', 'varchar', (col) => col.notNull())
8
+ .addColumn('creator', 'varchar', (col) => col.notNull())
9
+ .addColumn('postUri', 'varchar', (col) => col.notNull().unique())
10
+ .addColumn('createdAt', 'varchar', (col) => col.notNull())
11
+ .addColumn('indexedAt', 'varchar', (col) => col.notNull())
12
+ .execute()
13
+ }
14
+
15
+ export async function down(db: Kysely<unknown>): Promise<void> {
16
+ await db.schema.dropTable('post_gate').execute()
17
+ }
@@ -0,0 +1,25 @@
1
+ import { Kysely } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .alterTable('post')
6
+ .addColumn('violatesEmbeddingRules', 'boolean')
7
+ .execute()
8
+ await db.schema
9
+ .alterTable('post')
10
+ .addColumn('hasThreadGate', 'boolean')
11
+ .execute()
12
+ await db.schema
13
+ .alterTable('post')
14
+ .addColumn('hasPostGate', 'boolean')
15
+ .execute()
16
+ }
17
+
18
+ export async function down(db: Kysely<unknown>): Promise<void> {
19
+ await db.schema
20
+ .alterTable('post')
21
+ .dropColumn('violatesEmbeddingRules')
22
+ .execute()
23
+ await db.schema.alterTable('post').dropColumn('hasThreadGate').execute()
24
+ await db.schema.alterTable('post').dropColumn('hasPostGate').execute()
25
+ }
@@ -38,3 +38,7 @@ export * as _20240530T170337073Z from './20240530T170337073Z-account-deactivatio
38
38
  export * as _20240606T171229898Z from './20240606T171229898Z-thread-mutes'
39
39
  export * as _20240606T222548219Z from './20240606T222548219Z-starter-packs'
40
40
  export * as _20240719T203853939Z from './20240719T203853939Z-priority-notifs'
41
+ export * as _20240723T220700077Z from './20240723T220700077Z-quotes-post-aggs'
42
+ export * as _20240723T220703655Z from './20240723T220703655Z-quotes'
43
+ export * as _20240801T193939827Z from './20240801T193939827Z-post-gate'
44
+ export * as _20240808T224251220Z from './20240808T224251220Z-post-gate-flags'
@@ -7,6 +7,7 @@ export interface PostAgg {
7
7
  likeCount: Generated<number>
8
8
  replyCount: Generated<number>
9
9
  repostCount: Generated<number>
10
+ quoteCount: Generated<number>
10
11
  }
11
12
 
12
13
  export type PartialDB = {
@@ -0,0 +1,12 @@
1
+ const tableName = 'post_gate'
2
+
3
+ export interface Postgate {
4
+ uri: string
5
+ cid: string
6
+ creator: string
7
+ postUri: string
8
+ createdAt: string
9
+ indexedAt: string
10
+ }
11
+
12
+ export type PartialDB = { [tableName]: Postgate }
@@ -15,6 +15,9 @@ export interface Post {
15
15
  tags: string[] | null
16
16
  invalidReplyRoot: boolean | null
17
17
  violatesThreadGate: boolean | null
18
+ violatesEmbeddingRules: boolean | null
19
+ hasThreadGate: boolean | null
20
+ hasPostGate: boolean | null
18
21
  createdAt: string
19
22
  indexedAt: string
20
23
  sortAt: GeneratedAlways<string>
@@ -0,0 +1,15 @@
1
+ import { GeneratedAlways } from 'kysely'
2
+
3
+ const tableName = 'quote'
4
+
5
+ export interface Quote {
6
+ uri: string
7
+ cid: string
8
+ subject: string
9
+ subjectCid: string
10
+ createdAt: string
11
+ indexedAt: string
12
+ sortAt: GeneratedAlways<string>
13
+ }
14
+
15
+ export type PartialDB = { [tableName]: Quote }
@@ -17,6 +17,7 @@ import { Database } from '../db'
17
17
  import { Actor } from '../db/tables/actor'
18
18
  import * as Post from './plugins/post'
19
19
  import * as Threadgate from './plugins/thread-gate'
20
+ import * as Postgate from './plugins/post-gate'
20
21
  import * as Like from './plugins/like'
21
22
  import * as Repost from './plugins/repost'
22
23
  import * as Follow from './plugins/follow'
@@ -38,6 +39,7 @@ export class IndexingService {
38
39
  records: {
39
40
  post: Post.PluginType
40
41
  threadGate: Threadgate.PluginType
42
+ postGate: Postgate.PluginType
41
43
  like: Like.PluginType
42
44
  repost: Repost.PluginType
43
45
  follow: Follow.PluginType
@@ -60,6 +62,7 @@ export class IndexingService {
60
62
  this.records = {
61
63
  post: Post.makePlugin(this.db, this.background),
62
64
  threadGate: Threadgate.makePlugin(this.db, this.background),
65
+ postGate: Postgate.makePlugin(this.db, this.background),
63
66
  like: Like.makePlugin(this.db, this.background),
64
67
  repost: Repost.makePlugin(this.db, this.background),
65
68
  follow: Follow.makePlugin(this.db, this.background),
@@ -365,6 +368,10 @@ export class IndexingService {
365
368
  .deleteFrom('thread_gate')
366
369
  .where('creator', '=', did)
367
370
  .execute()
371
+ await this.db.db
372
+ .deleteFrom('post_gate')
373
+ .where('creator', '=', did)
374
+ .execute()
368
375
  // notifications
369
376
  await this.db.db
370
377
  .deleteFrom('notification')
@@ -0,0 +1,104 @@
1
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
2
+ import { InvalidRequestError } from '@atproto/xrpc-server'
3
+ import { CID } from 'multiformats/cid'
4
+ import * as Postgate from '../../../../lexicon/types/app/bsky/feed/postgate'
5
+ import * as lex from '../../../../lexicon/lexicons'
6
+ import { DatabaseSchema, DatabaseSchemaType } from '../../db/database-schema'
7
+ import { Database } from '../../db'
8
+ import RecordProcessor from '../processor'
9
+ import { BackgroundQueue } from '../../background'
10
+
11
+ const lexId = lex.ids.AppBskyFeedPostgate
12
+ type IndexedGate = DatabaseSchemaType['post_gate']
13
+
14
+ const insertFn = async (
15
+ db: DatabaseSchema,
16
+ uri: AtUri,
17
+ cid: CID,
18
+ obj: Postgate.Record,
19
+ timestamp: string,
20
+ ): Promise<IndexedGate | null> => {
21
+ const postUri = new AtUri(obj.post)
22
+ if (postUri.host !== uri.host || postUri.rkey !== uri.rkey) {
23
+ throw new InvalidRequestError(
24
+ 'Creator and rkey of post gate does not match its post',
25
+ )
26
+ }
27
+ const inserted = await db
28
+ .insertInto('post_gate')
29
+ .values({
30
+ uri: uri.toString(),
31
+ cid: cid.toString(),
32
+ creator: uri.host,
33
+ postUri: obj.post,
34
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
35
+ indexedAt: timestamp,
36
+ })
37
+ .onConflict((oc) => oc.doNothing())
38
+ .returningAll()
39
+ .executeTakeFirst()
40
+ await db
41
+ .updateTable('post')
42
+ .where('uri', '=', postUri.toString())
43
+ .set({ hasPostGate: true })
44
+ .executeTakeFirst()
45
+ return inserted || null
46
+ }
47
+
48
+ const findDuplicate = async (
49
+ db: DatabaseSchema,
50
+ _uri: AtUri,
51
+ obj: Postgate.Record,
52
+ ): Promise<AtUri | null> => {
53
+ const found = await db
54
+ .selectFrom('post_gate')
55
+ .where('postUri', '=', obj.post)
56
+ .selectAll()
57
+ .executeTakeFirst()
58
+ return found ? new AtUri(found.uri) : null
59
+ }
60
+
61
+ const notifsForInsert = () => {
62
+ return []
63
+ }
64
+
65
+ const deleteFn = async (
66
+ db: DatabaseSchema,
67
+ uri: AtUri,
68
+ ): Promise<IndexedGate | null> => {
69
+ const deleted = await db
70
+ .deleteFrom('post_gate')
71
+ .where('uri', '=', uri.toString())
72
+ .returningAll()
73
+ .executeTakeFirst()
74
+ if (deleted) {
75
+ await db
76
+ .updateTable('post')
77
+ .where('uri', '=', deleted.postUri)
78
+ .set({ hasPostGate: false })
79
+ .executeTakeFirst()
80
+ }
81
+ return deleted || null
82
+ }
83
+
84
+ const notifsForDelete = () => {
85
+ return { notifs: [], toDelete: [] }
86
+ }
87
+
88
+ export type PluginType = RecordProcessor<Postgate.Record, IndexedGate>
89
+
90
+ export const makePlugin = (
91
+ db: Database,
92
+ background: BackgroundQueue,
93
+ ): PluginType => {
94
+ return new RecordProcessor(db, background, {
95
+ lexId,
96
+ insertFn,
97
+ findDuplicate,
98
+ deleteFn,
99
+ notifsForInsert,
100
+ notifsForDelete,
101
+ })
102
+ }
103
+
104
+ export default makePlugin