v0.33.4
This commit is contained in:
1 parent
de090d20fa
commit
c6486bfe85
16 files changed
+254
-104
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.3.
|
compatibility. Current source version: 0.33.4.
|
||||||
|
|
||||||
## Delivery
|
## Delivery
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
> 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.4
|
||||||
|
|
||||||
|
- Unify per-Web-Service live metrics with Vhost cards: six matching live metric
|
||||||
|
tiles in a responsive, always-visible full-width grid instead of a collapsible
|
||||||
|
section, plus a full-width Vhost-style rate-history chart.
|
||||||
|
- Refresh visible WWW readings on service-list redraw and continue live status
|
||||||
|
updates without a disclosure toggle. Keep existing rate sampling, history
|
||||||
|
limits, PHP capacity semantics, language packs, authentication and API shapes.
|
||||||
|
- Add simulated browser regression coverage for layout parity at desktop and
|
||||||
|
phone widths, immediate metrics, refreshed counters and rate-chart width.
|
||||||
|
|
||||||
## 0.33.3
|
## 0.33.3
|
||||||
|
|
||||||
- Avoid one-point holes in full-width Overview counter-rate histories and
|
- Avoid one-point holes in full-width Overview counter-rate histories and
|
||||||
|
|||||||
Generated
+1
-1
@@ -1881,7 +1881,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-proxy"
|
name = "rust-proxy"
|
||||||
version = "0.33.3"
|
version = "0.33.4"
|
||||||
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.3"
|
version = "0.33.4"
|
||||||
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,16 @@
|
|||||||
# RustProxy 0.33.3
|
# RustProxy 0.33.4
|
||||||
|
|
||||||
|
## 0.33.4: unified Web Server live metrics
|
||||||
|
|
||||||
|
- Web Services now display six always-visible live metric tiles using the same
|
||||||
|
responsive layout, borders, labels and typography as Vhost live metrics.
|
||||||
|
- Each service's request-rate history uses the same full-width Vhost chart
|
||||||
|
container, responsive SVG rendering and chart palette. The live metrics no
|
||||||
|
longer need to be expanded, and values appear as soon as status is available.
|
||||||
|
- Preserve the existing per-service counters, rate sampling, history limits,
|
||||||
|
authenticated status stream and PHP availability semantics. No configuration,
|
||||||
|
database or API changes are required.
|
||||||
|
|
||||||
|
|
||||||
## 0.33.3 sidebar appearance follow-up
|
## 0.33.3 sidebar appearance follow-up
|
||||||
|
|
||||||
|
|||||||
+10
@@ -1,5 +1,15 @@
|
|||||||
# RustProxy roadmap
|
# RustProxy roadmap
|
||||||
|
|
||||||
|
## 0.33.4 - Web Server/Vhost live-metric visual parity
|
||||||
|
|
||||||
|
- [x] Show per-Web-Service live metrics without expanding a section.
|
||||||
|
- [x] Reuse Vhost metric tiles and the full-width traffic chart with the same
|
||||||
|
desktop/mobile layout, chart styling and theme-aware appearance.
|
||||||
|
- [x] Preserve counter sampling, PHP fallback, existing translations and the
|
||||||
|
existing status feed without new API or persistence state.
|
||||||
|
- [x] Add simulated Chromium layout, rendering and sampled-status regressions.
|
||||||
|
|
||||||
|
|
||||||
## 0.33.3 - Adjustable Overview card widths and cross-type ordering
|
## 0.33.3 - Adjustable Overview card widths and cross-type ordering
|
||||||
|
|
||||||
- [x] Apply Appearance **theme** highlighting and corner radius to sidebar
|
- [x] Apply Appearance **theme** highlighting and corner radius to sidebar
|
||||||
|
|||||||
+18
-16
@@ -1,13 +1,13 @@
|
|||||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
||||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
||||||
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
||||||
676a30c813406f4e17972ee8bea175a7e3d5ee282158283a9cbe920f2b65da7f AGENTS.md
|
90823b5d38f99f0031cd5fe81248506ed8fc5b0c6f72292bb023e95ed98d9f38 AGENTS.md
|
||||||
3a6bb0fdf41ef4519beefa9f4d9c124a4883c304e49e6a76bb64cd5333ffb174 CHANGELOG.md
|
d331e6aab4fd331e73fe89bfcb96f57dd502f3b2089d6298139c63b6d12154bc CHANGELOG.md
|
||||||
1a59436e03172328606fc0c2142da05319e7673ae2a87197409a06aa22d87b27 Cargo.lock
|
629a3594e8cffea80603ec4650b91e5f4bea9d65266a1e1122b815ed0d6c5f45 Cargo.lock
|
||||||
551040f53df829264a6784c9a2ab4fe2cb4299d5d4f517857a74ee58797ab5b7 Cargo.toml
|
e1d3659e9f745d37ff27a8ecdf0233b450127da4410ca51d4b3582a4d92c59bf Cargo.toml
|
||||||
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
||||||
6aa95661d76c2ce0f317fe7004791568dbcc552ccff87fe6bdd2f0f235d6ef7c README.md
|
ca0c0f5658f91381c99a91fa3d2daddee82d185e310c8dce037c150c615dbf47 README.md
|
||||||
97becee1b75d0dde90dd412e8fdb8aa68fd11c5e1872bc15095ac6632b2ea0e9 ROADMAP.md
|
d02e695435b061b9d29408e72a1335390e549add0dc9ec901f852d982bfd66e2 ROADMAP.md
|
||||||
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
||||||
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
|
f645f218dd062bf6cb54a8ee634290562cad364a4cccf09f16e3c8303582af8c build.rs
|
||||||
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
|
ea8d091843f86f4c66a5c76b20268b2feb397070851b6e05c5bc7d7a8d9157eb deploy/README.md
|
||||||
@@ -70,7 +70,7 @@ cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
|
|||||||
fe3ec6ca16ff933239c12dacf39dd7b45717c0551a8b9f6ce601bf78136c9e37 docs/TRANSPORT.md
|
fe3ec6ca16ff933239c12dacf39dd7b45717c0551a8b9f6ce601bf78136c9e37 docs/TRANSPORT.md
|
||||||
fd1c187ea8eef101764670d4ebb0c554173412514e37b909c81ea3f22ba8e7ee docs/VHOSTS.md
|
fd1c187ea8eef101764670d4ebb0c554173412514e37b909c81ea3f22ba8e7ee docs/VHOSTS.md
|
||||||
09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md
|
09b60fb7b40db4d0139fa2a9e69a2aa9f28d4e61b8643521cfc7e936d7df8774 docs/VHOST_ACCESS.md
|
||||||
a101fb3f3d8fa1ab50977418dbc16d33d4bf3590f966970e26044c9727045965 docs/WEB_SERVER.md
|
4257729a4c1b1f3949538410af78a12067bd3417c70525a15bd01411155d25fd docs/WEB_SERVER.md
|
||||||
c336f998027330c58d5afbbbb3a3a682fd44ae3997643882fe342633822f8bb3 docs/screenshots/README.md
|
c336f998027330c58d5afbbbb3a3a682fd44ae3997643882fe342633822f8bb3 docs/screenshots/README.md
|
||||||
5502e8ce44276213803b66c58e3a4d804a1354f5ed5d83f47ee805e8c44ab088 examples/acl-headers.json
|
5502e8ce44276213803b66c58e3a4d804a1354f5ed5d83f47ee805e8c44ab088 examples/acl-headers.json
|
||||||
2431dd88a2d9ad2cf3cd4b8fcdfa602563ab1013c612d8917fddbe7febfb2dc3 examples/cache-dashboard.json
|
2431dd88a2d9ad2cf3cd4b8fcdfa602563ab1013c612d8917fddbe7febfb2dc3 examples/cache-dashboard.json
|
||||||
@@ -96,8 +96,8 @@ cb01fdef286f38b442dd9252c23c4f8e44eb72e9ecdd99adb926e8e9f6318bb1 integrations/r
|
|||||||
96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh
|
96563a83e87cfb33e238c2a970ce36b1a1b51e91318d09b123c3755002ba2646 integrations/routeros-oci-build/build-routeros-oci.sh
|
||||||
3f267cafa16006e5456ade78ee2d5fae02cc7ce747839523068ffd8d7c98798a integrations/routeros-oci-build/docker-entrypoint.sh
|
3f267cafa16006e5456ade78ee2d5fae02cc7ce747839523068ffd8d7c98798a integrations/routeros-oci-build/docker-entrypoint.sh
|
||||||
13189fa546c3488dddd8efc80c47f6da5241e5fc28b79cbfe795414ea05c7a4f langs/README.md
|
13189fa546c3488dddd8efc80c47f6da5241e5fc28b79cbfe795414ea05c7a4f langs/README.md
|
||||||
fe738b800824e8700fb8a4796b7316f5d4882e0d43aed7e743fc996d42aa99c1 langs/en_US.json
|
7783e629ca4b2c1614e776838bf90f3692d521e50ee580aa5e92236c92ada844 langs/en_US.json
|
||||||
d9878b53adc3bdcf20fecb6b4ae6270158c9150e9df39186dc2c4b571d948aa2 langs/pl_pl.json
|
58f2fdaf0aa9d3a3ce25d47659501a8b3df622e0c08eee0a4baaad4d5f1ac660 langs/pl_pl.json
|
||||||
a99ad040be10fa028351e290b9363236b3513a1cadbdc9fb9e0260d517b9b0ae make_zip.py
|
a99ad040be10fa028351e290b9363236b3513a1cadbdc9fb9e0260d517b9b0ae make_zip.py
|
||||||
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
|
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
|
||||||
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
|
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
|
||||||
@@ -135,15 +135,17 @@ aa3ca7419d6d7d506c5aaf4d624d20cc7e8ee66bec3cb0cf9b5e7d164bcbab57 scripts/tests/
|
|||||||
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
|
||||||
0c8a566979b51cd62601d138697ccd5c4ed47b8fa33e8faa28fa3a085a72ad97 scripts/tests/dashboard_resizing_0333_browser_test.py
|
0c8a566979b51cd62601d138697ccd5c4ed47b8fa33e8faa28fa3a085a72ad97 scripts/tests/dashboard_resizing_0333_browser_test.py
|
||||||
|
f82d57d3c01858eb60aa6882f7d3470e6587d7fc60e7ee6dbd8c5bec1f223c66 scripts/tests/dashboard_resume_rate_charts_ui_test.mjs
|
||||||
aa12d00acdd226602b3efc90221fbc936f69f73bcabbaa6c01dfb0e05d08c505 scripts/tests/overview_samples_0330_ui_test.mjs
|
aa12d00acdd226602b3efc90221fbc936f69f73bcabbaa6c01dfb0e05d08c505 scripts/tests/overview_samples_0330_ui_test.mjs
|
||||||
0ec69b7811db3dcd51cba60387900ca8df50eb836b22cc2a3ecfcc22b40a2edd scripts/tests/progressbar_0332_browser_test.py
|
0ec69b7811db3dcd51cba60387900ca8df50eb836b22cc2a3ecfcc22b40a2edd scripts/tests/progressbar_0332_browser_test.py
|
||||||
222077dab4d4928ae9110a9f2d64c07cc8386ea14099819b43304bbd2364e5b5 scripts/tests/sidebar_nav_0333_browser_test.py
|
222077dab4d4928ae9110a9f2d64c07cc8386ea14099819b43304bbd2364e5b5 scripts/tests/sidebar_nav_0333_browser_test.py
|
||||||
d5f675b97a85220043a9475347412972db904e559d863ce5058aa18f649f9e16 scripts/tests/system_ram_meter_0332_browser_test.py
|
d5f675b97a85220043a9475347412972db904e559d863ce5058aa18f649f9e16 scripts/tests/system_ram_meter_0332_browser_test.py
|
||||||
046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/tests/test_stream_tcp.py
|
046b4282550f5cb4e53906665c91c7442be70fc68fd0743466fd832582f7c979 scripts/tests/test_stream_tcp.py
|
||||||
f0b48ec0cc4411755cc0b4128eebce710375afa99344cb76198cf31074f8eb63 scripts/tests/test_stream_udp.py
|
f0b48ec0cc4411755cc0b4128eebce710375afa99344cb76198cf31074f8eb63 scripts/tests/test_stream_udp.py
|
||||||
91bf803f05b97b4654a6e607de40fccdd0268882039d6d1c5d677edce9459006 scripts/tests/web_observability_0330_browser_test.py
|
2da8b8d56b6e53b061664c352df0e7a351602c4e460e5f35ce61b738467a5d23 scripts/tests/web_observability_0330_browser_test.py
|
||||||
fc982558e5c27281d71c2b3cbc926c415ab9134cf595e7ac12b7214f8a180447 scripts/tests/web_observability_0330_source_test.py
|
fc982558e5c27281d71c2b3cbc926c415ab9134cf595e7ac12b7214f8a180447 scripts/tests/web_observability_0330_source_test.py
|
||||||
8b600edbe0aff817f871f2bf3821a35ca8d2d29870c0f6b6bf3c0cef2139e221 scripts/tests/web_server_0330_ui_test.py
|
651330e4bd38d1f2c45d304e5f7461848f414e80de5089358775af4e5eba5b97 scripts/tests/web_server_0330_ui_test.py
|
||||||
|
17839b4048e055c55b15c46f95e6745d8d87bde17ea0d04e557a85411526e83e scripts/tests/web_server_0334_browser_test.py
|
||||||
22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py
|
22e880e98c7b9d700c7a4c02b9b1d543e5dbd1f4ecdca42813a1083165b32a19 scripts/version.py
|
||||||
55485a513e8c4da2a58daa7abcb15b32ed408a5fc062cf23599fb83d16b90cfe src/acl.rs
|
55485a513e8c4da2a58daa7abcb15b32ed408a5fc062cf23599fb83d16b90cfe src/acl.rs
|
||||||
63efab1e95d41134fc30cc57320c32810bbae9f81a640bdb24b1be2059e63d8a src/acl/dynamic.rs
|
63efab1e95d41134fc30cc57320c32810bbae9f81a640bdb24b1be2059e63d8a src/acl/dynamic.rs
|
||||||
@@ -170,7 +172,7 @@ c6b552cd5acd9e743c28b1a5564420c214c619fe891ab0561b7d4499aae8d6c0 src/admin/file
|
|||||||
c720a7cad6a15c57b1f2ae7b3e5fbf30a97c64afb9137915f37b23f4e576bdc5 src/admin/file_logs_query.rs
|
c720a7cad6a15c57b1f2ae7b3e5fbf30a97c64afb9137915f37b23f4e576bdc5 src/admin/file_logs_query.rs
|
||||||
74636c6560618ee95757335452baf8db03394e1ad18acd54252f54b15746b8d5 src/admin/logging_api.rs
|
74636c6560618ee95757335452baf8db03394e1ad18acd54252f54b15746b8d5 src/admin/logging_api.rs
|
||||||
33be50109497d0adb23c36a10894acb5508dd38f3c01136bd0438a4ce0ffe74b src/admin/mod.rs
|
33be50109497d0adb23c36a10894acb5508dd38f3c01136bd0438a4ce0ffe74b src/admin/mod.rs
|
||||||
8e20a424d5e251ac713baaee063d9dfddcfe06063ee68eb9eac37bf9215c7664 src/admin/openapi.json
|
6884210022056d7f2a92160b6d37026e420483c2285368b7ae3c01b1c981df8b src/admin/openapi.json
|
||||||
d9120f7dc61d27621c5ac27ea49815dc7945c44d5b99d208da503818c198f649 src/admin/operations.rs
|
d9120f7dc61d27621c5ac27ea49815dc7945c44d5b99d208da503818c198f649 src/admin/operations.rs
|
||||||
30895e05c5d5d0a26be29f667e0f6d72f16a96a5c8a98e09cd11d8ef2d7b4763 src/admin/panel_assets.rs
|
30895e05c5d5d0a26be29f667e0f6d72f16a96a5c8a98e09cd11d8ef2d7b4763 src/admin/panel_assets.rs
|
||||||
75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs
|
75bbedd54fdabb2b489642a642ee7c6533f69cdacb85e251763a56ba11174d5c src/admin/panel_content.rs
|
||||||
@@ -196,7 +198,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
|
||||||
107b010a64ecc4135d610eecf4f8d4fb12329806003d286b4328e31e9e3d859f src/admin/web/app/overview-wide-metrics.js
|
94ca621117738e064276d426552cc4dfe87ba9b038ddcd73a8d4024c6659a474 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
|
||||||
@@ -204,7 +206,7 @@ daa20d634f921191cb4ac37889504f203ab39ad7e8fd8f33e1b182fc4206c24d src/admin/web/
|
|||||||
31201586925e6f4792071d7d03fb05dcc4a69a03c02a4e85eb0746f45af68074 src/admin/web/app/session-resume.js
|
31201586925e6f4792071d7d03fb05dcc4a69a03c02a4e85eb0746f45af68074 src/admin/web/app/session-resume.js
|
||||||
86ac42bd4a6a84bb137d5a3c3dc3d1f4e404a543447845e6ea6e6e8210e268ce src/admin/web/app/settings.js
|
86ac42bd4a6a84bb137d5a3c3dc3d1f4e404a543447845e6ea6e6e8210e268ce src/admin/web/app/settings.js
|
||||||
8386710827e5ccc9432fb46413368f94afc1016425404a6092b460903636837e src/admin/web/app/stream-dashboard.js
|
8386710827e5ccc9432fb46413368f94afc1016425404a6092b460903636837e src/admin/web/app/stream-dashboard.js
|
||||||
20ff05cfddc7f48931b0e9348b63188f82e8eaec38a7fe3e1829fd2e87301568 src/admin/web/app/web-overview.js
|
655db0b4d5e7ee14dbe69fa22bdb9d99eecc32abcf3b1b76a5819a8015f72309 src/admin/web/app/web-overview.js
|
||||||
0603dc8793a731a0e582cbbec53aa6d1cc6e007faafcec5d8843730b2cdc39e1 src/admin/web/cache-panel.js
|
0603dc8793a731a0e582cbbec53aa6d1cc6e007faafcec5d8843730b2cdc39e1 src/admin/web/cache-panel.js
|
||||||
02a9f5a61379e68e1723e64ef4c589500e9a993adcb02fe352f1cf9456c90c52 src/admin/web/certificate-api.js
|
02a9f5a61379e68e1723e64ef4c589500e9a993adcb02fe352f1cf9456c90c52 src/admin/web/certificate-api.js
|
||||||
aec5755244242424e5091ff57c0258d2618588618fe1d52d36f081d03879f686 src/admin/web/dashboard-defaults.json
|
aec5755244242424e5091ff57c0258d2618588618fe1d52d36f081d03879f686 src/admin/web/dashboard-defaults.json
|
||||||
@@ -240,7 +242,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
|
||||||
3a869db8f9cdf23eef6c99a52b892f14adf2210bce7f153351341247a878024c src/admin/web/style.css
|
c02d71ff3e1b63128045d3786f37bb2733bb70bf8d1037929e3cf65cbfc2df4e 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
|
||||||
@@ -250,7 +252,7 @@ b3239e67901f1796d7a974ade0796f2e3590ff8064628a5629b78c7f6ce2f32e src/admin/web/
|
|||||||
2170cfddf5c611672de34b0525d36ade8fcbe459cc21269281d6b615d8601b93 src/admin/web/vhosts/rates.js
|
2170cfddf5c611672de34b0525d36ade8fcbe459cc21269281d6b615d8601b93 src/admin/web/vhosts/rates.js
|
||||||
b991e81a98dd604a1d7c30158c0cd8cd825479bd2cd94b082b3550de1071bb58 src/admin/web/vhosts/render.js
|
b991e81a98dd604a1d7c30158c0cd8cd825479bd2cd94b082b3550de1071bb58 src/admin/web/vhosts/render.js
|
||||||
4dfdeb7c73c2775eacaca65fd8ef1dc80b275421b15522d319d338f5b96288cc src/admin/web/web-server-profiles.js
|
4dfdeb7c73c2775eacaca65fd8ef1dc80b275421b15522d319d338f5b96288cc src/admin/web/web-server-profiles.js
|
||||||
a273847795799679bf15540f7ce34347ff40c013969d09e9ffbf530776d13c0b src/admin/web/web-server.js
|
0049e5c2a35e3ec25f3c1590879684a7b15165584e96eb1b022fb60d3c548f2c src/admin/web/web-server.js
|
||||||
53237ae8b08d261ce21e0aa6fd225bf4f3bb17a37a80529e2a4039d45b13f7c5 src/auth/files.rs
|
53237ae8b08d261ce21e0aa6fd225bf4f3bb17a37a80529e2a4039d45b13f7c5 src/auth/files.rs
|
||||||
b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs
|
b2f948bcc3dbd24952c7b1b02d41d06757f49f0c38a78caf51f8f8a687d8480d src/auth/files/tests.rs
|
||||||
775178ee5781e64574bdd7c1fb7d64e7e283977fbd9c98c99896532a7f1df612 src/auth/mod.rs
|
775178ee5781e64574bdd7c1fb7d64e7e283977fbd9c98c99896532a7f1df612 src/auth/mod.rs
|
||||||
|
|||||||
+8
-6
@@ -22,12 +22,14 @@ The Web Server implementation is split under `src/web_server/` by responsibility
|
|||||||
|
|
||||||
## Live Web Service diagnostics (0.33.0)
|
## Live Web Service diagnostics (0.33.0)
|
||||||
|
|
||||||
Each configured Web Service card now contains an **expandable Live metrics**
|
Each configured Web Service card contains an **always-visible Live metrics**
|
||||||
section with completed requests, responses/s, HTTP 5xx count, mean elapsed time
|
section (since 0.33.4), styled like the Vhost live metrics: six responsive
|
||||||
until response headers, PHP request permits in use/limit, static/PHP/other response
|
metric tiles and a full-width per-service response-rate history chart. It shows
|
||||||
counts, delivered response body bytes and a compact per-service rate history.
|
completed requests, responses/s, HTTP 5xx count, mean elapsed time until
|
||||||
The section reuses the existing authenticated live status stream and is closed
|
response headers, PHP request permits in use/limit and delivered response body
|
||||||
by default. Unconfigured/non-PHP services do not claim PHP worker capacity.
|
bytes. Static/PHP/other counts appear below the chart. The section needs no
|
||||||
|
expansion and reuses the existing authenticated live status stream.
|
||||||
|
Unconfigured/non-PHP services do not claim PHP worker capacity.
|
||||||
|
|
||||||
**Overview** provides 12 additional opt-in Web Server blocks under
|
**Overview** provides 12 additional opt-in Web Server blocks under
|
||||||
**Settings -> Appearance -> Dashboard layout -> Overview cards**. All 12 are
|
**Settings -> Appearance -> Dashboard layout -> Overview cards**. All 12 are
|
||||||
|
|||||||
+2
-2
@@ -2695,10 +2695,10 @@
|
|||||||
"webMetricSeriesThreexx": "3xx",
|
"webMetricSeriesThreexx": "3xx",
|
||||||
"webMetricSeriesFourxx": "4xx",
|
"webMetricSeriesFourxx": "4xx",
|
||||||
"webMetricSeriesFivexx": "5xx",
|
"webMetricSeriesFivexx": "5xx",
|
||||||
"webLiveMetrics": "Live metrics",
|
|
||||||
"webLiveNoData": "Waiting for Web Server metrics",
|
"webLiveNoData": "Waiting for Web Server metrics",
|
||||||
"webLiveRequests": "Responses total",
|
"webLiveRequests": "Responses total",
|
||||||
"webLiveRate": "Rate",
|
"webLiveRate": "Rate",
|
||||||
|
"webLiveRateHistory": "Response rate history",
|
||||||
"webLiveErrors": "5xx",
|
"webLiveErrors": "5xx",
|
||||||
"webLiveLatency": "Header latency",
|
"webLiveLatency": "Header latency",
|
||||||
"webLivePhpCapacity": "PHP capacity",
|
"webLivePhpCapacity": "PHP capacity",
|
||||||
@@ -2710,4 +2710,4 @@
|
|||||||
"overviewLayoutWiden": "Increase card width",
|
"overviewLayoutWiden": "Increase card width",
|
||||||
"overviewLayoutWidth": "Card width in 12-column grid",
|
"overviewLayoutWidth": "Card width in 12-column grid",
|
||||||
"overviewLayoutInlineHint": "Layout unlocked: move any card anywhere; use − / + to resize (2, 3, 4, 6 or 12 columns). Save and lock or cancel to revert."
|
"overviewLayoutInlineHint": "Layout unlocked: move any card anywhere; use − / + to resize (2, 3, 4, 6 or 12 columns). Save and lock or cancel to revert."
|
||||||
}
|
}
|
||||||
+2
-2
@@ -2695,10 +2695,10 @@
|
|||||||
"webMetricSeriesThreexx": "3xx",
|
"webMetricSeriesThreexx": "3xx",
|
||||||
"webMetricSeriesFourxx": "4xx",
|
"webMetricSeriesFourxx": "4xx",
|
||||||
"webMetricSeriesFivexx": "5xx",
|
"webMetricSeriesFivexx": "5xx",
|
||||||
"webLiveMetrics": "Metryki na żywo",
|
|
||||||
"webLiveNoData": "Oczekiwanie na metryki WWW",
|
"webLiveNoData": "Oczekiwanie na metryki WWW",
|
||||||
"webLiveRequests": "Odpowiedzi łącznie",
|
"webLiveRequests": "Odpowiedzi łącznie",
|
||||||
"webLiveRate": "Tempo",
|
"webLiveRate": "Tempo",
|
||||||
|
"webLiveRateHistory": "Historia odpowiedzi/s",
|
||||||
"webLiveErrors": "5xx",
|
"webLiveErrors": "5xx",
|
||||||
"webLiveLatency": "Czas do nagłówków",
|
"webLiveLatency": "Czas do nagłówków",
|
||||||
"webLivePhpCapacity": "Limit PHP",
|
"webLivePhpCapacity": "Limit PHP",
|
||||||
@@ -2710,4 +2710,4 @@
|
|||||||
"overviewLayoutWiden": "Zwiększ szerokość karty",
|
"overviewLayoutWiden": "Zwiększ szerokość karty",
|
||||||
"overviewLayoutWidth": "Szerokość karty w siatce 12 kolumn",
|
"overviewLayoutWidth": "Szerokość karty w siatce 12 kolumn",
|
||||||
"overviewLayoutInlineHint": "Układ odblokowany: przenoś dowolne karty i zmieniaj ich szerokość przyciskami − / + (2, 3, 4, 6 lub 12 kolumn). Zapisz lub anuluj zmiany."
|
"overviewLayoutInlineHint": "Układ odblokowany: przenoś dowolne karty i zmieniaj ich szerokość przyciskami − / + (2, 3, 4, 6 lub 12 kolumn). Zapisz lub anuluj zmiany."
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ with sync_playwright() as p:
|
|||||||
envelope = {config:{web_services:[{id:'public-site', name:'Public site',document_root:'/srv/www/public'}],vhosts:[]}};
|
envelope = {config:{web_services:[{id:'public-site', name:'Public site',document_root:'/srv/www/public'}],vhosts:[]}};
|
||||||
window.rustProxyWebServer.render();
|
window.rustProxyWebServer.render();
|
||||||
const details = document.querySelector('.www-service-panel');
|
const details = document.querySelector('.www-service-panel');
|
||||||
details.open = true;
|
if (!details || details.tagName !== 'SECTION') throw Error('Missing always-visible WWW metrics');
|
||||||
window.__clock = 7000;
|
window.__clock = 7000;
|
||||||
window.rustProxyWebServer.renderLive(before, true);
|
window.rustProxyWebServer.renderLive(before, true);
|
||||||
window.__clock = 10000;
|
window.__clock = 10000;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ with sync_playwright() as p:
|
|||||||
assert '/s' in state['top'], state
|
assert '/s' in state['top'], state
|
||||||
assert state['layoutWidth'] <= state['screenWidth'] + 2, (width, state)
|
assert state['layoutWidth'] <= state['screenWidth'] + 2, (width, state)
|
||||||
assert not errors, (width, errors)
|
assert not errors, (width, errors)
|
||||||
# Exercise the Web Server per-service accordion with the same status,
|
# Exercise the always-visible per-service metrics with the same status,
|
||||||
# without any real HTTP/PHP request or extra polling.
|
# without any real HTTP/PHP request or extra polling.
|
||||||
details = page.evaluate("""(status) => {
|
details = page.evaluate("""(status) => {
|
||||||
envelope = { revision: 10, config: {
|
envelope = { revision: 10, config: {
|
||||||
@@ -110,11 +110,11 @@ with sync_playwright() as p:
|
|||||||
window.rustProxyWebServer.render();
|
window.rustProxyWebServer.render();
|
||||||
const detail = document.querySelector('#web-service-list .www-service-panel');
|
const detail = document.querySelector('#web-service-list .www-service-panel');
|
||||||
if (!detail) return { missing: true };
|
if (!detail) return { missing: true };
|
||||||
detail.open = true;
|
|
||||||
window.rustProxyWebServer.renderLive(status, false);
|
window.rustProxyWebServer.renderLive(status, false);
|
||||||
return { text: detail.textContent, rows: detail.querySelectorAll('[data-web-live-field]').length };
|
return { text: detail.textContent, rows: detail.querySelectorAll('[data-web-live-field]').length,
|
||||||
|
alwaysVisible: detail.tagName === 'SECTION' && !detail.querySelector('summary') };
|
||||||
}""", second)
|
}""", second)
|
||||||
assert not details.get('missing') and details['rows'] == 7, (width, details)
|
assert not details.get('missing') and details['rows'] == 7 and details['alwaysVisible'], (width, details)
|
||||||
assert '18 / 32' in details['text'], (width, details)
|
assert '18 / 32' in details['text'], (width, details)
|
||||||
# The first reading after an overdue browser interval must establish a
|
# The first reading after an overdue browser interval must establish a
|
||||||
# fresh baseline without adding a fake 0/s dip to the service chart.
|
# fresh baseline without adding a fake 0/s dip to the service chart.
|
||||||
@@ -129,7 +129,7 @@ with sync_playwright() as p:
|
|||||||
const during = chart.getAttribute('points'), unavailable = rate.textContent;
|
const during = chart.getAttribute('points'), unavailable = rate.textContent;
|
||||||
window.__clock = 103000; api.renderLive(resumed);
|
window.__clock = 103000; api.renderLive(resumed);
|
||||||
return { before, during, unavailable, after: chart.getAttribute('points'), resumedRate: rate.textContent };
|
return { before, during, unavailable, after: chart.getAttribute('points'), resumedRate: rate.textContent };
|
||||||
}""", [observation(2), observation(3), observation(10), observation(11)])
|
}""", [observation(2), observation(3), observation(10), observation(12)])
|
||||||
assert continuity['before'] == continuity['during'], (width, continuity)
|
assert continuity['before'] == continuity['during'], (width, continuity)
|
||||||
assert continuity['unavailable'] == '-', (width, continuity)
|
assert continuity['unavailable'] == '-', (width, continuity)
|
||||||
assert continuity['resumedRate'] != '-', (width, continuity)
|
assert continuity['resumedRate'] != '-', (width, continuity)
|
||||||
@@ -146,7 +146,7 @@ with sync_playwright() as p:
|
|||||||
}""")
|
}""")
|
||||||
assert old == {'rate': '-', 'status': '-', 'top': 0}, (width, old)
|
assert old == {'rate': '-', 'status': '-', 'top': 0}, (width, old)
|
||||||
assert not errors, (width, errors)
|
assert not errors, (width, errors)
|
||||||
print(f'PASS simulated Web Server cards and service accordion at {width}px: {state["rps"]}, {state["php"]}, layout {state["layoutWidth"]}px')
|
print(f'PASS simulated Web Server cards and always-visible service metrics at {width}px: {state["rps"]}, {state["php"]}, layout {state["layoutWidth"]}px')
|
||||||
page.close()
|
page.close()
|
||||||
browser.close()
|
browser.close()
|
||||||
print('PASS production JS bundle + all twelve visible WWW widgets (simulated data only)')
|
print('PASS production JS bundle + all twelve visible WWW widgets (simulated data only)')
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""WWW/Vhost visual parity in production UI files, using simulated status.
|
||||||
|
|
||||||
|
Chromium exercises the real assembled admin JS/CSS and responsive layouts.
|
||||||
|
It does not establish Rust, SSE or PHP-FPM runtime acceptance.
|
||||||
|
"""
|
||||||
|
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()
|
||||||
|
CSS = (ROOT / 'src/admin/web/style.css').read_text()
|
||||||
|
ROUTER = (ROOT / 'src/admin/router.rs').read_text()
|
||||||
|
|
||||||
|
|
||||||
|
def app_bundle():
|
||||||
|
found = re.search(r'fn admin_app_javascript\([^)]*\)[^{]*\{', ROUTER)
|
||||||
|
assert found
|
||||||
|
code = ROUTER[found.end():]
|
||||||
|
code = code[:re.search(r'\]\s*\.join\("\\n"\)', code).start()]
|
||||||
|
fragments = []
|
||||||
|
for line in code.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
include = re.search(r'include_str!\("([^"]+)"\)', line)
|
||||||
|
if include:
|
||||||
|
fragments.append((ROOT / 'src/admin' / include.group(1)).read_text())
|
||||||
|
elif line == 'admin_vhosts_javascript(),':
|
||||||
|
fragments.append('(() => {\n' + '\n'.join(
|
||||||
|
(ROOT / 'src/admin/web/vhosts' / (name + '.js')).read_text()
|
||||||
|
for name in ('core', 'protocol', 'health', 'rates', 'editor', 'render')) + '\n})();')
|
||||||
|
elif line.startswith('"') and line.endswith('",'):
|
||||||
|
fragments.append(json.loads(line[:-1]))
|
||||||
|
return '\n'.join(fragments)
|
||||||
|
|
||||||
|
|
||||||
|
def status(step):
|
||||||
|
return {'web_server_metrics': {
|
||||||
|
'site-A': {'web': {
|
||||||
|
'requests': step * 120, 'response_headers_us': step * 2_400_000,
|
||||||
|
'status_5xx': step * 4, 'response_bytes': step * 20_480,
|
||||||
|
'static_requests': step * 80, 'php_requests': step * 30,
|
||||||
|
'other_requests': step * 10,
|
||||||
|
}, 'php': {'capacity_used': 3, 'capacity_limit': 16}},
|
||||||
|
'site-B': {'web': {
|
||||||
|
'requests': step * 24, 'response_headers_us': step * 100_000,
|
||||||
|
'status_5xx': 0, 'response_bytes': step * 8_192,
|
||||||
|
'static_requests': step * 24, 'php_requests': 0,
|
||||||
|
'other_requests': 0,
|
||||||
|
}, 'php': None},
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
|
with sync_playwright() as playwright:
|
||||||
|
browser = playwright.chromium.launch(
|
||||||
|
headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||||
|
for width in (320, 375, 640, 800, 980, 1440, 1920):
|
||||||
|
page = browser.new_page(viewport={'width': width, 'height': 900})
|
||||||
|
errors = []
|
||||||
|
page.on('pageerror', lambda e: errors.append(str(e)))
|
||||||
|
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=app_bundle())
|
||||||
|
initial = page.evaluate('''(status) => {
|
||||||
|
document.documentElement.classList.remove('app-loading', 'app-startup-failed');
|
||||||
|
document.getElementById('login').hidden = true;
|
||||||
|
document.getElementById('workspace').hidden = false;
|
||||||
|
document.getElementById('panel-web-server').hidden = false;
|
||||||
|
currentPanel = 'web-server';
|
||||||
|
envelope = {revision: 10, config: {web_services: [
|
||||||
|
{id: 'site-A', name: 'Example service', document_root: '/var/www/a', index: ['index.html'], php: {enabled: true}},
|
||||||
|
{id: 'site-B', name: 'Static service', document_root: '/var/www/b', index: ['index.html']}
|
||||||
|
], vhosts: [], php_fpm_profiles: []}};
|
||||||
|
window.__clock = 1000;
|
||||||
|
Object.defineProperty(performance, 'now', {configurable: true, value: () => window.__clock});
|
||||||
|
latestStatus = status;
|
||||||
|
window.rustProxyWebServer.render();
|
||||||
|
const list = document.querySelector('#web-service-list');
|
||||||
|
const cards = [...list.querySelectorAll(':scope > .vhost-card')];
|
||||||
|
const metricPanels = cards.map(card => card.querySelector('.www-service-panel'));
|
||||||
|
const grid = metricPanels[0].querySelector('.vhost-live-grid');
|
||||||
|
const chart = metricPanels[0].querySelector('.vhost-chart');
|
||||||
|
const svg = chart.querySelector('svg');
|
||||||
|
const svgBox = svg.getBoundingClientRect(), svgMatrix = svg.getScreenCTM();
|
||||||
|
const rect = node => node.getBoundingClientRect();
|
||||||
|
const expectedMetricLabels = ['webLiveRequests','webLiveRate','webLiveErrors',
|
||||||
|
'webLiveLatency','webLivePhpCapacity','webLiveBytes'];
|
||||||
|
return {
|
||||||
|
cards: cards.length, sectionTags: metricPanels.map(panel => panel.tagName),
|
||||||
|
collapsibles: list.querySelectorAll('details, summary').length,
|
||||||
|
liveFields: metricPanels.map(panel => panel.querySelectorAll('[data-web-live-field]').length),
|
||||||
|
labels: [...grid.children].map(tile => tile.querySelector('span')?.dataset.i18n),
|
||||||
|
tileClasses: [...grid.children].map(tile => tile.className),
|
||||||
|
values: [...grid.querySelectorAll('strong')].map(el => el.textContent),
|
||||||
|
phpMissing: metricPanels[1].querySelector('[data-web-live-field="php"]').textContent,
|
||||||
|
columns: getComputedStyle(grid).gridTemplateColumns.split(' ').length,
|
||||||
|
chartWidth: rect(chart).width, gridWidth: rect(grid).width,
|
||||||
|
cardWidth: rect(cards[0]).width, listWidth: rect(list).width,
|
||||||
|
svgWidth: svgBox.width, svgContentWidth: svg.viewBox.baseVal.width * Math.hypot(svgMatrix.a, svgMatrix.b),
|
||||||
|
preserveAspect: svg.getAttribute('preserveAspectRatio'),
|
||||||
|
stroke: getComputedStyle(svg.querySelector('polyline')).vectorEffect,
|
||||||
|
chartLabel: !!chart.querySelector('.vhost-chart-head .small-label'),
|
||||||
|
rendered: getComputedStyle(metricPanels[0]).display !== 'none' && rect(chart).height > 60,
|
||||||
|
fitsViewport: document.documentElement.scrollWidth <= innerWidth + 2,
|
||||||
|
expectedMetricLabels,
|
||||||
|
};
|
||||||
|
}''', status(1))
|
||||||
|
expected_cols = 2 if width <= 640 else 3 if width <= 980 else 6
|
||||||
|
assert initial['cards'] == 2, (width, initial)
|
||||||
|
assert initial['sectionTags'] == ['SECTION', 'SECTION'], (width, initial)
|
||||||
|
assert initial['collapsibles'] == 0, (width, initial)
|
||||||
|
assert initial['liveFields'] == [7, 7], (width, initial)
|
||||||
|
assert initial['labels'] == initial['expectedMetricLabels'], (width, initial)
|
||||||
|
assert initial['tileClasses'] == ['vhost-live-metric'] * 6, (width, initial)
|
||||||
|
assert initial['values'][0] != '-' and initial['values'][4] == '3 / 16', (width, initial)
|
||||||
|
assert initial['phpMissing'] != '-', (width, initial)
|
||||||
|
assert initial['columns'] == expected_cols, (width, initial)
|
||||||
|
assert initial['chartWidth'] > 0.95 * initial['gridWidth'], (width, initial)
|
||||||
|
assert initial['listWidth'] - initial['cardWidth'] < 3, (width, initial)
|
||||||
|
assert abs(initial['svgWidth'] - initial['svgContentWidth']) <= 2, (width, initial)
|
||||||
|
assert initial['preserveAspect'] == 'none' and initial['stroke'] == 'non-scaling-stroke', (width, initial)
|
||||||
|
assert initial['chartLabel'] and initial['rendered'] and initial['fitsViewport'], (width, initial)
|
||||||
|
|
||||||
|
sampled = page.evaluate('''([first, second, last]) => {
|
||||||
|
const api = window.rustProxyWebServer;
|
||||||
|
const panel = document.querySelector('[data-web-live-id="site-A"]');
|
||||||
|
const rate = panel.querySelector('[data-web-live-field="rate"]');
|
||||||
|
window.__clock = 4000; api.renderLive(first, true);
|
||||||
|
window.__clock = 7000; api.renderLive(second, true);
|
||||||
|
const before = {rate: rate.textContent, points: panel.querySelector('polyline').getAttribute('points')};
|
||||||
|
// Service-list redraw must show the current status without opening anything.
|
||||||
|
latestStatus = last;
|
||||||
|
api.render();
|
||||||
|
const updated = document.querySelector('[data-web-live-id="site-A"]');
|
||||||
|
const total = updated.querySelector('[data-web-live-field="requests"]').textContent;
|
||||||
|
const split = updated.querySelector('[data-web-live-field="split"]').textContent;
|
||||||
|
return {before, total, split,
|
||||||
|
pointsAfterRedraw: updated.querySelector('polyline').getAttribute('points'),
|
||||||
|
metricsRemainVisible: !!updated.querySelector('.vhost-live-metric'),
|
||||||
|
overflow: document.documentElement.scrollWidth - innerWidth};
|
||||||
|
}''', [status(2), status(3), status(4)])
|
||||||
|
assert sampled['before']['rate'] not in ('-', '0/s', '0.0/s'), (width, sampled)
|
||||||
|
assert sampled['before']['points'].count(' ') >= 1, (width, sampled)
|
||||||
|
assert sampled['total'] != initial['values'][0], (width, sampled)
|
||||||
|
assert sampled['split'] != '-' and sampled['metricsRemainVisible'], (width, sampled)
|
||||||
|
assert sampled['pointsAfterRedraw'] == sampled['before']['points'], (width, sampled)
|
||||||
|
assert sampled['overflow'] <= 2, (width, sampled)
|
||||||
|
assert not errors, (width, errors)
|
||||||
|
print(f'PASS 0.33.4 WWW live metrics visual parity at {width}px; {expected_cols} columns')
|
||||||
|
page.close()
|
||||||
|
browser.close()
|
||||||
|
print('PASS always-visible full-width WWW/Vhost parity with simulated updates (not Rust runtime)')
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"openapi": "3.0.3",
|
"openapi": "3.0.3",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "RustProxy Administration API",
|
"title": "RustProxy Administration API",
|
||||||
"version": "0.33.3",
|
"version": "0.33.4",
|
||||||
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust."
|
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust."
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
|
|||||||
+6
-46
@@ -11588,62 +11588,22 @@ html:is([data-corner-style=square], [data-corner-style=subtle], [data-corner-sty
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Web Services reuse the same full-width live tiles and chart as Vhosts. */
|
||||||
#panel-web-server .www-service-panel {
|
#panel-web-server .www-service-panel {
|
||||||
border-top: 1px solid var(--line);
|
|
||||||
margin-top: 10px;
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-panel summary {
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--chart-accent);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-live {
|
|
||||||
padding: 12px 0 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-values {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-values>div {
|
|
||||||
display: grid;
|
|
||||||
gap: 5px;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#panel-web-server .www-service-values small {
|
#panel-web-server .www-service-heading {
|
||||||
color: var(--muted);
|
margin: 0;
|
||||||
font: 10px var(--mono);
|
font-size: 11px;
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-values strong {
|
|
||||||
font: 500 14px var(--mono);
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-live svg {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
max-height: 86px;
|
|
||||||
margin: 10px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#panel-web-server .www-service-live polyline {
|
|
||||||
fill: none;
|
|
||||||
stroke: var(--chart-accent);
|
|
||||||
stroke-width: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#panel-web-server .www-service-split {
|
#panel-web-server .www-service-split {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font: 11px var(--mono);
|
font: 11px var(--mono);
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
margin-top: 4px;
|
margin: 4px 10px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(prefers-reduced-motion:reduce) {
|
@media(prefers-reduced-motion:reduce) {
|
||||||
|
|||||||
+19
-20
@@ -286,7 +286,6 @@
|
|||||||
try {
|
try {
|
||||||
const preset = presetConfig($("web-service-preset").value);
|
const preset = presetConfig($("web-service-preset").value);
|
||||||
if (!preset) { toast(t("webServicePresetChoose"), true); return; }
|
if (!preset) { toast(t("webServicePresetChoose"), true); return; }
|
||||||
// Keep deployment-specific FastCGI endpoint/tuning while replacing application policy.
|
|
||||||
preset.php.endpoint = $("web-service-php-endpoint").value.trim() || preset.php.endpoint;
|
preset.php.endpoint = $("web-service-php-endpoint").value.trim() || preset.php.endpoint;
|
||||||
preset.php.connect_timeout_ms = Number($("web-service-php-connect-timeout").value || preset.php.connect_timeout_ms);
|
preset.php.connect_timeout_ms = Number($("web-service-php-connect-timeout").value || preset.php.connect_timeout_ms);
|
||||||
preset.php.request_timeout_ms = Number($("web-service-php-request-timeout").value || preset.php.request_timeout_ms);
|
preset.php.request_timeout_ms = Number($("web-service-php-request-timeout").value || preset.php.request_timeout_ms);
|
||||||
@@ -312,9 +311,6 @@
|
|||||||
|
|
||||||
function presetChanged() {
|
function presetChanged() {
|
||||||
updatePresetSummary();
|
updatePresetSummary();
|
||||||
// Presets are one-shot form generators. Selecting one must immediately
|
|
||||||
// materialize its normal Web Service fields; otherwise a user can select a
|
|
||||||
// CMS, save the form and unknowingly persist the previous/custom values.
|
|
||||||
if ($("web-service-preset").value !== "custom") applyPreset();
|
if ($("web-service-preset").value !== "custom") applyPreset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,31 +388,34 @@
|
|||||||
item.append(el("span", t(key), "small-label"), el("b", value));
|
item.append(el("span", t(key), "small-label"), el("b", value));
|
||||||
details.append(item);
|
details.append(item);
|
||||||
}
|
}
|
||||||
const live = el("details", undefined, "www-service-panel");
|
const live = el("section", undefined, "www-service-panel");
|
||||||
const summary = el("summary", t("webLiveMetrics")); live.append(summary);
|
|
||||||
const panel = el("div", undefined, "www-service-live"); panel.dataset.webLiveId = service.id;
|
const panel = el("div", undefined, "www-service-live"); panel.dataset.webLiveId = service.id;
|
||||||
const figures = el("div", undefined, "www-service-values");
|
const figures = el("div", undefined, "vhost-live-grid");
|
||||||
for (const [field, key] of [
|
for (const [field, key] of [
|
||||||
["requests", "webLiveRequests"], ["rate", "webLiveRate"],
|
["requests", "webLiveRequests"], ["rate", "webLiveRate"],
|
||||||
["errors", "webLiveErrors"], ["latency", "webLiveLatency"],
|
["errors", "webLiveErrors"], ["latency", "webLiveLatency"],
|
||||||
["php", "webLivePhpCapacity"], ["bytes", "webLiveBytes"]]) {
|
["php", "webLivePhpCapacity"], ["bytes", "webLiveBytes"]]) {
|
||||||
const item = el("div");
|
const item = el("div", undefined, "vhost-live-metric");
|
||||||
const name = el("small", t(key));
|
const name = el("span", t(key)); name.dataset.i18n = key;
|
||||||
name.dataset.i18n = key;
|
|
||||||
const value = el("strong", "-"); value.dataset.webLiveField = field;
|
const value = el("strong", "-"); value.dataset.webLiveField = field;
|
||||||
item.append(name, value); figures.append(item);
|
item.append(name, value); figures.append(item);
|
||||||
}
|
}
|
||||||
const chart = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
const chart = el("div", undefined, "vhost-chart");
|
||||||
chart.setAttribute("viewBox", "0 0 300 60"); chart.setAttribute("role", "img");
|
const chartHead = el("div", undefined, "vhost-chart-head");
|
||||||
chart.setAttribute("aria-label", t("webLiveRate"));
|
chartHead.append(el("span", t("webLiveRateHistory"), "small-label")); chart.append(chartHead);
|
||||||
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
|
svg.setAttribute("viewBox", "0 0 320 54"); svg.setAttribute("preserveAspectRatio", "none");
|
||||||
|
svg.setAttribute("role", "img"); svg.setAttribute("aria-label", t("webLiveRate"));
|
||||||
const line = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
|
||||||
line.setAttribute("points", "0,55 300,55"); chart.append(line);
|
line.setAttribute("class", "vhost-chart-line");
|
||||||
|
line.setAttribute("points", "0,50 320,50"); svg.append(line); chart.append(svg);
|
||||||
const split = el("p", "-", "www-service-split"); split.dataset.webLiveField = "split";
|
const split = el("p", "-", "www-service-split"); split.dataset.webLiveField = "split";
|
||||||
panel.append(figures, chart, split); live.append(panel);
|
chart.append(split); panel.append(figures, chart); live.append(panel);
|
||||||
live.addEventListener("toggle", () => { if (live.open && latestStatus) renderLive(latestStatus, false); });
|
|
||||||
box.append(details, live); return box;
|
box.append(details, live); return box;
|
||||||
});
|
});
|
||||||
$("web-service-list").replaceChildren(...(cards.length ? cards : [el("p", t(query ? "webServiceSearchEmpty" : "webServiceEmpty"), "fine")]));
|
$("web-service-list").replaceChildren(...(cards.length ? cards : [el("p", t(query ? "webServiceSearchEmpty" : "webServiceEmpty"), "fine")]));
|
||||||
|
if (latestStatus && currentPanel === "web-server") renderLive(latestStatus, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceHistory = new Map();
|
const serviceHistory = new Map();
|
||||||
@@ -425,7 +424,6 @@
|
|||||||
if (currentPanel !== "web-server") return;
|
if (currentPanel !== "web-server") return;
|
||||||
const services = status?.web_server_metrics || {};
|
const services = status?.web_server_metrics || {};
|
||||||
for (const panel of document.querySelectorAll("#web-service-list [data-web-live-id]")) {
|
for (const panel of document.querySelectorAll("#web-service-list [data-web-live-id]")) {
|
||||||
if (!panel.closest("details")?.open) continue;
|
|
||||||
const id = panel.dataset.webLiveId, values = services[id], web = values?.web;
|
const id = panel.dataset.webLiveId, values = services[id], web = values?.web;
|
||||||
const lookup = key => panel.querySelector(`[data-web-live-field="${key}"]`);
|
const lookup = key => panel.querySelector(`[data-web-live-field="${key}"]`);
|
||||||
const set = (key, value) => { const node = lookup(key); if (node) node.textContent = value; };
|
const set = (key, value) => { const node = lookup(key); if (node) node.textContent = value; };
|
||||||
@@ -439,11 +437,12 @@
|
|||||||
const deltas = record ? sample.deltas : null;
|
const deltas = record ? sample.deltas : null;
|
||||||
const rate = deltas ? deltas[0] / sample.seconds : null;
|
const rate = deltas ? deltas[0] / sample.seconds : null;
|
||||||
const previous = serviceHistory.get(id) || [];
|
const previous = serviceHistory.get(id) || [];
|
||||||
// An unavailable counter delta is not a 0/s measurement; do not plot it.
|
|
||||||
if (record && rate !== null) { previous.push(rate); window.rustProxyOverviewSamples.trim(previous); serviceHistory.set(id, previous); }
|
if (record && rate !== null) { previous.push(rate); window.rustProxyOverviewSamples.trim(previous); serviceHistory.set(id, previous); }
|
||||||
const peak = Math.max(1, ...previous.filter(x => x != null));
|
const peak = Math.max(1, ...previous.filter(x => x != null));
|
||||||
const points = previous.map((value, i) => `${300 * i / Math.max(1, previous.length - 1)},${55 - (value || 0) / peak * 50}`).join(" ");
|
const y = value => 50 - (value || 0) / peak * 46;
|
||||||
panel.querySelector("polyline")?.setAttribute("points", points || "0,55 300,55");
|
const points = previous.length === 1 ? `0,${y(previous[0])} 320,${y(previous[0])}` :
|
||||||
|
previous.map((value, i) => `${320 * i / Math.max(1, previous.length - 1)},${y(value)}`).join(" ");
|
||||||
|
panel.querySelector("polyline")?.setAttribute("points", points || "0,50 320,50");
|
||||||
set("requests", fmt(total)); set("rate", rate == null ? "-" : `${fmt(rate, 1)}/s`);
|
set("requests", fmt(total)); set("rate", rate == null ? "-" : `${fmt(rate, 1)}/s`);
|
||||||
set("errors", fmt(n(web.status_5xx)));
|
set("errors", fmt(n(web.status_5xx)));
|
||||||
set("latency", deltas?.[0] > 0 ? `${fmt(deltas[1] / deltas[0] / 1000, 1)} ms` : "-");
|
set("latency", deltas?.[0] > 0 ? `${fmt(deltas[1] / deltas[0] / 1000, 1)} ms` : "-");
|
||||||
|
|||||||
Reference in new issue
Block a user