// host-services — `bare index.js`. One page listing everything this host
// serves (pgh apps, systemd units, every listening port) with active / dormant
// state, plus the other devices on the LAN and what they serve.
//
// ⛔ LAN-only, no login (the services it lists have none either). The pgh
// defaults bind 0.0.0.0:4060 and Caddy fronts it as http://<host>.local/; the
// code default is 127.0.0.1 so a bare `bare index.js` on a dev machine exposes
// nothing.
import fs from 'bare-fs'
import os from 'bare-os'
import path from 'bare-path'
import process from 'bare-process'
import { HostCollector } from './lib/collect.js'
import { LanScanner } from './lib/lan.js'
import { createServer } from './lib/server.js'
const port = Number(process.env.HOST_SERVICES_PORT || 4060)
const host = process.env.HOST_SERVICES_HOST || '127.0.0.1'
const configDir = process.env.HOST_SERVICES_CONFIG || path.join(os.homedir(), '.config', 'host-services')
const lanScan = process.env.HOST_SERVICES_LAN_SCAN !== '0'
// Friendly names for known ports and units. The one shipped with the app,
// overridden key-by-key by ~/.config/host-services/labels.json if present.
function loadLabels () {
const read = (p) => {
try {
return JSON.parse(fs.readFileSync(p).toString())
} catch {
return {}
}
}
const base = read(path.join(path.dirname(new URL(import.meta.url).pathname), 'labels.json'))
const local = read(path.join(configDir, 'labels.json'))
return {
ports: { ...base.ports, ...local.ports },
units: { ...base.units, ...local.units }
}
}
const collector = new HostCollector({ labels: loadLabels() })
const lan = new LanScanner({ configDir })
const server = createServer({ host: collector, lan })
server.listen(port, host, () => {
console.log(JSON.stringify({ msg: 'host-services up', url: 'http://' + host + ':' + server.address().port, config: configDir, lanScan }))
if (lanScan) lan.start()
})
lan.on('scanned', (r) => console.log(JSON.stringify({ msg: 'lan scanned', hosts: r.hosts.length, ms: r.durationMs })))
const shutdown = () => {
lan.stop()
server.close(() => process.exit(0))
setTimeout(() => process.exit(0), 3000)
}
process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown)