Repository: eooce/nodejs-argo Branch: main Commit: cd9b3a0a8d04 Files: 5 Total size: 26.4 KB Directory structure: gitextract_nrkbglbp/ ├── .github/ │ └── workflows/ │ └── build-docker-image.yml ├── Dockerfile ├── README.md ├── index.js └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build-docker-image.yml ================================================ name: Build and Push Docker Image on: push: branches: [ main ] paths: - 'Dockerfile' - 'index.js' - 'package.json' pull_request: branches: [ main ] workflow_dispatch: jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: | ghcr.io/${{ github.repository_owner }}/nodejs:latest labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.description=HTTP Server org.opencontainers.image.licenses=MIT cache-from: type=gha cache-to: type=gha,mode=max ================================================ FILE: Dockerfile ================================================ FROM node:alpine3.20 WORKDIR /tmp COPY . . EXPOSE 3000/tcp RUN apk update && apk upgrade &&\ apk add --no-cache openssl curl gcompat iproute2 coreutils &&\ apk add --no-cache bash &&\ chmod +x index.js &&\ npm install CMD ["node", "index.js"] ================================================ FILE: README.md ================================================

nodejs-argo隧道代理

nodejs-argo是一个强大的Argo隧道部署工具,专为PaaS平台和游戏玩具平台设计。它支持多种代理协议(VLESS、VMess、Trojan等),并集成了哪吒探针功能。 --- Telegram交流反馈群组:https://t.me/eooceu
## 郑重声明 * 本项目自2025年10月29日15时45分起,已更改开源协议,并包含以下特定要求 * 此项目仅限个人使用,禁止用于商业行为(包括但不限于:youtube,bilibili,tiktok,facebook..等等) * 禁止新建项目将代码复制到自己仓库中用做商业行为 * 请遵守当地法律法规,禁止滥用做公共代理行为 * 如有违反以上条款者将追究法律责任 ## 说明 (部署前请仔细阅读) * 本项目是针对node环境的paas平台和游戏玩具而生,采用Argo隧道部署节点,集成哪吒探针v0或v1可选。 * node玩具平台只需上传index.js和package.json即可,paas平台需要docker部署的才上传Dockerfile。 * 不填写ARGO_DOMAIN和ARGO_AUTH两个变量即启用临时隧道,反之则使用固定隧道。 * 哪吒v0/v1可选,当哪吒端口为{443,8443,2096,2087,2083,2053}其中之一时,自动开启tls。 ## 📋 环境变量 | 变量名 | 是否必须 | 默认值 | 说明 | |--------|----------|--------|------| | UPLOAD_URL | 否 | - | 订阅上传地址 | | PROJECT_URL | 否 | https://www.google.com | 项目分配的域名 | | AUTO_ACCESS | 否 | false | 是否开启自动访问保活 | | PORT | 否 | 3000 | HTTP服务监听端口 | | ARGO_PORT | 否 | 8001 | Argo隧道端口 | | UUID | 否 | 89c13786-25aa-4520-b2e7-12cd60fb5202 | 用户UUID | | NEZHA_SERVER | 否 | - | 哪吒面板域名 | | NEZHA_PORT | 否 | - | 哪吒端口 | | NEZHA_KEY | 否 | - | 哪吒密钥 | | ARGO_DOMAIN | 否 | - | Argo固定隧道域名 | | ARGO_AUTH | 否 | - | Argo固定隧道密钥 | | CFIP | 否 | www.visa.com.tw | 节点优选域名或IP | | CFPORT | 否 | 443 | 节点端口 | | NAME | 否 | Vls | 节点名称前缀 | | FILE_PATH | 否 | ./tmp | 运行目录 | | SUB_PATH | 否 | sub | 订阅路径 | ## 🌐 订阅地址 - 标准端口:`https://your-domain.com/sub` - 非标端口:`http://your-domain.com:port/sub` --- ## 🚀 进阶使用 ### 安装 ```bash # 全局安装(推荐) npm install -g nodejs-argo # 或者使用yarn yarn global add nodejs-argo # 或者使用pnpm pnpm add -g nodejs-argo ``` ### 基本使用 ```bash # 直接运行(使用默认配置) nodejs-argo # 使用npx运行 npx nodejs-argo # 设置环境变量运行 PORT=3000 npx nodejs-argo ``` ### 环境变量配置 可使用 `.env` 文件来配置环境变量运行 或者直接在命令行中设置: ```bash export UPLOAD_URL="https://your-merge-sub-domain.com" export PROJECT_URL="https://your-project-domain.com" export PORT=3000 export UUID="your-uuid-here" export NEZHA_SERVER="nz.your-domain.com:8008" export NEZHA_KEY="your-nezha-key" ``` ## 📦 作为npm模块使用 ```javascript // CommonJS const nodejsArgo = require('nodejs-argo'); // ES6 Modules import nodejsArgo from 'nodejs-argo'; // 启动服务 nodejsArgo.start(); ``` ## 🔧 后台运行 ### 使用screen(推荐) ```bash # 创建screen会话 screen -S argo # 运行应用 nodejs-argo # 按 Ctrl+A 然后按 D 分离会话 # 重新连接:screen -r argo ``` ### 使用tmux ```bash # 创建tmux会话 tmux new-session -d -s argo # 运行应用 tmux send-keys -t argo "nodejs-argo" Enter # 分离会话:tmux detach -s argo # 重新连接:tmux attach -t argo ``` ### 使用PM2 ```bash # 安装PM2 npm install -g pm2 # 启动应用 pm2 start nodejs-argo --name "argo-service" # 管理应用 pm2 status pm2 logs argo-service pm2 restart argo-service ``` ### 使用systemd(Linux系统服务) ```bash # 创建服务文件 sudo nano /etc/systemd/system/nodejs-argo.service ``` [Unit] Description=Node.js Argo Service After=network.target [Service] Type=simple User=root WorkingDirectory=/root/test Environment=ARGO_PORT=8080 Environment=PORT=3000 ExecStart=/usr/bin/npx nodejs-argo Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` # 启动服务 sudo systemctl start nodejs-argo sudo systemctl enable nodejs-argo ``` ## 🔄 更新 ```bash # 更新全局安装的包 npm update -g nodejs-argo # 或者重新安装 npm uninstall -g nodejs-argo npm install -g nodejs-argo ``` ## 📚 更多信息 - [GitHub仓库](https://github.com/eooce/nodejs-argo) - [npm包页面](https://www.npmjs.com/package/nodejs-argo) - [问题反馈](https://github.com/eooce/nodejs-argo/issues) --- ## 赞助 * 感谢[VPS.Town](https://vps.town)提供赞助 https://vps.town * 感谢[ZMTO](https://zmto.com/?affid=1548)提供赞助优质双isp vps。 ================================================ FILE: index.js ================================================ const express = require("express"); const app = express(); const axios = require("axios"); const os = require('os'); const fs = require("fs"); const path = require("path"); const { promisify } = require('util'); const exec = promisify(require('child_process').exec); const UPLOAD_URL = process.env.UPLOAD_URL || ''; // 节点或订阅自动上传地址,需填写部署Merge-sub项目后的首页地址,例如:https://merge.xxx.com const PROJECT_URL = process.env.PROJECT_URL || ''; // 需要上传订阅或保活时需填写项目分配的url,例如:https://google.com const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // false关闭自动保活,true开启,需同时填写PROJECT_URL变量 const FILE_PATH = process.env.FILE_PATH || '.tmp'; // 运行目录,sub节点文件保存目录 const SUB_PATH = process.env.SUB_PATH || 'sub'; // 订阅路径 const PORT = process.env.SERVER_PORT || process.env.PORT || 3000; // http服务订阅端口 const UUID = process.env.UUID || '9afd1229-b893-40c1-84dd-51e7ce204913'; // 使用哪吒v1,在不同的平台运行需修改UUID,否则会覆盖 const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒v1填写形式: nz.abc.com:8008 哪吒v0填写形式:nz.abc.com const NEZHA_PORT = process.env.NEZHA_PORT || ''; // 使用哪吒v1请留空,哪吒v0需填写 const NEZHA_KEY = process.env.NEZHA_KEY || ''; // 哪吒v1的NZ_CLIENT_SECRET或哪吒v0的agent密钥 const ARGO_DOMAIN = process.env.ARGO_DOMAIN || ''; // 固定隧道域名,留空即启用临时隧道 const ARGO_AUTH = process.env.ARGO_AUTH || ''; // 固定隧道密钥json或token,留空即启用临时隧道,json获取地址:https://json.zone.id const ARGO_PORT = process.env.ARGO_PORT || 8001; // 固定隧道端口,使用token需在cloudflare后台设置和这里一致 const CFIP = process.env.CFIP || 'saas.sin.fan'; // 节点优选域名或优选ip const CFPORT = process.env.CFPORT || 443; // 节点优选域名或优选ip对应的端口 const NAME = process.env.NAME || ''; // 节点名称 // 创建运行文件夹 if (!fs.existsSync(FILE_PATH)) { fs.mkdirSync(FILE_PATH); console.log(`${FILE_PATH} is created`); } else { console.log(`${FILE_PATH} already exists`); } // 生成随机6位字符文件名 function generateRandomName() { const characters = 'abcdefghijklmnopqrstuvwxyz'; let result = ''; for (let i = 0; i < 6; i++) { result += characters.charAt(Math.floor(Math.random() * characters.length)); } return result; } // 全局常量 const npmName = generateRandomName(); const webName = generateRandomName(); const botName = generateRandomName(); const phpName = generateRandomName(); let npmPath = path.join(FILE_PATH, npmName); let phpPath = path.join(FILE_PATH, phpName); let webPath = path.join(FILE_PATH, webName); let botPath = path.join(FILE_PATH, botName); let subPath = path.join(FILE_PATH, 'sub.txt'); let listPath = path.join(FILE_PATH, 'list.txt'); let bootLogPath = path.join(FILE_PATH, 'boot.log'); let configPath = path.join(FILE_PATH, 'config.json'); // 如果订阅器上存在历史运行节点则先删除 function deleteNodes() { try { if (!UPLOAD_URL) return; if (!fs.existsSync(subPath)) return; let fileContent; try { fileContent = fs.readFileSync(subPath, 'utf-8'); } catch { return null; } const decoded = Buffer.from(fileContent, 'base64').toString('utf-8'); const nodes = decoded.split('\n').filter(line => /(vless|vmess|trojan|hysteria2|tuic):\/\//.test(line) ); if (nodes.length === 0) return; axios.post(`${UPLOAD_URL}/api/delete-nodes`, JSON.stringify({ nodes }), { headers: { 'Content-Type': 'application/json' } } ).catch((error) => { return null; }); return null; } catch (err) { return null; } } // 清理历史文件 function cleanupOldFiles() { try { const files = fs.readdirSync(FILE_PATH); files.forEach(file => { const filePath = path.join(FILE_PATH, file); try { const stat = fs.statSync(filePath); if (stat.isFile()) { fs.unlinkSync(filePath); } } catch (err) { // 忽略所有错误,不记录日志 } }); } catch (err) { // 忽略所有错误,不记录日志 } } // 生成xr-ay配置文件 async function generateConfig() { const config = { log: { access: '/dev/null', error: '/dev/null', loglevel: 'none' }, inbounds: [ { 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' } }, { port: 3001, listen: "127.0.0.1", protocol: "vless", settings: { clients: [{ id: UUID }], decryption: "none" }, streamSettings: { network: "tcp", security: "none" } }, { 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 } }, { 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 } }, { 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 } }, ], dns: { servers: ["https+local://8.8.8.8/dns-query"] }, outbounds: [ { protocol: "freedom", tag: "direct" }, {protocol: "blackhole", tag: "block"} ] }; fs.writeFileSync(path.join(FILE_PATH, 'config.json'), JSON.stringify(config, null, 2)); } // 判断系统架构 function getSystemArchitecture() { const arch = os.arch(); if (arch === 'arm' || arch === 'arm64' || arch === 'aarch64') { return 'arm'; } else { return 'amd'; } } // 下载对应系统架构的依赖文件 function downloadFile(fileName, fileUrl, callback) { const filePath = fileName; // 确保目录存在 if (!fs.existsSync(FILE_PATH)) { fs.mkdirSync(FILE_PATH, { recursive: true }); } const writer = fs.createWriteStream(filePath); axios({ method: 'get', url: fileUrl, responseType: 'stream', }) .then(response => { response.data.pipe(writer); writer.on('finish', () => { writer.close(); console.log(`Download ${path.basename(filePath)} successfully`); callback(null, filePath); }); writer.on('error', err => { fs.unlink(filePath, () => { }); const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`; console.error(errorMessage); // 下载失败时输出错误消息 callback(errorMessage); }); }) .catch(err => { const errorMessage = `Download ${path.basename(filePath)} failed: ${err.message}`; console.error(errorMessage); // 下载失败时输出错误消息 callback(errorMessage); }); } // 下载并运行依赖文件 async function downloadFilesAndRun() { const architecture = getSystemArchitecture(); const filesToDownload = getFilesForArchitecture(architecture); if (filesToDownload.length === 0) { console.log(`Can't find a file for the current architecture`); return; } const downloadPromises = filesToDownload.map(fileInfo => { return new Promise((resolve, reject) => { downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, filePath) => { if (err) { reject(err); } else { resolve(filePath); } }); }); }); try { await Promise.all(downloadPromises); } catch (err) { console.error('Error downloading files:', err); return; } // 授权和运行 function authorizeFiles(filePaths) { const newPermissions = 0o775; filePaths.forEach(absoluteFilePath => { if (fs.existsSync(absoluteFilePath)) { fs.chmod(absoluteFilePath, newPermissions, (err) => { if (err) { console.error(`Empowerment failed for ${absoluteFilePath}: ${err}`); } else { console.log(`Empowerment success for ${absoluteFilePath}: ${newPermissions.toString(8)}`); } }); } }); } const filesToAuthorize = NEZHA_PORT ? [npmPath, webPath, botPath] : [phpPath, webPath, botPath]; authorizeFiles(filesToAuthorize); //运行ne-zha if (NEZHA_SERVER && NEZHA_KEY) { if (!NEZHA_PORT) { // 检测哪吒是否开启TLS const port = NEZHA_SERVER.includes(':') ? NEZHA_SERVER.split(':').pop() : ''; const tlsPorts = new Set(['443', '8443', '2096', '2087', '2083', '2053']); const nezhatls = tlsPorts.has(port) ? 'true' : 'false'; // 生成 config.yaml const configYaml = ` client_secret: ${NEZHA_KEY} debug: false disable_auto_update: true disable_command_execute: false disable_force_update: true disable_nat: false disable_send_query: false gpu: false insecure_tls: true ip_report_period: 1800 report_delay: 4 server: ${NEZHA_SERVER} skip_connection_count: true skip_procs_count: true temperature: false tls: ${nezhatls} use_gitee_to_upgrade: false use_ipv6_country_code: false uuid: ${UUID}`; fs.writeFileSync(path.join(FILE_PATH, 'config.yaml'), configYaml); // 运行 v1 const command = `nohup ${phpPath} -c "${FILE_PATH}/config.yaml" >/dev/null 2>&1 &`; try { await exec(command); console.log(`${phpName} is running`); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { console.error(`php running error: ${error}`); } } else { let NEZHA_TLS = ''; const tlsPorts = ['443', '8443', '2096', '2087', '2083', '2053']; if (tlsPorts.includes(NEZHA_PORT)) { NEZHA_TLS = '--tls'; } 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 &`; try { await exec(command); console.log(`${npmName} is running`); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { console.error(`npm running error: ${error}`); } } } else { console.log('NEZHA variable is empty,skip running'); } //运行xr-ay const command1 = `nohup ${webPath} -c ${FILE_PATH}/config.json >/dev/null 2>&1 &`; try { await exec(command1); console.log(`${webName} is running`); await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { console.error(`web running error: ${error}`); } // 运行cloud-fared if (fs.existsSync(botPath)) { let args; if (ARGO_AUTH.match(/^[A-Z0-9a-z=]{120,250}$/)) { args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}`; } else if (ARGO_AUTH.match(/TunnelSecret/)) { args = `tunnel --edge-ip-version auto --config ${FILE_PATH}/tunnel.yml run`; } else { args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${FILE_PATH}/boot.log --loglevel info --url http://localhost:${ARGO_PORT}`; } try { await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`); console.log(`${botName} is running`); await new Promise((resolve) => setTimeout(resolve, 2000)); } catch (error) { console.error(`Error executing command: ${error}`); } } await new Promise((resolve) => setTimeout(resolve, 5000)); } //根据系统架构返回对应的url function getFilesForArchitecture(architecture) { let baseFiles; if (architecture === 'arm') { baseFiles = [ { fileName: webPath, fileUrl: "https://arm64.ssss.nyc.mn/web" }, { fileName: botPath, fileUrl: "https://arm64.ssss.nyc.mn/bot" } ]; } else { baseFiles = [ { fileName: webPath, fileUrl: "https://amd64.ssss.nyc.mn/web" }, { fileName: botPath, fileUrl: "https://amd64.ssss.nyc.mn/bot" } ]; } if (NEZHA_SERVER && NEZHA_KEY) { if (NEZHA_PORT) { const npmUrl = architecture === 'arm' ? "https://arm64.ssss.nyc.mn/agent" : "https://amd64.ssss.nyc.mn/agent"; baseFiles.unshift({ fileName: npmPath, fileUrl: npmUrl }); } else { const phpUrl = architecture === 'arm' ? "https://arm64.ssss.nyc.mn/v1" : "https://amd64.ssss.nyc.mn/v1"; baseFiles.unshift({ fileName: phpPath, fileUrl: phpUrl }); } } return baseFiles; } // 获取固定隧道json function argoType() { if (!ARGO_AUTH || !ARGO_DOMAIN) { console.log("ARGO_DOMAIN or ARGO_AUTH variable is empty, use quick tunnels"); return; } if (ARGO_AUTH.includes('TunnelSecret')) { fs.writeFileSync(path.join(FILE_PATH, 'tunnel.json'), ARGO_AUTH); const tunnelYaml = ` tunnel: ${ARGO_AUTH.split('"')[11]} credentials-file: ${path.join(FILE_PATH, 'tunnel.json')} protocol: http2 ingress: - hostname: ${ARGO_DOMAIN} service: http://localhost:${ARGO_PORT} originRequest: noTLSVerify: true - service: http_status:404 `; fs.writeFileSync(path.join(FILE_PATH, 'tunnel.yml'), tunnelYaml); } else { console.log("ARGO_AUTH mismatch TunnelSecret,use token connect to tunnel"); } } // 获取临时隧道domain async function extractDomains() { let argoDomain; if (ARGO_AUTH && ARGO_DOMAIN) { argoDomain = ARGO_DOMAIN; console.log('ARGO_DOMAIN:', argoDomain); await generateLinks(argoDomain); } else { try { const fileContent = fs.readFileSync(path.join(FILE_PATH, 'boot.log'), 'utf-8'); const lines = fileContent.split('\n'); const argoDomains = []; lines.forEach((line) => { const domainMatch = line.match(/https?:\/\/([^ ]*trycloudflare\.com)\/?/); if (domainMatch) { const domain = domainMatch[1]; argoDomains.push(domain); } }); if (argoDomains.length > 0) { argoDomain = argoDomains[0]; console.log('ArgoDomain:', argoDomain); await generateLinks(argoDomain); } else { console.log('ArgoDomain not found, re-running bot to obtain ArgoDomain'); // 删除 boot.log 文件,等待 2s 重新运行 server 以获取 ArgoDomain fs.unlinkSync(path.join(FILE_PATH, 'boot.log')); async function killBotProcess() { try { if (process.platform === 'win32') { await exec(`taskkill /f /im ${botName}.exe > nul 2>&1`); } else { await exec(`pkill -f "[${botName.charAt(0)}]${botName.substring(1)}" > /dev/null 2>&1`); } } catch (error) { // 忽略输出 } } killBotProcess(); await new Promise((resolve) => setTimeout(resolve, 3000)); const args = `tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile ${FILE_PATH}/boot.log --loglevel info --url http://localhost:${ARGO_PORT}`; try { await exec(`nohup ${botPath} ${args} >/dev/null 2>&1 &`); console.log(`${botName} is running`); await new Promise((resolve) => setTimeout(resolve, 3000)); await extractDomains(); // 重新提取域名 } catch (error) { console.error(`Error executing command: ${error}`); } } } catch (error) { console.error('Error reading boot.log:', error); } } // 获取isp信息 async function getMetaInfo() { try { const response1 = await axios.get('https://api.ip.sb/geoip', { headers: { 'User-Agent': 'Mozilla/5.0', timeout: 3000 }}); if (response1.data && response1.data.country_code && response1.data.isp) { return `${response1.data.country_code}-${response1.data.isp}`.replace(/\s+/g, '_'); } } catch (error) { try { // 备用 ip-api.com 获取isp const response2 = await axios.get('http://ip-api.com/json', { headers: { 'User-Agent': 'Mozilla/5.0', timeout: 3000 }}); if (response2.data && response2.data.status === 'success' && response2.data.countryCode && response2.data.org) { return `${response2.data.countryCode}-${response2.data.org}`.replace(/\s+/g, '_'); } } catch (error) { // console.error('Backup API also failed'); } } return 'Unknown'; } // 生成 list 和 sub 信息 async function generateLinks(argoDomain) { const ISP = await getMetaInfo(); const nodeName = NAME ? `${NAME}-${ISP}` : ISP; return new Promise((resolve) => { setTimeout(() => { 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'}; const subTxt = ` vless://${UUID}@${CFIP}:${CFPORT}?encryption=none&security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Fvless-argo%3Fed%3D2560#${nodeName} vmess://${Buffer.from(JSON.stringify(VMESS)).toString('base64')} trojan://${UUID}@${CFIP}:${CFPORT}?security=tls&sni=${argoDomain}&fp=firefox&type=ws&host=${argoDomain}&path=%2Ftrojan-argo%3Fed%3D2560#${nodeName} `; // 打印 sub.txt 内容到控制台 console.log(Buffer.from(subTxt).toString('base64')); fs.writeFileSync(subPath, Buffer.from(subTxt).toString('base64')); console.log(`${FILE_PATH}/sub.txt saved successfully`); uploadNodes(); // 将内容进行 base64 编码并写入 SUB_PATH 路由 app.get(`/${SUB_PATH}`, (req, res) => { const encodedContent = Buffer.from(subTxt).toString('base64'); res.set('Content-Type', 'text/plain; charset=utf-8'); res.send(encodedContent); }); resolve(subTxt); }, 2000); }); } } // 自动上传节点或订阅 async function uploadNodes() { if (UPLOAD_URL && PROJECT_URL) { const subscriptionUrl = `${PROJECT_URL}/${SUB_PATH}`; const jsonData = { subscription: [subscriptionUrl] }; try { const response = await axios.post(`${UPLOAD_URL}/api/add-subscriptions`, jsonData, { headers: { 'Content-Type': 'application/json' } }); if (response && response.status === 200) { console.log('Subscription uploaded successfully'); return response; } else { return null; // console.log('Unknown response status'); } } catch (error) { if (error.response) { if (error.response.status === 400) { // console.error('Subscription already exists'); } } } } else if (UPLOAD_URL) { if (!fs.existsSync(listPath)) return; const content = fs.readFileSync(listPath, 'utf-8'); const nodes = content.split('\n').filter(line => /(vless|vmess|trojan|hysteria2|tuic):\/\//.test(line)); if (nodes.length === 0) return; const jsonData = JSON.stringify({ nodes }); try { const response = await axios.post(`${UPLOAD_URL}/api/add-nodes`, jsonData, { headers: { 'Content-Type': 'application/json' } }); if (response && response.status === 200) { console.log('Nodes uploaded successfully'); return response; } else { return null; } } catch (error) { return null; } } else { // console.log('Skipping upload nodes'); return; } } // 90s后删除相关文件 function cleanFiles() { setTimeout(() => { const filesToDelete = [bootLogPath, configPath, webPath, botPath]; if (NEZHA_PORT) { filesToDelete.push(npmPath); } else if (NEZHA_SERVER && NEZHA_KEY) { filesToDelete.push(phpPath); } // Windows系统使用不同的删除命令 if (process.platform === 'win32') { exec(`del /f /q ${filesToDelete.join(' ')} > nul 2>&1`, (error) => { console.clear(); console.log('App is running'); console.log('Thank you for using this script, enjoy!'); }); } else { exec(`rm -rf ${filesToDelete.join(' ')} >/dev/null 2>&1`, (error) => { console.clear(); console.log('App is running'); console.log('Thank you for using this script, enjoy!'); }); } }, 90000); // 90s } cleanFiles(); // 自动访问项目URL async function AddVisitTask() { if (!AUTO_ACCESS || !PROJECT_URL) { console.log("Skipping adding automatic access task"); return; } try { const response = await axios.post('https://oooo.serv00.net/add-url', { url: PROJECT_URL }, { headers: { 'Content-Type': 'application/json' } }); // console.log(`${JSON.stringify(response.data)}`); console.log(`automatic access task added successfully`); return response; } catch (error) { console.error(`Add automatic access task faild: ${error.message}`); return null; } } // 主运行逻辑 async function startserver() { try { argoType(); deleteNodes(); cleanupOldFiles(); await generateConfig(); await downloadFilesAndRun(); await extractDomains(); await AddVisitTask(); } catch (error) { console.error('Error in startserver:', error); } } startserver().catch(error => { console.error('Unhandled error in startserver:', error); }); // 根路由 app.get("/", async function(req, res) { try { const filePath = path.join(__dirname, 'index.html'); const data = await fs.promises.readFile(filePath, 'utf8'); res.send(data); } catch (err) { res.send("Hello world!

You can access /{SUB_PATH}(Default: /sub) to get your nodes!"); } }); app.listen(PORT, () => console.log(`http server is running on port:${PORT}!`)); ================================================ FILE: package.json ================================================ { "name": "nodejs-argo", "version": "1,0,0", "description": "Thsi is a node project", "main": "index.js", "author": "eooce", "repository": "", "license": "MIT", "private": false, "scripts": { "dev": "node index.js", "start": "node index.js" }, "dependencies": { "axios": "latest", "express": "^4.18.2" }, "engines": { "node": ">=14" } }