v0.33.5
This commit is contained in:
1 parent
c6486bfe85
commit
5dd802ae83
12 files changed
+295
-26
No files matched your search
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes.
|
Read README.md, ROADMAP.md and docs/TEST_REPORT.md before making changes.
|
||||||
The project name is RustProxy; the Cargo package/binary remains `rust-proxy` for
|
The project name is RustProxy; the Cargo package/binary remains `rust-proxy` for
|
||||||
compatibility. Current source version: 0.33.4.
|
compatibility. Current source version: 0.33.5.
|
||||||
|
|
||||||
## Delivery
|
## Delivery
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
> Operator-facing release history. Historical engineering notes are kept in
|
> Operator-facing release history. Historical engineering notes are kept in
|
||||||
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
|
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
|
||||||
|
|
||||||
|
## 0.33.5
|
||||||
|
|
||||||
|
- Autoscale the Overview **System and process CPU** time series using the
|
||||||
|
measured CPU history instead of the fixed 0–100% vertical range. Actual
|
||||||
|
CPU percentages in the summary and legend are unaffected.
|
||||||
|
- Retain the same full, responsive plot height as other wide Overview charts.
|
||||||
|
No other charts or dashboard sizing, data sources or settings are changed.
|
||||||
|
- Add simulated Chromium checks for adaptive plot geometry, legend visibility
|
||||||
|
and responsive chart heights at desktop and mobile viewport sizes.
|
||||||
|
- Overview heading cleanup: move the existing request-rate maximum (`max XX.X
|
||||||
|
req/s`) from the chart footer to the upper-right Requests per second header;
|
||||||
|
remove its LIVE TRAFFIC eyebrow and LIVE badge. Remove the LIVE badge from
|
||||||
|
Connection Activity. No change to collected values, scaling or APIs.
|
||||||
|
- Add simulated Chromium checks for chart headings, live peak placement,
|
||||||
|
dashboard densities and resized widget widths.
|
||||||
|
|
||||||
## 0.33.4
|
## 0.33.4
|
||||||
|
|
||||||
- Unify per-Web-Service live metrics with Vhost cards: six matching live metric
|
- Unify per-Web-Service live metrics with Vhost cards: six matching live metric
|
||||||
|
|||||||
Generated
+1
-1
@@ -1881,7 +1881,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-proxy"
|
name = "rust-proxy"
|
||||||
version = "0.33.4"
|
version = "0.33.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rust-proxy"
|
name = "rust-proxy"
|
||||||
version = "0.33.4"
|
version = "0.33.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.99"
|
rust-version = "1.99"
|
||||||
description = "RustProxy - independent reverse proxy with local administration"
|
description = "RustProxy - independent reverse proxy with local administration"
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
# RustProxy 0.33.4
|
# RustProxy 0.33.5
|
||||||
|
|
||||||
|
## 0.33.5: adaptive CPU chart scaling on Overview
|
||||||
|
|
||||||
|
- **System and process CPU** now uses the same adaptive vertical scale as other
|
||||||
|
Overview time-series charts, instead of forcing a 0–100% axis. Low CPU
|
||||||
|
readings are visible while the legend continues to show their actual values.
|
||||||
|
- Keep the CPU chart at the same responsive height as other wide Overview
|
||||||
|
charts (including HTTP protocol mix); do not shrink it or change any other
|
||||||
|
charts, grid layout, sampling, APIs, databases or saved preferences.
|
||||||
|
- Overview heading follow-up: **Requests per second** shows the existing
|
||||||
|
dynamic `max XX.X req/s` value in the upper-right instead of the LIVE badge;
|
||||||
|
remove the redundant LIVE TRAFFIC eyebrow and its former peak footer label.
|
||||||
|
Remove the LIVE badge from **Connection Activity**, without changing its
|
||||||
|
counter, history, legend or chart rendering.
|
||||||
|
|
||||||
## 0.33.4: unified Web Server live metrics
|
## 0.33.4: unified Web Server live metrics
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -1,5 +1,19 @@
|
|||||||
# RustProxy roadmap
|
# RustProxy roadmap
|
||||||
|
|
||||||
|
## 0.33.5 - Adaptive Overview CPU chart scale
|
||||||
|
|
||||||
|
- [x] Autoscale only the System and process CPU timeline using its measured
|
||||||
|
history; retain actual percentage labels and both plotted series.
|
||||||
|
- [x] Give the CPU plot the standard wide-chart height; leave other chart
|
||||||
|
scales, Overview cards and dashboard sizing behavior unchanged.
|
||||||
|
- [x] Add simulated real-browser checks for plot geometry and low/high CPU
|
||||||
|
samples across viewport widths and Dashboard densities.
|
||||||
|
- [x] Replace the Overview Requests per second LIVE badge with its live maximum
|
||||||
|
rate, remove the LIVE TRAFFIC eyebrow and the duplicate peak footer entry.
|
||||||
|
- [x] Remove Connection Activity's LIVE badge, preserving its metrics.
|
||||||
|
- [x] Browser-test the two card headings, dynamic peak update and responsive
|
||||||
|
placement across viewport widths, dashboard densities and card spans.
|
||||||
|
|
||||||
## 0.33.4 - Web Server/Vhost live-metric visual parity
|
## 0.33.4 - Web Server/Vhost live-metric visual parity
|
||||||
|
|
||||||
- [x] Show per-Web-Service live metrics without expanding a section.
|
- [x] Show per-Web-Service live metrics without expanding a section.
|
||||||
@@ -17,8 +31,6 @@
|
|||||||
keep menu colors independent of the chart palette.
|
keep menu colors independent of the chart palette.
|
||||||
- [x] Test navigation colors in Chromium across 4 themes and 8 chart palettes
|
- [x] Test navigation colors in Chromium across 4 themes and 8 chart palettes
|
||||||
(invariance within each theme), 5 corner settings, hover, mobile and RTL.
|
(invariance within each theme), 5 corner settings, hover, mobile and RTL.
|
||||||
|
|
||||||
|
|
||||||
- [x] Keep the visual layout editor readable at 320-1920px with stable
|
- [x] Keep the visual layout editor readable at 320-1920px with stable
|
||||||
three/two/one-column previews, wrapping controls and no horizontal overflow;
|
three/two/one-column previews, wrapping controls and no horizontal overflow;
|
||||||
leave actual dashboard widths and free ordering unchanged.
|
leave actual dashboard widths and free ordering unchanged.
|
||||||
|
|||||||
+11
-9
@@ -1,13 +1,13 @@
|
|||||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
||||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
||||||
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
||||||
90823b5d38f99f0031cd5fe81248506ed8fc5b0c6f72292bb023e95ed98d9f38 AGENTS.md
|
ffae7ce20cc0fb1990394822515c933843ba39304aa44134403f0d25a6a66d9f AGENTS.md
|
||||||
d331e6aab4fd331e73fe89bfcb96f57dd502f3b2089d6298139c63b6d12154bc CHANGELOG.md
|
95f433de4f779002f6358209dbb178933eab2036f8e69dabda72c5946ef32716 CHANGELOG.md
|
||||||
629a3594e8cffea80603ec4650b91e5f4bea9d65266a1e1122b815ed0d6c5f45 Cargo.lock
|
a35a5d557bb57df37d130a03752ffc092f005cb4849733d3d81485fb9450a931 Cargo.lock
|
||||||
e1d3659e9f745d37ff27a8ecdf0233b450127da4410ca51d4b3582a4d92c59bf Cargo.toml
|
8b77b2beffb31dd14ff62e225ff5384084d2b4e66faa100aaf38470883fca879 Cargo.toml
|
||||||
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
||||||
ca0c0f5658f91381c99a91fa3d2daddee82d185e310c8dce037c150c615dbf47 README.md
|
273fe8c9504f7565986b775850287fbc579a3de13aaffbfdd897a01ae64305fa README.md
|
||||||
d02e695435b061b9d29408e72a1335390e549add0dc9ec901f852d982bfd66e2 ROADMAP.md
|
de605453cec3511eae8b010af5791eb7d526fb09569f91c503b9dd53d0c2fe0e ROADMAP.md
|
||||||
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
||||||
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
|
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
|
||||||
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
|
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
|
||||||
@@ -130,7 +130,9 @@ e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/runtim
|
|||||||
174c8059625e3315a3d9fefe74c17f78ed850ff25ee15bfdabc148107dd344eb scripts/start_stream_backends.sh
|
174c8059625e3315a3d9fefe74c17f78ed850ff25ee15bfdabc148107dd344eb scripts/start_stream_backends.sh
|
||||||
046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/test_stream_tcp.py
|
046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/test_stream_tcp.py
|
||||||
f0b48ec0cc4411755cc0b4128eebce710375afa99344cb76198cf31074f8eb63 scripts/test_stream_udp.py
|
f0b48ec0cc4411755cc0b4128eebce710375afa99344cb76198cf31074f8eb63 scripts/test_stream_udp.py
|
||||||
|
cc7026ec9c5b69850a815816df093de0ffdf4aeadabb3af702783483122ede55 scripts/tests/dashboard_chart_headers_0335_browser_test.py
|
||||||
aa3ca7419d6d7d506c5aaf4d624d20cc7e8ee66bec3cb0cf9b5e7d164bcbab57 scripts/tests/dashboard_chart_width_0333_browser_test.py
|
aa3ca7419d6d7d506c5aaf4d624d20cc7e8ee66bec3cb0cf9b5e7d164bcbab57 scripts/tests/dashboard_chart_width_0333_browser_test.py
|
||||||
|
3171c4e61c72e9ab12f41be7564fe68204aaa8bee7737b32ed6a2ca4e7a7ba94 scripts/tests/dashboard_cpu_autoscale_0335_browser_test.py
|
||||||
52eacb467df123dc4a35f14933e006b090c9238df1b14dcff856a16b37c8063f scripts/tests/dashboard_layout_0331_browser_test.py
|
52eacb467df123dc4a35f14933e006b090c9238df1b14dcff856a16b37c8063f scripts/tests/dashboard_layout_0331_browser_test.py
|
||||||
276e732fc2dfb0d39badb159e4fdcba0d7d73509c60ad5c9df49621c03046f1b scripts/tests/dashboard_metric_visuals_ui_test.mjs
|
276e732fc2dfb0d39badb159e4fdcba0d7d73509c60ad5c9df49621c03046f1b scripts/tests/dashboard_metric_visuals_ui_test.mjs
|
||||||
61a0fb2c4de86f25debec9662aedd923bce8c2ccd51e75593d485b2e0c9b8551 scripts/tests/dashboard_modal_0333_browser_test.py
|
61a0fb2c4de86f25debec9662aedd923bce8c2ccd51e75593d485b2e0c9b8551 scripts/tests/dashboard_modal_0333_browser_test.py
|
||||||
@@ -198,7 +200,7 @@ f3b566d79ba47848021a74653dbd7fec72f0fb819c6b2a07259e047bc8c427fd src/admin/web/
|
|||||||
2f1669cbeb9f49aef43a548e2096825aa14c64c7097ffad520822bbbaefb2b4c src/admin/web/app/overview-extra-metrics.js
|
2f1669cbeb9f49aef43a548e2096825aa14c64c7097ffad520822bbbaefb2b4c src/admin/web/app/overview-extra-metrics.js
|
||||||
5e62cccf6a9507fe208e9f41bbd08453c3d8c6a6b83c56d93a2bb22550ea6e9a src/admin/web/app/overview-masonry.js
|
5e62cccf6a9507fe208e9f41bbd08453c3d8c6a6b83c56d93a2bb22550ea6e9a src/admin/web/app/overview-masonry.js
|
||||||
b52456b41451f156edf5f58492119c22726f2cf40cee938527748997e8ee7412 src/admin/web/app/overview-samples.js
|
b52456b41451f156edf5f58492119c22726f2cf40cee938527748997e8ee7412 src/admin/web/app/overview-samples.js
|
||||||
94ca621117738e064276d426552cc4dfe87ba9b038ddcd73a8d4024c6659a474 src/admin/web/app/overview-wide-metrics.js
|
9c039138fa58d8382601a4b2de366ba7763beb4d35f1cff7899cf2ce36c55afb src/admin/web/app/overview-wide-metrics.js
|
||||||
5bde5f0881320464eb133cd44de9eff5211e6816e79f7f7e9574b16784fab126 src/admin/web/app/preferences.js
|
5bde5f0881320464eb133cd44de9eff5211e6816e79f7f7e9574b16784fab126 src/admin/web/app/preferences.js
|
||||||
8e75b0915edfbadefc4388701431541f4d35fd5d717363297b63740b3afd34d2 src/admin/web/app/rate-sampling.js
|
8e75b0915edfbadefc4388701431541f4d35fd5d717363297b63740b3afd34d2 src/admin/web/app/rate-sampling.js
|
||||||
43f4dddd1c3ea7fa42eeb9d7b655a8db48b5ddcc6c68213e08dfc2fcfbe8b17d src/admin/web/app/refresh.js
|
43f4dddd1c3ea7fa42eeb9d7b655a8db48b5ddcc6c68213e08dfc2fcfbe8b17d src/admin/web/app/refresh.js
|
||||||
@@ -232,7 +234,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/
|
|||||||
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
|
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
|
||||||
7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 src/admin/web/file-logs.js
|
7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 src/admin/web/file-logs.js
|
||||||
b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js
|
b14e7b3578322f309627f6fe965cc16abcf85da3437c9070be6caecae1908743 src/admin/web/host404.js
|
||||||
da102bf2c797c0614e964f535908e89064abd75172395bbca98da68b5e4edcc3 src/admin/web/index.html
|
840c6ef69b9db1d8c4b710ad0eadb4253ac729bd05a7f60e3bfaf41640dc57a3 src/admin/web/index.html
|
||||||
03ad9572bb14c34059636d612cf6b876d6361ce601817cff674c57b715dc65dd src/admin/web/integrations.js
|
03ad9572bb14c34059636d612cf6b876d6361ce601817cff674c57b715dc65dd src/admin/web/integrations.js
|
||||||
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json
|
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f src/admin/web/login-keys.json
|
||||||
cbd41678eadd994dc5252628ee8ef9551fa9f7d8694ca5abd206240d9a96ad30 src/admin/web/login.html
|
cbd41678eadd994dc5252628ee8ef9551fa9f7d8694ca5abd206240d9a96ad30 src/admin/web/login.html
|
||||||
@@ -242,7 +244,7 @@ c2e5f554810b8e34fb47361fb90a01073a2e332ee26d82e030956fad3ec1f83e src/admin/web/
|
|||||||
c28f4254ddc1f720d62646ee0f0b590b60a89c0bf447e8ec74ff3dde0c7635ba src/admin/web/settings.js
|
c28f4254ddc1f720d62646ee0f0b590b60a89c0bf447e8ec74ff3dde0c7635ba src/admin/web/settings.js
|
||||||
8eadbada619b15087783dcfa0e889a81f88e78034d084c5ab616f7912b835940 src/admin/web/status_200.html
|
8eadbada619b15087783dcfa0e889a81f88e78034d084c5ab616f7912b835940 src/admin/web/status_200.html
|
||||||
c13cffe18a616fe5faa5ee945ab0d94d8d392ae8f32474c62fe41010e063ddb1 src/admin/web/streams.js
|
c13cffe18a616fe5faa5ee945ab0d94d8d392ae8f32474c62fe41010e063ddb1 src/admin/web/streams.js
|
||||||
c02d71ff3e1b63128045d3786f37bb2733bb70bf8d1037929e3cf65cbfc2df4e src/admin/web/style.css
|
7ffd017d38297a21160fd61926bb1d406b465e8fe610db9164234220dd8b111a src/admin/web/style.css
|
||||||
d290c048450655b3eeae7c6ec54a82d645d1965513694ab29fdf7eef796a1abd src/admin/web/traffic-panel.js
|
d290c048450655b3eeae7c6ec54a82d645d1965513694ab29fdf7eef796a1abd src/admin/web/traffic-panel.js
|
||||||
69f412bd897e45f4889b71e9982cfff56df9ef20ba2da109b454713a5ecd7c74 src/admin/web/transform-panel.js
|
69f412bd897e45f4889b71e9982cfff56df9ef20ba2da109b454713a5ecd7c74 src/admin/web/transform-panel.js
|
||||||
d816f2f70eef398c18091479320c36d07f9afe07278a250b840ebcdd7bc715f2 src/admin/web/vhosts/core.js
|
d816f2f70eef398c18091479320c36d07f9afe07278a250b840ebcdd7bc715f2 src/admin/web/vhosts/core.js
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Overview traffic/connection heading cleanup against Chromium and the bundled JS.
|
||||||
|
|
||||||
|
Runs a simulated authenticated DOM rather than a live RustProxy service.
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
HTML = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
||||||
|
CSS = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8')
|
||||||
|
ROUTER = (ROOT / 'src/admin/router.rs').read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def app_bundle():
|
||||||
|
match = re.search(r'fn admin_app_javascript\([^)]*\)[^{]*\{', ROUTER)
|
||||||
|
assert match
|
||||||
|
body = ROUTER[match.end():]
|
||||||
|
body = body[:re.search(r'\]\s*\.join\("\\n"\)', body).start()]
|
||||||
|
result = []
|
||||||
|
for line in body.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
include = re.search(r'include_str!\("([^"]+)"\)', line)
|
||||||
|
if include:
|
||||||
|
result.append((ROOT / 'src/admin' / include.group(1)).read_text(encoding='utf-8'))
|
||||||
|
elif line == 'admin_vhosts_javascript(),':
|
||||||
|
result.append('(() => {\n' + '\n'.join(
|
||||||
|
(ROOT / 'src/admin/web/vhosts' / (name + '.js')).read_text(encoding='utf-8')
|
||||||
|
for name in ('core', 'protocol', 'health', 'rates', 'editor', 'render')) + '\n})();')
|
||||||
|
elif line.startswith('"') and line.endswith('",'):
|
||||||
|
result.append(json.loads(line[:-1]))
|
||||||
|
return '\n'.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||||
|
bundle = app_bundle()
|
||||||
|
for width in (320, 375, 768, 1440, 1920):
|
||||||
|
page = browser.new_page(viewport={'width': width, 'height': 920})
|
||||||
|
errors = []
|
||||||
|
page.on('pageerror', lambda error: errors.append(str(error)))
|
||||||
|
page.set_content(HTML.replace('/assets/app___RUSTPROXY_ASSET_ID__.js', '')
|
||||||
|
.replace('/assets/style___RUSTPROXY_ASSET_ID__.css', ''))
|
||||||
|
page.add_style_tag(content=CSS)
|
||||||
|
page.add_script_tag(content=bundle)
|
||||||
|
page.evaluate('''() => {
|
||||||
|
document.documentElement.classList.remove('app-loading', 'app-startup-failed');
|
||||||
|
document.getElementById('workspace').hidden = false;
|
||||||
|
document.getElementById('panel-overview').hidden = false;
|
||||||
|
currentPanel = 'overview';
|
||||||
|
samples.splice(0); samples.push(1.5, 85.5, 12.3);
|
||||||
|
redrawOverviewHistoryCharts();
|
||||||
|
}''')
|
||||||
|
for density in ('normal', 'compact', 'ultra'):
|
||||||
|
for span in (3, 6):
|
||||||
|
data = page.evaluate('''({density,span}) => {
|
||||||
|
document.documentElement.dataset.dashboardDensity = density;
|
||||||
|
const visible = ['traffic_chart', 'connection_chart'];
|
||||||
|
const hidden = ADMIN_DASHBOARD_WIDGETS.filter(id => !visible.includes(id));
|
||||||
|
applyDashboardLayout([...visible,...hidden], hidden,
|
||||||
|
{traffic_chart:span,connection_chart:span});
|
||||||
|
const traffic = document.querySelector('[data-dashboard-widget="traffic_chart"]');
|
||||||
|
const connection = document.querySelector('[data-dashboard-widget="connection_chart"]');
|
||||||
|
const heading = traffic.querySelector('.card-heading');
|
||||||
|
const peak = traffic.querySelector('#chart-peak');
|
||||||
|
const peakRect = peak.getBoundingClientRect();
|
||||||
|
const cardRect = traffic.getBoundingClientRect();
|
||||||
|
const headingRect = heading.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
title: heading.querySelector('h2')?.textContent.trim(),
|
||||||
|
peak: peak.textContent,
|
||||||
|
peakInHeading: peak.parentElement === heading,
|
||||||
|
peakNotInFooter: !traffic.querySelector('.chart-footer #chart-peak'),
|
||||||
|
noTrafficEyebrow: !traffic.querySelector('.small-label'),
|
||||||
|
noTrafficLiveBadge: !traffic.querySelector('.pill.live'),
|
||||||
|
noConnectionLiveBadge: !connection.querySelector('.pill.live'),
|
||||||
|
connectionTitleIntact: !!connection.querySelector('.card-heading .small-label[data-i18n="connectionActivity"]'),
|
||||||
|
peakInsideCard: peakRect.left >= cardRect.left - 1 && peakRect.right <= cardRect.right + 1,
|
||||||
|
peakInsideHeading: peakRect.top >= headingRect.top - 1 && peakRect.bottom <= headingRect.bottom + 1,
|
||||||
|
browserOverflow: document.documentElement.scrollWidth - innerWidth,
|
||||||
|
};
|
||||||
|
}''', {'density':density,'span':span})
|
||||||
|
assert data['title'] == 'Requests per second', (width, density, span, data)
|
||||||
|
assert data['peak'] == 'max 85.5 req/s', (width, density, span, data)
|
||||||
|
for key in ('peakInHeading', 'peakNotInFooter', 'noTrafficEyebrow',
|
||||||
|
'noTrafficLiveBadge', 'noConnectionLiveBadge',
|
||||||
|
'connectionTitleIntact', 'peakInsideCard', 'peakInsideHeading'):
|
||||||
|
assert data[key], (width, density, span, key, data)
|
||||||
|
assert data['browserOverflow'] <= 2, (width, density, span, data)
|
||||||
|
assert not errors, (width, errors)
|
||||||
|
print(f'PASS traffic/connection chart headers at {width}px (all densities, spans 3 and 6)')
|
||||||
|
page.close()
|
||||||
|
browser.close()
|
||||||
|
print('PASS Overview chart heading cleanup')
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""0.33.5 CPU plot autoscaling regression with production HTML/CSS/JS.
|
||||||
|
|
||||||
|
Uses Chromium with simulated authenticated DOM and status samples, not a live
|
||||||
|
RustProxy server, SSE stream or real CPU telemetry.
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
HTML = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
||||||
|
CSS = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8')
|
||||||
|
WIDE_JS = (ROOT / 'src/admin/web/app/overview-wide-metrics.js').read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def inspect(page):
|
||||||
|
return page.evaluate('''() => {
|
||||||
|
const get = id => document.querySelector(`#dashboard-overview-grid [data-dashboard-widget="${id}"]`);
|
||||||
|
const cpu = get('wide_cpu'), cpuSvg = cpu.querySelector('svg');
|
||||||
|
const protocolSvg = get('wide_protocol_rates').querySelector('svg');
|
||||||
|
const backendSvg = get('backend_traffic').querySelector('svg');
|
||||||
|
const legend = cpu.querySelector('.overview-wide-legend').getBoundingClientRect();
|
||||||
|
const bounds = cpu.getBoundingClientRect();
|
||||||
|
const matrix = cpuSvg.getScreenCTM();
|
||||||
|
return {
|
||||||
|
cpuHeight: cpuSvg.getBoundingClientRect().height,
|
||||||
|
protocolHeight: protocolSvg.getBoundingClientRect().height,
|
||||||
|
backendHeight: backendSvg.getBoundingClientRect().height,
|
||||||
|
svgWidth: cpuSvg.getBoundingClientRect().width,
|
||||||
|
drawnWidth: Math.hypot(matrix.a, matrix.b) * cpuSvg.viewBox.baseVal.width,
|
||||||
|
ratio: cpuSvg.getAttribute('preserveAspectRatio'),
|
||||||
|
cpuSeries: cpuSvg.querySelectorAll('.overview-wide-series').length,
|
||||||
|
lineEffect: getComputedStyle(cpuSvg.querySelector('.overview-wide-series')).vectorEffect,
|
||||||
|
gridEffect: getComputedStyle(cpuSvg.querySelector('.chart-grid')).vectorEffect,
|
||||||
|
legendInCard: legend.top >= bounds.top && legend.bottom <= bounds.bottom + 1,
|
||||||
|
overflow: document.documentElement.scrollWidth - innerWidth,
|
||||||
|
};
|
||||||
|
}''')
|
||||||
|
|
||||||
|
|
||||||
|
def render_cpu(page, revision, system, process):
|
||||||
|
return page.evaluate('''({revision,system,process}) => {
|
||||||
|
window.rustProxyWideOverview.render({revision,live_system:{
|
||||||
|
cpu_percent:system, process_cpu_percent:process
|
||||||
|
}});
|
||||||
|
const read = name => {
|
||||||
|
const prefix = `overview-wide-wide_cpu-${name}`;
|
||||||
|
return {
|
||||||
|
path: document.getElementById(`${prefix}-line`).getAttribute('d'),
|
||||||
|
label: document.getElementById(`${prefix}-value`).textContent,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {system:read('system'),process:read('process')};
|
||||||
|
}''', {'revision': revision, 'system': system, 'process': process})
|
||||||
|
|
||||||
|
|
||||||
|
def ys(path):
|
||||||
|
return [int(v) for v in re.findall(r'[ML]\d+\s+(\d+)', path)]
|
||||||
|
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||||
|
for viewport in (320, 375, 768, 1440, 1920):
|
||||||
|
page = browser.new_page(viewport={'width': viewport, 'height': 950})
|
||||||
|
errors = []
|
||||||
|
page.on('pageerror', lambda error: errors.append(str(error)))
|
||||||
|
page.set_content(HTML.replace('/assets/app___RUSTPROXY_ASSET_ID__.js', '')
|
||||||
|
.replace('/assets/style___RUSTPROXY_ASSET_ID__.css', ''))
|
||||||
|
page.add_style_tag(content=CSS)
|
||||||
|
page.evaluate('''() => {
|
||||||
|
document.documentElement.classList.remove('app-loading', 'app-startup-failed');
|
||||||
|
document.getElementById('workspace').hidden = false;
|
||||||
|
document.getElementById('panel-overview').hidden = false;
|
||||||
|
const grid = document.getElementById('dashboard-overview-grid');
|
||||||
|
grid.classList.add('is-aligned');
|
||||||
|
for (const card of grid.querySelectorAll(':scope > [data-dashboard-widget]')) {
|
||||||
|
card.hidden = !['wide_cpu', 'wide_protocol_rates', 'backend_traffic'].includes(card.dataset.dashboardWidget);
|
||||||
|
}
|
||||||
|
window.currentPanel = 'overview';
|
||||||
|
window.$ = id => document.getElementById(id);
|
||||||
|
window.fmt = (value, digits = 0) => Number(value).toFixed(digits);
|
||||||
|
window.fmtBytes = value => `${value} B`;
|
||||||
|
window.rustProxyOverviewSamples = {getLimit: () => 60, onChange: () => {}};
|
||||||
|
}''')
|
||||||
|
page.add_script_tag(content=WIDE_JS)
|
||||||
|
|
||||||
|
for density in ('compact', 'normal', 'ultra'):
|
||||||
|
page.evaluate('(density) => document.documentElement.dataset.dashboardDensity = density', density)
|
||||||
|
result = inspect(page)
|
||||||
|
assert result['protocolHeight'] >= 124, (viewport, density, result)
|
||||||
|
assert abs(result['cpuHeight'] - result['protocolHeight']) <= 1, (
|
||||||
|
'CPU must use standard chart height', viewport, density, result)
|
||||||
|
assert result['backendHeight'] > 0, (viewport, density, result)
|
||||||
|
assert result['ratio'] == 'none' and result['cpuSeries'] == 2, result
|
||||||
|
assert result['lineEffect'] == result['gridEffect'] == 'non-scaling-stroke', result
|
||||||
|
assert result['svgWidth'] > 100 and abs(result['svgWidth'] - result['drawnWidth']) < 2, result
|
||||||
|
assert result['legendInCard'] and result['overflow'] <= 2, (viewport, density, result)
|
||||||
|
|
||||||
|
# Measured 0.5-0.8% must be visible, not flattened by a 100% ceiling.
|
||||||
|
first = render_cpu(page, 1, 0.5, 0.2)
|
||||||
|
second = render_cpu(page, 1, 0.8, 0.6)
|
||||||
|
assert first['system']['label'] == '0.5%' and second['process']['label'] == '0.6%'
|
||||||
|
assert ys(second['system']['path']) == [77, 41], (viewport, second)
|
||||||
|
assert ys(second['process']['path']) == [113, 65], (viewport, second)
|
||||||
|
|
||||||
|
# The peak follows observed history and also works above 100% (multi-core).
|
||||||
|
third = render_cpu(page, 1, 3.4, 2.2)
|
||||||
|
assert ys(third['system']['path'])[-1] == 17, (viewport, third)
|
||||||
|
assert ys(third['process']['path'])[-1] == 59, (viewport, third)
|
||||||
|
high = render_cpu(page, 1, 250, 123)
|
||||||
|
assert high['system']['label'] == '250.0%'
|
||||||
|
assert ys(high['system']['path'])[-1] == 17, (viewport, high)
|
||||||
|
assert 17 <= ys(high['process']['path'])[-1] < 137, (viewport, high)
|
||||||
|
|
||||||
|
# Revision changes reset the history. Zero stays at the plot baseline.
|
||||||
|
idle = render_cpu(page, 2, 0, 0)
|
||||||
|
assert ys(idle['system']['path']) == [137] and ys(idle['process']['path']) == [137]
|
||||||
|
missing = render_cpu(page, 3, None, None)
|
||||||
|
assert missing['system']['path'] == '' and missing['process']['label'] == '-'
|
||||||
|
assert not errors, (viewport, errors)
|
||||||
|
print(f'PASS CPU auto Y-scale and standard plot height at {viewport}px (all densities)')
|
||||||
|
page.close()
|
||||||
|
browser.close()
|
||||||
@@ -85,14 +85,10 @@ window.rustProxyWideOverview = (() => {
|
|||||||
for (const [id, config] of Object.entries(configurations)) {
|
for (const [id, config] of Object.entries(configurations)) {
|
||||||
const node = tile(id);
|
const node = tile(id);
|
||||||
if (!node) { baselines.delete(id); continue; }
|
if (!node) { baselines.delete(id); continue; }
|
||||||
// Keep collecting while hidden so restoring a widget does not restart rates.
|
|
||||||
const values = config.counters ? counterRates(id, config.counters(status), now, record)
|
const values = config.counters ? counterRates(id, config.counters(status), now, record)
|
||||||
: config.values(status).map(finite);
|
: config.values(status).map(finite);
|
||||||
const series = history.get(id) || config.names.map(() => []);
|
const series = history.get(id) || config.names.map(() => []);
|
||||||
if (record) {
|
if (record) {
|
||||||
// Counter rates have no meaningful value on the first sample after a
|
|
||||||
// paused tab. Do not add null to the path: it would split a live line.
|
|
||||||
// Still show "-" above the chart until a fresh interval is sampled.
|
|
||||||
if (!config.counters || values.every(value => value !== null)) {
|
if (!config.counters || values.every(value => value !== null)) {
|
||||||
values.forEach((value, i) => {
|
values.forEach((value, i) => {
|
||||||
series[i].push(value);
|
series[i].push(value);
|
||||||
@@ -102,8 +98,7 @@ window.rustProxyWideOverview = (() => {
|
|||||||
}
|
}
|
||||||
last.set(id, values);
|
last.set(id, values);
|
||||||
}
|
}
|
||||||
const peak = Math.max(config.unit === "percent" ? 100 : 1,
|
const peak = Math.max(1, ...series.flat().filter(n => n !== null));
|
||||||
...series.flat().filter(n => n !== null));
|
|
||||||
config.names.forEach((key, i) => {
|
config.names.forEach((key, i) => {
|
||||||
const value = values[i];
|
const value = values[i];
|
||||||
const field = $(`overview-wide-${id}-${key}-value`);
|
const field = $(`overview-wide-${id}-${key}-value`);
|
||||||
|
|||||||
@@ -1083,9 +1083,8 @@
|
|||||||
</article>
|
</article>
|
||||||
<article class="card activity-card" data-dashboard-widget="traffic_chart">
|
<article class="card activity-card" data-dashboard-widget="traffic_chart">
|
||||||
<div class="card-heading">
|
<div class="card-heading">
|
||||||
<div><span class="small-label" data-i18n="liveTraffic">LIVE TRAFFIC</span>
|
<h2 data-i18n="requestsPerSecond">Requests per second</h2>
|
||||||
<h2 data-i18n="requestsPerSecond">Requests per second</h2>
|
<span id="chart-peak">max 0.0 req/s</span>
|
||||||
</div><span class="pill live" data-i18n="live">LIVE</span>
|
|
||||||
</div><svg preserveAspectRatio="none" aria-label="Recent request rate" id="traffic-chart"
|
</div><svg preserveAspectRatio="none" aria-label="Recent request rate" id="traffic-chart"
|
||||||
role="img" viewbox="0 0 600 130">
|
role="img" viewbox="0 0 600 130">
|
||||||
<path class="chart-grid" d="M0 25H600 M0 65H600 M0 105H600"></path>
|
<path class="chart-grid" d="M0 25H600 M0 65H600 M0 105H600"></path>
|
||||||
@@ -1112,7 +1111,7 @@
|
|||||||
id="rate-transform">0.0/s</b></span>
|
id="rate-transform">0.0/s</b></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-footer"><span data-i18n="lastMinute">Recent samples / resets on
|
<div class="chart-footer"><span data-i18n="lastMinute">Recent samples / resets on
|
||||||
reload</span><span id="chart-peak">peak 0 req/s</span></div>
|
reload</span></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="card system-card" data-dashboard-widget="system_chart">
|
<article class="card system-card" data-dashboard-widget="system_chart">
|
||||||
<div class="card-heading">
|
<div class="card-heading">
|
||||||
@@ -1143,7 +1142,7 @@
|
|||||||
<div><span class="small-label" data-i18n="connectionActivity">CONNECTION ACTIVITY</span>
|
<div><span class="small-label" data-i18n="connectionActivity">CONNECTION ACTIVITY</span>
|
||||||
<h2><span id="connection-current">0</span> <small data-i18n="connections">Open
|
<h2><span id="connection-current">0</span> <small data-i18n="connections">Open
|
||||||
connections</small></h2>
|
connections</small></h2>
|
||||||
</div><span class="pill live" data-i18n="live">LIVE</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<svg preserveAspectRatio="none" aria-label="Connection activity history"
|
<svg preserveAspectRatio="none" aria-label="Connection activity history"
|
||||||
data-i18n-aria="connectionActivity" id="connection-chart" role="img"
|
data-i18n-aria="connectionActivity" id="connection-chart" role="img"
|
||||||
|
|||||||
@@ -770,6 +770,17 @@ h1 {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.activity-card .card-heading {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-card #chart-peak {
|
||||||
|
margin-left: auto;
|
||||||
|
color: var(--muted);
|
||||||
|
font: 9px var(--mono);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.pill {
|
.pill {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border: 1px solid var(--pill-line);
|
border: 1px solid var(--pill-line);
|
||||||
|
|||||||
Reference in new issue
Block a user