Compare commits

..

342 Commits

Author SHA1 Message Date
CPU Ranking Bot
ca49a07a99 更新CPU性能排行榜数据 - 2026-04-20 01:38:35 2026-04-20 01:38:35 +00:00
CPU Ranking Bot
3daba66873 更新CPU性能排行榜数据 - 2026-04-19 01:37:38 2026-04-19 01:37:38 +00:00
CPU Ranking Bot
30018f49f1 更新CPU性能排行榜数据 - 2026-04-18 01:39:03 2026-04-18 01:39:03 +00:00
CPU Ranking Bot
e9e1eb42e5 更新CPU性能排行榜数据 - 2026-04-17 01:38:06 2026-04-17 01:38:06 +00:00
CPU Ranking Bot
6f1a6c9b98 更新CPU性能排行榜数据 - 2026-04-16 01:37:45 2026-04-16 01:37:45 +00:00
CPU Ranking Bot
343b599e99 更新CPU性能排行榜数据 - 2026-04-15 01:39:44 2026-04-15 01:39:44 +00:00
CPU Ranking Bot
5b098d9cdf 更新CPU性能排行榜数据 - 2026-04-14 01:40:06 2026-04-14 01:40:06 +00:00
CPU Ranking Bot
b16e91a82a 更新CPU性能排行榜数据 - 2026-04-13 01:38:55 2026-04-13 01:38:55 +00:00
CPU Ranking Bot
9dcb5ee17e 更新CPU性能排行榜数据 - 2026-04-12 01:37:55 2026-04-12 01:37:55 +00:00
CPU Ranking Bot
eeb2d51c51 更新CPU性能排行榜数据 - 2026-04-11 01:37:57 2026-04-11 01:37:57 +00:00
CPU Ranking Bot
d994eb8c1b 更新CPU性能排行榜数据 - 2026-04-10 01:38:42 2026-04-10 01:38:42 +00:00
CPU Ranking Bot
b8c3bb0825 更新CPU性能排行榜数据 - 2026-04-09 01:39:39 2026-04-09 01:39:39 +00:00
CPU Ranking Bot
1e2e6631d3 更新CPU性能排行榜数据 - 2026-04-08 01:38:03 2026-04-08 01:38:03 +00:00
CPU Ranking Bot
4a2a2d2347 更新CPU性能排行榜数据 - 2026-04-07 01:40:40 2026-04-07 01:40:41 +00:00
CPU Ranking Bot
47b0c9428f 更新CPU性能排行榜数据 - 2026-04-06 01:38:14 2026-04-06 01:38:14 +00:00
CPU Ranking Bot
b695c08a6d 更新CPU性能排行榜数据 - 2026-04-05 01:37:58 2026-04-05 01:37:58 +00:00
CPU Ranking Bot
7c58374d2c 更新CPU性能排行榜数据 - 2026-04-04 01:37:39 2026-04-04 01:37:39 +00:00
CPU Ranking Bot
bb91e030ee 更新CPU性能排行榜数据 - 2026-04-03 01:39:07 2026-04-03 01:39:07 +00:00
CPU Ranking Bot
a2f5b62f2d 更新CPU性能排行榜数据 - 2026-04-02 01:37:16 2026-04-02 01:37:16 +00:00
CPU Ranking Bot
67393bf58b 更新CPU性能排行榜数据 - 2026-04-01 01:38:25 2026-04-01 01:38:25 +00:00
CPU Ranking Bot
fc49230534 更新CPU性能排行榜数据 - 2026-03-31 01:39:35 2026-03-31 01:39:35 +00:00
CPU Ranking Bot
ed7c6e94ee 更新CPU性能排行榜数据 - 2026-03-30 01:40:13 2026-03-30 01:40:13 +00:00
CPU Ranking Bot
27d6bcab34 更新CPU性能排行榜数据 - 2026-03-29 01:37:58 2026-03-29 01:37:58 +00:00
CPU Ranking Bot
f666752a39 更新CPU性能排行榜数据 - 2026-03-28 01:39:55 2026-03-28 01:39:55 +00:00
CPU Ranking Bot
fdbc77f7c2 更新CPU性能排行榜数据 - 2026-03-27 01:39:07 2026-03-27 01:39:07 +00:00
CPU Ranking Bot
30a04e5325 更新CPU性能排行榜数据 - 2026-03-26 01:38:41 2026-03-26 01:38:41 +00:00
CPU Ranking Bot
4409adc15f 更新CPU性能排行榜数据 - 2026-03-25 01:39:17 2026-03-25 01:39:17 +00:00
CPU Ranking Bot
fa6bdd5257 更新CPU性能排行榜数据 - 2026-03-24 01:38:11 2026-03-24 01:38:11 +00:00
CPU Ranking Bot
e9eef56c1c 更新CPU性能排行榜数据 - 2026-03-23 01:36:55 2026-03-23 01:36:55 +00:00
CPU Ranking Bot
fed476c823 更新CPU性能排行榜数据 - 2026-03-22 01:38:02 2026-03-22 01:38:02 +00:00
CPU Ranking Bot
d85b0dd94a 更新CPU性能排行榜数据 - 2026-03-21 01:38:42 2026-03-21 01:38:42 +00:00
CPU Ranking Bot
e5d49d7fbf 更新CPU性能排行榜数据 - 2026-03-20 01:38:27 2026-03-20 01:38:27 +00:00
CPU Ranking Bot
24a477ba84 更新CPU性能排行榜数据 - 2026-03-19 01:39:03 2026-03-19 01:39:03 +00:00
CPU Ranking Bot
c517548ccd 更新CPU性能排行榜数据 - 2026-03-18 01:43:21 2026-03-18 01:43:21 +00:00
CPU Ranking Bot
f2d05ce47f 更新CPU性能排行榜数据 - 2026-03-17 01:41:42 2026-03-17 01:41:43 +00:00
CPU Ranking Bot
64027801eb 更新CPU性能排行榜数据 - 2026-03-16 01:42:32 2026-03-16 01:42:32 +00:00
CPU Ranking Bot
5e3df67ac6 更新CPU性能排行榜数据 - 2026-03-15 01:42:07 2026-03-15 01:42:07 +00:00
CPU Ranking Bot
bff865cc21 更新CPU性能排行榜数据 - 2026-03-14 01:41:24 2026-03-14 01:41:24 +00:00
CPU Ranking Bot
cd074fe9dc 更新CPU性能排行榜数据 - 2026-03-13 01:41:10 2026-03-13 01:41:10 +00:00
CPU Ranking Bot
fe8e54b39c 更新CPU性能排行榜数据 - 2026-03-12 01:42:52 2026-03-12 01:42:52 +00:00
CPU Ranking Bot
4c3d869ca0 更新CPU性能排行榜数据 - 2026-03-11 01:42:11 2026-03-11 01:42:11 +00:00
CPU Ranking Bot
f3edfeb8d1 更新CPU性能排行榜数据 - 2026-03-10 01:42:02 2026-03-10 01:42:02 +00:00
CPU Ranking Bot
3a9ad7c96f 更新CPU性能排行榜数据 - 2026-03-09 01:42:48 2026-03-09 01:42:48 +00:00
CPU Ranking Bot
b279c220e9 更新CPU性能排行榜数据 - 2026-03-08 01:43:57 2026-03-08 01:43:57 +00:00
CPU Ranking Bot
c67d6836a6 更新CPU性能排行榜数据 - 2026-03-07 01:41:40 2026-03-07 01:41:40 +00:00
CPU Ranking Bot
9bb57b2e23 更新CPU性能排行榜数据 - 2026-03-06 01:43:08 2026-03-06 01:43:08 +00:00
CPU Ranking Bot
87ac5b5fff 更新CPU性能排行榜数据 - 2026-03-05 01:41:46 2026-03-05 01:41:46 +00:00
CPU Ranking Bot
463c403a3a 更新CPU性能排行榜数据 - 2026-03-04 01:44:00 2026-03-04 01:44:00 +00:00
CPU Ranking Bot
a8e95c67f1 更新CPU性能排行榜数据 - 2026-03-03 01:42:26 2026-03-03 01:42:26 +00:00
CPU Ranking Bot
c31b6fc2e0 更新CPU性能排行榜数据 - 2026-03-02 01:42:44 2026-03-02 01:42:44 +00:00
CPU Ranking Bot
a274cf12ea 更新CPU性能排行榜数据 - 2026-03-01 01:41:58 2026-03-01 01:41:58 +00:00
CPU Ranking Bot
84cb0ca84d 更新CPU性能排行榜数据 - 2026-02-28 01:41:17 2026-02-28 01:41:17 +00:00
CPU Ranking Bot
101b8e53f2 更新CPU性能排行榜数据 - 2026-02-27 01:43:43 2026-02-27 01:43:43 +00:00
CPU Ranking Bot
460f8c78a4 更新CPU性能排行榜数据 - 2026-02-26 01:47:19 2026-02-26 01:47:19 +00:00
CPU Ranking Bot
88e3865fea 更新CPU性能排行榜数据 - 2026-02-25 01:42:42 2026-02-25 01:42:42 +00:00
CPU Ranking Bot
553c765ad9 更新CPU性能排行榜数据 - 2026-02-24 01:42:07 2026-02-24 01:42:07 +00:00
CPU Ranking Bot
50f9c8a795 更新CPU性能排行榜数据 - 2026-02-23 01:41:10 2026-02-23 01:41:10 +00:00
CPU Ranking Bot
dcf6bf81b3 更新CPU性能排行榜数据 - 2026-02-22 01:38:23 2026-02-22 01:38:23 +00:00
CPU Ranking Bot
761afce37c 更新CPU性能排行榜数据 - 2026-02-21 01:39:36 2026-02-21 01:39:36 +00:00
CPU Ranking Bot
09645afad8 更新CPU性能排行榜数据 - 2026-02-20 01:42:20 2026-02-20 01:42:20 +00:00
CPU Ranking Bot
a9a922b282 更新CPU性能排行榜数据 - 2026-02-19 01:42:15 2026-02-19 01:42:16 +00:00
CPU Ranking Bot
6f919291ad 更新CPU性能排行榜数据 - 2026-02-18 01:42:43 2026-02-18 01:42:43 +00:00
CPU Ranking Bot
77afed63f6 更新CPU性能排行榜数据 - 2026-02-17 01:41:14 2026-02-17 01:41:14 +00:00
CPU Ranking Bot
dca63a014e 更新CPU性能排行榜数据 - 2026-02-16 01:39:46 2026-02-16 01:39:46 +00:00
CPU Ranking Bot
5aa4d65488 更新CPU性能排行榜数据 - 2026-02-15 01:42:45 2026-02-15 01:42:45 +00:00
CPU Ranking Bot
f7a1726c66 更新CPU性能排行榜数据 - 2026-02-14 01:44:07 2026-02-14 01:44:07 +00:00
CPU Ranking Bot
336f4e6321 更新CPU性能排行榜数据 - 2026-02-13 01:41:19 2026-02-13 01:41:19 +00:00
CPU Ranking Bot
23b95958bc 更新CPU性能排行榜数据 - 2026-02-12 01:42:25 2026-02-12 01:42:25 +00:00
CPU Ranking Bot
faa7f111c9 更新CPU性能排行榜数据 - 2026-02-11 01:44:20 2026-02-11 01:44:20 +00:00
CPU Ranking Bot
8802bb3928 更新CPU性能排行榜数据 - 2026-02-10 01:37:04 2026-02-10 01:37:04 +00:00
CPU Ranking Bot
685d66c8ba 更新CPU性能排行榜数据 - 2026-02-09 01:36:47 2026-02-09 01:36:47 +00:00
CPU Ranking Bot
6cc9060885 更新CPU性能排行榜数据 - 2026-02-08 01:39:33 2026-02-08 01:39:33 +00:00
CPU Ranking Bot
7db4221b6e 更新CPU性能排行榜数据 - 2026-02-07 01:37:06 2026-02-07 01:37:06 +00:00
CPU Ranking Bot
0f26704f7b 更新CPU性能排行榜数据 - 2026-02-06 01:37:38 2026-02-06 01:37:38 +00:00
CPU Ranking Bot
439ab6a3d6 更新CPU性能排行榜数据 - 2026-02-05 01:36:10 2026-02-05 01:36:10 +00:00
CPU Ranking Bot
5673428072 更新CPU性能排行榜数据 - 2026-02-04 01:37:21 2026-02-04 01:37:21 +00:00
CPU Ranking Bot
d34073eafe 更新CPU性能排行榜数据 - 2026-02-03 01:39:45 2026-02-03 01:39:45 +00:00
CPU Ranking Bot
6703767817 更新CPU性能排行榜数据 - 2026-02-02 01:37:52 2026-02-02 01:37:52 +00:00
CPU Ranking Bot
78c1229708 更新CPU性能排行榜数据 - 2026-02-01 01:39:44 2026-02-01 01:39:44 +00:00
CPU Ranking Bot
e8b846a41c 更新CPU性能排行榜数据 - 2026-01-31 01:36:47 2026-01-31 01:36:47 +00:00
CPU Ranking Bot
f5bb2aebb7 更新CPU性能排行榜数据 - 2026-01-30 01:40:29 2026-01-30 01:40:29 +00:00
CPU Ranking Bot
33f7570d6e 更新CPU性能排行榜数据 - 2026-01-29 01:36:39 2026-01-29 01:36:39 +00:00
CPU Ranking Bot
d6eb89adff 更新CPU性能排行榜数据 - 2026-01-28 01:37:23 2026-01-28 01:37:23 +00:00
CPU Ranking Bot
c539079818 更新CPU性能排行榜数据 - 2026-01-27 01:39:29 2026-01-27 01:39:29 +00:00
CPU Ranking Bot
1675cf64bb 更新CPU性能排行榜数据 - 2026-01-26 01:37:38 2026-01-26 01:37:38 +00:00
CPU Ranking Bot
b953c6be03 更新CPU性能排行榜数据 - 2026-01-25 01:36:59 2026-01-25 01:36:59 +00:00
CPU Ranking Bot
d35baed41c 更新CPU性能排行榜数据 - 2026-01-24 01:35:47 2026-01-24 01:35:47 +00:00
CPU Ranking Bot
dc6ed446ae 更新CPU性能排行榜数据 - 2026-01-23 01:35:55 2026-01-23 01:35:56 +00:00
CPU Ranking Bot
18d9e20f64 更新CPU性能排行榜数据 - 2026-01-22 01:36:18 2026-01-22 01:36:18 +00:00
CPU Ranking Bot
4a89123c76 更新CPU性能排行榜数据 - 2026-01-21 01:40:24 2026-01-21 01:40:24 +00:00
CPU Ranking Bot
0b138771fa 更新CPU性能排行榜数据 - 2026-01-20 01:37:03 2026-01-20 01:37:03 +00:00
CPU Ranking Bot
166a4fb265 更新CPU性能排行榜数据 - 2026-01-19 01:38:18 2026-01-19 01:38:18 +00:00
CPU Ranking Bot
b6de8053a9 更新CPU性能排行榜数据 - 2026-01-18 01:35:49 2026-01-18 01:35:49 +00:00
CPU Ranking Bot
ecdf9e64e0 更新CPU性能排行榜数据 - 2026-01-17 01:39:45 2026-01-17 01:39:45 +00:00
CPU Ranking Bot
ea41447b96 更新CPU性能排行榜数据 - 2026-01-16 01:36:34 2026-01-16 01:36:34 +00:00
CPU Ranking Bot
3d094e8aa3 更新CPU性能排行榜数据 - 2026-01-15 01:39:29 2026-01-15 01:39:29 +00:00
CPU Ranking Bot
34fa63d714 更新CPU性能排行榜数据 - 2026-01-14 01:39:35 2026-01-14 01:39:35 +00:00
CPU Ranking Bot
3c71ba8073 更新CPU性能排行榜数据 - 2026-01-13 01:38:30 2026-01-13 01:38:30 +00:00
CPU Ranking Bot
d3c41e0f0d 更新CPU性能排行榜数据 - 2026-01-12 01:38:33 2026-01-12 01:38:33 +00:00
CPU Ranking Bot
2c822ce7ca 更新CPU性能排行榜数据 - 2026-01-11 01:36:03 2026-01-11 01:36:03 +00:00
CPU Ranking Bot
fdb18354fd 更新CPU性能排行榜数据 - 2026-01-10 01:37:30 2026-01-10 01:37:30 +00:00
CPU Ranking Bot
743d9ff91b 更新CPU性能排行榜数据 - 2026-01-09 01:41:10 2026-01-09 01:41:10 +00:00
CPU Ranking Bot
5556ef5a55 更新CPU性能排行榜数据 - 2026-01-08 01:36:30 2026-01-08 01:36:30 +00:00
CPU Ranking Bot
344cbe630e 更新CPU性能排行榜数据 - 2026-01-07 01:38:21 2026-01-07 01:38:21 +00:00
CPU Ranking Bot
59799cef86 更新CPU性能排行榜数据 - 2026-01-06 01:37:03 2026-01-06 01:37:03 +00:00
CPU Ranking Bot
ba689a9d52 更新CPU性能排行榜数据 - 2026-01-05 01:41:22 2026-01-05 01:41:22 +00:00
CPU Ranking Bot
b5dc634a65 更新CPU性能排行榜数据 - 2026-01-04 01:43:38 2026-01-04 01:43:38 +00:00
CPU Ranking Bot
84d6cb12ab 更新CPU性能排行榜数据 - 2026-01-03 01:37:34 2026-01-03 01:37:34 +00:00
CPU Ranking Bot
0b07834674 更新CPU性能排行榜数据 - 2026-01-02 01:39:21 2026-01-02 01:39:21 +00:00
CPU Ranking Bot
6975c3ff70 更新CPU性能排行榜数据 - 2026-01-01 01:37:36 2026-01-01 01:37:36 +00:00
CPU Ranking Bot
e4e0bf7e5a 更新CPU性能排行榜数据 - 2025-12-31 01:36:02 2025-12-31 01:36:02 +00:00
CPU Ranking Bot
dbe412fb41 更新CPU性能排行榜数据 - 2025-12-30 01:36:23 2025-12-30 01:36:23 +00:00
CPU Ranking Bot
2c8c2fe407 更新CPU性能排行榜数据 - 2025-12-29 01:37:23 2025-12-29 01:37:23 +00:00
CPU Ranking Bot
921f4578c8 更新CPU性能排行榜数据 - 2025-12-28 01:36:02 2025-12-28 01:36:02 +00:00
CPU Ranking Bot
9854a430a8 更新CPU性能排行榜数据 - 2025-12-27 01:37:19 2025-12-27 01:37:19 +00:00
CPU Ranking Bot
96c94ce1fe 更新CPU性能排行榜数据 - 2025-12-26 01:39:31 2025-12-26 01:39:31 +00:00
CPU Ranking Bot
67b4815e5f 更新CPU性能排行榜数据 - 2025-12-25 01:36:06 2025-12-25 01:36:06 +00:00
CPU Ranking Bot
f6973de413 更新CPU性能排行榜数据 - 2025-12-24 01:35:19 2025-12-24 01:35:19 +00:00
CPU Ranking Bot
fe5f4a74d3 更新CPU性能排行榜数据 - 2025-12-23 01:39:44 2025-12-23 01:39:44 +00:00
CPU Ranking Bot
fbafcfc48d 更新CPU性能排行榜数据 - 2025-12-22 01:38:55 2025-12-22 01:38:55 +00:00
CPU Ranking Bot
5a53c23b16 更新CPU性能排行榜数据 - 2025-12-21 01:39:45 2025-12-21 01:39:45 +00:00
CPU Ranking Bot
6380788df8 更新CPU性能排行榜数据 - 2025-12-20 01:41:14 2025-12-20 01:41:14 +00:00
CPU Ranking Bot
94028a314d 更新CPU性能排行榜数据 - 2025-12-19 01:39:26 2025-12-19 01:39:26 +00:00
CPU Ranking Bot
22db5bdfe5 更新CPU性能排行榜数据 - 2025-12-18 01:39:34 2025-12-18 01:39:34 +00:00
CPU Ranking Bot
92f46cf693 更新CPU性能排行榜数据 - 2025-12-17 01:39:13 2025-12-17 01:39:13 +00:00
CPU Ranking Bot
696327aee1 更新CPU性能排行榜数据 - 2025-12-16 01:38:30 2025-12-16 01:38:30 +00:00
CPU Ranking Bot
4dfe2763b2 更新CPU性能排行榜数据 - 2025-12-15 01:39:34 2025-12-15 01:39:34 +00:00
CPU Ranking Bot
69f239cc61 更新CPU性能排行榜数据 - 2025-12-14 01:38:58 2025-12-14 01:38:59 +00:00
CPU Ranking Bot
c74a08d34d 更新CPU性能排行榜数据 - 2025-12-13 01:37:56 2025-12-13 01:37:56 +00:00
CPU Ranking Bot
a9ea6256c7 更新CPU性能排行榜数据 - 2025-12-12 01:38:53 2025-12-12 01:38:53 +00:00
CPU Ranking Bot
7855d27e71 更新CPU性能排行榜数据 - 2025-12-11 01:38:46 2025-12-11 01:38:46 +00:00
CPU Ranking Bot
f654f6ab33 更新CPU性能排行榜数据 - 2025-12-10 01:37:44 2025-12-10 01:37:44 +00:00
CPU Ranking Bot
8b42fc10e2 更新CPU性能排行榜数据 - 2025-12-09 01:38:27 2025-12-09 01:38:28 +00:00
CPU Ranking Bot
8435b74cc3 更新CPU性能排行榜数据 - 2025-12-08 01:39:42 2025-12-08 01:39:42 +00:00
CPU Ranking Bot
5820ea6924 更新CPU性能排行榜数据 - 2025-12-07 01:36:48 2025-12-07 01:36:48 +00:00
CPU Ranking Bot
db4a9e365b 更新CPU性能排行榜数据 - 2025-12-06 01:39:12 2025-12-06 01:39:12 +00:00
CPU Ranking Bot
14276a6e20 更新CPU性能排行榜数据 - 2025-12-05 01:34:03 2025-12-05 01:34:03 +00:00
CPU Ranking Bot
08af10b886 更新CPU性能排行榜数据 - 2025-12-04 01:37:43 2025-12-04 01:37:43 +00:00
CPU Ranking Bot
842c01e3e9 更新CPU性能排行榜数据 - 2025-12-03 01:38:04 2025-12-03 01:38:04 +00:00
CPU Ranking Bot
6429af8c5f 更新CPU性能排行榜数据 - 2025-12-02 01:38:36 2025-12-02 01:38:36 +00:00
CPU Ranking Bot
7987b3354b 更新CPU性能排行榜数据 - 2025-12-01 01:37:35 2025-12-01 01:37:35 +00:00
CPU Ranking Bot
ed83a01ca6 更新CPU性能排行榜数据 - 2025-11-30 01:38:19 2025-11-30 01:38:19 +00:00
CPU Ranking Bot
9944df70eb 更新CPU性能排行榜数据 - 2025-11-29 01:38:56 2025-11-29 01:38:56 +00:00
CPU Ranking Bot
dd2b0a7ee2 更新CPU性能排行榜数据 - 2025-11-28 01:37:39 2025-11-28 01:37:39 +00:00
CPU Ranking Bot
35b6f08b02 更新CPU性能排行榜数据 - 2025-11-27 01:38:09 2025-11-27 01:38:09 +00:00
CPU Ranking Bot
07fe65d2c4 更新CPU性能排行榜数据 - 2025-11-26 01:38:18 2025-11-26 01:38:18 +00:00
CPU Ranking Bot
66c064e325 更新CPU性能排行榜数据 - 2025-11-25 01:37:20 2025-11-25 01:37:20 +00:00
CPU Ranking Bot
3332eb304b 更新CPU性能排行榜数据 - 2025-11-24 01:38:58 2025-11-24 01:38:58 +00:00
CPU Ranking Bot
b09be731ed 更新CPU性能排行榜数据 - 2025-11-23 01:38:39 2025-11-23 01:38:39 +00:00
CPU Ranking Bot
fcc7fc6b74 更新CPU性能排行榜数据 - 2025-11-22 01:36:56 2025-11-22 01:36:56 +00:00
CPU Ranking Bot
ec8179a312 更新CPU性能排行榜数据 - 2025-11-21 01:39:02 2025-11-21 01:39:03 +00:00
CPU Ranking Bot
a8244b77cc 更新CPU性能排行榜数据 - 2025-11-20 01:38:25 2025-11-20 01:38:26 +00:00
CPU Ranking Bot
457da0f624 更新CPU性能排行榜数据 - 2025-11-19 01:37:30 2025-11-19 01:37:31 +00:00
CPU Ranking Bot
565021b320 更新CPU性能排行榜数据 - 2025-11-18 01:38:27 2025-11-18 01:38:27 +00:00
CPU Ranking Bot
d109e9dd52 更新CPU性能排行榜数据 - 2025-11-17 01:38:20 2025-11-17 01:38:20 +00:00
CPU Ranking Bot
9e2035d477 更新CPU性能排行榜数据 - 2025-11-16 01:37:28 2025-11-16 01:37:28 +00:00
CPU Ranking Bot
b95dd760f7 更新CPU性能排行榜数据 - 2025-11-15 01:38:02 2025-11-15 01:38:02 +00:00
CPU Ranking Bot
2c45af87ba 更新CPU性能排行榜数据 - 2025-11-14 01:38:52 2025-11-14 01:38:52 +00:00
CPU Ranking Bot
51f817ec4e 更新CPU性能排行榜数据 - 2025-11-13 01:37:34 2025-11-13 01:37:34 +00:00
CPU Ranking Bot
d9aa9c8b14 更新CPU性能排行榜数据 - 2025-11-12 01:39:02 2025-11-12 01:39:02 +00:00
CPU Ranking Bot
4161ba2114 更新CPU性能排行榜数据 - 2025-11-11 01:38:56 2025-11-11 01:38:56 +00:00
CPU Ranking Bot
e494905310 更新CPU性能排行榜数据 - 2025-11-10 01:37:49 2025-11-10 01:37:49 +00:00
CPU Ranking Bot
c15d33c984 更新CPU性能排行榜数据 - 2025-11-09 01:38:40 2025-11-09 01:38:40 +00:00
CPU Ranking Bot
1d90a13bc2 更新CPU性能排行榜数据 - 2025-11-08 01:39:01 2025-11-08 01:39:01 +00:00
CPU Ranking Bot
4def19a43e 更新CPU性能排行榜数据 - 2025-11-07 01:38:08 2025-11-07 01:38:08 +00:00
CPU Ranking Bot
4a0fe0e9f6 更新CPU性能排行榜数据 - 2025-11-06 01:38:24 2025-11-06 01:38:24 +00:00
CPU Ranking Bot
623d0c2ec6 更新CPU性能排行榜数据 - 2025-11-05 01:37:44 2025-11-05 01:37:44 +00:00
CPU Ranking Bot
ad656b865f 更新CPU性能排行榜数据 - 2025-11-04 01:38:37 2025-11-04 01:38:37 +00:00
CPU Ranking Bot
44428ec197 更新CPU性能排行榜数据 - 2025-11-03 01:39:35 2025-11-03 01:39:35 +00:00
CPU Ranking Bot
c29c9775db 更新CPU性能排行榜数据 - 2025-11-02 01:39:27 2025-11-02 01:39:27 +00:00
spiritlhl
e785c01a60 Revise README file content and structure
Updated file descriptions and links in README.
2025-11-01 22:31:16 +08:00
CPU Ranking Bot
de3bbdb7ff 更新CPU性能排行榜数据 - 2025-11-01 01:37:46 2025-11-01 01:37:46 +00:00
CPU Ranking Bot
a075c8cf3d 更新CPU性能排行榜数据 - 2025-10-31 01:38:10 2025-10-31 01:38:10 +00:00
CPU Ranking Bot
3b72b4c86e 更新CPU性能排行榜数据 - 2025-10-30 01:38:49 2025-10-30 01:38:50 +00:00
CPU Ranking Bot
3aed20fe37 更新CPU性能排行榜数据 - 2025-10-29 01:39:50 2025-10-29 01:39:50 +00:00
CPU Ranking Bot
02da203b50 更新CPU性能排行榜数据 - 2025-10-28 01:37:52 2025-10-28 01:37:52 +00:00
CPU Ranking Bot
b427078db8 更新CPU性能排行榜数据 - 2025-10-27 01:40:13 2025-10-27 01:40:13 +00:00
CPU Ranking Bot
f433a4b117 更新CPU性能排行榜数据 - 2025-10-26 01:39:37 2025-10-26 01:39:37 +00:00
CPU Ranking Bot
425abcd206 更新CPU性能排行榜数据 - 2025-10-25 01:38:50 2025-10-25 01:38:50 +00:00
CPU Ranking Bot
fe48a944c8 更新CPU性能排行榜数据 - 2025-10-24 01:39:36 2025-10-24 01:39:36 +00:00
CPU Ranking Bot
c5d2527763 更新CPU性能排行榜数据 - 2025-10-23 01:40:02 2025-10-23 01:40:02 +00:00
CPU Ranking Bot
22e60b4124 更新CPU性能排行榜数据 - 2025-10-22 01:37:43 2025-10-22 01:37:43 +00:00
CPU Ranking Bot
8f1f71809f 更新CPU性能排行榜数据 - 2025-10-21 01:39:36 2025-10-21 01:39:36 +00:00
CPU Ranking Bot
d20523371c 更新CPU性能排行榜数据 - 2025-10-20 01:39:41 2025-10-20 01:39:41 +00:00
CPU Ranking Bot
a046238dbc 更新CPU性能排行榜数据 - 2025-10-19 01:37:48 2025-10-19 01:37:48 +00:00
CPU Ranking Bot
c917036cb6 更新CPU性能排行榜数据 - 2025-10-18 01:38:51 2025-10-18 01:38:51 +00:00
CPU Ranking Bot
7736554620 更新CPU性能排行榜数据 - 2025-10-17 01:40:07 2025-10-17 01:40:07 +00:00
CPU Ranking Bot
b8502d5886 更新CPU性能排行榜数据 - 2025-10-16 01:38:43 2025-10-16 01:38:43 +00:00
CPU Ranking Bot
b5393f0454 更新CPU性能排行榜数据 - 2025-10-15 01:39:59 2025-10-15 01:39:59 +00:00
CPU Ranking Bot
eb9043856a 更新CPU性能排行榜数据 - 2025-10-14 01:39:36 2025-10-14 01:39:36 +00:00
CPU Ranking Bot
11f589b622 更新CPU性能排行榜数据 - 2025-10-13 01:38:33 2025-10-13 01:38:33 +00:00
CPU Ranking Bot
7997f83ba5 更新CPU性能排行榜数据 - 2025-10-12 01:39:36 2025-10-12 01:39:36 +00:00
CPU Ranking Bot
6c555095b3 更新CPU性能排行榜数据 - 2025-10-11 01:39:55 2025-10-11 01:39:55 +00:00
CPU Ranking Bot
41954b9522 更新CPU性能排行榜数据 - 2025-10-10 01:38:42 2025-10-10 01:38:42 +00:00
CPU Ranking Bot
4994b2b8af 更新CPU性能排行榜数据 - 2025-10-09 01:39:56 2025-10-09 01:39:56 +00:00
CPU Ranking Bot
d26bb65377 更新CPU性能排行榜数据 - 2025-10-08 01:39:41 2025-10-08 01:39:41 +00:00
CPU Ranking Bot
b030a1f2ed 更新CPU性能排行榜数据 - 2025-10-07 01:39:19 2025-10-07 01:39:19 +00:00
CPU Ranking Bot
5edb53d957 更新CPU性能排行榜数据 - 2025-10-06 01:39:47 2025-10-06 01:39:47 +00:00
CPU Ranking Bot
2dd100b1da 更新CPU性能排行榜数据 - 2025-10-05 01:39:59 2025-10-05 01:39:59 +00:00
CPU Ranking Bot
310fbb676e 更新CPU性能排行榜数据 - 2025-10-04 01:39:23 2025-10-04 01:39:23 +00:00
CPU Ranking Bot
8db6aaf026 更新CPU性能排行榜数据 - 2025-10-03 01:39:58 2025-10-03 01:39:59 +00:00
CPU Ranking Bot
f2c80c30a6 更新CPU性能排行榜数据 - 2025-10-02 01:39:41 2025-10-02 01:39:41 +00:00
CPU Ranking Bot
e422765293 更新CPU性能排行榜数据 - 2025-10-01 01:39:18 2025-10-01 01:39:18 +00:00
CPU Ranking Bot
338491c585 更新CPU性能排行榜数据 - 2025-09-30 01:40:00 2025-09-30 01:40:00 +00:00
CPU Ranking Bot
e0fc9f7f0f 更新CPU性能排行榜数据 - 2025-09-29 01:39:29 2025-09-29 01:39:29 +00:00
CPU Ranking Bot
8b57282d7f 更新CPU性能排行榜数据 - 2025-09-28 01:38:22 2025-09-28 01:38:22 +00:00
CPU Ranking Bot
8394f3a78d 更新CPU性能排行榜数据 - 2025-09-27 01:39:10 2025-09-27 01:39:10 +00:00
CPU Ranking Bot
01e6bac876 更新CPU性能排行榜数据 - 2025-09-26 01:39:46 2025-09-26 01:39:46 +00:00
CPU Ranking Bot
5a0bba48ae 更新CPU性能排行榜数据 - 2025-09-25 01:38:16 2025-09-25 01:38:16 +00:00
CPU Ranking Bot
49a4ef0543 更新CPU性能排行榜数据 - 2025-09-24 01:39:17 2025-09-24 01:39:17 +00:00
CPU Ranking Bot
8f74567cbf 更新CPU性能排行榜数据 - 2025-09-23 01:39:31 2025-09-23 01:39:32 +00:00
CPU Ranking Bot
0f01bd359d 更新CPU性能排行榜数据 - 2025-09-22 01:38:08 2025-09-22 01:38:08 +00:00
spiritlhl
d40bc77ba9 Update index.html 2025-09-21 23:51:02 +08:00
CPU Ranking Bot
c730aa59b4 更新CPU性能排行榜数据 - 2025-09-21 15:01:53 2025-09-21 15:01:53 +00:00
CPU Ranking Bot
8a77cff853 更新CPU性能排行榜数据 - 2025-09-21 01:39:15 2025-09-21 01:39:15 +00:00
spiritlhl
8aaa05d45f fix 2025-09-20 19:40:42 +08:00
CPU Ranking Bot
7e7b65c2b7 更新CPU性能排行榜数据 - 2025-09-20 01:39:29 2025-09-20 01:39:29 +00:00
spiritlhl
65296157d9 Merge pull request #14 from oneclickvirt/copilot/fix-4e1e0c95-b391-49d3-b126-a1c59ac93663
Fix index.html JSON loading with CDN fallback mechanism
2025-09-20 00:34:26 +08:00
copilot-swe-agent[bot]
61d1607366 Fix index.html JSON loading with CDN fallback and data transformation
Co-authored-by: spiritLHLS <103393591+spiritLHLS@users.noreply.github.com>
2025-09-19 16:30:52 +00:00
copilot-swe-agent[bot]
8533cfd108 Initial plan 2025-09-19 16:23:22 +00:00
CPU Ranking Bot
8512f5e34b 更新CPU性能排行榜数据 - 2025-09-19 16:19:22 2025-09-19 16:19:22 +00:00
spiritlhl
2c509714a5 fix: 升级版本 2025-09-20 00:10:50 +08:00
CPU Ranking Bot
a042b66c1e 更新CPU性能排行榜数据 - 2025-09-19 01:38:03 2025-09-19 01:38:03 +00:00
CPU Ranking Bot
5edf6ca24d 更新CPU性能排行榜数据 - 2025-09-18 01:38:56 2025-09-18 01:38:56 +00:00
CPU Ranking Bot
adf6fcbcd9 更新CPU性能排行榜数据 - 2025-09-17 01:39:14 2025-09-17 01:39:14 +00:00
CPU Ranking Bot
c5445ed679 更新CPU性能排行榜数据 - 2025-09-16 01:39:22 2025-09-16 01:39:22 +00:00
CPU Ranking Bot
a3c116a25d 更新CPU性能排行榜数据 - 2025-09-15 01:39:15 2025-09-15 01:39:15 +00:00
CPU Ranking Bot
84f2b41b05 更新CPU性能排行榜数据 - 2025-09-14 01:38:54 2025-09-14 01:38:54 +00:00
CPU Ranking Bot
fca661388d 更新CPU性能排行榜数据 - 2025-09-13 01:38:59 2025-09-13 01:38:59 +00:00
CPU Ranking Bot
e5cf95b05f 更新CPU性能排行榜数据 - 2025-09-12 01:38:49 2025-09-12 01:38:49 +00:00
CPU Ranking Bot
dbceca7169 更新CPU性能排行榜数据 - 2025-09-11 01:39:31 2025-09-11 01:39:31 +00:00
CPU Ranking Bot
86b28ef383 更新CPU性能排行榜数据 - 2025-09-10 01:38:43 2025-09-10 01:38:43 +00:00
CPU Ranking Bot
c1a8695149 更新CPU性能排行榜数据 - 2025-09-09 01:38:44 2025-09-09 01:38:44 +00:00
CPU Ranking Bot
ebcf2da2d8 更新CPU性能排行榜数据 - 2025-09-08 01:38:47 2025-09-08 01:38:47 +00:00
CPU Ranking Bot
a7207afb50 更新CPU性能排行榜数据 - 2025-09-07 01:38:39 2025-09-07 01:38:39 +00:00
CPU Ranking Bot
5aeaa0218a 更新CPU性能排行榜数据 - 2025-09-06 01:38:49 2025-09-06 01:38:49 +00:00
CPU Ranking Bot
8ac7230b06 更新CPU性能排行榜数据 - 2025-09-05 01:39:08 2025-09-05 01:39:08 +00:00
CPU Ranking Bot
3c468794a3 更新CPU性能排行榜数据 - 2025-09-04 01:38:50 2025-09-04 01:38:50 +00:00
CPU Ranking Bot
c764f60de9 更新CPU性能排行榜数据 - 2025-09-03 01:39:25 2025-09-03 01:39:25 +00:00
CPU Ranking Bot
850c3cb129 更新CPU性能排行榜数据 - 2025-09-02 01:38:52 2025-09-02 01:38:52 +00:00
CPU Ranking Bot
cee4e66ad0 更新CPU性能排行榜数据 - 2025-09-01 01:38:49 2025-09-01 01:38:49 +00:00
CPU Ranking Bot
4cb5c67183 更新CPU性能排行榜数据 - 2025-08-31 01:38:49 2025-08-31 01:38:49 +00:00
CPU Ranking Bot
ff040b1ed2 更新CPU性能排行榜数据 - 2025-08-30 01:38:23 2025-08-30 01:38:24 +00:00
CPU Ranking Bot
d739f44bf3 更新CPU性能排行榜数据 - 2025-08-29 01:39:12 2025-08-29 01:39:12 +00:00
CPU Ranking Bot
2be35bdffe 更新CPU性能排行榜数据 - 2025-08-28 01:37:16 2025-08-28 01:37:17 +00:00
CPU Ranking Bot
10e0d09c6f 更新CPU性能排行榜数据 - 2025-08-27 01:38:54 2025-08-27 01:38:54 +00:00
CPU Ranking Bot
6616da58cd 更新CPU性能排行榜数据 - 2025-08-26 01:38:47 2025-08-26 01:38:47 +00:00
CPU Ranking Bot
afa684f042 更新CPU性能排行榜数据 - 2025-08-25 01:38:28 2025-08-25 01:38:28 +00:00
CPU Ranking Bot
25c1d60d2b 更新CPU性能排行榜数据 - 2025-08-24 01:38:31 2025-08-24 01:38:31 +00:00
CPU Ranking Bot
f4beb22fb2 更新CPU性能排行榜数据 - 2025-08-23 01:38:30 2025-08-23 01:38:30 +00:00
CPU Ranking Bot
df9e4d2a78 更新CPU性能排行榜数据 - 2025-08-22 01:38:41 2025-08-22 01:38:41 +00:00
CPU Ranking Bot
4facdad7b5 更新CPU性能排行榜数据 - 2025-08-21 01:38:35 2025-08-21 01:38:35 +00:00
CPU Ranking Bot
5faea697b0 更新CPU性能排行榜数据 - 2025-08-20 01:38:34 2025-08-20 01:38:34 +00:00
CPU Ranking Bot
07d09e4b41 更新CPU性能排行榜数据 - 2025-08-19 01:38:18 2025-08-19 01:38:18 +00:00
CPU Ranking Bot
eb41920720 更新CPU性能排行榜数据 - 2025-08-18 01:38:17 2025-08-18 01:38:17 +00:00
CPU Ranking Bot
7a4208ecd3 更新CPU性能排行榜数据 - 2025-08-17 01:38:09 2025-08-17 01:38:09 +00:00
CPU Ranking Bot
416170ec30 更新CPU性能排行榜数据 - 2025-08-16 01:38:20 2025-08-16 01:38:20 +00:00
CPU Ranking Bot
e26fa8312f 更新CPU性能排行榜数据 - 2025-08-15 01:38:39 2025-08-15 01:38:39 +00:00
CPU Ranking Bot
288a71f13f 更新CPU性能排行榜数据 - 2025-08-14 01:38:10 2025-08-14 01:38:10 +00:00
CPU Ranking Bot
ea1b982c0d 更新CPU性能排行榜数据 - 2025-08-13 01:38:07 2025-08-13 01:38:07 +00:00
CPU Ranking Bot
005bcd750d 更新CPU性能排行榜数据 - 2025-08-12 01:38:14 2025-08-12 01:38:14 +00:00
CPU Ranking Bot
d08008a51e 更新CPU性能排行榜数据 - 2025-08-11 01:39:09 2025-08-11 01:39:09 +00:00
CPU Ranking Bot
a5153cbdb4 更新CPU性能排行榜数据 - 2025-08-10 01:38:38 2025-08-10 01:38:38 +00:00
CPU Ranking Bot
c20bcc2515 更新CPU性能排行榜数据 - 2025-08-09 01:38:19 2025-08-09 01:38:19 +00:00
CPU Ranking Bot
691e934aa9 更新CPU性能排行榜数据 - 2025-08-08 01:38:32 2025-08-08 01:38:32 +00:00
CPU Ranking Bot
578fc5338f 更新CPU性能排行榜数据 - 2025-08-07 01:38:31 2025-08-07 01:38:31 +00:00
CPU Ranking Bot
48c6d76863 更新CPU性能排行榜数据 - 2025-08-06 01:38:10 2025-08-06 01:38:10 +00:00
CPU Ranking Bot
ed69abb382 更新CPU性能排行榜数据 - 2025-08-05 01:38:22 2025-08-05 01:38:22 +00:00
CPU Ranking Bot
c807be29d8 更新CPU性能排行榜数据 - 2025-08-04 01:38:38 2025-08-04 01:38:38 +00:00
CPU Ranking Bot
8554b9184a 更新CPU性能排行榜数据 - 2025-08-03 01:38:16 2025-08-03 01:38:16 +00:00
CPU Ranking Bot
e787c934c0 更新CPU性能排行榜数据 - 2025-08-02 01:38:11 2025-08-02 01:38:11 +00:00
CPU Ranking Bot
a363ee6ee3 更新CPU性能排行榜数据 - 2025-08-01 01:38:37 2025-08-01 01:38:37 +00:00
CPU Ranking Bot
ec731a4775 更新CPU性能排行榜数据 - 2025-07-31 01:37:55 2025-07-31 01:37:55 +00:00
CPU Ranking Bot
cb8d8226b6 更新CPU性能排行榜数据 - 2025-07-30 01:38:14 2025-07-30 01:38:14 +00:00
CPU Ranking Bot
6bd0a1c02d 更新CPU性能排行榜数据 - 2025-07-29 01:38:09 2025-07-29 01:38:09 +00:00
CPU Ranking Bot
a86f7c4d6c 更新CPU性能排行榜数据 - 2025-07-28 01:38:11 2025-07-28 01:38:11 +00:00
CPU Ranking Bot
12c5ece7bf 更新CPU性能排行榜数据 - 2025-07-27 01:37:50 2025-07-27 01:37:50 +00:00
CPU Ranking Bot
f34e182ae4 更新CPU性能排行榜数据 - 2025-07-26 01:38:13 2025-07-26 01:38:13 +00:00
CPU Ranking Bot
9f3d1a1960 更新CPU性能排行榜数据 - 2025-07-25 01:38:15 2025-07-25 01:38:15 +00:00
CPU Ranking Bot
c19b29e88a 更新CPU性能排行榜数据 - 2025-07-24 01:38:04 2025-07-24 01:38:04 +00:00
CPU Ranking Bot
2ccf5a9b09 更新CPU性能排行榜数据 - 2025-07-23 01:38:07 2025-07-23 01:38:07 +00:00
CPU Ranking Bot
8d1a1c8b3b 更新CPU性能排行榜数据 - 2025-07-22 01:37:39 2025-07-22 01:37:39 +00:00
CPU Ranking Bot
c65b236a89 更新CPU性能排行榜数据 - 2025-07-21 01:56:23 2025-07-21 01:56:23 +00:00
CPU Ranking Bot
c78ba25f02 更新CPU性能排行榜数据 - 2025-07-21 01:40:26 2025-07-21 01:40:26 +00:00
CPU Ranking Bot
0a406e744e 更新CPU性能排行榜数据 - 2025-07-20 01:37:50 2025-07-20 01:37:50 +00:00
CPU Ranking Bot
acb8699211 更新CPU性能排行榜数据 - 2025-07-19 01:37:45 2025-07-19 01:37:45 +00:00
CPU Ranking Bot
3b2493f770 更新CPU性能排行榜数据 - 2025-07-18 01:37:59 2025-07-18 01:37:59 +00:00
CPU Ranking Bot
af6bda9b86 更新CPU性能排行榜数据 - 2025-07-17 01:37:42 2025-07-17 01:37:42 +00:00
CPU Ranking Bot
4608e1bf3a 更新CPU性能排行榜数据 - 2025-07-16 01:37:47 2025-07-16 01:37:47 +00:00
CPU Ranking Bot
5f3c257158 更新CPU性能排行榜数据 - 2025-07-15 01:37:57 2025-07-15 01:37:57 +00:00
CPU Ranking Bot
441b4d28ef 更新CPU性能排行榜数据 - 2025-07-14 01:37:39 2025-07-14 01:37:39 +00:00
CPU Ranking Bot
6c2c2a6791 更新CPU性能排行榜数据 - 2025-07-13 13:13:21 2025-07-13 13:13:21 +00:00
CPU Ranking Bot
4eb982a22d 更新CPU性能排行榜数据 - 2025-07-13 12:59:41 2025-07-13 12:59:41 +00:00
CPU Ranking Bot
88c50e3c1c 更新CPU性能排行榜数据 - 2025-07-13 12:24:59 2025-07-13 12:24:59 +00:00
CPU Ranking Bot
c272da23fb 更新CPU性能排行榜数据 - 2025-07-13 12:12:35 2025-07-13 12:12:35 +00:00
CPU Ranking Bot
094b05ffbd 更新CPU性能排行榜数据 - 2025-07-13 12:03:36 2025-07-13 12:03:36 +00:00
spiritlhl
63d2c2be15 fix 2025-07-13 11:58:57 +00:00
CPU Ranking Bot
9d540c1f94 更新CPU性能排行榜数据 - 2025-07-13 11:49:55 2025-07-13 11:49:55 +00:00
CPU Ranking Bot
f9b588007f 更新CPU性能排行榜数据 - 2025-07-13 01:35:39 2025-07-13 01:35:39 +00:00
CPU Ranking Bot
cb261032fd 更新CPU性能排行榜数据 - 2025-07-12 01:35:40 2025-07-12 01:35:40 +00:00
CPU Ranking Bot
1fca387a39 更新CPU性能排行榜数据 - 2025-07-11 01:35:52 2025-07-11 01:35:52 +00:00
CPU Ranking Bot
370695c670 更新CPU性能排行榜数据 - 2025-07-10 01:35:32 2025-07-10 01:35:32 +00:00
CPU Ranking Bot
fa0cb5f194 更新CPU性能排行榜数据 - 2025-07-09 01:35:25 2025-07-09 01:35:25 +00:00
CPU Ranking Bot
ba9a9e7cbd 更新CPU性能排行榜数据 - 2025-07-08 01:35:25 2025-07-08 01:35:25 +00:00
CPU Ranking Bot
66d907a68a 更新CPU性能排行榜数据 - 2025-07-07 01:35:43 2025-07-07 01:35:43 +00:00
CPU Ranking Bot
8f99d36b4f 更新CPU性能排行榜数据 - 2025-07-06 01:35:23 2025-07-06 01:35:23 +00:00
CPU Ranking Bot
76af1e9600 更新CPU性能排行榜数据 - 2025-07-05 01:35:28 2025-07-05 01:35:28 +00:00
CPU Ranking Bot
6044e328f2 更新CPU性能排行榜数据 - 2025-07-04 01:35:32 2025-07-04 01:35:32 +00:00
CPU Ranking Bot
fca03354bb 更新CPU性能排行榜数据 - 2025-07-03 01:35:27 2025-07-03 01:35:27 +00:00
CPU Ranking Bot
795eaede40 更新CPU性能排行榜数据 - 2025-07-02 01:35:25 2025-07-02 01:35:25 +00:00
CPU Ranking Bot
2b7991df96 更新CPU性能排行榜数据 - 2025-07-01 01:35:31 2025-07-01 01:35:31 +00:00
CPU Ranking Bot
90d664194b 更新CPU性能排行榜数据 - 2025-06-30 01:34:40 2025-06-30 01:34:40 +00:00
CPU Ranking Bot
fd62156564 更新CPU性能排行榜数据 - 2025-06-29 01:35:17 2025-06-29 01:35:17 +00:00
CPU Ranking Bot
ebd581306c 更新CPU性能排行榜数据 - 2025-06-28 01:35:19 2025-06-28 01:35:19 +00:00
CPU Ranking Bot
695f13a129 更新CPU性能排行榜数据 - 2025-06-27 16:12:17 2025-06-27 16:12:17 +00:00
CPU Ranking Bot
3941537061 更新CPU性能排行榜数据 - 2025-06-27 01:34:38 2025-06-27 01:34:38 +00:00
CPU Ranking Bot
daf8f65c12 更新CPU性能排行榜数据 - 2025-06-26 01:34:31 2025-06-26 01:34:31 +00:00
CPU Ranking Bot
06addade41 更新CPU性能排行榜数据 - 2025-06-25 01:34:38 2025-06-25 01:34:38 +00:00
CPU Ranking Bot
8fda92a19c 更新CPU性能排行榜数据 - 2025-06-24 01:34:34 2025-06-24 01:34:34 +00:00
CPU Ranking Bot
03b7c1541c 更新CPU性能排行榜数据 - 2025-06-23 01:34:32 2025-06-23 01:34:32 +00:00
CPU Ranking Bot
7273321854 更新CPU性能排行榜数据 - 2025-06-22 01:34:27 2025-06-22 01:34:27 +00:00
CPU Ranking Bot
0556680e61 更新CPU性能排行榜数据 - 2025-06-21 01:34:29 2025-06-21 01:34:29 +00:00
CPU Ranking Bot
805809795e 更新CPU性能排行榜数据 - 2025-06-20 01:34:30 2025-06-20 01:34:30 +00:00
CPU Ranking Bot
e7ac550065 更新CPU性能排行榜数据 - 2025-06-19 01:34:34 2025-06-19 01:34:34 +00:00
CPU Ranking Bot
2be8e0ee88 更新CPU性能排行榜数据 - 2025-06-18 01:34:23 2025-06-18 01:34:23 +00:00
CPU Ranking Bot
e338f1a512 更新CPU性能排行榜数据 - 2025-06-17 01:34:31 2025-06-17 01:34:31 +00:00
CPU Ranking Bot
48023f9ed1 更新CPU性能排行榜数据 - 2025-06-16 01:34:27 2025-06-16 01:34:27 +00:00
CPU Ranking Bot
7f9fad3fea 更新CPU性能排行榜数据 - 2025-06-15 15:32:26 2025-06-15 15:32:26 +00:00
CPU Ranking Bot
d5d139e218 更新CPU性能排行榜数据 - 2025-06-15 14:21:56 2025-06-15 14:21:56 +00:00
CPU Ranking Bot
35e0fd426d 更新CPU性能排行榜数据 - 2025-06-15 12:37:39 2025-06-15 12:37:40 +00:00
CPU Ranking Bot
7bf141e303 更新CPU性能排行榜数据 - 2025-06-15 12:29:09 2025-06-15 12:29:09 +00:00
CPU Ranking Bot
12dbceca0d 更新CPU性能排行榜数据 - 2025-06-15 10:27:06 2025-06-15 10:27:06 +00:00
CPU Ranking Bot
e07d8b5114 更新CPU性能排行榜数据 - 2025-06-15 10:18:15 2025-06-15 10:18:15 +00:00
CPU Ranking Bot
0ceb45d17c 更新CPU性能排行榜数据 - 2025-06-15 10:02:38 2025-06-15 10:02:38 +00:00
spiritlhl
0bbfa95b9c 删除无效内容 2025-06-15 09:58:05 +00:00
CPU Ranking Bot
f927185459 更新CPU性能排行榜数据 - 2025-06-15 09:53:42 2025-06-15 09:53:42 +00:00
spiritlhl
c8271b713c Create CNAME 2025-06-15 17:50:16 +08:00
CPU Ranking Bot
27f3bad9ef 更新CPU性能排行榜数据 - 2025-06-15 09:47:49 2025-06-15 09:47:49 +00:00
spiritlhl
dd85b0fd2d Create CNAME 2025-06-15 17:47:09 +08:00
CPU Ranking Bot
47fdb43e54 更新CPU性能排行榜数据 - 2025-06-15 09:30:31 2025-06-15 09:30:31 +00:00
CPU Ranking Bot
6e19ab79d7 更新CPU性能排行榜数据 - 2025-06-15 09:21:06 2025-06-15 09:21:07 +00:00
CPU Ranking Bot
dee3211d3b 更新CPU性能排行榜数据 - 2025-06-15 09:17:35 2025-06-15 09:17:35 +00:00
40 changed files with 21660 additions and 10524 deletions

