Skip to content

Commit b3f5927

Browse files
authored
Reduced number of database queries in dispatchers (#1512)
ref https://linear.app/ghost/issue/BER-3012 - in [dispatchers.ts](https://github.com/TryGhost/ActivityPub/blob/main/src/dispatchers.ts), we currently make 3 database queries to load the host site, account and user - with this change, we will make a single database query to load all the necessary host data - note: in `keypairDispatcher`, we do an additional database query to retrieve the public/private key pair, as a tradeoff between safety and performance gains. We don't want to expose the public/private key pair to the Account entity, as there is a risk to inadvertently expose the private key - instead, we chose to do an additional database query
1 parent 1b030a2 commit b3f5927

File tree

5 files changed

+1451
-264
lines changed

5 files changed

+1451
-264
lines changed

src/account/account.repository.knex.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,24 @@ export class KnexAccountRepository {
372372
return this.mapRowToAccountEntity(accountRow);
373373
}
374374

375+
async getKeyPair(
376+
accountId: number,
377+
): Promise<{ publicKey: string | null; privateKey: string | null } | null> {
378+
const row = await this.db('accounts')
379+
.select('ap_public_key', 'ap_private_key')
380+
.where({ id: accountId })
381+
.first();
382+
383+
if (!row) {
384+
return null;
385+
}
386+
387+
return {
388+
publicKey: row.ap_public_key,
389+
privateKey: row.ap_private_key,
390+
};
391+
}
392+
375393
private async mapRowToAccountEntity(row: AccountRow): Promise<Account> {
376394
if (!row.uuid) {
377395
row.uuid = randomUUID();

src/account/account.service.integration.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,4 +1397,35 @@ describe('AccountService', () => {
13971397
expect(backoff).toBeNull();
13981398
});
13991399
});
1400+
1401+
describe('getKeyPair', () => {
1402+
it('returns keypair with both keys for an internal account', async () => {
1403+
const [account] = await fixtureManager.createInternalAccount();
1404+
1405+
const result = await service.getKeyPair(account.id);
1406+
1407+
expect(result).toStrictEqual([
1408+
null,
1409+
{
1410+
publicKey: expect.any(String),
1411+
privateKey: expect.any(String),
1412+
},
1413+
]);
1414+
});
1415+
1416+
it('returns key-pair-not-found error for an external account', async () => {
1417+
// An external account does not have a private key
1418+
const account = await fixtureManager.createExternalAccount();
1419+
1420+
const result = await service.getKeyPair(account.id);
1421+
1422+
expect(result).toStrictEqual(['key-pair-not-found', null]);
1423+
});
1424+
1425+
it('returns account-not-found error when account does not exist', async () => {
1426+
const result = await service.getKeyPair(999999);
1427+
1428+
expect(result).toStrictEqual(['account-not-found', null]);
1429+
});
1430+
});
14001431
});

src/account/account.service.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,4 +867,28 @@ export class AccountService {
867867
backoffSeconds: backoff.backoff_seconds,
868868
};
869869
}
870+
871+
async getKeyPair(
872+
accountId: number,
873+
): Promise<
874+
Result<
875+
{ publicKey: string; privateKey: string },
876+
'account-not-found' | 'key-pair-not-found'
877+
>
878+
> {
879+
const account = await this.accountRepository.getKeyPair(accountId);
880+
881+
if (!account) {
882+
return error('account-not-found');
883+
}
884+
885+
if (!account.publicKey || !account.privateKey) {
886+
return error('key-pair-not-found');
887+
}
888+
889+
return ok({
890+
publicKey: account.publicKey,
891+
privateKey: account.privateKey,
892+
});
893+
}
870894
}

0 commit comments

Comments
 (0)