@eooce/idx 1.0.5 → 1.0.7

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 (2) hide show
  1. package/index.js +175 -59
  2. package/package.json +7 -7
package/index.js CHANGED
@@ -6,6 +6,7 @@ const axios = require("axios");
6
6
  const os = require('os');
7
7
  const fs = require("fs");
8
8
  const path = require("path");
9
+ require('dotenv').config();
9
10
  const { promisify } = require('util');
10
11
  const exec = promisify(require('child_process').exec);
11
12
  const { execSync } = require('child_process');
@@ -15,24 +16,26 @@ const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // false关闭自动保活
15
16
  const YT_WARPOUT = process.env.YT_WARPOUT || false; // 设置为true时强制使用warp出站访问youtube,false时自动检测是否设置warp出站
16
17
  const FILE_PATH = process.env.FILE_PATH || '.npm'; // sub.txt订阅文件路径
17
18
  const SUB_PATH = process.env.SUB_PATH || 'sub'; // 订阅sub路径,默认为sub,例如:https://google.com/sub
18
- const UUID = process.env.UUID || '0a6568ff-ea3c-4271-9020-450560e10d63'; // 在不同的平台运行了v1哪吒请修改UUID,否则会覆盖
19
+ const UUID = process.env.UUID || '96c78932-b21d-42f9-906c-6de0d74e0e4b'; // 在不同的平台运行了v1哪吒请修改UUID,否则会覆盖
19
20
  const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒面板地址,v1形式:nz.serv00.net:8008 v0形式:nz.serv00.net
20
21
  const NEZHA_PORT = process.env.NEZHA_PORT || ''; // v1哪吒请留空,v0 agent端口,当端口为{443,8443,2087,2083,2053,2096}时,自动开启tls
21
22
  const NEZHA_KEY = process.env.NEZHA_KEY || ''; // v1的NZ_CLIENT_SECRET或v0 agwnt密钥
22
23
  const ARGO_DOMAIN = process.env.ARGO_DOMAIN || ''; // argo固定隧道域名,留空即使用临时隧道
23
24
  const ARGO_AUTH = process.env.ARGO_AUTH || ''; // argo固定隧道token或json,留空即使用临时隧道
24
25
  const ARGO_PORT = process.env.ARGO_PORT || 8001; // argo固定隧道端口,使用token需在cloudflare控制台设置和这里一致,否则节点不通
26
+ const S5_PORT = process.env.S5_PORT || ''; // socks5端口,支持多端口的可以填写,否则留空
25
27
  const TUIC_PORT = process.env.TUIC_PORT || ''; // tuic端口,支持多端口的可以填写,否则留空
26
28
  const HY2_PORT = process.env.HY2_PORT || ''; // hy2端口,支持多端口的可以填写,否则留空
29
+ const ANYTLS_PORT = process.env.ANYTLS_PORT || ''; // AnyTLS端口,支持多端口的可以填写,否则留空
27
30
  const REALITY_PORT = process.env.REALITY_PORT || ''; // reality端口,支持多端口的可以填写,否则留空
28
- const CFIP = process.env.CFIP || 'cdns.doon.eu.org'; // 优选域名或优选IP
31
+ const ANYREALITY_PORT = process.env.ANYREALITY_PORT || ''; // Anyr-eality端口,支持多端口的可以填写,否则留空
32
+ const CFIP = process.env.CFIP || 'saas.sin.fan'; // 优选域名或优选IP
29
33
  const CFPORT = process.env.CFPORT || 443; // 优选域名或优选IP对应端口
30
34
  const PORT = process.env.PORT || 3000; // http订阅端口
31
35
  const NAME = process.env.NAME || ''; // 节点名称
32
36
  const CHAT_ID = process.env.CHAT_ID || ''; // Telegram chat_id 两个变量不全不推送节点到TG
33
37
  const BOT_TOKEN = process.env.BOT_TOKEN || ''; // Telegram bot_token 两个变量不全不推送节点到TG
34
-
35
- require('dotenv').config();
38
+ const DISABLE_ARGO = process.env.DISABLE_ARGO || false; // 设置为 true 时禁用argo,false开启
36
39
 
37
40
  //创建运行文件夹