View File

@@ -1,310 +0,0 @@
#!/usr/bin/env python3
"""
Script to create public branch by removing security dependencies and references.
This script properly handles Go file modifications to ensure the code can compile.
"""
import re
import os
import sys
import shutil
def read_file(filepath):
"""Read file content."""
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
def write_file(filepath, content):
"""Write content to file."""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
def modify_go_mod(filepath):
"""
Modify go.mod to remove privatespeedtest (and optional security) dependencies.
Automatically matches module names regardless of version or indirect comment.
"""
content = read_file(filepath)
# Modules to remove
remove_modules = [
r'github\.com/oneclickvirt/privatespeedtest',
r'github\.com/oneclickvirt/security',
]
for mod in remove_modules:
# Remove full require line (with or without // indirect)
content = re.sub(
rf'^[ \t]*{mod}[ \t]+v[^\s]+(?:[ \t]+// indirect)?[ \t]*\n',
'',
content,
flags=re.MULTILINE
)
write_file(filepath, content)
print(f"✓ Removed privatespeedtest/security from {filepath}")
def remove_code_block(lines, start_marker, end_condition='empty_line'):
"""
Remove code block from lines starting with start_marker.
Args:
lines: List of file lines
start_marker: String or list of strings to identify block start
end_condition: 'empty_line' (default) or 'closing_brace' or custom function
Returns:
Modified lines with the block removed
"""
if isinstance(start_marker, str):
start_marker = [start_marker]
result = []
skip_mode = False
brace_depth = 0
i = 0
while i < len(lines):
line = lines[i]
# Check if we should start skipping
if not skip_mode:
for marker in start_marker:
if marker in line:
skip_mode = True
if end_condition == 'closing_brace':
# Count opening braces on the function declaration line
brace_depth = line.count('{') - line.count('}')
break
if not skip_mode:
result.append(line)
else:
# We're in skip mode
if end_condition == 'empty_line':
# Skip until we find an empty line
if line.strip() == '':
skip_mode = False
# Don't add the empty line, continue to next
elif end_condition == 'closing_brace':
# Track brace depth
brace_depth += line.count('{') - line.count('}')
if brace_depth == 0 and '}' in line:
# Function ended, skip until next empty line
end_condition = 'empty_line'
i += 1
return result
def modify_speed_go(filepath):
"""
Remove privatespeedtest-related code from speed.go.
Uses line-by-line processing for reliability.
"""
content = read_file(filepath)
lines = content.split('\n')
# Remove specific code blocks by their comment markers
blocks_to_remove = [
'// formatString 格式化字符串到指定宽度',
'// printTableRow 打印表格行',
'// privateSpeedTest 使用 privatespeedtest 进行单个运营商测速',
'// privateSpeedTestWithFallback 使用私有测速,如果失败则回退到 global 节点',
'// 对于三网测速cmcc、cu、ct和 other优先使用 privatespeedtest 进行私有测速',
]
for block_marker in blocks_to_remove:
lines = remove_code_block(lines, block_marker)
# Reconstruct content
content = '\n'.join(lines)
# Remove privatespeedtest import
content = re.sub(
r'\n\s*"github\.com/oneclickvirt/privatespeedtest/pst"\s*\n',
'\n',
content,
flags=re.MULTILINE
)
# Remove time import (only used by privatespeedtest)
content = re.sub(
r'\n\s*"time"\s*\n',
'\n',
content,
flags=re.MULTILINE
)
# Clean up multiple consecutive empty lines (optional)
content = re.sub(r'\n{3,}', '\n\n', content)
write_file(filepath, content)
print(f"✓ Removed privatespeedtest from {filepath}")
def modify_utils_go(filepath):
"""
Modify utils/utils.go to:
1. Replace security/network import with basics/network
2. Replace SecurityUploadToken usage with hardcoded token
"""
content = read_file(filepath)
# Replace import
content = re.sub(
r'"github\.com/oneclickvirt/security/network"',
r'"github.com/oneclickvirt/basics/network"',
content
)
# Replace token usage - find the exact line and replace it
content = re.sub(
r'\ttoken := network\.SecurityUploadToken',
r'\ttoken := "OvwKx5qgJtf7PZgCKbtyojSU.MTcwMTUxNzY1MTgwMw"',
content
)
# Update title for public version
content = re.sub(
r'VPS融合怪测试',
r'VPS融合怪测试(非官方编译)',
content
)
content = re.sub(
r'VPS Fusion Monster Test',
r'VPS Fusion Monster Test (Unofficial)',
content
)
write_file(filepath, content)
print(f"✓ Modified {filepath}")
def modify_params_go(filepath):
"""
Modify internal/params/params.go to change security flag default to false.
"""
content = read_file(filepath)
# Change default value in struct initialization
content = re.sub(
r'(\s+SecurityTestStatus:\s+)true,',
r'\1false,',
content
)
# Change flag default value
content = re.sub(
r'(c\.GoecsFlag\.BoolVar\(&c\.SecurityTestStatus, "security", )true(, "Enable/Disable security test"\))',
r'\1false\2',
content
)
write_file(filepath, content)
print(f"✓ Modified {filepath}")
def modify_readme(filepath, is_english=False):
"""
Modify README files to update Go version and security status.
"""
content = read_file(filepath)
# Extract Go version from go.mod
go_mod_content = read_file('go.mod')
go_version_match = re.search(r'^go (\d+\.\d+(?:\.\d+)?)', go_mod_content, re.MULTILINE)
if not go_version_match:
print(f"⚠ Warning: Could not extract Go version from go.mod")
return
go_version = go_version_match.group(1)
if is_english:
# Update Go version in English README
content = re.sub(
r'Select go \d+\.\d+\.\d+ version to install',
f'Select go {go_version} version to install',
content
)
# Update security status
content = re.sub(
r', binary files compiled in \[securityCheck\][^\)]*\)',
', but open sourced',
content
)
# Update help text for security flag
content = re.sub(
r'security\s+Enable/Disable security test \(default true\)',
'security Enable/Disable security test (default false)',
content
)
else:
# Update Go version in Chinese README
content = re.sub(
r'选择 go \d+\.\d+\.\d+ 的版本进行安装',
f'选择 go {go_version} 的版本进行安装',
content
)
# Update security status
content = re.sub(
r'二进制文件编译至 \[securityCheck\][^\)]*\)',
'但已开源',
content
)
# Update help text for security flag
content = re.sub(
r'security\s+Enable/Disable security test \(default true\)',
'security Enable/Disable security test (default false)',
content
)
write_file(filepath, content)
print(f"✓ Modified {filepath}")
def main():
"""Main function to process all files."""
print("Starting public branch creation process...")
print()
# Check if we're in the right directory
if not os.path.exists('go.mod'):
print("Error: go.mod not found. Please run this script from the project root.")
sys.exit(1)
# Modify Go source files
print("Modifying Go source files...")
modify_speed_go('internal/tests/speed.go')
modify_utils_go('utils/utils.go')
modify_params_go('internal/params/params.go')
print()
# Modify go.mod
print("Modifying go.mod...")
modify_go_mod('go.mod')
print()
# Modify README files
print("Modifying README files...")
modify_readme('README_ZH.md', is_english=False)
modify_readme('README.md', is_english=True)
print()
print("✓ All modifications completed successfully!")
print()
print("Next steps:")
print("1. Run 'go mod tidy' to clean up dependencies")
print("2. Run 'go build -o maintest' to verify compilation")
print("3. Test the binary with: ./maintest -menu=false -l en -security=false -upload=false")
if __name__ == '__main__':
main()

View File

@@ -1,99 +0,0 @@
name: Build and Release
on:
workflow_dispatch:
tags:
- "v*.*.*"
jobs:
goreleaser:
runs-on: ubuntu-latest
container:
# 1.20 是 Windows 7/8 Server 2008/2012 最后一个支持版本
image: goreleaser/goreleaser-cross:v1.20
steps:
- run: |
git config --global --add safe.directory /__w/ecs/ecs
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: false
swap-storage: false
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.25.4
# - name: Install UPX
# run: |
# apk add --no-cache upx
- name: Configure Git for Private Modules
run: |
git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/"
git config --global url."git@github.com:".insteadOf "https://github.com/"
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
- name: Clean Go cache before build
run: |
go clean -cache -modcache -testcache
df -h
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
# version: latest
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GHT }}
GOPRIVATE: github.com/oneclickvirt/security,github.com/oneclickvirt/privatespeedtest
- name: Update goecs.sh with new version
run: |
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
else
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.37")
fi
echo "Using version: $VERSION"
FILE="goecs.sh"
BRANCH="master"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global --unset url."git@github.com:".insteadOf || true
git fetch origin $BRANCH
git checkout $BRANCH
if [ ! -f "$FILE" ]; then
echo "Error: $FILE not found"
exit 1
fi
sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "$FILE"
sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "$FILE"
if git diff --quiet "$FILE"; then
echo "No changes detected in $FILE"
exit 0
fi
git add "$FILE"
git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh"
git push origin $BRANCH
env:
GITHUB_TOKEN: ${{ secrets.GHT }}

View File

@@ -1,61 +0,0 @@
name: Build and Push Docker Image
on:
workflow_run:
workflows: ["Build and Release"]
types:
- completed
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# - name: Login to CNB Registry
# uses: docker/login-action@v2
# with:
# registry: ${{ secrets.CNB_DOCKER_REGISTRY }}
# username: ${{ secrets.CNB_USERNAME }}
# password: ${{ secrets.CNB_TOKEN }}
- name: Login to Aliyun Container Registry
uses: docker/login-action@v2
with:
registry: crpi-8tmognxgyb86bm61.cn-guangzhou.personal.cr.aliyuncs.com
username: ${{ secrets.ALIYUN_USERNAME }}
password: ${{ secrets.ALIYUN_PASSWORD }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker images
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/riscv64
# linux/mips,linux/mipsle 暂不支持 alpine, linux/s390x 编译卡死cnb组织空间不足无法推送
# ${{ secrets.CNB_DOCKER_REGISTRY }}/oneclickvirt/ecs:latest
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/goecs:latest
crpi-8tmognxgyb86bm61.cn-guangzhou.personal.cr.aliyuncs.com/oneclickvirt/ecs:latest
ghcr.io/${{ github.repository_owner }}/goecs:latest

View File

@@ -1,84 +0,0 @@
name: Public Build
on:
workflow_run:
workflows: ["Build and Release"]
types:
- completed
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25.4'
- name: Update master branch README files
run: |
git config --global user.name 'GitHub Actions'
git config --global user.email 'actions@github.com'
if [ -f "go.mod" ]; then
GO_VERSION=$(grep "^go " go.mod | head -n 1 | awk '{print $2}')
echo "提取到的 Go 版本: $GO_VERSION"
if [ -n "$GO_VERSION" ] && [[ "$GO_VERSION" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
echo "版本验证成功,开始替换..."
if [ -f "README.md" ]; then
sed -i "s/选择 go [0-9]\+\.[0-9]\+\.[0-9]\+ 的版本进行安装/选择 go $GO_VERSION 的版本进行安装/g" README.md
sed -i 's|但二进制文件编译至 \[securityCheck\].*)|但已开源|g' README.md
sed -i 's|security.*Enable/Disable security test (default true)|security Enable/Disable security test (default false)|g' README.md
echo "已更新 README.md"
fi
if [ -f "README_EN.md" ]; then
sed -i "s/Select go [0-9]\+\.[0-9]\+\.[0-9]\+ version to install/Select go $GO_VERSION version to install/g" README_EN.md
sed -i 's|but binary files compiled in \[securityCheck\].*)|but open sourced|g' README_EN.md
sed -i 's|security.*Enable/Disable security test (default true)|security Enable/Disable security test (default false)|g' README_EN.md
echo "已更新 README_EN.md"
fi
git add README.md
[ -f "README_EN.md" ] && git add README_EN.md || true
git commit -m "Auto update README files" || echo "No changes to commit"
git push origin ${{ github.ref_name }}
else
echo "错误:未能提取到有效的 Go 版本号或版本号格式不正确"
exit 1
fi
else
echo "错误:未找到 go.mod 文件"
exit 1
fi
- name: Create public branch
run: |
# 删除本地 public 分支(如果存在)
git branch -D public 2>/dev/null || true
# 基于当前分支创建新的 public 分支(完全覆盖)
git checkout -b public
- name: Remove security package references
run: |
python3 .back/create_public_branch.py
rm -f go.sum
go clean -modcache
go clean -cache -testcache -fuzzcache
go mod tidy
- name: Update Go version in README files
run: |
# This step is now handled by the Python script
echo "README files already updated by create_public_branch.py"
- name: Build and Test
run: |
go build -o maintest
./maintest -menu=false -l en -security=false -upload=false || exit 1
rm -rf maintest
- name: Commit and push changes
run: |
git add .
git commit -m "Auto update public version (no security package)" || echo "No changes to commit"
git push -f origin public

View File

@@ -1,59 +0,0 @@
name: Sync Latest Release
on:
workflow_run:
workflows: ["Build and Release"]
types:
- completed
workflow_dispatch:
jobs:
sync-release:
runs-on: ubuntu-latest
steps:
- name: Checkout source repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get latest release
id: get_release
run: |
echo "RELEASE_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: Create temporary directory
run: |
mkdir -p temp_repo
cd temp_repo
git init
git config --local user.name "GitHub Action"
git config --local user.email "action@github.com"
- name: Copy repository files
run: |
cp goecs.sh temp_repo/
cp README.md temp_repo/
[ -f "README_EN.md" ] && cp README_EN.md temp_repo/ || true
[ -f "README_ZH.md" ] && cp README_ZH.md temp_repo/ || true
cp LICENSE temp_repo/
- name: Download release assets
run: |
cd temp_repo
gh release download ${{ env.RELEASE_TAG }} --repo ${{ github.repository }} --dir .
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Push to target repository
run: |
cd temp_repo
git add .
git commit -m "Sync release ${{ env.RELEASE_TAG }} from ${{ github.repository }}"
git branch -M main
git remote add target https://cnb.cool/oneclickvirt/ecs.git
echo "machine cnb.cool login ${{ secrets.CNB_USERNAME }} password ${{ secrets.CNB_TOKEN }}" > ~/.netrc
chmod 600 ~/.netrc
git push -f target main
env:
CNB_USERNAME: ${{ secrets.CNB_USERNAME }}
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}

5
.gitignore vendored
View File

@@ -1,5 +0,0 @@
vendor/
.idea/
ecs
goecs.txt
*.log

View File

@@ -1,107 +0,0 @@
before:
hooks:
- go mod tidy -v
- go clean -cache
project_name: goecs
builds:
- id: universal
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
goos:
- linux
- windows
- freebsd
goarch:
- arm
- arm64
- 386
- amd64
- mips
- mipsle
- mips64
- mips64le
- ppc64
- ppc64le
- s390x
- riscv64
gomips:
- softfloat
ignore:
- goos: windows
goarch: arm
main: ./
binary: goecs
- id: darwin-amd64
env:
- CGO_ENABLED=1
- CC=o64-clang
- CXX=o64-clang++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
goos:
- darwin
goarch:
- amd64
main: ./
binary: goecs
- id: darwin-arm64
env:
- CGO_ENABLED=1
- CC=oa64-clang
- CXX=oa64-clang++
ldflags:
- -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0
goos:
- darwin
goarch:
- arm64
main: ./
binary: goecs
universal_binaries:
- name_template: "goecs"
replace: false
checksum:
name_template: "checksums.txt"
snapshot:
name_template: "goecs"
archives:
- name_template: "goecs_{{ .Os }}_{{ .Arch }}"
format: zip
files:
- none*
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore"
- Merge pull request
- Merge branch
- go mod tidy
- New translations
upx:
- enabled: true
brute: true
goos:
- linux
- windows
goarch:
- amd64
- 386
- arm64
- ppc64le
- s390x
- riscv64

1
CNAME Normal file
View File

@@ -0,0 +1 @@
sysbench.spiritlhl.net

View File

@@ -1,29 +0,0 @@
# syntax=docker/dockerfile:1
FROM alpine:latest
RUN apk update && apk add --no-cache wget curl bash || \
(echo "Standard repo failed, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main wget curl bash)
RUN apk add --no-cache bind-tools || \
(echo "Standard repo failed for bind-tools, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main bind-tools)
RUN apk add --no-cache grep openssl ca-certificates || \
(echo "Standard repo failed, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main grep openssl ca-certificates)
RUN apk add --no-cache uuidgen || \
apk add --no-cache util-linux || \
(echo "Standard repo failed for uuidgen, trying edge repo..." && \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main uuidgen) || \
apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main util-linux
RUN export noninteractive=true
# 下载并执行 goecs.sh 脚本
RUN curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && \
chmod +x goecs.sh && \
bash goecs.sh env && \
bash goecs.sh install
# 设置 goecs 为入口点
ENTRYPOINT ["goecs"]

674
LICENSE
View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

444
README.md
View File

@@ -1,436 +1,26 @@
# ECS
# CPU性能测试排行榜
[![Build and Release](https://github.com/oneclickvirt/ecs/actions/workflows/build_binary.yaml/badge.svg)](https://github.com/oneclickvirt/ecs/actions/workflows/build_binary.yaml)
本目录包含CPU性能测试的结果和排行榜数据。
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_shield)
## 在线查看
[![Hits](https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false)](https://hits.spiritlhl.net) [![Downloads](https://ghdownload.spiritlhl.net/oneclickvirt/ecs?color=36c600)](https://github.com/oneclickvirt/ecs/releases)
可通过 https://sysbench.spiritlhl.net 访问。
Fusion Monster Evaluation Project - GO Version
## 数据字段说明
(No additional shell file dependencies unless necessary to install the environment using the shell, the environment is installed just to measure more accurately, in extreme cases no environment dependencies can also be fully measured project)
| 字段名 | 说明 |
|--------|------|
| 排名 | 在当前排序中的排名 |
| CPU型号 | 完整的CPU型号信息 |
| CPU核心数 | CPU的核心数量 |
| 单核得分 | 单核性能测试得分 |
| 多核得分 | 多核性能测试得分 |
| 多核线程数 | 多核测试使用的线程数 |
Please report any issues via [issues](https://github.com/oneclickvirt/ecs/issues).
## 更新时间
Go version: [https://github.com/oneclickvirt/ecs](https://github.com/oneclickvirt/ecs)
最后更新时间: 2026-04-20 01:38:35 UTC
Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https://github.com/spiritLHLS/ecs/blob/main/README_EN.md)
## 数据来源
---
## **Language**
[English Docs](README.md) | [中文文档](README_ZH.md)
---
## **Supported Systems and Architectures**
### **Compilation and Testing Support**
| Supported for Compilation | Tested on | Supported OS for Compilation | Tested OS |
|---------------------------|-----------|------------------------------|-----------|
| amd64 | amd64 | Linux | Linux |
| arm64 | arm64 | Windows | Windows |
| arm | | MacOS(Darwin) | MacOS |
| 386 | | FreeBSD | |
| mips,mipsle | | Android | |
| mips64,mips64le | | | |
| ppc64,ppc64le | | | |
| s390x | s390x | | |
| riscv64 | | | |
> For more information about the architecture and system, please test or compile it yourself, and open issues if you have any questions.
### **Systems Pending Support**
| OS | Notes |
|--------|-------------------------------------------------------------------------------------------------|
| OpenBSD/NetBSD | Some of Golang's official libraries do not support this system (especially net-related items) |
---
## **Features**
- System basic information query and concurrent IP basic information query: Self-developed [basics](https://github.com/oneclickvirt/basics), [gostun](https://github.com/oneclickvirt/gostun)
- CPU test: Self-developed [cputest](https://github.com/oneclickvirt/cputest) supporting sysbench(lua/golang version), geekbench, winsat
- Memory test: Self-developed [memorytest](https://github.com/oneclickvirt/memorytest) supporting sysbench, dd, winsat, mbw, stream
- Disk test: Self-developed [disktest](https://github.com/oneclickvirt/disktest) supporting dd, fio, winsat
- Streaming platform unlock tests concurrent query: Self-developed to [UnlockTests](https://github.com/oneclickvirt/UnlockTests), logic modified from [RegionRestrictionCheck](https://github.com/lmc999/RegionRestrictionCheck) and others
- IP quality/security information concurrent query: Self-developed, but open sourced
- Email port test: Self-developed [portchecker](https://github.com/oneclickvirt/portchecker)
- Three-network return path test: Modified from [zhanghanyun/backtrace](https://github.com/zhanghanyun/backtrace) to [oneclickvirt/backtrace](https://github.com/oneclickvirt/backtrace)
- Three-network route test: Modified from [NTrace-core](https://github.com/nxtrace/NTrace-core) to [nt3](https://github.com/oneclickvirt/nt3)
- Speed test: Based on data from [speedtest.net](https://github.com/spiritLHLS/speedtest.net-CN-ID) and [speedtest.cn](https://github.com/spiritLHLS/speedtest.cn-CN-ID), developed to [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)
- Three-network Ping test: Modified from [ecsspeed](https://github.com/spiritLHLS/ecsspeed) to [pingtest](https://github.com/oneclickvirt/pingtest)
- Support root or admin environment testing, support non-root or non-admin environment testing, support offline environment for testing, **not yet** support no DNS online environment for testing
**For first-time users of this project, it is recommended to check the instructions: [Jump to](https://github.com/oneclickvirt/ecs/blob/master/README_NEW_USER.md)**
---
## **Instructions for Use**
### **Linux/FreeBSD/MacOS**
#### **One-click command**
**One-Click Command** will **Not install Dependencies** by Default, **Not update Package Manager** by Default, **Non-Interactive Mode** by Default.
- **International users without acceleration:**
```bash
export noninteractive=true && curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs -l=en
```
- **International/domestic users with CDN acceleration:**
```bash
export noninteractive=true && curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs -l=en
```
- **Domestic users with CNB acceleration:**
```bash
export noninteractive=true && export CN=true && curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs -l=en
```
- **Short Link:**
```bash
export noninteractive=true && curl -L https://bash.spiritlhl.net/goecs -o goecs.sh && chmod +x goecs.sh && bash goecs.sh install && goecs -l=en
```
OR
```bash
export noninteractive=true && curl -L https://ba.sh/JrVa -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs -l=en
```
**For more accurate testing, please follow the detailed instructions below to install and add non-essential dependencies**
#### **Detailed instructions**
The following commands control whether dependencies are installed, whether the package manager is updated, and whether interactive or non-interactive mode is used.
<details>
<summary>Expand to view detailed instructions</summary>
1. **Download the script**
**International users without acceleration:**
```bash
curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
**International/domestic users with CDN acceleration:**
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
**Domestic users with CNB acceleration:**
```bash
export CN=true && curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
2. **Update package manager (optional) and install environment**
```bash
./goecs.sh env
```
**Non-interactive mode:**
```bash
export noninteractive=true && ./goecs.sh env
```
3. **Install `goecs`**
```bash
./goecs.sh install
```
4. **Upgrade `goecs`**
```bash
./goecs.sh upgrade
```
5. **Uninstall `goecs`**
```bash
./goecs.sh uninstall
6. **help command**
```bash
./goecs.sh -h
```
7. **Invoke the menu**
```bash
goecs -l=en
```
</details>
---
#### **Command parameterization**
<details>
<summary>Expand to view parameter descriptions</summary>
```bash
Usage: goecs [options]
-backtrace
Enable/Disable backtrace test (in 'en' language or on windows it always false) (default true)
-basic
Enable/Disable basic test (default true)
-ut
Enable/Disable unlock media test (default true)
-cpu
Enable/Disable CPU test (default true)
-cpum string
Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench")
-cpu-method string
Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench")
-cput string
Set CPU test thread mode (supported: single, multi) (default "multi")
-cpu-thread string
Set CPU test thread mode (supported: single, multi) (default "multi")
-disk
Enable/Disable disk test (default true)
-diskm string
Set disk test method (supported: fio, dd, winsat) (default "fio")
-disk-method string
Set disk test method (supported: fio, dd, winsat) (default "fio")
-diskmc
Enable/Disable multiple disk checks, e.g., -diskmc=false
-diskp string
Set disk test path, e.g., -diskp /root
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-lang string
Set language (supported: en, zh) (default "zh")
-log
Enable/Disable logging in the current path
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-memory-method string
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3-location string
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-nt3-type string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security Enable/Disable security test (default false)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>
---
### **Windows**
1. Download the compressed file with the .exe file: [Releases](https://github.com/oneclickvirt/ecs/releases)
2. After unzipping, right-click and run as administrator.
PS: If it's a VM environment, it's OK not to run it in administrator mode, because VMs have no native testing tools and will automatically enable alternative methods for testing.
PPS: Please refrain from downloading executable files labelled with a GUI for the time being, as they have not been fully adapted. The compressed packages for the CI version are unaffected.
---
### **Docker**
<details>
<summary>Expand to view how to use it</summary>
International image: https://hub.docker.com/r/spiritlhl/goecs
Please ensure Docker is installed on your machine before executing the following commands
Privileged mode + host network
```shell
docker run --rm --privileged --network host spiritlhl/goecs:latest -menu=false -l=en
```
Unprivileged mode + non-host network
```shell
docker run --rm spiritlhl/goecs:latest -menu=false -l=en
```
Using Docker to execute tests will result in some hardware testing bias and virtualization architecture detection failure. Direct testing is recommended over Docker testing.
Mirror image: https://cnb.cool/oneclickvirt/ecs/-/packages/docker/ecs
Please ensure Docker is installed on your machine before executing the following commands
Privileged mode + host network
```shell
docker run --rm --privileged --network host docker.cnb.cool/oneclickvirt/ecs:latest -menu=false -l=en
```
Unprivileged mode + non-host network
```shell
docker run --rm docker.cnb.cool/oneclickvirt/ecs:latest -menu=false -l=en
```
</details>
---
### Compiling from source code
<details>
<summary>Expand to view compilation instructions</summary>
1. Clone the public branch of the repository (without private dependencies)
```bash
git clone -b public https://github.com/oneclickvirt/ecs.git
cd ecs
```
2. Install Go environment (skip if already installed)
Select go 1.25.4 version to install
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/install_scripts/go.sh -o go.sh && chmod +x go.sh && bash go.sh
```
3. Compile
```bash
go build -o goecs
```
4. Run test
```bash
./goecs -menu=false -l=en
```
Supported compilation parameters:
- GOOS: supports linux, windows, darwin, freebsd, openbsd
- GOARCH: supports amd64, arm, arm64, 386, mips, mipsle, s390x, riscv64
Cross-platform compilation examples:
```bash
# Compile Windows version
GOOS=windows GOARCH=amd64 go build -o goecs.exe
# Compile MacOS version
GOOS=darwin GOARCH=amd64 go build -o goecs_darwin
```
</details>
---
## QA
#### Q: Why is sysbench used by default instead of geekbench?
#### A: Comparing the characteristics of both:
| Comparison | sysbench | geekbench |
|------------|----------|-----------|
| Application scope | Lightweight, runs on almost any server | Heavyweight, won't run on small machines |
| Test requirements | No network needed, no special hardware requirements | Requires internet, IPv4 environment, minimum 1GB memory |
| Open source status | Based on LUA, open source, can compile for various architectures | Official binaries are closed source, cannot compile your own version |
| Test stability | Core test components unchanged for 10+ years | Each major version updates test items, making scores hard to compare between versions (each version benchmarks against current best CPUs) |
| Test content | Only tests computing performance | Covers multiple performance aspects with weighted scores, though some tests aren't commonly used |
| Suitable scenarios | Good for quick tests, focuses on computing performance | Good for comprehensive testing |
| Ranking | [sysbench.spiritlhl.net](https://sysbench.spiritlhl.net/) | [browser.geekbench.com](https://browser.geekbench.com/) |
Note that `goecs` allows you to specify CPU test method via parameters. The default is chosen for faster testing across more systems.
#### Q: Why use Golang instead of Rust for refactoring?
#### A: Because network-related projects currently trend toward Golang, with many components maintained by open source communities. Many Rust components would require building from scratch, ~~I'm too lazy~~ I don't have that technical capability.
#### Q: Why not continue developing the Shell version instead of refactoring?
#### A: Because there were too many varied environment issues. Pre-compiled binary files are easier for solving environment problems (better generalization).
#### Q: Are there explanations for each test item?
#### A: Each test project has its own maintenance repository. Click through to view the repository description.
#### Q: How do I manually terminate a test halfway through?
#### A: Press Ctrl+C to terminate the program. After termination, a goecs.txt file and share link will still be generated in the current directory containing information tested so far.
#### Q: How do I test in a non-Root environment?
#### A: Execute the installation command manually. If you can't install it, simply download the appropriate architecture package from releases, extract it, and run the file if you have execution permissions. Alternatively, use Docker if you can.
## Thanks
Thank [he.net](https://he.net) [bgp.tools](https://bgp.tools) [ipinfo.io](https://ipinfo.io) [maxmind.com](https://www.maxmind.com/en/home) [cloudflare.com](https://www.cloudflare.com/) [ip.sb](https://ip.sb) [scamalytics.com](https://scamalytics.com) [abuseipdb.com](https://www.abuseipdb.com/) [ip2location.com](https://ip2location.com/) [ip-api.com](https://ip-api.com) [ipregistry.co](https://ipregistry.co/) [ipdata.co](https://ipdata.co/) [ipgeolocation.io](https://ipgeolocation.io) [ipwhois.io](https://ipwhois.io) [ipapi.com](https://ipapi.com/) [ipapi.is](https://ipapi.is/) [ipqualityscore.com](https://www.ipqualityscore.com/) [bigdatacloud.com](https://www.bigdatacloud.com/) [dkly.net](https://data.dkly.net) [virustotal.com](https://www.virustotal.com/) [ipfighter.com](https://ipfighter.com/) [getipintel.net](http://check.getipintel.net/) [fraudlogix.com](https://fraudlogix.com) and others for providing APIs for testing, and thanks to various websites on the Internet for providing query resources.
Thank
<a href="https://h501.io/?from=69" target="_blank">
<img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501" style="height: 50px;">
</a>
provided free hosting support for this open source project's shared test results storage
Thanks also to the following platforms for editorial and testing support
<a href="https://www.jetbrains.com/go/" target="_blank">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland" style="height: 50px;">
</a>
<a href="https://community.ibm.com/zsystems/form/l1cc-oss-vm-request/" target="_blank">
<img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm" style="height: 50px;">
</a>
<a href="https://console.zmto.com/?affid=1524" target="_blank">
<img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="zmto" style="height: 50px;">
</a>
## History Usage
![goecs](https://hits.spiritlhl.net/chart/goecs.svg)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/oneclickvirt/ecs.svg?variant=adaptive)](https://www.spiritlhl.net)
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_large)
数据来源于用户提交的CPU性能测试结果经过自动化脚本匿名处理和排序生成。

File diff suppressed because it is too large Load Diff

View File

@@ -1,441 +0,0 @@
# ECS
[![Build and Release](https://github.com/oneclickvirt/ecs/actions/workflows/build_binary.yaml/badge.svg)](https://github.com/oneclickvirt/ecs/actions/workflows/build_binary.yaml)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_shield)
[![Hits](https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false)](https://hits.spiritlhl.net) [![Downloads](https://ghdownload.spiritlhl.net/oneclickvirt/ecs?color=36c600)](https://github.com/oneclickvirt/ecs/releases)
融合怪测评项目 - GO版本
(仅环境安装[非必须]使用shell外无额外shell文件依赖环境安装只是为了测的更准极端情况下无环境依赖安装也可全测项目)
如有问题请 [issues](https://github.com/oneclickvirt/ecs/issues) 反馈。
Go 版本:[https://github.com/oneclickvirt/ecs](https://github.com/oneclickvirt/ecs)
Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS/ecs)
---
## **语言**
[English Docs](README.md) | [中文文档](README_ZH.md)
---
## **适配系统和架构**
### **编译与测试支持情况**
| 编译支持的架构 | 测试支持的架构 | 编译支持的系统 | 测试支持的系统 |
|---------------------------|--------------|---------------------------|---------------|
| amd64 | amd64 | Linux | Linux |
| arm64 | arm64 | Windows | Windows |
| arm | | MacOS(Darwin) | MacOS |
| 386 | | FreeBSD | |
| mips,mipsle | | Android | |
| mips64,mips64le | | | |
| ppc64,ppc64le | | | |
| s390x | s390x | | |
| riscv64 | | | |
> 更多架构与系统请自行测试或编译,如有问题请开 issues。
### **待支持的系统**
| 系统 | 说明 |
|----------------|---------------------------|
| OpenBSD/NetBSD | 部分Golang的官方库未支持本系统(尤其是net相关项目) |
---
## **功能**
- 系统基础信息查询IP基础信息并发查询[basics](https://github.com/oneclickvirt/basics)、[gostun](https://github.com/oneclickvirt/gostun)
- CPU 测试:[cputest](https://github.com/oneclickvirt/cputest),支持 sysbench(lua/golang版本)、geekbench、winsat
- 内存测试:[memorytest](https://github.com/oneclickvirt/memorytest),支持 sysbench、dd、winsat、mbw、stream
- 硬盘测试:[disktest](https://github.com/oneclickvirt/disktest),支持 dd、fio、winsat
- 流媒体平台解锁测试并发查询:[UnlockTests](https://github.com/oneclickvirt/UnlockTests),逻辑借鉴 [RegionRestrictionCheck](https://github.com/lmc999/RegionRestrictionCheck) 等
- IP 质量/安全信息并发查询:但已开源
- 邮件端口测试:[portchecker](https://github.com/oneclickvirt/portchecker)
- 上游及回程路由线路检测:借鉴 [zhanghanyun/backtrace](https://github.com/zhanghanyun/backtrace),二次开发至 [oneclickvirt/backtrace](https://github.com/oneclickvirt/backtrace)
- 三网路由测试:基于 [NTrace-core](https://github.com/nxtrace/NTrace-core),二次开发至 [nt3](https://github.com/oneclickvirt/nt3)
- 网速测试:基于 [speedtest.net](https://github.com/spiritLHLS/speedtest.net-CN-ID) 和 [speedtest.cn](https://github.com/spiritLHLS/speedtest.cn-CN-ID) 数据,开发 [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest),同时融合私有国内测速节点
- 三网 Ping 值测试:借鉴 [ecsspeed](https://github.com/spiritLHLS/ecsspeed),二次开发至 [pingtest](https://github.com/oneclickvirt/pingtest)
- 支持root或admin环境下测试支持非root或非admin环境下测试支持离线环境下进行测试**暂未**支持无DNS的在线环境下进行测试
**本项目初次使用建议查看说明:[跳转](https://github.com/oneclickvirt/ecs/blob/master/README_NEW_USER.md)**
---
## **使用说明**
### **Linux/FreeBSD/MacOS**
#### **一键命令**
**一键命令**将默认**不安装依赖**,默认**不更新包管理器**,默认**非互动模式**
- **国际用户无加速:**
```bash
export noninteractive=true && curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs
```
- **国际/国内使用 CDN 加速:**
```bash
export noninteractive=true && curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs
```
- **国内用户使用 CNB 加速:**
```bash
export noninteractive=true && export CN=true && curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs
```
- **短链接:**
```bash
export noninteractive=true && curl -L https://bash.spiritlhl.net/goecs -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs
```
```bash
export noninteractive=true && curl -L https://ba.sh/JrVa -o goecs.sh && chmod +x goecs.sh && ./goecs.sh install && goecs
```
**如果需要测试更准确,请按照下面的详细说明进行安装,添加非必需的依赖**
#### **详细说明**
以下命令可控制**是否安装依赖****是否更新包管理器****互动模式和非交互模式**
<details>
<summary>展开查看详细说明</summary>
1. **下载脚本**
**国际用户无加速:**
```bash
curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
**国际/国内使用 CDN 加速:**
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
**国内用户使用 CNB 加速:**
```bash
export CN=true && curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh
```
2. **更新包管理器(可选择)并安装环境**
```bash
./goecs.sh env
```
**非互动模式:**
```bash
export noninteractive=true && ./goecs.sh env
```
3. **安装 `goecs` 本体(仅下载二进制文件无依赖安装)**
```bash
./goecs.sh install
```
4. **升级 `goecs` 本体**
```bash
./goecs.sh upgrade
```
5. **卸载 `goecs` 本体**
```bash
./goecs.sh uninstall
```
6. **帮助命令**
```bash
./goecs.sh -h
```
7. **唤起菜单**
```bash
goecs
```
</details>
---
#### **命令参数化**
<details>
<summary>展开查看各参数说明</summary>
```bash
Usage: goecs [options]
-backtrace
Enable/Disable backtrace test (in 'en' language or on windows it always false) (default true)
-basic
Enable/Disable basic test (default true)
-ut
Enable/Disable unlock media test (default true)
-cpu
Enable/Disable CPU test (default true)
-cpum string
Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench")
-cpu-method string
Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench")
-cput string
Set CPU test thread mode (supported: single, multi) (default "multi")
-cpu-thread string
Set CPU test thread mode (supported: single, multi) (default "multi")
-disk
Enable/Disable disk test (default true)
-diskm string
Set disk test method (supported: fio, dd, winsat) (default "fio")
-disk-method string
Set disk test method (supported: fio, dd, winsat) (default "fio")
-diskmc
Enable/Disable multiple disk checks, e.g., -diskmc=false
-diskp string
Set disk test path, e.g., -diskp /root
-email
Enable/Disable email port test (default true)
-h Show help information
-help
Show help information
-l string
Set language (supported: en, zh) (default "zh")
-lang string
Set language (supported: en, zh) (default "zh")
-log
Enable/Disable logging in the current path
-memory
Enable/Disable memory test (default true)
-memorym string
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-memory-method string
Set memory test method (supported: stream, sysbench, dd, winsat, auto) (default "stream")
-menu
Enable/Disable menu mode, disable example: -menu=false (default true)
-nt3
Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true)
-nt3loc string
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3-location string
Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all) (default "GZ")
-nt3t string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-nt3-type string
Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4")
-ping
Enable/Disable ping test
-security Enable/Disable security test (default false)
-speed
Enable/Disable speed test (default true)
-spnum int
Set the number of servers per operator for speed test (default 2)
-tgdc
Enable/Disable Telegram DC test
-upload
Enable/Disable upload the result (default true)
-v Display version information
-version
Display version information
-web
Enable/Disable popular websites test
```
</details>
---
### **Windows**
1. 下载带 exe 文件的压缩包:[Releases](https://github.com/oneclickvirt/ecs/releases)
2. 解压后,右键以管理员模式运行。
PS如果是虚拟机环境不以管理员模式运行也行因为虚拟机无原生的测试工具将自动启用替代方法测试。
PPS: 暂时不要下载带GUI标签的exe文件未完整适配CI版本的压缩包是没问题的。
---
### **Docker**
<details>
<summary>展开查看使用说明</summary>
国际镜像地址https://hub.docker.com/r/spiritlhl/goecs
请确保执行下述命令前本机已安装Docker
特权模式+host网络
```shell
docker run --rm --privileged --network host spiritlhl/goecs:latest -menu=false -l zh
```
非特权模式+非host网络
```shell
docker run --rm spiritlhl/goecs:latest -menu=false -l zh
```
使用Docker执行测试硬件测试会有一些偏差和虚拟化架构判断失效还是推荐直接测试而不使用Docker测试。
国内阿里云镜像加速
请确保执行下述命令前本机已安装Docker
特权模式+host网络
```shell
docker run --rm --privileged --network host crpi-8tmognxgyb86bm61.cn-guangzhou.personal.cr.aliyuncs.com/oneclickvirt/ecs:latest -menu=false -l zh
```
非特权模式+非host网络
```shell
docker run --rm crpi-8tmognxgyb86bm61.cn-guangzhou.personal.cr.aliyuncs.com/oneclickvirt/ecs:latest -menu=false -l zh
```
实际上还有CNB镜像地址 https://cnb.cool/oneclickvirt/ecs/-/packages/docker/ecs 但很可惜组织空间不足无法推送了,更推荐使用阿里云镜像加速
</details>
---
### 从源码进行编译
<details>
<summary>展开查看编译说明</summary>
1. 克隆仓库的 public 分支(不含私有依赖)
```bash
git clone -b public https://github.com/oneclickvirt/ecs.git
cd ecs
```
2. 安装 Go 环境(如已安装可跳过)
选择 go 1.25.4 的版本进行安装
```bash
curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/install_scripts/go.sh -o go.sh && chmod +x go.sh && bash go.sh
```
3. 编译
```bash
go build -o goecs
```
4. 运行测试
```bash
./goecs -menu=false -l zh
```
支持的编译参数:
- GOOS支持 linux、windows、darwin、freebsd、openbsd
- GOARCH支持 amd64、arm、arm64、386、mips、mipsle、s390x、riscv64
跨平台编译示例:
```bash
# 编译 Windows 版本
GOOS=windows GOARCH=amd64 go build -o goecs.exe
# 编译 MacOS 版本
GOOS=darwin GOARCH=amd64 go build -o goecs_darwin
```
</details>
---
## QA
#### Q: 为什么默认使用sysbench而不是geekbench
#### A: 比较二者特点
| 比较项 | sysbench | geekbench |
|------------------|----------|-----------|
| 适用范围 | 轻量级,几乎可在任何服务器上运行 | 重量级,小型机器无法运行 |
| 测试要求 | 无需网络,无特殊硬件需求 | 需联网IPV4环境至少1G内存 |
| 开源情况 | 基于LUA开源可自行编译各架构版本 | 官方二进制闭源代码,不支持自行编译 |
| 测试稳定性 | 核心测试组件10年以上未变 | 每个大版本更新测试项,分数不同版本间难以对比(每个版本对标当前最好的CPU) |
| 测试内容 | 仅测试计算性能 | 覆盖多种性能测试,分数加权计算,但部分测试实际不常用 |
| 适用场景 | 适合快速测试,仅测试计算性能 | 适合综合全面的测试 |
| 排行榜 | [sysbench.spiritlhl.net](https://sysbench.spiritlhl.net/) | [browser.geekbench.com](https://browser.geekbench.com/) |
且```goecs```测试使用何种CPU测试方式可使用参数指定默认只是为了更多用户快速测试的需求
#### Q: 为什么使用Golang而不是Rust重构
#### A: 因为网络相关的项目目前以Golang语言为趋势大多组件有开源生态维护Rust很多得自己手搓~~我懒得搞~~我没那个技术力
#### Q: 为什么不继续开发Shell版本而是选择重构
#### A: 因为太多千奇百怪的环境问题了,还是提前编译好测试的二进制文件比较容易解决环境问题(泛化性更好)
#### Q: 每个测试项目的说明有吗?
#### A: 每个测试项目有对应的维护仓库,自行点击查看仓库说明
#### Q: 测试进行到一半如何手动终止?
#### A: 按ctrl键和c键终止程序终止后依然会在当前目录下生成goecs.txt文件和分享链接里面是已经测试到的信息。
#### Q: 非Root环境如何进行测试
#### A: 手动执行安装命令实在装不上也没问题直接在release中下载对应架构的压缩包解压后执行即可只要你能执行的了文件。或者你能使用docker的话用docker执行。
## 致谢
感谢
[DKLYDataHub - IP Geolocation Data](https://data.dkly.net)
[he.net](https://he.net) [bgp.tools](https://bgp.tools) [ipinfo.io](https://ipinfo.io) [maxmind.com](https://www.maxmind.com/en/home) [cloudflare.com](https://www.cloudflare.com/) [ip.sb](https://ip.sb) [scamalytics.com](https://scamalytics.com) [abuseipdb.com](https://www.abuseipdb.com/) [ip2location.com](https://ip2location.com/) [ip-api.com](https://ip-api.com) [ipregistry.co](https://ipregistry.co/) [ipdata.co](https://ipdata.co/) [ipgeolocation.io](https://ipgeolocation.io) [ipwhois.io](https://ipwhois.io) [ipapi.com](https://ipapi.com/) [ipapi.is](https://ipapi.is/) [ipqualityscore.com](https://www.ipqualityscore.com/) [bigdatacloud.com](https://www.bigdatacloud.com/) [virustotal.com](https://www.virustotal.com/) [ipfighter.com](https://ipfighter.com/) [getipintel.net](http://check.getipintel.net/) [fraudlogix.com](https://fraudlogix.com) 等网站提供的API进行检测感谢互联网各网站提供的查询资源
感谢
<a href="https://h501.io/?from=69" target="_blank">
<img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501" style="height: 50px;">
</a>
提供的免费托管支持本开源项目的共享测试结果存储
同时感谢以下平台提供编辑和测试支持
<a href="https://www.jetbrains.com/go/" target="_blank">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland" style="height: 50px;">
</a>
<a href="https://community.ibm.com/zsystems/form/l1cc-oss-vm-request/" target="_blank">
<img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm" style="height: 50px;">
</a>
<a href="https://console.zmto.com/?affid=1524" target="_blank">
<img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="zmto" style="height: 50px;">
</a>
## History Usage
![goecs](https://hits.spiritlhl.net/chart/goecs.svg)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/oneclickvirt/ecs.svg?variant=adaptive)](https://www.spiritlhl.net)
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_large)

View File

@@ -1,56 +0,0 @@
package api
const (
// Version API版本号
Version = "v1.0.0"
// DefaultVersion 默认的ECS版本号
DefaultVersion = "v0.1.114"
)
// 测试方法常量
const (
// CPU测试方法
CpuMethodSysbench = "sysbench"
CpuMethodGeekbench = "geekbench"
CpuMethodWinsat = "winsat"
// 内存测试方法
MemoryMethodStream = "stream"
MemoryMethodSysbench = "sysbench"
MemoryMethodDD = "dd"
MemoryMethodWinsat = "winsat"
// 硬盘测试方法
DiskMethodFio = "fio"
DiskMethodDD = "dd"
DiskMethodWinsat = "winsat"
// 线程模式
ThreadModeSingle = "single"
ThreadModeMulti = "multi"
// 语言选项
LanguageZH = "zh"
LanguageEN = "en"
// IP检测类型
CheckTypeIPv4 = "ipv4"
CheckTypeIPv6 = "ipv6"
CheckTypeAuto = "auto"
// 测速平台
PlatformCN = "cn"
PlatformNet = "net"
// 运营商类型
OperatorCMCC = "cmcc" // 中国移动
OperatorCU = "cu" // 中国联通
OperatorCT = "ct" // 中国电信
OperatorGlobal = "global" // 全球节点
OperatorOther = "other" // 其他
OperatorHK = "hk" // 香港
OperatorTW = "tw" // 台湾
OperatorJP = "jp" // 日本
OperatorSG = "sg" // 新加坡
)

View File

@@ -1,266 +0,0 @@
package api
import (
"github.com/oneclickvirt/ecs/internal/params"
)
// Config 配置接口,导出用于外部调用
type Config = params.Config
// NewConfig 创建默认配置
// version: 版本号字符串
func NewConfig(version string) *Config {
return params.NewConfig(version)
}
// NewDefaultConfig 创建默认配置(使用默认版本号)
func NewDefaultConfig() *Config {
return params.NewConfig("v0.1.114")
}
// ConfigOption 配置选项函数类型
type ConfigOption func(*Config)
// WithLanguage 设置语言
func WithLanguage(lang string) ConfigOption {
return func(c *Config) {
c.Language = lang
}
}
// WithCpuTestMethod 设置CPU测试方法
// method: "sysbench" 或 "geekbench"
func WithCpuTestMethod(method string) ConfigOption {
return func(c *Config) {
c.CpuTestMethod = method
}
}
// WithCpuTestThreadMode 设置CPU测试线程模式
// mode: "single" 或 "multi"
func WithCpuTestThreadMode(mode string) ConfigOption {
return func(c *Config) {
c.CpuTestThreadMode = mode
}
}
// WithMemoryTestMethod 设置内存测试方法
// method: "stream", "sysbench", "dd"
func WithMemoryTestMethod(method string) ConfigOption {
return func(c *Config) {
c.MemoryTestMethod = method
}
}
// WithDiskTestMethod 设置硬盘测试方法
// method: "fio" 或 "dd"
func WithDiskTestMethod(method string) ConfigOption {
return func(c *Config) {
c.DiskTestMethod = method
}
}
// WithDiskTestPath 设置硬盘测试路径
func WithDiskTestPath(path string) ConfigOption {
return func(c *Config) {
c.DiskTestPath = path
}
}
// WithDiskMultiCheck 设置是否进行硬盘多路径检测
func WithDiskMultiCheck(enable bool) ConfigOption {
return func(c *Config) {
c.DiskMultiCheck = enable
}
}
// WithSpeedTestNum 设置测速节点数量
func WithSpeedTestNum(num int) ConfigOption {
return func(c *Config) {
c.SpNum = num
}
}
// WithWidth 设置输出宽度
func WithWidth(width int) ConfigOption {
return func(c *Config) {
c.Width = width
}
}
// WithFilePath 设置输出文件路径
func WithFilePath(path string) ConfigOption {
return func(c *Config) {
c.FilePath = path
}
}
// WithEnableUpload 设置是否启用上传
func WithEnableUpload(enable bool) ConfigOption {
return func(c *Config) {
c.EnableUpload = enable
}
}
// WithAnalyzeResult 设置是否启用测试后结果总结分析
func WithAnalyzeResult(enable bool) ConfigOption {
return func(c *Config) {
c.AnalyzeResult = enable
}
}
// WithEnableLogger 设置是否启用日志
func WithEnableLogger(enable bool) ConfigOption {
return func(c *Config) {
c.EnableLogger = enable
}
}
// WithBasicTest 设置是否执行基础信息测试
func WithBasicTest(enable bool) ConfigOption {
return func(c *Config) {
c.BasicStatus = enable
}
}
// WithCpuTest 设置是否执行CPU测试
func WithCpuTest(enable bool) ConfigOption {
return func(c *Config) {
c.CpuTestStatus = enable
}
}
// WithMemoryTest 设置是否执行内存测试
func WithMemoryTest(enable bool) ConfigOption {
return func(c *Config) {
c.MemoryTestStatus = enable
}
}
// WithDiskTest 设置是否执行硬盘测试
func WithDiskTest(enable bool) ConfigOption {
return func(c *Config) {
c.DiskTestStatus = enable
}
}
// WithUnlockTest 设置是否执行流媒体解锁测试
func WithUnlockTest(enable bool) ConfigOption {
return func(c *Config) {
c.UtTestStatus = enable
}
}
// WithSecurityTest 设置是否执行IP质量测试
func WithSecurityTest(enable bool) ConfigOption {
return func(c *Config) {
c.SecurityTestStatus = enable
}
}
// WithEmailTest 设置是否执行邮件端口测试
func WithEmailTest(enable bool) ConfigOption {
return func(c *Config) {
c.EmailTestStatus = enable
}
}
// WithBacktraceTest 设置是否执行回程路由测试
func WithBacktraceTest(enable bool) ConfigOption {
return func(c *Config) {
c.BacktraceStatus = enable
}
}
// WithNt3Test 设置是否执行三网路由测试
func WithNt3Test(enable bool) ConfigOption {
return func(c *Config) {
c.Nt3Status = enable
}
}
// WithSpeedTest 设置是否执行测速测试
func WithSpeedTest(enable bool) ConfigOption {
return func(c *Config) {
c.SpeedTestStatus = enable
}
}
// WithPingTest 设置是否执行PING测试
func WithPingTest(enable bool) ConfigOption {
return func(c *Config) {
c.PingTestStatus = enable
}
}
// WithTgdcTest 设置是否执行Telegram DC测试
func WithTgdcTest(enable bool) ConfigOption {
return func(c *Config) {
c.TgdcTestStatus = enable
}
}
// WithWebTest 设置是否执行网站测试
func WithWebTest(enable bool) ConfigOption {
return func(c *Config) {
c.WebTestStatus = enable
}
}
// WithNt3CheckType 设置三网路由检测类型
// checkType: "ipv4", "ipv6" 或 "auto"
func WithNt3CheckType(checkType string) ConfigOption {
return func(c *Config) {
c.Nt3CheckType = checkType
}
}
// WithNt3Location 设置三网路由检测位置
func WithNt3Location(location string) ConfigOption {
return func(c *Config) {
c.Nt3Location = location
}
}
// WithAutoChangeDiskMethod 设置是否自动切换硬盘测试方法
func WithAutoChangeDiskMethod(enable bool) ConfigOption {
return func(c *Config) {
c.AutoChangeDiskMethod = enable
}
}
// WithOnlyChinaTest 设置是否只进行国内测试
func WithOnlyChinaTest(enable bool) ConfigOption {
return func(c *Config) {
c.OnlyChinaTest = enable
}
}
// WithMenuMode 设置是否启用菜单模式
func WithMenuMode(enable bool) ConfigOption {
return func(c *Config) {
c.MenuMode = enable
}
}
// WithOnlyIpInfoCheck 设置是否只进行IP信息检测
func WithOnlyIpInfoCheck(enable bool) ConfigOption {
return func(c *Config) {
c.OnlyIpInfoCheck = enable
}
}
// WithChoice 设置菜单选择
func WithChoice(choice string) ConfigOption {
return func(c *Config) {
c.Choice = choice
}
}
// ApplyOptions 应用配置选项
func ApplyOptions(config *Config, options ...ConfigOption) *Config {
for _, opt := range options {
opt(config)
}
return config
}

View File

@@ -1,27 +0,0 @@
package api
import (
"github.com/oneclickvirt/ecs/internal/menu"
"github.com/oneclickvirt/ecs/utils"
)
// GetMenuChoice 获取用户菜单选择
// language: 语言 ("zh" 或 "en")
// 返回: 用户选择的选项
func GetMenuChoice(language string) string {
return menu.GetMenuChoice(language)
}
// PrintMenuOptions 打印菜单选项
// preCheck: 网络检查结果
// config: 配置对象
func PrintMenuOptions(preCheck utils.NetCheckResult, config *Config) {
menu.PrintMenuOptions(preCheck, config)
}
// HandleMenuMode 处理菜单模式
// preCheck: 网络检查结果
// config: 配置对象
func HandleMenuMode(preCheck utils.NetCheckResult, config *Config) {
menu.HandleMenuMode(preCheck, config)
}

View File

@@ -1,190 +0,0 @@
package api
import (
"sync"
"time"
"github.com/oneclickvirt/ecs/internal/runner"
"github.com/oneclickvirt/ecs/utils"
)
// RunResult 运行结果
type RunResult struct {
Output string // 完整输出
Duration time.Duration // 运行时长
StartTime time.Time // 开始时间
EndTime time.Time // 结束时间
}
// RunAllTests 执行所有测试(高级接口)
// preCheck: 网络检查结果
// config: 配置对象
// 返回: 运行结果
func RunAllTests(preCheck utils.NetCheckResult, config *Config) *RunResult {
var (
wg1, wg2, wg3 sync.WaitGroup
basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo string
output, tempOutput string
outputMutex sync.Mutex
infoMutex sync.Mutex
)
startTime := time.Now()
switch config.Language {
case "zh":
runner.RunChineseTests(preCheck, config, &wg1, &wg2, &wg3,
&basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo,
&output, tempOutput, startTime, &outputMutex, &infoMutex)
case "en":
runner.RunEnglishTests(preCheck, config, &wg1, &wg2, &wg3,
&basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo,
&output, tempOutput, startTime, &outputMutex, &infoMutex)
default:
runner.RunChineseTests(preCheck, config, &wg1, &wg2, &wg3,
&basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo,
&output, tempOutput, startTime, &outputMutex, &infoMutex)
}
if config.AnalyzeResult {
output = runner.AppendAnalysisSummary(config, output, tempOutput, &outputMutex)
}
endTime := time.Now()
return &RunResult{
Output: output,
Duration: endTime.Sub(startTime),
StartTime: startTime,
EndTime: endTime,
}
}
// RunBasicTests 运行基础信息测试
func RunBasicTests(preCheck utils.NetCheckResult, config *Config) string {
var (
basicInfo, securityInfo string
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunBasicTests(preCheck, config, &basicInfo, &securityInfo, output, tempOutput, &outputMutex)
}
// RunCPUTest 运行CPU测试
func RunCPUTest(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunCPUTest(config, output, tempOutput, &outputMutex)
}
// RunMemoryTest 运行内存测试
func RunMemoryTest(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunMemoryTest(config, output, tempOutput, &outputMutex)
}
// RunDiskTest 运行硬盘测试
func RunDiskTest(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunDiskTest(config, output, tempOutput, &outputMutex)
}
// RunIpInfoCheck 执行IP信息检测
func RunIpInfoCheck(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunIpInfoCheck(config, output, tempOutput, &outputMutex)
}
// RunStreamingTests 运行流媒体测试
func RunStreamingTests(config *Config, mediaInfo string) string {
var (
wg1 sync.WaitGroup
output, tempOutput string
outputMutex sync.Mutex
infoMutex sync.Mutex
)
return runner.RunStreamingTests(config, &wg1, &mediaInfo, output, tempOutput, &outputMutex, &infoMutex)
}
// RunSecurityTests 运行安全测试
func RunSecurityTests(config *Config, securityInfo string) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunSecurityTests(config, securityInfo, output, tempOutput, &outputMutex)
}
// RunEmailTests 运行邮件端口测试
func RunEmailTests(config *Config, emailInfo string) string {
var (
wg2 sync.WaitGroup
output, tempOutput string
outputMutex sync.Mutex
infoMutex sync.Mutex
)
return runner.RunEmailTests(config, &wg2, &emailInfo, output, tempOutput, &outputMutex, &infoMutex)
}
// RunNetworkTests 运行网络测试(中文模式)
func RunNetworkTests(config *Config, ptInfo string) string {
var (
wg3 sync.WaitGroup
output, tempOutput string
outputMutex sync.Mutex
infoMutex sync.Mutex
)
return runner.RunNetworkTests(config, &wg3, &ptInfo, output, tempOutput, &outputMutex, &infoMutex)
}
// RunSpeedTests 运行测速测试(中文模式)
func RunSpeedTests(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunSpeedTests(config, output, tempOutput, &outputMutex)
}
// RunEnglishNetworkTests 运行网络测试(英文模式)
func RunEnglishNetworkTests(config *Config, ptInfo string) string {
var (
wg3 sync.WaitGroup
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunEnglishNetworkTests(config, &wg3, &ptInfo, output, tempOutput, &outputMutex)
}
// RunEnglishSpeedTests 运行测速测试(英文模式)
func RunEnglishSpeedTests(config *Config) string {
var (
output, tempOutput string
outputMutex sync.Mutex
)
return runner.RunEnglishSpeedTests(config, output, tempOutput, &outputMutex)
}
// AppendTimeInfo 添加时间信息
func AppendTimeInfo(config *Config, output string, startTime time.Time) string {
var (
tempOutput string
outputMutex sync.Mutex
)
return runner.AppendTimeInfo(config, output, tempOutput, startTime, &outputMutex)
}
// HandleUploadResults 处理上传结果
func HandleUploadResults(config *Config, output string) {
runner.HandleUploadResults(config, output)
}

View File

@@ -1,101 +0,0 @@
package api
import (
"github.com/oneclickvirt/ecs/internal/tests"
)
// TestResult 测试结果结构
type TestResult struct {
TestMethod string // 实际使用的测试方法
Output string // 测试输出结果
Success bool // 是否成功
Error error // 错误信息
}
// CpuTest CPU测试公共接口
// language: 语言 ("zh" 或 "en")
// testMethod: 测试方法 ("sysbench" 或 "geekbench")
// testThread: 线程模式 ("single" 或 "multi")
// 返回: (实际测试方法, 测试结果)
func CpuTest(language, testMethod, testThread string) (string, string) {
return tests.CpuTest(language, testMethod, testThread)
}
// MemoryTest 内存测试公共接口
// language: 语言 ("zh" 或 "en")
// testMethod: 测试方法 ("stream", "sysbench", "dd")
// 返回: (实际测试方法, 测试结果)
func MemoryTest(language, testMethod string) (string, string) {
return tests.MemoryTest(language, testMethod)
}
// DiskTest 硬盘测试公共接口
// language: 语言 ("zh" 或 "en")
// testMethod: 测试方法 ("fio" 或 "dd")
// testPath: 测试路径
// isMultiCheck: 是否多路径检测
// autoChange: 是否自动切换方法
// 返回: (实际测试方法, 测试结果)
func DiskTest(language, testMethod, testPath string, isMultiCheck, autoChange bool) (string, string) {
return tests.DiskTest(language, testMethod, testPath, isMultiCheck, autoChange)
}
// MediaTest 流媒体解锁测试公共接口
// language: 语言 ("zh" 或 "en")
// 返回: 测试结果
func MediaTest(language string) string {
return tests.MediaTest(language)
}
// SpeedTestShowHead 显示测速表头
// language: 语言 ("zh" 或 "en")
func SpeedTestShowHead(language string) {
tests.ShowHead(language)
}
// SpeedTestNearby 就近节点测速
func SpeedTestNearby() {
tests.NearbySP()
}
// SpeedTestCustom 自定义测速
// platform: 平台 ("cn" 或 "net")
// operator: 运营商 ("cmcc", "cu", "ct", "global", "other" 等)
// num: 测试节点数量
// language: 语言 ("zh" 或 "en")
func SpeedTestCustom(platform, operator string, num int, language string) {
tests.CustomSP(platform, operator, num, language)
}
// NextTrace3Check 三网路由追踪测试
// language: 语言 ("zh" 或 "en")
// location: 位置
// checkType: 检测类型 ("ipv4", "ipv6")
func NextTrace3Check(language, location, checkType string) {
tests.NextTrace3Check(language, location, checkType)
}
// UpstreamsCheck 上游及回程线路检测
func UpstreamsCheck(language string) {
tests.UpstreamsCheck(language)
}
// GetIPv4Address 获取当前IPv4地址
func GetIPv4Address() string {
return tests.IPV4
}
// GetIPv6Address 获取当前IPv6地址
func GetIPv6Address() string {
return tests.IPV6
}
// SetIPv4Address 设置IPv4地址用于测试
func SetIPv4Address(ipv4 string) {
tests.IPV4 = ipv4
}
// SetIPv6Address 设置IPv6地址用于测试
func SetIPv6Address(ipv6 string) {
tests.IPV6 = ipv6
}

View File

@@ -1,91 +0,0 @@
package api
import (
"time"
"github.com/oneclickvirt/ecs/utils"
)
// NetCheckResult 网络检查结果
type NetCheckResult = utils.NetCheckResult
// StatsResponse 统计信息响应
type StatsResponse = utils.StatsResponse
// GitHubRelease GitHub发布信息
type GitHubRelease = utils.GitHubRelease
// CheckPublicAccess 检查公网访问能力
// timeout: 超时时间
// 返回: 网络检查结果
func CheckPublicAccess(timeout time.Duration) NetCheckResult {
return utils.CheckPublicAccess(timeout)
}
// GetGoescStats 获取goecs统计信息
// 返回: (统计响应, 错误)
func GetGoescStats() (*StatsResponse, error) {
return utils.GetGoescStats()
}
// GetLatestEcsRelease 获取最新的ECS版本信息
// 返回: (GitHub发布信息, 错误)
func GetLatestEcsRelease() (*GitHubRelease, error) {
return utils.GetLatestEcsRelease()
}
// PrintHead 打印程序头部信息
// language: 语言 ("zh" 或 "en")
// width: 显示宽度
// version: 版本号
func PrintHead(language string, width int, version string) {
utils.PrintHead(language, width, version)
}
// PrintCenteredTitle 打印居中标题
// title: 标题文本
// width: 显示宽度
func PrintCenteredTitle(title string, width int) {
utils.PrintCenteredTitle(title, width)
}
// ProcessAndUpload 处理并上传结果
// output: 输出内容
// filePath: 文件路径
// enableUpload: 是否启用上传
// 返回: (HTTP URL, HTTPS URL)
func ProcessAndUpload(output, filePath string, enableUpload bool, language string) (string, string) {
return utils.ProcessAndUpload(output, filePath, enableUpload, language)
}
// BasicsAndSecurityCheck 基础信息和安全检查
// language: 语言
// checkType: 检查类型
// securityTestStatus: 是否执行安全测试
// 返回: (IPv4地址, IPv6地址, 基础信息, 安全信息, 检查类型)
func BasicsAndSecurityCheck(language, checkType string, securityTestStatus bool) (string, string, string, string, string) {
return utils.BasicsAndSecurityCheck(language, checkType, securityTestStatus)
}
// OnlyBasicsIpInfo 仅获取基础IP信息
// language: 语言
// 返回: (IPv4地址, IPv6地址, IP信息)
func OnlyBasicsIpInfo(language string) (string, string, string) {
return utils.OnlyBasicsIpInfo(language)
}
// FormatGoecsNumber 格式化数字显示
// num: 数字
// 返回: 格式化后的字符串
func FormatGoecsNumber(num int) string {
return utils.FormatGoecsNumber(num)
}
// PrintAndCapture 打印并捕获输出
// fn: 执行的函数
// tempOutput: 临时输出
// existingOutput: 现有输出
// 返回: 捕获的输出
func PrintAndCapture(fn func(), tempOutput, existingOutput string) string {
return utils.PrintAndCapture(fn, tempOutput, existingOutput)
}

20826
cpu_statistics.json Normal file

File diff suppressed because it is too large Load Diff

128
go.mod
View File

@@ -1,128 +0,0 @@
module github.com/oneclickvirt/ecs
go 1.25.4
require (
github.com/charmbracelet/bubbles v1.0.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/imroc/req/v3 v3.54.0
github.com/oneclickvirt/UnlockTests v0.0.35-20260207053956
github.com/oneclickvirt/backtrace v0.0.8-20251109090457
github.com/oneclickvirt/basics v0.0.16-20251112033526
github.com/oneclickvirt/cputest v0.0.12-20251111095842
github.com/oneclickvirt/defaultset v0.0.2-20240624082446
github.com/oneclickvirt/disktest v0.0.10-20250924030424
github.com/oneclickvirt/gostun v0.0.5-20250727155022
github.com/oneclickvirt/memorytest v0.0.10-20251218032900
github.com/oneclickvirt/nt3 v0.0.11-20260112140912
github.com/oneclickvirt/pingtest v0.0.9-20251104112920
github.com/oneclickvirt/portchecker v0.0.3-20250728015900
github.com/oneclickvirt/speedtest v0.0.11-20251102151740
)
require (
github.com/PuerkitoBio/goquery v1.9.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.9.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gofrs/uuid/v5 v5.2.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huin/goupnp v1.2.0 // indirect
github.com/icholy/digest v1.1.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jaypipes/ghw v0.17.0 // indirect
github.com/jaypipes/pcidb v1.0.1 // indirect
github.com/jsdelivr/globalping-cli v1.5.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/nxtrace/NTrace-core v1.5.0 // indirect
github.com/oneclickvirt/dd v0.0.2-20250808062818 // indirect
github.com/oneclickvirt/fio v0.0.2-20250808045755 // indirect
github.com/oneclickvirt/mbw v0.0.1-20250808061222 // indirect
github.com/oneclickvirt/stream v0.0.2-20250924154001 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/stun/v2 v2.0.0 // indirect
github.com/pion/transport/v2 v2.2.1 // indirect
github.com/pion/transport/v3 v3.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus-community/pro-bing v0.4.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.55.0 // indirect
github.com/refraction-networking/utls v1.7.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rodaine/table v1.3.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/schollz/progressbar/v3 v3.17.1 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/showwin/speedtest-go v1.7.10 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/tsosunchia/powclient v0.2.0 // indirect
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/tools v0.38.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect
)

354
go.sum
View File

@@ -1,354 +0,0 @@
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY=
github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=
github.com/imroc/req/v3 v3.54.0 h1:kwWJSpT7OvjJ/Q8ykp+69Ye5H486RKDcgEoepw1Ren4=
github.com/imroc/req/v3 v3.54.0/go.mod h1:P8gCJjG/XNUFeP6WOi40VAXfYwT+uPM00xvoBWiwzUQ=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jaypipes/ghw v0.17.0 h1:EVLJeNcy5z6GK/Lqby0EhBpynZo+ayl8iJWY0kbEUJA=
github.com/jaypipes/ghw v0.17.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic=
github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGFXMCeE4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jsdelivr/globalping-cli v1.5.1 h1:7RZNmIljSBXe0xBeOoGQHXZNwHo6zDuQ0BI9hF12gLY=
github.com/jsdelivr/globalping-cli v1.5.1/go.mod h1:Gw70OWvN6hIt0t4hftyUhcHuJQMTn4CvoobJiaTU0qg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/nxtrace/NTrace-core v1.5.0 h1:n+a/FObw/+CcqvhuSQiWcm1q+ODtfo7Wt3VmaIx504I=
github.com/nxtrace/NTrace-core v1.5.0/go.mod h1:/jME48iJ7QaVTzsrTPQyTJ+yExhjeWjax2L6uBd4ckk=
github.com/oneclickvirt/UnlockTests v0.0.35-20260207053956 h1:yccGrw/sYOHZMaFJghPVN3Xn6JyTOXsEQc9v0I92k3M=
github.com/oneclickvirt/UnlockTests v0.0.35-20260207053956/go.mod h1:oOa6wj/qECtRMxwBO6D7o0L0F0Q/5sQ747OCnFQqoGE=
github.com/oneclickvirt/backtrace v0.0.8-20251109090457 h1:599/R/qMAtfPCPG1bPoi6KbjNJzVkKtxm8dvVIdtn5o=
github.com/oneclickvirt/backtrace v0.0.8-20251109090457/go.mod h1:mj9TSow7FNszBb3bQj2Hhm41LwBo7HQP6sgaPtovKdM=
github.com/oneclickvirt/basics v0.0.16-20251112033526 h1:bgoLaqStV3a6mbPiM++0mYizd278GVa6J6yeIiusV+A=
github.com/oneclickvirt/basics v0.0.16-20251112033526/go.mod h1:2PV+1ge01zb0Sqzj2V2I7P0wAdFSLF1XgAiumchJJbg=
github.com/oneclickvirt/cputest v0.0.12-20251111095842 h1:ixZUvIkSlsIZfsg+dNDKq/FTofEtUjfA2LtpTrNr/6s=
github.com/oneclickvirt/cputest v0.0.12-20251111095842/go.mod h1:vjlH8tkPFft1tlLOpeNskXVvurxkHaJ3+dgFxQGLXY4=
github.com/oneclickvirt/dd v0.0.2-20250808062818 h1:0KHrKkdpL5oBE1OHsrRd2siRw4/2k6f9LBaP7T4JpOc=
github.com/oneclickvirt/dd v0.0.2-20250808062818/go.mod h1:tImu9sPTkLWo2tf1dEN1xQzrylWKauj9hbU8PHfyAeU=
github.com/oneclickvirt/defaultset v0.0.2-20240624082446 h1:5Pg3mK/u/vQvSz7anu0nxzrNdELi/AcDAU1mMsmPzyc=
github.com/oneclickvirt/defaultset v0.0.2-20240624082446/go.mod h1:e9Jt4tf2sbemCtc84/XgKcHy9EZ2jkc5x2sW1NiJS+E=
github.com/oneclickvirt/disktest v0.0.10-20250924030424 h1:56Aq2xygO/vA/co5vJ7/MQTNijIDl8eYbVk8uCWN4mI=
github.com/oneclickvirt/disktest v0.0.10-20250924030424/go.mod h1:Vp3iMVBD4ccReDJz5n5SlzUdq0kDuVhpRklQk21KT+8=
github.com/oneclickvirt/fio v0.0.2-20250808045755 h1:eWihCRWcalJjPIdrF8dMe68ZiPnMkSfHC8ENvElp/xE=
github.com/oneclickvirt/fio v0.0.2-20250808045755/go.mod h1:NIq+XYTey68KNERGIy/oRDlzpwLzBVoHOCiqX8didsE=
github.com/oneclickvirt/gostun v0.0.5-20250727155022 h1:/e3gSUrOp1tg/1NTRx+P8B51OGcP26Q6//5EoSIjOvk=
github.com/oneclickvirt/gostun v0.0.5-20250727155022/go.mod h1:pfp7MFZJK9n/KTLAVqqFcCAns4xqMykmjI+1UeF/vdE=
github.com/oneclickvirt/mbw v0.0.1-20250808061222 h1:WGXOe6QvHiDRhPVMI0VcctjzW08kGvJf50yq5YeZCtw=
github.com/oneclickvirt/mbw v0.0.1-20250808061222/go.mod h1:0Vq6NRpyLmGUdfHfL3uDcFsuZhi7KlG+OCs5ky2757Y=
github.com/oneclickvirt/memorytest v0.0.10-20251218032900 h1:SmRFfPLyGfTVWIgC50lEGgOpbqahtMHIlyOMSbrhj9Y=
github.com/oneclickvirt/memorytest v0.0.10-20251218032900/go.mod h1:4kiHsEWkW9r3/1ZcV5xIweU0smiKP0IRfQj74AUIiVI=
github.com/oneclickvirt/nt3 v0.0.11-20260112140912 h1:e3tgkEmydsML6ziOdWwsVGwysTRYS82SuWrP0HnIw9g=
github.com/oneclickvirt/nt3 v0.0.11-20260112140912/go.mod h1:u/y3sMhyt4wiQlR7yS68CudwjXCa/4V6ozWI7awsCws=
github.com/oneclickvirt/pingtest v0.0.9-20251104112920 h1:j3Fjhy0YHT/VF7iuAVVELaRXkquvRd64tWWfFLJs01o=
github.com/oneclickvirt/pingtest v0.0.9-20251104112920/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900 h1:AomzdppSOFB70AJESQhlp0IPbsHTTJGimAWDk2TzCWM=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900/go.mod h1:9sjMDPCd4Z40wkYB0S9gQPGH8YPtnNE1ZJthVIuHUzA=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740 h1:1NUrNt5ay6/xVNC5x62UrQjPqK8jgbKtyjBml/3boZg=
github.com/oneclickvirt/speedtest v0.0.11-20251102151740/go.mod h1:fy0II2Wo7kDWVBKTwcHdodZwyfmJo0g8N9V02EwQDZE=
github.com/oneclickvirt/stream v0.0.2-20250924154001 h1:GuJWdiPkoK84+y/+oHKr2Ghl3c/MzS9Z5m1nM+lMmy4=
github.com/oneclickvirt/stream v0.0.2-20250924154001/go.mod h1:oWaizaHTC2VQciBC9RfaLbAOf8qeR6n20/gY7QxriDE=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus-community/pro-bing v0.4.1 h1:aMaJwyifHZO0y+h8+icUz0xbToHbia0wdmzdVZ+Kl3w=
github.com/prometheus-community/pro-bing v0.4.1/go.mod h1:aLsw+zqCaDoa2RLVVSX3+UiCkBBXTMtZC3c7EkfWnAE=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo=
github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE=
github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/showwin/speedtest-go v1.7.10 h1:9o5zb7KsuzZKn+IE2//z5btLKJ870JwO6ETayUkqRFw=
github.com/showwin/speedtest-go v1.7.10/go.mod h1:Ei7OCTmNPdWofMadzcfgq1rUO7mvJy9Jycj//G7vyfA=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/tsosunchia/powclient v0.2.0 h1:BDrI3O69CbzarbD+CnnY10Kuwn8xlmtQR0m5tBp+BG8=
github.com/tsosunchia/powclient v0.2.0/go.mod h1:fkb7tTW+HMH3ZWZzQUgwvvFKMj/8Ys+C8Sm/uGQzDA0=
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f h1:glX3VZCYwW1/OmFxOjazfCtBLxXB3YNZk9LF2lYx+Lw=
github.com/xjasonlyu/windivert-go v0.0.0-20201010013527-4239d0afa76f/go.mod h1:gh//RKyt2Gesx3eOj3ulzrSQ60ySj2UA4qnOdrtarvg=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201008064518-c1f3e3309c71/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=

113
goecs.go
View File

@@ -1,113 +0,0 @@
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"runtime"
"sync"
"syscall"
"time"
unlocktestmodel "github.com/oneclickvirt/UnlockTests/model"
backtracemodel "github.com/oneclickvirt/backtrace/model"
basicmodel "github.com/oneclickvirt/basics/model"
cputestmodel "github.com/oneclickvirt/cputest/model"
disktestmodel "github.com/oneclickvirt/disktest/disk"
menu "github.com/oneclickvirt/ecs/internal/menu"
params "github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/runner"
"github.com/oneclickvirt/ecs/utils"
gostunmodel "github.com/oneclickvirt/gostun/model"
memorytestmodel "github.com/oneclickvirt/memorytest/memory"
nt3model "github.com/oneclickvirt/nt3/model"
ptmodel "github.com/oneclickvirt/pingtest/model"
speedtestmodel "github.com/oneclickvirt/speedtest/model"
)
var (
ecsVersion = "v0.1.122" // 融合怪版本号
configs = params.NewConfig(ecsVersion) // 全局配置实例
userSetFlags = make(map[string]bool) // 用于跟踪哪些参数是用户显式设置的
)
func initLogger() {
if configs.EnableLogger {
gostunmodel.EnableLoger = true
basicmodel.EnableLoger = true
cputestmodel.EnableLoger = true
memorytestmodel.EnableLoger = true
disktestmodel.EnableLoger = true
unlocktestmodel.EnableLoger = true
ptmodel.EnableLoger = true
backtracemodel.EnableLoger = true
nt3model.EnableLoger = true
speedtestmodel.EnableLoger = true
}
}
func handleLanguageSpecificSettings() {
if configs.Language == "en" {
configs.BacktraceStatus = false
configs.Nt3Status = false
}
if !configs.EnableUpload {
configs.SecurityTestStatus = false
}
}
func main() {
configs.ParseFlags(os.Args[1:])
if configs.HandleHelpAndVersion("goecs") {
return
}
initLogger()
utils.CheckAndFixAndroidDNS(configs.Language)
preCheck := utils.CheckPublicAccess(3 * time.Second)
go func() {
if preCheck.Connected {
http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
}
}()
if configs.MenuMode {
menu.HandleMenuMode(preCheck, configs)
} else {
configs.OnlyIpInfoCheck = true
}
handleLanguageSpecificSettings()
if !preCheck.Connected {
configs.EnableUpload = false
}
var (
wg1, wg2, wg3 sync.WaitGroup
basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo string
output, tempOutput string
outputMutex sync.Mutex
infoMutex sync.Mutex // 保护并发字符串写入
)
startTime := time.Now()
uploadDone := make(chan bool, 1)
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go runner.HandleSignalInterrupt(sig, configs, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch configs.Language {
case "zh":
runner.RunChineseTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex, &infoMutex)
case "en":
runner.RunEnglishTests(preCheck, configs, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex, &infoMutex)
default:
fmt.Println("Unsupported language")
}
if configs.AnalyzeResult {
output = runner.AppendAnalysisSummary(configs, output, tempOutput, &outputMutex)
}
if preCheck.Connected {
runner.HandleUploadResults(configs, output)
}
configs.Finish = true
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
}

694
goecs.sh
View File

@@ -1,694 +0,0 @@
#!/bin/sh
# From https://github.com/oneclickvirt/ecs
# 2025.10.08
# curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh
# 或
# curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh
cat <<"EOF"
,ad8888ba, ,ad8888ba, 88888888888 ,ad8888ba, ad88888ba
d8"' `"8b d8"' `"8b 88 d8"' `"8b d8" "8b
d8' d8' `8b 88 d8' Y8a
88 88 88 88aaaaa 88 `"Y8aaaaa,
88 88888 88 88 88""""" 88 `"""""8b,
Y8, 88 Y8, ,8P 88 Y8, `8b
Y8a. .a88 Y8a. .a8P 88 Y8a. .a8P Y8a a8P
`"Y88888P" `"Y8888Y"' 88888888888 `"Y8888Y"' "Y88888P"
EOF
cd /root >/dev/null 2>&1
if [ ! -d "/usr/bin/" ]; then
mkdir -p "/usr/bin/"
fi
_red() { printf "\033[31m\033[01m%s\033[0m\n" "$*"; }
_green() { printf "\033[32m\033[01m%s\033[0m\n" "$*"; }
_yellow() { printf "\033[33m\033[01m%s\033[0m\n" "$*"; }
_blue() { printf "\033[36m\033[01m%s\033[0m\n" "$*"; }
reading() {
printf "\033[32m\033[01m%s\033[0m" "$1"
read "$2"
}
check_cdn() {
local o_url="$1"
local cdn_url
for cdn_url in $cdn_urls; do
if curl -4 -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then
cdn_success_url="$cdn_url"
return 0
fi
sleep 0.5
done
cdn_success_url=""
return 1
}
check_cdn_file() {
check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test"
if [ -n "$cdn_success_url" ]; then
_green "CDN available, using CDN"
else
_yellow "No CDN available, no use CDN"
fi
}
download_file() {
local url="$1"
local output="$2"
if ! wget -O "$output" "$url" 2>/dev/null; then
_yellow "wget failed, trying curl..."
if ! curl -L -o "$output" "$url" 2>/dev/null; then
_red "Both wget and curl failed. Unable to download the file."
return 1
fi
fi
return 0
}
check_china() {
_yellow "Detecting IP region......"
if [ -z "${CN}" ]; then
if curl -m 6 -s https://ipapi.co/json | grep -q 'China'; then
_yellow "According to ipapi.co, this IP may be located in China"
if [ "$noninteractive" != "true" ]; then
reading "Use China mirror for installation? ([y]/n) " input
case $input in
[yY][eE][sS] | [yY] | "")
_green "China mirror selected"
CN=true
;;
[nN][oO] | [nN])
_yellow "China mirror not selected"
CN=false
;;
*)
_green "China mirror selected"
CN=true
;;
esac
else
# In non-interactive mode, default to not using China mirror
CN=false
fi
else
CN=false
fi
fi
}
get_memory_size() {
if [ -f /proc/meminfo ]; then
local mem_kb
mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
echo $((mem_kb / 1024)) # Convert to MB
return 0
fi
if command -v free >/dev/null 2>&1; then
local mem_kb
mem_kb=$(free -m | awk '/^Mem:/ {print $2}')
echo "$mem_kb" # Already in MB
return 0
fi
if command -v sysctl >/dev/null 2>&1; then
local mem_bytes
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || sysctl -n hw.physmem 2>/dev/null)
if [ -n "$mem_bytes" ]; then
echo $((mem_bytes / 1024 / 1024)) # Convert to MB
return 0
fi
fi
echo 0
return 1
}
cleanup_epel() {
_yellow "Cleaning up EPEL repositories..."
rm -f /etc/yum.repos.d/*epel*
yum clean all >/dev/null 2>&1
}
goecs_check() {
if command -v apt-get >/dev/null 2>&1; then
INSTALL_CMD="apt-get -y install"
elif command -v yum >/dev/null 2>&1; then
INSTALL_CMD="yum -y install"
elif command -v dnf >/dev/null 2>&1; then
INSTALL_CMD="dnf -y install"
elif command -v pacman >/dev/null 2>&1; then
INSTALL_CMD="pacman -S --noconfirm"
elif command -v apk >/dev/null 2>&1; then
INSTALL_CMD="apk add"
elif command -v zypper >/dev/null 2>&1; then
INSTALL_CMD="zypper install -y"
fi
if ! command -v unzip >/dev/null 2>&1; then
_green "Installing unzip"
${INSTALL_CMD} unzip
fi
if ! command -v curl >/dev/null 2>&1; then
_green "Installing curl"
${INSTALL_CMD} curl
fi
os=$(uname -s 2>/dev/null || echo "Unknown")
arch=$(uname -m 2>/dev/null || echo "Unknown")
check_china
ECS_VERSION="0.1.122"
for api in \
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \
"https://githubapi.spiritlhl.top/repos/oneclickvirt/ecs/releases/latest"; do
ECS_VERSION=$(curl -m 6 -sSL "$api" | awk -F \" '/tag_name/{gsub(/^v/,"",$4); print $4}')
if [ -n "$ECS_VERSION" ]; then
break
fi
sleep 1
done
if [ -z "$ECS_VERSION" ]; then
_yellow "Unable to get version info, using default version 0.1.122"
ECS_VERSION="0.1.122"
fi
version_output=""
for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do
if command -v "$cmd_path" >/dev/null 2>&1; then
version_output=$($cmd_path -v command 2>/dev/null)
break
fi
done
if [ -n "$version_output" ]; then
extracted_version=${version_output#*v}
extracted_version=${extracted_version#v}
if [ -n "$extracted_version" ]; then
ecs_version=$ECS_VERSION
if [ "$(printf '%s\n%s\n' "$extracted_version" "$ecs_version" | sort -V | tail -n 1)" = "$extracted_version" ]; then
_green "goecs version ($extracted_version) is up to date, no upgrade needed"
return 0
else
_yellow "goecs version ($extracted_version) < $ecs_version, upgrade needed, starting in 5 seconds"
rm -rf /usr/bin/goecs /usr/local/bin/goecs ./goecs
fi
fi
else
_green "goecs not found, installation needed, starting in 5 seconds"
fi
sleep 5
if [ "$CN" = "true" ]; then
_yellow "Using China mirror for download..."
base_url="https://cnb.cool/oneclickvirt/ecs/-/git/raw/main"
else
cdn_urls="https://cdn0.spiritlhl.top/ http://cdn3.spiritlhl.net/ http://cdn1.spiritlhl.net/ http://cdn2.spiritlhl.net/"
check_cdn_file
if [ -n "$cdn_success_url" ]; then
base_url="${cdn_success_url}https://github.com/oneclickvirt/ecs/releases/download/v${ECS_VERSION}"
else
base_url="https://github.com/oneclickvirt/ecs/releases/download/v${ECS_VERSION}"
fi
fi
local zip_file=""
case $os in
Linux|linux|LINUX)
case $arch in
x86_64|amd64|x64) zip_file="goecs_linux_amd64.zip" ;;
i386|i686) zip_file="goecs_linux_386.zip" ;;
aarch64|arm64|armv8|armv8l) zip_file="goecs_linux_arm64.zip" ;;
arm|armv7l) zip_file="goecs_linux_arm.zip" ;;
mips) zip_file="goecs_linux_mips.zip" ;;
mipsle) zip_file="goecs_linux_mipsle.zip" ;;
s390x) zip_file="goecs_linux_s390x.zip" ;;
riscv64) zip_file="goecs_linux_riscv64.zip" ;;
*) zip_file="goecs_linux_amd64.zip" ;;
esac
;;
FreeBSD|freebsd)
case $arch in
x86_64|amd64) zip_file="goecs_freebsd_amd64.zip" ;;
i386|i686) zip_file="goecs_freebsd_386.zip" ;;
arm64|aarch64) zip_file="goecs_freebsd_arm64.zip" ;;
*) zip_file="goecs_freebsd_amd64.zip" ;;
esac
;;
Darwin|darwin)
case $arch in
x86_64|amd64) zip_file="goecs_darwin_amd64.zip" ;;
arm64|aarch64) zip_file="goecs_darwin_arm64.zip" ;;
*) zip_file="goecs_darwin_amd64.zip" ;;
esac
;;
*)
_yellow "Unknown system $os, trying amd64 version"
zip_file="goecs_linux_amd64.zip"
;;
esac
download_url="${base_url}/${zip_file}"
_green "Downloading $download_url"
local max_retries=3
local retry_count=0
while [ $retry_count -lt $max_retries ]; do
if download_file "$download_url" "goecs.zip"; then
break
fi
_yellow "Download failed, retrying (${retry_count}/${max_retries})..."
retry_count=$((retry_count + 1))
sleep 2
done
if [ $retry_count -eq $max_retries ]; then
_red "Download failed, please check your network connection or download manually"
return 1
fi
if ! unzip -o goecs.zip >/dev/null 2>&1; then
_red "Extraction failed"
return 1
fi
rm -f goecs.zip README.md LICENSE README_EN.md
chmod 777 goecs
installed_to_system=false
for install_path in "/usr/bin" "/usr/local/bin"; do
if [ -d "$install_path" ]; then
if cp -f goecs "$install_path/" 2>/dev/null; then
installed_to_system=true
break
fi
fi
done
if [ "$installed_to_system" = "false" ]; then
_yellow "Insufficient permissions to install to system path, goecs is kept in the current directory"
_yellow "Please use the following command to run: ./goecs"
fi
if [ "$os" != "Darwin" ]; then
PARAM="net.ipv4.ping_group_range"
NEW_VALUE="0 2147483647"
if [ -f /etc/sysctl.conf ]; then
if grep -q "^$PARAM" /etc/sysctl.conf 2>/dev/null; then
sed -i "s/^$PARAM.*/$PARAM = $NEW_VALUE/" /etc/sysctl.conf 2>/dev/null || true
else
echo "$PARAM = $NEW_VALUE" >> /etc/sysctl.conf 2>/dev/null || true
fi
sysctl -p >/dev/null 2>&1 || true
fi
fi
setcap cap_net_raw=+ep goecs 2>/dev/null || true
setcap cap_net_raw=+ep /usr/bin/goecs 2>/dev/null || true
setcap cap_net_raw=+ep /usr/local/bin/goecs 2>/dev/null || true
_green "goecs installation complete, current version:"
goecs -v 2>/dev/null || ./goecs -v
}
InstallSysbench() {
if [ -f "/etc/opencloudos-release" ]; then # OpenCloudOS
Var_OSRelease="opencloudos"
elif [ -f "/etc/centos-release" ]; then # CentOS
Var_OSRelease="centos"
elif [ -f "/etc/fedora-release" ]; then # Fedora
Var_OSRelease="fedora"
elif [ -f "/etc/redhat-release" ]; then # RedHat
Var_OSRelease="rhel"
elif [ -f "/etc/astra_version" ]; then # Astra
Var_OSRelease="astra"
elif [ -f "/etc/lsb-release" ]; then # Ubuntu
Var_OSRelease="ubuntu"
elif [ -f "/etc/debian_version" ]; then # Debian
Var_OSRelease="debian"
elif [ -f "/etc/alpine-release" ]; then # Alpine Linux
Var_OSRelease="alpinelinux"
elif [ -f "/etc/almalinux-release" ]; then # almalinux
Var_OSRelease="almalinux"
elif [ -f "/etc/arch-release" ]; then # archlinux
Var_OSRelease="arch"
elif [ -f "/etc/freebsd-update.conf" ]; then # freebsd
Var_OSRelease="freebsd"
else
Var_OSRelease="unknown" # 未知系统分支
fi
local mem_size
mem_size=$(get_memory_size)
if [ -z "$mem_size" ] || [ "$mem_size" -eq 0 ]; then
echo "Error: Unable to determine memory size or memory size is zero."
elif [ "$mem_size" -lt 1024 ]; then
_red "Warning: Your system has less than 1GB RAM (${mem_size}MB)"
if [ "$noninteractive" != "true" ]; then
reading "Do you want to continue with EPEL installation? (y/N): " confirm
case "$confirm" in
[Yy]*)
;;
*)
_yellow "Skipping EPEL installation"
return 1
;;
esac
fi
case "$Var_OSRelease" in
ubuntu | debian | astra)
if ! apt-get install -y sysbench; then
apt-get --fix-broken install -y
apt-get install --no-install-recommends -y sysbench
fi
;;
centos | rhel | almalinux | redhat | opencloudos)
if ! yum -y install epel-release || ! yum -y install sysbench; then
if command -v dnf >/dev/null 2>&1; then
dnf install epel-release -y
dnf install sysbench -y
fi
fi
;;
fedora)
dnf -y install sysbench ;;
arch)
pacman -S --needed --noconfirm sysbench
pacman -S --needed --noconfirm libaio
ldconfig
;;
freebsd)
pkg install -y sysbench ;;
alpinelinux)
if [ "$noninteractive" != "true" ]; then
reading "Do you want to continue with sysbench installation? (y/N): " confirm
case "$confirm" in
[Yy]*)
;;
*)
_yellow "Skipping sysbench installation"
return 1
;;
esac
fi
ALPINE_VERSION=$(grep -o '^[0-9]\+\.[0-9]\+' /etc/alpine-release)
COMMUNITY_REPO="http://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community"
if ! grep -q "^${COMMUNITY_REPO}" /etc/apk/repositories; then
echo "Enabling community repository..."
echo "${COMMUNITY_REPO}" >> /etc/apk/repositories
echo "Community repository has been added."
echo "Updating apk package index..."
apk update && echo "Package index updated successfully."
else
echo "Community repository is already enabled."
fi
if apk info sysbench >/dev/null 2>&1; then
echo "Sysbench already installed."
else
if ! apk add --no-cache sysbench; then
echo "Sysbench Module not found, installing ..."
echo "SysBench Current not support Alpine Linux, Skipping..."
Var_Skip_SysBench="1"
else
echo "Sysbench installed successfully."
fi
fi
;;
*)
_red "Sysbench Install Error: Unknown OS release: $Var_OSRelease" ;;
esac
case "$SYSTEM" in
CentOS|RHEL|AlmaLinux)
_yellow "Installing EPEL repository..."
if ! yum -y install epel-release; then
_red "EPEL installation failed!"
cleanup_epel
_yellow "Attempting to continue without EPEL..."
fi
;;
esac
fi
}
env_check() {
# 检测是否为 macOS 系统
if [ "$(uname -s)" = "Darwin" ]; then
_green "Detected macOS system"
_green "macOS has built-in tools, skipping dependency installation"
_green "Environment preparation complete."
_green "Next command is: ./goecs.sh install"
return 0
fi
if [ -f /etc/opencloudos-release ]; then
SYS="opencloudos"
elif [ -s /etc/os-release ]; then
SYS="$(grep -i pretty_name /etc/os-release | cut -d \" -f2)"
elif command -v hostnamectl >/dev/null 2>&1; then
SYS="$(hostnamectl | grep -i system | cut -d : -f2 | sed 's/^ *//')"
elif command -v lsb_release >/dev/null 2>&1; then
SYS="$(lsb_release -sd)"
elif [ -s /etc/lsb-release ]; then
SYS="$(grep -i description /etc/lsb-release | cut -d \" -f2)"
elif [ -s /etc/redhat-release ]; then
SYS="$(cat /etc/redhat-release)"
elif [ -s /etc/issue ]; then
SYS="$(head -n1 /etc/issue | cut -d '\' -f1 | sed '/^[ ]*$/d')"
else
SYS="$(uname -s)"
fi
SYSTEM=""
sys_lower=$(echo "$SYS" | tr '[:upper:]' '[:lower:]')
if echo "$sys_lower" | grep -E "debian|astra" >/dev/null 2>&1; then
SYSTEM="Debian"
UPDATE_CMD="apt-get update"
INSTALL_CMD="apt-get -y install"
REMOVE_CMD="apt-get -y remove"
UNINSTALL_CMD="apt-get -y autoremove"
elif echo "$sys_lower" | grep -E "ubuntu" >/dev/null 2>&1; then
SYSTEM="Ubuntu"
UPDATE_CMD="apt-get update"
INSTALL_CMD="apt-get -y install"
REMOVE_CMD="apt-get -y remove"
UNINSTALL_CMD="apt-get -y autoremove"
elif echo "$sys_lower" | grep -E "centos|red hat|kernel|oracle linux|alma|rocky" >/dev/null 2>&1; then
SYSTEM="CentOS"
UPDATE_CMD="yum -y update"
INSTALL_CMD="yum -y install"
REMOVE_CMD="yum -y remove"
UNINSTALL_CMD="yum -y autoremove"
elif echo "$sys_lower" | grep -E "amazon linux" >/dev/null 2>&1; then
SYSTEM="CentOS"
UPDATE_CMD="yum -y update"
INSTALL_CMD="yum -y install"
REMOVE_CMD="yum -y remove"
UNINSTALL_CMD="yum -y autoremove"
elif echo "$sys_lower" | grep -E "fedora" >/dev/null 2>&1; then
SYSTEM="Fedora"
UPDATE_CMD="yum -y update"
INSTALL_CMD="yum -y install"
REMOVE_CMD="yum -y remove"
UNINSTALL_CMD="yum -y autoremove"
elif echo "$sys_lower" | grep -E "arch" >/dev/null 2>&1; then
SYSTEM="Arch"
UPDATE_CMD="pacman -Sy"
INSTALL_CMD="pacman -Sy --noconfirm --needed"
REMOVE_CMD="pacman -Rsc --noconfirm"
UNINSTALL_CMD="pacman -Rns --noconfirm"
elif echo "$sys_lower" | grep -E "freebsd" >/dev/null 2>&1; then
SYSTEM="FreeBSD"
UPDATE_CMD="pkg update"
INSTALL_CMD="pkg install -y"
REMOVE_CMD="pkg delete"
UNINSTALL_CMD="pkg autoremove"
elif echo "$sys_lower" | grep -E "alpine" >/dev/null 2>&1; then
SYSTEM="Alpine"
UPDATE_CMD="apk update"
INSTALL_CMD="apk add --no-cache"
REMOVE_CMD="apk del"
UNINSTALL_CMD="apk autoremove"
elif echo "$sys_lower" | grep -E "openbsd" >/dev/null 2>&1; then
SYSTEM="OpenBSD"
UPDATE_CMD="pkg_add -qu"
INSTALL_CMD="pkg_add -I"
REMOVE_CMD="pkg_delete -I"
UNINSTALL_CMD="pkg_delete -a"
elif echo "$sys_lower" | grep -E "opencloudos" >/dev/null 2>&1; then
SYSTEM="OpenCloudOS"
UPDATE_CMD="yum -y update"
INSTALL_CMD="yum -y install"
REMOVE_CMD="yum -y remove"
UNINSTALL_CMD="yum -y autoremove"
fi
if [ -z "$SYSTEM" ]; then
_yellow "Unable to recognize system, trying common package managers..."
if command -v apt-get >/dev/null 2>&1; then
SYSTEM="Unknown-Debian"
UPDATE_CMD="apt-get update"
INSTALL_CMD="apt-get -y install"
REMOVE_CMD="apt-get -y remove"
UNINSTALL_CMD="apt-get -y autoremove"
elif command -v yum >/dev/null 2>&1; then
SYSTEM="Unknown-RHEL"
UPDATE_CMD="yum -y update"
INSTALL_CMD="yum -y install"
REMOVE_CMD="yum -y remove"
UNINSTALL_CMD="yum -y autoremove"
elif command -v dnf >/dev/null 2>&1; then
SYSTEM="Unknown-Fedora"
UPDATE_CMD="dnf -y update"
INSTALL_CMD="dnf -y install"
REMOVE_CMD="dnf -y remove"
UNINSTALL_CMD="dnf -y autoremove"
elif command -v pacman >/dev/null 2>&1; then
SYSTEM="Unknown-Arch"
UPDATE_CMD="pacman -Sy"
INSTALL_CMD="pacman -S --noconfirm"
REMOVE_CMD="pacman -R --noconfirm"
UNINSTALL_CMD="pacman -Rns --noconfirm"
elif command -v apk >/dev/null 2>&1; then
SYSTEM="Unknown-Alpine"
UPDATE_CMD="apk update"
INSTALL_CMD="apk add"
REMOVE_CMD="apk del"
UNINSTALL_CMD="apk del"
elif command -v zypper >/dev/null 2>&1; then
SYSTEM="Unknown-SLES"
UPDATE_CMD="zypper refresh"
INSTALL_CMD="zypper install -y"
REMOVE_CMD="zypper remove -y"
UNINSTALL_CMD="zypper remove -y"
else
_red "Unable to recognize package manager, exiting installation"
exit 1
fi
fi
_green "System information: $SYSTEM"
_green "Update command: $UPDATE_CMD"
_green "Install command: $INSTALL_CMD"
cdn_urls="https://cdn0.spiritlhl.top/ http://cdn3.spiritlhl.net/ http://cdn1.spiritlhl.net/ http://cdn2.spiritlhl.net/"
check_cdn_file
_yellow "Warning: System update will be performed"
_yellow "This operation may:"
_yellow "1. Take considerable time"
_yellow "2. Cause temporary network interruptions"
_yellow "3. Impact system stability"
_yellow "4. Affect subsequent system startups"
if [ "$noninteractive" != "true" ]; then
reading "Continue with system update? (y/N): " update_confirm
case "$update_confirm" in
[Yy]*)
_green "Updating system package manager..."
if ! ${UPDATE_CMD} 2>/dev/null; then
_red "System update failed!"
fi
;;
*)
_yellow "Skipping system update"
_yellow "Note: Some packages may fail to install"
;;
esac
fi
for cmd in sudo wget tar unzip iproute2 systemd-detect-virt dd fio; do
if ! command -v "$cmd" >/dev/null 2>&1; then
_green "Installing $cmd"
${INSTALL_CMD} "$cmd"
fi
done
if ! command -v sysbench >/dev/null 2>&1; then
_green "Installing sysbench"
if ! ${INSTALL_CMD} sysbench; then
_red "Unable to install sysbench through package manager"
_yellow "Sysbench installation skipped"
fi
fi
if ! command -v geekbench >/dev/null 2>&1; then
_green "Installing geekbench"
curl -L "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/cputest/main/dgb.sh" -o dgb.sh && chmod +x dgb.sh
sh dgb.sh -v gb5
rm -rf dgb.sh
fi
if ! command -v speedtest >/dev/null 2>&1; then
_green "Installing speedtest"
curl -L "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/speedtest/main/dspt.sh" -o dspt.sh && chmod +x dspt.sh
sh dspt.sh
rm -rf dspt.sh
rm -rf speedtest.tar.gz
fi
if ! command -v ping >/dev/null 2>&1; then
_green "Installing ping"
${INSTALL_CMD} iputils-ping >/dev/null 2>&1 || ${INSTALL_CMD} ping >/dev/null 2>&1
fi
if ! grep -q "^net.ipv4.ping_group_range = 0 2147483647$" /etc/sysctl.conf 2>/dev/null; then
echo "net.ipv4.ping_group_range = 0 2147483647" >> /etc/sysctl.conf 2>/dev/null
sysctl -p >/dev/null 2>&1
fi
_green "Environment preparation complete."
_green "Next command is: ./goecs.sh install"
}
uninstall_goecs() {
rm -rf /root/goecs
rm -rf /usr/bin/goecs
_green "The command (goecs) has been uninstalled."
}
show_help() {
cat <<"EOF"
可用命令:
./goecs.sh env 检查并安装依赖包
注意: macOS系统会自动跳过依赖安装
警告: 此命令会执行系统更新(可选择),可能:
1. 耗时较长
2. 导致网络短暂中断
3. 影响系统稳定性
4. 影响后续系统启动
对于内存小于1GB的系统还可能导致:
1. 系统卡死
2. SSH连接中断
3. 关键服务失败
推荐:
环境依赖安装过程中挂起执行
可选组件:
sysbench/geekbench (CPU性能测试)
sudo, tar, unzip, dd, fio
speedtest (网络测试)
ping (网络连通性测试)
systemd-detect-virt/dmidecode (系统信息检测)
./goecs.sh install 安装 goecs 命令
./goecs.sh upgrade 升级 goecs 命令
./goecs.sh uninstall 卸载 goecs 命令
./goecs.sh help 显示此消息
Available commands:
./goecs.sh env Check and Install dependencies
Note: macOS systems will skip dependency installation
Warning: This command performs system update(optional), which may:
1. Take considerable time
2. Cause temporary network interruptions
3. Impact system stability
4. Affect subsequent system startups
For systems with less than 1GB RAM, additional risks:
1. System freeze
2. SSH connection loss
3. Critical service failures
Recommended:
Hanging execution during environment dependency installation
Optional components:
sysbench/geekbench (CPU testing)
sudo, tar, unzip, dd, fio
speedtest (Network testing)
ping (Network connectivity)
systemd-detect-virt/dmidecode (System info detection)
./goecs.sh install Install goecs command
./goecs.sh upgrade Upgrade goecs command
./goecs.sh uninstall Uninstall goecs command
./goecs.sh help Show this message
EOF
}
case "$1" in
"help")
show_help
;;
"env")
env_check
;;
"install" | "upgrade")
goecs_check
;;
"uninstall")
uninstall_goecs
;;
*)
echo "No command found."
echo
show_help
;;
esac

View File

@@ -1,9 +0,0 @@
package main
import (
"testing"
)
func Test(t *testing.T) {
main()
}

816
index.html Executable file
View File

@@ -0,0 +1,816 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="google-site-verification" content="wdrGBim_2XmtMrqxivze70saMiPQAiOhpmN3KAWb0Sw" />
<title>CPU Performance Ladder For Sysbench</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #fafafa;
color: #37352f;
line-height: 1.5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
}
.header {
text-align: center;
margin-bottom: 48px;
padding-bottom: 24px;
border-bottom: 1px solid #e9e9e7;
position: relative;
}
.header h1 {
font-size: 32px;
font-weight: 700;
color: #2d2d2d;
margin-bottom: 8px;
}
.header p {
font-size: 16px;
color: #6b6b6b;
}
.language-toggle {
position: absolute;
top: 0;
right: 0;
background: white;
border: 1px solid #e9e9e7;
border-radius: 8px;
overflow: hidden;
display: flex;
}
.language-toggle button {
padding: 8px 16px;
border: none;
background: transparent;
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
color: #6b6b6b;
}
.language-toggle button.active {
background: #0066cc;
color: white;
}
.language-toggle button:hover:not(.active) {
background: #f7f7f5;
}
.controls {
display: flex;
gap: 16px;
margin-bottom: 32px;
flex-wrap: wrap;
align-items: center;
}
.search-box {
flex: 1;
min-width: 200px;
position: relative;
display: flex;
gap: 8px;
}
.search-box input {
flex: 1;
padding: 12px 16px;
border: 1px solid #e9e9e7;
border-radius: 8px;
font-size: 14px;
background: white;
transition: all 0.2s ease;
}
.search-box input:focus {
outline: none;
border-color: #0066cc;
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
}
.search-buttons {
display: flex;
gap: 4px;
}
.search-btn {
padding: 12px 16px;
border: 1px solid #e9e9e7;
background: white;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s ease;
color: #6b6b6b;
}
.search-btn:hover {
background: #f7f7f5;
}
.search-btn.search {
background: #0066cc;
color: white;
border-color: #0066cc;
}
.search-btn.clear {
background: #e91e63;
color: white;
border-color: #e91e63;
}
.view-toggle {
display: flex;
background: white;
border: 1px solid #e9e9e7;
border-radius: 8px;
overflow: hidden;
}
.view-toggle button {
padding: 12px 20px;
border: none;
background: transparent;
cursor: pointer;
font-size: 14px;
transition: all 0.2s ease;
}
.view-toggle button.active {
background: #0066cc;
color: white;
}
.view-toggle button:hover:not(.active) {
background: #f7f7f5;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.stat-card {
background: white;
padding: 24px;
border-radius: 12px;
border: 1px solid #e9e9e7;
text-align: center;
transition: transform 0.2s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px -8px rgba(0, 0, 0, 0.1);
}
.stat-value {
font-size: 28px;
font-weight: 700;
color: #0066cc;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
color: #6b6b6b;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
margin-bottom: 32px;
flex-wrap: wrap;
}
.pagination button {
padding: 8px 16px;
border: 1px solid #e9e9e7;
background: white;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s ease;
}
.pagination button:hover:not(:disabled) {
background: #f7f7f5;
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination button.active {
background: #0066cc;
color: white;
border-color: #0066cc;
}
.pagination-info {
font-size: 14px;
color: #6b6b6b;
}
.page-jump {
display: flex;
align-items: center;
gap: 8px;
}
.page-jump input {
width: 60px;
padding: 6px 8px;
border: 1px solid #e9e9e7;
border-radius: 4px;
text-align: center;
font-size: 14px;
}
.page-jump button {
padding: 6px 12px;
font-size: 12px;
}
.cpu-groups {
display: grid;
gap: 24px;
}
.cpu-group {
background: white;
border-radius: 12px;
border: 1px solid #e9e9e7;
overflow: hidden;
transition: all 0.2s ease;
}
.cpu-group:hover {
box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.1);
}
.group-header {
padding: 20px 24px;
background: #f7f7f5;
border-bottom: 1px solid #e9e9e7;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.group-header:hover {
background: #f0f0ef;
}
.group-title-section {
display: flex;
align-items: center;
gap: 16px;
}
.rank-badge {
background: #0066cc;
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
min-width: 40px;
text-align: center;
}
.group-title {
font-size: 18px;
font-weight: 600;
color: #2d2d2d;
}
.group-stats {
display: flex;
gap: 24px;
font-size: 14px;
color: #6b6b6b;
}
.expand-icon {
transition: transform 0.2s ease;
font-size: 12px;
}
.cpu-group.expanded .expand-icon {
transform: rotate(180deg);
}
.group-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.cpu-group.expanded .group-content {
max-height: 2000px;
}
.cpu-table {
width: 100%;
border-collapse: collapse;
}
.cpu-table th,
.cpu-table td {
padding: 16px 24px;
text-align: left;
border-bottom: 1px solid #f0f0ef;
}
.cpu-table th {
font-weight: 600;
color: #6b6b6b;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
background: #fafafa;
}
.cpu-table td {
font-size: 14px;
}
.cpu-table tbody tr:hover {
background: #fafafa;
}
.score-cell {
font-weight: 600;
}
.score-single {
color: #0066cc;
}
.score-multi {
color: #e91e63;
}
.loading {
text-align: center;
padding: 60px 20px;
color: #6b6b6b;
}
.spinner {
display: inline-block;
width: 32px;
height: 32px;
border: 3px solid #f0f0ef;
border-top: 3px solid #0066cc;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 16px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.no-data {
text-align: center;
padding: 60px 20px;
color: #6b6b6b;
}
.footer {
margin-top: 60px;
padding: 32px 0;
border-top: 1px solid #e9e9e7;
text-align: center;
background: white;
border-radius: 12px;
}
.footer-content {
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
}
.footer-links {
display: flex;
gap: 32px;
align-items: center;
flex-wrap: wrap;
justify-content: center;
}
.footer-link {
color: #0066cc;
text-decoration: none;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
transition: color 0.2s ease;
}
.footer-link:hover {
color: #004499;
}
.footer-text {
font-size: 13px;
color: #6b6b6b;
}
@media (max-width: 768px) {
.container {
padding: 20px 16px;
}
.header {
position: static;
}
.language-toggle {
position: static;
margin-bottom: 16px;
align-self: center;
}
.header h1 {
font-size: 24px;
}
.controls {
flex-direction: column;
align-items: stretch;
}
.search-box {
min-width: auto;
flex-direction: column;
}
.search-buttons {
justify-content: center;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.stat-card {
padding: 16px;
}
.stat-value {
font-size: 20px;
}
.group-stats {
flex-direction: column;
gap: 8px;
}
.group-title-section {
flex-direction: column;
gap: 8px;
align-items: flex-start;
}
.cpu-table {
font-size: 12px;
}
.cpu-table th,
.cpu-table td {
padding: 12px 16px;
}
.pagination {
flex-wrap: wrap;
gap: 8px;
}
.footer-links {
flex-direction: column;
gap: 16px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="language-toggle">
<button id="langZh" class="active">中文</button>
<button id="langEn">English</button>
</div>
<h1 data-zh="CPU Performance Ladder For Sysbench" data-en="CPU Performance Ladder For Sysbench">CPU Performance Ladder For Sysbench</h1>
<p data-zh="Sysbench天梯图" data-en="Sysbench CPU Benchmark Rankings">Sysbench天梯图</p>
</div>
<div class="controls">
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索CPU型号或关键词..." data-placeholder-zh="搜索CPU型号或关键词..." data-placeholder-en="Search CPU model or keywords...">
<div class="search-buttons">
<button id="searchBtn" class="search-btn search" data-zh="搜索" data-en="Search">搜索</button>
<button id="clearBtn" class="search-btn clear" data-zh="清除" data-en="Clear">清除</button>
</div>
</div>
<div class="view-toggle">
<button id="compactView" class="active" data-zh="紧凑视图" data-en="Compact View">紧凑视图</button>
<button id="detailView" data-zh="详细视图" data-en="Detail View">详细视图</button>
</div>
</div>
<div class="stats-grid" id="statsGrid">
<div class="stat-card">
<div class="stat-value" id="totalCpus">-</div>
<div class="stat-label" data-zh="CPU型号总数" data-en="Total CPU Models">CPU型号总数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="totalTests">-</div>
<div class="stat-label" data-zh="测试样本数" data-en="Test Samples">测试样本数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="topSingle">-</div>
<div class="stat-label" data-zh="最高单核分数" data-en="Highest Single Score">最高单核分数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="topMulti">-</div>
<div class="stat-label" data-zh="最高多核分数" data-en="Highest Multi Score">最高多核分数</div>
</div>
</div>
<div id="loadingIndicator" class="loading">
<div class="spinner"></div>
<div data-zh="正在加载CPU数据..." data-en="Loading CPU data...">正在加载CPU数据...</div>
</div>
<div class="pagination" id="pagination" style="display: none;">
<button id="prevPage" data-zh="上一页" data-en="Previous">上一页</button>
<div class="pagination-info" id="pageInfo">-</div>
<div class="page-jump">
<span data-zh="跳转到" data-en="Go to">跳转到</span>
<input type="number" id="pageInput" min="1">
<span data-zh="页" data-en="page"></span>
<button id="jumpBtn" data-zh="跳转" data-en="Go">跳转</button>
</div>
<button id="nextPage" data-zh="下一页" data-en="Next">下一页</button>
</div>
<div id="cpuGroups" class="cpu-groups" style="display: none;"></div>
<div id="noData" class="no-data" style="display: none;">
<div data-zh="📄 未找到CPU数据文件" data-en="📄 CPU data file not found">📄 未找到CPU数据文件</div>
<p data-zh="请确保 cpu_statistics.json 文件存在于当前目录" data-en="Please ensure cpu_statistics.json file exists in current directory">请确保 cpu_statistics.json 文件存在于当前目录</p>
</div>
<div class="footer">
<div class="footer-content">
<div class="footer-links">
<a href="https://github.com/oneclickvirt/ecs" class="footer-link" target="_blank" rel="noopener">
<span>🚀</span>
<span data-zh="测试脚本" data-en="Test Script">测试脚本</span>
</a>
<a href="https://www.spiritlhl.net/" class="footer-link" target="_blank" rel="noopener">
<span>🌐</span>
<span data-zh="spiritlhl 官网" data-en="spiritlhl Official">spiritlhl 官网</span>
</a>
</div>
<div class="footer-text" data-zh="本项目隶属于 spiritlhl 旗下" data-en="This project is under spiritlhl">本项目隶属于 spiritlhl 旗下</div>
</div>
</div>
</div>
<script>
class CPUDashboard {
constructor() {
this.data = [];
this.groupedData = {};
this.filteredGroups = {};
this.sortedPrefixes = [];
this.allSortedPrefixes = [];
this.prefixRanks = {};
this.isDetailView = false;
this.searchTerm = '';
this.currentPage = 1;
this.itemsPerPage = 10;
this.currentLang = 'zh';
this.total_samples = 0;
this.initializeEventListeners();
this.loadData();
}
initializeEventListeners() {
const searchInput = document.getElementById('searchInput');
const searchBtn = document.getElementById('searchBtn');
const clearBtn = document.getElementById('clearBtn');
const performSearch = () => {
this.searchTerm = searchInput.value.toLowerCase();
this.currentPage = 1;
this.filterAndRender();
};
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') performSearch();
});
searchBtn.addEventListener('click', performSearch);
clearBtn.addEventListener('click', () => {
searchInput.value = '';
this.searchTerm = '';
this.currentPage = 1;
this.filterAndRender();
});
document.getElementById('compactView').addEventListener('click', () => {
this.setViewMode(false);
});
document.getElementById('detailView').addEventListener('click', () => {
this.setViewMode(true);
});
document.getElementById('prevPage').addEventListener('click', () => {
if (this.currentPage > 1) {
this.currentPage--;
this.renderGroups();
}
});
document.getElementById('nextPage').addEventListener('click', () => {
const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage);
if (this.currentPage < totalPages) {
this.currentPage++;
this.renderGroups();
}
});
document.getElementById('jumpBtn').addEventListener('click', () => {
const page = parseInt(document.getElementById('pageInput').value);
const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage);
if (page >= 1 && page <= totalPages) {
this.currentPage = page;
this.renderGroups();
}
});
document.getElementById('langZh').addEventListener('click', () => {
this.setLanguage('zh');
});
document.getElementById('langEn').addEventListener('click', () => {
this.setLanguage('en');
});
}
setLanguage(lang) {
this.currentLang = lang;
document.getElementById('langZh').classList.toggle('active', lang === 'zh');
document.getElementById('langEn').classList.toggle('active', lang === 'en');
document.querySelectorAll('[data-zh]').forEach(el => {
if (el.tagName === 'INPUT') {
el.placeholder = el.getAttribute(`data-placeholder-${lang}`);
} else {
el.textContent = el.getAttribute(`data-${lang}`);
}
});
this.updatePagination();
this.renderGroups();
}
async loadData() {
const cdnUrls = [
"http://cdn1.spiritlhl.net/",
"http://cdn2.spiritlhl.net/",
"http://cdn3.spiritlhl.net/",
"http://cdn4.spiritlhl.net/"
];
const urls = [
'cpu_statistics.json',
...cdnUrls.map(cdn => `${cdn}https://raw.githubusercontent.com/oneclickvirt/ecs/refs/heads/ranks/cpu_statistics.json`)
];
for (let url of urls) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error('请求失败');
const data = await response.json();
if (data.cpu_statistics) {
this.data = data.cpu_statistics.map(stat => ({
cpu_prefix: stat.cpu_prefix,
cpu_model: stat.cpu_model,
cpu_cores: stat.typical_cores,
single_score: stat.max_single_score,
multi_score: stat.max_multi_score,
multi_threads: stat.typical_threads
}));
if (data.total_samples) {
this.total_samples = data.total_samples;
}
} else {
this.data = Array.isArray(data) ? data : [];
}
this.processData();
this.updateStats();
this.filterAndRender();
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('pagination').style.display = 'flex';
document.getElementById('cpuGroups').style.display = 'block';
return;
} catch (error) {
continue;
}
}
document.getElementById('loadingIndicator').style.display = 'none';
document.getElementById('noData').style.display = 'block';
}
processData() {
this.groupedData = {};
this.data.forEach(cpu => {
const prefix = cpu.cpu_prefix;
if (!this.groupedData[prefix]) {
this.groupedData[prefix] = [];
}
this.groupedData[prefix].push(cpu);
});
Object.keys(this.groupedData).forEach(prefix => {
this.groupedData[prefix].sort((a, b) => b.single_score - a.single_score);
});
this.allSortedPrefixes = Object.keys(this.groupedData).sort((a, b) => {
const maxSingleA = Math.max(...this.groupedData[a].map(cpu => cpu.single_score));
const maxSingleB = Math.max(...this.groupedData[b].map(cpu => cpu.single_score));
return maxSingleB - maxSingleA;
});
this.allSortedPrefixes.forEach((prefix, index) => {
this.prefixRanks[prefix] = index + 1;
});
this.sortedPrefixes = [...this.allSortedPrefixes];
}
updateStats() {
const totalCpus = Object.keys(this.groupedData).length;
const totalTests = this.total_samples;
const topSingle = Math.max(...this.data.map(cpu => cpu.single_score));
const topMulti = Math.max(...this.data.map(cpu => cpu.multi_score));
document.getElementById('totalCpus').textContent = totalCpus.toLocaleString();
document.getElementById('totalTests').textContent = totalTests.toLocaleString();
document.getElementById('topSingle').textContent = topSingle.toLocaleString();
document.getElementById('topMulti').textContent = topMulti.toLocaleString();
}
filterAndRender() {
if (this.searchTerm) {
this.filteredGroups = {};
const filteredPrefixes = [];
this.allSortedPrefixes.forEach(prefix => {
const filteredCpus = this.groupedData[prefix].filter(cpu =>
cpu.cpu_model.toLowerCase().includes(this.searchTerm) ||
cpu.cpu_prefix.toLowerCase().includes(this.searchTerm)
);
if (filteredCpus.length > 0) {
this.filteredGroups[prefix] = filteredCpus;
filteredPrefixes.push(prefix);
}
});
this.sortedPrefixes = filteredPrefixes;
} else {
this.filteredGroups = this.groupedData;
this.sortedPrefixes = [...this.allSortedPrefixes];
}
this.renderGroups();
}
renderGroups() {
const container = document.getElementById('cpuGroups');
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
const currentPagePrefixes = this.sortedPrefixes.slice(startIndex, endIndex);
container.innerHTML = currentPagePrefixes.map(prefix => {
const cpus = this.filteredGroups[prefix];
const topCpu = cpus[0];
const maxSingle = Math.max(...cpus.map(cpu => cpu.single_score));
const maxMulti = Math.max(...cpus.map(cpu => cpu.multi_score));
const rank = this.prefixRanks[prefix];
const sampleText = this.currentLang === 'zh' ? '样本' : 'Samples';
const singleText = this.currentLang === 'zh' ? '单核最高' : 'Max Single';
const multiText = this.currentLang === 'zh' ? '多核最高' : 'Max Multi';
return `
<div class="cpu-group" data-prefix="${prefix}">
<div class="group-header">
<div class="group-title-section">
<div class="rank-badge">#${rank}</div>
<div class="group-title">${topCpu.cpu_model}</div>
</div>
<div class="group-stats">
<span>${sampleText}: ${cpus.length}</span>
<span>${singleText}: ${maxSingle.toLocaleString()}</span>
<span>${multiText}: ${maxMulti.toLocaleString()}</span>
</div>
<div class="expand-icon">▼</div>
</div>
<div class="group-content">
${this.renderTable(cpus)}
</div>
</div>
`;
}).join('');
container.querySelectorAll('.group-header').forEach(header => {
header.addEventListener('click', () => {
const group = header.parentElement;
group.classList.toggle('expanded');
});
});
this.updatePagination();
}
updatePagination() {
const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage);
const pageInfo = document.getElementById('pageInfo');
const pageText = this.currentLang === 'zh'
? `${this.currentPage} 页,共 ${totalPages}`
: `Page ${this.currentPage} of ${totalPages}`;
pageInfo.textContent = pageText;
const pageInput = document.getElementById('pageInput');
pageInput.max = totalPages;
pageInput.value = this.currentPage;
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
prevBtn.disabled = this.currentPage === 1;
nextBtn.disabled = this.currentPage === totalPages;
}
renderTable(cpus) {
const headers = this.isDetailView
? (this.currentLang === 'zh'
? ['CPU型号', '核心数', '单核分数', '多核分数', '多核线程']
: ['CPU Model', 'Cores', 'Single Score', 'Multi Score', 'Multi Threads'])
: (this.currentLang === 'zh'
? ['CPU型号', '核心数', '单核分数', '多核分数']
: ['CPU Model', 'Cores', 'Single Score', 'Multi Score']);
const rows = cpus.slice(0, this.isDetailView ? cpus.length : 10).map(cpu => {
const cells = this.isDetailView
? [cpu.cpu_model, cpu.cpu_cores, cpu.single_score.toLocaleString(),
cpu.multi_score.toLocaleString(), cpu.multi_threads]
: [cpu.cpu_model, cpu.cpu_cores, cpu.single_score.toLocaleString(),
cpu.multi_score.toLocaleString()];
return `
<tr>
${cells.map((cell, index) => {
let className = '';
if (index === 2) className = 'score-cell score-single';
else if (index === 3) className = 'score-cell score-multi';
return `<td class="${className}">${cell}</td>`;
}).join('')}
</tr>
`;
}).join('');
return `
<table class="cpu-table">
<thead>
<tr>
${headers.map(header => `<th>${header}</th>`).join('')}
</tr>
</thead>
<tbody>
${rows}
</tbody>
</table>
`;
}
setViewMode(isDetail) {
this.isDetailView = isDetail;
document.getElementById('compactView').classList.toggle('active', !isDetail);
document.getElementById('detailView').classList.toggle('active', isDetail);
this.renderGroups();
}
}
new CPUDashboard();
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,347 +0,0 @@
package menu
import (
"context"
"fmt"
"os"
"os/signal"
"regexp"
"strings"
"sync"
"syscall"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/utils"
)
// GetMenuChoice prompts user for menu choice
func GetMenuChoice(language string) string {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
go func() {
select {
case <-sigChan:
if language == "zh" {
fmt.Println("\n程序在选择过程中被用户中断")
} else {
fmt.Println("\nProgram interrupted by user during selection")
}
os.Exit(0)
case <-ctx.Done():
return
}
}()
for {
var input string
if language == "zh" {
fmt.Print("请输入选项: ")
} else {
fmt.Print("Please enter your choice: ")
}
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.TrimRight(input, "\n")
re := regexp.MustCompile(`^\d+$`)
if re.MatchString(input) {
inChoice := input
switch inChoice {
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10":
return inChoice
default:
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}
} else {
if language == "zh" {
fmt.Println("输入错误,请输入一个纯数字")
} else {
fmt.Println("Invalid input, please enter a number")
}
}
}
}
// PrintMenuOptions displays menu options
func PrintMenuOptions(preCheck utils.NetCheckResult, config *params.Config) {
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
if preCheck.Connected {
var pwg sync.WaitGroup
pwg.Add(2)
go func() {
defer pwg.Done()
stats, statsErr = utils.GetGoescStats()
}()
go func() {
defer pwg.Done()
githubInfo, githubErr = utils.GetLatestEcsRelease()
}()
pwg.Wait()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
if statsErr != nil {
statsInfo = "NULL"
} else {
switch config.Language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(config.EcsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch config.Language {
case "zh":
fmt.Printf("VPS融合怪版本: %s\n", config.EcsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("检测到新版本 %s 如有必要请更新!\n", githubInfo.TagName)
}
fmt.Printf("使用统计: %s\n", statsInfo)
}
fmt.Println("1. 融合怪完全体(能测全测)")
fmt.Println("2. 极简版(系统信息+CPU+内存+磁盘+测速节点5个)")
fmt.Println("3. 精简版(系统信息+CPU+内存+磁盘+跨国平台解锁+路由+测速节点5个)")
fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)")
fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+跨国平台解锁+测速节点5个)")
fmt.Println("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+TGDC+网站延迟+测速节点11个)")
fmt.Println("7. 解锁单项(跨国平台解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+全国延迟+TGDC+网站延迟")
fmt.Println("0. 退出程序")
case "en":
fmt.Printf("VPS Fusion Monster Test Version: %s\n", config.EcsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
fmt.Println("2. Minimal Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)")
fmt.Println("3. Standard Test Suite (System Info + CPU + Memory + Disk + International Platform Unlock + Routing + 5 Speed Test Nodes)")
fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + Backtrace + Routing + 5 Speed Test Nodes)")
fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + International Platform Unlock + 5 Speed Test Nodes)")
fmt.Println("6. Network-Only Test (IP Quality Test + Upstream & 3-Network Backtrace + Guangzhou 3-Network Detailed Routing + National Latency + TGDC + Websites + 11 Speed Test Nodes)")
fmt.Println("7. Unlock-Only Test (International Platform Unlock)")
fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)")
fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)")
fmt.Println("0. Exit Program")
}
}
// HandleMenuMode handles menu selection using the interactive TUI
func HandleMenuMode(preCheck utils.NetCheckResult, config *params.Config) {
savedParams := config.SaveUserSetParams()
config.BasicStatus = false
config.CpuTestStatus = false
config.MemoryTestStatus = false
config.DiskTestStatus = false
config.UtTestStatus = false
config.SecurityTestStatus = false
config.EmailTestStatus = false
config.BacktraceStatus = false
config.Nt3Status = false
config.SpeedTestStatus = false
config.TgdcTestStatus = false
config.WebTestStatus = false
config.AutoChangeDiskMethod = true
result := RunTuiMenu(preCheck, config)
if result.quit {
os.Exit(0)
}
// Update language if changed by TUI selection
config.Language = result.language
if result.custom {
config.Choice = "custom"
applyCustomResult(result, preCheck, config)
if config.SpeedTestStatus {
config.OnlyChinaTest = utils.CheckChina(config.EnableLogger, config.Language)
}
} else {
config.Choice = result.choice
switch result.choice {
case "0":
os.Exit(0)
case "1":
SetFullTestStatus(preCheck, config)
config.OnlyChinaTest = utils.CheckChina(config.EnableLogger, config.Language)
case "2":
SetMinimalTestStatus(preCheck, config)
case "3":
SetStandardTestStatus(preCheck, config)
case "4":
SetNetworkFocusedTestStatus(preCheck, config)
case "5":
SetUnlockFocusedTestStatus(preCheck, config)
case "6":
SetNetworkOnlyTestStatus(config)
case "7":
SetUnlockOnlyTestStatus(config)
case "8":
SetHardwareOnlyTestStatus(preCheck, config)
case "9":
SetIPQualityTestStatus(config)
case "10":
config.Nt3Location = "ALL"
SetRouteTestStatus(config)
}
// Apply quick options set on the main menu page
config.AnalyzeResult = result.mainAnalyze
config.EnableUpload = result.mainUpload
}
config.RestoreUserSetParams(savedParams)
}
// SetFullTestStatus enables all tests
func SetFullTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.UtTestStatus = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
}
// SetMinimalTestStatus sets minimal test configuration
func SetMinimalTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.SpeedTestStatus = true
}
}
// SetStandardTestStatus sets standard test configuration
func SetStandardTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.UtTestStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetNetworkFocusedTestStatus sets network-focused test configuration
func SetNetworkFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.BacktraceStatus = true
config.Nt3Status = true
config.SpeedTestStatus = true
}
}
// SetUnlockFocusedTestStatus sets unlock-focused test configuration
func SetUnlockFocusedTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
if preCheck.Connected {
config.UtTestStatus = true
config.SpeedTestStatus = true
}
}
// SetNetworkOnlyTestStatus sets network-only test configuration
func SetNetworkOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.SpeedTestStatus = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// SetUnlockOnlyTestStatus sets unlock-only test configuration
func SetUnlockOnlyTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.UtTestStatus = true
}
// SetHardwareOnlyTestStatus sets hardware-only test configuration
func SetHardwareOnlyTestStatus(preCheck utils.NetCheckResult, config *params.Config) {
_ = preCheck
config.BasicStatus = true
config.CpuTestStatus = true
config.MemoryTestStatus = true
config.DiskTestStatus = true
config.SecurityTestStatus = false
config.AutoChangeDiskMethod = false
}
// SetIPQualityTestStatus sets IP quality test configuration
func SetIPQualityTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.SecurityTestStatus = true
config.EmailTestStatus = true
}
// SetRouteTestStatus sets route test configuration
func SetRouteTestStatus(config *params.Config) {
config.OnlyIpInfoCheck = true
config.BacktraceStatus = true
config.Nt3Status = true
config.PingTestStatus = true
config.TgdcTestStatus = true
config.WebTestStatus = true
}
// PrintInvalidChoice prints invalid choice message
func PrintInvalidChoice(language string) {
if language == "zh" {
fmt.Println("无效的选项")
} else {
fmt.Println("Invalid choice")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,485 +0,0 @@
package params
import (
"flag"
"fmt"
"strings"
)
// Config holds all configuration parameters
type Config struct {
EcsVersion string
MenuMode bool
OnlyChinaTest bool
Input string
Choice string
ShowVersion bool
EnableLogger bool
Language string
CpuTestMethod string
CpuTestThreadMode string
MemoryTestMethod string
DiskTestMethod string
DiskTestPath string
DiskMultiCheck bool
Nt3CheckType string
Nt3Location string
SpNum int
Width int
BasicStatus bool
CpuTestStatus bool
MemoryTestStatus bool
DiskTestStatus bool
UtTestStatus bool
SecurityTestStatus bool
EmailTestStatus bool
BacktraceStatus bool
Nt3Status bool
SpeedTestStatus bool
PingTestStatus bool
TgdcTestStatus bool
WebTestStatus bool
AutoChangeDiskMethod bool
FilePath string
EnableUpload bool
AnalyzeResult bool
OnlyIpInfoCheck bool
Help bool
Finish bool
UserSetFlags map[string]bool
GoecsFlag *flag.FlagSet
}
// NewConfig creates a new Config with default values
func NewConfig(version string) *Config {
return &Config{
EcsVersion: version,
MenuMode: true,
OnlyChinaTest: false,
Input: "",
Choice: "",
ShowVersion: false,
EnableLogger: false,
Language: "zh",
CpuTestMethod: "sysbench",
CpuTestThreadMode: "multi",
MemoryTestMethod: "stream",
DiskTestMethod: "fio",
DiskTestPath: "",
DiskMultiCheck: false,
Nt3CheckType: "ipv4",
SpNum: 2,
Width: 82,
BasicStatus: true,
CpuTestStatus: true,
MemoryTestStatus: true,
DiskTestStatus: true,
UtTestStatus: true,
SecurityTestStatus: false,
EmailTestStatus: true,
BacktraceStatus: true,
Nt3Status: true,
SpeedTestStatus: true,
PingTestStatus: false,
TgdcTestStatus: false,
WebTestStatus: false,
AutoChangeDiskMethod: true,
FilePath: "goecs.txt",
EnableUpload: true,
AnalyzeResult: false,
OnlyIpInfoCheck: false,
Help: false,
Finish: false,
UserSetFlags: make(map[string]bool),
GoecsFlag: flag.NewFlagSet("goecs", flag.ContinueOnError),
}
}
// normalizeBoolArgs preprocesses args so that bool flags written as
// "-flag true" or "-flag false" (space-separated) are converted to
// "-flag=true" / "-flag=false" that the standard flag package understands.
// This also strips any duplicate spaces that may appear between tokens when
// args have been assembled by shell scripts or other callers.
func normalizeBoolArgs(args []string) []string {
// All known boolean flag names (without leading dash).
boolFlags := map[string]bool{
"h": true, "help": true, "v": true, "version": true,
"menu": true, "basic": true, "cpu": true, "memory": true,
"disk": true, "ut": true, "security": true, "email": true,
"backtrace": true, "nt3": true, "speed": true, "ping": true,
"tgdc": true, "web": true, "log": true, "upload": true,
"analysis": true, "analyze": true,
"diskmc": true,
}
out := make([]string, 0, len(args))
i := 0
for i < len(args) {
arg := args[i]
// Skip empty tokens that can appear from split on multiple spaces.
if arg == "" {
i++
continue
}
// Detect flag tokens: -flag or --flag (without embedded =).
if strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") {
name := strings.TrimLeft(arg, "-")
if boolFlags[name] {
// Peek at next token: if it is "true" or "false", merge.
if i+1 < len(args) {
next := strings.ToLower(strings.TrimSpace(args[i+1]))
if next == "true" || next == "false" {
out = append(out, arg+"="+next)
i += 2
continue
}
}
}
}
out = append(out, arg)
i++
}
return out
}
// ParseFlags parses command line flags
func (c *Config) ParseFlags(args []string) {
args = normalizeBoolArgs(args)
c.GoecsFlag.BoolVar(&c.Help, "h", false, "Show help information")
c.GoecsFlag.BoolVar(&c.Help, "help", false, "Show help information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "v", false, "Display version information")
c.GoecsFlag.BoolVar(&c.ShowVersion, "version", false, "Display version information")
c.GoecsFlag.BoolVar(&c.MenuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false")
c.GoecsFlag.StringVar(&c.Language, "lang", "zh", "Set language (supported: en, zh)")
c.GoecsFlag.StringVar(&c.Language, "l", "zh", "Set language (supported: en, zh)")
c.GoecsFlag.BoolVar(&c.BasicStatus, "basic", true, "Enable/Disable basic test")
c.GoecsFlag.BoolVar(&c.CpuTestStatus, "cpu", true, "Enable/Disable CPU test")
c.GoecsFlag.BoolVar(&c.MemoryTestStatus, "memory", true, "Enable/Disable memory test")
c.GoecsFlag.BoolVar(&c.DiskTestStatus, "disk", true, "Enable/Disable disk test")
c.GoecsFlag.BoolVar(&c.UtTestStatus, "ut", true, "Enable/Disable unlock media test")
c.GoecsFlag.BoolVar(&c.SecurityTestStatus, "security", false, "Enable/Disable security test")
c.GoecsFlag.BoolVar(&c.EmailTestStatus, "email", true, "Enable/Disable email port test")
c.GoecsFlag.BoolVar(&c.BacktraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.Nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)")
c.GoecsFlag.BoolVar(&c.SpeedTestStatus, "speed", true, "Enable/Disable speed test")
c.GoecsFlag.BoolVar(&c.PingTestStatus, "ping", false, "Enable/Disable ping test")
c.GoecsFlag.BoolVar(&c.TgdcTestStatus, "tgdc", false, "Enable/Disable Telegram DC test")
c.GoecsFlag.BoolVar(&c.WebTestStatus, "web", false, "Enable/Disable popular websites test")
c.GoecsFlag.StringVar(&c.CpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
c.GoecsFlag.StringVar(&c.CpuTestMethod, "cpu-method", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)")
c.GoecsFlag.StringVar(&c.CpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)")
c.GoecsFlag.StringVar(&c.CpuTestThreadMode, "cpu-thread", "multi", "Set CPU test thread mode (supported: single, multi)")
c.GoecsFlag.StringVar(&c.MemoryTestMethod, "memorym", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
c.GoecsFlag.StringVar(&c.MemoryTestMethod, "memory-method", "stream", "Set memory test method (supported: stream, sysbench, dd, winsat, auto)")
c.GoecsFlag.StringVar(&c.DiskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)")
c.GoecsFlag.StringVar(&c.DiskTestMethod, "disk-method", "fio", "Set disk test method (supported: fio, dd, winsat)")
c.GoecsFlag.StringVar(&c.DiskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root")
c.GoecsFlag.BoolVar(&c.DiskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false")
c.GoecsFlag.StringVar(&c.Nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
c.GoecsFlag.StringVar(&c.Nt3Location, "nt3-location", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
c.GoecsFlag.StringVar(&c.Nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
c.GoecsFlag.StringVar(&c.Nt3CheckType, "nt3-type", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)")
c.GoecsFlag.IntVar(&c.SpNum, "spnum", 2, "Set the number of servers per operator for speed test")
c.GoecsFlag.BoolVar(&c.EnableLogger, "log", false, "Enable/Disable logging in the current path")
c.GoecsFlag.BoolVar(&c.EnableUpload, "upload", true, "Enable/Disable upload the result")
c.GoecsFlag.BoolVar(&c.AnalyzeResult, "analysis", false, "Enable/Disable post-test concise summary analysis")
c.GoecsFlag.BoolVar(&c.AnalyzeResult, "analyze", false, "Enable/Disable post-test concise summary analysis")
c.GoecsFlag.Parse(args)
c.GoecsFlag.Visit(func(f *flag.Flag) {
c.UserSetFlags[f.Name] = true
})
}
// HandleHelpAndVersion handles help and version flags
func (c *Config) HandleHelpAndVersion(programName string) bool {
if c.Help {
fmt.Printf("Usage: %s [options]\n", programName)
c.GoecsFlag.PrintDefaults()
return true
}
if c.ShowVersion {
fmt.Println(c.EcsVersion)
return true
}
return false
}
// SaveUserSetParams saves user-set parameters
func (c *Config) SaveUserSetParams() map[string]interface{} {
saved := make(map[string]interface{})
if c.UserSetFlags["basic"] {
saved["basic"] = c.BasicStatus
}
if c.UserSetFlags["cpu"] {
saved["cpu"] = c.CpuTestStatus
}
if c.UserSetFlags["memory"] {
saved["memory"] = c.MemoryTestStatus
}
if c.UserSetFlags["disk"] {
saved["disk"] = c.DiskTestStatus
}
if c.UserSetFlags["ut"] {
saved["ut"] = c.UtTestStatus
}
if c.UserSetFlags["security"] {
saved["security"] = c.SecurityTestStatus
}
if c.UserSetFlags["email"] {
saved["email"] = c.EmailTestStatus
}
if c.UserSetFlags["backtrace"] {
saved["backtrace"] = c.BacktraceStatus
}
if c.UserSetFlags["nt3"] {
saved["nt3"] = c.Nt3Status
}
if c.UserSetFlags["speed"] {
saved["speed"] = c.SpeedTestStatus
}
if c.UserSetFlags["ping"] {
saved["ping"] = c.PingTestStatus
}
if c.UserSetFlags["tgdc"] {
saved["tgdc"] = c.TgdcTestStatus
}
if c.UserSetFlags["web"] {
saved["web"] = c.WebTestStatus
}
if c.UserSetFlags["cpum"] || c.UserSetFlags["cpu-method"] {
saved["cpum"] = c.CpuTestMethod
}
if c.UserSetFlags["cput"] || c.UserSetFlags["cpu-thread"] {
saved["cput"] = c.CpuTestThreadMode
}
if c.UserSetFlags["memorym"] || c.UserSetFlags["memory-method"] {
saved["memorym"] = c.MemoryTestMethod
}
if c.UserSetFlags["diskm"] || c.UserSetFlags["disk-method"] {
saved["diskm"] = c.DiskTestMethod
}
if c.UserSetFlags["diskp"] {
saved["diskp"] = c.DiskTestPath
}
if c.UserSetFlags["diskmc"] {
saved["diskmc"] = c.DiskMultiCheck
}
if c.UserSetFlags["nt3loc"] || c.UserSetFlags["nt3-location"] {
saved["nt3loc"] = c.Nt3Location
}
if c.UserSetFlags["nt3t"] || c.UserSetFlags["nt3-type"] {
saved["nt3t"] = c.Nt3CheckType
}
if c.UserSetFlags["spnum"] {
saved["spnum"] = c.SpNum
}
if c.UserSetFlags["analysis"] || c.UserSetFlags["analyze"] {
saved["analysis"] = c.AnalyzeResult
}
return saved
}
// RestoreUserSetParams restores user-set parameters
func (c *Config) RestoreUserSetParams(saved map[string]interface{}) {
if val, ok := saved["basic"]; ok {
if boolVal, ok := val.(bool); ok {
c.BasicStatus = boolVal
}
}
if val, ok := saved["cpu"]; ok {
if boolVal, ok := val.(bool); ok {
c.CpuTestStatus = boolVal
}
}
if val, ok := saved["memory"]; ok {
if boolVal, ok := val.(bool); ok {
c.MemoryTestStatus = boolVal
}
}
if val, ok := saved["disk"]; ok {
if boolVal, ok := val.(bool); ok {
c.DiskTestStatus = boolVal
}
}
if val, ok := saved["ut"]; ok {
if boolVal, ok := val.(bool); ok {
c.UtTestStatus = boolVal
}
}
if val, ok := saved["security"]; ok {
if boolVal, ok := val.(bool); ok {
c.SecurityTestStatus = boolVal
}
}
if val, ok := saved["email"]; ok {
if boolVal, ok := val.(bool); ok {
c.EmailTestStatus = boolVal
}
}
if val, ok := saved["backtrace"]; ok {
if boolVal, ok := val.(bool); ok {
c.BacktraceStatus = boolVal
}
}
if val, ok := saved["nt3"]; ok {
if boolVal, ok := val.(bool); ok {
c.Nt3Status = boolVal
}
}
if val, ok := saved["speed"]; ok {
if boolVal, ok := val.(bool); ok {
c.SpeedTestStatus = boolVal
}
}
if val, ok := saved["ping"]; ok {
if boolVal, ok := val.(bool); ok {
c.PingTestStatus = boolVal
}
}
if val, ok := saved["tgdc"]; ok {
if boolVal, ok := val.(bool); ok {
c.TgdcTestStatus = boolVal
}
}
if val, ok := saved["web"]; ok {
if boolVal, ok := val.(bool); ok {
c.WebTestStatus = boolVal
}
}
if val, ok := saved["cpum"]; ok {
if strVal, ok := val.(string); ok {
c.CpuTestMethod = strVal
}
}
if val, ok := saved["cput"]; ok {
if strVal, ok := val.(string); ok {
c.CpuTestThreadMode = strVal
}
}
if val, ok := saved["memorym"]; ok {
if strVal, ok := val.(string); ok {
c.MemoryTestMethod = strVal
}
}
if val, ok := saved["diskm"]; ok {
if strVal, ok := val.(string); ok {
c.DiskTestMethod = strVal
}
}
if val, ok := saved["diskp"]; ok {
if strVal, ok := val.(string); ok {
c.DiskTestPath = strVal
}
}
if val, ok := saved["diskmc"]; ok {
if boolVal, ok := val.(bool); ok {
c.DiskMultiCheck = boolVal
}
}
if val, ok := saved["nt3loc"]; ok {
if c.Choice != "10" {
if strVal, ok := val.(string); ok {
c.Nt3Location = strVal
}
}
}
if val, ok := saved["nt3t"]; ok {
if strVal, ok := val.(string); ok {
c.Nt3CheckType = strVal
}
}
if val, ok := saved["spnum"]; ok {
if intVal, ok := val.(int); ok {
c.SpNum = intVal
}
}
if val, ok := saved["analysis"]; ok {
if boolVal, ok := val.(bool); ok {
c.AnalyzeResult = boolVal
}
}
c.ValidateParams()
}
// ValidateParams validates parameter values
func (c *Config) ValidateParams() {
validCpuMethods := map[string]bool{"sysbench": true, "geekbench": true, "winsat": true}
if !validCpuMethods[c.CpuTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: CPU测试方法 '%s' 无效,使用默认值 'sysbench'\n", c.CpuTestMethod)
} else {
fmt.Printf("Warning: Invalid CPU test method '%s', using default 'sysbench'\n", c.CpuTestMethod)
}
c.CpuTestMethod = "sysbench"
}
validThreadModes := map[string]bool{"single": true, "multi": true}
if !validThreadModes[c.CpuTestThreadMode] {
if c.Language == "zh" {
fmt.Printf("警告: CPU线程模式 '%s' 无效,使用默认值 'multi'\n", c.CpuTestThreadMode)
} else {
fmt.Printf("Warning: Invalid CPU thread mode '%s', using default 'multi'\n", c.CpuTestThreadMode)
}
c.CpuTestThreadMode = "multi"
}
validMemoryMethods := map[string]bool{"stream": true, "sysbench": true, "dd": true, "winsat": true, "auto": true}
if !validMemoryMethods[c.MemoryTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 内存测试方法 '%s' 无效,使用默认值 'stream'\n", c.MemoryTestMethod)
} else {
fmt.Printf("Warning: Invalid memory test method '%s', using default 'stream'\n", c.MemoryTestMethod)
}
c.MemoryTestMethod = "stream"
}
validDiskMethods := map[string]bool{"fio": true, "dd": true, "winsat": true}
if !validDiskMethods[c.DiskTestMethod] {
if c.Language == "zh" {
fmt.Printf("警告: 磁盘测试方法 '%s' 无效,使用默认值 'fio'\n", c.DiskTestMethod)
} else {
fmt.Printf("Warning: Invalid disk test method '%s', using default 'fio'\n", c.DiskTestMethod)
}
c.DiskTestMethod = "fio"
}
validNt3Locations := map[string]bool{"GZ": true, "SH": true, "BJ": true, "CD": true, "ALL": true}
if !validNt3Locations[c.Nt3Location] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试位置 '%s' 无效,使用默认值 'GZ'\n", c.Nt3Location)
} else {
fmt.Printf("Warning: Invalid NT3 location '%s', using default 'GZ'\n", c.Nt3Location)
}
c.Nt3Location = "GZ"
}
validNt3Types := map[string]bool{"both": true, "ipv4": true, "ipv6": true}
if !validNt3Types[c.Nt3CheckType] {
if c.Language == "zh" {
fmt.Printf("警告: NT3测试类型 '%s' 无效,使用默认值 'ipv4'\n", c.Nt3CheckType)
} else {
fmt.Printf("Warning: Invalid NT3 check type '%s', using default 'ipv4'\n", c.Nt3CheckType)
}
c.Nt3CheckType = "ipv4"
}
if c.SpNum < 0 {
if c.Language == "zh" {
fmt.Printf("警告: 测速节点数量 '%d' 无效,使用默认值 2\n", c.SpNum)
} else {
fmt.Printf("Warning: Invalid speed test node count '%d', using default 2\n", c.SpNum)
}
c.SpNum = 2
}
validLanguages := map[string]bool{"zh": true, "en": true}
if !validLanguages[c.Language] {
fmt.Printf("Warning: Invalid language '%s', using default 'zh'\n", c.Language)
c.Language = "zh"
}
}

View File

@@ -1,550 +0,0 @@
package runner
import (
"bufio"
"context"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/oneclickvirt/ecs/internal/analysis"
"github.com/oneclickvirt/ecs/internal/params"
"github.com/oneclickvirt/ecs/internal/tests"
"github.com/oneclickvirt/ecs/utils"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
)
// RunChineseTests runs all tests in Chinese mode
func RunChineseTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex, infoMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if config.UtTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !config.OnlyChinaTest {
wg1.Add(1)
go func() {
defer wg1.Done()
result := tests.MediaTest(config.Language)
infoMutex.Lock()
*mediaInfo = result
infoMutex.Unlock()
}()
}
if config.EmailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg2.Add(1)
go func() {
defer wg2.Done()
result := email.EmailCheck()
infoMutex.Lock()
*emailInfo = result
infoMutex.Unlock()
}()
}
if (config.OnlyChinaTest || config.PingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg3.Done()
result := pt.PingTest()
infoMutex.Lock()
*ptInfo = result
infoMutex.Unlock()
}()
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex, infoMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex, infoMutex)
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex, infoMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunEnglishTests runs all tests in English mode
func RunEnglishTests(preCheck utils.NetCheckResult, config *params.Config, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex, infoMutex *sync.Mutex) {
*output = RunBasicTests(preCheck, config, basicInfo, securityInfo, *output, tempOutput, outputMutex)
*output = RunCPUTest(config, *output, tempOutput, outputMutex)
*output = RunMemoryTest(config, *output, tempOutput, outputMutex)
*output = RunDiskTest(config, *output, tempOutput, outputMutex)
if config.OnlyIpInfoCheck && !config.BasicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = RunIpInfoCheck(config, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if config.UtTestStatus {
wg1.Add(1)
go func() {
defer wg1.Done()
result := tests.MediaTest(config.Language)
infoMutex.Lock()
*mediaInfo = result
infoMutex.Unlock()
}()
}
if config.EmailTestStatus {
wg2.Add(1)
go func() {
defer wg2.Done()
result := email.EmailCheck()
infoMutex.Lock()
*emailInfo = result
infoMutex.Unlock()
}()
}
*output = RunStreamingTests(config, wg1, mediaInfo, *output, tempOutput, outputMutex, infoMutex)
*output = RunSecurityTests(config, *securityInfo, *output, tempOutput, outputMutex)
*output = RunEmailTests(config, wg2, emailInfo, *output, tempOutput, outputMutex, infoMutex)
*output = RunEnglishNetworkTests(config, wg3, ptInfo, *output, tempOutput, outputMutex)
*output = RunEnglishSpeedTests(config, *output, tempOutput, outputMutex)
}
*output = AppendTimeInfo(config, *output, tempOutput, startTime, outputMutex)
}
// RunIpInfoCheck performs IP info check
func RunIpInfoCheck(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
tests.IPV4, tests.IPV6, ipinfo = utils.OnlyBasicsIpInfo(config.Language)
if ipinfo != "" {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP信息", config.Width)
} else {
utils.PrintCenteredTitle("IP-Information", config.Width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
// RunBasicTests runs basic system tests
func RunBasicTests(preCheck utils.NetCheckResult, config *params.Config, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
utils.PrintHead(config.Language, config.Width, config.EcsVersion)
if config.BasicStatus || config.SecurityTestStatus {
if config.BasicStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("系统基础信息", config.Width)
} else {
utils.PrintCenteredTitle("System-Basic-Information", config.Width)
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, config.Nt3CheckType, config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv4", config.SecurityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "ipv6", config.SecurityTestStatus)
} else {
tests.IPV4, tests.IPV6, *basicInfo, *securityInfo, config.Nt3CheckType = utils.BasicsAndSecurityCheck(config.Language, "", false)
config.SecurityTestStatus = false
}
if config.BasicStatus {
fmt.Printf("%s", *basicInfo)
} else if (config.Choice == "6" || config.Choice == "9") && config.SecurityTestStatus {
scanner := bufio.NewScanner(strings.NewReader(*basicInfo))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "IPV") {
fmt.Println(line)
}
}
}
}
}, tempOutput, output)
}
// RunCPUTest runs CPU test
func RunCPUTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.CpuTestStatus {
realTestMethod, res := tests.CpuTest(config.Language, config.CpuTestMethod, config.CpuTestThreadMode)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunMemoryTest runs memory test
func RunMemoryTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.MemoryTestStatus {
realTestMethod, res := tests.MemoryTest(config.Language, config.MemoryTestMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
}
}, tempOutput, output)
}
// RunDiskTest runs disk test
func RunDiskTest(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.DiskTestStatus && config.AutoChangeDiskMethod {
realTestMethod, res := tests.DiskTest(config.Language, config.DiskTestMethod, config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", realTestMethod), config.Width)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", realTestMethod), config.Width)
}
fmt.Print(res)
} else if config.DiskTestStatus && !config.AutoChangeDiskMethod {
if config.Language == "zh" {
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
} else {
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), config.Width)
_, res := tests.DiskTest(config.Language, "dd", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), config.Width)
_, res = tests.DiskTest(config.Language, "fio", config.DiskTestPath, config.DiskMultiCheck, config.AutoChangeDiskMethod)
fmt.Print(res)
}
}
}, tempOutput, output)
}
// RunStreamingTests runs platform unlock tests
func RunStreamingTests(config *params.Config, wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex, infoMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.UtTestStatus && (config.Language == "zh" && !config.OnlyChinaTest || config.Language == "en") {
wg1.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("跨国平台解锁", config.Width)
} else {
utils.PrintCenteredTitle("Cross-Border-Platform-Unlock", config.Width)
}
infoMutex.Lock()
info := *mediaInfo
infoMutex.Unlock()
fmt.Printf("%s", info)
}
}, tempOutput, output)
}
// RunSecurityTests runs security tests
func RunSecurityTests(config *params.Config, securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SecurityTestStatus {
if config.Language == "zh" {
utils.PrintCenteredTitle("IP质量检测", config.Width)
} else {
utils.PrintCenteredTitle("IP-Quality-Check", config.Width)
}
fmt.Printf("%s", securityInfo)
}
}, tempOutput, output)
}
// RunEmailTests runs email port tests
func RunEmailTests(config *params.Config, wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex, infoMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.EmailTestStatus {
wg2.Wait()
if config.Language == "zh" {
utils.PrintCenteredTitle("邮件端口检测", config.Width)
} else {
utils.PrintCenteredTitle("Email-Port-Check", config.Width)
}
infoMutex.Lock()
info := *emailInfo
infoMutex.Unlock()
fmt.Println(info)
}
}, tempOutput, output)
}
// RunNetworkTests runs network tests (Chinese mode)
func RunNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex, infoMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.BacktraceStatus && !config.OnlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", config.Width)
tests.UpstreamsCheck(config.Language)
}
if config.Nt3Status && !config.OnlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", config.Width)
tests.NextTrace3Check(config.Language, config.Nt3Location, config.Nt3CheckType)
}
infoMutex.Lock()
info := *ptInfo
infoMutex.Unlock()
if config.OnlyChinaTest && info != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(info)
}
if config.PingTestStatus && info != "" {
wg3.Wait()
utils.PrintCenteredTitle("PING值检测", config.Width)
fmt.Println(info)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
if !config.OnlyChinaTest && !config.PingTestStatus && (config.TgdcTestStatus || config.WebTestStatus) {
utils.PrintCenteredTitle("PING值检测", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
// 等待第三方库的输出完全刷新到标准输出
time.Sleep(300 * time.Millisecond)
}, tempOutput, output)
}
// RunSpeedTests runs speed tests (Chinese mode)
func RunSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("就近节点测速", config.Width)
tests.ShowHead(config.Language)
if config.Choice == "1" || !config.MenuMode {
tests.NearbySP()
tests.CustomSP("net", "global", 2, config.Language)
tests.CustomSP("net", "cu", config.SpNum, config.Language)
tests.CustomSP("net", "ct", config.SpNum, config.Language)
tests.CustomSP("net", "cmcc", config.SpNum, config.Language)
} else if config.Choice == "2" || config.Choice == "3" || config.Choice == "4" || config.Choice == "5" {
// 中文模式:就近测速 + 三网各1个 + Other 1个带回退
if config.Language == "zh" {
tests.NearbySP()
tests.CustomSP("net", "other", 1, config.Language)
tests.CustomSP("net", "cu", 1, config.Language)
tests.CustomSP("net", "ct", 1, config.Language)
tests.CustomSP("net", "cmcc", 1, config.Language)
} else {
// 英文模式保持原有逻辑测4个global节点
tests.CustomSP("net", "global", 4, config.Language)
}
} else if config.Choice == "6" {
tests.CustomSP("net", "global", 11, config.Language)
} else {
// Custom menu mode and any other fallback choices.
tests.NearbySP()
tests.CustomSP("net", "cu", config.SpNum, config.Language)
tests.CustomSP("net", "ct", config.SpNum, config.Language)
tests.CustomSP("net", "cmcc", config.SpNum, config.Language)
}
// 等待第三方库的输出完全刷新到标准输出
time.Sleep(500 * time.Millisecond)
}
}, tempOutput, output)
}
// RunEnglishNetworkTests runs network tests (English mode)
func RunEnglishNetworkTests(config *params.Config, wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.TgdcTestStatus || config.WebTestStatus {
utils.PrintCenteredTitle("PING-Test", config.Width)
if config.TgdcTestStatus {
fmt.Println(pt.TelegramDCTest())
}
if config.WebTestStatus {
fmt.Println(pt.WebsiteTest())
}
}
// 等待第三方库的输出完全刷新到标准输出
time.Sleep(300 * time.Millisecond)
}, tempOutput, output)
}
// RunEnglishSpeedTests runs speed tests (English mode)
func RunEnglishSpeedTests(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
if config.SpeedTestStatus {
utils.PrintCenteredTitle("Speed-Test", config.Width)
tests.ShowHead(config.Language)
tests.NearbySP()
tests.CustomSP("net", "global", -1, config.Language)
// 等待第三方库的输出完全刷新到标准输出
time.Sleep(500 * time.Millisecond)
}
}, tempOutput, output)
}
// AppendTimeInfo appends timing information
func AppendTimeInfo(config *params.Config, output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
endTime := time.Now()
duration := endTime.Sub(startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
return utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, tempOutput, output)
}
// AppendAnalysisSummary appends a concise bilingual summary for easier interpretation.
func AppendAnalysisSummary(config *params.Config, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
finalOutput := output
return utils.PrintAndCapture(func() {
summary := analysis.GenerateSummary(config, finalOutput)
if strings.TrimSpace(summary) == "" {
return
}
fmt.Println(summary)
}, tempOutput, output)
}
// HandleSignalInterrupt handles interrupt signals
func HandleSignalInterrupt(sig chan os.Signal, config *params.Config, startTime *time.Time, output *string, tempOutput string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !config.Finish {
endTime := time.Now()
duration := endTime.Sub(*startTime)
minutes := int(duration.Minutes())
seconds := int(duration.Seconds()) % 60
currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006")
outputMutex.Lock()
timeInfo := utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("", config.Width)
if config.Language == "zh" {
fmt.Printf("花费 : %d 分 %d 秒\n", minutes, seconds)
fmt.Printf("时间 : %s\n", currentTime)
} else {
fmt.Printf("Cost Time : %d min %d sec\n", minutes, seconds)
fmt.Printf("Current Time : %s\n", currentTime)
}
utils.PrintCenteredTitle("", config.Width)
}, "", "")
*output += timeInfo
finalOutput := *output
outputMutex.Unlock()
resultChan := make(chan struct {
httpURL string
httpsURL string
}, 1)
if config.EnableUpload {
// 使用context来控制上传goroutine
uploadCtx, uploadCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer uploadCancel()
go func() {
httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, config.FilePath, config.EnableUpload, config.Language)
select {
case resultChan <- struct {
httpURL string
httpsURL string
}{httpURL, httpsURL}:
case <-uploadCtx.Done():
// 上传被取消或超时,直接返回
return
}
}()
select {
case result := <-resultChan:
uploadCancel() // 成功完成取消context
if result.httpURL != "" || result.httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", result.httpURL, result.httpsURL)
}
}
time.Sleep(100 * time.Millisecond)
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
case <-uploadCtx.Done():
if config.Language == "en" {
fmt.Println("Upload timeout, program exit")
} else {
fmt.Println("上传超时,程序退出")
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(1)
}
} else {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
os.Exit(0)
}
}
os.Exit(0)
}
}
// HandleUploadResults handles uploading results
func HandleUploadResults(config *params.Config, output string) {
httpURL, httpsURL := utils.ProcessAndUpload(output, config.FilePath, config.EnableUpload, config.Language)
if httpURL != "" || httpsURL != "" {
if config.Language == "en" {
fmt.Printf("Upload successfully!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("Each Test Benchmark: https://bash.spiritlhl.net/ecsguide")
} else {
fmt.Printf("上传成功!\nHttp URL: %s\nHttps URL: %s\n", httpURL, httpsURL)
fmt.Println("每项测试基准见: https://bash.spiritlhl.net/ecsguide")
}
}
}

View File

@@ -1,56 +0,0 @@
package tests
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/oneclickvirt/cputest/cpu"
)
func CpuTest(language, testMethod, testThread string) (realTestMethod, res string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] CpuTest panic: %v\n", r)
res = fmt.Sprintf("\nCPU test failed: %v\n", r)
realTestMethod = "error"
}
}()
if runtime.GOOS == "windows" {
if testMethod != "winsat" && testMethod != "" {
// res = "Detected host is Windows, using Winsat for testing.\n"
realTestMethod = "winsat"
}
res += cpu.WinsatTest(language, testThread)
} else {
switch testMethod {
case "sysbench":
res = cpu.SysBenchTest(language, testThread)
if res == "" {
// res = "Sysbench test failed, switching to Geekbench for testing.\n"
realTestMethod = "geekbench"
res += cpu.GeekBenchTest(language, testThread)
} else {
realTestMethod = "sysbench"
}
case "geekbench":
res = cpu.GeekBenchTest(language, testThread)
if res == "" {
// res = "Geekbench test failed, switching to Sysbench for testing.\n"
realTestMethod = "sysbench"
res += cpu.SysBenchTest(language, testThread)
} else {
realTestMethod = "geekbench"
}
default:
res = "Invalid test method specified.\n"
realTestMethod = "null"
}
}
if !strings.Contains(res, "\n") && res != "" {
res += "\n"
}
return
}

View File

@@ -1,51 +0,0 @@
package tests
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/oneclickvirt/disktest/disk"
)
func DiskTest(language, testMethod, testPath string, isMultiCheck bool, autoChange bool) (realTestMethod, res string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] DiskTest panic: %v\n", r)
res = fmt.Sprintf("\nDisk test failed: %v\n", r)
realTestMethod = "error"
}
}()
switch testMethod {
case "fio":
res = disk.FioTest(language, isMultiCheck, testPath)
if res == "" && autoChange {
res += disk.DDTest(language, isMultiCheck, testPath)
realTestMethod = "dd"
} else {
realTestMethod = "fio"
}
case "dd":
res = disk.DDTest(language, isMultiCheck, testPath)
if res == "" && autoChange {
res += disk.FioTest(language, isMultiCheck, testPath)
realTestMethod = "fio"
} else {
realTestMethod = "dd"
}
default:
if runtime.GOOS == "windows" {
realTestMethod = "winsat"
res = disk.WinsatTest(language, isMultiCheck, testPath)
} else {
res = disk.DDTest(language, isMultiCheck, testPath)
realTestMethod = "dd"
}
}
if !strings.Contains(res, "\n") && res != "" {
res += "\n"
}
return
}

View File

@@ -1,233 +0,0 @@
package tests
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/oneclickvirt/memorytest/memory"
)
func MemoryTest(language, testMethod string) (realTestMethod, res string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] MemoryTest panic: %v\n", r)
res = fmt.Sprintf("\nMemory test failed: %v\n", r)
realTestMethod = "error"
}
}()
testMethod = strings.ToLower(testMethod)
if testMethod == "" {
testMethod = "auto"
}
if runtime.GOOS == "windows" {
switch testMethod {
case "stream":
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "winsat"
}
} else {
realTestMethod = "stream"
}
case "dd":
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "stream"
}
} else {
realTestMethod = "winsat"
}
} else {
realTestMethod = "dd"
}
case "sysbench":
// Windows下不支持sysbench使用stream → winsat → dd
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "winsat"
}
} else {
realTestMethod = "stream"
}
case "auto":
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "winsat"
}
} else {
realTestMethod = "stream"
}
case "winsat":
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "stream"
}
} else {
realTestMethod = "winsat"
}
default:
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WinsatTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.WindowsDDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "winsat"
}
} else {
realTestMethod = "stream"
}
}
} else {
switch testMethod {
case "stream":
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.DDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "sysbench"
}
} else {
realTestMethod = "stream"
}
case "dd":
res = memory.DDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "sysbench"
}
} else {
realTestMethod = "stream"
}
} else {
realTestMethod = "dd"
}
case "sysbench":
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.DDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "sysbench"
}
} else {
realTestMethod = "stream"
}
} else {
realTestMethod = "sysbench"
}
case "auto":
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.DDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "sysbench"
}
} else {
realTestMethod = "stream"
}
case "winsat":
// winsat 仅 Windows 支持,非 Windows fallback 到 stream → sysbench → dd
res = memory.StreamTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.SysBenchTest(language)
if res == "" || strings.TrimSpace(res) == "" {
res = memory.DDTest(language)
if res == "" || strings.TrimSpace(res) == "" {
realTestMethod = ""
} else {
realTestMethod = "dd"
}
} else {
realTestMethod = "sysbench"
}
} else {
realTestMethod = "stream"
}
default:
res = "Unsupported test method"
realTestMethod = ""
}
}
if !strings.Contains(res, "\n") && res != "" {
res += "\n"
}
return
}

