Blocking: count Advanced Blocking, Dashboard layout and KPI cards
- 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:
1 parent
b396bfd317
commit
172e06f1ce
40 files changed
+3126
-208
No files matched your search
+44
-3
@@ -176,7 +176,7 @@ const settings = {
|
||||
cacheMaximumEntries: 10000, cacheMinimumRecordTtl: 10, cacheMaximumRecordTtl: 86400, cacheNegativeRecordTtl: 300, cacheFailureRecordTtl: 60,
|
||||
enableCachePrefetch: true, cachePrefetchEligibility: 2, cachePrefetchTrigger: 9,
|
||||
enableBlocking: true, allowTxtBlockingReport: true, temporaryDisableBlockingTill: null, blockingBypassList: [], blockingType: 'NxDomain', blockingAnswerTtl: 30,
|
||||
customBlockingAddresses: [], blockListUrls: ['https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts', '# Privacy', 'https://big.oisd.nl/domainswild', 'https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt'],
|
||||
customBlockingAddresses: [], blockListUrls: ['https://lists.invalid/hosts', '# Privacy', 'https://big.oisd.nl/domainswild', 'https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt'],
|
||||
blockListUpdateIntervalHours: 24, blockListNextUpdatedOn: iso(now + 3 * 3_600_000),
|
||||
proxy: null, forwarders: ['1.1.1.1', '9.9.9.9'], forwarderProtocol: 'Udp', concurrentForwarding: true, forwarderRetries: 3, forwarderTimeout: 2000, forwarderConcurrency: 2,
|
||||
loggingType: 'File, Console', ignoreResolverLogs: false, logQueries: false, useLocalTime: false, logFolder: 'logs', maxLogFileDays: 365, enableInMemoryStats: false, maxStatFileDays: 365,
|
||||
@@ -220,10 +220,42 @@ if (VARIANT === 'b') {
|
||||
apps = apps.filter((a) => a.name !== 'Split Horizon')
|
||||
apps = apps.map((a) => (a.name === 'Query Logs (Sqlite)' ? { ...a, version: '7.1', updateAvailable: true, updateVersion: '7.2', updateUrl: 'https://download.technitium.com/dns/apps/QueryLogsSqliteApp-v7.2.zip' } : a))
|
||||
}
|
||||
// A small fake list the mock serves itself at /lists/<name>.txt (no internet), so the Advanced Blocking
|
||||
// "Block List Domains" count has something to download. Open to any origin, as a public list host is.
|
||||
const FAKE_LISTS = {
|
||||
'hosts.txt': ['# fake hosts list', '127.0.0.1 localhost', ...Array.from({ length: 120 }, (_, i) => `0.0.0.0 ads${i}.tracker.example`)].join('\n'),
|
||||
'easylist.txt': ['[Adblock Plus 2.0]', '! fake adblock list', ...Array.from({ length: 45 }, (_, i) => `||banner${i}.adnet.example^`)].join('\n'),
|
||||
}
|
||||
const listUrl = (name) => `http://127.0.0.1:${PORT}/lists/${name}`
|
||||
const group = (over) => ({
|
||||
allowTxtBlockingReport: true, blockAsNxDomain: true, blockingAddresses: ['0.0.0.0', '::'],
|
||||
allowed: [], blocked: [], allowListUrls: [], blockListUrls: [], allowedRegex: [], blockedRegex: [],
|
||||
regexAllowListUrls: [], regexBlockListUrls: [], adblockListUrls: [], ...over,
|
||||
})
|
||||
const appConfigs = {
|
||||
'Advanced Blocking': VARIANT === 'b'
|
||||
? { enableBlocking: true, blockingAnswerTtl: 60, localGroup: 'office', networkGroupMap: { '0.0.0.0/0': 'default' }, groups: [{ name: 'default', enableBlocking: false, blockAsNxDomain: false }] }
|
||||
: { enableBlocking: true, blockingAnswerTtl: 30, localGroup: 'home', networkGroupMap: { '0.0.0.0/0': 'default' }, groups: [{ name: 'default', enableBlocking: true, blockAsNxDomain: false }] },
|
||||
? {
|
||||
enableBlocking: true, blockingAnswerTtl: 60, blockListUrlUpdateIntervalHours: 24, localEndPointGroupMap: { '127.0.0.1': 'bypass' },
|
||||
networkGroupMap: { '0.0.0.0/0': 'default', '::/0': 'default' },
|
||||
groups: [
|
||||
group({ name: 'default', enableBlocking: true, blocked: ['tracker.example'], blockedRegex: ['^ads\\.'], blockListUrls: [listUrl('hosts.txt')], adblockListUrls: [listUrl('easylist.txt')] }),
|
||||
group({ name: 'bypass', enableBlocking: false }),
|
||||
],
|
||||
}
|
||||
: {
|
||||
enableBlocking: true, blockingAnswerTtl: 30, blockListUrlUpdateIntervalHours: 24, localEndPointGroupMap: { '127.0.0.1': 'bypass' },
|
||||
networkGroupMap: { '192.168.10.0/24': 'family', '0.0.0.0/0': 'everyone', '::/0': 'everyone' },
|
||||
groups: [
|
||||
group({
|
||||
name: 'everyone', enableBlocking: true, blocked: ['ads.example.com', 'telemetry.example.net'], allowed: ['good.example.org'],
|
||||
blockListUrls: [listUrl('hosts.txt'), 'https://lists.invalid/hosts'],
|
||||
allowListUrls: ['https://example.org/allow.txt'], blockedRegex: ['^ads\\.', '^track\\d+\\.'], allowedRegex: ['^cdn\\.good\\.'],
|
||||
regexBlockListUrls: ['https://example.org/regex.txt'], adblockListUrls: [listUrl('easylist.txt')],
|
||||
}),
|
||||
group({ name: 'family', enableBlocking: true, blocked: ['games.example.com'], blockListUrls: [listUrl('hosts.txt')], regexAllowListUrls: ['https://example.org/regex-allow.txt'] }),
|
||||
group({ name: 'bypass', enableBlocking: false }),
|
||||
],
|
||||
},
|
||||
'Query Logs (Sqlite)': { enableLogging: true, maxLogDays: 365, sqliteDbPath: VARIANT === 'b' ? '/var/lib/dns/ndns/querylogs.db' : '/var/lib/dns/mdns/querylogs.db' },
|
||||
'Split Horizon': { domainGroupMap: { 'lan.example.net': 'internal' }, networkGroupMap: { '10.0.0.0/8': 'internal' } },
|
||||
}
|
||||
@@ -438,6 +470,15 @@ http.createServer((req, res) => {
|
||||
const q = new URLSearchParams(url.search)
|
||||
if ((req.headers['content-type'] ?? '').includes('application/x-www-form-urlencoded')) for (const [k, v] of new URLSearchParams(body)) q.set(k, v)
|
||||
if ((req.headers['content-type'] ?? '').includes('text/plain') && body !== '') q.set('__text', body)
|
||||
const fakeList = url.pathname.match(/^\/lists\/([\w.-]+)$/)
|
||||
if (fakeList !== null) {
|
||||
const text = FAKE_LISTS[fakeList[1]]
|
||||
res.statusCode = text === undefined ? 404 : 200
|
||||
res.setHeader('access-control-allow-origin', '*')
|
||||
res.setHeader('content-type', 'text/plain')
|
||||
res.end(text ?? 'not found')
|
||||
return
|
||||
}
|
||||
const file = fileFor(path, q)
|
||||
if (file !== undefined) {
|
||||
res.setHeader('content-type', 'text/plain')
|
||||
|
||||
Reference in new issue
Block a user