Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
1import { Elysia } from 'elysia' 2import { NodeOAuthClient } from '@atproto/oauth-client-node' 3import { getSitesByDid, getDomainByDid } from '../lib/db' 4import { syncSitesFromPDS } from '../lib/sync-sites' 5 6export const authRoutes = (client: NodeOAuthClient) => new Elysia() 7 .post('/api/auth/signin', async (c) => { 8 try { 9 const { handle } = await c.request.json() 10 const state = crypto.randomUUID() 11 const url = await client.authorize(handle, { state }) 12 return { url: url.toString() } 13 } catch (err) { 14 console.error('Signin error', err) 15 return { error: 'Authentication failed' } 16 } 17 }) 18 .get('/api/auth/callback', async (c) => { 19 const params = new URLSearchParams(c.query) 20 const { session } = await client.callback(params) 21 if (!session) return { error: 'Authentication failed' } 22 23 const cookieSession = c.cookie 24 cookieSession.did.value = session.did 25 26 // Sync sites from PDS to database cache 27 console.log('[Auth] Syncing sites from PDS for', session.did) 28 try { 29 const syncResult = await syncSitesFromPDS(session.did, session) 30 console.log(`[Auth] Sync complete: ${syncResult.synced} sites synced`) 31 if (syncResult.errors.length > 0) { 32 console.warn('[Auth] Sync errors:', syncResult.errors) 33 } 34 } catch (err) { 35 console.error('[Auth] Failed to sync sites:', err) 36 // Don't fail auth if sync fails, just log it 37 } 38 39 // Check if user has any sites or domain 40 const sites = await getSitesByDid(session.did) 41 const domain = await getDomainByDid(session.did) 42 43 // If no sites and no domain, redirect to onboarding 44 if (sites.length === 0 && !domain) { 45 return c.redirect('/onboarding') 46 } 47 48 return c.redirect('/editor') 49 })