View File

@@ -1,98 +0,0 @@
package tests
import (
"fmt"
"net"
"os"
"strings"
"github.com/oneclickvirt/nt3/nt"
)
func NextTrace3Check(language, nt3Location, nt3CheckType string) {
// 先检查 ICMP 权限
conn, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
// 没有权限,显示友好提示并跳过
if language == "zh" {
fmt.Println("路由追踪测试需要 root 权限或 CAP_NET_RAW 能力,已跳过")
fmt.Fprintf(os.Stderr, "[WARN] ICMP权限不足: %v\n", err)
} else {
fmt.Println("Route tracing test requires root privileges or CAP_NET_RAW capability, skipped")
fmt.Fprintf(os.Stderr, "[WARN] Insufficient ICMP permission: %v\n", err)
}
return
}
conn.Close()
defer func() {
if r := recover(); r != nil {
if language == "zh" {
fmt.Println("路由追踪测试出现错误,已跳过")
fmt.Fprintf(os.Stderr, "[WARN] 路由追踪panic: %v\n", r)
} else {
fmt.Println("Route tracing test failed, skipped")
fmt.Fprintf(os.Stderr, "[WARN] Route tracing panic: %v\n", r)
}
}
}()
resultChan := make(chan nt.TraceResult, 100)
errorOccurred := false
go func() {
defer func() {
if r := recover(); r != nil {
errorOccurred = true
resultChan <- nt.TraceResult{
Index: -1,
ISPName: "Error",
Output: []string{fmt.Sprintf("Route tracing error: %v", r)},
}
close(resultChan)
}
}()
nt.TraceRoute(language, nt3Location, nt3CheckType, resultChan)
}()
for result := range resultChan {
if result.Index == -1 {
for index, res := range result.Output {
res = strings.TrimSpace(res)
if res != "" && index == 0 {
fmt.Println(res)
}
}
continue
}
if result.ISPName == "Error" {
if language == "zh" {
fmt.Println("路由追踪测试失败(可能因为权限不足),已跳过")
} else {
fmt.Println("Route tracing test failed (possibly due to insufficient permissions), skipped")
}
for _, res := range result.Output {
res = strings.TrimSpace(res)
if res != "" {
fmt.Fprintf(os.Stderr, "[WARN] %s\n", res)
}
}
errorOccurred = true
continue
}
for _, res := range result.Output {
res = strings.TrimSpace(res)
if res == "" {
continue
}
if strings.Contains(res, "ICMP") {
fmt.Print(res)
} else {
fmt.Println(res)
}
}
}
if errorOccurred {
if language == "zh" {
fmt.Println("提示: 路由追踪需要 root 权限或 CAP_NET_RAW 能力")
} else {
fmt.Println("Hint: Route tracing requires root privileges or CAP_NET_RAW capability")
}
}
}

