Sifa professional network API (Fastify, AT Protocol, Jetstream) sifa.id/
at main 84 lines 2.2 kB view raw
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}