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