View File

@@ -1,84 +0,0 @@
package tests
import (
"fmt"
"os"
"runtime"
"strings"
"github.com/oneclickvirt/speedtest/model"
"github.com/oneclickvirt/speedtest/sp"
)
func ShowHead(language string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] ShowHead panic: %v\n", r)
}
}()
sp.ShowHead(language)
}
func NearbySP() {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] NearbySP panic: %v\n", r)
}
}()
if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil {
sp.NearbySpeedTest()
} else {
sp.OfficialNearbySpeedTest()
}
}
func CustomSP(platform, operator string, num int, language string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] CustomSP panic: %v\n", r)
}
}()
var url, parseType string
if strings.ToLower(platform) == "cn" {
if strings.ToLower(operator) == "cmcc" {
url = model.CnCMCC
} else if strings.ToLower(operator) == "cu" {
url = model.CnCU
} else if strings.ToLower(operator) == "ct" {
url = model.CnCT
} else if strings.ToLower(operator) == "hk" {
url = model.CnHK
} else if strings.ToLower(operator) == "tw" {
url = model.CnTW
} else if strings.ToLower(operator) == "jp" {
url = model.CnJP
} else if strings.ToLower(operator) == "sg" {
url = model.CnSG
}
parseType = "url"
} else if strings.ToLower(platform) == "net" {
if strings.ToLower(operator) == "cmcc" {
url = model.NetCMCC
} else if strings.ToLower(operator) == "cu" {
url = model.NetCU
} else if strings.ToLower(operator) == "ct" {
url = model.NetCT
} else if strings.ToLower(operator) == "hk" {
url = model.NetHK
} else if strings.ToLower(operator) == "tw" {
url = model.NetTW
} else if strings.ToLower(operator) == "jp" {
url = model.NetJP
} else if strings.ToLower(operator) == "sg" {
url = model.NetSG
} else if strings.ToLower(operator) == "global" || strings.ToLower(operator) == "other" {
// other 类型回退到 global 节点
url = model.NetGlobal
}
parseType = "id"
}
if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil {
sp.CustomSpeedTest(url, parseType, num, language)
} else {
sp.OfficialCustomSpeedTest(url, parseType, num, language)
}
}

