v0.36.5
This commit is contained in:
1 parent
34490fd5fc
commit
6ef01212ec
21 files changed
+204
-77
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# RustProxy source and documentation maintenance
|
||||
|
||||
Current source version: **0.36.4**. The Cargo package and binary are named `rust-proxy`.
|
||||
Current source version: **0.36.5**. The Cargo package and binary are named `rust-proxy`.
|
||||
|
||||
Before editing, read [README.md](README.md), the relevant [documentation guide](docs/README.md) and the actual code and tests. Historical release experiments are not current specifications. Use the bundled `src/admin/openapi.json` for API routes and payloads; the source implementation is authoritative for runtime behavior.
|
||||
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
Release history focused on **features, settings, default behavior, compatibility and fixes**. Changes to internal code structure and preliminary performance experiments are intentionally omitted. Historical descriptions reflect their respective releases; consult the current documentation for the settings available in the latest release.
|
||||
|
||||
## 0.36.5 — automatic certificate choice and clearer host conflicts
|
||||
|
||||
- **Certificate auto-selection:** when creating a vhost, redirect or 404 host, RustProxy picks the stored certificate that covers every host name you enter (an exact name beats a wildcard, the narrowest certificate wins). A hint names the chosen certificate and the selector still lets you pick another one or the global default. Existing entries are never changed.
|
||||
- **Clearer host conflicts:** the "overlapping frontend bindings for hostname …" error now names the entry that already uses the host (`vhost`, `redirect` or `404 host` plus its ID) and the conflicting listener address.
|
||||
- **Clearer validation errors:** duplicate or invalid IDs, repeated hostnames, missing hostnames or listeners, invalid location IDs, regular expressions, exact paths and duplicate location matchers now say which value or location is wrong and how to fix it.
|
||||
|
||||
## 0.36.4 — cache readability and editor UX polish
|
||||
|
||||
- **Cache → Domains and effectiveness:** decision counters are one compact line (`name count`, top three, the rest in a `+N` tooltip) instead of a wall of chips; the rest of the table keeps its 0.36.3 look.
|
||||
|
||||
Generated
+1
-1
@@ -1943,7 +1943,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-proxy"
|
||||
version = "0.36.4"
|
||||
version = "0.36.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rust-proxy"
|
||||
version = "0.36.4"
|
||||
version = "0.36.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.99.1"
|
||||
description = "RustProxy - independent reverse proxy with local administration"
|
||||
|
||||
+20
-20
@@ -1,10 +1,10 @@
|
||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/.dockerignore
|
||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/.env.example
|
||||
e76e688874828e3743d03693b1d2872b9633b4db09909673786caf30f7cfe489 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/.gitignore
|
||||
8007bd50f172afa9030b44e46d7c7030af387466c7c31c365dd9521a0171a58c /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/AGENTS.md
|
||||
e4a89126bee0eb26f4e598f4bd6ceb38b649415afd21781071aed918948c63e3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/CHANGELOG.md
|
||||
ec5374a1dfe13f26d45d55e94a6e8d53a9ae9aea31d4dea8599ba07010da0a2d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/Cargo.lock
|
||||
a3656e331f57811d45ab37ce4c48c069e92fb5aa9b756ffc07ce993d202d11ff /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/Cargo.toml
|
||||
875fa17d64e0cf8f097cd4635ca801686c256d556680a39a9259dcdb1155ed6c /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/AGENTS.md
|
||||
a544bba6a7558f7aa780ddd3e1c9e67aae58e371dddb99e8b277455f2d4cb46b /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/CHANGELOG.md
|
||||
eae2405a396e300ca4f2997a36239648b74acebf9e724b8c8d095c11969e3fd9 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/Cargo.lock
|
||||
202e5377c7875ef538d420ccb563c26d4cb010dbcd49ce80ca1a1555d267b038 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/Cargo.toml
|
||||
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/Dockerfile
|
||||
0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/LICENSE
|
||||
82d1dee2fc68158f0ea73259151461e64c8b18d807cd1fecfb1ed99dc30cf5f4 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/README.md
|
||||
@@ -88,12 +88,12 @@ de6550ab8f48d556d752c252212c38ba65491fd224f972c10fbe552f91ed7520 /sessions/rcw-
|
||||
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/.env.routeros.example
|
||||
11aa102de1089ecc375f20c4bd5420fcfd3b2ef23fe0c8bdf04953fcfaf40ef0 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/Dockerfile.routeros
|
||||
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/Dockerfile.routeros.dockerignore
|
||||
97b7819edfaed798a9eb51f8feed74e68c45f697e85420da8c3444e328106aa1 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/README.md
|
||||
8545b871df9ffb6b204f1cf30ba33fb11bd2335287982d464fa6ccd480f7c869 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/README.md
|
||||
3f092a9ab80221eddd67be4d1a776acbc6e04c1eb6011b588280c28e83b2582f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/build-routeros-oci.sh
|
||||
3f267cafa16006e5456ade78ee2d5fae02cc7ce747839523068ffd8d7c98798a /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/integrations/routeros-oci-build/docker-entrypoint.sh
|
||||
13189fa546c3488dddd8efc80c47f6da5241e5fc28b79cbfe795414ea05c7a4f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/langs/README.md
|
||||
d86c6082998e8c68100b16b0550454df577452fe2ff37d487b25957204463ede /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/langs/en_US.json
|
||||
96d6cd26c0d6b425b2fc4952cc0b0b51c749ce46e7471fbb794000431a34b51a /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/langs/pl_pl.json
|
||||
87d87fb37d3d08ae98b365db24f2fcc64b46b21c3ac5cafdad7720c16d0f3e14 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/langs/en_US.json
|
||||
20989ca82958acaa6859af792176b653008ad962cd3de31a61a9f36000e262c0 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/langs/pl_pl.json
|
||||
9664543e4d03422788a65e42f968c1f026c2235cb5be6fc3e0ad318997077538 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/make_zip.py
|
||||
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/migrations/0002_admin.sql
|
||||
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/migrations/0003_certificate_renewals.sql
|
||||
@@ -165,7 +165,7 @@ d58a2ed83319176de4e66790ed292447c9497ae031464ec55cfe27174acb639a /sessions/rcw-
|
||||
77b8c05645969834cc8a58e249a7b60a57d6c47ad1c5cd92ffa5e4a9b5a8e73c /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/file_logs_query.rs
|
||||
743bb7436f77e04015328a0e7e7d425e43ef8aa8d61467bde157e462c1dcdc2e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/logging_api.rs
|
||||
243f4a9c3fa99781e19c3952354d2632c0ce65e1dafac729567cbe3c177f94cb /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/mod.rs
|
||||
28fc4617820aca52a552534184b74a07c4ef09a259ac45dc754b44934cb20bef /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/openapi.json
|
||||
73aeded6ca5a7162000e71c4fe4e210d2e9810d8b9af1877e86e43f0aa896d39 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/openapi.json
|
||||
f7a356109786635dd58cba590c71c796dbf36b47f25845190c75ee5c6a9fcb86 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/operations.rs
|
||||
257d93a29d0bda96462bcf695aba8d0ec0f9412c379e066ec46ae8ee034e3a53 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/panel_assets.rs
|
||||
f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/panel_content.rs
|
||||
@@ -189,7 +189,7 @@ faa9735c2a02746b2954bc4955eb609f94afebb685cd5521d2629640cea2777a /sessions/rcw-
|
||||
aed3aea4eef0011c6b51d39736782e7d1ec52662e422f40e0c7b9ddfc19d0378 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/certificate-inspection.js
|
||||
f6b0533ffd6d0e6471519120c440619a7fb4b255854ba3dd23d81cb35cd21afb /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/chart-scales.js
|
||||
e5b39b5592ba81292571033f26934ac0cd42bcdca7395e91094973cecb3eeaf8 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/config-history.js
|
||||
2e4a18d1aebe9e35255ed15ba2fa17015bf5d7f3996867394aec756f2b7c67da /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/core.js
|
||||
446131e27946dc32df3524e80074f99accc394c004bcfa6cf9770c1b698a0e03 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/core.js
|
||||
d695ec90fb5c98c27d7ba1d2bebf54f9840985fc530efcec3fcaf48d74b36c6b /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/dashboard-inline-layout.js
|
||||
e4238322a985d3e8b7f2de94b7af31e1c718775590b2c7b3428b878cd43337b7 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/dashboard-layout-editor.js
|
||||
a184c50e1ca6bb5a14954e31b15f09ff9978fb939064ca724851bbb4e661dc56 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/app/dashboard-presets.js
|
||||
@@ -217,7 +217,7 @@ ec3ff1b7824a7f03ee6d6b7ae9b6e0cbdb300e4891d44f982b6c04b3870f5c2b /sessions/rcw-
|
||||
2d703473f7b751b587c32af6954c838a3160e9efa448236b6abe291832486b72 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/40-editors.css
|
||||
87df062d7478176641364c07de396ca988a37b1a8bcb892b53cc578fe8471850 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/50-settings.css
|
||||
eed482c4b7dd247682c135fac132bb66e501511423ca53d50e9a79f4314cbac5 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/60-monitoring.css
|
||||
d9ea4824dca9e355521de8d49f0dbf5fd23fa9c0d9a91fb8719498858cb1a80e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/70-responsive.css
|
||||
83cb48e2e4748bff2260dc7d9d84e922781d35812eecb4d2e34831cebb21d015 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/70-responsive.css
|
||||
8009afdb04d5170687816730cc930a22fead1fe0248835ca7e8de81d6907395d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/README.md
|
||||
8241bd9489dfed082863948829a44a49bf0a8e9cd63c55809655109e393e0918 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/css/manifest.json
|
||||
ee51ab0912e0ade6a3e1c2ea6f533bb2f65a6d3e6ec9087e25b9f0df009c19b2 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/dashboard-defaults.json
|
||||
@@ -243,7 +243,7 @@ e6c4d91d1542d0a5b47bb8736f9539d43a6713df5a473b635519d5b7eac2eb9c /sessions/rcw-
|
||||
003d382cc8dd69ad7237679632c04a9389366bb71dd7dca8ee64ee06614aa9a5 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/error_auth_provider.html
|
||||
0a5fff35555153f59d4346933030a67b6e1a8768cee7a47cfa2fcf1a2c564f63 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/favicon.svg
|
||||
7cbdef9192a9dee1f8eac42b85f52b3190f7f6efae50004aa7ad116a910e4036 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/file-logs.js
|
||||
cc9bcae4d2fe37f64cb2dfdd4d91d59bdf6fee27049896f33b899555b30e7f2c /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/host404.js
|
||||
14377f09e0fd04cca00c4ca58c3a243b10ddb43dcbd0faf58a57209303d73d8e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/host404.js
|
||||
0a7bd0afcc0f65fced7dcfc1dfa1469a8ed6051af274701c23f94885d8e9107d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/index.html
|
||||
7979c490b7fcfcd8f5e52e99fcf7757d78509337a625341c98f62c6da50da374 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/integrations.js
|
||||
7a12f020fc587205694978ab41b660e9a80487d413fbf6e34fc525eabc58308f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/login-keys.json
|
||||
@@ -253,14 +253,14 @@ a74b0d8d9a5aedb4d1ee4b7f7805693ee3cf2152054e35a310bd72e81ea1ca4f /sessions/rcw-
|
||||
1f3d10e389c9c20e6eb019509fa6e82b50c6b43bbf988c1210a4d42bb73985d0 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/partials/overview-charts.html
|
||||
32b3f9b3ce9d1de928d2f23265d09b65a135fd045d1d24b98e830922be21432f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/partials/overview-metrics.html
|
||||
3928afce2ed6d5f2d3e29475dd29e4c3bd3c6df180ad5119beaff0c7a58e3b3b /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/prometheus-token.js
|
||||
ff8222eae4e11a3d85b77705128e064aa6c8cafc83afcd3dc194299a3dbf3911 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/redirects.js
|
||||
a5c05ab544c7e1ea96f2fd133b55adfd8036837036bed3007259070a6a68e38f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/redirects.js
|
||||
76a01f590d49649dcb1f554c221290919b2a8438e6001737f3c096fa6daec74d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/settings.js
|
||||
8eadbada619b15087783dcfa0e889a81f88e78034d084c5ab616f7912b835940 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/status_200.html
|
||||
792d59e169991749bd119ec6acb9386182f4fc9be1705e5f9f3f24d7d2bfc057 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/streams.js
|
||||
ae0259cf32f53d69a22a640b1c34562018d4ffb3864494012de3a31c0bb4ac1e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/traffic-panel.js
|
||||
675ea169d4cc55fda5e291af536338b882bede1eaa6c9d5cc7dfde0779c7c4d1 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/transform-panel.js
|
||||
87917b0388090f2b27ec6460be56fea96dc09b42735319e0a91330748848e288 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/core.js
|
||||
28961dba1537265f795911bfc6826065ab00b154a295f47f5b78b9235a06d43d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/editor.js
|
||||
141565fbd8d2dceaa9b6f488046b96c897c94d475450db22ce854ce534336318 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/editor.js
|
||||
3776f72d7051af86ac5fbdd4581040dd88a424c0c6cde789301b032b159094d8 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/health.js
|
||||
b3239e67901f1796d7a974ade0796f2e3590ff8064628a5629b78c7f6ce2f32e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/protocol.js
|
||||
8c3da49973c62333559b0e3588c02beb0b425c025eac5cfa82a1bb968f70d9dd /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/admin/web/vhosts/rates.js
|
||||
@@ -339,12 +339,12 @@ a1132b980833b1b4ce2bba9aa681ff255a0e2d61faaa65a316860da503ea1e27 /sessions/rcw-
|
||||
1a2eb6684d9ea4a7e90970d936c75f7c8bbec46a4181fdf273be9bdac8eec847 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/compression/mod.rs
|
||||
8de82d9188c9c337690c6ec4afdad9fff20651c358e34602a93d8bb8c3d2489a /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/compression/tests.rs
|
||||
e695079333ebdc2a6e7b3337cfabef171b3a0a01da0597b77feff70d417f833e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config.rs
|
||||
d727e5ebd6c6184943d5f773c6b5244954832139c744851c1357614880f09c6d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/backend.rs
|
||||
367b46f2b57143db17044378b1f5a55bab1eae3ad6ba40f388896995d1b2a13d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/compile.rs
|
||||
2c4312dd7410dc231ed204d7d201d716eedb41045fd95df7cbb51d5561540d99 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/backend.rs
|
||||
6ad644fc4ae821d2c30329ace36652a7ac12d674e71e7dbdcdda3554c983f6f9 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/compile.rs
|
||||
fbea01fea1213d98aea812c1b78d5db289e09200b98ae514d742235cd3fa70e1 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/internal_routing.rs
|
||||
cc75698be2828000e88f2c482bd26f2fa4050e3432bec1b5a581dca17fef4a37 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/model.rs
|
||||
75839458ddbc7480ec4492a95eeb67b617ba7d62f7b67e7a767a3453b74dbcbc /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/route_policy_tests.rs
|
||||
736b46108c72220b8142f4f2ab128876b6bd82e0d09891578abdce6aa03c82f3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/routing.rs
|
||||
e442c6561723e8dbf73d25912227cebf7aa84ac1523e8bda8e6b4bdad1b674df /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/routing.rs
|
||||
dd9a6ad8f58e0cdd1ae74f3f972fee0ac14383cd8b8f518403b9a36c169f35bd /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/snapshot.rs
|
||||
477cddaa1e1c0b9b605a2f550971a04cf30287519cba70cfbb2e1860e3a93ff2 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/tests.rs
|
||||
f35de0ee1516ece6bbfd2b2a7db7d1e0f5048b0ec47cf2a3765dffb799abfd93 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/config/transport_tests.rs
|
||||
@@ -400,7 +400,7 @@ a719c51aebcba0f16f8810c37618201954e79d6806da085ee6c5e4697a05b521 /sessions/rcw-
|
||||
31154052757c2bb975b8ce8cad67f3fad7b5132b615a73d5b0d7bf1b5311722b /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/metrics/rate_sampling.rs
|
||||
f27f1f8bed769883d1a7ae6a48f89f7712394dfab65d05702b0f944f0a45bd8e /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/metrics/vhost/mod.rs
|
||||
ee401f943bd8b2706c33419ed69102269c08dcb4ceba6f4be2ac0861115874a7 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/metrics/vhost/tests.rs
|
||||
9c96c3e4edb20dd0c36b5c373f1925dd60368e75853002206523072d418d5417 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/not_found/mod.rs
|
||||
f2a632f9d3d7f9985ff796545263ebe03fbf5e9a5c2e88288e89a700d3af81d3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/not_found/mod.rs
|
||||
fa2fc6c6987a8356a9da92cade10db62426d8863e9cef3c0d4324c45780c5b33 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/not_found/tests.rs
|
||||
c19998732c2d65b464f43d17a58644db5bcf7b4a6dd4c31a65a091c57692a5bb /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/oidc/mod.rs
|
||||
c4dbe0f8ded237d45b6a3977bdc7544296e4408996815cae9e28af028419d0b7 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/oidc/tests.rs
|
||||
@@ -456,7 +456,7 @@ a44421714aab37f7ceda19e3ec2512c645d2a3ce5dcb3da5bc7a0d87a442d6a8 /sessions/rcw-
|
||||
999cbf85cb6e917db529e30ba4a5eed1cc9e95a054da7b3039c730bb80061449 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/proxy/websocket/validation.rs
|
||||
80d0f744f7ad9f0d1edeb0d5c23b13cb40431f6b6f4d10d534ed34d4198e3dfd /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/rate_limit/mod.rs
|
||||
c85c9f05dac65cde6878bef3ef7258eb9de79ca14c26ca2492e0e13839c40fc3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/rate_limit/tests.rs
|
||||
e53c40b85792d4804a432e3c97dc13af8e29304bb0d250ee9ab9018bde522eeb /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/redirect/mod.rs
|
||||
db254128c8013adaad7280f40a0edd8b196083afef299cd9e193e6db1d698b82 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/redirect/mod.rs
|
||||
811c83ae9fd0f5a13d35b5443cd37843f82b9d600bee4bb136ba9b87732a5347 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/redirect/tests.rs
|
||||
244d72b3dab36ec505c96d26a967420714fd3807268820f680fb3dcea0e88c0f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/runtime.rs
|
||||
3c71d8eef9ccb49ad93cf6014c5c27756ce0eaeddfca116048f24c483fda68e2 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/runtime/certificates.rs
|
||||
@@ -531,7 +531,7 @@ d61c427c1e9cf457f1232b822d73e846e9f329cfbe61215e7ca061d53762155d /sessions/rcw-
|
||||
a8c5ec5095178a0656a001b0d7100e240d1f32b052ea60df37f8681269638ab3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/vhost/model.rs
|
||||
fc8af7342a3b147921e9040116af5dfe680b3df910db2416ada18198bbeea7df /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/vhost/redirect.rs
|
||||
2651d7e3e3fadf3de74a75eafe2bbad8d02939551ccc0504c1a092a0b33b0db5 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/vhost/tests.rs
|
||||
190b2bbdc7ef5181ae4dbe1659f6f0c7ead5d09936b109d0c5be7ea2e7edd681 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/vhost/validation.rs
|
||||
c32ff8fae0c291bf072404aa118cb81016752b2929c89c7ec845d97006e677ce /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/vhost/validation.rs
|
||||
8d1eff4b24f01d137414ef5419f75dd281879228adb70409645b11b3c78c20e9 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/web_server/config.rs
|
||||
9744e8c29ae72a4d9fbc9a76166a97f8292c4c312295ea40d1cb699e03d80a9d /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/web_server/config/compile.rs
|
||||
5ed79fa5b9c53f95ca200d89a9ba927dda816b61fbdf3b852bd3c9670e3d2d4f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/src/web_server/config/model.rs
|
||||
@@ -561,7 +561,7 @@ f00743af2cf4a5952caef7a1dcf68e7817b5599a252a9973e1c22045517c0b0e /sessions/rcw-
|
||||
102b83171a6429b6375d8001d6c5294702385cd58ee55727cb657b1f3566e877 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/panel_icons_browser.py
|
||||
52e61c389a60d147184ee76925cac76e454f8f2e3cb91039bb06e17787ab900f /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/panel_icons_contract.js
|
||||
01af3008be27fabb486c141c67099af104f2725cc56f4c656aea9741a3c150cd /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/panel_interface_contract.py
|
||||
2346b7de3a705e1128794fa89ce4811cdf240e1105f982520f3c4eed1358eafa /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/panel_refresh_contract.js
|
||||
e7b9656dc584e441ca50ac289ec0fd9a37ffc4e7872c486f683e6712c806b9bd /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/panel_refresh_contract.js
|
||||
e24a0f84f1bfc46964df865ac387d651f7b384d781cbc8f3a9a5b0b60f3d69ca /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tests/vhost_auth_selection.js
|
||||
eca68a07c2b1e45fdb8f512294f5a432db3ae5a6aec8fb200b47f155cd9a31d6 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tools/README.md
|
||||
7c30c280d0b0a8bf855eeffab583ca34dc161255f7ef6124148aa979c17c26d3 /sessions/rcw-01mmsgszuju6d7f2h8egrxc4/mnt/RustProxy/tools/dev/memory_load.py
|
||||
|
||||
@@ -77,10 +77,10 @@ ENV_FILE=integrations/routeros-oci-build/.env \
|
||||
bash integrations/routeros-oci-build/build-routeros-oci.sh push
|
||||
```
|
||||
|
||||
For example, with RustProxy version `0.36.4` and the defaults this publishes:
|
||||
For example, with RustProxy version `0.36.5` and the defaults this publishes:
|
||||
|
||||
```text
|
||||
zot.example.net/network/rustproxy:0.36.4
|
||||
zot.example.net/network/rustproxy:0.36.5
|
||||
zot.example.net/network/rustproxy:latest
|
||||
zot.example.net/network/rustproxy:routeros
|
||||
```
|
||||
|
||||
@@ -1732,6 +1732,7 @@
|
||||
"autoIdChange": "Change",
|
||||
"autoIdPending": "Generated from the name",
|
||||
"autoIdBadge": "auto",
|
||||
"autoCertHint": "Certificate \"{id}\" was selected automatically because it covers all host names. You can choose another one.",
|
||||
"dockerLiveAccessLogsHelp": "Live HTTP access logs for this Docker vhost, not the container stdout/stderr.",
|
||||
"networkInterfacesHistory": "Traffic by interface",
|
||||
"networkInterfaceRate": "RX / TX",
|
||||
|
||||
@@ -1732,6 +1732,7 @@
|
||||
"autoIdChange": "Zmień",
|
||||
"autoIdPending": "Generowane z nazwy",
|
||||
"autoIdBadge": "auto",
|
||||
"autoCertHint": "Certyfikat \"{id}\" został wybrany automatycznie, bo pokrywa wszystkie nazwy hostów. Możesz wybrać inny.",
|
||||
"dockerLiveAccessLogsHelp": "Logi żądań HTTP dla tego Docker vhosta, nie stdout/stderr kontenera.",
|
||||
"networkInterfacesHistory": "Ruch według interfejsów",
|
||||
"networkInterfaceRate": "RX / TX",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "RustProxy Administration API",
|
||||
"version": "0.36.4",
|
||||
"version": "0.36.5",
|
||||
"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. The API also permits explicitly scoped GeneralApiBearer tokens on a narrow route allowlist, with RO/RW and source CIDR checks; API-token management and Prometheus-token management are browser-session-only."
|
||||
},
|
||||
"servers": [
|
||||
|
||||
@@ -727,3 +727,56 @@ const rustProxyAutoId = (() => {
|
||||
return { slugify, unique, attach };
|
||||
})();
|
||||
window.rustProxyAutoId = rustProxyAutoId;
|
||||
|
||||
// Automatic certificate choice: for a new entry, pick the stored certificate that covers every host name (exact names beat wildcards); the user can still choose another one.
|
||||
const rustProxyAutoCertificate = (() => {
|
||||
const states = new WeakMap();
|
||||
const covers = (pattern, host) => {
|
||||
pattern = String(pattern).toLowerCase(); host = String(host).toLowerCase();
|
||||
if (pattern === host) return true;
|
||||
if (host.startsWith("*.") || !pattern.startsWith("*.")) return false;
|
||||
const suffix = pattern.slice(1);
|
||||
return host.length > suffix.length && host.endsWith(suffix) && !host.slice(0, -suffix.length).includes(".");
|
||||
};
|
||||
function suggest(hosts, certificates) {
|
||||
const names = [...new Set((hosts || []).map(host => String(host).trim().toLowerCase()).filter(Boolean))];
|
||||
if (!names.length) return "";
|
||||
let best = "", bestScore = -Infinity;
|
||||
for (const cert of certificates || []) {
|
||||
const coverage = certificateInventory.get(cert.id)?.covered_hosts?.length ? certificateInventory.get(cert.id).covered_hosts : (cert.covered_hosts || []);
|
||||
let score = 0, all = true;
|
||||
for (const host of names) {
|
||||
if (coverage.some(pattern => String(pattern).toLowerCase() === host)) score += 2;
|
||||
else if (coverage.some(pattern => covers(pattern, host))) score += 1;
|
||||
else { all = false; break; }
|
||||
}
|
||||
if (!all) continue;
|
||||
score -= coverage.length / 1000;
|
||||
if (score > bestScore) { best = cert.id; bestScore = score; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
// options: { hosts: () => host names, certificates: () => certificate objects, auto: boolean, anchor: element after which the hint is placed }
|
||||
function attach(select, options) {
|
||||
let state = states.get(select);
|
||||
if (!state) {
|
||||
state = { hint: document.createElement("small") };
|
||||
state.hint.className = "fine auto-cert-hint"; state.hint.hidden = true;
|
||||
(options.anchor || select).after(state.hint);
|
||||
select.addEventListener("change", event => { if (!event.isTrusted) return; const current = states.get(select); current.auto = false; current.hint.hidden = true; });
|
||||
states.set(select, state);
|
||||
}
|
||||
state.options = options; state.auto = options.auto !== false && !select.value; state.hint.hidden = true;
|
||||
const refresh = hosts => {
|
||||
const current = states.get(select);
|
||||
if (!current.auto) return;
|
||||
const list = hosts || current.options.hosts();
|
||||
const best = suggest(list, current.options.certificates());
|
||||
if (select.value !== best) { select.value = best; select.dispatchEvent(new Event("change", { bubbles: true })); }
|
||||
current.hint.hidden = !best; current.hint.textContent = best ? t("autoCertHint").replace("{id}", best) : "";
|
||||
};
|
||||
return { refresh };
|
||||
}
|
||||
return { attach, suggest, covers };
|
||||
})();
|
||||
window.rustProxyAutoCertificate = rustProxyAutoCertificate;
|
||||
@@ -1767,9 +1767,9 @@
|
||||
.vh-setting-section>h3 { letter-spacing: .01em; }
|
||||
.vh-tabs { flex-wrap: wrap; overflow: visible; row-gap: 0; }
|
||||
@media (max-width:700px) { .vh-tabs button { font-size: 11px; padding: 7px 8px; } }
|
||||
.auto-id-hint { display: block; margin-top: 3px; color: var(--muted); font-size: 11px; line-height: 1.35; }
|
||||
.auto-id-hint, .auto-cert-hint { display: block; margin-top: 3px; color: var(--muted); font-size: 11px; line-height: 1.35; }
|
||||
.auto-id-reset { justify-self: start; margin-top: 3px; padding: 2px 8px; font-size: 11px; }
|
||||
.auto-id-hint[hidden], .auto-id-reset[hidden] { display: none; }
|
||||
.auto-id-hint[hidden], .auto-cert-hint[hidden], .auto-id-reset[hidden] { display: none; }
|
||||
.auto-id-field>span:first-child { color: var(--muted); }
|
||||
.auto-id-field.is-compact>input { display: none; }
|
||||
.auto-id-row { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 2px; }
|
||||
@@ -1779,6 +1779,3 @@
|
||||
.auto-id-change { padding: 2px 8px; font-size: 11px; margin-left: auto; }
|
||||
.auto-id-row[hidden], .auto-id-change[hidden], .auto-id-badge[hidden] { display: none; }
|
||||
.auto-id-field input { font-family: var(--mono, monospace); font-size: 12px; }
|
||||
.auto-id-hint { display: block; margin-top: 3px; color: var(--muted); font-size: 11px; line-height: 1.35; }
|
||||
.auto-id-reset { justify-self: start; margin-top: 3px; padding: 2px 8px; font-size: 11px; }
|
||||
.auto-id-hint[hidden], .auto-id-reset[hidden] { display: none; }
|
||||
@@ -3,7 +3,7 @@
|
||||
const form = $("notfound-form"), dialog = $("notfound-dialog"), list = $("notfound-list");
|
||||
if (!form || !dialog || !list) return;
|
||||
const sortColumns = ["id", "name", "hostname", "status", "mode", "listeners", "certificate"];
|
||||
let editing = null, revision = null, dirty = false, readBindings = () => [], readHosts = () => [], listPrefs = { sort: "id", descending: false };
|
||||
let notFoundAutoCertificate = null, editing = null, revision = null, dirty = false, readBindings = () => [], readHosts = () => [], listPrefs = { sort: "id", descending: false };
|
||||
function normalizeListPrefs(preferences) { return { sort: sortColumns.includes(preferences?.notfound_list_sort) ? preferences.notfound_list_sort : "id", descending: preferences?.notfound_list_descending === true }; }
|
||||
function syncSortDirection() { const control = $("notfound-sort-direction"); if (!control) return; control.textContent = listPrefs.descending ? "↓" : "↑"; control.setAttribute("aria-label", t(listPrefs.descending ? "backendSortDescending" : "backendSortAscending")); }
|
||||
function applyPreferences(preferences) { listPrefs = normalizeListPrefs(preferences); if ($("notfound-sort")) $("notfound-sort").value = listPrefs.sort; syncSortDirection(); render(); }
|
||||
@@ -46,8 +46,9 @@
|
||||
$("notfound-id").value = item.id || ""; $("notfound-id").disabled = Boolean(editing); $("notfound-name").value = item.name || "";
|
||||
window.rustProxyAutoId.attach($("notfound-id"), { elements: [$("notfound-name")], taken: () => (envelope?.config.not_found_hosts || []).map(value => value.id) });
|
||||
$("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 });
|
||||
const hostHolder = $("notfound-hosts-editor"); hostHolder.replaceChildren(); readHosts = window.rustProxyIntegrations.hostnames(hostHolder, item.frontend?.hosts || [], { labelKey: "notfoundHosts", helpKey: "vhHostsHelp", requiredKey: "notfoundHostsRequired", onChange: () => { changed(); notFoundAutoCertificate?.refresh(); } });
|
||||
listenerChoices(item.frontend?.listener_ids || []); certificateChoices(item.certificate_id);
|
||||
notFoundAutoCertificate = window.rustProxyAutoCertificate.attach($("notfound-certificate"), { auto: !editing, hosts: () => { try { return readHosts(); } catch { return []; } }, certificates: () => envelope?.config.tls?.certificates || [] }); notFoundAutoCertificate.refresh();
|
||||
$("notfound-mode").value = item.page?.kind || "welcome"; populateStatuses(item.page?.status ?? 404); $("notfound-html").value = item.page?.html || ""; syncMode(); dialog.showModal();
|
||||
}
|
||||
function suspendDraft() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
const form = $("redirect-form"), dialog = $("redirect-dialog"), list = $("redirect-list"), rules = $("redirect-rules");
|
||||
if (!form || !dialog || !list || !rules) return;
|
||||
const sortColumns = ["id", "name", "hostname", "status", "target", "rules", "listeners", "certificate"];
|
||||
let editing = null, revision = null, dirty = false, readRedirectBindings = () => [], readRedirectHosts = () => [], listPrefs = { sort: "id", descending: false };
|
||||
let redirectAutoCertificate = null, editing = null, revision = null, dirty = false, readRedirectBindings = () => [], readRedirectHosts = () => [], listPrefs = { sort: "id", descending: false };
|
||||
function normalizeListPrefs(preferences) { return { sort: sortColumns.includes(preferences?.redirect_list_sort) ? preferences.redirect_list_sort : "id", descending: preferences?.redirect_list_descending === true }; }
|
||||
function syncSortDirection() { const control = $("redirect-sort-direction"); if (!control) return; control.textContent = listPrefs.descending ? "↓" : "↑"; control.setAttribute("aria-label", t(listPrefs.descending ? "backendSortDescending" : "backendSortAscending")); }
|
||||
function applyPreferences(preferences) { listPrefs = normalizeListPrefs(preferences); if ($("redirect-sort")) $("redirect-sort").value = listPrefs.sort; syncSortDirection(); render(); }
|
||||
@@ -71,9 +71,10 @@
|
||||
$("redirect-id").value = item.id || ""; $("redirect-id").disabled = Boolean(editing);
|
||||
$("redirect-name").value = item.name || "";
|
||||
window.rustProxyAutoId.attach($("redirect-id"), { elements: [$("redirect-name")], taken: () => (envelope?.config.redirects || []).map(value => value.id) });
|
||||
const hostHolder = $("redirect-hosts-editor"); hostHolder.replaceChildren(); readRedirectHosts = window.rustProxyIntegrations.hostnames(hostHolder, item.frontend?.hosts || [], { labelKey: "redirectHosts", helpKey: "vhHostsHelp", requiredKey: "redirectHostsRequired", onChange: changed });
|
||||
const hostHolder = $("redirect-hosts-editor"); hostHolder.replaceChildren(); readRedirectHosts = window.rustProxyIntegrations.hostnames(hostHolder, item.frontend?.hosts || [], { labelKey: "redirectHosts", helpKey: "vhHostsHelp", requiredKey: "redirectHostsRequired", onChange: () => { changed(); redirectAutoCertificate?.refresh(); } });
|
||||
$("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);
|
||||
redirectAutoCertificate = window.rustProxyAutoCertificate.attach($("redirect-certificate"), { auto: !editing, hosts: () => { try { return readRedirectHosts(); } catch { return []; } }, certificates: () => envelope?.config.tls?.certificates || [] }); redirectAutoCertificate.refresh();
|
||||
rules.replaceChildren(); for (const [i, rule] of (item.rules?.length ? item.rules : defaults().rules).entries()) addRule(rule, i);
|
||||
dialog.showModal();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ function load(value = defaults(), migrationIds = [], show = true) {
|
||||
container.replaceChildren(); $("vhost-editing").textContent = t(editing ? "vhEditLabel" : "vhNewLabel");
|
||||
let g = group("vhEditor"); const basicFields = subsection(g, "vhGeneralDetailsSection");
|
||||
const vhostIdInput = field(basicFields, "id", "vhId", value.id, "text", { required: true, maxLength: 64, pattern: "[A-Za-z0-9_-]{1,64}" }); if (editing) note(basicFields, "vhIdRenameHelp");
|
||||
let refreshCanonical = () => { }, vhostAutoId = null;
|
||||
let refreshCanonical = () => { }, vhostAutoId = null, vhostAutoCertificate = null;
|
||||
vhostAutoId = window.rustProxyAutoId.attach(vhostIdInput, { text: () => lines(read("hosts")?.value || "")[0] || "", taken: () => (envelope?.config.vhosts || []).map(item => item.id), resettable: !editing });
|
||||
const enabledControl = field(basicFields, "enabled", "vhEnabled", value.enabled !== false, "checkbox");
|
||||
window.rustProxyEnableControl(enabledControl, "vhEnableHint");
|
||||
@@ -89,7 +89,7 @@ function load(value = defaults(), migrationIds = [], show = true) {
|
||||
const websocketLegacy = field(websocketFields, "websocket-legacy-support", "vhWebsocketLegacySupport", value.websocket_legacy_support === true, "checkbox");
|
||||
note(websocketFields, "vhWebsocketLegacySupportHelp");
|
||||
g = group("vhFrontends");
|
||||
readHosts = hostTagEditor(g, value.frontend?.hosts || [], hosts => { refreshCanonical(hosts); vhostAutoId?.refresh(); });
|
||||
readHosts = hostTagEditor(g, value.frontend?.hosts || [], hosts => { refreshCanonical(hosts); vhostAutoId?.refresh(); vhostAutoCertificate?.refresh(hosts); });
|
||||
if (!editing) g.lastElementChild?.after(vhostIdInput.closest("label"));
|
||||
const bindingControl = window.rustProxyIntegrations.bindings(g, value.frontend?.listener_ids || [], !editing); readBindings = bindingControl.read;
|
||||
note(g, "listenerDefaultPortsHint");
|
||||
@@ -350,6 +350,7 @@ function load(value = defaults(), migrationIds = [], show = true) {
|
||||
const certificateRow = el("div", undefined, "vh-wide vh-certificate-select-row"); g.append(certificateRow);
|
||||
const selector = field(certificateRow, "certificate-id", "vhCertificate", value.certificate_id || "", "select");
|
||||
const fillCertificates = () => { const selected = selector.value || value.certificate_id || ""; const inherit = text("option", "vhCertificateDefault"); inherit.value = ""; selector.replaceChildren(inherit, ...[...(envelope?.config.tls?.certificates || []), ...stagedCertificates].map(c => { const o = el("option", c.id); o.value = c.id; return o; })); selector.value = selected; }; fillCertificates();
|
||||
vhostAutoCertificate = window.rustProxyAutoCertificate.attach(selector, { auto: !editing, anchor: certificateRow, hosts: () => { try { return readHosts(); } catch { return []; } }, certificates: () => [...(envelope?.config.tls?.certificates || []), ...stagedCertificates] }); vhostAutoCertificate.refresh();
|
||||
certificateRow.append(button("certManage", async () => { if (!(await closeEditor())) return; setPanel("tls"); }));
|
||||
note(g, "certSelectHint");
|
||||
const mismatch = field(g, "allow-certificate-mismatch", "vhCertificateMismatchForce", value.allow_certificate_mismatch === true, "checkbox");
|
||||
|
||||
+23
-10
@@ -150,28 +150,35 @@ pub fn compile_reusing(
|
||||
type LocalHostBuild = (
|
||||
HashMap<String, Vec<Arc<crate::redirect::CompiledHost>>>,
|
||||
HashMap<String, Vec<Arc<crate::not_found::CompiledHost>>>,
|
||||
HashMap<String, Vec<crate::vhost::Listener>>,
|
||||
HashMap<String, Vec<(String, crate::vhost::Listener)>>,
|
||||
);
|
||||
|
||||
fn compile_local_hosts(config: &Config) -> Result<LocalHostBuild> {
|
||||
let mut redirects: HashMap<String, Vec<Arc<crate::redirect::CompiledHost>>> = HashMap::new();
|
||||
let mut not_found_hosts: HashMap<String, Vec<Arc<crate::not_found::CompiledHost>>> =
|
||||
HashMap::new();
|
||||
let mut claimed_hosts: HashMap<String, Vec<crate::vhost::Listener>> = HashMap::new();
|
||||
let mut claimed_hosts: HashMap<String, Vec<(String, crate::vhost::Listener)>> =
|
||||
HashMap::new();
|
||||
for redirect in &config.redirects {
|
||||
let bindings = redirect.frontend.resolved(&config.listeners)?;
|
||||
for name in &redirect.frontend.hosts {
|
||||
let claimed = claimed_hosts.entry(name.clone()).or_default();
|
||||
for a in claimed.iter() {
|
||||
for (owner, a) in claimed.iter() {
|
||||
for b in &bindings {
|
||||
ensure!(
|
||||
!crate::vhost::socket_overlap(a.address, b.address),
|
||||
"overlapping frontend bindings for hostname {}",
|
||||
name
|
||||
"overlapping frontend bindings for hostname {} (already used by {} on {})",
|
||||
name,
|
||||
owner,
|
||||
a.address
|
||||
);
|
||||
}
|
||||
}
|
||||
claimed.extend(bindings.iter().copied());
|
||||
claimed.extend(
|
||||
bindings
|
||||
.iter()
|
||||
.map(|binding| (format!("redirect {}", redirect.id), *binding)),
|
||||
);
|
||||
}
|
||||
if redirect.enabled {
|
||||
let compiled = Arc::new(crate::redirect::compile(redirect, &config.listeners)?);
|
||||
@@ -187,16 +194,22 @@ fn compile_local_hosts(config: &Config) -> Result<LocalHostBuild> {
|
||||
let bindings = host.frontend.resolved(&config.listeners)?;
|
||||
for name in &host.frontend.hosts {
|
||||
let claimed = claimed_hosts.entry(name.clone()).or_default();
|
||||
for a in claimed.iter() {
|
||||
for (owner, a) in claimed.iter() {
|
||||
for b in &bindings {
|
||||
ensure!(
|
||||
!crate::vhost::socket_overlap(a.address, b.address),
|
||||
"overlapping frontend bindings for hostname {}",
|
||||
name
|
||||
"overlapping frontend bindings for hostname {} (already used by {} on {})",
|
||||
name,
|
||||
owner,
|
||||
a.address
|
||||
);
|
||||
}
|
||||
}
|
||||
claimed.extend(bindings.iter().copied());
|
||||
claimed.extend(
|
||||
bindings
|
||||
.iter()
|
||||
.map(|binding| (format!("404 host {}", host.id), *binding)),
|
||||
);
|
||||
}
|
||||
if host.enabled {
|
||||
let compiled = Arc::new(crate::not_found::compile(host, &config.listeners)?);
|
||||
|
||||
+21
-8
@@ -65,7 +65,8 @@ pub fn compile_with_active(
|
||||
php_profiles
|
||||
.insert(profile.id.clone(), profile.php.clone())
|
||||
.is_none(),
|
||||
"duplicate PHP-FPM profile id"
|
||||
"duplicate PHP-FPM profile id {:?}: every profile needs a unique ID",
|
||||
profile.id
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
@@ -120,7 +121,8 @@ pub fn compile_with_active(
|
||||
);
|
||||
ensure!(
|
||||
web_services.insert(service.id.clone(), compiled).is_none(),
|
||||
"duplicate Web Service id"
|
||||
"duplicate Web Service id {:?}: every Web Service needs a unique ID",
|
||||
service.id
|
||||
);
|
||||
}
|
||||
ensure!(config.vhosts.len() <= 64, "too many vhosts (max 64)");
|
||||
@@ -145,7 +147,11 @@ pub fn compile_with_active(
|
||||
validate_source(&route.source)?;
|
||||
}
|
||||
}
|
||||
ensure!(vhost_ids.insert(host.id.clone()), "duplicate vhost id");
|
||||
ensure!(
|
||||
vhost_ids.insert(host.id.clone()),
|
||||
"duplicate vhost id {:?}: every vhost needs a unique ID",
|
||||
host.id
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
config.redirects.len() <= 64,
|
||||
@@ -158,7 +164,8 @@ pub fn compile_with_active(
|
||||
.with_context(|| format!("redirect {}", redirect.id))?;
|
||||
ensure!(
|
||||
redirect_ids.insert(redirect.id.clone()),
|
||||
"duplicate redirect id"
|
||||
"duplicate redirect id {:?}: every redirect needs a unique ID",
|
||||
redirect.id
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
@@ -171,7 +178,8 @@ pub fn compile_with_active(
|
||||
.with_context(|| format!("404 host {}", host.id))?;
|
||||
ensure!(
|
||||
not_found_ids.insert(host.id.clone()),
|
||||
"duplicate 404 host id"
|
||||
"duplicate 404 host id {:?}: every 404 host needs a unique ID",
|
||||
host.id
|
||||
);
|
||||
}
|
||||
let effective_tls = crate::tls::for_all_frontends(
|
||||
@@ -279,7 +287,11 @@ pub fn compile_with_active(
|
||||
let mut acl_profiles: HashMap<String, (Option<CompiledAcl>, crate::rate_limit::CountryAccess)> =
|
||||
HashMap::new();
|
||||
for profile in &mut config.acl_profiles {
|
||||
ensure!(valid_id(&profile.id), "invalid ACL profile id");
|
||||
ensure!(
|
||||
valid_id(&profile.id),
|
||||
"invalid ACL profile id {:?}: use 1-64 letters, digits, '-' or '_'",
|
||||
profile.id
|
||||
);
|
||||
ensure!(
|
||||
!profile.name.trim().is_empty()
|
||||
&& profile.name.len() <= 96
|
||||
@@ -293,7 +305,8 @@ pub fn compile_with_active(
|
||||
acl_profiles
|
||||
.insert(profile.id.clone(), (acl, profile.country_access.clone()))
|
||||
.is_none(),
|
||||
"duplicate ACL profile id"
|
||||
"duplicate ACL profile id {:?}: every ACL profile needs a unique ID",
|
||||
profile.id
|
||||
);
|
||||
}
|
||||
crate::acl::dynamic::configured_hostnames(&config)?;
|
||||
@@ -380,7 +393,7 @@ fn normalize_prefix(prefix: &mut String) -> Result<()> {
|
||||
&& !prefix.contains('?')
|
||||
&& !prefix.contains('#')
|
||||
&& !prefix.bytes().any(|b| b <= 32 || b == 127),
|
||||
"invalid path prefix"
|
||||
"invalid path prefix: it must start with '/', be at most 512 bytes and contain no '?', '#', spaces or control characters"
|
||||
);
|
||||
if prefix.len() > 1 {
|
||||
*prefix = prefix.trim_end_matches('/').to_owned();
|
||||
|
||||
+25
-10
@@ -79,16 +79,22 @@ fn compile_routing(
|
||||
);
|
||||
for name in &routing_hosts {
|
||||
let claimed = claimed_hosts.entry(name.clone()).or_default();
|
||||
for a in claimed.iter() {
|
||||
for (owner, a) in claimed.iter() {
|
||||
for b in &host_bindings {
|
||||
ensure!(
|
||||
!crate::vhost::socket_overlap(a.address, b.address),
|
||||
"overlapping frontend bindings for hostname {}",
|
||||
name
|
||||
"overlapping frontend bindings for hostname {} (already used by {} on {})",
|
||||
name,
|
||||
owner,
|
||||
a.address
|
||||
);
|
||||
}
|
||||
}
|
||||
claimed.extend(host_bindings.iter().copied());
|
||||
claimed.extend(
|
||||
host_bindings
|
||||
.iter()
|
||||
.map(|binding| (format!("vhost {}", host.id), *binding)),
|
||||
);
|
||||
}
|
||||
let needs_proxy_pool = host.source.is_reverse_proxy()
|
||||
|| host.routes.iter().any(|route| {
|
||||
@@ -323,7 +329,8 @@ fn compile_routing(
|
||||
for route in &mut host.routes {
|
||||
ensure!(
|
||||
valid_id(&route.id) && path_ids.insert(route.id.clone()),
|
||||
"invalid/duplicate vhost path id"
|
||||
"location ID {:?} is invalid or used twice in this vhost (use 1-64 letters, digits, '-' or '_', unique per vhost)",
|
||||
route.id
|
||||
);
|
||||
let path_regex = match route.match_type {
|
||||
crate::vhost::PathMatch::Regex | crate::vhost::PathMatch::RegexInsensitive => {
|
||||
@@ -331,7 +338,8 @@ fn compile_routing(
|
||||
!route.path_prefix.is_empty()
|
||||
&& route.path_prefix.len() <= 1024
|
||||
&& !route.path_prefix.chars().any(char::is_control),
|
||||
"invalid vhost location regex"
|
||||
"location {}: the regular expression must be 1-1024 characters without control characters",
|
||||
route.id
|
||||
);
|
||||
let mut builder = regex::RegexBuilder::new(&route.path_prefix);
|
||||
builder
|
||||
@@ -340,17 +348,23 @@ fn compile_routing(
|
||||
)
|
||||
.size_limit(2 * 1024 * 1024)
|
||||
.dfa_size_limit(2 * 1024 * 1024);
|
||||
Some(builder.build().context("invalid vhost location regex")?)
|
||||
Some(builder.build().with_context(|| {
|
||||
format!(
|
||||
"location {}: invalid or too complex regular expression",
|
||||
route.id
|
||||
)
|
||||
})?)
|
||||
}
|
||||
crate::vhost::PathMatch::Exact => {
|
||||
ensure!(
|
||||
!route.path_prefix.contains('?')
|
||||
&& !route.path_prefix.contains('#')
|
||||
&& !route.path_prefix.bytes().any(|b| b <= 32 || b == 127),
|
||||
"invalid exact location path"
|
||||
"location {}: an exact path must not contain '?', '#', spaces or control characters",
|
||||
route.id
|
||||
);
|
||||
route.path_prefix = crate::acl::routing_path(&route.path_prefix)
|
||||
.context("invalid exact location path")?;
|
||||
.with_context(|| format!("location {}: invalid exact path", route.id))?;
|
||||
None
|
||||
}
|
||||
crate::vhost::PathMatch::Prefix | crate::vhost::PathMatch::PrefixPriority => {
|
||||
@@ -361,7 +375,8 @@ fn compile_routing(
|
||||
if !route.internal {
|
||||
ensure!(
|
||||
path_bindings.insert((route.match_type, route.path_prefix.clone())),
|
||||
"duplicate vhost location matcher"
|
||||
"location {}: another location already uses the same path and match type",
|
||||
route.id
|
||||
);
|
||||
}
|
||||
validate_route_rewrite(route, path_regex.as_ref())?;
|
||||
|
||||
+10
-2
@@ -37,7 +37,11 @@ fn default_page() -> crate::pages::Page {
|
||||
|
||||
impl Host {
|
||||
pub fn validate(&mut self) -> Result<()> {
|
||||
ensure!(crate::config::valid_id(&self.id), "invalid 404 host id");
|
||||
ensure!(
|
||||
crate::config::valid_id(&self.id),
|
||||
"invalid 404 host id {:?}: use 1-64 letters, digits, '-' or '_'",
|
||||
self.id
|
||||
);
|
||||
if self.name.trim().is_empty() {
|
||||
self.name = self.id.clone();
|
||||
}
|
||||
@@ -55,7 +59,11 @@ impl Host {
|
||||
let mut hosts = std::collections::HashSet::new();
|
||||
for host in &mut self.frontend.hosts {
|
||||
*host = crate::config::normalize_host_pattern(host)?;
|
||||
ensure!(hosts.insert(host.clone()), "duplicate 404 hostname");
|
||||
ensure!(
|
||||
hosts.insert(host.clone()),
|
||||
"hostname {} is listed twice in this 404 host",
|
||||
host
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
!self.frontend.listener_ids.is_empty() && self.frontend.listener_ids.len() <= 16,
|
||||
|
||||
+12
-4
@@ -127,7 +127,11 @@ pub struct Host {
|
||||
}
|
||||
impl Host {
|
||||
pub fn validate(&mut self) -> Result<()> {
|
||||
ensure!(crate::config::valid_id(&self.id), "invalid redirect id");
|
||||
ensure!(
|
||||
crate::config::valid_id(&self.id),
|
||||
"invalid redirect id {:?}: use 1-64 letters, digits, '-' or '_'",
|
||||
self.id
|
||||
);
|
||||
if self.name.is_empty() {
|
||||
self.name = self.id.clone();
|
||||
}
|
||||
@@ -140,16 +144,20 @@ impl Host {
|
||||
);
|
||||
ensure!(
|
||||
!self.frontend.hosts.is_empty(),
|
||||
"redirect requires at least one hostname"
|
||||
"redirect requires at least one hostname (for example old.example.com)"
|
||||
);
|
||||
let mut hosts = std::collections::HashSet::new();
|
||||
for host in &mut self.frontend.hosts {
|
||||
*host = crate::config::normalize_host_pattern(host)?;
|
||||
ensure!(hosts.insert(host.clone()), "duplicate redirect hostname");
|
||||
ensure!(
|
||||
hosts.insert(host.clone()),
|
||||
"hostname {} is listed twice in this redirect",
|
||||
host
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
!self.frontend.listener_ids.is_empty() && self.frontend.listener_ids.len() <= 16,
|
||||
"redirect requires 1..16 named listeners"
|
||||
"select between 1 and 16 listeners for this redirect"
|
||||
);
|
||||
if self.rules.is_empty() {
|
||||
self.rules.push(Rule::default());
|
||||
|
||||
+14
-5
@@ -46,7 +46,11 @@ impl PathRoute {
|
||||
}
|
||||
impl Vhost {
|
||||
pub fn validate(&mut self) -> Result<()> {
|
||||
ensure!(crate::config::valid_id(&self.id), "invalid vhost id");
|
||||
ensure!(
|
||||
crate::config::valid_id(&self.id),
|
||||
"invalid vhost id {:?}: use 1-64 letters, digits, '-' or '_'",
|
||||
self.id
|
||||
);
|
||||
ensure!(
|
||||
!self.websocket_legacy_support || self.websockets,
|
||||
"legacy WebSocket compatibility requires WebSocket support to be enabled"
|
||||
@@ -69,12 +73,16 @@ impl Vhost {
|
||||
);
|
||||
ensure!(
|
||||
!self.frontend.hosts.is_empty(),
|
||||
"vhost requires at least one hostname"
|
||||
"vhost requires at least one hostname (for example app.example.com) in the Frontend section"
|
||||
);
|
||||
let mut hosts = std::collections::HashSet::new();
|
||||
for host in &mut self.frontend.hosts {
|
||||
*host = crate::config::normalize_host_pattern(host)?;
|
||||
ensure!(hosts.insert(host.clone()), "duplicate vhost hostname");
|
||||
ensure!(
|
||||
hosts.insert(host.clone()),
|
||||
"hostname {} is listed twice in this vhost",
|
||||
host
|
||||
);
|
||||
}
|
||||
if let Some(target) = &mut self.www_redirect_target {
|
||||
*target = crate::config::normalize_host_pattern(target)?;
|
||||
@@ -101,7 +109,7 @@ impl Vhost {
|
||||
self.content_type_policy.validate()?;
|
||||
ensure!(
|
||||
!self.frontend.listener_ids.is_empty(),
|
||||
"vhost requires at least one named listener"
|
||||
"select at least one listener for this vhost in the Frontend section"
|
||||
);
|
||||
ensure!(
|
||||
self.frontend.listener_ids.len() <= 16,
|
||||
@@ -150,7 +158,8 @@ impl Vhost {
|
||||
for route in &mut self.routes {
|
||||
ensure!(
|
||||
crate::config::valid_id(&route.id) && route_ids.insert(route.id.clone()),
|
||||
"invalid/duplicate vhost path id"
|
||||
"location ID {:?} is invalid or used twice in this vhost (use 1-64 letters, digits, '-' or '_', unique per vhost)",
|
||||
route.id
|
||||
);
|
||||
route.source.validate()?;
|
||||
ensure!(
|
||||
|
||||
@@ -51,5 +51,5 @@ const events = openapi.paths["/api/events"].get;
|
||||
const logsFlag = (events.parameters || []).find(parameter => parameter.name === "logs");
|
||||
assert.equal(logsFlag?.schema?.type, "boolean");
|
||||
assert.equal(logsFlag?.schema?.default, true);
|
||||
assert.equal(openapi.info.version, "0.36.4");
|
||||
assert.equal(openapi.info.version, "0.36.5");
|
||||
console.log("Panel refresh/API contract checks: OK");
|
||||
Reference in new issue
Block a user