nodejs-argo 2.0.1 → 2.0.2

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.
Files changed (4) hide show
  1. package/LICENSE +1 -1
  2. package/index.js +180 -94
  3. package/package.json +13 -16
  4. package/README.md +0 -195
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 eooce
3
+ Copyright (c) 2024 Your Name
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- require('dotenv').config();
4
-
5
3
  const express = require("express");
6
4
  const app = express();
7
5
  const axios = require("axios");
@@ -11,7 +9,7 @@ const path = require("path");
11
9
  const { promisify } = require('util');
12
10
  const exec = promisify(require('child_process').exec);
13
11
  const { execSync } = require('child_process'); // 只填写UPLOAD_URL将上传节点,同时填写UPLOAD_URL和PROJECT_URL将上传订阅
14
- const UPLOAD_URL = process.env.UPLOAD_URL || ''; // 节点或订阅自动上传地址,需填写部署Merge-sub项目后的首页地址,例如:https://merge.serv00.net
12
+ const UPLOAD_URL = process.env.UPLOAD_URL || ''; // 节点或订阅自动上传地址,需填写部署Merge-sub项目后的首页地址,例如:https://merge.xxx.com
15
13
  const PROJECT_URL = process.env.PROJECT_URL || ''; // 需要上传订阅或保活时需填写项目分配的url,例如:https://google.com
16
14
  const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // false关闭自动保活,true开启,需同时填写PROJECT_URL变量
17
15
  const FILE_PATH = process.env.FILE_PATH || './tmp'; // 运行目录,sub节点文件保存目录
@@ -22,13 +20,15 @@ const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒v1填写形
22
20
  const NEZHA_PORT = process.env.NEZHA_PORT || ''; // 使用哪吒v1请留空,哪吒v0需填写
23
21
  const NEZHA_KEY = process.env.NEZHA_KEY || ''; // 哪吒v1的NZ_CLIENT_SECRET或哪吒v0的agent密钥
24
22
  const ARGO_DOMAIN = process.env.ARGO_DOMAIN || ''; // 固定隧道域名,留空即启用临时隧道
25
- const ARGO_AUTH = process.env.ARGO_AUTH || ''; // 固定隧道密钥json或token,留空即启用临时隧道,json获取地址:https://fscarmen.cloudflare.now.cc
23
+ const ARGO_AUTH = process.env.ARGO_AUTH || ''; // 固定隧道密钥json或token,留空即启用临时隧道,json获取地址:https://json.zone.id
26
24
  const ARGO_PORT = process.env.ARGO_PORT || 8001; // 固定隧道端口,使用token需在cloudflare后台设置和这里一致
27
- const CFIP = process.env.CFIP || 'www.visa.com.tw'; // 节点优选域名或优选ip
25
+ const CFIP = process.env.CFIP || 'cdns.doon.eu.org'; // 节点优选域名或优选ip
28
26
  const CFPORT = process.env.CFPORT || 443; // 节点优选域名或优选ip对应的端口
29
- const NAME = process.env.NAME || 'Vls'; // 节点名称
27
+ const NAME = process.env.NAME || ''; // 节点名称
28
+
29
+ require('dotenv').config();
30
30
 
