diff --git a/.gitignore b/.gitignore index 375383a..c1d3d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,18 @@ test_mirror_*/ #混淆脚本 obfuscate_pages.py obfuscate_config.json +advanced_obfuscate.js +build_dynamic.js +dynamic_key_workers.js +test_decrypt.js +chinese_to_english.py +workers_en.js + +# 其他JS文件(只保留workers相关) +*.js +!workers.js +!破皮版workers.js +!vless_workers.js #示例文件 examples/ diff --git a/README.md b/README.md index c6a9197..665e94e 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,10 @@ | 特性 | 说明 | |------|------| | **伪装首页** | 根路径 `/` 显示 Nginx 默认欢迎页面,伪装成普通 HTTP 服务器 | -| **隐藏入口** | 真实 API 需访问 `/x2727admin` 路径 | +| **隐藏入口** | 真实 API 需访问 `/x2727admin` 路径获取访问密钥 | +| **双重验证** | 需先获取密钥,再访问 `/x2727admin/{密钥}` 获取数据 | | **X27CN v2 加密** | 所有 API 响应使用动态密钥加密,需专用工具解密 | -| **代码混淆** | 全部代码压缩为单行,移除所有中文字符,密钥动态生成 | +| **代码混淆** | 全部代码压缩为单行,移除所有中文字符 | | **降低特征** | 移除部分可被检测的关键词和特征模式(但无法完全消除风险) | **快速使用:** @@ -43,10 +44,21 @@ # 1. 使用小号登录 Cloudflare # 2. 部署 破皮版workers.js 到 Workers # 3. 访问根路径会看到 Nginx 伪装页面 -# 4. 访问 /x2727admin 获取加密配置 -# 5. 使用解密工具获取 VLESS 链接 +# 4. 访问 /x2727admin 获取加密的密钥提示 +# 5. 使用解密工具(密钥: x27cn2026)解密获得访问密钥 +# 6. 访问 /x2727admin/{你的密钥} 获取加密的配置 +# 7. 使用解密工具(密钥: 你的密钥)解密获取 VLESS 链接 ``` +**自定义访问密钥:** + +可以通过环境变量设置自定义访问密钥: +- `ACCESSKEY` - 自定义访问密钥(优先级最高) +- `ACCESS_KEY` - 自定义访问密钥(备选) +- `AKEY` - 自定义访问密钥(简写) + +如果不设置环境变量,系统会基于你的 UUID/密码自动生成唯一的8位密钥。 + **解密工具:** [https://x27cn.cfspider.com](https://x27cn.cfspider.com) --- diff --git a/破皮版workers.js b/破皮版workers.js index 8b4653c..edf5675 100644 --- a/破皮版workers.js +++ b/破皮版workers.js @@ -1 +1 @@ -import{connect as e}from"cloudflare:sockets";var _p1=[0x78,0x32,0x37],_p2=[0x63,0x6e],_p3=[0x32,0x30,0x32,0x36],_s1=function(_a){return _a.map(x=>(x^0x5a)^0x5a)},_s2=function(_a,_b){var _c=[];for(var _d=0;_d<_a.length;_d++)_c.push(_a[_d]^(_b[_d%_b.length]||0));return _c},_kf=function(){return String.fromCharCode(..._s1(_p1).concat(_s2(_p2,[0])).concat(_s1(_p3)))};function s(e,t=_kf()){if(!e)return"";const n=(new TextEncoder).encode(t),k=new Uint8Array(256),b=new Uint8Array(256);for(let i=0;i<256;i++)k[i]=n[i%n.length]^(7*i+13)&255,b[i]=167*i+89&255;const r=(new TextEncoder).encode(e),o=new Uint8Array(r.length);let c=n.reduce((a,v)=>a^v,0);for(let i=0;i>3)&255,v=(v+3*i+c)&255,c=(c+v+k[(i+128)%256])&255,o[i]=v}const h=Array.from(o).map(v=>v.toString(16).padStart(2,"0")).join("");let w="";for(let i=0;i";return w}let r,o,a,i="",c=null,l=!1,d="",u={},p=0,h=!0,f="https://doh.cmliussss.net/CMLiussss",g=["*tapecontent.net","*cloudatacdn.com","*loadshare.org","*cdn-centaurus.com","scholar.google.com"];const m="https://edt-pages.github.io";export default{async fetch(e,t,n){const o=new URL(e.url),a=e.headers.get("User-Agent")||"null",p=e.headers.get("Upgrade"),C=t.ADMIN||t.admin||t.PASSWORD||t.password||t.pswd||t.TOKEN||t.KEY||t.UUID||t.uuid||"cfspider-public",T=t.KEY||"cfspider-default-key",A=await S(C+T),P=/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,_=t.UUID||t.uuid,F=_&&P.test(_)?_.toLowerCase():[A.slice(0,8),A.slice(8,12),"4"+A.slice(13,16),"8"+A.slice(17,20),A.slice(20)].join("-"),D=(t.HOST?(await v(t.HOST)).map(e=>e.toLowerCase().replace(/^https?:\/\//,"").split("/")[0].split(":")[0]):[o.hostname])[0];if(t.PROXYIP){const e=await v(t.PROXYIP);i=e[Math.floor(Math.random()*e.length)],h=!1}else i=(e.cf.colo+".PrOxYIp.CmLiUsSsS.nEt").toLowerCase();const N=e.headers.get("X-Real-IP")||e.headers.get("CF-Connecting-IP")||e.headers.get("X-Forwarded-For")||e.headers.get("True-Client-IP")||e.headers.get("Fly-Client-IP")||e.headers.get("X-Appengine-Remote-Addr")||e.headers.get("X-Forwarded-For")||e.headers.get("X-Real-IP")||e.headers.get("X-Cluster-Client-IP")||e.cf?.clientTcpRtt||"unknownIP";if(t.GO2SOCKS5&&(g=await v(t.GO2SOCKS5)),f=t.ECH_DOH||t.DOH||f,p&&"websocket"===p){if(C){await async function(e){const t=new URL(e.url),{pathname:n,searchParams:r}=t,s=n.toLowerCase();d=r.get("socks5")||r.get("http")||null,l=r.has("globalproxy")||!1;const o=s.match(/\/(proxyip[.=]|pyip=|ip=)(.+)/);if(r.has("proxyip")){const e=r.get("proxyip");return i=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(h=!1)}if(o){const e="proxyip."===o[1]?`proxyip.${o[2]}`:o[2];return i=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(h=!1)}let a;if(a=n.match(/\/(socks5?|http):\/?\/?(.+)/i)){if(c="http"===a[1].toLowerCase()?"http":"socks5",d=a[2].split("#")[0],l=!0,d.includes("@")){const e=d.lastIndexOf("@");let t=d.substring(0,e).replaceAll("%3D","=");/^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i.test(t)&&!t.includes(":")&&(t=atob(t)),d=`${t}@${d.substring(e+1)}`}}else if(a=n.match(/\/(g?s5|socks5|g?http)=(.+)/i)){const e=a[1].toLowerCase();d=a[2],c=e.includes("http")?"http":"socks5",l=e.startsWith("g")||l}if(d)try{u=await E(d),c=r.get("http")?"http":c}catch(e){console.error("parseSOCKS5addrFailed:",e.message),c=null}else c=null}(e);const n=new URL(e.url).pathname;let r="";if(n.includes("two_proxy=")){const e=n.match(/two_proxy=([^&]+)/);e&&(r=decodeURIComponent(decodeURIComponent(e[1])))}const s=r||t.TWO_PROXY||t.two_proxy||"";return await async function(e,t,n=""){const r=new WebSocketPair,[s,o]=Object.values(r);o.accept();let a={socket:null},i=!1;const c=e.headers.get("sec-websocket-protocol")||"",l=function(e,t){let n=!1;return new ReadableStream({start(r){e.addEventListener("message",e=>{n||r.enqueue(e.data)}),e.addEventListener("close",()=>{n||(b(e),r.close())}),e.addEventListener("error",e=>r.error(e));const{earlyData:s,error:o}=function(e){if(!e)return{error:null};try{const t=atob(e.replace(/-/g,"+").replace(/_/g,"/")),n=new Uint8Array(t.length);for(let e=0;e=2&&(u={hostname:e[0],port:parseInt(e[1],10),username:e[2]||"",password:e[3]||""},console.log(`[twoProxy] enabled: ${u.hostname}:${u.port}`))}return l.pipeTo(new WritableStream({async write(e){if(i)return await y(e,o,null);if(a.socket){const t=a.socket.writable.getWriter();return await t.write(e),void t.releaseLock()}if(null===d){const t=new Uint8Array(e);d=t.byteLength>=58&&13===t[56]&&10===t[57]}if(a.socket){const t=a.socket.writable.getWriter();return await t.write(e),void t.releaseLock()}if(d){const{port:n,hostname:r,rawClientData:s}=function(e,t){const n=function(e){const t=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],n=(e,t)=>(e>>>t|e<<32-t)>>>0,r=8*(e=unescape(encodeURIComponent(e))).length;for(e+=String.fromCharCode(128);8*e.length%512!=448;)e+=String.fromCharCode(0);const s=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428],o=Math.floor(r/4294967296),a=4294967295&r;e+=String.fromCharCode(o>>>24&255,o>>>16&255,o>>>8&255,255&o,a>>>24&255,a>>>16&255,a>>>8&255,255&a);const i=[];for(let t=0;t>>3,s=n(r[e-2],17)^n(r[e-2],19)^r[e-2]>>>10;r[e]=r[e-16]+t+r[e-7]+s>>>0}let[o,a,c,l,d,u,p,h]=s;for(let e=0;e<64;e++){const s=h+(n(d,6)^n(d,11)^n(d,25))+(d&u^~d&p)+t[e]+r[e]>>>0,i=o&a^o&c^a&c;h=p,p=u,u=d,d=l+s>>>0,l=c,c=a,a=o,o=s+((n(o,2)^n(o,13)^n(o,22))+i>>>0)>>>0}for(let e=0;e<8;e++)s[e]=s[e]+(0===e?o:1===e?a:2===e?c:3===e?l:4===e?d:5===e?u:6===e?p:h)>>>0}let c="";for(let e=0;e<7;e++)for(let t=24;t>=0;t-=8)c+=(s[e]>>>t&255).toString(16).padStart(2,"0");return c}(t);if(e.byteLength<56)return{hasError:!0,message:"invalid data"};if(13!==new Uint8Array(e.slice(56,57))[0]||10!==new Uint8Array(e.slice(57,58))[0])return{hasError:!0,message:"invalid header format"};if((new TextDecoder).decode(e.slice(0,56))!==n)return{hasError:!0,message:"invalid password"};const r=e.slice(58);if(r.byteLength<6)return{hasError:!0,message:"invalid S5 request data"};const s=new DataView(r);if(1!==s.getUint8(0))return{hasError:!0,message:"unsupported command, only TCP is allowed"};const o=s.getUint8(1);let a=0,i=2,c="";switch(o){case 1:a=4,c=new Uint8Array(r.slice(i,i+a)).join(".");break;case 3:a=new Uint8Array(r.slice(i,i+1))[0],i+=1,c=(new TextDecoder).decode(r.slice(i,i+a));break;case 4:a=16;const e=new DataView(r.slice(i,i+a)),t=[];for(let n=0;n<8;n++)t.push(e.getUint16(2*n).toString(16));c=t.join(":");break;default:return{hasError:!0,message:`invalid addressType is ${o}`}}if(!c)return{hasError:!0,message:`address is empty, addressType is ${o}`};const l=i+a,d=r.slice(l,l+2);return{hasError:!1,addressType:o,port:new DataView(d).getUint16(0),hostname:c,rawClientData:r.slice(l+4)}}(e,t);if(x(r))throw new Error("Speedtest site is blocked");await w(r,n,s,o,null,a,t,u)}else{const{port:n,hostname:r,rawIndex:s,version:c,isUDP:l}=function(e,t){if(e.byteLength<24)return{hasError:!0,message:"Invalid data"};const n=new Uint8Array(e.slice(0,1));if(function(e,t=0){const n=[...e.slice(t,t+16)].map(e=>e.toString(16).padStart(2,"0")).join("");return`${n.substring(0,8)}-${n.substring(8,12)}-${n.substring(12,16)}-${n.substring(16,20)}-${n.substring(20)}`}(new Uint8Array(e.slice(1,17)))!==t)return{hasError:!0,message:"Invalid uuid"};const r=new Uint8Array(e.slice(17,18))[0],s=new Uint8Array(e.slice(18+r,19+r))[0];let o=!1;if(1===s);else{if(2!==s)return{hasError:!0,message:"Invalid command"};o=!0}const a=19+r,i=new DataView(e.slice(a,a+2)).getUint16(0);let c=a+2,l=0,d=c+1,u="";const p=new Uint8Array(e.slice(c,d))[0];switch(p){case 1:l=4,u=new Uint8Array(e.slice(d,d+l)).join(".");break;case 2:l=new Uint8Array(e.slice(d,d+1))[0],d+=1,u=(new TextDecoder).decode(e.slice(d,d+l));break;case 3:l=16;const t=[],n=new DataView(e.slice(d,d+l));for(let e=0;e<8;e++)t.push(n.getUint16(2*e).toString(16));u=t.join(":");break;default:return{hasError:!0,message:`Invalid address type: ${p}`}}return u?{hasError:!1,addressType:p,port:i,hostname:u,isUDP:o,rawIndex:d+l,version:n}:{hasError:!0,message:`Invalid address: ${p}`}}(e,t);if(x(r))throw new Error("Speedtest site is blocked");if(l){if(53!==n)throw new Error("UDP is not supported");i=!0}const d=new Uint8Array([c[0],0]),p=e.slice(s);if(i)return y(p,o,d);await w(r,n,p,o,d,a,t,u)}}})).catch(e=>{}),new Response(null,{status:101,webSocket:s})}(e,F,s)}}else{if("http:"===o.protocol)return Response.redirect(o.href.replace(`http://${o.hostname}`,`https://${o.hostname}`),301);const i=o.pathname.slice(1).toLowerCase(),c=("false"!==t.NEW_IP&&t.NEW_IP,!_||!P.test(_)),l=t.TWO_PROXY||t.two_proxy||"";if(""===i||"/"===i)return new Response('Welcome to nginx!

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

