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 fc71b497b435985f7e4882051fd87f89ec0d7815 120 lines 3.2 kB view raw
1import { Elysia, t } 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, getDomainsBySite, getAllWispDomains } from '../lib/db' 6import { syncSitesFromPDS } from '../lib/sync-sites' 7import { logger } from '../lib/logger' 8 9export const userRoutes = (client: NodeOAuthClient, cookieSecret: string) => 10 new Elysia({ 11 prefix: '/api/user', 12 cookie: { 13 secrets: cookieSecret, 14 sign: ['did'] 15 } 16 }) 17 .derive(async ({ cookie }) => { 18 const auth = await requireAuth(client, cookie) 19 return { auth } 20 }) 21 .get('/status', async ({ auth }) => { 22 try { 23 // Check if user has any sites 24 const sites = await getSitesByDid(auth.did) 25 26 // Check if user has claimed a domain 27 const domain = await getDomainByDid(auth.did) 28 29 return { 30 did: auth.did, 31 hasSites: sites.length > 0, 32 hasDomain: !!domain, 33 domain: domain || null, 34 sitesCount: sites.length 35 } 36 } catch (err) { 37 logger.error('[User] Status error', err) 38 throw new Error('Failed to get user status') 39 } 40 }) 41 .get('/info', async ({ auth }) => { 42 try { 43 // Get user's handle from AT Protocol 44 const agent = new Agent((url, init) => auth.session.fetchHandler(url, init)) 45 46 let handle = 'unknown' 47 try { 48 const profile = await agent.getProfile({ actor: auth.did }) 49 handle = profile.data.handle 50 } catch (err) { 51 logger.error('[User] Failed to fetch profile', err) 52 } 53 54 return { 55 did: auth.did, 56 handle 57 } 58 } catch (err) { 59 logger.error('[User] Info error', err) 60 throw new Error('Failed to get user info') 61 } 62 }) 63 .get('/sites', async ({ auth }) => { 64 try { 65 const sites = await getSitesByDid(auth.did) 66 return { sites } 67 } catch (err) { 68 logger.error('[User] Sites error', err) 69 throw new Error('Failed to get sites') 70 } 71 }) 72 .get('/domains', async ({ auth }) => { 73 try { 74 // Get all wisp.place subdomains with mappings (up to 3) 75 const wispDomains = await getAllWispDomains(auth.did) 76 77 // Get custom domains 78 const customDomains = await getCustomDomainsByDid(auth.did) 79 80 return { 81 wispDomains: wispDomains.map(d => ({ 82 domain: d.domain, 83 rkey: d.rkey || null 84 })), 85 customDomains 86 } 87 } catch (err) { 88 logger.error('[User] Domains error', err) 89 throw new Error('Failed to get domains') 90 } 91 }) 92 .post('/sync', async ({ auth }) => { 93 try { 94 logger.debug('[User] Manual sync requested for', auth.did) 95 const result = await syncSitesFromPDS(auth.did, auth.session) 96 97 return { 98 success: true, 99 synced: result.synced, 100 errors: result.errors 101 } 102 } catch (err) { 103 logger.error('[User] Sync error', err) 104 throw new Error('Failed to sync sites') 105 } 106 }) 107 .get('/site/:rkey/domains', async ({ auth, params }) => { 108 try { 109 const { rkey } = params 110 const domains = await getDomainsBySite(auth.did, rkey) 111 112 return { 113 rkey, 114 domains 115 } 116 } catch (err) { 117 logger.error('[User] Site domains error', err) 118 throw new Error('Failed to get domains for site') 119 } 120 })