fogact 1.2.7 → 1.2.8
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/lib/index.js +9 -1
- package/lib/services/node-service.js +39 -9
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -17,6 +17,12 @@ const MENU_CHOICES = [
|
|
|
17
17
|
{ title: "4. 退出", value: "exit" },
|
|
18
18
|
];
|
|
19
19
|
|
|
20
|
+
const MENU_COLORS = {
|
|
21
|
+
test: "\x1b[34m",
|
|
22
|
+
exit: "\x1b[90m",
|
|
23
|
+
};
|
|
24
|
+
const ANSI_RESET = "\x1b[0m";
|
|
25
|
+
|
|
20
26
|
const UPDATE_TIMEOUT_MS = 2500;
|
|
21
27
|
|
|
22
28
|
function parseVersion(version) {
|
|
@@ -158,7 +164,9 @@ function renderMenu(cursor = 0) {
|
|
|
158
164
|
];
|
|
159
165
|
|
|
160
166
|
MENU_CHOICES.forEach((choice, index) => {
|
|
161
|
-
|
|
167
|
+
const color = MENU_COLORS[choice.value] || "";
|
|
168
|
+
const title = color ? `${color}${choice.title}${ANSI_RESET}` : choice.title;
|
|
169
|
+
lines.push(`${index === cursor ? "❯" : " "} ${title}`);
|
|
162
170
|
});
|
|
163
171
|
|
|
164
172
|
lines.push("");
|
|
@@ -5,6 +5,29 @@ const net = require("net");
|
|
|
5
5
|
const { testNode } = require("./fogact-api");
|
|
6
6
|
|
|
7
7
|
const PROBE_TIMEOUT_MS = 5000;
|
|
8
|
+
const ANSI = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
green: "\x1b[32m",
|
|
11
|
+
yellow: "\x1b[33m",
|
|
12
|
+
red: "\x1b[31m",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function shouldColorize(options = {}) {
|
|
16
|
+
if (options.color === true || process.env.FORCE_COLOR) return true;
|
|
17
|
+
if (options.color === false || process.env.NO_COLOR) return false;
|
|
18
|
+
return Boolean(process.stdout.isTTY);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function colorize(value, color, enabled) {
|
|
22
|
+
if (!enabled || !color) return String(value);
|
|
23
|
+
return `${ANSI[color]}${value}${ANSI.reset}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function latencyColor(latency) {
|
|
27
|
+
if (latency < 60) return "green";
|
|
28
|
+
if (latency < 150) return "yellow";
|
|
29
|
+
return "red";
|
|
30
|
+
}
|
|
8
31
|
|
|
9
32
|
function parseNodeUrl(nodeUrl) {
|
|
10
33
|
const url = new URL(nodeUrl);
|
|
@@ -178,21 +201,26 @@ function padCell(value, width) {
|
|
|
178
201
|
return `${text}${" ".repeat(Math.max(0, width - displayWidth))}`;
|
|
179
202
|
}
|
|
180
203
|
|
|
181
|
-
function formatProbe(entry) {
|
|
182
|
-
if (!entry?.ok) return "--";
|
|
183
|
-
|
|
204
|
+
function formatProbe(entry, colorEnabled) {
|
|
205
|
+
if (!entry?.ok) return colorize("--", "red", colorEnabled);
|
|
206
|
+
const value = `${entry.latency}ms`;
|
|
207
|
+
return colorize(value, latencyColor(entry.latency), colorEnabled);
|
|
184
208
|
}
|
|
185
209
|
|
|
186
|
-
function formatNodeLine(result, best) {
|
|
187
|
-
const mark = result.available ? "✓" : "✗";
|
|
210
|
+
function formatNodeLine(result, best, colorEnabled) {
|
|
211
|
+
const mark = colorize(result.available ? "✓" : "✗", result.available ? "green" : "red", colorEnabled);
|
|
188
212
|
const name = padCell(result.name || result.url || "FogAct", 12);
|
|
213
|
+
const ping = `ping:${formatProbe(result.ping, colorEnabled)}`;
|
|
214
|
+
const tcp = `tcp:${formatProbe(result.tcp, colorEnabled)}`;
|
|
215
|
+
const http = `http:${formatProbe(result.http, colorEnabled)}`;
|
|
189
216
|
|
|
190
217
|
if (!result.available) {
|
|
191
|
-
return ` ${mark} ${name}
|
|
218
|
+
return ` ${mark} ${name} ${ping} ${tcp} ${http} ${colorize("不可达", "red", colorEnabled)}`;
|
|
192
219
|
}
|
|
193
220
|
|
|
194
|
-
const
|
|
195
|
-
|
|
221
|
+
const latency = colorize(`${result.avgLatency}ms`, latencyColor(result.avgLatency), colorEnabled);
|
|
222
|
+
const bestMark = best && best === result ? ` ${colorize("★ 最优", "yellow", colorEnabled)}` : "";
|
|
223
|
+
return ` ${mark} ${name} ${ping} ${tcp} ${http} ${latency} (±${result.latencyStdDev}ms) ${stabilityLabel(result.latencyStdDev)} ${getResultScore(result)}分${bestMark}`;
|
|
196
224
|
}
|
|
197
225
|
|
|
198
226
|
function formatNodeResults(results, options = {}) {
|
|
@@ -201,13 +229,14 @@ function formatNodeResults(results, options = {}) {
|
|
|
201
229
|
const availableCount = results.filter((result) => result.available).length;
|
|
202
230
|
const lines = [];
|
|
203
231
|
const title = options.title || "节点测试结果";
|
|
232
|
+
const colorEnabled = shouldColorize(options);
|
|
204
233
|
|
|
205
234
|
lines.push(` ${title}`);
|
|
206
235
|
lines.push(" ───────────────────────────────────────────────────");
|
|
207
236
|
lines.push("");
|
|
208
237
|
|
|
209
238
|
for (const result of sorted) {
|
|
210
|
-
lines.push(formatNodeLine(result, best));
|
|
239
|
+
lines.push(formatNodeLine(result, best, colorEnabled));
|
|
211
240
|
}
|
|
212
241
|
|
|
213
242
|
lines.push("");
|
|
@@ -223,4 +252,5 @@ module.exports = {
|
|
|
223
252
|
sortNodeResults,
|
|
224
253
|
probeNode,
|
|
225
254
|
testTcp,
|
|
255
|
+
shouldColorize,
|
|
226
256
|
};
|