Blocking: count Advanced Blocking, Dashboard layout and KPI cards
CI / check (push) Successful in 3m7s
Hub / check (push) Successful in 53s
CI / image (push) Successful in 1m29s
Hub / image (push) Successful in 1m19s

- Status, sidebar mark and header treat a server that blocks with the
  Advanced Blocking app as blocking, per server behind the hub.
- Overview panel summarising the app's groups; Lists and Rules show and
  edit the app's list URLs and rules (read, change one field, write back).
- Block List Domains adds the app's list domains, counted by the hub
  (hub/blocking/app-lists/count, public hosts only unless
  HUB_LISTS_ALLOW_PRIVATE=1) or by the browser where CORS allows.
- Header and KPI cards follow the Dashboard: selector in the chrome slot,
  Compare servers in the header actions, period under the title, StatCard
  tiles without sparklines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-11-01 18:34:17 +07:00
1 parent b396bfd317
commit 172e06f1ce
40 files changed
+3126 -208

No files matched your search

+16
View File
@@ -35,6 +35,7 @@ import { Drift } from './drift.ts'
import { Webhook, type WebhookOptions } from './webhook.ts'
import { Profiles } from './profiles.ts'
import { SettingsConfig } from './settings-config.ts'
import { AppLists } from './app-lists.ts'
import { BLOCKING_FIELDS } from './settings-sync.ts'
import { ZoneUserError, ZonesSync, buildZoneMatrix } from './zones-sync.ts'
import {
@@ -167,6 +168,7 @@ export function createHub(config: HubConfig, users: Users = Users.load(config.da
const appsConfig = new AppsConfig(config, upstream)
const zonesSync = new ZonesSync(config, upstream)
const settingsConfig = new SettingsConfig(config, upstream)
const appLists = new AppLists(upstream, { allowPrivate: config.listsAllowPrivate })
const profiles = new Profiles(config, upstream, appsConfig, settingsConfig, writer)
const byId = new Map(config.servers.map((s) => [s.id, s]))
@@ -917,6 +919,20 @@ export function createHub(config: HubConfig, users: Users = Users.load(config.da
await profilesRead(res, apiPath, params, who, ip)
return
}
if (apiPath === 'hub/blocking/app-lists/count') {
const server = byId.get(params.node ?? '')
if (server === undefined) {
send(res, 400, fail('Choose a server: the lists are counted for one server at a time.'))
return
}
try {
send(res, 200, ok(await appLists.count(server, params.name || 'Advanced Blocking')))
} catch (e) {
if (!(e instanceof UpstreamError)) throw e
send(res, 200, fail(e.kind === 'unreachable' ? `${server.id} did not answer: ${e.message}` : `${server.id}: ${e.message}`))
}
return
}
if (apiPath.startsWith('hub/settings/') || apiPath.startsWith('hub/blocking/')) {
await settingsRead(res, apiPath, params, who, ip)
return