v0.26.7
This commit is contained in:
1 parent
5d2aaeabe6
commit
9d6614c1b4
62 files changed
+558
-230
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user