38
41
  if (!fs.existsSync(FILE_PATH)) {
@@ -130,6 +133,11 @@ function cleanupOldFiles() {
130
133
 
131
134
  // 获取固定隧道json
132
135
  function argoType() {
136
+ if (DISABLE_ARGO === 'true' || DISABLE_ARGO === true) {
137
+ console.log("DISABLE_ARGO is set to true, disable argo tunnel");
138
+ return;
139
+ }
140
+
133
141
  if (!ARGO_AUTH || !ARGO_DOMAIN) {
134
142
  console.log("ARGO_DOMAIN or ARGO_AUTH variable is empty, use quick tunnels");
135
143
  return;
@@ -436,25 +444,25 @@ eQ6OFb9LbLYL9f+sAiAffoMbi4y/0YUSlTtz7as9S8/lciBF5VCUoVIKS+vX2g==
436
444
  }
437
445
  ],
438
446
  "endpoints": [
439
- {
440
- "type": "wireguard",
441
- "tag": "wireguard-out",
442
- "mtu": 1280,
443
- "address": [
444
- "172.16.0.2/32",
445
- "2606:4700:110:8dfe:d141:69bb:6b80:925/128"
446
- ],
447
- "private_key": "YFYOAdbw1bKTHlNNi+aEjBM3BO7unuFC5rOkMRAz9XY=",
448
- "peers": [
449
- {
450
- "address": "engage.cloudflareclient.com",
451
- "port": 2408,
452
- "public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
453
- "allowed_ips": ["0.0.0.0/0", "::/0"],
454
- "reserved": [78, 135, 76]
455
- }
456
- ]
457
- }
447
+ {
448
+ "type": "wireguard",
449
+ "tag": "wireguard-out",
450
+ "mtu": 1280,
451
+ "address": [
452
+ "172.16.0.2/32",
453
+ "2606:4700:110:8dfe:d141:69bb:6b80:925/128"
454
+ ],
455
+ "private_key": "YFYOAdbw1bKTHlNNi+aEjBM3BO7unuFC5rOkMRAz9XY=",
456
+ "peers": [
457
+ {
458
+ "address": "engage.cloudflareclient.com",
459
+ "port": 2408,
460
+ "public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
461
+ "allowed_ips": ["0.0.0.0/0", "::/0"],
462
+ "reserved": [78, 135, 76]
463
+ }
464
+ ]
465
+ }
458
466
  ],
459
467
  "outbounds": [
460
468
  {
@@ -574,6 +582,82 @@ eQ6OFb9LbLYL9f+sAiAffoMbi4y/0YUSlTtz7as9S8/lciBF5VCUoVIKS+vX2g==
574
582
  // 忽略错误,继续运行
575
583
  }
576
584
 
585
+ // S5配置
586
+ try {
587
+ if (isValidPort(S5_PORT)) {
588
+ config.inbounds.push({
589
+ "tag": "s5-in",
590
+ "type": "socks",
591
+ "listen": "::",
592
+ "listen_port": parseInt(S5_PORT),
593
+ "users": [
594
+ {
595
+ "username": UUID.substring(0, 8),
596
+ "password": UUID.slice(-12)
597
+ }
598
+ ]
599
+ });
600
+ }
601
+ } catch (error) {
602
+ // 忽略错误,继续运行
603
+ }
604
+
605
+ // AnyTLS配置
606
+ try {
607
+ if (isValidPort(ANYTLS_PORT)) {
608
+ config.inbounds.push({
609
+ "tag": "anytls-in",
610
+ "type": "anytls",
611
+ "listen": "::",
612
+ "listen_port": parseInt(ANYTLS_PORT),
613
+ "users": [
614
+ {
615
+ "password": UUID
616
+ }
617
+ ],
618
+ "tls": {
619
+ "enabled": true,
620
+ "certificate_path": path.join(FILE_PATH, "cert.pem"),
621
+ "key_path": path.join(FILE_PATH, "private.key")
622
+ }
623
+ });
624
+ }
625
+ } catch (error) {
626
+ // 忽略错误,继续运行
627
+ }
628
+
629
+ // AnyReality配置
630
+ try {
631
+ if (isValidPort(ANYREALITY_PORT)) {
632
+ config.inbounds.push({
633
+ "tag": "anyreality-in",
634
+ "type": "anytls",
635
+ "listen": "::",
636
+ "listen_port": parseInt(ANYREALITY_PORT),
637
+ "users": [
638
+ {
639
+ "password": UUID
640
+ }
641
+ ],
642
+ "tls": {
643
+ "enabled": true,
644
+ "server_name": "www.iij.ad.jp",
645
+ "reality": {
646
+ "enabled": true,
647
+ "handshake": {
648
+ "server": "www.iij.ad.jp",
649
+ "server_port": 443
650
+ },
651
+ "private_key": privateKey,
652
+ "short_id": [""]
653
+ }
654
+ }
655
+ });
656
+ }
657
+ } catch (error) {
658
+ // 忽略错误,继续运行
659
+ }
660
+
577
661
  // 检测YouTube可访问性并智能配置出站规则
578
662
  try {
579
663
  // console.log(`YT_WARPOUT environment variable is set to: ${YT_WARPOUT}`);
@@ -703,29 +787,29 @@ eQ6OFb9LbLYL9f+sAiAffoMbi4y/0YUSlTtz7as9S8/lciBF5VCUoVIKS+vX2g==
703
787
  }
704
788
 
705
789
  // 运行cloud-fared
706
- // 修改检查和执行命令以使用随机文件名
707
- if (fs.existsSync(path.join(FILE_PATH, botRandomName))) {
708
- let args;
709
-
710
- if (ARGO_AUTH.match(/^[A-Z0-9a-z=]{120,250}$/)) {
711
- args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}`;
712
- } else if (ARGO_AUTH.match(/TunnelSecret/)) {
713
- args = `tunnel --edge-ip-version auto --config ${path.join(FILE_PATH, 'tunnel.yml')} run`;
714
- } else {
715
- args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${path.join(FILE_PATH, 'boot.log')} --loglevel info --url http://localhost:${ARGO_PORT}`;
716
- }
790
+ if (DISABLE_ARGO !== 'true' && DISABLE_ARGO !== true) {
791
+ if (fs.existsSync(path.join(FILE_PATH, botRandomName))) {
792
+ let args;
793
+
794
+ if (ARGO_AUTH.match(/^[A-Z0-9a-z=]{120,250}$/)) {
795
+ args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}`;
796
+ } else if (ARGO_AUTH.match(/TunnelSecret/)) {
797
+ args = `tunnel --edge-ip-version auto --config ${path.join(FILE_PATH, 'tunnel.yml')} run`;
798
+ } else {
799
+ args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${path.join(FILE_PATH, 'boot.log')} --loglevel info --url http://localhost:${ARGO_PORT}`;
800
+ }
717
801
 
718
- try {
719
- await execPromise(`nohup ${path.join(FILE_PATH, botRandomName)} ${args} >/dev/null 2>&1 &`);
720
- console.log('bot is running');
721
- await new Promise((resolve) => setTimeout(resolve, 2000));
722
- } catch (error) {
723
- console.error(`Error executing command: ${error}`);
802
+ try {
803
+ await execPromise(`nohup ${path.join(FILE_PATH, botRandomName)} ${args} >/dev/null 2>&1 &`);
804
+ console.log('bot is running');
805
+ await new Promise((resolve) => setTimeout(resolve, 2000));
806
+ } catch (error) {
807
+ console.error(`Error executing command: ${error}`);
808
+ }
724
809
  }
725
810
  }
811
+ // 无论是否禁用 Argo,都需要生成节点信息
726
812
  await new Promise((resolve) => setTimeout(resolve, 5000));
727
-
728
- // 提取域名并生成sub.txt文件
729
813
  await extractDomains();
730
814
  });
731
815
  };
