From bbb45f17f36be701ba1bd642377e56538c81c800 Mon Sep 17 00:00:00 2001 From: violettools Date: Sat, 24 Jan 2026 10:53:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B7=B7=E6=B7=86=E7=89=88?= =?UTF-8?q?=E6=9C=ACworkers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 破皮版workers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/破皮版workers.js b/破皮版workers.js index 989c83c..8e22772 100644 --- a/破皮版workers.js +++ b/破皮版workers.js @@ -1 +1 @@ -import{connect as e}from"cloudflare:sockets";let t,n,s,r="",o=null,a=!1,i="",c={},l=0,d=!0,u="https://doh.cmliussss.net/CMLiussss",p=["*tapecontent.net","*cloudatacdn.com","*loadshare.org","*cdn-centaurus.com","scholar.google.com"];const h="https://edt-pages.github.io";export default{async fetch(e,n,s){const l=new URL(e.url),w=e.headers.get("User-Agent")||"null",b=e.headers.get("Upgrade"),C=n.ADMIN||n.admin||n.PASSWORD||n.password||n.pswd||n.TOKEN||n.KEY||n.UUID||n.uuid||"cfspider-public",A=n.KEY||"cfspider-default-key",E=await $(C+A),O=/^[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}$/,R=n.UUID||n.uuid,_=R&&O.test(R)?R.toLowerCase():[E.slice(0,8),E.slice(8,12),"4"+E.slice(13,16),"8"+E.slice(17,20),E.slice(20)].join("-"),j=(n.HOST?(await I(n.HOST)).map(e=>e.toLowerCase().replace(/^https?:\/\//,"").split("/")[0].split(":")[0]):[l.hostname])[0];if(n.PROXYIP){const e=await I(n.PROXYIP);r=e[Math.floor(Math.random()*e.length)],d=!1}else r=(e.cf.colo+".PrOxYIp.CmLiUsSsS.nEt").toLowerCase();const D=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(n.GO2SOCKS5&&(p=await I(n.GO2SOCKS5)),u=n.echDoh||n.DOH||u,b&&"websocket"===b){if(C){await async function(e){const t=new URL(e.url),{pathname:n,searchParams:s}=t,l=n.toLowerCase();i=s.get("socks5")||s.get("http")||null,a=s.has("globalproxy")||!1;const u=l.match(/\/(proxyip[.=]|pyip=|ip=)(.+)/);if(s.has("proxyip")){const e=s.get("proxyip");return r=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(d=!1)}if(u){const e="proxyip."===u[1]?`proxyip.${u[2]}`:u[2];return r=e.includes(",")?e.split(",")[Math.floor(Math.random()*e.split(",").length)]:e,void(d=!1)}let p;if(p=n.match(/\/(socks5?|http):\/?\/?(.+)/i)){if(o="http"===p[1].toLowerCase()?"http":"socks5",i=p[2].split("#")[0],a=!0,i.includes("@")){const e=i.lastIndexOf("@");let t=i.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)),i=`${t}@${i.substring(e+1)}`}}else if(p=n.match(/\/(g?s5|socks5|g?http)=(.+)/i)){const e=p[1].toLowerCase();i=p[2],o=e.includes("http")?"http":"socks5",a=e.startsWith("g")||a}if(i)try{c=await v(i),o=s.get("http")?"http":o}catch(e){console.error("parseSocks5failed:",e.message),o=null}else o=null}(e);const t=new URL(e.url).pathname;let s="";if(t.includes("two_proxy=")){const e=t.match(/two_proxy=([^&]+)/);e&&(s=decodeURIComponent(decodeURIComponent(e[1])))}const l=s||n.TWO_PROXY||n.two_proxy||"";return await async function(e,t,n=""){const s=new WebSocketPair,[r,o]=Object.values(s);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(s){e.addEventListener("message",e=>{n||s.enqueue(e.data)}),e.addEventListener("close",()=>{n||(m(e),s.close())}),e.addEventListener("error",e=>s.error(e));const{earlyData:r,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 g(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:s,rawClientData:r}=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,s=8*(e=unescape(encodeURIComponent(e))).length;for(e+=String.fromCharCode(128);8*e.length%512!=448;)e+=String.fromCharCode(0);const r=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428],o=Math.floor(s/4294967296),a=4294967295&s;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,r=n(s[e-2],17)^n(s[e-2],19)^s[e-2]>>>10;s[e]=s[e-16]+t+s[e-7]+r>>>0}let[o,a,c,l,d,u,p,h]=r;for(let e=0;e<64;e++){const r=h+(n(d,6)^n(d,11)^n(d,25))+(d&u^~d&p)+t[e]+s[e]>>>0,i=o&a^o&c^a&c;h=p,p=u,u=d,d=l+r>>>0,l=c,c=a,a=o,o=r+((n(o,2)^n(o,13)^n(o,22))+i>>>0)>>>0}for(let e=0;e<8;e++)r[e]=r[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+=(r[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 s=e.slice(58);if(s.byteLength<6)return{hasError:!0,message:"invalid S5 request data"};const r=new DataView(s);if(1!==r.getUint8(0))return{hasError:!0,message:"unsupported command, only TCP is allowed"};const o=r.getUint8(1);let a=0,i=2,c="";switch(o){case 1:a=4,c=new Uint8Array(s.slice(i,i+a)).join(".");break;case 3:a=new Uint8Array(s.slice(i,i+1))[0],i+=1,c=(new TextDecoder).decode(s.slice(i,i+a));break;case 4:a=16;const e=new DataView(s.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=s.slice(l,l+2);return{hasError:!1,addressType:o,port:new DataView(d).getUint16(0),hostname:c,rawClientData:s.slice(l+4)}}(e,t);if(y(s))throw new Error("Speedtest site is blocked");await f(s,n,r,o,null,a,t,u)}else{const{port:n,hostname:s,rawIndex:r,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 s=new Uint8Array(e.slice(17,18))[0],r=new Uint8Array(e.slice(18+s,19+s))[0];let o=!1;if(1===r);else{if(2!==r)return{hasError:!0,message:"Invalid command"};o=!0}const a=19+s,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(y(s))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(r);if(i)return g(p,o,d);await f(s,n,p,o,d,a,t,u)}}})).catch(e=>{}),new Response(null,{status:101,webSocket:r})}(e,_,l)}}else{if("http:"===l.protocol)return Response.redirect(l.href.replace(`http://${l.hostname}`,`https://${l.hostname}`),301);const r=l.pathname.slice(1).toLowerCase(),o=("false"!==n.NEW_IP&&n.NEW_IP,!R||!O.test(R)),a=n.TWO_PROXY||n.two_proxy||"";if(""===r||"/"===r){const t=e.cf?.colo||"UNKNOWN",n="/"+_+(a?"?two_proxy="+encodeURIComponent(a):""),s="vless://"+_+"@"+l.hostname+":443?security=tls&type=ws&host="+l.hostname+"&sni="+l.hostname+"&path="+encodeURIComponent(n)+"&encryption=none#CFspider-"+t;return new Response(JSON.stringify({status:"online",version:"1.8.7",colo:t,host:l.hostname,uuid:_,vless:s,two_proxy:a||null},null,2),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("api/proxyip"===r){const t=e.cf?.colo||"UNKNOWN",s=(t+".proxyip.cmliussss.net").toLowerCase(),r=n.PROXYIP||"";return new Response(JSON.stringify({colo:t,default:s,hk:"proxyip.cfspider.com",env:r||null,current:r||s,options:{default:{name:"nlNode",address:s},hk:{name:"hkNode",address:"proxyip.cfspider.com"}}}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("api/uuid"===r||"api/config"===r){const e="false"!==n.NEW_IP&&"0"!==n.NEW_IP,t=n.TWO_PROXY||n.two_proxy||"",s={host:l.hostname,new_ip:e,version:"1.8.7",is_default_uuid:o,two_proxy_enabled:!!t};if(o?(s.uuid=_,s.vless_path=t?"/"+_+"?two_proxy="+encodeURIComponent(t):"/"+_):t&&(s.two_proxy=t),t){const e=t.split(":");s.two_proxy_host=e[0]||"",s.two_proxy_port=e[1]||""}return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if("proxy"===r||r.startsWith("proxy?")){const t=l.searchParams.get("url"),s=l.searchParams.get("method")||"GET",r=l.searchParams.get("two_proxy");if(!t)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-cfspider-header-")&&(o[t.substring(18)]=n);let a;const i=r||n.TWO_PROXY||n.two_proxy||"";if(i){const n=i.split(":"),r=n[0],a=parseInt(n[1])||3128,c=n[2]||"",l=n[3]||"",d=new URL(t),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 不支持通过 /proxy API。请使用 Python cfspider.get() 配合 two_proxy param。",hint:"cfspider.get(url, cf_proxies=..., uuid=..., two_proxy=...)",reason:"Workers /proxy API 仅支持 HTTP twoProxy"}),{status:501,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});{const n=h({hostname:r,port:a}),i=n.writable.getWriter(),d=n.readable.getReader();let p=`${s} ${t} 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],C=parseInt(b.split(" ")[1])||200,x=new Headers;return w.split("\r\n").slice(1).forEach(e=>{const[t,...n]=e.split(":");t&&n.length&&x.set(t.trim(),n.join(":").trim())}),x.set("Access-Control-Allow-Origin","*"),x.set("X-CF-Colo",e.cf?.colo||"unknown"),x.set("X-CFspider-Version","1.8.6"),x.set("X-CFspider-TwoProxy","enabled"),new Response(y,{status:C,headers:x})}}{const n=new Request(t,{method:s,headers:o,body:"GET"!==s&&"HEAD"!==s?e.body:null});a=await fetch(n);const r=new Headers(a.headers);return r.set("Access-Control-Allow-Origin","*"),r.set("X-CF-Colo",e.cf?.colo||"unknown"),r.set("X-CFspider-Version","1.8.6"),new Response(a.body,{status:a.status,headers:r})}}catch(e){return new Response(JSON.stringify({error:e.message}),{status:500,headers:{"Content-Type":"application/json"}})}}if("api/config/new_ip"===r&&"POST"===e.method){const e="false"!==n.NEW_IP&&"0"!==n.NEW_IP;return new Response(JSON.stringify({new_ip:e,message:"请通过 Cloudflare Dashboard 或 wrangler.toml set NEW_IP env变量"}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}if(!C)return fetch(h+"/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(n.KV&&"function"==typeof n.KV.get){const r=l.pathname.slice(1).toLowerCase(),o=l.pathname.slice(1);if(o===A&&"勿动此def密钥,有需求请自行通过添加变量KEY进行修改"!==A){const e=new URLSearchParams(l.search);return e.set("token",await $(j+_)),new Response("redirect中...",{status:302,headers:{Location:`/sub?${e.toString()}`}})}if("login"===r){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n==await $(w+A+C))return new Response("redirect中...",{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 $(w+A+C)}; Path=/; Max-Age=86400; HttpOnly`),e}}return fetch(h+"/login")}if("admin"===r||r.startsWith("admin/")){const a=e.headers.get("Cookie")||"",i=a.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(!i||i!==await $(w+A+C))return new Response("redirect中...",{status:302,headers:{Location:"/login"}});if("admin/log.json"===r){const e=await n.KV.get("log.json")||"[]";return new Response(e,{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}if("admin/getCloudflareUsage"===o)try{const e=await U(l.searchParams.get("Email"),l.searchParams.get("GlobalAPIKey"),l.searchParams.get("AccountID"),l.searchParams.get("APIToken"));return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json"}})}catch(e){const t={msg:"查询request量failed,failed原因:"+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"===o){if(l.searchParams.get("url")){const e=l.searchParams.get("url");try{new URL(e);const t=await P([e],l.searchParams.get("port")||"443"),n=t[0].length>0?t[0]:t[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:"verify优选APIfailed,failed原因:"+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"===r){let e;if(l.searchParams.has("socks5"))e=await k("socks5",l.searchParams.get("socks5"));else{if(!l.searchParams.has("http"))return new Response(JSON.stringify({error:"缺少proxyparam"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});e=await k("http",l.searchParams.get("http"))}return new Response(JSON.stringify(e,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}}if(t=await T(n,j,_,n.PATH),"admin/init"===r)try{return t=await T(n,j,_,n.PATH,!0),s.waitUntil(x(n,e,D,"Init_Config",t)),t.init="config已reset为defvalue",new Response(JSON.stringify(t,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}catch(e){const t={msg:"configresetfailed,failed原因:"+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"===r)try{const r=await e.json();return r.UUID&&r.HOST?(await n.KV.put("config.json",JSON.stringify(r,null,2)),s.waitUntil(x(n,e,D,"Save_Config",t)),new Response(JSON.stringify({success:!0,message:"config已save"}),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})):new Response(JSON.stringify({error:"config不完整"}),{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"===r)try{const r=await e.json(),o={Email:null,GlobalAPIKey:null,AccountID:null,APIToken:null,UsageAPI:null};if(!r.init||!0!==r.init)if(r.Email&&r.GlobalAPIKey)o.Email=r.Email,o.GlobalAPIKey=r.GlobalAPIKey;else if(r.AccountID&&r.APIToken)o.AccountID=r.AccountID,o.APIToken=r.APIToken;else{if(!r.UsageAPI)return new Response(JSON.stringify({error:"config不完整"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});o.UsageAPI=r.UsageAPI}return await n.KV.put("cf.json",JSON.stringify(o,null,2)),s.waitUntil(x(n,e,D,"Save_Config",t)),new Response(JSON.stringify({success:!0,message:"config已save"}),{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"===r)try{const r=await e.json();if(r.init&&!0===r.init){const e={BotToken:null,ChatID:null};await n.KV.put("tg.json",JSON.stringify(e,null,2))}else{if(!r.BotToken||!r.ChatID)return new Response(JSON.stringify({error:"config不完整"}),{status:400,headers:{"Content-Type":"application/json;charset=utf-8"}});await n.KV.put("tg.json",JSON.stringify(r,null,2))}return s.waitUntil(x(n,e,D,"Save_Config",t)),new Response(JSON.stringify({success:!0,message:"config已save"}),{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"!==o)return new Response(JSON.stringify({error:"不支持的POSTrequestpath"}),{status:404,headers:{"Content-Type":"application/json;charset=utf-8"}});try{const r=await e.text();return await n.KV.put("ADD.txt",r),s.waitUntil(x(n,e,D,"Save_Custom_IPs",t)),new Response(JSON.stringify({success:!0,message:"customIP已save"}),{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"===r)return new Response(JSON.stringify(t,null,2),{status:200,headers:{"Content-Type":"application/json"}});if("admin/ADD.txt"===o){let s=await n.KV.get("ADD.txt")||"null";return"null"==s&&(s=(await S(e,t.优选subscribegen.本地IP库.rand数量,t.优选subscribegen.本地IP库.指定port))[1]),new Response(s,{status:200,headers:{"Content-Type":"text/plain;charset=utf-8",asn:e.cf.asn}})}if("admin/cf.json"===r)return new Response(JSON.stringify(e.cf,null,2),{status:200,headers:{"Content-Type":"application/json;charset=utf-8"}})}return s.waitUntil(x(n,e,D,"Admin_Login",t)),fetch(h+"/admin")}if("logout"===r||O.test(r)){const e=new Response("redirect中...",{status:302,headers:{Location:"/login"}});return e.headers.set("Set-Cookie","auth=; Path=/; Max-Age=0; HttpOnly"),e}if("sub"===r){const r=await $(j+_);if(l.searchParams.get("token")===r){t=await T(n,j,_,n.PATH),s.waitUntil(x(n,e,D,"Get_SUB",t));const o=w.toLowerCase(),a=4102329600,i=Date.now(),c=new Date(i);c.setHours(0,0,0,0);const d=Math.floor((i-c.getTime())/864e5*24*1099511627776/2);let p=d,h=d,f=26388279066624;t.CF.Usage.success&&(p=t.CF.Usage.pages,h=t.CF.Usage.workers,f=Number.isFinite(t.CF.Usage.max)?t.CF.Usage.max/1e3*1024:102400);const g={"content-type":"text/plain; charset=utf-8","Profile-Update-Interval":t.优选subscribegen.SUBUpdateTime,"Profile-web-page-url":l.protocol+"//"+l.host+"/admin","Subscription-Userinfo":`upload=${p}; download=${h}; total=${f}; expire=${a}`,"Cache-Control":"no-store"},m=l.searchParams.has("b64")||l.searchParams.has("base64")||e.headers.get("subconverter-request")||e.headers.get("subconverter-version")||o.includes("subconverter")||o.includes("CF-Workers-SUB".toLowerCase())?"mixed":l.searchParams.has("target")?l.searchParams.get("target"):l.searchParams.has("clash")||o.includes("clash")||o.includes("meta")||o.includes("mihomo")?"clash":l.searchParams.has("sb")||l.searchParams.has("singbox")||o.includes("singbox")||o.includes("sing-box")?"singbox":l.searchParams.has("surge")||o.includes("surge")?"surge&ver=4":l.searchParams.has("quanx")||o.includes("quantumult")?"quanx":l.searchParams.has("loon")||o.includes("loon")?"loon":"mixed";o.includes("mozilla")||(g["Content-Disposition"]=`attachment; filename*=utf-8''${encodeURIComponent(t.优选subscribegen.SUBNAME)}`);const y=l.searchParams.has("surge")||o.includes("surge")?"trojan":t.protocoltype;let b="";if("mixed"===m){const s=t.enabled0RTT?t.PATH+"?ed=2560":t.PATH,r="Shadowrocket"==t.TLS分片?`&fragment=${encodeURIComponent("1,40-60,30-50,tlshello")}`:"Happ"==t.TLS分片?`&fragment=${encodeURIComponent("3,1,tlshello")}`:"";let o=[],a="";if(!l.searchParams.has("sub")&&t.优选subscribegen.local){const s=t.优选subscribegen.本地IP库.randIP?(await S(e,t.优选subscribegen.本地IP库.rand数量,t.优选subscribegen.本地IP库.指定port))[0]:await n.KV.get("ADD.txt")?await I(await n.KV.get("ADD.txt")):(await S(e,t.优选subscribegen.本地IP库.rand数量,t.优选subscribegen.本地IP库.指定port))[0],r=[],i=[],c=[];for(const e of s)if(e.toLowerCase().startsWith("https://"))r.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 i.push(e);const l=await P(r),d=[...new Set(c.concat(l[1]))];a=d.length>0?d.join("\n")+"\n":"";const u=l[0];o=[...new Set(i.concat(u))]}else{let e=l.searchParams.get("sub")||t.优选subscribegen.SUB;e=e&&!/^https?:\/\//i.test(e)?`https://${e}`:e;const n=`${e}/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("优选subscribegen器异常:"+e.statusText,{status:e.status});const t=atob(await e.text()),s=t.includes("\r\n")?t.split("\r\n"):t.split("\n");for(const e of s)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],s="";const r=e.match(/#(.+)$/);r&&(s="#"+decodeURIComponent(r[1])),o.push(n+s)}}else a+=e+"\n"}catch(e){return new Response("优选subscribegen器异常:"+e.message,{status:403})}}const i=t.ECH?`&ech=${encodeURIComponent("cloudflare-ech.com+"+u)}`:"";b=a+o.map(e=>{const n=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,c="443";return n?(o=n[1],c=n[2]||"443",a=n[3]||o,`${y}://00000000-0000-4000-8000-000000000000@${o}:${c}?security=tls&type=${t.传输protocol+i}&host=example.com&fp=${t.Fingerprint}&sni=example.com&path=${encodeURIComponent(t.randpath?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("/")}`}()+s:s)+r}&encryption=none${t.skip证书verify?"&insecure=1&allowInsecure=1":""}#${encodeURIComponent(a)}`):(console.warn(`[subscribe内容] 不规范的IP格式已忽略: ${e}`),null)}).filter(e=>null!==e).join("\n")}else{const e=`${t.subscribeconvertconfig.SUBAPI}/sub?target=${m}&url=${encodeURIComponent(l.protocol+"//"+l.host+"/sub?target=mixed&token="+r+(l.searchParams.has("sub")&&""!=l.searchParams.get("sub")?`&sub=${l.searchParams.get("sub")}`:""))}&config=${encodeURIComponent(t.subscribeconvertconfig.SUBCONFIG)}&emoji=${t.subscribeconvertconfig.SUBEMOJI}&scv=${t.skip证书verify}`;try{const n=await fetch(e,{headers:{"User-Agent":"Subconverter for "+m+" edgetunnel(https://github.com/cmliu/edgetunnel)"}});if(!n.ok)return new Response("subscribeconvert后端异常:"+n.statusText,{status:n.status});b=await n.text(),(l.searchParams.has("surge")||o.includes("surge"))&&(b=function(e,t,n){const s=e.includes("\r\n")?e.split("\r\n"):e.split("\n");let r="";const o=n.enabled0RTT?n.PATH+"?ed=2560":n.PATH;for(let e of s)if(!e.includes("= trojan,")||e.includes("ws=true")||e.includes("ws-path="))r+=e+"\n";else{const t=e.split("sni=")[1].split(",")[0],s=`sni=${t}, skip-cert-verify=${n.skip证书verify}`,a=`sni=${t}, skip-cert-verify=${n.skip证书verify}, ws=true, ws-path=${o}, ws-headers=Host:"${t}"`;r+=e.replace(new RegExp(s,"g"),a).replace("[","").replace("]","")+"\n"}return r=`#!MANAGED-CONFIG ${t} interval=${60*n.优选subscribegen.SUBUpdateTime*60} strict=false`+r.substring(r.indexOf("\n")),r}(b,l.protocol+"//"+l.host+"/sub?token="+r+"&surge",t))}catch(e){return new Response("subscribeconvert后端异常:"+e.message,{status:403})}}return o.includes("subconverter")||(b=await function(e,n,s=2){const r=[...t.HOSTS].sort(()=>Math.random()-.5);let o=0,a=null;return e.replace(/example\.com/g,()=>(o%s===0&&(a=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 s=[];Array.isArray(e.inet4_route_exclude_address)&&s.push(...e.inet4_route_exclude_address),Array.isArray(e.inet6_route_exclude_address)&&s.push(...e.inet6_route_exclude_address),s.length>0&&(e.route_exclude_address=s,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",s=String(e[n]).toUpperCase();"REJECT"!==s&&"BLOCK"!==s||(e.action="reject",e.method="drop",delete e[n])})};r.dns&&r.dns.rules&&a(r.dns.rules,!0),r.route&&r.route.rules&&a(r.route.rules,!1),o.size>0&&(r.route||(r.route={}),r.route.rule_set=Array.from(o.values())),r.outbounds||(r.outbounds=[]),r.outbounds=r.outbounds.filter(e=>"REJECT"!==e.tag&&"block"!==e.tag);const i=new Set(r.outbounds.map(e=>e.tag));if(i.has("DIRECT")||(r.outbounds.push({type:"direct",tag:"DIRECT"}),i.add("DIRECT")),r.dns&&r.dns.servers){const e=new Set(r.dns.servers.map(e=>e.tag));r.dns.rules&&r.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)&&(r.dns.servers.push({tag:t.server,address:"rcode://success"}),e.add(t.server)))})}return r.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&&r.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}),s&&(e.tls.ech={enabled:!0,config:`-----BEGIN ECH CONFIGS-----\n${s}\n-----END ECH CONFIGS-----`}))}),JSON.stringify(r,null,2)}catch(t){return console.error("Singbox热补丁执行failed:",t),JSON.stringify(JSON.parse(e),null,2)}}(b,t.UUID,t.Fingerprint,t.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 s=2;for(;s0){const e=s.map(e=>` "${e}":\n - tls://8.8.8.8\n - https://doh.cmliussss.com/CMLiussss\n - ${u}`).join("\n");if(/^\s{2}nameserver-policy:\s*(?:\n|$)/m.test(r))r=r.replace(/^(\s{2}nameserver-policy:\s*\n)/m,`$1${e}\n`);else{const t=r.split("\n");let n=-1,s=!1;for(let e=0;e0&&i+1=0;t--)if(n[t].trim()){e=t;break}if(e>=0){const t=" ".repeat(r);n.splice(e+1,0,`${t}ech-opts:`,`${t} enable: true`)}}a.push(...n)}else a.push(e),i++}return a.join("\n")}(b,t.UUID,t.ECH,t.HOSTS),g["content-type"]="application/x-yaml; charset=utf-8"),new Response(b,{status:200,headers:g})}}else if("locations"===r){const t=e.headers.get("Cookie")||"",n=t.split(";").find(e=>e.trim().startsWith("auth="))?.split("=")[1];if(n&&n==await $(w+A+C))return fetch(new Request("https://speed.cloudflare.com/locations",{headers:{Referer:"https://speed.cloudflare.com/"}}))}else if("robots.txt"===r)return new Response("User-agent: *\nDisallow: /",{status:200,headers:{"Content-Type":"text/plain; charset=UTF-8"}})}else if(!R)return fetch(h+"/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 N=n.URL||"nginx";if(N&&"nginx"!==N&&"1101"!==N){N=N.trim().replace(/\/$/,""),N.match(/^https?:\/\//i)||(N="https://"+N),N.toLowerCase().startsWith("http://")&&(N="https://"+N.substring(7));try{const e=new URL(N);N=e.protocol+"//"+e.host}catch(e){N="nginx"}}if("1101"===N)return new Response(await async function(e,t){const n=new Date,s=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"),r=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