dns-worker 1.1.2 → 1.1.3-canary.eba4e13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -440,7 +440,7 @@ echo "Please change your system DNS to 127.0.0.1"
440
440
  JOIN passkeys pk ON upwk.passkey_id = pk.id
441
441
  WHERE pk.user_id = ?
442
442
  LIMIT 1
443
- `).bind(t.id).first()),d?Response.json({encryptedSk:d.encrypted_sk,iv:d.iv}):new Response("Wrapped key not found for this passkey",{status:404})}if(c==="recovery-wrapped-key"&&n.method==="GET"){let a=await e.DB.prepare("SELECT encrypted_sk, iv, salt FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1").bind(t.id).first();return a?Response.json({encryptedSk:a.encrypted_sk,iv:a.iv,salt:a.salt}):new Response("Recovery wrapped key not found",{status:404})}if(c==="init"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{publicKey:l,passkeyId:d,encryptedSk:u,iv:f,recovery:p}=a;if(!l||!d&&!p)return new Response("Missing required fields (publicKey and either passkey or recovery)",{status:400});let m=null;if(d&&(m=await o.getByIdOrCredentialId(d,t.id),!m)){let E=await o.listByUser(t.id);E.length>0&&(m=E[0])}let h=await i.listByOwner(t.id),g=Math.floor(Date.now()/1e3),b=typeof l=="string"?l:JSON.stringify(l),w=[],y=h.length>0?h[0].id:null;if(y){m&&u&&f&&w.push(e.DB.prepare("INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at").bind(m.id,y,u,f,g));for(let E of h)w.push(e.DB.prepare("INSERT INTO user_log_keys (profile_id, public_key, created_at, is_active) VALUES (?, ?, ?, 1) ON CONFLICT(profile_id) DO UPDATE SET public_key = excluded.public_key, created_at = excluded.created_at, is_active = 1").bind(E.id,b,g)),p&&p.encryptedSk&&p.iv&&p.salt&&w.push(e.DB.prepare("INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at").bind(E.id,p.encryptedSk,p.iv,p.salt,g))}w.length>0&&await e.DB.batch(w);for(let E of h)en(E.id);return Response.json({success:!0})}if(c==="wrap"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{passkeyId:l,encryptedSk:d,iv:u}=a;if(!l||!d||!u)return new Response("Missing required fields (passkeyId, encryptedSk, iv)",{status:400});let f=await o.getByIdOrCredentialId(l,t.id);if(!f){let g=await o.listByUser(t.id);g.length>0&&(f=g[0])}if(!f)return new Response("Passkey not found or unauthorized",{status:403});let p=await i.listByOwner(t.id),m=p.length>0?p[0].id:null;if(!m)return new Response("No profiles found for user",{status:400});let h=Math.floor(Date.now()/1e3);return await e.DB.prepare("INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at").bind(f.id,m,d,u,h).run(),Response.json({success:!0})}if(c==="wrap-recovery"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{encryptedSk:l,iv:d,salt:u}=a;if(!l||!d||!u)return new Response("Missing required fields (encryptedSk, iv, salt)",{status:400});let f=await i.listByOwner(t.id),p=Math.floor(Date.now()/1e3),m=[];for(let h of f)m.push(e.DB.prepare("INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at").bind(h.id,l,d,u,p));return m.length>0&&await e.DB.batch(m),Response.json({success:!0})}if(c==="enable"&&n.method==="POST"){let a=await i.listByOwner(t.id);await e.DB.prepare("UPDATE user_log_keys SET is_active = 1 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id).run();for(let l of a)en(l.id);return Response.json({success:!0,enabled:!0})}if(n.method==="DELETE"&&(!c||c==="keys")){let l=new URL(n.url).searchParams.get("purge")==="true",d=await i.listByOwner(t.id);l?await e.DB.batch([e.DB.prepare("DELETE FROM user_log_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id),e.DB.prepare("DELETE FROM user_passkey_wrapped_keys WHERE passkey_id IN (SELECT id FROM passkeys WHERE user_id = ?)").bind(t.id),e.DB.prepare("DELETE FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id)]):await e.DB.prepare("UPDATE user_log_keys SET is_active = 0 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id).run();for(let u of d)en(u.id);return Response.json({success:!0,purged:l,enabled:!1})}return new Response("Not Found",{status:404})}async function bl(n,e,t,r,s){let o=r[2];return o==="me"||o==="delete"||o==="logs"||o==="db-size"?dl(n,e,t,r,s):o==="password"||o==="totp"||o==="passkeys"||o==="mfa"||o==="migrate-password"||o==="pin"||o==="recovery-keys"?gl(n,e,t,r,s):o==="activity"||o==="sessions"?yl(n,e,t,r,s):o==="e2ee"?wl(n,e,t,r,s):new Response("Not Found",{status:404})}pe();Te();async function El(n,e,t,r,s){let o=new Y(e.DB,e),i=new q(e.DB);if(r[2]==="users"){if(n.method==="GET"){let c=await o.listAll();return new Response(JSON.stringify(c),{headers:{"Content-Type":"application/json"}})}if(n.method==="POST"){let{username:c,password:a,role:l}=await n.json();if(!c||!Rt.test(c))return new Response("Invalid username format",{status:400});if(!a||!St.test(a))return new Response("Password format error",{status:400});let d=await ot(a,2),u=j(15);try{return await o.create({id:u,username:c,passwordHash:d,role:l||"user",passwordVersion:2}),new Response(JSON.stringify({id:u}),{status:201})}catch(f){return new Response(f.message,{status:400})}}if(n.method==="DELETE"&&r[3]){let c=r[3];return c===t.id?new Response("Cannot delete yourself",{status:400}):(await i.deleteByOwner(c),await o.delete(c),new Response(null,{status:204}))}}if(r[2]==="settings"){let c=new he(e.DB);if(n.method==="GET"){let a=await c.getAll();return delete a.jwt_secret,new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}})}if(n.method==="PATCH"){let a=await n.json();return delete a.jwt_secret,await c.setMany(a),new Response(JSON.stringify({success:!0}))}}return new Response("Not Found",{status:404})}async function _l(n,e,t,r){let o=new URL(n.url).pathname.split("/").filter(Boolean);return o[1]==="account"?bl(n,e,t,o,r):o[1]==="admin"?Yn.isAdmin(t)?El(n,e,t,o,r):new Response("Forbidden",{status:403}):new Response("Not Found",{status:404})}ge();async function Sl(n,e){let t=new URL(n.url),r=typeof caches<"u"?caches.default:null;if(t.pathname==="/api/clientinfo"){let s=n.headers.get("CF-Connecting-IP")||"127.0.0.1",o=await P.get(r,`active_dns:${s}`),i=n.cf,c=i?.country||n.headers.get("CF-IPCountry")||"",a=c,l=i?.region||n.headers.get("CF-Region")||"",d=i?.city||n.headers.get("CF-IPCity")||"",u=i?.timezone||n.headers.get("CF-Timezone")||"",f=i?.asn?Number(i.asn):n.headers.get("CF-ASN")?Number(n.headers.get("CF-ASN")):0,p=i?.asOrganization||n.headers.get("CF-AS-Org")||"";if(!c||c==="UNKNOWN"||c==="UN"||c==="XX")if(!qe(s))c="Private Network",a="LAN",l="LAN",d="Local",u="UNKNOWN",p="Private Network";else try{let h=await Er(s);if(h&&(c=h.country||h.country_code||"UNKNOWN",a=h.country_code||"UNKNOWN",l=h.region||"UNKNOWN",d=h.city||"UNKNOWN",u=h.timezone||"UNKNOWN",p=h.org||h.isp||"UNKNOWN",!f&&h.as)){let g=h.as.match(/^AS(\d+)/i);g&&(f=Number(g[1]))}}catch(h){console.warn(`[ClientInfo] Failed resolving geoip for ${s}:`,h)}let m=e.SERVERFULL_DEFAULT_PROFILE_KEY!==void 0||e.SERVERFULL_HOST!==void 0||e.SERVERFULL_DOT_DOMAIN!==void 0||e.SERVERFULL_HTTP_PORT!==void 0||e.SERVERFULL_HTTPS_PORT!==void 0;return new Response(JSON.stringify({ip:s,country:c||"UNKNOWN",countryCode:a||void 0,region:l||"UNKNOWN",city:d||"UNKNOWN",timezone:u||"UNKNOWN",asn:f||0,asOrganization:p||"UNKNOWN",connectedProfileId:o||null,substituteDomain:e.SUBSTITUTE_DOMAIN||tr,dotDomain:e.SERVERFULL_DOT_DOMAIN||e.DOT_DOMAIN||null,isServerfull:m,mode:m?"serverfull":"cloudflare"}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/regions"){let s={};for(let[o,i]of Object.entries(e))if(o.startsWith("IP_REGION_")&&typeof i=="string")try{let c=o.replace("IP_REGION_",""),a=i.trim();a=a.replace(/^"""|"""$/g,"").replace(/^"|"$/g,"").replace(/^'|'$/g,"").trim(),s[c]=JSON.parse(a)}catch{}return Object.keys(s).length===0&&(s.CN=Xr),new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/substitute"){let s=e.SUBSTITUTE_DOMAIN||tr,o=null,i=null,c=async a=>{let l=["https://cloudflare-dns.com/dns-query","https://1.1.1.1/dns-query"];for(let d of l)try{let u=await fetch(`${d}?name=${s}&type=${a}`,{headers:{Accept:"application/dns-json"},signal:AbortSignal.timeout(3e3)});if(u.ok){let f=await u.json();if(f?.Answer?.length>0){let p=f.Answer.find(m=>m.type===(a==="A"?1:28));if(p?.data)return p.data}}}catch(u){console.error(`[Substitute Resolve] Failed resolving ${a} via ${d}:`,u)}return null};try{let[a,l]=await Promise.all([c("A"),c("AAAA")]);o=a,i=l}catch(a){console.error("[Substitute API] Error resolving substitute domain:",a)}return new Response(JSON.stringify({ip:o,ipv6:i}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/upstreams"){let s=Yr;if(e.PRESET_UPSTREAMS)try{let o=JSON.parse(e.PRESET_UPSTREAMS);Array.isArray(o)&&o.length>0&&(s=o)}catch(o){console.warn("[System API] Failed to parse PRESET_UPSTREAMS from env:",o)}return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/filters"){let s=zr;if(e.PRESET_EXTERNAL_FILTERS)try{let o=JSON.parse(e.PRESET_EXTERNAL_FILTERS);Array.isArray(o)&&o.length>0&&(s=o)}catch(o){console.warn("[System API] Failed to parse PRESET_EXTERNAL_FILTERS from env:",o)}return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/ech-fronting-domains"){let s=Ji(e);return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname.startsWith("/api/icon/")){let s=t.pathname.replace("/api/icon/","");if(!s||!/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(s)||s.includes("..")||s.includes("/")||s.includes("\\"))return new Response("Invalid domain format",{status:400});let o=`https://icons.duckduckgo.com/ip3/${s}`;try{let i=await fetch(o),c=new Headers(i.headers);return c.set("Cache-Control","public, max-age=2592000"),c.delete("Access-Control-Allow-Origin"),new Response(i.body,{status:i.status,statusText:i.statusText,headers:c})}catch{return new Response("Error fetching icon",{status:500})}}if(t.pathname==="/api/geoip"){let s=t.searchParams.get("ip"),o=n.headers.get("CF-Connecting-IP"),i=(s||o||"").trim();if(i==="127.0.0.1"||i==="localhost"||i==="::1"||i.startsWith("192.168.")||i.startsWith("10.")||/^172\.(1[6-9]|2\d|3[01])\./.test(i))return new Response(JSON.stringify({success:!0,ip:i,city:"Local",country:"Private Network",flag:{emoji:"\u{1F3E0}"}}),{headers:{"Content-Type":"application/json"}});try{let c=i?`https://ipwho.is/${encodeURIComponent(i)}`:"https://ipwho.is/",a=await fetch(c,{signal:AbortSignal.timeout(3500)});if(a.ok){let l=await a.json();return new Response(JSON.stringify(l),{headers:{"Content-Type":"application/json"}})}}catch(c){console.warn(`[System API] Failed resolving geoip for ${i||"server"}:`,c)}return new Response(JSON.stringify({success:!1,ip:i||"unknown"}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/resolve"){let s=t.searchParams.get("name")||t.searchParams.get("domain");if(!s)return new Response(JSON.stringify({error:"Missing domain parameter"}),{status:400,headers:{"Content-Type":"application/json"}});if(/^(\d{1,3}\.){3}\d{1,3}$/.test(s))return new Response(JSON.stringify({ipv4:[s],ipv6:[]}),{headers:{"Content-Type":"application/json"}});if(/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(s))return new Response(JSON.stringify({ipv4:[],ipv6:[s]}),{headers:{"Content-Type":"application/json"}});let o=async i=>{let c=["https://cloudflare-dns.com/dns-query","https://1.1.1.1/dns-query"];for(let a of c)try{let l=await fetch(`${a}?name=${encodeURIComponent(s)}&type=${i}`,{headers:{Accept:"application/dns-json"},signal:AbortSignal.timeout(3e3)});if(l.ok){let d=await l.json();if(Array.isArray(d?.Answer)&&d.Answer.length>0){let u=i==="A"?1:28;return d.Answer.filter(f=>f.type===u&&typeof f.data=="string").map(f=>f.data)}}}catch{}return[]};try{let[i,c]=await Promise.all([o("A"),o("AAAA")]);if(i.length===0&&c.length===0&&typeof process<"u"&&process.versions?.node)try{let a=await import("node:dns/promises"),[l,d]=await Promise.allSettled([a.resolve4(s),a.resolve6(s)]);l.status==="fulfilled"&&Array.isArray(l.value)&&(i=l.value),d.status==="fulfilled"&&Array.isArray(d.value)&&(c=d.value)}catch{}return new Response(JSON.stringify({ipv4:i,ipv6:c}),{headers:{"Content-Type":"application/json"}})}catch(i){return console.error("DNS resolve failed:",i),new Response(JSON.stringify({ipv4:[],ipv6:[],error:"Internal server error"}),{status:500,headers:{"Content-Type":"application/json"}})}}return new Response("Not Found",{status:404})}ge();async function Rl(n,e,t){try{let r=caches.default,s=Math.floor(Date.now()/1e3),o="cron:last_cleanup_timestamp",i=await P.get(r,o);if(!i||s-i>=3600){try{let c=new Y(e.DB,e),{clearedProfiles:a,deletedUsers:l}=await c.applyInactivityPolicy(s);(a>0||l>0)&&console.log(`[Cron] Inactivity cleanup: cleared ${a} profile(s), deleted ${l} user(s).`)}catch(c){console.error("[Cron] Inactivity policy execution failed:",c)}try{let c=new Ie(e.DB),a=e.MAX_LOG_RETENTION_DAYS!==void 0&&e.MAX_LOG_RETENTION_DAYS!==""?Number(e.MAX_LOG_RETENTION_DAYS):30,l=e.LOG_CLEANUP_BATCH_LIMIT!==void 0&&e.LOG_CLEANUP_BATCH_LIMIT!==""?Number(e.LOG_CLEANUP_BATCH_LIMIT):1e3,d=e.LOG_CLEANUP_DAILY_BUDGET!==void 0&&e.LOG_CLEANUP_DAILY_BUDGET!==""?Number(e.LOG_CLEANUP_DAILY_BUDGET):2e4;await c.cleanupGlobal(a,l,d)}catch(c){console.error("[Cron] Global log cleanup failed:",c)}try{let{SessionModel:c}=await Promise.resolve().then(()=>(Be(),pr)),a=new c(e.DB),{deletedSessions:l,deletedPending:d}=await a.deleteExpiredSessions(s);(l>0||d>0)&&console.log(`[Cron] Purged ${l} expired session(s) and ${d} pending token(s).`)}catch(c){console.error("[Cron] Session cleanup failed:",c)}await P.set(r,o,s,3600),console.log("[Cron] Cleanup phase completed at",new Date().toISOString())}try{await Bo(e.DB)}catch(c){console.error("[Cron] Cloudflare IP range sync failed:",c)}try{let c="cron:last_idle_sync_check",l=await P.get(r,c);if(!!!(l&&s-l<600)){let u=Number(e.SYNC_PROFILE_INTERVAL_SEC)||86400,f=s-u,p=new q(e.DB),m=Number(e.SYNC_BATCH_SIZE)||1,h=await p.getSyncTargets(f,m);if(h.length>0){await P.delete(r,c);for(let g of h)try{await zn(g.id,e,t)}catch(b){let w=b instanceof Error?b.message:String(b);console.error(`[Cron] Sync failed for profile ${g.id}:`,w)}console.log(`[Cron] Sync: processed ${h.length} profile(s).`)}else await P.set(r,c,s,600)}}catch(c){let a=c instanceof Error?c.message:String(c);console.error("[Cron] Sync phase failed:",a)}}catch(r){let s=r instanceof Error?r.message:String(r);console.error("[Cron] Critical Failure:",s)}}async function Tl(n,e,t){let r=new Request(new URL("https://obex.local/world-110m-v2.json"),n),s=caches.default,o=await s.match(r);if(!o)try{let c=await fetch("https://openlayers.org/en/latest/examples/data/topojson/world-110m.json",{headers:{"User-Agent":"DNS Worker/1.0 (Cloudflare Worker Map Proxy)"}});if(!c.ok)throw new Error(`Failed to fetch map data from openlayers.org: ${c.status}`);let a=await c.json();if(a&&a.objects){let l={};a.objects.countries&&(l.countries=a.objects.countries),a.objects.land&&(l.land=a.objects.land),a.objects=l}a&&a.objects&&a.objects.countries&&Array.isArray(a.objects.countries.geometries)&&a.objects.countries.geometries.forEach(l=>{l.id!==void 0&&l.id!==null&&(l.id=String(l.id).padStart(3,"0"))}),o=new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json; charset=utf-8","Cache-Control":"public, max-age=2592000","Access-Control-Allow-Origin":"*"}}),t.waitUntil(s.put(r,o.clone()))}catch(i){return console.error("[MapData API Proxy] Error fetching or transforming TopoJSON:",i),new Response(JSON.stringify({error:"Failed to fetch map data"}),{status:502,headers:{"Content-Type":"application/json"}})}return o}Fe();var Eo={async fetch(n,e,t){let r=new Uint8Array(16);crypto.getRandomValues(r);let s=Array.from(r).map(l=>l.toString(16).padStart(2,"0")).join(""),o=null,c=await(async()=>{let l=new URL(n.url);if(l.pathname==="/world-110m.json")return Tl(n,e,t);if(l.pathname.startsWith("/api/")){let p=!1;try{p=!e.DB}catch{p=!0}let m=!Oe(e.JWT_SECRET);if(p||m)return new Response(JSON.stringify({error:"configuration_error",isDbMissing:p,isJwtSecretMissing:m,message:"DNS Worker is not fully configured. Please configure the D1 database binding and JWT_SECRET secret variable."}),{status:503,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});o=await Li(n,e,t);let h=["/api/auth/login","/api/auth/signup","/api/auth/prelogin","/api/auth/check-username","/api/auth/config","/api/auth/refresh","/api/auth/logout","/api/auth/unlock-session","/api/clientinfo","/api/regions","/api/substitute","/api/resolve","/api/geoip"].includes(l.pathname)||l.pathname.startsWith("/api/icon/")||l.pathname.startsWith("/api/presets/")||l.pathname.startsWith("/api/auth/forgot-password");return!o&&!h?new Response("Unauthorized",{status:401}):o&&o.isPaused&&!["/api/auth/unlock-session","/api/auth/logout","/api/auth/refresh"].includes(l.pathname)?new Response("session_paused",{status:403}):o&&["POST","PUT","PATCH","DELETE"].includes(n.method)&&!h&&!Oi(n)?new Response("CSRF validation failed",{status:403}):l.pathname.startsWith("/api/auth/")?Zc(n,e):l.pathname==="/api/clientinfo"||l.pathname==="/api/regions"||l.pathname==="/api/substitute"||l.pathname==="/api/resolve"||l.pathname==="/api/geoip"||l.pathname.startsWith("/api/presets/")||l.pathname.startsWith("/api/icon/")?Sl(n,e):l.pathname.startsWith("/api/profiles")?cl(n,e,o,t):l.pathname.startsWith("/api/account")||l.pathname.startsWith("/api/admin")?_l(n,e,o,t):new Response("API Not Found",{status:404})}if(l.pathname==="/setup.sh"){let p=l.searchParams.get("key");if(!p||!Wt.test(p))return new Response("Missing or invalid key parameter",{status:400});let m=Fc(l.origin,p);return new Response(m,{headers:{"Content-Type":"text/plain; charset=utf-8","Cache-Control":"no-cache"}})}let d=l.pathname.slice(1),u=Wt.test(d),f=n.method==="POST"||l.searchParams.has("dns")||n.headers.get("accept")?.includes("dns-message");if(u&&(f||n.method==="OPTIONS"))return n.method==="OPTIONS"?new Response(null,{status:204,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Accept","Access-Control-Max-Age":"86400"}}):Uc(n,e,t,d);try{let p=await e.ASSETS.fetch(n);if(p.status===404&&!l.pathname.startsWith("/assets/")&&(n.method==="GET"||n.method==="HEAD")&&(p=await e.ASSETS.fetch(new Request(l.origin+"/",{method:n.method,headers:n.headers}))),l.pathname.endsWith(".wasm")){let h=new Headers(p.headers);return h.set("Content-Type","application/wasm"),h.set("Cross-Origin-Resource-Policy","same-origin"),new Response(p.body,{status:p.status,statusText:p.statusText,headers:h})}if((p.headers.get("Content-Type")||"").includes("text/html")){let h=!1;try{h=!e.DB}catch{h=!0}let g=!Oe(e.JWT_SECRET),b=e.SERVERFULL_DEFAULT_PROFILE_KEY!==void 0||e.SERVERFULL_HOST!==void 0||e.SERVERFULL_DOT_DOMAIN!==void 0||e.SERVERFULL_HTTP_PORT!==void 0||e.SERVERFULL_HTTPS_PORT!==void 0,w="{}";try{w=JSON.stringify({nonce:s,isDbMissing:h,isJwtSecretMissing:g,isServerfull:b,mode:b?"serverfull":"cloudflare"})}catch{}return new HTMLRewriter().on("head",{element(y){y.prepend(`<script nonce="${s}">window.DNS_WORKER_CONFIG = ${w};</script>`,{html:!0})}}).transform(p)}return p}catch{return new Response("Asset Fetch Error",{status:500})}})(),a=o;if(a&&!_n(n.headers.get("Cookie"))){let l=a.sessionId?.startsWith("k_");try{let d=j(32);c.headers.append("Set-Cookie",it(d,e,l))}catch{let u=new Headers(c.headers),f=j(32);u.append("Set-Cookie",it(f,e,l)),c=new Response(c.body,{status:c.status,statusText:c.statusText,headers:u})}}if(c.headers.get("Content-Type")?.includes("application/dns-message")){let l=new Headers(c.headers);return l.set("Access-Control-Allow-Origin","*"),l.set("Access-Control-Allow-Methods","GET, POST, OPTIONS"),l.set("Access-Control-Allow-Headers","Content-Type, Accept"),new Response(c.body,{status:c.status,statusText:c.statusText,headers:l})}return ki(c,s)},async scheduled(n,e,t){await Rl(n,e,t)}};var Vu={".html":"text/html; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".webp":"image/webp",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".webmanifest":"application/manifest+json",".wasm":"application/wasm"};function Al(n,e){if(n.ASSETS)return;let t=e||_o.join(Mt(),"static");n.ASSETS={fetch:async r=>{try{let s=new URL(r.url),o=decodeURIComponent(s.pathname);(o==="/"||o==="")&&(o="/index.html");let i=_o.normalize(o).replace(/^(\.\.[\/\\])+/,""),c=_o.join(t,i);if(jr.existsSync(c)&&jr.statSync(c).isFile()){let a=_o.extname(c).toLowerCase(),l=Vu[a]||"application/octet-stream",d=jr.readFileSync(c);return new Response(d,{status:200,headers:{"Content-Type":l,"Cache-Control":a===".html"?"no-cache":"public, max-age=31536000, immutable","Cross-Origin-Opener-Policy":"same-origin","Cross-Origin-Embedder-Policy":"credentialless","Cross-Origin-Resource-Policy":"same-origin"}})}return new Response("Not Found",{status:404})}catch{return new Response("Asset Error",{status:500})}}}}function So(n){if(!n||typeof n!="string")return null;let e=n.trim();if(!e)return null;if(e.startsWith("[")){let t=e.indexOf("]");t>1&&(e=e.substring(1,t))}else if(e.includes(":")&&e.indexOf(":")===e.lastIndexOf(":")){let[t,r]=e.split(":");r&&/^\d+$/.test(r)&&st(t)&&(e=t)}return e.startsWith("::ffff:")&&(e=e.substring(7)),st(e)||Ft(e)?e:null}function Gu(n,e){return e?(Array.isArray(e)?e:e.split(",").map(r=>r.trim()).filter(Boolean)).includes(n):!1}function Ju(n,e,t){let r=So(e);if(r&&qe(r)&&!Gu(r,t))return r;let s=So(n.get("CF-Connecting-IP"));if(s)return s;let o=So(n.get("X-Real-IP"));if(o)return o;let i=n.get("X-Forwarded-For");if(i){let c=i.split(",");for(let a of c){let l=So(a);if(l)return l}}return r||"127.0.0.1"}function vl(n,e,t,r){return async(s,o)=>{try{let i=s.headers["x-forwarded-proto"]||e,c=s.headers.host||`${t}:${r}`,a=new URL(s.url||"/",`${i}://${c}`),l=new Headers;for(let[w,y]of Object.entries(s.headers))y&&(Array.isArray(y)?y.forEach(E=>l.append(w,E)):l.set(w,String(y)));let d=Ju(l,s.socket.remoteAddress,n.TRUSTED_PROXIES);l.set("CF-Connecting-IP",d);let u=l.get("CF-IPLatitude");(!u||u.trim()==="")&&l.set("CF-IPLatitude","0.0");let f=l.get("CF-IPLongitude");(!f||f.trim()==="")&&l.set("CF-IPLongitude","0.0");let p=null;if(s.method!=="GET"&&s.method!=="HEAD"){let w=[];for await(let y of s)w.push(typeof y=="string"?xl.from(y):y);p=xl.concat(w)}let m=new Request(a.toString(),{method:s.method,headers:l,body:p,duplex:"half"}),h={waitUntil(w){w.catch(y=>console.error(`[${e.toUpperCase()} Background Task Error]:`,y))},passThroughOnException(){}},g=await Eo.fetch(m,n,h);o.statusCode=g.status,o.statusMessage=g.statusText;let b=typeof g.headers.getSetCookie=="function"?g.headers.getSetCookie():[];if(b.length===0&&g.headers.has("set-cookie")){let w=g.headers.get("set-cookie");w&&(b=[w])}if(g.headers.forEach((w,y)=>{let E=y.toLowerCase();E!=="set-cookie"&&(e==="https"&&E==="alt-svc"?o.setHeader("Alt-Svc",`h3=":${r}"; ma=86400, h3-29=":${r}"; ma=86400`):o.setHeader(y,w))}),b.length>0){let y=i==="http"?b.map(E=>E.replace(/;\s*Secure\b/gi,"")):b;o.setHeader("Set-Cookie",y)}if(e==="https"&&!o.hasHeader("Alt-Svc")&&o.setHeader("Alt-Svc",`h3=":${r}"; ma=86400, h3-29=":${r}"; ma=86400`),g.body){let w=g.body.getReader();for(;;){let{done:y,value:E}=await w.read();if(y)break;o.write(E)}}o.end()}catch(i){console.error(`[${e.toUpperCase()} Server] Request processing failed:`,i),o.headersSent||(o.statusCode=500,o.setHeader("Content-Type","application/json"),o.end(JSON.stringify({error:"Internal Server Error"})))}}}var Ro=class{constructor(e){this.options=e;Al(this.options.env,this.options.staticDir)}options;server=null;isRunning=!1;start(){return new Promise((e,t)=>{let{port:r,host:s,env:o}=this.options,i=vl(o,"http",s,r);this.server=Wu.createServer(i),this.server.keepAliveTimeout=65e3,this.server.headersTimeout=66e3,this.server.on("connection",a=>{a.setNoDelay(!0)});let c=a=>{this.server=null,console.error(sn({serviceName:"Web Dashboard & DoH (HTTP)",protocol:"HTTP",port:r,err:a,alternateOption:`--http-port <port> (e.g. --http-port ${r+1})`})),t(a)};this.server.once("error",c),this.server.listen(r,s,()=>{this.server?.removeListener("error",c),this.server?.on("error",a=>{console.error("[HTTP Server] Runtime error:",a)}),this.isRunning=!0,console.log(`[HTTP Server] Web Dashboard & DoH listening on http://${s}:${r}`),e()})})}stop(){return new Promise(e=>{this.server&&this.isRunning?this.server.close(()=>{this.isRunning=!1,console.log("[HTTP Server] Service stopped."),e()}):e()})}},To=class{constructor(e){this.options=e;Al(this.options.env,this.options.staticDir)}options;server=null;isRunning=!1;start(){return new Promise((e,t)=>{let{port:r,host:s,certPath:o,keyPath:i,env:c}=this.options;try{let a=jr.readFileSync(o),l=jr.readFileSync(i),d=vl(c,"https",s,r);this.server=ju.createServer({cert:a,key:l},d),this.server.keepAliveTimeout=65e3,this.server.headersTimeout=66e3,this.server.on("connection",f=>{f.setNoDelay(!0)});let u=f=>{this.server=null,console.error(sn({serviceName:"Web Dashboard & DoH (HTTPS)",protocol:"HTTPS",port:r,err:f,alternateOption:`--https-port <port> (e.g. --https-port ${r+1})`})),t(f)};this.server.once("error",u),this.server.listen(r,s,()=>{this.server?.removeListener("error",u),this.server?.on("error",f=>{console.error("[HTTPS Server] Runtime error:",f)}),this.isRunning=!0,console.log(`[HTTPS Server] Web Dashboard & DoH listening on https://${s}:${r}`),e()})}catch(a){t(a)}})}stop(){return new Promise(e=>{this.server&&this.isRunning?this.server.close(()=>{this.isRunning=!1,console.log("[HTTPS Server] Service stopped."),e()}):e()})}};Fe();import Pl from"node:fs";import Yu from"node:net";import zu from"node:dgram";import{DatabaseSync as Xu}from"node:sqlite";Fe();function Fi(n,e,t=300){return new Promise(r=>{let s=new Yu.Socket,o=!1,i=a=>{if(!o){o=!0;try{s.destroy()}catch{}r(a)}};s.setTimeout(t),s.once("connect",()=>i("active")),s.once("timeout",()=>i("free")),s.once("error",()=>i("free"));let c=e==="0.0.0.0"||e==="::"?"127.0.0.1":e;try{s.connect(n,c)}catch{i("free")}})}function Qu(n,e){return new Promise(t=>{let r=zu.createSocket("udp4"),s=!1,o=i=>{if(!s){s=!0;try{r.close()}catch{}t(i)}};r.once("error",i=>{let c=i?.code;o(c==="EADDRINUSE"?"active":"free")});try{r.bind(n,e,()=>{o("free")})}catch{o("active")}})}function Zu(n){if(!Pl.existsSync(n))return{exists:!1,statusText:"Not Initialized (File does not exist yet; created on first start)"};try{let e=Pl.statSync(n),t=e.size>1024*1024?`${(e.size/(1024*1024)).toFixed(2)} MB`:`${(e.size/1024).toFixed(1)} KB`,r=0,s=0,o=0;try{let c=new Xu(n,{readOnly:!0}),a=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='d1_migrations'").get();a&&a.cnt>0&&(r=c.prepare("SELECT count(*) as count FROM d1_migrations").get()?.count||0);let l=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='users'").get();l&&l.cnt>0&&(s=c.prepare("SELECT count(*) as count FROM users").get()?.count||0);let d=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='profiles'").get();d&&d.cnt>0&&(o=c.prepare("SELECT count(*) as count FROM profiles").get()?.count||0),c.close()}catch{}let i=[t,r>0?`${r} migrations`:null,s>0?`${s} user(s)`:null,o>0?`${o} profile(s)`:null].filter(Boolean).join(", ");return{exists:!0,sizeFormatted:t,migrationsCount:r,usersCount:s,profilesCount:o,statusText:`Ready (${i})`}}catch(e){return{exists:!0,statusText:`Exists (Read warning: ${e instanceof Error?e.message:String(e)})`}}}async function Cl(n,e){let t=jn(n.tlsCertPath,n.tlsKeyPath),r=!n.disableHttps&&t.configured&&t.filesExist,[s,o,i,c]=await Promise.all([Fi(n.httpPort,n.host),r?Fi(n.httpsPort,n.host):Promise.resolve("disabled"),n.disableUdp?Promise.resolve("disabled"):Qu(n.udpPort,n.host),n.disableDot?Promise.resolve("disabled"):Fi(n.dotPort,n.host)]),a=Zu(n.dbPath),l="Default (Insecure fallback, please configure JWT_SECRET)";Oe(e)&&(l=bt(e)?"Configured (Strong, >= 32 characters)":"Configured (Warning: Shorter than 32 characters)");let d=s==="active"?nn(n.httpPort,"TCP"):null,u=d?`: ${rn(d)}`:"",f=s==="active"?`http://${n.host}:${n.httpPort} [Active / Listening${u}]`:`http://${n.host}:${n.httpPort} [Stopped / Port Available]`,p="";if(n.disableHttps)p="Disabled (--disable-https)";else if(!t.configured)p=`https://${n.host}:${n.httpsPort} [Disabled - TLS Certificates Not Configured]`;else if(!t.filesExist)p=`https://${n.host}:${n.httpsPort} [Warning - TLS Certificate Files Missing]`;else if(o==="active"){let ie=nn(n.httpsPort,"TCP"),k=ie?`: ${rn(ie)}`:"";p=`https://${n.host}:${n.httpsPort} [Active / Listening${k}]`}else p=`https://${n.host}:${n.httpsPort} [Stopped / Port Available]`;let m=!n.disableUdp&&i==="active"?nn(n.udpPort,"UDP"):null,h=m?`: ${rn(m)}`:"",g=n.disableUdp?"Disabled (--disable-udp)":i==="active"?`udp://${n.host}:${n.udpPort} [Active / In Use${h}]`:`udp://${n.host}:${n.udpPort} [Stopped / Port Available]`,b="",w=n.dotDomain?` (${n.dotDomain})`:"";if(n.disableDot)b="Disabled (--disable-dot)";else if(!t.configured)b=`tls://${n.host}:${n.dotPort}${w} [Disabled - TLS Certificates Not Configured]`;else if(!t.filesExist)b=`tls://${n.host}:${n.dotPort}${w} [Warning - TLS Certificate Files Missing]`;else if(!t.isWildcard)b=`tls://${n.host}:${n.dotPort}${w} [Paused - Requires Wildcard Certificate (*.domain)]`;else if(c==="active"){let ie=nn(n.dotPort,"TCP"),k=ie?`: ${rn(ie)}`:"";b=`tls://${n.host}:${n.dotPort}${w} [Active / Listening${k}]`}else b=`tls://${n.host}:${n.dotPort}${w} [Stopped / Port Available]`;let y=n.defaultProfileKey?`Explicit (${n.defaultProfileKey})`:"Auto (Database default profile)",E=Ce({title:"DNS Worker Runtime Status (Serverfull Mode)",borderChar:"=",bullet:"\u2022 "}),_=nt({title:`
443
+ `).bind(t.id).first()),d?Response.json({encryptedSk:d.encrypted_sk,iv:d.iv}):new Response("Wrapped key not found for this passkey",{status:404})}if(c==="recovery-wrapped-key"&&n.method==="GET"){let a=await e.DB.prepare("SELECT encrypted_sk, iv, salt FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?) LIMIT 1").bind(t.id).first();return a?Response.json({encryptedSk:a.encrypted_sk,iv:a.iv,salt:a.salt}):new Response("Recovery wrapped key not found",{status:404})}if(c==="init"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{publicKey:l,passkeyId:d,encryptedSk:u,iv:f,recovery:p}=a;if(!l||!d&&!p)return new Response("Missing required fields (publicKey and either passkey or recovery)",{status:400});let m=null;if(d&&(m=await o.getByIdOrCredentialId(d,t.id),!m)){let E=await o.listByUser(t.id);E.length>0&&(m=E[0])}let h=await i.listByOwner(t.id),g=Math.floor(Date.now()/1e3),b=typeof l=="string"?l:JSON.stringify(l),w=[],y=h.length>0?h[0].id:null;if(y){m&&u&&f&&w.push(e.DB.prepare("INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at").bind(m.id,y,u,f,g));for(let E of h)w.push(e.DB.prepare("INSERT INTO user_log_keys (profile_id, public_key, created_at, is_active) VALUES (?, ?, ?, 1) ON CONFLICT(profile_id) DO UPDATE SET public_key = excluded.public_key, created_at = excluded.created_at, is_active = 1").bind(E.id,b,g)),p&&p.encryptedSk&&p.iv&&p.salt&&w.push(e.DB.prepare("INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at").bind(E.id,p.encryptedSk,p.iv,p.salt,g))}w.length>0&&await e.DB.batch(w);for(let E of h)en(E.id);return Response.json({success:!0})}if(c==="wrap"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{passkeyId:l,encryptedSk:d,iv:u}=a;if(!l||!d||!u)return new Response("Missing required fields (passkeyId, encryptedSk, iv)",{status:400});let f=await o.getByIdOrCredentialId(l,t.id);if(!f){let g=await o.listByUser(t.id);g.length>0&&(f=g[0])}if(!f)return new Response("Passkey not found or unauthorized",{status:403});let p=await i.listByOwner(t.id),m=p.length>0?p[0].id:null;if(!m)return new Response("No profiles found for user",{status:400});let h=Math.floor(Date.now()/1e3);return await e.DB.prepare("INSERT INTO user_passkey_wrapped_keys (passkey_id, profile_id, encrypted_sk, iv, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(passkey_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, created_at = excluded.created_at").bind(f.id,m,d,u,h).run(),Response.json({success:!0})}if(c==="wrap-recovery"&&n.method==="POST"){let a;try{a=await n.json()}catch{return new Response("Invalid JSON body",{status:400})}let{encryptedSk:l,iv:d,salt:u}=a;if(!l||!d||!u)return new Response("Missing required fields (encryptedSk, iv, salt)",{status:400});let f=await i.listByOwner(t.id),p=Math.floor(Date.now()/1e3),m=[];for(let h of f)m.push(e.DB.prepare("INSERT INTO user_recovery_wrapped_keys (profile_id, encrypted_sk, iv, salt, created_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(profile_id) DO UPDATE SET encrypted_sk = excluded.encrypted_sk, iv = excluded.iv, salt = excluded.salt, created_at = excluded.created_at").bind(h.id,l,d,u,p));return m.length>0&&await e.DB.batch(m),Response.json({success:!0})}if(c==="enable"&&n.method==="POST"){let a=await i.listByOwner(t.id);await e.DB.prepare("UPDATE user_log_keys SET is_active = 1 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id).run();for(let l of a)en(l.id);return Response.json({success:!0,enabled:!0})}if(n.method==="DELETE"&&(!c||c==="keys")){let l=new URL(n.url).searchParams.get("purge")==="true",d=await i.listByOwner(t.id);l?await e.DB.batch([e.DB.prepare("DELETE FROM user_log_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id),e.DB.prepare("DELETE FROM user_passkey_wrapped_keys WHERE passkey_id IN (SELECT id FROM passkeys WHERE user_id = ?)").bind(t.id),e.DB.prepare("DELETE FROM user_recovery_wrapped_keys WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id)]):await e.DB.prepare("UPDATE user_log_keys SET is_active = 0 WHERE profile_id IN (SELECT id FROM profiles WHERE owner_id = ?)").bind(t.id).run();for(let u of d)en(u.id);return Response.json({success:!0,purged:l,enabled:!1})}return new Response("Not Found",{status:404})}async function bl(n,e,t,r,s){let o=r[2];return o==="me"||o==="delete"||o==="logs"||o==="db-size"?dl(n,e,t,r,s):o==="password"||o==="totp"||o==="passkeys"||o==="mfa"||o==="migrate-password"||o==="pin"||o==="recovery-keys"?gl(n,e,t,r,s):o==="activity"||o==="sessions"?yl(n,e,t,r,s):o==="e2ee"?wl(n,e,t,r,s):new Response("Not Found",{status:404})}pe();Te();async function El(n,e,t,r,s){let o=new Y(e.DB,e),i=new q(e.DB);if(r[2]==="users"){if(n.method==="GET"){let c=await o.listAll();return new Response(JSON.stringify(c),{headers:{"Content-Type":"application/json"}})}if(n.method==="POST"){let{username:c,password:a,role:l}=await n.json();if(!c||!Rt.test(c))return new Response("Invalid username format",{status:400});if(!a||!St.test(a))return new Response("Password format error",{status:400});let d=await ot(a,2),u=j(15);try{return await o.create({id:u,username:c,passwordHash:d,role:l||"user",passwordVersion:2}),new Response(JSON.stringify({id:u}),{status:201})}catch(f){return new Response(f.message,{status:400})}}if(n.method==="DELETE"&&r[3]){let c=r[3];return c===t.id?new Response("Cannot delete yourself",{status:400}):(await i.deleteByOwner(c),await o.delete(c),new Response(null,{status:204}))}}if(r[2]==="settings"){let c=new he(e.DB);if(n.method==="GET"){let a=await c.getAll();return delete a.jwt_secret,new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json"}})}if(n.method==="PATCH"){let a=await n.json();return delete a.jwt_secret,await c.setMany(a),new Response(JSON.stringify({success:!0}))}}return new Response("Not Found",{status:404})}async function _l(n,e,t,r){let o=new URL(n.url).pathname.split("/").filter(Boolean);return o[1]==="account"?bl(n,e,t,o,r):o[1]==="admin"?Yn.isAdmin(t)?El(n,e,t,o,r):new Response("Forbidden",{status:403}):new Response("Not Found",{status:404})}ge();async function Sl(n,e){let t=new URL(n.url),r=typeof caches<"u"?caches.default:null;if(t.pathname==="/api/clientinfo"){let s=n.headers.get("CF-Connecting-IP")||"127.0.0.1",o=await P.get(r,`active_dns:${s}`),i=n.cf,c=i?.country||n.headers.get("CF-IPCountry")||"",a=c,l=i?.region||n.headers.get("CF-Region")||"",d=i?.city||n.headers.get("CF-IPCity")||"",u=i?.timezone||n.headers.get("CF-Timezone")||"",f=i?.asn?Number(i.asn):n.headers.get("CF-ASN")?Number(n.headers.get("CF-ASN")):0,p=i?.asOrganization||n.headers.get("CF-AS-Org")||"";if(!c||c==="UNKNOWN"||c==="UN"||c==="XX")if(!qe(s))c="Private Network",a="LAN",l="LAN",d="Local",u="UNKNOWN",p="Private Network";else try{let h=await Er(s);if(h&&(c=h.country||h.country_code||"UNKNOWN",a=h.country_code||"UNKNOWN",l=h.region||"UNKNOWN",d=h.city||"UNKNOWN",u=h.timezone||"UNKNOWN",p=h.org||h.isp||"UNKNOWN",!f&&h.as)){let g=h.as.match(/^AS(\d+)/i);g&&(f=Number(g[1]))}}catch(h){console.warn(`[ClientInfo] Failed resolving geoip for ${s}:`,h)}let m=e.SERVERFULL_DEFAULT_PROFILE_KEY!==void 0||e.SERVERFULL_HOST!==void 0||e.SERVERFULL_DOT_DOMAIN!==void 0||e.SERVERFULL_HTTP_PORT!==void 0||e.SERVERFULL_HTTPS_PORT!==void 0;return new Response(JSON.stringify({ip:s,country:c||"UNKNOWN",countryCode:a||void 0,region:l||"UNKNOWN",city:d||"UNKNOWN",timezone:u||"UNKNOWN",asn:f||0,asOrganization:p||"UNKNOWN",connectedProfileId:o||null,substituteDomain:e.SUBSTITUTE_DOMAIN||tr,dotDomain:e.SERVERFULL_DOT_DOMAIN||e.DOT_DOMAIN||null,isServerfull:m,mode:m?"serverfull":"cloudflare"}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/regions"){let s={};for(let[o,i]of Object.entries(e))if(o.startsWith("IP_REGION_")&&typeof i=="string")try{let c=o.replace("IP_REGION_",""),a=i.trim();a=a.replace(/^"""|"""$/g,"").replace(/^"|"$/g,"").replace(/^'|'$/g,"").trim(),s[c]=JSON.parse(a)}catch{}return Object.keys(s).length===0&&(s.CN=Xr),new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/substitute"){let s=e.SUBSTITUTE_DOMAIN||tr,o=null,i=null,c=async a=>{let l=["https://cloudflare-dns.com/dns-query","https://1.1.1.1/dns-query"];for(let d of l)try{let u=await fetch(`${d}?name=${s}&type=${a}`,{headers:{Accept:"application/dns-json"},signal:AbortSignal.timeout(3e3)});if(u.ok){let f=await u.json();if(f?.Answer?.length>0){let p=f.Answer.find(m=>m.type===(a==="A"?1:28));if(p?.data)return p.data}}}catch(u){console.error(`[Substitute Resolve] Failed resolving ${a} via ${d}:`,u)}return null};try{let[a,l]=await Promise.all([c("A"),c("AAAA")]);o=a,i=l}catch(a){console.error("[Substitute API] Error resolving substitute domain:",a)}return new Response(JSON.stringify({ip:o,ipv6:i}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/upstreams"){let s=Yr;if(e.PRESET_UPSTREAMS)try{let o=JSON.parse(e.PRESET_UPSTREAMS);Array.isArray(o)&&o.length>0&&(s=o)}catch(o){console.warn("[System API] Failed to parse PRESET_UPSTREAMS from env:",o)}return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/filters"){let s=zr;if(e.PRESET_EXTERNAL_FILTERS)try{let o=JSON.parse(e.PRESET_EXTERNAL_FILTERS);Array.isArray(o)&&o.length>0&&(s=o)}catch(o){console.warn("[System API] Failed to parse PRESET_EXTERNAL_FILTERS from env:",o)}return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/presets/ech-fronting-domains"){let s=Ji(e);return new Response(JSON.stringify(s),{headers:{"Content-Type":"application/json"}})}if(t.pathname.startsWith("/api/icon/")){let s=t.pathname.replace("/api/icon/","");if(!s||!/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(s)||s.includes("..")||s.includes("/")||s.includes("\\"))return new Response("Invalid domain format",{status:400});let o=`https://icons.duckduckgo.com/ip3/${s}`;try{let i=await fetch(o),c=new Headers(i.headers);return c.set("Cache-Control","public, max-age=2592000"),c.delete("Access-Control-Allow-Origin"),new Response(i.body,{status:i.status,statusText:i.statusText,headers:c})}catch{return new Response("Error fetching icon",{status:500})}}if(t.pathname==="/api/geoip"){let s=t.searchParams.get("ip"),o=n.headers.get("CF-Connecting-IP"),i=(s||o||"").trim();if(i==="127.0.0.1"||i==="localhost"||i==="::1"||i.startsWith("192.168.")||i.startsWith("10.")||/^172\.(1[6-9]|2\d|3[01])\./.test(i))return new Response(JSON.stringify({success:!0,ip:i,city:"Local",country:"Private Network",flag:{emoji:"\u{1F6DC}"}}),{headers:{"Content-Type":"application/json"}});try{let c=i?`https://ipwho.is/${encodeURIComponent(i)}`:"https://ipwho.is/",a=await fetch(c,{signal:AbortSignal.timeout(3500)});if(a.ok){let l=await a.json();return new Response(JSON.stringify(l),{headers:{"Content-Type":"application/json"}})}}catch(c){console.warn(`[System API] Failed resolving geoip for ${i||"server"}:`,c)}return new Response(JSON.stringify({success:!1,ip:i||"unknown"}),{headers:{"Content-Type":"application/json"}})}if(t.pathname==="/api/resolve"){let s=t.searchParams.get("name")||t.searchParams.get("domain");if(!s)return new Response(JSON.stringify({error:"Missing domain parameter"}),{status:400,headers:{"Content-Type":"application/json"}});if(/^(\d{1,3}\.){3}\d{1,3}$/.test(s))return new Response(JSON.stringify({ipv4:[s],ipv6:[]}),{headers:{"Content-Type":"application/json"}});if(/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(s))return new Response(JSON.stringify({ipv4:[],ipv6:[s]}),{headers:{"Content-Type":"application/json"}});let o=async i=>{let c=["https://cloudflare-dns.com/dns-query","https://1.1.1.1/dns-query"];for(let a of c)try{let l=await fetch(`${a}?name=${encodeURIComponent(s)}&type=${i}`,{headers:{Accept:"application/dns-json"},signal:AbortSignal.timeout(3e3)});if(l.ok){let d=await l.json();if(Array.isArray(d?.Answer)&&d.Answer.length>0){let u=i==="A"?1:28;return d.Answer.filter(f=>f.type===u&&typeof f.data=="string").map(f=>f.data)}}}catch{}return[]};try{let[i,c]=await Promise.all([o("A"),o("AAAA")]);if(i.length===0&&c.length===0&&typeof process<"u"&&process.versions?.node)try{let a=await import("node:dns/promises"),[l,d]=await Promise.allSettled([a.resolve4(s),a.resolve6(s)]);l.status==="fulfilled"&&Array.isArray(l.value)&&(i=l.value),d.status==="fulfilled"&&Array.isArray(d.value)&&(c=d.value)}catch{}return new Response(JSON.stringify({ipv4:i,ipv6:c}),{headers:{"Content-Type":"application/json"}})}catch(i){return console.error("DNS resolve failed:",i),new Response(JSON.stringify({ipv4:[],ipv6:[],error:"Internal server error"}),{status:500,headers:{"Content-Type":"application/json"}})}}return new Response("Not Found",{status:404})}ge();async function Rl(n,e,t){try{let r=caches.default,s=Math.floor(Date.now()/1e3),o="cron:last_cleanup_timestamp",i=await P.get(r,o);if(!i||s-i>=3600){try{let c=new Y(e.DB,e),{clearedProfiles:a,deletedUsers:l}=await c.applyInactivityPolicy(s);(a>0||l>0)&&console.log(`[Cron] Inactivity cleanup: cleared ${a} profile(s), deleted ${l} user(s).`)}catch(c){console.error("[Cron] Inactivity policy execution failed:",c)}try{let c=new Ie(e.DB),a=e.MAX_LOG_RETENTION_DAYS!==void 0&&e.MAX_LOG_RETENTION_DAYS!==""?Number(e.MAX_LOG_RETENTION_DAYS):30,l=e.LOG_CLEANUP_BATCH_LIMIT!==void 0&&e.LOG_CLEANUP_BATCH_LIMIT!==""?Number(e.LOG_CLEANUP_BATCH_LIMIT):1e3,d=e.LOG_CLEANUP_DAILY_BUDGET!==void 0&&e.LOG_CLEANUP_DAILY_BUDGET!==""?Number(e.LOG_CLEANUP_DAILY_BUDGET):2e4;await c.cleanupGlobal(a,l,d)}catch(c){console.error("[Cron] Global log cleanup failed:",c)}try{let{SessionModel:c}=await Promise.resolve().then(()=>(Be(),pr)),a=new c(e.DB),{deletedSessions:l,deletedPending:d}=await a.deleteExpiredSessions(s);(l>0||d>0)&&console.log(`[Cron] Purged ${l} expired session(s) and ${d} pending token(s).`)}catch(c){console.error("[Cron] Session cleanup failed:",c)}await P.set(r,o,s,3600),console.log("[Cron] Cleanup phase completed at",new Date().toISOString())}try{await Bo(e.DB)}catch(c){console.error("[Cron] Cloudflare IP range sync failed:",c)}try{let c="cron:last_idle_sync_check",l=await P.get(r,c);if(!!!(l&&s-l<600)){let u=Number(e.SYNC_PROFILE_INTERVAL_SEC)||86400,f=s-u,p=new q(e.DB),m=Number(e.SYNC_BATCH_SIZE)||1,h=await p.getSyncTargets(f,m);if(h.length>0){await P.delete(r,c);for(let g of h)try{await zn(g.id,e,t)}catch(b){let w=b instanceof Error?b.message:String(b);console.error(`[Cron] Sync failed for profile ${g.id}:`,w)}console.log(`[Cron] Sync: processed ${h.length} profile(s).`)}else await P.set(r,c,s,600)}}catch(c){let a=c instanceof Error?c.message:String(c);console.error("[Cron] Sync phase failed:",a)}}catch(r){let s=r instanceof Error?r.message:String(r);console.error("[Cron] Critical Failure:",s)}}async function Tl(n,e,t){let r=new Request(new URL("https://obex.local/world-110m-v2.json"),n),s=caches.default,o=await s.match(r);if(!o)try{let c=await fetch("https://openlayers.org/en/latest/examples/data/topojson/world-110m.json",{headers:{"User-Agent":"DNS Worker/1.0 (Cloudflare Worker Map Proxy)"}});if(!c.ok)throw new Error(`Failed to fetch map data from openlayers.org: ${c.status}`);let a=await c.json();if(a&&a.objects){let l={};a.objects.countries&&(l.countries=a.objects.countries),a.objects.land&&(l.land=a.objects.land),a.objects=l}a&&a.objects&&a.objects.countries&&Array.isArray(a.objects.countries.geometries)&&a.objects.countries.geometries.forEach(l=>{l.id!==void 0&&l.id!==null&&(l.id=String(l.id).padStart(3,"0"))}),o=new Response(JSON.stringify(a),{headers:{"Content-Type":"application/json; charset=utf-8","Cache-Control":"public, max-age=2592000","Access-Control-Allow-Origin":"*"}}),t.waitUntil(s.put(r,o.clone()))}catch(i){return console.error("[MapData API Proxy] Error fetching or transforming TopoJSON:",i),new Response(JSON.stringify({error:"Failed to fetch map data"}),{status:502,headers:{"Content-Type":"application/json"}})}return o}Fe();var Eo={async fetch(n,e,t){let r=new Uint8Array(16);crypto.getRandomValues(r);let s=Array.from(r).map(l=>l.toString(16).padStart(2,"0")).join(""),o=null,c=await(async()=>{let l=new URL(n.url);if(l.pathname==="/world-110m.json")return Tl(n,e,t);if(l.pathname.startsWith("/api/")){let p=!1;try{p=!e.DB}catch{p=!0}let m=!Oe(e.JWT_SECRET);if(p||m)return new Response(JSON.stringify({error:"configuration_error",isDbMissing:p,isJwtSecretMissing:m,message:"DNS Worker is not fully configured. Please configure the D1 database binding and JWT_SECRET secret variable."}),{status:503,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});o=await Li(n,e,t);let h=["/api/auth/login","/api/auth/signup","/api/auth/prelogin","/api/auth/check-username","/api/auth/config","/api/auth/refresh","/api/auth/logout","/api/auth/unlock-session","/api/clientinfo","/api/regions","/api/substitute","/api/resolve","/api/geoip"].includes(l.pathname)||l.pathname.startsWith("/api/icon/")||l.pathname.startsWith("/api/presets/")||l.pathname.startsWith("/api/auth/forgot-password");return!o&&!h?new Response("Unauthorized",{status:401}):o&&o.isPaused&&!["/api/auth/unlock-session","/api/auth/logout","/api/auth/refresh"].includes(l.pathname)?new Response("session_paused",{status:403}):o&&["POST","PUT","PATCH","DELETE"].includes(n.method)&&!h&&!Oi(n)?new Response("CSRF validation failed",{status:403}):l.pathname.startsWith("/api/auth/")?Zc(n,e):l.pathname==="/api/clientinfo"||l.pathname==="/api/regions"||l.pathname==="/api/substitute"||l.pathname==="/api/resolve"||l.pathname==="/api/geoip"||l.pathname.startsWith("/api/presets/")||l.pathname.startsWith("/api/icon/")?Sl(n,e):l.pathname.startsWith("/api/profiles")?cl(n,e,o,t):l.pathname.startsWith("/api/account")||l.pathname.startsWith("/api/admin")?_l(n,e,o,t):new Response("API Not Found",{status:404})}if(l.pathname==="/setup.sh"){let p=l.searchParams.get("key");if(!p||!Wt.test(p))return new Response("Missing or invalid key parameter",{status:400});let m=Fc(l.origin,p);return new Response(m,{headers:{"Content-Type":"text/plain; charset=utf-8","Cache-Control":"no-cache"}})}let d=l.pathname.slice(1),u=Wt.test(d),f=n.method==="POST"||l.searchParams.has("dns")||n.headers.get("accept")?.includes("dns-message");if(u&&(f||n.method==="OPTIONS"))return n.method==="OPTIONS"?new Response(null,{status:204,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Accept","Access-Control-Max-Age":"86400"}}):Uc(n,e,t,d);try{let p=await e.ASSETS.fetch(n);if(p.status===404&&!l.pathname.startsWith("/assets/")&&(n.method==="GET"||n.method==="HEAD")&&(p=await e.ASSETS.fetch(new Request(l.origin+"/",{method:n.method,headers:n.headers}))),l.pathname.endsWith(".wasm")){let h=new Headers(p.headers);return h.set("Content-Type","application/wasm"),h.set("Cross-Origin-Resource-Policy","same-origin"),new Response(p.body,{status:p.status,statusText:p.statusText,headers:h})}if((p.headers.get("Content-Type")||"").includes("text/html")){let h=!1;try{h=!e.DB}catch{h=!0}let g=!Oe(e.JWT_SECRET),b=e.SERVERFULL_DEFAULT_PROFILE_KEY!==void 0||e.SERVERFULL_HOST!==void 0||e.SERVERFULL_DOT_DOMAIN!==void 0||e.SERVERFULL_HTTP_PORT!==void 0||e.SERVERFULL_HTTPS_PORT!==void 0,w="{}";try{w=JSON.stringify({nonce:s,isDbMissing:h,isJwtSecretMissing:g,isServerfull:b,mode:b?"serverfull":"cloudflare"})}catch{}return new HTMLRewriter().on("head",{element(y){y.prepend(`<script nonce="${s}">window.DNS_WORKER_CONFIG = ${w};</script>`,{html:!0})}}).transform(p)}return p}catch{return new Response("Asset Fetch Error",{status:500})}})(),a=o;if(a&&!_n(n.headers.get("Cookie"))){let l=a.sessionId?.startsWith("k_");try{let d=j(32);c.headers.append("Set-Cookie",it(d,e,l))}catch{let u=new Headers(c.headers),f=j(32);u.append("Set-Cookie",it(f,e,l)),c=new Response(c.body,{status:c.status,statusText:c.statusText,headers:u})}}if(c.headers.get("Content-Type")?.includes("application/dns-message")){let l=new Headers(c.headers);return l.set("Access-Control-Allow-Origin","*"),l.set("Access-Control-Allow-Methods","GET, POST, OPTIONS"),l.set("Access-Control-Allow-Headers","Content-Type, Accept"),new Response(c.body,{status:c.status,statusText:c.statusText,headers:l})}return ki(c,s)},async scheduled(n,e,t){await Rl(n,e,t)}};var Vu={".html":"text/html; charset=utf-8",".js":"application/javascript; charset=utf-8",".mjs":"application/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".webp":"image/webp",".png":"image/png",".jpg":"image/jpeg",".svg":"image/svg+xml",".ico":"image/x-icon",".webmanifest":"application/manifest+json",".wasm":"application/wasm"};function Al(n,e){if(n.ASSETS)return;let t=e||_o.join(Mt(),"static");n.ASSETS={fetch:async r=>{try{let s=new URL(r.url),o=decodeURIComponent(s.pathname);(o==="/"||o==="")&&(o="/index.html");let i=_o.normalize(o).replace(/^(\.\.[\/\\])+/,""),c=_o.join(t,i);if(jr.existsSync(c)&&jr.statSync(c).isFile()){let a=_o.extname(c).toLowerCase(),l=Vu[a]||"application/octet-stream",d=jr.readFileSync(c);return new Response(d,{status:200,headers:{"Content-Type":l,"Cache-Control":a===".html"?"no-cache":"public, max-age=31536000, immutable","Cross-Origin-Opener-Policy":"same-origin","Cross-Origin-Embedder-Policy":"credentialless","Cross-Origin-Resource-Policy":"same-origin"}})}return new Response("Not Found",{status:404})}catch{return new Response("Asset Error",{status:500})}}}}function So(n){if(!n||typeof n!="string")return null;let e=n.trim();if(!e)return null;if(e.startsWith("[")){let t=e.indexOf("]");t>1&&(e=e.substring(1,t))}else if(e.includes(":")&&e.indexOf(":")===e.lastIndexOf(":")){let[t,r]=e.split(":");r&&/^\d+$/.test(r)&&st(t)&&(e=t)}return e.startsWith("::ffff:")&&(e=e.substring(7)),st(e)||Ft(e)?e:null}function Gu(n,e){return e?(Array.isArray(e)?e:e.split(",").map(r=>r.trim()).filter(Boolean)).includes(n):!1}function Ju(n,e,t){let r=So(e);if(r&&qe(r)&&!Gu(r,t))return r;let s=So(n.get("CF-Connecting-IP"));if(s)return s;let o=So(n.get("X-Real-IP"));if(o)return o;let i=n.get("X-Forwarded-For");if(i){let c=i.split(",");for(let a of c){let l=So(a);if(l)return l}}return r||"127.0.0.1"}function vl(n,e,t,r){return async(s,o)=>{try{let i=s.headers["x-forwarded-proto"]||e,c=s.headers.host||`${t}:${r}`,a=new URL(s.url||"/",`${i}://${c}`),l=new Headers;for(let[w,y]of Object.entries(s.headers))y&&(Array.isArray(y)?y.forEach(E=>l.append(w,E)):l.set(w,String(y)));let d=Ju(l,s.socket.remoteAddress,n.TRUSTED_PROXIES);l.set("CF-Connecting-IP",d);let u=l.get("CF-IPLatitude");(!u||u.trim()==="")&&l.set("CF-IPLatitude","0.0");let f=l.get("CF-IPLongitude");(!f||f.trim()==="")&&l.set("CF-IPLongitude","0.0");let p=null;if(s.method!=="GET"&&s.method!=="HEAD"){let w=[];for await(let y of s)w.push(typeof y=="string"?xl.from(y):y);p=xl.concat(w)}let m=new Request(a.toString(),{method:s.method,headers:l,body:p,duplex:"half"}),h={waitUntil(w){w.catch(y=>console.error(`[${e.toUpperCase()} Background Task Error]:`,y))},passThroughOnException(){}},g=await Eo.fetch(m,n,h);o.statusCode=g.status,o.statusMessage=g.statusText;let b=typeof g.headers.getSetCookie=="function"?g.headers.getSetCookie():[];if(b.length===0&&g.headers.has("set-cookie")){let w=g.headers.get("set-cookie");w&&(b=[w])}if(g.headers.forEach((w,y)=>{let E=y.toLowerCase();E!=="set-cookie"&&(e==="https"&&E==="alt-svc"?o.setHeader("Alt-Svc",`h3=":${r}"; ma=86400, h3-29=":${r}"; ma=86400`):o.setHeader(y,w))}),b.length>0){let y=i==="http"?b.map(E=>E.replace(/;\s*Secure\b/gi,"")):b;o.setHeader("Set-Cookie",y)}if(e==="https"&&!o.hasHeader("Alt-Svc")&&o.setHeader("Alt-Svc",`h3=":${r}"; ma=86400, h3-29=":${r}"; ma=86400`),g.body){let w=g.body.getReader();for(;;){let{done:y,value:E}=await w.read();if(y)break;o.write(E)}}o.end()}catch(i){console.error(`[${e.toUpperCase()} Server] Request processing failed:`,i),o.headersSent||(o.statusCode=500,o.setHeader("Content-Type","application/json"),o.end(JSON.stringify({error:"Internal Server Error"})))}}}var Ro=class{constructor(e){this.options=e;Al(this.options.env,this.options.staticDir)}options;server=null;isRunning=!1;start(){return new Promise((e,t)=>{let{port:r,host:s,env:o}=this.options,i=vl(o,"http",s,r);this.server=Wu.createServer(i),this.server.keepAliveTimeout=65e3,this.server.headersTimeout=66e3,this.server.on("connection",a=>{a.setNoDelay(!0)});let c=a=>{this.server=null,console.error(sn({serviceName:"Web Dashboard & DoH (HTTP)",protocol:"HTTP",port:r,err:a,alternateOption:`--http-port <port> (e.g. --http-port ${r+1})`})),t(a)};this.server.once("error",c),this.server.listen(r,s,()=>{this.server?.removeListener("error",c),this.server?.on("error",a=>{console.error("[HTTP Server] Runtime error:",a)}),this.isRunning=!0,console.log(`[HTTP Server] Web Dashboard & DoH listening on http://${s}:${r}`),e()})})}stop(){return new Promise(e=>{this.server&&this.isRunning?this.server.close(()=>{this.isRunning=!1,console.log("[HTTP Server] Service stopped."),e()}):e()})}},To=class{constructor(e){this.options=e;Al(this.options.env,this.options.staticDir)}options;server=null;isRunning=!1;start(){return new Promise((e,t)=>{let{port:r,host:s,certPath:o,keyPath:i,env:c}=this.options;try{let a=jr.readFileSync(o),l=jr.readFileSync(i),d=vl(c,"https",s,r);this.server=ju.createServer({cert:a,key:l},d),this.server.keepAliveTimeout=65e3,this.server.headersTimeout=66e3,this.server.on("connection",f=>{f.setNoDelay(!0)});let u=f=>{this.server=null,console.error(sn({serviceName:"Web Dashboard & DoH (HTTPS)",protocol:"HTTPS",port:r,err:f,alternateOption:`--https-port <port> (e.g. --https-port ${r+1})`})),t(f)};this.server.once("error",u),this.server.listen(r,s,()=>{this.server?.removeListener("error",u),this.server?.on("error",f=>{console.error("[HTTPS Server] Runtime error:",f)}),this.isRunning=!0,console.log(`[HTTPS Server] Web Dashboard & DoH listening on https://${s}:${r}`),e()})}catch(a){t(a)}})}stop(){return new Promise(e=>{this.server&&this.isRunning?this.server.close(()=>{this.isRunning=!1,console.log("[HTTPS Server] Service stopped."),e()}):e()})}};Fe();import Pl from"node:fs";import Yu from"node:net";import zu from"node:dgram";import{DatabaseSync as Xu}from"node:sqlite";Fe();function Fi(n,e,t=300){return new Promise(r=>{let s=new Yu.Socket,o=!1,i=a=>{if(!o){o=!0;try{s.destroy()}catch{}r(a)}};s.setTimeout(t),s.once("connect",()=>i("active")),s.once("timeout",()=>i("free")),s.once("error",()=>i("free"));let c=e==="0.0.0.0"||e==="::"?"127.0.0.1":e;try{s.connect(n,c)}catch{i("free")}})}function Qu(n,e){return new Promise(t=>{let r=zu.createSocket("udp4"),s=!1,o=i=>{if(!s){s=!0;try{r.close()}catch{}t(i)}};r.once("error",i=>{let c=i?.code;o(c==="EADDRINUSE"?"active":"free")});try{r.bind(n,e,()=>{o("free")})}catch{o("active")}})}function Zu(n){if(!Pl.existsSync(n))return{exists:!1,statusText:"Not Initialized (File does not exist yet; created on first start)"};try{let e=Pl.statSync(n),t=e.size>1024*1024?`${(e.size/(1024*1024)).toFixed(2)} MB`:`${(e.size/1024).toFixed(1)} KB`,r=0,s=0,o=0;try{let c=new Xu(n,{readOnly:!0}),a=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='d1_migrations'").get();a&&a.cnt>0&&(r=c.prepare("SELECT count(*) as count FROM d1_migrations").get()?.count||0);let l=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='users'").get();l&&l.cnt>0&&(s=c.prepare("SELECT count(*) as count FROM users").get()?.count||0);let d=c.prepare("SELECT count(*) as cnt FROM sqlite_master WHERE type='table' AND name='profiles'").get();d&&d.cnt>0&&(o=c.prepare("SELECT count(*) as count FROM profiles").get()?.count||0),c.close()}catch{}let i=[t,r>0?`${r} migrations`:null,s>0?`${s} user(s)`:null,o>0?`${o} profile(s)`:null].filter(Boolean).join(", ");return{exists:!0,sizeFormatted:t,migrationsCount:r,usersCount:s,profilesCount:o,statusText:`Ready (${i})`}}catch(e){return{exists:!0,statusText:`Exists (Read warning: ${e instanceof Error?e.message:String(e)})`}}}async function Cl(n,e){let t=jn(n.tlsCertPath,n.tlsKeyPath),r=!n.disableHttps&&t.configured&&t.filesExist,[s,o,i,c]=await Promise.all([Fi(n.httpPort,n.host),r?Fi(n.httpsPort,n.host):Promise.resolve("disabled"),n.disableUdp?Promise.resolve("disabled"):Qu(n.udpPort,n.host),n.disableDot?Promise.resolve("disabled"):Fi(n.dotPort,n.host)]),a=Zu(n.dbPath),l="Default (Insecure fallback, please configure JWT_SECRET)";Oe(e)&&(l=bt(e)?"Configured (Strong, >= 32 characters)":"Configured (Warning: Shorter than 32 characters)");let d=s==="active"?nn(n.httpPort,"TCP"):null,u=d?`: ${rn(d)}`:"",f=s==="active"?`http://${n.host}:${n.httpPort} [Active / Listening${u}]`:`http://${n.host}:${n.httpPort} [Stopped / Port Available]`,p="";if(n.disableHttps)p="Disabled (--disable-https)";else if(!t.configured)p=`https://${n.host}:${n.httpsPort} [Disabled - TLS Certificates Not Configured]`;else if(!t.filesExist)p=`https://${n.host}:${n.httpsPort} [Warning - TLS Certificate Files Missing]`;else if(o==="active"){let ie=nn(n.httpsPort,"TCP"),k=ie?`: ${rn(ie)}`:"";p=`https://${n.host}:${n.httpsPort} [Active / Listening${k}]`}else p=`https://${n.host}:${n.httpsPort} [Stopped / Port Available]`;let m=!n.disableUdp&&i==="active"?nn(n.udpPort,"UDP"):null,h=m?`: ${rn(m)}`:"",g=n.disableUdp?"Disabled (--disable-udp)":i==="active"?`udp://${n.host}:${n.udpPort} [Active / In Use${h}]`:`udp://${n.host}:${n.udpPort} [Stopped / Port Available]`,b="",w=n.dotDomain?` (${n.dotDomain})`:"";if(n.disableDot)b="Disabled (--disable-dot)";else if(!t.configured)b=`tls://${n.host}:${n.dotPort}${w} [Disabled - TLS Certificates Not Configured]`;else if(!t.filesExist)b=`tls://${n.host}:${n.dotPort}${w} [Warning - TLS Certificate Files Missing]`;else if(!t.isWildcard)b=`tls://${n.host}:${n.dotPort}${w} [Paused - Requires Wildcard Certificate (*.domain)]`;else if(c==="active"){let ie=nn(n.dotPort,"TCP"),k=ie?`: ${rn(ie)}`:"";b=`tls://${n.host}:${n.dotPort}${w} [Active / Listening${k}]`}else b=`tls://${n.host}:${n.dotPort}${w} [Stopped / Port Available]`;let y=n.defaultProfileKey?`Explicit (${n.defaultProfileKey})`:"Auto (Database default profile)",E=Ce({title:"DNS Worker Runtime Status (Serverfull Mode)",borderChar:"=",bullet:"\u2022 "}),_=nt({title:`
444
444
  Service Transports:`,items:[{label:"Web Dashboard (HTTP)",value:f},{label:"Web Dashboard (HTTPS)",value:p},{label:"Classic UDP DNS",value:g},{label:"DNS over TLS (DoT)",value:b}]}),S=ts(),x=S.length>0?`${S.join(", ")} [Loaded]`:`None found (Using built-in presets & defaults; default path: ${Xe()})`,T=nt({title:`
