forked from
pds.dad/pds-dash-fork
A fork of pds-dash for selfhosted.social
1import type { RequestHandler } from './$types';
2import type { At, AppBskyActorProfile } from '@atcute/client/lexicons';
3import { accountMetadataCache } from '$lib/server/sqlCache';
4import type { AccountMetadata } from '$lib/server/sqlCache';
5import { json } from '@sveltejs/kit';
6import { simpleFetchHandler, XRPC } from '@atcute/client';
7import '@atcute/bluesky/lexicons';
8import { Config } from '$lib/config';
9import type { Repo } from '@atproto/api/dist/client/types/com/atproto/sync/listRepos';
10import { blueskyHandleFromDid } from '$lib/server/identity';
11
12const rpc = new XRPC({
13 handler: simpleFetchHandler({
14 service: Config.PDS_URL,
15 }),
16});
17
18const getAccountMetadata = async (did: At.Did): Promise<AccountMetadata | null> => {
19 // Check cache first with the accounts:${did} key
20 const cacheKey = `metadata:${did}`;
21 const cachedResult = accountMetadataCache.get(cacheKey);
22 if (cachedResult) {
23 return cachedResult;
24 }
25
26 const account: AccountMetadata = {
27 did: did,
28 handle: '', // Guaranteed to be filled out later
29 displayName: '',
30 avatarCid: null,
31 };
32
33 try {
34 const { data } = await rpc.get('com.atproto.repo.getRecord', {
35 params: {
36 repo: did,
37 collection: 'app.bsky.actor.profile',
38 rkey: 'self',
39 },
40 });
41 const value = data.value as AppBskyActorProfile.Record;
42 account.displayName = value.displayName || '';
43 if (value.avatar) {
44 account.avatarCid = value.avatar.ref['$link'];
45 }
46 } catch (e) {
47 console.warn(`Error fetching profile for ${did}:`, e);
48 }
49
50 try {
51 account.handle = await blueskyHandleFromDid(did);
52 } catch (e) {
53 console.error(`Error fetching handle for ${did}:`, e);
54 return null;
55 }
56
57 // Cache the result with the accounts:${did} key
58 accountMetadataCache.set(cacheKey, account);
59 return account;
60};
61
62const getDidsFromPDS = async (): Promise<At.Did[]> => {
63 const { data } = await rpc.get('com.atproto.sync.listRepos', {
64 params: {
65 limit: 1000,
66 },
67 });
68 return data.repos.filter((x) => x.active).map((repo: Repo) => repo.did)
69 .reverse() as At.Did[];
70};
71
72const getAllMetadataFromPds = async (): Promise<AccountMetadata[]> => {
73 const dids = await getDidsFromPDS();
74 const metadata = await Promise.all(
75 dids.map(async (repo: `did:${string}:${string}`) => {
76 return await getAccountMetadata(repo);
77 }),
78 );
79 return metadata.filter((account) => account !== null) as AccountMetadata[];
80};
81
82export const GET: RequestHandler = async () => {
83 try {
84 const metadata = await getAllMetadataFromPds();
85 return json(metadata);
86 } catch (error) {
87 console.error('Error fetching all accounts:', error);
88 return json(
89 { error: 'Failed to fetch accounts' },
90 { status: 500 }
91 );
92 }
93};