├── README.md ├── app.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Webhostmost和webfreecloud部署 node-ws 项目教程: (https://banfeng.us.kg/archives/53.html) 2 | 仓库来源于https://github.com/banfeng-git/Webhostmost-ws-nodejs # 进行修改 3 | 如果失效可以访问这个链接使用方案2https://github.com/banfeng-git/node-ws 4 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const http = require('http'); 3 | const { Buffer } = require('buffer'); 4 | const fs = require('fs'); 5 | const axios = require('axios'); 6 | const path = require('path'); 7 | const net = require('net'); 8 | const { exec, execSync } = require('child_process'); 9 | const { WebSocket, createWebSocketStream } = require('ws'); 10 | const logcb = (...args) => console.log.bind(this, ...args); 11 | const errcb = (...args) => console.error.bind(this, ...args); 12 | const UUID = process.env.UUID || 'b28f60af-d0b9-4ddf-baaa-7e49c93c380b'; 13 | const uuid = UUID.replace(/-/g, ""); 14 | const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; 15 | const NEZHA_PORT = process.env.NEZHA_PORT || '443'; // 端口为443时自动开启tls 16 | const NEZHA_KEY = process.env.NEZHA_KEY || ''; // 哪吒三个变量不全不运行 17 | const DOMAIN = process.env.DOMAIN || ''; //项目域名或已反代的域名,不带前缀,建议填已反代的域名 18 | const NAME = process.env.NAME || 'webhostmost-GCP'; 19 | const port = process.env.PORT || 3000; 20 | 21 | // 创建HTTP路由 22 | const httpServer = http.createServer((req, res) => { 23 | if (req.url === '/') { 24 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 25 | res.end('Hello, World\n'); 26 | } else if (req.url === '/sub') { 27 | const vlessURL = `vless://${UUID}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}&type=ws&host=${DOMAIN}&path=%2F#${NAME}`; 28 | 29 | const base64Content = Buffer.from(vlessURL).toString('base64'); 30 | 31 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 32 | res.end(base64Content + '\n'); 33 | } else { 34 | res.writeHead(404, { 'Content-Type': 'text/plain' }); 35 | res.end('Not Found\n'); 36 | } 37 | }); 38 | 39 | httpServer.listen(port, () => { 40 | console.log(`HTTP Server is running on port ${port}`); 41 | }); 42 | 43 | // 判断系统架构 44 | function getSystemArchitecture() { 45 | const arch = os.arch(); 46 | if (arch === 'arm' || arch === 'arm64') { 47 | return 'arm'; 48 | } else { 49 | return 'amd'; 50 | } 51 | } 52 | 53 | // 下载对应系统架构的ne-zha 54 | function downloadFile(fileName, fileUrl, callback) { 55 | const filePath = path.join("./", fileName); 56 | const writer = fs.createWriteStream(filePath); 57 | axios({ 58 | method: 'get', 59 | url: fileUrl, 60 | responseType: 'stream', 61 | }) 62 | .then(response => { 63 | response.data.pipe(writer); 64 | writer.on('finish', function () { 65 | writer.close(); 66 | callback(null, fileName); 67 | }); 68 | }) 69 | .catch(error => { 70 | callback(`Download ${fileName} failed: ${error.message}`); 71 | }); 72 | } 73 | 74 | function downloadFiles() { 75 | const architecture = getSystemArchitecture(); 76 | const filesToDownload = getFilesForArchitecture(architecture); 77 | 78 | if (filesToDownload.length === 0) { 79 | console.log(`Can't find a file for the current architecture`); 80 | return; 81 | } 82 | 83 | let downloadedCount = 0; 84 | 85 | filesToDownload.forEach(fileInfo => { 86 | downloadFile(fileInfo.fileName, fileInfo.fileUrl, (err, fileName) => { 87 | if (err) { 88 | console.log(`Download ${fileName} failed`); 89 | } else { 90 | console.log(`Download ${fileName} successfully`); 91 | 92 | downloadedCount++; 93 | 94 | if (downloadedCount === filesToDownload.length) { 95 | setTimeout(() => { 96 | authorizeFiles(); 97 | }, 3000); 98 | } 99 | } 100 | }); 101 | }); 102 | } 103 | 104 | function getFilesForArchitecture(architecture) { 105 | if (architecture === 'arm') { 106 | return [ 107 | { fileName: "npm", fileUrl: "https://github.com/eooce/test/releases/download/ARM/swith" }, 108 | ]; 109 | } else if (architecture === 'amd') { 110 | return [ 111 | { fileName: "npm", fileUrl: "https://github.com/eooce/test/releases/download/bulid/swith" }, 112 | ]; 113 | } 114 | return []; 115 | } 116 | 117 | // 授权并运行ne-zha 118 | function authorizeFiles() { 119 | const filePath = './npm'; 120 | const newPermissions = 0o775; 121 | fs.chmod(filePath, newPermissions, (err) => { 122 | if (err) { 123 | console.error(`Empowerment failed:${err}`); 124 | } else { 125 | console.log(`Empowerment success:${newPermissions.toString(8)} (${newPermissions.toString(10)})`); 126 | 127 | // 运行ne-zha 128 | let NEZHA_TLS = ''; 129 | if (NEZHA_SERVER && NEZHA_PORT && NEZHA_KEY) { 130 | if (NEZHA_PORT === '443') { 131 | NEZHA_TLS = '--tls'; 132 | } else { 133 | NEZHA_TLS = ''; 134 | } 135 | const command = `./npm -s ${NEZHA_SERVER}:${NEZHA_PORT} -p ${NEZHA_KEY} ${NEZHA_TLS} --skip-conn --disable-auto-update --skip-procs --report-delay 4 >/dev/null 2>&1 &`; 136 | try { 137 | exec(command); 138 | console.log('npm is running'); 139 | } catch (error) { 140 | console.error(`npm running error: ${error}`); 141 | } 142 | } else { 143 | console.log('NEZHA variable is empty,skip running'); 144 | } 145 | } 146 | }); 147 | } 148 | downloadFiles(); 149 | 150 | // WebSocket 服务器 151 | const wss = new WebSocket.Server({ server: httpServer }); 152 | wss.on('connection', ws => { 153 | console.log("WebSocket 连接成功"); 154 | ws.on('message', msg => { 155 | if (msg.length < 18) { 156 | console.error("数据长度无效"); 157 | return; 158 | } 159 | try { 160 | const [VERSION] = msg; 161 | const id = msg.slice(1, 17); 162 | if (!id.every((v, i) => v == parseInt(uuid.substr(i * 2, 2), 16))) { 163 | console.error("UUID 验证失败"); 164 | return; 165 | } 166 | let i = msg.slice(17, 18).readUInt8() + 19; 167 | const port = msg.slice(i, i += 2).readUInt16BE(0); 168 | const ATYP = msg.slice(i, i += 1).readUInt8(); 169 | const host = ATYP === 1 ? msg.slice(i, i += 4).join('.') : 170 | (ATYP === 2 ? new TextDecoder().decode(msg.slice(i + 1, i += 1 + msg.slice(i, i + 1).readUInt8())) : 171 | (ATYP === 3 ? msg.slice(i, i += 16).reduce((s, b, i, a) => (i % 2 ? s.concat(a.slice(i - 1, i + 1)) : s), []).map(b => b.readUInt16BE(0).toString(16)).join(':') : '')); 172 | console.log('连接到:', host, port); 173 | ws.send(new Uint8Array([VERSION, 0])); 174 | const duplex = createWebSocketStream(ws); 175 | net.connect({ host, port }, function () { 176 | this.write(msg.slice(i)); 177 | duplex.on('error', err => console.error("E1:", err.message)).pipe(this).on('error', err => console.error("E2:", err.message)).pipe(duplex); 178 | }).on('error', err => console.error("连接错误:", err.message)); 179 | } catch (err) { 180 | console.error("处理消息时出错:", err.message); 181 | } 182 | }).on('error', err => console.error("WebSocket 错误:", err.message)); 183 | }); 184 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-ws", 3 | "version": "1.0.0", 4 | "description": "Node.js Server", 5 | "main": "app.js", 6 | "author": "", 7 | "repository": "", 8 | "license": "MIT", 9 | "private": false, 10 | "scripts": { 11 | "start": "node app.js" 12 | }, 13 | "dependencies": { 14 | "ws": "^8.14.2", 15 | "axios": "^1.6.2" 16 | }, 17 | "engines": { 18 | "node": ">=14" 19 | } 20 | } 21 | --------------------------------------------------------------------------------