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