Skip to content

Commit 47b4626

Browse files
authored
Improved perf for followers and following list
Ref: https://linear.app/ghost/issue/AP-1042 Ref: https://linear.app/ghost/issue/AP-1041 - Using a single endpoint for both local and remote follow lookups - GET /.ghost/activitypub/account/following/?handle=@username@domain'. - If it's a ghost account(internal account), this endpoint will return the data from our db. - If the account is an external account, it will fall back to calling the outbox and retrieving data from there.
1 parent d0e2ebb commit 47b4626

File tree

6 files changed

+232
-389
lines changed

6 files changed

+232
-389
lines changed

Diff for: src/app.ts

+5-13
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,6 @@ import {
106106
createGetFeedHandler,
107107
createGetNotificationsHandler,
108108
createGetPostHandler,
109-
createGetProfileFollowersHandler,
110-
createGetProfileFollowingHandler,
111109
createGetThreadHandler,
112110
createPostPublishedWebhookHandler,
113111
createSearchHandler,
@@ -954,16 +952,6 @@ app.get(
954952
requireRole(GhostRole.Owner, GhostRole.Administrator),
955953
spanWrapper(createSearchHandler(accountService)),
956954
);
957-
app.get(
958-
'/.ghost/activitypub/profile/:handle/followers',
959-
requireRole(GhostRole.Owner, GhostRole.Administrator),
960-
spanWrapper(createGetProfileFollowersHandler(accountService)),
961-
);
962-
app.get(
963-
'/.ghost/activitypub/profile/:handle/following',
964-
requireRole(GhostRole.Owner, GhostRole.Administrator),
965-
spanWrapper(createGetProfileFollowingHandler(accountService)),
966-
);
967955
app.get(
968956
'/.ghost/activitypub/thread/:post_ap_id',
969957
spanWrapper(createGetThreadHandler(postRepository, accountService)),
@@ -991,7 +979,11 @@ app.get(
991979
'/.ghost/activitypub/account/:handle/follows/:type',
992980
requireRole(GhostRole.Owner, GhostRole.Administrator),
993981
spanWrapper(
994-
createGetAccountFollowsHandler(accountRepository, accountFollowsView),
982+
createGetAccountFollowsHandler(
983+
accountRepository,
984+
accountFollowsView,
985+
fedifyContextFactory,
986+
),
995987
),
996988
);
997989
app.get(

Diff for: src/http/api/account.ts

+38-12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Federation } from '@fedify/fedify';
22
import type { Account } from 'account/account.entity';
33
import type { KnexAccountRepository } from 'account/account.repository.knex';
44
import type { AccountService } from 'account/account.service';
5+
import type { FedifyContextFactory } from 'activitypub/fedify-context.factory';
56
import type { AppContext, ContextData } from 'app';
67
import { isHandle } from 'helpers/activitypub/actor';
78
import { lookupAPIdByHandle } from 'lookup-helpers';
@@ -11,7 +12,10 @@ import {
1112
getAccountDTOFromAccount,
1213
} from './helpers/account';
1314
import type { AccountDTO } from './types';
14-
import type { AccountFollowsView } from './views/account.follows.view';
15+
import type {
16+
AccountFollows,
17+
AccountFollowsView,
18+
} from './views/account.follows.view';
1519

1620
/**
1721
* Default number of posts to return in a profile
@@ -112,6 +116,7 @@ export function createGetAccountHandler(
112116
export function createGetAccountFollowsHandler(
113117
accountRepository: KnexAccountRepository,
114118
accountFollowsView: AccountFollowsView,
119+
fedifyContextFactory: FedifyContextFactory,
115120
) {
116121
/**
117122
* Handle a request for a list of account follows
@@ -121,30 +126,51 @@ export function createGetAccountFollowsHandler(
121126
return async function handleGetAccountFollows(ctx: AppContext) {
122127
const site = ctx.get('site');
123128

124-
// Validate input
125129
const handle = ctx.req.param('handle') || '';
126-
127130
if (handle === '') {
128131
return new Response(null, { status: 400 });
129132
}
130133

131134
const type = ctx.req.param('type');
132-
133135
if (!['following', 'followers'].includes(type)) {
134136
return new Response(null, { status: 400 });
135137
}
136138

137139
const siteDefaultAccount = await accountRepository.getBySite(site);
138140

139-
// Get follows accounts and paginate
140-
const queryNext = ctx.req.query('next') || '0';
141-
const offset = Number.parseInt(queryNext);
141+
const queryNext = ctx.req.query('next');
142+
const next = queryNext ? decodeURIComponent(queryNext) : null;
142143

143-
const accountFollows = await accountFollowsView.getFollows(
144-
type,
145-
siteDefaultAccount,
146-
offset,
147-
);
144+
let accountFollows: AccountFollows;
145+
146+
if (handle === 'me') {
147+
accountFollows = await accountFollowsView.getFollowsByAccount(
148+
siteDefaultAccount,
149+
type,
150+
Number.parseInt(next || '0'),
151+
siteDefaultAccount,
152+
);
153+
} else {
154+
const ctx = fedifyContextFactory.getFedifyContext();
155+
const apId = await lookupAPIdByHandle(ctx, handle);
156+
157+
if (!apId) {
158+
return new Response(null, { status: 400 });
159+
}
160+
161+
const account = await accountRepository.getByApId(new URL(apId));
162+
if (!account) {
163+
return new Response(null, { status: 400 });
164+
}
165+
166+
accountFollows = await accountFollowsView.getFollowsByHandle(
167+
handle,
168+
account,
169+
type,
170+
next,
171+
siteDefaultAccount,
172+
);
173+
}
148174

149175
// Return response
150176
return new Response(

Diff for: src/http/api/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export * from './feed';
33
export * from './notification';
44
export * from './note';
55
export * from './post';
6-
export * from './profile';
76
export * from './search';
87
export * from './thread';
98
export * from './webhook';

0 commit comments

Comments
 (0)