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 <[email protected]>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-10-31 22:50:09 +07:00
1 parent 8bb1e01936
commit 34de910e04
31 files changed
+1560 -40

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
import { useState, type ReactNode } from 'react'
import { Button } from './Button'
import { Dialog } from './Dialog'
import { Field, Input, Textarea } from './Field'
/*
One question in a dialog: a field (a line, or several with `multiline`) and a button. For the
step before a change on several servers that needs a value, a domain or a list of URLs, which
the next step (the server picker) does not ask for. `check` returns the sentence that says what
is wrong with the text, or `null` when it is fine; it is shown under the field and the answer
is not passed on.
*/
export function AskDialog({
title,
label,
intro,
initial = '',
placeholder,
multiline = false,
confirmLabel,
check,
onSubmit,
onClose,
}: {
title: string
label: string
intro?: ReactNode
initial?: string
placeholder?: string
multiline?: boolean
confirmLabel: string
check?: (text: string) => string | null
onSubmit: (text: string) => void
onClose: () => void
}) {
const [text, setText] = useState(initial)
const [problem, setProblem] = useState<string | null>(null)
function submit() {
const wrong = check?.(text) ?? null
setProblem(wrong)
if (wrong === null) onSubmit(text)
}
return (
<Dialog
open
onOpenChange={onClose}
title={title}
close="Cancel"
actions={
<Button variant="primary" onClick={submit}>
{confirmLabel}
</Button>
}
>
{intro != null && <p className="m-0 text-sm text-muted-foreground">{intro}</p>}
<Field label={label}>
{(id) =>
multiline ? (
<Textarea
id={id}
mono
rows={8}
placeholder={placeholder}
value={text}
aria-invalid={problem !== null || undefined}
onChange={(e) => setText(e.target.value)}
/>
) : (
<Input
id={id}
mono
placeholder={placeholder}
value={text}
aria-invalid={problem !== null || undefined}
onChange={(e) => setText(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') submit()
}}
/>
)
}
</Field>
{problem !== null && (
<p className="m-0 text-xs text-destructive" role="alert">
{problem}
</p>
)}
</Dialog>
)
}