Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

fix readme, fix script, add cache observability to dashboard

Changed files
+45 -10
scripts
src
routes
+2 -2
README.md
··· 43 44 # Hosting service 45 cd hosting-service 46 - cargo run 47 48 # CLI 49 cd cli ··· 60 61 - Backend: Bun + Elysia + PostgreSQL 62 - Frontend: React 19 + Tailwind 4 + Radix UI 63 - - Hosting: Rust microservice 64 - CLI: Rust + Jacquard (AT Protocol library) 65 - Protocol: AT Protocol OAuth + custom lexicons 66
··· 43 44 # Hosting service 45 cd hosting-service 46 + npm run start 47 48 # CLI 49 cd cli ··· 60 61 - Backend: Bun + Elysia + PostgreSQL 62 - Frontend: React 19 + Tailwind 4 + Radix UI 63 + - Hosting: Node microservice using Hono 64 - CLI: Rust + Jacquard (AT Protocol library) 65 - Protocol: AT Protocol OAuth + custom lexicons 66
+2 -2
scripts/change-admin-password.ts
··· 1 // Change admin password 2 - import { adminAuth } from './src/lib/admin-auth' 3 - import { db } from './src/lib/db' 4 import { randomBytes, createHash } from 'crypto' 5 6 // Get username and new password from command line
··· 1 // Change admin password 2 + import { adminAuth } from '../src/lib/admin-auth' 3 + import { db } from '../src/lib/db' 4 import { randomBytes, createHash } from 'crypto' 5 6 // Get username and new password from command line
+41 -6
src/routes/admin.ts
··· 106 // Get logs from hosting service 107 let hostingLogs: any[] = [] 108 try { 109 - const hostingPort = process.env.HOSTING_PORT || '3001' 110 const params = new URLSearchParams() 111 if (query.level) params.append('level', query.level as string) 112 if (query.service) params.append('service', query.service as string) ··· 114 if (query.eventType) params.append('eventType', query.eventType as string) 115 params.append('limit', String(filter.limit || 100)) 116 117 - const response = await fetch(`http://localhost:${hostingPort}/__internal__/observability/logs?${params}`) 118 if (response.ok) { 119 const data = await response.json() 120 hostingLogs = data.logs ··· 154 // Get errors from hosting service 155 let hostingErrors: any[] = [] 156 try { 157 - const hostingPort = process.env.HOSTING_PORT || '3001' 158 const params = new URLSearchParams() 159 if (query.service) params.append('service', query.service as string) 160 params.append('limit', String(filter.limit || 100)) 161 162 - const response = await fetch(`http://localhost:${hostingPort}/__internal__/observability/errors?${params}`) 163 if (response.ok) { 164 const data = await response.json() 165 hostingErrors = data.errors ··· 207 } 208 209 try { 210 - const hostingPort = process.env.HOSTING_PORT || '3001' 211 - const response = await fetch(`http://localhost:${hostingPort}/__internal__/observability/metrics?timeWindow=${timeWindow}`) 212 if (response.ok) { 213 const data = await response.json() 214 hostingServiceStats = data.stats ··· 273 set.status = 500 274 return { 275 error: 'Failed to fetch database stats', 276 message: error instanceof Error ? error.message : String(error) 277 } 278 }
··· 106 // Get logs from hosting service 107 let hostingLogs: any[] = [] 108 try { 109 + const hostingServiceUrl = process.env.HOSTING_SERVICE_URL || `http://localhost:${process.env.HOSTING_PORT || '3001'}` 110 const params = new URLSearchParams() 111 if (query.level) params.append('level', query.level as string) 112 if (query.service) params.append('service', query.service as string) ··· 114 if (query.eventType) params.append('eventType', query.eventType as string) 115 params.append('limit', String(filter.limit || 100)) 116 117 + const response = await fetch(`${hostingServiceUrl}/__internal__/observability/logs?${params}`) 118 if (response.ok) { 119 const data = await response.json() 120 hostingLogs = data.logs ··· 154 // Get errors from hosting service 155 let hostingErrors: any[] = [] 156 try { 157 + const hostingServiceUrl = process.env.HOSTING_SERVICE_URL || `http://localhost:${process.env.HOSTING_PORT || '3001'}` 158 const params = new URLSearchParams() 159 if (query.service) params.append('service', query.service as string) 160 params.append('limit', String(filter.limit || 100)) 161 162 + const response = await fetch(`${hostingServiceUrl}/__internal__/observability/errors?${params}`) 163 if (response.ok) { 164 const data = await response.json() 165 hostingErrors = data.errors ··· 207 } 208 209 try { 210 + const hostingServiceUrl = process.env.HOSTING_SERVICE_URL || `http://localhost:${process.env.HOSTING_PORT || '3001'}` 211 + const response = await fetch(`${hostingServiceUrl}/__internal__/observability/metrics?timeWindow=${timeWindow}`) 212 if (response.ok) { 213 const data = await response.json() 214 hostingServiceStats = data.stats ··· 273 set.status = 500 274 return { 275 error: 'Failed to fetch database stats', 276 + message: error instanceof Error ? error.message : String(error) 277 + } 278 + } 279 + }, { 280 + cookie: t.Cookie({ 281 + admin_session: t.Optional(t.String()) 282 + }, { 283 + secrets: cookieSecret, 284 + sign: ['admin_session'] 285 + }) 286 + }) 287 + 288 + // Get cache stats (protected) 289 + .get('/cache', async ({ cookie, set }) => { 290 + const check = requireAdmin({ cookie, set }) 291 + if (check) return check 292 + 293 + try { 294 + const hostingServiceUrl = process.env.HOSTING_SERVICE_URL || `http://localhost:${process.env.HOSTING_PORT || '3001'}` 295 + const response = await fetch(`${hostingServiceUrl}/__internal__/observability/cache`) 296 + 297 + if (response.ok) { 298 + const data = await response.json() 299 + return data 300 + } else { 301 + set.status = 503 302 + return { 303 + error: 'Failed to fetch cache stats from hosting service', 304 + message: 'Hosting service unavailable' 305 + } 306 + } 307 + } catch (error) { 308 + set.status = 500 309 + return { 310 + error: 'Failed to fetch cache stats', 311 message: error instanceof Error ? error.message : String(error) 312 } 313 }