View File

@@ -1,35 +0,0 @@
package tests
import (
"fmt"
"os"
"github.com/oneclickvirt/UnlockTests/executor"
"github.com/oneclickvirt/UnlockTests/utils"
"github.com/oneclickvirt/defaultset"
)
func MediaTest(language string) string {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] MediaTest panic: %v\n", r)
}
}()
var res string
readStatus := executor.ReadSelect(language, "0")
if !readStatus {
return ""
}
if executor.IPV4 {
res += defaultset.Blue("IPV4:") + "\n"
res += executor.RunTests(utils.Ipv4HttpClient, "ipv4", language, false)
return res
}
if executor.IPV6 {
res += defaultset.Blue("IPV6:") + "\n"
res += executor.RunTests(utils.Ipv6HttpClient, "ipv6", language, false)
return res
}
return ""
}

View File

@@ -1,94 +0,0 @@
package tests
import (
"fmt"
"os"
"sync"
"time"
"github.com/oneclickvirt/UnlockTests/executor"
bgptools "github.com/oneclickvirt/backtrace/bgptools"
backtrace "github.com/oneclickvirt/backtrace/bk"
. "github.com/oneclickvirt/defaultset"
)
type IpInfo struct {
Ip string `json:"ip"`
City string `json:"city"`
Region string `json:"region"`
Country string `json:"country"`
Org string `json:"org"`
}
type ConcurrentResults struct {
bgpResult string
backtraceResult string
bgpError error
// backtraceError error
}
var IPV4, IPV6 string
func UpstreamsCheck(language string) {
// 添加panic恢复机制
defer func() {
if r := recover(); r != nil {
if language == "zh" {
fmt.Println("\n上游检测出现错误已跳过")
} else {
fmt.Println("\nUpstream check failed, skipped")
}
fmt.Fprintf(os.Stderr, "[WARN] Upstream check panic: %v\n", r)
}
}()
results := ConcurrentResults{}
var wg sync.WaitGroup
if IPV4 != "" {
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] BGP info panic: %v\n", r)
}
}()
for i := 0; i < 2; i++ {
result, err := bgptools.GetPoPInfo(IPV4)
results.bgpError = err
if err == nil && result.Result != "" {
results.bgpResult = result.Result
return
}
if i == 0 {
time.Sleep(3 * time.Second)
}
}
}()
}
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[WARN] Backtrace panic: %v\n", r)
}
}()
result := backtrace.BackTrace(executor.IPV6)
results.backtraceResult = result
}()
wg.Wait()
if results.bgpResult != "" {
fmt.Print(results.bgpResult)
}
if results.backtraceResult != "" {
fmt.Printf("%s\n", results.backtraceResult)
}
if language == "zh" {
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
fmt.Println(Yellow("同一目标地址多个线路时,检测可能已越过汇聚层,除第一个线路外,后续信息可能无效"))
} else {
fmt.Println(Yellow("For accurate routing, check the detailed routes yourself. This result is for reference only."))
fmt.Println(Yellow("When multiple routes share the same destination, detection may have passed the aggregation layer; only the first route is reliable."))
}
}

