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});