Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
1import app from './server'; 2import { serve } from '@hono/node-server'; 3import { FirehoseWorker } from './lib/firehose'; 4import { createLogger } from '@wisp/observability'; 5import { mkdirSync, existsSync } from 'fs'; 6import { backfillCache } from './lib/backfill'; 7import { startDomainCacheCleanup, stopDomainCacheCleanup, setCacheOnlyMode } from './lib/db'; 8 9const logger = createLogger('hosting-service'); 10 11const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3001; 12const CACHE_DIR = process.env.CACHE_DIR || './cache/sites'; 13 14// Parse CLI arguments 15const args = process.argv.slice(2); 16const hasBackfillFlag = args.includes('--backfill'); 17const backfillOnStartup = hasBackfillFlag || process.env.BACKFILL_ON_STARTUP === 'true'; 18 19// Cache-only mode: service will only cache files locally, no DB writes 20const hasCacheOnlyFlag = args.includes('--cache-only'); 21export const CACHE_ONLY_MODE = hasCacheOnlyFlag || process.env.CACHE_ONLY_MODE === 'true'; 22 23// Configure cache-only mode in database module 24if (CACHE_ONLY_MODE) { 25 setCacheOnlyMode(true); 26} 27 28// Ensure cache directory exists 29if (!existsSync(CACHE_DIR)) { 30 mkdirSync(CACHE_DIR, { recursive: true }); 31 console.log('Created cache directory:', CACHE_DIR); 32} 33 34// Start domain cache cleanup 35startDomainCacheCleanup(); 36 37// Start firehose worker with observability logger 38const firehose = new FirehoseWorker((msg, data) => { 39 logger.info(msg, data); 40}); 41 42firehose.start(); 43 44// Run backfill if requested 45if (backfillOnStartup) { 46 console.log('🔄 Backfill requested, starting cache backfill...'); 47 backfillCache({ 48 skipExisting: true, 49 concurrency: 3, 50 }).then((stats) => { 51 console.log('✅ Cache backfill completed'); 52 }).catch((err) => { 53 console.error('❌ Cache backfill error:', err); 54 }); 55} 56 57// Add health check endpoint 58app.get('/health', (c) => { 59 const firehoseHealth = firehose.getHealth(); 60 return c.json({ 61 status: 'ok', 62 firehose: firehoseHealth, 63 }); 64}); 65 66// Start HTTP server with Node.js adapter 67const server = serve({ 68 fetch: app.fetch, 69 port: PORT, 70}); 71 72console.log(` 73Wisp Hosting Service 74 75Server: http://localhost:${PORT} 76Health: http://localhost:${PORT}/health 77Cache: ${CACHE_DIR} 78Firehose: Connected to Firehose 79Cache-Only: ${CACHE_ONLY_MODE ? 'ENABLED (no DB writes)' : 'DISABLED'} 80`); 81 82// Graceful shutdown 83process.on('SIGINT', async () => { 84 console.log('\n🛑 Shutting down...'); 85 firehose.stop(); 86 stopDomainCacheCleanup(); 87 server.close(); 88 process.exit(0); 89}); 90 91process.on('SIGTERM', async () => { 92 console.log('\n🛑 Shutting down...'); 93 firehose.stop(); 94 stopDomainCacheCleanup(); 95 server.close(); 96 process.exit(0); 97});