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.
- package/LICENSE +1 -1
- package/index.js +180 -94
- package/package.json +13 -16
- package/README.md +0 -195
package/LICENSE
CHANGED
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.
|
|
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://
|
|
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 || '
|
|
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 || '
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
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 =
|
|
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 ${
|
|
134
|
-
callback(null,
|
|
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 ${
|
|
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 ${
|
|
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,
|
|
199
|
+
downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, filePath) => {
|
|
164
200
|
if (err) {
|
|
165
201
|
reject(err);
|
|
166
202
|
} else {
|
|
167
|
-
resolve(
|
|
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(
|
|
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 ? [
|
|
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:
|
|
250
|
+
insecure_tls: true
|
|
216
251
|
ip_report_period: 1800
|
|
217
|
-
report_delay:
|
|
252
|
+
report_delay: 4
|
|
218
253
|
server: ${NEZHA_SERVER}
|
|
219
|
-
skip_connection_count:
|
|
220
|
-
skip_procs_count:
|
|
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
|
-
// 运行
|
|
230
|
-
const command = `nohup ${
|
|
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(
|
|
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 ${
|
|
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(
|
|
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 ${
|
|
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(
|
|
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(
|
|
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 ${
|
|
280
|
-
console.log(
|
|
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:
|
|
296
|
-
{ fileName:
|
|
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:
|
|
301
|
-
{ fileName:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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 ${
|
|
397
|
-
console.log(
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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: `${
|
|
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#${
|
|
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#${
|
|
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
|
-
|
|
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
|
|
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('
|
|
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
|
|
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
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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(
|
|
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
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
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": "
|
|
37
|
-
"express": "^
|
|
38
|
-
"dotenv": "^
|
|
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
|
-
[](https://www.npmjs.com/package/nodejs-argo)
|
|
4
|
-
[](https://www.npmjs.com/package/nodejs-argo)
|
|
5
|
-
[](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
|
-
* 使用本程序必循遵守部署免责声明,使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责。
|