Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
96
fork

Configure Feed

Select the types of activity you want to include in your feed.

at bbcbdb06cdb2e396d9bd4df47d00881cbe99cb50 90 lines 2.5 kB view raw
1import { Agent } from '@atproto/api' 2import type { OAuthSession } from '@atproto/oauth-client-node' 3import { upsertSite } from './db' 4 5/** 6 * Sync sites from user's PDS into the database cache 7 * - Fetches all place.wisp.fs records from AT Protocol repo 8 * - Validates record structure 9 * - Backfills into sites table 10 */ 11export async function syncSitesFromPDS( 12 did: string, 13 session: OAuthSession 14): Promise<{ synced: number; errors: string[] }> { 15 console.log(`[Sync] Starting site sync for ${did}`) 16 17 const agent = new Agent((url, init) => session.fetchHandler(url, init)) 18 const errors: string[] = [] 19 let synced = 0 20 21 try { 22 // List all records in the place.wisp.fs collection 23 console.log('[Sync] Fetching place.wisp.fs records from PDS') 24 const records = await agent.com.atproto.repo.listRecords({ 25 repo: did, 26 collection: 'place.wisp.fs', 27 limit: 100 // Adjust if users might have more sites 28 }) 29 30 console.log(`[Sync] Found ${records.data.records.length} records`) 31 32 // Process each record 33 for (const record of records.data.records) { 34 try { 35 const { uri, value } = record 36 37 // Extract rkey from URI (at://did/collection/rkey) 38 const rkey = uri.split('/').pop() 39 if (!rkey) { 40 errors.push(`Invalid URI format: ${uri}`) 41 continue 42 } 43 44 // Validate record structure 45 if (!value || typeof value !== 'object') { 46 errors.push(`Invalid record value for ${rkey}`) 47 continue 48 } 49 50 const siteValue = value as any 51 52 // Check for required fields 53 if (siteValue.$type !== 'place.wisp.fs') { 54 errors.push( 55 `Invalid $type for ${rkey}: ${siteValue.$type}` 56 ) 57 continue 58 } 59 60 if (!siteValue.site || typeof siteValue.site !== 'string') { 61 errors.push(`Missing or invalid site name for ${rkey}`) 62 continue 63 } 64 65 // Upsert into database 66 const displayName = siteValue.site 67 await upsertSite(did, rkey, displayName) 68 69 console.log( 70 `[Sync] ✓ Synced site: ${displayName} (${rkey})` 71 ) 72 synced++ 73 } catch (err) { 74 const errorMsg = `Error processing record: ${err instanceof Error ? err.message : 'Unknown error'}` 75 console.error(`[Sync] ${errorMsg}`) 76 errors.push(errorMsg) 77 } 78 } 79 80 console.log( 81 `[Sync] Complete: ${synced} synced, ${errors.length} errors` 82 ) 83 return { synced, errors } 84 } catch (err) { 85 const errorMsg = `Failed to fetch records from PDS: ${err instanceof Error ? err.message : 'Unknown error'}` 86 console.error(`[Sync] ${errorMsg}`) 87 errors.push(errorMsg) 88 return { synced, errors } 89 } 90}