Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79765d6729 | |||
| ffc93eb9d3 | |||
| 1337a4905a | |||
| c7418d9e1a | |||
| 2a94ffd4c9 | |||
| b2fe6caf33 |
27
changelog.md
27
changelog.md
@@ -1,5 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-02-21 - 7.4.0 - feat(opsserver)
|
||||
add real-time log push to ops dashboard and recent DNS query tracking
|
||||
|
||||
- Export baseLogger and add a log destination that pushes log entries to connected ops_dashboard TypedSocket clients (ts/opsserver/handlers/logs.handler.ts, ts/logger.ts).
|
||||
- Introduce a new TypedRequest (pushLogEntry) interface for server→client log pushes (ts_interfaces/requests/logs.ts) and wire client handling in the web UI (ts_web/appstate.ts, ts_web/plugins.ts).
|
||||
- Add TypedSocket client connection lifecycle to the web app, stream pushed log entries into app state and update log views incrementally (ts_web/appstate.ts, ts_web/elements/ops-view-logs.ts).
|
||||
- MetricsManager now records recent DNS queries (timestamp, domain, type, answered, responseTimeMs) and exposes them via stats endpoints for display in the UI (ts/monitoring/classes.metricsmanager.ts, ts/opsserver/handlers/stats.handler.ts, ts_interfaces/data/stats.ts).
|
||||
- UI overview now displays DNS query entries and uses answered flag to set log level (ts_web/elements/ops-view-overview.ts).
|
||||
- Add import/export for typedsocket in web plugins to enable real-time push (ts_web/plugins.ts).
|
||||
- Bump dependency @push.rocks/smartproxy patch version ^25.7.8 → ^25.7.9 (package.json).
|
||||
|
||||
## 2026-02-20 - 7.3.0 - feat(dcrouter)
|
||||
Wire DNS server 'query' events to MetricsManager for time-series tracking and bump @push.rocks/smartdns to ^7.9.0
|
||||
|
||||
- Add dnsServer 'query' event listener that iterates event.questions and calls metricsManager.trackDnsQuery(question.type, question.name, false, event.responseTimeMs).
|
||||
- Listener is guarded by a metricsManager existence check to avoid runtime errors when metrics are not configured.
|
||||
- Bump dependency @push.rocks/smartdns from ^7.8.1 to ^7.9.0 in package.json.
|
||||
|
||||
## 2026-02-20 - 7.2.0 - feat(logs)
|
||||
replace custom logs list with dees-chart-log component and push logs to chart, add log mapping and lifecycle sync, and bump smartlog dependency
|
||||
|
||||
- Replaced the legacy in-component log list and styling with a dees-chart-log element to render application logs.
|
||||
- Added updated() lifecycle handler to push new logs to the chart and new helper methods pushLogsToChart() and getMappedLogEntries() to map log entries to the chart's expected format.
|
||||
- Removed the streaming toggle, getActiveFilters(), legacy CSS for the log list, and the old per-entry rendering markup.
|
||||
- Added explicit typing for dropdown @selectedOption handlers (e: any).
|
||||
- Bumped dependency @push.rocks/smartlog from ^3.2.0 to ^3.2.1 in package.json.
|
||||
|
||||
## 2026-02-19 - 7.1.0 - feat(ops/monitoring)
|
||||
add in-memory log buffer, metrics time-series and ops UI integration
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@serve.zone/dcrouter",
|
||||
"private": false,
|
||||
"version": "7.1.0",
|
||||
"version": "7.4.0",
|
||||
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -38,18 +38,18 @@
|
||||
"@push.rocks/qenv": "^6.1.3",
|
||||
"@push.rocks/smartacme": "^9.1.3",
|
||||
"@push.rocks/smartdata": "^7.0.15",
|
||||
"@push.rocks/smartdns": "^7.8.1",
|
||||
"@push.rocks/smartdns": "^7.9.0",
|
||||
"@push.rocks/smartfile": "^13.1.2",
|
||||
"@push.rocks/smartguard": "^3.1.0",
|
||||
"@push.rocks/smartjwt": "^2.2.1",
|
||||
"@push.rocks/smartlog": "^3.2.0",
|
||||
"@push.rocks/smartlog": "^3.2.1",
|
||||
"@push.rocks/smartmetrics": "^3.0.1",
|
||||
"@push.rocks/smartmongo": "^5.1.0",
|
||||
"@push.rocks/smartmta": "^5.2.2",
|
||||
"@push.rocks/smartnetwork": "^4.4.0",
|
||||
"@push.rocks/smartpath": "^6.0.0",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"@push.rocks/smartproxy": "^25.7.8",
|
||||
"@push.rocks/smartproxy": "^25.7.9",
|
||||
"@push.rocks/smartradius": "^1.1.1",
|
||||
"@push.rocks/smartrequest": "^5.0.1",
|
||||
"@push.rocks/smartrx": "^3.0.10",
|
||||
|
||||
90
pnpm-lock.yaml
generated
90
pnpm-lock.yaml
generated
@@ -42,8 +42,8 @@ importers:
|
||||
specifier: ^7.0.15
|
||||
version: 7.0.15(socks@2.8.7)
|
||||
'@push.rocks/smartdns':
|
||||
specifier: ^7.8.1
|
||||
version: 7.8.1
|
||||
specifier: ^7.9.0
|
||||
version: 7.9.0
|
||||
'@push.rocks/smartfile':
|
||||
specifier: ^13.1.2
|
||||
version: 13.1.2
|
||||
@@ -54,8 +54,8 @@ importers:
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
'@push.rocks/smartlog':
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
'@push.rocks/smartmetrics':
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@@ -75,8 +75,8 @@ importers:
|
||||
specifier: ^4.2.3
|
||||
version: 4.2.3
|
||||
'@push.rocks/smartproxy':
|
||||
specifier: ^25.7.8
|
||||
version: 25.7.8
|
||||
specifier: ^25.7.9
|
||||
version: 25.7.9
|
||||
'@push.rocks/smartradius':
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1
|
||||
@@ -894,8 +894,8 @@ packages:
|
||||
'@push.rocks/smartdelay@3.0.5':
|
||||
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
|
||||
|
||||
'@push.rocks/smartdns@7.8.1':
|
||||
resolution: {integrity: sha512-qEizM9dFzhq4XGICDC8Im7JLjwdokHdDZ6wLufBInaEOupq+8XOa9bC6EGlBQVsCXFUyrKzsFk6eBa9BSZMKPw==}
|
||||
'@push.rocks/smartdns@7.9.0':
|
||||
resolution: {integrity: sha512-1nDUfyXQo6j9HTUfcjE+BLeAv9QZ7WtAsM1V28zIoFdUpjNg/5g382L024H73PHsxh6lSYNhYYmFvWqzFQhXKg==}
|
||||
|
||||
'@push.rocks/smartenv@5.0.13':
|
||||
resolution: {integrity: sha512-ACXmUcHZHl2CF2jnVuRw9saRRrZvJblCRs2d+K5aLR1DfkYFX3eA21kcMlKeLisI3aGNbIj9vz/rowN5qkRkfA==}
|
||||
@@ -960,8 +960,8 @@ packages:
|
||||
'@push.rocks/smartlog-interfaces@3.0.2':
|
||||
resolution: {integrity: sha512-8hGRTJehbsFSJxLhCQkA018mZtXVPxPTblbg9VaE/EqISRzUw+eosJ2EJV7M4Qu0eiTJZjnWnNLn8CkD77ziWw==}
|
||||
|
||||
'@push.rocks/smartlog@3.2.0':
|
||||
resolution: {integrity: sha512-d6IzsSG8HTmgxr8c9BVzZWpn8m3c17b5O+orHdwrlgHqPVa0+WXMe1ezItuPVZH5q6i8h+OfCv382PTloNiweg==}
|
||||
'@push.rocks/smartlog@3.2.1':
|
||||
resolution: {integrity: sha512-x9/P59pfzY6HOGYmYrhqmoRl/pliTVx44g2Vbb8dIr/0zA39cAJHlPze1+UGncn37XKGmutK2iLSsJLEsexD0A==}
|
||||
|
||||
'@push.rocks/smartmail@2.2.0':
|
||||
resolution: {integrity: sha512-28K4HAcda7ODUUpFCgbS/uA+eqwVRcmLJERIdM9AvLHXaHAPLHH97HmwPPcAu9Sp3z05Um0inmDF51X6yVVkcw==}
|
||||
@@ -1030,8 +1030,8 @@ packages:
|
||||
'@push.rocks/smartpromise@4.2.3':
|
||||
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
|
||||
|
||||
'@push.rocks/smartproxy@25.7.8':
|
||||
resolution: {integrity: sha512-rKuC/5DgCBQmk1iCY2mZd+ZdH2mBOfcP1hWMARTP4Je4KqnNTJ2STM1tJmc9FmKVXxtEQCxWJnEnq1wNqwQFRA==}
|
||||
'@push.rocks/smartproxy@25.7.9':
|
||||
resolution: {integrity: sha512-5esFvD72TEyveaEQbDYRgD7C5hDfWMSBvurNx3KPi02CBKG1gnhx/WWT7RHDS3KRF5fEQh9YxvI9aMkOwjc7sQ==}
|
||||
|
||||
'@push.rocks/smartpuppeteer@2.0.5':
|
||||
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
|
||||
@@ -3221,10 +3221,6 @@ packages:
|
||||
resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
minimatch@10.2.0:
|
||||
resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
minimatch@10.2.1:
|
||||
resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==}
|
||||
engines: {node: 20 || >=22}
|
||||
@@ -4300,7 +4296,7 @@ snapshots:
|
||||
'@push.rocks/smartfeed': 1.4.0
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartlog-destination-devtools': 1.0.12
|
||||
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||
'@push.rocks/smartmanifest': 2.0.2
|
||||
@@ -4349,7 +4345,7 @@ snapshots:
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartlog-destination-devtools': 1.0.12
|
||||
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||
'@push.rocks/smartmanifest': 2.0.2
|
||||
@@ -4416,7 +4412,7 @@ snapshots:
|
||||
'@apiclient.xyz/cloudflare@7.1.0':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 5.0.1
|
||||
'@push.rocks/smartstring': 4.1.0
|
||||
@@ -5150,7 +5146,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
typescript: 5.9.3
|
||||
@@ -5171,7 +5167,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartinteract': 2.0.16
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
@@ -5197,7 +5193,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartnpm': 2.0.6
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartrequest': 5.0.1
|
||||
@@ -5232,7 +5228,7 @@ snapshots:
|
||||
'@push.rocks/smartexpect': 2.5.0
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartmongo': 2.2.0(socks@2.8.7)
|
||||
'@push.rocks/smartnetwork': 4.4.0
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
@@ -5278,7 +5274,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartinteract': 2.0.16
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartshell': 3.3.0
|
||||
'@push.rocks/smartwatch': 6.3.0
|
||||
@@ -5711,7 +5707,7 @@ snapshots:
|
||||
'@push.rocks/qenv': 6.1.3
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@@ -5735,7 +5731,7 @@ snapshots:
|
||||
'@api.global/typedrequest': 3.2.6
|
||||
'@configvault.io/interfaces': 1.0.17
|
||||
'@push.rocks/smartfile': 11.2.7
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
|
||||
'@push.rocks/smartacme@9.1.3(socks@2.8.7)':
|
||||
@@ -5745,8 +5741,8 @@ snapshots:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdata': 7.0.15(socks@2.8.7)
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartdns': 7.8.1
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartdns': 7.9.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartnetwork': 4.4.0
|
||||
'@push.rocks/smartstring': 4.1.0
|
||||
'@push.rocks/smarttime': 4.2.3
|
||||
@@ -5856,7 +5852,7 @@ snapshots:
|
||||
'@push.rocks/smartcli@4.0.20':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartobject': 1.0.12
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@@ -5881,7 +5877,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartmongo': 2.2.0(socks@2.8.7)
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@@ -5910,7 +5906,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartmongo': 2.2.0(socks@2.8.7)
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@@ -5939,7 +5935,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
|
||||
'@push.rocks/smartdns@7.8.1':
|
||||
'@push.rocks/smartdns@7.9.0':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
@@ -5947,7 +5943,7 @@ snapshots:
|
||||
'@push.rocks/smartrust': 1.2.1
|
||||
'@tsclass/tsclass': 9.3.0
|
||||
acme-client: 5.4.0
|
||||
minimatch: 10.2.0
|
||||
minimatch: 10.2.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -6102,7 +6098,7 @@ snapshots:
|
||||
'@api.global/typedrequest-interfaces': 2.0.2
|
||||
'@tsclass/tsclass': 4.4.4
|
||||
|
||||
'@push.rocks/smartlog@3.2.0':
|
||||
'@push.rocks/smartlog@3.2.1':
|
||||
dependencies:
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
'@push.rocks/consolecolor': 2.0.3
|
||||
@@ -6117,7 +6113,7 @@ snapshots:
|
||||
|
||||
'@push.rocks/smartmail@2.2.0':
|
||||
dependencies:
|
||||
'@push.rocks/smartdns': 7.8.1
|
||||
'@push.rocks/smartdns': 7.9.0
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartmustache': 3.0.2
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
@@ -6149,7 +6145,7 @@ snapshots:
|
||||
'@push.rocks/smartmetrics@3.0.1':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
|
||||
'@push.rocks/smartmime@1.0.6':
|
||||
dependencies:
|
||||
@@ -6218,7 +6214,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/smartfile': 13.1.2
|
||||
'@push.rocks/smartfs': 1.3.1
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartmail': 2.2.0
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
'@push.rocks/smartrust': 1.2.1
|
||||
@@ -6235,7 +6231,7 @@ snapshots:
|
||||
|
||||
'@push.rocks/smartnetwork@4.4.0':
|
||||
dependencies:
|
||||
'@push.rocks/smartdns': 7.8.1
|
||||
'@push.rocks/smartdns': 7.9.0
|
||||
'@push.rocks/smartping': 1.0.8
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartstring': 4.1.0
|
||||
@@ -6321,10 +6317,10 @@ snapshots:
|
||||
|
||||
'@push.rocks/smartpromise@4.2.3': {}
|
||||
|
||||
'@push.rocks/smartproxy@25.7.8':
|
||||
'@push.rocks/smartproxy@25.7.9':
|
||||
dependencies:
|
||||
'@push.rocks/smartcrypto': 2.0.4
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartrust': 1.2.1
|
||||
'@tsclass/tsclass': 9.3.0
|
||||
minimatch: 10.2.1
|
||||
@@ -6405,7 +6401,7 @@ snapshots:
|
||||
'@cfworker/json-schema': 4.1.1
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartenv': 6.0.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpath': 6.0.0
|
||||
ws: 8.19.0
|
||||
transitivePeerDependencies:
|
||||
@@ -6440,7 +6436,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartenv': 5.0.13
|
||||
'@push.rocks/smartjson': 5.2.0
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
@@ -6555,7 +6551,7 @@ snapshots:
|
||||
'@design.estate/dees-element': 2.1.6
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
@@ -6571,7 +6567,7 @@ snapshots:
|
||||
'@design.estate/dees-element': 2.1.6
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
@@ -6587,7 +6583,7 @@ snapshots:
|
||||
'@design.estate/dees-element': 2.1.6
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.2.0
|
||||
'@push.rocks/smartlog': 3.2.1
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.2.3
|
||||
@@ -9120,10 +9116,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@isaacs/brace-expansion': 5.0.1
|
||||
|
||||
minimatch@10.2.0:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.2
|
||||
|
||||
minimatch@10.2.1:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.2
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '7.1.0',
|
||||
version: '7.4.0',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -1256,6 +1256,21 @@ export class DcRouter {
|
||||
// Start the DNS server (UDP only)
|
||||
await this.dnsServer.start();
|
||||
logger.log('info', `DNS server started on UDP ${vmIpAddress}:53`);
|
||||
|
||||
// Wire DNS query events to MetricsManager for time-series tracking
|
||||
if (this.metricsManager && this.dnsServer) {
|
||||
this.dnsServer.on('query', (event: plugins.smartdns.dnsServerMod.IDnsQueryCompletedEvent) => {
|
||||
for (const question of event.questions) {
|
||||
this.metricsManager.trackDnsQuery(
|
||||
question.type,
|
||||
question.name,
|
||||
false,
|
||||
event.responseTimeMs,
|
||||
event.answered,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Validate DNS configuration
|
||||
await this.validateDnsConfiguration();
|
||||
|
||||
@@ -14,8 +14,8 @@ const envMap: Record<string, 'local' | 'test' | 'staging' | 'production'> = {
|
||||
// In-memory log buffer for the OpsServer UI
|
||||
export const logBuffer = new SmartlogDestinationBuffer({ maxEntries: 2000 });
|
||||
|
||||
// Default Smartlog instance
|
||||
const baseLogger = new plugins.smartlog.Smartlog({
|
||||
// Default Smartlog instance (exported so OpsServer can add push destinations)
|
||||
export const baseLogger = new plugins.smartlog.Smartlog({
|
||||
logContext: {
|
||||
environment: envMap[nodeEnv] || 'production',
|
||||
runtime: 'node',
|
||||
|
||||
@@ -36,6 +36,7 @@ export class MetricsManager {
|
||||
lastResetDate: new Date().toDateString(),
|
||||
queryTimestamps: [] as number[], // Track query timestamps for rate calculation
|
||||
responseTimes: [] as number[], // Track response times in ms
|
||||
recentQueries: [] as Array<{ timestamp: number; domain: string; type: string; answered: boolean; responseTimeMs: number }>,
|
||||
};
|
||||
|
||||
// Per-minute time-series buckets for charts
|
||||
@@ -95,6 +96,7 @@ export class MetricsManager {
|
||||
this.dnsMetrics.topDomains.clear();
|
||||
this.dnsMetrics.queryTimestamps = [];
|
||||
this.dnsMetrics.responseTimes = [];
|
||||
this.dnsMetrics.recentQueries = [];
|
||||
this.dnsMetrics.lastResetDate = currentDate;
|
||||
}
|
||||
|
||||
@@ -228,6 +230,7 @@ export class MetricsManager {
|
||||
queryTypes: this.dnsMetrics.queryTypes,
|
||||
averageResponseTime: Math.round(avgResponseTime),
|
||||
activeDomains: this.dnsMetrics.topDomains.size,
|
||||
recentQueries: this.dnsMetrics.recentQueries.slice(),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -392,9 +395,21 @@ export class MetricsManager {
|
||||
}
|
||||
|
||||
// DNS event tracking methods
|
||||
public trackDnsQuery(queryType: string, domain: string, cacheHit: boolean, responseTimeMs?: number): void {
|
||||
public trackDnsQuery(queryType: string, domain: string, cacheHit: boolean, responseTimeMs?: number, answered?: boolean): void {
|
||||
this.dnsMetrics.totalQueries++;
|
||||
this.incrementDnsBucket();
|
||||
|
||||
// Store recent query entry
|
||||
this.dnsMetrics.recentQueries.push({
|
||||
timestamp: Date.now(),
|
||||
domain,
|
||||
type: queryType,
|
||||
answered: answered ?? true,
|
||||
responseTimeMs: responseTimeMs ?? 0,
|
||||
});
|
||||
if (this.dnsMetrics.recentQueries.length > 100) {
|
||||
this.dnsMetrics.recentQueries.shift();
|
||||
}
|
||||
|
||||
if (cacheHit) {
|
||||
this.dnsMetrics.cacheHits++;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import type { OpsServer } from '../classes.opsserver.js';
|
||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||
import { logBuffer } from '../../logger.js';
|
||||
import { logBuffer, baseLogger } from '../../logger.js';
|
||||
|
||||
export class LogsHandler {
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
|
||||
|
||||
constructor(private opsServerRef: OpsServer) {
|
||||
// Add this handler's router to the parent
|
||||
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
this.registerHandlers();
|
||||
this.setupLogPushDestination();
|
||||
}
|
||||
|
||||
private registerHandlers(): void {
|
||||
@@ -165,6 +166,50 @@ export class LogsHandler {
|
||||
return mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a log destination to the base logger that pushes entries
|
||||
* to all connected ops_dashboard TypedSocket clients.
|
||||
*/
|
||||
private setupLogPushDestination(): void {
|
||||
const opsServerRef = this.opsServerRef;
|
||||
|
||||
baseLogger.addLogDestination({
|
||||
async handleLog(logPackage: any) {
|
||||
// Access the TypedSocket server instance from OpsServer
|
||||
const typedsocket = opsServerRef.server?.typedserver?.typedsocket;
|
||||
if (!typedsocket) return;
|
||||
|
||||
let connections: any[];
|
||||
try {
|
||||
connections = await typedsocket.findAllTargetConnectionsByTag('role', 'ops_dashboard');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (connections.length === 0) return;
|
||||
|
||||
const entry: interfaces.data.ILogEntry = {
|
||||
timestamp: logPackage.timestamp || Date.now(),
|
||||
level: LogsHandler.mapLogLevel(logPackage.level),
|
||||
category: LogsHandler.deriveCategory(logPackage.context?.zone, logPackage.message),
|
||||
message: logPackage.message,
|
||||
metadata: logPackage.data,
|
||||
};
|
||||
|
||||
for (const conn of connections) {
|
||||
try {
|
||||
const push = typedsocket.createTypedRequest<interfaces.requests.IReq_PushLogEntry>(
|
||||
'pushLogEntry',
|
||||
conn,
|
||||
);
|
||||
push.fire({ entry }).catch(() => {}); // fire-and-forget
|
||||
} catch {
|
||||
// connection may have closed
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private setupLogStream(
|
||||
virtualStream: plugins.typedrequest.VirtualStream<Uint8Array>,
|
||||
levelFilter?: string[],
|
||||
|
||||
@@ -241,6 +241,7 @@ export class StatsHandler {
|
||||
averageResponseTime: 0,
|
||||
queryTypes: stats.queryTypes,
|
||||
timeSeries,
|
||||
recentQueries: stats.recentQueries,
|
||||
};
|
||||
})
|
||||
);
|
||||
@@ -422,6 +423,7 @@ export class StatsHandler {
|
||||
count: number;
|
||||
}>;
|
||||
queryTypes: { [key: string]: number };
|
||||
recentQueries?: Array<{ timestamp: number; domain: string; type: string; answered: boolean; responseTimeMs: number }>;
|
||||
domainBreakdown?: { [domain: string]: interfaces.data.IDnsStats };
|
||||
}> {
|
||||
// Get metrics from MetricsManager if available
|
||||
@@ -435,9 +437,10 @@ export class StatsHandler {
|
||||
cacheHitRate: dnsStats.cacheHitRate,
|
||||
topDomains: dnsStats.topDomains,
|
||||
queryTypes: dnsStats.queryTypes,
|
||||
recentQueries: dnsStats.recentQueries,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Fallback if MetricsManager not available
|
||||
return {
|
||||
queriesPerSecond: 0,
|
||||
|
||||
@@ -60,6 +60,13 @@ export interface IDnsStats {
|
||||
timeSeries?: {
|
||||
queries: ITimeSeriesPoint[];
|
||||
};
|
||||
recentQueries?: Array<{
|
||||
timestamp: number;
|
||||
domain: string;
|
||||
type: string;
|
||||
answered: boolean;
|
||||
responseTimeMs: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IRateLimitInfo {
|
||||
|
||||
@@ -41,4 +41,16 @@ export interface IReq_GetLogStream extends plugins.typedrequestInterfaces.implem
|
||||
response: {
|
||||
logStream: plugins.typedrequestInterfaces.IVirtualStream;
|
||||
};
|
||||
}
|
||||
|
||||
// Push Log Entry (server → client via TypedSocket)
|
||||
export interface IReq_PushLogEntry extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_PushLogEntry
|
||||
> {
|
||||
method: 'pushLogEntry';
|
||||
request: {
|
||||
entry: statsInterfaces.ILogEntry;
|
||||
};
|
||||
response: {};
|
||||
}
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '7.1.0',
|
||||
version: '7.4.0',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -1075,6 +1075,55 @@ export const toggleRemoteIngressAction = remoteIngressStatePart.createAction<{
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// TypedSocket Client for Real-time Log Streaming
|
||||
// ============================================================================
|
||||
|
||||
let socketClient: plugins.typedsocket.TypedSocket | null = null;
|
||||
const socketRouter = new plugins.domtools.plugins.typedrequest.TypedRouter();
|
||||
|
||||
// Register handler for pushed log entries from the server
|
||||
socketRouter.addTypedHandler(
|
||||
new plugins.domtools.plugins.typedrequest.TypedHandler<interfaces.requests.IReq_PushLogEntry>(
|
||||
'pushLogEntry',
|
||||
async (dataArg) => {
|
||||
const current = logStatePart.getState();
|
||||
const updated = [...current.recentLogs, dataArg.entry];
|
||||
// Cap at 2000 entries
|
||||
if (updated.length > 2000) {
|
||||
updated.splice(0, updated.length - 2000);
|
||||
}
|
||||
logStatePart.setState({ ...current, recentLogs: updated });
|
||||
return {};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
async function connectSocket() {
|
||||
if (socketClient) return;
|
||||
try {
|
||||
socketClient = await plugins.typedsocket.TypedSocket.createClient(
|
||||
socketRouter,
|
||||
plugins.typedsocket.TypedSocket.useWindowLocationOriginUrl(),
|
||||
);
|
||||
await socketClient.setTag('role', 'ops_dashboard');
|
||||
} catch (err) {
|
||||
console.error('TypedSocket connection failed:', err);
|
||||
socketClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function disconnectSocket() {
|
||||
if (socketClient) {
|
||||
try {
|
||||
await socketClient.stop();
|
||||
} catch {
|
||||
// ignore disconnect errors
|
||||
}
|
||||
socketClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Combined refresh action for efficient polling
|
||||
async function dispatchCombinedRefreshAction() {
|
||||
const context = getActionContext();
|
||||
@@ -1237,9 +1286,21 @@ let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessar
|
||||
if (state.isLoggedIn !== previousIsLoggedIn) {
|
||||
previousIsLoggedIn = state.isLoggedIn;
|
||||
startAutoRefresh();
|
||||
|
||||
// Connect/disconnect TypedSocket based on login state
|
||||
if (state.isLoggedIn) {
|
||||
connectSocket();
|
||||
} else {
|
||||
disconnectSocket();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initial start
|
||||
startAutoRefresh();
|
||||
|
||||
// Connect TypedSocket if already logged in (e.g., persistent session)
|
||||
if (loginStatePart.getState().isLoggedIn) {
|
||||
connectSocket();
|
||||
}
|
||||
})();
|
||||
@@ -29,6 +29,8 @@ export class OpsViewLogs extends DeesElement {
|
||||
@state()
|
||||
accessor filterLimit: number = 100;
|
||||
|
||||
private lastPushedCount = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const subscription = appstate.logStatePart
|
||||
@@ -55,87 +57,20 @@ export class OpsViewLogs extends DeesElement {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.logContainer {
|
||||
background: ${cssManager.bdTheme('#f8f9fa', '#1e1e1e')};
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.logEntry {
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logTimestamp {
|
||||
color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')};
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.logLevel {
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.logLevel.debug {
|
||||
color: ${cssManager.bdTheme('#6a9955', '#6a9955')};
|
||||
background: ${cssManager.bdTheme('rgba(106, 153, 85, 0.1)', 'rgba(106, 153, 85, 0.1)')};
|
||||
}
|
||||
.logLevel.info {
|
||||
color: ${cssManager.bdTheme('#569cd6', '#569cd6')};
|
||||
background: ${cssManager.bdTheme('rgba(86, 156, 214, 0.1)', 'rgba(86, 156, 214, 0.1)')};
|
||||
}
|
||||
.logLevel.warn {
|
||||
color: ${cssManager.bdTheme('#ce9178', '#ce9178')};
|
||||
background: ${cssManager.bdTheme('rgba(206, 145, 120, 0.1)', 'rgba(206, 145, 120, 0.1)')};
|
||||
}
|
||||
.logLevel.error {
|
||||
color: ${cssManager.bdTheme('#f44747', '#f44747')};
|
||||
background: ${cssManager.bdTheme('rgba(244, 71, 71, 0.1)', 'rgba(244, 71, 71, 0.1)')};
|
||||
}
|
||||
|
||||
.logCategory {
|
||||
color: ${cssManager.bdTheme('#c586c0', '#c586c0')};
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.logMessage {
|
||||
color: ${cssManager.bdTheme('#333', '#d4d4d4')};
|
||||
}
|
||||
|
||||
.noLogs {
|
||||
color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')};
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<ops-sectionheading>Logs</ops-sectionheading>
|
||||
|
||||
|
||||
<div class="controls">
|
||||
<div class="filterGroup">
|
||||
<dees-button
|
||||
<dees-button
|
||||
@click=${() => this.fetchLogs()}
|
||||
>
|
||||
Refresh Logs
|
||||
</dees-button>
|
||||
|
||||
<dees-button
|
||||
@click=${() => this.toggleStreaming()}
|
||||
.type=${this.logState.isStreaming ? 'highlighted' : 'normal'}
|
||||
>
|
||||
${this.logState.isStreaming ? 'Stop Streaming' : 'Start Streaming'}
|
||||
</dees-button>
|
||||
</div>
|
||||
|
||||
<div class="filterGroup">
|
||||
@@ -143,7 +78,7 @@ export class OpsViewLogs extends DeesElement {
|
||||
<dees-input-dropdown
|
||||
.options=${['all', 'debug', 'info', 'warn', 'error']}
|
||||
.selectedOption=${'all'}
|
||||
@selectedOption=${(e) => this.updateFilter('level', e.detail)}
|
||||
@selectedOption=${(e: any) => this.updateFilter('level', e.detail)}
|
||||
></dees-input-dropdown>
|
||||
</div>
|
||||
|
||||
@@ -152,7 +87,7 @@ export class OpsViewLogs extends DeesElement {
|
||||
<dees-input-dropdown
|
||||
.options=${['all', 'smtp', 'dns', 'security', 'system', 'email']}
|
||||
.selectedOption=${'all'}
|
||||
@selectedOption=${(e) => this.updateFilter('category', e.detail)}
|
||||
@selectedOption=${(e: any) => this.updateFilter('category', e.detail)}
|
||||
></dees-input-dropdown>
|
||||
</div>
|
||||
|
||||
@@ -161,32 +96,63 @@ export class OpsViewLogs extends DeesElement {
|
||||
<dees-input-dropdown
|
||||
.options=${['50', '100', '200', '500']}
|
||||
.selectedOption=${'100'}
|
||||
@selectedOption=${(e) => this.updateFilter('limit', e.detail)}
|
||||
@selectedOption=${(e: any) => this.updateFilter('limit', e.detail)}
|
||||
></dees-input-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logContainer">
|
||||
${this.logState.recentLogs.length > 0 ?
|
||||
this.logState.recentLogs.map(log => html`
|
||||
<div class="logEntry">
|
||||
<span class="logTimestamp">${new Date(log.timestamp).toLocaleTimeString()}</span>
|
||||
<span class="logLevel ${log.level}">${log.level.toUpperCase()}</span>
|
||||
<span class="logCategory">[${log.category}]</span>
|
||||
<span class="logMessage">${log.message}</span>
|
||||
</div>
|
||||
`) : html`
|
||||
<div class="noLogs">No logs to display</div>
|
||||
`
|
||||
}
|
||||
</div>
|
||||
<dees-chart-log
|
||||
.label=${'Application Logs'}
|
||||
.autoScroll=${true}
|
||||
.maxEntries=${2000}
|
||||
.showMetrics=${true}
|
||||
></dees-chart-log>
|
||||
`;
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
// Auto-fetch logs when the view mounts
|
||||
this.fetchLogs();
|
||||
this.lastPushedCount = 0;
|
||||
// Only fetch if state is empty (streaming will handle new entries)
|
||||
if (this.logState.recentLogs.length === 0) {
|
||||
this.fetchLogs();
|
||||
}
|
||||
}
|
||||
|
||||
async updated(changedProperties: Map<string, any>) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has('logState')) {
|
||||
this.pushLogsToChart();
|
||||
}
|
||||
}
|
||||
|
||||
private async pushLogsToChart() {
|
||||
const chartLog = this.shadowRoot?.querySelector('dees-chart-log') as any;
|
||||
if (!chartLog) return;
|
||||
|
||||
// Ensure the chart element has finished its own initialization
|
||||
await chartLog.updateComplete;
|
||||
|
||||
const allEntries = this.getMappedLogEntries();
|
||||
if (this.lastPushedCount === 0 && allEntries.length > 0) {
|
||||
// Initial load: push all entries
|
||||
chartLog.updateLog(allEntries);
|
||||
this.lastPushedCount = allEntries.length;
|
||||
} else if (allEntries.length > this.lastPushedCount) {
|
||||
// Incremental: only push new entries
|
||||
const newEntries = allEntries.slice(this.lastPushedCount);
|
||||
chartLog.updateLog(newEntries);
|
||||
this.lastPushedCount = allEntries.length;
|
||||
}
|
||||
}
|
||||
|
||||
private getMappedLogEntries() {
|
||||
return this.logState.recentLogs.map((log) => ({
|
||||
timestamp: new Date(log.timestamp).toISOString(),
|
||||
level: log.level as 'debug' | 'info' | 'warn' | 'error',
|
||||
message: log.message,
|
||||
source: log.category,
|
||||
}));
|
||||
}
|
||||
|
||||
private async fetchLogs() {
|
||||
@@ -214,17 +180,4 @@ export class OpsViewLogs extends DeesElement {
|
||||
|
||||
this.fetchLogs();
|
||||
}
|
||||
|
||||
private getActiveFilters() {
|
||||
return {
|
||||
level: this.filterLevel,
|
||||
category: this.filterCategory,
|
||||
limit: this.filterLimit,
|
||||
};
|
||||
}
|
||||
|
||||
private toggleStreaming() {
|
||||
// TODO: Implement log streaming with VirtualStream
|
||||
console.log('Streaming toggle not yet implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,8 +133,8 @@ export class OpsViewOverview extends DeesElement {
|
||||
.logEntries=${this.getRecentEventEntries()}
|
||||
></dees-chart-log>
|
||||
<dees-chart-log
|
||||
.label=${'Security Alerts'}
|
||||
.logEntries=${this.getSecurityAlertEntries()}
|
||||
.label=${'DNS Queries'}
|
||||
.logEntries=${this.getDnsQueryEntries()}
|
||||
></dees-chart-log>
|
||||
</div>
|
||||
`}
|
||||
@@ -395,6 +395,16 @@ export class OpsViewOverview extends DeesElement {
|
||||
}));
|
||||
}
|
||||
|
||||
private getDnsQueryEntries(): Array<{ timestamp: string; level: 'debug' | 'info' | 'warn' | 'error' | 'success'; message: string; source?: string }> {
|
||||
const queries: any[] = (this.statsState.dnsStats as any)?.recentQueries || [];
|
||||
return queries.map((q: any) => ({
|
||||
timestamp: new Date(q.timestamp).toISOString(),
|
||||
level: q.answered ? 'info' as const : 'warn' as const,
|
||||
message: `${q.type} ${q.domain} (${q.responseTimeMs}ms)`,
|
||||
source: 'dns',
|
||||
}));
|
||||
}
|
||||
|
||||
private getEmailTrafficSeries(): Array<{ name: string; color: string; data: Array<{ x: number; y: number }> }> {
|
||||
const ts = this.statsState.emailStats?.timeSeries;
|
||||
if (!ts) return [];
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
import * as deesElement from '@design.estate/dees-element';
|
||||
import * as deesCatalog from '@design.estate/dees-catalog';
|
||||
|
||||
// TypedSocket for real-time push communication
|
||||
import * as typedsocket from '@api.global/typedsocket';
|
||||
|
||||
export {
|
||||
deesElement,
|
||||
deesCatalog
|
||||
deesCatalog,
|
||||
typedsocket,
|
||||
}
|
||||
|
||||
// domtools gives us TypedRequest and other utilities
|
||||
|
||||
Reference in New Issue
Block a user