Monorepo for Wisp.place. A static site hosting service built on top of the AT Protocol.
1import { Elysia } from 'elysia' 2import { requireAuth } from '../lib/wisp-auth' 3import { NodeOAuthClient } from '@atproto/oauth-client-node' 4import { Agent } from '@atproto/api' 5import { getSitesByDid, getDomainByDid, getCustomDomainsByDid, getWispDomainInfo } from '../lib/db' 6import { syncSitesFromPDS } from '../lib/sync-sites' 7 8export const userRoutes = (client: NodeOAuthClient) => 9 new Elysia({ prefix: '/api/user' }) 10 .derive(async ({ cookie }) => { 11 const auth = await requireAuth(client, cookie) 12 return { auth } 13 }) 14 .get('/status', async ({ auth }) => { 15 try { 16 // Check if user has any sites 17 const sites = await getSitesByDid(auth.did) 18 19 // Check if user has claimed a domain 20 const domain = await getDomainByDid(auth.did) 21 22 return { 23 did: auth.did, 24 hasSites: sites.length > 0, 25 hasDomain: !!domain, 26 domain: domain || null, 27 sitesCount: sites.length 28 } 29 } catch (err) { 30 console.error('user/status error', err) 31 throw new Error('Failed to get user status') 32 } 33 }) 34 .get('/info', async ({ auth }) => { 35 try { 36 // Get user's handle from AT Protocol 37 const agent = new Agent((url, init) => auth.session.fetchHandler(url, init)) 38 39 let handle = 'unknown' 40 try { 41 const profile = await agent.getProfile({ actor: auth.did }) 42 handle = profile.data.handle 43 } catch (err) { 44 console.error('Failed to fetch profile:', err) 45 } 46 47 return { 48 did: auth.did, 49 handle 50 } 51 } catch (err) { 52 console.error('user/info error', err) 53 throw new Error('Failed to get user info') 54 } 55 }) 56 .get('/sites', async ({ auth }) => { 57 try { 58 const sites = await getSitesByDid(auth.did) 59 return { sites } 60 } catch (err) { 61 console.error('user/sites error', err) 62 throw new Error('Failed to get sites') 63 } 64 }) 65 .get('/domains', async ({ auth }) => { 66 try { 67 // Get wisp.place subdomain with mapping 68 const wispDomainInfo = await getWispDomainInfo(auth.did) 69 70 // Get custom domains 71 const customDomains = await getCustomDomainsByDid(auth.did) 72 73 return { 74 wispDomain: wispDomainInfo ? { 75 domain: wispDomainInfo.domain, 76 rkey: wispDomainInfo.rkey || null 77 } : null, 78 customDomains 79 } 80 } catch (err) { 81 console.error('user/domains error', err) 82 throw new Error('Failed to get domains') 83 } 84 }) 85 .post('/sync', async ({ auth }) => { 86 try { 87 console.log('[User] Manual sync requested for', auth.did) 88 const result = await syncSitesFromPDS(auth.did, auth.session) 89 90 return { 91 success: true, 92 synced: result.synced, 93 errors: result.errors 94 } 95 } catch (err) { 96 console.error('user/sync error', err) 97 throw new Error('Failed to sync sites') 98 } 99 })