634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xray for CodeSandbox
2 |
3 | 在 CodeSandbox 部署 xray 节点
4 |
5 | ## 项目特点
6 |
7 | * 本项目用于在 CodeSandbox 上部署 xray ,采用的方案为 Node.js + WebSocket + VMess/Vless/Trojan/Shadowsocks + TLS
8 | * vmess 和 vless 的 uuid 或 trojan 和 shadowsocks 的密码,路径既可以自定义,又或者使用默认值
9 | * 部署完成如发现不能上网,请检查域名是否被墙,可使用 Cloudflare CDN 或者 worker 解决。
10 |
11 | ## 部署
12 |
13 | * 注册 [CodeSandbox](https://codesandbox.io/)
14 | * 项目用到的变量
15 | | 变量名 | 是否必须 | 默认值 | 备注 |
16 | | ------------ | ------ | ------ | ------ |
17 | | UUID | 否 | de04add9-5c68-8bab-950c-08cd5320df18 | 可在线生成 https://www.uuidgenerator.net/ |
18 | | VMESS_WSPATH | 否 | /vmess | 以 / 开头 |
19 | | VLESS_WSPATH | 否 | /vless | 以 / 开头 |
20 | | TROJAN_WSPATH | 否 | /trojan | 以 / 开头 |
21 | | SS_WSPATH | 否 | /shadowsocks | 以 / 开头 |
22 |
23 | ## 鸣谢
24 |
25 | ifeng 的 v2ray 项目:https://github.com/hiifeng/V2ray-for-Codesandbox
26 |
27 | ## 免责声明
28 |
29 | * 本程序仅供学习了解, 非盈利目的,请于下载后 24 小时内删除, 不得用作任何商业用途, 文字、数据及图片均有所属版权, 如转载须注明来源。
30 | * 使用本程序必循遵守部署免责声明。使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责.
31 |
32 | ## 赞助
33 |
34 | 爱发电:https://afdian.net/a/Misaka-blog
35 |
36 | 
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | UUID=${UUID:-'de04add9-5c68-8bab-950c-08cd5320df18'}
4 | VMESS_WSPATH=${VMESS_WSPATH:-'/vmess'}
5 | VLESS_WSPATH=${VLESS_WSPATH:-'/vless'}
6 | TROJAN_WSPATH=${TROJAN_WSPATH:-'/trojan'}
7 | SS_WSPATH=${SS_WSPATH:-'/shadowsocks'}
8 |
9 | generate_config() {
10 | cat > config.json << EOF
11 | {
12 | "log":{
13 | "access":"/dev/null",
14 | "error":"/dev/null",
15 | "loglevel":"none"
16 | },
17 | "inbounds":[
18 | {
19 | "port":8080,
20 | "protocol":"vless",
21 | "settings":{
22 | "clients":[
23 | {
24 | "id":"${UUID}",
25 | "flow":"xtls-rprx-vision"
26 | }
27 | ],
28 | "decryption":"none",
29 | "fallbacks":[
30 | {
31 | "dest":3001
32 | },
33 | {
34 | "path":"${VLESS_WSPATH}",
35 | "dest":3002
36 | },
37 | {
38 | "path":"${VMESS_WSPATH}",
39 | "dest":3003
40 | },
41 | {
42 | "path":"${TROJAN_WSPATH}",
43 | "dest":3004
44 | },
45 | {
46 | "path":"${SS_WSPATH}",
47 | "dest":3005
48 | }
49 | ]
50 | },
51 | "streamSettings":{
52 | "network":"tcp"
53 | }
54 | },
55 | {
56 | "port":3001,
57 | "listen":"127.0.0.1",
58 | "protocol":"vless",
59 | "settings":{
60 | "clients":[
61 | {
62 | "id":"${UUID}"
63 | }
64 | ],
65 | "decryption":"none"
66 | },
67 | "streamSettings":{
68 | "network":"ws",
69 | "security":"none"
70 | }
71 | },
72 | {
73 | "port":3002,
74 | "listen":"127.0.0.1",
75 | "protocol":"vless",
76 | "settings":{
77 | "clients":[
78 | {
79 | "id":"${UUID}",
80 | "level":0
81 | }
82 | ],
83 | "decryption":"none"
84 | },
85 | "streamSettings":{
86 | "network":"ws",
87 | "security":"none",
88 | "wsSettings":{
89 | "path":"${VLESS_WSPATH}"
90 | }
91 | },
92 | "sniffing":{
93 | "enabled":true,
94 | "destOverride":[
95 | "http",
96 | "tls"
97 | ],
98 | "metadataOnly":false
99 | }
100 | },
101 | {
102 | "port":3003,
103 | "listen":"127.0.0.1",
104 | "protocol":"vmess",
105 | "settings":{
106 | "clients":[
107 | {
108 | "id":"${UUID}",
109 | "alterId":0
110 | }
111 | ]
112 | },
113 | "streamSettings":{
114 | "network":"ws",
115 | "wsSettings":{
116 | "path":"${VMESS_WSPATH}"
117 | }
118 | },
119 | "sniffing":{
120 | "enabled":true,
121 | "destOverride":[
122 | "http",
123 | "tls"
124 | ],
125 | "metadataOnly":false
126 | }
127 | },
128 | {
129 | "port":3004,
130 | "listen":"127.0.0.1",
131 | "protocol":"trojan",
132 | "settings":{
133 | "clients":[
134 | {
135 | "password":"${UUID}"
136 | }
137 | ]
138 | },
139 | "streamSettings":{
140 | "network":"ws",
141 | "security":"none",
142 | "wsSettings":{
143 | "path":"${TROJAN_WSPATH}"
144 | }
145 | },
146 | "sniffing":{
147 | "enabled":true,
148 | "destOverride":[
149 | "http",
150 | "tls"
151 | ],
152 | "metadataOnly":false
153 | }
154 | },
155 | {
156 | "port":3005,
157 | "listen":"127.0.0.1",
158 | "protocol":"shadowsocks",
159 | "settings":{
160 | "clients":[
161 | {
162 | "method":"chacha20-ietf-poly1305",
163 | "password":"${UUID}"
164 | }
165 | ],
166 | "decryption":"none"
167 | },
168 | "streamSettings":{
169 | "network":"ws",
170 | "wsSettings":{
171 | "path":"${SS_WSPATH}"
172 | }
173 | },
174 | "sniffing":{
175 | "enabled":true,
176 | "destOverride":[
177 | "http",
178 | "tls"
179 | ],
180 | "metadataOnly":false
181 | }
182 | }
183 | ],
184 | "outbounds":[
185 | {
186 | "protocol":"freedom"
187 | },
188 | {
189 | "tag": "WARP",
190 | "protocol": "wireguard",
191 | "settings": {
192 | "secretKey": "GAl2z55U2UzNU5FG+LW3kowK+BA/WGMi1dWYwx20pWk=",
193 | "address": [
194 | "172.16.0.2/32",
195 | "2606:4700:110:8f0a:fcdb:db2f:3b3:4d49/128"
196 | ],
197 | "peers": [
198 | {
199 | "publicKey": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
200 | "endpoint": "engage.cloudflareclient.com:2408"
201 | }
202 | ]
203 | }
204 | }
205 | ],
206 | "routing": {
207 | "domainStrategy": "AsIs",
208 | "rules": [
209 | {
210 | "type": "field",
211 | "domain": [
212 | "domain:openai.com",
213 | "domain:ai.com"
214 | ],
215 | "outboundTag": "WARP"
216 | }
217 | ]
218 | },
219 | "dns":{
220 | "servers":[
221 | "https+local://8.8.8.8/dns-query"
222 | ]
223 | }
224 | }
225 | EOF
226 | }
227 |
228 | generate_nezha() {
229 | cat > nezha.sh << EOF
230 | #!/usr/bin/env bash
231 |
232 | # 哪吒的三个参数
233 | NEZHA_SERVER=${NEZHA_SERVER}
234 | NEZHA_PORT=${NEZHA_PORT}
235 | NEZHA_KEY=${NEZHA_KEY}
236 |
237 | # 检测是否已运行
238 | check_run() {
239 | [[ \$(pidof nezha-agent) ]] && echo "哪吒客户端正在运行中" && exit
240 | }
241 |
242 | # 三个变量不全则不安装哪吒客户端
243 | check_variable() {
244 | [[ -z "\${NEZHA_SERVER}" || -z "\${NEZHA_PORT}" || -z "\${NEZHA_KEY}" ]] && exit
245 | }
246 |
247 | # 下载最新版本 Nezha Agent
248 | download_agent() {
249 | if [ ! -e nezha-agent ]; then
250 | URL=\$(wget -qO- -4 "https://api.github.com/repos/naiba/nezha/releases/latest" | grep -o "https.*linux_amd64.zip")
251 | wget -t 2 -T 10 -N \${URL}
252 | unzip -qod ./ nezha-agent_linux_amd64.zip && rm -f nezha-agent_linux_amd64.zip
253 | fi
254 | }
255 |
256 | # 运行客户端
257 | run() {
258 | [ -e nezha-agent ] && chmod +x nezha-agent && ./nezha-agent -s \${NEZHA_SERVER}:\${NEZHA_PORT} -p \${NEZHA_KEY}
259 | }
260 |
261 | check_run
262 | check_variable
263 | download_agent
264 | run
265 | wait
266 | EOF
267 | }
268 |
269 | generate_config
270 | generate_nezha
271 | [ -e nezha.sh ] && bash nezha.sh 2>&1 &
272 | wait
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const server = "127.0.0.1";
2 | const port = 3000;
3 | const express = require("express");
4 | const app = express();
5 | var exec = require("child_process").exec;
6 | const os = require("os");
7 | const { createProxyMiddleware } = require("http-proxy-middleware");
8 | var request = require("request");
9 | var fs = require("fs");
10 |
11 | //首页显示内容
12 | app.get("/", function (req, res) {
13 | res.send("Hello world!");
14 | });
15 |
16 | //启动web
17 | app.get("/start", function (req, res) {
18 | let cmdStr =
19 | "[ -e entrypoint.sh ] && bash entrypoint.sh; chmod +x ./web.js && ./web.js -c ./config.json >/dev/null 2>&1 &";
20 | exec(cmdStr, function (err, stdout, stderr) {
21 | if (err) {
22 | res.send("Web 执行错误:" + err);
23 | } else {
24 | res.send("Web 执行结果:" + "启动成功!");
25 | }
26 | });
27 | });
28 |
29 | //启动哪吒
30 | app.get("/nezha", function (req, res) {
31 | let cmdStr = "bash nezha.sh >/dev/null 2>&1 &";
32 | exec(cmdStr, function (err, stdout, stderr) {
33 | if (err) {
34 | res.send("哪吒部署错误:" + err);
35 | } else {
36 | res.send("哪吒执行结果:" + "启动成功!");
37 | }
38 | });
39 | });
40 |
41 | //获取系统监听端口
42 | app.get("/listen", function (req, res) {
43 | let cmdStr = "ss -nltp";
44 | exec(cmdStr, function (err, stdout, stderr) {
45 | if (err) {
46 | res.type("html").send("命令行执行错误:\n" + err + "
");
47 | } else {
48 | res.type("html").send("获取系统监听端口:\n" + stdout + "
");
49 | }
50 | });
51 | });
52 |
53 | //获取系统版本、内存信息
54 | app.get("/info", function (req, res) {
55 | let cmdStr = "cat /etc/*release | grep -E ^NAME";
56 | exec(cmdStr, function (err, stdout, stderr) {
57 | if (err) {
58 | res.send("命令行执行错误:" + err);
59 | } else {
60 | res.send(
61 | "命令行执行结果:\n" +
62 | "Linux System:" +
63 | stdout +
64 | "\nRAM:" +
65 | os.totalmem() / 1000 / 1000 +
66 | "MB"
67 | );
68 | }
69 | });
70 | });
71 |
72 | //文件系统只读测试
73 | app.get("/test", function (req, res) {
74 | fs.writeFile("./test.txt", "这里是新创建的文件内容!", function (err) {
75 | if (err) {
76 | res.send("创建文件失败,文件系统权限为只读:" + err);
77 | } else {
78 | res.send("创建文件成功,文件系统权限为非只读:");
79 | }
80 | });
81 | });
82 |
83 | // keepalive begin
84 | function keep_web_alive() {
85 | // 1.请求主页,保持唤醒
86 | request("http://" + server + ":" + port, function (error, response, body) {
87 | if (!error) {
88 | console.log("保活-请求主页-命令行执行成功,响应报文:" + body);
89 | } else {
90 | console.log("保活-请求主页-命令行执行错误: " + error);
91 | }
92 | });
93 |
94 | // 2.请求服务器进程状态列表,若web没在运行,则调起
95 | exec("ss -nltp", function (err, stdout, stderr) {
96 | // 1.查后台系统进程,保持唤醒
97 | if (stdout.includes("web.js")) {
98 | console.log("web 正在运行");
99 | } else {
100 | // web 未运行,命令行调起
101 | exec(
102 | "chmod +x web.js && ./web.js -c ./config.json >/dev/null 2>&1 &",
103 | function (err, stdout, stderr) {
104 | if (err) {
105 | console.log("保活-调起web-命令行执行错误:" + err);
106 | } else {
107 | console.log("保活-调起web-命令行执行成功!");
108 | }
109 | }
110 | );
111 | }
112 | });
113 | }
114 | setInterval(keep_web_alive, 10 * 1000);
115 |
116 | // 哪吒保活
117 | function keep_nezha_alive() {
118 | exec("pidof nezha-agent", function (err, stdout, stderr) {
119 | // 1.查后台系统进程,保持唤醒
120 | if (stdout != "") {
121 | console.log("哪吒正在运行");
122 | } else {
123 | // 哪吒未运行,命令行调起
124 | exec("bash nezha.sh 2>&1 &", function (err, stdout, stderr) {
125 | if (err) {
126 | console.log("保活-调起哪吒-命令行执行错误:" + err);
127 | } else {
128 | console.log("保活-调起哪吒-命令行执行成功!");
129 | }
130 | });
131 | }
132 | });
133 | }
134 | setInterval(keep_nezha_alive, 45 * 1000);
135 | // keepalive end
136 |
137 | app.use(
138 | "/",
139 | createProxyMiddleware({
140 | changeOrigin: true, // 默认false,是否需要改变原始主机头为目标URL
141 | onProxyReq: function onProxyReq(proxyReq, req, res) {},
142 | pathRewrite: {
143 | // 请求中去除/
144 | "^/": "/",
145 | },
146 | target: "http://127.0.0.1:8080/", // 需要跨域处理的请求地址
147 | ws: true, // 是否代理websockets
148 | })
149 | );
150 |
151 | //启动核心脚本运行web和哪吒
152 | exec("bash entrypoint.sh", function (err, stdout, stderr) {
153 | if (err) {
154 | console.error(err);
155 | return;
156 | }
157 | console.log(stdout);
158 | });
159 |
160 | app.listen(port, () => console.log(`Example app listening on port ${port}!`));
--------------------------------------------------------------------------------
/nezha-agent:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Misaka-blog/xray-for-codesandbox/f9d45f49db2e1ebc5b7a927b1a2a01f2b89f70d3/nezha-agent
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express-hello-world",
3 | "version": "1.0.0",
4 | "description": "Express Hello World",
5 | "main": "index.js",
6 | "repository": "",
7 | "author": "Misaka-blog",
8 | "license": "MIT",
9 | "private": false,
10 | "scripts": {
11 | "start": "node index.js"
12 | },
13 | "dependencies": {
14 | "express": "^4.18.2",
15 | "http-proxy-middleware": "^2.0.6",
16 | "request": "^2.88.2"
17 | },
18 | "engines": {
19 | "node": ">=14"
20 | }
21 | }
--------------------------------------------------------------------------------
/web.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Misaka-blog/xray-for-codesandbox/f9d45f49db2e1ebc5b7a927b1a2a01f2b89f70d3/web.js
--------------------------------------------------------------------------------