packages/server/src/utils/resolver.ts 1.4 K raw
1
// PDS cache TTL: 1 hour (PDS endpoints rarely change)
2
const PDS_CACHE_TTL_MS = 60 * 60 * 1000;
3
4
function isPdsCacheValid(cachedAt: string | null): boolean {
5
  if (!cachedAt) return false;
6
  const cacheTime = new Date(cachedAt).getTime();
7
  return Date.now() - cacheTime < PDS_CACHE_TTL_MS;
8
}
9
10
export async function resolvePds(
11
  db: D1Database,
12
  did: string
13
): Promise<string | null> {
14
  const cached = await db
15
    .prepare("SELECT pds_endpoint, cached_at FROM pds_cache WHERE did = ?")
16
    .bind(did)
17
    .first<{ pds_endpoint: string; cached_at: string }>();
18
19
  if (cached && isPdsCacheValid(cached.cached_at)) {
20
    return cached.pds_endpoint;
21
  }
22
23
  try {
24
    const response = await fetch(`https://plc.directory/${did}`);
25
    if (!response.ok) return null;
26
27
    const doc = (await response.json()) as {
28
      service?: Array<{ id: string; type: string; serviceEndpoint: string }>;
29
    };
30
31
    const pds = doc.service?.find((s) => s.id === "#atproto_pds");
32
    if (pds?.serviceEndpoint) {
33
      await db
34
        .prepare(
35
          `INSERT INTO pds_cache (did, pds_endpoint, cached_at)
36
           VALUES (?, ?, datetime('now'))
37
           ON CONFLICT(did) DO UPDATE SET pds_endpoint = ?, cached_at = datetime('now')`
38
        )
39
        .bind(did, pds.serviceEndpoint, pds.serviceEndpoint)
40
        .run();
41
42
      return pds.serviceEndpoint;
43
    }
44
45
    return null;
46
  } catch {
47
    return null;
48
  }
49
}