Hub stage 8: settings drift, webhook notices, profiles
The drift check now also covers the settings baseline and the Blocked and Allowed lists baseline; the drift dialog offers Open compare, Accept as baseline and Revert to baseline for them. Old drift.json files still load. HUB_WEBHOOK_URL (and an optional HUB_WEBHOOK_SECRET for an HMAC signature) receives drift.found, drift.cleared and write.failed notices, only on change, without values, never blocking a request and never logging the address beyond its host. Admins see the webhook status and can send a test from About and from the drift dialog. Profiles bundle the hub's app, settings and lists baselines. Applying one to a server runs install, app configs, settings and lists in order after a preview; a failed step stops that server's later steps and Retry failed resumes from it. Profiles export and import as a file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
392b644faa
commit
9742f0fe69
32 files changed
+3123
-106
No files matched your search
@@ -32,6 +32,10 @@ export interface HubConfig {
|
||||
/** How often the drift check runs (HUB_DRIFT_MINUTES); 0 turns it off. */
|
||||
driftMinutes: number
|
||||
timeoutMs: number
|
||||
/** Where drift and failed-write notices are POSTed (HUB_WEBHOOK_URL); null when not set. */
|
||||
webhookUrl: string | null
|
||||
/** Signs the notices with HMAC-SHA256 (HUB_WEBHOOK_SECRET); null when not set. Never logged. */
|
||||
webhookSecret: string | null
|
||||
/** Shown as `dnsServerDomain` (and `clusterDomain`) in the session. */
|
||||
domain: string
|
||||
/** The console's static build. */
|
||||
@@ -85,6 +89,17 @@ export function parseConfig(env: Env): HubConfig {
|
||||
throw new Error('HUB_SERVERS is required, e.g. dns=https://dns.example.net,ndns=https://ndns.example.net.')
|
||||
}
|
||||
|
||||
const webhookUrl = (env.HUB_WEBHOOK_URL ?? '').trim()
|
||||
if (webhookUrl !== '') {
|
||||
let ok = /^https?:\/\/[^/\s]/i.test(webhookUrl)
|
||||
try {
|
||||
new URL(webhookUrl)
|
||||
} catch {
|
||||
ok = false
|
||||
}
|
||||
if (!ok) throw new Error('HUB_WEBHOOK_URL must be an http or https address.')
|
||||
}
|
||||
|
||||
return {
|
||||
servers,
|
||||
bootstrap: user === '' ? null : { user, password },
|
||||
@@ -94,6 +109,8 @@ export function parseConfig(env: Env): HubConfig {
|
||||
cacheSeconds: number(env, 'HUB_CACHE_SECONDS', 15, 0),
|
||||
driftMinutes: number(env, 'HUB_DRIFT_MINUTES', 15, 0),
|
||||
timeoutMs: number(env, 'HUB_TIMEOUT_MS', 8000, 1),
|
||||
webhookUrl: webhookUrl === '' ? null : webhookUrl,
|
||||
webhookSecret: (env.HUB_WEBHOOK_SECRET ?? '') === '' ? null : (env.HUB_WEBHOOK_SECRET as string),
|
||||
domain: env.HUB_DOMAIN?.trim() || 'hub',
|
||||
staticDir: env.HUB_STATIC?.trim() || './dist',
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user