v0.29.0
This commit is contained in:
1 parent
65e082b9d1
commit
76cb4acef0
72 files changed
+6134
-4709
No files matched your search
@@ -43,7 +43,7 @@
|
||||
};
|
||||
const allowDiscard = async () => { if (!dirty) return true; if (!(await appConfirm(t("vhUnsaved"), { danger: true, confirmLabel: t("discardAction") }))) return false; dirty = false; return true; };
|
||||
const defaults = () => ({
|
||||
id: "", enabled: true, force_ssl: false, forward_client_ip: true, websockets: false, websocket_legacy_support: false, websocket_rfc8441: false, websocket_rfc9220: false, http2_enabled: true, http3_enabled: true, cloudflare_client_ip: false, timing_headers: false, backend_header: false, backend_timing_header: false,
|
||||
id: "", enabled: true, force_ssl: false, www_redirect: false, www_redirect_target: null, forward_client_ip: true, websockets: false, websocket_legacy_support: false, websocket_rfc8441: false, websocket_rfc9220: false, http2_enabled: true, http3_enabled: true, cloudflare_client_ip: false, timing_headers: false, backend_header: false, backend_timing_header: false,
|
||||
frontend: { hosts: [], listener_ids: [] },
|
||||
source: { type: "reverse_proxy" },
|
||||
backends: [{ id: "origin-1", url: "", weight: 1, enabled: true, pool_eligible: true, allow_private: true, accept_local_listener_risk: false, preserve_host: true, protocol: "http1", tls_verify: false }],
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
function activateTab(name) { document.querySelectorAll("#vhost-tabs button").forEach(b => { const active = b.dataset.tab === name; b.classList.toggle("active", active); b.setAttribute("aria-pressed", String(active)); b.setAttribute("aria-selected", String(active)); b.tabIndex = active ? 0 : -1; }); container.querySelectorAll(".vh-group").forEach(g => g.hidden = g.dataset.tab !== name); }
|
||||
function initTabs() { const tabs = $("vhost-tabs"); tabs.replaceChildren(); tabs.setAttribute("role", "tablist"); for (const [name, key] of [["details", "vhDetailsTab"], ["backends", "vhBackendPool"], ["health", "vhHealth"], ["locations", "vhLocations"], ["auth", "vhTabAuth"], ["ssl", "vhSsl"], ["cache", "vhCache"], ["compression", "vhCompression"], ["transform", "vhTransform"], ["security", "vhSecurity"], ["pages", "vhPages"], ["advanced", "advanced"]]) { const b = button(key, () => activateTab(name)); b.dataset.tab = name; b.setAttribute("role", "tab"); tabs.append(b); } activateTab("details"); }
|
||||
function collectCache() { const tri = name => str(name) === "" ? null : str(name) === "on", number = (name, mul = 1) => { if (str(name) === "") return null; const value = Number(str(name)) * mul; if (!Number.isSafeInteger(value)) throw new Error(t("vhJsonInvalid")); return value; }; return { enabled: tri("vh-cache-enabled"), max_ttl_seconds: number("vh-cache-ttl"), default_ttl_seconds: number("vh-cache-default"), max_object_bytes: number("vh-cache-object", 1024), bypass_query: tri("vh-cache-query"), static_ignore_cookies: tri("vh-cache-static-cookie"), static_extensions: yes("vh-cache-static-custom") ? lines(str("vh-cache-static-extensions")).map(v => v.replace(/^\./, "").toLowerCase()) : null, expose_status: tri("vh-cache-status"), expose_hits: tri("vh-cache-hits"), expose_cache_status: tri("vh-cache-rfc"), expose_resident_age: tri("vh-cache-resident-age"), bypass_prefixes: yes("vh-cache-prefix-custom") ? lines(str("vh-cache-prefixes")) : null }; }
|
||||
function collectCache() { const tri = name => str(name) === "" ? null : str(name) === "on", number = (name, mul = 1) => { if (str(name) === "") return null; const value = Number(str(name)) * mul; if (!Number.isSafeInteger(value)) throw new Error(t("vhJsonInvalid")); return value; }; return { enabled: tri("vh-cache-enabled"), max_ttl_seconds: number("vh-cache-ttl"), default_ttl_seconds: number("vh-cache-default"), max_object_bytes: number("vh-cache-object", 1024), bypass_query: tri("vh-cache-query"), static_ignore_cookies: tri("vh-cache-static-cookie"), static_extensions: yes("vh-cache-static-custom") ? lines(str("vh-cache-static-extensions")).map(v => v.replace(/^\./, "").toLowerCase()) : null, expose_status: tri("vh-cache-status"), expose_hits: tri("vh-cache-hits"), expose_cache_status: tri("vh-cache-rfc"), expose_resident_age: tri("vh-cache-resident-age"), bypass_prefixes: yes("vh-cache-prefix-custom") ? lines(str("vh-cache-prefixes")) : null, unsafe_force: { enabled: yes("vh-cache-force-enabled"), targets: lines(str("vh-cache-force-targets")) } }; }
|
||||
function collectCompression() { const tri = name => str(name) === "" ? null : str(name) === "on"; return { enabled: tri("vh-compression-enabled"), zstd: tri("vh-compression-zstd"), br: tri("vh-compression-br"), gzip: tri("vh-compression-gzip"), deflate: tri("vh-compression-deflate") }; }
|
||||
function collectTransform() {
|
||||
const tri = name => str(name) === "" ? null : str(name) === "on";
|
||||
@@ -312,7 +312,7 @@
|
||||
function readRows() {
|
||||
return [...rows.children].map(r => {
|
||||
const action = str("location-action", r), rewriteMode = str("location-rewrite-mode", r);
|
||||
const handled = action !== "return";
|
||||
const handled = action === "proxy" || action === "web_server";
|
||||
return {
|
||||
id: str("location-id", r), path_prefix: str("location-path", r), match_type: str("location-match", r),
|
||||
source: action === "web_server" ? { type: "web_server", web_service_id: str("location-web-service", r) } : { type: "reverse_proxy" },
|
||||
@@ -324,7 +324,8 @@
|
||||
method_policy: { mode: str("location-method-mode", r), methods: lines(str("location-method-list", r)).map(value => value.toUpperCase()) },
|
||||
headers: r.readHeaders(),
|
||||
rewrite: { mode: handled ? rewriteMode : "preserve", value: handled && rewriteMode !== "preserve" ? read("location-rewrite-value", r).value.trim() : "" },
|
||||
response: action === "return" ? { status: Number(str("location-response-status", r)), content_type: str("location-response-type", r), body: read("location-response-body", r).value } : null
|
||||
response: action === "return" ? { status: Number(str("location-response-status", r)), content_type: str("location-response-type", r), body: read("location-response-body", r).value } : null,
|
||||
redirect: action === "redirect" ? { status: Number(str("location-redirect-status", r)), target: str("location-redirect-target", r), rewrite_pattern: nullable("location-redirect-pattern", r), preserve_query: yes("location-redirect-query", r) } : null
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -338,8 +339,8 @@
|
||||
field(body, "location-id", "locationId", value.id || `location-${suffix}`, "text", { required: true, pattern: "[A-Za-z0-9_-]{1,64}", maxLength: 64 });
|
||||
const match = field(body, "location-match", "locationMatchType", value.match_type || "prefix", "select", { values: [["prefix", "locationMatchPrefix"], ["prefix_priority", "locationMatchPriority"], ["exact", "locationMatchExact"], ["regex", "locationMatchRegex"], ["regex_insensitive", "locationMatchRegexI"]] });
|
||||
const path = field(body, "location-path", "locationPathPattern", value.path_prefix || "/", "text", { required: true, maxLength: 1024 }); path.placeholder = "/api or ^/api/v[0-9]+/";
|
||||
const initialAction = value.response ? "return" : value.source?.type === "web_server" ? "web_server" : "proxy";
|
||||
const action = field(body, "location-action", "locationAction", initialAction, "select", { values: [["proxy", "locationProxyAction"], ["web_server", "locationWebServerAction"], ["return", "locationReturnAction"]] });
|
||||
const initialAction = value.response ? "return" : value.redirect ? "redirect" : value.source?.type === "web_server" ? "web_server" : "proxy";
|
||||
const action = field(body, "location-action", "locationAction", initialAction, "select", { values: [["proxy", "locationProxyAction"], ["web_server", "locationWebServerAction"], ["redirect", "locationRedirectAction"], ["return", "locationReturnAction"]] });
|
||||
const backend = field(body, "location-backend", "backend", value.backend_id || "", "select"); memberOptions(backend, value.backend_id || "");
|
||||
const webService = field(body, "location-web-service", "webService", value.source?.web_service_id || "", "select"); fillWebServices(webService, value.source?.web_service_id || "");
|
||||
field(body, "location-enabled", "enabled", value.enabled !== false, "checkbox");
|
||||
@@ -352,6 +353,12 @@
|
||||
field(returnBox, "location-response-status", "locationResponseStatus", value.response?.status ?? 200, "number", { min: 200, max: 599, required: true });
|
||||
field(returnBox, "location-response-type", "locationResponseType", value.response?.content_type || "text/plain; charset=utf-8", "text", { maxLength: 256, required: true });
|
||||
field(returnBox, "location-response-body", "locationResponseBody", value.response?.body || "", "textarea", { rows: 5, maxLength: 65536 });
|
||||
const redirectBox = el("div", undefined, "vh-wide vh-grid location-redirect-box"); handlingBody.append(redirectBox);
|
||||
field(redirectBox, "location-redirect-status", "locationRedirectStatus", value.redirect?.status ?? 302, "select", { values: [["301", "locationRedirect301"], ["302", "locationRedirect302"], ["303", "locationRedirect303"], ["307", "locationRedirect307"], ["308", "locationRedirect308"]] });
|
||||
field(redirectBox, "location-redirect-target", "locationRedirectTarget", value.redirect?.target || "", "url", { maxLength: 2048 });
|
||||
field(redirectBox, "location-redirect-pattern", "locationRedirectPattern", value.redirect?.rewrite_pattern || "", "text", { maxLength: 1024 });
|
||||
field(redirectBox, "location-redirect-query", "locationRedirectQuery", value.redirect?.preserve_query !== false, "checkbox");
|
||||
note(redirectBox, "locationRedirectHelp");
|
||||
note(handlingBody, "locationRewriteHelp");
|
||||
|
||||
const extra = el("details", undefined, "vh-wide location-section"), accessBody = el("div", undefined, "vh-grid"); extra.append(text("summary", "locationAccess"), accessBody); body.append(extra);
|
||||
@@ -387,15 +394,15 @@
|
||||
actions.append(dedicated, button("remove", () => { row.remove(); sync(); })); body.append(actions);
|
||||
|
||||
const syncUi = () => {
|
||||
const isReturn = action.value === "return", isWeb = action.value === "web_server", isProxy = action.value === "proxy", isRegex = match.value === "regex" || match.value === "regex_insensitive";
|
||||
const isReturn = action.value === "return", isRedirect = action.value === "redirect", isWeb = action.value === "web_server", isProxy = action.value === "proxy", isRegex = match.value === "regex" || match.value === "regex_insensitive";
|
||||
backend.disabled = !isProxy; cache.disabled = !isProxy; dedicated.disabled = !isProxy;
|
||||
webService.disabled = !isWeb; webService.required = isWeb;
|
||||
returnBox.hidden = !isReturn; rewriteMode.disabled = isReturn; rewriteValue.disabled = isReturn || rewriteMode.value === "preserve" || rewriteMode.value === "strip_prefix";
|
||||
returnBox.hidden = !isReturn; redirectBox.hidden = !isRedirect; rewriteMode.disabled = isReturn || isRedirect; rewriteValue.disabled = isReturn || isRedirect || rewriteMode.value === "preserve" || rewriteMode.value === "strip_prefix";
|
||||
for (const option of rewriteMode.options) if (option.value === "regex") option.disabled = !isRegex;
|
||||
if (!isRegex && rewriteMode.value === "regex") rewriteMode.value = "preserve";
|
||||
path.placeholder = isRegex ? "^/api/v[0-9]+/" : "/api/";
|
||||
const id = str("location-id", row) || t("locationId"), pattern = str("location-path", row) || "/";
|
||||
summary.textContent = `${id} · ${t(`locationMatch_${match.value}`)} · ${pattern} · ${t(isReturn ? "locationReturnShort" : isWeb ? "locationWebServerShort" : "locationProxyShort")}`;
|
||||
summary.textContent = `${id} · ${t(`locationMatch_${match.value}`)} · ${pattern} · ${t(isReturn ? "locationReturnShort" : isRedirect ? "locationRedirectShort" : isWeb ? "locationWebServerShort" : "locationProxyShort")}`;
|
||||
|
||||
previewStatus.hidden = yes("location-enabled", row);
|
||||
previewValues.match.textContent = `${t(`locationMatch_${match.value}`)} · ${pattern}`;
|
||||
@@ -404,6 +411,11 @@
|
||||
previewValues.handling.textContent = `${t("locationReturnAction")}: HTTP ${status} · ${type} · ${t("locationPreviewBackendSkipped")}`;
|
||||
previewValues.rewrite.textContent = "-";
|
||||
previewValues.cache.textContent = t("notEnabled");
|
||||
} else if (isRedirect) {
|
||||
const status = str("location-redirect-status", row) || "302", target = str("location-redirect-target", row) || "-", capture = str("location-redirect-pattern", row);
|
||||
previewValues.handling.textContent = `${t("locationRedirectAction")}: HTTP ${status} · ${target} · ${t("locationPreviewBackendSkipped")}`;
|
||||
previewValues.rewrite.textContent = capture || "-";
|
||||
previewValues.cache.textContent = t("notEnabled");
|
||||
} else {
|
||||
if (isWeb) {
|
||||
const selected = webService.selectedOptions?.[0]?.textContent || t("webServiceSelect");
|
||||
|
||||
Reference in new issue
Block a user