diff --git a/packages/bsky/src/api/app/bsky/actor/getProfile.ts b/packages/bsky/src/api/app/bsky/actor/getProfile.ts index b7860791507..a14718e394e 100644 --- a/packages/bsky/src/api/app/bsky/actor/getProfile.ts +++ b/packages/bsky/src/api/app/bsky/actor/getProfile.ts @@ -16,14 +16,11 @@ export default function (server: Server, ctx: AppContext) { server.app.bsky.actor.getProfile({ auth: ctx.authVerifier.optionalStandardOrRole, handler: async ({ auth, params, req, res }) => { - const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth) + const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth) const labelers = ctx.reqLabelers(req) - const hydrateCtx = { labelers, viewer } + const hydrateCtx = { labelers, viewer, includeTakedowns } - const result = await getProfile( - { ...params, hydrateCtx, canViewTakedowns }, - ctx, - ) + const result = await getProfile({ ...params, hydrateCtx }, ctx) const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer) setRepoRev(res, repoRev) @@ -54,11 +51,7 @@ const hydration = async (input: { skeleton: SkeletonState }) => { const { ctx, params, skeleton } = input - return ctx.hydrator.hydrateProfilesDetailed( - [skeleton.did], - params.hydrateCtx, - true, - ) + return ctx.hydrator.hydrateProfilesDetailed([skeleton.did], params.hydrateCtx) } const presentation = (input: { @@ -72,7 +65,7 @@ const presentation = (input: { if (!profile) { throw new InvalidRequestError('Profile not found') } else if ( - !params.canViewTakedowns && + !params.hydrateCtx.includeTakedowns && ctx.views.actorIsTakendown(skeleton.did, hydration) ) { throw new InvalidRequestError( @@ -90,7 +83,6 @@ type Context = { type Params = QueryParams & { hydrateCtx: HydrateCtx - canViewTakedowns: boolean } type SkeletonState = { did: string } diff --git a/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts b/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts index db312373ea0..fa2edd2d09e 100644 --- a/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts +++ b/packages/bsky/src/api/app/bsky/actor/getSuggestions.ts @@ -64,11 +64,7 @@ const hydration = async (input: { skeleton: Skeleton }) => { const { ctx, params, skeleton } = input - return ctx.hydrator.hydrateProfilesDetailed( - skeleton.dids, - params.hydrateCtx, - true, - ) + return ctx.hydrator.hydrateProfilesDetailed(skeleton.dids, params.hydrateCtx) } const noBlocksOrMutes = (input: { diff --git a/packages/bsky/src/api/app/bsky/feed/getAuthorFeed.ts b/packages/bsky/src/api/app/bsky/feed/getAuthorFeed.ts index a8174b164fc..01fd33272c7 100644 --- a/packages/bsky/src/api/app/bsky/feed/getAuthorFeed.ts +++ b/packages/bsky/src/api/app/bsky/feed/getAuthorFeed.ts @@ -28,14 +28,11 @@ export default function (server: Server, ctx: AppContext) { server.app.bsky.feed.getAuthorFeed({ auth: ctx.authVerifier.optionalStandardOrRole, handler: async ({ params, auth, req, res }) => { - const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth) + const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth) const labelers = ctx.reqLabelers(req) - const hydrateCtx = { labelers, viewer } + const hydrateCtx = { labelers, viewer, includeTakedowns } - const result = await getAuthorFeed( - { ...params, hydrateCtx, includeTakedowns: canViewTakedowns }, - ctx, - ) + const result = await getAuthorFeed({ ...params, hydrateCtx }, ctx) const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer) setRepoRev(res, repoRev) @@ -66,7 +63,7 @@ export const skeleton = async (inputs: { } const actors = await ctx.hydrator.actor.getActors( [did], - params.includeTakedowns, + params.hydrateCtx.includeTakedowns, ) const actor = actors.get(did) if (!actor) { @@ -100,11 +97,7 @@ const hydration = async (inputs: { }): Promise => { const { ctx, params, skeleton } = inputs const [feedPostState, profileViewerState] = await Promise.all([ - ctx.hydrator.hydrateFeedItems( - skeleton.items, - params.hydrateCtx, - params.includeTakedowns, - ), + ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx), ctx.hydrator.hydrateProfileViewers([skeleton.actor.did], params.hydrateCtx), ]) return mergeStates(feedPostState, profileViewerState) @@ -160,7 +153,6 @@ type Context = { type Params = QueryParams & { hydrateCtx: HydrateCtx - includeTakedowns: boolean } type Skeleton = { diff --git a/packages/bsky/src/api/app/bsky/graph/getFollowers.ts b/packages/bsky/src/api/app/bsky/graph/getFollowers.ts index b2d3288b4bd..267aad6ec2a 100644 --- a/packages/bsky/src/api/app/bsky/graph/getFollowers.ts +++ b/packages/bsky/src/api/app/bsky/graph/getFollowers.ts @@ -29,14 +29,11 @@ export default function (server: Server, ctx: AppContext) { server.app.bsky.graph.getFollowers({ auth: ctx.authVerifier.optionalStandardOrRole, handler: async ({ params, auth, req }) => { - const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth) + const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth) const labelers = ctx.reqLabelers(req) - const hydrateCtx = { labelers, viewer } + const hydrateCtx = { labelers, viewer, includeTakedowns } - const result = await getFollowers( - { ...params, hydrateCtx, canViewTakedowns }, - ctx, - ) + const result = await getFollowers({ ...params, hydrateCtx }, ctx) return { encoding: 'application/json', @@ -84,7 +81,6 @@ const hydration = async ( const profileState = await ctx.hydrator.hydrateProfiles( dids, params.hydrateCtx, - params.canViewTakedowns, ) return mergeStates(followState, profileState) } @@ -111,13 +107,16 @@ const presentation = ( ctx.views.actorIsTakendown(did, hydration) const subject = ctx.views.profile(subjectDid, hydration) - if (!subject || (!params.canViewTakedowns && isTakendown(subjectDid))) { + if ( + !subject || + (!params.hydrateCtx.includeTakedowns && isTakendown(subjectDid)) + ) { throw new InvalidRequestError(`Actor not found: ${params.actor}`) } const followers = mapDefined(followUris, (followUri) => { const followerDid = didFromUri(followUri) - if (!params.canViewTakedowns && isTakendown(followerDid)) { + if (!params.hydrateCtx.includeTakedowns && isTakendown(followerDid)) { return } return ctx.views.profile(didFromUri(followUri), hydration) @@ -133,7 +132,6 @@ type Context = { type Params = QueryParams & { hydrateCtx: HydrateCtx - canViewTakedowns: boolean } type SkeletonState = { diff --git a/packages/bsky/src/api/app/bsky/graph/getFollows.ts b/packages/bsky/src/api/app/bsky/graph/getFollows.ts index d1840f9a19a..f7c35b710ce 100644 --- a/packages/bsky/src/api/app/bsky/graph/getFollows.ts +++ b/packages/bsky/src/api/app/bsky/graph/getFollows.ts @@ -23,15 +23,12 @@ export default function (server: Server, ctx: AppContext) { server.app.bsky.graph.getFollows({ auth: ctx.authVerifier.optionalStandardOrRole, handler: async ({ params, auth, req }) => { - const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth) + const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth) const labelers = ctx.reqLabelers(req) - const hydrateCtx = { labelers, viewer } + const hydrateCtx = { labelers, viewer, includeTakedowns } // @TODO ensure canViewTakedowns gets threaded through and applied properly - const result = await getFollows( - { ...params, hydrateCtx, canViewTakedowns }, - ctx, - ) + const result = await getFollows({ ...params, hydrateCtx }, ctx) return { encoding: 'application/json', @@ -79,7 +76,6 @@ const hydration = async ( const profileState = await ctx.hydrator.hydrateProfiles( dids, params.hydrateCtx, - params.canViewTakedowns, ) return mergeStates(followState, profileState) } @@ -108,14 +104,17 @@ const presentation = ( ctx.views.actorIsTakendown(did, hydration) const subject = ctx.views.profile(subjectDid, hydration) - if (!subject || (!params.canViewTakedowns && isTakendown(subjectDid))) { + if ( + !subject || + (!params.hydrateCtx.includeTakedowns && isTakendown(subjectDid)) + ) { throw new InvalidRequestError(`Actor not found: ${params.actor}`) } const follows = mapDefined(followUris, (followUri) => { const followDid = hydration.follows?.get(followUri)?.record.subject if (!followDid) return - if (!params.canViewTakedowns && isTakendown(followDid)) { + if (!params.hydrateCtx.includeTakedowns && isTakendown(followDid)) { return } return ctx.views.profile(followDid, hydration) @@ -131,7 +130,6 @@ type Context = { type Params = QueryParams & { hydrateCtx: HydrateCtx - canViewTakedowns: boolean } type SkeletonState = { diff --git a/packages/bsky/src/auth-verifier.ts b/packages/bsky/src/auth-verifier.ts index 2936b1cd28b..44bde797932 100644 --- a/packages/bsky/src/auth-verifier.ts +++ b/packages/bsky/src/auth-verifier.ts @@ -258,7 +258,7 @@ export class AuthVerifier { ) { const viewer = creds.credentials.type === 'standard' ? creds.credentials.iss : null - const canViewTakedowns = + const includeTakedowns = (creds.credentials.type === 'role' && creds.credentials.admin) || creds.credentials.type === 'mod_service' || (creds.credentials.type === 'standard' && @@ -269,7 +269,7 @@ export class AuthVerifier { return { viewer, - canViewTakedowns, + includeTakedowns, canPerformTakedown, } } diff --git a/packages/bsky/src/hydration/hydrator.ts b/packages/bsky/src/hydration/hydrator.ts index d3012814eb1..f7b68ef17ea 100644 --- a/packages/bsky/src/hydration/hydrator.ts +++ b/packages/bsky/src/hydration/hydrator.ts @@ -49,6 +49,7 @@ import { export type HydrateCtx = { labelers: string[] viewer: string | null + includeTakedowns?: boolean } export type HydrationState = { @@ -132,14 +133,13 @@ export class Hydrator { async hydrateProfiles( dids: string[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const [actors, labels, profileViewersState] = await Promise.all([ - this.actor.getActors(dids, includeTakedowns), + this.actor.getActors(dids, ctx.includeTakedowns), this.label.getLabelsForSubjects(labelSubjectsForDid(dids), ctx.labelers), this.hydrateProfileViewers(dids, ctx), ]) - if (!includeTakedowns) { + if (!ctx.includeTakedowns) { actionTakedownLabels(dids, actors, labels) } return mergeStates(profileViewersState ?? {}, { @@ -156,9 +156,8 @@ export class Hydrator { async hydrateProfilesBasic( dids: string[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { - return this.hydrateProfiles(dids, ctx, includeTakedowns) + return this.hydrateProfiles(dids, ctx) } // app.bsky.actor.defs#profileViewDetailed @@ -168,10 +167,9 @@ export class Hydrator { async hydrateProfilesDetailed( dids: string[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const [state, profileAggs] = await Promise.all([ - this.hydrateProfiles(dids, ctx, includeTakedowns), + this.hydrateProfiles(dids, ctx), this.actor.getProfileAggregates(dids), ]) return { @@ -183,18 +181,10 @@ export class Hydrator { // app.bsky.graph.defs#listView // - list // - profile basic - async hydrateLists( - uris: string[], - ctx: HydrateCtx, - includeTakedowns = false, - ): Promise { + async hydrateLists(uris: string[], ctx: HydrateCtx): Promise { const [listsState, profilesState] = await Promise.all([ - await this.hydrateListsBasic(uris, ctx, includeTakedowns), - await this.hydrateProfilesBasic( - uris.map(didFromUri), - ctx, - includeTakedowns, - ), + await this.hydrateListsBasic(uris, ctx), + await this.hydrateProfilesBasic(uris.map(didFromUri), ctx), ]) return mergeStates(listsState, profilesState) @@ -205,7 +195,6 @@ export class Hydrator { async hydrateListsBasic( uris: string[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const [lists, listViewers, labels] = await Promise.all([ this.graph.getLists(uris), @@ -213,7 +202,7 @@ export class Hydrator { this.label.getLabelsForSubjects(uris, ctx.labelers), ]) - if (!includeTakedowns) { + if (!ctx.includeTakedowns) { actionTakedownLabels(uris, lists, labels) } @@ -255,13 +244,12 @@ export class Hydrator { async hydratePosts( refs: ItemRef[], ctx: HydrateCtx, - includeTakedowns = false, state: HydrationState = {}, ): Promise { const uris = refs.map((ref) => ref.uri) const postsLayer0 = await this.feed.getPosts( uris, - includeTakedowns, + ctx.includeTakedowns, state.posts, ) // first level embeds plus thread roots we haven't fetched yet @@ -271,7 +259,7 @@ export class Hydrator { const postUrisLayer1 = urisLayer1ByCollection.get(ids.AppBskyFeedPost) ?? [] const postsLayer1 = await this.feed.getPosts( [...postUrisLayer1, ...additionalRootUris], - includeTakedowns, + ctx.includeTakedowns, ) // second level embeds, ignoring any additional root uris we mixed-in to the previous layer const urisLayer2 = nestedRecordUrisFromPosts(postsLayer1, postUrisLayer1) @@ -284,7 +272,7 @@ export class Hydrator { } } const [postsLayer2, threadgates] = await Promise.all([ - this.feed.getPosts(postUrisLayer2, includeTakedowns), + this.feed.getPosts(postUrisLayer2, ctx.includeTakedowns), this.feed.getThreadgatesForPosts([...threadRootUris.values()]), ]) // collect list/feedgen embeds, lists in threadgates, post record hydration @@ -318,16 +306,12 @@ export class Hydrator { ctx.viewer ? this.feed.getPostViewerStates(refs, ctx.viewer) : undefined, this.label.getLabelsForSubjects(allPostUris, ctx.labelers), this.hydratePostBlocks(posts), - this.hydrateProfiles(allPostUris.map(didFromUri), ctx, includeTakedowns), - this.hydrateLists( - [...nestedListUris, ...gateListUris], - ctx, - includeTakedowns, - ), - this.hydrateFeedGens(nestedFeedGenUris, ctx, includeTakedowns), - this.hydrateLabelers(nestedLabelerDids, ctx, includeTakedowns), + this.hydrateProfiles(allPostUris.map(didFromUri), ctx), + this.hydrateLists([...nestedListUris, ...gateListUris], ctx), + this.hydrateFeedGens(nestedFeedGenUris, ctx), + this.hydrateLabelers(nestedLabelerDids, ctx), ]) - if (!includeTakedowns) { + if (!ctx.includeTakedowns) { actionTakedownLabels(allPostUris, posts, labels) } // combine all hydration state @@ -402,14 +386,13 @@ export class Hydrator { async hydrateFeedItems( items: FeedItem[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const postUris = items.map((item) => item.post.uri) const repostUris = mapDefined(items, (item) => item.repost?.uri) const [posts, reposts, repostProfileState] = await Promise.all([ - this.feed.getPosts(postUris, includeTakedowns), - this.feed.getReposts(repostUris, includeTakedowns), - this.hydrateProfiles(repostUris.map(didFromUri), ctx, includeTakedowns), + this.feed.getPosts(postUris, ctx.includeTakedowns), + this.feed.getReposts(repostUris, ctx.includeTakedowns), + this.hydrateProfiles(repostUris.map(didFromUri), ctx), ]) const postAndReplyRefs: ItemRef[] = [] posts.forEach((post, uri) => { @@ -419,12 +402,7 @@ export class Hydrator { postAndReplyRefs.push(post.record.reply.root, post.record.reply.parent) } }) - const postState = await this.hydratePosts( - postAndReplyRefs, - ctx, - includeTakedowns, - { posts }, - ) + const postState = await this.hydratePosts(postAndReplyRefs, ctx, { posts }) return mergeManyStates(postState, repostProfileState, { reposts, ctx, @@ -444,9 +422,8 @@ export class Hydrator { async hydrateThreadPosts( refs: ItemRef[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { - return this.hydratePosts(refs, ctx, includeTakedowns) + return this.hydratePosts(refs, ctx) } // app.bsky.feed.defs#generatorView @@ -456,11 +433,10 @@ export class Hydrator { async hydrateFeedGens( uris: string[], // @TODO any way to get refs here? ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const [feedgens, feedgenAggs, feedgenViewers, profileState, labels] = await Promise.all([ - this.feed.getFeedGens(uris), + this.feed.getFeedGens(uris, ctx.includeTakedowns), this.feed.getFeedGenAggregates(uris.map((uri) => ({ uri }))), ctx.viewer ? this.feed.getFeedGenViewerStates(uris, ctx.viewer) @@ -468,7 +444,7 @@ export class Hydrator { this.hydrateProfiles(uris.map(didFromUri), ctx), this.label.getLabelsForSubjects(uris, ctx.labelers), ]) - if (!includeTakedowns) { + if (!ctx.includeTakedowns) { actionTakedownLabels(uris, feedgens, labels) } return mergeStates(profileState, { @@ -569,16 +545,15 @@ export class Hydrator { async hydrateLabelers( dids: string[], ctx: HydrateCtx, - includeTakedowns = false, ): Promise { const [labelers, labelerAggs, labelerViewers, profileState] = await Promise.all([ - this.label.getLabelers(dids, includeTakedowns), + this.label.getLabelers(dids), this.label.getLabelerAggregates(dids), ctx.viewer ? this.label.getLabelerViewerStates(dids, ctx.viewer) : undefined, - this.hydrateProfiles(dids.map(didFromUri), ctx, includeTakedowns), + this.hydrateProfiles(dids.map(didFromUri), ctx), ]) return mergeStates(profileState, { labelers, diff --git a/packages/bsky/src/views/index.ts b/packages/bsky/src/views/index.ts index 4c573d6ace6..67a9bee51ef 100644 --- a/packages/bsky/src/views/index.ts +++ b/packages/bsky/src/views/index.ts @@ -134,8 +134,8 @@ export class Views { 'self', ).toString() const labels = [ - ...(state.labels?.get(did) ?? []), - ...(state.labels?.get(profileUri) ?? []), + ...(state.labels?.get(did)?.labels ?? []), + ...(state.labels?.get(profileUri)?.labels ?? []), ...this.selfLabels({ uri: profileUri, cid: actor.profileCid?.toString(), @@ -277,7 +277,7 @@ export class Views { const uri = AtUri.make(did, ids.AppBskyLabelerService, 'self').toString() const labels = [ - ...(state.labels?.get(uri) ?? []), + ...(state.labels?.get(uri)?.labels ?? []), ...this.selfLabels({ uri, cid: labeler.cid.toString(), @@ -402,7 +402,7 @@ export class Views { parsedUri.rkey, ).toString() const labels = [ - ...(state.labels?.get(uri) ?? []), + ...(state.labels?.get(uri)?.labels ?? []), ...this.selfLabels({ uri, cid: post.cid, @@ -880,7 +880,7 @@ export class Views { recordInfo = state.follows?.get(notif.uri) } if (!recordInfo) return - const labels = state.labels?.get(notif.uri) ?? [] + const labels = state.labels?.get(notif.uri)?.labels ?? [] const selfLabels = this.selfLabels({ uri: notif.uri, cid: recordInfo.cid,