v0.28.0
This commit is contained in:
1 parent
c24bc4279b
commit
3b3f0db377
84 files changed
+2686
-392
No files matched your search
@@ -116,7 +116,7 @@ function appConfirm(message, options = {}) {
|
||||
}
|
||||
function apiArea(path) {
|
||||
const section = path.match(/^\/api\/config\/([^/?]+)/)?.[1];
|
||||
if (section) return ({ "listeners": "settings", "http2": "settings", "docker-agents": "dockerVhosts", "response": "settings", "pages": "settings", "cache": "cache", "transform": "settings", "logging": "settings", "forwarding": "settings", "headers": "settings", "prometheus": "settings", "administration": "administration", "tls": "tls", "vhosts": "vhosts", "redirects": "redirects", "not_found_hosts": "notfound", "streams": "streams", "acl-profiles": "aclProfiles", "host-policies": "security" })[section] || "configuration";
|
||||
if (section) return ({ "listeners": "settings", "http2": "settings", "docker-agents": "dockerVhosts", "response": "settings", "pages": "settings", "cache": "cache", "transform": "settings", "logging": "settings", "geoip": "settings", "forwarding": "settings", "headers": "settings", "prometheus": "settings", "administration": "administration", "tls": "tls", "vhosts": "vhosts", "redirects": "redirects", "not_found_hosts": "notfound", "streams": "streams", "acl-profiles": "aclProfiles", "host-policies": "security" })[section] || "configuration";
|
||||
if (path === "/api/config") return "configuration";
|
||||
if (path.startsWith("/api/traffic")) return "traffic";
|
||||
if (path.startsWith("/api/cache")) return "cache";
|
||||
|
||||
@@ -359,7 +359,28 @@ function renderDiagnostics(status, appendSample = true) {
|
||||
const l = status.listeners || {};
|
||||
$("listener-pending").hidden = !l.restart_required;
|
||||
$("listener-pending").textContent = t("restartPending");
|
||||
$("listener-active").textContent = `${t("activeListeners")}: ${l.active_endpoints?.map(e => `${e.tls ? "HTTPS" : "HTTP"} ${e.address}${e.http3 ? " + H3/UDP" : ""}`).join(" | ") || `HTTP ${status.proxy_listen} / HTTPS ${status.tls_listen || t("notEnabled")}`}`;
|
||||
const activeListeners = $("listener-active"), listenerItems = el("div", undefined, "listener-active-items");
|
||||
let endpoints = Array.isArray(l.active_endpoints) ? l.active_endpoints : [];
|
||||
if (!endpoints.length) {
|
||||
endpoints = [];
|
||||
if (status.proxy_listen) endpoints.push({ tls: false, address: status.proxy_listen, http3: false });
|
||||
if (status.tls_listen) endpoints.push({ tls: true, address: status.tls_listen, http3: false });
|
||||
}
|
||||
for (const endpoint of endpoints) {
|
||||
const item = el("div", undefined, "listener-active-item");
|
||||
item.append(el("strong", endpoint.tls ? "HTTPS" : "HTTP", "listener-active-protocol"), el("code", endpoint.address || "-", "listener-active-address"));
|
||||
const protocols = ["H1"];
|
||||
if (endpoint.tls) {
|
||||
if (endpoint.http2) protocols.push("H2");
|
||||
if (endpoint.http3) protocols.push("H3/UDP");
|
||||
} else if (endpoint.h2c) {
|
||||
protocols.push("H2C");
|
||||
}
|
||||
for (const protocol of protocols) item.append(el("span", protocol, "listener-active-feature"));
|
||||
listenerItems.append(item);
|
||||
}
|
||||
if (!endpoints.length) listenerItems.append(el("span", t("notEnabled"), "fine"));
|
||||
activeListeners.replaceChildren(el("strong", t("activeListeners"), "listener-active-title"), listenerItems);
|
||||
}
|
||||
function renderHeaderRules(direction, rules) {
|
||||
const target = $(`${direction}-header-rules`); target.replaceChildren();
|
||||
|
||||
@@ -30,6 +30,8 @@ function renderSettings() {
|
||||
$("cache-query").checked = !!c.bypass_query; $("cache-prefixes").value = (c.bypass_prefixes || []).join("\n");
|
||||
$("cache-static-cookie").checked = c.static_ignore_cookies !== false; $("cache-static-extensions").value = (c.static_extensions || []).join("\n");
|
||||
$("cache-status-header").checked = !!c.expose_status; $("cache-hits-header").checked = !!c.expose_hits; $("cache-rfc-header").checked = !!c.expose_cache_status; $("cache-resident-age-header").checked = !!c.expose_resident_age;
|
||||
const viaHeader = c.via_header === undefined ? "RustProxy" : c.via_header;
|
||||
$("cache-via-enabled").checked = viaHeader !== null; $("cache-via-value").value = viaHeader || "RustProxy"; syncCacheVia();
|
||||
setCacheMemoryPresetSelection(["conservative", "recommended", "performance"].includes(c.memory_preset) ? c.memory_preset : null);
|
||||
setCacheColdPresetSelection(c.cold_preset === "recommended" ? "recommended" : null);
|
||||
syncColdCachePolicy();
|
||||
@@ -170,6 +172,10 @@ function applyCacheMemoryPreset(kind) {
|
||||
setCacheMemoryPresetSelection(kind);
|
||||
setSettingsDirty("cache-form"); updateCacheMemorySizing();
|
||||
}
|
||||
function syncCacheVia() {
|
||||
const enabled = $("cache-via-enabled")?.checked;
|
||||
if ($("cache-via-value")) $("cache-via-value").disabled = !enabled;
|
||||
}
|
||||
function applyRecommendedColdCachePreset() {
|
||||
const transient = Number($("cache-transient-memory").value);
|
||||
const queue = Number.isFinite(transient) && transient > 0 ? Math.max(0.0625, Math.floor((transient * .5) / 8) * 8 || Math.min(transient, 8)) : 32;
|
||||
@@ -279,7 +285,7 @@ bindSettingsForm("response-form", next => {
|
||||
next.compression = { min_bytes: Number($("compress-min").value) }; for (const name of ["gzip", "br", "zstd", "deflate"]) next.compression[name] = $(`compress-${name}`).checked;
|
||||
}, "response", next => ({ server_header: next.server_header, diagnostic_headers: next.diagnostic_headers, compression: next.compression }));
|
||||
bindSettingsForm("cache-form", next => {
|
||||
next.cache = { ...(next.cache || {}), enabled: $("cache-enabled").checked, memory_preset: selectedCacheMemoryPreset, max_bytes: Math.round(Number($("cache-memory").value) * 1048576), transient_max_bytes: Math.round(Number($("cache-transient-memory").value) * 1048576), max_object_bytes: Number($("cache-object").value) * 1024, max_entries: Number($("cache-entries").value), hot_eviction_max_objects: Number($("cache-eviction-limit").value), hot_auto_trim_enabled: $("cache-auto-trim-enabled").checked, hot_auto_trim_after_seconds: Number($("cache-auto-trim-after").value), hot_auto_trim_target_percent: Number($("cache-auto-trim-target").value), cold_enabled: $("cache-cold-enabled").checked, cold_preset: selectedCacheColdPreset, cold_path: $("cache-cold-path").value.trim(), cold_max_bytes: Math.round(Number($("cache-cold-bytes").value) * 1048576), cold_max_entries: Number($("cache-cold-entries").value), cold_queue_max_bytes: Math.round(Number($("cache-cold-queue-bytes").value) * 1048576), cold_gc_interval_seconds: Number($("cache-cold-gc-interval").value), cold_gc_max_delete: Number($("cache-cold-gc-max-delete").value), cold_after_seconds: Number($("cache-cold-after").value), cold_max_hot_seconds: Number($("cache-cold-max-hot").value), cold_on_idle: $("cache-cold-on-idle").checked, cold_on_age: $("cache-cold-on-age").checked, cold_on_lru: $("cache-cold-on-lru").checked, cold_on_shutdown: $("cache-cold-on-shutdown").checked, max_ttl_seconds: Number($("cache-ttl").value), default_ttl_seconds: Number($("cache-default-ttl").value), collapse_enabled: $("cache-collapse-enabled").checked, collapse_wait_timeout_ms: Number($("cache-collapse-timeout").value), collapse_timeout_fallback: $("cache-collapse-fallback").value, stale_if_error_timeout: $("cache-sie-timeout").checked, stale_if_error_connection: $("cache-sie-connection").checked, stale_if_error_dns: $("cache-sie-dns").checked, stale_if_error_tls: $("cache-sie-tls").checked, stale_if_error_statuses: $("cache-sie-statuses").value.split(/[\s,]+/).filter(Boolean).map(Number), proactive_refresh_enabled: $("cache-proactive-enabled").checked, proactive_refresh_before_expiry_seconds: Number($("cache-proactive-before").value), proactive_refresh_percent: Number($("cache-proactive-percent").value), proactive_refresh_max_inflight: Number($("cache-proactive-inflight").value), bypass_query: $("cache-query").checked, bypass_prefixes: $("cache-prefixes").value.split(/\r?\n/).map(v => v.trim()).filter(Boolean), static_ignore_cookies: $("cache-static-cookie").checked, static_extensions: $("cache-static-extensions").value.split(/[\n,]+/).map(v => v.trim().replace(/^\./, "").toLowerCase()).filter(Boolean), expose_status: $("cache-status-header").checked, expose_hits: $("cache-hits-header").checked, expose_cache_status: $("cache-rfc-header").checked, expose_resident_age: $("cache-resident-age-header").checked };
|
||||
next.cache = { ...(next.cache || {}), enabled: $("cache-enabled").checked, memory_preset: selectedCacheMemoryPreset, max_bytes: Math.round(Number($("cache-memory").value) * 1048576), transient_max_bytes: Math.round(Number($("cache-transient-memory").value) * 1048576), max_object_bytes: Number($("cache-object").value) * 1024, max_entries: Number($("cache-entries").value), hot_eviction_max_objects: Number($("cache-eviction-limit").value), hot_auto_trim_enabled: $("cache-auto-trim-enabled").checked, hot_auto_trim_after_seconds: Number($("cache-auto-trim-after").value), hot_auto_trim_target_percent: Number($("cache-auto-trim-target").value), cold_enabled: $("cache-cold-enabled").checked, cold_preset: selectedCacheColdPreset, cold_path: $("cache-cold-path").value.trim(), cold_max_bytes: Math.round(Number($("cache-cold-bytes").value) * 1048576), cold_max_entries: Number($("cache-cold-entries").value), cold_queue_max_bytes: Math.round(Number($("cache-cold-queue-bytes").value) * 1048576), cold_gc_interval_seconds: Number($("cache-cold-gc-interval").value), cold_gc_max_delete: Number($("cache-cold-gc-max-delete").value), cold_after_seconds: Number($("cache-cold-after").value), cold_max_hot_seconds: Number($("cache-cold-max-hot").value), cold_on_idle: $("cache-cold-on-idle").checked, cold_on_age: $("cache-cold-on-age").checked, cold_on_lru: $("cache-cold-on-lru").checked, cold_on_shutdown: $("cache-cold-on-shutdown").checked, max_ttl_seconds: Number($("cache-ttl").value), default_ttl_seconds: Number($("cache-default-ttl").value), collapse_enabled: $("cache-collapse-enabled").checked, collapse_wait_timeout_ms: Number($("cache-collapse-timeout").value), collapse_timeout_fallback: $("cache-collapse-fallback").value, stale_if_error_timeout: $("cache-sie-timeout").checked, stale_if_error_connection: $("cache-sie-connection").checked, stale_if_error_dns: $("cache-sie-dns").checked, stale_if_error_tls: $("cache-sie-tls").checked, stale_if_error_statuses: $("cache-sie-statuses").value.split(/[\s,]+/).filter(Boolean).map(Number), proactive_refresh_enabled: $("cache-proactive-enabled").checked, proactive_refresh_before_expiry_seconds: Number($("cache-proactive-before").value), proactive_refresh_percent: Number($("cache-proactive-percent").value), proactive_refresh_max_inflight: Number($("cache-proactive-inflight").value), bypass_query: $("cache-query").checked, bypass_prefixes: $("cache-prefixes").value.split(/\r?\n/).map(v => v.trim()).filter(Boolean), static_ignore_cookies: $("cache-static-cookie").checked, static_extensions: $("cache-static-extensions").value.split(/[\n,]+/).map(v => v.trim().replace(/^\./, "").toLowerCase()).filter(Boolean), expose_status: $("cache-status-header").checked, expose_hits: $("cache-hits-header").checked, expose_cache_status: $("cache-rfc-header").checked, expose_resident_age: $("cache-resident-age-header").checked, via_header: $("cache-via-enabled").checked ? $("cache-via-value").value.trim() : null };
|
||||
}, "cache");
|
||||
bindSettingsForm("transform-form", next => {
|
||||
next.transform = {
|
||||
@@ -384,6 +390,7 @@ $("server-mode").addEventListener("change", syncServerMode);
|
||||
$("log-file-enabled").addEventListener("change", syncLoggingFileMode);
|
||||
$("log-file-per-vhost").addEventListener("change", syncLoggingFileMode);
|
||||
for (const id of ["cache-cold-enabled", "cache-cold-on-idle", "cache-cold-on-age"]) $(id).addEventListener("change", syncColdCachePolicy);
|
||||
$("cache-via-enabled").addEventListener("change", syncCacheVia);
|
||||
document.querySelectorAll("[data-cache-memory-preset]").forEach(button => button.addEventListener("click", () => applyCacheMemoryPreset(button.dataset.cacheMemoryPreset)));
|
||||
document.querySelectorAll("[data-cache-cold-preset]").forEach(button => button.addEventListener("click", applyRecommendedColdCachePreset));
|
||||
document.querySelectorAll("[data-transform-preset]").forEach(button => button.addEventListener("click", () => applyTransformPreset(button.dataset.transformPreset)));
|
||||
|
||||
@@ -1749,9 +1749,12 @@
|
||||
data-settings-tab="pages" id="settings-tab-pages" role="tab" tabindex="-1"
|
||||
type="button"></button><button aria-controls="settings-page-logging" aria-selected="false"
|
||||
data-i18n="logging" data-settings-tab="logging" id="settings-tab-logging" role="tab"
|
||||
tabindex="-1" type="button"></button><button aria-controls="settings-page-prometheus"
|
||||
aria-selected="false" data-i18n="prometheusTitle" data-settings-tab="prometheus"
|
||||
id="settings-tab-prometheus" role="tab" tabindex="-1" type="button"></button><button
|
||||
tabindex="-1" type="button"></button><button aria-controls="settings-page-geoip"
|
||||
aria-selected="false" data-i18n="geoipUpdatesTitle" data-settings-tab="geoip"
|
||||
id="settings-tab-geoip" role="tab" tabindex="-1" type="button"></button><button
|
||||
aria-controls="settings-page-prometheus" aria-selected="false" data-i18n="prometheusTitle"
|
||||
data-settings-tab="prometheus" id="settings-tab-prometheus" role="tab" tabindex="-1"
|
||||
type="button"></button><button
|
||||
aria-controls="settings-page-administration" aria-selected="false"
|
||||
data-i18n="administration" data-settings-tab="administration"
|
||||
id="settings-tab-administration" role="tab" tabindex="-1" type="button"></button><button
|
||||
@@ -1915,7 +1918,7 @@
|
||||
<p class="fine" data-i18n="lowPortsHelp">Privileged ports may need CAP_NET_BIND_SERVICE.
|
||||
Do not run the whole application as root.</p>
|
||||
<p class="pending-note" hidden="" id="listener-pending"></p>
|
||||
<p class="fine" id="listener-active"></p>
|
||||
<div class="listener-active-summary" id="listener-active" role="status"></div>
|
||||
<div class="form-actions"><button class="primary" data-i18n="saveListeners"
|
||||
type="submit">Save listeners</button></div>
|
||||
</form>
|
||||
@@ -2264,6 +2267,15 @@
|
||||
type="checkbox" /><span>Cache-Status (RFC 9211)</span></label><label
|
||||
class="check"><input id="cache-resident-age-header"
|
||||
type="checkbox" /><span>X-Cache-Resident-Age</span></label></div>
|
||||
<div class="cache-via-setting">
|
||||
<label class="check"><input id="cache-via-enabled" type="checkbox" /><span
|
||||
data-i18n="cacheViaEnable">Add Via response header</span></label>
|
||||
<label><span data-i18n="cacheViaValue">Via header value</span><input
|
||||
id="cache-via-value" maxlength="256" placeholder="RustProxy"
|
||||
required="" type="text" /></label>
|
||||
</div>
|
||||
<p class="fine" data-i18n="cacheViaHelp">Defaults to Via: RustProxy. Changing this
|
||||
response-only header does not purge cached objects.</p>
|
||||
<p class="fine" data-i18n="cacheAgeHelp">Age is sent on cache hits unless an
|
||||
explicit response-header rule removes or replaces it. Such Age rules are
|
||||
wire-only and do not change cache freshness.</p>
|
||||
@@ -2548,6 +2560,49 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-labelledby="settings-tab-geoip" class="settings-page"
|
||||
data-settings-page="geoip" hidden="" id="settings-page-geoip" role="tabpanel">
|
||||
<form class="card edit-form" id="geoip-form">
|
||||
<div class="card-heading">
|
||||
<h2 data-i18n="geoipUpdatesTitle"></h2><span class="pill">AUTO</span>
|
||||
</div>
|
||||
<p class="fine" data-i18n="geoipUpdatesHelp"></p>
|
||||
<label class="check"><input id="geoip-enabled" type="checkbox" /><span
|
||||
data-i18n="geoipUpdatesEnable"></span></label>
|
||||
<div class="field-grid">
|
||||
<label><span data-i18n="geoipFormat"></span><select id="geoip-format">
|
||||
<option data-i18n="geoipFormatCsvPair" value="csv_pair"></option>
|
||||
<option data-i18n="geoipFormatMmdb" value="mmdb"></option>
|
||||
</select></label>
|
||||
<label><span data-i18n="geoipMaxFileMiB"></span><input id="geoip-max-mib" min="1"
|
||||
max="256" required="" type="number" /></label>
|
||||
<label><span data-i18n="geoipIntervalHours"></span><input id="geoip-interval-hours"
|
||||
min="0.0166667" max="744" step="any" required="" type="number" /></label>
|
||||
</div>
|
||||
<div class="field-grid" id="geoip-csv-fields">
|
||||
<label><span data-i18n="geoipIpv4Url"></span><input id="geoip-ipv4-url"
|
||||
maxlength="2048" required="" type="url" /></label>
|
||||
<label><span data-i18n="geoipIpv4ChecksumUrl"></span><input id="geoip-ipv4-checksum-url"
|
||||
maxlength="2048" type="url" /></label>
|
||||
<label><span data-i18n="geoipIpv6Url"></span><input id="geoip-ipv6-url"
|
||||
maxlength="2048" required="" type="url" /></label>
|
||||
<label><span data-i18n="geoipIpv6ChecksumUrl"></span><input id="geoip-ipv6-checksum-url"
|
||||
maxlength="2048" type="url" /></label>
|
||||
</div>
|
||||
<div class="field-grid" id="geoip-mmdb-fields" hidden="">
|
||||
<label><span data-i18n="geoipMmdbUrl"></span><input id="geoip-mmdb-url"
|
||||
maxlength="2048" type="url" /></label>
|
||||
<label><span data-i18n="geoipMmdbChecksumUrl"></span><input id="geoip-mmdb-checksum-url"
|
||||
maxlength="2048" type="url" /></label>
|
||||
</div>
|
||||
<p class="fine" data-i18n="geoipSourceHelp"></p>
|
||||
<p class="fine" id="geoip-status" role="status"></p>
|
||||
<div class="form-actions"><button class="primary" data-i18n="apply"
|
||||
type="submit"></button><button class="outline" data-i18n="geoipUpdateNow"
|
||||
id="geoip-update-now" type="button"></button></div>
|
||||
<p class="fine" data-i18n="geoipUpdateNowHelp"></p>
|
||||
</form>
|
||||
</div>
|
||||
<div aria-labelledby="settings-tab-prometheus" class="settings-page"
|
||||
data-settings-page="prometheus" hidden="" id="settings-page-prometheus" role="tabpanel">
|
||||
<form class="card edit-form" id="prometheus-form">
|
||||
@@ -2823,7 +2878,7 @@
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="page-footer"><span>RustProxy <b>0.27.3</b></span><span id="footer-status">API - /
|
||||
<footer class="page-footer"><span>RustProxy <b>0.28.0</b></span><span id="footer-status">API - /
|
||||
dropped: 0</span></footer>
|
||||
</div>
|
||||
</main>
|
||||
@@ -3567,7 +3622,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.27.3</strong></div>
|
||||
<div><span class="small-label">RustProxy</span><strong>0.28.0</strong></div>
|
||||
</div>
|
||||
<dl class="about-meta">
|
||||
<div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
const themes = new Set(["dark", "light"]);
|
||||
try { const cached = localStorage.getItem("rp-theme"); document.documentElement.dataset.theme = themes.has(cached) ? cached : "dark"; } catch { document.documentElement.dataset.theme = "dark"; }
|
||||
const panelPaths = new Set(["/overview", "/vhosts", "/docker-vhosts", "/redirects", "/404-hosts", "/streams", "/acl-profiles", "/configuration", "/tls", "/traffic", "/cache", "/transform", "/logging", "/logs", "/settings"]);
|
||||
const settingsTabs = new Set(["appearance", "listeners", "connections", "docker-agents", "response", "cache", "transform", "headers", "forwarding", "pages", "logging", "prometheus", "administration", "tls-policy", "manual-config", "security"]);
|
||||
const settingsTabs = new Set(["appearance", "listeners", "connections", "docker-agents", "response", "cache", "transform", "headers", "forwarding", "pages", "logging", "geoip", "prometheus", "administration", "tls-policy", "manual-config", "security"]);
|
||||
let packs = new Map(), fallback = null, language = null, setupRequired = false, ready = false;
|
||||
try { language = localStorage.getItem("rp-language"); } catch { }
|
||||
const t = key => packs.get(language)?.translations?.[key] ?? packs.get(fallback)?.translations?.[key] ?? key;
|
||||
|
||||
@@ -142,6 +142,29 @@ curl --fail --show-error --request POST \
|
||||
finally { certificateApiLoading = false; }
|
||||
}
|
||||
const defaults = { enabled: false, listen: "127.0.0.1:9191", path: "/metrics", allowed_cidrs: ["127.0.0.0/8", "::1/128"], bearer_token_env: null, allow_trusted_network_without_token: false, include_system: true, include_vhosts: true, include_traffic: true, include_backends: true, include_docker_vhosts: true, include_cache_domains: true, include_streams: true, include_logging: true };
|
||||
const geoipDefaults = {
|
||||
enabled: true, format: "csv_pair",
|
||||
ipv4_url: "https://github.com/sapics/ip-location-db/releases/download/latest/user-country-ipv4.csv",
|
||||
ipv6_url: "https://github.com/sapics/ip-location-db/releases/download/latest/user-country-ipv6.csv",
|
||||
mmdb_url: "https://github.com/sapics/ip-location-db/releases/download/latest/user-country.mmdb", ipv4_checksum_url: null, ipv6_checksum_url: null, mmdb_checksum_url: null,
|
||||
max_bytes: 268435456, interval_seconds: 86400
|
||||
};
|
||||
const legacyGeoipRelease = "https://github.com/sapics/ip-location-db/releases/download/latest/";
|
||||
const legacyGeoipChecksum = "https://github.com/sapics/ip-location-db/releases/download/checksum/";
|
||||
function normalizedGeoip(raw = {}) {
|
||||
const geoip = { ...geoipDefaults, ...raw };
|
||||
if (!raw.ipv4_url && raw.release_url && raw.release_url !== legacyGeoipRelease) geoip.ipv4_url = `${raw.release_url.replace(/\/?$/, "/")}user-country-ipv4.csv`;
|
||||
if (!raw.ipv6_url && raw.release_url && raw.release_url !== legacyGeoipRelease) geoip.ipv6_url = `${raw.release_url.replace(/\/?$/, "/")}user-country-ipv6.csv`;
|
||||
if (!raw.ipv4_checksum_url && raw.checksum_url && raw.checksum_url !== legacyGeoipChecksum) geoip.ipv4_checksum_url = `${raw.checksum_url.replace(/\/?$/, "/")}user-country-ipv4.csv.sha256`;
|
||||
if (!raw.ipv6_checksum_url && raw.checksum_url && raw.checksum_url !== legacyGeoipChecksum) geoip.ipv6_checksum_url = `${raw.checksum_url.replace(/\/?$/, "/")}user-country-ipv6.csv.sha256`;
|
||||
return geoip;
|
||||
}
|
||||
function syncGeoipFormat() {
|
||||
const mmdb = $("geoip-format").value === "mmdb";
|
||||
$("geoip-csv-fields").hidden = mmdb; $("geoip-mmdb-fields").hidden = !mmdb;
|
||||
$("geoip-ipv4-url").required = !mmdb; $("geoip-ipv6-url").required = !mmdb;
|
||||
$("geoip-mmdb-url").required = mmdb && $("geoip-enabled").checked;
|
||||
}
|
||||
function example() {
|
||||
const addr = $("prom-listen").value.trim(), path = $("prom-path").value.trim(), env = $("prom-token-env").value.trim();
|
||||
$("prom-scrape-example").textContent = `scrape_configs:\n - job_name: rustproxy\n metrics_path: ${JSON.stringify(path)}\n static_configs:\n - targets: [${JSON.stringify(addr)}]${env ? "\n authorization:\n credentials_file: /etc/prometheus/rustproxy.token" : ""}`;
|
||||
@@ -156,6 +179,22 @@ curl --fail --show-error --request POST \
|
||||
const status = latestStatus?.prometheus;
|
||||
$("prom-status").textContent = status?.restart_required ? t("restartPending") : status?.active?.enabled ? `${t("prometheusActive")}: ${status.active.listen}${p.path}` : t("prometheusDisabled"); example();
|
||||
|
||||
const geoip = normalizedGeoip(envelope.config.geoip_updates || {});
|
||||
if (!settingsDirty.has("geoip-form")) {
|
||||
$("geoip-enabled").checked = geoip.enabled !== false; $("geoip-format").value = geoip.format || "csv_pair";
|
||||
$("geoip-ipv4-url").value = geoip.ipv4_url || ""; $("geoip-ipv6-url").value = geoip.ipv6_url || "";
|
||||
$("geoip-mmdb-url").value = geoip.mmdb_url || "";
|
||||
$("geoip-ipv4-checksum-url").value = geoip.ipv4_checksum_url || ""; $("geoip-ipv6-checksum-url").value = geoip.ipv6_checksum_url || "";
|
||||
$("geoip-mmdb-checksum-url").value = geoip.mmdb_checksum_url || "";
|
||||
$("geoip-max-mib").value = Number(geoip.max_bytes || geoipDefaults.max_bytes) / 1048576;
|
||||
$("geoip-interval-hours").value = Number(geoip.interval_seconds || geoipDefaults.interval_seconds) / 3600;
|
||||
syncGeoipFormat();
|
||||
}
|
||||
const geoStatus = latestStatus?.geoip;
|
||||
$("geoip-status").textContent = geoStatus?.loaded
|
||||
? geoStatus.format === "mmdb" ? `${t("geoipStatusLoaded")}: ${geoStatus.source}` : `${t("geoipStatusLoaded")}: IPv4 ${fmt(geoStatus.ipv4_ranges)} · IPv6 ${fmt(geoStatus.ipv6_ranges)}`
|
||||
: geoip.enabled ? t("geoipStatusMissing") : t("geoipStatusDisabled");
|
||||
|
||||
const admin = envelope.config.administration || {}, adminStatus = latestStatus?.administration;
|
||||
if (!settingsDirty.has("administration-form")) {
|
||||
$("admin-listen").value = adminStatus?.locked_listen || admin.listen || adminStatus?.desired?.listen || latestStatus?.admin_listen || "127.0.0.1:9090";
|
||||
@@ -186,6 +225,30 @@ curl --fail --show-error --request POST \
|
||||
$("tls-policy-available").textContent = available.length ? `${t("tlsAvailableCiphers")}: ${available.join(", ")}` : "";
|
||||
renderCertificateApi(); loadCertificateApi();
|
||||
}
|
||||
$("geoip-format").addEventListener("change", syncGeoipFormat); $("geoip-enabled").addEventListener("change", syncGeoipFormat);
|
||||
bindSettingsForm("geoip-form", next => {
|
||||
next.geoip_updates = {
|
||||
enabled: $("geoip-enabled").checked, format: $("geoip-format").value,
|
||||
ipv4_url: $("geoip-ipv4-url").value.trim(), ipv6_url: $("geoip-ipv6-url").value.trim(), mmdb_url: $("geoip-mmdb-url").value.trim(),
|
||||
ipv4_checksum_url: $("geoip-ipv4-checksum-url").value.trim() || null,
|
||||
ipv6_checksum_url: $("geoip-ipv6-checksum-url").value.trim() || null,
|
||||
mmdb_checksum_url: $("geoip-mmdb-checksum-url").value.trim() || null,
|
||||
max_bytes: Math.round(Number($("geoip-max-mib").value) * 1048576),
|
||||
interval_seconds: Math.round(Number($("geoip-interval-hours").value) * 3600)
|
||||
};
|
||||
}, "geoip", next => next.geoip_updates);
|
||||
$("geoip-update-now").addEventListener("click", async () => {
|
||||
if (settingsDirty.has("geoip-form")) { toast(t("geoipUpdateSaveFirst"), true); return; }
|
||||
const button = $("geoip-update-now"); button.disabled = true;
|
||||
$("geoip-status").textContent = t("geoipUpdateWorking");
|
||||
try {
|
||||
const result = await api("/api/geoip/update", { method: "POST" });
|
||||
if (result.database) latestStatus = { ...(latestStatus || {}), geoip: result.database };
|
||||
render();
|
||||
toast(t(result.outcome === "updated" ? "geoipUpdateUpdated" : result.outcome === "busy" ? "geoipUpdateBusy" : "geoipUpdateCurrent"));
|
||||
} catch (error) { toast(error.message, true); render(); }
|
||||
finally { button.disabled = false; }
|
||||
});
|
||||
bindSettingsForm("prometheus-form", next => {
|
||||
next.prometheus = {
|
||||
enabled: $("prom-enabled").checked, listen: $("prom-listen").value.trim(), path: $("prom-path").value.trim(), allowed_cidrs: $("prom-cidrs").value.split(/[\n,]/).map(v => v.trim()).filter(Boolean), bearer_token_env: $("prom-token-env").value.trim() || null, allow_trusted_network_without_token: $("prom-trusted-no-token").checked,
|
||||
|
||||
+69
-3
@@ -247,13 +247,13 @@ h3 {
|
||||
.shell {
|
||||
display: grid;
|
||||
grid-template-columns: 218px minmax(0, 1fr);
|
||||
min-height: 100vh
|
||||
min-height: calc(100vh / var(--ui-scale, 1))
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
height: calc(100vh / var(--ui-scale, 1));
|
||||
background: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--line);
|
||||
padding: 34px 22px;
|
||||
@@ -9646,4 +9646,70 @@ html[data-chart-style] :is(#workspace, dialog) svg polyline {
|
||||
.acl-policy-step-head>button {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
.listener-active-summary {
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--panel2);
|
||||
display: grid;
|
||||
gap: 8px
|
||||
}
|
||||
|
||||
.listener-active-title {
|
||||
font-size: 10px;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .45px
|
||||
}
|
||||
|
||||
.listener-active-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px
|
||||
}
|
||||
|
||||
.listener-active-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--panel)
|
||||
}
|
||||
|
||||
.listener-active-protocol {
|
||||
font: 600 10px var(--mono);
|
||||
color: var(--accent)
|
||||
}
|
||||
|
||||
.listener-active-address {
|
||||
overflow-wrap: anywhere
|
||||
}
|
||||
|
||||
.listener-active-feature {
|
||||
padding: 2px 5px;
|
||||
border: 1px solid var(--line);
|
||||
font: 600 9px var(--mono);
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
.cache-via-setting {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(180px, .8fr) minmax(220px, 1.2fr);
|
||||
gap: 8px 14px;
|
||||
align-items: end;
|
||||
margin-top: 10px
|
||||
}
|
||||
|
||||
.location-header-editor .vh-header-rule-head > button {
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto
|
||||
}
|
||||
|
||||
@media(max-width:650px) {
|
||||
.cache-via-setting {
|
||||
grid-template-columns: 1fr
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@
|
||||
target.value = JSON.stringify(result, null, 2); target.setCustomValidity(""); onChange();
|
||||
};
|
||||
for (const direction of ["request", "response"]) {
|
||||
const block = el("section", undefined, "vh-wide location-header-direction"); block.dataset.headerDirection = direction;
|
||||
const block = el("section", undefined, "vh-wide location-header-direction location-header-editor"); block.dataset.headerDirection = direction;
|
||||
const heading = el("div", undefined, "location-header-direction-head");
|
||||
heading.append(text("h3", direction === "response" ? "locationResponseHeaders" : "locationProxyHeaders"), text("p", "headerRuleHelp", "fine"));
|
||||
block.append(heading);
|
||||
|
||||
Reference in new issue
Block a user