v0.26.4
This commit is contained in:
1 parent
cf25423dde
commit
52cc137c73
50 files changed
+410
-172
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
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
|
||||
compatibility. Current source version: 0.26.3.
|
||||
compatibility. Current source version: 0.26.4.
|
||||
|
||||
## Delivery
|
||||
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
> test-only changes and packaging implementation notes were moved to
|
||||
> [`docs/CHANGELOG_TECHNICAL_ARCHIVE.md`](docs/CHANGELOG_TECHNICAL_ARCHIVE.md).
|
||||
|
||||
## 0.26.4
|
||||
|
||||
- Add 404-host editors to the session draft dock so unfinished 404 forms can be minimized, switched away from and restored like Web Server, Vhost, Redirect and Stream drafts.
|
||||
- Complete Polish Button style translations and align compact toolbar search/filter inputs with the selected button height.
|
||||
- Standardize modal Enabled controls for 404 hosts, Redirects and Streams on the vhost state-control presentation.
|
||||
- Replace the Overview-card checkbox list in Appearance with compact responsive visibility/reorder tiles.
|
||||
- Make age-based upstream client recycling run from backend maintenance even without traffic, fixing idle backends that could remain at rotation count 0 for hours.
|
||||
- Keep SQLite schema 8 and the saved proxy configuration format unchanged; no migration is required from 0.26.3.
|
||||
|
||||
## 0.26.3
|
||||
|
||||
- Make the **Settings -> Appearance** interface scale global across the authenticated administration UI, including dialogs, overlays and other top-level surfaces, and extend the selectable range from 80-125% to 70-125%. Form fields also receive a bounded proportional size adjustment so they track the selected scale more visibly.
|
||||
|
||||
Generated
+1
-1
@@ -1688,7 +1688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-proxy"
|
||||
version = "0.26.3"
|
||||
version = "0.26.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rust-proxy"
|
||||
version = "0.26.3"
|
||||
version = "0.26.4"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
description = "RustProxy - independent reverse proxy with local administration"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# RustProxy 0.26.3
|
||||
# RustProxy 0.26.4
|
||||
|
||||
Independent Rust reverse proxy and static Web Server with a dashboard, SQLite configuration,
|
||||
streaming HTTP/TLS/WebSocket forwarding, raw TCP/UDP stream hosts, IP-bound vhosts and backend pools.
|
||||
|
||||
**0.26.3 release source.**
|
||||
**0.26.4 release source.**
|
||||
|
||||
## Layer-4 streams
|
||||
|
||||
@@ -11,7 +11,19 @@ Raw TCP/UDP forwarding has its own **Streams** page in the primary navigation, a
|
||||
|
||||
Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost hostnames, including multi-address listeners.
|
||||
|
||||
## What changed in 0.26.3
|
||||
## What changed in 0.26.4
|
||||
|
||||
- Extend session-only editor drafts to **404 hosts**, preserving the complete unfinished form state when another editor is opened or the dialog is minimized.
|
||||
- Complete the Polish translation for **Settings -> Appearance -> Button style** and keep the button-size preference independent from global interface scale.
|
||||
- Keep search/filter inputs in compact toolbars no taller than the active button style, including Backends, vhost-style lists, Docker, Logs and Settings search controls.
|
||||
- Reuse the vhost Enabled control presentation in 404 hosts, Redirects and Streams so modal enable/disable state is visually consistent.
|
||||
- Replace the tall Overview-card checkbox list in Appearance with a compact responsive tile grid; each tile toggles visibility and retains reorder controls.
|
||||
- Run time-based upstream-client pool recycling from backend maintenance as well as request dispatch, so an idle backend rotates after its configured age instead of remaining on generation 0 until the next request.
|
||||
- Keep SQLite schema 8 and the saved proxy configuration format unchanged; upgrading from 0.26.3 requires no data migration.
|
||||
|
||||
0.26.4 retains the 0.26.3 appearance and administration workflow changes below.
|
||||
|
||||
### Retained 0.26.3 release notes
|
||||
|
||||
- Make interface scaling truly global: the 70-125% setting now scales the authenticated dashboard, navigation, dialogs, overlays, draft dock and other top-level administration UI consistently. Form fields receive an additional proportional size adjustment so inputs/selects/textarea do not remain visually oversized at low scale or undersized at high scale.
|
||||
- Tighten the Overview metric cards so wide dashboards fit more telemetry with less empty vertical space.
|
||||
@@ -39,7 +51,7 @@ Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost
|
||||
- Replace backend sort text (`A-Z` / `Z-A`) with direction arrows (`↑` / `↓`).
|
||||
- Keep SQLite schema 8 and the saved proxy configuration format unchanged; upgrading from 0.26.1 requires no data migration.
|
||||
|
||||
The detailed 0.25.5 implementation audit remains available as historical baseline evidence. Current 0.26.3 verification status is maintained in [docs/TEST_REPORT.md](docs/TEST_REPORT.md).
|
||||
The detailed 0.25.5 implementation audit remains available as historical baseline evidence. Current 0.26.4 verification status is maintained in [docs/TEST_REPORT.md](docs/TEST_REPORT.md).
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -117,7 +129,7 @@ CARGO_BUILD_JOBS=1 CARGO_PROFILE_RELEASE_LTO=false \
|
||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 cargo build --release --locked
|
||||
```
|
||||
|
||||
The 0.26.3 source archive preserves the resolved `Cargo.lock` supplied with the source tree and
|
||||
The 0.26.4 source archive preserves the resolved `Cargo.lock` supplied with the source tree and
|
||||
updates only this package's own version entry. This packaging environment has no Cargo, so the
|
||||
dependency graph was not re-resolved here. On the compiler-enabled deployment host, run the
|
||||
locked check/test/release gates below and retain the verified lockfile. `dev.sh` can generate a
|
||||
@@ -356,7 +368,7 @@ python3 scripts/release_smoke.py
|
||||
# Runs those checks first, then the Rust suite when Cargo is available:
|
||||
bash dev.sh test
|
||||
# Source-only package, with no Git checkout required:
|
||||
python3 make_zip.py /tmp/RustProxy-0.26.3.zip
|
||||
python3 make_zip.py /tmp/RustProxy-0.26.4.zip
|
||||
```
|
||||
|
||||
## New management and test-origin guide
|
||||
|
||||
+13
@@ -1,5 +1,18 @@
|
||||
# RustProxy roadmap
|
||||
|
||||
## 0.26.4 - Administration consistency and backend rotation reliability
|
||||
|
||||
Current direction: 0.26.3 -> 0.26.4
|
||||
|
||||
- [x] Add 404-host forms to the session-only draft workflow.
|
||||
- [x] Complete Polish translations for the Button style appearance setting.
|
||||
- [x] Align toolbar search/filter input height with the active button style.
|
||||
- [x] Reuse the vhost Enabled control styling in 404 hosts, Redirects and Streams.
|
||||
- [x] Replace the Overview-card checkbox list with compact visibility/reorder tiles.
|
||||
- [x] Run age-based backend client recycling independently of request arrival.
|
||||
- [x] Keep SQLite schema 8 and saved proxy configuration compatibility with 0.26.3.
|
||||
- [ ] Run compiler-enabled `cargo fmt --all`, `cargo check --locked --all-targets` and `cargo test --locked --all-targets` on a Rust release host.
|
||||
|
||||
## 0.26.3 - Administration appearance and draft workflow polish
|
||||
|
||||
Current direction: 0.26.2 -> 0.26.3
|
||||
|
||||
+49
-48
@@ -1,19 +1,19 @@
|
||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
||||
9e6356475bf587091bab72246ec9e325b498f7e48a6220e9a18baa50d0a385a6 .gitignore
|
||||
78bfdaf9ea3b88e84a6acf6720dcef80c4b3f4eaab233ed202d17c33393c7e5f AGENTS.md
|
||||
8a4a00a320fece83cf042bcd9145da98b8951d76d3e2bd191ab6a8fd668efbb5 CHANGELOG.md
|
||||
d1b0cf138814b68b272c32616aac2334699f906b0b43489601f21cf36b513088 Cargo.lock
|
||||
61d358ab03a30e1425bb91f84b70221b8b39c5fd6324475ec098c5b1f53ac959 Cargo.toml
|
||||
3bb6c6c9cb4711103174833994befd1f25814c609ae99d444b6f6b5bc2c3c6a0 AGENTS.md
|
||||
6e4073eae721bc224dc6551caf0928d9fefadf7d120ecfa02f2162157e10c1a0 CHANGELOG.md
|
||||
56a24128244b2e0cab7050f0f83acae255dc97720fa165d06ea09bc14f50252f Cargo.lock
|
||||
c5e5651b1a1dbfbe524ad2e6b131e971d6ffb1038c9519677f067d2d35bc4e07 Cargo.toml
|
||||
d6d22ba63f4bc3d16a13012ecaabff8a156be57c824d2f20f66976502fb27d1f Dockerfile
|
||||
612e0d17a2fcda2a5799a44d8fd099ab285c284c1327fb84c1475d8e71ec116c README.md
|
||||
6aab906375937d1d0ccea95280a6d1efa41089b8d150278e1d73ca50dfb7dfc2 ROADMAP.md
|
||||
82b353c623d514e9404eead2803f61addedaf4f3e27117c0b7c5341a3af6c512 README.md
|
||||
fb8affd22820c5ef43a922ec8fa08ffe27d988bd4e24d4f2c4a0886dfabc1d7a ROADMAP.md
|
||||
da8311e3db945290666324a2f24bf4521b365272d7ac3f862ded1be993eea646 UPDATE_CHECK.toml
|
||||
928ba6a504bc431ae100c6e587861e5088b28822d2cb19da5d437b39d26c864a build.rs
|
||||
f8cc5f463b4642faadfa2bada5fb09298b6947c7284d938ae3ba4db12cb448ee debian/README.md
|
||||
fe510e22d31893d4df86de9bb48196dfed31ecb16d5c7649bb1fdba246fd49ce debian/build-package.sh
|
||||
8eb7682c9d1e261fbc201bee412d52a823c9ad3015aae363c844394814426efe debian/README.md
|
||||
37641fa2d47a0331249ccea8028bce95c8b88d2c2a13000a8a09116efb2cd6cf debian/build-package.sh
|
||||
0763a4bd8b429f7922b23a7276ff694f3a751b83fb127f6ad7ec654e5e507ecd debian/build-repository.sh
|
||||
b2579151f48cd68a751d8d99043de2a1b78cfaa51514fc6becf1223f2decb144 debian/changelog
|
||||
0af7b8ccded17dd9de34b23a9be73ffca0d3e420ab5902531889460f8cd22c20 debian/changelog
|
||||
c90457b19b1b2f7998d3db59a656d215eb0fab23769197405960457b66d95cf7 debian/control
|
||||
dff2ecb9c14623416981f444bc6df1c4e2bf55d330260f36a312d8d42245400f debian/rules
|
||||
c537d54fd92a9b97c5f6e6ec63bc1276cda324513e2946b341b2193f63b62e6a debian/rustproxy.environment
|
||||
@@ -24,7 +24,7 @@ cb0db03a2c1b06fa04a11f7690755a0ca90b17f19ed8fdb228810f505c48c78a debian/rustpro
|
||||
e8e18df40bcd69d6aa404282679186d9b8256ac63a5b4e80d7f081e8a0095a2e debian/source/format
|
||||
385ac7970fa94f043d0bcf04a91184e6e2c807587b85e55517ed064bb4bddba6 deploy/README.md
|
||||
7b7fdf383bbaea4f37b5c120e98a24e9a075fba56ae7838a31a5152c7edcde02 deploy/rustproxy.service
|
||||
4e766ffd22c9a096890a2555a7eac0ebafbe0599e3fc462413c1b782c393e450 dev.sh
|
||||
4353c58f038fbd83fda1a9879bb4765b924896e61c2676be6da8ab7154604807 dev.sh
|
||||
5f51efe621f45512a466fa7f2656867c6502a135ca040f4bfeec06a6919b39b6 docker-agent/README.md
|
||||
7fee7d51a7deb67e73e53a0bdff3aa4630aea9bab9b23a0f9b8001b2169892b1 docker-agent/certificate.go
|
||||
5e4f565c3c9de4dba897af2e801ea646a83395e72eae4717a6d502dff2fcec10 docker-agent/docker-agent.env.example
|
||||
@@ -33,14 +33,14 @@ bf687a851374d8f1fa59d050f3f76532f7c12f16a6daa7d3c36102c2d9a8ec9c docker-agent/d
|
||||
9d77c90767fa929ee8338734d72ce83284d3d965673270f62cb2b1613d587a33 docker-agent/install.sh
|
||||
b68234472bec5cedd67c7cfe1de7d8806a965dfd4101f944aa6320132f18c14e docker-agent/main.go
|
||||
3d4051a1f805d145df3877476e4b87773e74be8e123045065955a5133afdc68c docker-agent/main_test.go
|
||||
1803047c9588c2892b6846072ca23d7db20f09d35591c47f1460beb828e47de9 docker-agent/model.go
|
||||
eec37615a32bad4fab987f619734864a980e83ac09ace445a2e921bc17f8d326 docker-agent/model.go
|
||||
e52b31d0abf65ae3034f6cb367cca113633014ce8d1d799ad4dd5624d2c7b8bb docker-agent/network.go
|
||||
634790f35ff4627df2a78cec2ed371f6e9ec9df00afb386075595fc7f3ed72eb docker-agent/rustproxy-docker-agent.service
|
||||
5220d669171ec3ad65f50e0bc8de5178dadaa4904def8e1a4f61002aa9e9a55b docker-agent/server.go
|
||||
802a30b793a6ae6a5046489612a23659f33d25e41d0794551381db202daac904 docs/ACME_PROVIDERS.md
|
||||
ec15163f5f51c9212003b7edd1fc380321311c2496fbaa9b45c838281b9af475 docs/ACME_RFC2136.md
|
||||
d699f2261a0d7854e49e9b8bae44fa37008a7d2f6340a73cdc37e840722ea211 docs/API.md
|
||||
35d798657697a8031cac5efd417b6bb8de819dd7d4e79666e61d1805d8545d6e docs/API_ACCESS.md
|
||||
24cfd6b15a149d8dca0a334a8ac841ec008e6f64d3a11aac0563196ae5e1b168 docs/API_ACCESS.md
|
||||
0f7f159ad5b4b5277f80c19f3c47f4e7823ea439ef02e5913e1391f1b34d129f docs/ARCHITECTURE.md
|
||||
cd2597465af031cdb47db802f8ad6d3af34ef9e79f679df906a7b84220ea3260 docs/CACHE.md
|
||||
9cb338ed07ac1f92ad491e2099a7e3af63e66262417b1fef24d98c6a791d13fa docs/CACHE_DIAGNOSTICS.md
|
||||
@@ -49,9 +49,9 @@ db5534427ff0f004940fd6960906d3eaa75347a110e6322827c51c5d63f20adb docs/CONTAINER
|
||||
9538a52d508d8d7053c1387c42eef60e6d5dc1879c300b6fe951b3374c16884c docs/CUSTOM_LOCATIONS.md
|
||||
7db65a96f696aff1d7f74e2041cfc6ecdf437b1412ece90cee26da1ceff4cf7e docs/DOCKER_DISCOVERY.md
|
||||
86bb62f288f2e999994c302716fc9986dba1ac7a26e598dc97cb56a7bc21cfb5 docs/ERROR_LOGGING.md
|
||||
1594b5eee60bace5111d2a7ea3d59f6d6a479d8515c2e9256c52387c64cf18cd docs/FILE_LOGS.md
|
||||
8e9df23ee4539162955f9ef86a09a3e227ec7c34c3ee1206b7c14ac6926da19d docs/FILE_LOGS.md
|
||||
df6ecd4a50796640243676ad4b3a87789f6e5d20cbebea6255e92fbfff1c7131 docs/HTTP3.md
|
||||
5c753bf5de6709b8dd55ac449a4ef38757d9c7c8b55fbe1f5fd9b9caf999ec58 docs/INSTALL.md
|
||||
bd94e892186c612459d890f4a1195986e7034b8b3b9d1980090f71b1d07e0263 docs/INSTALL.md
|
||||
3b5c31c805e5aee0dc2100b81246a9b631b5abb996faa598ae94f87ba39305f3 docs/INTEGRATIONS_040.md
|
||||
4bb56add544f15d54695cf797cd16a51c09024360cdeef803a88988f4a54b3a5 docs/LIVE_LOGS.md
|
||||
7c35c4cdbe7bbb81d94f3bf6aeb22b8a866af3e7f0d62bbedc299e4d6f264e0d docs/MANAGEMENT.md
|
||||
@@ -62,7 +62,7 @@ e7516646dfb107d6240370f4e7460b1189f2eb21f398740381d5d102847bbfe6 docs/NOT_FOUND
|
||||
3da047768f32064c5ef0ad7a4c73c4b14930fe7317c54f5650f426af9f484593 docs/NPM_MIGRATION.md
|
||||
712cb1cbf84515384b081fd2929e86e40af7c10f65bc69919b78b7b3e2fd12b5 docs/PHP_FPM.md
|
||||
572c70fbd2f61fecd80a8ff9fa5de4a94c4f158de5e90da8e920f99b051241fa docs/PROMETHEUS.md
|
||||
cb8ebf15c59eacd4d5567b98c13101a1fd225d43c6dd5586100727fd00917fea docs/README.md
|
||||
a4329e7e8f2149e87c8d5610fbea6fe7bbf40b4d01a74b79a60b5cda09808bfe docs/README.md
|
||||
6e46a137b9c88dac8cb9e3fdca877430092392de9913d746278dbff8652460c2 docs/REDIRECTS.md
|
||||
0dfd0d431c5aedc67abac1ce5de040ffa790ec9578fd7d6f91a987c333d73358 docs/REFERENCES.md
|
||||
ce5afb4f531bb5fb4c453deb49f643f2a563f32f7908c995e5bc8e0d7de995b7 docs/RENEWAL.md
|
||||
@@ -72,7 +72,7 @@ a699fd663bfbf001525f22f0fbacdf03a58c5e825232d8d144e51cbb8b1008b2 docs/SECURITY.
|
||||
620b69e566d27af04f0604f4ca3f88cf95cdfea7fbc6ae9dd92446d2b2b5d9be docs/SECURITY_AUDIT.md
|
||||
f8baa8bdc8a67bf4e4a6e6d233435f5a43075a09bf08b08b597fcef1853ce5c5 docs/SETTINGS.md
|
||||
3611defd206cde107e75cac006159856d12cd8f15df7ae2eb9ec3592ed2b4abd docs/STREAMS.md
|
||||
c8eeed55e34d1a33a2d9d7ea61569eec786060b4f9d8342ea39b2eeaa1be9116 docs/TEST_REPORT.md
|
||||
2fe8ac5e426daec07395b4b1dde4cbd6a6a7def033ac062f6183ad53e29b8606 docs/TEST_REPORT.md
|
||||
cfb452d8496839e832b5fbacdd4e52525b0ecc3112fbb62340c1e580bd5c1717 docs/TODO.md
|
||||
8ee6187a5460ca45643b7fd789e27bffd264449e3a92ff70e9359edee011bbbb docs/TRANSFORM.md
|
||||
a1e9c6d38d21067d1ce7115ca3395ae8ff1f6a4c40fb0ebf00a6940ef79df439 docs/TRANSPORT.md
|
||||
@@ -103,6 +103,7 @@ bf9b4cf4fec2c8ce287e1b3c2e7d4828e101dafe4d6c173f099819f22493d144 docs/V0.25.5_A
|
||||
05e0b4cc074b07ff37d45fabc20c7272bada217286b3db3d7997cae25c1f6f11 docs/V0.26.1_IMPLEMENTATION.md
|
||||
91774f2c23f587a502c65fe2ffaf009aba81b221cb3ede4861ea79abb4d4a528 docs/V0.26.2_IMPLEMENTATION.md
|
||||
57edc3d5ca6c268b2b6b2e8027e81fb543c0dab86e8f69c4ee5776ea8df901c7 docs/V0.26.3_IMPLEMENTATION.md
|
||||
da5ec87b9482f2fc506c4334d9365f3bfaec89254a14e5585dd6460326b20e10 docs/V0.26.4_IMPLEMENTATION.md
|
||||
ce259052c023e90a7bfb6e39fe82e52b845a8b2a051bfa29883d29e87a9301a2 docs/VHOSTS.md
|
||||
b979afd3d56731eeb06e9dc38d37730693e7fbf3aa78467932842c0e712e7b26 docs/VHOST_ACCESS.md
|
||||
f0e76167944861517d1d42b9186ff0b2f184be0371b9e8597827e60895de4a76 docs/WEB_SERVER.md
|
||||
@@ -117,14 +118,14 @@ b26833cb4251f9390a44192ecc827ff7f052fd19016a5ccf7498c94503ab4ce3 examples/mock-
|
||||
5a8f3a8a054abc89c6e78896b9fe2f903212199a24dc3d2d2bcbd0696a31bcde examples/transport.json
|
||||
d242de9614fbb52933b4accd32f4715ca94107abe4a3b1d7e088362b07a6ba60 examples/vhosts-balanced.json
|
||||
af2bdb1343a8141069e94ccd2c7f92574ad848437e2f9fdb5c7576a9a18ef4a5 examples/vhosts-private.json
|
||||
ce0094ff903e0fcc979efbcd0a79d7aeb6d59eb8aa212c8c31fdbe8c742fdd12 grafana/README.md
|
||||
e93062ce0b61ddc6ee46f868c7de80a8ae00dfbe23542a887c9d096dadbbfd47 grafana/rustproxy-overview.json
|
||||
5c30f169bd7a034a843c7a4e6ccfb923ce009561c8d30c37a70d3c9841bbdbf1 grafana/README.md
|
||||
ca48eeeb4c7019647fb94ab7d4ca594d1cf03a556c1a8d1c5cfe1fd2d4e96300 grafana/rustproxy-overview.json
|
||||
30763fc61c38992a44bc24ad6932b88fd3b322724fce799db1f17f9a747eb59a install-debian13.sh
|
||||
858b39fa6886b71d20690bf369e76580922364b3cc7f0cf6ba9b040df9cd76e3 install.sh
|
||||
29cada0936762f26495064d1524a668262ed729b21f20aebddc8b6e5cfea363b langs/README.md
|
||||
17e39fd69378d4fdad965dcce96006393c8d2469b91d78611b73aacbd6a4ad74 langs/en_US.json
|
||||
f67f061e72bc14ebfcf883fab8c10e625e399157017e696e4a55cc26854cff08 langs/pl_pl.json
|
||||
089cd7c39e6e3c79f5e02b7ffe38648cef6b5cb89cc5ac6d6e7e249f0f91ee0b make_zip.py
|
||||
8878f138bc909c004181e6ba91bad14c0bf96ea81235c972c3b8d86a640e1b4e langs/en_US.json
|
||||
695ba1b195fb302dd05574049db98ee0bbe07fe71ef6a1c98dd43776d354235f langs/pl_pl.json
|
||||
038191475df4ecde40905bae55615ca207aeb05d17aa9bdc13728d4854dd77b5 make_zip.py
|
||||
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
|
||||
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
|
||||
5c62da30fa9db594a4d656c77ae676d3c388072b6a85f91170ec7d911d7cde43 migrations/0004_admin_preferences.sql
|
||||
@@ -143,11 +144,11 @@ edb6ee1445b01cd6f3a74a3a607f39fb107d411b49df028d2e0f4679e093de31 scripts/acme_p
|
||||
e69813fb1d3a4cdcfb7243964cc323fc21a603087bf2e84188fa42b9d3cee4b5 scripts/acme_rfc2136.py
|
||||
a5b4451e45ce975685772aa8409b64a57a20a7548466584c3ea7fb975e64729c scripts/admin_audit.py
|
||||
20e83f8b6347b819d1d1ea3de5c691c36f0816954a4210b0d3f39557fefe1931 scripts/backup.py
|
||||
2f91b721757d75e88fdef4cdfd1fcb87a56f95b4470cbb8b86894fb23d6976c7 scripts/check_source.py
|
||||
9fd134172caa4de5308eb7437a1f7ad1f992ab0053d01b4b7d1f4cf6804e6389 scripts/check_source.py
|
||||
187ee92c8029cdd59e89b4447cf4d170e856017a2091ceb85ae7c22dd1a79c72 scripts/deploy.py
|
||||
39932ae4797c966703fce9e2f999891066f7c3541dd171f02d2e37e88be2ef0d scripts/deploy_core.py
|
||||
a763d2dc2fec8cffe10714063364f40e4d08bd6412fabcfdefab828580b8d9cd scripts/deploy_core.py
|
||||
f014472588e889488976970dbaa95a8745b6907516aa81a7cca21d0f81eaaad3 scripts/deploy_runtime.py
|
||||
fab405bd0bfa9a8f8f70462aa536b4fc8ac9976e6bab25751726309ec679c6f7 scripts/docs_check.py
|
||||
f399b2ca97ba0c11675197ddf96ce6d8f233a2e1983619041c26e70bbef65558 scripts/docs_check.py
|
||||
6facb2d65bfab234f09855097d12da8f72669a38ebceb4b1cbd03ab42b8d2469 scripts/langs_check.py
|
||||
2e35b7c78887fd56f83ca9017caa168630963a9f47da71dfc53b1a8f9ad436c2 scripts/memory_contracts.py
|
||||
2cc658faaa610b21b48942c59b955525539ad4503fd60f2a49c7e626a22faaff scripts/migrate_npm.py
|
||||
@@ -157,8 +158,8 @@ d1418f8e574a7390fd45a2aa879bdf5720b773162d65e295ab0350506aa5220a scripts/mock_a
|
||||
2d3ff92300b09ce9cecbbaaa9309b4d33160fa4b2291c9952b784e8cfb599ab5 scripts/mock_server.py
|
||||
2d4c859aae5ba357a6d30024c8487ae8f59f01b192711f13f9c2f8fee0f48b50 scripts/os-acme-client/README.md
|
||||
c73b814a42bb4ff703441dbd177ee625055eeb115f9b5bb0c2150aede573a159 scripts/os-acme-client/opnsense-rustproxy-cert-deploy.sh
|
||||
d3c8a25c1b80eb00df494a1a8dab4a6325dd0a7b6e0d9f07e5bbfa511e9eb500 scripts/preflight.py
|
||||
31f0a16da2d97740be5130a76b128b6de617c21f1a66990f1c6192e1a72c2ec9 scripts/release_smoke.py
|
||||
4b000848d10f552d39cab5be309fc455e2f3b90490762fe1055e7abd06968bc3 scripts/preflight.py
|
||||
40c260046c93a43e12cd90e303b92de2c65d30dcdb365d68a739510749172b95 scripts/release_smoke.py
|
||||
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 scripts/routeros-oci-build/.env.routeros.example
|
||||
3b48cff8506e5a6ae7d3876f47721f5de7d63e2e02264a78cf5b6ddd52d9fbc6 scripts/routeros-oci-build/Dockerfile.routeros
|
||||
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e scripts/routeros-oci-build/Dockerfile.routeros.dockerignore
|
||||
@@ -170,11 +171,11 @@ ecfe0bb21a7549225a19b0846bb6de8c99cbfbbf5a706d33cf84db2c7146abed scripts/rust_s
|
||||
1c83054336b3508d7ad72cd259f039394682642439877fe69639c0b76be09154 scripts/security_contracts.py
|
||||
295b6f2625b1875950ea3eecccd142f49be327afdd00c92641286e6de530ec51 scripts/security_dependency_contracts.py
|
||||
6fefb5c459aacdef3dcabe9c5e6e7718a2145a0190b165c29d7a46216ac53bad scripts/source_module_contracts.py
|
||||
44d2b525cc7caf8fcfa4db67e3ec6b550bcb48623893acf4bacf0fb6b0d3bf60 scripts/swagger_assets.py
|
||||
28f516f8c5f686dfc72ebef0cb0338d06cbd4eb9f5ff93602f0dfbae3e9a871f scripts/swagger_assets.py
|
||||
102b23de19a88a8e60de392fb83aefd3dfb33e90cc97ed1690d9f3d4a33d8866 scripts/test_compression.py
|
||||
d5937dca7ae4e360c6d9bdbd2386f52826d0e5554d0db9abd617bab9cefb80fb scripts/upgrade.py
|
||||
010bc2454701e42e458e07a86c16ca62a9fa00449a9638ab0425b08739f07a91 scripts/upgrade.py
|
||||
0aaa80453e25aae7ea953fadb91b3ae3eaff627977c81c9ab5a2779f9a93582f scripts/web_assets.py
|
||||
0b3556f0cbaa4fe9025634ebac49a6eeea7151879fa7c72419432033b5375b28 scripts/web_server_contracts.py
|
||||
f22b1b4211b76851fb8be7d5330af0b31fe7732a080f342ce5116bb27582a26a scripts/web_server_contracts.py
|
||||
ced042b5e283fd40fe25c261457dceb57d946dffebd89876cd80d69b92823932 scripts/web_service_ui_test.mjs
|
||||
d8e2b51c39252dbb4305512fa9b1ef508ec610968aed316b10514fd160298762 src/acl.rs
|
||||
4e3be1ea1be0a81529c7c9021fb9bb77c24e5044acd54094972745443ad2c26a src/acl/tests.rs
|
||||
@@ -187,13 +188,13 @@ b9e57db4bd80ef37564dfc533a96897d8a494671d53acea9fb4bf28d23ba370d src/acme/provi
|
||||
55ef7ceb44c25c7a8ddcbfd5e0d4b647526f9bdcde901ee08db5314e4d055b39 src/admin/access.rs
|
||||
85f00f8157e83a152cc531f4cfecd0b1472ce269be9c8eb1ac17ad0693748b42 src/admin/certificates.rs
|
||||
1de4c7c116eeab2452ccccbaba3f2b7b63c6eb1c1e146ab870d19c5a5ca16f80 src/admin/documentation.rs
|
||||
e4aac0947a38d0cf6160328db853df940546a7b6b758fea4a33eedaf56f9f791 src/admin/documentation/documents.rs
|
||||
eab5d50e7326e2224b41fc8b5018a89a06ed9aa1f99954f84bf17d992e78bcf2 src/admin/documentation/documents.rs
|
||||
67b901485d5858d5b5bf78a6260035db32241531d99481754c3533fdf404facb src/admin/documentation/tests.rs
|
||||
002922c91aa07cbea17cb0ee11127bb6e7e015de108ee032761b34507dcfa06d src/admin/file_log_search.rs
|
||||
d3210e037481e1f95e840550428e088ffd83ec9a42f1810e4c976298fb555a9a src/admin/file_logs.rs
|
||||
beb8be241e4f1a8cea7b6c4e02b42f0359fb1fff10ab5ce2c70ccd0e63a554b0 src/admin/logging_api.rs
|
||||
58802528128f53e493f7339063433a747b605007a8842b5efc670132d897d6f7 src/admin/mod.rs
|
||||
20c73fc2d6cc0efda6016d2a9830815bea6882faf23f5a3fdb23bb1af67e48f7 src/admin/openapi.json
|
||||
8b8ae7ebefe9a74ac47e3e024da2abb38fdde02109dfe7924eca8413745ff157 src/admin/openapi.json
|
||||
a53309daf118381031122ca70764f7a2f92adc44af3fc91df5f946e6856f76ae src/admin/operations.rs
|
||||
e9ea015aabdfdbb2f8dd72ea734002b5d648dc1bbef4cc4a0b85d2bcc059e9a5 src/admin/preferences.rs
|
||||
b4fa393b780b2676b5d72e0f90cd7290292f424b8062d4b5983e722d6ca72c32 src/admin/public.rs
|
||||
@@ -211,10 +212,10 @@ f869e4da5de149fc04ce1aab658befeaf3208c862f149ca976235d15832c3cdb src/admin/test
|
||||
5fa006413fd93529ac9aaa4ffb28bf6b0b88d0a55a0bf0c49576db4a06fe9fc3 src/admin/tests/part_07.rs
|
||||
9afbca5b4f3d17690c2a7aedf0fd08de06e04c5e492b151d9969a326e07b19b8 src/admin/web/acme.js
|
||||
446011c2c7afddf7734ceb268c75a3b4146dbe8e915b11986495e956b6c62c69 src/admin/web/app/config-history.js
|
||||
77db5269365ee387f4e83d08e665bc1939586676431dc9153f874267b3613100 src/admin/web/app/core.js
|
||||
520de5c5df22aa0c26d472e83533ca288cc71ca0f75db62912b2c2fb7ab1f1c3 src/admin/web/app/preferences.js
|
||||
860bce21c27ced60f3d37344c1715d9c60899fa08f5f01c590f0ea81690b014a src/admin/web/app/core.js
|
||||
145430fa86e8111f2cc8c0ee484eb732df84d42cc8b628f4c2097b8f33bf51d9 src/admin/web/app/preferences.js
|
||||
d7f6675277c927f4eae1b83c7aa91b315429e7034dfe61c489c2f737a9c19f07 src/admin/web/app/runtime.js
|
||||
7642150e62978fb7dc1362b862a3805ee2e4f764a5a31767226825d32097c62a src/admin/web/app/settings.js
|
||||
8afb11a17fa61d42390667d076ae1485a7398be9b095d235f1486d552d410620 src/admin/web/app/settings.js
|
||||
642d013e2e18dc8a8eccc7a2fe93d6570521dc9a0644beac7af4bbe8c5cea1ee src/admin/web/cache-panel.js
|
||||
50d1ebcbaf4d0b2968da23bb943fc3f67131c5d32df3f61d23ff37aad9da5b81 src/admin/web/demo.html
|
||||
3b84a7a08f9f68e2c78fed4523b594c31a251addc5480a69c5f6949908230539 src/admin/web/docker.js
|
||||
@@ -230,21 +231,21 @@ f7d0955ab18c15e04369204e789f341d2e832f927c1ff20a3b2b9e35e7898f00 src/admin/web/
|
||||
e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c src/admin/web/error_504.html
|
||||
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 src/admin/web/favicon.svg
|
||||
af6e7a2e890df2fb7385a374bcb3fba52fb5a53dbe18adc1fb7865660fcba842 src/admin/web/file-logs.js
|
||||
dee3b113907f77138cdf919efdfb097e45b889cff3893bfc6f02ed998643bbae src/admin/web/host404.js
|
||||
ab1726532cae53df4e653d80c4855ccad069fbe0446c513492c64cede361b69d src/admin/web/index.html
|
||||
631860ab3793ffd827c7cf19ddd049d14419509f2c2d8b27994b0e54baa0e32a src/admin/web/host404.js
|
||||
b2b8b7ac06c79c7d69125d2cf092b33114d052b517dff37312eda023019c39a3 src/admin/web/index.html
|
||||
24db88842707f764064657712d3660c6ac129fbfb47d188c0903925cf8244ce5 src/admin/web/integrations.js
|
||||
cdab0c11a28deebdf401c0453fbc76b675e9e643722106fb20e812bb9648ccde src/admin/web/login-keys.json
|
||||
a253d202dc9a147f2add34c64e0f35c38fdc7f667f9908933ce5f959b1a92a2e src/admin/web/login.html
|
||||
45ca44ca82e43373f018629839287645218f225747f02b9b4a5d80e2bc8821cf src/admin/web/login.js
|
||||
1b096d96a9d6b904a87bd4194fa40f22310a42c765f3ffa299c4da1f14c6e248 src/admin/web/management.js
|
||||
e65311ad425bd91364ee8c0cc3f35671e1bcc726bea533e4a10d9e1f57706e12 src/admin/web/redirects.js
|
||||
a2dbdd205b899b92cb9817f76d0c79a27b153c80ac0fd60da435c7a4fddbb958 src/admin/web/redirects.js
|
||||
f1ab20afa09f75f3635511756e36afea46a7917f04937d6f4e89c4e9f3f43204 src/admin/web/settings.js
|
||||
f25a4395dc3ba7bf152fffb577aedfbf0aa4d06ea442ea78ef7f2a62e9922816 src/admin/web/streams.js
|
||||
f2ff4b717fdb12003f2c9841c455a783c336a1e9a78ca1ca228c645578c1badb src/admin/web/style.css
|
||||
f57c79477160dda93ba90aecb4c9293383caef3b3f8829a25608ad85954356f8 src/admin/web/streams.js
|
||||
778b0b5fba6a774afb3c79a5b57b4591221cfd1ae770322342deab99e6179603 src/admin/web/style.css
|
||||
a8a73a4fa849ae49391fcd6143e900c515c8b577f71b55cbae9701fa9f5bbe47 src/admin/web/traffic-panel.js
|
||||
e686d68fd6a368110a85dc4c5dc64e8093950d20d63e9aa44790c03dea6fc193 src/admin/web/transform-panel.js
|
||||
6856c230c4f6b18145e16efb6636433f8b21ce3ec5d34246d6561f0150ea0554 src/admin/web/vhosts/core.js
|
||||
d4938e0bf8cf41f79e9b49cf9b5cf2c8340668e792b73d5350cb1872e7ef1edf src/admin/web/vhosts/editor.js
|
||||
4d4ee863a3d77e5213ae4bdcad1f533a6222353b9c32e0b5f12f7ea60ab62bea src/admin/web/vhosts/editor.js
|
||||
e0011edc708d245cb514be170c1bbe846a0298b81d86276fe29af05010d215a4 src/admin/web/vhosts/render.js
|
||||
b3d8d3e22e4cbab10c5d588c268da6eb3075a9ec857728ce6b5d56e3ffd3c9a6 src/admin/web/web-server.js
|
||||
98c00d99822f9be54fc58afb6d4b577901712effd2c5eddce014312681864625 src/auth/files.rs
|
||||
@@ -259,7 +260,7 @@ b0efb68dec4b0e8611684457b21044d136882ddb5c529f934061a55d377d9b28 src/backup.rs
|
||||
c8ee6afde0e463828757a9c2aca25506eb0a7b3b9369f4ec955857ab04b1f9d1 src/backup/validation.rs
|
||||
426a946b2c9e244f09f6cbc5f9664b8ad0737529a9bb5a3d549d27196a1f8043 src/balance.rs
|
||||
3d4b3986f54bed99212063b6d135d40a202d556a3e4d253e24c74be4cb9e8ebc src/balance/health.rs
|
||||
5fef8187a5ca3d22cc76afc72bac211e502c7cffd230353190277d8b9daff583 src/balance/lease_runtime.rs
|
||||
dc9335e018d61cddfe3b4c9fa480913f4444c83e4fb1384e3b046cc40940a2c3 src/balance/lease_runtime.rs
|
||||
91c5cef82ece69765858c22c61feb2fb90f38d292cd5bce7ee4fca8f4cb5cd42 src/balance/model.rs
|
||||
b5bb2e5d389fc0290c362af8b28c7d625c836876e308bbdcdd7cffcfee2e5e6d src/balance/pool_core.rs
|
||||
477132dd381e23f1be197c32ff58a0925f41d3e6aa63fe77c10973cefb2b76f6 src/balance/pool_selection.rs
|
||||
@@ -350,11 +351,11 @@ a16613114273c54b6d2a16a94fa8748550e91468585469642622bd2c86087baa src/logging/fo
|
||||
17425ebbca6bbbb0dcaeb65ebc44b1e51488a4092b7b6add556e764c6d486d6a src/logging/tests.rs
|
||||
3836ed14b6710e183f048ee4d7807a35366c27a27338057fe4a2811129b63302 src/main.rs
|
||||
46732ed6d9cf2a33031624f941b8318d2676fb1b6bb5142e96820301f9fa1ac4 src/main/admin_runtime.rs
|
||||
777cc490f53417047b90ba359aa400961f10234b88684b47df9a5025e3929c36 src/main/bootstrap.rs
|
||||
d92c3414cf7a86b027042d14d5cedc8dd8adeeafd7bc961032e2c5f322d01d4f src/main/bootstrap.rs
|
||||
5a98d0e249338e73aa57ae2e352dc16db2f79c2d042c1544ce1c2022da76a31c src/main/proxy_runtime.rs
|
||||
4dd7c1084a6214c0f2513ebf86fe8ef144470695557772bfe25979edd64491ab src/main/shutdown.rs
|
||||
0a6578a0f4023b9f8d73043bf942dc5e096ef9afb8c0d2f131c14f65d08ac01d src/main/shutdown/tests.rs
|
||||
689d0eb4637b826c3802818a00f689eb7b40c3c96918d86377efb9ce047df01b src/main/tasks.rs
|
||||
88edd345a0ea0eb9dd6800e990661639ec810997bc7b1206006a3d85707af76c src/main/tasks.rs
|
||||
49d7e104585c8791ac9a203dddd95802784b68c0ba65d6f90f3d57154b4c7871 src/metrics.rs
|
||||
201ab702722d6d21585fe8bb49939150d61bfac7ed8dc198b98c2cc6098ffb5f src/metrics/global.rs
|
||||
49b11df65b4fb0b5b727f9f867436584680b45e3f4fbd53f74bc0cab8b9a88ec src/metrics/vhost.rs
|
||||
@@ -449,8 +450,8 @@ a4e1d2aa2913587c250f251356431097b76995aee6e8bf9e35c4978a95491de1 src/transform/
|
||||
639ed68b2c62a9c8ef23beac480ed4ecb0a7d31672177c9e0bde6c223137fac1 src/update_check.rs
|
||||
c42d000d1d576721af3ffe930589388693001b8113554a7658f6dcbc665b5d1b src/update_check/tests.rs
|
||||
0399f6211c6e33fa12298acc40957f1532ace235c3950c89050a3450ee127299 src/upstream.rs
|
||||
c16b95930f3e5e700961ebc2817c271931b78739707bbfafcb41d67d95a8d19b src/upstream/client.rs
|
||||
aec8868ba0823bc5ea4ab690caff91cc972b333464993d494ced8a949b5ff173 src/upstream/client_tests.rs
|
||||
19d707f8b14ea1b2828a8d51bbacf7505bb0fad79c458d855b80297b879e9ccc src/upstream/client.rs
|
||||
4aa2420a92d33c50f7027f57ac4eccdc050bdb7e9ec8fa6e0eaa809228b71cd8 src/upstream/client_tests.rs
|
||||
3a4b097c0d5c0d88fbe881d84bd26a89789f68e4c46ea68b9e4a923dd4977d0b src/upstream/tests.rs
|
||||
e4c1ff8f53112e7cfad101a266355f7e47b82895480db431662f4cc59a1d9702 src/vhost.rs
|
||||
f5bf21d13cb094706ae42c8b5cb760e12b4cf9c71a559c57ac0e1661b40cc17c src/vhost/model.rs
|
||||
@@ -461,7 +462,7 @@ f0479524d66bfee7a6eed5eea03601630ba82064fb944902deb9166f90fe17cf src/vhost/vali
|
||||
9c1c8d6eedecb95eadbfb23c3caea562f6f7a995606ef988f25b58c2d03bcdd5 src/web_server/config/model.rs
|
||||
6191f4d541476c4a3ae296e7dd28bdb9f8a123fc509b47619f6f44519c44d278 src/web_server/config/validation.rs
|
||||
86c744216d7132d8294a6e90ba9be5fd871646006313d1c62506d40c81db9b12 src/web_server/fastcgi.rs
|
||||
f11eb148a9f5f3c8f65ad067f497d88e19375306cfd10016110863b4a2bd6c75 src/web_server/fastcgi/protocol.rs
|
||||
1e5b3265e4aca4de07dc93a471faeaf248dbf843ed845a11b50226e9b61c950d src/web_server/fastcgi/protocol.rs
|
||||
134c4998f6a0aa27e121af1ae3355b7e335488b2ae867ac0a934c58b4eb77191 src/web_server/fastcgi/response.rs
|
||||
8186363315bd998919c6c783e6f7501528f257a5c0da77c5d60f423e8670db5e src/web_server/filesystem.rs
|
||||
0aafd7f37c4fe784b047646e4ce5ddcb5fe821231a41ed0a9f519529385288a8 src/web_server/htaccess.rs
|
||||
@@ -471,14 +472,14 @@ f11eb148a9f5f3c8f65ad067f497d88e19375306cfd10016110863b4a2bd6c75 src/web_server
|
||||
80f946570c14c8915fd21bfdb1a84dfb6a14c633ab6060acb828fab4c7c21daa src/web_server/mod.rs
|
||||
67ab8bef567452f123c4b9b46a942b39c93f1f765c128edca7ee808ff1c5e304 src/web_server/request.rs
|
||||
c5cb561b0f276e8082f904b9446fa5885f6b3c0608f25ea2ef425061e9f7f6b4 src/web_server/response.rs
|
||||
6b5506a79c48c58b9ee69bdcbed16fba63bd3b2dc5c11ddb2deb9b087cc8f35c src/web_server/response/file.rs
|
||||
4ef82ec2746afd520c43f7c823a059a1ae903f840680f3da812aeaf8d8655a51 src/web_server/response/file.rs
|
||||
9aa68251a5f549f4ce3b1bda88d4abb9efc629caf444989381b4117c9488e37b src/web_server/response/headers.rs
|
||||
2268a6aeed2f565f9791af33e1cd21c56c228c8873b031725b39a6c57184f2d1 src/web_server/routing.rs
|
||||
400e1c8aa2fe857d96036a64499fbc3de44c1e3a53811c5b25101fb843359792 src/web_server/security.rs
|
||||
3e0b4e92600a7d5abcec92efed532b3a01e0781cb4f2681daac6d6ae13b0a6e9 src/web_server/serve.rs
|
||||
cf86c4692080723bbdf8f4105e1abfae33d7fcf020d2ad9e13ebae3943bd0cbe src/web_server/tests.rs
|
||||
c62752fe2fa6b74ddc9d94d36f8d191376b1575b15d21fc434c5a5da4c932692 src/web_server/tests/part_01.rs
|
||||
aa142445a5c7a50f6f667a2b84ec25f80ea79790bb0d0aca3c922dfc3118594b src/web_server/tests/part_02.rs
|
||||
51b15848e0a43b08299c1e0420614966559c387a7f8731885b50399aa4ca8c6d src/web_server/tests/part_02.rs
|
||||
e54f44f6ca1ed2c41e620de9195d32c4f8bb6936c3881745316a07160f87b4fa src/web_server/tests/part_03.rs
|
||||
f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e tests/fixtures/argon2id.json
|
||||
1725c10331745dc04a07675784d385260da7b14567b8dba3cbae5e891eb5dadf tests/http2.rs
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Debian packaging
|
||||
|
||||
This directory builds the `rustproxy` binary package for RustProxy 0.26.3.
|
||||
This directory builds the `rustproxy` binary package for RustProxy 0.26.4.
|
||||
The package installs:
|
||||
|
||||
- `/usr/bin/rust-proxy`
|
||||
|
||||
Vendored
+1
-1
@@ -21,6 +21,6 @@ cargo fetch --locked
|
||||
dpkg-buildpackage -us -uc -b
|
||||
|
||||
mkdir -p dist
|
||||
find .. -maxdepth 1 -type f \( -name 'rustproxy_0.26.3_*.deb' -o -name 'rustproxy_0.26.3_*.changes' -o -name 'rustproxy_0.26.3_*.buildinfo' \) \
|
||||
find .. -maxdepth 1 -type f \( -name 'rustproxy_0.26.4_*.deb' -o -name 'rustproxy_0.26.4_*.changes' -o -name 'rustproxy_0.26.4_*.buildinfo' \) \
|
||||
-exec cp -f {} dist/ \;
|
||||
echo "Packages copied to $ROOT/dist"
|
||||
Vendored
+8
@@ -1,3 +1,11 @@
|
||||
rustproxy (0.26.4) stable; urgency=medium
|
||||
|
||||
* Add 404-host draft support and standardize modal Enabled controls.
|
||||
* Compact toolbar inputs and Overview-card appearance controls.
|
||||
* Recycle aged backend client pools without waiting for another request.
|
||||
|
||||
-- RustProxy Maintainers <noreply@example.invalid> Tue, 06 Oct 2026 11:05:00 +0200
|
||||
|
||||
rustproxy (0.26.2) stable; urgency=medium
|
||||
|
||||
* Attribute persistent protocol errors to configured vhosts using route, Host/authority or SNI.
|
||||
|
||||
@@ -18,7 +18,7 @@ if [[ $# -gt 0 ]]; then shift; fi
|
||||
case "$mode" in
|
||||
help|-h|--help)
|
||||
cat <<'HELP'
|
||||
RustProxy 0.26.3
|
||||
RustProxy 0.26.4
|
||||
./dev.sh Build and start proxy + local panel
|
||||
./dev.sh run [serve flags] Same, with extra server options
|
||||
./dev.sh admin setup-token Generate/print a one-time setup capability before first login
|
||||
@@ -139,7 +139,7 @@ if [[ "$mode" == "test" ]]; then
|
||||
printf 'Checking all Rust targets before tests...\n' >&2
|
||||
cargo check --locked --all-targets
|
||||
fi
|
||||
printf 'Building RustProxy 0.26.3...\n' >&2
|
||||
printf 'Building RustProxy 0.26.4...\n' >&2
|
||||
cargo build --locked
|
||||
BIN="$CARGO_TARGET_DIR/debug/rust-proxy"
|
||||
case "$mode" in
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package main
|
||||
|
||||
const version = "0.26.3"
|
||||
const version = "0.26.4"
|
||||
|
||||
type config struct {
|
||||
Listen string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Administration API access - 0.26.3
|
||||
# Administration API access - 0.26.4
|
||||
|
||||
## Sign-in boundary
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Stored log explorer
|
||||
|
||||
RustProxy 0.26.3 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
|
||||
RustProxy 0.26.4 provides an authenticated, read-only **Logs** workspace at `/logs`. The top bar has one **Logs** menu with separate **Stored logs** and **Live Logs** actions; the two data paths remain independent.
|
||||
|
||||
## Separation from Live Logs
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Installing and updating RustProxy 0.26.3
|
||||
# Installing and updating RustProxy 0.26.4
|
||||
|
||||
The scripts prepare a Linux/systemd installation. This source archive is **not a
|
||||
verified production build**: Rust/Cargo, real CA operations, live systemd startup
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
- [0.25.1 release audit](V0.25.1_AUDIT.md) - historical Traffic/module-split release scope and compiler-regression guards.
|
||||
- [0.25.0 specification audit](V0.25.0_AUDIT.md) - Web Server foundation point-by-point implementation status.
|
||||
- [0.25.0 pre-production security audit](V0.25.0_SECURITY_PREPROD_AUDIT.md) - threat-oriented source/configuration review, fixes made in-session and mandatory runtime security gates.
|
||||
- [0.26.4 implementation note](V0.26.4_IMPLEMENTATION.md) - 404 drafts, consistent modal controls, compact Overview settings and idle age-based backend rotation.
|
||||
- [0.26.3 implementation note](V0.26.3_IMPLEMENTATION.md) - global interface scaling, chart themes, square draft dock and Settings navigation polish.
|
||||
- [0.26.2 implementation note](V0.26.2_IMPLEMENTATION.md) - unified Logs navigation, field-query search, dynamic JSON columns and RAW parsing.
|
||||
- [0.26.1 implementation note](V0.26.1_IMPLEMENTATION.md) - historical vhost-aware protocol logging and Stored Logs UX milestone.
|
||||
@@ -63,7 +64,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
|
||||
## Historical or version-labelled context
|
||||
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md); [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) is retained as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence.
|
||||
Version-labelled `V*.md` files are retained as historical implementation/audit records. Their version number describes the audited milestone. The current release implementation note is [V0.26.4_IMPLEMENTATION.md](V0.26.4_IMPLEMENTATION.md); [V0.26.3_IMPLEMENTATION.md](V0.26.3_IMPLEMENTATION.md) is retained as the prior appearance/UI milestone, [V0.26.2_IMPLEMENTATION.md](V0.26.2_IMPLEMENTATION.md) as the prior Logs/UI milestone and [V0.26.1_IMPLEMENTATION.md](V0.26.1_IMPLEMENTATION.md) as the prior protocol-attribution milestone. The latest retained audit is [V0.25.5_AUDIT.md](V0.25.5_AUDIT.md); older audit files remain historical evidence.
|
||||
|
||||
Older context documents such as [INTEGRATIONS_040](INTEGRATIONS_040.md), [MANAGEMENT](MANAGEMENT.md), [SECURITY_AUDIT](SECURITY_AUDIT.md), [ACME_RFC2136](ACME_RFC2136.md) and older version audits remain available for traceability but should not override the current functional/technical documents above.
|
||||
|
||||
|
||||
+14
-16
@@ -1,20 +1,18 @@
|
||||
# Verification report - RustProxy 0.26.3
|
||||
# Verification report - RustProxy 0.26.4
|
||||
|
||||
## Scope
|
||||
|
||||
RustProxy 0.26.3 keeps the 0.26.2 log workflow and focuses on administration appearance, scaling and draft/settings ergonomics. Stored Logs and Live Logs now share one top-bar Logs menu while retaining independent data paths.
|
||||
RustProxy 0.26.4 is a compatibility-preserving administration/UI and backend-client lifecycle maintenance release. SQLite remains schema 8 and the saved proxy configuration format is unchanged from 0.26.3.
|
||||
|
||||
Stored Logs replaces the fixed event/status/client/vhost controls with an optional source selector, one bounded field-query input and page size. The query parser supports `field:value`, explicit `AND`, `!` negation, quoted values, wildcards, numeric comparisons and aliases such as `req` -> `method`. JSON/JSONL records expose a bounded discovered-field list for dynamic columns; protocol-specific fields such as `matched_by` and `sni` are no longer hard-coded table columns. RAW error-log lines are best-effort parsed into timestamp, level, target, message and detected `key=value` fields while preserving `raw`.
|
||||
The release adds 404-host forms to the existing session-only draft dock; completes the Polish Button style translation; makes compact toolbar search/filter inputs follow the selected button height; reuses the vhost Enabled-state presentation in 404-host, Redirect and Stream editors; replaces the tall Overview-card checkbox list with compact visibility/reorder tiles; and makes age-based upstream-client recycling run from backend maintenance even when no request arrives.
|
||||
|
||||
The dashboard removes the Configured files inventory but keeps an optional source selector with `All configured files` as the default, moves the column picker to the table toolbar, expands selected rows as parsed fields plus optional raw JSON, and uses `↑`/`↓` for backend sort direction. SQLite schema 8 and the saved proxy configuration format remain unchanged.
|
||||
|
||||
This workspace retains session-only draft docking for dirty Web Server, Vhost, Redirect and Stream editors; opening a second editor no longer reuses or blocks the suspended form state. In 0.26.3 the dock uses the same square-corner panel language as the rest of RustProxy, can collapse to one bar and shows draft age. **Settings -> Appearance** persists theme, language, 70-125% global interface scale, 12/24-hour display, eight chart styles, Normal/Compact button style, table/log density, dashboard density and Overview-card layout/visibility in the existing account-scoped administrator preference document. Scaling includes dialogs, overlays and the draft dock; form fields also receive a bounded additional size response so control proportions follow 70-125% more visibly. Compact buttons restore the smaller source-style control dimensions without changing the whole-interface scale and now apply explicitly to generated vhost/entity actions and dialog tab buttons. Settings adds content search; Appearance exposes chart palette previews, per-section resets and a full reset. Expanded Stored Logs details remain compact. SQLite schema 8 remains unchanged.
|
||||
The backend change keeps request-count recycling request-driven. The new maintenance path checks `pool_recycle_seconds`, uses the existing atomic generation/rotation guard and starts an idle-maintenance generation with zero requests. A Rust unit regression is included for age-based rotation without a triggering request, but this workspace cannot execute Rust tests because Cargo/rustc are unavailable.
|
||||
|
||||
## Source-level regression coverage
|
||||
|
||||
0.26.2 log-query Rust tests continue to cover field aliases, explicit AND, negation, quoted values, wildcard/status/numeric matching and built-in RAW error parsing. Retained 0.26.1 tests continue to cover canonical field recovery, safe source derivation, symlink refusal, bounded reads and protocol diagnostic normalization.
|
||||
Rust-independent contracts now require 404 draft registration, the shared Enabled-control helper, compact Overview tiles, toolbar controls bounded by `--button-min-height`, and backend maintenance calls for ordinary and WebSocket clients. Existing source, security, memory, Web Server, documentation, packaging, upgrade and SQLite migration contracts remain enabled.
|
||||
|
||||
Administration preference validation now permits up to 64 bounded, control-free Stored Logs field names so dynamically discovered JSON fields can be persisted per account. It also validates the language, 70-125% interface-scale, time-format, expanded chart-style fields, Normal/Compact button style, table/log density, dashboard density, dashboard card ordering and hidden-card list while retaining defaults for legacy preference JSON. Browser JavaScript is syntax-checked, the Web Server UI harness loads the draft manager in production order, and the language/documentation/source-contract suites cover the new UI strings and release identity.
|
||||
Browser JavaScript syntax checks cover the changed core/preferences/404/Redirect/Stream/vhost editor files. The simulated Web Server UI test remains green. Language packs and OpenAPI JSON parse successfully, and the Docker Agent Go tests pass.
|
||||
|
||||
## Verification status in this workspace
|
||||
|
||||
@@ -26,19 +24,19 @@ Administration preference validation now permits up to 64 bounded, control-free
|
||||
| `python3 scripts/security_contracts.py` | PASS |
|
||||
| `python3 scripts/security_dependency_contracts.py` | PASS |
|
||||
| `python3 scripts/source_module_contracts.py` | PASS |
|
||||
| `python3 scripts/rust_module_contracts.py` | PASS after keeping modified Rust modules within the 500-line contract |
|
||||
| `python3 scripts/rust_module_contracts.py` | PASS |
|
||||
| `python3 scripts/docs_check.py` | PASS |
|
||||
| `node scripts/web_service_ui_test.mjs` | PASS |
|
||||
| `node --check` for `settings.js`, `drafts.js`, `app/preferences.js`, `traffic-panel.js` and `file-logs.js` | PASS |
|
||||
| language/OpenAPI JSON parse | PASS |
|
||||
| `python3 scripts/check_source.py` | PASS; explicitly reports Rust parsing/types/runtime as not verified |
|
||||
| `python3 scripts/release_smoke.py` | PASS, including upgrade/package/reproducibility contracts |
|
||||
| `python3 scripts/release_smoke.py` | PASS, including SQLite migrations, stopped-source upgrade and deterministic source packaging contracts |
|
||||
| `node scripts/web_service_ui_test.mjs` | PASS |
|
||||
| `node --check` for changed administration JavaScript | PASS |
|
||||
| language/OpenAPI JSON parse | PASS |
|
||||
| `go test ./...` in `docker-agent` | PASS |
|
||||
| `./dev.sh test` | Rust-independent gates PASS; exits 127 when the Rust stage is reached because Cargo is unavailable |
|
||||
| `./dev.sh check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo fmt --check` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo clippy --all-targets --all-features -- -D warnings` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `bash dev.sh` complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable in this workspace |
|
||||
| `cargo test --all-targets --all-features` | BLOCKED: Rust/Cargo toolchain unavailable in this workspace; the new age-rotation unit test is therefore not runtime-executed here |
|
||||
| complete release build | BLOCKED at Rust compilation: Rust/Cargo toolchain unavailable in this workspace |
|
||||
|
||||
The source archive must not be described as compiler-verified until the Rust-enabled release-host gates pass. Rust-independent gates verify source structure, security contracts, documentation, browser JavaScript, packaging invariants and deterministic archive behavior, but they cannot substitute for Rust parsing, type checking, Clippy or runtime tests.
|
||||
The 0.26.4 source archive must not be described as compiler-verified until the Rust-enabled release-host gates pass. Rust-independent checks verify source structure, UI/translation contracts, security invariants, migrations, upgrades, documentation, JavaScript syntax, Go-agent behavior and deterministic archive behavior, but they do not substitute for Rust parsing, type checking, Clippy or runtime tests.
|
||||
@@ -0,0 +1,30 @@
|
||||
# RustProxy 0.26.4 implementation note
|
||||
|
||||
## Scope
|
||||
|
||||
0.26.4 is a compatibility-preserving administration and backend-lifecycle maintenance release. SQLite remains schema 8 and the saved proxy configuration document is unchanged from 0.26.3.
|
||||
|
||||
## Administration UI
|
||||
|
||||
- The 404-host editor now registers with the existing session-only draft manager. Dirty forms are captured with their host/listener editor nodes and read closures, can be minimized into the draft dock, and restore the exact unfinished form state.
|
||||
- The shared Enabled presentation used by vhosts is exposed as one administration helper and reused by 404 hosts, Redirects and Streams. State text, hint text and enabled/disabled visual treatment therefore stay consistent across these editors.
|
||||
- Polish Button style labels/help are translated instead of falling back to English values.
|
||||
- Search/filter controls in compact toolbars use the active button minimum height so they do not visually exceed adjacent actions. Ordinary form controls keep their existing sizing.
|
||||
- Appearance -> Overview cards uses a responsive tile grid instead of one checkbox row per metric card. A tile toggles visibility while the existing up/down controls retain ordering behavior and account-scoped persistence.
|
||||
|
||||
## Backend client rotation
|
||||
|
||||
Request-count recycling remains request-driven. Age-based recycling is now also checked by the existing backend health/maintenance loop. This removes the dependency on a new request arriving after `pool_recycle_seconds` and allows idle backend clients to advance generation/rotation counters on schedule.
|
||||
|
||||
Maintenance-driven rotation starts the new generation with zero requests; request-driven rotation continues to count the request that triggered the swap in the new generation. The rebuild remains guarded by the existing generation/rotation atomics, so concurrent request and maintenance checks cannot double-rotate the same generation.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- SQLite schema: 8 (unchanged).
|
||||
- Saved proxy configuration format: unchanged.
|
||||
- Administration preferences: existing fields only; no migration required.
|
||||
- Upgrade from 0.26.3: no data migration required.
|
||||
|
||||
## Verification
|
||||
|
||||
See [TEST_REPORT.md](TEST_REPORT.md). This workspace can run the Rust-independent source, documentation, browser-JavaScript, packaging and Go-agent gates. Rust compiler gates must still be run on a release host with Cargo before calling the archive compiler-verified.
|
||||
+1
-1
@@ -19,7 +19,7 @@ classes, cache outcomes, capture-state gauges and configured-route traffic. Raw
|
||||
are intentionally absent from Prometheus labels.
|
||||
|
||||
Import the JSON in Grafana and select a Prometheus datasource when prompted. The
|
||||
dashboard expects RustProxy 0.26.3 metric names and a scrape interval around 15s.
|
||||
dashboard expects RustProxy 0.26.4 metric names and a scrape interval around 15s.
|
||||
Collectors can be disabled in RustProxy settings; panels for disabled collectors
|
||||
will simply show no series.
|
||||
|
||||
|
||||
@@ -4529,9 +4529,9 @@
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "RustProxy 0.26.3 - Operations",
|
||||
"title": "RustProxy 0.26.4 - Operations",
|
||||
"uid": "rustproxy-0170",
|
||||
"version": 5,
|
||||
"weekStart": "",
|
||||
"description": "RustProxy 0.26.3 operations dashboard including backend client lifecycle/retry diagnostics, bounded Traffic analytics, country and User-Agent-family visibility, and built-in Web Server traffic."
|
||||
"description": "RustProxy 0.26.4 operations dashboard including backend client lifecycle/retry diagnostics, bounded Traffic analytics, country and User-Agent-family visibility, and built-in Web Server traffic."
|
||||
}
|
||||
+2
-1
@@ -2072,6 +2072,7 @@
|
||||
"buttonStyle": "Button style",
|
||||
"buttonStyleNormal": "Normal",
|
||||
"buttonStyleCompact": "Compact",
|
||||
"buttonStyleHelp": "Normal uses the refreshed controls; Compact restores the smaller source-style buttons across all actions and dialog tabs."
|
||||
"buttonStyleHelp": "Normal uses the refreshed controls; Compact restores the smaller source-style buttons across all actions and dialog tabs.",
|
||||
"entityEnableHint": "Controls whether this item is active."
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -2070,8 +2070,9 @@
|
||||
"draftAgeHours": "{count} godz.",
|
||||
"draftAgeDays": "{count} d",
|
||||
"buttonStyle": "Styl przycisków",
|
||||
"buttonStyleNormal": "Normal",
|
||||
"buttonStyleCompact": "Compact",
|
||||
"buttonStyleHelp": "Normal używa odświeżonych przycisków; Compact przywraca drobniejsze przyciski ze źródłowego interfejsu we wszystkich akcjach i zakładkach modali."
|
||||
"buttonStyleNormal": "Normalny",
|
||||
"buttonStyleCompact": "Kompaktowy",
|
||||
"buttonStyleHelp": "Normalny używa odświeżonych przycisków; Kompaktowy przywraca mniejsze przyciski ze źródłowego interfejsu we wszystkich akcjach i zakładkach modali.",
|
||||
"entityEnableHint": "Określa, czy ten element jest aktywny."
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -88,7 +88,7 @@ def add_file(archive: zipfile.ZipFile, rel: Path, data: bytes, timestamp: tuple[
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('output', nargs='?', type=Path, default=ROOT.parent / 'RustProxy-0.26.3.zip')
|
||||
parser.add_argument('output', nargs='?', type=Path, default=ROOT.parent / 'RustProxy-0.26.4.zip')
|
||||
output = parser.parse_args().output.resolve()
|
||||
paths = files()
|
||||
checksum = ROOT / 'SHA256SUMS'
|
||||
|
||||
+22
-13
@@ -24,7 +24,7 @@ def read_docker_agent_source() -> str:
|
||||
|
||||
preflight(root)
|
||||
manifest = tomllib.loads((root / "Cargo.toml").read_text())
|
||||
assert manifest["package"]["version"] == "0.26.3"
|
||||
assert manifest["package"]["version"] == "0.26.4"
|
||||
assert manifest["dependencies"]["quinn"]["version"] == "0.11.12"
|
||||
h3dep = manifest["dependencies"]["h3"]
|
||||
assert h3dep["package"] == "hpx-h3" and h3dep["version"] == "=2.5.19" and "quinn" in h3dep["features"]
|
||||
@@ -32,14 +32,14 @@ assert "h3-quinn" not in manifest["dependencies"]
|
||||
lock_path = root / "Cargo.lock"
|
||||
if lock_path.exists():
|
||||
lock = lock_path.read_text()
|
||||
assert 'name = "rust-proxy"\nversion = "0.26.3"' in lock
|
||||
assert 'name = "rust-proxy"\nversion = "0.26.4"' in lock
|
||||
assert 'name = "systemstat"' in lock and 'name = "metrics-process"' in lock and 'name = "sysinfo"' in lock, "Cargo.lock must be resolver-generated after adding cross-platform statistics libraries"
|
||||
for release_doc, marker in {
|
||||
"README.md": "# RustProxy 0.26.3",
|
||||
"CHANGELOG.md": "## 0.26.3",
|
||||
"ROADMAP.md": "## 0.26.3",
|
||||
"AGENTS.md": "Current source version: 0.26.3.",
|
||||
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.26.3",
|
||||
"README.md": "# RustProxy 0.26.4",
|
||||
"CHANGELOG.md": "## 0.26.4",
|
||||
"ROADMAP.md": "## 0.26.4",
|
||||
"AGENTS.md": "Current source version: 0.26.4.",
|
||||
"docs/TEST_REPORT.md": "# Verification report - RustProxy 0.26.4",
|
||||
}.items():
|
||||
assert marker in (root / release_doc).read_text(), f"stale release identity in {release_doc}"
|
||||
assert tomllib.loads((root / "rust-toolchain.toml").read_text())["toolchain"]["channel"] == "stable"
|
||||
@@ -160,6 +160,11 @@ assert 'id="security-password-editor"' in html and 'id="security-policy-editor"'
|
||||
assert 'docker-service-editor' in (root / "src/admin/web/docker.js").read_text() and 'docker-agent-details' in (root / "src/admin/web/docker.js").read_text()
|
||||
balance_source = rust_source(root, "src/balance.rs")
|
||||
assert 'mode: HealthMode::Tcp' in balance_source
|
||||
assert 'member.backend.client.maintain_rotation();' in balance_source
|
||||
assert 'member.backend.websocket_client.maintain_rotation();' in balance_source
|
||||
upstream_client_source = rust_source(root, "src/upstream/client.rs")
|
||||
assert 'fn maintain_rotation(&self)' in upstream_client_source
|
||||
assert 'self.rotate(self.generation.load(Ordering::Acquire), "age", false);' in upstream_client_source
|
||||
for marker in [
|
||||
'pub struct PassiveHealth', 'enabled: false', 'pub passive_health: PassiveHealth',
|
||||
'passive_health_failures_total', 'http_5xx_total', 'timeouts_total',
|
||||
@@ -185,13 +190,17 @@ assert '"forward-client-ip", "vhForwardClientIp"' in vhosts_js and 'forward_clie
|
||||
assert 'id="vhost-search"' in html and '$("vhost-search").addEventListener("input", render)' in vhosts_js
|
||||
for marker in ['id="pool-search"', 'id="pool-sort"', 'data-pool-column-toggle="vhost"', 'data-pool-column="generationAge"']:
|
||||
assert marker in html, marker
|
||||
for marker in ['toggleVhostEnabled', 'vhQuickEnable', 'vhQuickDisable', 'backend_table_columns', 'applyPoolTableView', 'saveSection("vhosts", next.vhosts || [])', 'vh-enable-control']:
|
||||
for marker in ['toggleVhostEnabled', 'vhQuickEnable', 'vhQuickDisable', 'backend_table_columns', 'applyPoolTableView', 'saveSection("vhosts", next.vhosts || [])', 'rustProxyEnableControl']:
|
||||
assert marker in vhosts_js, marker
|
||||
assert 'rustproxy-backend-table-v1' not in vhosts_js and 'poolTablePreferenceKey' not in vhosts_js
|
||||
assert 'window.rustProxyPreferences.update({' in vhosts_js and 'backend_table_sort' in vhosts_js and 'backend_table_descending' in vhosts_js
|
||||
style_ui = (root / 'src/admin/web/style.css').read_text()
|
||||
for marker in ['--scrollbar-thumb', '*::-webkit-scrollbar-thumb', '.backend-table-tools', '.vh-enable-control', '.vhost-quick-toggle']:
|
||||
for marker in ['--scrollbar-thumb', '*::-webkit-scrollbar-thumb', '.backend-table-tools', '.vh-enable-control', '.vhost-quick-toggle', '.dashboard-widget-toggle', 'height: var(--button-min-height)']:
|
||||
assert marker in style_ui, marker
|
||||
preferences_js = (root / "src/admin/web/app/preferences.js").read_text()
|
||||
assert 'dashboard-widget-toggle' in preferences_js and 'visible.type = "checkbox"' not in preferences_js
|
||||
core_js = (root / "src/admin/web/app/core.js").read_text()
|
||||
assert 'window.rustProxyEnableControl = enhanceEnabledControl' in core_js
|
||||
assert 'id="admin-session-days"' in html and 'min="7" max="30"' in html
|
||||
assert 'member-local-risk' in vhosts_js and 'vhLocalListenerRiskConfirm' in vhosts_js
|
||||
assert 'accept_local_listener_risk: yes("member-local-risk", row)' in vhosts_js
|
||||
@@ -240,13 +249,13 @@ assert 'Opt-in health decisions based on ordinary user traffic. Defaults to disa
|
||||
assert '"force_ssl"' in openapi and '"forward_client_ip"' in openapi
|
||||
assert '"backend_header"' in openapi and '"backend_timing_header"' in openapi
|
||||
assert '"StreamMetrics"' in openapi and '"streams"' in openapi
|
||||
for marker in ['"version": "0.26.3"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
|
||||
for marker in ['"version": "0.26.4"', '"/api/traffic"', '"capture_user_agent"', '"capture_country"', '"hot_auto_trim_enabled"', '"hot_auto_trim_after_seconds"', '"hot_auto_trim_target_percent"', '"/api/config/{section}"', '"ConfigSectionUpdate"', '"cold_on_idle"', '"cold_on_age"', '"cold_max_hot_seconds"', '"cold_on_lru"', '"cold_on_shutdown"', '"cold_gc_interval_seconds"', '"cold_gc_max_delete"', '"collapse_enabled"', '"collapse_wait_timeout_ms"', '"collapse_timeout_fallback"', '"stale_if_error_timeout"', '"stale_if_error_connection"', '"stale_if_error_dns"', '"stale_if_error_tls"', '"stale_if_error_statuses"', '"proactive_refresh_enabled"', '"proactive_refresh_before_expiry_seconds"', '"proactive_refresh_percent"', '"proactive_refresh_max_inflight"', '"TransformSettings"', '"TransformOverrides"', '"/api/cache/cold/cleanup"', '"/api/cache/invalidate"', '"/api/cache/bans"', '"/api/cache/bans/{id}"', '"CacheInvalidationRequest"', '"CacheInvalidationResponse"', '"ColdCleanupJob"', '"match_type"', '"regex_insensitive"', '"rewrite"', '"response"', '"covered_hosts"', '"default": true']:
|
||||
assert marker in openapi, marker
|
||||
for marker in ['"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
assert marker in openapi, marker
|
||||
assert 'id="panel-notfound"' in html and 'id="notfound-dialog"' in html and 'id="notfound-hosts-editor"' in html
|
||||
host404_js = (root / "src/admin/web/host404.js").read_text()
|
||||
for marker in ['rustProxyNotFound', 'saveSection("not_found_hosts"', 'rustProxyIntegrations.hostnames', 'rustProxyIntegrations.bindings', 'BUILTIN_STATUSES', 'ERROR_STATUSES']:
|
||||
for marker in ['rustProxyNotFound', 'saveSection("not_found_hosts"', 'rustProxyIntegrations.hostnames', 'rustProxyIntegrations.bindings', 'BUILTIN_STATUSES', 'ERROR_STATUSES', 'rustProxyDrafts.register("notfound"', 'rustProxyEnableControl($("notfound-enabled"))']:
|
||||
assert marker in host404_js, marker
|
||||
assert 'for (let code = 200; code <= 599; code++)' not in host404_js
|
||||
assert 'preserve_host: true' in vhosts_js, "new HTTP/1.1 backend drafts must preserve the public Host"
|
||||
@@ -1203,7 +1212,7 @@ assert '"error_log" => {' not in admin_0241 and 'request("/api/config/error_log"
|
||||
assert '"error_log"' in openapi and 'logging, error_log, forwarding' not in openapi
|
||||
assert 'logging_section_persists_error_log_settings_atomically' in admin_0241 and 'error_log_is_not_a_standalone_configuration_section' in admin_0241
|
||||
assert 'location.pathname === "/logging"' in settings_js_0241
|
||||
assert '"RuntimeInformation"' in openapi and '"version": "0.26.3"' in openapi
|
||||
assert '"RuntimeInformation"' in openapi and '"version": "0.26.4"' in openapi
|
||||
|
||||
# 0.24.1 compiler and hostname-cardinality regressions.
|
||||
tls_0241 = rust_source(root, "src/tls.rs")
|
||||
@@ -1248,5 +1257,5 @@ for schema_name in ["RedirectHost", "NotFoundHost"]:
|
||||
node = openapi_0241["components"]["schemas"][schema_name]["properties"]["frontend"]["properties"]["hosts"]
|
||||
assert "maxItems" not in node, schema_name
|
||||
|
||||
print("PASS: 0.26.3 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, Debian/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v8 migrations.")
|
||||
print("PASS: 0.26.4 manifest/package, shared system-statistics/runtime metadata, Settings logging, retained H2, Debian/Grafana/Prometheus/404-host/custom-location/path/cache/dashboard/Nagios UI/source contracts, stopped legacy-renewal removal, session/admin/transport contracts, deterministic package source and embedded SQLite schema-v8 migrations.")
|
||||
print("NOT VERIFIED: Rust parsing, types, compilation, linking or runtime behavior. Run ./dev.sh test.")
|
||||
@@ -97,7 +97,7 @@ def digest(path: Path) -> str:
|
||||
def fetch_https(url: str, max_bytes: int) -> bytes:
|
||||
if urllib.parse.urlparse(url).scheme != 'https':
|
||||
raise ValueError('GeoIP downloads require HTTPS.')
|
||||
request = urllib.request.Request(url, headers={'User-Agent': 'RustProxy-installer/0.26.3'})
|
||||
request = urllib.request.Request(url, headers={'User-Agent': 'RustProxy-installer/0.26.4'})
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
if urllib.parse.urlparse(response.geturl()).scheme != 'https':
|
||||
raise ValueError('GeoIP download redirected away from HTTPS.')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rust-independent documentation/Reader consistency checks for the 0.26.3 release."""
|
||||
"""Rust-independent documentation/Reader consistency checks for the 0.26.4 release."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
from rust_source import rust_source
|
||||
@@ -55,16 +55,16 @@ assert '"/docs/content.json"' in admin
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
roadmap = (ROOT / "ROADMAP.md").read_text(encoding="utf-8")
|
||||
changelog = (ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
|
||||
assert readme.startswith("# RustProxy 0.26.3")
|
||||
assert readme.startswith("# RustProxy 0.26.4")
|
||||
assert "docs/WEB_SERVER.md" in readme and "docs/README.md" in readme
|
||||
assert readme.count("## What changed in ") == 1, "README duplicates historical release notes; use CHANGELOG/archive"
|
||||
assert "## Release history" in readme and "docs/ROADMAP_HISTORY.md" in readme
|
||||
assert roadmap.startswith("# RustProxy roadmap") and "## 0.26.3" in roadmap and "## 0.26.0" in roadmap and "## 0.25.5" in roadmap and "## 0.25.4" in roadmap and "## 0.25.3" in roadmap and "## 0.25.2" in roadmap and "## 0.25.1" in roadmap and "## 0.25.0" in roadmap
|
||||
assert roadmap.startswith("# RustProxy roadmap") and "## 0.26.4" in roadmap and "## 0.26.0" in roadmap and "## 0.25.5" in roadmap and "## 0.25.4" in roadmap and "## 0.25.3" in roadmap and "## 0.25.2" in roadmap and "## 0.25.1" in roadmap and "## 0.25.0" in roadmap
|
||||
assert "## 0.24." not in roadmap and "## Web Server follow-up" in roadmap and "docs/ROADMAP_HISTORY.md" in roadmap
|
||||
assert changelog.startswith("# Changelog\n")
|
||||
for release in ["0.26.3", "0.26.1", "0.26.0", "0.25.6", "0.25.5", "0.25.4", "0.25.3"]:
|
||||
for release in ["0.26.4", "0.26.1", "0.26.0", "0.25.6", "0.25.5", "0.25.4", "0.25.3"]:
|
||||
assert f"## {release}" in changelog, release
|
||||
assert changelog.index("## 0.26.3") < changelog.index("## 0.26.1") < changelog.index("## 0.26.0") < changelog.index("## 0.25.6") < changelog.index("## 0.25.5")
|
||||
assert changelog.index("## 0.26.4") < changelog.index("## 0.26.1") < changelog.index("## 0.26.0") < changelog.index("## 0.25.6") < changelog.index("## 0.25.5")
|
||||
assert "CHANGELOG_TECHNICAL_ARCHIVE.md" in changelog
|
||||
for developer_only in ["Clean the remaining compiler warnings", "Update package/source/UI/OpenAPI/release identity", "source regressions and update release identity"]:
|
||||
assert developer_only not in changelog, f"developer-only CHANGELOG detail: {developer_only}"
|
||||
|
||||
@@ -75,10 +75,10 @@ def check(root: Path, repair_known: bool = False) -> list[str]:
|
||||
raise ValueError(f"Conflicting Rust modules: {flat.relative_to(root)} and {module.relative_to(root)}")
|
||||
for path in ["src/auth/mod.rs", "src/auth/store.rs", "src/backup/snapshot.rs", "src/upstream.rs", "src/tls.rs", "src/proxy/websocket.rs", "src/cache/mod.rs", "src/cache/model.rs", "src/cache/lifecycle.rs", "src/cache/cold.rs", "src/cache/dashboard.rs", "src/cache/tickets.rs", "src/cache/lookup.rs", "src/cache/policy.rs", "src/cache/fill.rs", "src/cache/tests.rs", "src/settings.rs", "src/compression.rs", "src/diagnostics.rs", "src/traffic.rs", "src/system_stats/mod.rs", "src/system_stats/linux.rs", "src/vhost.rs", "src/web_server/mod.rs", "src/web_server/config.rs", "src/web_server/filesystem.rs", "src/web_server/security.rs", "src/web_server/response.rs", "src/web_server/request.rs", "src/web_server/routing.rs", "src/web_server/fastcgi.rs", "src/web_server/htaccess.rs", "src/web_server/serve.rs", "src/web_server/tests.rs", "src/balance.rs", "src/security.rs", "src/admin/web/vhosts/core.js", "src/admin/web/vhosts/editor.js", "src/admin/web/vhosts/render.js", "src/admin/web/web-server.js", "src/admin/web/streams.js", "src/admin/web/host404.js", "src/admin/web/app/core.js", "src/admin/web/app/preferences.js", "src/admin/web/app/config-history.js", "src/admin/web/app/runtime.js", "src/admin/web/app/settings.js", "src/admin/web/management.js", "src/admin/web/drafts.js", "src/pages.rs", "scripts/mock_server.py", "scripts/mock_auth.py", "src/forwarding.rs", "src/tinyauth.rs", "src/http3.rs", "src/stream.rs", "src/admin/web/integrations.js", "src/admin/web/traffic-panel.js", "src/prometheus.rs", "src/admin/web/settings.js", "src/admin/web/docker.js", "src/docker.rs", "docker-agent/main.go", "docker-agent/model.go", "docker-agent/docker.go", "docker-agent/network.go", "docker-agent/certificate.go", "docker-agent/server.go", "docker-agent/main_test.go", "docker-agent/rustproxy-docker-agent.service", "docker-agent/install.sh", "docs/DOCKER_DISCOVERY.md", "docs/WEB_SERVER.md", "docs/PHP_FPM.md", "docs/README.md", "docs/TODO.md", "docs/V0.25.5_AUDIT.md", "docs/V0.25.4_AUDIT.md", "docs/V0.25.3_AUDIT.md", "docs/V0.25.2_AUDIT.md", "docs/CHANGELOG_TECHNICAL_ARCHIVE.md", "docs/ROADMAP_HISTORY.md", "scripts/docs_check.py", "scripts/web_server_contracts.py", "scripts/rust_source.py", "scripts/rust_module_contracts.py", "scripts/source_module_contracts.py", "scripts/acme_provider.py", "src/basic_auth.rs", "scripts/deploy.py", "scripts/deploy_core.py", "scripts/deploy_runtime.py", "scripts/migrate_npm.py", "scripts/migrate_npm_core.py", "scripts/migrate_npm_api.py", "docs/NPM_MIGRATION.md", "docs/STREAMS.md", "install.sh", "install-debian13.sh", "update.sh", "build.rs", "scripts/swagger_assets.py", "src/admin/documentation.rs", "src/admin/openapi.json", "langs/en_US.json", "langs/pl_pl.json", "Dockerfile", ".dockerignore", "docs/CONTAINER.md", "docs/PROMETHEUS.md", "docs/NOT_FOUND_HOSTS.md", "debian/control", "debian/rules", "debian/rustproxy.service", "debian/rustproxy.logrotate", "grafana/rustproxy-overview.json"]:
|
||||
if not (root / path).is_file():
|
||||
raise ValueError(f"Incomplete RustProxy 0.26.3 source tree: missing {path}")
|
||||
raise ValueError(f"Incomplete RustProxy 0.26.4 source tree: missing {path}")
|
||||
text = (root / "src/lib.rs").read_text(encoding="utf-8")
|
||||
if '#[path = "auth/mod.rs"]' not in text:
|
||||
raise ValueError("src/lib.rs is not the 0.26.3 entry point; extract the complete package, not individual files.")
|
||||
raise ValueError("src/lib.rs is not the 0.26.4 entry point; extract the complete package, not individual files.")
|
||||
return messages
|
||||
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ def upgrade_smoke(tmp: Path) -> None:
|
||||
assert backup.is_dir()
|
||||
assert (backup / "src/obsolete.rs").is_file()
|
||||
assert not (target / "src/obsolete.rs").exists()
|
||||
assert 'version = "0.26.3"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert 'version = "0.26.4"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert (target / "data/keep.txt").read_text(encoding="utf-8") == "preserve\n"
|
||||
assert (target / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
|
||||
assert (target / "langs/site.json").is_file()
|
||||
@@ -134,7 +134,7 @@ def upgrade_smoke(tmp: Path) -> None:
|
||||
assert (target / "docker-agent/certificate.go").is_file()
|
||||
assert (target / "docker-agent/server.go").is_file()
|
||||
manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8"))
|
||||
assert manifest["to_version"] == "0.26.3"
|
||||
assert manifest["to_version"] == "0.26.4"
|
||||
assert manifest["removed_obsolete_runtime_state"] is True
|
||||
assert not legacy.exists()
|
||||
assert not (backup / "legacy-data").exists()
|
||||
@@ -381,7 +381,7 @@ def ui_contract_smoke() -> None:
|
||||
assert "/assets/host404.js" in html
|
||||
for marker in ["rustProxyRedirects", 'saveSection("redirects"', "redirectTargetType", "redirectPathMode", "redirectQueryMode", "redirectCacheSeconds"]:
|
||||
assert marker in redirects, marker
|
||||
for marker in ["rustProxyNotFound", 'saveSection("not_found_hosts"', "rustProxyIntegrations.hostnames", "rustProxyIntegrations.bindings", "notfound-mode", "notfound-status", "BUILTIN_STATUSES", "ERROR_STATUSES"]:
|
||||
for marker in ["rustProxyNotFound", 'saveSection("not_found_hosts"', "rustProxyIntegrations.hostnames", "rustProxyIntegrations.bindings", "notfound-mode", "notfound-status", "BUILTIN_STATUSES", "ERROR_STATUSES", 'rustProxyDrafts.register("notfound"', 'rustProxyEnableControl($("notfound-enabled"))']:
|
||||
assert marker in host404, marker
|
||||
assert 'for (let code = 200; code <= 599; code++)' not in host404
|
||||
assert 'id="docker-search"' in html and 'id="docker-show-hidden"' in html
|
||||
@@ -392,14 +392,14 @@ def ui_contract_smoke() -> None:
|
||||
assert "vhForceSslNeedsTls" in vhosts and "vhForceSslNeedsHttp" in vhosts and "vhForceSslPortConflict" in vhosts
|
||||
assert "tlsPorts.has(443)" in vhosts
|
||||
assert '$("vhost-search").addEventListener("input", render)' in vhosts and "vhSearchEmpty" in vhosts
|
||||
for marker in ['toggleVhostEnabled', 'vhQuickEnable', 'vhQuickDisable', 'backend_table_columns', 'applyPoolTableView', 'vh-enable-control']:
|
||||
for marker in ['toggleVhostEnabled', 'vhQuickEnable', 'vhQuickDisable', 'backend_table_columns', 'applyPoolTableView', 'rustProxyEnableControl']:
|
||||
assert marker in vhosts, marker
|
||||
assert 'rustproxy-backend-table-v1' not in vhosts and 'poolTablePreferenceKey' not in vhosts
|
||||
assert 'window.rustProxyPreferences.update({' in vhosts and 'backend_table_sort' in vhosts and 'backend_table_descending' in vhosts
|
||||
for marker in ['id="pool-search"', 'id="pool-sort"', 'data-pool-column-toggle="vhost"']:
|
||||
assert marker in html, marker
|
||||
style = (ROOT / "src/admin/web/style.css").read_text(encoding="utf-8")
|
||||
for marker in ['--scrollbar-thumb', '*::-webkit-scrollbar-thumb', '.backend-table-tools', '.vhost-quick-toggle']:
|
||||
for marker in ['--scrollbar-thumb', '*::-webkit-scrollbar-thumb', '.backend-table-tools', '.vhost-quick-toggle', '.dashboard-widget-toggle', 'height: var(--button-min-height)']:
|
||||
assert marker in style, marker
|
||||
assert "pool-vhost-disabled" in vhosts
|
||||
for marker in ["locationMatchRegex", "headersVisualBuilder", "headersAdvancedJson", "openLocationHelp", "cloudflare-client-ip", "timing-headers"]:
|
||||
@@ -554,7 +554,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker in admin, marker
|
||||
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0]
|
||||
assert '"/api/status"' not in anonymous_block
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.26.3"' in openapi_0241
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.26.4"' in openapi_0241
|
||||
assert 'id="panel-logging"' not in html
|
||||
for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
|
||||
assert marker in html, marker
|
||||
@@ -1011,7 +1011,7 @@ def source_contract_smoke() -> None:
|
||||
assert '.managed-update' in main and "flush_hot_to_cold_for_update" in main
|
||||
openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
|
||||
assert '"/api/config/history/restore"' in openapi
|
||||
for marker in ['"version": "0.26.3"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
for marker in ['"version": "0.26.4"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
assert marker in openapi, marker
|
||||
assert '"software_update"' in openapi and '"update_available"' in openapi
|
||||
for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']:
|
||||
@@ -1186,7 +1186,7 @@ def main() -> None:
|
||||
source_contract_smoke()
|
||||
container_contract_smoke()
|
||||
debian_grafana_contract_smoke()
|
||||
print("PASS: 0.26.3 SQLite/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, Debian packaging, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
print("PASS: 0.26.4 SQLite/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, Debian packaging, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -85,7 +85,7 @@ def download(destination: Path) -> None:
|
||||
"""TLS verification uses Python's default trust store; never executes a shell."""
|
||||
started = time.monotonic()
|
||||
opener = build_opener(HttpsRedirects())
|
||||
request = Request(DOWNLOAD_URL, headers={"User-Agent": "RustProxy-build/0.26.3"})
|
||||
request = Request(DOWNLOAD_URL, headers={"User-Agent": "RustProxy-build/0.26.4"})
|
||||
with opener.open(request, timeout=30) as response, destination.open("wb") as output:
|
||||
length = response.headers.get("Content-Length")
|
||||
if length and int(length) > MAX_ARCHIVE:
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ def upgrade(source: Path, target: Path, *, stopped: bool, data_dir: Path | None
|
||||
for name in MANAGED:
|
||||
if (target / name).is_symlink():
|
||||
raise ValueError(f"Managed target {name} is a symlink; refusing to replace it.")
|
||||
backup = target / ".upgrade-backup" / ("before-0.26.3-" + uuid.uuid4().hex[:12])
|
||||
backup = target / ".upgrade-backup" / ("before-0.26.4-" + uuid.uuid4().hex[:12])
|
||||
backup.mkdir(parents=True, mode=0o700)
|
||||
installed: list[str] = []
|
||||
moved: list[str] = []
|
||||
@@ -84,7 +84,7 @@ def upgrade(source: Path, target: Path, *, stopped: bool, data_dir: Path | None
|
||||
remove_legacy = legacy.exists()
|
||||
if remove_legacy and (legacy.is_symlink() or not legacy.is_file()):
|
||||
raise ValueError("Obsolete renewal observation sidecar must be a regular file.")
|
||||
(backup / "upgrade.json").write_text(json.dumps({"from_directory":str(target), "to_version":"0.26.3", "managed_paths":MANAGED, "removed_obsolete_runtime_state": remove_legacy}, indent=2)+"\n", encoding="utf-8")
|
||||
(backup / "upgrade.json").write_text(json.dumps({"from_directory":str(target), "to_version":"0.26.4", "managed_paths":MANAGED, "removed_obsolete_runtime_state": remove_legacy}, indent=2)+"\n", encoding="utf-8")
|
||||
if remove_legacy:
|
||||
legacy.unlink()
|
||||
except BaseException:
|
||||
@@ -115,7 +115,7 @@ def main() -> None:
|
||||
backup = upgrade(Path(__file__).resolve().parents[1], args.target, stopped=args.stopped, data_dir=data_dir)
|
||||
except (OSError, ValueError) as error:
|
||||
parser.exit(2, f"Upgrade refused or rolled back: {error}\n")
|
||||
print(f"Installed RustProxy 0.26.3. Previous managed source files: {backup}")
|
||||
print(f"Installed RustProxy 0.26.4. Previous managed source files: {backup}")
|
||||
print("Preserved data, .env, .git, target and other unmanaged files. No database migration was run.")
|
||||
print(f"Next: cd {args.target}; ./dev.sh test; ./dev.sh")
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ need(web,
|
||||
'HeaderValue::from_static("no-store")',
|
||||
"builtin_error_response(StatusCode::FORBIDDEN, &method)",
|
||||
"empty_index_can_go_directly_to_directory_listing",
|
||||
"RustProxy <b>0.26.3</b>",
|
||||
"RustProxy <b>0.26.4</b>",
|
||||
"traversal_and_symlink_escape_are_not_served",
|
||||
"try_files_rewrites_cache_rules_headers_and_downloads_work",
|
||||
"precompressed_sidecars_keep_mime_validators_and_range_semantics",
|
||||
|
||||
@@ -399,6 +399,14 @@ pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
|
||||
"/docs/V0.25.1_AUDIT.md"
|
||||
)),
|
||||
),
|
||||
(
|
||||
"docs/V0.26.4_IMPLEMENTATION.md",
|
||||
"technical",
|
||||
include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/docs/V0.26.4_IMPLEMENTATION.md"
|
||||
)),
|
||||
),
|
||||
(
|
||||
"docs/V0.26.3_IMPLEMENTATION.md",
|
||||
"technical",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "RustProxy Administration API",
|
||||
"version": "0.26.3",
|
||||
"version": "0.26.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."
|
||||
},
|
||||
"servers": [
|
||||
|
||||
@@ -174,6 +174,22 @@ function uptime(value) {
|
||||
}
|
||||
function rateBytes(value) { value = Math.max(0, Number(value) || 0); if (value === 0) return "0.0 KiB"; value /= 1024; for (const unit of ["KiB", "MiB", "GiB", "TiB"]) { if (value < 1024 || unit === "TiB") { if (unit === "KiB" && value < 0.1) return "<0.1 KiB"; const digits = value < 10 ? 1 : 0; return `${Number(value.toFixed(digits))} ${unit}`; } value /= 1024; } }
|
||||
function el(tag, text, cls) { const e = document.createElement(tag); if (text !== undefined) e.textContent = text; if (cls) e.className = cls; return e; }
|
||||
function enhanceEnabledControl(input, hintKey = "entityEnableHint") {
|
||||
const label = input?.closest("label"); if (!label) return () => {};
|
||||
label.classList.add("vh-enable-control", "vh-wide");
|
||||
let hint = label.querySelector(".vh-enable-hint");
|
||||
if (!hint) { hint = el("small", undefined, "vh-enable-hint"); label.append(hint); }
|
||||
hint.dataset.i18n = hintKey; hint.textContent = t(hintKey);
|
||||
let state = label.querySelector(".vh-enable-state");
|
||||
if (!state) { state = el("strong", undefined, "vh-enable-state"); label.append(state); }
|
||||
const sync = () => {
|
||||
const key = input.checked ? "vhEnabled" : "vhDisabled";
|
||||
label.classList.toggle("is-disabled", !input.checked); label.classList.toggle("is-enabled", input.checked);
|
||||
state.dataset.i18n = key; state.textContent = t(key);
|
||||
};
|
||||
input.addEventListener("change", sync); sync(); return sync;
|
||||
}
|
||||
window.rustProxyEnableControl = enhanceEnabledControl;
|
||||
function action(label, fn) { const b = el("button", label, "quiet"); b.type = "button"; b.addEventListener("click", fn); return b; }
|
||||
function renderStatus(s) {
|
||||
const firstStatus = latestStatus === null; latestStatus = s; window.rustProxySettings?.render(); if (firstStatus) renderSettings();
|
||||
|
||||
@@ -92,15 +92,16 @@ function renderDashboardWidgetSettings() {
|
||||
const current = adminPreferences || ADMIN_APPEARANCE_DEFAULTS, order = normalizeDashboardWidgets(current.dashboard_widgets), hidden = new Set(normalizeHiddenDashboardWidgets(current.dashboard_hidden_widgets));
|
||||
const rows = order.map((id, index) => {
|
||||
const row = el("div", undefined, "dashboard-widget-setting"); row.dataset.dashboardWidgetSetting = id;
|
||||
const visible = document.createElement("input"); visible.type = "checkbox"; visible.checked = !hidden.has(id); visible.setAttribute("aria-label", `${t("dashboardWidgetVisible")}: ${dashboardWidgetLabel(id)}`);
|
||||
const label = el("span", dashboardWidgetLabel(id), "dashboard-widget-setting-label");
|
||||
const visible = !hidden.has(id);
|
||||
const toggle = el("button", undefined, "dashboard-widget-toggle"); toggle.type = "button"; toggle.classList.toggle("is-hidden", !visible); toggle.setAttribute("aria-pressed", String(visible)); toggle.setAttribute("aria-label", `${t("dashboardWidgetVisible")}: ${dashboardWidgetLabel(id)}`);
|
||||
toggle.append(el("span", undefined, "dashboard-widget-visibility"), el("span", dashboardWidgetLabel(id), "dashboard-widget-setting-label"));
|
||||
const actions = el("span", undefined, "dashboard-widget-setting-actions");
|
||||
const up = el("button", "↑", "quiet"); up.type = "button"; up.disabled = index === 0; up.setAttribute("aria-label", t("moveUp"));
|
||||
const down = el("button", "↓", "quiet"); down.type = "button"; down.disabled = index === order.length - 1; down.setAttribute("aria-label", t("moveDown"));
|
||||
visible.addEventListener("change", () => {
|
||||
const previous = [...hidden], next = new Set(hidden); if (visible.checked) next.delete(id); else next.add(id);
|
||||
toggle.addEventListener("click", () => {
|
||||
const previous = [...hidden], next = new Set(hidden); if (visible) next.add(id); else next.delete(id);
|
||||
if (adminPreferences) adminPreferences.dashboard_hidden_widgets = [...next]; applyDashboardLayout(order, [...next]); renderDashboardWidgetSettings();
|
||||
void saveAppearancePreference({ dashboard_hidden_widgets: [...next] }, visible, () => { if (adminPreferences) adminPreferences.dashboard_hidden_widgets = previous; applyDashboardLayout(order, previous); renderDashboardWidgetSettings(); });
|
||||
void saveAppearancePreference({ dashboard_hidden_widgets: [...next] }, toggle, () => { if (adminPreferences) adminPreferences.dashboard_hidden_widgets = previous; applyDashboardLayout(order, previous); renderDashboardWidgetSettings(); });
|
||||
});
|
||||
const move = delta => {
|
||||
const previous = [...order], next = [...order], at = next.indexOf(id), target = at + delta; if (at < 0 || target < 0 || target >= next.length) return;
|
||||
@@ -108,7 +109,7 @@ function renderDashboardWidgetSettings() {
|
||||
applyDashboardLayout(next, [...hidden]); renderDashboardWidgetSettings();
|
||||
void saveAppearancePreference({ dashboard_widgets: next }, null, () => { if (adminPreferences) adminPreferences.dashboard_widgets = previous; applyDashboardLayout(previous, [...hidden]); renderDashboardWidgetSettings(); });
|
||||
};
|
||||
up.addEventListener("click", () => move(-1)); down.addEventListener("click", () => move(1)); actions.append(up, down); row.append(visible, label, actions); return row;
|
||||
up.addEventListener("click", () => move(-1)); down.addEventListener("click", () => move(1)); actions.append(up, down); row.append(toggle, actions); return row;
|
||||
});
|
||||
list.replaceChildren(...rows);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ function renderSettings() {
|
||||
if (!settingsDirty.has("response-form")) {
|
||||
const server = config.server_header || {}, compression = config.compression || {}, diagnostics = config.diagnostic_headers || {};
|
||||
$("expose-request-id").checked = !!diagnostics.request_id;
|
||||
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.26.3";
|
||||
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.26.4";
|
||||
for (const name of ["gzip", "br", "zstd", "deflate"]) $(`compress-${name}`).checked = !!compression[name];
|
||||
$("compress-min").value = compression.min_bytes ?? 1024; syncServerMode();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const form = $("notfound-form"), dialog = $("notfound-dialog"), list = $("notfound-list");
|
||||
if (!form || !dialog || !list) return;
|
||||
let editing = null, revision = null, dirty = false, readBindings = () => [], readHosts = () => [];
|
||||
const syncEnabledControl = window.rustProxyEnableControl($("notfound-enabled"));
|
||||
const ERROR_STATUSES = [400, 401, 403, 404, 408, 413, 429, 500, 502, 503, 504];
|
||||
const BUILTIN_STATUSES = [401, 403, 404, 429, 503, 504];
|
||||
const STATUS_NAMES = { 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 408: "Request Timeout", 413: "Payload Too Large", 429: "Too Many Requests", 500: "Internal Server Error", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout" };
|
||||
@@ -38,11 +39,26 @@
|
||||
editing = item.id || null; revision = envelope?.revision ?? null; dirty = false;
|
||||
$("notfound-editing").textContent = editing || t("notfoundNewLabel");
|
||||
$("notfound-id").value = item.id || ""; $("notfound-id").disabled = Boolean(editing); $("notfound-name").value = item.name || "";
|
||||
$("notfound-enabled").checked = item.enabled !== false; $("notfound-http2").checked = item.http2_enabled !== false; $("notfound-http3").checked = item.http3_enabled !== false;
|
||||
$("notfound-enabled").checked = item.enabled !== false; syncEnabledControl(); $("notfound-http2").checked = item.http2_enabled !== false; $("notfound-http3").checked = item.http3_enabled !== false;
|
||||
const hostHolder = $("notfound-hosts-editor"); hostHolder.replaceChildren(); readHosts = window.rustProxyIntegrations.hostnames(hostHolder, item.frontend?.hosts || [], { labelKey: "notfoundHosts", helpKey: "vhHostsHelp", requiredKey: "notfoundHostsRequired", onChange: changed });
|
||||
listenerChoices(item.frontend?.listener_ids || []); certificateChoices(item.certificate_id);
|
||||
$("notfound-mode").value = item.page?.kind || "welcome"; populateStatuses(item.page?.status ?? 404); $("notfound-html").value = item.page?.html || ""; syncMode(); dialog.showModal();
|
||||
}
|
||||
function suspendDraft() {
|
||||
const state = {
|
||||
editing, revision, label: $("notfound-editing").textContent, controls: window.rustProxyDrafts.captureControls(form),
|
||||
hosts: window.rustProxyDrafts.takeChildren($("notfound-hosts-editor")), listeners: window.rustProxyDrafts.takeChildren($("notfound-listeners")),
|
||||
readBindings, readHosts
|
||||
};
|
||||
const title = value("notfound-id") || editing || t("draftUntitled"); editing = null; revision = null; dirty = false; dialog.close(); return { title, state };
|
||||
}
|
||||
function restoreDraft(state) {
|
||||
window.rustProxyDrafts.restoreChildren($("notfound-hosts-editor"), state.hosts); window.rustProxyDrafts.restoreChildren($("notfound-listeners"), state.listeners);
|
||||
readBindings = state.readBindings; readHosts = state.readHosts; editing = state.editing; revision = state.revision; dirty = true; $("notfound-editing").textContent = state.label;
|
||||
window.rustProxyDrafts.restoreControls(form, state.controls); syncEnabledControl(); syncMode(); if (!dialog.open) dialog.showModal();
|
||||
}
|
||||
function closeEditor() { if (dirty) return window.rustProxyDrafts.dock("notfound"); if (dialog.open) dialog.close(); return true; }
|
||||
|
||||
function collect() {
|
||||
const id = value("notfound-id"), name = value("notfound-name") || id, hosts = readHosts(), listener_ids = readBindings(), kind = value("notfound-mode"), status = Number(value("notfound-status")), html = $("notfound-html").value;
|
||||
if (!listener_ids.length) throw new Error(t("notfoundListenersRequired"));
|
||||
@@ -57,14 +73,15 @@
|
||||
list.replaceChildren(...entries.map(item => {
|
||||
const card = el("article", undefined, "vhost-card redirect-card"), head = el("div", undefined, "vhost-card-head"), identity = el("div", undefined, "vhost-card-identity"), title = el("div", undefined, "vhost-title-line");
|
||||
title.append(el("strong", item.name || item.id, "entity-title")); if (item.enabled === false) title.append(el("span", t("vhDisabled"), "pill")); identity.append(title, el("div", (item.frontend?.hosts || []).join(" · "), "vhost-hosts")); head.append(identity);
|
||||
const actions = el("div", undefined, "entity-actions"); actions.append(action(t("edit"), () => load(structuredClone(item))), action(t("remove"), async () => { if (busy || !(await appConfirm(t("confirmDelete"), { danger: true }))) return; const next = (envelope.config.not_found_hosts || []).filter(value => value.id !== item.id); if (await saveSection("not_found_hosts", next)) { if (editing === item.id) dialog.close(); render(); } })); head.append(actions); card.append(head);
|
||||
const actions = el("div", undefined, "entity-actions"); actions.append(action(t("edit"), () => { window.rustProxyDrafts.prepare(); load(structuredClone(item)); }), action(t("remove"), async () => { if (busy || !(await appConfirm(t("confirmDelete"), { danger: true }))) return; const next = (envelope.config.not_found_hosts || []).filter(value => value.id !== item.id); if (await saveSection("not_found_hosts", next)) { if (editing === item.id) dialog.close(); render(); } })); head.append(actions); card.append(head);
|
||||
const detail = el("div", undefined, "vhost-detail-strip"); for (const [key, text] of [["notfoundStatus", String(item.page?.status ?? 404)], ["notfoundMode", t(`notfoundMode${(item.page?.kind || "welcome").replace(/^./, c => c.toUpperCase())}`)], ["redirectListeners", (item.frontend?.listener_ids || []).join(", ")], ["vhCertificate", item.certificate_id || t("vhCertificateDefault")]]) { const node = el("div", undefined, "vhost-detail"); node.append(el("span", t(key), "small-label"), el("b", text)); detail.append(node); } card.append(detail); return card;
|
||||
}));
|
||||
if (!entries.length) list.append(el("p", t(query ? "notfoundSearchEmpty" : "notfoundEmpty"), "fine"));
|
||||
}
|
||||
form.addEventListener("input", changed); form.addEventListener("change", changed); $("notfound-mode").addEventListener("change", syncMode); $("notfound-status").addEventListener("change", syncMode);
|
||||
form.addEventListener("submit", async event => { event.preventDefault(); if (!envelope || busy) return; const invalid = [...form.elements].find(input => input.willValidate && !input.checkValidity()); if (invalid) { invalid.reportValidity(); return; } if (revision !== envelope.revision) { toast(t("stale"), true); return; } try { const item = collect(), next = structuredClone(envelope.config.not_found_hosts || []); if (!editing && next.some(value => value.id === item.id)) throw new Error(t("notfoundDuplicate")); const index = next.findIndex(value => value.id === editing); if (index < 0) next.push(item); else next[index] = item; if (await saveSection("not_found_hosts", next)) { dirty = false; editing = item.id; dialog.close(); render(); } } catch (error) { toast(error.message, true); } });
|
||||
$("notfound-new").addEventListener("click", async () => { if (await allowDiscard()) load(); }); $("notfound-search").addEventListener("input", render); $("notfound-close").addEventListener("click", async () => { if (await allowDiscard()) dialog.close(); }); $("notfound-reset").addEventListener("click", async () => { if (await allowDiscard()) load(editing ? structuredClone((envelope?.config.not_found_hosts || []).find(item => item.id === editing) || defaults()) : defaults()); }); dialog.addEventListener("cancel", event => { if (!dirty) return; event.preventDefault(); void allowDiscard().then(ok => { if (ok) dialog.close(); }); });
|
||||
$("notfound-new").addEventListener("click", () => { window.rustProxyDrafts.prepare(); load(); }); $("notfound-search").addEventListener("input", render); $("notfound-close").addEventListener("click", closeEditor); $("notfound-reset").addEventListener("click", async () => { if (await allowDiscard()) load(editing ? structuredClone((envelope?.config.not_found_hosts || []).find(item => item.id === editing) || defaults()) : defaults()); }); dialog.addEventListener("cancel", event => { event.preventDefault(); closeEditor(); });
|
||||
window.rustProxyDrafts.register("notfound", { labelKey: "notfound", isOpen: () => dialog.open, isDirty: () => dirty, suspend: suspendDraft, restore: restoreDraft, close: () => { dirty = false; if (dialog.open) dialog.close(); } });
|
||||
window.rustProxyNotFound = { render, reset() { dialog.close(); editing = null; revision = null; dirty = false; list.replaceChildren(); $("notfound-search").value = ""; } };
|
||||
if (envelope) render();
|
||||
})();
|
||||
@@ -1877,7 +1877,7 @@
|
||||
<option data-i18n="serverCustom" value="custom">Replace / add custom value</option>
|
||||
</select>
|
||||
<label data-i18n="serverValue" for="server-value">Custom Server value</label><input
|
||||
id="server-value" maxlength="256" placeholder="RustProxy/v0.26.3"
|
||||
id="server-value" maxlength="256" placeholder="RustProxy/v0.26.4"
|
||||
spellcheck="false" />
|
||||
<h3 class="subheading" data-i18n="diagnosticHeaders">Diagnostic headers</h3>
|
||||
<div class="switch-grid">
|
||||
@@ -2676,7 +2676,7 @@
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="page-footer"><span>RustProxy <b>0.26.3</b></span><span id="footer-status">API - /
|
||||
<footer class="page-footer"><span>RustProxy <b>0.26.4</b></span><span id="footer-status">API - /
|
||||
dropped: 0</span></footer>
|
||||
</div>
|
||||
</main>
|
||||
@@ -3387,7 +3387,7 @@ max_execution_time = 30</code></pre>
|
||||
<article class="card about-card">
|
||||
<div class="about-product">
|
||||
<span aria-hidden="true" class="brand-mark">r<span>/</span>p</span>
|
||||
<div><span class="small-label">RustProxy</span><strong>0.26.3</strong></div>
|
||||
<div><span class="small-label">RustProxy</span><strong>0.26.4</strong></div>
|
||||
</div>
|
||||
<dl class="about-meta">
|
||||
<div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const form = $("redirect-form"), dialog = $("redirect-dialog"), list = $("redirect-list"), rules = $("redirect-rules");
|
||||
if (!form || !dialog || !list || !rules) return;
|
||||
let editing = null, revision = null, dirty = false, readRedirectBindings = () => [], readRedirectHosts = () => [];
|
||||
const syncEnabledControl = window.rustProxyEnableControl($("redirect-enabled"));
|
||||
const yes = id => $(id).checked;
|
||||
const value = id => $(id).value.trim();
|
||||
const option = (value, key) => { const node = el("option", t(key)); node.value = value; return node; };
|
||||
@@ -65,7 +66,7 @@
|
||||
$("redirect-id").value = item.id || ""; $("redirect-id").disabled = Boolean(editing);
|
||||
$("redirect-name").value = item.name || "";
|
||||
const hostHolder = $("redirect-hosts-editor"); hostHolder.replaceChildren(); readRedirectHosts = window.rustProxyIntegrations.hostnames(hostHolder, item.frontend?.hosts || [], { labelKey: "redirectHosts", helpKey: "vhHostsHelp", requiredKey: "redirectHostsRequired", onChange: changed });
|
||||
$("redirect-enabled").checked = item.enabled !== false; $("redirect-http2").checked = item.http2_enabled !== false; $("redirect-http3").checked = item.http3_enabled !== false;
|
||||
$("redirect-enabled").checked = item.enabled !== false; syncEnabledControl(); $("redirect-http2").checked = item.http2_enabled !== false; $("redirect-http3").checked = item.http3_enabled !== false;
|
||||
listenerChoices(item.frontend?.listener_ids || []); certificateChoices(item.certificate_id);
|
||||
rules.replaceChildren(); for (const [i, rule] of (item.rules?.length ? item.rules : defaults().rules).entries()) addRule(rule, i);
|
||||
dialog.showModal();
|
||||
@@ -85,7 +86,7 @@
|
||||
function restoreDraft(state) {
|
||||
window.rustProxyDrafts.restoreChildren($("redirect-hosts-editor"), state.hosts); window.rustProxyDrafts.restoreChildren($("redirect-listeners"), state.listeners); window.rustProxyDrafts.restoreChildren(rules, state.rules);
|
||||
readRedirectBindings = state.readRedirectBindings; readRedirectHosts = state.readRedirectHosts; editing = state.editing; revision = state.revision; dirty = true; $("redirect-editing").textContent = state.label;
|
||||
window.rustProxyDrafts.restoreControls(form, state.controls); if (!dialog.open) dialog.showModal();
|
||||
window.rustProxyDrafts.restoreControls(form, state.controls); syncEnabledControl(); if (!dialog.open) dialog.showModal();
|
||||
}
|
||||
function closeEditor() { if (dirty) return window.rustProxyDrafts.dock("redirect"); if (dialog.open) dialog.close(); return true; }
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
field(g, "id", "streamId", value.id, "text", { required: true, maxLength: 64, pattern: "[A-Za-z0-9_-]{1,64}", readOnly: Boolean(editing) });
|
||||
field(g, "name", "streamName", value.name, "text", { maxLength: 96 });
|
||||
const protocol = field(g, "protocol", "streamProtocol", value.protocol || "tcp", "select", { values: [["tcp", "streamTcp"], ["udp", "streamUdp"]] });
|
||||
field(g, "enabled", "vhEnabled", value.enabled !== false, "checkbox");
|
||||
const enabledControl = field(g, "enabled", "vhEnabled", value.enabled !== false, "checkbox");
|
||||
window.rustProxyEnableControl(enabledControl);
|
||||
note(g, "streamsHelp");
|
||||
|
||||
g = group("streamNetwork", "network");
|
||||
|
||||
+65
-16
@@ -8788,43 +8788,75 @@ button.config-history-day.is-selected {
|
||||
|
||||
.dashboard-widget-list {
|
||||
display: grid;
|
||||
gap: 0;
|
||||
margin-top: 4px;
|
||||
border: 1px solid var(--line)
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 6px;
|
||||
margin-top: 4px
|
||||
}
|
||||
|
||||
.dashboard-widget-setting {
|
||||
display: grid;
|
||||
grid-template-columns: 22px minmax(0, 1fr) auto;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
min-height: 34px;
|
||||
padding: 4px 6px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 3px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--panel)
|
||||
}
|
||||
|
||||
.dashboard-widget-setting:last-child {
|
||||
border-bottom: 0;
|
||||
.dashboard-widget-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
min-height: 26px;
|
||||
padding: 3px 6px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.dashboard-widget-toggle:hover {
|
||||
background: var(--panel2)
|
||||
}
|
||||
|
||||
.dashboard-widget-toggle.is-hidden {
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
.dashboard-widget-visibility {
|
||||
flex: 0 0 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 1px solid var(--green);
|
||||
border-radius: 50%;
|
||||
background: var(--green)
|
||||
}
|
||||
|
||||
.dashboard-widget-toggle.is-hidden .dashboard-widget-visibility {
|
||||
border-color: var(--muted);
|
||||
background: transparent
|
||||
}
|
||||
|
||||
.dashboard-widget-setting-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 11px;
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
.dashboard-widget-setting-actions {
|
||||
display: inline-flex;
|
||||
gap: 3px;
|
||||
gap: 2px
|
||||
}
|
||||
|
||||
.dashboard-widget-setting-actions button {
|
||||
min-width: 28px;
|
||||
min-height: 26px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 0;
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 0
|
||||
}
|
||||
|
||||
.appearance-actions {
|
||||
@@ -9320,4 +9352,21 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
stroke-width: var(--chart-line-width);
|
||||
stroke-linecap: var(--chart-linecap);
|
||||
stroke-linejoin: var(--chart-linejoin);
|
||||
}
|
||||
}
|
||||
|
||||
/* Toolbar filters stay aligned with their tallest action button. */
|
||||
.backend-table-tools input,
|
||||
.backend-table-tools select,
|
||||
.vhost-list-tools input[type="search"],
|
||||
.docker-toolbar input[type="search"],
|
||||
.log-tools input:not([type="checkbox"]):not([type="radio"]),
|
||||
.log-tools select,
|
||||
.file-logs-filter input,
|
||||
.file-logs-filter select,
|
||||
#panel-settings .settings-filter input {
|
||||
box-sizing: border-box;
|
||||
height: var(--button-min-height);
|
||||
min-height: var(--button-min-height);
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px
|
||||
}
|
||||
@@ -39,10 +39,8 @@
|
||||
container.replaceChildren(); $("vhost-editing").textContent = t(editing ? "vhEditLabel" : "vhNewLabel");
|
||||
let g = group("vhEditor"); field(g, "id", "vhId", value.id, "text", { required: true, maxLength: 64, pattern: "[A-Za-z0-9_-]{1,64}" }); if (editing) note(g, "vhIdRenameHelp");
|
||||
readHosts = hostTagEditor(g, value.frontend?.hosts || []);
|
||||
const enabledControl = field(g, "enabled", "vhEnabled", value.enabled !== false, "checkbox"), enabledLabel = enabledControl.closest("label"), enabledState = el("strong", "", "vh-enable-state");
|
||||
if (enabledLabel) { enabledLabel.classList.add("vh-enable-control", "vh-wide"); enabledLabel.append(text("small", "vhEnableHint", "vh-enable-hint"), enabledState); }
|
||||
const syncEnabledState = () => { const key = enabledControl.checked ? "vhEnabled" : "vhDisabled"; enabledLabel?.classList.toggle("is-disabled", !enabledControl.checked); enabledLabel?.classList.toggle("is-enabled", enabledControl.checked); enabledState.dataset.i18n = key; enabledState.textContent = t(key); };
|
||||
enabledControl.addEventListener("change", syncEnabledState); syncEnabledState();
|
||||
const enabledControl = field(g, "enabled", "vhEnabled", value.enabled !== false, "checkbox");
|
||||
window.rustProxyEnableControl(enabledControl, "vhEnableHint");
|
||||
const mainSource = field(g, "source-type", "sourceType", value.source?.type || "reverse_proxy", "select", { values: [["reverse_proxy", "sourceReverseProxy"], ["web_server", "sourceWebServer"]] });
|
||||
const mainWebService = field(g, "source-web-service", "webService", value.source?.web_service_id || "", "select"); fillWebServices(mainWebService, value.source?.web_service_id || "");
|
||||
const syncMainSource = () => { const web = mainSource.value === "web_server"; mainWebService.disabled = !web; mainWebService.required = web; }; mainSource.addEventListener("change", syncMainSource); syncMainSource();
|
||||
|
||||
@@ -347,7 +347,14 @@ pub async fn health_loop(app: Weak<crate::runtime::App>, shutdown: CancellationT
|
||||
let snapshot = app.config.load_full();
|
||||
let members: Vec<_> = snapshot.pools.iter().flat_map(|pool|
|
||||
pool.members.iter().map(move |member| (member, &pool.settings.health_check))).collect();
|
||||
for (member, _) in &members { member.state.sample_traffic(); }
|
||||
for (member, _) in &members {
|
||||
member.state.sample_traffic();
|
||||
// Time-based pool recycling must not depend on receiving a
|
||||
// request; otherwise idle backends can stay on generation 0
|
||||
// indefinitely after the configured recycle age elapses.
|
||||
member.backend.client.maintain_rotation();
|
||||
member.backend.websocket_client.maintain_rotation();
|
||||
}
|
||||
// Rotate the bounded probe window: slow first members must not
|
||||
// starve later pools when all intervals equal their timeouts.
|
||||
let start = cursor;
|
||||
|
||||
@@ -41,7 +41,7 @@ use zeroize::Zeroizing;
|
||||
#[command(
|
||||
name = "rust-proxy",
|
||||
version,
|
||||
about = "RustProxy - independent Rust reverse proxy and static Web Server 0.26.3"
|
||||
about = "RustProxy - independent Rust reverse proxy and static Web Server 0.26.4"
|
||||
)]
|
||||
struct Cli {
|
||||
#[arg(
|
||||
|
||||
+1
-1
@@ -239,7 +239,7 @@ async fn live_logs_command(dir: PathBuf, addr: SocketAddr) -> Result<()> {
|
||||
}
|
||||
});
|
||||
let interactive = io::stdin().is_terminal() && io::stdout().is_terminal();
|
||||
println!("RustProxy live logs 0.26.3 - all vhosts. Type 'help' for interactive filters; Ctrl-C or 'quit' exits.");
|
||||
println!("RustProxy live logs 0.26.4 - all vhosts. Type 'help' for interactive filters; Ctrl-C or 'quit' exits.");
|
||||
if interactive { print!("> "); io::stdout().flush()?; }
|
||||
let mut filters = LiveLogFilters::default();
|
||||
let mut history = std::collections::BTreeMap::<u64, serde_json::Value>::new();
|
||||
|
||||
+35
-5
@@ -103,6 +103,11 @@ impl ProxyClient {
|
||||
pub fn rotation_stats(&self) -> ClientRotationStats {
|
||||
self.inner.stats()
|
||||
}
|
||||
|
||||
/// Recycle an aged connection pool even when no user request arrives.
|
||||
pub(crate) fn maintain_rotation(&self) {
|
||||
self.inner.maintain_rotation();
|
||||
}
|
||||
}
|
||||
|
||||
impl RotatingClient {
|
||||
@@ -136,12 +141,36 @@ impl RotatingClient {
|
||||
.pool_recycle_seconds
|
||||
.saturating_mul(1_000);
|
||||
if request_due || age_due {
|
||||
self.rotate(observed_generation, if request_due { "requests" } else { "age" });
|
||||
self.rotate(
|
||||
observed_generation,
|
||||
if request_due { "requests" } else { "age" },
|
||||
true,
|
||||
);
|
||||
}
|
||||
self.current.load_full()
|
||||
}
|
||||
|
||||
fn rotate(&self, observed_generation: u64, reason: &'static str) {
|
||||
fn maintain_rotation(&self) {
|
||||
self.maintain_rotation_at(monotonic_millis());
|
||||
}
|
||||
|
||||
fn maintain_rotation_at(&self, now_ms: u64) {
|
||||
let recycle_seconds = self.factory.keepalive.pool_recycle_seconds;
|
||||
if recycle_seconds == 0 {
|
||||
return;
|
||||
}
|
||||
let age_ms = now_ms.saturating_sub(self.generation_started_ms.load(Ordering::Relaxed));
|
||||
if age_ms >= recycle_seconds.saturating_mul(1_000) {
|
||||
self.rotate(self.generation.load(Ordering::Acquire), "age", false);
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate(
|
||||
&self,
|
||||
observed_generation: u64,
|
||||
reason: &'static str,
|
||||
triggering_request: bool,
|
||||
) {
|
||||
if self.generation.load(Ordering::Acquire) != observed_generation
|
||||
|| self
|
||||
.rotating
|
||||
@@ -159,9 +188,10 @@ impl RotatingClient {
|
||||
Ok(client) => {
|
||||
let next_generation = observed_generation.saturating_add(1);
|
||||
self.current.store(Arc::new(client));
|
||||
// The request that triggered the swap is sent through the new
|
||||
// generation after rotate() returns, so count it there.
|
||||
self.generation_requests.store(1, Ordering::Relaxed);
|
||||
// Request-driven rotation sends the triggering request through
|
||||
// the new generation. Maintenance-driven rotation happens idle.
|
||||
self.generation_requests
|
||||
.store(if triggering_request { 1 } else { 0 }, Ordering::Relaxed);
|
||||
self.generation_started_ms
|
||||
.store(monotonic_millis(), Ordering::Relaxed);
|
||||
self.generation.store(next_generation, Ordering::Release);
|
||||
|
||||
@@ -34,6 +34,33 @@ mod tests {
|
||||
assert_eq!(stats.generation, 1);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn generation_recycles_after_age_without_request() {
|
||||
let backend = backend("rotation-age-test", 18083);
|
||||
let mut keepalive = crate::balance::Keepalive::default();
|
||||
keepalive.pool_recycle_requests = 0;
|
||||
keepalive.pool_recycle_seconds = 1;
|
||||
let client = client_with_keepalive(
|
||||
&backend,
|
||||
AddressPolicy::new(&backend).unwrap(),
|
||||
false,
|
||||
&keepalive,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
client
|
||||
.inner
|
||||
.generation_started_ms
|
||||
.store(10, std::sync::atomic::Ordering::Relaxed);
|
||||
client.inner.maintain_rotation_at(2_010);
|
||||
|
||||
let stats = client.rotation_stats();
|
||||
assert_eq!(stats.rotations, 1);
|
||||
assert_eq!(stats.generation, 1);
|
||||
assert_eq!(stats.generation_requests, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generation_recycling_can_be_disabled() {
|
||||
let backend = backend("rotation-off", 18082);
|
||||
|
||||
@@ -159,7 +159,7 @@ pub(super) fn fastcgi_params(
|
||||
if context.tls { "on" } else { "off" }.into(),
|
||||
);
|
||||
params.insert("GATEWAY_INTERFACE".into(), "CGI/1.1".into());
|
||||
params.insert("SERVER_SOFTWARE".into(), "RustProxy/0.26.3".into());
|
||||
params.insert("SERVER_SOFTWARE".into(), "RustProxy/0.26.4".into());
|
||||
params.insert("REDIRECT_STATUS".into(), "200".into());
|
||||
if let Some(value) = headers
|
||||
.get(CONTENT_TYPE)
|
||||
|
||||
@@ -185,7 +185,7 @@ pub(super) fn directory_listing(path: &str, entries: &[DirectoryEntry]) -> Vec<u
|
||||
"<a class=\"{class}\" href=\"{href}\"><span class=\"type\">{type_label}</span><span class=\"name\">{label}{slash}</span><span class=\"kind\">{kind}</span></a>"
|
||||
));
|
||||
}
|
||||
html.push_str(r#"</div></section><footer class="page-footer"><span>RustProxy <b>0.26.3</b></span><span>Web Server · Directory listing</span></footer></main></body></html>"#);
|
||||
html.push_str(r#"</div></section><footer class="page-footer"><span>RustProxy <b>0.26.4</b></span><span>Web Server · Directory listing</span></footer></main></body></html>"#);
|
||||
html.into_bytes()
|
||||
}
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ async fn empty_index_can_go_directly_to_directory_listing() {
|
||||
let body = std::str::from_utf8(&body).unwrap();
|
||||
assert!(body.contains("file.txt"));
|
||||
assert!(body.contains("DIRECTORY LISTING"));
|
||||
assert!(body.contains("RustProxy <b>0.26.3</b>"));
|
||||
assert!(body.contains("RustProxy <b>0.26.4</b>"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in new issue
Block a user