Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
1import { useState } from 'react' 2 3export interface Site { 4 did: string 5 rkey: string 6 display_name: string | null 7 created_at: number 8 updated_at: number 9} 10 11export interface DomainInfo { 12 type: 'wisp' | 'custom' 13 domain: string 14 verified?: boolean 15 id?: string 16} 17 18export interface SiteWithDomains extends Site { 19 domains?: DomainInfo[] 20} 21 22export function useSiteData() { 23 const [sites, setSites] = useState<SiteWithDomains[]>([]) 24 const [sitesLoading, setSitesLoading] = useState(true) 25 const [isSyncing, setIsSyncing] = useState(false) 26 27 const fetchSites = async () => { 28 try { 29 const response = await fetch('/api/user/sites') 30 const data = await response.json() 31 const sitesData: Site[] = data.sites || [] 32 33 // Fetch domain info for each site 34 const sitesWithDomains = await Promise.all( 35 sitesData.map(async (site) => { 36 try { 37 const domainsResponse = await fetch(`/api/user/site/${site.rkey}/domains`) 38 const domainsData = await domainsResponse.json() 39 return { 40 ...site, 41 domains: domainsData.domains || [] 42 } 43 } catch (err) { 44 console.error(`Failed to fetch domains for site ${site.rkey}:`, err) 45 return { 46 ...site, 47 domains: [] 48 } 49 } 50 }) 51 ) 52 53 setSites(sitesWithDomains) 54 } catch (err) { 55 console.error('Failed to fetch sites:', err) 56 } finally { 57 setSitesLoading(false) 58 } 59 } 60 61 const syncSites = async () => { 62 setIsSyncing(true) 63 try { 64 const response = await fetch('/api/user/sync', { 65 method: 'POST' 66 }) 67 const data = await response.json() 68 if (data.success) { 69 console.log(`Synced ${data.synced} sites from PDS`) 70 // Refresh sites list 71 await fetchSites() 72 } 73 } catch (err) { 74 console.error('Failed to sync sites:', err) 75 alert('Failed to sync sites from PDS') 76 } finally { 77 setIsSyncing(false) 78 } 79 } 80 81 const deleteSite = async (rkey: string) => { 82 try { 83 const response = await fetch(`/api/site/${rkey}`, { 84 method: 'DELETE' 85 }) 86 87 const data = await response.json() 88 if (data.success) { 89 // Refresh sites list 90 await fetchSites() 91 return true 92 } else { 93 throw new Error(data.error || 'Failed to delete site') 94 } 95 } catch (err) { 96 console.error('Delete site error:', err) 97 alert( 98 `Failed to delete site: ${err instanceof Error ? err.message : 'Unknown error'}` 99 ) 100 return false 101 } 102 } 103 104 return { 105 sites, 106 sitesLoading, 107 isSyncing, 108 fetchSites, 109 syncSites, 110 deleteSite 111 } 112}