445
445
  Database & Storage:`,items:[{label:"SQLite Path",value:n.dbPath},{label:"Database Status",value:a.statusText},{label:"Persistent Data Dir",value:ht()}]}),v="Not Configured (HTTP only, HTTPS & DoT disabled)";if(t.configured)if(!t.filesExist)v="Configured [Files Missing]";else if(t.error)v=`Configured [Invalid Certificate: ${t.error}]`;else{let ie=t.isWildcard?"Wildcard (*.domain)":"Single Domain",k=t.expiresAt?t.expiresAt.toISOString().split("T")[0]:"Unknown";v=`Valid (${ie}, Expires: ${k})`}let C=n.dotDomain?`${n.dotDomain} (Wildcard / Single-domain compatible)`:"Not Configured (Auto SNI prefix matching)",L=!n.disableHttps&&t.configured&&t.filesExist?`Enabled via Alt-Svc (UDP :${n.httpsPort}, RFC 9114)`:"Disabled (Requires active HTTPS with TLS certificate)",H=t.configured&&t.filesExist?`Compatible (RFC 9250 / RFC 9460 DDR, UDP :${n.dotPort})`:"Disabled (Requires TLS certificate)",V=n.echEnabled?`Enabled (RFC 9460, Outer SNI: ${n.echFrontingDomain||"cloudflare-ech.com"})`:"Disabled",Q=nt({title:`
