dsh-provider-proxy 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -12
- package/lib/client.js +516 -89
- package/lib/client.js.map +3 -3
- package/lib/index.js +146 -46
- package/lib/index.js.map +3 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -39,13 +39,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
39
39
|
var require_client = __commonJS({
|
|
40
40
|
"src/client.css"(exports, module2) {
|
|
41
41
|
"use strict";
|
|
42
|
-
var __dshMap = { "root": "
|
|
42
|
+
var __dshMap = { "root": "bcf3ba25_root", "lead": "86cd7a9a_lead", "empty": "4ec3d74d_empty", "card": "61dfce67_card", "cardActive": "56b6c433_cardActive", "cardRejected": "24b300e2_cardRejected", "defaultCard": "665ef72b_defaultCard", "defaultLead": "351db191_defaultLead", "cardHead": "9ee651d9_cardHead", "cardTitles": "bb118443_cardTitles", "name": "a1b940d4_name", "id": "1870f756_id", "pillOn": "e9cfdcad_pillOn", "pillLive": "3d3b7405_pillLive", "pillOff": "a517f862_pillOff", "pillRejected": "3e11f4ea_pillRejected", "toggle": "31ab2dc8_toggle", "field": "52ffb01c_field", "label": "b8d05488_label", "hint": "d8b3bd46_hint", "input": "0b74e44b_input", "testButton": "47b71ed7_testButton", "diag": "f395d2a5_diag", "diagSummary": "84895b20_diagSummary", "diagList": "9ecca3cd_diagList" };
|
|
43
43
|
var __dshStyles = globalThis.__dshPluginStyles || (globalThis.__dshPluginStyles = /* @__PURE__ */ new Set());
|
|
44
44
|
if (!__dshStyles.has("dsh-provider-proxy/client.css")) {
|
|
45
45
|
__dshStyles.add("dsh-provider-proxy/client.css");
|
|
46
46
|
__dshStyle = document.createElement("style");
|
|
47
47
|
__dshStyle.setAttribute("data-plugin-css", "dsh-provider-proxy/client.css");
|
|
48
|
-
__dshStyle.textContent = '/* dsh-provider-proxy \u2014 provider proxy settings page.\n Tokens only: no literal colours, matching the shared --dsw-* system. */\n\n.
|
|
48
|
+
__dshStyle.textContent = '/* dsh-provider-proxy \u2014 provider proxy settings page.\n Tokens only: no literal colours, matching the shared --dsw-* system. */\n\n.bcf3ba25_root {\n display: flex;\n flex-direction: column;\n gap: 12px;\n max-width: 640px;\n}\n\n.86cd7a9a_lead {\n margin: 0 0 4px;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\n}\n\n.4ec3d74d_empty {\n margin: 0;\n padding: 16px;\n border: 1px dashed var(--dsw-alias-border-l1);\n border-radius: 8px;\n font-size: 12px;\n text-align: center;\n color: var(--dsw-alias-state-idle-primary);\n}\n\n/* ---- provider card ---- */\n\n.61dfce67_card,\n.56b6c433_cardActive,\n.24b300e2_cardRejected {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.56b6c433_cardActive {\n border-color: var(--dsw-alias-state-success-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An enabled route the Host refused: never dress it as active. */\n.24b300e2_cardRejected {\n border-color: var(--dsw-alias-state-error-primary, #f85149);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* ---- shared default endpoint ---- */\n\n/* A distinct outer layer, so it reads as "one setting for the page" rather than\n as one more provider card. */\n.665ef72b_defaultCard {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px 16px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.351db191_defaultLead {\n margin: -2px 0 0;\n font-size: 11px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n.9ee651d9_cardHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.bb118443_cardTitles {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.a1b940d4_name {\n overflow: hidden;\n font-size: 13px;\n font-weight: 600;\n white-space: nowrap;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.1870f756_id {\n flex: none;\n padding: 1px 6px;\n border-radius: 4px;\n background: var(--dsw-alias-bg-layer-2);\n font-family: ui-monospace, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* ---- status pill ---- */\n\n.e9cfdcad_pillOn,\n.3d3b7405_pillLive,\n.a517f862_pillOff,\n.3e11f4ea_pillRejected {\n flex: none;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 500;\n white-space: nowrap;\n}\n\n.e9cfdcad_pillOn {\n background: var(--dsw-alias-state-success-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.3d3b7405_pillLive {\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-secondary);\n}\n\n.a517f862_pillOff {\n color: var(--dsw-alias-state-idle-primary);\n}\n\n.3e11f4ea_pillRejected {\n border: 1px solid var(--dsw-alias-state-error-primary, #f85149);\n color: var(--dsw-alias-state-error-primary, #f85149);\n}\n\n/* ---- toggle ---- */\n\n.31ab2dc8_toggle {\n display: flex;\n align-items: center;\n gap: 8px;\n padding-top: 2px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n.31ab2dc8_toggle input {\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n/* ---- fields ---- */\n\n.52ffb01c_field {\n display: grid;\n grid-template-columns: 96px 1fr;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n}\n\n.b8d05488_label {\n color: var(--dsw-alias-label-secondary);\n}\n\n.d8b3bd46_hint {\n grid-column: 2;\n margin-top: -2px;\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.0b74e44b_input {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n padding: 5px 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n transition: border-color 120ms ease, background 120ms ease;\n}\n\n/* The base look of every input is INLINE too, so these three state rules only\n ever take effect as author-important declarations \u2014 same reason as the\n self-check button above. `cursor` has no inline base and needs none, and the\n hover border uses `l3` because `l1`\u2192`l2` is a 4%\u219210% black step nobody can\n see. */\n\n.0b74e44b_input:hover:not(:disabled) {\n border-color: var(--dsw-alias-border-l3) !important;\n}\n\n.0b74e44b_input:focus {\n border-color: var(--dsw-alias-brand-primary) !important;\n}\n\n.0b74e44b_input:disabled {\n color: var(--dsw-alias-state-idle-primary) !important;\n background: var(--dsw-alias-bg-layer-2) !important;\n cursor: not-allowed;\n}\n\n/* ---- self-check ---- */\n\n/* The hover state is the one thing an inline style cannot express, which is why\n this element carries a class at all. Layout stays inline.\n\n Two traps, both hit before this comment existed:\n - `!important` is load-bearing: the base border and background are INLINE, and\n the cascade sorts a style attribute after every author rule, so a plain\n declaration here would ship, match, and still lose.\n - the TOKENS must be visible ones. `bg-layer-1/2/3` all resolve to\n `neutral-bluish-00` in the light theme, so a layer swap is a no-op there, and\n `border-l1`\u2192`l2` is 4%\u219210% black on a 1px line. The app\'s own hovers use\n `interactive-bg-hover`; using it here is what makes the feedback exist. */\n.47b71ed7_testButton {\n transition: border-color 120ms ease, background 120ms ease;\n}\n\n.47b71ed7_testButton:hover:not(:disabled) {\n border-color: var(--dsw-alias-border-l3) !important;\n background: var(--dsw-alias-interactive-bg-hover) !important;\n}\n\n/* ---- diagnostics ---- */\n\n.f395d2a5_diag {\n margin-top: 2px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.84895b20_diagSummary {\n cursor: pointer;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.9ecca3cd_diagList {\n margin: 6px 0 0;\n padding-left: 18px;\n font-family: ui-monospace, monospace;\n word-break: break-all;\n}\n';
|
|
49
49
|
document.head.appendChild(__dshStyle);
|
|
50
50
|
}
|
|
51
51
|
var __dshStyle;
|
|
@@ -64,18 +64,60 @@ __export(client_exports, {
|
|
|
64
64
|
module.exports = __toCommonJS(client_exports);
|
|
65
65
|
var import_client = __toESM(require_client(), 1);
|
|
66
66
|
|
|
67
|
+
// src/host-rules.ts
|
|
68
|
+
function splitHostRules(raw) {
|
|
69
|
+
return raw.split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
70
|
+
}
|
|
71
|
+
function normalizeHostRule(raw) {
|
|
72
|
+
return raw.trim().replace(/^\*?\./, "").toLowerCase();
|
|
73
|
+
}
|
|
74
|
+
function isBracketedIpv6(token) {
|
|
75
|
+
return /^\[[0-9a-f:.]+\]$/i.test(token);
|
|
76
|
+
}
|
|
77
|
+
function hostRuleProblem(raw) {
|
|
78
|
+
const token = raw.trim();
|
|
79
|
+
if (token.length === 0) return void 0;
|
|
80
|
+
if (token === "*." || normalizeHostRule(token).length === 0) return "\u7A7A\u901A\u914D\u7B26";
|
|
81
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(token)) return "\u5E26\u534F\u8BAE\u524D\u7F00";
|
|
82
|
+
if (token.includes("/")) return "\u5E26\u8DEF\u5F84";
|
|
83
|
+
if (token.includes(":") && !isBracketedIpv6(token)) return "\u5E26\u7AEF\u53E3\u6216\u975E\u6CD5\u5192\u53F7";
|
|
84
|
+
const withoutLeadingWildcard = token.replace(/^\*\./, "");
|
|
85
|
+
if (withoutLeadingWildcard.includes("*") && token !== "*") return "\u6742\u6563\u901A\u914D\u7B26";
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
function deadRuleEntries(raw) {
|
|
89
|
+
return splitHostRules(raw).filter((entry) => hostRuleProblem(entry) !== void 0);
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
// src/proxy-address.ts
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
var PROXY_HOST_INVALID = "\u4EE3\u7406\u5730\u5740\u53EA\u80FD\u586B IPv4\u3001IPv6 \u6216\u57DF\u540D";
|
|
94
|
+
var IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
|
|
95
|
+
var DOMAIN = /^(?=.*[a-z])(?=.{1,253}$)(?!.*\.\d+$)[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)*$/i;
|
|
96
|
+
var IPV6_CHARS = /^[0-9a-f:.]+$/i;
|
|
97
|
+
function proxyHostProblem(rawHost) {
|
|
98
|
+
const host = (rawHost ?? "").trim().replace(/\.$/, "");
|
|
70
99
|
if (host.length === 0) return "\u672A\u586B\u5199\u4EE3\u7406\u5730\u5740";
|
|
71
|
-
if (
|
|
72
|
-
|
|
100
|
+
if (IPV4.test(host)) return void 0;
|
|
101
|
+
if (host.includes(":") || host.startsWith("[")) {
|
|
102
|
+
const literal = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
103
|
+
if (!IPV6_CHARS.test(literal)) return PROXY_HOST_INVALID;
|
|
104
|
+
try {
|
|
105
|
+
new URL(`http://[${literal}]:1`);
|
|
106
|
+
return void 0;
|
|
107
|
+
} catch {
|
|
108
|
+
return PROXY_HOST_INVALID;
|
|
109
|
+
}
|
|
73
110
|
}
|
|
111
|
+
return DOMAIN.test(host) ? void 0 : PROXY_HOST_INVALID;
|
|
112
|
+
}
|
|
113
|
+
function defaultProxyProblem(endpoint) {
|
|
114
|
+
const hostProblem = proxyHostProblem(endpoint.host);
|
|
115
|
+
if (hostProblem !== void 0) return hostProblem;
|
|
74
116
|
const port = endpoint.port ?? 0;
|
|
75
117
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
76
118
|
return "\u4EE3\u7406\u7AEF\u53E3\u65E0\u6548\uFF08\u9700\u4E3A 1-65535 \u7684\u6574\u6570\uFF09";
|
|
77
119
|
}
|
|
78
|
-
const hasUser = (endpoint.username ?? "").length > 0;
|
|
120
|
+
const hasUser = (endpoint.username ?? "").trim().length > 0;
|
|
79
121
|
const hasPassword = (endpoint.password ?? "").length > 0;
|
|
80
122
|
if (hasUser !== hasPassword) {
|
|
81
123
|
return hasUser ? "\u914D\u7F6E\u4E86\u4EE3\u7406\u7528\u6237\u540D\u4F46\u6CA1\u6709\u5BC6\u7801\uFF1B\u8BF7\u8865\u5168\u5BC6\u7801\u6216\u6E05\u7A7A\u7528\u6237\u540D" : "\u914D\u7F6E\u4E86\u4EE3\u7406\u5BC6\u7801\u4F46\u6CA1\u6709\u7528\u6237\u540D\uFF1B\u8BF7\u8865\u5168\u7528\u6237\u540D\u6216\u6E05\u7A7A\u5BC6\u7801";
|
|
@@ -170,12 +212,15 @@ async function writeConfig(ctx, ops) {
|
|
|
170
212
|
}
|
|
171
213
|
function knownProviders(configured, published) {
|
|
172
214
|
const rows = /* @__PURE__ */ new Map();
|
|
215
|
+
const byId = /* @__PURE__ */ new Map();
|
|
173
216
|
for (const row of published) {
|
|
217
|
+
byId.set(row.id, row);
|
|
174
218
|
if (row.live) rows.set(row.id, row);
|
|
175
219
|
}
|
|
176
|
-
for (const
|
|
220
|
+
for (const id of Object.keys(configured)) {
|
|
177
221
|
if (rows.has(id)) continue;
|
|
178
|
-
|
|
222
|
+
const known = byId.get(id);
|
|
223
|
+
rows.set(id, { id, name: known?.name ?? id, live: known?.live === true });
|
|
179
224
|
}
|
|
180
225
|
return [...rows.values()].sort(byName);
|
|
181
226
|
}
|
|
@@ -183,6 +228,8 @@ var CATALOG_PATH = "/dsh-provider-proxy/providers";
|
|
|
183
228
|
var TEST_PATH = "/dsh-provider-proxy/test";
|
|
184
229
|
var LATENCY_FAST_MS = 500;
|
|
185
230
|
var LATENCY_SLOW_MS = 1500;
|
|
231
|
+
var DEFAULT_TEST_URL = "https://api.ipify.org?format=json";
|
|
232
|
+
var MAX_TEST_URLS = 5;
|
|
186
233
|
function latencyColor(ms) {
|
|
187
234
|
if (typeof ms !== "number" || !Number.isFinite(ms)) return "var(--dsw-alias-label-secondary)";
|
|
188
235
|
if (ms <= LATENCY_FAST_MS) return "var(--dsw-alias-state-success-primary, #2ea043)";
|
|
@@ -196,6 +243,10 @@ function testResultFrom(body, strings) {
|
|
|
196
243
|
ok,
|
|
197
244
|
ms: typeof body?.ms === "number" && Number.isFinite(body.ms) ? body.ms : void 0,
|
|
198
245
|
ip: typeof body?.exitIp === "string" && body.exitIp.length > 0 ? body.exitIp : void 0,
|
|
246
|
+
status: typeof body?.status === "number" && Number.isInteger(body.status) ? body.status : void 0,
|
|
247
|
+
target: typeof body?.target === "string" && body.target.length > 0 ? body.target : void 0,
|
|
248
|
+
targetSource: body?.targetSource === "route" || body?.targetSource === "upstream" ? body.targetSource : body?.targetSource === "testUrl" ? "testUrl" : void 0,
|
|
249
|
+
cached: body?.cached === true,
|
|
199
250
|
// The Host probes a target the way routing would: a loopback/private upstream
|
|
200
251
|
// (or a bypassHosts match) is reached DIRECT, not through the proxy. The card
|
|
201
252
|
// says so, so a green check is not read as "the proxy works".
|
|
@@ -203,6 +254,57 @@ function testResultFrom(body, strings) {
|
|
|
203
254
|
error: ok ? void 0 : typeof body?.error === "string" && body.error.length > 0 ? body.error : strings.testFailed
|
|
204
255
|
};
|
|
205
256
|
}
|
|
257
|
+
function splitTestUrls(raw) {
|
|
258
|
+
return raw.split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
259
|
+
}
|
|
260
|
+
function testUrlHint(raw, t) {
|
|
261
|
+
const entries = splitTestUrls(raw);
|
|
262
|
+
if (entries.length === 0) return t.testUrlEmptyHint;
|
|
263
|
+
const invalid = entries.filter((entry) => !isAbsoluteHttpUrl(entry));
|
|
264
|
+
if (invalid.length > 0) return t.testUrlInvalidHint;
|
|
265
|
+
if (entries.length > MAX_TEST_URLS) return t.testUrlTooManyHint;
|
|
266
|
+
return void 0;
|
|
267
|
+
}
|
|
268
|
+
function isAbsoluteHttpUrl(value) {
|
|
269
|
+
try {
|
|
270
|
+
const url = new URL(value);
|
|
271
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0;
|
|
272
|
+
} catch {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function targetSourceSuffix(result, t) {
|
|
277
|
+
switch (result.targetSource) {
|
|
278
|
+
case "route":
|
|
279
|
+
return t.targetSourceRoute;
|
|
280
|
+
case "upstream":
|
|
281
|
+
return t.targetSourceUpstream;
|
|
282
|
+
case "testUrl":
|
|
283
|
+
return t.targetSourceTestUrl;
|
|
284
|
+
default:
|
|
285
|
+
return "";
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function redactSensitiveText(text, secrets = []) {
|
|
289
|
+
let safe = text;
|
|
290
|
+
for (const secret of secrets) {
|
|
291
|
+
if (typeof secret !== "string" || secret.length === 0) continue;
|
|
292
|
+
safe = safe.split(secret).join("***");
|
|
293
|
+
}
|
|
294
|
+
safe = safe.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^/@\s]+@/gi, "$1");
|
|
295
|
+
safe = safe.replace(/([?&#])[^\s"'<>]*/g, "$1");
|
|
296
|
+
return safe.length > 500 ? `${safe.slice(0, 500)}\u2026` : safe;
|
|
297
|
+
}
|
|
298
|
+
function secretsFromOps(ops) {
|
|
299
|
+
const out = [];
|
|
300
|
+
for (const op of ops) {
|
|
301
|
+
const field = op?.path?.[op.path.length - 1];
|
|
302
|
+
if (field === "password" && typeof op?.value === "string" && op.value.length > 0) {
|
|
303
|
+
out.push(op.value);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
206
308
|
var catalogueSetter = () => {
|
|
207
309
|
};
|
|
208
310
|
function relativeTime(at, t) {
|
|
@@ -273,9 +375,6 @@ function touchCatalogPolling() {
|
|
|
273
375
|
}, CATALOG_REFRESH_MS);
|
|
274
376
|
}
|
|
275
377
|
var byName = (a, b) => a.name.localeCompare(b.name);
|
|
276
|
-
function deadRuleEntries(raw) {
|
|
277
|
-
return raw.split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => /^[a-z][a-z0-9+.-]*:\/\//i.test(entry) || entry.includes("/"));
|
|
278
|
-
}
|
|
279
378
|
function view(route) {
|
|
280
379
|
return {
|
|
281
380
|
enabled: route?.enabled === true,
|
|
@@ -293,6 +392,14 @@ function view(route) {
|
|
|
293
392
|
function unbracket(hostname) {
|
|
294
393
|
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
295
394
|
}
|
|
395
|
+
function explicitPortOf(text) {
|
|
396
|
+
const authority = text.slice(text.indexOf("://") + 3).split(/[/?#]/, 1)[0];
|
|
397
|
+
const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
|
|
398
|
+
const match = /^\[[^\]]+\]:(\d+)$/.exec(hostPort) ?? /^[^:]+:(\d+)$/.exec(hostPort);
|
|
399
|
+
if (match === null) return 0;
|
|
400
|
+
const port = Number.parseInt(match[1], 10);
|
|
401
|
+
return Number.isNaN(port) ? 0 : port;
|
|
402
|
+
}
|
|
296
403
|
function splitProxyUrl(text) {
|
|
297
404
|
const trimmed = text.trim();
|
|
298
405
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) return void 0;
|
|
@@ -313,7 +420,7 @@ function splitProxyUrl(text) {
|
|
|
313
420
|
return value;
|
|
314
421
|
}
|
|
315
422
|
};
|
|
316
|
-
const parsedPort = url.port.length > 0 ? Number.parseInt(url.port, 10) :
|
|
423
|
+
const parsedPort = url.port.length > 0 ? Number.parseInt(url.port, 10) : explicitPortOf(trimmed);
|
|
317
424
|
return {
|
|
318
425
|
protocol,
|
|
319
426
|
host: unbracket(url.hostname),
|
|
@@ -324,6 +431,39 @@ function splitProxyUrl(text) {
|
|
|
324
431
|
}
|
|
325
432
|
var WRITE_DELAY_MS = 300;
|
|
326
433
|
var pendingWrites = /* @__PURE__ */ new Map();
|
|
434
|
+
var writeQueue = Promise.resolve();
|
|
435
|
+
function enqueueWrite(run) {
|
|
436
|
+
const next = writeQueue.then(run, run);
|
|
437
|
+
writeQueue = next.catch(() => void 0);
|
|
438
|
+
return next;
|
|
439
|
+
}
|
|
440
|
+
function scheduleWrite(key, run) {
|
|
441
|
+
const pending = pendingWrites.get(key);
|
|
442
|
+
if (pending?.timer !== void 0) clearTimeout(pending.timer);
|
|
443
|
+
const entry = {
|
|
444
|
+
flush: () => {
|
|
445
|
+
if (entry.timer !== void 0) clearTimeout(entry.timer);
|
|
446
|
+
pendingWrites.delete(key);
|
|
447
|
+
return enqueueWrite(run);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
entry.timer = setTimeout(() => {
|
|
451
|
+
void entry.flush();
|
|
452
|
+
}, WRITE_DELAY_MS);
|
|
453
|
+
pendingWrites.set(key, entry);
|
|
454
|
+
}
|
|
455
|
+
var testGeneration = /* @__PURE__ */ new Map();
|
|
456
|
+
var TEST_RELEVANT_FIELDS = /* @__PURE__ */ new Set([
|
|
457
|
+
"protocol",
|
|
458
|
+
"host",
|
|
459
|
+
"port",
|
|
460
|
+
"username",
|
|
461
|
+
"password",
|
|
462
|
+
"onlyHosts",
|
|
463
|
+
"bypassHosts",
|
|
464
|
+
"testTarget",
|
|
465
|
+
"useDefault"
|
|
466
|
+
]);
|
|
327
467
|
var diagnostics = [];
|
|
328
468
|
function note(message) {
|
|
329
469
|
if (!diagnostics.includes(message)) diagnostics.push(message);
|
|
@@ -349,6 +489,11 @@ var S = {
|
|
|
349
489
|
borderColor: "var(--dsw-alias-state-success-primary)",
|
|
350
490
|
background: "var(--dsw-alias-bg-layer-2)"
|
|
351
491
|
},
|
|
492
|
+
/** An enabled route the Host refused: active-looking styling would be a lie. */
|
|
493
|
+
cardRejected: {
|
|
494
|
+
borderColor: "var(--dsw-alias-state-error-primary, #f85149)",
|
|
495
|
+
background: "var(--dsw-alias-bg-layer-2)"
|
|
496
|
+
},
|
|
352
497
|
head: {
|
|
353
498
|
display: "flex",
|
|
354
499
|
alignItems: "center",
|
|
@@ -396,6 +541,14 @@ var S = {
|
|
|
396
541
|
cursor: "pointer"
|
|
397
542
|
},
|
|
398
543
|
testResult: {
|
|
544
|
+
// A flex row so the target can drop onto its own line: the container was a
|
|
545
|
+
// plain span, where `flexBasis: 100%` does nothing and the target ran into
|
|
546
|
+
// the "cached" label.
|
|
547
|
+
display: "flex",
|
|
548
|
+
flexWrap: "wrap",
|
|
549
|
+
alignItems: "baseline",
|
|
550
|
+
columnGap: "6px",
|
|
551
|
+
rowGap: "2px",
|
|
399
552
|
fontSize: "11px",
|
|
400
553
|
color: "var(--dsw-alias-label-secondary)",
|
|
401
554
|
wordBreak: "break-all"
|
|
@@ -439,6 +592,10 @@ var S = {
|
|
|
439
592
|
color: "var(--dsw-alias-label-secondary)"
|
|
440
593
|
},
|
|
441
594
|
pillOff: { color: "var(--dsw-alias-state-idle-primary)" },
|
|
595
|
+
pillRejected: {
|
|
596
|
+
border: "1px solid var(--dsw-alias-state-error-primary, #f85149)",
|
|
597
|
+
color: "var(--dsw-alias-state-error-primary, #f85149)"
|
|
598
|
+
},
|
|
442
599
|
/**
|
|
443
600
|
* The shared default endpoint sits on its own layer so it reads as global
|
|
444
601
|
* rather than as one more provider card — the mistake it exists to prevent is
|
|
@@ -500,6 +657,19 @@ var S = {
|
|
|
500
657
|
color: "var(--dsw-alias-label-primary)",
|
|
501
658
|
font: "inherit",
|
|
502
659
|
fontSize: "12px"
|
|
660
|
+
},
|
|
661
|
+
diag: {
|
|
662
|
+
marginTop: "4px",
|
|
663
|
+
padding: "8px 10px",
|
|
664
|
+
border: "1px solid var(--dsw-alias-border-l1)",
|
|
665
|
+
borderRadius: "8px",
|
|
666
|
+
fontSize: "11px",
|
|
667
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
668
|
+
},
|
|
669
|
+
hint: {
|
|
670
|
+
gridColumn: "2",
|
|
671
|
+
fontSize: "11px",
|
|
672
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
503
673
|
}
|
|
504
674
|
};
|
|
505
675
|
function renderPage(ctx) {
|
|
@@ -521,14 +691,20 @@ function renderPage(ctx) {
|
|
|
521
691
|
const [state, setState] = (React.useState ?? ((fn) => [fn(), () => {
|
|
522
692
|
}]))(() => {
|
|
523
693
|
void fetchCatalog().then((catalog) => catalogueSetter?.(catalog)).catch((error) => {
|
|
524
|
-
|
|
525
|
-
|
|
694
|
+
catalogueSetter?.({
|
|
695
|
+
rows: [],
|
|
696
|
+
hits: {},
|
|
697
|
+
problems: {},
|
|
698
|
+
lastUse: {},
|
|
699
|
+
error: String(error?.message ?? error)
|
|
700
|
+
});
|
|
526
701
|
});
|
|
527
702
|
return {
|
|
528
703
|
rows: void 0,
|
|
529
704
|
hits: {},
|
|
530
705
|
problems: {},
|
|
531
706
|
lastUse: {},
|
|
707
|
+
error: void 0,
|
|
532
708
|
pending: true
|
|
533
709
|
};
|
|
534
710
|
});
|
|
@@ -537,6 +713,7 @@ function renderPage(ctx) {
|
|
|
537
713
|
hits: catalog.hits,
|
|
538
714
|
problems: catalog.problems,
|
|
539
715
|
lastUse: catalog.lastUse,
|
|
716
|
+
error: catalog.error,
|
|
540
717
|
pending: false
|
|
541
718
|
});
|
|
542
719
|
touchCatalogPolling();
|
|
@@ -564,22 +741,56 @@ function renderPage(ctx) {
|
|
|
564
741
|
const [defaultDraft, setDefaultDraft] = (React.useState ?? ((v) => [v, () => {
|
|
565
742
|
}]))({});
|
|
566
743
|
const editDefault = (field2, value) => setDefaultDraft((current) => ({ ...current, [field2]: value }));
|
|
744
|
+
const [globalDraft, setGlobalDraft] = (React.useState ?? ((v) => [v, () => {
|
|
745
|
+
}]))({});
|
|
746
|
+
const editGlobal = (field2, value) => setGlobalDraft((current) => ({ ...current, [field2]: value }));
|
|
747
|
+
const [saveError, setSaveError] = (React.useState ?? ((v) => [v, () => {
|
|
748
|
+
}]))(void 0);
|
|
749
|
+
const [savedNote, setSavedNote] = (React.useState ?? ((v) => [v, () => {
|
|
750
|
+
}]))(void 0);
|
|
567
751
|
const [tests, setTests] = (React.useState ?? ((v) => [v, () => {
|
|
568
752
|
}]))({});
|
|
753
|
+
const invalidateTests = (ids) => {
|
|
754
|
+
if (ids.length === 0) return;
|
|
755
|
+
for (const id of ids) {
|
|
756
|
+
testGeneration.set(id, (testGeneration.get(id) ?? 0) + 1);
|
|
757
|
+
}
|
|
758
|
+
setTests((current) => {
|
|
759
|
+
let changed = false;
|
|
760
|
+
const next = { ...current };
|
|
761
|
+
for (const id of ids) {
|
|
762
|
+
if (next[id] === void 0) continue;
|
|
763
|
+
delete next[id];
|
|
764
|
+
changed = true;
|
|
765
|
+
}
|
|
766
|
+
return changed ? next : current;
|
|
767
|
+
});
|
|
768
|
+
};
|
|
569
769
|
const [filter, setFilter] = (React.useState ?? ((v) => [v, () => {
|
|
570
770
|
}]))("");
|
|
571
771
|
const runTest = (id) => {
|
|
772
|
+
const generation = (testGeneration.get(id) ?? 0) + 1;
|
|
773
|
+
testGeneration.set(id, generation);
|
|
572
774
|
setTests((current) => ({ ...current, [id]: { pending: true } }));
|
|
573
775
|
void (async () => {
|
|
574
776
|
try {
|
|
777
|
+
for (const [key, entry] of [...pendingWrites]) {
|
|
778
|
+
if (key.startsWith(`${id}\0`) || key.startsWith("\0default\0") || key.startsWith("\0global\0")) {
|
|
779
|
+
void entry.flush();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
await writeQueue;
|
|
783
|
+
if (testGeneration.get(id) !== generation) return;
|
|
575
784
|
const response = await globalThis.fetch(TEST_PATH, {
|
|
576
785
|
method: "POST",
|
|
577
786
|
headers: { "content-type": "application/json", accept: "application/json" },
|
|
578
787
|
body: JSON.stringify({ provider: id })
|
|
579
788
|
});
|
|
580
789
|
const body2 = await response.json();
|
|
790
|
+
if (testGeneration.get(id) !== generation) return;
|
|
581
791
|
setTests((current) => ({ ...current, [id]: testResultFrom(body2, t) }));
|
|
582
792
|
} catch (error) {
|
|
793
|
+
if (testGeneration.get(id) !== generation) return;
|
|
583
794
|
setTests((current) => ({
|
|
584
795
|
...current,
|
|
585
796
|
[id]: { pending: false, ok: false, error: String(error?.message ?? error) }
|
|
@@ -588,19 +799,25 @@ function renderPage(ctx) {
|
|
|
588
799
|
})();
|
|
589
800
|
};
|
|
590
801
|
const rows = knownProviders({ ...configured, ...draft }, published);
|
|
802
|
+
const inheritingIds = () => rows.filter((row) => view({ ...configured[row.id], ...draft[row.id] }).useDefault === true).map((row) => row.id);
|
|
591
803
|
const save = (key, path, label, value, onRefused) => {
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
804
|
+
const field2 = path[path.length - 1];
|
|
805
|
+
const secret = field2 === "password" && typeof value === "string" ? value : void 0;
|
|
806
|
+
const safeLabel = secret === void 0 ? label : `${label}\uFF08\u503C\u5DF2\u9690\u85CF\uFF09`;
|
|
807
|
+
scheduleWrite(
|
|
808
|
+
key,
|
|
809
|
+
() => writeConfig(ctx, [{ op: "set", path, value }]).then(() => {
|
|
810
|
+
setSaveError(void 0);
|
|
811
|
+
setSavedNote(secret === void 0 ? `\u5DF2\u4FDD\u5B58 ${label}` : `\u5DF2\u4FDD\u5B58 ${safeLabel}`);
|
|
598
812
|
}).catch((error) => {
|
|
599
|
-
|
|
813
|
+
const message = redactSensitiveText(String(error?.message ?? error), [
|
|
814
|
+
...secret === void 0 ? [] : [secret]
|
|
815
|
+
]);
|
|
816
|
+
setSavedNote(void 0);
|
|
817
|
+
setSaveError({ label: safeLabel, message });
|
|
600
818
|
onRefused();
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
pendingWrites.set(key, { timer });
|
|
819
|
+
})
|
|
820
|
+
);
|
|
604
821
|
};
|
|
605
822
|
const rollbackRouteField = (id, field2) => {
|
|
606
823
|
setDraft((current) => {
|
|
@@ -616,6 +833,7 @@ function renderPage(ctx) {
|
|
|
616
833
|
};
|
|
617
834
|
const bind = (id, field2) => (value) => {
|
|
618
835
|
edit(id, field2, value);
|
|
836
|
+
if (TEST_RELEVANT_FIELDS.has(field2)) invalidateTests([id]);
|
|
619
837
|
save(
|
|
620
838
|
`${id}\0${field2}`,
|
|
621
839
|
["providers", id, field2],
|
|
@@ -626,6 +844,7 @@ function renderPage(ctx) {
|
|
|
626
844
|
};
|
|
627
845
|
const bindDefault = (field2) => (value) => {
|
|
628
846
|
editDefault(field2, value);
|
|
847
|
+
invalidateTests(inheritingIds());
|
|
629
848
|
save(`\0default\0${field2}`, ["defaultProxy", field2], `defaultProxy.${field2}`, value, () => {
|
|
630
849
|
setDefaultDraft((current) => {
|
|
631
850
|
if (current[field2] === void 0) return current;
|
|
@@ -635,19 +854,34 @@ function renderPage(ctx) {
|
|
|
635
854
|
});
|
|
636
855
|
});
|
|
637
856
|
};
|
|
857
|
+
const bindGlobal = (field2) => (value) => {
|
|
858
|
+
editGlobal(field2, value);
|
|
859
|
+
invalidateTests(rows.map((row) => row.id));
|
|
860
|
+
save(`\0global\0${field2}`, [field2], field2, value, () => {
|
|
861
|
+
setGlobalDraft((current) => {
|
|
862
|
+
if (current[field2] === void 0) return current;
|
|
863
|
+
const next = { ...current };
|
|
864
|
+
delete next[field2];
|
|
865
|
+
return next;
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
};
|
|
638
869
|
const saveMany = (key, ops, label, onRefused) => {
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
870
|
+
const secrets = secretsFromOps(ops);
|
|
871
|
+
const hidesValues = ops.some((op) => op?.path?.[op.path.length - 1] === "password");
|
|
872
|
+
const safeLabel = hidesValues ? `${label}\uFF08\u503C\u5DF2\u9690\u85CF\uFF09` : label;
|
|
873
|
+
scheduleWrite(
|
|
874
|
+
key,
|
|
875
|
+
() => writeConfig(ctx, ops).then(() => {
|
|
876
|
+
setSaveError(void 0);
|
|
877
|
+
setSavedNote(`\u5DF2\u4FDD\u5B58 ${safeLabel}`);
|
|
645
878
|
}).catch((error) => {
|
|
646
|
-
|
|
879
|
+
const message = redactSensitiveText(String(error?.message ?? error), secrets);
|
|
880
|
+
setSavedNote(void 0);
|
|
881
|
+
setSaveError({ label: safeLabel, message });
|
|
647
882
|
onRefused();
|
|
648
|
-
})
|
|
649
|
-
|
|
650
|
-
pendingWrites.set(key, { timer });
|
|
883
|
+
})
|
|
884
|
+
);
|
|
651
885
|
};
|
|
652
886
|
const rollbackRouteFields = (id, fields) => {
|
|
653
887
|
setDraft((current) => {
|
|
@@ -663,6 +897,7 @@ function renderPage(ctx) {
|
|
|
663
897
|
};
|
|
664
898
|
const applyCardUrl = (id, parts) => {
|
|
665
899
|
setDraft((current) => ({ ...current, [id]: { ...current[id], ...parts } }));
|
|
900
|
+
invalidateTests([id]);
|
|
666
901
|
const fields = Object.keys(parts);
|
|
667
902
|
saveMany(
|
|
668
903
|
`${id}\0*paste`,
|
|
@@ -673,6 +908,7 @@ function renderPage(ctx) {
|
|
|
673
908
|
};
|
|
674
909
|
const applyDefaultUrl = (parts) => {
|
|
675
910
|
setDefaultDraft((current) => ({ ...current, ...parts }));
|
|
911
|
+
invalidateTests(inheritingIds());
|
|
676
912
|
const fields = Object.keys(parts);
|
|
677
913
|
saveMany(
|
|
678
914
|
"\0default\0*paste",
|
|
@@ -705,6 +941,7 @@ function renderPage(ctx) {
|
|
|
705
941
|
for (const id of ids) next[id] = { ...next[id], useDefault: true };
|
|
706
942
|
return next;
|
|
707
943
|
});
|
|
944
|
+
invalidateTests(ids);
|
|
708
945
|
saveMany(
|
|
709
946
|
"\0default\0applyAll",
|
|
710
947
|
ids.map((id) => ({ op: "set", path: ["providers", id, "useDefault"], value: true })),
|
|
@@ -754,7 +991,8 @@ function renderPage(ctx) {
|
|
|
754
991
|
const storedDefault = snapshot.value?.defaultProxy ?? {};
|
|
755
992
|
const defaultView = view({ ...storedDefault, ...defaultDraft });
|
|
756
993
|
const defaultWhy = defaultProxyProblem(defaultView);
|
|
757
|
-
const
|
|
994
|
+
const defaultHostWhy = defaultView.host.trim().length === 0 ? void 0 : proxyHostProblem(defaultView.host);
|
|
995
|
+
const defaultComplete = defaultWhy === void 0;
|
|
758
996
|
const defaultOrigin = `${defaultView.protocol}://${defaultView.host}:${defaultView.port}`;
|
|
759
997
|
const defaultBlock = h(
|
|
760
998
|
"div",
|
|
@@ -770,10 +1008,10 @@ function renderPage(ctx) {
|
|
|
770
1008
|
h(
|
|
771
1009
|
"span",
|
|
772
1010
|
{
|
|
773
|
-
className:
|
|
774
|
-
style: { ...S.pill, ...
|
|
1011
|
+
className: defaultComplete ? import_client.default.pillOn : import_client.default.pillOff,
|
|
1012
|
+
style: { ...S.pill, ...defaultComplete ? S.pillOn : S.pillOff }
|
|
775
1013
|
},
|
|
776
|
-
|
|
1014
|
+
defaultComplete ? t.complete : t.notConfigured
|
|
777
1015
|
)
|
|
778
1016
|
),
|
|
779
1017
|
h("p", { className: import_client.default.defaultLead, style: S.defaultLead }, t.defaultLead),
|
|
@@ -803,7 +1041,8 @@ function renderPage(ctx) {
|
|
|
803
1041
|
// Same convenience as the cards: a full URL fills the whole block.
|
|
804
1042
|
onPaste: onProxyUrlPaste(applyDefaultUrl)
|
|
805
1043
|
}),
|
|
806
|
-
t.hostHint
|
|
1044
|
+
defaultHostWhy ?? t.hostHint,
|
|
1045
|
+
defaultHostWhy !== void 0
|
|
807
1046
|
),
|
|
808
1047
|
field(
|
|
809
1048
|
t.port,
|
|
@@ -844,7 +1083,7 @@ function renderPage(ctx) {
|
|
|
844
1083
|
t.passwordHintProxy
|
|
845
1084
|
),
|
|
846
1085
|
// Bulk opt-in, in ONE write: a per-card walk would rebuild the table N times.
|
|
847
|
-
// Disabled while the default is
|
|
1086
|
+
// Disabled while the default configuration is incomplete, because pointing every card at an
|
|
848
1087
|
// empty endpoint would just refuse them all.
|
|
849
1088
|
h(
|
|
850
1089
|
"div",
|
|
@@ -855,13 +1094,82 @@ function renderPage(ctx) {
|
|
|
855
1094
|
className: import_client.default.testButton,
|
|
856
1095
|
style: S.testButton,
|
|
857
1096
|
type: "button",
|
|
858
|
-
disabled: !
|
|
1097
|
+
disabled: !defaultComplete || rows.length === 0,
|
|
859
1098
|
onClick: applyDefaultToAll
|
|
860
1099
|
},
|
|
861
1100
|
t.applyDefaultAll
|
|
862
1101
|
)
|
|
863
1102
|
)
|
|
864
1103
|
);
|
|
1104
|
+
const storedTestUrl = typeof snapshot.value?.testUrl === "string" ? snapshot.value.testUrl : DEFAULT_TEST_URL;
|
|
1105
|
+
const storedLocalDirect = snapshot.value?.localDirect !== false;
|
|
1106
|
+
const globalTestUrl = typeof globalDraft.testUrl === "string" ? globalDraft.testUrl : storedTestUrl;
|
|
1107
|
+
const globalLocalDirect = typeof globalDraft.localDirect === "boolean" ? globalDraft.localDirect : storedLocalDirect;
|
|
1108
|
+
const globalTestUrlHint = testUrlHint(globalTestUrl, t);
|
|
1109
|
+
const globalBlock = h(
|
|
1110
|
+
"details",
|
|
1111
|
+
{
|
|
1112
|
+
className: import_client.default.diag,
|
|
1113
|
+
style: {
|
|
1114
|
+
...S.diag,
|
|
1115
|
+
marginTop: "0"
|
|
1116
|
+
},
|
|
1117
|
+
key: "global-settings"
|
|
1118
|
+
},
|
|
1119
|
+
h("summary", { className: import_client.default.diagSummary }, t.globalTitle),
|
|
1120
|
+
h("p", { className: import_client.default.defaultLead, style: S.defaultLead }, t.globalLead),
|
|
1121
|
+
h(
|
|
1122
|
+
"label",
|
|
1123
|
+
{ className: import_client.default.field, style: S.field },
|
|
1124
|
+
h("span", { className: import_client.default.label, style: S.label }, t.testUrl),
|
|
1125
|
+
h("input", {
|
|
1126
|
+
className: import_client.default.input,
|
|
1127
|
+
style: S.input,
|
|
1128
|
+
value: globalTestUrl,
|
|
1129
|
+
onChange: (event) => bindGlobal("testUrl")(event.target.value)
|
|
1130
|
+
}),
|
|
1131
|
+
globalTestUrlHint === void 0 ? h("span", { style: S.hint }, t.testUrlHint) : h(
|
|
1132
|
+
"span",
|
|
1133
|
+
{
|
|
1134
|
+
className: import_client.default.hint,
|
|
1135
|
+
style: {
|
|
1136
|
+
...S.hint,
|
|
1137
|
+
color: "var(--dsw-alias-state-error-primary, #f85149)"
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
globalTestUrlHint
|
|
1141
|
+
)
|
|
1142
|
+
),
|
|
1143
|
+
h(
|
|
1144
|
+
"div",
|
|
1145
|
+
{ style: S.testRow },
|
|
1146
|
+
h(
|
|
1147
|
+
"button",
|
|
1148
|
+
{
|
|
1149
|
+
className: import_client.default.testButton,
|
|
1150
|
+
style: S.testButton,
|
|
1151
|
+
type: "button",
|
|
1152
|
+
onClick: () => bindGlobal("testUrl")(DEFAULT_TEST_URL)
|
|
1153
|
+
},
|
|
1154
|
+
t.restoreDefaultTestUrl
|
|
1155
|
+
)
|
|
1156
|
+
),
|
|
1157
|
+
h(
|
|
1158
|
+
"label",
|
|
1159
|
+
{ className: import_client.default.toggle, style: S.toggle },
|
|
1160
|
+
h("input", {
|
|
1161
|
+
type: "checkbox",
|
|
1162
|
+
checked: globalLocalDirect,
|
|
1163
|
+
onChange: (event) => bindGlobal("localDirect")(event.target.checked === true)
|
|
1164
|
+
}),
|
|
1165
|
+
h("span", null, t.localDirect)
|
|
1166
|
+
),
|
|
1167
|
+
h(
|
|
1168
|
+
"p",
|
|
1169
|
+
{ style: S.hint },
|
|
1170
|
+
globalLocalDirect ? t.localDirectOnHint : t.localDirectOffHint
|
|
1171
|
+
)
|
|
1172
|
+
);
|
|
865
1173
|
const query = filter.trim().toLowerCase();
|
|
866
1174
|
const shown = query.length === 0 ? rows : rows.filter((row) => `${row.id} ${row.name}`.toLowerCase().includes(query));
|
|
867
1175
|
const filterRow = rows.length === 0 ? null : field(
|
|
@@ -874,6 +1182,12 @@ function renderPage(ctx) {
|
|
|
874
1182
|
onChange: (event) => setFilter(event.target.value)
|
|
875
1183
|
})
|
|
876
1184
|
);
|
|
1185
|
+
const catalogueStatus = rows.length === 0 ? state.pending ? { text: t.catalogueLoading, error: false } : state.error !== void 0 ? { text: `${t.catalogueError}${state.error}`, error: true } : void 0 : void 0;
|
|
1186
|
+
const catalogueNotice = state.error !== void 0 && rows.length > 0 ? h(
|
|
1187
|
+
"p",
|
|
1188
|
+
{ key: "catalogue-error", style: S.inheritWarning },
|
|
1189
|
+
`${t.catalogueStale}${state.error}`
|
|
1190
|
+
) : null;
|
|
877
1191
|
const body = shown.length === 0 ? [
|
|
878
1192
|
h(
|
|
879
1193
|
"p",
|
|
@@ -887,10 +1201,11 @@ function renderPage(ctx) {
|
|
|
887
1201
|
borderRadius: "8px",
|
|
888
1202
|
fontSize: "12px",
|
|
889
1203
|
textAlign: "center",
|
|
890
|
-
color: "var(--dsw-alias-state-idle-primary)"
|
|
1204
|
+
color: catalogueStatus?.error === true ? "var(--dsw-alias-state-error-primary, #f85149)" : "var(--dsw-alias-state-idle-primary)",
|
|
1205
|
+
...catalogueStatus?.error === true ? { fontWeight: "500" } : {}
|
|
891
1206
|
}
|
|
892
1207
|
},
|
|
893
|
-
query.length > 0 ? t.noMatch : t.empty
|
|
1208
|
+
catalogueStatus?.text ?? (query.length > 0 ? t.noMatch : t.empty)
|
|
894
1209
|
)
|
|
895
1210
|
] : shown.map((row) => {
|
|
896
1211
|
const route = view({ ...configured[row.id], ...draft[row.id] });
|
|
@@ -899,8 +1214,15 @@ function renderPage(ctx) {
|
|
|
899
1214
|
const onlyDead = deadRuleEntries(route.onlyHosts);
|
|
900
1215
|
const bypassDead = deadRuleEntries(route.bypassHosts);
|
|
901
1216
|
const useDefault = route.useDefault === true;
|
|
902
|
-
const endpointStored = route.useDefault ?
|
|
1217
|
+
const endpointStored = route.useDefault ? defaultComplete : route.host.trim().length > 0 && Number.isInteger(route.port) && route.port >= 1 && route.port <= 65535;
|
|
903
1218
|
const showTest = route.enabled || endpointStored;
|
|
1219
|
+
const problemText = (() => {
|
|
1220
|
+
const recorded = problems[row.id];
|
|
1221
|
+
if (recorded !== void 0) return recorded;
|
|
1222
|
+
if (useDefault) return defaultComplete ? void 0 : `${t.defaultMissing}${defaultWhy}`;
|
|
1223
|
+
return route.enabled ? defaultProxyProblem(route) : void 0;
|
|
1224
|
+
})();
|
|
1225
|
+
const rejected = route.enabled && problemText !== void 0;
|
|
904
1226
|
const endpointFields = [
|
|
905
1227
|
field(
|
|
906
1228
|
t.protocol,
|
|
@@ -975,11 +1297,12 @@ function renderPage(ctx) {
|
|
|
975
1297
|
route.protocol === "socks5" ? t.passwordHint : t.passwordHintProxy
|
|
976
1298
|
)
|
|
977
1299
|
];
|
|
1300
|
+
const pill = rejected ? { className: import_client.default.pillRejected, style: { ...S.pill, ...S.pillRejected }, text: t.rejected } : route.enabled ? { className: import_client.default.pillOn, style: { ...S.pill, ...S.pillOn }, text: t.on } : { className: import_client.default.pillLive, style: { ...S.pill, ...S.pillLive }, text: t.off };
|
|
978
1301
|
return h(
|
|
979
1302
|
"section",
|
|
980
1303
|
{
|
|
981
|
-
className: route.enabled ? import_client.default.cardActive : import_client.default.card,
|
|
982
|
-
style: route.enabled ? { ...S.card, ...S.cardActive } : S.card,
|
|
1304
|
+
className: rejected ? import_client.default.cardRejected : route.enabled ? import_client.default.cardActive : import_client.default.card,
|
|
1305
|
+
style: rejected ? { ...S.card, ...S.cardRejected } : route.enabled ? { ...S.card, ...S.cardActive } : S.card,
|
|
983
1306
|
key: row.id
|
|
984
1307
|
},
|
|
985
1308
|
h(
|
|
@@ -1010,17 +1333,7 @@ function renderPage(ctx) {
|
|
|
1010
1333
|
lastText
|
|
1011
1334
|
)
|
|
1012
1335
|
),
|
|
1013
|
-
h(
|
|
1014
|
-
"span",
|
|
1015
|
-
{
|
|
1016
|
-
className: route.enabled ? import_client.default.pillOn : row.live ? import_client.default.pillLive : import_client.default.pillOff,
|
|
1017
|
-
style: {
|
|
1018
|
-
...S.pill,
|
|
1019
|
-
...route.enabled ? S.pillOn : row.live ? S.pillLive : S.pillOff
|
|
1020
|
-
}
|
|
1021
|
-
},
|
|
1022
|
-
route.enabled ? t.on : row.live ? t.live : t.off
|
|
1023
|
-
)
|
|
1336
|
+
h("span", { className: pill.className, style: pill.style }, pill.text)
|
|
1024
1337
|
),
|
|
1025
1338
|
h(
|
|
1026
1339
|
"label",
|
|
@@ -1046,16 +1359,12 @@ function renderPage(ctx) {
|
|
|
1046
1359
|
}),
|
|
1047
1360
|
h("span", null, t.useDefault)
|
|
1048
1361
|
),
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
},
|
|
1056
|
-
defaultUsable ? `${t.inheritingDefault}${defaultOrigin}` : `${t.defaultMissing}${defaultWhy}`
|
|
1057
|
-
)
|
|
1058
|
-
] : endpointFields,
|
|
1362
|
+
problemText !== void 0 ? h("p", { key: `${row.id}:problem`, style: S.inheritWarning }, problemText) : useDefault ? h(
|
|
1363
|
+
"p",
|
|
1364
|
+
{ key: `${row.id}:inherit`, style: S.inheritNote },
|
|
1365
|
+
`${t.inheritingDefault}${defaultOrigin}`
|
|
1366
|
+
) : null,
|
|
1367
|
+
...useDefault ? [] : endpointFields,
|
|
1059
1368
|
// The self-check is offered whenever there is something to check: an
|
|
1060
1369
|
// enabled route (the Host's table carries it, and a rejected one still
|
|
1061
1370
|
// reports WHY), or a disabled one whose endpoint is already complete —
|
|
@@ -1079,19 +1388,56 @@ function renderPage(ctx) {
|
|
|
1079
1388
|
result?.pending === true ? t.testRunning : t.testButton
|
|
1080
1389
|
),
|
|
1081
1390
|
result?.pending === false ? h(
|
|
1082
|
-
"
|
|
1391
|
+
"div",
|
|
1083
1392
|
{ style: S.testResult },
|
|
1084
1393
|
result.ok === true ? [
|
|
1085
|
-
//
|
|
1086
|
-
//
|
|
1394
|
+
// The band colour belongs to the LATENCY alone. The
|
|
1395
|
+
// status sits beside it in the plain label colour:
|
|
1396
|
+
// colouring the whole line made a successful check
|
|
1397
|
+
// over 1500 ms read as a failure.
|
|
1087
1398
|
h(
|
|
1088
1399
|
"span",
|
|
1089
1400
|
{ key: "lat", style: { color: latencyColor(result.ms) } },
|
|
1090
1401
|
`\u2713 ${typeof result.ms === "number" ? result.ms : "\u2014"} ms`
|
|
1091
1402
|
),
|
|
1092
|
-
result.
|
|
1093
|
-
|
|
1094
|
-
|
|
1403
|
+
typeof result.status === "number" ? h(
|
|
1404
|
+
"span",
|
|
1405
|
+
{ key: "status" },
|
|
1406
|
+
`HTTP ${result.status}`
|
|
1407
|
+
) : null,
|
|
1408
|
+
result.ip === void 0 ? null : h("span", { key: "ip" }, `${t.exitIpLabel}${result.ip}`),
|
|
1409
|
+
result.direct === true ? h("span", { key: "direct" }, t.directLabel) : null,
|
|
1410
|
+
result.cached === true ? h("span", { key: "cached" }, t.cachedLabel) : null,
|
|
1411
|
+
result.target === void 0 ? null : h(
|
|
1412
|
+
"span",
|
|
1413
|
+
{
|
|
1414
|
+
key: "target",
|
|
1415
|
+
style: {
|
|
1416
|
+
flexBasis: "100%",
|
|
1417
|
+
fontSize: "11px",
|
|
1418
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
1419
|
+
wordBreak: "break-all"
|
|
1420
|
+
}
|
|
1421
|
+
},
|
|
1422
|
+
`${t.testTargetLabel}${result.target}${targetSourceSuffix(result, t)}`
|
|
1423
|
+
)
|
|
1424
|
+
] : [
|
|
1425
|
+
h("span", { key: "err", style: S.testFailure }, `\u2717 ${result.error}`),
|
|
1426
|
+
typeof result.status === "number" ? h("span", { key: "status" }, `HTTP ${result.status}`) : null,
|
|
1427
|
+
result.target === void 0 ? null : h(
|
|
1428
|
+
"span",
|
|
1429
|
+
{
|
|
1430
|
+
key: "target",
|
|
1431
|
+
style: {
|
|
1432
|
+
flexBasis: "100%",
|
|
1433
|
+
fontSize: "11px",
|
|
1434
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
1435
|
+
wordBreak: "break-all"
|
|
1436
|
+
}
|
|
1437
|
+
},
|
|
1438
|
+
`${t.testTargetLabel}${result.target}${targetSourceSuffix(result, t)}`
|
|
1439
|
+
)
|
|
1440
|
+
]
|
|
1095
1441
|
) : null,
|
|
1096
1442
|
// Promote this card's endpoint to the shared default. Offered only
|
|
1097
1443
|
// when the card carries its OWN endpoint: an inheriting card would
|
|
@@ -1157,7 +1503,41 @@ function renderPage(ctx) {
|
|
|
1157
1503
|
"div",
|
|
1158
1504
|
{ className: import_client.default.root, style: S.root },
|
|
1159
1505
|
h("p", { className: import_client.default.lead, style: { margin: "0 0 4px", fontSize: "12px", lineHeight: "1.6", color: "var(--dsw-alias-label-secondary)" } }, t.lead),
|
|
1506
|
+
saveError === void 0 ? null : h(
|
|
1507
|
+
"div",
|
|
1508
|
+
{
|
|
1509
|
+
role: "alert",
|
|
1510
|
+
key: "save-error",
|
|
1511
|
+
style: {
|
|
1512
|
+
display: "flex",
|
|
1513
|
+
alignItems: "flex-start",
|
|
1514
|
+
justifyContent: "space-between",
|
|
1515
|
+
gap: "10px",
|
|
1516
|
+
padding: "8px 10px",
|
|
1517
|
+
border: "1px solid var(--dsw-alias-state-error-primary, #f85149)",
|
|
1518
|
+
borderRadius: "8px",
|
|
1519
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
1520
|
+
color: "var(--dsw-alias-state-error-primary, #f85149)",
|
|
1521
|
+
fontSize: "12px",
|
|
1522
|
+
fontWeight: "500",
|
|
1523
|
+
wordBreak: "break-all"
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
h("span", null, `${t.saveFailedPrefix}${saveError.label} \u2014 ${saveError.message}`),
|
|
1527
|
+
h(
|
|
1528
|
+
"button",
|
|
1529
|
+
{
|
|
1530
|
+
className: import_client.default.testButton,
|
|
1531
|
+
style: { ...S.testButton, flex: "none" },
|
|
1532
|
+
type: "button",
|
|
1533
|
+
onClick: () => setSaveError(void 0)
|
|
1534
|
+
},
|
|
1535
|
+
t.close
|
|
1536
|
+
)
|
|
1537
|
+
),
|
|
1160
1538
|
defaultBlock,
|
|
1539
|
+
globalBlock,
|
|
1540
|
+
catalogueNotice,
|
|
1161
1541
|
filterRow,
|
|
1162
1542
|
...body,
|
|
1163
1543
|
h(
|
|
@@ -1185,8 +1565,9 @@ function renderPage(ctx) {
|
|
|
1185
1565
|
h(
|
|
1186
1566
|
"ul",
|
|
1187
1567
|
{ className: import_client.default.diagList, style: { margin: "6px 0 0", paddingLeft: "18px" } },
|
|
1188
|
-
...problemCount === 0 && diagnostics.length === 0 ? [h("li", { key: "none" }, t.diagPending)] : [
|
|
1568
|
+
...problemCount === 0 && diagnostics.length === 0 && savedNote === void 0 ? [h("li", { key: "none" }, t.diagPending)] : [
|
|
1189
1569
|
...problemLines,
|
|
1570
|
+
...savedNote === void 0 ? [] : [h("li", { key: "saved" }, savedNote)],
|
|
1190
1571
|
...diagnostics.map((line, index) => h("li", { key: `d${index}` }, line))
|
|
1191
1572
|
]
|
|
1192
1573
|
)
|
|
@@ -1194,14 +1575,17 @@ function renderPage(ctx) {
|
|
|
1194
1575
|
);
|
|
1195
1576
|
}
|
|
1196
1577
|
var EN = {
|
|
1197
|
-
lead: "Route selected model providers through your own forward proxy. Other providers stay direct. Requests to loopback or private addresses (localhost, 127.x, private ranges)
|
|
1578
|
+
lead: "Route selected model providers through your own forward proxy. Other providers stay direct. Requests to loopback or private addresses (localhost, 127.x, private ranges) go direct by default; set the top-level localDirect to false to proxy them instead \u2014 a forward proxy normally cannot reach your own machine.",
|
|
1198
1579
|
empty: "No providers found yet.",
|
|
1580
|
+
catalogueLoading: "Loading providers\u2026",
|
|
1581
|
+
catalogueError: "Could not load the provider list: ",
|
|
1582
|
+
catalogueStale: "Could not load the provider list; showing the saved configuration: ",
|
|
1199
1583
|
useProxy: "Use a proxy for this provider",
|
|
1200
1584
|
useDefault: "Use the default proxy",
|
|
1201
1585
|
defaultTitle: "Default proxy",
|
|
1202
|
-
defaultLead: 'One endpoint, reused by every provider that turns on "Use the default proxy".',
|
|
1586
|
+
defaultLead: 'One endpoint, reused by every provider that turns on "Use the default proxy". The status only reports configuration completeness; use the Test button on a card to verify reachability.',
|
|
1203
1587
|
inheritingDefault: "Using the default proxy: ",
|
|
1204
|
-
defaultMissing: "The default proxy
|
|
1588
|
+
defaultMissing: "The default proxy configuration is incomplete, so this provider is not proxied: ",
|
|
1205
1589
|
filterLabel: "Filter",
|
|
1206
1590
|
filterPlaceholder: "Filter by name or id\u2026",
|
|
1207
1591
|
noMatch: "No provider matches the filter.",
|
|
@@ -1212,7 +1596,7 @@ var EN = {
|
|
|
1212
1596
|
port: "Port",
|
|
1213
1597
|
username: "Username",
|
|
1214
1598
|
password: "Password",
|
|
1215
|
-
hostHint: "
|
|
1599
|
+
hostHint: "Only an IPv4, IPv6, or domain name is allowed. A full proxy URL (socks5://user:pass@host:1080) can be pasted instead \u2014 it is split into the fields.",
|
|
1216
1600
|
passwordHint: "Sent to the SOCKS5 proxy for authentication; stored with this setting.",
|
|
1217
1601
|
passwordHintProxy: "Sent to the proxy as an authentication password; stored with this setting.",
|
|
1218
1602
|
hitCountPrefix: "",
|
|
@@ -1231,30 +1615,53 @@ var EN = {
|
|
|
1231
1615
|
testFailed: "Test failed",
|
|
1232
1616
|
exitIpLabel: "exit IP ",
|
|
1233
1617
|
directLabel: "direct (not through the proxy)",
|
|
1618
|
+
cachedLabel: "cached",
|
|
1619
|
+
testTargetLabel: "Target: ",
|
|
1620
|
+
targetSourceRoute: " (this card's own target)",
|
|
1621
|
+
targetSourceUpstream: " (this provider's real upstream)",
|
|
1622
|
+
targetSourceTestUrl: " (global self-check URL)",
|
|
1623
|
+
globalTitle: "Global settings (advanced)",
|
|
1624
|
+
globalLead: "These fields are saved with the plugin rather than per provider. Leave testUrl empty to use the built-in default; comma/space separated candidates are tried in order and only the first 5 are used. localDirect is on by default; turn it off only when the proxy itself is a loopback or private target.",
|
|
1625
|
+
testUrl: "Self-check URL",
|
|
1626
|
+
testUrlHint: "Comma/space separated; http(s) URLs in the order they should be tried.",
|
|
1627
|
+
testUrlEmptyHint: "Empty means the Host uses its built-in default URL.",
|
|
1628
|
+
testUrlInvalidHint: "Some candidates are not absolute http(s) URLs. They are saved, but those checks will fail.",
|
|
1629
|
+
testUrlTooManyHint: "More than 5 candidates are listed; only the first 5 take effect.",
|
|
1630
|
+
restoreDefaultTestUrl: "Restore default",
|
|
1631
|
+
localDirect: "Loopback / private targets go direct",
|
|
1632
|
+
localDirectOnHint: "On: localhost, 127.x and private ranges bypass the proxy.",
|
|
1633
|
+
localDirectOffHint: "Off: loopback/private targets also use the proxy. Enable only when the proxy itself is the target.",
|
|
1234
1634
|
advanced: "Advanced: target host rules",
|
|
1235
1635
|
onlyHosts: "Proxy only",
|
|
1236
1636
|
onlyHostsHint: "Comma/space separated; when set, only requests to these hosts are proxied (suffix match, * for all).",
|
|
1237
1637
|
bypassHosts: "Bypass",
|
|
1238
1638
|
bypassHostsHint: "Comma/space separated; requests to these hosts go direct, taking priority.",
|
|
1239
|
-
ruleDeadHint: "These entries never match \u2014
|
|
1639
|
+
ruleDeadHint: "These entries never match \u2014 use bare host names (no port, scheme or path); a wildcard must be `*` or start with `*.`: ",
|
|
1240
1640
|
testTarget: "Self-check target",
|
|
1241
1641
|
testTargetHint: "Empty uses the top-level testUrl; when set, this card's Test requests this URL instead \u2014 point it at a real upstream to verify the end-to-end path.",
|
|
1242
1642
|
socks5Hint: "SOCKS5 tunnels the connection; the proxy resolves the target host name.",
|
|
1243
1643
|
on: "proxied",
|
|
1244
|
-
|
|
1644
|
+
rejected: "config rejected",
|
|
1245
1645
|
off: "direct",
|
|
1646
|
+
complete: "complete",
|
|
1647
|
+
notConfigured: "not configured",
|
|
1246
1648
|
diagnostics: "Connection diagnostics",
|
|
1247
|
-
diagPending: "(collecting\u2026)"
|
|
1649
|
+
diagPending: "(collecting\u2026)",
|
|
1650
|
+
saveFailedPrefix: "Save failed: ",
|
|
1651
|
+
close: "Close"
|
|
1248
1652
|
};
|
|
1249
1653
|
var ZH = {
|
|
1250
|
-
lead: "\u8BA9\u6307\u5B9A\u7684 provider \u8D70\u4F60\u81EA\u5DF1\u7684\u6B63\u5411\u4EE3\u7406\uFF0C\u5176\u5B83 provider \u4FDD\u6301\u76F4\u8FDE\u3002\u6307\u5411\u672C\u673A\u6216\u5185\u7F51\u5730\u5740\uFF08localhost\u3001127.x\u3001\u79C1\u6709\u7F51\u6BB5\uFF09\u7684\u8BF7\u6C42\
|
|
1654
|
+
lead: "\u8BA9\u6307\u5B9A\u7684 provider \u8D70\u4F60\u81EA\u5DF1\u7684\u6B63\u5411\u4EE3\u7406\uFF0C\u5176\u5B83 provider \u4FDD\u6301\u76F4\u8FDE\u3002\u6307\u5411\u672C\u673A\u6216\u5185\u7F51\u5730\u5740\uFF08localhost\u3001127.x\u3001\u79C1\u6709\u7F51\u6BB5\uFF09\u7684\u8BF7\u6C42\u9ED8\u8BA4\u76F4\u8FDE\uFF0C\u9876\u5C42 localDirect \u8BBE\u4E3A false \u65F6\u6062\u590D\u8D70\u4EE3\u7406\u2014\u2014\u6B63\u5411\u4EE3\u7406\u901A\u5E38\u591F\u4E0D\u5230\u4F60\u81EA\u5DF1\u7684\u673A\u5668\u3002",
|
|
1251
1655
|
empty: "\u6CA1\u6709\u68C0\u6D4B\u5230\u4EFB\u4F55 provider\u3002",
|
|
1656
|
+
catalogueLoading: "\u6B63\u5728\u8BFB\u53D6 provider \u5217\u8868\u2026",
|
|
1657
|
+
catalogueError: "\u8BFB\u53D6 provider \u5217\u8868\u5931\u8D25\uFF1A",
|
|
1658
|
+
catalogueStale: "provider \u5217\u8868\u8BFB\u53D6\u5931\u8D25\uFF0C\u4E0B\u9762\u663E\u793A\u7684\u662F\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\uFF1A",
|
|
1252
1659
|
useProxy: "\u4E3A\u8BE5 provider \u542F\u7528\u4EE3\u7406",
|
|
1253
1660
|
useDefault: "\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406",
|
|
1254
1661
|
defaultTitle: "\u9ED8\u8BA4\u4EE3\u7406",
|
|
1255
|
-
defaultLead: "\u4E3A\u52FE\u9009\u300C\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406\u300D\u7684 provider \u590D\u7528\u7684\u7AEF\u70B9\uFF0C\u53EA\u586B\u4E00\u6B21\u3002",
|
|
1662
|
+
defaultLead: "\u4E3A\u52FE\u9009\u300C\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406\u300D\u7684 provider \u590D\u7528\u7684\u7AEF\u70B9\uFF0C\u53EA\u586B\u4E00\u6B21\u3002\u72B6\u6001\u53EA\u8868\u793A\u914D\u7F6E\u662F\u5426\u5B8C\u6574\uFF1B\u4EE3\u7406\u662F\u5426\u53EF\u8FBE\u8BF7\u7528\u5361\u7247\u4E0A\u7684\u300C\u6D4B\u8BD5\u300D\u9A8C\u8BC1\u3002",
|
|
1256
1663
|
inheritingDefault: "\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406\uFF1A",
|
|
1257
|
-
defaultMissing: "\u9876\u5C42\u9ED8\u8BA4\u4EE3\u7406\u4E0D\
|
|
1664
|
+
defaultMissing: "\u9876\u5C42\u9ED8\u8BA4\u4EE3\u7406\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8BE5 provider \u4E0D\u4F1A\u8D70\u4EE3\u7406\uFF1A",
|
|
1258
1665
|
filterLabel: "\u8FC7\u6EE4",
|
|
1259
1666
|
filterPlaceholder: "\u6309\u540D\u79F0\u6216 id \u8FC7\u6EE4\u2026",
|
|
1260
1667
|
noMatch: "\u6CA1\u6709\u5339\u914D\u7684 provider\u3002",
|
|
@@ -1265,7 +1672,7 @@ var ZH = {
|
|
|
1265
1672
|
port: "\u7AEF\u53E3",
|
|
1266
1673
|
username: "\u7528\u6237\u540D",
|
|
1267
1674
|
password: "\u5BC6\u7801",
|
|
1268
|
-
hostHint: "\u53EA\
|
|
1675
|
+
hostHint: "\u53EA\u5141\u8BB8\u586B\u5199 IPv4\u3001IPv6 \u6216\u57DF\u540D\uFF1B\u4E5F\u53EF\u4EE5\u76F4\u63A5\u7C98\u8D34\u5B8C\u6574\u4EE3\u7406 URL\uFF08\u5982 socks5://user:pass@host:1080\uFF09\uFF0C\u4F1A\u81EA\u52A8\u62C6\u5206\u5230\u5404\u5B57\u6BB5\u3002",
|
|
1269
1676
|
passwordHint: "\u4EC5\u4F5C\u4E3A SOCKS5 \u8BA4\u8BC1\u53E3\u4EE4\u53D1\u9001\u7ED9\u4EE3\u7406\uFF1B\u968F\u8BE5\u8BBE\u7F6E\u4E00\u8D77\u4FDD\u5B58\u3002",
|
|
1270
1677
|
passwordHintProxy: "\u4F5C\u4E3A\u4EE3\u7406\u8BA4\u8BC1\u53E3\u4EE4\u53D1\u9001\u7ED9\u4EE3\u7406\uFF1B\u968F\u8BE5\u8BBE\u7F6E\u4E00\u8D77\u4FDD\u5B58\u3002",
|
|
1271
1678
|
hitCountPrefix: "\u5DF2\u4EE3\u7406 ",
|
|
@@ -1284,20 +1691,40 @@ var ZH = {
|
|
|
1284
1691
|
testFailed: "\u6D4B\u8BD5\u5931\u8D25",
|
|
1285
1692
|
exitIpLabel: "\u51FA\u53E3 IP ",
|
|
1286
1693
|
directLabel: "\u76F4\u8FDE\uFF08\u672A\u7ECF\u8FC7\u4EE3\u7406\uFF09",
|
|
1694
|
+
cachedLabel: "\u7F13\u5B58",
|
|
1695
|
+
testTargetLabel: "\u76EE\u6807 ",
|
|
1696
|
+
targetSourceRoute: "\uFF08\u672C\u5361\u7247\u81EA\u68C0\u76EE\u6807\uFF09",
|
|
1697
|
+
targetSourceUpstream: "\uFF08\u8BE5 provider \u7684\u771F\u5B9E\u4E0A\u6E38\uFF09",
|
|
1698
|
+
targetSourceTestUrl: "\uFF08\u5168\u5C40\u81EA\u68C0\u5730\u5740\uFF09",
|
|
1699
|
+
globalTitle: "\u5168\u5C40\u8BBE\u7F6E\uFF08\u9AD8\u7EA7\uFF09",
|
|
1700
|
+
globalLead: "\u8FD9\u91CC\u7684\u4E24\u9879\u5C5E\u4E8E\u63D2\u4EF6\u5168\u5C40\u8BBE\u7F6E\u3002testUrl \u7559\u7A7A\u65F6 Host \u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u503C\uFF1B\u591A\u4E2A\u5019\u9009\u7528\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF0C\u6309\u987A\u5E8F\u56DE\u9000\uFF0C\u53EA\u6709\u524D 5 \u4E2A\u751F\u6548\u3002localDirect \u9ED8\u8BA4\u5F00\u542F\uFF1B\u53EA\u6709\u4EE3\u7406\u672C\u8EAB\u5C31\u662F\u672C\u673A\u6216\u5185\u7F51\u76EE\u6807\u65F6\u624D\u9700\u8981\u5173\u95ED\u3002",
|
|
1701
|
+
testUrl: "\u81EA\u68C0\u5730\u5740",
|
|
1702
|
+
testUrlHint: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u6309\u987A\u5E8F\u5C1D\u8BD5\u7684 http(s) URL\u3002",
|
|
1703
|
+
testUrlEmptyHint: "\u7559\u7A7A\u65F6 Host \u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u5730\u5740\u3002",
|
|
1704
|
+
testUrlInvalidHint: "\u90E8\u5206\u5019\u9009\u4E0D\u662F\u7EDD\u5BF9\u7684 http(s) URL\u3002\u4ECD\u4F1A\u4FDD\u5B58\uFF0C\u4F46\u8FD9\u4E9B\u5019\u9009\u4F1A\u6D4B\u8BD5\u5931\u8D25\u3002",
|
|
1705
|
+
testUrlTooManyHint: "\u5019\u9009\u8D85\u8FC7 5 \u4E2A\uFF0C\u53EA\u6709\u524D 5 \u4E2A\u751F\u6548\u3002",
|
|
1706
|
+
restoreDefaultTestUrl: "\u6062\u590D\u9ED8\u8BA4",
|
|
1707
|
+
localDirect: "\u672C\u673A / \u5185\u7F51\u76EE\u6807\u76F4\u8FDE",
|
|
1708
|
+
localDirectOnHint: "\u5F00\u542F\uFF1Alocalhost\u3001127.x \u4E0E\u79C1\u6709\u7F51\u6BB5\u7ED5\u8FC7\u4EE3\u7406\u3002",
|
|
1709
|
+
localDirectOffHint: "\u5173\u95ED\uFF1A\u672C\u673A / \u5185\u7F51\u76EE\u6807\u4E5F\u8D70\u4EE3\u7406\uFF1B\u4EC5\u5728\u4EE3\u7406\u672C\u8EAB\u5C31\u662F\u76EE\u6807\u65F6\u4F7F\u7528\u3002",
|
|
1287
1710
|
advanced: "\u9AD8\u7EA7\uFF1A\u76EE\u6807\u57DF\u540D\u89C4\u5219",
|
|
1288
1711
|
onlyHosts: "\u4EC5\u8D70\u4EE3\u7406",
|
|
1289
1712
|
onlyHostsHint: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u975E\u7A7A\u65F6\u53EA\u6709\u5339\u914D\u8FD9\u4E9B\u4E3B\u673A\u7684\u8BF7\u6C42\u8D70\u4EE3\u7406\uFF08\u540E\u7F00\u5339\u914D\uFF0C* \u8868\u793A\u5168\u90E8\uFF09\u3002",
|
|
1290
1713
|
bypassHosts: "\u76F4\u8FDE",
|
|
1291
1714
|
bypassHostsHint: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u5339\u914D\u8FD9\u4E9B\u4E3B\u673A\u7684\u8BF7\u6C42\u76F4\u8FDE\uFF0C\u4F18\u5148\u4E8E\u4E0A\u4E00\u9879\u3002",
|
|
1292
|
-
ruleDeadHint: "\u4EE5\u4E0B\u6761\u76EE\u6C38\u8FDC\u5339\u914D\u4E0D\u4E0A\u2014\u2014\u89C4\u5219\u53EA\u8BA4\u4E3B\u673A\u540D\
|
|
1715
|
+
ruleDeadHint: "\u4EE5\u4E0B\u6761\u76EE\u6C38\u8FDC\u5339\u914D\u4E0D\u4E0A\u2014\u2014\u89C4\u5219\u53EA\u8BA4\u4E3B\u673A\u540D\uFF08\u4E0D\u8981\u5E26\u7AEF\u53E3\u3001\u534F\u8BAE\u524D\u7F00\u6216\u8DEF\u5F84\uFF09\uFF0C\u901A\u914D\u7B26\u53EA\u80FD\u662F\u5355\u72EC\u7684 * \u6216\u4EE5 *. \u5F00\u5934\uFF1A",
|
|
1293
1716
|
testTarget: "\u81EA\u68C0\u76EE\u6807",
|
|
1294
1717
|
testTargetHint: "\u7559\u7A7A\u7528\u9876\u5C42 testUrl\uFF1B\u586B\u5199\u540E\u8BE5\u5361\u7247\u7684\u300C\u6D4B\u8BD5\u300D\u6539\u4E3A\u8BF7\u6C42\u8FD9\u4E2A\u5730\u5740\uFF0C\u53EF\u62FF\u771F\u5B9E\u4E0A\u6E38\u9A8C\u8BC1\u7AEF\u5230\u7AEF\u8DEF\u5F84\u3002",
|
|
1295
1718
|
socks5Hint: "SOCKS5 \u4F1A\u5EFA\u7ACB\u96A7\u9053\uFF0C\u76EE\u6807\u57DF\u540D\u7531\u4EE3\u7406\u7AEF\u89E3\u6790\u3002",
|
|
1296
1719
|
on: "\u8D70\u4EE3\u7406",
|
|
1297
|
-
|
|
1720
|
+
rejected: "\u914D\u7F6E\u88AB\u62D2",
|
|
1298
1721
|
off: "\u76F4\u8FDE",
|
|
1722
|
+
complete: "\u914D\u7F6E\u5B8C\u6574",
|
|
1723
|
+
notConfigured: "\u672A\u914D\u7F6E",
|
|
1299
1724
|
diagnostics: "\u8FDE\u63A5\u8BCA\u65AD",
|
|
1300
|
-
diagPending: "\uFF08\u6B63\u5728\u6536\u96C6\u2026\uFF09"
|
|
1725
|
+
diagPending: "\uFF08\u6B63\u5728\u6536\u96C6\u2026\uFF09",
|
|
1726
|
+
saveFailedPrefix: "\u4FDD\u5B58\u5931\u8D25\uFF1A",
|
|
1727
|
+
close: "\u5173\u95ED"
|
|
1301
1728
|
};
|
|
1302
1729
|
function stringsFor(ctx) {
|
|
1303
1730
|
const language = localeOf(ctx);
|