nodejs-argo 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/index.js +643 -635
- package/package.json +5 -5
package/index.js
CHANGED
|
@@ -1,636 +1,644 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const express = require("express");
|
|
4
|
-
const app = express();
|
|
5
|
-
const axios = require("axios");
|
|
6
|
-
const os = require('os');
|
|
7
|
-
const fs = require("fs");
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const UPLOAD_URL = process.env.UPLOAD_URL || ''; // 节点或订阅自动上传地址,需填写部署Merge-sub项目后的首页地址,例如:https://merge.xxx.com
|
|
13
|
-
const PROJECT_URL = process.env.PROJECT_URL || ''; // 需要上传订阅或保活时需填写项目分配的url,例如:https://google.com
|
|
14
|
-
const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // false关闭自动保活,true开启,需同时填写PROJECT_URL变量
|
|
15
|
-
const FILE_PATH = process.env.FILE_PATH || '
|
|
16
|
-
const SUB_PATH = process.env.SUB_PATH || 'sub'; // 订阅路径
|
|
17
|
-
const PORT = process.env.SERVER_PORT || process.env.PORT || 3000; // http服务订阅端口
|
|
18
|
-
const UUID = process.env.UUID || '9afd1229-b893-40c1-84dd-51e7ce204913'; // 使用哪吒v1,在不同的平台运行需修改UUID,否则会覆盖
|
|
19
|
-
const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒v1填写形式: nz.abc.com:8008 哪吒v0填写形式:nz.abc.com
|
|
20
|
-
const NEZHA_PORT = process.env.NEZHA_PORT || ''; // 使用哪吒v1请留空,哪吒v0需填写
|
|
21
|
-
const NEZHA_KEY = process.env.NEZHA_KEY || ''; // 哪吒v1的NZ_CLIENT_SECRET或哪吒v0的agent密钥
|
|
22
|
-
const ARGO_DOMAIN = process.env.ARGO_DOMAIN || ''; // 固定隧道域名,留空即启用临时隧道
|
|
23
|
-
const ARGO_AUTH = process.env.ARGO_AUTH || ''; // 固定隧道密钥json或token,留空即启用临时隧道,json获取地址:https://json.zone.id
|
|
24
|
-
const ARGO_PORT = process.env.ARGO_PORT || 8001; // 固定隧道端口,使用token需在cloudflare后台设置和这里一致
|
|
25
|
-
const CFIP = process.env.CFIP || 'cdns.doon.eu.org'; // 节点优选域名或优选ip
|
|
26
|
-
const CFPORT = process.env.CFPORT || 443; // 节点优选域名或优选ip对应的端口
|
|
27
|
-
const NAME = process.env.NAME || ''; // 节点名称
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log(`${FILE_PATH}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let
|
|
55
|
-
let
|
|
56
|
-
let
|
|
57
|
-
let
|
|
58
|
-
let
|
|
59
|
-
let
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.
|
|
169
|
-
callback(
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
console.
|
|
612
|
-
return
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
await
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const express = require("express");
|
|
4
|
+
const app = express();
|
|
5
|
+
const axios = require("axios");
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
require('dotenv').config();
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
const exec = promisify(require('child_process').exec); // 只填写UPLOAD_URL将上传单节点,同时填写UPLOAD_URL和PROJECT_URL将上传订阅
|
|
12
|
+
const UPLOAD_URL = process.env.UPLOAD_URL || ''; // 节点或订阅自动上传地址,需填写部署Merge-sub项目后的首页地址,例如:https://merge.xxx.com
|
|
13
|
+
const PROJECT_URL = process.env.PROJECT_URL || ''; // 需要上传订阅或保活时需填写项目分配的url,例如:https://google.com
|
|
14
|
+
const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // false关闭自动保活,true开启,需同时填写PROJECT_URL变量
|
|
15
|
+
const FILE_PATH = process.env.FILE_PATH || '.npm'; // 运行目录,sub节点文件保存目录
|
|
16
|
+
const SUB_PATH = process.env.SUB_PATH || 'sub'; // 订阅路径
|
|
17
|
+
const PORT = process.env.SERVER_PORT || process.env.PORT || 3000; // http服务订阅端口
|
|
18
|
+
const UUID = process.env.UUID || '9afd1229-b893-40c1-84dd-51e7ce204913'; // 使用哪吒v1,在不同的平台运行需修改UUID,否则会覆盖
|
|
19
|
+
const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒v1填写形式: nz.abc.com:8008 哪吒v0填写形式:nz.abc.com
|
|
20
|
+
const NEZHA_PORT = process.env.NEZHA_PORT || ''; // 使用哪吒v1请留空,哪吒v0需填写
|
|
21
|
+
const NEZHA_KEY = process.env.NEZHA_KEY || ''; // 哪吒v1的NZ_CLIENT_SECRET或哪吒v0的agent密钥
|
|
22
|
+
const ARGO_DOMAIN = process.env.ARGO_DOMAIN || ''; // 固定隧道域名,留空即启用临时隧道
|
|
23
|
+
const ARGO_AUTH = process.env.ARGO_AUTH || ''; // 固定隧道密钥json或token,留空即启用临时隧道,json获取地址:https://json.zone.id
|
|
24
|
+
const ARGO_PORT = process.env.ARGO_PORT || 8001; // 固定隧道端口,使用token需在cloudflare后台设置和这里一致
|
|
25
|
+
const CFIP = process.env.CFIP || 'cdns.doon.eu.org'; // 节点优选域名或优选ip
|
|
26
|
+
const CFPORT = process.env.CFPORT || 443; // 节点优选域名或优选ip对应的端口
|
|
27
|
+
const NAME = process.env.NAME || ''; // 节点名称
|
|
28
|
+
|
|
29
|
+
// 创建运行文件夹
|
|
30
|
+
if (!fs.existsSync(FILE_PATH)) {
|
|
31
|
+
fs.mkdirSync(FILE_PATH);
|
|
32
|
+
console.log(`${FILE_PATH} is created`);
|
|
33
|
+
} else {
|
|
34
|
+
console.log(`${FILE_PATH} already exists`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 生成随机6位字符文件名
|
|
38
|
+
function generateRandomName() {
|
|
39
|
+
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
40
|
+
let result = '';
|
|
41
|
+
for (let i = 0; i < 6; i++) {
|
|
42
|
+
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 全局常量
|
|
48
|
+
const npmName = generateRandomName();
|
|
49
|
+
const webName = generateRandomName();
|
|
50
|
+
const botName = generateRandomName();
|
|
51
|
+
const phpName = generateRandomName();
|
|
52
|
+
let npmPath = path.join(FILE_PATH, npmName);
|
|
53
|
+
let phpPath = path.join(FILE_PATH, phpName);
|
|
54
|
+
let webPath = path.join(FILE_PATH, webName);
|
|
55
|
+
let botPath = path.join(FILE_PATH, botName);
|
|
56
|
+
let subPath = path.join(FILE_PATH, 'sub.txt');
|
|
57
|
+
let listPath = path.join(FILE_PATH, 'list.txt');
|
|
58
|
+
let bootLogPath = path.join(FILE_PATH, 'boot.log');
|
|
59
|
+
let configPath = path.join(FILE_PATH, 'config.json');
|
|
60
|
+
|
|
61
|
+
// 如果订阅器上存在历史运行节点则先删除
|
|
62
|
+
function deleteNodes() {
|
|
63
|
+
try {
|
|
64
|
+
if (!UPLOAD_URL) return;
|
|
65
|
+
if (!fs.existsSync(subPath)) return;
|
|
66
|
+
|
|
67
|
+
let fileContent;
|
|
68
|
+
try {
|
|
69
|
+
fileContent = fs.readFileSync(subPath, 'utf-8');
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const decoded = Buffer.from(fileContent, 'base64').toString('utf-8');
|
|
75
|
+
const nodes = decoded.split('\n').filter(line =>
|
|
76
|
+
/(vless|vmess|trojan|hysteria2|tuic):\/\//.test(line)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (nodes.length === 0) return;
|
|
80
|
+
|
|
81
|
+
axios.post(`${UPLOAD_URL}/api/delete-nodes`,
|
|
82
|
+
JSON.stringify({ nodes }),
|
|
83
|
+
{ headers: { 'Content-Type': 'application/json' } }
|
|
84
|
+
).catch((error) => {
|
|
85
|
+
return null;
|
|
86
|
+
});
|
|
87
|
+
return null;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 清理历史文件
|
|
94
|
+
function cleanupOldFiles() {
|
|
95
|
+
try {
|
|
96
|
+
const files = fs.readdirSync(FILE_PATH);
|
|
97
|
+
files.forEach(file => {
|
|
98
|
+
const filePath = path.join(FILE_PATH, file);
|
|
99
|
+
try {
|
|
100
|
+
const stat = fs.statSync(filePath);
|
|
101
|
+
if (stat.isFile()) {
|
|
102
|
+
fs.unlinkSync(filePath);
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
// 忽略所有错误,不记录日志
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
// 忽略所有错误,不记录日志
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 生成xr-ay配置文件
|
|
114
|
+
async function generateConfig() {
|
|
115
|
+
const config = {
|
|
116
|
+
log: { access: '/dev/null', error: '/dev/null', loglevel: 'none' },
|
|
117
|
+
inbounds: [
|
|
118
|
+
{ port: ARGO_PORT, protocol: 'vless', settings: { clients: [{ id: UUID, flow: 'xtls-rprx-vision' }], decryption: 'none', fallbacks: [{ dest: 3001 }, { path: "/vless-argo", dest: 3002 }, { path: "/vmess-argo", dest: 3003 }, { path: "/trojan-argo", dest: 3004 }] }, streamSettings: { network: 'tcp' } },
|
|
119
|
+
{ port: 3001, listen: "127.0.0.1", protocol: "vless", settings: { clients: [{ id: UUID }], decryption: "none" }, streamSettings: { network: "tcp", security: "none" } },
|
|
120
|
+
{ port: 3002, listen: "127.0.0.1", protocol: "vless", settings: { clients: [{ id: UUID, level: 0 }], decryption: "none" }, streamSettings: { network: "ws", security: "none", wsSettings: { path: "/vless-argo" } }, sniffing: { enabled: true, destOverride: ["http", "tls", "quic"], metadataOnly: false } },
|
|
121
|
+
{ port: 3003, listen: "127.0.0.1", protocol: "vmess", settings: { clients: [{ id: UUID, alterId: 0 }] }, streamSettings: { network: "ws", wsSettings: { path: "/vmess-argo" } }, sniffing: { enabled: true, destOverride: ["http", "tls", "quic"], metadataOnly: false } },
|
|
122
|
+
{ port: 3004, listen: "127.0.0.1", protocol: "trojan", settings: { clients: [{ password: UUID }] }, streamSettings: { network: "ws", security: "none", wsSettings: { path: "/trojan-argo" } }, sniffing: { enabled: true, destOverride: ["http", "tls", "quic"], metadataOnly: false } },
|
|
123
|
+
],
|
|
124
|
+
dns: { servers: ["https+local://8.8.8.8/dns-query"] },
|
|
125
|
+
outbounds: [ { protocol: "freedom", tag: "direct" }, {protocol: "blackhole", tag: "block"} ]
|
|
126
|
+
};
|
|
127
|
+
fs.writeFileSync(path.join(FILE_PATH, 'config.json'), JSON.stringify(config, null, 2));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 判断系统架构
|
|
131
|
+
function getSystemArchitecture() {
|
|
132
|
+
const arch = os.arch();
|
|
133
|
+
if (arch === 'arm' || arch === 'arm64' || arch === 'aarch64') {
|
|
134
|
+
return 'arm';
|
|
135
|
+
} else {
|
|
136
|
+
return 'amd';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 下载对应系统架构的依赖文件
|
|
141
|
+
function downloadFile(fileName, fileUrl, callback) {
|
|
142
|
+
const filePath = fileName;
|
|
143
|
+
|
|
144
|
+
// 确保目录存在
|
|
145
|
+
if (!fs.existsSync(FILE_PATH)) {
|
|
146
|
+
fs.mkdirSync(FILE_PATH, { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const writer = fs.createWriteStream(filePath);
|
|
150
|
+
|
|
151
|
+
axios({
|
|
152
|
+
method: 'get',
|
|
153
|
+
url: fileUrl,
|
|
154
|
+
responseType: 'stream',
|
|
155
|
+
})
|
|
156
|
+
.then(response => {
|
|
157
|
+
response.data.pipe(writer);
|
|
158
|
+
|
|
159
|
+
writer.on('finish', () => {
|
|
160
|
+
writer.close();
|
|
161
|
+
console.log(`Download ${path.basename(filePath)} successfully`);
|
|
162
|
+
callback(null, filePath);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
writer.on('error', err => {
|
|
166
|
+
fs.unlink(filePath, () => { });
|
|
167
|
+
const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`;
|
|
168
|
+
console.error(errorMessage); // 下载失败时输出错误消息
|
|
169
|
+
callback(errorMessage);
|
|
170
|
+
});
|
|
171
|
+
})
|
|
172
|
+
.catch(err => {
|
|
173
|
+
const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`;
|
|
174
|
+
console.error(errorMessage); // 下载失败时输出错误消息
|
|
175
|
+
callback(errorMessage);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 下载并运行依赖文件
|
|
180
|
+
async function downloadFilesAndRun() {
|
|
181
|
+
|
|
182
|
+
const architecture = getSystemArchitecture();
|
|
183
|
+
const filesToDownload = getFilesForArchitecture(architecture);
|
|
184
|
+
|
|
185
|
+
if (filesToDownload.length === 0) {
|
|
186
|
+
console.log(`Can't find a file for the current architecture`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const downloadPromises = filesToDownload.map(fileInfo => {
|
|
191
|
+
return new Promise((resolve, reject) => {
|
|
192
|
+
downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, filePath) => {
|
|
193
|
+
if (err) {
|
|
194
|
+
reject(err);
|
|
195
|
+
} else {
|
|
196
|
+
resolve(filePath);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
await Promise.all(downloadPromises);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error('Error downloading files:', err);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// 授权和运行
|
|
209
|
+
function authorizeFiles(filePaths) {
|
|
210
|
+
const newPermissions = 0o775;
|
|
211
|
+
filePaths.forEach(absoluteFilePath => {
|
|
212
|
+
if (fs.existsSync(absoluteFilePath)) {
|
|
213
|
+
fs.chmod(absoluteFilePath, newPermissions, (err) => {
|
|
214
|
+
if (err) {
|
|
215
|
+
console.error(`Empowerment failed for ${absoluteFilePath}: ${err}`);
|
|
216
|
+
} else {
|
|
217
|
+
console.log(`Empowerment success for ${absoluteFilePath}: ${newPermissions.toString(8)}`);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const filesToAuthorize = NEZHA_PORT ? [npmPath, webPath, botPath] : [phpPath, webPath, botPath];
|
|
224
|
+
authorizeFiles(filesToAuthorize);
|
|
225
|
+
|
|
226
|
+
//运行ne-zha
|
|
227
|
+
if (NEZHA_SERVER && NEZHA_KEY) {
|
|
228
|
+
if (!NEZHA_PORT) {
|
|
229
|
+
// 检测哪吒是否开启TLS
|
|
230
|
+
const port = NEZHA_SERVER.includes(':') ? NEZHA_SERVER.split(':').pop() : '';
|
|
231
|
+
const tlsPorts = new Set(['443', '8443', '2096', '2087', '2083', '2053']);
|
|
232
|
+
const nezhatls = tlsPorts.has(port) ? 'true' : 'false';
|
|
233
|
+
// 生成 config.yaml
|
|
234
|
+
const configYaml = `
|
|
235
|
+
client_secret: ${NEZHA_KEY}
|
|
236
|
+
debug: false
|
|
237
|
+
disable_auto_update: true
|
|
238
|
+
disable_command_execute: false
|
|
239
|
+
disable_force_update: true
|
|
240
|
+
disable_nat: false
|
|
241
|
+
disable_send_query: false
|
|
242
|
+
gpu: false
|
|
243
|
+
insecure_tls: true
|
|
244
|
+
ip_report_period: 1800
|
|
245
|
+
report_delay: 4
|
|
246
|
+
server: ${NEZHA_SERVER}
|
|
247
|
+
skip_connection_count: true
|
|
248
|
+
skip_procs_count: true
|
|
249
|
+
temperature: false
|
|
250
|
+
tls: ${nezhatls}
|
|
251
|
+
use_gitee_to_upgrade: false
|
|
252
|
+
use_ipv6_country_code: false
|
|
253
|
+
uuid: ${UUID}`;
|
|
254
|
+
|
|
255
|
+
fs.writeFileSync(path.join(FILE_PATH, 'config.yaml'), configYaml);
|
|
256
|
+
|
|
257
|
+
// 运行 v1
|
|
258
|
+
const command = `nohup ${phpPath} -c "${FILE_PATH}/config.yaml" >/dev/null 2>&1 &`;
|
|
259
|
+
try {
|
|
260
|
+
await exec(command);
|
|
261
|
+
console.log(`${phpName} is running`);
|
|
262
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error(`php running error: ${error}`);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
let NEZHA_TLS = '';
|
|
268
|
+
const tlsPorts = ['443', '8443', '2096', '2087', '2083', '2053'];
|
|
269
|
+
if (tlsPorts.includes(NEZHA_PORT)) {
|
|
270
|
+
NEZHA_TLS = '--tls';
|
|
271
|
+
}
|
|
272
|
+
const command = `nohup ${npmPath} -s ${NEZHA_SERVER}:${NEZHA_PORT} -p ${NEZHA_KEY} ${NEZHA_TLS} --disable-auto-update --report-delay 4 --skip-conn --skip-procs >/dev/null 2>&1 &`;
|
|
273
|
+
try {
|
|
274
|
+
await exec(command);
|
|
275
|
+
console.log(`${npmName} is running`);
|
|
276
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error(`npm running error: ${error}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
console.log('NEZHA variable is empty,skip running');
|
|
283
|
+
}
|
|
284
|
+
//运行xr-ay
|
|
285
|
+
const command1 = `nohup ${webPath} -c ${FILE_PATH}/config.json >/dev/null 2>&1 &`;
|
|
286
|
+
try {
|
|
287
|
+
await exec(command1);
|
|
288
|
+
console.log(`${webName} is running`);
|
|
289
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error(`web running error: ${error}`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 运行cloud-fared
|
|
295
|
+
if (fs.existsSync(botPath)) {
|
|
296
|
+
let args;
|
|
297
|
+
|
|
298
|
+
if (ARGO_AUTH.match(/^[A-Z0-9a-z=]{120,250}$/)) {
|
|
299
|
+
args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}`;
|
|
300
|
+
} else if (ARGO_AUTH.match(/TunnelSecret/)) {
|
|
301
|
+
args = `tunnel --edge-ip-version auto --config ${FILE_PATH}/tunnel.yml run`;
|
|
302
|
+
} else {
|
|
303
|
+
args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${FILE_PATH}/boot.log --loglevel info --url http://localhost:${ARGO_PORT}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`);
|
|
308
|
+
console.log(`${botName} is running`);
|
|
309
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error(`Error executing command: ${error}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
//根据系统架构返回对应的url
|
|
319
|
+
function getFilesForArchitecture(architecture) {
|
|
320
|
+
let baseFiles;
|
|
321
|
+
if (architecture === 'arm') {
|
|
322
|
+
baseFiles = [
|
|
323
|
+
{ fileName: webPath, fileUrl: "https://arm64.ssss.nyc.mn/web" },
|
|
324
|
+
{ fileName: botPath, fileUrl: "https://arm64.ssss.nyc.mn/bot" }
|
|
325
|
+
];
|
|
326
|
+
} else {
|
|
327
|
+
baseFiles = [
|
|
328
|
+
{ fileName: webPath, fileUrl: "https://amd64.ssss.nyc.mn/web" },
|
|
329
|
+
{ fileName: botPath, fileUrl: "https://amd64.ssss.nyc.mn/bot" }
|
|
330
|
+
];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (NEZHA_SERVER && NEZHA_KEY) {
|
|
334
|
+
if (NEZHA_PORT) {
|
|
335
|
+
const npmUrl = architecture === 'arm'
|
|
336
|
+
? "https://arm64.ssss.nyc.mn/agent"
|
|
337
|
+
: "https://amd64.ssss.nyc.mn/agent";
|
|
338
|
+
baseFiles.unshift({
|
|
339
|
+
fileName: npmPath,
|
|
340
|
+
fileUrl: npmUrl
|
|
341
|
+
});
|
|
342
|
+
} else {
|
|
343
|
+
const phpUrl = architecture === 'arm'
|
|
344
|
+
? "https://arm64.ssss.nyc.mn/v1"
|
|
345
|
+
: "https://amd64.ssss.nyc.mn/v1";
|
|
346
|
+
baseFiles.unshift({
|
|
347
|
+
fileName: phpPath,
|
|
348
|
+
fileUrl: phpUrl
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return baseFiles;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 获取固定隧道json
|
|
357
|
+
function argoType() {
|
|
358
|
+
if (!ARGO_AUTH || !ARGO_DOMAIN) {
|
|
359
|
+
console.log("ARGO_DOMAIN or ARGO_AUTH variable is empty, use quick tunnels");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (ARGO_AUTH.includes('TunnelSecret')) {
|
|
364
|
+
fs.writeFileSync(path.join(FILE_PATH, 'tunnel.json'), ARGO_AUTH);
|
|
365
|
+
const tunnelYaml = `
|
|
366
|
+
tunnel: ${ARGO_AUTH.split('"')[11]}
|
|
367
|
+
credentials-file: ${path.join(FILE_PATH, 'tunnel.json')}
|
|
368
|
+
protocol: http2
|
|
369
|
+
|
|
370
|
+
ingress:
|
|
371
|
+
- hostname: ${ARGO_DOMAIN}
|
|
372
|
+
service: http://localhost:${ARGO_PORT}
|
|
373
|
+
originRequest:
|
|
374
|
+
noTLSVerify: true
|
|
375
|
+
- service: http_status:404
|
|
376
|
+
`;
|
|
377
|
+
fs.writeFileSync(path.join(FILE_PATH, 'tunnel.yml'), tunnelYaml);
|
|
378
|
+
} else {
|
|
379
|
+
console.log(`Using token connect to tunnel,please set ${ARGO_PORT} in cloudflare tunnel`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
argoType();
|
|
383
|
+
|
|
384
|
+
// 获取临时隧道domain
|
|
385
|
+
async function extractDomains() {
|
|
386
|
+
let argoDomain;
|
|
387
|
+
|
|
388
|
+
if (ARGO_AUTH && ARGO_DOMAIN) {
|
|
389
|
+
argoDomain = ARGO_DOMAIN;
|
|
390
|
+
console.log('ARGO_DOMAIN:', argoDomain);
|
|
391
|
+
await generateLinks(argoDomain);
|
|
392
|
+
} else {
|
|
393
|
+
try {
|
|
394
|
+
const fileContent = fs.readFileSync(path.join(FILE_PATH, 'boot.log'), 'utf-8');
|
|
395
|
+
const lines = fileContent.split('\n');
|
|
396
|
+
const argoDomains = [];
|
|
397
|
+
lines.forEach((line) => {
|
|
398
|
+
const domainMatch = line.match(/https?:\/\/([^ ]*trycloudflare\.com)\/?/);
|
|
399
|
+
if (domainMatch) {
|
|
400
|
+
const domain = domainMatch[1];
|
|
401
|
+
argoDomains.push(domain);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (argoDomains.length > 0) {
|
|
406
|
+
argoDomain = argoDomains[0];
|
|
407
|
+
console.log('ArgoDomain:', argoDomain);
|
|
408
|
+
await generateLinks(argoDomain);
|
|
409
|
+
} else {
|
|
410
|
+
console.log('ArgoDomain not found, re-running bot to obtain ArgoDomain');
|
|
411
|
+
// 删除 boot.log 文件,等待 2s 重新运行 server 以获取 ArgoDomain
|
|
412
|
+
fs.unlinkSync(path.join(FILE_PATH, 'boot.log'));
|
|
413
|
+
async function killBotProcess() {
|
|
414
|
+
try {
|
|
415
|
+
// Windows系统使用taskkill命令
|
|
416
|
+
if (process.platform === 'win32') {
|
|
417
|
+
await exec(`taskkill /f /im ${botName}.exe > nul 2>&1`);
|
|
418
|
+
} else {
|
|
419
|
+
await exec(`pkill -f "[${botName.charAt(0)}]${botName.substring(1)}" > /dev/null 2>&1`);
|
|
420
|
+
}
|
|
421
|
+
} catch (error) {
|
|
422
|
+
// 忽略输出
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
killBotProcess();
|
|
426
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
427
|
+
const args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${FILE_PATH}/boot.log --loglevel info --url http://localhost:${ARGO_PORT}`;
|
|
428
|
+
try {
|
|
429
|
+
await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`);
|
|
430
|
+
console.log(`${botName} is running`);
|
|
431
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
432
|
+
await extractDomains(); // 重新提取域名
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error(`Error executing command: ${error}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.error('Error reading boot.log:', error);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 生成 list 和 sub 信息
|
|
443
|
+
async function generateLinks(argoDomain) {
|
|
444
|
+
let ISP = 'Unknown';
|
|
445
|
+
try {
|
|
446
|
+
const response = await axios.get('https://api.ip.sb/geoip', {
|
|
447
|
+
timeout: 5000,
|
|
448
|
+
headers: {
|
|
449
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
if (response.data) {
|
|
454
|
+
const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;
|
|
455
|
+
if (data.country_code && data.isp) {
|
|
456
|
+
ISP = `${data.country_code}-${data.isp}`.replace(/\s+/g, '_');
|
|
457
|
+
} else {
|
|
458
|
+
ISP = data.country_code || data.isp;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
try {
|
|
463
|
+
const response2 = await axios.get('http://ip-api.com/json', {
|
|
464
|
+
timeout: 3000,
|
|
465
|
+
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }
|
|
466
|
+
});
|
|
467
|
+
if (response2.data && response2.data.status === 'success' && response2.data.countryCode && response2.data.org) {
|
|
468
|
+
ISP = `${response2.data.countryCode}_${response2.data.org}`.replace(/\s+/g, '_');
|
|
469
|
+
} else {
|
|
470
|
+
ISP = 'Unknown';
|
|
471
|
+
}
|
|
472
|
+
} catch (execErr) {
|
|
473
|
+
ISP = 'Unknown';
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// 如果 NAME 为空,则只使用 ISP 作为名称
|
|
477
|
+
const nodeName = NAME ? `${NAME}-${ISP}` : ISP;
|
|
478
|
+
|
|
479
|
+
return new Promise((resolve) => {
|
|
480
|
+
setTimeout(() => {
|
|
481
|
+
const VMESS = { v: '2', ps: `${nodeName}`, add: CFIP, port: CFPORT, id: UUID, aid: '0', scy: 'auto', net: 'ws', type: 'none', host: argoDomain, path: '/vmess-argo?ed=2560', tls: 'tls', sni: argoDomain, alpn: '', fp: 'firefox'};
|
|
482
|
+
const subTxt = `
|
|
483
|
+
vless://${UUID}@${CFIP}:${CFPORT}?encryption=none&security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Fvless-argo%3Fed%3D2560#${nodeName}
|
|
484
|
+
|
|
485
|
+
vmess://${Buffer.from(JSON.stringify(VMESS)).toString('base64')}
|
|
486
|
+
|
|
487
|
+
trojan://${UUID}@${CFIP}:${CFPORT}?security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Ftrojan-argo%3Fed%3D2560#${nodeName}
|
|
488
|
+
`;
|
|
489
|
+
// 打印 sub.txt 内容到控制台
|
|
490
|
+
console.log('\x1b[32m' + Buffer.from(subTxt).toString('base64') + '\x1b[0m'); // 输出绿色
|
|
491
|
+
console.log('\x1b[35m' + 'Logs will be deleted in 90 seconds,you can copy the above nodes' + '\x1b[0m'); // 洋红色
|
|
492
|
+
fs.writeFileSync(subPath, Buffer.from(subTxt).toString('base64'));
|
|
493
|
+
console.log(`${FILE_PATH}/sub.txt saved successfully`);
|
|
494
|
+
uploadNodes();
|
|
495
|
+
// 将内容进行 base64 编码并写入 SUB_PATH 路由
|
|
496
|
+
app.get(`/${SUB_PATH}`, (req, res) => {
|
|
497
|
+
const encodedContent = Buffer.from(subTxt).toString('base64');
|
|
498
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
499
|
+
res.send(encodedContent);
|
|
500
|
+
});
|
|
501
|
+
resolve(subTxt);
|
|
502
|
+
}, 2000);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// 自动上传节点或订阅
|
|
508
|
+
async function uploadNodes() {
|
|
509
|
+
if (UPLOAD_URL && PROJECT_URL) {
|
|
510
|
+
const subscriptionUrl = `${PROJECT_URL}/${SUB_PATH}`;
|
|
511
|
+
const jsonData = {
|
|
512
|
+
subscription: [subscriptionUrl]
|
|
513
|
+
};
|
|
514
|
+
try {
|
|
515
|
+
const response = await axios.post(`${UPLOAD_URL}/api/add-subscriptions`, jsonData, {
|
|
516
|
+
headers: {
|
|
517
|
+
'Content-Type': 'application/json'
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
if (response && response.status === 200) {
|
|
522
|
+
console.log('Subscription uploaded successfully');
|
|
523
|
+
return response;
|
|
524
|
+
} else {
|
|
525
|
+
return null;
|
|
526
|
+
// console.log('Unknown response status');
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
if (error.response) {
|
|
530
|
+
if (error.response.status === 400) {
|
|
531
|
+
// console.error('Subscription already exists');
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} else if (UPLOAD_URL) {
|
|
536
|
+
if (!fs.existsSync(listPath)) return;
|
|
537
|
+
const content = fs.readFileSync(listPath, 'utf-8');
|
|
538
|
+
const nodes = content.split('\n').filter(line => /(vless|vmess|trojan|hysteria2|tuic):\/\//.test(line));
|
|
539
|
+
|
|
540
|
+
if (nodes.length === 0) return;
|
|
541
|
+
|
|
542
|
+
const jsonData = JSON.stringify({ nodes });
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
const response = await axios.post(`${UPLOAD_URL}/api/add-nodes`, jsonData, {
|
|
546
|
+
headers: { 'Content-Type': 'application/json' }
|
|
547
|
+
});
|
|
548
|
+
if (response && response.status === 200) {
|
|
549
|
+
console.log('Nodes uploaded successfully');
|
|
550
|
+
return response;
|
|
551
|
+
} else {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
} catch (error) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
// console.log('Skipping upload nodes');
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// 90s后删除相关文件
|
|
564
|
+
function cleanFiles() {
|
|
565
|
+
setTimeout(() => {
|
|
566
|
+
const filesToDelete = [bootLogPath, configPath, webPath, botPath];
|
|
567
|
+
|
|
568
|
+
if (NEZHA_PORT) {
|
|
569
|
+
filesToDelete.push(npmPath);
|
|
570
|
+
} else if (NEZHA_SERVER && NEZHA_KEY) {
|
|
571
|
+
filesToDelete.push(phpPath);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Windows系统使用不同的删除命令
|
|
575
|
+
if (process.platform === 'win32') {
|
|
576
|
+
exec(`del /f /q ${filesToDelete.join(' ')} > nul 2>&1`, (error) => {
|
|
577
|
+
console.clear();
|
|
578
|
+
console.log('App is running');
|
|
579
|
+
console.log('Thank you for using this script, enjoy!');
|
|
580
|
+
});
|
|
581
|
+
} else {
|
|
582
|
+
exec(`rm -rf ${filesToDelete.join(' ')} >/dev/null 2>&1`, (error) => {
|
|
583
|
+
console.clear();
|
|
584
|
+
console.log('App is running');
|
|
585
|
+
console.log('Thank you for using this script, enjoy!');
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}, 90000); // 90s
|
|
589
|
+
}
|
|
590
|
+
cleanFiles();
|
|
591
|
+
|
|
592
|
+
// 自动访问项目URL
|
|
593
|
+
async function AddVisitTask() {
|
|
594
|
+
if (!AUTO_ACCESS || !PROJECT_URL) {
|
|
595
|
+
console.log("Skipping adding automatic access task");
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
const response = await axios.post('https://oooo.serv00.net/add-url', {
|
|
601
|
+
url: PROJECT_URL
|
|
602
|
+
}, {
|
|
603
|
+
headers: {
|
|
604
|
+
'Content-Type': 'application/json'
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// console.log(`${JSON.stringify(response.data)}`);
|
|
608
|
+
console.log(`automatic access task added successfully`);
|
|
609
|
+
return response;
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error(`Add automatic access task faild: ${error.message}`);
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// 主运行逻辑
|
|
617
|
+
async function startserver() {
|
|
618
|
+
try {
|
|
619
|
+
deleteNodes();
|
|
620
|
+
cleanupOldFiles();
|
|
621
|
+
await generateConfig();
|
|
622
|
+
await downloadFilesAndRun();
|
|
623
|
+
await extractDomains();
|
|
624
|
+
await AddVisitTask();
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.error('Error in startserver:', error);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
startserver().catch(error => {
|
|
630
|
+
console.error('Unhandled error in startserver:', error);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// 根路由
|
|
634
|
+
app.get("/", async function(req, res) {
|
|
635
|
+
try {
|
|
636
|
+
const filePath = path.join(__dirname, 'index.html');
|
|
637
|
+
const data = await fs.promises.readFile(filePath, 'utf8');
|
|
638
|
+
res.send(data);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
res.send("Hello world!<br><br>You can visit /{SUB_PATH}(Default: /sub) get your nodes!");
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
636
644
|
app.listen(PORT, () => console.log(`http server is running on port:${PORT}!`));
|