',{status:200,headers:{"Content-Type":"text/html","Server":"nginx/1.18.0"}});if("x2727admin"===i){const t=e.cf?.colo||"UNKNOWN",n="/"+F+(l?"?two_proxy="+encodeURIComponent(l):""),r="vless://"+F+"@"+o.hostname+":443?security=tls&type=ws&host="+o.hostname+"&sni="+o.hostname+"&path="+encodeURIComponent(n)+"&encryption=none#Node-"+t,a={status:"online",version:"1.8.7",colo:t,host:o.hostname,uuid:F,vless:r,two_proxy:l||null};return new Response(s(JSON.stringify(a)),{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn"}})}if("api/proxyip"===i){const n=e.cf?.colo||"UNKNOWN",r=(n+".proxyip.cmliussss.net").toLowerCase(),o=t.PROXYIP||"",a={colo:n,default:r,hk:"proxyip.cfspider.com",env:o||null,current:o||r,options:{default:{name:"nlNode",address:r},hk:{name:"hkNode",address:"proxyip.cfspider.com"}}};return new Response(s(JSON.stringify(a)),{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn"}})}if("api/uuid"===i||"api/config"===i){const e="false"!==t.NEW_IP&&"0"!==t.NEW_IP,n=t.TWO_PROXY||t.two_proxy||"",r={host:o.hostname,new_ip:e,version:"1.8.7",is_default_uuid:c,two_proxy_enabled:!!n};if(c?(r.uuid=F,r.vless_path=n?"/"+F+"?two_proxy="+encodeURIComponent(n):"/"+F):n&&(r.two_proxy=n),n){const e=n.split(":");r.two_proxy_host=e[0]||"",r.two_proxy_port=e[1]||""}return new Response(s(JSON.stringify(r)),{headers:{"Content-Type":"text/plain","Access-Control-Allow-Origin":"*","X-Enc":"x27cn"}})}if("proxy"===i||i.startsWith("proxy?")){const n=o.searchParams.get("url"),r=o.searchParams.get("method")||"GET",s=o.searchParams.get("two_proxy");if(!n)return new Response(JSON.stringify({error:"Missing url parameter"}),{status:400,headers:{"Content-Type":"application/json"}});try{const o={};for(const[t,n]of e.headers)t.toLowerCase().startsWith("x-custom-header-")&&(o[t.substring(18)]=n);let a;const i=s||t.TWO_PROXY||t.two_proxy||"";if(i){const t=i.split(":"),s=t[0],a=parseInt(t[1])||3128,c=t[2]||"",l=t[3]||"",d=new URL(n),u=d.hostname,p=(d.port||d.protocol,"https:"===d.protocol),{connect:h}=await import("cloudflare:sockets");if(p)return new Response(JSON.stringify({error:"HTTPS + two_proxy notSupportedViaProxyAPI。useCfspiderGetWithTwoProxy。",hint:"client.get(url, cf_proxies=..., uuid=..., two_proxy=...)",reason:"Workers /proxy API onlyHTTPTwoProxySupported"}),{status:501,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});{const t=h({hostname:s,port:a}),i=t.writable.getWriter(),d=t.readable.getReader();let p=`${r} ${n} HTTP/1.1\r\nHost: ${u}\r\n`;c&&l&&(p+=`Proxy-Authorization: Basic ${btoa(`${c}:${l}`)}\r\n`);for(const[e,t]of Object.entries(o))p+=`${e}: ${t}\r\n`;p+="Connection: close\r\n\r\n",await i.write((new TextEncoder).encode(p));let f=new Uint8Array(0);for(;;){const{value:e,done:t}=await d.read();if(t)break;const n=new Uint8Array(f.length+e.length);n.set(f),n.set(e,f.length),f=n}const g=(new TextDecoder).decode(f),m=g.indexOf("\r\n\r\n"),w=g.substring(0,m),y=f.slice((new TextEncoder).encode(g.substring(0,m+4)).length),b=w.split("\r\n")[0],x=parseInt(b.split(" ")[1])||200,C=new Headers;return w.split("\r\n").slice(1).forEach(e=>{const[t,...n]=e.split(":");t&&n.length&&C.set(t.trim(),n.join(":").trim())}),C.set("Access-Control-Allow-Origin","*"),C.set("X-CF-Colo",e.cf?.colo||"unknown"),C.set("X-Worker-Version","1.8.6"),C.set("X-Two-Proxy","enabled"),new Response(y,{status:x,headers:C})}}{const t=new Request(n,{method:r,headers:o,body:"GET"!==r&&"HEAD"!==r?e.body:null});a=await fetch(t);const s=new Headers(a.headers);return s.set("Access-Control-Allow-Origin","*"),s.set("X-CF-Colo",e.cf?.colo||"unknown"),s.set("X-Worker-Version","1.8.6"),new Response(a.body,{status:a.status,headers:s})}}catch(e){return new Response(JSON.stringify({error:e.message}),{status:500,headers:{"Content-Type":"application/json"}})}}if("api/config/new_ip"===i&&"POST"===e.method){const e="false"!==t.NEW_IP&&"0"!==t.NEW_IP;return new Response(JSON.stringify({new_ip:e,message:"setNEWIPViaDashboard"}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if(!C)return fetch(m+"/noADMIN").then(e=>{const t=new Headers(e.headers);return t.set("Cache-Control","no-store, no-cache, must-revalidate, proxy-revalidate"),t.set("Pragma","no-cache"),t.set("Expires","0"),new Response(e.body,{status:404,statusText:e.statusText,headers:t})});if(t.KV&&"function"==typeof t.KV.get){const s=o.pathname.slice(1).toLowerCase(),i=o.pathname.slice(1);if(i===T&&"doNotModifyDefaultKey"!==T){const e=new URLSearchParams(o.search);return e.set("token",await S(D+F)),new Response("redirecting...",{status:302,headers:{Location:`/sub?${e.toString()}`}})}if("login"===s){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n==await S(a+T+C))return new Response("redirecting...",{status:302,headers:{Location:"/admin"}});if("POST"===e.method){const t=await e.text();if(new URLSearchParams(t).get("password")===C){const e=new Response(JSON.stringify({success:!0}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}});return e.headers.set("Set-Cookie",`auth=${await S(a+T+C)}; Path=/; Max-Age=86400; HttpOnly`),e}}return fetch(m+"/login")}if("admin"===s||s.startsWith("admin/")){const c=e.headers.get("Cookie")||"",l=c.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(!l||l!==await S(a+T+C))return new Response("redirecting...",{status:302,headers:{Location:"/login"}});if("admin/log.json"===s){const e=await t.KV.get("log.json")||"[]";return new Response(e,{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}if("admin/getCloudflareUsage"===i)try{const e=await O(o.searchParams.get("Email"),o.searchParams.get("GlobalAPIKey"),o.searchParams.get("AccountID"),o.searchParams.get("APIToken"));return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json"}})}catch(e){const t={msg:"queryRequestFailed,reason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else{if("admin/getADDAPI"===i){if(o.searchParams.get("url")){const t=o.searchParams.get("url");try{new URL(t);const e=await k([t],o.searchParams.get("port")||"443"),n=e[0].length>0?e[0]:e[1];return new Response(JSON.stringify({success:!0,data:n},null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){const t={msg:"verifyAPIFailed,reason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}}return new Response(JSON.stringify({success:!1,data:[]},null,2),{status:403,headers:{"Content-Type":"application/json;charset=utf-8"}})}if("admin/check"===s){let e;if(o.searchParams.has("socks5"))e=await R("socks5",o.searchParams.get("socks5"));else{if(!o.searchParams.has("http"))return new Response(JSON.stringify({error:"missingProxyParam"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});e=await R("http",o.searchParams.get("http"))}return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}}if(r=await I(t,D,F,t.PATH),"admin/init"===s)try{return r=await I(t,D,F,t.PATH,!0),n.waitUntil($(t,e,N,"Init_Config",r)),r.init="configResetToDefault",new Response(JSON.stringify(r,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){const t={msg:"configResetFailed,reason:"+e.message,error:e.message};return new Response(JSON.stringify(t,null,2),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("POST"===e.method)if("admin/config.json"===s)try{const s=await e.json();return s.UUID&&s.HOST?(await t.KV.put("config.json",JSON.stringify(s,null,2)),n.waitUntil($(t,e,N,"Save_Config",r)),new Response(JSON.stringify({success:!0,message:"configSaved"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})):new Response(JSON.stringify({error:"incompleteConfig"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("saveConfigFailed:",e),new Response(JSON.stringify({error:"saveConfigFailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("admin/cf.json"===s)try{const s=await e.json(),o={Email:null,GlobalAPIKey:null,AccountID:null,APIToken:null,UsageAPI:null};if(!s.init||!0!==s.init)if(s.Email&&s.GlobalAPIKey)o.Email=s.Email,o.GlobalAPIKey=s.GlobalAPIKey;else if(s.AccountID&&s.APIToken)o.AccountID=s.AccountID,o.APIToken=s.APIToken;else{if(!s.UsageAPI)return new Response(JSON.stringify({error:"incompleteConfig"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});o.UsageAPI=s.UsageAPI}return await t.KV.put("cf.json",JSON.stringify(o,null,2)),n.waitUntil($(t,e,N,"Save_Config",r)),new Response(JSON.stringify({success:!0,message:"configSaved"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("saveConfigFailed:",e),new Response(JSON.stringify({error:"saveConfigFailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else if("admin/tg.json"===s)try{const s=await e.json();if(s.init&&!0===s.init){const e={BotToken:null,ChatID:null};await t.KV.put("tg.json",JSON.stringify(e,null,2))}else{if(!s.BotToken||!s.ChatID)return new Response(JSON.stringify({error:"incompleteConfig"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});await t.KV.put("tg.json",JSON.stringify(s,null,2))}return n.waitUntil($(t,e,N,"Save_Config",r)),new Response(JSON.stringify({success:!0,message:"configSaved"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("saveConfigFailed:",e),new Response(JSON.stringify({error:"saveConfigFailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}else{if("admin/ADD.txt"!==i)return new Response(JSON.stringify({error:"unsupportedPOSTPath"}),{status:404,headers:{"Content-Type":"application/json;charset=utf-8"}});try{const s=await e.text();return await t.KV.put("ADD.txt",s),n.waitUntil($(t,e,N,"Save_Custom_IPs",r)),new Response(JSON.stringify({success:!0,message:"customIPSaved"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){return console.error("saveCustomIPfailed:",e),new Response(JSON.stringify({error:"saveCustomIPfailed: "+e.message}),{status:500,headers:{"Content-Type":"application/json;charset=utf-8"}})}}else{if("admin/config.json"===s)return new Response(JSON.stringify(r,null,2),{status:200,headers:{"Content-Type":"application/json"}});if("admin/ADD.txt"===i){let n=await t.KV.get("ADD.txt")||"null";return"null"==n&&(n=(await U(e,r.subGenerator.localIPPool.randomCount,r.subGenerator.localIPPool.specifiedPort))[1]),new Response(n,{status:200,headers:{"Content-Type":"text/plain;charset=utf-8",asn:e.cf.asn}})}if("admin/cf.json"===s)return new Response(JSON.stringify(e.cf,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}return n.waitUntil($(t,e,N,"Admin_Login",r)),fetch(m+"/admin")}if("logout"===s||P.test(s)){const e=new Response("redirecting...",{status:302,headers:{Location:"/login"}});return e.headers.set("Set-Cookie","auth=; Path=/; Max-Age=0; HttpOnly"),e}if("sub"===s){const s=await S(D+F);if(o.searchParams.get("token")===s){r=await I(t,D,F,t.PATH),n.waitUntil($(t,e,N,"Get_SUB",r));const i=a.toLowerCase(),c=4102329600,l=Date.now(),d=new Date(l);d.setHours(0,0,0,0);const u=Math.floor((l-d.getTime())/864e5*24*1099511627776/2);let p=u,h=u,g=26388279066624;r.CF.Usage.success&&(p=r.CF.Usage.pages,h=r.CF.Usage.workers,g=Number.isFinite(r.CF.Usage.max)?r.CF.Usage.max/1e3*1024:102400);const m={"content-type":"text/plain; charset=utf-8","Profile-Update-Interval":r.subGenerator.SUBUpdateTime,"Profile-web-page-url":o.protocol+"//"+o.host+"/admin","Subscription-Userinfo":`upload=${p}; download=${h}; total=${g}; expire=${c}`,"Cache-Control":"no-store"},w=o.searchParams.has("b64")||o.searchParams.has("base64")||e.headers.get("subconverter-request")||e.headers.get("subconverter-version")||i.includes("subconverter")||i.includes("CF-Workers-SUB".toLowerCase())?"mixed":o.searchParams.has("target")?o.searchParams.get("target"):o.searchParams.has("clash")||i.includes("clash")||i.includes("meta")||i.includes("mihomo")?"clash":o.searchParams.has("sb")||o.searchParams.has("singbox")||i.includes("singbox")||i.includes("sing-box")?"singbox":o.searchParams.has("surge")||i.includes("surge")?"surge&ver=4":o.searchParams.has("quanx")||i.includes("quantumult")?"quanx":o.searchParams.has("loon")||i.includes("loon")?"loon":"mixed";i.includes("mozilla")||(m["Content-Disposition"]=`attachment; filename*=utf-8''${encodeURIComponent(r.subGenerator.SUBNAME)}`);const y=o.searchParams.has("surge")||i.includes("surge")?"trojan":r.protocolType;let b="";if("mixed"===w){const n=r.enable0RTT?r.PATH+"?ed=2560":r.PATH,s="Shadowrocket"==r.tlsFragment?`&fragment=${encodeURIComponent("1,40-60,30-50,tlshello")}`:"Happ"==r.tlsFragment?`&fragment=${encodeURIComponent("3,1,tlshello")}`:"";let a=[],i="";if(!o.searchParams.has("sub")&&r.subGenerator.local){const n=r.subGenerator.localIPPool.randomIP?(await U(e,r.subGenerator.localIPPool.randomCount,r.subGenerator.localIPPool.specifiedPort))[0]:await t.KV.get("ADD.txt")?await v(await t.KV.get("ADD.txt")):(await U(e,r.subGenerator.localIPPool.randomCount,r.subGenerator.localIPPool.specifiedPort))[0],s=[],o=[],c=[];for(const e of n)if(e.toLowerCase().startsWith("https://"))s.push(e);else if(e.toLowerCase().includes("://"))if(e.includes("#")){const t=e.split("#");c.push(t[0]+"#"+encodeURIComponent(decodeURIComponent(t[1])))}else c.push(e);else o.push(e);const l=await k(s),d=[...new Set(c.concat(l[1]))];i=d.length>0?d.join("\n")+"\n":"";const u=l[0];a=[...new Set(o.concat(u))]}else{let t=o.searchParams.get("sub")||r.subGenerator.SUB;t=t&&!/^https?:\/\//i.test(t)?`https://${t}`:t;const n=`${t}/sub?host=example.com&uuid=00000000-0000-4000-8000-000000000000`;try{const e=await fetch(n,{headers:{"User-Agent":"v2rayN/edgetunnel (https://github.com/cmliu/edgetunnel)"}});if(!e.ok)return new Response("subGenError:"+e.statusText,{status:e.status});const t=atob(await e.text()),r=t.includes("\r\n")?t.split("\r\n"):t.split("\n");for(const e of r)if(e.trim())if(e.includes("00000000-0000-4000-8000-000000000000")&&e.includes("example.com")){const t=e.match(/:\/\/[^@]+@([^?]+)/);if(t){let n=t[1],r="";const s=e.match(/#(.+)$/);s&&(r="#"+decodeURIComponent(s[1])),a.push(n+r)}}else i+=e+"\n"}catch(e){return new Response("subGenError:"+e.message,{status:403})}}const c=r.ECH?`&ech=${encodeURIComponent("cloudflare-ech.com+"+f)}`:"";b=i+a.map(e=>{const t=e.match(/^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/);let o,a,i="443";return t?(o=t[1],i=t[2]||"443",a=t[3]||o,`${y}://00000000-0000-4000-8000-000000000000@${o}:${i}?security=tls&type=${r.transport+c}&host=example.com&fp=${r.Fingerprint}&sni=example.com&path=${encodeURIComponent(r.randomPath?function(){const e=Math.floor(3*Math.random()+1);return`/${["about","account","acg","act","activity","ad","ads","ajax","album","albums","anime","api","app","apps","archive","archives","article","articles","ask","auth","avatar","bbs","bd","blog","blogs","book","books","bt","buy","cart","category","categories","cb","channel","channels","chat","china","city","class","classify","clip","clips","club","cn","code","collect","collection","comic","comics","community","company","config","contact","content","course","courses","cp","data","detail","details","dh","directory","discount","discuss","dl","dload","doc","docs","document","documents","doujin","download","downloads","drama","edu","en","ep","episode","episodes","event","events","f","faq","favorite","favourites","favs","feedback","file","files","film","films","forum","forums","friend","friends","game","games","gif","go","go.html","go.php","group","groups","help","home","hot","htm","html","image","images","img","index","info","intro","item","items","ja","jp","jump","jump.html","jump.php","jumping","knowledge","lang","lesson","lessons","lib","library","link","links","list","live","lives","m","mag","magnet","mall","manhua","map","member","members","message","messages","mobile","movie","movies","music","my","new","news","note","novel","novels","online","order","out","out.html","out.php","outbound","p","page","pages","pay","payment","pdf","photo","photos","pic","pics","picture","pictures","play","player","playlist","post","posts","product","products","program","programs","project","qa","question","rank","ranking","read","readme","redirect","redirect.html","redirect.php","reg","register","res","resource","retrieve","sale","search","season","seasons","section","seller","series","service","services","setting","settings","share","shop","show","shows","site","soft","sort","source","special","star","stars","static","stock","store","stream","streaming","streams","student","study","tag","tags","task","teacher","team","tech","temp","test","thread","tool","tools","topic","topics","torrent","trade","travel","tv","txt","type","u","upload","uploads","url","urls","user","users","v","version","video","videos","view","vip","vod","watch","web","wenku","wiki","work","www","zh","zh-cn","zh-tw","zip"].sort(()=>.5-Math.random()).slice(0,e).join("/")}`}()+n:n)+s}&encryption=none${r.skipCertVerify?"&insecure=1&allowInsecure=1":""}#${encodeURIComponent(a)}`):(console.warn(`[subContent] invalidIPIgnored: ${e}`),null)}).filter(e=>null!==e).join("\n")}else{const t=`${r.subConverterConfig.SUBAPI}/sub?target=${w}&url=${encodeURIComponent(o.protocol+"//"+o.host+"/sub?target=mixed&token="+s+(o.searchParams.has("sub")&&""!=o.searchParams.get("sub")?`&sub=${o.searchParams.get("sub")}`:""))}&config=${encodeURIComponent(r.subConverterConfig.SUBCONFIG)}&emoji=${r.subConverterConfig.SUBEMOJI}&scv=${r.skipCertVerify}`;try{const e=await fetch(t,{headers:{"User-Agent":"Subconverter for "+w+" edgetunnel(https://github.com/cmliu/edgetunnel)"}});if(!e.ok)return new Response("subConverterError:"+e.statusText,{status:e.status});b=await e.text(),(o.searchParams.has("surge")||i.includes("surge"))&&(b=function(e,t,n){const r=e.includes("\r\n")?e.split("\r\n"):e.split("\n");let s="";const o=n.enable0RTT?n.PATH+"?ed=2560":n.PATH;for(let e of r)if(!e.includes("= trojan,")||e.includes("ws=true")||e.includes("ws-path="))s+=e+"\n";else{const t=e.split("sni=")[1].split(",")[0],r=`sni=${t}, skip-cert-verify=${n.skipCertVerify}`,a=`sni=${t}, skip-cert-verify=${n.skipCertVerify}, ws=true, ws-path=${o}, ws-headers=Host:"${t}"`;s+=e.replace(new RegExp(r,"g"),a).replace("[","").replace("]","")+"\n"}return s=`#!MANAGED-CONFIG ${t} interval=${60*n.subGenerator.SUBUpdateTime*60} strict=false`+s.substring(s.indexOf("\n")),s}(b,o.protocol+"//"+o.host+"/sub?token="+s+"&surge",r))}catch(e){return new Response("subConverterError:"+e.message,{status:403})}}return i.includes("subconverter")||(b=await function(e,t,n=2){const r=[...t].sort(()=>Math.random()-.5);let s=0,o=null;return e.replace(/example\.com/g,()=>(s%n===0&&(o=function(e){if(!e?.includes("*"))return e;return e.replace(/\*/g,()=>{let e="";for(let t=0;t{if("tun"===e.type){const t=[];e.inet4_address&&t.push(e.inet4_address),e.inet6_address&&t.push(e.inet6_address),t.length>0&&(e.address=t,delete e.inet4_address,delete e.inet6_address);const n=[];Array.isArray(e.inet4_route_address)&&n.push(...e.inet4_route_address),Array.isArray(e.inet6_route_address)&&n.push(...e.inet6_route_address),n.length>0&&(e.route_address=n,delete e.inet4_route_address,delete e.inet6_route_address);const r=[];Array.isArray(e.inet4_route_exclude_address)&&r.push(...e.inet4_route_exclude_address),Array.isArray(e.inet6_route_exclude_address)&&r.push(...e.inet6_route_exclude_address),r.length>0&&(e.route_exclude_address=r,delete e.inet4_route_exclude_address,delete e.inet6_route_exclude_address)}});const o=new Map,a=(e,t=!1)=>{Array.isArray(e)&&e.forEach(e=>{if(e.geosite){const t=Array.isArray(e.geosite)?e.geosite:[e.geosite];e.rule_set=t.map(e=>{const t=`geosite-${e}`;return o.has(t)||o.set(t,{tag:t,type:"remote",format:"binary",url:`https://gh.090227.xyz/https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-${e}.srs`,download_detour:"DIRECT"}),t}),delete e.geosite}if(e.geoip){const t=Array.isArray(e.geoip)?e.geoip:[e.geoip];e.rule_set=e.rule_set||[],t.forEach(t=>{const n=`geoip-${t}`;o.has(n)||o.set(n,{tag:n,type:"remote",format:"binary",url:`https://gh.090227.xyz/https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-${t}.srs`,download_detour:"DIRECT"}),e.rule_set.push(n)}),delete e.geoip}const n=t?"server":"outbound",r=String(e[n]).toUpperCase();"REJECT"!==r&&"BLOCK"!==r||(e.action="reject",e.method="drop",delete e[n])})};s.dns&&s.dns.rules&&a(s.dns.rules,!0),s.route&&s.route.rules&&a(s.route.rules,!1),o.size>0&&(s.route||(s.route={}),s.route.rule_set=Array.from(o.values())),s.outbounds||(s.outbounds=[]),s.outbounds=s.outbounds.filter(e=>"REJECT"!==e.tag&&"block"!==e.tag);const i=new Set(s.outbounds.map(e=>e.tag));if(i.has("DIRECT")||(s.outbounds.push({type:"direct",tag:"DIRECT"}),i.add("DIRECT")),s.dns&&s.dns.servers){const e=new Set(s.dns.servers.map(e=>e.tag));s.dns.rules&&s.dns.rules.forEach(t=>{t.server&&!e.has(t.server)&&("dns_block"===t.server&&e.has("block")?t.server="block":t.server.toLowerCase().includes("block")&&!e.has(t.server)&&(s.dns.servers.push({tag:t.server,address:"rcode://success"}),e.add(t.server)))})}return s.outbounds.forEach(e=>{"selector"!==e.type&&"urltest"!==e.type||Array.isArray(e.outbounds)&&(e.outbounds=e.outbounds.filter(e=>{const t=e.toUpperCase();return i.has(e)&&"REJECT"!==t&&"BLOCK"!==t}),0===e.outbounds.length&&e.outbounds.push("DIRECT"))}),t&&s.outbounds.forEach(e=>{(e.uuid&&e.uuid===t||e.password&&e.password===t)&&(e.tls||(e.tls={enabled:!0}),n&&(e.tls.utls={enabled:!0,fingerprint:n}),r&&(e.tls.ech={enabled:!0,config:`-----BEGIN ECH CONFIGS-----\n${r}\n-----END ECH CONFIGS-----`}))}),JSON.stringify(s,null,2)}catch(t){return console.error("singboxPatchFailed:",t),JSON.stringify(JSON.parse(e),null,2)}}(b,r.UUID,r.Fingerprint,r.ECH?await async function(e){try{const t=await fetch(`https://1.1.1.1/dns-query?name=${encodeURIComponent(e)}&type=65`,{headers:{accept:"application/dns-json"}}),n=await t.json();if(!n.Answer?.length)return"";for(let e of n.Answer){if(65!==e.type||!e.data)continue;const t=e.data.match(/ech=([^\s]+)/);if(t)return t[1].replace(/"/g,"");if(e.data.startsWith("\\#")){const t=e.data.split(" ").slice(2).join(""),n=new Uint8Array(t.match(/.{1,2}/g).map(e=>parseInt(e,16)));let r=2;for(;r0){const e=r.map(e=>` "${e}":\n - tls://8.8.8.8\n - https://doh.cmliussss.com/CMLiussss\n - ${f}`).join("\n");if(/^\s{2}nameserver-policy:\s*(?:\n|$)/m.test(s))s=s.replace(/^(\s{2}nameserver-policy:\s*\n)/m,`$1${e}\n`);else{const t=s.split("\n");let n=-1,r=!1;for(let e=0;e0&&i+1=0;t--)if(n[t].trim()){e=t;break}if(e>=0){const t=" ".repeat(s);n.splice(e+1,0,`${t}ech-opts:`,`${t} enable: true`)}}a.push(...n)}else a.push(e),i++}return a.join("\n")}(b,r.UUID,r.ECH,r.HOSTS),m["content-type"]="application/x-yaml; charset=utf-8"),new Response(b,{status:200,headers:m})}}else if("locations"===s){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n&&n==await S(a+T+C))return fetch(new Request("https://speed.cloudflare.com/locations",{headers:{Referer:"https://speed.cloudflare.com/"}}))}else if("robots.txt"===s)return new Response("User-agent: *\nDisallow: /",{status:200,headers:{"Content-Type":"text/plain; charset=UTF-8"}})}else if(!_)return fetch(m+"/noKV").then(e=>{const t=new Headers(e.headers);return t.set("Cache-Control","no-store, no-cache, must-revalidate, proxy-revalidate"),t.set("Pragma","no-cache"),t.set("Expires","0"),new Response(e.body,{status:404,statusText:e.statusText,headers:t})})}let j=t.URL||"nginx";if(j&&"nginx"!==j&&"1101"!==j){j=j.trim().replace(/\/$/,""),j.match(/^https?:\/\//i)||(j="https://"+j),j.toLowerCase().startsWith("http://")&&(j="https://"+j.substring(7));try{const e=new URL(j);j=e.protocol+"//"+e.host}catch(e){j="nginx"}}if("1101"===j)return new Response(await async function(e,t){const n=new Date,r=n.getFullYear()+"-"+String(n.getMonth()+1).padStart(2,"0")+"-"+String(n.getDate()).padStart(2,"0")+" "+String(n.getHours()).padStart(2,"0")+":"+String(n.getMinutes()).padStart(2,"0")+":"+String(n.getSeconds()).padStart(2,"0"),s=Array.from(crypto.getRandomValues(new Uint8Array(8))).map(e=>e.toString(16).padStart(2,"0")).join("");return`\n\x3c!--[if lt IE 7]> \x3c!--\x3e \x3c!--\nWorker threw exception | ${e} | Cloudflare\n\n\n\n\n\n\n\x3c!--[if lt IE 9]>body{margin:0;padding:0}\n\n\n\x3c!--[if gte IE 10]>\x3c!--\x3e\n