31
- //创建运行文件夹
31
+ // 创建运行文件夹
32
32
  if (!fs.existsSync(FILE_PATH)) {
33
33
  fs.mkdirSync(FILE_PATH);
34
34
  console.log(`${FILE_PATH} is created`);
@@ -36,10 +36,25 @@ if (!fs.existsSync(FILE_PATH)) {
36
36
  console.log(`${FILE_PATH} already exists`);
37
37
  }
38
38
 
39
- let npmPath = path.join(FILE_PATH, 'npm');
40
- let phpPath = path.join(FILE_PATH, 'php');
41
- let webPath = path.join(FILE_PATH, 'web');
42
- let botPath = path.join(FILE_PATH, 'bot');
39
+ // 生成随机6位字符文件名
40
+ function generateRandomName() {
41
+ const characters = 'abcdefghijklmnopqrstuvwxyz';
42
+ let result = '';
43
+ for (let i = 0; i < 6; i++) {
44
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
45
+ }
46
+ return result;
47
+ }
48
+
49
+ // 全局常量
50
+ const npmName = generateRandomName();
51
+ const webName = generateRandomName();
52
+ const botName = generateRandomName();
53
+ const phpName = generateRandomName();
54
+ let npmPath = path.join(FILE_PATH, npmName);
55
+ let phpPath = path.join(FILE_PATH, phpName);
56
+ let webPath = path.join(FILE_PATH, webName);
57
+ let botPath = path.join(FILE_PATH, botName);
43
58
  let subPath = path.join(FILE_PATH, 'sub.txt');
44
59
  let listPath = path.join(FILE_PATH, 'list.txt');
45
60
  let bootLogPath = path.join(FILE_PATH, 'boot.log');
@@ -65,24 +80,36 @@ function deleteNodes() {
65
80
 
66
81
  if (nodes.length === 0) return;
67
82
 
68
- return axios.post(`${UPLOAD_URL}/api/delete-nodes`,
83
+ axios.post(`${UPLOAD_URL}/api/delete-nodes`,
69
84
  JSON.stringify({ nodes }),
70
85
  { headers: { 'Content-Type': 'application/json' } }
71
86
  ).catch((error) => {
72
87
  return null;
73
88
  });
89
+ return null;
74
90
  } catch (err) {
75
91
  return null;
76
92
  }
77
93
  }
78
94
 
79
- //清理历史文件
95
+ // 清理历史文件
80
96
  function cleanupOldFiles() {
81
- const pathsToDelete = ['web', 'bot', 'npm', 'php', 'sub.txt', 'boot.log'];
82
- pathsToDelete.forEach(file => {
83
- const filePath = path.join(FILE_PATH, file);
84
- fs.unlink(filePath, () => {});
85
- });
97
+ try {
98
+ const files = fs.readdirSync(FILE_PATH);
99
+ files.forEach(file => {
100
+ const filePath = path.join(FILE_PATH, file);
101
+ try {
102
+ const stat = fs.statSync(filePath);
103
+ if (stat.isFile()) {
104
+ fs.unlinkSync(filePath);
105
+ }
106
+ } catch (err) {
107
+ // 忽略所有错误,不记录日志
108
+ }
109
+ });
110
+ } catch (err) {
111
+ // 忽略所有错误,不记录日志
112
+ }
86
113
  }
87
114
 
88
115
  // 根路由
@@ -91,19 +118,21 @@ app.get("/", function(req, res) {
91
118
  });
92
119
 
93
120
  // 生成xr-ay配置文件
94
- const config = {
95
- log: { access: '/dev/null', error: '/dev/null', loglevel: 'none' },
96
- inbounds: [
97
- { 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' } },
98
- { port: 3001, listen: "127.0.0.1", protocol: "vless", settings: { clients: [{ id: UUID }], decryption: "none" }, streamSettings: { network: "tcp", security: "none" } },
99
- { 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 } },
100
- { 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 } },
101
- { 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 } },
102
- ],
103
- dns: { servers: ["https+local://8.8.8.8/dns-query"] },
104
- outbounds: [ { protocol: "freedom", tag: "direct" }, {protocol: "blackhole", tag: "block"} ]
105
- };
106
- fs.writeFileSync(path.join(FILE_PATH, 'config.json'), JSON.stringify(config, null, 2));
121
+ async function generateConfig() {
122
+ const config = {
123
+ log: { access: '/dev/null', error: '/dev/null', loglevel: 'none' },
124
+ inbounds: [
125
+ { 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' } },
126
+ { port: 3001, listen: "127.0.0.1", protocol: "vless", settings: { clients: [{ id: UUID }], decryption: "none" }, streamSettings: { network: "tcp", security: "none" } },
127
+ { 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 } },
128
+ { 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 } },
129
+ { 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 } },
130
+ ],
131
+ dns: { servers: ["https+local://8.8.8.8/dns-query"] },
132
+ outbounds: [ { protocol: "freedom", tag: "direct" }, {protocol: "blackhole", tag: "block"} ]
133
+ };
134
+ fs.writeFileSync(path.join(FILE_PATH, 'config.json'), JSON.stringify(config, null, 2));
135
+ }
107
136
 
108
137
  // 判断系统架构
