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}