This commit is contained in:
Mateusz Gruszczyński committed 2026-10-03 18:53:51 +02:00
1 parent d3f0f060bf
commit 2e4aa4df4d
33 files changed
+3704 -3566

No files matched your search

+11 -8
View File
@@ -778,7 +778,7 @@ function renderSettings() {
if (!settingsDirty.has("response-form")) {
const server = config.server_header || {}, compression = config.compression || {}, diagnostics = config.diagnostic_headers || {};
$("expose-request-id").checked = !!diagnostics.request_id;
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.24.9";
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.24.10";
for (const name of ["gzip", "br", "zstd", "deflate"]) $(`compress-${name}`).checked = !!compression[name];
$("compress-min").value = compression.min_bytes ?? 1024; syncServerMode();
}
@@ -821,23 +821,23 @@ function renderSettings() {
updateTransformResourceSizing();
}
if (!settingsDirty.has("logging-form")) {
const l = config.logging || {}, file = l.file || {}, logstash = l.logstash || {}, loki = l.loki || {}, format = l.format || {}, errorLog = config.error_log || {};
const l = config.logging || {}, file = l.file || {}, logstash = l.logstash || {}, loki = l.loki || {}, format = l.format || {};
$("log-file-enabled").checked = !!file.enabled; $("log-file-per-vhost").checked = !!file.per_vhost; $("log-file-path").value = file.path || "/var/log/rustproxy/access.jsonl"; $("log-file-directory").value = file.directory || "/var/log/rustproxy/vhosts"; syncLoggingFileMode();
$("log-logstash-enabled").checked = !!logstash.enabled; $("log-logstash-address").value = logstash.address || "127.0.0.1:5044";
$("log-loki-enabled").checked = !!loki.enabled; $("log-loki-url").value = loki.url || "http://127.0.0.1:3100/loki/api/v1/push"; $("log-loki-token").value = loki.bearer_token_env || "";
$("log-loki-labels").value = JSON.stringify(loki.labels || { job: "rustproxy" }, null, 2); $("log-format-fields").value = JSON.stringify(format.fields || {}, null, 2); $("log-format-static").value = JSON.stringify(format.static_fields || {}, null, 2);
$("log-format-preset").value = detectLoggingPreset(format.fields || {});
}
if (!settingsDirty.has("error-log-form")) {
const errorLog = config.error_log || {};
$("error-log-enabled").checked = !!errorLog.enabled;
$("error-log-path").value = errorLog.path || "/var/log/rustproxy/error.log";
$("error-log-format").value = ["JSON", "RAW"].includes(errorLog.format) ? errorLog.format : "JSON";
syncErrorLogMode();
}
const sink = latestStatus?.logging?.sinks, errorSink = latestStatus?.logging?.error_file;
if (sink) {
let status = `${t("loggingQueue")}: ${fmt(sink.queue_dropped)} ${t("loggingDropped")} / ${fmt(sink.sent)} ${t("loggingSent")} / ${fmt(sink.errors)} ${t("errors")} · ${t("loggingCapacity")}: ${fmt(sink.queue_capacity)}`;
if (errorSink) status += ` · ${t("errorLogTitle")}: ${fmt(errorSink.queue_dropped)} ${t("loggingDropped")} / ${fmt(errorSink.written)} ${t("loggingSent")} / ${fmt(errorSink.errors)} ${t("errors")}`;
$("logging-status").textContent = status;
}
$("logging-status").textContent = sink ? `${t("loggingQueue")}: ${fmt(sink.queue_dropped)} ${t("loggingDropped")} / ${fmt(sink.sent)} ${t("loggingSent")} / ${fmt(sink.errors)} ${t("errors")} · ${t("loggingCapacity")}: ${fmt(sink.queue_capacity)}` : "";
$("error-log-status").textContent = errorSink ? `${t("loggingQueue")}: ${fmt(errorSink.queue_dropped)} ${t("loggingDropped")} / ${fmt(errorSink.written)} ${t("loggingSent")} / ${fmt(errorSink.errors)} ${t("errors")} · ${t("loggingCapacity")}: ${fmt(errorSink.queue_capacity)}` : "";
if (!settingsDirty.has("forwarding-form")) { $("trusted-proxies").value = (config.forwarding?.trusted_proxies || []).join("\n"); $("trusted-proto").checked = !!config.forwarding?.trust_forwarded_proto; }
if (!settingsDirty.has("headers-form")) { for (const direction of ["request", "response"]) renderHeaderRules(direction, config.headers?.[direction] || []); }
}
@@ -1120,6 +1120,7 @@ function applyLoggingPreset(name) {
$("log-format-fields").value = JSON.stringify(LOGGING_PRESETS[name], null, 2);
setSettingsDirty("logging-form");
}
const selectLoggingSection = next => ({ logging: next.logging, error_log: next.error_log });
bindSettingsForm("logging-form", next => {
const parseObject = id => parseJsonObjectValue($(id).value, $(id));
next.logging = {
@@ -1128,12 +1129,14 @@ bindSettingsForm("logging-form", next => {
loki: { enabled: $("log-loki-enabled").checked, url: $("log-loki-url").value.trim(), bearer_token_env: $("log-loki-token").value.trim() || null, labels: parseObject("log-loki-labels") },
format: { fields: parseObject("log-format-fields"), static_fields: parseObject("log-format-static") }
};
}, "logging", selectLoggingSection);
bindSettingsForm("error-log-form", next => {
next.error_log = {
enabled: $("error-log-enabled").checked,
path: $("error-log-path").value.trim(),
format: $("error-log-format").value
};
}, "logging");
}, "logging", selectLoggingSection);
bindSettingsForm("forwarding-form", next => { next.forwarding = { trusted_proxies: $("trusted-proxies").value.split(/[\n,]/).map(s => s.trim()).filter(Boolean), trust_forwarded_proto: $("trusted-proto").checked }; }, "forwarding");
bindSettingsForm("headers-form", next => { next.headers = { request: collectHeaderRules("request"), response: collectHeaderRules("response") }; }, "headers");
$("log-format-preset").addEventListener("change", event => applyLoggingPreset(event.target.value));
+32 -21
View File
@@ -479,17 +479,22 @@
by interface</span><b id="network-interface-chart-value">-</b></div>
<div class="network-interface-charts" id="network-interface-charts"></div>
</div>
<details class="mini-live-chart mini-live-chart-collapsible">
<summary class="mini-live-chart-head"><span
data-i18n="responseLatencyHistory">Response time history</span><b
id="latency-chart-value">-</b></summary>
<div class="mini-live-chart">
<div class="mini-live-chart-head"><span
data-i18n="responseLatencyHistory">Response time history</span><span
class="mini-live-chart-actions"><details class="metric-info">
<summary data-i18n-aria="responseLatencyInfo" aria-label="Response time information">i</summary>
<div class="metric-info-popover">
<p class="fine" data-i18n="responseLatencyHint">The value is the cumulative mean duration of completed HTTP exchanges since process start. The chart shows successive samples of that mean; it is not a percentile or the latency of one request.</p>
</div>
</details><b id="latency-chart-value">-</b></span></div>
<svg aria-label="Mean response latency history"
data-i18n-aria="responseLatencyHistory" role="img" viewbox="0 0 300 60">
<path class="chart-grid" d="M0 15H300 M0 30H300 M0 45H300"></path>
<polyline class="diagnostic-line" id="latency-sparkline" points="0,55 300,55">
</polyline>
</svg>
</details>
</div>
</div>
</article>
</div>
@@ -1391,7 +1396,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.9"
id="server-value" maxlength="256" placeholder="RustProxy/v0.24.10"
spellcheck="false" />
<h3 class="subheading" data-i18n="diagnosticHeaders">Diagnostic headers</h3>
<div class="switch-grid">
@@ -1798,20 +1803,6 @@
<label><span data-i18n="loggingLabels">Labels (JSON object)</span><textarea
id="log-loki-labels" rows="4" spellcheck="false"></textarea></label>
</article>
<article class="card">
<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>
<label><span data-i18n="errorLogPath">Error log path</span><input
id="error-log-path" maxlength="1024"
placeholder="/var/log/rustproxy/error.log" /></label>
<label><span data-i18n="errorLogFormat">Format</span><select
id="error-log-format">
<option value="JSON">JSON</option>
<option value="RAW">RAW</option>
</select></label>
<p class="fine" data-i18n="errorLogHelp">Only ERROR events are written. Rotation is handled by system logrotate.</p>
</article>
</div>
<h3 class="subheading" data-i18n="loggingFormat">Configurable JSON format</h3>
<p class="fine" data-i18n="loggingFormatHelp">Map output names to source fields. Leave
@@ -1849,6 +1840,26 @@
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>
<label class="check"><input id="error-log-enabled" type="checkbox" /><span
data-i18n="enabled">Enabled</span></label>
<div class="field-grid">
<label><span data-i18n="errorLogPath">Error log path</span><input
id="error-log-path" maxlength="1024"
placeholder="/var/log/rustproxy/error.log" /></label>
<label><span data-i18n="errorLogFormat">Format</span><select
id="error-log-format">
<option value="JSON">JSON</option>
<option value="RAW">RAW</option>
</select></label>
</div>
<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>
</div>
<div aria-labelledby="settings-tab-prometheus" class="settings-page"
data-settings-page="prometheus" hidden="" id="settings-page-prometheus" role="tabpanel">
@@ -2112,7 +2123,7 @@
</section>
</div>
</section>
<footer class="page-footer"><span>RustProxy <b>0.24.9</b></span><span id="footer-status">API - /
<footer class="page-footer"><span>RustProxy <b>0.24.10</b></span><span id="footer-status">API - /
dropped: 0</span></footer>
</div>
</main>
+12 -29
View File
@@ -1758,6 +1758,14 @@ progress {
text-align: right
}
.mini-live-chart-actions {
display: inline-flex;
align-items: center;
justify-content: flex-end;
gap: 7px;
min-width: 0
}
.mini-live-chart svg {
display: block;
width: 100%;
@@ -3773,6 +3781,10 @@ body[data-density="dense"] .stat strong {
max-width: none
}
.settings-page>.edit-form + .edit-form {
margin-top: var(--ui-gap)
}
.settings-page>.card {
padding: var(--ui-card)
}
@@ -6359,31 +6371,6 @@ body[data-density="compact"] .page-footer {
}
}
.mini-live-chart-collapsible>summary {
cursor: pointer;
list-style: none;
user-select: none
}
.mini-live-chart-collapsible>summary::-webkit-details-marker {
display: none
}
.mini-live-chart-collapsible>summary::before {
content: "▸";
flex: 0 0 auto;
color: var(--muted);
margin-right: 1px
}
.mini-live-chart-collapsible[open]>summary::before {
content: "▾"
}
.mini-live-chart-collapsible>summary>span {
flex: 1 1 auto
}
.network-interface-charts {
gap: 8px;
padding-right: 0
@@ -6552,10 +6539,6 @@ body[data-density="compact"] .page-footer {
}
}
.mini-live-chart-collapsible:not([open])>summary {
margin-bottom: 0
}
.docker-route-grid {
grid-template-columns: minmax(190px, 1fr) minmax(250px, 1.35fr) minmax(210px, 1fr) minmax(120px, .65fr)
}
+1 -1
View File
@@ -600,7 +600,7 @@
authMode.addEventListener("change", syncAuth); syncAuth();
g = group("vhResponsePolicy"); const server = value.server_header, diagnostics = value.diagnostic_headers;
const serverMode = field(g, "server-mode", "serverHeader", server?.mode || "inherit", "select", { values: [["inherit", "vhInherit"], ["preserve", "serverPreserve"], ["hide", "serverHide"], ["custom", "serverCustom"]] });
const serverValue = field(g, "server-value", "serverValue", server?.value || "RustProxy/v0.24.9", "text", { maxLength: 256 });
const serverValue = field(g, "server-value", "serverValue", server?.value || "RustProxy/v0.24.10", "text", { maxLength: 256 });
const syncServer = () => { serverValue.disabled = serverMode.value !== "custom"; serverValue.required = !serverValue.disabled; }; serverMode.addEventListener("change", syncServer); syncServer();
const inheritDiagnostics = field(g, "diag-inherit", "diagInherit", !diagnostics, "checkbox"), diagInputs = [];
for (const [name, key] of [["request_id", "diagRequestId"]]) diagInputs.push(field(g, `diag-${name}`, key, diagnostics?.[name] ?? envelope?.config.diagnostic_headers?.[name], "checkbox"));