v0.25.0
This commit is contained in:
1 parent
2e4aa4df4d
commit
87e571c170
106 files changed
+17952
-6185
No files matched your search
+276
-46
@@ -12,10 +12,12 @@
|
||||
<script defer="" src="/assets/management.js"></script>
|
||||
<script defer="" src="/assets/integrations.js"></script>
|
||||
<script defer="" src="/assets/vhosts.js"></script>
|
||||
<script defer="" src="/assets/web-server.js"></script>
|
||||
<script defer="" src="/assets/docker.js"></script>
|
||||
<script defer="" src="/assets/streams.js"></script>
|
||||
<script defer="" src="/assets/redirects.js"></script>
|
||||
<script defer="" src="/assets/host404.js"></script>
|
||||
<script defer="" src="/assets/traffic-panel.js"></script>
|
||||
<script defer="" src="/assets/cache-panel.js"></script>
|
||||
<script defer="" src="/assets/transform-panel.js"></script>
|
||||
<script defer="" src="/assets/acme.js"></script>
|
||||
@@ -32,19 +34,21 @@
|
||||
<button class="nav active" data-panel="overview"><span>01</span><b
|
||||
data-i18n="overview">Overview</b></button>
|
||||
<button class="nav" data-panel="vhosts"><span>02</span><b data-i18n="vhosts">Vhosts</b></button>
|
||||
<button class="nav" data-panel="docker-vhosts"><span>03</span><b data-i18n="docker-vhosts">Docker
|
||||
<button class="nav" data-panel="web-server"><span>03</span><b data-i18n="webServer">Web Server</b></button>
|
||||
<button class="nav" data-panel="docker-vhosts"><span>04</span><b data-i18n="docker-vhosts">Docker
|
||||
vhosts</b></button>
|
||||
<button class="nav" data-panel="redirects"><span>04</span><b
|
||||
<button class="nav" data-panel="redirects"><span>05</span><b
|
||||
data-i18n="redirects">Redirects</b></button>
|
||||
<button class="nav" data-panel="notfound"><span>05</span><b data-i18n="notfound">404 hosts</b></button>
|
||||
<button class="nav" data-panel="streams"><span>06</span><b data-i18n="streams">Streams</b></button>
|
||||
<button class="nav" data-panel="acl-profiles"><span>07</span><b data-i18n="aclProfiles">ACL
|
||||
<button class="nav" data-panel="notfound"><span>06</span><b data-i18n="notfound">404 hosts</b></button>
|
||||
<button class="nav" data-panel="streams"><span>07</span><b data-i18n="streams">Streams</b></button>
|
||||
<button class="nav" data-panel="acl-profiles"><span>08</span><b data-i18n="aclProfiles">ACL
|
||||
profiles</b></button>
|
||||
<button class="nav" data-panel="tls"><span>08</span><b data-i18n="tls">TLS</b></button>
|
||||
<button class="nav" data-panel="cache"><span>09</span><b data-i18n="cache">Cache</b></button>
|
||||
<button class="nav" data-panel="transform"><span>10</span><b
|
||||
<button class="nav" data-panel="tls"><span>09</span><b data-i18n="tls">TLS</b></button>
|
||||
<button class="nav" data-panel="traffic"><span>10</span><b data-i18n="traffic">Traffic</b></button>
|
||||
<button class="nav" data-panel="cache"><span>11</span><b data-i18n="cache">Cache</b></button>
|
||||
<button class="nav" data-panel="transform"><span>12</span><b
|
||||
data-i18n="transform">Transform</b></button>
|
||||
<button class="nav" data-panel="settings"><span>11</span><b data-i18n="settings">Settings</b></button>
|
||||
<button class="nav" data-panel="settings"><span>13</span><b data-i18n="settings">Settings</b></button>
|
||||
</nav>
|
||||
<div class="sidebar-bottom"><span class="small-label" data-i18n="runtime">RUNTIME</span>
|
||||
<p>Rust / Tokio / Hyper<br />Axum / Tower / SQLite</p>
|
||||
@@ -498,7 +502,7 @@
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<article class="card pool-card">
|
||||
<article class="card pool-card" id="backend-diagnostics-card">
|
||||
<div class="card-heading">
|
||||
<h2 data-i18n="vhHealthTitle">Backends</h2><button class="quiet" data-i18n="vhManage"
|
||||
id="pool-open" type="button">Manage vhosts</button>
|
||||
@@ -547,6 +551,23 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel" hidden="" id="panel-web-server">
|
||||
<p class="notice fine" data-i18n="webServerIntro">Create reusable local Web Services here, then attach them to a vhost or Custom location.</p>
|
||||
<div class="vhost-layout">
|
||||
<article class="card">
|
||||
<div class="card-heading">
|
||||
<h2 data-i18n="webServiceList">Web Services</h2>
|
||||
<div class="vhost-list-tools">
|
||||
<label class="sr-only" data-i18n="webServiceSearch" for="web-service-search">Search Web Services</label>
|
||||
<input autocomplete="off" data-i18n-aria="webServiceSearch" data-i18n-placeholder="webServiceSearchPlaceholder" id="web-service-search" type="search" />
|
||||
<button class="outline" data-i18n="webServiceNew" id="web-service-new" type="button">New Web Service</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entity-list" id="web-service-list"></div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel" hidden="" id="panel-docker-vhosts">
|
||||
<p class="notice fine" data-i18n="dockerVhostsIntro">Published TCP ports discovered by configured
|
||||
Docker agents. Map names and aliases, select the Docker host IP, and inspect/tune live backend
|
||||
@@ -937,6 +958,45 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="panel" hidden="" id="panel-traffic">
|
||||
<div class="cache-toolbar card"><label><span data-i18n="logVhost">Vhost</span><select id="traffic-scope">
|
||||
<option data-i18n="logAllVhosts" value="*">All vhosts</option>
|
||||
</select></label><button class="quiet" data-i18n="cacheRefresh" id="traffic-refresh" type="button">Refresh</button>
|
||||
<button class="quiet" data-i18n="settings" id="traffic-settings" type="button">Settings</button>
|
||||
<div class="cache-toolbar-status"><span class="fine" id="traffic-panel-state" role="status"></span>
|
||||
<label class="check cache-live-toggle"><input checked="" id="traffic-live" type="checkbox" />
|
||||
<span data-i18n="cacheLive">Live (3 s)</span></label></div>
|
||||
</div>
|
||||
<div class="stats-grid cache-stats">
|
||||
<article class="stat"><span data-i18n="requests">Requests</span><strong id="traffic-requests">-</strong><small data-i18n="trafficSinceStart">Since process start</small></article>
|
||||
<article class="stat"><span data-i18n="errors">Errors</span><strong id="traffic-errors">-</strong><small id="traffic-error-ratio">-</small></article>
|
||||
<article class="stat"><span data-i18n="trafficResponseBytes">Response bytes</span><strong id="traffic-response-bytes">-</strong><small id="traffic-request-bytes">-</small></article>
|
||||
<article class="stat"><span data-i18n="trafficMeanLatency">Mean latency</span><strong id="traffic-latency">-</strong><small id="traffic-dropped">-</small></article>
|
||||
</div>
|
||||
<article class="card">
|
||||
<div class="card-heading"><div><span class="small-label" data-i18n="trafficBoundedLabel">BOUNDED / ASYNC</span><h2 data-i18n="trafficDimensionsTitle">Traffic dimensions</h2></div></div>
|
||||
<p class="fine" data-i18n="trafficApproxHelp">Top lists are approximate and bounded so high-cardinality traffic cannot grow proxy memory without limit.</p>
|
||||
<p class="fine" id="traffic-capture-status"></p>
|
||||
</article>
|
||||
<div class="traffic-chart-grid">
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficTopCountries">Top countries</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-countries" role="img"><span id="traffic-chart-countries-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-countries-legend"></ol></div></article>
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficTopUserAgents">Top User-Agents</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-user-agents" role="img"><span id="traffic-chart-user-agents-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-user-agents-legend"></ol></div></article>
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficTopClientIps">Top client IPs</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-client-ips" role="img"><span id="traffic-chart-client-ips-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-client-ips-legend"></ol></div></article>
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficMethods">Methods</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-methods" role="img"><span id="traffic-chart-methods-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-methods-legend"></ol></div></article>
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficStatusClasses">Status classes</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-statuses" role="img"><span id="traffic-chart-statuses-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-statuses-legend"></ol></div></article>
|
||||
<article class="card traffic-chart-card"><h3 class="subheading" data-i18n="trafficCacheOutcomes">Cache outcomes</h3><div class="traffic-donut-layout"><div class="traffic-donut" id="traffic-chart-cache" role="img"><span id="traffic-chart-cache-total">-</span></div><ol class="traffic-chart-legend" id="traffic-chart-cache-legend"></ol></div></article>
|
||||
</div>
|
||||
<div class="logging-grid">
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficTopCountries">Top countries</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-countries"></tbody></table></div><p class="empty" id="traffic-countries-empty" data-i18n="trafficNoCountryData">Country collection is disabled or no GeoIP data is available.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficTopUserAgents">Top User-Agents</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th>User-Agent</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-user-agents"></tbody></table></div><p class="empty" id="traffic-user-agents-empty" data-i18n="trafficNoUserAgentData">No User-Agent data collected.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficTopClientIps">Top client IPs</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficClientIp">Client IP</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-client-ips"></tbody></table></div><p class="empty" id="traffic-client-ips-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficTopPaths">Top paths</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-paths"></tbody></table></div><p class="empty" id="traffic-paths-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficTopRoutes">Top routes</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-routes"></tbody></table></div><p class="empty" id="traffic-routes-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficMethods">Methods</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-methods"></tbody></table></div><p class="empty" id="traffic-methods-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficStatusClasses">Status classes</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-statuses"></tbody></table></div><p class="empty" id="traffic-statuses-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
<article class="card"><h3 class="subheading" data-i18n="trafficCacheOutcomes">Cache outcomes</h3><div class="table-scroll"><table class="cache-table"><thead><tr><th data-i18n="trafficValue">Value</th><th data-i18n="requests">Requests</th></tr></thead><tbody id="traffic-cache"></tbody></table></div><p class="empty" id="traffic-cache-empty" data-i18n="trafficNoData">No traffic data yet.</p></article>
|
||||
</div>
|
||||
</section>
|
||||
<section class="panel" hidden="" id="panel-cache">
|
||||
<div class="cache-toolbar card"><label><span data-i18n="logVhost">Vhost</span><select
|
||||
id="cache-scope">
|
||||
@@ -1396,7 +1456,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.24.10"
|
||||
id="server-value" maxlength="256" placeholder="RustProxy/v0.25.0"
|
||||
spellcheck="false" />
|
||||
<h3 class="subheading" data-i18n="diagnosticHeaders">Diagnostic headers</h3>
|
||||
<div class="switch-grid">
|
||||
@@ -1483,10 +1543,14 @@
|
||||
<div><label data-i18n="cacheEvictionLimit" for="cache-eviction-limit">LRU
|
||||
removals per admission</label><input id="cache-eviction-limit"
|
||||
max="4096" min="1" required="" type="number" /></div>
|
||||
<div><label data-i18n="cacheAutoTrimAfter" for="cache-auto-trim-after">Idle age before HOT trim (seconds)</label><input id="cache-auto-trim-after" max="86400" min="5" required="" type="number" /></div>
|
||||
<div><label data-i18n="cacheAutoTrimTarget" for="cache-auto-trim-target">HOT trim target (%)</label><input id="cache-auto-trim-target" max="95" min="10" required="" type="number" /></div>
|
||||
<div><label data-i18n="cacheMaxTtl" for="cache-ttl">Maximum TTL
|
||||
(seconds)</label><input id="cache-ttl" max="604800" min="1" required=""
|
||||
type="number" /></div>
|
||||
</div>
|
||||
<label class="check"><input id="cache-auto-trim-enabled" type="checkbox" /><span data-i18n="cacheAutoTrimEnabled">Automatically release idle HOT cache memory</span></label>
|
||||
<p class="fine" data-i18n="cacheAutoTrimHelp">When HOT memory is above the target, only entries idle for the configured age are removed in bounded passes. Active objects are not purged.</p>
|
||||
<label data-i18n="cacheDefaultTtl" for="cache-default-ttl">Fallback TTL for public
|
||||
responses (0 = off)</label><input id="cache-default-ttl" max="604800" min="0"
|
||||
required="" type="number" />
|
||||
@@ -1756,16 +1820,32 @@
|
||||
</div>
|
||||
<div aria-labelledby="settings-tab-logging" class="settings-page" data-settings-page="logging"
|
||||
hidden="" id="settings-page-logging" role="tabpanel">
|
||||
<form class="card edit-form" id="logging-form">
|
||||
<div class="card edit-form" id="logging-settings-card">
|
||||
<div class="card-heading">
|
||||
<div><span class="small-label" data-i18n="loggingMode">JSON / ASYNC / BOUNDED</span>
|
||||
<h2 data-i18n="loggingTitle">Structured access logging</h2>
|
||||
</div><span class="pill live" data-i18n="hotApply">LIVE APPLY</span>
|
||||
</div>
|
||||
<p class="fine" data-i18n="loggingHelp">Live log viewing stays in memory. External sinks
|
||||
run on
|
||||
a bounded background queue and never block proxy request I/O.</p>
|
||||
<div class="logging-grid">
|
||||
<nav aria-label="Logging settings" class="settings-tabs sub-tabs" id="logging-settings-tabs"
|
||||
role="tablist">
|
||||
<button aria-selected="true" data-i18n="loggingTabOutputs"
|
||||
data-logging-settings-tab="outputs" type="button">Outputs</button>
|
||||
<button aria-selected="false" data-i18n="loggingTabFormat"
|
||||
data-logging-settings-tab="format" type="button">JSON format</button>
|
||||
<button aria-selected="false" data-i18n="loggingTabErrors"
|
||||
data-logging-settings-tab="errors" type="button">Error log</button>
|
||||
</nav>
|
||||
<form class="logging-settings-subform" id="logging-form">
|
||||
<div data-logging-settings-page="outputs">
|
||||
<p class="fine" data-i18n="loggingHelp">Live log viewing stays in memory. External sinks
|
||||
run on a bounded background queue and never block proxy request I/O.</p>
|
||||
<article class="card soft-card">
|
||||
<h3 class="subheading" data-i18n="loggingCaptureTitle">Captured request dimensions</h3>
|
||||
<label class="check"><input id="log-capture-user-agent" type="checkbox" /><span data-i18n="loggingCaptureUserAgent">Capture User-Agent</span></label>
|
||||
<label class="check"><input id="log-capture-country" type="checkbox" /><span data-i18n="loggingCaptureCountry">Capture country from GeoIP</span></label>
|
||||
<p class="fine" data-i18n="loggingCaptureCountryHelp">Country lookup is optional and disabled by default. Enable it only when country logs/statistics are needed; country ACL/rate-limit rules still resolve GeoIP when required.</p>
|
||||
</article>
|
||||
<div class="logging-grid">
|
||||
<article class="card">
|
||||
<h3 class="subheading" data-i18n="loggingFile">JSON file</h3>
|
||||
<label class="check"><input id="log-file-enabled" type="checkbox" /><span
|
||||
@@ -1803,13 +1883,15 @@
|
||||
<label><span data-i18n="loggingLabels">Labels (JSON object)</span><textarea
|
||||
id="log-loki-labels" rows="4" spellcheck="false"></textarea></label>
|
||||
</article>
|
||||
</div>
|
||||
<p class="fine" id="logging-status" role="status"></p>
|
||||
</div>
|
||||
<h3 class="subheading" data-i18n="loggingFormat">Configurable JSON format</h3>
|
||||
<p class="fine" data-i18n="loggingFormatHelp">Map output names to source fields. Leave
|
||||
mapping
|
||||
empty for the complete standard entry.</p>
|
||||
<div class="logging-format-actions"><label><span data-i18n="loggingPreset">Format
|
||||
preset</span><select id="log-format-preset">
|
||||
<div data-logging-settings-page="format" hidden>
|
||||
<h3 class="subheading" data-i18n="loggingFormat">Configurable JSON format</h3>
|
||||
<p class="fine" data-i18n="loggingFormatHelp">Map output names to source fields. Leave
|
||||
mapping empty for the complete standard entry.</p>
|
||||
<div class="logging-format-actions"><label><span data-i18n="loggingPreset">Format
|
||||
preset</span><select id="log-format-preset">
|
||||
<option data-i18n="loggingPresetCustom" value="custom">Custom</option>
|
||||
<option data-i18n="loggingPresetStandard" value="standard">RustProxy
|
||||
standard
|
||||
@@ -1818,32 +1900,29 @@
|
||||
</option>
|
||||
<option data-i18n="loggingPresetEcs" value="ecs">ECS-style</option>
|
||||
<option data-i18n="loggingPresetMinimal" value="minimal">Minimal</option>
|
||||
</select></label>
|
||||
<div class="logging-builder-action"><span class="small-label"
|
||||
data-i18n="loggingBuilder">Visual field builder</span><button
|
||||
class="outline" data-i18n="loggingBuilderOpen" id="log-format-open"
|
||||
type="button">Configure
|
||||
fields</button></div>
|
||||
</div>
|
||||
<details>
|
||||
<summary data-i18n="loggingAdvancedJson">Advanced JSON mapping</summary>
|
||||
<div class="field-grid"><label><span data-i18n="loggingFields">Field mapping (JSON
|
||||
object)</span><textarea id="log-format-fields" rows="7"
|
||||
spellcheck="false"></textarea></label>
|
||||
<label><span data-i18n="loggingStatic">Static fields (JSON
|
||||
object)</span><textarea id="log-format-static" rows="7"
|
||||
spellcheck="false"></textarea></label>
|
||||
</select></label>
|
||||
<div class="logging-builder-action"><span class="small-label"
|
||||
data-i18n="loggingBuilder">Visual field builder</span><button
|
||||
class="outline" data-i18n="loggingBuilderOpen" id="log-format-open"
|
||||
type="button">Configure fields</button></div>
|
||||
</div>
|
||||
</details>
|
||||
<p class="fine" id="logging-status" role="status"></p>
|
||||
<details>
|
||||
<summary data-i18n="loggingAdvancedJson">Advanced JSON mapping</summary>
|
||||
<div class="field-grid"><label><span data-i18n="loggingFields">Field mapping (JSON
|
||||
object)</span><textarea id="log-format-fields" rows="7"
|
||||
spellcheck="false"></textarea></label>
|
||||
<label><span data-i18n="loggingStatic">Static fields (JSON
|
||||
object)</span><textarea id="log-format-static" rows="7"
|
||||
spellcheck="false"></textarea></label>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="form-actions"><button class="primary" data-i18n="apply"
|
||||
type="submit">Validate, save
|
||||
and apply</button></div>
|
||||
</form>
|
||||
<form class="card edit-form" id="error-log-form">
|
||||
<div class="card-heading">
|
||||
<h2 data-i18n="errorLogTitle">Error file</h2><span class="pill live" data-i18n="hotApply">LIVE APPLY</span>
|
||||
</div>
|
||||
</form>
|
||||
<form class="logging-settings-subform" data-logging-settings-page="errors" hidden id="error-log-form">
|
||||
<h3 class="subheading" data-i18n="errorLogTitle">Error file</h3>
|
||||
<label class="check"><input id="error-log-enabled" type="checkbox" /><span
|
||||
data-i18n="enabled">Enabled</span></label>
|
||||
<div class="field-grid">
|
||||
@@ -1859,7 +1938,8 @@
|
||||
<p class="fine" id="error-log-status" role="status"></p>
|
||||
<div class="form-actions"><button class="primary" data-i18n="apply"
|
||||
type="submit">Validate, save and apply</button></div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-labelledby="settings-tab-prometheus" class="settings-page"
|
||||
data-settings-page="prometheus" hidden="" id="settings-page-prometheus" role="tabpanel">
|
||||
@@ -2123,7 +2203,7 @@
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="page-footer"><span>RustProxy <b>0.24.10</b></span><span id="footer-status">API - /
|
||||
<footer class="page-footer"><span>RustProxy <b>0.25.0</b></span><span id="footer-status">API - /
|
||||
dropped: 0</span></footer>
|
||||
</div>
|
||||
</main>
|
||||
@@ -2365,6 +2445,156 @@
|
||||
<p class="fine log-footnote" data-i18n="logBufferHint">Live view starts with the latest 256 completed exchanges.
|
||||
Increase buffer to keep accumulating live entries up to 4096 until the log window is closed.</p>
|
||||
</dialog>
|
||||
<dialog aria-labelledby="web-service-editor-title" class="vhost-dialog" id="web-service-dialog">
|
||||
<form class="card vh-form" id="web-service-form" novalidate="">
|
||||
<div class="card-heading">
|
||||
<h2 data-i18n="webServiceEditor" id="web-service-editor-title">Web Service configuration</h2>
|
||||
<span class="pill" id="web-service-editing">NEW</span>
|
||||
<button class="quiet" data-i18n="close" id="web-service-close" type="button">Close</button>
|
||||
</div>
|
||||
<nav aria-label="Web Service configuration" class="vh-tabs" id="web-service-tabs"></nav>
|
||||
<div class="web-service-pages">
|
||||
<div class="vh-grid web-service-page" data-web-service-page="general">
|
||||
<fieldset class="vh-group vh-wide">
|
||||
<legend data-i18n="webServicePresetTitle">Configuration preset</legend>
|
||||
<div class="vh-grid">
|
||||
<label><span data-i18n="webServicePreset">Preset</span><select id="web-service-preset">
|
||||
<option data-i18n="webServicePresetCustom" value="custom">Custom</option>
|
||||
<option data-i18n="webServicePresetStatic" value="static">Static site</option>
|
||||
<option data-i18n="webServicePresetSpa" value="spa">SPA</option>
|
||||
<option data-i18n="webServicePresetPhpGeneric" value="php_generic">PHP generic</option>
|
||||
<option value="wordpress">WordPress</option>
|
||||
<option value="drupal">Drupal</option>
|
||||
<option value="laravel">Laravel</option>
|
||||
<option value="symfony">Symfony</option>
|
||||
<option value="joomla">Joomla</option>
|
||||
<option value="prestashop">PrestaShop</option>
|
||||
<option data-i18n="webServicePresetDownloads" value="downloads">Downloads / file repository</option>
|
||||
</select></label>
|
||||
<div class="vh-field"><span class="small-label" data-i18n="webServicePresetAction">Preset action</span><button class="outline" data-i18n="webServicePresetApply" id="web-service-preset-apply" type="button">Apply preset</button></div>
|
||||
<label class="check"><input id="web-service-hardening-attacks" type="checkbox" /><span data-i18n="webServiceHardeningAttacks">Block common attack probes</span></label>
|
||||
<label class="check"><input id="web-service-hardening-headers" type="checkbox" /><span data-i18n="webServiceHardeningHeaders">Strip unnecessary fingerprint headers</span></label>
|
||||
<p class="fine vh-wide" data-i18n="webServicePresetHelp" id="web-service-preset-summary">Presets fill normal Web Service settings and remain fully editable. Document root, service ID/name and PHP-FPM endpoint are not replaced.</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
<label><span data-i18n="webServiceId">Service ID</span><input id="web-service-id" maxlength="64" pattern="[A-Za-z0-9_-]{1,64}" required="" /></label>
|
||||
<label><span data-i18n="webServiceName">Name</span><input id="web-service-name" maxlength="96" required="" /></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceRoot">Document root</span><input id="web-service-root" maxlength="4096" placeholder="/var/www/main" required="" /></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceIndex">Index files</span><textarea id="web-service-index" maxlength="4096" rows="5" spellcheck="false"></textarea><small class="fine" data-i18n="webServiceIndexHelp">Optional ordered index list. index.php may be used when PHP-FPM is enabled.</small></label>
|
||||
<label class="check"><input id="web-service-listing" type="checkbox" /><span data-i18n="webServiceDirectoryListing">Directory listing</span></label>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="routing" hidden>
|
||||
<label class="vh-wide"><span data-i18n="webServiceTryFiles">try_files</span><textarea id="web-service-try-files" maxlength="8192" rows="7" spellcheck="false" placeholder="$uri $uri/ /index.html"></textarea><small class="fine" data-i18n="webServiceTryFilesHelp">One candidate per line. Supports $uri, $query_string and a final =404/other error status.</small></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceRewrites">Rewrite rules</span><textarea id="web-service-rewrites" maxlength="32768" rows="8" spellcheck="false" placeholder="^/old/(.*)$ => 301:/new/$1 ^/app$ => /index.html"></textarea><small class="fine" data-i18n="webServiceRewritesHelp">One regex rule per line. Use pattern => /target for an internal rewrite or pattern => 301:/target for a redirect.</small></label>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="htaccess" hidden>
|
||||
<label class="check"><input id="web-service-htaccess-enabled" type="checkbox" /><span data-i18n="webServiceHtaccessEnabled">Enable .htaccess compatibility</span></label>
|
||||
<label><span data-i18n="webServiceHtaccessFilename">Configuration file</span><input id="web-service-htaccess-filename" maxlength="64" value=".htaccess" /></label>
|
||||
<label class="check"><input id="web-service-htaccess-recursive" type="checkbox" checked /><span data-i18n="webServiceHtaccessRecursive">Scan subdirectories</span></label>
|
||||
<label class="check"><input id="web-service-htaccess-strict" type="checkbox" /><span data-i18n="webServiceHtaccessStrict">Strict unsupported-directive mode</span></label>
|
||||
<div class="vh-wide"><button class="outline" id="web-service-htaccess-help" data-i18n="webServiceHtaccessDocs" type="button">Compatibility documentation</button></div>
|
||||
<p class="fine vh-wide" data-i18n="webServiceHtaccessHelp">RustProxy compiles a bounded Apache-compatible subset when the Web Service is applied. Files are not parsed on every request; apply the Web Service again after editing .htaccess on disk.</p>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="mime" hidden>
|
||||
<label class="vh-wide"><span data-i18n="webServiceMimeOverrides">MIME overrides</span><textarea id="web-service-mime-types" maxlength="16384" rows="8" spellcheck="false" placeholder="wasm = application/wasm webp = image/webp"></textarea><small class="fine" data-i18n="webServiceMimeHelp">Optional extension-to-Content-Type overrides, one per line. Built-in mappings are used for all other known extensions.</small></label>
|
||||
<label><span data-i18n="webServiceDefaultMime">Default MIME type</span><input id="web-service-default-mime" maxlength="256" value="application/octet-stream" required="" /></label>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="cache" hidden>
|
||||
<label><span data-i18n="webServiceCacheControl">Default Cache-Control</span><input id="web-service-cache-control" maxlength="1024" placeholder="public, max-age=3600" /></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceCacheRules">Cache-Control rules</span><textarea id="web-service-cache-rules" maxlength="32768" rows="7" spellcheck="false" placeholder="ext:html = no-cache mime:image/ = public, max-age=604800"></textarea><small class="fine" data-i18n="webServiceCacheRulesHelp">Specific rules are evaluated in order before the default. Match ext:css or mime:image/.</small></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceResponseHeaders">Response headers</span><textarea id="web-service-response-headers" maxlength="32768" rows="7" spellcheck="false" placeholder="X-Content-Type-Options = nosniff"></textarea><small class="fine" data-i18n="webServiceResponseHeadersHelp">Fixed headers for this service. Vhost/location response-header rules are applied later and can override them.</small></label>
|
||||
<p class="fine vh-wide" data-i18n="webServiceCacheHelp">Local files use filesystem/OS page cache; this controls browser/proxy caching and does not create a second RustProxy object cache.</p>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="delivery" hidden>
|
||||
<label class="check"><input id="web-service-precompressed" type="checkbox" /><span data-i18n="webServicePrecompressed">Serve precompressed sidecars</span></label>
|
||||
<label class="check"><input id="web-service-precompressed-br" type="checkbox" /><span data-i18n="webServicePrecompressedBr">Brotli (.br)</span></label>
|
||||
<label class="check"><input id="web-service-precompressed-gzip" type="checkbox" /><span data-i18n="webServicePrecompressedGzip">Gzip (.gz)</span></label>
|
||||
<label class="vh-wide"><span data-i18n="webServiceDownloadExtensions">Download extensions</span><textarea id="web-service-download-extensions" maxlength="4096" rows="5" spellcheck="false" placeholder="zip pdf tar"></textarea><small class="fine" data-i18n="webServiceDownloadExtensionsHelp">Files with these extensions receive Content-Disposition: attachment.</small></label>
|
||||
<p class="fine vh-wide" data-i18n="webServiceRangeHelp">Static and precompressed representations keep validators, single-range requests, 206/416 and streaming delivery.</p>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="php" hidden>
|
||||
<label class="check"><input id="web-service-php-enabled" type="checkbox" /><span data-i18n="webServicePhpEnabled">Enable PHP-FPM / FastCGI</span></label>
|
||||
<label class="vh-wide"><span data-i18n="webServicePhpEndpoint">PHP-FPM endpoint</span><input id="web-service-php-endpoint" maxlength="512" placeholder="unix:/run/php/php-fpm.sock" /><small class="fine" data-i18n="webServicePhpEndpointHelp">Unix: unix:/run/php/php-fpm.sock. TCP: tcp:127.0.0.1:9000 or tcp:[::1]:9000. TCP is intentionally limited to loopback addresses.</small></label>
|
||||
<label><span data-i18n="webServicePhpExtensions">PHP extensions</span><input id="web-service-php-extensions" maxlength="512" placeholder="php" /></label>
|
||||
<label class="check"><input id="web-service-php-pass-authorization" type="checkbox" /><span data-i18n="webServicePhpPassAuthorization">Forward Authorization header</span></label>
|
||||
<label><span data-i18n="webServicePhpConnectTimeout">Connect timeout (ms)</span><input id="web-service-php-connect-timeout" min="50" max="60000" step="1" type="number" /></label>
|
||||
<label><span data-i18n="webServicePhpRequestTimeout">Total FastCGI request timeout (ms)</span><input id="web-service-php-request-timeout" min="100" max="3600000" step="1" type="number" /></label>
|
||||
<label><span data-i18n="webServicePhpConcurrency">Max concurrent PHP requests</span><input id="web-service-php-concurrency" min="1" max="4096" step="1" type="number" /></label>
|
||||
<label><span data-i18n="webServicePhpBodyLimit">PHP request body limit (bytes, 0 = unlimited)</span><input id="web-service-php-body-limit" min="0" max="8589934592" step="1" type="number" /></label>
|
||||
<label><span data-i18n="webServicePhpHeaderLimit">Max FastCGI response header bytes</span><input id="web-service-php-header-limit" min="4096" max="1048576" step="1" type="number" /></label>
|
||||
<label><span data-i18n="webServicePhpStderrLimit">Max PHP-FPM stderr log bytes</span><input id="web-service-php-stderr-limit" min="0" max="65536" step="1" type="number" /></label>
|
||||
<label class="vh-wide"><span data-i18n="webServicePhpCustomParams">Custom FastCGI parameters</span><textarea id="web-service-php-custom-params" maxlength="65536" rows="6" spellcheck="false" placeholder="APP_ENV = production APP_DEBUG = 0"></textarea><small class="fine" data-i18n="webServicePhpCustomParamsHelp">One NAME = value entry per line. Static trusted values only; core CGI, HTTP_* and PHP_* control names are reserved.</small></label>
|
||||
<label class="vh-wide"><span data-i18n="webServicePhpValues">PHP INI overrides (PHP_VALUE)</span><textarea id="web-service-php-values" maxlength="65536" rows="6" spellcheck="false" placeholder="memory_limit = 256M upload_max_filesize = 64M post_max_size = 64M"></textarea><small class="fine" data-i18n="webServicePhpValuesHelp">One php.ini directive = value per line. RustProxy sends the validated set as PHP_VALUE; PHP_ADMIN_VALUE is never accepted here.</small></label>
|
||||
<div class="vh-wide form-actions"><button class="outline" data-i18n="webServicePhpDocsButton" id="web-service-php-help" type="button">FastCGI syntax and safety</button></div>
|
||||
<p class="fine vh-wide" data-i18n="webServicePhpHelp">RustProxy speaks FastCGI directly to a long-running PHP-FPM pool. PHP is not spawned per request and PHP source is never served when PHP handling is selected.</p>
|
||||
</div>
|
||||
<div class="vh-grid web-service-page" data-web-service-page="security" hidden>
|
||||
<label><span data-i18n="webServiceSymlinkPolicy">Symlink policy</span><select id="web-service-symlink-policy"><option data-i18n="webServiceSymlinkDeny" value="deny">Deny</option><option data-i18n="webServiceSymlinkInsideRoot" value="inside_root">Allow only inside document root (Linux)</option></select></label>
|
||||
<div class="vh-wide">
|
||||
<span class="small-label" data-i18n="webServiceHardeningTitle">Request/response hardening</span>
|
||||
<p class="fine" data-i18n="webServiceHardeningSecurityHelp">Hardening switches are shown beside the preset selector on General. They remain independent settings after a preset is applied.</p>
|
||||
</div>
|
||||
<p class="fine vh-wide" data-i18n="webServiceSafety">Document root is a strict filesystem boundary. The default denies symlinks. Linux can optionally follow only targets that resolve inside the root; PHP-FPM currently requires the deny policy.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="primary" data-i18n="apply" type="submit">Validate, save and apply</button>
|
||||
<button class="quiet" data-i18n="vhReset" id="web-service-reset" type="button">Discard changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog aria-labelledby="htaccess-help-title" class="vhost-dialog location-help-dialog" id="htaccess-help-dialog">
|
||||
<div class="window-heading">
|
||||
<h2 data-i18n="webServiceHtaccessDocsTitle" id="htaccess-help-title">.htaccess compatibility</h2>
|
||||
<button class="outline" data-i18n="close" id="htaccess-help-close" type="button">Close</button>
|
||||
</div>
|
||||
<article class="card location-help-content">
|
||||
<p data-i18n="webServiceHtaccessDocsIntro">This is a migration-oriented compatibility layer, not a complete Apache runtime. Supported directives are compiled into RustProxy Web Server rules.</p>
|
||||
<h3 data-i18n="webServiceHtaccessDocsSupportedTitle">Supported subset</h3>
|
||||
<p><code>RewriteEngine</code>, <code>RewriteBase</code>, <code>RewriteCond</code>, <code>RewriteRule</code>, <code>DirectoryIndex</code>, <code>Options +/-Indexes</code>, <code>AddType</code>, <code>AddHandler</code> (PHP extensions), <code>Header set/add/unset</code>, <code>ExpiresActive</code>, <code>ExpiresDefault</code>, <code>ExpiresByType</code>, <code>Require all granted/denied</code>.</p>
|
||||
<h3 data-i18n="webServiceHtaccessDocsRewriteTitle">Rewrite compatibility</h3>
|
||||
<p data-i18n="webServiceHtaccessDocsRewrite">Rewrite conditions support REQUEST_FILENAME file tests (-f, -d, -s), REQUEST_URI, REQUEST_METHOD, QUERY_STRING, HTTP_HOST, HTTPS, DOCUMENT_ROOT, THE_REQUEST and HTTP headers. Flags: L, END, R, R=301/302/307/308, QSA, NC, F, OR. PT/NE are accepted as no-ops. E= is accepted as a compatibility no-op because request HTTP headers already reach PHP-FPM.</p>
|
||||
<pre><code>RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]</code></pre>
|
||||
<h3 data-i18n="webServiceHtaccessDocsPhpTitle">PHP-FPM</h3>
|
||||
<p data-i18n="webServiceHtaccessDocsPhp">.htaccess may add PHP extensions with AddHandler, but the PHP-FPM endpoint, timeouts and concurrency remain controlled by the Web Service PHP-FPM tab. Apache SetHandler proxy socket syntax is intentionally not accepted.</p>
|
||||
<h3 data-i18n="webServiceHtaccessDocsReloadTitle">Loading and limits</h3>
|
||||
<p data-i18n="webServiceHtaccessDocsReload">Files are read and compiled on Validate, save and apply. RustProxy does not parse .htaccess per request. Re-apply after changing a file. The scan is bounded to 128 files, depth 16, 128 KiB per file and 1 MiB total.</p>
|
||||
<h3 data-i18n="webServiceHtaccessDocsUnsupportedTitle">Not Apache-complete</h3>
|
||||
<p data-i18n="webServiceHtaccessDocsUnsupported">ErrorDocument, SetEnvIf, Redirect/RedirectMatch and FallbackResource are not implemented in this compatibility layer. General Files/FilesMatch, unsupported authentication/authorization directives, non-PHP handlers and SetHandler are rejected even when Strict mode is off because silently ignoring them could weaken access control or expose source. .htaccess/.htpasswd files are always blocked from HTTP delivery. Other unsupported non-security directives are logged and ignored unless Strict mode is enabled.</p>
|
||||
<p class="fine" data-i18n="webServiceHtaccessDocsSecurity">Vhost/location ACL, authentication, error pages and response rules still remain authoritative. .htaccess cannot escape the document root, select an arbitrary FastCGI socket or override protected framing headers.</p>
|
||||
</article>
|
||||
</dialog>
|
||||
<dialog aria-labelledby="php-help-title" class="vhost-dialog location-help-dialog" id="php-help-dialog">
|
||||
<div class="window-heading">
|
||||
<h2 data-i18n="webServicePhpDocsTitle" id="php-help-title">PHP-FPM / FastCGI configuration</h2>
|
||||
<button class="outline" data-i18n="close" id="php-help-close" type="button">Close</button>
|
||||
</div>
|
||||
<article class="card location-help-content">
|
||||
<p data-i18n="webServicePhpDocsIntro">RustProxy connects directly to an existing PHP-FPM pool. Prefer a Unix socket on the same host. TCP is accepted only on loopback and is not a public/network FastCGI transport.</p>
|
||||
<h3 data-i18n="webServicePhpDocsEndpointTitle">Endpoint syntax</h3>
|
||||
<pre><code>unix:/run/php/php-fpm.sock
|
||||
tcp:127.0.0.1:9000
|
||||
tcp:[::1]:9000</code></pre>
|
||||
<p class="fine" data-i18n="webServicePhpDocsEndpoint">For TCP, include the tcp: prefix, a literal loopback IP and port. Hostnames and non-loopback addresses are rejected. For Unix sockets, RustProxy must have permission to connect to the socket.</p>
|
||||
<h3 data-i18n="webServicePhpDocsCustomTitle">Custom FastCGI parameters</h3>
|
||||
<pre><code>APP_ENV = production
|
||||
APP_DEBUG = 0
|
||||
TENANT = main</code></pre>
|
||||
<p data-i18n="webServicePhpDocsCustom">Use one NAME = value entry per line. Names are normalized to uppercase and may contain A-Z, 0-9 and underscore. These are static trusted operator values. RustProxy rejects core CGI names, HTTP_* names, PHP_VALUE and PHP_ADMIN_VALUE so they cannot override request routing, script selection or HTTP-derived parameters.</p>
|
||||
<h3 data-i18n="webServicePhpDocsValuesTitle">PHP INI overrides</h3>
|
||||
<pre><code>memory_limit = 256M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_execution_time = 30</code></pre>
|
||||
<p data-i18n="webServicePhpDocsValues">These lines are combined into the FastCGI PHP_VALUE parameter. PHP-FPM/PHP still decides which directives are changeable at this scope. Newlines/control characters are rejected and PHP_ADMIN_VALUE is intentionally unavailable.</p>
|
||||
<h3 data-i18n="webServicePhpDocsLimitsTitle">Limits and headers</h3>
|
||||
<p data-i18n="webServicePhpDocsLimits">Request timeout covers the FastCGI exchange. The body limit is enforced while streaming and returns 413 when exceeded; 0 disables the RustProxy-side body limit. Response-header and stderr limits bound memory/diagnostic data. Authorization forwarding can be disabled for applications that do not need HTTP_AUTHORIZATION.</p>
|
||||
<p class="fine" data-i18n="webServicePhpDocsSecurity">Do not put secrets in custom parameters or PHP_VALUE unless you accept storing them in normal Web Service configuration. Use a dedicated non-root PHP-FPM pool identity and keep PHP-FPM off public networks.</p>
|
||||
</article>
|
||||
</dialog>
|
||||
<dialog aria-labelledby="vhost-editor-title" class="vhost-dialog" id="vhost-dialog">
|
||||
<form class="card vh-form" id="vhost-form" novalidate="">
|
||||
<div class="card-heading">
|
||||
|
||||
Reference in new issue
Block a user