109
138
  function getSystemArchitecture() {
@@ -117,7 +146,13 @@ function getSystemArchitecture() {
117
146
 
118
147
  // 下载对应系统架构的依赖文件
119
148
  function downloadFile(fileName, fileUrl, callback) {
120
- const filePath = path.join(FILE_PATH, fileName);
149
+ const filePath = fileName;
150
+
151
+ // 确保目录存在
152
+ if (!fs.existsSync(FILE_PATH)) {
153
+ fs.mkdirSync(FILE_PATH, { recursive: true });
154
+ }
155
+
121
156
  const writer = fs.createWriteStream(filePath);
122
157
 
123
158
  axios({
@@ -130,26 +165,27 @@ function downloadFile(fileName, fileUrl, callback) {
130
165
 
131
166
  writer.on('finish', () => {
132
167
  writer.close();
133
- console.log(`Download ${fileName} successfully`);
134
- callback(null, fileName);
168
+ console.log(`Download ${path.basename(filePath)} successfully`);
169
+ callback(null, filePath);
135
170
  });
136
171
 
137
172
  writer.on('error', err => {
138
173
  fs.unlink(filePath, () => { });
139
- const errorMessage = `Download ${fileName} failed: ${err.message}`;
174
+ const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`;
140
175
  console.error(errorMessage); // 下载失败时输出错误消息
141
176
  callback(errorMessage);
142
177
  });
143
178
  })
144
179
  .catch(err => {
145
- const errorMessage = `Download ${fileName} failed: ${err.message}`;
180
+ const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`;
146
181
  console.error(errorMessage); // 下载失败时输出错误消息
147
182
  callback(errorMessage);
148
183
  });
149
184
  }
150
185
 
151
186
  // 下载并运行依赖文件
152
- async function downloadFilesAndRun() {
187
+ async function downloadFilesAndRun() {
188
+
153
189
  const architecture = getSystemArchitecture();
154
190
  const filesToDownload = getFilesForArchitecture(architecture);
155
191
 
@@ -160,11 +196,11 @@ async function downloadFilesAndRun() {
160
196
 
161
197
  const downloadPromises = filesToDownload.map(fileInfo => {
162
198
  return new Promise((resolve, reject) => {
163
- downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, fileName) => {
199
+ downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, filePath) => {
164
200
  if (err) {
165
201
  reject(err);
166
202
  } else {
167
- resolve(fileName);
203
+ resolve(filePath);
168
204
  }
169
205
  });
170
206
  });
@@ -179,8 +215,7 @@ async function downloadFilesAndRun() {
179
215
  // 授权和运行
180
216
  function authorizeFiles(filePaths) {
181
217
  const newPermissions = 0o775;
182
- filePaths.forEach(relativeFilePath => {
183
- const absoluteFilePath = path.join(FILE_PATH, relativeFilePath);
218
+ filePaths.forEach(absoluteFilePath => {
184
219
  if (fs.existsSync(absoluteFilePath)) {
185
220
  fs.chmod(absoluteFilePath, newPermissions, (err) => {
186
221
  if (err) {
@@ -192,7 +227,7 @@ async function downloadFilesAndRun() {
192
227
  }
193
228
  });
194
229
  }
195
- const filesToAuthorize = NEZHA_PORT ? ['./npm', './web', './bot'] : ['./php', './web', './bot'];
230
+ const filesToAuthorize = NEZHA_PORT ? [npmPath, webPath, botPath] : [phpPath, webPath, botPath];
196
231
  authorizeFiles(filesToAuthorize);
197
232
 
198
233
  //运行ne-zha
@@ -212,12 +247,12 @@ disable_force_update: true
212
247
  disable_nat: false
213
248
  disable_send_query: false
214
249
  gpu: false
215
- insecure_tls: false
250
+ insecure_tls: true
216
251
  ip_report_period: 1800
217
- report_delay: 1
252
+ report_delay: 4
218
253
  server: ${NEZHA_SERVER}
219
- skip_connection_count: false
220
- skip_procs_count: false
254
+ skip_connection_count: true
255
+ skip_procs_count: true
221
256
  temperature: false
222
257
  tls: ${nezhatls}
223
258
  use_gitee_to_upgrade: false
@@ -226,11 +261,11 @@ uuid: ${UUID}`;
226
261
 
227
262
  fs.writeFileSync(path.join(FILE_PATH, 'config.yaml'), configYaml);
228
263
 
229
- // 运行 php
230
- const command = `nohup ${FILE_PATH}/php -c "${FILE_PATH}/config.yaml" >/dev/null 2>&1 &`;
264
+ // 运行 v1
265
+ const command = `nohup ${phpPath} -c "${FILE_PATH}/config.yaml" >/dev/null 2>&1 &`;
231
266
  try {
232
267
  await exec(command);
233
- console.log('php is running');
268
+ console.log(`${phpName} is running`);
234
269
  await new Promise((resolve) => setTimeout(resolve, 1000));
235
270
  } catch (error) {
236
271
  console.error(`php running error: ${error}`);
@@ -241,10 +276,10 @@ uuid: ${UUID}`;
241
276
  if (tlsPorts.includes(NEZHA_PORT)) {
242
277
  NEZHA_TLS = '--tls';
243
278
  }
244
- const command = `nohup ${FILE_PATH}/npm -s ${NEZHA_SERVER}:${NEZHA_PORT} -p ${NEZHA_KEY} ${NEZHA_TLS} >/dev/null 2>&1 &`;
279
+ 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 &`;
245
280
  try {
246
281
  await exec(command);
247
- console.log('npm is running');
282
+ console.log(`${npmName} is running`);
248
283
  await new Promise((resolve) => setTimeout(resolve, 1000));
249
284
  } catch (error) {
250
285
  console.error(`npm running error: ${error}`);
@@ -254,17 +289,17 @@ uuid: ${UUID}`;
254
289
  console.log('NEZHA variable is empty,skip running');
255
290
  }
256
291
  //运行xr-ay
257
- const command1 = `nohup ${FILE_PATH}/web -c ${FILE_PATH}/config.json >/dev/null 2>&1 &`;
292
+ const command1 = `nohup ${webPath} -c ${FILE_PATH}/config.json >/dev/null 2>&1 &`;
258
293
  try {
259
294
  await exec(command1);
260
- console.log('web is running');
295
+ console.log(`${webName} is running`);
261
296
  await new Promise((resolve) => setTimeout(resolve, 1000));
262
297
  } catch (error) {
263
298
  console.error(`web running error: ${error}`);
264
299
  }
265
300
 
266
301
  // 运行cloud-fared
267
- if (fs.existsSync(path.join(FILE_PATH, 'bot'))) {
302
+ if (fs.existsSync(botPath)) {
268
303
  let args;
269
304
 
270
305
  if (ARGO_AUTH.match(/^[A-Z0-9a-z=]{120,250}$/)) {
@@ -276,8 +311,8 @@ uuid: ${UUID}`;
276
311
  }
277
312
 
278
313
  try {
279
- await exec(`nohup ${FILE_PATH}/bot ${args} >/dev/null 2>&1 &`);
280
- console.log('bot is running');
314
+ await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`);
315
+ console.log(`${botName} is running`);
281
316
  await new Promise((resolve) => setTimeout(resolve, 2000));
282
317
  } catch (error) {
283
318
  console.error(`Error executing command: ${error}`);
@@ -292,13 +327,13 @@ function getFilesForArchitecture(architecture) {
292
327
  let baseFiles;
293
328
  if (architecture === 'arm') {
294
329
  baseFiles = [
295
- { fileName: "web", fileUrl: "https://arm64.ssss.nyc.mn/web" },
296
- { fileName: "bot", fileUrl: "https://arm64.ssss.nyc.mn/2go" }
330
+ { fileName: webPath, fileUrl: "https://arm64.ssss.nyc.mn/web" },
331
+ { fileName: botPath, fileUrl: "https://arm64.ssss.nyc.mn/bot" }
297
332
  ];
298
333
  } else {
299
334
  baseFiles = [
300
- { fileName: "web", fileUrl: "https://amd64.ssss.nyc.mn/web" },
301
- { fileName: "bot", fileUrl: "https://amd64.ssss.nyc.mn/2go" }
335
+ { fileName: webPath, fileUrl: "https://amd64.ssss.nyc.mn/web" },
336
+ { fileName: botPath, fileUrl: "https://amd64.ssss.nyc.mn/bot" }
302
337
  ];
303
338
  }
304
339
 
@@ -308,7 +343,7 @@ function getFilesForArchitecture(architecture) {
308
343
  ? "https://arm64.ssss.nyc.mn/agent"
309
344
  : "https://amd64.ssss.nyc.mn/agent";
310
345
  baseFiles.unshift({
311
- fileName: "npm",
346
+ fileName: npmPath,
312
347
  fileUrl: npmUrl
313
348
  });
314
349
  } else {
@@ -316,7 +351,7 @@ function getFilesForArchitecture(architecture) {
316
351
  ? "https://arm64.ssss.nyc.mn/v1"
317
352
  : "https://amd64.ssss.nyc.mn/v1";
318
353
  baseFiles.unshift({
319
- fileName: "php",
354
+ fileName: phpPath,
320
355
  fileUrl: phpUrl
321
356
  });
322
357
  }
@@ -384,7 +419,12 @@ async function extractDomains() {
384
419
  fs.unlinkSync(path.join(FILE_PATH, 'boot.log'));
385
420
  async function killBotProcess() {
386
421
  try {
387
- await exec('pkill -f "[b]ot" > /dev/null 2>&1');
422
+ // Windows系统使用taskkill命令
423
+ if (process.platform === 'win32') {
424
+ await exec(`taskkill /f /im ${botName}.exe > nul 2>&1`);
425
+ } else {
426
+ await exec(`pkill -f "[${botName.charAt(0)}]${botName.substring(1)}" > /dev/null 2>&1`);
427
+ }
388
428
  } catch (error) {
389
429
  // 忽略输出
390
430
  }
@@ -393,8 +433,8 @@ async function extractDomains() {
393
433
  await new Promise((resolve) => setTimeout(resolve, 3000));
394
434
  const args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${FILE_PATH}/boot.log --loglevel info --url http://localhost:${ARGO_PORT}`;
395
435
  try {
396
- await exec(`nohup ${path.join(FILE_PATH, 'bot')} ${args} >/dev/null 2>&1 &`);
397
- console.log('bot is running.');
436
+ await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`);
437
+ console.log(`${botName} is running`);
398
438
  await new Promise((resolve) => setTimeout(resolve, 3000));
399
439
  await extractDomains(); // 重新提取域名
400
440
  } catch (error) {
@@ -408,27 +448,53 @@ async function extractDomains() {
408
448
 
409
449
  // 生成 list 和 sub 信息
410
450
  async function generateLinks(argoDomain) {
411
- const metaInfo = execSync(
412
- 'curl -s https://speed.cloudflare.com/meta | awk -F\\" \'{print $26"-"$18}\' | sed -e \'s/ /_/g\'',
413
- { encoding: 'utf-8' }
414
- );
415
- const ISP = metaInfo.trim();
451
+ let ISP = 'Unknown';
452
+
453
+ try {
454
+ const response = await axios.get('https://speed.cloudflare.com/meta', {
455
+ timeout: 5000,
456
+ headers: {
457
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
458
+ }
459
+ });
460
+
461
+ if (response.data) {
462
+ const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;
463
+ if (data.country && data.asOrganization) {
464
+ ISP = `${data.country}-${data.asOrganization}`.replace(/\s+/g, '_');
465
+ } else {
466
+ ISP = data.country || data.asOrganization;
467
+ }
468
+ }
469
+ } catch (err) {
470
+ try {
471
+ const metaInfo = execSync(
472
+ 'curl -s https://speed.cloudflare.com/meta | awk -F\\" \'{print $26\"-\"$18}\' | sed -e \'s/ /_/g\'',
473
+ { encoding: 'utf-8', timeout: 5000 }
474
+ );
475
+ ISP = metaInfo.trim() || 'Unknown';
476
+ } catch (execErr) {
477
+ ISP = 'Unknown';
478
+ }
479
+ }
480
+ // 如果 NAME 为空,则只使用 ISP 作为名称
481
+ const nodeName = NAME ? `${NAME}-${ISP}` : ISP;
416
482
 
417
483
  return new Promise((resolve) => {
418
484
  setTimeout(() => {
419
- const VMESS = { v: '2', ps: `${NAME}-${ISP}`, add: CFIP, port: CFPORT, id: UUID, aid: '0', scy: 'none', net: 'ws', type: 'none', host: argoDomain, path: '/vmess-argo?ed=2560', tls: 'tls', sni: argoDomain, alpn: '' };
485
+ 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'};
420
486
  const subTxt = `
421
- vless://${UUID}@${CFIP}:${CFPORT}?encryption=none&security=tls&sni=${argoDomain}&type=ws&host=${argoDomain}&path=%2Fvless-argo%3Fed%3D2560#${NAME}-${ISP}
487
+ vless://${UUID}@${CFIP}:${CFPORT}?encryption=none&security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Fvless-argo%3Fed%3D2560#${nodeName}
422
488
 
423
489
  vmess://${Buffer.from(JSON.stringify(VMESS)).toString('base64')}
424
490
 
425
- trojan://${UUID}@${CFIP}:${CFPORT}?security=tls&sni=${argoDomain}&type=ws&host=${argoDomain}&path=%2Ftrojan-argo%3Fed%3D2560#${NAME}-${ISP}
491
+ trojan://${UUID}@${CFIP}:${CFPORT}?security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Ftrojan-argo%3Fed%3D2560#${nodeName}
426
492
  `;
427
493
  // 打印 sub.txt 内容到控制台
428
494
  console.log(Buffer.from(subTxt).toString('base64'));
429
495
  fs.writeFileSync(subPath, Buffer.from(subTxt).toString('base64'));
430
496
  console.log(`${FILE_PATH}/sub.txt saved successfully`);
431
- uplodNodes();
497
+ uploadNodes();
432
498
  // 将内容进行 base64 编码并写入 SUB_PATH 路由
433
499
  app.get(`/${SUB_PATH}`, (req, res) => {
434
500
  const encodedContent = Buffer.from(subTxt).toString('base64');
@@ -442,7 +508,7 @@ trojan://${UUID}@${CFIP}:${CFPORT}?security=tls&sni=${argoDomain}&type=ws&host=$
442
508
  }
443
509
 
444
510
  // 自动上传节点或订阅
445
- async function uplodNodes() {
511
+ async function uploadNodes() {
446
512
  if (UPLOAD_URL && PROJECT_URL) {
447
513
  const subscriptionUrl = `${PROJECT_URL}/${SUB_PATH}`;
448
514
  const jsonData = {
@@ -455,8 +521,9 @@ async function uplodNodes() {
455
521
  }
456
522
  });
457
523
 
458
- if (response.status === 200) {
524
+ if (response && response.status === 200) {
459
525
  console.log('Subscription uploaded successfully');
526
+ return response;
460
527
  } else {
461
528
  return null;
462
529
  // console.log('Unknown response status');
@@ -478,11 +545,12 @@ async function uplodNodes() {
478
545
  const jsonData = JSON.stringify({ nodes });
479
546
 
480
547
  try {
481
- await axios.post(`${UPLOAD_URL}/api/add-nodes`, jsonData, {
548
+ const response = await axios.post(`${UPLOAD_URL}/api/add-nodes`, jsonData, {
482
549
  headers: { 'Content-Type': 'application/json' }
483
550
  });
484
- if (response.status === 200) {
485
- console.log('Subscription uploaded successfully');
551
+ if (response && response.status === 200) {
552
+ console.log('Nodes uploaded successfully');
553
+ return response;
486
554
  } else {
487
555
  return null;
488
556
  }
@@ -498,7 +566,7 @@ async function uplodNodes() {
498
566
  // 90s后删除相关文件
499
567
  function cleanFiles() {
500
568
  setTimeout(() => {
501
- const filesToDelete = [bootLogPath, configPath, webPath, botPath, phpPath, npmPath];
569
+ const filesToDelete = [bootLogPath, configPath, webPath, botPath];
502
570
 
503
571
  if (NEZHA_PORT) {
504
572
  filesToDelete.push(npmPath);
@@ -506,11 +574,20 @@ function cleanFiles() {
506
574
  filesToDelete.push(phpPath);
507
575
  }
508
576
 
509
- exec(`rm -rf ${filesToDelete.join(' ')} >/dev/null 2>&1`, (error) => {
510
- console.clear();
511
- console.log('App is running');
512
- console.log('Thank you for using this script, enjoy!');
513
- });
577
+ // Windows系统使用不同的删除命令
578
+ if (process.platform === 'win32') {
579
+ exec(`del /f /q ${filesToDelete.join(' ')} > nul 2>&1`, (error) => {
580
+ console.clear();
581
+ console.log('App is running');
582
+ console.log('Thank you for using this script, enjoy!');
583
+ });
584
+ } else {
585
+ exec(`rm -rf ${filesToDelete.join(' ')} >/dev/null 2>&1`, (error) => {
586
+ console.clear();
587
+ console.log('App is running');
588
+ console.log('Thank you for using this script, enjoy!');
589
+ });
590
+ }
514
591
  }, 90000); // 90s
515
592
  }
516
593
  cleanFiles();
@@ -532,19 +609,28 @@ async function AddVisitTask() {
532
609
  });
533
610
  // console.log(`${JSON.stringify(response.data)}`);
534
611
  console.log(`automatic access task added successfully`);
612
+ return response;
535
613
  } catch (error) {
536
- console.error(`添加URL失败: ${error.message}`);
614
+ console.error(`Add automatic access task faild: ${error.message}`);
615
+ return null;
537
616
  }
538
617
  }
539
618
 
540
- // 回调运行
619
+ // 主运行逻辑
541
620
  async function startserver() {
542
- deleteNodes();
543
- cleanupOldFiles();
544
- await downloadFilesAndRun();
545
- await extractDomains();
546
- AddVisitTask();
621
+ try {
622
+ deleteNodes();
623
+ cleanupOldFiles();
624
+ await generateConfig();
625
+ await downloadFilesAndRun();
626
+ await extractDomains();
627
+ await AddVisitTask();
628
+ } catch (error) {
629
+ console.error('Error in startserver:', error);
630
+ }
547
631
  }
548
- startserver();
632
+ startserver().catch(error => {
633
+ console.error('Unhandled error in startserver:', error);
634
+ });
549
635
 
550
- app.listen(PORT, () => console.log(`http server is running on port:${PORT}!`));
636
+ app.listen(PORT, () => console.log(`http server is running on port:${PORT}!`));
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "nodejs-argo",
3
- "version": "2.0.1",
4
- "description": "Node.js Argo tunnel deployment tool for PaaS platforms and gaming toys",
3
+ "version": "2.0.2",
4
+ "description": "nodejs-argo",
5
5
  "main": "index.js",
6
+ "bin": {
7
+ "nodejs-argo": "./index.js"
8
+ },
6
9
  "keywords": [
10
+ "vmess",
11
+ "vless",
12
+ "trojan",
7
13
  "argo",
8
14
  "tunnel",
9
- "paas",
10
- "deployment",
11
- "nodejs",
12
- "proxy",
13
- "vless",
14
- "vmess",
15
- "trojan"
15
+ "nezha"
16
16
  ],
17
17
  "author": "eooce",
18
18
  "repository": {
@@ -25,17 +25,15 @@
25
25
  "homepage": "https://github.com/eooce/nodejs-argo#readme",
26
26
  "license": "MIT",
27
27
  "private": false,
28
- "bin": {
29
- "nodejs-argo": "./index.js"
30
- },
31
28
  "scripts": {
32
29
  "start": "node index.js",
30
+ "dev": "node index.js",
33
31
  "test": "echo \"Error: no test specified\" && exit 1"
34
32
  },
35
33
  "dependencies": {
36
- "axios": "latest",
37
- "express": "^4.18.2",
38
- "dotenv": "^16.3.1"
34
+ "axios": "^1.12.2",
35
+ "express": "^5.1.0",
36
+ "dotenv": "^17.2.3"
39
37
  },
40
38
  "engines": {
41
39
  "node": ">=14"
@@ -43,7 +41,6 @@
43
41
  "files": [
44
42
  "index.js",
45
43
  "package.json",
46
- "README.md",
47
44
  "LICENSE"
48
45
  ],
49
46
  "publishConfig": {
package/README.md DELETED
@@ -1,195 +0,0 @@
1
- # nodejs-argo隧道代理
2
-
3
- [![npm version](https://img.shields.io/npm/v/nodejs-argo.svg)](https://www.npmjs.com/package/nodejs-argo)
4
- [![npm downloads](https://img.shields.io/npm/dm/nodejs-argo.svg)](https://www.npmjs.com/package/nodejs-argo)
5
- [![License](https://img.shields.io/npm/l/nodejs-argo.svg)](https://github.com/eooce/nodejs-argo/blob/main/LICENSE)
6
-
7
- nodejs-argo是一个强大的Argo隧道部署工具,专为PaaS平台和游戏玩具平台设计。它支持多种代理协议(VLESS、VMess、Trojan等),并集成了哪吒探针功能。
8
-
9
- ## 说明 (部署前请仔细阅读完)
10
-
11
- * 本项目是针对node环境的paas平台和游戏玩具而生,采用Argo隧道部署节点,集成哪吒探针v0或v1可选。
12
- * node玩具平台只需上传index.js和package.json即可,paas平台需要docker部署的才上传Dockerfile。
13
- * 不填写ARGO_DOMAIN和ARGO_AUTH两个变量即启用临时隧道,反之则使用固定隧道。
14
- * 哪吒v0/v1可选,当哪吒端口为{443,8443,2096,2087,2083,2053}其中之一时,自动开启tls。
15
-
16
-
17
- ## 📋 环境变量说明
18
-
19
- | 变量名 | 是否必须 | 默认值 | 说明 |
20
- |--------|----------|--------|------|
21
- | UPLOAD_URL | 否 | - | 订阅上传地址 |
22
- | PROJECT_URL | 否 | https://www.google.com | 项目分配的域名 |
23
- | AUTO_ACCESS | 否 | false | 是否开启自动访问保活 |
24
- | PORT | 否 | 3000 | HTTP服务监听端口 |
25
- | ARGO_PORT | 否 | 8001 | Argo隧道端口 |
26
- | UUID | 否 | 89c13786-25aa-4520-b2e7-12cd60fb5202 | 用户UUID |
27
- | NEZHA_SERVER | 否 | - | 哪吒面板域名 |
28
- | NEZHA_PORT | 否 | - | 哪吒端口 |
29
- | NEZHA_KEY | 否 | - | 哪吒密钥 |
30
- | ARGO_DOMAIN | 否 | - | Argo固定隧道域名 |
31
- | ARGO_AUTH | 否 | - | Argo固定隧道密钥 |
32
- | CFIP | 否 | www.visa.com.tw | 节点优选域名或IP |
33
- | CFPORT | 否 | 443 | 节点端口 |
34
- | NAME | 否 | Vls | 节点名称前缀 |
35
- | FILE_PATH | 否 | ./tmp | 运行目录 |
36
- | SUB_PATH | 否 | sub | 订阅路径 |
37
-
38
- ## 🌐 订阅地址
39
-
40
- - 标准端口:`https://your-domain.com/sub`
41
- - 非标端口:`http://your-domain.com:port/sub`
42
-
43
- ---
44
-
45
- ## 🚀 进阶使用
46
-
47
- ### 安装
48
-
49
- ```bash
50
- # 全局安装(推荐)
51
- npm install -g nodejs-argo
52
-
53
- # 或者使用yarn
54
- yarn global add nodejs-argo
55
-
56
- # 或者使用pnpm
57
- pnpm add -g nodejs-argo
58
- ```
59
-
60
- ### 基本使用
61
-
62
- ```bash
63
- # 直接运行(使用默认配置)
64
- nodejs-argo
65
-
66
- # 使用npx运行
67
- npx nodejs-argo
68
-
69
- # 设置环境变量运行
70
- PORT=3000 npx nodejs-argo
71
- ```
72
-
73
- ### 环境变量配置
74
-
75
- 可使用 `.env` 文件来配置环境变量运行
76
-
77
-
78
- 或者直接在命令行中设置:
79
-
80
- ```bash
81
- export UPLOAD_URL="https://your-merge-sub-domain.com"
82
- export PROJECT_URL="https://your-project-domain.com"
83
- export PORT=3000
84
- export UUID="your-uuid-here"
85
- export NEZHA_SERVER="nz.your-domain.com:8008"
86
- export NEZHA_KEY="your-nezha-key"
87
- ```
88
-
89
- ## 📦 作为npm模块使用
90
-
91
- ```javascript
92
- // CommonJS
93
- const nodejsArgo = require('nodejs-argo');
94
-
95
- // ES6 Modules
96
- import nodejsArgo from 'nodejs-argo';
97
-
98
- // 启动服务
99
- nodejsArgo.start();
100
- ```
101
-
102
- ## 🔧 后台运行
103
-
104
- ### 使用screen(推荐)
105
- ```bash
106
- # 创建screen会话
107
- screen -S argo
108
-
109
- # 运行应用
110
- nodejs-argo
111
-
112
- # 按 Ctrl+A 然后按 D 分离会话
113
- # 重新连接:screen -r argo
114
- ```
115
-
116
- ### 使用tmux
117
- ```bash
118
- # 创建tmux会话
119
- tmux new-session -d -s argo
120
-
121
- # 运行应用
122
- tmux send-keys -t argo "nodejs-argo" Enter
123
-
124
- # 分离会话:tmux detach -s argo
125
- # 重新连接:tmux attach -t argo
126
- ```
127
-
128
- ### 使用PM2
129
- ```bash
130
- # 安装PM2
131
- npm install -g pm2
132
-
133
- # 启动应用
134
- pm2 start nodejs-argo --name "argo-service"
135
-
136
- # 管理应用
137
- pm2 status
138
- pm2 logs argo-service
139
- pm2 restart argo-service
140
- ```
141
-
142
- ### 使用systemd(Linux系统服务)
143
- ```bash
144
- # 创建服务文件
145
- sudo nano /etc/systemd/system/nodejs-argo.service
146
-
147
- ```
148
- [Unit]
149
- Description=Node.js Argo Service
150
- After=network.target
151
-
152
- [Service]
153
- Type=simple
154
- User=root
155
- WorkingDirectory=/root/test
156
- Environment=ARGO_PORT=8080
157
- Environment=PORT=3000
158
- ExecStart=/usr/bin/npx nodejs-argo
159
- Restart=always
160
- RestartSec=10
161
-
162
- [Install]
163
- WantedBy=multi-user.target
164
- ```
165
-
166
- # 启动服务
167
- sudo systemctl start nodejs-argo
168
- sudo systemctl enable nodejs-argo
169
- ```
170
-
171
- ## 🔄 更新
172
-
173
- ```bash
174
- # 更新全局安装的包
175
- npm update -g nodejs-argo
176
-
177
- # 或者重新安装
178
- npm uninstall -g nodejs-argo
179
- npm install -g nodejs-argo
180
- ```
181
-
182
- ## 📚 更多信息
183
-
184
- - [GitHub仓库](https://github.com/eooce/nodejs-argo)
185
- - [npm包页面](https://www.npmjs.com/package/nodejs-argo)
186
- - [问题反馈](https://github.com/eooce/nodejs-argo/issues)
187
-
188
- ---
189
-
190
- ## 赞助
191
- * 感谢[ZMTO](https://zmto.com/?affid=1548)提供赞助优质双isp vps。
192
-
193
- # 免责声明
194
- * 本程序仅供学习了解, 非盈利目的,请于下载后 24 小时内删除, 不得用作任何商业用途, 文字、数据及图片均有所属版权, 如转载须注明来源。
195
- * 使用本程序必循遵守部署免责声明,使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责。