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]>
94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
import { useContext, useState } from 'react'
|
|
import { useAllServersFlow } from '../../lib/hub-all'
|
|
import { Button } from '../../ui/Button'
|
|
import { AskDialog } from '../../ui/AskDialog'
|
|
import { HubMode } from '../../ui/ClusterNodeSelect'
|
|
|
|
/*
|
|
Behind a hub: the block list actions that go to several servers. "Update lists" asks every chosen
|
|
server to download its lists now; "Edit list URLs" REPLACES the list of URLs on every chosen
|
|
server with the one typed here (one per line), so the dialog says so. The
|
|
caller shows this only to a role that may change blocking. Drawn nowhere outside a hub.
|
|
*/
|
|
|
|
/*
|
|
What the dialog starts with: the lines of the chosen server's list as it holds them, `!` allow
|
|
lists, `#` switched-off lists and headings included, so saving it back changes nothing.
|
|
*/
|
|
export function plainUrls(lines: readonly string[] | null | undefined): string {
|
|
return (lines ?? []).map((l) => l.trim()).filter((l) => l !== '').join('\n')
|
|
}
|
|
|
|
/* The hub's rules (hub/servers-run.ts `cleanUrls`), so the dialog and the hub give the same answer. */
|
|
export function checkUrls(text: string): string | null {
|
|
const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l !== '')
|
|
if (lines.length === 0) return 'Give at least one block list URL.'
|
|
if (lines.length > 300) return 'At most 300 lines.'
|
|
for (const line of lines) {
|
|
if (line.includes(',')) return `A line cannot contain a comma: ${line.slice(0, 60)}`
|
|
if (line.startsWith('#')) {
|
|
if (line.length > 200) return 'A comment line is too long.'
|
|
continue
|
|
}
|
|
const body = line.startsWith('!') ? line.slice(1).trim() : line
|
|
let ok = line.length <= 2000 && !/\s/.test(body)
|
|
if (ok) {
|
|
try {
|
|
ok = ['http:', 'https:', 'file:'].includes(new URL(body).protocol)
|
|
} catch {
|
|
ok = false
|
|
}
|
|
}
|
|
if (!ok) return `Not a valid http, https or file address: ${line.slice(0, 60)}`
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function AllServersActions({ token, initialUrls }: { token: string | null; initialUrls?: readonly string[] | null }) {
|
|
const hub = useContext(HubMode)
|
|
const { start, flow } = useAllServersFlow(token)
|
|
const [editing, setEditing] = useState(false)
|
|
if (!hub) return null
|
|
return (
|
|
<>
|
|
<Button
|
|
onClick={() =>
|
|
start({
|
|
action: 'blocking.updateLists',
|
|
title: 'Update block lists on several servers',
|
|
intro: 'Each chosen server downloads its block lists now.',
|
|
confirmLabel: 'Update lists',
|
|
})
|
|
}
|
|
>
|
|
Update lists on all servers
|
|
</Button>
|
|
<Button onClick={() => setEditing(true)}>Edit list URLs on all servers</Button>
|
|
{editing && (
|
|
<AskDialog
|
|
title="Edit list URLs on all servers"
|
|
label="Block list URLs"
|
|
intro="One address per line: a list, `!` before an allow list, `#` before a switched-off list or a heading. This replaces the whole list of block list URLs on every server you choose next."
|
|
initial={plainUrls(initialUrls)}
|
|
multiline
|
|
placeholder="https://example.com/hosts.txt"
|
|
confirmLabel="Choose servers"
|
|
check={checkUrls}
|
|
onClose={() => setEditing(false)}
|
|
onSubmit={(text) => {
|
|
setEditing(false)
|
|
start({
|
|
action: 'blocking.setListUrls',
|
|
params: { urls: text },
|
|
title: 'Replace block list URLs on several servers',
|
|
intro: 'The list of URLs on each chosen server is replaced by the one you typed.',
|
|
confirmLabel: 'Replace URLs',
|
|
})
|
|
}}
|
|
/>
|
|
)}
|
|
{flow}
|
|
</>
|
|
)
|
|
}
|