Hub stage 6: blocking and cache actions on several servers
One endpoint, hub/servers/run (operate), runs a fixed set of actions on the chosen servers in parallel with each server's admin token: add or remove a domain in Blocked or Allowed, disable blocking for a time or enable it, update block lists, replace the block list URLs, flush the cache or delete a domain from it. Each server gets its own result and the console offers Retry failed. In "All servers", Blocking, Cache and Query Logs rows open this flow instead of being disabled. The hub validates domains, minutes and the server's list grammar (allow lists with !, switched-off lines with #) and logs no URL list. Operators can now use the blocking switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8bb1e01936
commit
34de910e04
31 files changed
+1560
-40
No files matched your search
@@ -27,6 +27,7 @@ import { Audit } from './audit.ts'
|
||||
import { effectiveRoute, largeBodyLimit, routeFor, type Route } from './routes.ts'
|
||||
import { WEB_SERVICE_MESSAGE, isForbiddenSettingsField, redactSettings, safeTarget, webServiceChanges } from './guards.ts'
|
||||
import { buildMatrix } from './apps-matrix.ts'
|
||||
import { ServersRunner, planAction } from './servers-run.ts'
|
||||
import { AppsWriter, actionForPath, type FileAction, type TargetResult } from './apps-write.ts'
|
||||
import { TooLarge, parseMultipart, readLimited, type UploadedFile } from './multipart.ts'
|
||||
import { AppsConfig, UserError } from './apps-config.ts'
|
||||
@@ -144,6 +145,7 @@ export function createHub(config: HubConfig, users: Users = Users.load(config.da
|
||||
const limiter = new LoginLimiter()
|
||||
const audit = new Audit(config.dataDir)
|
||||
const writer = new AppsWriter(config, upstream)
|
||||
const runner = new ServersRunner(config, upstream)
|
||||
const appsConfig = new AppsConfig(config, upstream)
|
||||
const byId = new Map(config.servers.map((s) => [s.id, s]))
|
||||
|
||||
@@ -384,6 +386,27 @@ export function createHub(config: HubConfig, users: Users = Users.load(config.da
|
||||
await configWrite(res, apiPath, params, who, ip)
|
||||
return
|
||||
}
|
||||
if (apiPath === 'hub/servers/run') {
|
||||
const planned = planAction(params.action, params)
|
||||
const line = { user: who.user, role: who.role, ip, action: 'error' in planned ? 'servers.run' : `servers.${planned.plan.action}` }
|
||||
if ('error' in planned) {
|
||||
audit.log({ ...line, outcome: 'refused', detail: planned.error })
|
||||
send(res, 400, fail(planned.error))
|
||||
return
|
||||
}
|
||||
const outcome = await runner.run(planned.plan, csv(params.targets))
|
||||
if ('error' in outcome) {
|
||||
audit.log({ ...line, target: planned.plan.target, outcome: 'refused', detail: outcome.error })
|
||||
send(res, 400, fail(outcome.error))
|
||||
return
|
||||
}
|
||||
// One line per server. The target is a domain or a count, never the list of URLs.
|
||||
for (const r of outcome.results) {
|
||||
audit.log({ ...line, server: r.server, target: planned.plan.target, outcome: r.ok ? 'ok' : 'failed', detail: r.message })
|
||||
}
|
||||
send(res, 200, ok({ results: outcome.results }))
|
||||
return
|
||||
}
|
||||
if (apiPath === 'hub/apps/run') {
|
||||
const action = params.action
|
||||
if (action !== 'install-store' && action !== 'update-store' && action !== 'uninstall') {
|
||||
|
||||
Reference in new issue
Block a user