前几天佬友说扣子编程出了一个免费版,所以也想薅一把
事先声明:扣子编程是国内的,所以无法翻墙,本质也是一个容器。能做一个代理隐藏IP来访问国内的网站。
所以,说老实话,自己很少有这个场景来使用
废话不多说,直接开始:
扣子编程的网址:https://code.coze.cn
本质是火山引擎旗下的东西,也是一个容器,而且吧,比较有意思,跟 claude 和 codex 差不多
基本是:讲述式编程方式,一天智能建3个项目,一个项目是1cpu,2G内存
打开网站,新建项目,然后点击网页应用,下面的文本输入框就是编程的地方了:
我们先让它自己生成一个应用,再在生成的基础上进行修改,直接硬来会有无穷的麻烦,它有自己固定的架构
建立一个极简的nodejs程序,不依赖任何前端和后端框架,不要用到next框架。前端页面是index.html,里面是Hello world,主程序是index.js,用来显示index.html的内容,绝对不要用到任何js框架。 然后系统就叽里呱啦、哔哔赖赖开始自己搞了,它缺省创立的项目无论你再怎么强调,都是基于next.js的,都会拉出一坨next的屎,所以会神经兮兮的思考来思考去,产生一堆废物,我们可以不用理他
然后显示正常
从上图我们得到几个关键信息:
端口是5000 文件夹可以看到文件,里面有一堆缺省的配置,就算强调,依然有next拉的屎 部署按钮,我们部署测试一下 按部署按钮,会得到一个域名
然后部署成功,点击箭头
打开后,就是我们想要的
我们记下来这个域名,大善人啊,免费域名和免费证书
然后回到文件夹,来修改三个文件,index.html 和 index.js 和 package.json
先来改 package.json, 在dependencies中,加两句,注意这两句的上一句需要加个逗号,axios最后没有逗号
"ws": "^8.14.2", "axios": "^1.12.2" 注意:写到最后,发现这里可以再部署一下再改index.jsp和index.html比较好,就不用经历我下面的回档了!!!!!
我下面是没有再部署,直接硬改,点开文件夹图标,选中index.js,先别贴,需要修改混淆的:
const http = require('http'); const https = require('https'); const fs = require('fs'); const dns = require('dns'); const axios = require('axios'); const net = require('net'); const path = require('path'); const crypto = require('crypto'); const { Buffer } = require('buffer'); const { WebSocket, createWebSocketStream } = require('ws'); // 生成 UUID v4 function generateUUID() { return crypto.randomUUID(); } const UUID = process.env.UUID || generateUUID(); const DOMAIN = process.env.DOMAIN || 'xxxxxxxx.coze.site'; // 填写项目域名 const WSPATH = process.env.WSPATH || UUID.slice(0, 8); // 节点路径,默认获取uuid前8位 const SUB_PATH = process.env.SUB_PATH || 'sub'; // 节点的订阅路径 const NAME = process.env.NAME || ''; // 节点名称 const PORT = process.env.PORT || 5000; // http和ws服务端口 const TLS_KEY_PATH = process.env.TLS_KEY_PATH || ''; const TLS_CERT_PATH = process.env.TLS_CERT_PATH || ''; let ISP = ''; const GetISP = async () => { try { const res = await axios.get('https://api.ip.sb/geoip'); const data = res.data; ISP = `${data.country_code}-${data.isp}`.replace(/ /g, '_'); } catch (e) { ISP = 'Unknown'; } } GetISP(); function normalizeSecret(value) { if (!value) return ''; return value.includes('\\n') ? value.replace(/\\n/g, '\n') : value; } function readOptionalFile(filePath) { if (!filePath) return ''; const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(__dirname, filePath); try { return fs.readFileSync(resolvedPath, 'utf8'); } catch (error) { console.warn(`Failed to read TLS file ${resolvedPath}: ${error.message}`); return ''; } } function getTLSOptions() { let key = normalizeSecret(process.env.TLS_KEY); let cert = normalizeSecret(process.env.TLS_CERT); if (!key) { key = readOptionalFile(TLS_KEY_PATH); } if (!cert) { cert = readOptionalFile(TLS_CERT_PATH); } if (key && cert) { return { key, cert }; } if (key || cert) { console.warn('Both TLS key and certificate are required; falling back to HTTP.'); } return null; } const requestHandler = (req, res) => { if (req.url === '/') { const filePath = path.join(__dirname, 'index.html'); fs.readFile(filePath, 'utf8', (err, content) => { if (err) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end('Hello world!'); return; } res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(content); }); return; } else if (req.url === `/${SUB_PATH}`) { const namePart = NAME ? `${NAME}-${ISP}` : ISP; const vlessURL = `vless://${UUID}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}&fp=chrome&type=ws&host=${DOMAIN}&path=%2F${WSPATH}#${namePart}`; const trojanURL = `trojan://${UUID}@${DOMAIN}:443?security=tls&sni=${DOMAIN}&fp=chrome&type=ws&host=${DOMAIN}&path=%2F${WSPATH}#${namePart}`; const subscription = vlessURL + '\n' + trojanURL; const base64Content = Buffer.from(subscription).toString('base64'); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(base64Content + '\n'); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found\n'); } }; const tlsOptions = getTLSOptions(); const httpServer = tlsOptions ? https.createServer(tlsOptions, requestHandler) : http.createServer(requestHandler); const wss = new WebSocket.Server({ server: httpServer }); const uuid = UUID.replace(/-/g, ""); const DNS_SERVERS = ['114.114.114.114', '223.5.5.5']; // Custom DNS async function resolveHost(host) { const target = String(host || '').trim(); if (!target) throw new Error('Host is empty'); if (net.isIP(target)) return target; try { const addresses = await dns.promises.lookup(target, { all: true }); const preferred = addresses.find(a => a.family === 4) || addresses[0]; if (preferred?.address) return preferred.address; } catch (_) { // fall through to custom DNS servers } const resolver = new dns.promises.Resolver(); for (const server of DNS_SERVERS) { try { resolver.setServers([server]); const v4 = await resolver.resolve4(target); if (v4?.[0]) return v4[0]; } catch (_) {} try { resolver.setServers([server]); const v6 = await resolver.resolve6(target); if (v6?.[0]) return v6[0]; } catch (_) {} } throw new Error(`Failed to resolve ${target}`); } // VLE-SS处理 function handleVlessConnection(ws, msg) { const [VERSION] = msg; const id = msg.slice(1, 17); if (!id.every((v, i) => v == parseInt(uuid.substr(i * 2, 2), 16))) return false; let i = msg.slice(17, 18).readUInt8() + 19; const port = msg.slice(i, i += 2).readUInt16BE(0); const ATYP = msg.slice(i, i += 1).readUInt8(); const host = ATYP == 1 ? msg.slice(i, i += 4).join('.') : (ATYP == 2 ? new TextDecoder().decode(msg.slice(i + 1, i += 1 + msg.slice(i, i + 1).readUInt8())) : (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(':') : '')); ws.send(new Uint8Array([VERSION, 0])); const duplex = createWebSocketStream(ws); resolveHost(host) .then(resolvedIP => { net.connect({ host: resolvedIP, port }, function() { this.write(msg.slice(i)); duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex); }).on('error', () => {}); }) .catch(error => { net.connect({ host, port }, function() { this.write(msg.slice(i)); duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex); }).on('error', () => {}); }); return true; } // Tro-jan处理 function handleTrojanConnection(ws, msg) { try { if (msg.length < 58) return false; const receivedPasswordHash = msg.slice(0, 56).toString(); const possiblePasswords = [ UUID, ]; let matchedPassword = null; for (const pwd of possiblePasswords) { const hash = crypto.createHash('sha224').update(pwd).digest('hex'); if (hash === receivedPasswordHash) { matchedPassword = pwd; break; } } if (!matchedPassword) return false; let offset = 56; if (msg[offset] === 0x0d && msg[offset + 1] === 0x0a) { offset += 2; } const cmd = msg[offset]; if (cmd !== 0x01) return false; offset += 1; const atyp = msg[offset]; offset += 1; let host, port; if (atyp === 0x01) { host = msg.slice(offset, offset + 4).join('.'); offset += 4; } else if (atyp === 0x03) { const hostLen = msg[offset]; offset += 1; host = msg.slice(offset, offset + hostLen).toString(); offset += hostLen; } else if (atyp === 0x04) { host = msg.slice(offset, offset + 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(':'); offset += 16; } else { return false; } port = msg.readUInt16BE(offset); offset += 2; if (offset < msg.length && msg[offset] === 0x0d && msg[offset + 1] === 0x0a) { offset += 2; } const duplex = createWebSocketStream(ws); resolveHost(host) .then(resolvedIP => { net.connect({ host: resolvedIP, port }, function() { if (offset < msg.length) { this.write(msg.slice(offset)); } duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex); }).on('error', () => {}); }) .catch(error => { net.connect({ host, port }, function() { if (offset < msg.length) { this.write(msg.slice(offset)); } duplex.on('error', () => {}).pipe(this).on('error', () => {}).pipe(duplex); }).on('error', () => {}); }); return true; } catch (error) { return false; } } // Ws 连接处理 wss.on('connection', (ws, req) => { const url = req.url || ''; ws.once('message', msg => { if (msg.length > 17 && msg[0] === 0) { const id = msg.slice(1, 17); const isVless = id.every((v, i) => v == parseInt(uuid.substr(i * 2, 2), 16)); if (isVless) { if (!handleVlessConnection(ws, msg)) { ws.close(); } return; } } if (!handleTrojanConnection(ws, msg)) { ws.close(); } }).on('error', () => {}); }); httpServer.listen(PORT, () => { const scheme = tlsOptions ? 'HTTPS/WSS' : 'HTTP/WS'; console.log(`Server is running on ${scheme} port ${PORT}`); }); 原始文件长这样:
...