dsh-remote-mobile 1.1.0 → 1.2.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/CHANGELOG.md +29 -0
- package/README.md +187 -106
- package/README_EN.md +187 -106
- package/lib/auth/crypto.js +35 -21
- package/lib/auth/gate.js +2 -1
- package/lib/auth/token.d.ts +13 -1
- package/lib/auth/token.js +53 -20
- package/lib/client.js +171 -89
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/routes/api.js +8 -2
- package/lib/routes/login-page.js +54 -12
- package/package.json +7 -3
- package/lib/types/auth.d.ts +0 -49
- package/lib/types/auth.js +0 -4
package/lib/auth/token.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes, randomInt } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from 'node:fs';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { EventEmitter } from 'node:events';
|
|
@@ -90,9 +90,10 @@ export function readGlobalLocale(filePath = GLOBAL_SETTINGS_FILE) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* 将最新的插件配置安全原子写回 ~/.dsh/settings.yaml 中的 dsh-remote-mobile 命名空间
|
|
94
94
|
*/
|
|
95
95
|
export function writeBackToSettingsYaml(options, filePath = GLOBAL_SETTINGS_FILE) {
|
|
96
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
96
97
|
try {
|
|
97
98
|
if (!existsSync(filePath))
|
|
98
99
|
return false;
|
|
@@ -126,10 +127,16 @@ export function writeBackToSettingsYaml(options, filePath = GLOBAL_SETTINGS_FILE
|
|
|
126
127
|
lines.splice(startIdx, endIdx - startIdx, newBlock);
|
|
127
128
|
updatedContent = lines.join('\n');
|
|
128
129
|
}
|
|
129
|
-
writeFileSync(
|
|
130
|
+
writeFileSync(tmpPath, updatedContent, 'utf8');
|
|
131
|
+
renameSync(tmpPath, filePath);
|
|
130
132
|
return true;
|
|
131
133
|
}
|
|
132
134
|
catch {
|
|
135
|
+
try {
|
|
136
|
+
if (existsSync(tmpPath))
|
|
137
|
+
unlinkSync(tmpPath);
|
|
138
|
+
}
|
|
139
|
+
catch { }
|
|
133
140
|
return false;
|
|
134
141
|
}
|
|
135
142
|
}
|
|
@@ -168,12 +175,24 @@ export function parseDeviceName(ua = '') {
|
|
|
168
175
|
return `💻 Linux (${browser})`;
|
|
169
176
|
return `📱 移动设备 (${browser})`;
|
|
170
177
|
}
|
|
178
|
+
const activeStores = new Set();
|
|
179
|
+
if (typeof process !== 'undefined' && typeof process.on === 'function') {
|
|
180
|
+
process.on('beforeExit', () => {
|
|
181
|
+
for (const s of activeStores) {
|
|
182
|
+
try {
|
|
183
|
+
s.flushPersistedData();
|
|
184
|
+
}
|
|
185
|
+
catch { }
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
171
189
|
export class SessionStore extends EventEmitter {
|
|
172
190
|
sessions = new Map();
|
|
173
191
|
shortCodes = new Map();
|
|
174
192
|
ipStats = new Map();
|
|
175
193
|
options;
|
|
176
194
|
settingsMutator;
|
|
195
|
+
debounceTimer = null;
|
|
177
196
|
persistPath;
|
|
178
197
|
settingsFilePath;
|
|
179
198
|
constructor(options = {}) {
|
|
@@ -211,6 +230,7 @@ export class SessionStore extends EventEmitter {
|
|
|
211
230
|
};
|
|
212
231
|
this.persistPath = this.options.devicesFile;
|
|
213
232
|
this.loadPersistedData();
|
|
233
|
+
activeStores.add(this);
|
|
214
234
|
}
|
|
215
235
|
getOptions() {
|
|
216
236
|
return { ...this.options };
|
|
@@ -378,7 +398,7 @@ export class SessionStore extends EventEmitter {
|
|
|
378
398
|
}
|
|
379
399
|
}
|
|
380
400
|
this.emit('ip-security-updated', { stats: this.getIpSecurityStats() });
|
|
381
|
-
this.
|
|
401
|
+
this.savePersistedDataDebounced();
|
|
382
402
|
return { allowed: true };
|
|
383
403
|
}
|
|
384
404
|
/**
|
|
@@ -625,7 +645,7 @@ export class SessionStore extends EventEmitter {
|
|
|
625
645
|
}
|
|
626
646
|
if (isReconnecting) {
|
|
627
647
|
this.emit('device-online', session);
|
|
628
|
-
this.
|
|
648
|
+
this.savePersistedDataDebounced();
|
|
629
649
|
}
|
|
630
650
|
return true;
|
|
631
651
|
}
|
|
@@ -770,21 +790,6 @@ export class SessionStore extends EventEmitter {
|
|
|
770
790
|
}
|
|
771
791
|
}
|
|
772
792
|
else if (data && typeof data === 'object') {
|
|
773
|
-
if (data.config) {
|
|
774
|
-
if (typeof data.config.allowTailscale === 'boolean') {
|
|
775
|
-
this.options.allowTailscale = data.config.allowTailscale;
|
|
776
|
-
}
|
|
777
|
-
if (typeof data.config.allowLan === 'boolean') {
|
|
778
|
-
this.options.allowLan = data.config.allowLan;
|
|
779
|
-
}
|
|
780
|
-
if (typeof data.config.secretHash === 'string') {
|
|
781
|
-
this.options.secretHash = data.config.secretHash;
|
|
782
|
-
}
|
|
783
|
-
else if (typeof data.config.secret === 'string' && data.config.secret) {
|
|
784
|
-
// 自动将老版本的明文 secret 迁移升级为安全加盐哈希
|
|
785
|
-
this.options.secretHash = hashSecret(data.config.secret);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
793
|
if (Array.isArray(data.devices)) {
|
|
789
794
|
for (const s of data.devices) {
|
|
790
795
|
if (s && s.token) {
|
|
@@ -831,7 +836,35 @@ export class SessionStore extends EventEmitter {
|
|
|
831
836
|
}
|
|
832
837
|
catch { }
|
|
833
838
|
}
|
|
839
|
+
/**
|
|
840
|
+
* 同步立即持久化落盘(由关键路径调用:配对成功、密码认证、撤销设备、手动改密/配置等)
|
|
841
|
+
*/
|
|
834
842
|
savePersistedData() {
|
|
843
|
+
this.flushPersistedData();
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* 500ms 防抖持久化落盘(由高频访问路径调用:每次打开 /auth、validateToken 重连时间戳更新等)
|
|
847
|
+
*/
|
|
848
|
+
savePersistedDataDebounced() {
|
|
849
|
+
if (this.debounceTimer)
|
|
850
|
+
return;
|
|
851
|
+
this.debounceTimer = setTimeout(() => {
|
|
852
|
+
this.debounceTimer = null;
|
|
853
|
+
this.flushPersistedData();
|
|
854
|
+
}, 500);
|
|
855
|
+
if (typeof this.debounceTimer.unref === 'function') {
|
|
856
|
+
;
|
|
857
|
+
this.debounceTimer.unref();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* 立即执行待写的持久化数据落盘并清理定时器
|
|
862
|
+
*/
|
|
863
|
+
flushPersistedData() {
|
|
864
|
+
if (this.debounceTimer) {
|
|
865
|
+
clearTimeout(this.debounceTimer);
|
|
866
|
+
this.debounceTimer = null;
|
|
867
|
+
}
|
|
835
868
|
try {
|
|
836
869
|
mkdirSync(dirname(this.persistPath), { recursive: true });
|
|
837
870
|
const payload = {
|
package/lib/client.js
CHANGED
|
@@ -876,20 +876,6 @@ var translations = {
|
|
|
876
876
|
}
|
|
877
877
|
};
|
|
878
878
|
function resolveLocale(ctx, status) {
|
|
879
|
-
try {
|
|
880
|
-
if (ctx) {
|
|
881
|
-
if (typeof ctx.get === "function") {
|
|
882
|
-
var loc = ctx.get("locale");
|
|
883
|
-
if (loc && loc.snapshot && loc.snapshot.active) {
|
|
884
|
-
return loc.snapshot.active.startsWith("en") ? "en" : "zh";
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
if (ctx.locale && ctx.locale.snapshot && ctx.locale.snapshot.active) {
|
|
888
|
-
return ctx.locale.snapshot.active.startsWith("en") ? "en" : "zh";
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
} catch (e) {
|
|
892
|
-
}
|
|
893
879
|
if (typeof document !== "undefined" && document.documentElement && document.documentElement.lang) {
|
|
894
880
|
return document.documentElement.lang.toLowerCase().startsWith("en") ? "en" : "zh";
|
|
895
881
|
}
|
|
@@ -914,6 +900,39 @@ function t(key, lang, params) {
|
|
|
914
900
|
}
|
|
915
901
|
return text;
|
|
916
902
|
}
|
|
903
|
+
function formatDeviceName(name, lang) {
|
|
904
|
+
if (!name) return lang === "en" ? "Mobile Device" : "\u79FB\u52A8\u7AEF\u8BBE\u5907";
|
|
905
|
+
if (lang !== "en") return name;
|
|
906
|
+
return name.replace(/手机/g, "Phone").replace(/移动设备/g, "Mobile Device").replace(/微信内置浏览器/g, "WeChat Browser").replace(/局域网免密临时设备/g, "LAN Bypass Device").replace(/Tailscale免密临时设备/g, "Tailscale Bypass Device");
|
|
907
|
+
}
|
|
908
|
+
function formatAuthType(authType, lang) {
|
|
909
|
+
if (!authType) return lang === "en" ? "\u26AA Pending" : "\u26AA \u5F85\u8BA4\u8BC1";
|
|
910
|
+
if (lang !== "en") return authType;
|
|
911
|
+
if (authType.includes("\u626B\u7801\u914D\u5BF9") || authType.includes("\u914D\u5BF9\u7801")) {
|
|
912
|
+
if (authType.includes("365")) return "\u{1F4F1} QR Pairing (365d)";
|
|
913
|
+
return "\u{1F4F1} QR Authenticated";
|
|
914
|
+
}
|
|
915
|
+
if (authType.includes("\u957F\u671F\u5BC6\u7801") || authType.includes("\u5BC6\u7801\u9A8C\u8BC1")) {
|
|
916
|
+
if (authType.includes("365")) return "\u{1F511} Password Auth (365d)";
|
|
917
|
+
return "\u{1F511} Password Auth";
|
|
918
|
+
}
|
|
919
|
+
if (authType.includes("\u5C40\u57DF\u7F51") || authType.includes("LAN")) {
|
|
920
|
+
return "\u26A1 LAN Direct Bypass";
|
|
921
|
+
}
|
|
922
|
+
if (authType.includes("Tailscale")) {
|
|
923
|
+
return "\u26A1 Tailscale Bypass";
|
|
924
|
+
}
|
|
925
|
+
if (authType.includes("\u5F85\u8BA4\u8BC1") || authType.includes("\u672A\u8BA4\u8BC1")) {
|
|
926
|
+
return "\u26AA Pending";
|
|
927
|
+
}
|
|
928
|
+
return authType;
|
|
929
|
+
}
|
|
930
|
+
function formatFailedAttempts(count, lang) {
|
|
931
|
+
if (lang === "en") {
|
|
932
|
+
return count === 1 ? "\u26A0\uFE0F 1 failed attempt" : `\u26A0\uFE0F ${count} failed attempts`;
|
|
933
|
+
}
|
|
934
|
+
return `\u26A0\uFE0F \u66FE\u5931\u8D25 ${count} \u6B21`;
|
|
935
|
+
}
|
|
917
936
|
|
|
918
937
|
// src/client/components/NetworkCard.jsx
|
|
919
938
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -958,20 +977,19 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
958
977
|
"div",
|
|
959
978
|
{
|
|
960
979
|
style: {
|
|
961
|
-
display: "flex",
|
|
962
|
-
alignItems: "center",
|
|
963
|
-
justifyContent: "space-between",
|
|
964
|
-
flexWrap: "wrap",
|
|
965
|
-
gap: "12px",
|
|
966
980
|
paddingBottom: "16px",
|
|
967
981
|
borderBottom: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))",
|
|
968
|
-
marginBottom: "16px"
|
|
982
|
+
marginBottom: "16px",
|
|
983
|
+
display: "flex",
|
|
984
|
+
justifyContent: "space-between",
|
|
985
|
+
alignItems: "flex-start",
|
|
986
|
+
gap: "16px"
|
|
969
987
|
},
|
|
970
988
|
children: [
|
|
971
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: "
|
|
972
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
973
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "16px" }, children: "\u{1F512}" }),
|
|
974
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "14px", fontWeight: "700" }, children: t("tailscaleSectionTitle", lang) }),
|
|
989
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "6px" }, children: [
|
|
990
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [
|
|
991
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "16px", flexShrink: 0 }, children: "\u{1F512}" }),
|
|
992
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "14px", fontWeight: "700", color: "var(--dsw-alias-label-primary, inherit)" }, children: t("tailscaleSectionTitle", lang) }),
|
|
975
993
|
status.tailscaleIp ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
976
994
|
"span",
|
|
977
995
|
{
|
|
@@ -981,7 +999,9 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
981
999
|
borderRadius: "4px",
|
|
982
1000
|
background: "rgba(16,185,129,0.15)",
|
|
983
1001
|
color: "#10b981",
|
|
984
|
-
fontWeight: "600"
|
|
1002
|
+
fontWeight: "600",
|
|
1003
|
+
whiteSpace: "nowrap",
|
|
1004
|
+
flexShrink: 0
|
|
985
1005
|
},
|
|
986
1006
|
children: lang === "en" ? "Connected" : "\u5DF2\u8FDE\u63A5"
|
|
987
1007
|
}
|
|
@@ -994,7 +1014,9 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
994
1014
|
borderRadius: "4px",
|
|
995
1015
|
background: "rgba(239,68,68,0.15)",
|
|
996
1016
|
color: "#ef4444",
|
|
997
|
-
fontWeight: "600"
|
|
1017
|
+
fontWeight: "600",
|
|
1018
|
+
whiteSpace: "nowrap",
|
|
1019
|
+
flexShrink: 0
|
|
998
1020
|
},
|
|
999
1021
|
children: lang === "en" ? "Not Connected" : "\u672A\u8FDE\u63A5"
|
|
1000
1022
|
}
|
|
@@ -1005,26 +1027,30 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1005
1027
|
{
|
|
1006
1028
|
style: {
|
|
1007
1029
|
fontSize: "13px",
|
|
1008
|
-
marginTop: "4px",
|
|
1009
1030
|
fontFamily: "monospace",
|
|
1010
|
-
color: "var(--dsw-alias-brand-primary, #3b82f6)"
|
|
1031
|
+
color: "var(--dsw-alias-brand-primary, #3b82f6)",
|
|
1032
|
+
display: "flex",
|
|
1033
|
+
alignItems: "center",
|
|
1034
|
+
flexWrap: "wrap",
|
|
1035
|
+
gap: "8px"
|
|
1011
1036
|
},
|
|
1012
1037
|
children: [
|
|
1013
|
-
`http://${tsHost}:${port}
|
|
1038
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: `http://${tsHost}:${port}` }),
|
|
1014
1039
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1015
1040
|
"button",
|
|
1016
1041
|
{
|
|
1017
1042
|
type: "button",
|
|
1018
1043
|
onClick: () => copyText(`http://${tsHost}:${port}`, t("copiedTip", lang)),
|
|
1019
1044
|
style: {
|
|
1020
|
-
|
|
1021
|
-
padding: "2px 6px",
|
|
1045
|
+
padding: "2px 8px",
|
|
1022
1046
|
background: "transparent",
|
|
1023
|
-
border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.
|
|
1047
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
|
|
1024
1048
|
borderRadius: "4px",
|
|
1025
1049
|
fontSize: "11px",
|
|
1026
1050
|
cursor: "pointer",
|
|
1027
|
-
color: "var(--dsw-alias-label-secondary, inherit)"
|
|
1051
|
+
color: "var(--dsw-alias-label-secondary, inherit)",
|
|
1052
|
+
whiteSpace: "nowrap",
|
|
1053
|
+
flexShrink: 0
|
|
1028
1054
|
},
|
|
1029
1055
|
children: t("copyUrlBtn", lang)
|
|
1030
1056
|
}
|
|
@@ -1037,7 +1063,7 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1037
1063
|
style: {
|
|
1038
1064
|
fontSize: "12px",
|
|
1039
1065
|
color: "var(--dsw-alias-label-tertiary, #888)",
|
|
1040
|
-
|
|
1066
|
+
lineHeight: "1.4"
|
|
1041
1067
|
},
|
|
1042
1068
|
children: t("tailscaleGuide", lang)
|
|
1043
1069
|
}
|
|
@@ -1048,20 +1074,21 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1048
1074
|
style: {
|
|
1049
1075
|
fontSize: "12px",
|
|
1050
1076
|
color: "var(--dsw-alias-label-tertiary, #888)",
|
|
1051
|
-
|
|
1077
|
+
lineHeight: "1.4"
|
|
1052
1078
|
},
|
|
1053
1079
|
children: t("tailscaleBypassDesc", lang)
|
|
1054
1080
|
}
|
|
1055
1081
|
) : null
|
|
1056
1082
|
] }),
|
|
1057
|
-
status.tailscaleIp ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
1083
|
+
status.tailscaleIp ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0, paddingTop: "2px" }, children: [
|
|
1058
1084
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1059
1085
|
"span",
|
|
1060
1086
|
{
|
|
1061
1087
|
style: {
|
|
1062
1088
|
fontSize: "12px",
|
|
1063
1089
|
color: status.allowTailscale ? "var(--dsw-alias-brand-primary, #3b82f6)" : "var(--dsw-alias-label-secondary, #888)",
|
|
1064
|
-
fontWeight: "600"
|
|
1090
|
+
fontWeight: "600",
|
|
1091
|
+
whiteSpace: "nowrap"
|
|
1065
1092
|
},
|
|
1066
1093
|
children: status.allowTailscale ? t("directBypassOn", lang) : t("directBypassOff", lang)
|
|
1067
1094
|
}
|
|
@@ -1075,7 +1102,8 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1075
1102
|
width: "44px",
|
|
1076
1103
|
height: "24px",
|
|
1077
1104
|
cursor: "pointer",
|
|
1078
|
-
userSelect: "none"
|
|
1105
|
+
userSelect: "none",
|
|
1106
|
+
flexShrink: 0
|
|
1079
1107
|
},
|
|
1080
1108
|
children: [
|
|
1081
1109
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1132,16 +1160,15 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1132
1160
|
{
|
|
1133
1161
|
style: {
|
|
1134
1162
|
display: "flex",
|
|
1135
|
-
alignItems: "center",
|
|
1136
1163
|
justifyContent: "space-between",
|
|
1137
|
-
|
|
1138
|
-
gap: "
|
|
1164
|
+
alignItems: "flex-start",
|
|
1165
|
+
gap: "16px"
|
|
1139
1166
|
},
|
|
1140
1167
|
children: [
|
|
1141
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: "
|
|
1142
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
1143
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "16px" }, children: "\u{1F3E0}" }),
|
|
1144
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "14px", fontWeight: "700" }, children: t("lanSectionTitle", lang) }),
|
|
1168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "6px" }, children: [
|
|
1169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" }, children: [
|
|
1170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "16px", flexShrink: 0 }, children: "\u{1F3E0}" }),
|
|
1171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "14px", fontWeight: "700", color: "var(--dsw-alias-label-primary, inherit)" }, children: t("lanSectionTitle", lang) }),
|
|
1145
1172
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1146
1173
|
"span",
|
|
1147
1174
|
{
|
|
@@ -1151,7 +1178,9 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1151
1178
|
borderRadius: "4px",
|
|
1152
1179
|
background: "rgba(16,185,129,0.15)",
|
|
1153
1180
|
color: "#10b981",
|
|
1154
|
-
fontWeight: "600"
|
|
1181
|
+
fontWeight: "600",
|
|
1182
|
+
whiteSpace: "nowrap",
|
|
1183
|
+
flexShrink: 0
|
|
1155
1184
|
},
|
|
1156
1185
|
children: lang === "en" ? "Ready" : "\u5DF2\u5C31\u7EEA"
|
|
1157
1186
|
}
|
|
@@ -1162,26 +1191,30 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1162
1191
|
{
|
|
1163
1192
|
style: {
|
|
1164
1193
|
fontSize: "13px",
|
|
1165
|
-
marginTop: "4px",
|
|
1166
1194
|
fontFamily: "monospace",
|
|
1167
|
-
color: "var(--dsw-alias-brand-primary, #3b82f6)"
|
|
1195
|
+
color: "var(--dsw-alias-brand-primary, #3b82f6)",
|
|
1196
|
+
display: "flex",
|
|
1197
|
+
alignItems: "center",
|
|
1198
|
+
flexWrap: "wrap",
|
|
1199
|
+
gap: "8px"
|
|
1168
1200
|
},
|
|
1169
1201
|
children: [
|
|
1170
|
-
`http://${lanHost}:${port}
|
|
1202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: `http://${lanHost}:${port}` }),
|
|
1171
1203
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1172
1204
|
"button",
|
|
1173
1205
|
{
|
|
1174
1206
|
type: "button",
|
|
1175
1207
|
onClick: () => copyText(`http://${lanHost}:${port}`, t("copiedTip", lang)),
|
|
1176
1208
|
style: {
|
|
1177
|
-
|
|
1178
|
-
padding: "2px 6px",
|
|
1209
|
+
padding: "2px 8px",
|
|
1179
1210
|
background: "transparent",
|
|
1180
|
-
border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.
|
|
1211
|
+
border: "1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))",
|
|
1181
1212
|
borderRadius: "4px",
|
|
1182
1213
|
fontSize: "11px",
|
|
1183
1214
|
cursor: "pointer",
|
|
1184
|
-
color: "var(--dsw-alias-label-secondary, inherit)"
|
|
1215
|
+
color: "var(--dsw-alias-label-secondary, inherit)",
|
|
1216
|
+
whiteSpace: "nowrap",
|
|
1217
|
+
flexShrink: 0
|
|
1185
1218
|
},
|
|
1186
1219
|
children: t("copyUrlBtn", lang)
|
|
1187
1220
|
}
|
|
@@ -1190,7 +1223,7 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1190
1223
|
}
|
|
1191
1224
|
)
|
|
1192
1225
|
] }),
|
|
1193
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0, paddingTop: "2px" }, children: [
|
|
1194
1227
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1195
1228
|
"span",
|
|
1196
1229
|
{
|
|
@@ -1200,9 +1233,10 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1200
1233
|
fontWeight: status.allowLan ? "700" : "500",
|
|
1201
1234
|
display: "flex",
|
|
1202
1235
|
alignItems: "center",
|
|
1203
|
-
gap: "4px"
|
|
1236
|
+
gap: "4px",
|
|
1237
|
+
whiteSpace: "nowrap"
|
|
1204
1238
|
},
|
|
1205
|
-
children: status.allowLan ? lang === "en" ? "\u{1F6A8} LAN Bypass
|
|
1239
|
+
children: status.allowLan ? lang === "en" ? "\u{1F6A8} LAN Bypass (High Risk)" : "\u{1F6A8} \u5C40\u57DF\u7F51\u514D\u5BC6\uFF08\u9AD8\u5371\uFF09" : t("directBypassOff", lang)
|
|
1206
1240
|
}
|
|
1207
1241
|
),
|
|
1208
1242
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1214,7 +1248,8 @@ function NetworkCard({ status, toggleTailscale, toggleLan, copyText, lang }) {
|
|
|
1214
1248
|
width: "44px",
|
|
1215
1249
|
height: "24px",
|
|
1216
1250
|
cursor: "pointer",
|
|
1217
|
-
userSelect: "none"
|
|
1251
|
+
userSelect: "none",
|
|
1252
|
+
flexShrink: 0
|
|
1218
1253
|
},
|
|
1219
1254
|
children: [
|
|
1220
1255
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1526,13 +1561,14 @@ function QrPairingCard({
|
|
|
1526
1561
|
onClick: copyDirectLink,
|
|
1527
1562
|
style: {
|
|
1528
1563
|
padding: "6px 16px",
|
|
1529
|
-
background: "
|
|
1530
|
-
color: "
|
|
1564
|
+
background: "#2563eb",
|
|
1565
|
+
color: "#ffffff",
|
|
1531
1566
|
border: "none",
|
|
1532
1567
|
borderRadius: "6px",
|
|
1533
1568
|
fontSize: "12px",
|
|
1534
1569
|
fontWeight: "600",
|
|
1535
1570
|
cursor: "pointer",
|
|
1571
|
+
boxShadow: "0 2px 6px rgba(37, 99, 235, 0.3)",
|
|
1536
1572
|
transition: "background-color 0.2s"
|
|
1537
1573
|
},
|
|
1538
1574
|
children: status.copied ? `\u2705 ${lang === "en" ? "Copied!" : "\u5DF2\u590D\u5236\uFF01"}` : t("copyDirectAuthLink", lang)
|
|
@@ -1828,15 +1864,16 @@ function SecretCard({
|
|
|
1828
1864
|
style: {
|
|
1829
1865
|
padding: "0 16px",
|
|
1830
1866
|
height: "38px",
|
|
1831
|
-
background: "
|
|
1832
|
-
color: "
|
|
1867
|
+
background: "#2563eb",
|
|
1868
|
+
color: "#ffffff",
|
|
1833
1869
|
border: "none",
|
|
1834
1870
|
borderRadius: "8px",
|
|
1835
1871
|
fontSize: "13px",
|
|
1836
1872
|
fontWeight: "600",
|
|
1837
1873
|
cursor: "pointer",
|
|
1838
1874
|
whiteSpace: "nowrap",
|
|
1839
|
-
flexShrink: 0
|
|
1875
|
+
flexShrink: 0,
|
|
1876
|
+
boxShadow: "0 2px 6px rgba(37, 99, 235, 0.3)"
|
|
1840
1877
|
},
|
|
1841
1878
|
children: t("saveSecretBtn", lang)
|
|
1842
1879
|
}
|
|
@@ -1990,7 +2027,7 @@ function DeviceListCard({ status, revokeDevice, revokeAll, lang }) {
|
|
|
1990
2027
|
gap: "8px"
|
|
1991
2028
|
},
|
|
1992
2029
|
children: [
|
|
1993
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: dev.deviceName
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: formatDeviceName(dev.deviceName, lang) }),
|
|
1994
2031
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1995
2032
|
"span",
|
|
1996
2033
|
{
|
|
@@ -2003,7 +2040,7 @@ function DeviceListCard({ status, revokeDevice, revokeAll, lang }) {
|
|
|
2003
2040
|
fontWeight: "normal",
|
|
2004
2041
|
whiteSpace: "nowrap"
|
|
2005
2042
|
},
|
|
2006
|
-
children: dev.authType
|
|
2043
|
+
children: formatAuthType(dev.authType, lang)
|
|
2007
2044
|
}
|
|
2008
2045
|
)
|
|
2009
2046
|
]
|
|
@@ -2257,18 +2294,15 @@ function SecurityCard({ status, unlockIp, clearIpStats, lang }) {
|
|
|
2257
2294
|
flexWrap: "wrap"
|
|
2258
2295
|
},
|
|
2259
2296
|
children: [
|
|
2260
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.
|
|
2261
|
-
effectiveDeviceName ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: "var(--dsw-alias-label-primary, inherit)" }, children: effectiveDeviceName }) : null,
|
|
2262
|
-
effectiveDeviceName ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: "var(--dsw-alias-label-tertiary, #888)", fontWeight: "normal" }, children: "\xB7" }) : null,
|
|
2263
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: effectiveDeviceName ? "var(--dsw-alias-label-secondary, #888)" : "inherit" }, children: ipLabel })
|
|
2264
|
-
] }),
|
|
2297
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontWeight: "600" }, children: stat.deviceName ? `${formatDeviceName(stat.deviceName, lang)} \xB7 ${stat.ip}${isLocal ? lang === "en" ? " (Local)" : " (\u672C\u673A)" : ""}` : `${stat.ip}${isLocal ? lang === "en" ? " (Local)" : " (\u672C\u673A)" : ""}` }),
|
|
2265
2298
|
effectiveAuthType ? (() => {
|
|
2266
|
-
|
|
2267
|
-
let
|
|
2299
|
+
const translatedAuth = formatAuthType(effectiveAuthType, lang);
|
|
2300
|
+
let bg = "rgba(59,130,246,0.12)";
|
|
2301
|
+
let color = "#3b82f6";
|
|
2268
2302
|
if (effectiveAuthType.indexOf("\u514D\u5BC6") !== -1 || effectiveAuthType.indexOf("Bypass") !== -1) {
|
|
2269
2303
|
bg = "rgba(16,185,129,0.15)";
|
|
2270
2304
|
color = "#10b981";
|
|
2271
|
-
} else if (effectiveAuthType.indexOf("\u626B\u7801") !== -1 || effectiveAuthType.indexOf("Pairing") !== -1) {
|
|
2305
|
+
} else if (effectiveAuthType.indexOf("\u626B\u7801") !== -1 || effectiveAuthType.indexOf("Pairing") !== -1 || effectiveAuthType.indexOf("QR") !== -1) {
|
|
2272
2306
|
bg = "rgba(59,130,246,0.15)";
|
|
2273
2307
|
color = "#3b82f6";
|
|
2274
2308
|
} else if (effectiveAuthType.indexOf("\u5BC6\u7801") !== -1 || effectiveAuthType.indexOf("Password") !== -1) {
|
|
@@ -2287,7 +2321,7 @@ function SecurityCard({ status, unlockIp, clearIpStats, lang }) {
|
|
|
2287
2321
|
fontWeight: "600",
|
|
2288
2322
|
whiteSpace: "nowrap"
|
|
2289
2323
|
},
|
|
2290
|
-
children:
|
|
2324
|
+
children: translatedAuth
|
|
2291
2325
|
}
|
|
2292
2326
|
);
|
|
2293
2327
|
})() : null,
|
|
@@ -2317,7 +2351,7 @@ function SecurityCard({ status, unlockIp, clearIpStats, lang }) {
|
|
|
2317
2351
|
fontWeight: "600",
|
|
2318
2352
|
whiteSpace: "nowrap"
|
|
2319
2353
|
},
|
|
2320
|
-
children:
|
|
2354
|
+
children: formatFailedAttempts(stat.failedAttempts, lang)
|
|
2321
2355
|
}
|
|
2322
2356
|
) : !effectiveAuthType ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2323
2357
|
"span",
|
|
@@ -2834,15 +2868,16 @@ function ConfigCard({
|
|
|
2834
2868
|
disabled: isSaving,
|
|
2835
2869
|
style: {
|
|
2836
2870
|
padding: "6px 18px",
|
|
2837
|
-
background: "
|
|
2871
|
+
background: "#2563eb",
|
|
2838
2872
|
border: "none",
|
|
2839
|
-
color: "
|
|
2873
|
+
color: "#ffffff",
|
|
2840
2874
|
borderRadius: "6px",
|
|
2841
2875
|
fontSize: "12px",
|
|
2842
2876
|
fontWeight: "600",
|
|
2843
2877
|
cursor: isSaving ? "not-allowed" : "pointer",
|
|
2844
2878
|
opacity: isSaving ? 0.7 : 1,
|
|
2845
|
-
whiteSpace: "nowrap"
|
|
2879
|
+
whiteSpace: "nowrap",
|
|
2880
|
+
boxShadow: "0 2px 6px rgba(37, 99, 235, 0.3)"
|
|
2846
2881
|
},
|
|
2847
2882
|
children: isSaving ? t("savingConfigBtn", lang) : t("saveConfigBtn", lang)
|
|
2848
2883
|
}
|
|
@@ -2856,7 +2891,8 @@ function ConfigCard({
|
|
|
2856
2891
|
}
|
|
2857
2892
|
|
|
2858
2893
|
// src/client/version.js
|
|
2859
|
-
var PLUGIN_VERSION = "v1.1
|
|
2894
|
+
var PLUGIN_VERSION = "v1.2.1";
|
|
2895
|
+
var BUILD_TIME = "2026-08-23 00:31:17";
|
|
2860
2896
|
|
|
2861
2897
|
// src/client/components/MainSection.jsx
|
|
2862
2898
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -2892,6 +2928,7 @@ function TailscaleMobileSection(props) {
|
|
|
2892
2928
|
const [maxFailedInput, setMaxFailedInput] = (0, import_react8.useState)("5");
|
|
2893
2929
|
const [lockDurationMinsInput, setLockDurationMinsInput] = (0, import_react8.useState)("15");
|
|
2894
2930
|
const [isSaving, setIsSaving] = (0, import_react8.useState)(false);
|
|
2931
|
+
const [showVersionTip, setShowVersionTip] = (0, import_react8.useState)(false);
|
|
2895
2932
|
const [currentLang, setCurrentLang] = (0, import_react8.useState)(resolveLocale(ctx, status));
|
|
2896
2933
|
const refreshStatusOnly = (0, import_react8.useCallback)(() => {
|
|
2897
2934
|
fetchStatus().then((data) => {
|
|
@@ -3256,18 +3293,63 @@ function TailscaleMobileSection(props) {
|
|
|
3256
3293
|
children: t("title", currentLang)
|
|
3257
3294
|
}
|
|
3258
3295
|
),
|
|
3259
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
3260
|
-
"
|
|
3296
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3297
|
+
"div",
|
|
3261
3298
|
{
|
|
3262
|
-
style: {
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3299
|
+
style: { position: "relative", display: "inline-flex", alignItems: "center" },
|
|
3300
|
+
onMouseEnter: () => setShowVersionTip(true),
|
|
3301
|
+
onMouseLeave: () => setShowVersionTip(false),
|
|
3302
|
+
onClick: () => {
|
|
3303
|
+
setToast({
|
|
3304
|
+
message: `\u{1F4E6} dsh-remote-mobile ${PLUGIN_VERSION} (${currentLang === "en" ? "Build: " : "\u6784\u5EFA\u65F6\u95F4: "}${BUILD_TIME})`,
|
|
3305
|
+
type: "info"
|
|
3306
|
+
});
|
|
3307
|
+
setTimeout(() => setToast(null), 3e3);
|
|
3269
3308
|
},
|
|
3270
|
-
children:
|
|
3309
|
+
children: [
|
|
3310
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3311
|
+
"span",
|
|
3312
|
+
{
|
|
3313
|
+
style: {
|
|
3314
|
+
fontSize: "11px",
|
|
3315
|
+
padding: "2px 7px",
|
|
3316
|
+
borderRadius: "10px",
|
|
3317
|
+
background: "rgba(59,130,246,0.15)",
|
|
3318
|
+
color: "var(--dsw-alias-brand-primary, #3b82f6)",
|
|
3319
|
+
fontWeight: "600",
|
|
3320
|
+
cursor: "pointer",
|
|
3321
|
+
userSelect: "none",
|
|
3322
|
+
transition: "all 0.2s ease"
|
|
3323
|
+
},
|
|
3324
|
+
children: PLUGIN_VERSION
|
|
3325
|
+
}
|
|
3326
|
+
),
|
|
3327
|
+
showVersionTip ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3328
|
+
"div",
|
|
3329
|
+
{
|
|
3330
|
+
style: {
|
|
3331
|
+
position: "absolute",
|
|
3332
|
+
top: "calc(100% + 6px)",
|
|
3333
|
+
left: "50%",
|
|
3334
|
+
transform: "translateX(-50%)",
|
|
3335
|
+
backgroundColor: "#0f172a",
|
|
3336
|
+
color: "#f8fafc",
|
|
3337
|
+
padding: "5px 10px",
|
|
3338
|
+
borderRadius: "6px",
|
|
3339
|
+
fontSize: "11px",
|
|
3340
|
+
fontWeight: "500",
|
|
3341
|
+
whiteSpace: "nowrap",
|
|
3342
|
+
boxShadow: "0 8px 16px rgba(0,0,0,0.3)",
|
|
3343
|
+
border: "1px solid rgba(255,255,255,0.15)",
|
|
3344
|
+
zIndex: 9999,
|
|
3345
|
+
pointerEvents: "none",
|
|
3346
|
+
lineHeight: "1.4",
|
|
3347
|
+
textAlign: "center"
|
|
3348
|
+
},
|
|
3349
|
+
children: `${currentLang === "en" ? "Build: " : "\u6253\u5305\u65F6\u95F4: "}${BUILD_TIME}`
|
|
3350
|
+
}
|
|
3351
|
+
) : null
|
|
3352
|
+
]
|
|
3271
3353
|
}
|
|
3272
3354
|
)
|
|
3273
3355
|
]
|
|
@@ -3307,7 +3389,7 @@ function TailscaleMobileSection(props) {
|
|
|
3307
3389
|
},
|
|
3308
3390
|
children: [
|
|
3309
3391
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { style: { width: "16px", height: "16px", fill: "currentColor" }, viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" }) }),
|
|
3310
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "
|
|
3392
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "dsh-remote-mobile" })
|
|
3311
3393
|
]
|
|
3312
3394
|
}
|
|
3313
3395
|
)
|