View File

@@ -1,734 +0,0 @@
package utils
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/imroc/req/v3"
"github.com/oneclickvirt/UnlockTests/executor"
bnetwork "github.com/oneclickvirt/basics/network"
"github.com/oneclickvirt/basics/system"
butils "github.com/oneclickvirt/basics/utils"
. "github.com/oneclickvirt/defaultset"
"github.com/oneclickvirt/basics/network"
)
// IsAndroid 检测当前是否在 Android (Termux) 环境下运行
func IsAndroid() bool {
// Termux 会设置 TERMUX_VERSION 或 PREFIX 环境变量
if os.Getenv("TERMUX_VERSION") != "" {
return true
}
if prefix := os.Getenv("PREFIX"); strings.Contains(prefix, "termux") {
return true
}
// Android 系统标志文件
if _, err := os.Stat("/system/build.prop"); err == nil {
return true
}
// ANDROID_ROOT 环境变量 (Android 系统设置为 /system)
if os.Getenv("ANDROID_ROOT") != "" {
return true
}
return false
}
// androidDNSServers 是用于修复 Android/Termux 下 DNS 问题的备用服务器列表
var androidDNSServers = []string{
"nameserver 8.8.8.8",
"nameserver 8.8.4.4",
"nameserver 1.1.1.1",
"nameserver 223.5.5.5",
}
// CheckAndFixAndroidDNS 检测并尝试修复 Android Termux 下的 DNS 解析问题。
// 仅在检测到 Android 环境下调用。不出现问题时无任何输出。
// language: "zh" 或 "en"
func CheckAndFixAndroidDNS(language string) {
if !IsAndroid() {
return
}
resolvPath := "/etc/resolv.conf"
hasValidDNS := false
if data, err := os.ReadFile(resolvPath); err == nil {
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "nameserver") && len(strings.Fields(line)) >= 2 {
hasValidDNS = true
break
}
}
}
if hasValidDNS {
// DNS 配置正常,无需处理
return
}
// /etc/resolv.conf 缺失或无有效 nameserver尝试自动创建
content := strings.Join(androidDNSServers, "\n") + "\n"
err := os.WriteFile(resolvPath, []byte(content), 0644)
if err != nil {
// 写入失败(权限不足),给出双语提示和修复建议
fmt.Println("-----------------------------------------------------")
fmt.Println("[Android/Termux] 检测到 DNS 解析配置缺失!")
fmt.Println("这将导致依赖系统 DNS 的测试项目无法正常运行。")
fmt.Println("解决方案(推荐):")
fmt.Println(" 通过 Magisk / KernelSU 刷入以下模块后重启手机:")
fmt.Println(" https://github.com/weigui404/resolv.conf")
fmt.Println("解决方案(临时):")
fmt.Println(" 以 root 身份执行: echo 'nameserver 8.8.8.8' > /etc/resolv.conf")
fmt.Println("-----------------------------------------------------")
fmt.Println("[Android/Termux] DNS resolver config is missing!")
fmt.Println("This will cause DNS-dependent tests to fail.")
fmt.Println("Fix (recommended):")
fmt.Println(" Flash the following module via Magisk / KernelSU and reboot:")
fmt.Println(" https://github.com/weigui404/resolv.conf")
fmt.Println("Fix (temporary):")
fmt.Println(" Run as root: echo 'nameserver 8.8.8.8' > /etc/resolv.conf")
fmt.Println("-----------------------------------------------------")
} else {
// 写入成功
if language == "zh" {
fmt.Println("[Android/Termux] DNS 配置缺失,已自动写入 /etc/resolv.confDNS 解析已恢复。")
} else {
fmt.Println("[Android/Termux] DNS config was missing; auto-written to /etc/resolv.conf, DNS resolution restored.")
}
}
}
// 获取本程序本日及总执行的统计信息
type StatsResponse struct {
Counter string `json:"counter"`
Action string `json:"action"`
Total int `json:"total"`
Daily int `json:"daily"`
Date string `json:"date"`
Timestamp string `json:"timestamp"`
}
// 获取最新的Github的仓库中的版本
type GitHubRelease struct {
TagName string `json:"tag_name"`
}
// PrintCenteredTitle 根据指定的宽度打印居中标题
func PrintCenteredTitle(title string, width int) {
// 计算字符串的字符数
titleLength := utf8.RuneCountInString(title)
totalPadding := width - titleLength
padding := totalPadding / 2
paddingStr := strings.Repeat("-", padding)
fmt.Println(paddingStr + title + paddingStr + strings.Repeat("-", totalPadding%2))
}
// PrintHead 根据语言打印头部信息
func PrintHead(language string, width int, ecsVersion string) {
if language == "zh" {
PrintCenteredTitle("VPS融合怪测试(非官方编译)", width)
fmt.Printf("版本:%s\n", ecsVersion)
fmt.Println("测评频道: https://t.me/+UHVoo2U4VyA5NTQ1\n" +
"Go项目地址https://github.com/oneclickvirt/ecs\n" +
"Shell项目地址https://github.com/spiritLHLS/ecs")
} else {
PrintCenteredTitle("VPS Fusion Monster Test (Unofficial)", width)
fmt.Printf("Version: %s\n", ecsVersion)
fmt.Println("Review Channel: https://t.me/+UHVoo2U4VyA5NTQ1\n" +
"Go Project: https://github.com/oneclickvirt/ecs\n" +
"Shell Project: https://github.com/spiritLHLS/ecs")
}
}
func CheckChina(enableLogger bool, language string) bool {
if enableLogger {
InitLogger()
defer Logger.Sync()
}
var selectChina bool
client := req.C()
client.SetTimeout(6 * time.Second)
client.R().
SetRetryCount(2).
SetRetryBackoffInterval(1*time.Second, 3*time.Second).
SetRetryFixedInterval(2 * time.Second)
ipapiURL := "https://ipapi.co/json"
ipapiResp, err := client.R().Get(ipapiURL)
if err != nil {
if enableLogger {
Logger.Info("Failed to get IP info: " + err.Error())
}
return false
}
defer ipapiResp.Body.Close()
ipapiBody, err := ipapiResp.ToString()
if err != nil {
if enableLogger {
Logger.Info("Failed to read IP info response: " + err.Error())
}
return false
}
isInChina := strings.Contains(ipapiBody, "China")
if isInChina {
var input string
if language == "zh" {
fmt.Println("根据 ipapi.co 提供的信息当前IP可能在中国")
fmt.Print("是否选用中国专项测试(无平台解锁测试有三网Ping值测试)? ([y]/n) ")
} else {
fmt.Println("According to ipapi.co, this IP may be located in China")
fmt.Print("Use China-specific test (no platform unlock test, includes 3-network ping test)? ([y]/n) ")
}
fmt.Scanln(&input)
switch strings.ToLower(input) {
case "yes", "y":
if language == "zh" {
fmt.Println("使用中国专项测试")
} else {
fmt.Println("Using China-specific test")
}
selectChina = true
case "no", "n":
if language == "zh" {
fmt.Println("不使用中国专项测试")
} else {
fmt.Println("Not using China-specific test")
}
default:
if language == "zh" {
fmt.Println("使用中国专项测试")
} else {
fmt.Println("Using China-specific test")
}
selectChina = true
}
}
return selectChina
}
// OnlyBasicsIpInfo 仅检查和输出IP信息
func OnlyBasicsIpInfo(language string) (string, string, string) {
ipv4, ipv6, ipInfo, _, err := bnetwork.NetworkCheck("both", false, language)
if err != nil {
return "", "", ""
}
basicInfo := ipInfo
if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") && ipv4 != "" && ipv6 != "" {
executor.IPV4 = true
executor.IPV6 = true
} else if strings.Contains(ipInfo, "IPV4") && ipv4 != "" {
executor.IPV4 = true
executor.IPV6 = false
} else if strings.Contains(ipInfo, "IPV6") && ipv6 != "" {
executor.IPV6 = true
executor.IPV4 = false
}
basicInfo = strings.ReplaceAll(basicInfo, "\n\n", "\n")
return ipv4, ipv6, basicInfo
}
// BasicsAndSecurityCheck 执行安全检查
func BasicsAndSecurityCheck(language, nt3CheckType string, securityCheckStatus bool) (string, string, string, string, string) {
var wgt sync.WaitGroup
var ipv4, ipv6, ipInfo, securityInfo, systemInfo string
wgt.Add(1)
go func() {
defer wgt.Done()
ipv4, ipv6, ipInfo, securityInfo, _ = network.NetworkCheck("both", securityCheckStatus, language)
// if err != nil {
// fmt.Println(err.Error())
// }
}()
wgt.Add(1)
go func() {
defer wgt.Done()
systemInfo = system.CheckSystemInfo(language)
}()
wgt.Wait()
basicInfo := systemInfo + ipInfo
if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") && ipv4 != "" && ipv6 != "" {
executor.IPV4 = true
executor.IPV6 = true
if nt3CheckType == "" {
nt3CheckType = "ipv4"
}
} else if strings.Contains(ipInfo, "IPV4") && ipv4 != "" {
executor.IPV4 = true
executor.IPV6 = false
if nt3CheckType == "" {
nt3CheckType = "ipv4"
}
} else if strings.Contains(ipInfo, "IPV6") && ipv6 != "" {
executor.IPV6 = true
executor.IPV4 = false
if nt3CheckType == "" {
nt3CheckType = "ipv6"
}
}
if nt3CheckType == "ipv4" && !strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") {
nt3CheckType = "ipv6"
} else if nt3CheckType == "ipv6" && !strings.Contains(ipInfo, "IPV6") && strings.Contains(ipInfo, "IPV4") {
nt3CheckType = "ipv4"
}
basicInfo = strings.ReplaceAll(basicInfo, "\n\n", "\n")
return ipv4, ipv6, basicInfo, securityInfo, nt3CheckType
}
// CaptureOutput 捕获函数输出和错误输出,实时输出,并返回字符串
func CaptureOutput(f func()) string {
// 保存旧的 stdout 和 stderr
oldStdout := os.Stdout
oldStderr := os.Stderr
// 创建管道
stdoutPipeR, stdoutPipeW, err := os.Pipe()
if err != nil {
return "Error creating stdout pipe"
}
stderrPipeR, stderrPipeW, err := os.Pipe()
if err != nil {
stdoutPipeW.Close()
stdoutPipeR.Close()
return "Error creating stderr pipe"
}
// 替换标准输出和标准错误输出为管道写入端
os.Stdout = stdoutPipeW
os.Stderr = stderrPipeW
// 缓冲区
var stdoutBuf, stderrBuf bytes.Buffer
// 并发读取 stdout 和 stderr
done := make(chan struct{}, 2)
go func() {
multiWriter := io.MultiWriter(&stdoutBuf, oldStdout)
io.Copy(multiWriter, stdoutPipeR)
done <- struct{}{}
}()
go func() {
multiWriter := io.MultiWriter(&stderrBuf, oldStderr)
io.Copy(multiWriter, stderrPipeR)
done <- struct{}{}
}()
// 执行函数
f()
// 确保所有输出都已经刷新
os.Stdout.Sync()
os.Stderr.Sync()
// 等待一小段时间确保后台goroutine的输出完成
time.Sleep(100 * time.Millisecond)
// 关闭管道写入端,让管道读取端可以读取所有数据
stdoutPipeW.Close()
stderrPipeW.Close()
// 等待两个 goroutine 完成
<-done
<-done
// 恢复标准输出和标准错误输出,并关闭管道读取端
os.Stdout = oldStdout
os.Stderr = oldStderr
stdoutPipeR.Close()
stderrPipeR.Close()
// 返回捕获的输出字符串
// stderrBuf.String()
return stdoutBuf.String()
}
// PrintAndCapture 捕获函数输出的同时打印内容
func PrintAndCapture(f func(), tempOutput, output string) string {
tempOutput = CaptureOutput(f)
output += tempOutput
return output
}
// UploadText 上传文本内容到指定URL
func UploadText(absPath string) (string, string, error) {
primaryURL := "http://hpaste.spiritlhl.net/api/UL/upload"
backupURL := "https://paste.spiritlhl.net/api/UL/upload"
token := "OvwKx5qgJtf7PZgCKbtyojSU.MTcwMTUxNzY1MTgwMw"
client := req.C().SetTimeout(6 * time.Second)
client.R().
SetRetryCount(2).
SetRetryBackoffInterval(1*time.Second, 5*time.Second).
SetRetryFixedInterval(2 * time.Second)
// 打开文件
file, err := os.Open(absPath)
if err != nil {
return "", "", fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// 获取文件信息并检查大小
fileInfo, err := file.Stat()
if err != nil {
return "", "", fmt.Errorf("failed to get file info: %w", err)
}
if fileInfo.Size() > 25*1024 { // 25KB
return "", "", fmt.Errorf("file size exceeds 25KB limit")
}
// 上传逻辑
upload := func(url string) (string, string, error) {
file, err := os.Open(absPath)
if err != nil {
return "", "", fmt.Errorf("failed to re-open file for %s: %w", url, err)
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
return "", "", fmt.Errorf("failed to read file content for %s: %w", url, err)
}
resp, err := client.R().
SetHeader("Authorization", token).
SetFileBytes("file", filepath.Base(absPath), content).
Post(url)
if err != nil {
return "", "", fmt.Errorf("failed to make request to %s: %w", url, err)
}
if resp.StatusCode >= 200 && resp.StatusCode <= 299 && resp.String() != "" {
fileID := strings.TrimSpace(resp.String())
if strings.Contains(fileID, "show") {
fileID = fileID[strings.LastIndex(fileID, "/")+1:]
}
httpURL := fmt.Sprintf("http://hpaste.spiritlhl.net/#/show/%s", fileID)
httpsURL := fmt.Sprintf("https://paste.spiritlhl.net/#/show/%s", fileID)
return httpURL, httpsURL, nil
}
return "", "", fmt.Errorf("upload failed for %s with status code: %d", url, resp.StatusCode)
}
// 尝试上传到主URL
httpURL, httpsURL, err := upload(primaryURL)
if err == nil {
return httpURL, httpsURL, nil
}
// 尝试上传到备份URL
httpURL, httpsURL, err = upload(backupURL)
if err != nil {
return "", "", fmt.Errorf("failed to upload to both primary and backup URLs: %w", err)
}
return httpURL, httpsURL, nil
}
// ProcessAndUpload 创建结果文件并上传文件
func ProcessAndUpload(output string, filePath string, enableUplaod bool, language string) (string, string) {
// 使用 defer 来处理 panic
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(os.Stderr, "[ERROR] Fatal error during upload: %v\n", r)
}
}()
// 检查文件是否存在
if _, err := os.Stat(filePath); err == nil {
// 文件存在,删除文件
err = os.Remove(filePath)
if err != nil {
if language == "zh" {
fmt.Println("无法删除文件:", err)
} else {
fmt.Println("Failed to delete file:", err)
}
return "", ""
}
}
// 创建文件
file, err := os.Create(filePath)
if err != nil {
if language == "zh" {
fmt.Println("无法创建文件:", err)
} else {
fmt.Println("Failed to create file:", err)
}
return "", ""
}
defer file.Close()
// 匹配 ANSI 转义序列
ansiRegex := regexp.MustCompile("\x1B\\[[0-9;]+[a-zA-Z]")
// 移除 ANSI 转义序列
cleanedOutput := ansiRegex.ReplaceAllString(output, "")
// 使用 bufio.Writer 提高写入效率
writer := bufio.NewWriter(file)
_, err = writer.WriteString(cleanedOutput)
if err != nil {
if language == "zh" {
fmt.Println("无法写入文件:", err)
} else {
fmt.Println("Failed to write file:", err)
}
return "", ""
}
// 确保写入缓冲区的数据都刷新到文件中
err = writer.Flush()
if err != nil {
if language == "zh" {
fmt.Println("无法刷新文件缓冲:", err)
} else {
fmt.Println("Failed to flush file buffer:", err)
}
return "", ""
}
if language == "zh" {
fmt.Printf("测试结果已写入 %s\n", filePath)
} else {
fmt.Printf("Test results written to %s\n", filePath)
}
if enableUplaod {
// 获取文件的绝对路径
absPath, err := filepath.Abs(filePath)
if err != nil {
if language == "zh" {
fmt.Println("无法获取文件绝对路径:", err)
} else {
fmt.Println("Failed to get absolute file path:", err)
}
return "", ""
}
// 上传文件并生成短链接
http_url, https_url, err := UploadText(absPath)
if err != nil {
if language == "zh" {
fmt.Println("上传失败,无法生成链接")
} else {
fmt.Println("Upload failed, unable to generate link")
}
fmt.Println(err.Error())
return "", ""
}
return http_url, https_url
}
return "", ""
}
var StackType string
type NetCheckResult struct {
HasIPv4 bool
HasIPv6 bool
Connected bool
StackType string // "IPv4", "IPv6", "DualStack", "None"
}
func makeResolver(proto, dnsAddr string) *net.Resolver {
return &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: 5 * time.Second,
}
return d.DialContext(ctx, proto, dnsAddr)
},
}
}
// 前置联网能力检测
func CheckPublicAccess(timeout time.Duration) NetCheckResult {
if timeout < 2*time.Second {
timeout = 2 * time.Second
}
var wg sync.WaitGroup
resultChan := make(chan string, 8)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
checks := []struct {
Tag string
Addr string
Kind string // udp4, udp6, http4, http6
}{
// UDP DNS
{"IPv4", "223.5.5.5:53", "udp4"}, // 阿里 DNS
{"IPv4", "8.8.8.8:53", "udp4"}, // Google DNS
{"IPv6", "[2400:3200::1]:53", "udp6"}, // 阿里 IPv6 DNS
{"IPv6", "[2001:4860:4860::8888]:53", "udp6"}, // Google IPv6 DNS
// HTTP HEAD
{"IPv4", "https://www.baidu.com", "http4"}, // 百度
{"IPv4", "https://1.1.1.1", "http4"}, // Cloudflare
{"IPv6", "https://[2400:3200::1]", "http6"}, // 阿里 IPv6
{"IPv6", "https://[2606:4700::1111]", "http6"}, // Cloudflare IPv6
}
for _, check := range checks {
wg.Add(1)
go func(tag, addr, kind string) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
// 记录panic但不影响其他检查输出到stderr避免污染主输出
fmt.Fprintf(os.Stderr, "[WARN] Panic in network check for %s (%s): %v\n", tag, addr, r)
}
}()
switch kind {
case "udp4", "udp6":
dialer := &net.Dialer{
Timeout: timeout / 4,
}
conn, err := dialer.DialContext(ctx, kind, addr)
if err == nil && conn != nil {
conn.Close()
select {
case resultChan <- tag:
case <-ctx.Done():
return
}
}
case "http4", "http6":
var resolver *net.Resolver
if kind == "http4" {
resolver = makeResolver("udp4", "223.5.5.5:53")
} else {
resolver = makeResolver("udp6", "[2400:3200::1]:53")
}
dialer := &net.Dialer{
Timeout: timeout / 4,
Resolver: resolver,
}
transport := &http.Transport{
DialContext: dialer.DialContext,
MaxIdleConns: 1,
MaxIdleConnsPerHost: 1,
IdleConnTimeout: time.Second,
TLSHandshakeTimeout: timeout / 4,
ResponseHeaderTimeout: timeout / 4,
DisableKeepAlives: true,
}
client := &http.Client{
Timeout: timeout / 4,
Transport: transport,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequestWithContext(ctx, "HEAD", addr, nil)
if err != nil {
return
}
resp, err := client.Do(req)
if err == nil && resp != nil {
if resp.Body != nil {
resp.Body.Close()
}
if resp.StatusCode < 500 {
select {
case resultChan <- tag:
case <-ctx.Done():
return
}
}
}
}
}(check.Tag, check.Addr, check.Kind)
}
go func() {
wg.Wait()
close(resultChan)
}()
hasV4 := false
hasV6 := false
for {
select {
case res, ok := <-resultChan:
if !ok {
goto result
}
if res == "IPv4" {
hasV4 = true
}
if res == "IPv6" {
hasV6 = true
}
case <-ctx.Done():
goto result
}
}
result:
stack := "None"
if hasV4 && hasV6 {
stack = "DualStack"
} else if hasV4 {
stack = "IPv4"
} else if hasV6 {
stack = "IPv6"
}
StackType = stack
butils.CheckPublicAccess(3 * time.Second) // 设置basics检测避免部分测试未启用
return NetCheckResult{
HasIPv4: hasV4,
HasIPv6: hasV6,
Connected: hasV4 || hasV6,
StackType: stack,
}
}
// 获取每日/总的程序执行统计信息
func GetGoescStats() (*StatsResponse, error) {
client := req.C().SetTimeout(5 * time.Second)
var stats StatsResponse
resp, err := client.R().
SetSuccessResult(&stats).
Get("https://hits.spiritlhl.net/goecs")
if err != nil {
return nil, err
}
if !resp.IsSuccessState() {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
return &stats, nil
}
// 统计结果单位转换
func FormatGoecsNumber(num int) string {
if num >= 1000000 {
return fmt.Sprintf("%.1fM", float64(num)/1000000)
} else if num >= 1000 {
return fmt.Sprintf("%.1fK", float64(num)/1000)
}
return fmt.Sprintf("%d", num)
}
// 通过Github的API检索仓库最新TAG的版本
func GetLatestEcsRelease() (*GitHubRelease, error) {
urls := []string{
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest",
"https://fd.spiritlhl.top/https://api.github.com/repos/oneclickvirt/ecs/releases/latest",
"https://githubapi.spiritlhl.top/repos/oneclickvirt/ecs/releases/latest",
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest",
}
client := req.C().SetTimeout(3 * time.Second)
for _, url := range urls {
var release GitHubRelease
resp, err := client.R().
SetSuccessResult(&release).
Get(url)
if err != nil {
continue
}
if resp.IsSuccessState() && release.TagName != "" {
return &release, nil
}
}
return nil, fmt.Errorf("failed to fetch release from all sources")
}
// 比较程序版本是否需要升级
func CompareVersions(v1, v2 string) int {
normalize := func(s string) []int {
s = strings.TrimPrefix(strings.ToLower(s), "v")
parts := strings.Split(s, ".")
result := make([]int, 3)
for i := 0; i < 3 && i < len(parts); i++ {
n, _ := strconv.Atoi(parts[i])
result[i] = n
}
return result
}
a := normalize(v1)
b := normalize(v2)
for i := 0; i < 3; i++ {
if a[i] < b[i] {
return -1
} else if a[i] > b[i] {
return 1
}
}
return 0
}

View File

@@ -1,31 +0,0 @@
package utils
import (
"fmt"
"testing"
"time"
)
// func TestCheckPublicAccess(t *testing.T) {
// timeout := 3 * time.Second
// result := CheckPublicAccess(timeout)
// if result.Connected {
// fmt.Print("✅ 本机有公网连接,类型: %s\n", result.StackType)
// } else {
// fmt.Println("❌ 本机未检测到公网连接")
// }
// }
func TestBasicsAndSecurityCheck(t *testing.T) {
timeout := 3 * time.Second
result := CheckPublicAccess(timeout)
if result.Connected {
fmt.Printf("✅ 本机有公网连接,类型: %s\n", result.StackType)
} else {
fmt.Println("❌ 本机未检测到公网连接")
}
_, _, basicInfo, securityInfo, nt3CheckType := BasicsAndSecurityCheck("zh", "ipv4", false)
fmt.Println(basicInfo)
fmt.Println(securityInfo)
fmt.Println(nt3CheckType)
}