dsh-provider-proxy 0.5.0 → 0.5.1
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 +23 -10
- package/lib/client.js +189 -71
- package/lib/client.js.map +2 -2
- package/lib/index.js +42 -24
- 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;
|
|
@@ -65,17 +65,34 @@ module.exports = __toCommonJS(client_exports);
|
|
|
65
65
|
var import_client = __toESM(require_client(), 1);
|
|
66
66
|
|
|
67
67
|
// src/proxy-address.ts
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
var PROXY_HOST_INVALID = "\u4EE3\u7406\u5730\u5740\u53EA\u80FD\u586B IPv4\u3001IPv6 \u6216\u57DF\u540D";
|
|
69
|
+
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)$/;
|
|
70
|
+
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;
|
|
71
|
+
var IPV6_CHARS = /^[0-9a-f:.]+$/i;
|
|
72
|
+
function proxyHostProblem(rawHost) {
|
|
73
|
+
const host = (rawHost ?? "").trim().replace(/\.$/, "");
|
|
70
74
|
if (host.length === 0) return "\u672A\u586B\u5199\u4EE3\u7406\u5730\u5740";
|
|
71
|
-
if (
|
|
72
|
-
|
|
75
|
+
if (IPV4.test(host)) return void 0;
|
|
76
|
+
if (host.includes(":") || host.startsWith("[")) {
|
|
77
|
+
const literal = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
78
|
+
if (!IPV6_CHARS.test(literal)) return PROXY_HOST_INVALID;
|
|
79
|
+
try {
|
|
80
|
+
new URL(`http://[${literal}]:1`);
|
|
81
|
+
return void 0;
|
|
82
|
+
} catch {
|
|
83
|
+
return PROXY_HOST_INVALID;
|
|
84
|
+
}
|
|
73
85
|
}
|
|
86
|
+
return DOMAIN.test(host) ? void 0 : PROXY_HOST_INVALID;
|
|
87
|
+
}
|
|
88
|
+
function defaultProxyProblem(endpoint) {
|
|
89
|
+
const hostProblem = proxyHostProblem(endpoint.host);
|
|
90
|
+
if (hostProblem !== void 0) return hostProblem;
|
|
74
91
|
const port = endpoint.port ?? 0;
|
|
75
92
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
76
93
|
return "\u4EE3\u7406\u7AEF\u53E3\u65E0\u6548\uFF08\u9700\u4E3A 1-65535 \u7684\u6574\u6570\uFF09";
|
|
77
94
|
}
|
|
78
|
-
const hasUser = (endpoint.username ?? "").length > 0;
|
|
95
|
+
const hasUser = (endpoint.username ?? "").trim().length > 0;
|
|
79
96
|
const hasPassword = (endpoint.password ?? "").length > 0;
|
|
80
97
|
if (hasUser !== hasPassword) {
|
|
81
98
|
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 +187,15 @@ async function writeConfig(ctx, ops) {
|
|
|
170
187
|
}
|
|
171
188
|
function knownProviders(configured, published) {
|
|
172
189
|
const rows = /* @__PURE__ */ new Map();
|
|
190
|
+
const byId = /* @__PURE__ */ new Map();
|
|
173
191
|
for (const row of published) {
|
|
192
|
+
byId.set(row.id, row);
|
|
174
193
|
if (row.live) rows.set(row.id, row);
|
|
175
194
|
}
|
|
176
|
-
for (const
|
|
195
|
+
for (const id of Object.keys(configured)) {
|
|
177
196
|
if (rows.has(id)) continue;
|
|
178
|
-
|
|
197
|
+
const known = byId.get(id);
|
|
198
|
+
rows.set(id, { id, name: known?.name ?? id, live: known?.live === true });
|
|
179
199
|
}
|
|
180
200
|
return [...rows.values()].sort(byName);
|
|
181
201
|
}
|
|
@@ -293,6 +313,14 @@ function view(route) {
|
|
|
293
313
|
function unbracket(hostname) {
|
|
294
314
|
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
295
315
|
}
|
|
316
|
+
function explicitPortOf(text) {
|
|
317
|
+
const authority = text.slice(text.indexOf("://") + 3).split(/[/?#]/, 1)[0];
|
|
318
|
+
const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
|
|
319
|
+
const match = /^\[[^\]]+\]:(\d+)$/.exec(hostPort) ?? /^[^:]+:(\d+)$/.exec(hostPort);
|
|
320
|
+
if (match === null) return 0;
|
|
321
|
+
const port = Number.parseInt(match[1], 10);
|
|
322
|
+
return Number.isNaN(port) ? 0 : port;
|
|
323
|
+
}
|
|
296
324
|
function splitProxyUrl(text) {
|
|
297
325
|
const trimmed = text.trim();
|
|
298
326
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) return void 0;
|
|
@@ -313,7 +341,7 @@ function splitProxyUrl(text) {
|
|
|
313
341
|
return value;
|
|
314
342
|
}
|
|
315
343
|
};
|
|
316
|
-
const parsedPort = url.port.length > 0 ? Number.parseInt(url.port, 10) :
|
|
344
|
+
const parsedPort = url.port.length > 0 ? Number.parseInt(url.port, 10) : explicitPortOf(trimmed);
|
|
317
345
|
return {
|
|
318
346
|
protocol,
|
|
319
347
|
host: unbracket(url.hostname),
|
|
@@ -324,6 +352,39 @@ function splitProxyUrl(text) {
|
|
|
324
352
|
}
|
|
325
353
|
var WRITE_DELAY_MS = 300;
|
|
326
354
|
var pendingWrites = /* @__PURE__ */ new Map();
|
|
355
|
+
var writeQueue = Promise.resolve();
|
|
356
|
+
function enqueueWrite(run) {
|
|
357
|
+
const next = writeQueue.then(run, run);
|
|
358
|
+
writeQueue = next.catch(() => void 0);
|
|
359
|
+
return next;
|
|
360
|
+
}
|
|
361
|
+
function scheduleWrite(key, run) {
|
|
362
|
+
const pending = pendingWrites.get(key);
|
|
363
|
+
if (pending?.timer !== void 0) clearTimeout(pending.timer);
|
|
364
|
+
const entry = {
|
|
365
|
+
flush: () => {
|
|
366
|
+
if (entry.timer !== void 0) clearTimeout(entry.timer);
|
|
367
|
+
pendingWrites.delete(key);
|
|
368
|
+
return enqueueWrite(run);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
entry.timer = setTimeout(() => {
|
|
372
|
+
void entry.flush();
|
|
373
|
+
}, WRITE_DELAY_MS);
|
|
374
|
+
pendingWrites.set(key, entry);
|
|
375
|
+
}
|
|
376
|
+
var testGeneration = /* @__PURE__ */ new Map();
|
|
377
|
+
var TEST_RELEVANT_FIELDS = /* @__PURE__ */ new Set([
|
|
378
|
+
"protocol",
|
|
379
|
+
"host",
|
|
380
|
+
"port",
|
|
381
|
+
"username",
|
|
382
|
+
"password",
|
|
383
|
+
"onlyHosts",
|
|
384
|
+
"bypassHosts",
|
|
385
|
+
"testTarget",
|
|
386
|
+
"useDefault"
|
|
387
|
+
]);
|
|
327
388
|
var diagnostics = [];
|
|
328
389
|
function note(message) {
|
|
329
390
|
if (!diagnostics.includes(message)) diagnostics.push(message);
|
|
@@ -349,6 +410,11 @@ var S = {
|
|
|
349
410
|
borderColor: "var(--dsw-alias-state-success-primary)",
|
|
350
411
|
background: "var(--dsw-alias-bg-layer-2)"
|
|
351
412
|
},
|
|
413
|
+
/** An enabled route the Host refused: active-looking styling would be a lie. */
|
|
414
|
+
cardRejected: {
|
|
415
|
+
borderColor: "var(--dsw-alias-state-error-primary, #f85149)",
|
|
416
|
+
background: "var(--dsw-alias-bg-layer-2)"
|
|
417
|
+
},
|
|
352
418
|
head: {
|
|
353
419
|
display: "flex",
|
|
354
420
|
alignItems: "center",
|
|
@@ -439,6 +505,10 @@ var S = {
|
|
|
439
505
|
color: "var(--dsw-alias-label-secondary)"
|
|
440
506
|
},
|
|
441
507
|
pillOff: { color: "var(--dsw-alias-state-idle-primary)" },
|
|
508
|
+
pillRejected: {
|
|
509
|
+
border: "1px solid var(--dsw-alias-state-error-primary, #f85149)",
|
|
510
|
+
color: "var(--dsw-alias-state-error-primary, #f85149)"
|
|
511
|
+
},
|
|
442
512
|
/**
|
|
443
513
|
* The shared default endpoint sits on its own layer so it reads as global
|
|
444
514
|
* rather than as one more provider card — the mistake it exists to prevent is
|
|
@@ -521,14 +591,20 @@ function renderPage(ctx) {
|
|
|
521
591
|
const [state, setState] = (React.useState ?? ((fn) => [fn(), () => {
|
|
522
592
|
}]))(() => {
|
|
523
593
|
void fetchCatalog().then((catalog) => catalogueSetter?.(catalog)).catch((error) => {
|
|
524
|
-
|
|
525
|
-
|
|
594
|
+
catalogueSetter?.({
|
|
595
|
+
rows: [],
|
|
596
|
+
hits: {},
|
|
597
|
+
problems: {},
|
|
598
|
+
lastUse: {},
|
|
599
|
+
error: String(error?.message ?? error)
|
|
600
|
+
});
|
|
526
601
|
});
|
|
527
602
|
return {
|
|
528
603
|
rows: void 0,
|
|
529
604
|
hits: {},
|
|
530
605
|
problems: {},
|
|
531
606
|
lastUse: {},
|
|
607
|
+
error: void 0,
|
|
532
608
|
pending: true
|
|
533
609
|
};
|
|
534
610
|
});
|
|
@@ -537,6 +613,7 @@ function renderPage(ctx) {
|
|
|
537
613
|
hits: catalog.hits,
|
|
538
614
|
problems: catalog.problems,
|
|
539
615
|
lastUse: catalog.lastUse,
|
|
616
|
+
error: catalog.error,
|
|
540
617
|
pending: false
|
|
541
618
|
});
|
|
542
619
|
touchCatalogPolling();
|
|
@@ -566,20 +643,47 @@ function renderPage(ctx) {
|
|
|
566
643
|
const editDefault = (field2, value) => setDefaultDraft((current) => ({ ...current, [field2]: value }));
|
|
567
644
|
const [tests, setTests] = (React.useState ?? ((v) => [v, () => {
|
|
568
645
|
}]))({});
|
|
646
|
+
const invalidateTests = (ids) => {
|
|
647
|
+
if (ids.length === 0) return;
|
|
648
|
+
for (const id of ids) {
|
|
649
|
+
testGeneration.set(id, (testGeneration.get(id) ?? 0) + 1);
|
|
650
|
+
}
|
|
651
|
+
setTests((current) => {
|
|
652
|
+
let changed = false;
|
|
653
|
+
const next = { ...current };
|
|
654
|
+
for (const id of ids) {
|
|
655
|
+
if (next[id] === void 0) continue;
|
|
656
|
+
delete next[id];
|
|
657
|
+
changed = true;
|
|
658
|
+
}
|
|
659
|
+
return changed ? next : current;
|
|
660
|
+
});
|
|
661
|
+
};
|
|
569
662
|
const [filter, setFilter] = (React.useState ?? ((v) => [v, () => {
|
|
570
663
|
}]))("");
|
|
571
664
|
const runTest = (id) => {
|
|
665
|
+
const generation = (testGeneration.get(id) ?? 0) + 1;
|
|
666
|
+
testGeneration.set(id, generation);
|
|
572
667
|
setTests((current) => ({ ...current, [id]: { pending: true } }));
|
|
573
668
|
void (async () => {
|
|
574
669
|
try {
|
|
670
|
+
for (const [key, entry] of [...pendingWrites]) {
|
|
671
|
+
if (key.startsWith(`${id}\0`) || key.startsWith("\0default\0")) {
|
|
672
|
+
void entry.flush();
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
await writeQueue;
|
|
676
|
+
if (testGeneration.get(id) !== generation) return;
|
|
575
677
|
const response = await globalThis.fetch(TEST_PATH, {
|
|
576
678
|
method: "POST",
|
|
577
679
|
headers: { "content-type": "application/json", accept: "application/json" },
|
|
578
680
|
body: JSON.stringify({ provider: id })
|
|
579
681
|
});
|
|
580
682
|
const body2 = await response.json();
|
|
683
|
+
if (testGeneration.get(id) !== generation) return;
|
|
581
684
|
setTests((current) => ({ ...current, [id]: testResultFrom(body2, t) }));
|
|
582
685
|
} catch (error) {
|
|
686
|
+
if (testGeneration.get(id) !== generation) return;
|
|
583
687
|
setTests((current) => ({
|
|
584
688
|
...current,
|
|
585
689
|
[id]: { pending: false, ok: false, error: String(error?.message ?? error) }
|
|
@@ -588,19 +692,17 @@ function renderPage(ctx) {
|
|
|
588
692
|
})();
|
|
589
693
|
};
|
|
590
694
|
const rows = knownProviders({ ...configured, ...draft }, published);
|
|
695
|
+
const inheritingIds = () => rows.filter((row) => view({ ...configured[row.id], ...draft[row.id] }).useDefault === true).map((row) => row.id);
|
|
591
696
|
const save = (key, path, label, value, onRefused) => {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
pendingWrites.delete(key);
|
|
596
|
-
void writeConfig(ctx, [{ op: "set", path, value }]).then(() => {
|
|
697
|
+
scheduleWrite(
|
|
698
|
+
key,
|
|
699
|
+
() => writeConfig(ctx, [{ op: "set", path, value }]).then(() => {
|
|
597
700
|
note(`\u5DF2\u4FDD\u5B58 ${label} = ${JSON.stringify(value)}`);
|
|
598
701
|
}).catch((error) => {
|
|
599
702
|
note(`\u4FDD\u5B58\u5931\u8D25 ${label}\uFF1A${error?.message ?? error}`);
|
|
600
703
|
onRefused();
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
pendingWrites.set(key, { timer });
|
|
704
|
+
})
|
|
705
|
+
);
|
|
604
706
|
};
|
|
605
707
|
const rollbackRouteField = (id, field2) => {
|
|
606
708
|
setDraft((current) => {
|
|
@@ -616,6 +718,7 @@ function renderPage(ctx) {
|
|
|
616
718
|
};
|
|
617
719
|
const bind = (id, field2) => (value) => {
|
|
618
720
|
edit(id, field2, value);
|
|
721
|
+
if (TEST_RELEVANT_FIELDS.has(field2)) invalidateTests([id]);
|
|
619
722
|
save(
|
|
620
723
|
`${id}\0${field2}`,
|
|
621
724
|
["providers", id, field2],
|
|
@@ -626,6 +729,7 @@ function renderPage(ctx) {
|
|
|
626
729
|
};
|
|
627
730
|
const bindDefault = (field2) => (value) => {
|
|
628
731
|
editDefault(field2, value);
|
|
732
|
+
invalidateTests(inheritingIds());
|
|
629
733
|
save(`\0default\0${field2}`, ["defaultProxy", field2], `defaultProxy.${field2}`, value, () => {
|
|
630
734
|
setDefaultDraft((current) => {
|
|
631
735
|
if (current[field2] === void 0) return current;
|
|
@@ -636,18 +740,15 @@ function renderPage(ctx) {
|
|
|
636
740
|
});
|
|
637
741
|
};
|
|
638
742
|
const saveMany = (key, ops, label, onRefused) => {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
pendingWrites.delete(key);
|
|
643
|
-
void writeConfig(ctx, ops).then(() => {
|
|
743
|
+
scheduleWrite(
|
|
744
|
+
key,
|
|
745
|
+
() => writeConfig(ctx, ops).then(() => {
|
|
644
746
|
note(`\u5DF2\u4FDD\u5B58 ${label}`);
|
|
645
747
|
}).catch((error) => {
|
|
646
748
|
note(`\u4FDD\u5B58\u5931\u8D25 ${label}\uFF1A${error?.message ?? error}`);
|
|
647
749
|
onRefused();
|
|
648
|
-
})
|
|
649
|
-
|
|
650
|
-
pendingWrites.set(key, { timer });
|
|
750
|
+
})
|
|
751
|
+
);
|
|
651
752
|
};
|
|
652
753
|
const rollbackRouteFields = (id, fields) => {
|
|
653
754
|
setDraft((current) => {
|
|
@@ -663,6 +764,7 @@ function renderPage(ctx) {
|
|
|
663
764
|
};
|
|
664
765
|
const applyCardUrl = (id, parts) => {
|
|
665
766
|
setDraft((current) => ({ ...current, [id]: { ...current[id], ...parts } }));
|
|
767
|
+
invalidateTests([id]);
|
|
666
768
|
const fields = Object.keys(parts);
|
|
667
769
|
saveMany(
|
|
668
770
|
`${id}\0*paste`,
|
|
@@ -673,6 +775,7 @@ function renderPage(ctx) {
|
|
|
673
775
|
};
|
|
674
776
|
const applyDefaultUrl = (parts) => {
|
|
675
777
|
setDefaultDraft((current) => ({ ...current, ...parts }));
|
|
778
|
+
invalidateTests(inheritingIds());
|
|
676
779
|
const fields = Object.keys(parts);
|
|
677
780
|
saveMany(
|
|
678
781
|
"\0default\0*paste",
|
|
@@ -705,6 +808,7 @@ function renderPage(ctx) {
|
|
|
705
808
|
for (const id of ids) next[id] = { ...next[id], useDefault: true };
|
|
706
809
|
return next;
|
|
707
810
|
});
|
|
811
|
+
invalidateTests(ids);
|
|
708
812
|
saveMany(
|
|
709
813
|
"\0default\0applyAll",
|
|
710
814
|
ids.map((id) => ({ op: "set", path: ["providers", id, "useDefault"], value: true })),
|
|
@@ -754,7 +858,8 @@ function renderPage(ctx) {
|
|
|
754
858
|
const storedDefault = snapshot.value?.defaultProxy ?? {};
|
|
755
859
|
const defaultView = view({ ...storedDefault, ...defaultDraft });
|
|
756
860
|
const defaultWhy = defaultProxyProblem(defaultView);
|
|
757
|
-
const
|
|
861
|
+
const defaultHostWhy = defaultView.host.trim().length === 0 ? void 0 : proxyHostProblem(defaultView.host);
|
|
862
|
+
const defaultComplete = defaultWhy === void 0;
|
|
758
863
|
const defaultOrigin = `${defaultView.protocol}://${defaultView.host}:${defaultView.port}`;
|
|
759
864
|
const defaultBlock = h(
|
|
760
865
|
"div",
|
|
@@ -770,10 +875,10 @@ function renderPage(ctx) {
|
|
|
770
875
|
h(
|
|
771
876
|
"span",
|
|
772
877
|
{
|
|
773
|
-
className:
|
|
774
|
-
style: { ...S.pill, ...
|
|
878
|
+
className: defaultComplete ? import_client.default.pillOn : import_client.default.pillOff,
|
|
879
|
+
style: { ...S.pill, ...defaultComplete ? S.pillOn : S.pillOff }
|
|
775
880
|
},
|
|
776
|
-
|
|
881
|
+
defaultComplete ? t.complete : t.notConfigured
|
|
777
882
|
)
|
|
778
883
|
),
|
|
779
884
|
h("p", { className: import_client.default.defaultLead, style: S.defaultLead }, t.defaultLead),
|
|
@@ -803,7 +908,8 @@ function renderPage(ctx) {
|
|
|
803
908
|
// Same convenience as the cards: a full URL fills the whole block.
|
|
804
909
|
onPaste: onProxyUrlPaste(applyDefaultUrl)
|
|
805
910
|
}),
|
|
806
|
-
t.hostHint
|
|
911
|
+
defaultHostWhy ?? t.hostHint,
|
|
912
|
+
defaultHostWhy !== void 0
|
|
807
913
|
),
|
|
808
914
|
field(
|
|
809
915
|
t.port,
|
|
@@ -844,7 +950,7 @@ function renderPage(ctx) {
|
|
|
844
950
|
t.passwordHintProxy
|
|
845
951
|
),
|
|
846
952
|
// Bulk opt-in, in ONE write: a per-card walk would rebuild the table N times.
|
|
847
|
-
// Disabled while the default is
|
|
953
|
+
// Disabled while the default configuration is incomplete, because pointing every card at an
|
|
848
954
|
// empty endpoint would just refuse them all.
|
|
849
955
|
h(
|
|
850
956
|
"div",
|
|
@@ -855,7 +961,7 @@ function renderPage(ctx) {
|
|
|
855
961
|
className: import_client.default.testButton,
|
|
856
962
|
style: S.testButton,
|
|
857
963
|
type: "button",
|
|
858
|
-
disabled: !
|
|
964
|
+
disabled: !defaultComplete || rows.length === 0,
|
|
859
965
|
onClick: applyDefaultToAll
|
|
860
966
|
},
|
|
861
967
|
t.applyDefaultAll
|
|
@@ -874,6 +980,12 @@ function renderPage(ctx) {
|
|
|
874
980
|
onChange: (event) => setFilter(event.target.value)
|
|
875
981
|
})
|
|
876
982
|
);
|
|
983
|
+
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;
|
|
984
|
+
const catalogueNotice = state.error !== void 0 && rows.length > 0 ? h(
|
|
985
|
+
"p",
|
|
986
|
+
{ key: "catalogue-error", style: S.inheritWarning },
|
|
987
|
+
`${t.catalogueStale}${state.error}`
|
|
988
|
+
) : null;
|
|
877
989
|
const body = shown.length === 0 ? [
|
|
878
990
|
h(
|
|
879
991
|
"p",
|
|
@@ -887,10 +999,11 @@ function renderPage(ctx) {
|
|
|
887
999
|
borderRadius: "8px",
|
|
888
1000
|
fontSize: "12px",
|
|
889
1001
|
textAlign: "center",
|
|
890
|
-
color: "var(--dsw-alias-state-idle-primary)"
|
|
1002
|
+
color: catalogueStatus?.error === true ? "var(--dsw-alias-state-error-primary, #f85149)" : "var(--dsw-alias-state-idle-primary)",
|
|
1003
|
+
...catalogueStatus?.error === true ? { fontWeight: "500" } : {}
|
|
891
1004
|
}
|
|
892
1005
|
},
|
|
893
|
-
query.length > 0 ? t.noMatch : t.empty
|
|
1006
|
+
catalogueStatus?.text ?? (query.length > 0 ? t.noMatch : t.empty)
|
|
894
1007
|
)
|
|
895
1008
|
] : shown.map((row) => {
|
|
896
1009
|
const route = view({ ...configured[row.id], ...draft[row.id] });
|
|
@@ -899,8 +1012,15 @@ function renderPage(ctx) {
|
|
|
899
1012
|
const onlyDead = deadRuleEntries(route.onlyHosts);
|
|
900
1013
|
const bypassDead = deadRuleEntries(route.bypassHosts);
|
|
901
1014
|
const useDefault = route.useDefault === true;
|
|
902
|
-
const endpointStored = route.useDefault ?
|
|
1015
|
+
const endpointStored = route.useDefault ? defaultComplete : route.host.trim().length > 0 && Number.isInteger(route.port) && route.port >= 1 && route.port <= 65535;
|
|
903
1016
|
const showTest = route.enabled || endpointStored;
|
|
1017
|
+
const problemText = (() => {
|
|
1018
|
+
const recorded = problems[row.id];
|
|
1019
|
+
if (recorded !== void 0) return recorded;
|
|
1020
|
+
if (useDefault) return defaultComplete ? void 0 : `${t.defaultMissing}${defaultWhy}`;
|
|
1021
|
+
return route.enabled ? defaultProxyProblem(route) : void 0;
|
|
1022
|
+
})();
|
|
1023
|
+
const rejected = route.enabled && problemText !== void 0;
|
|
904
1024
|
const endpointFields = [
|
|
905
1025
|
field(
|
|
906
1026
|
t.protocol,
|
|
@@ -975,11 +1095,12 @@ function renderPage(ctx) {
|
|
|
975
1095
|
route.protocol === "socks5" ? t.passwordHint : t.passwordHintProxy
|
|
976
1096
|
)
|
|
977
1097
|
];
|
|
1098
|
+
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
1099
|
return h(
|
|
979
1100
|
"section",
|
|
980
1101
|
{
|
|
981
|
-
className: route.enabled ? import_client.default.cardActive : import_client.default.card,
|
|
982
|
-
style: route.enabled ? { ...S.card, ...S.cardActive } : S.card,
|
|
1102
|
+
className: rejected ? import_client.default.cardRejected : route.enabled ? import_client.default.cardActive : import_client.default.card,
|
|
1103
|
+
style: rejected ? { ...S.card, ...S.cardRejected } : route.enabled ? { ...S.card, ...S.cardActive } : S.card,
|
|
983
1104
|
key: row.id
|
|
984
1105
|
},
|
|
985
1106
|
h(
|
|
@@ -1010,17 +1131,7 @@ function renderPage(ctx) {
|
|
|
1010
1131
|
lastText
|
|
1011
1132
|
)
|
|
1012
1133
|
),
|
|
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
|
-
)
|
|
1134
|
+
h("span", { className: pill.className, style: pill.style }, pill.text)
|
|
1024
1135
|
),
|
|
1025
1136
|
h(
|
|
1026
1137
|
"label",
|
|
@@ -1046,16 +1157,12 @@ function renderPage(ctx) {
|
|
|
1046
1157
|
}),
|
|
1047
1158
|
h("span", null, t.useDefault)
|
|
1048
1159
|
),
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
},
|
|
1056
|
-
defaultUsable ? `${t.inheritingDefault}${defaultOrigin}` : `${t.defaultMissing}${defaultWhy}`
|
|
1057
|
-
)
|
|
1058
|
-
] : endpointFields,
|
|
1160
|
+
problemText !== void 0 ? h("p", { key: `${row.id}:problem`, style: S.inheritWarning }, problemText) : useDefault ? h(
|
|
1161
|
+
"p",
|
|
1162
|
+
{ key: `${row.id}:inherit`, style: S.inheritNote },
|
|
1163
|
+
`${t.inheritingDefault}${defaultOrigin}`
|
|
1164
|
+
) : null,
|
|
1165
|
+
...useDefault ? [] : endpointFields,
|
|
1059
1166
|
// The self-check is offered whenever there is something to check: an
|
|
1060
1167
|
// enabled route (the Host's table carries it, and a rejected one still
|
|
1061
1168
|
// reports WHY), or a disabled one whose endpoint is already complete —
|
|
@@ -1158,6 +1265,7 @@ function renderPage(ctx) {
|
|
|
1158
1265
|
{ className: import_client.default.root, style: S.root },
|
|
1159
1266
|
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),
|
|
1160
1267
|
defaultBlock,
|
|
1268
|
+
catalogueNotice,
|
|
1161
1269
|
filterRow,
|
|
1162
1270
|
...body,
|
|
1163
1271
|
h(
|
|
@@ -1194,14 +1302,17 @@ function renderPage(ctx) {
|
|
|
1194
1302
|
);
|
|
1195
1303
|
}
|
|
1196
1304
|
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)
|
|
1305
|
+
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
1306
|
empty: "No providers found yet.",
|
|
1307
|
+
catalogueLoading: "Loading providers\u2026",
|
|
1308
|
+
catalogueError: "Could not load the provider list: ",
|
|
1309
|
+
catalogueStale: "Could not load the provider list; showing the saved configuration: ",
|
|
1199
1310
|
useProxy: "Use a proxy for this provider",
|
|
1200
1311
|
useDefault: "Use the default proxy",
|
|
1201
1312
|
defaultTitle: "Default proxy",
|
|
1202
|
-
defaultLead: 'One endpoint, reused by every provider that turns on "Use the default proxy".',
|
|
1313
|
+
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
1314
|
inheritingDefault: "Using the default proxy: ",
|
|
1204
|
-
defaultMissing: "The default proxy
|
|
1315
|
+
defaultMissing: "The default proxy configuration is incomplete, so this provider is not proxied: ",
|
|
1205
1316
|
filterLabel: "Filter",
|
|
1206
1317
|
filterPlaceholder: "Filter by name or id\u2026",
|
|
1207
1318
|
noMatch: "No provider matches the filter.",
|
|
@@ -1212,7 +1323,7 @@ var EN = {
|
|
|
1212
1323
|
port: "Port",
|
|
1213
1324
|
username: "Username",
|
|
1214
1325
|
password: "Password",
|
|
1215
|
-
hostHint: "
|
|
1326
|
+
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
1327
|
passwordHint: "Sent to the SOCKS5 proxy for authentication; stored with this setting.",
|
|
1217
1328
|
passwordHintProxy: "Sent to the proxy as an authentication password; stored with this setting.",
|
|
1218
1329
|
hitCountPrefix: "",
|
|
@@ -1241,20 +1352,25 @@ var EN = {
|
|
|
1241
1352
|
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
1353
|
socks5Hint: "SOCKS5 tunnels the connection; the proxy resolves the target host name.",
|
|
1243
1354
|
on: "proxied",
|
|
1244
|
-
|
|
1355
|
+
rejected: "config rejected",
|
|
1245
1356
|
off: "direct",
|
|
1357
|
+
complete: "complete",
|
|
1358
|
+
notConfigured: "not configured",
|
|
1246
1359
|
diagnostics: "Connection diagnostics",
|
|
1247
1360
|
diagPending: "(collecting\u2026)"
|
|
1248
1361
|
};
|
|
1249
1362
|
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\
|
|
1363
|
+
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
1364
|
empty: "\u6CA1\u6709\u68C0\u6D4B\u5230\u4EFB\u4F55 provider\u3002",
|
|
1365
|
+
catalogueLoading: "\u6B63\u5728\u8BFB\u53D6 provider \u5217\u8868\u2026",
|
|
1366
|
+
catalogueError: "\u8BFB\u53D6 provider \u5217\u8868\u5931\u8D25\uFF1A",
|
|
1367
|
+
catalogueStale: "provider \u5217\u8868\u8BFB\u53D6\u5931\u8D25\uFF0C\u4E0B\u9762\u663E\u793A\u7684\u662F\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\uFF1A",
|
|
1252
1368
|
useProxy: "\u4E3A\u8BE5 provider \u542F\u7528\u4EE3\u7406",
|
|
1253
1369
|
useDefault: "\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406",
|
|
1254
1370
|
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",
|
|
1371
|
+
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
1372
|
inheritingDefault: "\u4F7F\u7528\u9ED8\u8BA4\u4EE3\u7406\uFF1A",
|
|
1257
|
-
defaultMissing: "\u9876\u5C42\u9ED8\u8BA4\u4EE3\u7406\u4E0D\
|
|
1373
|
+
defaultMissing: "\u9876\u5C42\u9ED8\u8BA4\u4EE3\u7406\u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8BE5 provider \u4E0D\u4F1A\u8D70\u4EE3\u7406\uFF1A",
|
|
1258
1374
|
filterLabel: "\u8FC7\u6EE4",
|
|
1259
1375
|
filterPlaceholder: "\u6309\u540D\u79F0\u6216 id \u8FC7\u6EE4\u2026",
|
|
1260
1376
|
noMatch: "\u6CA1\u6709\u5339\u914D\u7684 provider\u3002",
|
|
@@ -1265,7 +1381,7 @@ var ZH = {
|
|
|
1265
1381
|
port: "\u7AEF\u53E3",
|
|
1266
1382
|
username: "\u7528\u6237\u540D",
|
|
1267
1383
|
password: "\u5BC6\u7801",
|
|
1268
|
-
hostHint: "\u53EA\
|
|
1384
|
+
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
1385
|
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
1386
|
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
1387
|
hitCountPrefix: "\u5DF2\u4EE3\u7406 ",
|
|
@@ -1294,8 +1410,10 @@ var ZH = {
|
|
|
1294
1410
|
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
1411
|
socks5Hint: "SOCKS5 \u4F1A\u5EFA\u7ACB\u96A7\u9053\uFF0C\u76EE\u6807\u57DF\u540D\u7531\u4EE3\u7406\u7AEF\u89E3\u6790\u3002",
|
|
1296
1412
|
on: "\u8D70\u4EE3\u7406",
|
|
1297
|
-
|
|
1413
|
+
rejected: "\u914D\u7F6E\u88AB\u62D2",
|
|
1298
1414
|
off: "\u76F4\u8FDE",
|
|
1415
|
+
complete: "\u914D\u7F6E\u5B8C\u6574",
|
|
1416
|
+
notConfigured: "\u672A\u914D\u7F6E",
|
|
1299
1417
|
diagnostics: "\u8FDE\u63A5\u8BCA\u65AD",
|
|
1300
1418
|
diagPending: "\uFF08\u6B63\u5728\u6536\u96C6\u2026\uFF09"
|
|
1301
1419
|
};
|