demos for spacedust
1#!/usr/bin/env node
2
3import { createRemoteJWKSet } from 'jose';
4import fs from 'node:fs';
5import { randomBytes } from 'node:crypto';
6import https from 'node:https';
7import webpush from 'web-push';
8import { DB } from './db.js';
9import { connectSpacedust } from './notifications.js';
10import { server } from './api.js';
11
12const getOrCreateSecrets = filename => {
13 let secrets;
14 try {
15 const serialized = fs.readFileSync(filename);
16 secrets = JSON.parse(serialized);
17 } catch (err) {
18 if (err.code != 'ENOENT') throw err;
19 secrets = {
20 pushKeys: webpush.generateVAPIDKeys(),
21 appSecret: randomBytes(32).toString('hex'),
22 };
23 const serialized = JSON.stringify(secrets);
24 fs.writeFileSync(filename, serialized);
25 }
26 console.log(`Keys ready with webpush pubkey: ${secrets.pushKeys.publicKey}`);
27 return secrets;
28}
29
30function startHealthcheckPing(endpoint) {
31 const next = () => setTimeout(() => startHealthcheckPing(endpoint), 90 * 1000);
32
33 https
34 .get(endpoint, res => {
35 if (res.statusCode !== 200) console.warn('non-200 health check response', res.statusCode);
36 res
37 .on('data', () => {})
38 .on('end', next);
39 })
40 .on('error', err => {
41 console.warn('healthcheck request errored', err);
42 next();
43 });
44}
45
46const main = env => {
47 if (!env.ADMIN_DID) throw new Error('ADMIN_DID is required to run');
48 const adminDid = env.ADMIN_DID;
49
50 if (!env.SECRETS_FILE) throw new Error('SECRETS_FILE is required to run');
51 const secrets = getOrCreateSecrets(env.SECRETS_FILE);
52 webpush.setVapidDetails(
53 'mailto:phil@bad-example.com',
54 secrets.pushKeys.publicKey,
55 secrets.pushKeys.privateKey,
56 );
57
58 const whoamiHost = env.WHOAMI_HOST ?? 'https://who-am-i.microcosm.blue';
59 const jwks = createRemoteJWKSet(new URL(`${whoamiHost}/.well-known/jwks.json`));
60
61 const dbFilename = env.DB_FILE ?? './db.sqlite3';
62 const initDb = process.argv.includes('--init-db');
63 console.log(`connecting sqlite db file: ${dbFilename} (initializing: ${initDb})`);
64 const db = new DB(dbFilename, initDb);
65
66 const spacedustHost = env.SPACEDUST_HOST ?? 'wss://spacedust.microcosm.blue';
67 const { updateSubs, push } = connectSpacedust(db, spacedustHost);
68
69 const host = env.HOST ?? 'localhost';
70 const port = parseInt(env.PORT ?? 8000, 10);
71
72 const allowedOrigin = env.ALLOWED_ORIGIN ?? 'http://127.0.0.1:5173';
73
74 if (env.HEALTHCHECK) startHealthcheckPing(env.HEALTHCHECK);
75 else console.warn('no HEALTHCHECK in env, not sending healthcheck pings');
76
77 server(secrets, jwks, allowedOrigin, whoamiHost, db, updateSubs, push, adminDid).listen(
78 port,
79 host,
80 () => console.log(`listening at http://${host}:${port} with allowed origin: ${allowedOrigin}`),
81 );
82};
83
84main(process.env);