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