446
446
  Environment & Security:`,items:[{label:"Node.js Runtime",value:`v${process.versions.node} (${process.platform} ${process.arch})`},{label:"Config Directory",value:mn()},{label:"Config Source",value:x},{label:"TLS Certificate",value:v},{label:"JWT Secret",value:l},{label:"DoT Base Domain",value:C},{label:"HTTP/3 (h3)",value:L},{label:"DNS over QUIC (DoQ)",value:H},{label:"ECH (Encrypted Client Hello)",value:V},{label:"Default Profile Key",value:y}]}),Z=[];if(s==="free"&&(i==="free"||n.disableUdp)){let ie=process.platform==="win32"?"dns-worker service install (in Administrator terminal)":"sudo dns-worker service install";Z.push("Services are currently stopped."),Z.push(`To run continuously as a persistent background daemon: ${ie}`)}let _e=Fr(n.dotDomain);!t.configured||!t.filesExist?(Z.push(`To enable secure HTTPS (port ${n.httpsPort}) and DoT (port ${n.dotPort}):`),Z.push(` ${_e}`)):t.isWildcard||(Z.push("Current certificate is single-domain. To support per-profile DoT (<profile>.domain):"),Z.push(` ${_e}`)),Z.push("Run 'dns-worker ech' to view active ECH configuration, outer SNI, and RFC 9460 DNS records.");let K=Z.length>0?Wn(Z):"",Se=[E,_,T,Q,K].filter(Boolean).join(`