at main 2.8 kB view raw
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);