forked from rusifeng88/stallTCP1.3V2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsnippets.js
More file actions
180 lines (164 loc) · 31.5 KB
/
Copy pathsnippets.js
File metadata and controls
180 lines (164 loc) · 31.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { connect } from 'cloudflare:sockets';
// =============================================================================
// 🟣 用户配置区域 【请手动修改下方参数】
// =============================================================================
const UUID = "06b65903-406d-4a41-8463-6fd5c0ee7798"; // 修改可用的uuid
const WEB_PASSWORD = "你的登录密码"; //自己要修改自定义的登录密码
const SUB_PASSWORD = "你的订阅密码"; // 自己要修改自定义的订阅密码
const DEFAULT_PROXY_IP = "ProxyIP.US.CMLiussss.net"; //可修改自定义的proxyip
const DEFAULT_SUB_DOMAIN = "sub.cmliussss.net"; //可修改自定义的sub订阅器
const NAVIGATION_URL = "https://nva.saas.ae.kg/"; // 🧭 导航按钮链接
const TG_GROUP_URL = "https://t.me/zyssadmin"; //可修改自定义内容
const PROXY_CHECK_URL = "https://kaic.hidns.co/"; //可修改自定义的proxyip检测站
const DEFAULT_CONVERTER = "https://subapi.cmliussss.net"; //可修改自定义后端api
const CLASH_CONFIG = "https://raw.githubusercontent.com/cmliu/ACL4SSR/main/Clash/config/ACL4SSR_Online_Full_MultiMode.ini"; //可修改自定义订阅配置转换ini
const SINGBOX_CONFIG_V12 = "https://raw.githubusercontent.com/sinspired/sub-store-template/main/1.12.x/sing-box.json"; //禁止修改 优先使用1.12 后用1.11
const SINGBOX_CONFIG_V11 = "https://raw.githubusercontent.com/sinspired/sub-store-template/main/1.11.x/sing-box.json"; //禁止修改
const TG_BOT_TOKEN = ""; //你的机器人token
const TG_CHAT_ID = ""; //你的TG ID
const ADMIN_IP = ""; //你的白名单IP 保护你不会被自己域名拉黑 (支持多IP,IPV4跟IPV6 使用英文逗号分隔)
// =============================================================================
// ⚡️ 核心常量
// =============================================================================
const MAX_PENDING=2*1024*1024,KEEPALIVE=15e3,STALL_TO=8e3,MAX_STALL=12,MAX_RECONN=24,S_TAG="s"+"ocks",S5_TAG=S_TAG+"5";
// =============================================================================
// 🟢 主入口
// =============================================================================
export default {
async fetch(r, e, c) {
try {
const u = new URL(r.url), UA = (r.headers.get("User-Agent")||"").toLowerCase();
// WebSocket -> StallTCP
if (r.headers.get("Upgrade")?.toLowerCase() === "websocket") {
const { proxyIP: pip, s5, enableSocks: es, globalProxy: gp } = parsePC(u.pathname);
const { 0: cl, 1: sv } = new WebSocketPair();
sv.accept();
handleTCP(sv, pip, s5, es, gp);
return new Response(null, { status: 101, webSocket: cl });
}
const isSub = (SUB_PASSWORD && u.pathname === `/${SUB_PASSWORD}`);
if (!isSub && /bot|spider|python|curl|wget|crawler/i.test(UA)) return new Response("403 Forbidden", { status: 403 });
if ("/favicon.ico" === u.pathname) return new Response(null, { status: 404 });
const flg = u.searchParams.get("flag");
if (flg === "github") { await sTg(c, "点击了烈火项目", r, "来源: 登录页面直达链接"); return new Response(null, { status: 204 }); }
if (flg === "proxycheck") { await sTg(c, "🛠️ 点击了ProxyIP检测站", r, "管理员操作"); return new Response(null, { status: 204 }); }
if (flg === "test") { await sTg(c, "🚀 点击了手动订阅测试", r, "管理员操作"); return new Response(null, { status: 204 }); }
if (isSub) return await hSub(r, c, u, UA, u.hostname);
if ("/sub" === u.pathname) {
if (u.searchParams.get("uuid") !== UUID) return new Response("Invalid UUID", { status: 403 });
const t = sTg(c, "常规订阅访问 (/sub)", r); c?.waitUntil?.(t);
return new Response("", { status: 200, headers: { "Content-Type": "text/plain; charset=utf-8" } });
}
const h = { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" };
if (WEB_PASSWORD?.trim().length > 0) {
const ck = r.headers.get("Cookie") || "";
const auth = ck.match(/auth=([^;]+)/)?.[1];
if (auth !== WEB_PASSWORD) {
// 逻辑修改:只有当 auth 存在(即用户输入过密码)但错误时,才传入 true 显示错误提示
// 如果 auth 为 undefined(首次访问),传入 false,不显示错误
return new Response(pLogin(!!auth), { status: 200, headers: h });
}
}
await sTg(c, "✅ 后台登录成功", r, "进入管理面板");
return new Response(pDash(u.hostname, UUID), { status: 200, headers: h });
} catch (err) { return new Response(err.toString(), { status: 500 }); }
}
};
// =============================================================================
// 🔧 辅助与解析
// =============================================================================
const bUUID=(a,i)=>[...a.slice(i,i+16)].map(n=>n.toString(16).padStart(2,'0')).join('').replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/,'$1-$2-$3-$4-$5');
const xAddr=b=>{
const o1=18+b[17]+1,p=(b[o1]<<8)|b[o1+1],t=b[o1+2];let o2=o1+3,h,l;
if(t===1){l=4;h=b.slice(o2,o2+l).join('.');}
else if(t===2){l=b[o2++];h=new TextDecoder().decode(b.slice(o2,o2+l));}
else if(t===3){l=16;h=`[${Array.from({length:8},(_,i)=>((b[o2+i*2]<<8)|b[o2+i*2+1]).toString(16)).join(':')}]`;}
else throw new Error('Type?');
return {host:h,port:p,payload:b.slice(o2+l),addressType:t};
};
const pAddrPt=s=>{if(s.startsWith("[")){const m=s.match(/^\[(.+?)\]:(\d+)$/);return m?[m[1],Number(m[2])]:[s.slice(1,-1),443];}const[a,p]=s.split(":");return[a,Number(p)||443];};
const pS5=(r)=>{
let u,p,h,pt;
if(r.includes('://')&&!r.match(new RegExp(`^(${S_TAG}5?|https?):\\/\\/`,'i'))){
const U=new URL(r);h=U.hostname;pt=U.port||(U.protocol==='http:'?80:1080);
const A=U.username||U.password?`${U.username}:${U.password}`:U.username;
if(A){if(A.includes(':'))[u,p]=A.split(':');else try{const d=atob(A.replace(/%3D/g,'=').padEnd(A.length+(4-A.length%4)%4,'=')).split(':');if(d.length===2)[u,p]=d;}catch{}}
}else{
let aP='',hP=r;const at=r.lastIndexOf('@');if(at!==-1){aP=r.substring(0,at);hP=r.substring(at+1);}
if(aP&&!aP.includes(':'))try{const d=atob(aP.replace(/%3D/g,'=').padEnd(aP.length+(4-aP.length%4)%4,'=')).split(':');if(d.length===2)[u,p]=d;}catch{}
if(!u&&aP&&aP.includes(':'))[u,p]=aP.split(':');
const[H,P]=pAddrPt(hP);h=H;pt=P||(r.includes('http=')?80:1080);
}
if(!h||isNaN(pt))throw new Error("Cfg Err");return{username:u,password:p,hostname:h,port:pt};
};
function parsePC(p){
let pip=null,s5=null,es=null,gp=null;
const gm=p.match(new RegExp(`(${S_TAG}5?|https?):\\/\\/([^/#?]+)`,'i'));
if(gm){gp={type:gm[1].toLowerCase().includes('5')||gm[1].includes(S_TAG)?S5_TAG:'http',cfg:pS5(gm[2])};return{proxyIP:pip,s5,enableSocks:es,globalProxy:gp};}
const im=p.match(/(?:^|\/)(?:proxy)?ip[=\/]([^?#]+)/i);
if(im){const[a,rt]=pAddrPt(im[1]);pip={address:a.includes('[')?a.slice(1,-1):a,port:+rt};}
const lm=p.match(new RegExp(`(?:^|\\/)(${S_TAG}5?|s5|http)[=\\/]([^/#?]+)`,'i'));
if(lm){s5=pS5(lm[2]);es=lm[1].toLowerCase().includes('http')?'http':S5_TAG;}
return{proxyIP:pip,s5,enableSocks:es,globalProxy:gp};
}
// =============================================================================
// 🚀 Socket 连接
// =============================================================================
async function cS5(t,a,p,c){
const{username:u,password:pw,hostname:h,port:pt}=c,s=connect({hostname:h,port:pt}),w=s.writable.getWriter();
await w.write(new Uint8Array([5,u?2:1,0,u?2:0]));
const r=s.readable.getReader(),enc=new TextEncoder();
let v=(await r.read()).value;
if(v[1]===2){await w.write(new Uint8Array([1,u.length,...enc.encode(u),pw.length,...enc.encode(pw)]));v=(await r.read()).value;if(v[1]!==0)throw new Error("Auth Fail");}
let D;if(t===1)D=new Uint8Array([1,...a.split(".").map(Number)]);else if(t===2)D=new Uint8Array([3,a.length,...enc.encode(a)]);else{const b=a.slice(1,-1).split(':').flatMap(x=>[parseInt(x.slice(0,2),16),parseInt(x.slice(2,4),16)]);D=new Uint8Array([4,...b]);}
await w.write(new Uint8Array([5,1,0,...D,(p>>8)&0xff,p&0xff]));v=(await r.read()).value;if(v[1]!==0)throw new Error("Conn Fail");
w.releaseLock();r.releaseLock();return s;
}
async function cH(t,a,p,c){
const{username:u,password:pw,hostname:h,port:pt}=c,s=connect({hostname:h,port:pt}),w=s.writable.getWriter();
const q=`CONNECT ${a}:${p} HTTP/1.1\r\nHost: ${a}:${p}\r\n`+(u&&pw?`Proxy-Authorization: Basic ${btoa(`${u}:${pw}`)}\r\n`:'')+"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36\r\nConnection: keep-alive\r\n\r\n";
await w.write(new TextEncoder().encode(q));w.releaseLock();
const r=s.readable.getReader();let b=new Uint8Array(0);
while(true){const{value:v,done:d}=await r.read();if(d)throw new Error("Cls");const n=new Uint8Array(b.length+v.length);n.set(b);n.set(v,b.length);b=n;const txt=new TextDecoder().decode(b);if(txt.includes("\r\n\r\n")){if(/^HTTP\/1\.[01] 2/i.test(txt)){r.releaseLock();return s;}throw new Error("Refused");}}
}
// =============================================================================
// 🧠 StallTCP 核心
// =============================================================================
class Pool{constructor(){this.b=new ArrayBuffer(16384);this.p=0;this.l=[];this.m=8;}a(s){if(s<=4096&&s<=16384-this.p){const v=new Uint8Array(this.b,this.p,s);this.p+=s;return v;}const r=this.l.pop();return r&&r.byteLength>=s?new Uint8Array(r.buffer,0,s):new Uint8Array(s);}f(b){if(b.buffer===this.b){this.p=Math.max(0,this.p-b.length);return;}if(this.l.length<this.m&&b.byteLength>=1024)this.l.push(b);}r(){this.p=0;this.l=[];}}
const handleTCP=(ws,pip,s5,es,gp)=>{
const pl=new Pool();let sk,w,r,inf,fst=true,rx=0,stl=0,rc=0,lA=Date.now(),cn=false,rd=false,tm={},pd=[],pb=0,sc=1.0,lC=Date.now(),lR=0;
let st={t:0,c:0,w:0,ts:Date.now()},md='adaptive',as=0,tp=[];
const uMd=s=>{st.t+=s;st.c++;as=as*0.9+s*0.1;const n=Date.now();if(n-st.ts>1000){tp.push(st.w);if(tp.length>5)tp.shift();st.w=s;st.ts=n;const avg=tp.reduce((a,b)=>a+b,0)/tp.length;if(st.c>=20)md=(avg<8388608||as<4096)?'buffered':(avg>16777216&&as>12288)?'direct':'adaptive';}else st.w+=s;};
const rL=async()=>{if(rd)return;rd=true;let bt=[],bz=0,bT=null;const fl=()=>{if(!bz)return;const m=new Uint8Array(bz);let p=0;for(const c of bt){m.set(c,p);p+=c.length;}if(ws.readyState===1)ws.send(m);bt=[];bz=0;if(bT){clearTimeout(bT);bT=null;}};try{while(true){if(pb>MAX_PENDING){await new Promise(r=>setTimeout(r,100));continue;}const{done:d,value:v}=await r.read();if(v?.length){rx+=v.length;lA=Date.now();stl=0;uMd(v.length);const n=Date.now();if(n-lC>5000){const el=n-lC,by=rx-lR,t=by/el;if(t>500)sc=Math.min(1,sc+0.05);else if(t<50)sc=Math.max(0.1,sc-0.05);lC=n;lR=rx;}if(md==='buffered'){if(v.length<16384){bt.push(v);bz+=v.length;if(bz>=65536)fl();else if(!bT)bT=setTimeout(fl,as>8192?8:25);}else{fl();if(ws.readyState===1)ws.send(v);}}else if(md==='direct'){fl();if(ws.readyState===1)ws.send(v);}else{if(v.length<8192){bt.push(v);bz+=v.length;if(bz>=49152)fl();else if(!bT)bT=setTimeout(fl,12);}else{fl();if(ws.readyState===1)ws.send(v);}}}if(d){fl();rd=false;rec();break;}}}catch{fl();if(bT)clearTimeout(bT);rd=false;rec();}};
const tC=async(h,p,t)=>{if(gp)return gp.type===S5_TAG?await cS5(t,h,p,gp.cfg):await cH(t,h,p,gp.cfg);try{const s=connect({hostname:h,port:p});if(s.opened)await s.opened;return s;}catch(e){if(!s5&&!pip)throw e;if(s5)try{const s=es==='http'?await cH(t,h,p,s5):await cS5(t,h,p,s5);if(s.opened)await s.opened;return s;}catch{}if(pip)try{const s=connect({hostname:pip.address,port:pip.port});if(s.opened)await s.opened;return s;}catch{}throw e;}};
const est=async()=>{try{sk=await tC(inf.host,inf.port,inf.addressType);if(sk.opened)await sk.opened;w=sk.writable.getWriter();r=sk.readable.getReader();const bt=pd.splice(0,10);for(const b of bt){await w.write(b);pb-=b.length;pl.f(b);}cn=false;rc=0;sc=Math.min(1,sc+0.15);lA=Date.now();rL();}catch{cn=false;sc=Math.max(0.1,sc-0.2);rec();}};
const rec=async()=>{if(!inf||ws.readyState!==1||rc>=MAX_RECONN){cln();ws.close(1011);return;}if(cn)return;rc++;let d=Math.min(50*Math.pow(1.5,rc-1),3000)*(1.5-sc*0.5);try{cls();if(pb>MAX_PENDING*2)while(pb>MAX_PENDING&&pd.length>5){const dp=pd.shift();pb-=dp.length;pl.f(dp);}await new Promise(r=>setTimeout(r,Math.max(50,Math.floor(d))));cn=true;sk=connect({hostname:inf.host,port:inf.port});await sk.opened;w=sk.writable.getWriter();r=sk.readable.getReader();const bt=pd.splice(0,10);for(const b of bt){await w.write(b);pb-=b.length;pl.f(b);}cn=false;rc=0;sc=Math.min(1,sc+0.15);stl=0;lA=Date.now();rL();}catch{cn=false;sc=Math.max(0.1,sc-0.2);if(rc<MAX_RECONN&&ws.readyState===1)setTimeout(rec,500);else{cln();ws.close(1011);}}};
const sTm=()=>{tm.ka=setInterval(async()=>{if(!cn&&w&&Date.now()-lA>KEEPALIVE){try{await w.write(new Uint8Array(0));lA=Date.now();}catch{rec();}}},KEEPALIVE/3);tm.hc=setInterval(()=>{if(!cn&&st.t>0&&Date.now()-lA>STALL_TO){stl++;if(stl>=MAX_STALL){stl=0;rec();}}},STALL_TO/2);};
const cls=()=>{rd=false;try{w?.releaseLock();r?.releaseLock();sk?.close();}catch{}};
const cln=()=>{Object.values(tm).forEach(clearInterval);cls();while(pd.length)pl.f(pd.shift());pb=0;pl.r();};
ws.addEventListener('message',async e=>{try{if(fst){fst=false;const b=new Uint8Array(e.data);if(bUUID(b,1)!==UUID)throw 0;const{host:h,port:p,payload:l,addressType:t}=xAddr(b);inf={host:h,port:p,addressType:t};ws.send(new Uint8Array([b[0],0]));cn=true;if(l.length){const bf=pl.a(l.length);bf.set(l);pd.push(bf);pb+=bf.length;}sTm();est();}else{lA=Date.now();if(cn||!w){const bf=pl.a(e.data.byteLength);bf.set(new Uint8Array(e.data));pd.push(bf);pb+=bf.length;}else await w.write(e.data);}}catch{cln();ws.close(1006);}});ws.addEventListener('close',cln);ws.addEventListener('error',cln);
};
// =============================================================================
// 📋 订阅与通知
// =============================================================================
async function sTg(c,t,r,n=""){
if(!TG_BOT_TOKEN||!TG_CHAT_ID||!r)return;try{const s=r.headers.get("cf-connecting-ip")||"Uk",u=new URL(r.url),i=r.cf?.city||"Uk",p=(ADMIN_IP&&ADMIN_IP.split(',').some(x=>x.trim()===s))?"🛡️ [管理员]":"👤 [用户]";const m=`<b>${p} ${t}</b>\n\n<b>🕒 时间:</b> <code>${new Date().toLocaleString("zh-CN",{timeZone:"Asia/Shanghai"})}</code>\n<b>🌍 IP:</b> <code>${s} (${i})</code>\n<b>🔗 域名:</b> <code>${u.hostname}</code>\n<b>🛣️ 路径:</b> <code>${u.pathname}</code>\n<b>📱 客户端:</b> <code>${r.headers.get("User-Agent")}</code>\n`+(n?`<b>ℹ️ 详情:</b> ${n}`:"");const f=fetch(`https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:TG_CHAT_ID,text:m,parse_mode:"HTML",disable_web_page_preview:true})});if(c?.waitUntil){c.waitUntil(f);}else{await f;}}catch(e){console.error(e);}
}
async function hSub(r,c,u,UA,h){
const flg=u.searchParams.has("flag"),now=Date.now(),_d=s=>atob(s);
const cr=[['TWlob21v','bWlob21v'],['RmxDbGFzaA==','ZmxjbGFzaA=='],['Q2xhc2g=','Y2xhc2g='],['Q2xhc2g=','bWV0YQ=='],['Q2xhc2g=','c3Rhc2g='],['SGlkZGlmeQ==','aGlkZGlmeQ=='],['U2luZy1ib3g=','c2luZy1ib3g='],['U2luZy1ib3g=','c2luZ2JveA=='],['U2luZy1ib3g=','c2Zp'],['U2luZy1ib3g=','Ym94'],['djJyYXlOL0NvcmU=','djJyYXk='],['U3VyZ2U=','c3VyZ2U='],['UXVhbnR1bXVsdCBY','cXVhbnR1bXVsdA=='],['U2hhZG93cm9ja2V0','c2hhZG93cm9ja2V0'],['TG9vbg==','bG9vbg=='],['SGFB','aGFwcA==']];
let cn="未知客户端",ipc=false;for(const[n,k]of cr){if(UA.includes(_d(k))){cn=_d(n);ipc=true;break;}}if(!ipc&&(UA.includes("mozilla")||UA.includes("chrome")))cn="浏览器";
if(!flg){const p=sTg(c,ipc?"🔄 快速订阅更新":"🌐 访问快速订阅页",r,`类型: ${cn}`);c?.waitUntil?.(p);}
const iS=["Sing-box","Hiddify"].includes(cn),iC=["Clash","Mihomo","FlClash"].includes(cn);
if(iS&&!flg){const t=u.searchParams.get("proxyip");let n=`https://${h}/${SUB_PASSWORD}?flag=true`;t&&(n+=`&proxyip=${encodeURIComponent(t)}`);const s=`${DEFAULT_CONVERTER}/sub?target=singbox&url=${encodeURIComponent(n)}&config=${encodeURIComponent(UA.includes("1.11.")?SINGBOX_CONFIG_V11:SINGBOX_CONFIG_V12)}&emoji=true&list=false&sort=false&fdn=false&scv=false&_t=${now}`,o=await fetch(s);if(!o.ok)return new Response("Err",{status:500});const hd=new Headers(o.headers);hd.set("Cache-Control","no-store");return new Response(o.body,{status:200,headers:hd});}
if(iC&&!flg){const t=u.searchParams.get("proxyip");let n=`https://${h}/${SUB_PASSWORD}?flag=true`;t&&(n+=`&proxyip=${encodeURIComponent(t)}`);const a=`${DEFAULT_CONVERTER}/sub?target=clash&url=${encodeURIComponent(n)}&config=${encodeURIComponent(CLASH_CONFIG)}&emoji=true&list=false&tfo=false&scv=false&fdn=false&sort=false&_t=${now}`,s=await fetch(a);if(!s.ok)return new Response("Err",{status:500});const hd=new Headers(s.headers);hd.set("Cache-Control","no-store");return new Response(s.body,{status:200,headers:hd});}
let up=DEFAULT_SUB_DOMAIN.trim().replace(/^https?:\/\//,"").replace(/\/$/,"")||h,pip=u.searchParams.get("proxyip");if(!pip&&DEFAULT_PROXY_IP)pip=DEFAULT_PROXY_IP;
let tp=(pip&&pip.trim())?`/proxyip=${pip.trim()}`:"/";
const p=new URLSearchParams();p.append("uuid",UUID);p.append("host",up);p.append("sni",up);p.append("path",tp);p.append("type","ws");p.append("encryption","none");p.append("security","tls");p.append("alpn","h3");p.append("fp","random");p.append("allowInsecure","1");
try{const e=await fetch(`https://${up}/sub?${p.toString()}`,{headers:{"User-Agent":"Mozilla/5.0"}});if(e.ok){let t=atob(await e.text());t=t.replace(/path=[^&#]*/g,`path=${encodeURIComponent(tp)}&udp=false`).replace(/host=[^&]*/g,`host=${h}`).replace(/sni=[^&]*/g,`sni=${h}`);return new Response(btoa(t),{status:200,headers:{"Content-Type":"text/plain; charset=utf-8"}});}}catch{}return new Response("",{status:200,headers:{"Content-Type":"text/plain; charset=utf-8"}});
}
// =============================================================================
// 🖥️ UI
// =============================================================================
function pLogin(e){return`<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>管理员登陆</title><style>@keyframes gradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-10px)}}@keyframes fadeIn{0%{opacity:0;transform:scale(.95)}100%{opacity:1;transform:scale(1)}}body{background:linear-gradient(135deg,#0a0e27,#141b2d,#1a1f3a,#0f1419);background-size:400% 400%;animation:gradient 15s ease infinite;color:#fff;font-family:'Segoe UI',sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;overflow:hidden}body::before{content:'';position:absolute;width:200%;height:200%;background:radial-gradient(circle,rgba(20,27,45,.3) 0%,transparent 70%);animation:float 8s ease-in-out infinite;pointer-events:none}.glass-box{background:rgba(255,255,255,.12);backdrop-filter:blur(15px);border:1px solid rgba(255,255,255,.25);padding:40px;border-radius:20px;box-shadow:0 8px 32px rgba(31,38,135,.4),inset 0 1px 0 rgba(255,255,255,.3);text-align:center;width:320px;position:relative;animation:fadeIn .6s ease-out;z-index:1}h2{margin-top:0;margin-bottom:20px;font-weight:600;letter-spacing:1px;text-shadow:0 2px 10px rgba(0,0,0,.3)}input{width:100%;padding:14px;margin-bottom:20px;border-radius:10px;border:1px solid rgba(255,255,255,.35);background:rgba(0,0,0,.25);color:#fff;box-sizing:border-box;text-align:center;font-size:1rem;outline:none;transition:.3s;box-shadow:inset 0 2px 5px rgba(0,0,0,.2)}input:focus{background:rgba(0,0,0,.35);border-color:#a29bfe;box-shadow:0 0 15px rgba(162,155,254,.4)}button{width:100%;padding:12px;border-radius:10px;border:none;background:linear-gradient(135deg,#a29bfe,#6c5ce7,#5f3dc4);background-size:200% 200%;color:#fff;font-weight:700;cursor:pointer;font-size:1rem;box-shadow:0 4px 15px rgba(108,92,231,.4);transition:.3s;animation:gradient 3s ease infinite}button:hover{transform:translateY(-3px);box-shadow:0 6px 25px rgba(108,92,231,.6)}.nav-btn{margin-top:15px;background:linear-gradient(135deg,#00b894,#00cec9);box-shadow:0 4px 15px rgba(0,184,148,.4)}.nav-btn:hover{box-shadow:0 6px 25px rgba(0,184,148,.6);transform:translateY(-3px)}.social-links{margin-top:20px;display:flex;justify-content:space-between;gap:10px;border-top:1px solid rgba(255,255,255,.15);padding-top:20px}.social-links a{flex:1;text-decoration:none;color:#e2e8f0;font-size:.9rem;padding:12px;background:rgba(0,0,0,.3);border-radius:10px;border:1px solid rgba(255,255,255,.2);transition:.3s;display:flex;align-items:center;justify-content:center;gap:5px;box-shadow:0 2px 8px rgba(0,0,0,.2);white-space:nowrap}.social-links a:hover{background:rgba(162,155,254,.3);transform:translateY(-2px);border-color:#a29bfe;box-shadow:0 4px 12px rgba(162,155,254,.4)}.error-msg{background:rgba(231,76,60,.35);border:1px solid rgba(231,76,60,.6);color:#ff7675;padding:10px;border-radius:10px;margin-bottom:15px;font-size:.9rem;display:${e?"block":"none"};animation:fadeIn .4s ease-out}</style></head><body><div class="glass-box"><h2>🔒 管理员登陆</h2><div class="error-msg">⚠️ 密码错误,请重试</div><input type="password" id="pwd" placeholder="请输入密码" autofocus onkeypress="if(event.keyCode===13)verify()"><button onclick="verify()">立即登陆</button><button class="nav-btn" onclick="window.open('${NAVIGATION_URL}','_blank')">🧭 天诚网站导航</button><div class="social-links"><a href="javascript:void(0)" onclick="gh()">🔥 烈火项目直达</a><a href="${TG_GROUP_URL}" target="_blank">✈️ 天诚交流群</a></div></div><script>function gh(){fetch("?flag=github&t="+Date.now(),{keepalive:!0});window.open("https://github.com/xtgm/stallTCP1.3V1","_blank")}function verify(){const p=document.getElementById("pwd").value;document.cookie="auth="+p+";path=/",location.reload()}<\/script></body></html>`;}
function pDash(e,t){const s=TG_BOT_TOKEN&&TG_CHAT_ID?'<div class="status-item available">🤖 Telegram 通知: <span style="color:#00b894;font-weight:bold">已开启</span></div>':'<div class="status-item">🤖 Telegram 通知: <span style="color:#fab1a0">未配置</span></div>';return`<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Worker 订阅管理</title><style>@keyframes gradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}@keyframes fadeIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}@keyframes pulse{0%,100%{opacity:.6}50%{opacity:.9}}:root{--glass:rgba(255,255,255,.12);--border:rgba(255,255,255,.25)}body{background:linear-gradient(135deg,#0a0e27,#141b2d,#1a1f3a,#0f1419);background-size:400% 400%;animation:gradient 20s ease infinite;color:#fff;font-family:'Segoe UI',system-ui,sans-serif;margin:0;padding:20px;min-height:100vh;display:flex;justify-content:center;box-sizing:border-box}body::before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:radial-gradient(circle at 20% 50%,rgba(20,27,45,.15) 0%,transparent 50%),radial-gradient(circle at 80% 80%,rgba(15,20,25,.15) 0%,transparent 50%);animation:pulse 8s ease-in-out infinite;pointer-events:none;z-index:0}.container{max-width:800px;width:100%;position:relative;z-index:1;animation:fadeIn .8s ease-out}.card{background:var(--glass);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:20px;padding:25px;margin-bottom:20px;box-shadow:0 8px 32px rgba(0,0,0,.35),inset 0 1px 0 rgba(255,255,255,.2);transition:.3s}.card:hover{box-shadow:0 12px 40px rgba(108,92,231,.4),inset 0 1px 0 rgba(255,255,255,.3);transform:translateY(-2px)}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;padding-bottom:15px;border-bottom:1px solid var(--border)}h1{margin:0;font-size:1.5rem;font-weight:600;text-shadow:0 2px 10px rgba(0,0,0,.4)}h3{margin-top:0;font-size:1.1rem;border-bottom:1px solid var(--border);padding-bottom:10px;color:#dfe6e9}.btn-group{display:flex;gap:10px}.btn-small{font-size:.85rem;cursor:pointer;background:rgba(0,0,0,.3);padding:5px 12px;border-radius:8px;text-decoration:none;color:#fff;transition:.3s;border:1px solid rgba(255,255,255,.2);box-shadow:0 2px 8px rgba(0,0,0,.2)}.btn-small:hover{background:rgba(162,155,254,.3);border-color:#a29bfe;transform:translateY(-2px);box-shadow:0 4px 12px rgba(162,155,254,.4)}.field{margin-bottom:18px}.label{display:block;font-size:.9rem;color:#dfe6e9;margin-bottom:8px;font-weight:500}.input-group{display:flex;gap:10px}input,textarea{width:100%;background:rgba(0,0,0,.3);border:1px solid var(--border);color:#fff;padding:12px;border-radius:10px;font-family:monospace;outline:none;transition:.3s;box-sizing:border-box;box-shadow:inset 0 2px 5px rgba(0,0,0,.2)}input:focus,textarea:focus{background:rgba(0,0,0,.4);border-color:#a29bfe;box-shadow:0 0 15px rgba(162,155,254,.3)}textarea{min-height:120px;resize:vertical;line-height:1.4}button.main-btn{background:linear-gradient(135deg,#a29bfe,#6c5ce7,#5f3dc4);background-size:200% 200%;color:#fff;border:none;padding:12px 20px;border-radius:10px;cursor:pointer;font-weight:600;width:100%;margin-top:5px;transition:.3s;box-shadow:0 4px 15px rgba(108,92,231,.4);font-size:1rem;animation:gradient 3s ease infinite}button.main-btn:hover{transform:translateY(-3px);box-shadow:0 6px 25px rgba(108,92,231,.6)}button.sec-btn{background:rgba(255,255,255,.18);color:#fff;border:1px solid var(--border);padding:12px;border-radius:10px;cursor:pointer;white-space:nowrap;transition:.3s;box-shadow:0 2px 8px rgba(0,0,0,.2)}button.sec-btn:hover{background:rgba(162,155,254,.3);border-color:#a29bfe;transform:translateY(-2px);box-shadow:0 4px 12px rgba(162,155,254,.4)}.toast{position:fixed;bottom:30px;left:50%;transform:translateX(-50%);background:linear-gradient(135deg,#00b894,#00cec9);color:#fff;padding:10px 24px;border-radius:30px;opacity:0;transition:.3s;pointer-events:none;box-shadow:0 5px 20px rgba(0,184,148,.5);font-weight:700}.toast.show{opacity:1;bottom:50px}.desc{font-size:.8rem;color:#b2bec3;margin-top:6px}.checkbox-wrapper{display:flex;align-items:center;margin-top:10px;background:rgba(0,0,0,.25);padding:8px 12px;border-radius:8px;width:fit-content;border:1px solid rgba(255,255,255,.15);transition:.3s}.checkbox-wrapper:hover{background:rgba(162,155,254,.2);border-color:#a29bfe}.checkbox-wrapper input{width:auto;margin-right:8px;cursor:pointer}.checkbox-wrapper label{cursor:pointer;font-size:.9rem;color:#dfe6e9}.status-item{background:rgba(0,0,0,.25);padding:8px 12px;border-radius:8px;font-size:.9rem;margin-top:10px;display:inline-block;border:1px solid rgba(255,255,255,.15)}</style></head><body><div class="container"><div class="card"><div class="header"><h1>⚡ Worker 管理面板</h1><div class="btn-group"><a href="${TG_GROUP_URL}" target="_blank" class="btn-small">✈️ 加入群组</a><span class="btn-small" onclick="logout()">退出登录</span></div></div><div style="margin-bottom:20px;text-align:center">${s}</div><div class="field" style="background:rgba(108,92,231,.25);padding:15px;border-radius:12px;border:1px solid rgba(162,155,254,.5);box-shadow:0 4px 15px rgba(108,92,231,.2)"><span class="label" style="color:#a29bfe;font-weight:700">🚀 快速自适应订阅 (推荐) 通用订阅复制这里</span><div class="input-group"><input type="text" id="shortSub" value="https://${e}/${SUB_PASSWORD}" readonly onclick="this.select()"><button class="sec-btn" onclick="copyId('shortSub')">复制</button></div><div class="desc">直接使用此链接。支持通用订阅客户端(自适应客户端订阅)。<br/>节点将自动抓取上游并替换为Worker加速。</div><div style="margin-top:10px;font-size:.9rem;color:#ff4757;font-weight:700;text-align:center">【↓下方的可修改内容指向手动订阅链接】</div></div><div class="field"><span class="label">1. 订阅数据源 (Sub优选订阅器处)</span><input type="text" id="subBaseUrl" value="${DEFAULT_SUB_DOMAIN}" placeholder="https://你的sub地址或者是worker域名地址" oninput="updateLink()"><div class="desc">这里可修改成你的sub地址或者是你的worker域名地址。</div></div><div class="field"><span class="label">2.Proxyip修改处 (ProxyIP)</span><div class="input-group"><input type="text" id="proxyIp" value="${DEFAULT_PROXY_IP}" placeholder="例如: 你的proxyip地址" oninput="updateLink()"><button class="sec-btn" onclick="checkProxy()">🔍 检测</button></div><div class="desc">这里决定了你的proxyip地址,谨慎修改正确的proxyip地址内容。</div></div><div class="field" id="clashSettings" style="display:none;background:rgba(0,0,0,.2);padding:15px;border-radius:10px;margin-bottom:18px;border:1px dashed rgba(162,155,254,.5)"><span class="label" style="color:#a29bfe">⚙️ Clash 高级配置</span><div style="margin-bottom:10px"><span class="label" style="font-size:.85rem">转换后端:</span><input type="text" id="converterUrl" value="${DEFAULT_CONVERTER}" oninput="updateLink()"></div><div><span class="label" style="font-size:.85rem">远程配置:</span><input type="text" id="configUrl" value="https://raw.githubusercontent.com/sinspired/sub-store-template/main/1.12.x/sing-box.json" oninput="updateLink()"></div></div><div class="field"><span class="label">3. 手动生成订阅链接 (Legacy)</span><input type="text" id="resultUrl" readonly onclick="this.select()"><div class="checkbox-wrapper"><input type="checkbox" id="clashMode" onchange="toggleClashMode()"><label for="clashMode">🔄 开启 Clash 转换</label></div></div><div class="input-group"><button class="main-btn" onclick="testSub()">📄 复制订阅链接</button><button class="sec-btn" onclick="testSub(true)" style="width:120px">🚀 测试</button></div></div></div><div id="toast" class="toast">已复制!</div><script>function toggleClashMode(){const e=document.getElementById("clashMode").checked;document.getElementById("clashSettings").style.display=e?"block":"none",updateLink()}function updateLink(){let e=document.getElementById("subBaseUrl").value.trim();e.endsWith("/")&&(e=e.slice(0,-1)),e.startsWith("http")||(e="https://"+e);const t=document.getElementById("proxyIp").value.trim(),s="${t}",h="${e}",n=document.getElementById("clashMode").checked;let r="/";t&&(r="/proxyip="+t);const o=e+"/sub?uuid="+s+"&encryption=none&security=tls&sni="+h+"&alpn=h3&fp=random&allowInsecure=1&type=ws&host="+h+"&path="+encodeURIComponent(r)+"&udp=false";if(n){let e=document.getElementById("converterUrl").value.trim();e.endsWith("/")&&(e=e.slice(0,-1));const t=document.getElementById("configUrl").value.trim();let s=t?"&config="+encodeURIComponent(t):"";document.getElementById("resultUrl").value=e+"/sub?target=clash&url="+encodeURIComponent(o)+s+"&emoji=true&list=false&tfo=false&scv=false&fdn=false&sort=false"}else document.getElementById("resultUrl").value=o}function copyId(e){navigator.clipboard.writeText(document.getElementById(e).value).then((()=>showToast("已复制!")))}function checkProxy(){const e=document.getElementById("proxyIp").value.trim();fetch("?flag=proxycheck");if(e){navigator.clipboard.writeText(e).then((()=>{alert("ProxyIP 已复制!"),window.open("${PROXY_CHECK_URL}","_blank")}))}else{window.open("${PROXY_CHECK_URL}","_blank")}}function testSub(isTest){const e=document.getElementById("resultUrl").value;if(isTest){fetch("?flag=test");if(e) window.open(e, "_blank")}else{copyId('resultUrl')}}function showToast(e){const t=document.getElementById("toast");t.innerText=e,t.classList.add("show"),setTimeout((()=>t.classList.remove("show")),2e3)}function logout(){document.cookie="auth=;expires=Thu,01 Jan 1970 00:00:00 UTC;path=/;",location.reload()}window.onload=()=>{updateLink()};<\/script></body></html>`;}