Sifa professional network API (Fastify, AT Protocol, Jetstream)
sifa.id/
1import type { Database } from '../db/index.js';
2import { externalAccountVerifications } from '../db/schema/index.js';
3import { logger } from '../logger.js';
4
5const FETCH_TIMEOUT = 10000;
6
7const VERIFIABLE_PLATFORMS = new Set(['rss', 'fediverse', 'website', 'github']);
8
9export function isVerifiablePlatform(platform: string): boolean {
10 return VERIFIABLE_PLATFORMS.has(platform);
11}
12
13export async function verifyRelMe(
14 url: string,
15 sifaProfileUrl: string,
16 did: string,
17): Promise<boolean> {
18 try {
19 const response = await fetch(url, {
20 signal: AbortSignal.timeout(FETCH_TIMEOUT),
21 headers: { 'User-Agent': 'Sifa/1.0 (+https://sifa.id)' },
22 redirect: 'follow',
23 });
24
25 if (!response.ok) return false;
26
27 const html = await response.text();
28 const relMeLinks = html.match(/<a[^>]+rel=["'][^"']*me[^"']*["'][^>]*>/gi) ?? [];
29 const relMeLinkElements = html.match(/<link[^>]+rel=["'][^"']*me[^"']*["'][^>]*>/gi) ?? [];
30
31 const allLinks = [...relMeLinks, ...relMeLinkElements];
32
33 for (const link of allLinks) {
34 const hrefMatch = link.match(/href=["']([^"']+)["']/i);
35 if (!hrefMatch) continue;
36 const href = hrefMatch[1];
37 if (!href) continue;
38
39 if (href.includes(sifaProfileUrl) || href.includes(did)) {
40 return true;
41 }
42 }
43
44 return false;
45 } catch (err) {
46 logger.warn({ err, url }, 'rel=me verification failed');
47 return false;
48 }
49}
50
51export async function checkAndStoreVerification(
52 db: Database,
53 did: string,
54 url: string,
55 platform: string,
56 sifaHandle: string,
57): Promise<boolean> {
58 if (!isVerifiablePlatform(platform)) {
59 return false;
60 }
61
62 const sifaProfileUrl = `sifa.id/p/${sifaHandle}`;
63 const verified = await verifyRelMe(url, sifaProfileUrl, did);
64
65 await db
66 .insert(externalAccountVerifications)
67 .values({
68 did,
69 url,
70 verified,
71 verifiedVia: verified ? 'rel-me' : null,
72 checkedAt: new Date(),
73 })
74 .onConflictDoUpdate({
75 target: [externalAccountVerifications.did, externalAccountVerifications.url],
76 set: {
77 verified,
78 verifiedVia: verified ? 'rel-me' : null,
79 checkedAt: new Date(),
80 },
81 });
82
83 return verified;
84}