@@ -784,6 +868,11 @@ function getFilesForArchitecture(architecture) {
784
868
 
785
869
  // 获取临时隧道domain
786
870
  async function extractDomains() {
871
+ if (DISABLE_ARGO === 'true' || DISABLE_ARGO === true) {
872
+ await generateLinks(null);
873
+ return;
874
+ }
875
+
787
876
  let argoDomain;
788
877
 
789
878
  if (ARGO_AUTH && ARGO_DOMAIN) {
@@ -842,14 +931,14 @@ async function getMetaInfo() {
842
931
  try {
843
932
  const response1 = await axios.get('https://api.ip.sb/geoip', { headers: { 'User-Agent': 'Mozilla/5.0', timeout: 3000 }});
844
933
  if (response1.data && response1.data.country_code && response1.data.isp) {
845
- return `${response1.data.country_code}_${response1.data.isp}`;
934
+ return `${response1.data.country_code}-${response1.data.isp}`.replace(/\s+/g, '_');
846
935
  }
847
936
  } catch (error) {
848
937
  try {
849
938
  // 备用 ip-api.com 获取isp
850
939
  const response2 = await axios.get('http://ip-api.com/json', { headers: { 'User-Agent': 'Mozilla/5.0', timeout: 3000 }});
851
940
  if (response2.data && response2.data.status === 'success' && response2.data.countryCode && response2.data.org) {
852
- return `${response2.data.countryCode}_${response2.data.org}`;
941
+ return `${response2.data.countryCode}-${response2.data.org}`.replace(/\s+/g, '_');
853
942
  }
854
943
  } catch (error) {
855
944
  // console.error('Backup API also failed');
@@ -862,25 +951,36 @@ async function getMetaInfo() {
862
951
  async function generateLinks(argoDomain) {
863
952
  let SERVER_IP = '';
864
953
  try {
865
- const response = await axios.get('https://ipv4.ip.sb', { timeout: 3000 });
866
- SERVER_IP = response.data.trim();
954
+ const ipv4Response = await axios.get('http://ipv4.ip.sb', { timeout: 3000 });
955
+ SERVER_IP = ipv4Response.data.trim();
867
956
  } catch (err) {
868
957
  try {
869
- const response = await axios.get('https://ipv6.ip.sb', { timeout: 2000 });
870
- SERVER_IP = `[${response.data.trim()}]`;
871
- } catch (ipv6Err) {
872
958
  SERVER_IP = execSync('curl -sm 3 ipv4.ip.sb').toString().trim();
959
+ } catch (curlErr) {
960
+ try {
961
+ const ipv6Response = await axios.get('http://ipv6.ip.sb', { timeout: 3000 });
962
+ SERVER_IP = `[${ipv6Response.data.trim()}]`;
963
+ } catch (ipv6AxiosErr) {
964
+ try {
965
+ SERVER_IP = `[${execSync('curl -sm 3 ipv6.ip.sb').toString().trim()}]`;
966
+ } catch (ipv6CurlErr) {
967
+ console.error('Failed to get IP address:', ipv6CurlErr.message);
968
+ }
969
+ }
873
970
  }
874
971
  }
875
972
 
876
973
  const ISP = await getMetaInfo();
877
974
  const nodeName = NAME ? `${NAME}-${ISP}` : ISP;
878
-
879
975
  return new Promise((resolve) => {
880
976
  setTimeout(() => {
881
- const vmessNode = `vmess://${Buffer.from(JSON.stringify({ 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'})).toString('base64')}`;
977
+ let subTxt = '';
882
978
 
883
- let subTxt = vmessNode; // 始终生成vmess节点
979
+ // 只有当 DISABLE_ARGO 不为 'true' 且 argoDomain 存在时才生成默认的 vmess 节点
980
+ if ((DISABLE_ARGO !== 'true' && DISABLE_ARGO !== true) && argoDomain) {
981
+ const vmessNode = `vmess://${Buffer.from(JSON.stringify({ 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'})).toString('base64')}`;
982
+ subTxt = vmessNode;
983
+ }
884
984
 
885
985
  // TUIC_PORT是有效端口号时生成tuic节点
886
986
  if (isValidPort(TUIC_PORT)) {
@@ -900,8 +1000,28 @@ async function generateLinks(argoDomain) {
900
1000
  subTxt += vlessNode;
901
1001
  }
902
1002
 
1003
+ // ANYTLS_PORT是有效端口号时生成anytls节点
1004
+ if (isValidPort(ANYTLS_PORT)) {
1005
+ const anytlsNode = `\nanytls://${UUID}@${SERVER_IP}:${ANYTLS_PORT}?security=tls&sni=${SERVER_IP}&fp=chrome&insecure=1&allowInsecure=1#${nodeName}`;
1006
+ subTxt += anytlsNode;
1007
+ }
1008
+
1009
+ // ANYREALITY_PORT是有效端口号时生成anyreality节点
1010
+ if (isValidPort(ANYREALITY_PORT)) {
1011
+ const anyrealityNode = `\nanytls://${UUID}@${SERVER_IP}:${ANYREALITY_PORT}?security=reality&sni=www.iij.ad.jp&fp=chrome&pbk=${publicKey}&type=tcp&headerType=none#${nodeName}`;
1012
+ subTxt += anyrealityNode;
1013
+ }
1014
+
1015
+ // S5_PORT是有效端口号时生成socks5节点
1016
+ if (isValidPort(S5_PORT)) {
1017
+ const S5_AUTH = Buffer.from(`${UUID.substring(0, 8)}:${UUID.slice(-12)}`).toString('base64');
1018
+ const s5Node = `\nsocks://${S5_AUTH}@${SERVER_IP}:${S5_PORT}#${nodeName}`;
1019
+ subTxt += s5Node;
1020
+ }
1021
+
903
1022
  // 打印 sub.txt 内容到控制台
904
- console.log(Buffer.from(subTxt).toString('base64'));
1023
+ console.log('\x1b[32m' + Buffer.from(subTxt).toString('base64') + '\x1b[0m'); // 输出绿色
1024
+ console.log('\x1b[35m' + 'Logs will be deleted in 90 seconds,you can copy the above nodes' + '\x1b[0m'); // 洋红色
905
1025
  fs.writeFileSync(subPath, Buffer.from(subTxt).toString('base64'));
906
1026
  fs.writeFileSync(listPath, subTxt, 'utf8');
907
1027
  console.log(`${FILE_PATH}/sub.txt saved successfully`);
@@ -928,14 +1048,10 @@ function cleanFiles() {
928
1048
  } else if (NEZHA_SERVER && NEZHA_KEY) {
929
1049
  filesToDelete.push(phpPath);
930
1050
  }
931
-
932
- // 修改为使用随机文件名删除文件
933
1051
  const filePathsToDelete = filesToDelete.map(file => {
934
- // 对于已经使用随机路径的变量,直接使用
935
1052
  if ([webPath, botPath, phpPath, npmPath].includes(file)) {
936
1053
  return file;
937
1054
  }
938
- // 对于其他文件,使用原始路径
939
1055
  return path.join(FILE_PATH, path.basename(file));
940
1056
  });
941
1057
 
@@ -1048,7 +1164,7 @@ async function startserver() {
1048
1164
  cleanupOldFiles();
1049
1165
  argoType();
1050
1166
  await downloadFilesAndRun();
1051
- AddVisitTask();
1167
+ await AddVisitTask();
1052
1168
  cleanFiles();
1053
1169
  }
1054
1170
  startserver();
@@ -1060,7 +1176,7 @@ app.get("/", async function(req, res) {
1060
1176
  const data = await fs.promises.readFile(filePath, 'utf8');
1061
1177
  res.send(data);
1062
1178
  } catch (err) {
1063
- res.send("Hello world!<br><br>You can visit /{SUB_PATH}(Default: /sub) get your nodes!");
1179
+ res.send("Hello world!<br><br>You can access /{SUB_PATH}(Default: /sub) get your nodes!");
1064
1180
  }
1065
1181
  });
1066
1182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eooce/idx",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "@eooce/idx",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -9,11 +9,11 @@
9
9
  "keywords": [
10
10
  "vmess",
11
11
  "vless",
12
- "hysteria2",
13
12
  "tuic",
14
- "reality",
15
13
  "argo",
16
- "tunnel"
14
+ "tunnel",
15
+ "reality",
16
+ "hysteria2"
17
17
  ],
18
18
  "author": "eooce",
19
19
  "license": "MIT",
@@ -24,9 +24,9 @@
24
24
  "test": "echo \"Error: no test specified\" && exit 1"
25
25
  },
26
26
  "dependencies": {
27
- "axios": "^1.12.2",
28
- "express": "^4.18.2",
29
- "dotenv": "^17.2.3"
27
+ "axios": "^1.13.2",
28
+ "dotenv": "^17.2.3",
29
+ "express": "^5.2.1"
30
30
  },
31
31
  "engines": {
32
32
  "node": ">=14"