forked from
nekomimi.pet/wisp.place-monorepo
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 })