[
  {
    "path": "README.md",
    "content": "# Xray + Argo for Express.js PaaS\n\n在没有公网的平台挖啊挖啊挖，Argo打通各式服务连接千万家。---为 JS 平台而生\n\n* * *\n\n# 目录\n\n- [项目特点](README.md#项目特点)\n- [部署](README.md#部署)\n- [在 Glitch 部署重点](README.md#在-glitch-部署重点)\n- [在 Daki 部署重点](README.md#在-daki-部署重点)\n- [ttyd webssh / filebrowser webftp 的部署](README.md#ttyd-webssh--filebrowser-webftp-的部署)\n- [鸣谢下列作者的文章和项目](README.md#鸣谢下列作者的文章和项目)\n- [免责声明](README.md#免责声明)\n\n* * *\n\n## 项目特点:\n* 本项目用于在 Express.js PaaS 平台上部署 Xray，采用的方案为 Argo + Xray + WebSocket + TLS\n* 解锁 ChatGPT\n* 在浏览器查看系统各项信息，方便直观\n* 使用 CloudFlare 的 Argo 隧道，直接优选 + 隧道，CDN 不用再做 workers\n* 回流分流，同时支持 Xray 4 种主流协议: vless /  vmess / trojan / shadowsocks\n* vmess 和 vless 的 uuid，trojan 和 shadowsocks 的 password，各协议的 ws 路径既可以自定义，又或者使用默认值\n* 集成哪吒探针，可以自由选择是否安装\n* 前端 js 定时保活，会玩的用户可以根据具体情况修改间隔时间\n* 节点信息以 V2rayN / Clash / 小火箭 链接方式输出\n* Xray 文件重新编译官方文件增加隐秘性，修改了运行时的显示信息，文件为: https://github.com/XTLS/Xray-core/blob/main/core/core.go\n* 可以使用浏览器使用 webssh 和 webftp，更方便管理系统\n\n<img width=\"718\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/215277537-ff358dc1-7696-481f-b8e4-74f0cdff30f4.png\">\n\n## 部署:\n\n### PaaS 平台用到的变量:\n \n* 在 `server.js` 文件的第1、2行修改查询网页的用户名和密码\n  | 变量名        | 是否必须 | 默认值 | 备注 |\n  | ------------ | ------ | ------ | ------ |\n  | WEB_USERNAME | 是 | admin | 网页的用户名 |\n  | WEB_PASSWORD | 是 | password | 网页的密码 |\n\n<img width=\"939\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/221387298-4183a1d6-ae14-45f9-b498-1789a4f7117e.png\">\n\n* 在 `entrypoint.sh` 文件的前面 4-15 行修改；访问页面的认证在 `server.js` 文件的第1、2行修改必填\n  | 变量名        | 是否必须 | 默认值 | 备注 |\n  | ------------ | ------ | ------ | ------ |\n  | UUID         | 否 | de04add9-5c68-8bab-950c-08cd5320df18 | 可在线生成 https://www.zxgj.cn/g/uuid |\n  | WSPATH       | 否 | argo | 勿以 / 开头，各协议路径为 `/WSPATH-协议`，如 `/argo-vless`,`/argo-vmess`,`/argo-trojan`,`/argo-shadowsocks` |\n  | NEZHA_SERVER | 否 |        | 哪吒探针服务端的 IP 或域名 |\n  | NEZHA_PORT   | 否 |        | 哪吒探针服务端的端口 |\n  | NEZHA_KEY    | 否 |        | 哪吒探针客户端专用 Key |\n  | NEZHA_TLS    | 否 |        | 哪吒探针是否启用 SSL/TLS 加密 ，如不启用请删除，如要启用填\"1\" |\n  | ARGO_AUTH    | 否 |        | Argo 的 Token 或者 json 值，其中 json 可以通过以下网站，在不需绑卡的情况下轻松获取: https://fscarmen.cloudflare.now.cc/ |\n  | ARGO_DOMAIN  | 否 |        | Argo 的域名，须与 ARGO_DOMAIN 必需一起填了才能生效 |\n  | SSH_DOMAIN   | 否 |        | webssh 的域名，用户名和密码就是 <WEB_USERNAME> 和 <WEB_PASSWORD> |\n  | FTP_DOMAIN   | 否 |        | webftp 的域名，用户名和密码就是 <WEB_USERNAME> 和 <WEB_PASSWORD> |  \n\n\n<img width=\"1301\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/226095672-ecbfc8e7-80f3-4821-abb4-df75c4ece483.png\">\n\n* 需要应用的 js\n  | 命令 | 说明 |\n  | ---- |------ |\n  | <URL>/list   | 查看节点数据 |\n  | <URL>/status | 查看后台进程 |\n  | <URL>/listen | 查看后台监听端口 |\n  | <URL>/test\t | 测试是否为只读系统 |\n\n## 在 Glitch 部署重点\n\n这里只作重点的展示，更详细可以参考项目: https://github.com/fscarmen2/X-for-Glitch\n\n<img width=\"1105\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/214567653-768f4f91-13b5-4205-9118-f5510081e260.png\">\n\n<img width=\"1680\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/215279773-d550494e-647b-42e8-b5dc-5d32679fbf9e.jpg\">\n\n<img width=\"322\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/214568380-b07dd83b-a4d6-43fe-9ead-79f1393e909c.png\">\n\n## 在 Daki 部署重点\n\n<img width=\"1198\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/212642015-e84e07de-9f07-466d-b446-8cd8431e7220.png\">\n\n<img width=\"1198\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/212642096-dfcce6d1-d6b2-4b55-9b94-995e5561ac44.png\">\n\n<img width=\"1198\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/212642206-6b12179d-b35a-4a1e-b4e1-963b537c7693.png\">\n\n<img width=\"1547\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/214568691-54cc283b-614f-4fe7-8782-c48ed46cff31.png\">\n\n<img width=\"1664\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/214580345-765231a7-ec63-4564-a188-ceae28308258.png\">\n\n<img width=\"1137\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/215279783-0400e80e-83be-4142-8592-2385c54e36e6.jpg\">\n\n<img width=\"322\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/214580604-8d4f6454-3b78-41a9-b765-cff714b85638.png\">\n\n## ttyd webssh / filebrowser webftp 的部署\n\n* 原理\n```\n+---------+     argo     +---------+     http     +--------+    ssh    +-----------+\n| browser | <==========> | CF edge | <==========> |  ttyd  | <=======> | ssh server|\n+---------+     argo     +---------+   websocket  +--------+    ssh    +-----------+\n\n+---------+     argo     +---------+     http     +--------------+    ftp    +-----------+\n| browser | <==========> | CF edge | <==========> | filebrowser  | <=======> | ftp server|\n+---------+     argo     +---------+   websocket  +--------------+    ftp    +-----------+\n\n```\n\n* 使用 Json 方式建的隧道\n  \n<img width=\"1643\" alt=\"image\" src=\"https://user-images.githubusercontent.com/92626977/235453084-a8c55417-18b4-4a47-9eef-ee3053564bff.png\">\n\n<img width=\"1303\" alt=\"image\" src=\"https://github.com/fscarmen2/aa/assets/92626977/652ef3ff-c9a9-4771-92c8-bab6c516abeb\">\n\n<img width=\"1001\" alt=\"image\" src=\"https://github.com/fscarmen2/aa/assets/92626977/5b5e0143-ba5a-4b6a-a7fd-e77ef9d0703e\">\n\n<img width=\"1527\" alt=\"image\" src=\"https://github.com/fscarmen2/rrr/assets/92626977/91cece0d-cc61-4681-8eae-03f961a16976\">\n\n## 鸣谢下列作者的文章和项目:\n* 前端 JS 在大佬 Nike Jeff 的项目 基础上，为了通用性和扩展功能作修改，https://github.com/hrzyang/glitch-trojan\n* 后端全部原创，如转载须注明来源。\n\n## 免责声明:\n* 本程序仅供学习了解, 非盈利目的，请于下载后 24 小时内删除, 不得用作任何商业用途, 文字、数据及图片均有所属版权, 如转载须注明来源。\n* 使用本程序必循遵守部署免责声明。使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责。"
  },
  {
    "path": "entrypoint.sh",
    "content": "#!/usr/bin/env bash\n\n# 设置各变量，WS 路径前缀。(注意:伪装路径不需要 / 符号开始,为避免不必要的麻烦,请不要使用特殊符号.)\nWSPATH=${WSPATH:-'argo'}\nUUID=${UUID:-'de04add9-5c68-8bab-950c-08cd5320df18'}\nWEB_USERNAME=${WEB_USERNAME:-'admin'}\nWEB_PASSWORD=${WEB_PASSWORD:-'password'}\n\n# 哪吒4个参数，ssl/tls 看是否需要，不需要的话可以留空，删除或在这4行最前面加 # 以注释\nNEZHA_SERVER=\"$NEZHA_SERVER\"\nNEZHA_PORT=\"$NEZHA_PORT\"\nNEZHA_KEY=\"$NEZHA_KEY\"\nNEZHA_TLS=\"$NEZHA_TLS\"\n\n# Argo 固定域名隧道的两个参数,这个可以填 Json 内容或 Token 内容，不需要的话可以留空，删除或在这三行最前面加 # 以注释\nARGO_AUTH=''\nARGO_DOMAIN=\"$ARGO_DOMAIN\"\n\n# ttyd / filebrowser argo 域名\nSSH_DOMAIN=\"$SSH_AUTH\"\nFTP_DOMAIN=\"$FTP_AUTH\"\n\n# 安装系统依赖\ncheck_dependencies() {\n  DEPS_CHECK=(\"wget\" \"unzip\" \"ss\" \"tar\")\n  DEPS_INSTALL=(\" wget\" \" unzip\" \" iproute2\" \"tar\")\n  for ((i=0;i<${#DEPS_CHECK[@]};i++)); do [[ ! $(type -p ${DEPS_CHECK[i]}) ]] && DEPS+=${DEPS_INSTALL[i]}; done\n  [ -n \"$DEPS\" ] && { apt-get update >/dev/null 2>&1; apt-get install -y $DEPS >/dev/null 2>&1; }\n}\n\n# 生成 X 配置文件\ngenerate_config() {\n  cat > config.json << EOF\n{\n    \"log\":{\n        \"access\":\"/dev/null\",\n        \"error\":\"/dev/null\",\n        \"loglevel\":\"none\"\n    },\n    \"inbounds\":[\n        {\n            \"port\":8080,\n            \"protocol\":\"vless\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"id\":\"${UUID}\",\n                        \"flow\":\"xtls-rprx-vision\"\n                    }\n                ],\n                \"decryption\":\"none\",\n                \"fallbacks\":[\n                    {\n                        \"dest\":3001\n                    },\n                    {\n                        \"path\":\"/${WSPATH}-vless\",\n                        \"dest\":3002\n                    },\n                    {\n                        \"path\":\"/${WSPATH}-vmess\",\n                        \"dest\":3003\n                    },\n                    {\n                        \"path\":\"/${WSPATH}-trojan\",\n                        \"dest\":3004\n                    },\n                    {\n                        \"path\":\"/${WSPATH}-shadowsocks\",\n                        \"dest\":3005\n                    }\n                ]\n            },\n            \"streamSettings\":{\n                \"network\":\"tcp\"\n            }\n        },\n        {\n            \"port\":3001,\n            \"listen\":\"127.0.0.1\",\n            \"protocol\":\"vless\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"id\":\"${UUID}\"\n                    }\n                ],\n                \"decryption\":\"none\"\n            },\n            \"streamSettings\":{\n                \"network\":\"ws\",\n                \"security\":\"none\"\n            }\n        },\n        {\n            \"port\":3002,\n            \"listen\":\"127.0.0.1\",\n            \"protocol\":\"vless\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"id\":\"${UUID}\",\n                        \"level\":0\n                    }\n                ],\n                \"decryption\":\"none\"\n            },\n            \"streamSettings\":{\n                \"network\":\"ws\",\n                \"security\":\"none\",\n                \"wsSettings\":{\n                    \"path\":\"/${WSPATH}-vless\"\n                }\n            },\n            \"sniffing\":{\n                \"enabled\":true,\n                \"destOverride\":[\n                    \"http\",\n                    \"tls\",\n                    \"quic\"\n                ],\n                \"metadataOnly\":false\n            }\n        },\n        {\n            \"port\":3003,\n            \"listen\":\"127.0.0.1\",\n            \"protocol\":\"vmess\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"id\":\"${UUID}\",\n                        \"alterId\":0\n                    }\n                ]\n            },\n            \"streamSettings\":{\n                \"network\":\"ws\",\n                \"wsSettings\":{\n                    \"path\":\"/${WSPATH}-vmess\"\n                }\n            },\n            \"sniffing\":{\n                \"enabled\":true,\n                \"destOverride\":[\n                    \"http\",\n                    \"tls\",\n                    \"quic\"\n                ],\n                \"metadataOnly\":false\n            }\n        },\n        {\n            \"port\":3004,\n            \"listen\":\"127.0.0.1\",\n            \"protocol\":\"trojan\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"password\":\"${UUID}\"\n                    }\n                ]\n            },\n            \"streamSettings\":{\n                \"network\":\"ws\",\n                \"security\":\"none\",\n                \"wsSettings\":{\n                    \"path\":\"/${WSPATH}-trojan\"\n                }\n            },\n            \"sniffing\":{\n                \"enabled\":true,\n                \"destOverride\":[\n                    \"http\",\n                    \"tls\",\n                    \"quic\"\n                ],\n                \"metadataOnly\":false\n            }\n        },\n        {\n            \"port\":3005,\n            \"listen\":\"127.0.0.1\",\n            \"protocol\":\"shadowsocks\",\n            \"settings\":{\n                \"clients\":[\n                    {\n                        \"method\":\"chacha20-ietf-poly1305\",\n                        \"password\":\"${UUID}\"\n                    }\n                ],\n                \"decryption\":\"none\"\n            },\n            \"streamSettings\":{\n                \"network\":\"ws\",\n                \"wsSettings\":{\n                    \"path\":\"/${WSPATH}-shadowsocks\"\n                }\n            },\n            \"sniffing\":{\n                \"enabled\":true,\n                \"destOverride\":[\n                    \"http\",\n                    \"tls\",\n                    \"quic\"\n                ],\n                \"metadataOnly\":false\n            }\n        }\n    ],\n    \"dns\":{\n        \"servers\":[\n            \"https+local://8.8.8.8/dns-query\"\n        ]\n    },\n    \"outbounds\":[\n        {\n            \"protocol\":\"freedom\"\n        },\n        {\n            \"tag\":\"WARP\",\n            \"protocol\":\"wireguard\",\n            \"settings\":{\n                \"secretKey\":\"YFYOAdbw1bKTHlNNi+aEjBM3BO7unuFC5rOkMRAz9XY=\",\n                \"address\":[\n                    \"172.16.0.2/32\",\n                    \"2606:4700:110:8a36:df92:102a:9602:fa18/128\"\n                ],\n                \"peers\":[\n                    {\n                        \"publicKey\":\"bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=\",\n                        \"allowedIPs\":[\n                            \"0.0.0.0/0\",\n                            \"::/0\"\n                        ],\n                        \"endpoint\":\"162.159.193.10:2408\"\n                    }\n                ],\n                \"reserved\":[78, 135, 76],\n                \"mtu\":1280\n            }\n        }\n    ],\n    \"routing\":{\n        \"domainStrategy\":\"AsIs\",\n        \"rules\":[\n            {\n                \"type\":\"field\",\n                \"domain\":[\n                    \"domain:openai.com\",\n                    \"domain:ai.com\"\n                ],\n                \"outboundTag\":\"WARP\"\n            }\n        ]\n    }\n}\nEOF\n}\n\ngenerate_argo() {\n  cat > argo.sh << ABC\n#!/usr/bin/env bash\n\nARGO_AUTH=${ARGO_AUTH}\nARGO_DOMAIN=${ARGO_DOMAIN}\nSSH_DOMAIN=${SSH_DOMAIN}\nFTP_DOMAIN=${FTP_DOMAIN}\n\n# 下载并运行 Argo\ncheck_file() {\n  [ ! -e cloudflared ] && wget -O cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 && chmod +x cloudflared\n}\n\nrun() {\n  if [[ -n \"\\${ARGO_AUTH}\" && -n \"\\${ARGO_DOMAIN}\" ]]; then\n    if [[ \"\\$ARGO_AUTH\" =~ TunnelSecret ]]; then\n      echo \"\\$ARGO_AUTH\" | sed 's@{@{\"@g;s@[,:]@\"\\0\"@g;s@}@\"}@g' > tunnel.json\n      cat > tunnel.yml << EOF\ntunnel: \\$(sed \"s@.*TunnelID:\\(.*\\)}@\\1@g\" <<< \"\\$ARGO_AUTH\")\ncredentials-file: $(pwd)/tunnel.json\nprotocol: http2\n\ningress:\n  - hostname: \\$ARGO_DOMAIN\n    service: http://localhost:8080\nEOF\n      [ -n \"\\${SSH_DOMAIN}\" ] && cat >> tunnel.yml << EOF\n  - hostname: \\$SSH_DOMAIN\n    service: http://localhost:2222\nEOF\n      [ -n \"\\${FTP_DOMAIN}\" ] && cat >> tunnel.yml << EOF\n  - hostname: \\$FTP_DOMAIN\n    service: http://localhost:3333\nEOF\n      cat >> tunnel.yml << EOF\n  - service: http_status:404\nEOF\n      nohup ./cloudflared tunnel --edge-ip-version auto --config tunnel.yml run 2>/dev/null 2>&1 &\n    elif [[ \\$ARGO_AUTH =~ ^[A-Z0-9a-z=]{120,250}$ ]]; then\n      nohup ./cloudflared tunnel --edge-ip-version auto --protocol http2 run --token ${ARGO_AUTH} 2>/dev/null 2>&1 &\n    fi\n  else\n    nohup ./cloudflared tunnel --edge-ip-version auto --protocol http2 --no-autoupdate --url http://localhost:8080 2>/dev/null 2>&1 &\n    sleep 5\n    local LOCALHOST=\\$(ss -nltp | grep '\"cloudflared\"' | awk '{print \\$4}')\n    ARGO_DOMAIN=\\$(wget -qO- http://\\$LOCALHOST/quicktunnel | cut -d\\\" -f4)\n  fi\n}\n\nexport_list() {\n  VMESS=\"{ \\\"v\\\": \\\"2\\\", \\\"ps\\\": \\\"Argo-Vmess\\\", \\\"add\\\": \\\"icook.hk\\\", \\\"port\\\": \\\"443\\\", \\\"id\\\": \\\"${UUID}\\\", \\\"aid\\\": \\\"0\\\", \\\"scy\\\": \\\"none\\\", \\\"net\\\": \\\"ws\\\", \\\"type\\\": \\\"none\\\", \\\"host\\\": \\\"\\${ARGO_DOMAIN}\\\", \\\"path\\\": \\\"/${WSPATH}-vmess?ed=2048\\\", \\\"tls\\\": \\\"tls\\\", \\\"sni\\\": \\\"\\${ARGO_DOMAIN}\\\", \\\"alpn\\\": \\\"\\\" }\"\n  cat > list << EOF\n*******************************************\nV2-rayN:\n----------------------------\nvless://${UUID}@icook.hk:443?encryption=none&security=tls&sni=\\${ARGO_DOMAIN}&type=ws&host=\\${ARGO_DOMAIN}&path=%2F${WSPATH}-vless?ed=2048#Argo-Vless\n----------------------------\nvmess://\\$(echo \\$VMESS | base64 -w0)\n----------------------------\ntrojan://${UUID}@icook.hk:443?security=tls&sni=\\${ARGO_DOMAIN}&type=ws&host=\\${ARGO_DOMAIN}&path=%2F${WSPATH}-trojan?ed=2048#Argo-Trojan\n----------------------------\nss://$(echo \"chacha20-ietf-poly1305:${UUID}@icook.hk:443\" | base64 -w0)@icook.hk:443#Argo-Shadowsocks\n由于该软件导出的链接不全，请自行处理如下: 传输协议: WS ， 伪装域名: \\${ARGO_DOMAIN} ，路径: /${WSPATH}-shadowsocks?ed=2048 ， 传输层安全: tls ， sni: \\${ARGO_DOMAIN}\n*******************************************\n小火箭:\n----------------------------\nvless://${UUID}@icook.hk:443?encryption=none&security=tls&type=ws&host=\\${ARGO_DOMAIN}&path=/${WSPATH}-vless?ed=2048&sni=\\${ARGO_DOMAIN}#Argo-Vless\n----------------------------\nvmess://$(echo \"none:${UUID}@icook.hk:443\" | base64 -w0)?remarks=Argo-Vmess&obfsParam=\\${ARGO_DOMAIN}&path=/${WSPATH}-vmess?ed=2048&obfs=websocket&tls=1&peer=\\${ARGO_DOMAIN}&alterId=0\n----------------------------\ntrojan://${UUID}@icook.hk:443?peer=\\${ARGO_DOMAIN}&plugin=obfs-local;obfs=websocket;obfs-host=\\${ARGO_DOMAIN};obfs-uri=/${WSPATH}-trojan?ed=2048#Argo-Trojan\n----------------------------\nss://$(echo \"chacha20-ietf-poly1305:${UUID}@icook.hk:443\" | base64 -w0)?obfs=wss&obfsParam=\\${ARGO_DOMAIN}&path=/${WSPATH}-shadowsocks?ed=2048#Argo-Shadowsocks\n*******************************************\nClash:\n----------------------------\n- {name: Argo-Vless, type: vless, server: icook.hk, port: 443, uuid: ${UUID}, tls: true, servername: \\${ARGO_DOMAIN}, skip-cert-verify: false, network: ws, ws-opts: {path: /${WSPATH}-vless?ed=2048, headers: { Host: \\${ARGO_DOMAIN}}}, udp: true}\n----------------------------\n- {name: Argo-Vmess, type: vmess, server: icook.hk, port: 443, uuid: ${UUID}, alterId: 0, cipher: none, tls: true, skip-cert-verify: true, network: ws, ws-opts: {path: /${WSPATH}-vmess?ed=2048, headers: {Host: \\${ARGO_DOMAIN}}}, udp: true}\n----------------------------\n- {name: Argo-Trojan, type: trojan, server: icook.hk, port: 443, password: ${UUID}, udp: true, tls: true, sni: \\${ARGO_DOMAIN}, skip-cert-verify: false, network: ws, ws-opts: { path: /${WSPATH}-trojan?ed=2048, headers: { Host: \\${ARGO_DOMAIN} } } }\n----------------------------\n- {name: Argo-Shadowsocks, type: ss, server: icook.hk, port: 443, cipher: chacha20-ietf-poly1305, password: ${UUID}, plugin: v2ray-plugin, plugin-opts: { mode: websocket, host: \\${ARGO_DOMAIN}, path: /${WSPATH}-shadowsocks?ed=2048, tls: true, skip-cert-verify: false, mux: false } }\n*******************************************\nEOF\n  cat list\n}\ncheck_file\nrun\nexport_list\nABC\n}\n\ngenerate_nezha() {\n  cat > nezha.sh << EOF\n#!/usr/bin/env bash\n\n# 哪吒的4个参数\nNEZHA_SERVER=\"$NEZHA_SERVER\"\nNEZHA_PORT=\"$NEZHA_PORT\"\nNEZHA_KEY=\"$NEZHA_KEY\"\nNEZHA_TLS=\"$NEZHA_TLS\"\n\n# 检测是否已运行\ncheck_run() {\n  [[ \\$(pgrep -laf nezha-agent) ]] && echo \"哪吒客户端正在运行中!\" && exit\n}\n\n# 三个变量不全则不安装哪吒客户端\ncheck_variable() {\n  [[ -z \"\\${NEZHA_SERVER}\" || -z \"\\${NEZHA_PORT}\" || -z \"\\${NEZHA_KEY}\" ]] && exit\n}\n\n# 下载最新版本 Nezha Agent\ndownload_agent() {\n  if [ ! -e nezha-agent ]; then\n    URL=\\$(wget -qO- -4 \"https://api.github.com/repos/nezhahq/agent/releases/latest\" | grep -o \"https.*linux_amd64.zip\")\n    URL=\\${URL:-https://github.com/nezhahq/agent/releases/download/v0.15.6/nezha-agent_linux_amd64.zip}\n    wget -t 2 -T 10 -N \\${URL}\n    unzip -qod ./ nezha-agent_linux_amd64.zip && rm -f nezha-agent_linux_amd64.zip\n  fi\n}\n\n# 运行客户端\nrun() {\n  TLS=\\${NEZHA_TLS:+'--tls'}\n  [[ ! \\$PROCESS =~ nezha-agent && -e nezha-agent ]] && ./nezha-agent -s \\${NEZHA_SERVER}:\\${NEZHA_PORT} -p \\${NEZHA_KEY} \\${TLS} 2>&1 &\n}\n\ncheck_run\ncheck_variable\ndownload_agent\nrun\nEOF\n}\n\ngenerate_ttyd() {\n  cat > ttyd.sh << EOF\n#!/usr/bin/env bash\n\n# ttyd 三个参数\nWEB_USERNAME=${WEB_USERNAME}\nWEB_PASSWORD=${WEB_PASSWORD}\nSSH_DOMAIN=${SSH_DOMAIN}\n\n# 检测是否已运行\ncheck_run() {\n  [[ \\$(pgrep -lafx ttyd) ]] && echo \"ttyd 正在运行中\" && exit\n}\n\n# ssh argo 域名不设置，则不安装 ttyd 服务端\ncheck_variable() {\n  [ -z \"\\${SSH_DOMAIN}\" ] && exit\n}\n\n# 下载最新版本 ttyd\ndownload_ttyd() {\n  if [ ! -e ttyd ]; then\n    URL=\\$(wget -qO- \"https://api.github.com/repos/tsl0922/ttyd/releases/latest\" | grep -o \"https.*x86_64\")\n    URL=\\${URL:-https://github.com/tsl0922/ttyd/releases/download/1.7.3/ttyd.x86_64}\n    wget -O ttyd \\${URL}\n    chmod +x ttyd\n  fi\n}\n\n# 运行 ttyd 服务端\nrun() {\n  [ -e ttyd ] && nohup ./ttyd -c \\${WEB_USERNAME}:\\${WEB_PASSWORD} -p 2222 bash >/dev/null 2>&1 &\n}\n\ncheck_run\ncheck_variable\ndownload_ttyd\nrun\nEOF\n}\n\ngenerate_filebrowser () {\n  cat > filebrowser.sh << EOF\n#!/usr/bin/env bash\n\n# filebrowser 三个参数\nWEB_USERNAME=${WEB_USERNAME}\nWEB_PASSWORD=${WEB_PASSWORD}\nFTP_DOMAIN=${FTP_DOMAIN}\n\n# 检测是否已运行\ncheck_run() {\n  [[ \\$(pgrep -lafx filebrowser) ]] && echo \"filebrowser 正在运行中\" && exit\n}\n\n# 若 ftp argo 域名不设置，则不安装 filebrowser\ncheck_variable() {\n  [ -z \"\\${FTP_DOMAIN}\" ] && exit\n}\n\n# 下载最新版本 filebrowser\ndownload_filebrowser() {\n  if [ ! -e filebrowser ]; then\n    URL=\\$(wget -qO- \"https://api.github.com/repos/filebrowser/filebrowser/releases/latest\" | grep -o \"https.*linux-amd64.*gz\")\n    URL=\\${URL:-https://github.com/filebrowser/filebrowser/releases/download/v2.23.0/linux-amd64-filebrowser.tar.gz}\n    wget -O filebrowser.tar.gz \\${URL}\n    tar xzvf filebrowser.tar.gz filebrowser\n    rm -f filebrowser.tar.gz\n    chmod +x filebrowser\n  fi\n}\n\n# 运行 filebrowser 服务端\nrun() {\n  PASSWORD_HASH=\\$(./filebrowser hash \\$WEB_PASSWORD)\n  [ -e filebrowser ] && nohup ./filebrowser --port 3333 --username \\${WEB_USERNAME} --password \"\\${PASSWORD_HASH}\" >/dev/null 2>&1 &\n}\n\ncheck_run\ncheck_variable\ndownload_filebrowser\nrun\nEOF\n}\n\ngenerate_config\ngenerate_argo\ngenerate_nezha\ngenerate_ttyd\ngenerate_filebrowser\n\n[ -e nezha.sh ] && bash nezha.sh\n[ -e argo.sh ] && bash argo.sh\n[ -e ttyd.sh ] && bash ttyd.sh\n[ -e filebrowser.sh ] && bash filebrowser.sh\n"
  },
  {
    "path": "index.js",
    "content": "const username = process.env.WEB_USERNAME || \"admin\";\r\nconst password = process.env.WEB_PASSWORD || \"password\";\r\nconst url = \"http://127.0.0.1\";\r\nconst port = process.env.PORT || 3000; /* 当容器平台分配不规则端口时,此处需修改为分配端口 */\r\nconst express = require(\"express\");\r\nconst app = express();\r\nvar exec = require(\"child_process\").exec;\r\nconst os = require(\"os\");\r\nconst { legacyCreateProxyMiddleware } = require(\"http-proxy-middleware\");\r\nvar request = require(\"request\");\r\nvar fs = require(\"fs\");\r\nvar path = require(\"path\");\r\nconst auth = require(\"basic-auth\");\r\n\r\napp.get(\"/\", function (req, res) {\r\n  res.send(\"hello world\");\r\n});\r\n\r\n// 页面访问密码\r\napp.use((req, res, next) => {\r\n  const user = auth(req);\r\n  if (user && user.name === username && user.pass === password) {\r\n    return next();\r\n  }\r\n  res.set(\"WWW-Authenticate\", 'Basic realm=\"Node\"');\r\n  return res.status(401).send();\r\n});\r\n\r\n//获取系统进程表\r\napp.get(\"/status\", function (req, res) {\r\n  let cmdStr = \"ps -ef\";\r\n  exec(cmdStr, function (err, stdout, stderr) {\r\n    if (err) {\r\n      res.type(\"html\").send(\"<pre>命令行执行错误：\\n\" + err + \"</pre>\");\r\n    }\r\n    else {\r\n      res.type(\"html\").send(\"<pre>获取系统进程表：\\n\" + stdout + \"</pre>\");\r\n    }\r\n  });\r\n});\r\n\r\n//获取系统监听端口\r\napp.get(\"/listen\", function (req, res) {\r\n    let cmdStr = \"ss -nltp\";\r\n    exec(cmdStr, function (err, stdout, stderr) {\r\n      if (err) {\r\n        res.type(\"html\").send(\"<pre>命令行执行错误：\\n\" + err + \"</pre>\");\r\n      }\r\n      else {\r\n        res.type(\"html\").send(\"<pre>获取系统监听端口：\\n\" + stdout + \"</pre>\");\r\n      }\r\n    });\r\n  });\r\n\r\n\r\n//获取节点数据\r\napp.get(\"/list\", function (req, res) {\r\n    let cmdStr = \"cat list\";\r\n    exec(cmdStr, function (err, stdout, stderr) {\r\n      if (err) {\r\n        res.type(\"html\").send(\"<pre>命令行执行错误：\\n\" + err + \"</pre>\");\r\n      }\r\n      else {\r\n        res.type(\"html\").send(\"<pre>节点数据：\\n\\n\" + stdout + \"</pre>\");\r\n      }\r\n    });\r\n  });\r\n\r\n//获取系统版本、内存信息\r\napp.get(\"/info\", function (req, res) {\r\n  let cmdStr = \"cat /etc/*release | grep -E ^NAME\";\r\n  exec(cmdStr, function (err, stdout, stderr) {\r\n    if (err) {\r\n      res.send(\"命令行执行错误：\" + err);\r\n    }\r\n    else {\r\n      res.send(\r\n        \"命令行执行结果：\\n\" +\r\n          \"Linux System:\" +\r\n          stdout +\r\n          \"\\nRAM:\" +\r\n          os.totalmem() / 1000 / 1000 +\r\n          \"MB\"\r\n      );\r\n    }\r\n  });\r\n});\r\n\r\n//文件系统只读测试\r\napp.get(\"/test\", function (req, res) {\r\n  let cmdStr = 'mount | grep \" / \" | grep \"(ro,\" >/dev/null';\r\n  exec(cmdStr, function (error, stdout, stderr) {\r\n    if (error !== null) {\r\n      res.send(\"系统权限为---非只读\");\r\n    } else {\r\n      res.send(\"系统权限为---只读\");\r\n    }\r\n  });\r\n});\r\n\r\n// keepalive begin\r\n//web保活\r\nfunction keep_web_alive() {\r\n  // 1.请求主页，保持唤醒\r\n  exec(\"curl -m8 \" + url + \":\" + port, function (err, stdout, stderr) {\r\n    if (err) {\r\n      console.log(\"保活-请求主页-命令行执行错误：\" + err);\r\n    } else {\r\n      console.log(\"保活-请求主页-命令行执行成功，响应报文:\" + stdout);\r\n    }\r\n  });\r\n\r\n  // 2.请求服务器进程状态列表，若web没在运行，则调起\r\n  exec(\"pgrep -laf web.js\", function (err, stdout, stderr) {\r\n    // 1.查后台系统进程，保持唤醒\r\n    if (stdout.includes(\"./web.js -c ./config.json\")) {\r\n      console.log(\"web 正在运行\");\r\n    }\r\n    else {\r\n      //web 未运行，命令行调起\r\n      exec(\r\n        \"chmod +x web.js && ./web.js -c ./config.json >/dev/null 2>&1 &\", function (err, stdout, stderr) {\r\n          if (err) {\r\n            console.log(\"保活-调起web-命令行执行错误:\" + err);\r\n          }\r\n          else {\r\n            console.log(\"保活-调起web-命令行执行成功!\");\r\n          }\r\n        }\r\n      );\r\n    }\r\n  });\r\n}\r\nsetInterval(keep_web_alive, 10 * 1000);\r\n\r\n//Argo保活\r\nfunction keep_argo_alive() {\r\n  exec(\"pgrep -laf cloudflared\", function (err, stdout, stderr) {\r\n    // 1.查后台系统进程，保持唤醒\r\n    if (stdout.includes(\"./cloudflared\")) {\r\n      console.log(\"Argo 正在运行\");\r\n    }\r\n    else {\r\n      //Argo 未运行，命令行调起\r\n      exec(\r\n        \"bash argo.sh 2>&1 &\", function (err, stdout, stderr) {\r\n          if (err) {\r\n            console.log(\"保活-调起Argo-命令行执行错误:\" + err);\r\n          }\r\n          else {\r\n            console.log(\"保活-调起Argo-命令行执行成功!\");\r\n          }\r\n        }\r\n      );\r\n    }\r\n  });\r\n}\r\nsetInterval(keep_argo_alive, 30 * 1000);\r\n\r\n//哪吒保活\r\nfunction keep_nezha_alive() {\r\n  exec(\"pgrep -laf nezha-agent\", function (err, stdout, stderr) {\r\n    // 1.查后台系统进程，保持唤醒\r\n    if (stdout.includes(\"./nezha-agent\")) {\r\n      console.log(\"哪吒正在运行\");\r\n    }\r\n    else {\r\n      //哪吒未运行，命令行调起\r\n      exec(\r\n        \"bash nezha.sh 2>&1 &\", function (err, stdout, stderr) {\r\n          if (err) {\r\n            console.log(\"保活-调起哪吒-命令行执行错误:\" + err);\r\n          }\r\n          else {\r\n            console.log(\"保活-调起哪吒-命令行执行成功!\");\r\n          }\r\n        }\r\n      );\r\n    }\r\n  });\r\n}\r\nsetInterval(keep_nezha_alive, 45 * 1000);\r\n// keepalive end\r\n\r\n//下载web可执行文件\r\napp.get(\"/download\", function (req, res) {\r\n  download_web((err) => {\r\n    if (err) {\r\n      res.send(\"下载文件失败\");\r\n    }\r\n    else {\r\n      res.send(\"下载文件成功\");\r\n    }\r\n  });\r\n});\r\n\r\n\r\napp.use( /* 具体配置项迁移参见 https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md */\r\n  legacyCreateProxyMiddleware({\r\n    target: 'http://127.0.0.1:8080/', /* 需要跨域处理的请求地址 */\r\n    ws: true, /* 是否代理websocket */\r\n    changeOrigin: true, /* 是否需要改变原始主机头为目标URL,默认false */ \r\n    on: {  /* http代理事件集 */ \r\n      proxyRes: function proxyRes(proxyRes, req, res) { /* 处理代理请求 */\r\n        // console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2)); //for debug\r\n        // console.log(req) //for debug\r\n        // console.log(res) //for debug\r\n      },\r\n      proxyReq: function proxyReq(proxyReq, req, res) { /* 处理代理响应 */\r\n        // console.log(proxyReq); //for debug\r\n        // console.log(req) //for debug\r\n        // console.log(res) //for debug\r\n      },\r\n      error: function error(err, req, res) { /* 处理异常  */\r\n        console.warn('websocket error.', err);\r\n      }\r\n    },\r\n    pathRewrite: {\r\n      '^/': '/', /* 去除请求中的斜线号  */\r\n    },\r\n    // logger: console /* 是否打开log日志  */\r\n  })\r\n);\r\n\r\n//初始化，下载web\r\nfunction download_web(callback) {\r\n  let fileName = \"web.js\";\r\n  let web_url =\r\n    \"https://github.com/fscarmen2/Argo-X-Container-PaaS/raw/main/files/web.js\";\r\n  let stream = fs.createWriteStream(path.join(\"./\", fileName));\r\n  request(web_url)\r\n    .pipe(stream)\r\n    .on(\"close\", function (err) {\r\n      if (err) {\r\n        callback(\"下载文件失败\");\r\n      }\r\n      else {\r\n        callback(null);\r\n      }\r\n    });\r\n}\r\n\r\ndownload_web((err) => {\r\n  if (err) {\r\n    console.log(\"初始化-下载web文件失败\");\r\n  }\r\n  else {\r\n    console.log(\"初始化-下载web文件成功\");\r\n  }\r\n});\r\n\r\n//启动核心脚本运行web,哪吒和argo\r\nexec(\"bash entrypoint.sh\", function (err, stdout, stderr) {\r\n  if (err) {\r\n    console.error(err);\r\n    return;\r\n  }\r\n  console.log(stdout);\r\n});\r\n\r\nconsole.log(`Username is: ${username}`);\r\nconsole.log(`Password is: ${password}`);\r\n\r\napp.listen(port, () => console.log(`Example app listening on port ${port}!`));\r\n"
  },
  {
    "path": "package.json",
    "content": "{\r\n  \"name\": \"express-hello-world\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"Express Hello World\",\r\n  \"main\": \"index.js\",\r\n  \"repository\": \"https://github.com/fscarmen2/Argo-X-JS-PaaS\",\r\n  \"author\": \"fscarmen\",\r\n  \"license\": \"MIT\",\r\n  \"private\": false,\r\n  \"scripts\": {\r\n    \"start\": \"node index.js\"\r\n  },\r\n  \"dependencies\": {\r\n    \"express\": \"^4.18.2\",\r\n    \"http-proxy-middleware\": \"^3.0.0-beta.0\",\r\n    \"request\": \"^2.88.2\",\r\n    \"basic-auth\": \"^2.0.1\"\r\n  },\r\n  \"engines\": {\r\n    \"node\": \">=14\"\r\n  }\r\n}"
  }
]