tt-help-cli-ycl 1.4.176 → 1.4.178
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/package.json +1 -1
- package/scripts/deploy-node.sh +48 -24
- package/scripts/test-login-import-skip.mjs +52 -0
- package/scripts/test-login-pool-ui.mjs +11 -2
- package/scripts/test-login-pool.mjs +110 -1
- package/src/cli/login-pool.js +207 -0
- package/src/lib/args.js +69 -0
- package/src/lib/constants.js +15 -4
- package/src/main.js +3 -0
- package/src/npm-main.js +3 -0
- package/src/watch/login-health.js +108 -0
- package/src/watch/login-pool.js +111 -2
- package/src/watch/public/app.js +29 -1
- package/src/watch/server.js +123 -34
- package/src/watchdog/login-import.js +43 -28
|
@@ -50,9 +50,8 @@ function withTimeout(promise, ms, label) {
|
|
|
50
50
|
]);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/** 单口导入:起/附 Edge → 验证 → 导出 cookies → 上传。未登录返回 skip */
|
|
53
|
+
/** 单口导入:起/附 Edge → 验证 → 导出 cookies → 上传。未登录返回 skip;不负责 marker(由 syncLoginStates 统一记账) */
|
|
54
54
|
async function importPort({ serverUrl, userId, port, proxyServer }) {
|
|
55
|
-
const marker = loadMarker(); // 本地已导入标识(永久只导一次)
|
|
56
55
|
// 探测该口是否已有 Edge 在跑:同步自己起的,导出完关掉;本来就跑着的不动
|
|
57
56
|
let selfStarted = false;
|
|
58
57
|
try {
|
|
@@ -92,9 +91,6 @@ async function importPort({ serverUrl, userId, port, proxyServer }) {
|
|
|
92
91
|
);
|
|
93
92
|
const j = await res.json().catch(() => null);
|
|
94
93
|
if (!res.ok || !j || j.error) throw new Error((j && j.error) || `HTTP ${res.status}`);
|
|
95
|
-
// 成功(含 dedup:中心已有即视为已同步)→ 写本地标识,永久只导一次
|
|
96
|
-
marker[port] = j.account.username;
|
|
97
|
-
saveMarker(marker);
|
|
98
94
|
return { ok: true, dedup: !!j.dedup, username: j.account.username, status: j.account.status };
|
|
99
95
|
} finally {
|
|
100
96
|
await page.close().catch(() => {}); // 只关自建页,不清光页面(POC 坑②)
|
|
@@ -107,6 +103,10 @@ async function importPort({ serverUrl, userId, port, proxyServer }) {
|
|
|
107
103
|
|
|
108
104
|
/**
|
|
109
105
|
* 同步入口:逐口导入本节点登录态。
|
|
106
|
+
* 跳过策略(按优先级,零成本跳过不受总超时影响):
|
|
107
|
+
* 1. 服务端 lastImportFrom 认领(本节点+本口导入过)→ 免重复起 Edge
|
|
108
|
+
* 2. 本地 marker(成功导入过 = 永久跳过,与账号中心状态无关)
|
|
109
|
+
* marker 的读/判/写全部收拢在本函数(importPort 只做单口机构)。
|
|
110
110
|
* @param {object} opts { serverUrl, userId, ports[], proxyServer?, portTimeoutMs?, totalTimeoutMs? }
|
|
111
111
|
* @returns {Promise<{imported: Array, skipped: Array, failed: Array}>}
|
|
112
112
|
*/
|
|
@@ -123,38 +123,23 @@ export async function syncLoginStates(opts = {}) {
|
|
|
123
123
|
if (!serverUrl || !userId || !Array.isArray(ports) || ports.length === 0) return summary;
|
|
124
124
|
const deadline = Date.now() + totalTimeoutMs;
|
|
125
125
|
|
|
126
|
-
//
|
|
126
|
+
// 本地标识:成功导入过一次即永久跳过
|
|
127
127
|
const marker = loadMarker();
|
|
128
|
-
|
|
129
|
-
// 热路径:本节点此前已成功导入过的口(lastImportFrom 元数据)直接跳过,免重复起 Edge
|
|
130
|
-
const imported = new Set();
|
|
131
|
-
try {
|
|
132
|
-
const r = await fetch(`${String(serverUrl).replace(/\/+$/, "")}/api/login-pool`, {
|
|
133
|
-
signal: AbortSignal.timeout(15000),
|
|
134
|
-
});
|
|
135
|
-
const j = await r.json();
|
|
136
|
-
for (const a of j.accounts || []) {
|
|
137
|
-
const m = a.lastImportFrom;
|
|
138
|
-
if (m && m.userId === userId && ports.includes(Number(m.port))) {
|
|
139
|
-
imported.add(Number(m.port));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch (e) {
|
|
143
|
-
console.error(`[登录导入] 中心视图获取失败(继续逐口尝试): ${e.message}`);
|
|
144
|
-
}
|
|
128
|
+
const serverConfirmed = await serverImportedPorts({ serverUrl, userId, ports, marker });
|
|
145
129
|
|
|
146
130
|
for (const port of ports) {
|
|
147
131
|
const p = Number(port);
|
|
132
|
+
const reason = skipReason(p, marker, serverConfirmed);
|
|
133
|
+
if (reason) {
|
|
134
|
+
summary.skipped.push({ port: p, reason });
|
|
135
|
+
console.error(`[登录导入] 端口 ${p}: ${reason},跳过`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
148
138
|
if (Date.now() > deadline) {
|
|
149
139
|
summary.failed.push({ port: p, reason: "总超时" });
|
|
150
140
|
console.error(`[登录导入] 端口 ${p}: 总超时,剩余端口放弃`);
|
|
151
141
|
continue;
|
|
152
142
|
}
|
|
153
|
-
if (imported.has(p)) {
|
|
154
|
-
summary.skipped.push({ port: p, reason: "本节点此前已导入" });
|
|
155
|
-
console.error(`[登录导入] 端口 ${p}: 此前已导入,跳过`);
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
143
|
console.error(`[登录导入] 端口 ${p}: 检测并导入...`);
|
|
159
144
|
try {
|
|
160
145
|
const r = await withTimeout(
|
|
@@ -163,6 +148,8 @@ export async function syncLoginStates(opts = {}) {
|
|
|
163
148
|
`端口 ${p} 导入`,
|
|
164
149
|
);
|
|
165
150
|
if (r.ok) {
|
|
151
|
+
marker[p] = r.username; // 成功(含 dedup:中心已有即视为已同步)→ 写本地标识,永久只导一次
|
|
152
|
+
saveMarker(marker);
|
|
166
153
|
summary.imported.push({ port: p, username: r.username, dedup: r.dedup });
|
|
167
154
|
console.error(
|
|
168
155
|
`[登录导入] 端口 ${p}: @${r.username} ${r.dedup ? "已在池,幂等跳过" : "新入池(空闲)✓"}`,
|
|
@@ -178,3 +165,31 @@ export async function syncLoginStates(opts = {}) {
|
|
|
178
165
|
}
|
|
179
166
|
return summary;
|
|
180
167
|
}
|
|
168
|
+
|
|
169
|
+
/** 服务端认领:中心 lastImportFrom 指向本节点本口的端口集合;全口都有本地 marker 时不发 fetch */
|
|
170
|
+
async function serverImportedPorts({ serverUrl, userId, ports, marker }) {
|
|
171
|
+
const confirmed = new Set();
|
|
172
|
+
if (ports.every((x) => marker[Number(x)])) return confirmed; // 本地标识已全覆盖,无需问中心
|
|
173
|
+
try {
|
|
174
|
+
const r = await fetch(`${String(serverUrl).replace(/\/+$/, "")}/api/login-pool`, {
|
|
175
|
+
signal: AbortSignal.timeout(15000),
|
|
176
|
+
});
|
|
177
|
+
const j = await r.json();
|
|
178
|
+
for (const a of j.accounts || []) {
|
|
179
|
+
const m = a.lastImportFrom;
|
|
180
|
+
if (m && m.userId === userId && ports.includes(Number(m.port))) {
|
|
181
|
+
confirmed.add(Number(m.port));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.error(`[登录导入] 中心视图获取失败(继续逐口尝试): ${e.message}`);
|
|
186
|
+
}
|
|
187
|
+
return confirmed;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** 跳过判定:返回原因字符串(跳过)或 null(需导入) */
|
|
191
|
+
function skipReason(p, marker, serverConfirmed) {
|
|
192
|
+
if (serverConfirmed.has(p)) return "本节点此前已导入";
|
|
193
|
+
if (marker[p]) return "本地已导入";
|
|
194
|
+
return null;
|
|
195
|
+
}
|