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
@@ -35,6 +35,7 @@ export class Audit {
|
||||
#ring: AuditEntry[] = []
|
||||
#size = 0
|
||||
#memoryOnly = false
|
||||
#listener: ((entry: AuditEntry) => void) | null = null
|
||||
|
||||
constructor(dir: string, options: { maxBytes?: number; now?: () => number } = {}) {
|
||||
this.#maxBytes = options.maxBytes ?? MAX_BYTES
|
||||
@@ -62,6 +63,11 @@ export class Audit {
|
||||
this.#memoryOnly = true
|
||||
}
|
||||
|
||||
/** One function is told about every entry as it is written (the webhook uses it for failed writes). */
|
||||
onEntry(listener: (entry: AuditEntry) => void): void {
|
||||
this.#listener = listener
|
||||
}
|
||||
|
||||
log(input: AuditInput): void {
|
||||
const entry: AuditEntry = {
|
||||
ts: new Date(this.#now()).toISOString(),
|
||||
@@ -75,6 +81,11 @@ export class Audit {
|
||||
...(input.detail === undefined ? {} : { detail: cut(input.detail, DETAIL_MAX) }),
|
||||
}
|
||||
this.#ring.push(entry)
|
||||
try {
|
||||
this.#listener?.(entry)
|
||||
} catch {
|
||||
// A listener never stops the log.
|
||||
}
|
||||
if (this.#ring.length > RING) this.#ring.shift()
|
||||
if (this.#memoryOnly) return
|
||||
const line = JSON.stringify(entry) + '\n'
|
||||
|
||||
Reference in new issue
Block a user