Harden live server apply for alerts and peer roles
Release rolling / release (push) Successful in 7m37s
Release rolling / release (push) Successful in 7m37s
Flush alert config immediately, re-arm runtime without restart, and push role updates to connected clients so ACL changes apply live.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
import * as peerPolicy from '../core/peer-policy.js'
|
||||
import * as connectionInvites from '../core/connection-invites.js'
|
||||
import { peers } from '../core/peer-registry.js'
|
||||
import { Roles } from '../../shared/protocol.js'
|
||||
import { Roles, Pushes } from '../../shared/protocol.js'
|
||||
import * as validation from '../utils/validation.js'
|
||||
import { decodePeardockInvite } from '../../shared/crypto-auth.js'
|
||||
|
||||
@@ -263,7 +263,20 @@ export function registerPeerHandlers(session) {
|
||||
if (!peerId || !role) throw new Error('peerId and role required')
|
||||
const entry = peerPolicy.setPeerRole(peerId, role)
|
||||
const live = peers.get(peerId)
|
||||
if (live) live.role = role
|
||||
return { success: true, data: entry }
|
||||
if (live) {
|
||||
// Apply ACL live — no reconnect / server restart
|
||||
live.role = role
|
||||
try {
|
||||
live.push(Pushes.session, {
|
||||
type: 'roleUpdate',
|
||||
role,
|
||||
peerId,
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
} catch {
|
||||
// ignore push failures
|
||||
}
|
||||
}
|
||||
return { success: true, data: entry, appliedLive: Boolean(live) }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -334,15 +334,28 @@ function normalizeConfig(raw) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist config promptly. Coalesce bursts so rapid UI edits don't thrash disk,
|
||||
* but never wait more than ~120ms so process death can't strand live config.
|
||||
*/
|
||||
function scheduleSave() {
|
||||
if (saveTimer) return
|
||||
saveTimer = setTimeout(() => {
|
||||
saveTimer = null
|
||||
saveDisk()
|
||||
}, 400)
|
||||
}, 120)
|
||||
if (typeof saveTimer.unref === 'function') saveTimer.unref()
|
||||
}
|
||||
|
||||
/** Flush any pending debounced save immediately (e.g. before critical ops). */
|
||||
function flushSave() {
|
||||
if (saveTimer) {
|
||||
clearTimeout(saveTimer)
|
||||
saveTimer = null
|
||||
}
|
||||
saveDisk()
|
||||
}
|
||||
|
||||
function saveDisk() {
|
||||
try {
|
||||
const payload = {
|
||||
@@ -1260,7 +1273,7 @@ export function updateAlertsConfig(partial, opts = {}) {
|
||||
config = normalizeConfig(merged)
|
||||
}
|
||||
config.updatedAt = new Date().toISOString()
|
||||
saveDisk()
|
||||
flushSave()
|
||||
// Live apply: poll interval, enable/disable — no process restart
|
||||
applyRuntimeConfig({
|
||||
kickPoll: config.enabled && (!wasEnabled || partial.pollIntervalMs != null),
|
||||
@@ -1284,8 +1297,8 @@ export function upsertAlertChannel(input) {
|
||||
} else {
|
||||
config.channels.push(ch)
|
||||
}
|
||||
scheduleSave()
|
||||
// Channel list is read on each fire — already live; log for ops clarity
|
||||
// In-memory config is used on next fire; flush disk promptly (no restart)
|
||||
flushSave()
|
||||
logger.debug('alerts: channel upserted live', {
|
||||
id: ch.id,
|
||||
enabled: ch.enabled,
|
||||
@@ -1303,7 +1316,7 @@ export function deleteAlertChannel(id) {
|
||||
rule.channelIds = rule.channelIds.filter((x) => x !== id)
|
||||
}
|
||||
}
|
||||
scheduleSave()
|
||||
flushSave()
|
||||
return before !== config.channels.length
|
||||
}
|
||||
|
||||
@@ -1312,7 +1325,7 @@ export function upsertAlertRule(input) {
|
||||
const idx = config.rules.findIndex((r) => r.id === rule.id)
|
||||
if (idx >= 0) config.rules[idx] = rule
|
||||
else config.rules.push(rule)
|
||||
scheduleSave()
|
||||
flushSave()
|
||||
logger.debug('alerts: rule upserted live', {
|
||||
id: rule.id,
|
||||
enabled: rule.enabled,
|
||||
@@ -1324,7 +1337,7 @@ export function upsertAlertRule(input) {
|
||||
export function deleteAlertRule(id) {
|
||||
const before = config.rules.length
|
||||
config.rules = config.rules.filter((r) => r.id !== id)
|
||||
scheduleSave()
|
||||
flushSave()
|
||||
return before !== config.rules.length
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ function arm(job) {
|
||||
job.timer = null
|
||||
}
|
||||
if (!job.enabled || job.intervalMs < 60_000) return
|
||||
// Live: re-arm whenever upserted — no server restart required
|
||||
job.timer = setInterval(() => {
|
||||
runJob(job)
|
||||
}, job.intervalMs)
|
||||
|
||||
Reference in New Issue
Block a user