This commit is contained in:
Mateusz Gruszczyński committed 2026-10-06 23:08:43 +02:00
1 parent 5d2aaeabe6
commit 9d6614c1b4
62 files changed
+558 -230

No files matched your search

+17 -42
View File
@@ -1,5 +1,15 @@
//! Embedded Markdown documents for the authenticated Reader.
macro_rules! reader_doc {
($path:literal, $category:literal) => {
(
$path,
$category,
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path)),
)
};
}
pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
(
"README.md",
@@ -399,48 +409,13 @@ pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
"/docs/V0.25.1_AUDIT.md"
)),
),
(
"docs/V0.26.6_IMPLEMENTATION.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/V0.26.6_IMPLEMENTATION.md")),
),
(
"docs/V0.26.5_IMPLEMENTATION.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/V0.26.5_IMPLEMENTATION.md")),
),
(
"docs/V0.26.4_IMPLEMENTATION.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/V0.26.4_IMPLEMENTATION.md"
)),
),
(
"docs/V0.26.3_IMPLEMENTATION.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/V0.26.3_IMPLEMENTATION.md"
)),
),
(
"docs/V0.26.2_IMPLEMENTATION.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/V0.26.2_IMPLEMENTATION.md"
)),
),
(
"docs/V0.26.1_IMPLEMENTATION.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/V0.26.1_IMPLEMENTATION.md"
)),
),
reader_doc!("docs/V0.26.7_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.6_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.5_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.4_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.3_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.2_IMPLEMENTATION.md", "technical"),
reader_doc!("docs/V0.26.1_IMPLEMENTATION.md", "technical"),
(
"docs/V0.25.5_AUDIT.md",
"audits",
+1 -1
View File
@@ -2,7 +2,7 @@
"openapi": "3.0.3",
"info": {
"title": "RustProxy Administration API",
"version": "0.26.6",
"version": "0.26.7",
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust."
},
"servers": [
+1 -1
View File
@@ -6,7 +6,7 @@ function renderSettings() {
if (!settingsDirty.has("response-form")) {
const server = config.server_header || {}, compression = config.compression || {}, diagnostics = config.diagnostic_headers || {};
$("expose-request-id").checked = !!diagnostics.request_id;
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.26.6";
$("server-mode").value = server.mode || "preserve"; $("server-value").value = server.value || "RustProxy/v0.26.7";
for (const name of ["gzip", "br", "zstd", "deflate"]) $(`compress-${name}`).checked = !!compression[name];
$("compress-min").value = compression.min_bytes ?? 1024; syncServerMode();
}
+27 -5
View File
@@ -10,6 +10,19 @@
const scope = () => $("cache-scope").value;
const domainFilter = () => $("cache-domain-filter").value;
const number = v => fmt(v ?? 0);
const occupancy = (used, limit) => {
const boundedLimit = Number(limit) || 0;
if (boundedLimit <= 0) return 0;
return Math.min(100, Math.max(0, (Number(used) || 0) / boundedLimit * 100));
};
const setMeter = (id, used, limit) => {
const value = occupancy(used, limit), meter = $(id);
if (meter) {
meter.value = value;
meter.setAttribute("aria-valuetext", `${value.toFixed(1)}%`);
}
return value;
};
const operationMessage = (id, value = "", error = false) => {
const box = $(id);
if (!box) return;
@@ -111,18 +124,27 @@
const total = field => groups.reduce((n, row) => n + (row[field] || 0), 0), all = scope() === "*";
const hits = total("hits"), misses = total("misses");
const hotEntries = all ? (m.entries || 0) : total("hot_entries"), coldEntries = all ? (m.cold_entries || 0) : total("cold_entries");
const hotEntryLimit = Number(data.settings?.max_entries) || 0;
const hotEntryPercent = setMeter("cp-hot-entries-meter", hotEntries, hotEntryLimit);
$("cp-entries").textContent = number(hotEntries);
$("cp-capacity").textContent = `${t("cacheGlobalLimit")}: ${number(data.settings?.max_entries)}`;
$("cp-capacity").textContent = `${t("cacheGlobalLimit")}: ${number(hotEntryLimit)} · ${hotEntryPercent.toFixed(1)}%`;
const resident = all ? (m.hot_allocated_bytes ?? m.resident_bytes ?? 0) : total("resident_bytes");
const logical = all ? (m.hot_logical_bytes ?? 0) : total("logical_bytes");
const hotLimit = m.hot_limit_bytes ?? data.settings?.max_bytes ?? 0;
$("cp-bytes").textContent = fmtBytes(resident); $("cp-memory").value = resident / Math.max(1, hotLimit) * 100;
$("cp-memory-detail").textContent = `${t("cacheLogicalShort")}: ${fmtBytes(logical)} · ${t("cacheLimitShort")}: ${fmtBytes(hotLimit)}`;
const hotPercent = setMeter("cp-memory", resident, hotLimit);
$("cp-bytes").textContent = fmtBytes(resident);
$("cp-memory-detail").textContent = `${t("cacheLogicalShort")}: ${fmtBytes(logical)} · ${t("cacheLimitShort")}: ${fmtBytes(hotLimit)} · ${hotPercent.toFixed(1)}%`;
$("cp-transient").textContent = `${fmtBytes(m.transient_bytes)} / ${fmtBytes(m.transient_limit_bytes ?? data.settings?.transient_max_bytes)}`;
$("cp-transient-detail").textContent = `${t("cacheFillShort")}: ${fmtBytes(m.fill_bytes_active)} · ${t("cacheQueueShort")}: ${fmtBytes(m.cold_queue_bytes)} / ${fmtBytes(m.cold_queue_limit_bytes ?? data.settings?.cold_queue_max_bytes)} · ${t("cacheIoShort")}: ${fmtBytes((Number(m.cold_write_bytes_active) || 0) + (Number(m.cold_read_bytes_active) || 0))}`;
const coldBytes = all ? (m.cold_bytes || 0) : total("cold_bytes");
$("cp-cold-entries").textContent = number(coldEntries); $("cp-cold-capacity").textContent = `${t("cacheGlobalLimit")}: ${number(data.settings?.cold_max_entries || 0)}`;
$("cp-cold-bytes").textContent = fmtBytes(coldBytes); $("cp-cold-disk").value = coldBytes / Math.max(1, data.settings?.cold_max_bytes || 1) * 100;
const coldEntryLimit = Number(data.settings?.cold_max_entries) || 0;
const coldEntryPercent = setMeter("cp-cold-entries-meter", coldEntries, coldEntryLimit);
$("cp-cold-entries").textContent = number(coldEntries);
$("cp-cold-capacity").textContent = `${t("cacheGlobalLimit")}: ${number(coldEntryLimit)} · ${coldEntryPercent.toFixed(1)}%`;
const coldLimit = Number(data.settings?.cold_max_bytes) || 0;
const coldPercent = setMeter("cp-cold-disk", coldBytes, coldLimit);
$("cp-cold-bytes").textContent = fmtBytes(coldBytes);
$("cp-cold-disk-detail").textContent = `${t("cacheLimitShort")}: ${fmtBytes(coldLimit)} · ${coldPercent.toFixed(1)}%`;
$("cp-ratio").textContent = `${fmt((all ? (m.hit_ratio || 0) : (hits + misses ? hits / (hits + misses) : 0)) * 100, 1)}%`;
$("cp-requests").textContent = `${number(all ? m.hits : hits)} HIT / ${number(all ? m.misses : misses)} MISS`;
const bypasses = all ? (Number(m.bypasses) || 0) : total("bypasses");
+14 -9
View File
@@ -1320,8 +1320,10 @@
</div>
</details>
<div class="stats-grid cache-stats">
<article class="stat"><span data-i18n="cacheHotObjects">Hot objects</span><strong
id="cp-entries">-</strong><small id="cp-capacity">-</small></article>
<article class="stat"><span data-i18n="cacheHotObjects" id="cache-hot-objects-label">Hot objects</span><strong
id="cp-entries">-</strong><small id="cp-capacity">-</small><progress
aria-labelledby="cache-hot-objects-label" id="cp-hot-entries-meter" max="100"
value="0"></progress></article>
<article class="stat"><span data-i18n="cacheHotMemory" id="cache-memory-label">Hot
memory</span><strong id="cp-bytes">-</strong><small
id="cp-memory-detail">-</small><progress aria-labelledby="cache-memory-label"
@@ -1329,10 +1331,13 @@
</article>
<article class="stat"><span data-i18n="cacheTransientMemory">Transient cache RAM</span><strong
id="cp-transient">-</strong><small id="cp-transient-detail">-</small></article>
<article class="stat"><span data-i18n="cacheColdObjects">Cold objects</span><strong
id="cp-cold-entries">-</strong><small id="cp-cold-capacity">-</small></article>
<article class="stat"><span data-i18n="cacheColdDisk">Cold disk</span><strong
id="cp-cold-bytes">-</strong><progress id="cp-cold-disk" max="100" value="0"></progress>
<article class="stat"><span data-i18n="cacheColdObjects" id="cache-cold-objects-label">Cold objects</span><strong
id="cp-cold-entries">-</strong><small id="cp-cold-capacity">-</small><progress
aria-labelledby="cache-cold-objects-label" id="cp-cold-entries-meter" max="100"
value="0"></progress></article>
<article class="stat"><span data-i18n="cacheColdDisk" id="cache-cold-disk-label">Cold disk</span><strong
id="cp-cold-bytes">-</strong><small id="cp-cold-disk-detail">-</small><progress
aria-labelledby="cache-cold-disk-label" id="cp-cold-disk" max="100" value="0"></progress>
</article>
<article class="stat"><span data-i18n="cacheHitRatio">Hit ratio</span><strong
id="cp-ratio">-</strong><small id="cp-requests">-</small></article>
@@ -1919,7 +1924,7 @@
<option data-i18n="serverCustom" value="custom">Replace / add custom value</option>
</select>
<label data-i18n="serverValue" for="server-value">Custom Server value</label><input
id="server-value" maxlength="256" placeholder="RustProxy/v0.26.6"
id="server-value" maxlength="256" placeholder="RustProxy/v0.26.7"
spellcheck="false" />
<h3 class="subheading" data-i18n="diagnosticHeaders">Diagnostic headers</h3>
<div class="switch-grid">
@@ -2718,7 +2723,7 @@
</section>
</div>
</section>
<footer class="page-footer"><span>RustProxy <b>0.26.6</b></span><span id="footer-status">API - /
<footer class="page-footer"><span>RustProxy <b>0.26.7</b></span><span id="footer-status">API - /
dropped: 0</span></footer>
</div>
</main>
@@ -3429,7 +3434,7 @@ max_execution_time = 30</code></pre>
<article class="card about-card">
<div class="about-product">
<span aria-hidden="true" class="brand-mark">r<span>/</span>p</span>
<div><span class="small-label">RustProxy</span><strong>0.26.6</strong></div>
<div><span class="small-label">RustProxy</span><strong>0.26.7</strong></div>
</div>
<dl class="about-meta">
<div>
+1 -1
View File
@@ -72,7 +72,7 @@
for (const direction of ["request", "response"]) {
const rules = value[direction] || []; if (!Array.isArray(rules) || rules.length > 32) fail(); for (const rule of rules) {
if (!rule || typeof rule !== "object" || Object.keys(rule).some(k => !["op", "name", "value"].includes(k)) || !["set", "append", "remove"].includes(rule.op) || typeof rule.name !== "string" || rule.name.length > 128 || !/^[-!#$%&'*+.^_`|~0-9A-Za-z]+$/.test(rule.name)) fail();
const name = rule.name.toLowerCase(); if (["host", "connection", "content-length", "transfer-encoding", "te", "trailer", "content-encoding", "content-range", "upgrade", "keep-alive", "proxy-connection", "proxy-authorization", "proxy-authenticate", "forwarded", "x-real-ip", "x-request-id", "x-rust-proxy-backend", "x-rust-proxy-backend-time", "x-rustproxy-backend-time"].includes(name) || name.startsWith("x-forwarded-") || name.startsWith("sec-websocket-")) throw new Error(t("headersProtected"));
const name = rule.name.toLowerCase(); if (["host", "age", "connection", "content-length", "transfer-encoding", "te", "trailer", "content-encoding", "content-range", "upgrade", "keep-alive", "proxy-connection", "proxy-authorization", "proxy-authenticate", "forwarded", "x-real-ip", "x-request-id", "x-rust-proxy-backend", "x-rust-proxy-backend-time", "x-rustproxy-backend-time"].includes(name) || name.startsWith("x-forwarded-") || name.startsWith("sec-websocket-")) throw new Error(t("headersProtected"));
if (rule.op === "remove") { if (Object.hasOwn(rule, "value") && rule.value !== null) fail(); } else if (typeof rule.value !== "string" || rule.value.length > 8192 || /[\r\n\0]/.test(rule.value)) fail();
}
} return value;