dns-worker 1.1.1 → 1.1.2
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.
- package/README.md +3 -3
- package/dist/serverfull.mjs +73 -69
- package/dist/serverfull.mjs.map +4 -4
- package/package.json +3 -1
- package/static/assets/AccountView-p5jSjMAE.js +4 -0
- package/static/assets/{AnalyticsView-C_g_jz14.js → AnalyticsView-BywwDzah.js} +2 -2
- package/static/assets/AuthView-qoUKd5PS.js +2 -0
- package/static/assets/{DestinationMap-jSW7gimP.js → DestinationMap-9Yc54Mdj.js} +1 -1
- package/static/assets/FilteringView-Bs3__NpV.js +1 -0
- package/static/assets/LogsView-B-U9oaxB.js +5 -0
- package/static/assets/{QRCodeCanvas-Cu_-aNyM.js → QRCodeCanvas-DQ-xNuwF.js} +2 -2
- package/static/assets/{RulesView-CfVeQv0-.js → RulesView-B2kL1QhI.js} +1 -1
- package/static/assets/SettingsView-DTGI6eSd.js +1 -0
- package/static/assets/SetupView-DE9MM-32.js +1 -0
- package/static/assets/{SwipeableDrawer-BrmEzhuP.js → SwipeableDrawer-B81ANpsc.js} +1 -1
- package/static/assets/{TrendChart-Dc5yJndv.js → TrendChart-CCR1W3fC.js} +3 -3
- package/static/assets/browser-Dc5iKjY4.js +1 -0
- package/static/assets/date-CpvuBM37.js +1 -0
- package/static/assets/{de-DE-CA3VPcoz.js → de-DE-kDnumQOF.js} +1 -1
- package/static/assets/{es-ES-CHvRWwd-.js → es-ES-CqKS7vHl.js} +1 -1
- package/static/assets/{fr-FR-CqGOuepx.js → fr-FR-BRcdpT35.js} +1 -1
- package/static/assets/getFlagEmoji-DRubsb7D.js +1 -0
- package/static/assets/index-Bheo8y3N.css +2 -0
- package/static/assets/index-dXN_pYn1.js +16 -0
- package/static/assets/{ja-JP-BJSbUSmh.js → ja-JP-C5jk6SuH.js} +1 -1
- package/static/assets/{ko-KR-Chzg6jwO.js → ko-KR-z38NtMFa.js} +1 -1
- package/static/assets/{ru-RU-CSyCGGPn.js → ru-RU-Bicx52lz.js} +1 -1
- package/static/assets/services-BVnEJBDq.js +1 -0
- package/static/assets/vendor-i18next-BKUzkAKP.js +1 -0
- package/static/assets/vendor-react-BmoHsW1x.js +11 -0
- package/static/assets/{vendor-ui-core-sfZn2C08.js → vendor-ui-core-CY_WyMIf.js} +1 -1
- package/static/assets/{vendor-utils-CnB7BG7m.js → vendor-utils-BWSB0jGn.js} +1 -1
- package/static/assets/{vi-VN-D7rcA1IX.js → vi-VN-CvS_mTAu.js} +1 -1
- package/static/assets/{zh-CN-CSPdaOkF.js → zh-CN-B0nRgJTD.js} +1 -1
- package/static/assets/{zh-TW-DETA1X3X.js → zh-TW-lc_SSoVI.js} +1 -1
- package/static/index.html +6 -6
- package/static/sw.js +1 -1
- package/static/assets/AccountView-CGRwz6ae.js +0 -4
- package/static/assets/AuthView-BQzmEyvO.js +0 -2
- package/static/assets/FilteringView-CQ7sX3Rp.js +0 -1
- package/static/assets/LogsView-eLfZMRVo.js +0 -5
- package/static/assets/SettingsView-agJiYEt5.js +0 -1
- package/static/assets/SetupView-CTgCUAgk.js +0 -1
- package/static/assets/browser-DPi2BX_1.js +0 -1
- package/static/assets/date-IKj2stuv.js +0 -1
- package/static/assets/getFlagEmoji-_Wxhq0lT.js +0 -1
- package/static/assets/index-919BqnYI.css +0 -2
- package/static/assets/index-CbTjGMTZ.js +0 -16
- package/static/assets/services-Cwvmit1q.js +0 -1
- package/static/assets/vendor-i18next-cO7FDf_e.js +0 -1
- package/static/assets/vendor-react-CHk5ngkq.js +0 -11
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/SetupView-DE9MM-32.js","assets/vendor-icons-other-Bme3ywH9.js","assets/vendor-icons-16-BgwRBCeU.js","assets/rolldown-runtime-C4Xdq4VV.js","assets/vendor-icons-20-X5NPN60g.js","assets/vendor-react-BmoHsW1x.js","assets/vendor-ui-core-CY_WyMIf.js","assets/vendor-utils-BWSB0jGn.js","assets/SwipeableDrawer-B81ANpsc.js","assets/FilteringView-Bs3__NpV.js","assets/RulesView-B2kL1QhI.js","assets/SettingsView-DTGI6eSd.js","assets/AnalyticsView-BywwDzah.js","assets/getFlagEmoji-DRubsb7D.js","assets/LogsView-B-U9oaxB.js","assets/formatCountryName-CSBD8pP8.js","assets/AuthView-qoUKd5PS.js","assets/vendor-i18next-BKUzkAKP.js","assets/QRCodeCanvas-DQ-xNuwF.js","assets/AccountView-p5jSjMAE.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{i as e}from"./rolldown-runtime-C4Xdq4VV.js";import{A as t,At as n,C as r,Dt as i,Ft as a,G as o,J as s,K as c,M as l,Mt as u,Nt as d,Ot as f,P as p,Pt as m,Qt as h,R as g,T as _,U as v,V as y,W as b,X as x,Z as S,_t as ee,at as te,b as C,ct as ne,dt as re,en as ie,jt as ae,kt as w,mt as oe,nt as se,ot as ce,tn as le,tt as ue,ut as de,v as fe,w as pe,z as me}from"./vendor-react-BmoHsW1x.js";import{S as he,b as ge,g as _e,x as T}from"./vendor-icons-other-Bme3ywH9.js";import{n as E,t as ve}from"./vendor-i18next-BKUzkAKP.js";import{A as ye,B as be,E as xe,F as Se,H as Ce,I as we,N as D,P as O,S as Te,T as Ee,U as De,V as k,c as A,f as Oe,i as ke,k as Ae,n as je,r as Me,s as Ne,t as Pe,v as Fe,w as j,x as M}from"./vendor-ui-core-CY_WyMIf.js";import{_t as Ie,vt as Le,yt as N}from"./vendor-utils-BWSB0jGn.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var Re=le(),ze={common:{account:`Account`,logout:`Logout`,selectProfile:`Select Profile`,add:`Add`,newProfileName:`New Profile Name...`,profileNameFormatTip:`Profile Name tip: 1-30 characters, duplicates not allowed`,profileLimitExceeded:`Profile limit exceeded (max {{max}})`,profileNameExists:`The profile name already exists`,profileNameFormatError:`Invalid Profile Name format`,create:`Create`,welcome:`Welcome`,createProfileToStart:`Create a profile to get started.`,loading:`Loading...`,errorNetwork:`Network Error`,confirmDelete:`Are you sure you want to delete this profile?`,confirmLogout:`Are you sure you want to log out?`,notFound:`404 - Not Found`,notFoundDesc:`The page you are looking for does not exist or has been removed.`,backToHome:`Back to Home`,apac:`Asia Pacific`,mainlandChina:`Mainland China`,HongKong:`Hong Kong`,Singapore:`Singapore`,Tokyo:`Tokyo`,NorthAmerica:`North America`,nae:`North America East`,naw:`North America West`,eu:`Europe`,invalidFormat:`Invalid file format`,import:`Import`,importSuccess:`Profile imported successfully`,importError:`Failed to import profile. Please ensure the format is correct and try again.`,save:`Save`,saveSuccess:`Settings saved`,close:`Close`,cancel:`Cancel`,continue:`Continue`,back:`Back`,copy:`Copy`,copied:`Copied`,copyKey:`Copy New Recovery Key`,confirm:`Confirm`,delete:`Delete`,rename:`Rename`,disable:`Disable`,confirmDisable:`Confirm Disable`,modeServerfull:`Serverfull Mode`,modeCloudflare:`Cloudflare Worker Mode`},nav:{setup:`Setup`,rules:`Rules`,stats:`Analytics`,logs:`Logs`,filter:`Filters`,settings:`Settings`},theme:{light:`Light`,dark:`Dark`,system:`System`},auth:{keepLoggedIn:`Keep me logged in for {{days}} days`,login:`Login to DNS Worker`,signup:`Create an account to start`,welcomeBack:`Welcome back, please enter your credentials`,protectInternet:`Protect your first hop on the internet`,error:`Error`,username:`Username`,usernamePlaceholder:`Your username`,password:`Password`,passwordPlaceholder:`Your password`,loginBtn:`Login`,signupBtn:`Sign Up`,totpVerification:`TOTP Verification`,noAccount:`Don't have an account? Sign up now`,haveAccount:`Already have an account? Back to login`,formatTipUsername:`Username tip: 5-32 chars, Unix format (starts with lowercase letter/underscore, containing lowercase letters, numbers, hyphens and underscores)`,formatTipPassword:`Password tip: 12-100 characters`,authFailed:`Authentication failed, check username or password`,networkError:`Network error, please try again later`,turnstileRequired:`Please complete the human verification`,turnstileError:`Verification service failed to load, please reload the page and try again`,verifying:`Verifying security check...`,passwordLeaked:`Your password was found in a public data breach. For your security, please use a strong random password.`,usernameExists:`This username is already taken. Please try another one.`,skip:`Skip`,showPassword:`Show password`,hidePassword:`Hide password`,geolocationRequired:`Geolocation failed. Please try re-entering.`,unauthorizedDefault:`Your session has ended. Please log in again.`,unauthorizedGeo:`Session invalidated due to geographic location change (exceeded distance limit).`,unauthorizedExpired:`Your session has expired. Please log in again.`,unauthorizedReuse:`Security notice: Multiple logins detected. Session terminated.`,unauthorizedRevoked:`Your session has been revoked or deleted. Please log in again.`,sessionLockTitle:`Idle Session Lock`,sessionLockDesc:`Lock the interface with a 4-digit PIN when the application is idle to protect active sessions from unauthorized physical access.`,enableSessionLock:`Enable Session Lock`,inactivityTimeout:`Inactivity Timeout`,timeout1m:`1 Minute`,timeout2m:`2 Minutes`,timeout5m:`5 Minutes`,timeout15m:`15 Minutes`,timeout30m:`30 Minutes`,timeout60m:`60 Minutes`,changePin:`Change PIN`,disablePin:`Disable Session Lock`,configurePin:`Configure PIN`,newPin:`New 4-Digit PIN`,pinHelper:`Digits only, e.g., 1234`,confirmPin:`Confirm New PIN`,verifyIdentity:`Verify Identity`,usePassword:`Use Password`,use2fa:`Use TOTP Code`,totpCode:`TOTP Code`,currentPassword:`Current Password`,disablePinWarning:`Disabling the PIN will disable the inactivity session locking feature completely.`,pinFormatTip:`PIN must be exactly 4 digits`,pinMatchTip:`PINs do not match`,passwordRequired:`Password is required for verification`,sessionLocked:`Session Locked due to inactivity`,sessionExpired:`Too many failed attempts. Session terminated.`,pinIncorrect:`Incorrect PIN`,attemptsRemaining:`{{count}} attempts remaining`,logoutAndReturn:`Log Out Session`,lockNow:`Lock Now`,cfErrorWithDetails:`Cloudflare Edge error (Error {{code}}: {{desc}}). Service is temporarily unavailable, please try again later. {{rayId}}`,cfErrorCodeOnly:`Cloudflare Edge error (Error {{code}}). Service is temporarily unavailable, please try again later. {{rayId}}`,cfErrorGeneric:`Cloudflare Edge error, service is temporarily unavailable. Please try again later. {{rayId}}`,htmlErrorGeneric:`Server returned an unexpected page response (HTTP {{status}}). Please try again later.`,tooManyAttempts:`Too many requests or attempts, temporarily rate limited. Please try again later.`,dbQuotaExceeded:`Database daily read quota has been exceeded. Please wait for the daily reset or upgrade.`,noUsersRegistered:`No accounts registered yet. Please sign up to become an administrator.`,registerAdminTitle:`Register Administrator Account`,registerAdminDesc:`No accounts in the system. The first user will become the system administrator.`,goToRegisterAdmin:`Register Admin`,dbNotInitialized:`Database is not initialized or missing tables. Please run npm run db:migrate:prod in terminal.`,verifyWithPasskey:`Verify with Passkey`,orUseTotp:`Or use authenticator code`,passwordRequiredFirst:`Please enter your password first`,passkeyVerification:`Passkey Verification`,passkeyPromptDesc:`Verify using your registered biometric or security key`,forgotPassword:`Forgot password?`,forgotPasswordTitle:`Reset Password`,forgotPasswordDesc:`Recover your account securely using multi-factor authentication (Passkey or TOTP).`,noMfaContactAdmin:`This account has not configured any multi-factor authentication (Passkey or TOTP) and cannot self-recover. Please contact a system administrator to reset your password.`,chooseMfaForReset:`Choose an authentication method to verify your identity:`,authViaPasskey:`Verify with Passkey`,authViaPasskeyDesc:`1-click authentication using biometrics or a hardware security key`,authViaTotp:`Verify with Authenticator App (TOTP)`,authViaTotpDesc:`Enter the 6-digit dynamic code from your authenticator app`,authViaRecoveryKey:`Use Emergency Recovery Key`,authViaRecoveryKeyDesc:`Enter a 10-character emergency recovery key`,verifyAndProceed:`Verify and Continue`,setNewPassword:`Set New Password`,setNewPasswordDesc:`Identity verified. Please set a new password for your account.`,newPassword:`New Password`,confirmNewPassword:`Confirm New Password`,passwordMismatch:`New passwords do not match`,saveAndReset:`Save and Reset Password`,resetSuccessTitle:`Password Reset Successfully`,resetSuccessDesc:`Your password has been updated and all existing login sessions have been terminated. Please log in with your new password.`,goToLogin:`Proceed to Login`,mfaSetupTitle:`Enhance Account Security`,mfaSetupDesc:`Configuring Multi-Factor Authentication (MFA) safeguards your account, unlocks passwordless login, and enables self-service password recovery.`,mfaRecommended:`Recommended`,passkeySignupDesc:`Use Touch ID, Face ID, Windows Hello, or a security key. Lightning-fast and phishing-resistant.`,bindPasskeyNow:`Register Passkey Now`,mfaUniversal:`Universal`,totpSignupDesc:`Use Google Authenticator, 1Password, or any standard authenticator app.`,setupTotpBtn:`Set Up Authenticator App`,skipMfaSetup:`Skip for now, complete signup`,backToMfaOptions:`Back to options`,otherOptions:`Other options`,next:`Next`,mfaVerification:`Multi-Factor Authentication`,totpPrompt:`Enter the 6-digit dynamic code from your authenticator app`,recoveryPrompt:`Enter your 30-digit recovery key (5 groups of 6 digits)`,recoveryChecksumError:`Checksum error: this group is not divisible by 11`,recoveryChecksumErrorDesc:`Checksum error in group {{groups}}, please check and try again`,backToPasskey:`Back to Passkey verification`,recoveryKeyRequired:`Please enter your recovery key`,totpFormatError:`Please enter a valid 6-digit code`,registrationDisabledTitle:`Registration Disabled`,registrationDisabledDesc:`New user registration is currently closed. Please contact a system administrator to obtain an account.`,backToLogin:`Back to login`,jwtSecretPreset:`Default preset JWT_SECRET detected. Please update and customize JWT_SECRET in environment variables before deploying.`,jwtSecretMissing:`JWT_SECRET is missing. Please configure a secure key in environment variables before deploying.`,jwtSecretInvalid:`JWT_SECRET format is invalid, please check configuration.`,orUseOtherMfa:`or use another method`,totpUseRecovery:`Use Recovery Key`,totpUseApp:`Use Authentication App`,mfaMethodTotp:`Authenticator App (TOTP)`,invalidTotp:`Invalid TOTP verification code.`},setup:{title:`Setup Guide`,subtitle:`Configure your devices to use DNS Worker and master your internet security.`,regionOptimization:`Region Optimization (Windows)`,otherRegion:`Other Region`,copied:`Copied to clipboard`,verifyConnection:`Verify Your Connection`,verifying:`Verifying...`,connected:`Connected`,notConnected:`Not Connected`,profileMatch:`Filtering through this profile.`,profileMismatch:`Using Obex, but Profile ID mismatch.`,verifyHint:`Click to verify after configuration.`,refreshStatus:`Refresh Status`,egressIp:`Egress IP Address`,currentLocation:`Current Location`,edgeServer:`Edge Server`,serverLocation:`Server Location`,dohUrlTitle:`Current Config DoH URL`,accessPointTitle:`Access DoH URL`,defaultAccessPointName:`Device-1`,moreAccessPoints:`More Access Points ↗`,accessPointName:`Access Point Name`,accessPointToken:`Access Key`,addAccessPoint:`Add Access Point`,renameAccessPoint:`Rename`,rotateToken:`Rotate Key`,rotateTokenConfirmDesc:`Rotating the key will immediately disconnect devices using the old key. Are you sure you want to continue?`,deleteAccessPoint:`Delete`,deleteAccessPointConfirm:`Are you sure you want to delete this access point?`,manageAccessPoints:`Manage Access Points`,searchAccessPoints:`Search Access Points...`,accessPointNameExists:`Access Point name already exists`,accessPointCreated:`Access Point created successfully`,accessPointRenamed:`Access Point renamed successfully`,accessPointDeleted:`Access Point deleted successfully`,accessPointNameFormatTip:`Access Point Name tip: 1-30 characters, letters, numbers, hyphens and underscores only`,accessPointLimitExceeded:`Access Point limit exceeded (max {{max}})`,tokenRotated:`Access Key rotated successfully`,whatIsDoh:`What is DoH?`,dohDesc:`DNS over HTTPS (DoH) is a protocol for performing remote DNS resolution via the HTTPS protocol.`,dohBenefit1:`Prevents ISP hijacking and eavesdropping`,dohBenefit2:`Enhances DNS resolution and overall security`,learnMore:`Learn more`,copyUrl:`Copy URL`,browsers:`Browsers`,browserTitle:`Browser Configuration (Chrome/Edge)`,browserSteps:`Settings > Privacy and security > Security > Use secure DNS > Custom > Paste URL.`,browserTips:`Or paste this into your address bar:`,apple:`Apple`,appleTitle:`macOS / iOS / iPadOS / tvOS`,appleDesc:`Apple devices support quick configuration via profiles.`,appleWarning:`Profiles cannot be installed in 'Lockdown Mode'. Please ensure your system is not in this state.`,downloadConfig:`Download Profile`,appleInstallHint:`After downloading, go to 'Settings' > 'Profile Downloaded' to install.`,windows:`MS Windows`,windowsTitle:`Windows 11 Configuration`,windowsStep0:`Settings > Network & internet`,windowsStep1:`Properties > DNS server assignment > Edit > Manual.`,windowsStep2:`Fill in DNS IPs:`,windowsStep3:`Enable DoH (manual template) and paste URL.`,windowsStep4:`Disable Fallback to plaintext.`,android:`Android`,androidTitle:`Android / Private DNS (DoT & DoQ)`,androidDesc:`Supports native system Private DNS (DoT) or third-party apps for DoH.`,androidDotTitle:`Private DNS (DoT / DoQ)`,androidDotDesc:`Settings > Network & internet > Private DNS > Private DNS provider hostname (also supports modern DoQ clients):`,androidWildcardHint:`(Wildcard certificate, routes to this profile)`,androidSingleDomainHint:`(Single-domain certificate, routes to default profile)`,androidIpNotice:`Accessed via IP or local address. Android Private DNS only accepts domain names; please configure SERVERFULL_DOT_DOMAIN in .env (e.g. dns.example.com) to view hostname guide.`,androidWarning:`Note: Android's native 'Private DNS' uses the DoT protocol. When deployed on Cloudflare Workers, use apps like Intra for DoH. When running in Serverfull mode with TLS configured, you can directly use the DoT hostname above.`,dynamicFromDomain:`Dynamic (from {{domain}})`,dynamicFromDomainV6:`Dynamic IPv6 (from {{domain}})`,linux:`Linux`,linuxTitle:`Linux (Cloudflared DoH Proxy)`,linuxDesc:`Run a local DNS proxy to forward queries to Obex via DoH.`,linuxStep1:`Run this script to install cloudflared and setup systemd service (listening on 127.0.0.1:53).`,linuxStep2:`Change your system DNS to 127.0.0.1.`,routeros:`MikroTik`,routerosTitle:`MikroTik RouterOS`,routerosDesc:`Use this resolver for every device on your network by setting it as the router's DoH server.`,routerosStep1:`Run in the RouterOS terminal (RouterOS 7):`,routerosNote:`Queries forwarded by the router show up in the logs as the router, not as individual devices. Set up devices directly if you need per-device logs.`,routerosDocs:`MikroTik DNS documentation`,accessPointsCardTitle:`Access Points`,currentAccessPoint:`Current Access Point`,selectAccessPointToConfigure:`Select Access Point to Configure`,quic:`QUIC & HTTP/3`,quicTitle:`DNS over QUIC (DoQ) & HTTP/3 (DoH3)`,quicDesc:`Native support for RFC 9250 (DNS over QUIC) and RFC 9460 (HTTP/3 & Encrypted Client Hello), featuring 0-RTT handshakes and anti-SNI inspection protection.`,doqTitle:`DNS over QUIC (DoQ - RFC 9250)`,doqAppHint:`Compatible with modern clients such as AdGuard, Clash Meta (Mihomo), Sing-box, etc.:`,doqWildcardHint:`(Wildcard certificate, routes to this profile)`,doqSingleDomainHint:`(Single-domain certificate, routes to default profile)`,doqIpNotice:`DoQ requires a TLS certificate and a valid domain name. Please set SERVERFULL_DOT_DOMAIN in .env and refresh.`,h3Title:`HTTP/3 (DoH3) & ECH (Encrypted Client Hello)`,h3Desc:`Modern browsers (Chrome/Firefox/Safari) automatically negotiate HTTP/3 and ECH encryption via Alt-Svc and RFC 9460 HTTPS records:`,ddrHint:`Integrated with RFC 9460 Section 8 DDR (Discovery of Designated Resolvers), enabling clients to automatically negotiate and upgrade to DoQ/DoT and ECH via _dns queries.`,e2eeDesc:`Encrypts DNS logs at the edge with your unique post-quantum hybrid key (P256-MLKEM768). The cloud cannot inspect logs; data is decrypted and analyzed only on devices authorized by your hardware Passkey.`,detecting:`Detecting...`,dotUnavailableCfTitle:`DoT / DoQ Unavailable in Cloudflare Worker Mode`,dotUnavailableCfDesc:`Currently running in Cloudflare Worker (Serverless) mode. Due to edge serverless runtime network limitations, listening on raw TCP/UDP port 853 is unsupported. To use DoT or DoQ, deploy in Standalone (Serverfull) mode; in the current mode, please use DoH (DNS over HTTPS).`},rules:{title:`Custom Rules`,subtitle:`Manually control DNS behavior for specific domains (Allow, Block, or Redirect).`,action:`Action`,typeBlock:`BLOCK`,typeAllow:`ALLOW`,typeRedirect:`REDIRECT`,pattern:`Domain Pattern`,patternPlaceholder:`example.com or *.test.com`,addRule:`Add Rule`,redirectSettings:`Redirect Settings`,aRecord:`A Record (IPv4)`,aaaaRecord:`AAAA Record (IPv6)`,cnameRecord:`CNAME Record`,txtRecord:`TXT Record`,optional:`(optional)`,ipv4Placeholder:`e.g. 1.2.3.4`,ipv6Placeholder:`e.g. 2001:db8::1`,cnamePlaceholder:`e.g. target.com`,txtPlaceholder:`Enter text content...`,saveRedirectRule:`Save Redirect Rule`,noRulesTitle:`No Custom Rules`,noRulesDesc:`You haven't added any manual rules yet.`,tableAction:`Action`,tablePattern:`Pattern`,tableDetails:`Details`,tableOps:`Ops`,labelBlock:`Block`,labelAllow:`Allow`,labelRedirect:`Redirect`,detailBlock:`Resolves to`,detailAllow:`Forward to upstream`,patternEmpty:`Domain pattern cannot be empty`,duplicatePattern:`Rule for this domain already exists`,addSuccess:`Rule added successfully`,addFailed:`Failed to add rule`,updateSuccess:`Rule updated successfully`,updateFailed:`Failed to update rule`,tableCreatedAt:`Added Time`,clickToSort:`Click to sort`,editRule:`Edit Rule`,cancel:`Cancel`,saveChanges:`Save Changes`,delete:`Delete`,close:`Close`},analytics:{customRange:`Custom Range`,startTime:`Start Time`,endTime:`End Time`,apply:`Apply`,custom:`Custom`,totalQueries:`Total Queries`,blocked:`Blocked`,redirected:`Redirected`,blockRate:`Block Rate`,activeDevices:`Active Devices`,activeIPs:`Active IP addresses`,queryTrend:`Query Trend`,noRecords:`No records in this period`,topAllowed:`Top Allowed Domains`,topBlocked:`Top Blocked Domains`,noData:`No data`,clientActivity:`Client Activity`,ipAddress:`IP Address`,location:`Location`,queries:`Queries`,destinationDistribution:`Destination Distribution`,requests:`Requests`,topIsps:`Top ISPs`,pressToShowMore:`Press to show more`,maximize:`Maximize`,minimize:`Minimize`,loading:`Loading...`},logs:{title:`Query Logs`,subtitle:`View DNS resolution logs and block history.`,realtime:`Realtime`,allStatus:`All Status`,onlyPass:`Allowed`,onlyBlock:`Blocked`,onlyRedirect:`Redirected`,searchPlaceholder:`Search domains...`,noResults:`No results found`,noRecords:`No records yet`,noResultsDesc:`No domain records matching "{{query}}" found.`,noRecordsDesc:`No DNS queries received in the selected timeframe.`,tableTime:`Time`,tableDomain:`Domain`,tableAnswer:`Answer`,tableSource:`Source`,tableType:`Type`,tableStatus:`Status`,tableReason:`Reason`,loadedAll:`Loaded all {{count}} records`,realtimeLoadMoreTip:`Disable 'Real-time' mode to load more`,filterAccessPoint:`Access Point`,allAccessPoint:`All Access Points`,allCountry:`All Countries`,filterCountry:`Dest Country`,allIsp:`All ISPs`,filterIsp:`ISP`,timeRange:`Time Range`,clearFilters:`Clear Filters`,details:`Query Details`,detailAccessPoint:`Access Point`,basicInfo:`Basic Info`,detailDomain:`Domain`,detailType:`Type`,detailTime:`Time`,detailLatency:`Latency`,detailProfile:`Profile Used`,detailStatus:`Status`,detailUpstream:`Upstream`,detailReason:`Reason`,detailNoReason:`None (Allowed)`,detailECS:`ECS`,resolutionResult:`Resolution Result`,noResult:`No result`,networkDetails:`Network Details`,clientSource:`Client (Source)`,destination:`Destination`,quickActions:`Quick Actions`,actionAllow:`Allow`,actionBlock:`Block`,actionRedirect:`Redirect`,statusPass:`PASS`,statusBlock:`BLOCK`,statusRedirect:`REDIRECT`,export:`Export Logs`,rewritten:`Rewritten`,logDetails:`Log Details`,searchCountry:`Search country...`,noMatchingCountries:`No matching countries found`,searchIsp:`Search ISP...`,noMatchingIsps:`No matching ISPs found`,loadingIsps:`Loading ISPs...`,loggingDisabledTitle:`Logging Disabled`,loggingDisabledDesc:`Log retention for this profile is set to 'Disabled'. No query logs are recorded. To view logs, enable log retention in Settings.`,detailIsClientEncrypted:`Client-Side Encrypted`,encryptedLocked:`Yes (Locked)`,encryptedDecrypted:`Yes (Decrypted Locally)`,encryptedYes:`Yes`,notEncrypted:`No`,encryptedRecord:`[Encrypted Record]`,exportSuccess:`Logs exported successfully`,exportError:`Failed to export logs`},filtering:{title:`External Filters`,subtitle:`Subscribe to third-party blocklists (e.g., AdGuard).`,formatDisclaimer:`This system only supports a limited set of AdGuard format lists and plain domain lists. Only basic blocking rules (e.g., ||example.com^) are supported, and "@@" whitelist rules will be bypassed. "$" rewrite rules and "*" wildcard/omit rules are temporarily not supported. The system will automatically match all subdomains of the domains.`,syncAll:`Sync All Lists`,urlPlaceholder:`Enter list URL...`,addSubscription:`Add List`,supportedFormats:`Supported Formats:`,formatAdGuard:`Compatible with ||domain.com^ rules.`,formatHosts:`Compatible with standard hosts files.`,noListsTitle:`No Lists Added`,noListsDesc:`You haven't subscribed to any external blocklists yet.`,tableUrl:`List URL`,tableLastSync:`Last Sync`,tableStatus:`Status`,tableOps:`Ops`,neverSynced:`Never`,enabled:`Active`,disabled:`Error`,statusNormal:`Active`,statusDisabled:`Disabled`,statusOutdated:`Outdated`,statusMissing:`Missing`,presetOisdBig:`OISD Big (Ads/Malware etc.)`,presetOisdNsfw:`OISD NSFW (Adult/Shock content)`,presetAdGuard:`AdGuard AdServers`,presetStevenBlack:`StevenBlack Hosts`,syncTaskStarted:`Sync Task Started`,syncCheckComplete:`Sync Check Completed`,syncTaskFailed:`Sync Task Failed`,syncing:`Syncing...`,syncSuccess:`List synced successfully`,syncError:`Failed to sync list`,syncErrorDesc:`Failed to download or parse the filter list. Please check the URL and ensure it is accessible.`,deleteConfirm:`Are you sure you want to delete this list subscription?`,deleteSuccess:`List deleted successfully`,deleteError:`Failed to delete list`,addSuccess:`List added successfully`,addError:`Failed to add list`,listEnabled:`List active`,listEnabledHint:`Disable to stop filtering with this list without deleting it. Its subscription and sync history are kept.`,listEnabledToast:`List enabled - rebuilding filter`,listDisabledToast:`List disabled - rebuilding filter`,checkDomain:`Check a domain`,checkDomainHint:`Lists are stored as compact filters, so their entries cannot be browsed. You can test any domain against this list instantly.`,check:`Check`,checkBlocked:`Blocked by this list`,checkNotBlocked:`Not blocked by this list`,checkViaParent:`(matched a parent domain)`,checkNotSynced:`This list has not finished syncing yet, so there is nothing to check against.`,checkListDisabled:`This list is currently disabled, so it is not filtering anything right now.`,checkProbabilisticRate:`Filters are probabilistic: roughly 1 match in {{odds}} is a false positive. Adding an exception also covers every subdomain of it.`,checkProbabilistic:`Filters are probabilistic, so a small share of matches are false positives. Adding an exception also covers every subdomain of it.`,checkAllowAction:`Allow anyway`,checkAllowed:`Exception added - this domain now resolves`,allowRuleAdded:`Exception added for {{domain}}`,listDetails:`List Details`},settings:{title:`Settings`,subtitle:`Manage resolution policies and advanced options.`,saveChanges:`Save Changes`,saveSuccess:`Settings saved successfully`,saveError:`Failed to save settings`,upstreamTitle:`Upstream Services`,dohUrl:`DoH URL`,dnsIP:`DNS IP`,classicDns:`Classic DNS`,httpsOnly:`HTTPS Only`,upstreamDesc:`All unblocked queries will be forwarded to this upstream server. Supports DoH (HTTPS), DoT (tls://), DNS Stamp (sdns://), or Classic DNS. Classic DNS is forwarded via TCP.`,defaultPolicyTitle:`Default Policy`,onNoMatch:`On No Match`,allowAll:`Allow all queries`,blockAll:`Block all queries`,logRetentionTitle:`Log Retention`,retentionDuration:`Duration`,retentionDesc:`Logs are cleared automatically.`,skipLogOnPass:`Do not log allowed queries`,skipLogOnPassDesc:`When enabled, normal allowed queries will not be recorded. Only blocked, redirected, or failed queries are logged, significantly reducing storage and write operations.`,ecsTitle:`EDNS Client Subnet (ECS)`,ecsUpstreamHint:`Not all upstream DNS servers support ECS. For recursive resolvers like ControlD, ECS affects internal routing only.`,sendEcs:`Send ECS`,customSubnet:`Custom Subnet`,customIpv4:`Custom IPv4 Subnet`,customIpv6:`Custom IPv6 Subnet`,ipv4Hint:`(e.g., 1.2.3.0/24)`,ipv6Hint:`(e.g., 2001:db8::/32)`,testToolTitle:`Resolution Test Tool`,testDomain:`Test Domain`,recordType:`Record Type`,runTest:`Run Test`,domainPlaceholder:`e.g., google.com`,hitRule:`Rule / Reason`,defaultAllow:`Default Allow`,parseLatency:`Latency`,sourceIp:`Source IP`,answerSection:`Answer Section`,noRecordsReturned:`No records returned for this query.`,diagnostics:`Diagnostics`,errorDetail:`Error Details`,upstreamResponse:`Upstream Response`,presetCloudflareSecurity:`Cloudflare Security`,presetCloudflareFamilies:`Cloudflare Families`,presetQuad9:`Quad9 (No Log/No ECS)`,presetQuad9Ecs:`Quad9 ECS (No Log/With ECS)`,presetControlDFree:`ControlD Free (Remove ads/tracking/malicious/phishing)`,presetControlDUncensored:`ControlD Uncensored`,presetAdGuard:`AdGuard (Anti-track)`,presetAdGuardFamily:`AdGuard Family`,retention10m:`10 Minutes`,retention1h:`1 Hour`,retention24h:`24 Hours`,retention7d:`7 Days`,retention30d:`30 Days`,retention90d:`90 Days`,retention180d:`180 Days`,retention360d:`360 Days`,retentionMaxSuffix:`(Maximum)`,daysUnit:`Days`,blockModeTitle:`Block Response Mode`,blockModeDesc:`Response content when a domain is blocked`,default:`Default`,NXDOMAIN:`NXDOMAIN`,NODATA:`NODATA`,customIP:`Custom IP`,export:`Export Config`,exportSuccess:`Config exported successfully`,exportError:`Export failed`,nameUpdateSuccess:`Profile name updated`,saving:`Saving...`,echTitle:`Rewrite ECH`,enableEch:`Enable ECH Rewrite`,echDesc:`For Cloudflare-proxied domains, automatically injects and rewrites ECH parameters and outer SNI on HTTPS (Type 65) / SVCB (Type 64) queries to protect domain privacy.`,echFrontingTitle:`Outer SNI (ECH Fronting)`,echFrontingDesc:`Domain name visible in plaintext on the outer TLS handshake`,echFrontingCustomPlaceholder:`Enter custom fronting domain, e.g. crypto.cloudflare.com`,echPreferred:`Recommended`,whatIsEch:`What is ECH (Encrypted Client Hello)?`,echHelpDesc:`ECH (Encrypted Client Hello) is a crucial TLS 1.3 privacy extension that fully encrypts the real Server Name Indication (Inner SNI), exposing only a fronting domain (Outer SNI) to network observers.`,echHelpBenefit1:`Prevents ISPs and eavesdroppers from inspecting the exact domain or subdomain you are visiting`,echHelpBenefit2:`Combines with DoH and ECH Fronting to eliminate plaintext SNI leakage and filtering`,retentionDisabled:`Disabled (Privacy Concerns)`,retentionDisabledDesc:`Logging is disabled. The system will not record any DNS query logs and will immediately clear historical logs.`,localRetentionTitle:`Local Storage & Retention`,localRetentionDuration:`Local Retention Duration`,localRetentionDesc:`Logs are stored persistently in your browser's local SQLite database (OPFS), delivering instant local search and saving cloud storage costs. Local logs exceeding this duration will be automatically purged.`,localRetention30d:`30 Days`,localRetention90d:`90 Days (Recommended)`,localRetention180d:`180 Days`,localRetention365d:`365 Days (1 Year)`,localRetentionUnlimited:`Unlimited (Until manually cleared)`,localEngineStorage:`Storage Engine`,localEngineOpfs:`OPFS Persistent Storage`,localEngineMemory:`In-Memory Temporary (Private Mode)`,localCachedCount:`Local Cached Logs`,localCachedCountUnit:`{{count}} records`,clearLocalData:`Clear Local Logs`,clearLocalDataConfirm:`Are you sure you want to clear all local logs stored for this profile? (Server logs remain unaffected)`,clearLocalDataSuccess:`Local logs cleared successfully`,clearLocalDataError:`Failed to clear local logs`,e2eeTitle:`Log End-to-End Encryption (E2EE)`,e2eeEnabled:`Enabled`,e2eeDisabled:`Disabled`,e2eeDesc:`Encrypts DNS logs at the edge with your unique post-quantum hybrid key (P256-MLKEM768). The cloud cannot inspect logs; data is decrypted and analyzed only on devices authorized by your hardware Passkey.`,e2eePasskeyRequired:`End-to-End Encryption requires hardware Passkey (WebAuthn) support. No registered Passkey was detected on your account. Please add a Passkey in Account Settings first.`,e2eeGoToAccount:`Go to Account Settings to Add Passkey`,e2eeEnableSwitch:`Enable Log End-to-End Encryption`,e2eeKeyType:`Encryption Algorithm`,e2eeDeviceState:`Current Device State`,e2eeUnlocked:`Unlocked on this device`,e2eeUnlockButton:`Verify Passkey to Unlock`,e2eeProtectedPasskeys:`Authorized Passkey Credentials`,e2eeDevicesUnit:`credential(s)`,e2eeEnableSuccess:`End-to-End Encryption enabled successfully`,e2eeEnableError:`Failed to enable End-to-End Encryption`,e2eeDisableSuccess:`End-to-End Encryption disabled`,e2eeDisableError:`Failed to disable End-to-End Encryption`,e2eeUnlockSuccess:`Private key unlocked successfully on this device`,e2eeUnlockError:`Failed to verify Passkey and unlock`,e2eeDisableConfirm:`Are you sure you want to disable End-to-End Encryption? Future DNS logs will be stored in plaintext. (Historical encrypted logs still require private key to decrypt)`,e2eeUnlockPrompt:`This profile uses End-to-End Encryption. Verify your Passkey to decrypt and view plaintext logs.`,e2eeUnlockNow:`Unlock Encrypted Logs`,e2eeUnlockRecovery:`Unlock with Recovery Key`,securityTitle:`Security & Access`,rotateKeyTitle:`Access URL`,rotateKeyDesc:`Generate a new access URL for this profile. Any devices currently using the old URL will immediately lose their connection to DNS Worker and will need to be reconfigured with the new URL.`,rotateKeyBtn:`Rotate URL`,copied:`Copied to clipboard`,nameUpdateFailed:`Failed to update profile name`},account:JSON.parse(`{"title":"Account Settings","subtitle":"Manage your security credentials and system users.","e2ee":{"title":"End-to-End Encryption (E2EE)","desc":"Secures your sensitive data using client-side post-quantum hybrid encryption (P256-MLKEM768 + AES-256-GCM / NIST FIPS 203) envelope encryption backed by hardware Passkeys. Cloudflare Workers and database only store ciphertext; data is strictly decrypted on your authorized devices.","pqcTag":"PQC / NIST FIPS 203","upgradePqcBtn":"Upgrade to Post-Quantum (PQC)","upgradePqcSuccess":"Successfully upgraded to Post-Quantum Hybrid Encryption (P256-MLKEM768)","upgradePqcError":"Failed to upgrade to Post-Quantum Encryption","enabled":"Enabled","disabled":"Disabled","passkeyRequired":"End-to-End Encryption requires at least one hardware Passkey (WebAuthn). Please register a Passkey in the Multi-Factor Authentication section above.","scopesTitle":"Encrypted Data Categories","scopesSubtitle":"Choose which data categories to encrypt end-to-end with your hardware Passkey.","scopeLogsTitle":"DNS Query Logs","scopeLogsDesc":"Encrypts domain names, client IPs, response records, and upstream latency at edge before persisting to D1.","scopeRulesTitle":"Custom Rules & Redirects","scopeRulesDesc":"Client-side encrypted custom allow/block rules, rewrite targets, and redirection policies.","scopeClientsTitle":"Client & Device Metadata","scopeClientsDesc":"Client device aliases, access point identifiers, and network topology metadata.","statusActive":"Active","statusPlanned":"Planned","profileSelect":"Configure Profile","keyType":"Encryption Algorithm","deviceState":"Device Status","unlocked":"Unlocked on this device","lockDevice":"Lock this device","lockSuccess":"Locked, private key cleared from local storage","unlockButton":"Verify Passkey to Unlock","protectedPasskeys":"Authorized Passkey Credentials","devicesUnit":"credential(s)","enableSuccess":"End-to-End Encryption enabled successfully","enableError":"Failed to enable End-to-End Encryption","disableSuccess":"End-to-End Encryption disabled","disableError":"Failed to disable End-to-End Encryption","unlockSuccess":"Private key unlocked successfully on this device","unlockError":"Failed to verify Passkey and unlock","disableConfirm":"Are you sure you want to disable End-to-End Encryption for this profile? Future DNS logs will be stored in plaintext. (Historical encrypted logs still require your private key to decrypt)","noKeypairTitle":"End-to-End Encryption Keypair Not Initialized","noKeypairDesc":"End-to-End Encryption requires generating a dedicated post-quantum (P256-MLKEM768) keypair for your account. The private key will be envelope-encrypted via your Passkey or Recovery Key so the server cannot read your logs.","initPasskeyBtn":"Verify Existing Passkey to Generate Keypair","addPasskeyBtn":"Add Passkey to Support Hardware Encryption","initRecoveryBtn":"Generate Keypair with Recovery Key","initWithRecoveryTitle":"Generate Keypair with Recovery Key","initWithRecoveryDesc":"Enter your current 30-digit emergency recovery key. The system will generate a post-quantum (P256-MLKEM768) keypair locally and wrap it. Under the single-use rule, the recovery key will be rotated immediately upon use.","keypairGenSuccess":"End-to-End Encryption keypair generated successfully!","keypairGenError":"Failed to generate End-to-End Encryption keypair","unlockRecoveryButton":"Unlock with Recovery Key","unlockWithRecoveryTitle":"Unlock with Recovery Key","unlockWithRecoveryDesc":"Enter your 30-digit emergency recovery key. Note: Under the single-use rule, the recovery key will be rotated immediately once verified.","recoveryKeyInputLabel":"Recovery Key","unlockConfirm":"Unlock Private Key","recoveryUnlockFailed":"Invalid Recovery Key or decryption failed","generateKeypairBtn":"Generate Keypair","enableSwitch":"Enable Query Logs End-to-End Encryption","enableSwitchDesc":"Encrypts domain names, client IPs, response records, and upstream latency on the edge before persisting to cloud storage.","trustBoundaryTitle":"Security & Trust Boundary Notice","trustBoundaryDesc":"All DNS query traffic inevitably passes through the hosting server (Cloudflare Workers edge nodes or self-hosted server runtime) during resolution, where queries are processed in memory before being encrypted and saved. This feature strictly protects data at rest in the cloud database (D1 / persistent storage) from data leaks or offline analysis; it does not conceal active network traffic from the hosting server processing the request.","initFailed":"Failed to initialize End-to-End Encryption","enrollPasskeyBtn":"Enroll Current Passkey","enrollPasskeySuccess":"Passkey successfully enrolled into E2EE!","enrollPasskeyFailed":"Failed to enroll Passkey","resetKeypairBtn":"Reset Keypair","confirmResetBtn":"Confirm Reset & Re-generate","resetKeypairTitle":"Reset E2EE Keypair","resetKeypairDesc":"If your recovery key does not match or cannot decrypt, and no passkeys are enrolled, resetting will purge old keys from the server and generate a brand-new post-quantum (P256-MLKEM768) keypair.","resetKeypairPasskeyNotice":"Confirming will immediately invoke your configured Passkey to encrypt and authorize the new keypair.","resetKeypairRecoveryNotice":"Confirming will prompt you to enter your current Recovery Key to generate and encrypt the new keypair.","resetAndInitPasskeySuccess":"Keypair successfully reset and re-encrypted with your Passkey!","resetSuccessEnterRecovery":"Old keys purged. Please initialize a new keypair with your Recovery Key.","resetFailed":"Failed to reset keypair"},"jwtSecretWarningTitle":"Security Notice: Weak JWT Secret","jwtSecretWarningDesc":"The current JWT_SECRET is shorter than 32 characters, which introduces potential security risks. Please configure the KEK_v1 environment variable before rotating JWT_SECRET to ensure existing envelope-encrypted TOTP and recovery keys remain decryptable.","roleAdmin":"Administrator","roleUser":"User","personalInfo":"Personal Info","username":"Username","userId":"User ID","changePassword":"Change Password","currentPassword":"Current Password","newPassword":"New Password","updatePassword":"Update Password","userManagement":"User Management (Admin)","createUser":"Create User","role":"Role","createdAt":"Created At","lastActive":"Last Active","lastResolve":"Last Resolve","actions":"Actions","dangerZone":"Danger Zone","clearLogs":"Clear All Logs","clearLogsDesc":"This will delete all resolution logs for all your profiles. This cannot be undone.","currentDbSize":"Current Database Size","clearLogsBtn":"Clear Logs Now","deleteAccount":"Delete Account","deleteAccountDesc":"Permanently delete your account and all associated data. You will lose all DNS configurations.","deleteAccountBtn":"Delete Account Now","createNewUser":"Create New User","initialPassword":"Initial Password","userRole":"User Role","cancel":"Cancel","createNow":"Create Now","formatTipUsername":"Username tip: 5-32 chars, Unix format","formatTipPassword":"Password tip: 12-100 characters","passwordSuccess":"Password updated successfully","updateFailed":"Update failed","confirmDeleteUser":"Are you sure you want to delete this user? All their profiles will be removed.","confirmClearLogs":"Are you sure you want to clear all logs? This cannot be undone.","clearLogsSuccess":"Logs cleared successfully","confirmDeleteAccount":"WARNING: Are you sure you want to delete your account?\\n\\nAll DNS profiles, custom rules, and logs will be permanently deleted. This is irreversible!","inactivityPolicy":"Inactivity Policy","inactivityDesc":"To protect user privacy and save resources, all query logs and DNS configurations for accounts with no resolution activity for 30 days will be cleared. The account itself will be removed after 90 days of no login activity.","systemSettings":"System Settings (Admin)","adminOnlyView":"Visible to admins only","featureToggle":"Feature Toggle","enableTurnstileSignup":"Enable verification on Signup","enableTurnstileLogin":"Enable verification on Login","totp":{"title":"Authenticator App (TOTP)","enabled":"Enabled","disabled":"Disabled","setupDesc":"Add an extra layer of security to your account. Use Google Authenticator, Authy, or any TOTP-compatible app.","setupBtn":"Set Up Authenticator App (TOTP)","scanQR":"Scan this QR code with your authenticator app:","orEnterManually":"Or enter the key manually:","enterCode":"Verify with your authenticator code:","enterCodeHint":"Enter the 6-digit code to confirm the key was scanned correctly.","activate":"Activate TOTP","skipPassword":"Passwordless login — hide password field at login (TOTP only)","disable":"Disable Authenticator App (TOTP)","disableTitle":"Disable Authenticator App (TOTP)","disableWarning":"After disabling TOTP, if no Passkeys are configured, your account will only be protected by password.","confirmDisable":"Disable TOTP","recoveryKeysTitle":"Save Your Recovery Keys","recoveryKeysWarning":"Store your recovery key safely (e.g., in a password manager). Each key can only be used once.","copyKeys":"Copy All Keys","copied":"Copied!","done":"Done — I've saved my keys","activeDesc":"Authenticator app (TOTP) is active and generating 6-digit verification codes for your account.","invalidCode":"Please enter a 6-digit code","testBtn":"Test Code","testTitle":"Test Authenticator App (TOTP)","testDesc":"Enter the 6-digit verification code from your authenticator app to verify that it is properly synchronized and generating valid codes.","testSuccess":"Verification successful! Your authenticator app is configured properly.","testFailed":"Invalid verification code or clock desynchronized","verificationCode":"6-Digit Verification Code","verifyBtn":"Verify Now"},"sessions":{"session":"Session","title":"Active Sessions","device":"Device","ip":"IP Address","loginTime":"Login Time","expires":"Expires","action":"Action","current":"Current","revoke":"Revoke","revokeFailed":"Failed to revoke session.","empty":"No active sessions found.","unknownDevice":"Unknown Device","revokeOthers":"Revoke Other Sessions","confirmRevokeOthers":"Revoke Other Sessions","revokeOthersConfirmText":"Are you sure you want to revoke all other active sessions? All other logged-in devices will be signed out immediately."},"activity":{"title":"Account Activity","action":"Event","time":"Time","session":"Session","ip":"IP Address","empty":"No activity recorded yet.","loadMore":"Load More","signup":"Register","loginSuccess":"Login","loginFail":"Login Failed","logout":"Logout","pwChangeSuccess":"Password Changed","pwChangeFail":"Password Change Failed","totpVerifySuccess":"TOTP Verification Succeeded","totpVerifyFail":"TOTP Verification Failed","totpSetup":"Configured TOTP Authenticator","totpRemoved":"Disabled TOTP Authenticator","recoveryKeyUsed":"Recovery Key Used","sessionRevoked":"Session Revoked","reasons":{"user_active":"Active logout","user_revoke":"Session revoked","expired":"Session expired","geolocation_mismatch":"Geographic location mismatch","geolocation_missing":"Geolocation missing","token_reuse":"Token reused (security warning)"},"passkeyRegistered":"Passkey Registered","passkeyDeleted":"Passkey Deleted","passkeyVerifySuccess":"Passkey Verified","passkeyVerifyFail":"Passkey Verification Failed","recoveryKeyRotated":"Recovery key rotated","device":"Device"},"passkey":{"title":"Passkeys","addBtn":"Add Passkey","desc":"Use biometric authentication (Touch ID, Face ID, Windows Hello) or hardware security keys (YubiKey) for fast, phishing-resistant MFA.","empty":"No passkeys added yet.","addTitle":"Add Passkey","nameLabel":"Passkey Name","namePlaceholder":"e.g. my_passkey","promptNotice":"After clicking continue, your browser will prompt you to complete biometric or security key registration.","continue":"Continue","renameTitle":"Rename Passkey","deleteTitle":"Delete Passkey","deleteConfirm":"Are you sure you want to delete passkey \\"{{name}}\\"? You will no longer be able to use it for multi-factor authentication.","defaultName":"Passkey","notSupported":"Your browser or current environment does not support passkeys (WebAuthn).","regFailed":"Passkey registration failed","count":"{{count}} configured","none":"Not configured","created":"Created","lastUsed":"Last used","neverUsed":"Never used","updated":"Passkey updated successfully","registered":"Passkey registered successfully","deleted":"Passkey deleted","appNameLabel":"Domain","appNameHelper":"The passkey will be bound to this application domain","formatTip":"Passkey name tip: 1-30 characters, letters, numbers, hyphens and underscores only","verifyPrompt":"Authenticate using your device's biometric sensor (Face ID, Touch ID, Windows Hello) or security key.","changePwNotice":"You will be prompted to verify via biometric authentication or your security key when submitting.","submitNotice":"You will be prompted to verify via biometric authentication or your security key when submitting.","testBtn":"Test Passkey","testThis":"Test this passkey","testSuccess":"Passkey verified successfully: {{name}}","testCancelled":"Passkey verification cancelled","testFailed":"Passkey verification failed"},"mfaNone":"None","resetUserPassword":"Reset Password","resetPasswordTitle":"Reset User Password","resetPasswordWarning":"After reset, all existing login sessions for this user will be invalidated immediately. The user must sign in with the new password.","resetPasswordSuccess":"Password reset successfully. The user's active sessions have been terminated.","confirmReset":"Confirm Reset","mfa":{"title":"Multi-Factor Authentication (MFA)","enabled":"Configured","notConfigured":"Not Configured","desc":"Passkeys and Authenticator Apps (TOTP) both serve as MFA authentication methods. Configuring at least one factor protects your account and enables passwordless login and self-service password recovery.","passwordlessTitle":"Passwordless Login","passwordlessDesc":"When enabled, logging in will skip the password requirement and verify your identity directly via Passkey or TOTP.","unconfiguredHint":"No MFA method configured yet. Set up either a Passkey or Authenticator App below to safeguard your account and unlock passwordless login and account recovery.","password":"Password","passkey":"Passkey","totp":"TOTP"},"recoveryKey":{"title":"Emergency Recovery Key","desc":"An emergency recovery key allows you to regain access or reset your password if you lose all registered Passkeys or Authenticator Apps. To ensure zero-knowledge security, the recovery key is stored as a one-way cryptographic hash and cannot be viewed in plaintext. It must be rotated to change, and is automatically rotated immediately upon each use.","configured":"Configured","notConfigured":"Not Configured","oneWayHash":"One-Way Cryptographic Hash","oneWayTooltip":"Protected with SHA-256 one-way cryptographic hash. The server never stores plaintext and cannot decrypt it.","rotateBtn":"Rotate Recovery Key","yourKey":"Your 30-Digit Recovery Key","newKeyGenerated":"New 30-Digit Recovery Key Generated (Save Now)","securityTip":"Keep this recovery key secret. Anyone with access to this key can regain access to your account.","oneWayWarning":"Notice: This recovery key is displayed ONLY ONCE. The server stores only an irreversible hash, so please copy and store it securely offline.","rotateConfirm":"Rotating your recovery key will immediately invalidate the current recovery key. Rotation permits authentication only via existing Passkey or original Recovery Key. Continue?","confirmRotateBtn":"Proceed to Rotate","rotateVerifyTitle":"Verify Identity to Rotate Key","rotateSuccess":"Recovery key rotated successfully! Please store your new key in a safe place immediately.","autoRotatedTitle":"Recovery Key Automatically Rotated","autoRotatedNotice":"Under the single-use rule, your previous recovery key has been invalidated and a fresh 30-digit key generated. Please copy and store it safely offline!","savedAndClose":"I Have Saved It Safely","savedAndContinue":"I Have Saved It & Continue to Dashboard","descOneWay":"Emergency Recovery Key is the sole credential to regain account access and decrypt E2EE data if all Passkeys or authenticators are lost. To maintain zero-knowledge security, it is stored as a one-way irreversible hash, cannot be viewed in plaintext on the web, and can only be rotated using the current Recovery Key or existing Passkey. The key rotates automatically upon use.","keyRequired":"Recovery key is required","verifyBtn":"Recovery Key","verifyPromptLabel":"Original Recovery Key","rotateSuccessE2eeWarning":"Recovery Key successfully rotated! Note: Because E2EE was previously locked and could not be decrypted, old E2EE data still requires the previous recovery key to unlock, or you can click \\"Reset Keypair\\" below."},"verifyIdentity":"Security Verification","verifyIdentityDesc":"To protect your account security, please verify your identity before proceeding.","usePasskeyInstead":"Authenticate with Passkey","changePasswordWithPasskey":"Verify with Passkey & Update","enableRegistration":"Enable user registration","timezone":"Timezone","timezoneAuto":"Auto (Browser Default)","totpCode":"Authenticator Code"}`),intro:{item1Title:`Obex Protective DNS`,item1Desc:`Cloudflare Workers & D1 based DNS resolver, optimized for speed and security.`,item2Title:`DoH Privacy`,item2Desc:`Prevent hijacking and enhance privacy via DNS over HTTPS (RFC 8484) encryption.`,item3Title:`Global Edge Network`,item3Desc:`Extreme speed and ultra-low latency powered by Cloudflare's edge network.`,item4Title:`Fine-grained Filtering`,item4Desc:`Full support for whitelist/blacklist, AdGuard rules, and custom DNS redirects.`,item5Title:`Real-time Analytics`,item5Desc:`Gain insights into network health with detailed logs and comprehensive dashboards.`,item6Title:`Modern UI & Dark Mode`,item6Desc:`High-density dashboard designed for power users, built with Blueprint UI.`,item7Title:`Cloud Native Architecture`,item7Desc:`Serverless architecture ensures zero maintenance and infinite scalability.`,item8Title:`Multi-Profile Management`,item8Desc:`Manage independent configurations for different devices or environments.`,item9Title:`Multiple Access Points`,item9Desc:`Configure unique sub-URLs for different devices to monitor logs and statistics independently.`},deploymentGuide:{title:`DNS Worker Setup & Deployment Guide`,subtitle:`Your instance is not fully configured yet. Please complete the setup below to start using DNS Worker.`,dbStatus:`D1 Database Binding (env.DB)`,jwtStatus:`JWT Secret Key (env.JWT_SECRET)`,configured:`Configured`,missing:`Missing / Not Configured`,refresh:`Refresh & Verify`,methodCli:`CLI Deployment`,methodDash:`Cloudflare Dashboard Deployment`,copy:`Copy`,copied:`Copied!`,stepDbTitle:`1. Bind D1 Database`,stepDbDesc:`Create a D1 database and bind it to your worker. In your terminal, run:`,stepDbCommand1:`wrangler d1 create dns_worker_db`,stepDbDesc2:`Copy the output D1 database details and paste them into your wrangler.toml under [[d1_databases]]:`,stepDbCommand2:`[[d1_databases]]
|
|
3
|
+
binding = "DB"
|
|
4
|
+
database_name = "dns_worker_db"
|
|
5
|
+
database_id = "<YOUR_D1_DATABASE_ID>"
|
|
6
|
+
migrations_dir = "migrations"`,stepDbDashDesc:`1. Go to Cloudflare Dashboard -> Workers & Pages -> D1. Click "Create Database" and name it 'dns_worker_db'.
|
|
7
|
+
2. Go to your Worker -> Settings -> Variables -> D1 Database Bindings -> Add Binding.
|
|
8
|
+
3. Set Binding name to 'DB', and select 'dns_worker_db' as D1 database. Click Save.
|
|
9
|
+
4. Go to your D1 database -> Console, copy and execute the SQL tables schema script from the migrations folder to set up database structure.`,stepJwtTitle:`2. Set JWT Secret Key`,stepJwtDesc:`DNS Worker requires a JWT secret for signing session tokens and API security. Configure it as a Cloudflare Secret:`,stepJwtCommand1:`wrangler secret put JWT_SECRET`,stepJwtDashDesc:`1. Go to your Worker -> Settings -> Variables -> Environment Variables -> Add Variable.
|
|
10
|
+
2. Set Name to 'JWT_SECRET', choose type 'Secret', and enter a secure random string (e.g. 64 hex characters) as Value.
|
|
11
|
+
3. Save and Deploy.`,stepJwtDesc2:`Cloudflare Dashboard`,stepJwtCommand2:`https://dash.cloudflare.com/`,stepDeployTitle:`3. Deploy & Apply Migrations`,stepDeployDesc:`Run the deploy command to compile frontend, apply D1 database schema migrations automatically, and publish configurations live:`,stepDeployCommand:`npm run deploy`,stepDeployDashDesc:`If you completed all configurations via the Cloudflare Dashboard, go to your Worker's Deployments tab, click 'Deploy' or 'Re-deploy' to publish the latest configurations.`,jwtGeneratorTitle:`Secure JWT_SECRET Generator`,jwtGeneratorDesc:`Use this cryptographically secure key generator to generate a random 64-character alphanumeric secret for your env.JWT_SECRET.`,regenerate:`Regenerate`,serverfullTitle:`DNS Worker Serverfull Setup & Deployment Guide`,serverfullSubtitle:`Your instance is running in Serverfull (Standalone) mode. Follow the steps below to initialize database and configure environment parameters.`,serverfullDbStatus:`SQLite Database (Persistent Data Dir)`,serverfullDbConfigured:`Ready & Initialized`,serverfullDbMissing:`Database File Missing`,serverfullDbMissingHint:`The database is normally initialized upon installation. If it is missing, please reinstall or restart the service to trigger auto-initialization.`,serverfullJwtStatus:`JWT Secret (.env Config)`,serverfullJwtConfigured:`Configured`,serverfullJwtMissing:`Not Configured`,serverfullJwtMissingHint:`JWT_SECRET can be modified in .env (or /etc/dns-worker/.env). Paste the generated secret below and restart the service.`,serverfullStepInstallTitle:`1. Check or Reinstall (Initialize Database)`,serverfullStepInstallDesc:`If the SQLite database is missing, reinstall globally to automatically create the data directory and initialize schema:`,serverfullStepInstallCmd:`sudo npm install -g dns-worker@latest`,serverfullStepEnvTitle:`2. Configure JWT_SECRET in .env`,serverfullStepEnvDesc:`Edit /etc/dns-worker/.env and insert the JWT_SECRET generated below:`,serverfullStepEnvCmd:`sudo nano /etc/dns-worker/.env
|
|
12
|
+
# Set JWT_SECRET=<generated_secret_below>`,serverfullStepServiceTitle:`3. Restart & Verify Service`,serverfullStepServiceDesc:`Restart the system daemon after modifying configuration and check service status:`,serverfullStepServiceCmd:`sudo dns-worker service restart
|
|
13
|
+
sudo dns-worker status`},meta:{defaultDesc:`Secure, fast, and customizable DNS resolution for all your devices. Based on Cloudflare Workers. Open-source and privacy-focused.`,setupDesc:`Configure your devices to use DNS Worker.`}},Be={"zh-CN":()=>T(()=>import(`./zh-CN-B0nRgJTD.js`),[]).then(e=>e.default),"zh-SG":()=>T(()=>import(`./zh-CN-B0nRgJTD.js`),[]).then(e=>e.default),"zh-TW":()=>T(()=>import(`./zh-TW-lc_SSoVI.js`),[]).then(e=>e.default),"zh-HK":()=>T(()=>import(`./zh-TW-lc_SSoVI.js`),[]).then(e=>e.default),"ja-JP":()=>T(()=>import(`./ja-JP-C5jk6SuH.js`),[]).then(e=>e.default),"ko-KR":()=>T(()=>import(`./ko-KR-z38NtMFa.js`),[]).then(e=>e.default),"fr-FR":()=>T(()=>import(`./fr-FR-BRcdpT35.js`),[]).then(e=>e.default),"de-DE":()=>T(()=>import(`./de-DE-kDnumQOF.js`),[]).then(e=>e.default),"es-ES":()=>T(()=>import(`./es-ES-CqKS7vHl.js`),[]).then(e=>e.default),"ru-RU":()=>T(()=>import(`./ru-RU-Bicx52lz.js`),[]).then(e=>e.default),"vi-VN":()=>T(()=>import(`./vi-VN-CvS_mTAu.js`),[]).then(e=>e.default)};async function Ve(e){if(E.hasResourceBundle(e,`translation`))return!0;let t=Be[e];if(!t)return!1;try{let n=await t();return E.addResourceBundle(e,`translation`,n,!0,!0),!0}catch(t){return console.warn(`[i18n] Failed to load locale "${e}":`,t),!1}}E.use(ve).use(ie).init({resources:{"en-US":{translation:ze},"en-GB":{translation:ze},"en-SG":{translation:ze}},fallbackLng:`en-US`,interpolation:{escapeValue:!1}}),Ve(E.language);var He=E,P=e(he(),1),F=e(ge(),1);const Ue=()=>(0,F.jsx)(`a`,{href:`https://github.com/Obein/DNS-Worker`,target:`_blank`,rel:`noopener noreferrer`,className:`github-corner fixed bottom-0 right-0 z-1000 hidden md:block transition-all duration-300 hover:scale-125 origin-bottom-right opacity-40 hover:opacity-100`,"aria-label":`View source on GitHub`,children:(0,F.jsxs)(`svg`,{width:`60`,height:`60`,viewBox:`0 0 250 250`,style:{fill:`#151513`,color:`#fff`,transform:`rotate(90deg)`},"aria-hidden":`true`,children:[(0,F.jsx)(`path`,{d:`M0,0 L115,115 L115,115 L142,142 L250,250 L250,0 Z`}),(0,F.jsxs)(`g`,{transform:`rotate(-135 180 80)`,children:[(0,F.jsx)(`path`,{d:`M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2`,fill:`currentColor`,style:{transformOrigin:`130px 106px`},className:`octo-arm`}),(0,F.jsx)(`path`,{d:`M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.3 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.9 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z`,fill:`currentColor`,className:`octo-body`})]})]})});function I(e){let t=()=>e().catch(e=>{if(e.message?.includes(`Failed to fetch dynamically imported module`)||e.name===`TypeError`||/dynamically imported module/i.test(e.message||``)){let e=sessionStorage.getItem(`chunk_reload`),t=Date.now();(!e||t-parseInt(e,10)>1e4)&&(sessionStorage.setItem(`chunk_reload`,t.toString()),window.location.reload())}throw e}),n=(0,P.lazy)(t);return n.preload=t,n}function We(){let[e,t]=(0,P.useState)(window.innerWidth<768);return(0,P.useEffect)(()=>{let e=()=>t(window.innerWidth<768);return window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[]),e}const Ge=()=>{let e=u(),{t}=h();return(0,F.jsx)(`div`,{className:`h-full flex items-center justify-center`,children:(0,F.jsx)(Ne,{icon:`search`,title:t(`common.notFound`),description:t(`common.notFoundDesc`),action:(0,F.jsx)(D,{intent:k.PRIMARY,onClick:()=>e(`/`),children:t(`common.backToHome`)})})})};var Ke=I(()=>T(()=>import(`./SetupView-DE9MM-32.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8])).then(e=>({default:e.SetupView}))),qe=I(()=>T(()=>import(`./FilteringView-Bs3__NpV.js`),__vite__mapDeps([9,1,2,3,4,5,6,7])).then(e=>({default:e.FilteringView}))),Je=I(()=>T(()=>import(`./RulesView-B2kL1QhI.js`),__vite__mapDeps([10,1,2,3,4,5,6,7])).then(e=>({default:e.RulesView}))),Ye=I(()=>T(()=>import(`./SettingsView-DTGI6eSd.js`),__vite__mapDeps([11,1,2,3,4,5,6,7])).then(e=>({default:e.SettingsView}))),Xe=I(()=>T(()=>import(`./AnalyticsView-BywwDzah.js`),__vite__mapDeps([12,1,2,3,4,5,6,7,13])).then(e=>({default:e.AnalyticsView}))),Ze=I(()=>T(()=>import(`./LogsView-B-U9oaxB.js`),__vite__mapDeps([14,1,2,3,4,5,6,7,8,15,13])).then(e=>({default:e.LogsView})));const Qe=()=>{Ke.preload(),Ze.preload()},$e=()=>{Ye.preload(),Je.preload(),qe.preload()},et=({selectedProfile:e,prefilledRule:t,setPrefilledRule:r,handleQuickAction:i,toasterRef:a,currentUser:o,onSavingChange:s})=>{let{profileId:c}=d(),l=c||e?.id||``;return(0,F.jsx)(P.Suspense,{fallback:(0,F.jsx)(`div`,{className:`p-20 flex justify-center`,children:(0,F.jsx)(O,{size:40})}),children:(0,F.jsxs)(n,{children:[(0,F.jsx)(w,{path:`setup`,element:(0,F.jsx)(Ke,{profileId:l,profileKey:e?.profile_key||l,profileName:e?.name,toasterRef:a})}),(0,F.jsx)(w,{path:`filter`,element:(0,F.jsx)(qe,{profileId:l,toasterRef:a})}),(0,F.jsx)(w,{path:`rules`,element:(0,F.jsx)(Je,{profileId:l,prefill:t,onPrefillUsed:()=>r(null),toasterRef:a})}),(0,F.jsx)(w,{path:`settings`,element:(0,F.jsx)(Ye,{profileId:l,toasterRef:a,currentUser:o,onSavingChange:s})}),(0,F.jsx)(w,{path:`stats`,element:(0,F.jsx)(Xe,{profileId:l})}),(0,F.jsx)(w,{path:`logs`,element:(0,F.jsx)(Ze,{profileId:l,onQuickAction:i,toasterRef:a})}),(0,F.jsx)(w,{path:`*`,element:(0,F.jsx)(Ge,{})})]})})};var L=class extends Error{status;bodyText;constructor(e,t){super(t),this.status=e,this.bodyText=t,this.name=`ApiError`}};async function tt(){let e=await fetch(`/api/auth/config`);if(!e.ok)throw new L(e.status,await e.text());return e.json()}async function nt(e){let t=await fetch(`/api/auth/check-username?username=${encodeURIComponent(e)}`);if(!t.ok)throw new L(t.status,await t.text());return(await t.json()).exists}async function rt(e){let t=await fetch(`/api/auth/prelogin`,{method:`POST`,headers:{"Content-Type":`application/json`},credentials:`same-origin`,body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function it(e){let t=await fetch(`/api/auth/login`,{method:`POST`,headers:{"Content-Type":`application/json`},credentials:`same-origin`,body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function at(e){let t=await fetch(`/api/auth/signup`,{method:`POST`,headers:{"Content-Type":`application/json`},credentials:`same-origin`,body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function ot(){let e=await fetch(`/api/auth/logout`,{method:`POST`,credentials:`same-origin`});if(!e.ok)throw new L(e.status,await e.text())}var R=null;async function z(){return R||(R=(async()=>{try{let e=await fetch(`/api/auth/refresh`,{method:`POST`,credentials:`same-origin`});if(!e.ok)throw new L(e.status,await e.text());return await e.json()}finally{R=null}})(),R)}async function st(){let e=await fetch(`/api/auth/unlock-session`,{method:`GET`});if(!e.ok)throw new L(e.status,await e.text());return e.json()}async function ct(e,t){let n=await fetch(`/api/auth/unlock-session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pinHash:e,nonce:t})});if(!n.ok)throw new L(n.status,await n.text())}async function lt(){let e=await fetch(`/api/auth/lock-session`,{method:`POST`});if(!e.ok)throw new L(e.status,await e.text())}function ut(){return typeof window<`u`&&window.PublicKeyCredential!==void 0&&typeof window.PublicKeyCredential==`function`}function B(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`);for(;t.length%4!=0;)t+=`=`;let n=atob(t),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r}function V(e){let t=e instanceof Uint8Array?e:new Uint8Array(e),n=``;for(let e=0;e<t.length;e++)n+=String.fromCharCode(t[e]);return btoa(n).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}async function dt(e){if(!ut())throw Error(`Passkeys are not supported by this browser.`);let t={...e,challenge:B(e.challenge),user:{...e.user,id:B(e.user.id)},excludeCredentials:(e.excludeCredentials||[]).map(e=>({...e,id:B(e.id)})),extensions:{...e.extensions,prf:{}}},n=await navigator.credentials.create({publicKey:t});if(!n)throw Error(`Credential creation was cancelled or failed.`);let r=n.response,i=r.getTransports?r.getTransports():[],a=n.getClientExtensionResults?n.getClientExtensionResults():void 0;return{id:n.id,rawId:V(n.rawId),response:{clientDataJSON:V(r.clientDataJSON),attestationObject:V(r.attestationObject),transports:i},clientExtensionResults:a}}async function ft(e){if(!ut())throw Error(`Passkeys are not supported by this browser.`);let t={...e,challenge:B(e.challenge),allowCredentials:(e.allowCredentials||[]).map(e=>({...e,id:B(e.id)})),extensions:e.extensions},n=await navigator.credentials.get({publicKey:t});if(!n)throw Error(`Passkey authentication was cancelled.`);let r=n.response,i=n.getClientExtensionResults?n.getClientExtensionResults():void 0;return{id:n.id,rawId:V(n.rawId),response:{clientDataJSON:V(r.clientDataJSON),authenticatorData:V(r.authenticatorData),signature:V(r.signature),userHandle:r.userHandle?V(r.userHandle):null},clientExtensionResults:i}}async function pt(){let e=await fetch(`/api/account/me`);if(!e.ok)throw new L(e.status,await e.text());return e.json()}async function mt(e){let t=await fetch(`/api/account/me`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(await t.text());return t.json()}async function ht(){let e=await fetch(`/api/account/me`,{method:`DELETE`});if(!e.ok)throw Error(await e.text())}async function gt(e){let t=await fetch(`/api/account/password`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(await t.text())}async function _t(){let e=await fetch(`/api/account/sessions`);if(!e.ok)throw Error(`Failed to fetch sessions`);return e.json()}async function vt(e){let t=await fetch(`/api/account/sessions/${e}`,{method:`DELETE`});if(!t.ok)throw Error(await t.text());return t.json()}async function yt(){let e=await fetch(`/api/account/sessions/others`,{method:`DELETE`});if(!e.ok)throw Error(await e.text());return e.json()}async function bt(e){let t=await fetch(`/api/account/activity?${e}`);if(!t.ok)throw Error(`Failed to fetch activity log`);return t.json()}async function xt(){let e=await fetch(`/api/account/db-size`);if(!e.ok)throw Error(await e.text());return e.json()}async function St(){let e=await fetch(`/api/account/logs`,{method:`DELETE`});if(!e.ok)throw Error(await e.text())}async function Ct(){let e=await fetch(`/api/account/totp/setup`);if(!e.ok)throw Error(await e.text());return e.json()}async function wt(e){let t=await fetch(`/api/account/totp/confirm`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(await t.text());return t.json()}async function Tt(e){let t=await fetch(`/api/account/totp`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({password:e})});if(!t.ok)throw Error(await t.text())}async function Et(e){let t=await fetch(`/api/account/totp/settings`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({skip_password:e})});if(!t.ok)throw Error(await t.text())}const Dt=Et;async function Ot(e,t){let n=await fetch(`/api/account/migrate-password`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({clientHash:e,oldPassword:t})});if(!n.ok)throw Error(await n.text())}async function kt(e,t){let n=await fetch(`/api/account/pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pinHash:e,...t})});if(!n.ok)throw new L(n.status,await n.text())}async function At(e){let t=await fetch(`/api/account/pin`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text())}async function jt(){let e=await fetch(`/api/account/passkeys`);if(!e.ok)throw new L(e.status,await e.text());return e.json()}async function Mt(){let e=await fetch(`/api/account/passkeys/register/options`,{method:`POST`});if(!e.ok)throw new L(e.status,await e.text());return e.json()}async function Nt(e){let t=await fetch(`/api/account/passkeys/register/verify`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function Pt(e,t){let n=await fetch(`/api/account/passkeys/${e}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:t})});if(!n.ok)throw new L(n.status,await n.text())}async function Ft(e){let t=await fetch(`/api/account/passkeys/${e}`,{method:`DELETE`});if(!t.ok)throw new L(t.status,await t.text())}async function It(e){let t=await ft(await Rt(e)),n=await fetch(`/api/account/passkeys/test`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({credential:t})});if(!n.ok)throw new L(n.status,await n.text());return n.json()}async function Lt(e){let t=e.trim().replace(/\s/g,``),n=crypto.randomUUID(),r=new TextEncoder().encode(t+n),i=await crypto.subtle.digest(`SHA-256`,r),a=Array.from(new Uint8Array(i)).map(e=>e.toString(16).padStart(2,`0`)).join(``),o=await fetch(`/api/account/totp/test`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({totpTokenHash:a,salt:n})});if(!o.ok)throw new L(o.status,await o.text());return o.json()}async function Rt(e){let t=await fetch(`/api/account/passkeys/auth-options`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e?{passkeyId:e}:{})});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function zt(e){let t=await fetch(`/api/account/recovery-keys/rotate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw new L(t.status,await t.text());return t.json()}async function H(e,t){let n=await fetch(e,t);return n.status===404&&(window.location.href=`/404`),n}async function Bt(){let e=await H(`/api/profiles`);if(!e.ok)throw Error(`Failed to fetch profiles`);return e.json()}async function Vt(e){let t=await H(`/api/profiles/${e}`);if(!t.ok)throw Error(`Failed to fetch profile details`);return t.json()}async function Ht(e){let t=await H(`/api/profiles`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:e})});if(!t.ok)throw Error(await t.text());return t.json()}async function Ut(e){let t=await H(`/api/profiles/${e}`,{method:`DELETE`});if(!t.ok)throw Error(await t.text())}async function Wt(e,t){let n=await H(`/api/profiles/${e}/settings`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(await n.text())}async function Gt(e){let t=await H(`/api/profiles/${e}/lists`);if(!t.ok)throw Error(`Failed to fetch lists`);return t.json()}async function Kt(e,t,n){let r=await H(`/api/profiles/${e}/lists/${t}/check?domain=${encodeURIComponent(n)}`);if(!r.ok)throw Error(await r.text());return r.json()}async function qt(e,t,n){let r=await H(`/api/profiles/${e}/lists/${t}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({enabled:n})});if(!r.ok)throw Error(await r.text())}async function Jt(e,t){let n=await H(`/api/profiles/${e}/lists`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({url:t})});if(!n.ok)throw Error(await n.text())}async function Yt(e,t){let n=await H(`/api/profiles/${e}/lists`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:t})});if(!n.ok)throw Error(await n.text())}async function Xt(e){let t=await H(`/api/profiles/${e}/lists/sync`,{method:`POST`});if(!t.ok)throw Error(await t.text())}async function Zt(e){let t=await H(`/api/profiles/${e}/rules`);if(!t.ok)throw Error(`Failed to fetch rules`);return t.json()}async function Qt(e,t){let n=await H(`/api/profiles/${e}/rules`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(await n.text())}async function $t(e,t){let n=await H(`/api/profiles/${e}/rules`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(await n.text());return n.json()}async function en(e,t){let n=await H(`/api/profiles/${e}/lists`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(await n.text());return n.json()}async function tn(e,t){let n=await H(`/api/profiles/${e}/rules`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(await n.text())}async function nn(e,t){let n=await H(`/api/profiles/${e}/rules`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:t})});if(!n.ok)throw Error(await n.text())}async function rn(e){let t=await H(`/api/profiles/${e}/access_points`);if(!t.ok)throw Error(`Failed to fetch access points`);return t.json()}async function an(e,t){let n=await H(`/api/profiles/${e}/access_points`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:t})});if(!n.ok)throw Error(await n.text());return n.json()}async function on(e,t){let n=await H(`/api/profiles/${e}/access_points/${t}`,{method:`DELETE`});if(!n.ok)throw Error(await n.text())}async function sn(e,t,n){let r=await H(`/api/profiles/${e}/access_points/${t}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:n})});if(!r.ok)throw Error(await r.text())}async function cn(e,t){let n=await H(`/api/profiles/${e}/access_points/${t}/rotate_token`,{method:`POST`});if(!n.ok)throw Error(await n.text());return n.json()}async function ln(e,t,n){let r=await H(`/api/profiles/${e}/logs?${t.startsWith(`?`)?t.slice(1):t}`,{signal:n?.signal});if(!r.ok)throw Error(`Failed to fetch logs`);return r.json()}async function un(e,t){let n=await H(`/api/profiles/${e}/logs/export?${t.startsWith(`?`)?t.slice(1):t}`);if(!n.ok)throw Error(`Failed to export logs`);return n.json()}async function dn(e,t,n,r){let i=await H(`/api/profiles/${e}/logs/${t}${n===void 0?``:`?timestamp=${n}`}`,{signal:r?.signal});if(!i.ok)throw Error(`Failed to fetch log details`);return i.json()}async function fn(e,t,n,r){let i=n.startsWith(`?`)?n.slice(1):n,a=await H(`/api/profiles/${e}/analytics${t?`/${t}`:``}?${i}`,{signal:r?.signal});if(!a.ok)throw Error(`Failed to fetch analytics${t?` for ${t}`:``}`);return a.json()}async function pn(e,t){let n=await H(`/api/profiles/${e}/test`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok)throw Error(`Resolution test failed`);return n.json()}async function mn(e,t){let n=await H(`/api/profiles/${e}`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({name:t})});if(!n.ok)throw Error(await n.text())}async function hn(){let e=await fetch(`/api/clientinfo`);if(!e.ok)throw Error(`Failed to fetch client info`);return e.json()}async function gn(){try{let e=await fetch(`/cdn-cgi/trace`);if(!e.ok)return null;let t=await e.text(),n=t.split(`
|
|
14
|
+
`),r={};for(let e of n){let t=e.indexOf(`=`);if(t!==-1){let n=e.slice(0,t).trim();r[n]=e.slice(t+1).trim()}}return{colo:r.colo||`UNKNOWN`,raw:t}}catch(e){return console.warn(`Failed to fetch /cdn-cgi/trace:`,e),null}}async function _n(){let e=await fetch(`/world-110m.json`);if(!e.ok)throw Error(`Failed to load map topology`);return e.json()}async function vn(e,t,n){let r=await fetch(`https://${e}/dns-query?name=${encodeURIComponent(t)}&type=${n}`,{headers:{Accept:`application/dns-json`}});if(!r.ok)throw Error(`DoH query failed: ${r.statusText}`);return r.json()}async function yn(){let e=await fetch(`/api/presets/upstreams`);if(!e.ok)throw Error(`Failed to fetch preset upstreams`);return e.json()}async function bn(){let e=await fetch(`/api/presets/filters`);if(!e.ok)throw Error(`Failed to fetch preset filters`);return e.json()}async function xn(){let e=await fetch(`/api/presets/ech-fronting-domains`);if(!e.ok)throw Error(`Failed to fetch preset ECH fronting domains`);return e.json()}async function Sn(e){let t=e.trim().toLowerCase();if(!t)return{ipv4:[],ipv6:[]};if(/^(\d{1,3}\.){3}\d{1,3}$/.test(t))return{ipv4:[t],ipv6:[]};if(/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(t))return{ipv4:[],ipv6:[t]};let n=async e=>{for(let n of[`cloudflare-dns.com`,`1.1.1.1`])try{let r=await vn(n,t,e);if(Array.isArray(r?.Answer)&&r.Answer.length>0){let t=e===`A`?1:28,n=r.Answer.filter(e=>e.type===t&&typeof e.data==`string`).map(e=>e.data);if(n.length>0)return n}}catch{}return[]};try{let[e,t]=await Promise.all([n(`A`),n(`AAAA`)]);if(e.length>0||t.length>0)return{ipv4:e,ipv6:t}}catch{}try{let e=await fetch(`/api/resolve?name=${encodeURIComponent(t)}`);if(e.ok){let t=await e.json();return{ipv4:Array.isArray(t.ipv4)?t.ipv4:[],ipv6:Array.isArray(t.ipv6)?t.ipv6:[]}}}catch{}return{ipv4:[],ipv6:[]}}function Cn(e){if(!e)return!0;let t=e.trim().toLowerCase();return t?t===`dns.example.com`||t===`example.com`||t.endsWith(`.example.com`)||t.endsWith(`.example`)||t.endsWith(`.example.net`)||t.endsWith(`.example.org`)||t.endsWith(`.invalid`)||t.endsWith(`.test`):!0}async function wn(e){let t=e.trim().toLowerCase();if(Cn(t)){try{let e=await fetch(`/api/geoip`);if(e.ok){let t=await e.json();if(t.success&&t.country&&t.country!==`Private Network`){let e=[];t.city&&t.city!==`Local`&&e.push(t.city),t.region&&t.region!==t.city&&e.push(t.region),t.country&&e.push(t.country);let n=t.flag?.emoji?` ${t.flag.emoji}`:``,r=e.join(`, `);if(r)return`${r}${n}`}}}catch{}return``}if(!t||t===`localhost`||t===`127.0.0.1`||t===`::1`)return`Localhost / 127.0.0.1 🏠`;if(t.startsWith(`192.168.`)||t.startsWith(`10.`)||/^172\.(1[6-9]|2\d|3[01])\./.test(t))return`Private Network (LAN) 🏠`;let n=t;if(!(/^(\d{1,3}\.){3}\d{1,3}$/.test(t)||/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(t))){let e=await Sn(t);if(e.ipv4.length>0)n=e.ipv4[0];else if(e.ipv6.length>0)n=e.ipv6[0];else{try{let e=await fetch(`/api/geoip`);if(e.ok){let t=await e.json();if(t.success&&t.country&&t.country!==`Private Network`){let e=[];t.city&&t.city!==`Local`&&e.push(t.city),t.region&&t.region!==t.city&&e.push(t.region),t.country&&e.push(t.country);let n=t.flag?.emoji?` ${t.flag.emoji}`:``,r=e.join(`, `);if(r)return`${r}${n}`}}}catch{}return``}}try{let e=await fetch(`https://ipwho.is/${encodeURIComponent(n)}`,{signal:AbortSignal.timeout(3500)});if(e.ok){let t=await e.json();if(t.success){let e=[];t.city&&t.city!==`Local`&&e.push(t.city),t.region&&t.region!==t.city&&e.push(t.region),t.country&&e.push(t.country);let n=t.flag?.emoji?` ${t.flag.emoji}`:``,r=e.join(`, `);if(r)return`${r}${n}`}}}catch{}try{let e=await fetch(`/api/geoip?ip=${encodeURIComponent(n)}`);if(e.ok){let t=await e.json();if(t.success){let e=[];t.city&&t.city!==`Local`&&e.push(t.city),t.region&&t.region!==t.city&&e.push(t.region),t.country&&e.push(t.country);let n=t.flag?.emoji?` ${t.flag.emoji}`:``,r=e.join(`, `);if(r)return`${r}${n}`}}}catch{}return``}async function Tn(){let e=await fetch(`/api/admin/users`);if(!e.ok)throw Error(`Failed to fetch users`);return e.json()}async function En(e){let t=await fetch(`/api/admin/users`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(await t.text())}async function Dn(e){let t=await fetch(`/api/admin/users/${e}`,{method:`DELETE`});if(!t.ok)throw Error(await t.text())}async function On(){let e=await fetch(`/api/admin/settings`);if(!e.ok)throw Error(`Failed to fetch system settings`);return e.json()}async function kn(e){let t=await fetch(`/api/admin/settings`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(await t.text())}var An=class{unlockedKeys=new Map;isKeyCompatibleWithServer(e,t){if(!t)return!0;try{let n=typeof t==`string`?JSON.parse(t):t,r=n.alg===`P256-MLKEM768`||!!n.pqc_pk,i=`seedBase64`in e||e.alg===`P256-MLKEM768`;return!(r&&!i)}catch{return!0}}validateCachedKeyAgainstStatus(e){if(!e.publicKey)return;let t=this.getPrivateKey(`account`);t&&!this.isKeyCompatibleWithServer(t,e.publicKey)&&(console.warn(`[E2EE KeyStore] Cached private key does not match server algorithm. Purging stale key.`),this.clearStorage())}isUnlocked(){return this.getPrivateKey(`account`)!==null}isProfileUnlocked(e){return this.getPrivateKey(e)!==null}purgeProfileStorageKeys(){let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(`obex_e2ee_sk_`)&&n!==`obex_e2ee_sk_account`&&e.push(n)}e.forEach(e=>sessionStorage.removeItem(e));let t=[];for(let e=0;e<localStorage.length;e++){let n=localStorage.key(e);n?.startsWith(`obex_e2ee_sk_`)&&n!==`obex_e2ee_sk_account`&&t.push(n)}t.forEach(e=>localStorage.removeItem(e))}getPrivateKey(e){if(this.unlockedKeys.has(`account`))return this.unlockedKeys.get(`account`);if(e&&this.unlockedKeys.has(e))return this.unlockedKeys.get(e);let t=sessionStorage.getItem(`obex_e2ee_sk_account`);if(t)try{let n=JSON.parse(t);return this.unlockedKeys.set(`account`,n),e&&this.unlockedKeys.set(e,n),n}catch{sessionStorage.removeItem(`obex_e2ee_sk_account`)}let n=localStorage.getItem(`obex_e2ee_sk_account`);if(n)try{let t=JSON.parse(n);return this.unlockedKeys.set(`account`,t),e&&this.unlockedKeys.set(e,t),t}catch{localStorage.removeItem(`obex_e2ee_sk_account`)}if(e){let t=sessionStorage.getItem(`obex_e2ee_sk_${e}`);if(t)try{let e=JSON.parse(t);return this.setPrivateKey(`account`,e),e}catch{sessionStorage.removeItem(`obex_e2ee_sk_${e}`)}let n=localStorage.getItem(`obex_e2ee_sk_${e}`);if(n)try{let e=JSON.parse(n);return this.setPrivateKey(`account`,e),e}catch{localStorage.removeItem(`obex_e2ee_sk_${e}`)}}return null}setPrivateKey(e,t){this.unlockedKeys.clear(),this.unlockedKeys.set(`account`,t),e&&e!==`account`&&this.unlockedKeys.set(e,t);try{this.purgeProfileStorageKeys();let e=JSON.stringify(t);sessionStorage.setItem(`obex_e2ee_sk_account`,e),localStorage.setItem(`obex_e2ee_sk_account`,e)}catch(e){console.warn(`[E2EE KeyStore] Could not persist key to storage:`,e)}}deleteProfileKey(e){this.unlockedKeys.delete(e),sessionStorage.removeItem(`obex_e2ee_sk_${e}`),localStorage.removeItem(`obex_e2ee_sk_${e}`)}clearStorage(){this.unlockedKeys.clear();let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(`obex_e2ee_sk_`)&&e.push(n)}e.forEach(e=>sessionStorage.removeItem(e));let t=[];for(let e=0;e<localStorage.length;e++){let n=localStorage.key(e);n?.startsWith(`obex_e2ee_sk_`)&&t.push(n)}t.forEach(e=>localStorage.removeItem(e))}};function U(e){let t=e instanceof Uint8Array?e:new Uint8Array(e),n=``;for(let e=0;e<t.length;e++)n+=String.fromCharCode(t[e]);return btoa(n)}function W(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}function jn(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`);for(;t.length%4!=0;)t+=`=`;return W(t)}async function Mn(e){let t=crypto.subtle,n=await t.importKey(`raw`,e,{name:`HKDF`},!1,[`deriveKey`]);return await t.deriveKey({name:`HKDF`,hash:`SHA-256`,salt:new Uint8Array,info:new TextEncoder().encode(`obex-pqc-dek-v2`)},n,{name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`])}async function Nn(e,t){let n=JSON.parse(t),r=W(n.iv),i=W(n.ciphertext),a=await crypto.subtle.decrypt({name:`AES-GCM`,iv:r},e,i);return JSON.parse(new TextDecoder().decode(a))}var Pn=class{dekCache=new Map;keyStore;constructor(e){this.keyStore=e}clearDekCache(){this.dekCache.clear()}async decryptLogEntry(e,t){let n=t.encrypt_version??(t.encrypted_payload?1:0);if(n===0||!t.encrypted_payload)return t;let r=this.keyStore.getPrivateKey(e);if(!r)return{...t,domain:t.domain||`[Encrypted]`,client_ip:t.client_ip||`[Encrypted]`};try{if(n===2){let e;if(t.kem_key_id&&(e=this.dekCache.get(t.kem_key_id),!e&&t.kem_ct&&`seedBase64`in r)){let n=W(r.seedBase64),i=W(t.kem_ct);e=await Mn(N.decapsulate(i,n)),this.dekCache.set(t.kem_key_id,e)}if(!e)return{...t,domain:t.domain||`[Encrypted]`,client_ip:t.client_ip||`[Encrypted]`};let n=await Nn(e,t.encrypted_payload),i=n.dest_country_code??t.dest_country_code,a=n.dest_country??t.dest_country,o=n.dest_isp??t.dest_isp,s=n.dest_geoip??t.dest_geoip;if((!i||!a)&&s)try{let e=JSON.parse(s);!i&&e.country_code&&(i=e.country_code.toUpperCase()),!a&&e.country&&(a=e.country),!o&&e.isp&&(o=e.isp)}catch{}return{...t,domain:n.domain||t.domain,client_ip:n.client_ip||t.client_ip,geo_country:n.geo_country??t.geo_country,answer:n.answer??t.answer,dest_geoip:s,dest_country_code:i,dest_country:a,dest_isp:o,ecs:n.ecs??t.ecs,upstream:n.upstream??t.upstream,reason:n.reason??t.reason,encrypt_version:0}}if(n===1){let e=JSON.parse(t.encrypted_payload),n=`kty`in r?r:`legacyKey`in r&&r.legacyKey?r.legacyKey:null;if(!n)return{...t,domain:`[Decryption Failed: Legacy Key Mismatch]`};let i=await crypto.subtle.importKey(`jwk`,n,{name:`ECDH`,namedCurve:`P-256`},!1,[`deriveKey`]),a=await crypto.subtle.importKey(`jwk`,e.ephem_pk,{name:`ECDH`,namedCurve:`P-256`},!1,[]),o=await crypto.subtle.deriveKey({name:`ECDH`,public:a},i,{name:`AES-GCM`,length:256},!1,[`decrypt`]),s=await crypto.subtle.decrypt({name:`AES-GCM`,iv:W(e.iv)},o,W(e.ciphertext)),c=JSON.parse(new TextDecoder().decode(s)),l=c.dest_country_code??t.dest_country_code,u=c.dest_country??t.dest_country,d=c.dest_isp??t.dest_isp,f=c.dest_geoip??t.dest_geoip;if((!l||!u)&&f)try{let e=JSON.parse(f);!l&&e.country_code&&(l=e.country_code.toUpperCase()),!u&&e.country&&(u=e.country),!d&&e.isp&&(d=e.isp)}catch{}return{...t,domain:c.domain||t.domain,client_ip:c.client_ip||t.client_ip,geo_country:c.geo_country??t.geo_country,answer:c.answer??t.answer,dest_geoip:f,dest_country_code:l,dest_country:u,dest_isp:d,ecs:c.ecs??t.ecs,upstream:c.upstream??t.upstream,reason:c.reason??t.reason,encrypt_version:0}}return t}catch(e){return console.warn(`[E2EE DecryptEngine] Failed to decrypt log #%s:`,String(t.id),e),{...t,domain:`[Decryption Failed]`}}}async decryptLogsBatch(e,t){if(!t||t.length===0||!t.some(e=>(e.encrypt_version??(e.encrypted_payload?1:0))>0))return t;let n=this.keyStore.getPrivateKey(e);if(n&&`seedBase64`in n){let e=W(n.seedBase64),r=new Map;for(let e of t)e.encrypt_version===2&&e.kem_key_id&&e.kem_ct&&!this.dekCache.has(e.kem_key_id)&&r.set(e.kem_key_id,e.kem_ct);for(let[t,n]of r.entries())try{let r=W(n),i=await Mn(N.decapsulate(r,e));this.dekCache.set(t,i)}catch(e){console.warn(`[E2EE DecryptEngine] Failed to decapsulate DEK for ${t}:`,e)}}return Promise.all(t.map(t=>this.decryptLogEntry(e,t)))}};async function G(e=`account`){let t=await Rt(),n=new Uint8Array(32),r=new TextEncoder().encode(`obex-dns-log-e2ee-salt:account`);n.set(r.subarray(0,32));let i;if(e&&e!==`account`){i=new Uint8Array(32);let t=new TextEncoder().encode(`obex-dns-log-e2ee-salt:${e}`);i.set(t.subarray(0,32))}let a={first:n};i&&(a.second=i);let o=jn(t.challenge),s=await navigator.credentials.get({publicKey:{challenge:o,timeout:6e4,userVerification:`preferred`,rpId:t.rpId||window.location.hostname,allowCredentials:(t.allowCredentials||[]).map(e=>({...e,id:jn(e.id)})),extensions:{prf:{eval:a}}}});if(!s)throw Error(`Passkey assertion was cancelled or failed`);let c=s.getClientExtensionResults(),l,u;if(c?.prf?.results?.first)l=new Uint8Array(c.prf.results.first),c?.prf?.results?.second&&(u=new Uint8Array(c.prf.results.second));else throw Error(`Passkey authenticator does not support WebAuthn PRF extension. Please unlock using your Recovery Key.`);let d=s.id,f=e=>(e||``).replace(/=/g,``).replace(/\+/g,`-`).replace(/\//g,`_`),p=s.rawId?V(s.rawId):``,m=f(s.id),h=f(p);if(t.allowCredentials&&t.allowCredentials.length>0){let e=t.allowCredentials.find(e=>{let t=f(e.id);return t===m||t===h||e.passkey_id===s.id});e?.passkey_id&&(d=e.passkey_id)}if(!d||d===s.id)try{let e=await jt(),t=e.find(e=>{let t=f(e.credential_id);return t===m||t===h||e.id===s.id});t?.id?d=t.id:e.length>0&&(d=e[0].id)}catch(e){console.warn(`[E2EE] Could not load passkeys to match credential:`,e)}return{kek:l,altKek:u,passkeyId:d}}async function Fn(e,t){let n=e.replace(/[-\s]/g,``).toUpperCase().trim(),r=new TextEncoder().encode(n),i=await crypto.subtle.importKey(`raw`,r,{name:`PBKDF2`},!1,[`deriveKey`]);return crypto.subtle.deriveKey({name:`PBKDF2`,salt:t,iterations:1e5,hash:`SHA-256`},i,{name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`])}async function In(e,t){let n=crypto.getRandomValues(new Uint8Array(16)),r=crypto.getRandomValues(new Uint8Array(12)),i=await Fn(t,n),a=new TextEncoder().encode(JSON.stringify(e));return{encryptedSk:U(await crypto.subtle.encrypt({name:`AES-GCM`,iv:r},i,a)),iv:U(r),salt:U(n)}}async function Ln(e,t,n,r){let i=W(n),a=W(t),o=W(e),s=await Fn(r,i),c=await crypto.subtle.decrypt({name:`AES-GCM`,iv:a},s,o);return JSON.parse(new TextDecoder().decode(c))}var Rn=class{keyStore;constructor(e){this.keyStore=e}async getUserStatus(){try{let e=await fetch(`/api/account/e2ee/status`);if(e.ok){let t=await e.json();return this.keyStore.validateCachedKeyAgainstStatus(t),t}}catch{}try{let e=await fetch(`/api/profiles`);if(e.ok){let t=await e.json();if(t&&t.length>0){let e=await this.getStatus(t[0].id);return this.keyStore.validateCachedKeyAgainstStatus(e),e}}}catch(e){console.warn(`[E2EE API] Fallback status check failed:`,e)}return{enabled:!1,publicKey:null,wrappedPasskeys:[],hasRecoveryKey:!1,userPasskeyCount:0,createdAt:null}}async getStatus(e){let t=await H(`/api/profiles/${e}/e2ee/status`);if(!t.ok)throw Error(`Failed to fetch E2EE status: ${await t.text()}`);let n=await t.json();return this.keyStore.validateCachedKeyAgainstStatus(n),n}async enableUserE2ee(e){if((await this.getUserStatus()).hasKeys){let e=await fetch(`/api/account/e2ee/enable`,{method:`POST`});if(!e.ok)throw Error(`Failed to enable user E2EE: ${await e.text()}`);return!0}let{kek:t,passkeyId:n}=await G(`account`),r=e||n||`primary`,i=N.keygen(),a={alg:`P256-MLKEM768`,pqc_pk:U(i.publicKey)},o={alg:`P256-MLKEM768`,seedBase64:U(i.secretKey)},s=await crypto.subtle.importKey(`raw`,t,{name:`AES-GCM`},!1,[`encrypt`]),c=crypto.getRandomValues(new Uint8Array(12)),l=new TextEncoder().encode(JSON.stringify(o)),u=U(await crypto.subtle.encrypt({name:`AES-GCM`,iv:c},s,l)),d=U(c),f=await fetch(`/api/account/e2ee/init`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({publicKey:a,passkeyId:r,encryptedSk:u,iv:d})});if(!f.ok)throw Error(`Failed to enable user E2EE: ${await f.text()}`);return this.keyStore.setPrivateKey(`account`,o),!0}async rotateUserE2eeKey(e){let{kek:t,passkeyId:n}=await G(`account`),r=e||n||`primary`,i=this.keyStore.getPrivateKey(`account`),a;i&&`kty`in i?a=i:i&&`legacyKey`in i&&(a=i.legacyKey);let o=N.keygen(),s={alg:`P256-MLKEM768`,pqc_pk:U(o.publicKey)},c={alg:`P256-MLKEM768`,seedBase64:U(o.secretKey),...a?{legacyKey:a}:{}},l=await crypto.subtle.importKey(`raw`,t,{name:`AES-GCM`},!1,[`encrypt`]),u=crypto.getRandomValues(new Uint8Array(12)),d=new TextEncoder().encode(JSON.stringify(c)),f=U(await crypto.subtle.encrypt({name:`AES-GCM`,iv:u},l,d)),p=U(u),m=await fetch(`/api/account/e2ee/init`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({publicKey:s,passkeyId:r,encryptedSk:f,iv:p})});if(!m.ok)throw Error(`Failed to upgrade user E2EE key: ${await m.text()}`);return this.keyStore.setPrivateKey(`account`,c),!0}async enableE2ee(e,t){let{kek:n}=await G(e),r=N.keygen(),i={alg:`P256-MLKEM768`,pqc_pk:U(r.publicKey)},a={alg:`P256-MLKEM768`,seedBase64:U(r.secretKey)},o=await crypto.subtle.importKey(`raw`,n,{name:`AES-GCM`},!1,[`encrypt`]),s=crypto.getRandomValues(new Uint8Array(12)),c=new TextEncoder().encode(JSON.stringify(a)),l=U(await crypto.subtle.encrypt({name:`AES-GCM`,iv:s},o,c)),u=U(s),d=await H(`/api/profiles/${e}/e2ee/init`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({publicKey:i,passkeyId:t,encryptedSk:l,iv:u})});if(!d.ok)throw Error(`Failed to enable E2EE: ${await d.text()}`);return this.keyStore.setPrivateKey(e,a),this.keyStore.setPrivateKey(`account`,a),!0}async unlockUser(){let e=await this.getUserStatus();if(!e.hasKeys&&!e.enabled&&e.wrappedPasskeys.length===0)return!1;let t=this.keyStore.getPrivateKey(`account`);if(t&&this.keyStore.isKeyCompatibleWithServer(t,e.publicKey))return!0;if(!e.wrappedPasskeys||e.wrappedPasskeys.length===0)throw Error(`No passkeys have been enrolled in E2EE yet. Please unlock with Recovery Key or reset the keypair.`);let{kek:n,altKek:r,passkeyId:i}=await G(`account`),a=i||e.wrappedPasskeys[0],o=await fetch(`/api/account/e2ee/wrapped-key${a?`?passkey_id=${encodeURIComponent(a)}`:``}`);if(!o.ok){let e=await fetch(`/api/profiles`);if(e.ok){let t=await e.json();t&&t.length>0&&(o=await fetch(`/api/profiles/${t[0].id}/e2ee/wrapped-key${a?`?passkey_id=${encodeURIComponent(a)}`:``}`))}}if(!o.ok)throw Error(`Failed to fetch wrapped key: ${await o.text()}`);let{encryptedSk:s,iv:c}=await o.json(),l=W(s),u=W(c),d=null;try{let e=await crypto.subtle.importKey(`raw`,n,{name:`AES-GCM`},!1,[`decrypt`]);d=await crypto.subtle.decrypt({name:`AES-GCM`,iv:u},e,l)}catch{if(r)try{let e=await crypto.subtle.importKey(`raw`,r,{name:`AES-GCM`},!1,[`decrypt`]);d=await crypto.subtle.decrypt({name:`AES-GCM`,iv:u},e,l)}catch{}if(!d)throw Error(`Failed to decrypt private key with this Passkey. Please unlock using your Recovery Key.`)}let f=JSON.parse(new TextDecoder().decode(d));return this.keyStore.setPrivateKey(`account`,f),!0}async unlockProfile(e){let t=await this.getStatus(e);if(!t.hasKeys&&!t.enabled&&t.wrappedPasskeys.length===0)return!1;let n=this.keyStore.getPrivateKey(e);if(n&&this.keyStore.isKeyCompatibleWithServer(n,t.publicKey))return!0;let{kek:r,altKek:i,passkeyId:a}=await G(e),o=a||t.wrappedPasskeys[0],s=await fetch(`/api/profiles/${e}/e2ee/wrapped-key${o?`?passkey_id=${encodeURIComponent(o)}`:``}`);if(s.ok||(s=await fetch(`/api/account/e2ee/wrapped-key${o?`?passkey_id=${encodeURIComponent(o)}`:``}`)),!s.ok)throw Error(`Failed to fetch wrapped key: ${await s.text()}`);let{encryptedSk:c,iv:l}=await s.json(),u=W(c),d=W(l),f=null;try{let e=await crypto.subtle.importKey(`raw`,r,{name:`AES-GCM`},!1,[`decrypt`]);f=await crypto.subtle.decrypt({name:`AES-GCM`,iv:d},e,u)}catch{if(i)try{let e=await crypto.subtle.importKey(`raw`,i,{name:`AES-GCM`},!1,[`decrypt`]);f=await crypto.subtle.decrypt({name:`AES-GCM`,iv:d},e,u)}catch{}if(!f)throw Error(`Failed to decrypt private key with this Passkey. Please unlock using your Recovery Key.`)}let p=JSON.parse(new TextDecoder().decode(f));return this.keyStore.setPrivateKey(e,p),this.keyStore.setPrivateKey(`account`,p),!0}async initUserE2eeWithRecoveryKey(e){let t=N.keygen(),n={alg:`P256-MLKEM768`,pqc_pk:U(t.publicKey)},r={alg:`P256-MLKEM768`,seedBase64:U(t.secretKey)},i=await In(r,e),a=await fetch(`/api/account/e2ee/init`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({publicKey:n,recovery:i})});if(!a.ok)throw Error(`Failed to initialize E2EE with recovery key: ${await a.text()}`);return this.keyStore.setPrivateKey(`account`,r),!0}async unlockWithRecoveryKey(e){let t=await fetch(`/api/account/e2ee/recovery-wrapped-key`);if(!t.ok)throw Error(`Failed to fetch recovery wrapped key: ${await t.text()}`);let{encryptedSk:n,iv:r,salt:i}=await t.json(),a;try{a=await Ln(n,r,i,e)}catch{throw Error(`Invalid Recovery Key or corrupted data`)}return this.keyStore.setPrivateKey(`account`,a),!0}async wrapCurrentKeyForPasskey(e){let t=this.keyStore.getPrivateKey(`account`);if(!t)return console.warn(`[E2EE API] Cannot wrap passkey: private key is not unlocked in this session`),!1;try{let{kek:n}=await G(`account`),r=await crypto.subtle.importKey(`raw`,n,{name:`AES-GCM`},!1,[`encrypt`]),i=crypto.getRandomValues(new Uint8Array(12)),a=new TextEncoder().encode(JSON.stringify(t)),o=await crypto.subtle.encrypt({name:`AES-GCM`,iv:i},r,a);return(await fetch(`/api/account/e2ee/wrap`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({passkeyId:e,encryptedSk:U(o),iv:U(i)})})).ok}catch(e){return console.warn(`[E2EE API] Failed to wrap passkey KEK:`,e),!1}}async wrapCurrentKeyForRecovery(e){let t=this.keyStore.getPrivateKey(`account`);if(!t)return console.warn(`[E2EE API] Cannot wrap recovery: private key is not unlocked in this session`),!1;let n=await In(t,e);return(await fetch(`/api/account/e2ee/wrap-recovery`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(n)})).ok}async disableUserE2ee(){let e=await fetch(`/api/account/e2ee`,{method:`DELETE`});if(!e.ok)throw Error(`Failed to disable E2EE: ${await e.text()}`);return this.keyStore.clearStorage(),!0}async disableE2ee(e){let t=await H(`/api/profiles/${e}/e2ee`,{method:`DELETE`});if(!t.ok)throw Error(`Failed to disable E2EE: ${await t.text()}`);return this.keyStore.deleteProfileKey(e),!0}},zn=class{keyStore=new An;decryptEngine=new Pn(this.keyStore);api=new Rn(this.keyStore);async getUserStatus(){return this.api.getUserStatus()}async getStatus(e){return this.api.getStatus(e)}isUnlocked(){return this.keyStore.isUnlocked()}isProfileUnlocked(e){return this.keyStore.isProfileUnlocked(e)}isKeyCompatibleWithServer(e,t){return this.keyStore.isKeyCompatibleWithServer(e,t)}clearStorage(){this.keyStore.clearStorage(),this.decryptEngine.clearDekCache()}async derivePasskeyKek(e=`account`){return G(e)}async deriveRecoveryKek(e,t){return Fn(e,t)}async wrapPrivateKeyWithRecoveryKey(e,t){return In(e,t)}async unwrapPrivateKeyWithRecoveryKey(e,t,n,r){return Ln(e,t,n,r)}async enableUserE2ee(e){return this.api.enableUserE2ee(e)}async rotateUserE2eeKey(e){return this.api.rotateUserE2eeKey(e)}async enableE2ee(e,t){return this.api.enableE2ee(e,t)}async disableUserE2ee(){let e=await this.api.disableUserE2ee();return this.decryptEngine.clearDekCache(),e}async disableE2ee(e){let t=await this.api.disableE2ee(e);return this.decryptEngine.clearDekCache(),t}async unlockUser(){return this.api.unlockUser()}async unlockProfile(e){return this.api.unlockProfile(e)}async unlockWithRecoveryKey(e){return this.api.unlockWithRecoveryKey(e)}async initUserE2eeWithRecoveryKey(e){return this.api.initUserE2eeWithRecoveryKey(e)}async wrapCurrentKeyForPasskey(e){return this.api.wrapCurrentKeyForPasskey(e)}async wrapCurrentKeyForRecovery(e){return this.api.wrapCurrentKeyForRecovery(e)}async decryptLogEntry(e,t){return this.decryptEngine.decryptLogEntry(e,t)}async decryptLogsBatch(e,t){return this.decryptEngine.decryptLogsBatch(e,t)}};const K=new zn;function Bn(e){return new Worker(`/assets/sqlite.worker-C50yoTbX.js`,{type:`module`,name:e?.name})}const Vn=new class{worker=null;pendingRequests=new Map;initPromise=null;isOpfs=!1;workerFailed=!1;getWorker(){if(!this.worker)try{this.worker=new Bn,this.worker.onmessage=e=>{let{id:t,success:n,data:r,error:i}=e.data,a=this.pendingRequests.get(t);a&&(this.pendingRequests.delete(t),n?a.resolve(r):a.reject(Error(i||`Worker execution error`)))},this.worker.onerror=e=>{console.error(`[LocalDb] Worker fatal error:`,e),this.workerFailed=!0;for(let[,t]of this.pendingRequests.entries())t.reject(Error(`SQLite Worker error: `+(e?.message||`Load failure`)));this.pendingRequests.clear()}}catch(e){throw console.error(`[LocalDb] Failed to instantiate worker:`,e),this.workerFailed=!0,e}return this.worker}sendRequest(e,t){let n=this.getWorker(),r=`${Date.now()}_${Math.random().toString(36).slice(2,9)}`;return new Promise((i,a)=>{this.pendingRequests.set(r,{resolve:i,reject:a}),n.postMessage({id:r,type:e,payload:t})})}async init(){return this.workerFailed?!1:(this.initPromise||=(async()=>{try{let e=new Promise((e,t)=>setTimeout(()=>t(Error(`SQLite Worker init timed out (12s)`)),12e3)),t=await Promise.race([this.sendRequest(`INIT`),e]);this.isOpfs=t.isOpfs,console.log(`[LocalDb] Initialized (OPFS storage: ${this.isOpfs?`ENABLED`:`IN-MEMORY`})`);let n=this.getLocalRetentionDays();return n>0&&this.cleanup(n),t.ready}catch(e){return console.warn(`[LocalDb] SQLite engine unavailable, falling back to server:`,e),this.workerFailed=!0,!1}})(),this.initPromise)}getIsOpfs(){return this.isOpfs}async getWatermark(e){return await this.init(),this.sendRequest(`GET_WATERMARK`,{profileId:e})}inFlightSyncs=new Map;async syncProfileLogs(e,t,n,r,i=!1){if(this.inFlightSyncs.has(e))return this.inFlightSyncs.get(e);let a=(async()=>{if(await this.init(),r?.aborted)return 0;let a=await this.getWatermark(e),o=Math.floor(Date.now()/1e3);if(!i&&a&&a.last_synced_at&&o-a.last_synced_at<15)return 0;let s;s=a&&a.latest_timestamp>0?Math.max(0,a.latest_timestamp-60):n===void 0?Math.floor(o-86400):n;let c=0,l,u=!0,d=0;for(;u&&d<2&&!r?.aborted;){let n=`/api/profiles/${e}/logs?start=${s}&end=${o}&limit=100`;l!==void 0&&(n+=`&before=${l}`);let i=await H(n,{signal:r});if(!i.ok)throw Error(`Failed to fetch logs from server: ${await i.text()}`);let a=await i.json();if(!a||!Array.isArray(a)||a.length===0)break;let f=await K.decryptLogsBatch(e,a),{inserted:p}=await this.sendRequest(`SYNC_BATCH`,{profileId:e,logs:f});if(c+=p,d++,t&&t(c,c),f.length<100)u=!1;else{let e=Number(f[f.length-1].timestamp);isNaN(e)||e<=s||l!==void 0&&e>=l?u=!1:l=e}}return c})();this.inFlightSyncs.set(e,a);try{return await a}finally{this.inFlightSyncs.delete(e)}}async backfillLogs(e,t,n,r=50,i){if(await this.init(),i?.aborted||t<=n)return[];let a=await H(`/api/profiles/${e}/logs?start=${n}&end=${t}&limit=${Math.min(r,100)}&before=${t}`,{signal:i});if(!a.ok)return[];let o=await a.json();if(!o||!Array.isArray(o)||o.length===0)return[];let s=await K.decryptLogsBatch(e,o);return await this.sendRequest(`SYNC_BATCH`,{profileId:e,logs:s}),s}async batchInsertLogs(e,t){return!t||t.length===0?0:(await this.init(),(await this.sendRequest(`SYNC_BATCH`,{profileId:e,logs:t}))?.inserted||0)}async queryLogs(e){return await this.init(),this.sendRequest(`QUERY_LOGS`,e)}async queryAnalytics(e){return await this.init(),this.sendRequest(`QUERY_ANALYTICS`,e)}async queryISPs(e){return await this.init(),this.sendRequest(`QUERY_ISPS`,e)}async cleanup(e){await this.init();let{deleted:t}=await this.sendRequest(`CLEANUP`,{retentionDays:e});return t>0&&console.log(`[LocalDb] Cleaned up ${t} expired local logs`),t}async getStorageInfo(){return await this.init(),this.sendRequest(`GET_STORAGE_INFO`)}async clearProfile(e){await this.init(),await this.sendRequest(`CLEAR_PROFILE`,{profileId:e})}async reDecryptLocalLogs(e){await this.init();let t=await this.sendRequest(`GET_ENCRYPTED_LOGS`,{profileId:e,limit:1e3});if(!t.rows||t.rows.length===0)return 0;let n=(await K.decryptLogsBatch(e,t.rows)).filter(e=>(e.encrypt_version??0)===0);return n.length===0?0:(await this.sendRequest(`UPDATE_LOGS_BATCH`,{profileId:e,logs:n})).updated}getLocalRetentionDays(){let e=localStorage.getItem(`obex_local_log_retention`);if(e===null)return 90;let t=parseInt(e,10);return isNaN(t)?90:t}setLocalRetentionDays(e){localStorage.setItem(`obex_local_log_retention`,e.toString()),e>0&&this.cleanup(e)}};var Hn=null;function q(e){Hn=e}function Un(){return Hn}const Wn=new class{ws=null;activeProfileId=null;isConnected=!1;lastTimestamp=0;lastId=0;reconnectAttempts=0;reconnectTimer=null;pingInterval=null;newLogsListeners=new Set;statusListeners=new Set;syncResolver=null;connect(e,t,n){if(this.ws&&this.activeProfileId===e&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING)){this.updateCursor(t,n);return}this.disconnect(),this.activeProfileId=e,this.lastTimestamp=t,this.lastId=n||0;let r=Un();if(!r){z().then(r=>{q(r.accessToken),this.connect(e,t,n)}).catch(()=>{this.scheduleReconnect()});return}let i=`${window.location.protocol===`https:`?`wss:`:`ws:`}//${window.location.host}/api/profiles/${encodeURIComponent(e)}/logs/ws?token=${encodeURIComponent(r)}`;try{this.ws=new WebSocket(i),this.setupHandlers(this.ws,e)}catch(e){console.warn(`[Logs WS] Connection creation failed:`,e),this.scheduleReconnect()}}disconnect(){if(this.reconnectTimer&&=(clearTimeout(this.reconnectTimer),null),this.pingInterval&&=(clearInterval(this.pingInterval),null),this.ws){try{this.ws.close()}catch{}this.ws=null}this.activeProfileId=null,this.reconnectAttempts=0,this.notifyStatus(!1)}updateCursor(e,t){(e>this.lastTimestamp||e===this.lastTimestamp&&(t||0)>this.lastId)&&(this.lastTimestamp=e,this.lastId=t||0,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify({type:`CHECK_NEW`,sinceTimestamp:this.lastTimestamp,lastId:this.lastId})))}async requestSyncGap(e,t,n=100){return!this.ws||this.ws.readyState!==WebSocket.OPEN||!this.activeProfileId?[]:new Promise(r=>{this.syncResolver=r,this.ws?.send(JSON.stringify({type:`SYNC_GAP`,since:e,until:t,limit:n})),setTimeout(()=>{this.syncResolver===r&&(this.syncResolver=null,r([]))},5e3)})}onNewLogs(e){return this.newLogsListeners.add(e),()=>{this.newLogsListeners.delete(e)}}onStatusChange(e){return this.statusListeners.add(e),e(this.isConnected),()=>{this.statusListeners.delete(e)}}get connected(){return this.isConnected}setupHandlers(e,t){e.onopen=()=>{this.ws===e&&(this.reconnectAttempts=0,this.notifyStatus(!0),e.send(JSON.stringify({type:`SUBSCRIBE`,sinceTimestamp:this.lastTimestamp,lastId:this.lastId})),this.pingInterval&&clearInterval(this.pingInterval),this.pingInterval=setInterval(()=>{e.readyState===WebSocket.OPEN&&e.send(JSON.stringify({type:`CHECK_NEW`,sinceTimestamp:this.lastTimestamp,lastId:this.lastId}))},5e3))},e.onmessage=async n=>{if(this.ws===e)try{let e=JSON.parse(n.data);if(e.type===`NEW_LOGS`&&Array.isArray(e.logs)&&e.logs.length>0){let n=await K.decryptLogsBatch(t,e.logs);await Vn.batchInsertLogs(t,n),e.latestTimestamp&&(this.lastTimestamp=Math.max(this.lastTimestamp,e.latestTimestamp)),e.latestId&&(this.lastId=e.latestId),this.newLogsListeners.forEach(e=>e(n))}else if(e.type===`SYNC_DATA`&&Array.isArray(e.logs)){let n=await K.decryptLogsBatch(t,e.logs);await Vn.batchInsertLogs(t,n),this.syncResolver&&=(this.syncResolver(n),null)}}catch(e){console.warn(`[Logs WS] Message handling error:`,e)}},e.onclose=()=>{this.ws===e&&(this.notifyStatus(!1),this.scheduleReconnect())},e.onerror=t=>{if(this.ws===e){console.warn(`[Logs WS] Socket error:`,t);try{e.close()}catch{}}}}scheduleReconnect(){if(!this.activeProfileId)return;this.reconnectAttempts++;let e=Math.min(1e3*1.5**this.reconnectAttempts,1e4);this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=setTimeout(()=>{this.activeProfileId&&(this.reconnectAttempts===1?z().then(e=>{q(e.accessToken)}).catch(()=>{}).finally(()=>{this.activeProfileId&&this.connect(this.activeProfileId,this.lastTimestamp,this.lastId)}):this.connect(this.activeProfileId,this.lastTimestamp,this.lastId))},e)}notifyStatus(e){this.isConnected=e,this.statusListeners.forEach(t=>t(e))}},Gn=e=>{let{t}=h(),n=(0,P.useRef)(null),[r,i]=(0,P.useState)(!1);return{fileInputRef:n,importing:r,handleImportClick:()=>n.current?.click(),handleFileChange:async r=>{let a=r.target.files?.[0];if(!a)return;i(!0);let o=null;try{let n=await a.text(),r;try{r=JSON.parse(n)}catch{alert(t(`common.invalidFormat`,`无效文件格式`));return}if(!r||typeof r!=`object`||!r.settings||!r.name){alert(t(`common.invalidFormat`,`无效文件格式`));return}let i=typeof r.version==`number`?r.version:1;i>2&&console.warn(`[ProfileImport] File version (${i}) is newer than supported version (2). Proceeding with backward-compatible import.`);let s=await Bt().catch(()=>[]),c=new Set(s.map(e=>e.name.trim().toLowerCase())),l=String(r.name).trim();l=l.replace(/[^\p{L}\p{N}_ ()-]/gu,``).trim()||`Imported`;let u=new Date,d=`${String(u.getMonth()+1).padStart(2,`0`)}-${String(u.getDate()).padStart(2,`0`)}`,f=` (${d})`,p=`${l.slice(0,Math.max(1,30-f.length)).trim()}${f}`,m=1;for(;c.has(p.toLowerCase())&&m<100;){let e=` (${d}-${m})`;p=`${l.slice(0,Math.max(1,30-e.length)).trim()}${e}`,m++}if(o=(await Ht(p)).id,await Wt(o,r.settings),r.rules&&Array.isArray(r.rules)&&r.rules.length>0){let e=new Set,t=[];for(let n of r.rules)if(n&&typeof n.pattern==`string`&&(n.type===`ALLOW`||n.type===`BLOCK`||n.type===`REDIRECT`)){let r=n.pattern.trim().toLowerCase();if(!r||e.has(r))continue;e.add(r),t.push({type:n.type,pattern:n.pattern.trim(),v_a:n.v_a||void 0,v_aaaa:n.v_aaaa||void 0,v_cname:n.v_cname||void 0,v_txt:n.v_txt||void 0,created_at:typeof n.created_at==`number`?n.created_at:void 0})}if(t.length>0)try{await $t(o,t)}catch(e){console.warn(`[ProfileImport] Bulk rules failed, falling back to sequential:`,e);for(let e of t)try{await Qt(o,e)}catch(e){if(!(e instanceof Error?e.message:String(e)).includes(`Rule for this domain already exists`))throw e}}}let h=e=>{if(!e||typeof e!=`object`)return[];let t=e.filters??e.filter??e.lists??e.list??e.blocklists??e.blocklist??e.subscriptions??e.subscription??e.external_filters??e.settings?.filters??e.settings?.filter??e.settings?.lists??e.settings?.list??e.settings?.blocklists??e.settings?.blocklist;if(!t)return[];if(Array.isArray(t))return t;if(typeof t==`string`)return[t];if(typeof t==`object`){let e=[];for(let[n,r]of Object.entries(t))typeof r==`string`?e.push(r):typeof r==`boolean`||typeof r==`number`?e.push({url:n,enabled:!!r}):r&&typeof r==`object`&&e.push(r);return e}return[]},g=e=>{if(!e)return null;let t=``,n=!0;if(typeof e==`string`)t=e.trim();else if(typeof e==`object`){let r=e;if(r.enabled!==void 0&&(n=!!r.enabled),typeof r.url==`string`)t=r.url.trim();else if(typeof r.link==`string`)t=r.link.trim();else if(typeof r.uri==`string`)t=r.uri.trim();else if(typeof r.address==`string`)t=r.address.trim();else if(typeof r.source==`string`)t=r.source.trim();else if(typeof r.target==`string`)t=r.target.trim();else if(typeof r.download_url==`string`)t=r.download_url.trim();else for(let e of Object.values(r))if(typeof e==`string`&&(e.startsWith(`http://`)||e.startsWith(`https://`)||e.startsWith(`//`))){t=e.trim();break}}if(t=t.replace(/^["']|["']$/g,``).trim(),t.startsWith(`//`))t=`https:${t}`;else if(!t.startsWith(`http://`)&&!t.startsWith(`https://`))if(t.includes(`.`)&&!t.includes(` `)&&t.length>3)t=`https://${t}`;else return null;return{url:t,enabled:n}},_=h(r);if(_.length>0){let e=new Set,t=[];for(let n of _){let r=g(n);if(!r)continue;let i=r.url.toLowerCase();e.has(i)||(e.add(i),t.push(r))}if(t.length>0)try{await en(o,t)}catch(e){console.warn(`[ProfileImport] Bulk filter import failed, falling back to sequential:`,e);for(let e of t)try{await Jt(o,e.url)}catch(t){console.warn(`[ProfileImport] Failed to sequentially import filter:`,e.url,t)}}}alert(t(`common.importSuccess`,`配置成功导入`)),e?.()}catch(e){console.error(`[ProfileImport]`,e),o&&await Ut(o).catch(()=>{});let n=e?.message||String(e||``);if(n.startsWith(`Profile limit exceeded`)){let e=n.match(/\(max (\d+)\)/),r=e?e[1]:`10`;alert(t(`common.profileLimitExceeded`,{max:r,defaultValue:`Profile limit exceeded (max ${r})`}))}else n===`The profile name already exists`?alert(t(`common.profileNameExists`,`该配置名称已存在`)):n===`Invalid Profile Name format`?alert(t(`common.profileNameFormatError`,`配置名称格式不正确`)):alert(n?`${t(`common.importError`,`配置导入失败`)}: ${n}`:t(`common.importError`,`配置导入失败`))}finally{i(!1),r.target&&(r.target.value=``),n.current&&(n.current.value=``)}}}};var J=new Map,Kn=void 0,qn=typeof navigator<`u`?navigator.language:`en-US`;function Jn(e){Kn=e||void 0,J.clear()}function Yn(e){qn=e||(typeof navigator<`u`?navigator.language:`en-US`),J.clear()}function Xn(e,t){let n=`${e||``}:${t?JSON.stringify(t):``}`,r=J.get(n);return r||(r=new Intl.DateTimeFormat(e,t),J.set(n,r)),r}function Zn(e,t){let n={timeZone:Kn,timeZoneName:`short`,...t||{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}};return Xn(qn,n).format(e)}async function Qn(e){let t=e||`en-US`,n=await Ve(t);He.changeLanguage(t),Yn(t);try{return await mt({locale:t}),{loaded:n,persisted:!0}}catch(e){return console.error(`Failed to persist locale to server:`,e),{loaded:n,persisted:!1}}}const $n=[{label:`English`,value:`en-US`,flag:`🇺🇸`},{label:`English`,value:`en-GB`,flag:`🇬🇧`},{label:`English`,value:`en-SG`,flag:`🇸🇬`},{label:`中文 (简体)`,value:`zh-CN`,flag:`🇨🇳`},{label:`中文 (简体)`,value:`zh-SG`,flag:`🇸🇬`},{label:`中文 (正體)`,value:`zh-TW`,flag:`🇹🇼`},{label:`中文 (繁體)`,value:`zh-HK`,flag:`🇭🇰`},{label:`日本語`,value:`ja-JP`,flag:`🇯🇵`},{label:`한국어`,value:`ko-KR`,flag:`🇰🇷`},{label:`Tiếng Việt`,value:`vi-VN`,flag:`🇻🇳`},{label:`Español`,value:`es-ES`,flag:`🇪🇸`},{label:`Русский`,value:`ru-RU`,flag:`🇷🇺`},{label:`Français`,value:`fr-FR`,flag:`🇫🇷`},{label:`Deutsch`,value:`de-DE`,flag:`🇩🇪`}],Y=({minimal:e=!0,size:t=`small`})=>{let{i18n:n}=h(),[r,i]=(0,P.useState)(null),a=$n.find(e=>e.value===n.language)||$n[0],o=async e=>{if(!(e===n.language||r!==null)){i(e);try{await Qn(e)}finally{i(null)}}},s=r!==null;return(0,F.jsx)(Ee,{placement:`bottom-start`,disabled:s,content:(0,F.jsx)(xe,{style:{maxHeight:`300px`,overflowY:`auto`},children:$n.map(e=>(0,F.jsx)(j,{text:e.label+` [`+e.flag+`]`,active:n.language===e.value,disabled:s,labelElement:r===e.value?(0,F.jsx)(O,{size:12}):void 0,onClick:()=>o(e.value)},e.value))}),children:(0,F.jsxs)(D,{variant:e?`minimal`:void 0,size:t,icon:s?(0,F.jsx)(O,{size:14}):(0,F.jsx)(ue,{size:14}),children:[(0,F.jsx)(`span`,{className:`hidden sm:inline ml-1`,children:a.label}),(0,F.jsx)(`span`,{className:`inline sm:hidden ml-1`,children:a.flag})]})})},er=({currentUser:e,minimal:t=!0,size:n=`small`})=>{let{t:r}=h(),[i,a]=(0,P.useState)(!1);return e?.pin_enabled?(0,F.jsx)(D,{variant:t?`minimal`:void 0,size:n,icon:i?(0,F.jsx)(O,{size:14}):(0,F.jsx)(x,{size:14}),onClick:async()=>{if(!i){a(!0);try{await lt()}catch(e){console.warn(`Failed to lock session on server`,e)}finally{sessionStorage.setItem(`obex_session_locked`,`true`),window.dispatchEvent(new Event(`session_paused`)),a(!1)}}},disabled:i,title:r(`auth.lockNow`,`Lock Now`),"aria-label":r(`auth.lockNow`,`Lock Now`)}):null};var X=`/assets/obex_cat_eye_logo-256-CgYNvUIC.webp`;const tr=({isMobile:e,navigate:t,handleLogout:n,currentUser:r})=>{let{t:i}=h(),a=typeof window<`u`?window.location.host||window.location.hostname:``;return(0,F.jsxs)(`div`,{className:`sticky top-0 z-30 h-14 border-b border-gray-200/50 dark:border-gray-800/50 bg-white/70 dark:bg-gray-900/70 backdrop-blur-lg flex items-center justify-between px-4 md:px-6 shrink-0`,style:{transform:`translateZ(0)`},children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,F.jsx)(`img`,{src:X,alt:`DNS Worker`,className:`w-8 h-8 object-contain shrink-0`}),(0,F.jsxs)(`div`,{className:`flex flex-col justify-center`,children:[(0,F.jsx)(`span`,{className:`font-bold text-base leading-tight tracking-tight dark:text-white`,children:`DNS Worker`}),a&&(0,F.jsx)(`span`,{className:`text-xs text-gray-500 dark:text-gray-400 font-mono leading-tight mt-0.5 select-all`,title:a,children:a})]})]}),(0,F.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,F.jsx)(er,{currentUser:r}),(0,F.jsx)(Y,{}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(C,{size:18}),text:e?``:i(`common.account`),onClick:()=>t(`/account`)}),(0,F.jsx)(Ee,{content:(0,F.jsxs)(`div`,{className:`p-4 space-y-3`,children:[(0,F.jsx)(`div`,{className:`font-bold text-sm`,children:i(`common.confirmLogout`)}),(0,F.jsx)(D,{fill:!0,intent:k.DANGER,text:i(`common.logout`),onClick:n})]}),children:(0,F.jsx)(D,{variant:`minimal`,intent:k.DANGER,icon:(0,F.jsx)(s,{size:18}),text:e?``:i(`common.logout`)})})]})]})]})},nr=/^[a-z_][a-z0-9_-]{4,31}$/,rr=/^[a-zA-Z\d~`!@#$%^&*()_\-+={[}\]|\\:;"'<,>.?\/]{12,100}$/,ir=/^[\p{L}\p{N}_ ()-]{1,30}$/u,ar=/^[a-zA-Z0-9_-]{1,30}$/,or=/^[a-zA-Z0-9_-]{1,30}$/,sr=/^\d{4}$/;function cr(e){return or.test(e)}function lr(e){return nr.test(e)}function ur(e){return rr.test(e)}function dr(e,t){return!!(t===`password_leaked`||e.status===403&&(t.includes(`password_leaked`)||t.includes(`password breached`)))}function fr(e){if(!e||typeof e!=`string`)return null;let t=e.trim(),n=t.startsWith(`<!DOCTYPE`)||t.startsWith(`<html`)||/<[a-z][\s\S]*>/i.test(t),r=t.includes(`cf-error-details`)||t.includes(`cf-wrapper`)||t.includes(`cf.errors.css`)||t.includes(`Cloudflare Ray ID`)||/Worker threw exception/i.test(t);if(!n&&!r)return null;let i=t.match(/class=["']cf-error-code["'][^>]*>([^<]+)</i)||t.match(/Error\s+([0-9]{3,4})/i),a=i?i[1].trim():null,o=t.match(/data-translate=["']error_desc["'][^>]*>([^<]+)</i)||t.match(/class=["']cf-subheadline["'][^>]*>([^<]+)</i)||t.match(/<title>([^<|]+)/i),s=o?o[1].trim():null,c=t.match(/Ray ID:\s*<strong[^>]*>([a-f0-9]+)<\/strong>/i)||t.match(/Ray ID:\s*([a-f0-9]+)/i)||t.match(/Cloudflare Ray ID:\s*<strong[^>]*>([a-f0-9]+)<\/strong>/i);return{isCf:r,isHtml:n,errorCode:a,errorDesc:s,rayId:c?c[1].trim():null}}function pr(e,t){if(!e)return t(`auth.authFailed`,`Authentication failed`);let n=typeof e==`string`?e:e.bodyText||e.message||``,r=fr(n);if(r){let n=r.rayId?` (Ray ID: ${r.rayId})`:``;return r.errorCode&&r.errorDesc?t(`auth.cfErrorWithDetails`,{defaultValue:`Cloudflare 边缘节点异常 (错误代码 ${r.errorCode}: ${r.errorDesc})。服务暂时不可用,请稍后重试。${n}`,code:r.errorCode,desc:r.errorDesc,rayId:n}):r.errorCode?t(`auth.cfErrorCodeOnly`,{defaultValue:`Cloudflare 边缘服务异常 (错误代码 ${r.errorCode})。服务暂时不可用,请稍后重试。${n}`,code:r.errorCode,rayId:n}):r.isCf?t(`auth.cfErrorGeneric`,{defaultValue:`Cloudflare 边缘服务异常,服务暂时不可用,请稍后重试。${n}`,rayId:n}):t(`auth.htmlErrorGeneric`,{defaultValue:`服务器返回异常网页响应 (HTTP ${e.status||500}),请稍后重试。`,status:e.status||500})}return e?.status===429||n.toLowerCase().includes(`too many`)||n===`rate_limited`?t(`auth.tooManyAttempts`,{defaultValue:`请求过于频繁或尝试次数过多,已被临时限制,请稍后再试。`}):n.includes(`row read limit`)||n.includes(`exceeded D1`)||n===`database_unavailable`?t(`auth.dbQuotaExceeded`,{defaultValue:`数据库每日免费读取配额已用尽,请等待次日自动重置或升级配额。`}):n===`no_users_registered`||n.includes(`no_users_registered`)?t(`auth.noUsersRegistered`,`系统尚无账号,请注册成为管理员。`):n.includes(`no such table`)||n.includes(`SQLITE_ERROR`)?t(`auth.dbNotInitialized`,{defaultValue:`数据库未初始化或缺少数据表,请在终端执行 'npm run db:migrate:prod' 应用数据库迁移。`}):n===`invalid_credentials`||n===`user_not_found`?t(`auth.authFailed`,`Authentication failed, please check your username or password.`):n===`invalid_totp`?t(`auth.invalidTotp`,`Invalid TOTP verification code.`):n===`invalid_turnstile`||n===`turnstile_failed`?t(`auth.turnstileRequired`,`Please complete Turnstile verification.`):n===`geolocation_missing`?t(`auth.geolocationRequired`,`Geolocation retrieval failed, please re-enter.`):n===`username_exists`?t(`auth.usernameExists`,`Username already exists.`):n===`password_leaked`?t(`auth.passwordLeaked`,`Your password has been leaked.`):n===`jwt_secret_preset`||n.includes(`jwt_secret_preset`)||n.includes(`is a documentation placeholder`)?t(`auth.jwtSecretPreset`,`检测到正在使用预设的 JWT_SECRET,请在环境变量中修改并替换为自定义安全密钥后重新部署。`):n===`jwt_secret_missing`||n.includes(`jwt_secret_missing`)||n.includes(`JWT_SECRET is missing`)?t(`auth.jwtSecretMissing`,`缺少 JWT_SECRET,请在环境变量中配置安全密钥后重新部署。`):n===`jwt_secret_invalid`?t(`auth.jwtSecretInvalid`,`JWT_SECRET 密钥格式无效,请检查配置。`):n&&n.length<200&&!n.includes(`<`)&&!n.includes(`>`)?n:t(`auth.authFailed`,`Authentication failed, please check your username or password.`)}function mr(e){if(window.turnstile){e();return}if(window.onloadTurnstileCallback=e,!document.querySelector(`script[src*="turnstile/v0/api.js"]`)){let e=document.createElement(`script`);e.src=`https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=onloadTurnstileCallback`,e.async=!0,e.defer=!0;let t=window.DNS_WORKER_CONFIG?.nonce;t&&(e.nonce=t),document.head.appendChild(e)}}async function hr(e,t){let n=new TextEncoder().encode(e+t),r=await crypto.subtle.digest(`SHA-256`,n);return Array.from(new Uint8Array(r)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}async function gr(e,t,n=6e5){let r=new TextEncoder,i=r.encode(e),a=r.encode(t.toLowerCase()),o=await crypto.subtle.importKey(`raw`,i,`PBKDF2`,!1,[`deriveBits`,`deriveKey`]),s=await crypto.subtle.deriveBits({name:`PBKDF2`,salt:a,iterations:n,hash:`SHA-256`},o,256);return Array.from(new Uint8Array(s)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}function _r(e){let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substring(n*2,n*2+2),16);return t}async function vr(e,t){let n=new TextEncoder().encode(e),r=_r(t),i=await crypto.subtle.importKey(`raw`,n,`PBKDF2`,!1,[`deriveBits`,`deriveKey`]),a=await crypto.subtle.deriveBits({name:`PBKDF2`,salt:r,iterations:1e5,hash:`SHA-256`},i,256),o=new Uint8Array(r.length+a.byteLength);return o.set(r),o.set(new Uint8Array(a),r.length),btoa(String.fromCharCode(...o))}async function yr(e,t){let n=new TextEncoder,r=n.encode(e),i=n.encode(t),a=await crypto.subtle.importKey(`raw`,r,{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]),o=await crypto.subtle.sign(`HMAC`,a,i);return Array.from(new Uint8Array(o)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}async function br(e,t){return gr(e,t)}async function xr(e,t){return gr(e,t,1e4)}function Sr(e){return/^\d{6}$/.test(e.trim())?parseInt(e.trim(),10)%11==0:!1}const Cr=({newName:e,setNewName:t,onCreate:n,setShowCreate:r,error:i})=>{let{t:a}=h(),[o,s]=(0,P.useState)(!1);return(0,F.jsxs)(M,{elevation:Ce.TWO,className:`mb-6 p-4 dark:bg-gray-900 dark:border-gray-800 rounded-xl`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,F.jsx)(Fe,{content:a(`common.profileNameFormatTip`,`Profile Name tip: 1-30 characters, duplicates not allowed`),isOpen:o,position:be.TOP,intent:k.PRIMARY,className:`flex-1`,children:(0,F.jsx)(`div`,{className:`w-full block`,children:(0,F.jsx)(Oe,{fill:!0,placeholder:a(`common.newProfileName`),value:e,onChange:e=>t(e.target.value),onFocus:()=>s(!0),onBlur:()=>s(!1),autoFocus:!0})})}),(0,F.jsx)(D,{intent:k.SUCCESS,onClick:n,text:a(`common.create`),className:`whitespace-nowrap`,disabled:!ir.test(e)}),(0,F.jsx)(D,{variant:`minimal`,onClick:()=>r(!1),icon:`cross`})]}),i&&(0,F.jsx)(`div`,{className:`mt-2 text-red-500 text-xs`,children:(e=>{if(!e)return``;if(e.startsWith(`Profile limit exceeded`)){let t=e.match(/\(max (\d+)\)/),n=t?t[1]:`10`;return a(`common.profileLimitExceeded`,{max:n,defaultValue:`Profile limit exceeded (max ${n})`})}return e===`The profile name already exists`?a(`common.profileNameExists`,`The profile name already exists`):e===`Invalid Profile Name format`?a(`common.profileNameFormatError`,`Invalid Profile Name format`):e})(i)})]})},wr=({profiles:e,onSelect:n,onDelete:i})=>{let{t:a}=h();return(0,F.jsxs)(`div`,{className:`grid gap-3`,children:[e.map(e=>(0,F.jsxs)(M,{interactive:!0,onClick:()=>n(e),className:`flex justify-between items-center p-4 dark:bg-gray-900 dark:border-gray-800 rounded-xl border border-gray-200`,children:[(0,F.jsxs)(`div`,{className:`flex flex-col`,children:[(0,F.jsx)(`span`,{className:`font-bold text-base dark:text-white`,children:e.name}),(0,F.jsx)(`code`,{className:`text-gray-400 text-[10px] font-mono uppercase mt-0.5`,children:e.id})]}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,F.jsx)(D,{variant:`minimal`,intent:k.DANGER,icon:(0,F.jsx)(r,{size:16}),onClick:t=>{t.stopPropagation(),i(t,e.id)}}),(0,F.jsx)(oe,{size:18,className:`text-gray-300`})]})]},e.id)),e.length===0&&(0,F.jsx)(Ne,{icon:(0,F.jsx)(t,{size:48,className:`text-gray-300`}),title:a(`common.welcome`),description:a(`common.createProfileToStart`)})]})},Tr=({profiles:e,onSelect:t,onCreate:n,showCreate:r,setShowCreate:i,newName:a,setNewName:o,error:s,onDelete:c,handleLogout:l,navigate:u,onRefresh:d,currentUser:f})=>{let p=We(),{t:m}=h(),{fileInputRef:g,importing:_,handleImportClick:v,handleFileChange:y}=Gn(d);return(0,P.useEffect)(()=>{Qe();let e=setTimeout($e,2e3);return()=>clearTimeout(e)},[]),(0,F.jsxs)(`div`,{className:`min-h-screen bg-gray-50 dark:bg-gray-950 flex flex-col`,children:[(0,F.jsx)(`input`,{type:`file`,ref:g,className:`hidden`,accept:`.json`,onChange:y}),(0,F.jsx)(tr,{isMobile:p,navigate:u,handleLogout:l,currentUser:f}),(0,F.jsx)(`div`,{className:`flex-1 flex flex-col items-center justify-start md:justify-center p-4 pt-8 md:pt-4`,children:(0,F.jsxs)(`div`,{className:`w-full max-w-md`,children:[(0,F.jsxs)(`div`,{className:`flex justify-between items-center mb-6`,children:[(0,F.jsx)(ye,{className:`dark:text-white font-bold`,style:{marginBottom:0},children:m(`common.selectProfile`)}),(0,F.jsxs)(`div`,{className:`flex gap-1`,children:[(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(ce,{size:16}),onClick:v,text:p?``:m(`common.import`,`导入`),loading:_}),(0,F.jsx)(D,{variant:`minimal`,intent:k.PRIMARY,icon:(0,F.jsx)(me,{size:18}),onClick:()=>i(!0),text:m(`common.add`),disabled:r})]})]}),r&&(0,F.jsx)(Cr,{newName:a,setNewName:o,onCreate:n,setShowCreate:i,error:s}),(0,F.jsx)(wr,{profiles:e,onSelect:t,onDelete:c})]})})]})},Er=({isSidebarOpen:e,setIsSidebarOpen:t,isProfileActive:n,activeId:r,location:i,navigate:a,handleLogout:o,currentUser:u,navItems:d})=>{let{t:f}=h();return(0,F.jsxs)(`aside`,{className:Ie(`flex flex-col border-r border-gray-200 dark:border-gray-800 transition-all duration-300 bg-white dark:bg-gray-900`,e?`w-64`:`w-16`),children:[(0,F.jsxs)(`div`,{className:`h-14 flex items-center px-4 shrink-0`,children:[(0,F.jsx)(`img`,{src:X,alt:`DNS Worker`,className:`w-8 h-8 object-contain shrink-0`}),e&&(0,F.jsxs)(`div`,{className:`ml-3 flex flex-col justify-center min-w-0`,children:[(0,F.jsx)(`span`,{className:`font-bold text-base leading-tight dark:text-white truncate`,children:`DNS Worker`}),(0,F.jsx)(`span`,{className:`text-xs text-gray-500 dark:text-gray-400 font-mono leading-tight mt-0.5 truncate select-all`,title:typeof window<`u`?window.location.host||window.location.hostname:``,children:typeof window<`u`?window.location.host||window.location.hostname:``})]})]}),(0,F.jsx)(`div`,{className:`flex-1 py-4 px-2 overflow-y-auto overflow-x-hidden`,children:(0,F.jsxs)(xe,{className:`bg-transparent p-0`,children:[d.map(t=>(0,F.jsx)(j,{icon:t.icon,text:e?t.label:``,disabled:!n,active:i.pathname.endsWith(t.id===`stats`?`/stats`:`/${t.id}`),onClick:()=>a(t.path)},t.id)),(0,F.jsx)(j,{icon:(0,F.jsx)(S,{size:18}),text:e?f(`nav.filter`):``,disabled:!n,active:i.pathname.endsWith(`/filter`),onClick:()=>a(`/dash/${r}/filter`)}),(0,F.jsx)(j,{icon:(0,F.jsx)(l,{size:18}),text:e?f(`nav.settings`):``,disabled:!n,active:i.pathname.endsWith(`/settings`),onClick:()=>a(`/dash/${r}/settings`)}),(0,F.jsx)(`li`,{className:`my-4 border-t border-gray-100 dark:border-gray-800`}),(0,F.jsx)(j,{icon:(0,F.jsx)(C,{size:18}),text:e?f(`common.account`):``,active:i.pathname===`/account`,onClick:()=>a(`/account`)}),(0,F.jsx)(Ee,{className:`w-full`,placement:`right-end`,content:(0,F.jsxs)(`div`,{className:`p-4 space-y-3`,children:[(0,F.jsx)(`div`,{className:`font-bold text-sm`,children:f(`common.confirmLogout`)}),(0,F.jsx)(D,{fill:!0,intent:k.DANGER,text:f(`common.logout`),onClick:o})]}),children:(0,F.jsx)(j,{icon:(0,F.jsx)(s,{size:18}),text:e?f(`common.logout`):``,intent:k.DANGER,shouldDismissPopover:!1})})]})}),(0,F.jsxs)(`div`,{className:`p-4 border-t border-gray-100 dark:border-gray-800 flex items-center justify-between`,children:[(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(c,{size:18}),onClick:()=>t(!e)}),e&&(0,F.jsx)(Me,{minimal:!0,round:!0,children:u?.username.toUpperCase()||`USER`})]})]})},Dr=({theme:e,setTheme:t,selectedProfile:n,isProfileActive:r,location:i,navigate:a,isSaving:o,currentUser:s})=>{let{t:c}=h();return(0,F.jsxs)(A,{className:`absolute! top-0 left-0 right-0 border-b! border-gray-200/50 dark:border-gray-800/50 shadow-none! bg-white/70! dark:bg-gray-900/70! backdrop-blur-lg! h-14 items-center px-4 shrink-0`,style:{zIndex:10,transform:`translateZ(0)`,willChange:`transform`},children:[(0,F.jsxs)(A.Group,{align:De.LEFT,children:[(0,F.jsxs)(`button`,{onClick:()=>a(`/dash`),className:`font-bold text-blue-600 dark:text-blue-400 bg-transparent border-none p-0 cursor-pointer flex items-center gap-1`,children:[(0,F.jsx)(Se,{icon:`caret-left`}),(0,F.jsx)(`span`,{className:`truncate max-w-30 md:max-w-none`,children:i.pathname===`/account`?c(`common.account`):r?n?.name||c(`common.loading`):c(`common.selectProfile`)})]}),r&&i.pathname!==`/account`&&(0,F.jsx)(`button`,{onClick:()=>{let e=n?.id||i.pathname.split(`/`)[2];e&&a(`/dash/${e}/settings`)},className:Le(`md:hidden ml-2 hover:text-gray-700 dark:hover:text-gray-200 bg-transparent border-none p-1 cursor-pointer flex items-center transition-colors`,i.pathname.includes(`/settings`)?`text-blue-500 dark:text-blue-400`:`text-gray-500 dark:text-gray-400`),title:c(`nav.settings`),children:(0,F.jsx)(l,{size:18})})]}),(0,F.jsx)(A.Group,{align:De.RIGHT,children:(0,F.jsxs)(`div`,{className:`flex items-center gap-2`,children:[o&&(0,F.jsxs)(`div`,{className:`flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium text-blue-600 dark:text-blue-400 bg-blue-50/80 dark:bg-blue-950/50 border border-blue-200/50 dark:border-blue-800/50 rounded-full animate-pulse mr-1`,children:[(0,F.jsx)(O,{size:12}),(0,F.jsx)(`span`,{children:c(`settings.saving`)})]}),(0,F.jsx)(er,{currentUser:s}),(0,F.jsx)(Y,{}),(0,F.jsxs)(`div`,{className:`flex items-center gap-1 bg-gray-100/50 dark:bg-gray-800/50 p-1 rounded-lg`,children:[(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(_,{size:14}),size:`small`,active:e===`light`,onClick:()=>t(`light`)}),(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(v,{size:14}),size:`small`,active:e===`dark`,onClick:()=>t(`dark`)}),(0,F.jsx)(D,{variant:`minimal`,icon:(0,F.jsx)(b,{size:14}),size:`small`,active:e===`system`,onClick:()=>t(`system`)})]})]})})]})},Or=({isProfileActive:e,navItems:t,location:n,navigate:r})=>{let{t:i}=h();return e?(0,F.jsxs)(`div`,{className:`fixed bottom-0 left-0 right-0 h-16 bg-white/80 dark:bg-gray-900/80 backdrop-blur-xl border-t border-gray-200/50 dark:border-gray-800/50 flex items-center justify-around px-2 z-50 pb-safe`,children:[t.map(e=>(0,F.jsxs)(`button`,{onClick:()=>r(e.path),className:Ie(`flex flex-col items-center justify-center gap-1 w-16 h-full transition-colors`,(e.id===`rules`?n.pathname.includes(`/rules`)||n.pathname.includes(`/filter`):n.pathname.includes(e.id===`stats`?`/stats`:`/${e.id}`))?`text-blue-500`:`text-gray-400`),children:[e.icon,(0,F.jsx)(`span`,{className:`text-[10px] font-medium`,children:e.label})]},e.id)),(0,F.jsxs)(`button`,{onClick:()=>r(`/account`),className:Ie(`flex flex-col items-center justify-center gap-1 w-16 h-full transition-colors`,n.pathname===`/account`?`text-blue-500`:`text-gray-400`),children:[(0,F.jsx)(C,{size:20}),(0,F.jsx)(`span`,{className:`text-[10px] font-medium`,children:i(`common.account`)})]})]}):null},kr=({children:e,isSidebarOpen:t,setIsSidebarOpen:n,theme:r,setTheme:i,selectedProfile:a,profiles:o,setSelectedProfile:s,location:c,navigate:l,handleLogout:u,currentUser:f,isSaving:p})=>{let{profileId:m}=d(),g=We(),{t:_}=h(),v=m||a?.id,b=!!v;(0,P.useEffect)(()=>{if(m&&o.length>0&&a?.id!==m){let e=o.find(e=>e.id===m);e&&s(e)}},[m,o,a,s]);let x=[{id:`setup`,label:_(`nav.setup`),icon:(0,F.jsx)(ce,{size:20}),path:`/dash/${v}/setup`},{id:`rules`,label:g&&c.pathname.includes(`/filter`)?_(`nav.filter`):_(`nav.rules`),icon:g&&c.pathname.includes(`/filter`)?(0,F.jsx)(S,{size:20}):(0,F.jsx)(y,{size:20}),path:g&&c.pathname.includes(`/filter`)?`/dash/${v}/filter`:`/dash/${v}/rules`},{id:`stats`,label:_(`nav.stats`),icon:(0,F.jsx)(ee,{size:20}),path:`/dash/${v}/stats`},{id:`logs`,label:_(`nav.logs`),icon:(0,F.jsx)(re,{size:20}),path:`/dash/${v}/logs`}];return(0,F.jsxs)(`div`,{className:`flex h-screen w-full bg-white dark:bg-gray-950 overflow-hidden flex-col md:flex-row`,children:[!g&&(0,F.jsx)(Er,{isSidebarOpen:t,setIsSidebarOpen:n,isProfileActive:b,activeId:v,location:c,navigate:l,handleLogout:u,currentUser:f,navItems:x}),(0,F.jsxs)(`main`,{className:`flex-1 min-w-0 h-full relative bg-gray-50/20 dark:bg-gray-950/20 flex flex-col overflow-hidden`,children:[(0,F.jsx)(Dr,{theme:r,setTheme:i,selectedProfile:a,isProfileActive:b,location:c,navigate:l,isSaving:p,currentUser:f}),(0,F.jsx)(`div`,{className:`flex-1 min-h-0 flex flex-col relative z-0 isolate`,style:{isolation:`isolate`},children:c.pathname.endsWith(`/logs`)?(0,F.jsx)(`div`,{className:`flex-1 overflow-y-auto`,children:e}):(0,F.jsx)(`div`,{className:`flex-1 overflow-y-auto pt-14`,children:(0,F.jsx)(`div`,{className:`p-2 md:p-4 pb-24 md:pb-8`,children:e})})}),g&&(0,F.jsx)(Or,{isProfileActive:b,navItems:x,location:c,navigate:l})]})]})};function Ar(){let[e,t]=(0,P.useState)(`dark`);return(0,P.useEffect)(()=>{let t=window.document.documentElement;(e=>{t.classList.remove(`light`,`dark`,`bp6-dark`),t.classList.add(e),e===`dark`&&t.classList.add(`bp6-dark`)})(e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`:e)},[e]),{theme:e,setTheme:t}}function jr(){let{t:e}=h();return(t=>{let n=``,r=e(`meta.defaultDesc`,`Secure, fast, and customizable DNS resolution for all your devices. Based on Cloudflare Workers. Open-source and privacy-focused.`);return t===`/dash`?n=e(`common.selectProfile`):t===`/account`?n=e(`common.account`):t.endsWith(`/setup`)?(n=e(`nav.setup`),r=e(`meta.setupDesc`,`Configure your devices to use DNS Worker.`)):t.endsWith(`/filter`)?n=e(`nav.filter`):t.endsWith(`/rules`)?n=e(`nav.rules`):t.endsWith(`/settings`)?n=e(`nav.settings`):t.endsWith(`/stats`)?n=e(`nav.stats`):t.endsWith(`/logs`)&&(n=e(`nav.logs`)),{title:n?`${n} | DNS Worker`:`DNS Worker`,description:r}})(ae().pathname)}var Z=()=>{let e=typeof window<`u`&&window.location.protocol===`https:`?`; Secure`:``;document.cookie=`csrf_token=; Max-Age=0; path=/; SameSite=Lax${e}`;try{sessionStorage.removeItem(`obex_session_active`),sessionStorage.removeItem(`obex_session_locked`),sessionStorage.removeItem(`obex_username`),sessionStorage.removeItem(`obex_user_id`)}catch{}};function Mr(e){let[t,n]=(0,P.useState)(null),[r,i]=(0,P.useState)(null),{t:a,i18n:o}=h(),s=async()=>{try{let e=Un(),t=document.cookie.includes(`csrf_token=`);if(!e&&!t){n(!1);return}if(!e)try{q((await z()).accessToken)}catch{Z(),n(!1);return}try{let e=await pt();i(e),e.username&&sessionStorage.setItem(`obex_username`,e.username),e.id&&sessionStorage.setItem(`obex_user_id`,e.id),e.timezone&&Jn(e.timezone),e.locale&&(Yn(e.locale),o.changeLanguage(e.locale)),n(!0)}catch(e){if(e instanceof L&&e.status===403&&e.bodyText===`session_paused`){n(!0),window.dispatchEvent(new Event(`session_paused`));return}e instanceof L&&e.status===401&&Z(),n(!1)}}catch{n(!1)}};return(0,P.useEffect)(()=>{s()},[]),(0,P.useEffect)(()=>{let e=async()=>{try{i(await pt())}catch(e){console.warn(`Failed to refresh user on user_updated event`,e)}};return window.addEventListener(`user_updated`,e),()=>{window.removeEventListener(`user_updated`,e)}},[]),(0,P.useEffect)(()=>{let t=t=>{K.clearStorage(),Z(),n(!1);let r=t.detail?.reason;if(r===`missing`)return;let i=a(`auth.unauthorizedDefault`);r===`geolocation_mismatch`||r===`geolocation_missing`?i=a(`auth.unauthorizedGeo`):r===`expired`?i=a(`auth.unauthorizedExpired`):r===`token_reuse`?i=a(`auth.unauthorizedReuse`):r===`session_not_found`&&(i=a(`auth.unauthorizedRevoked`)),e.current&&e.current.show({message:i,intent:`danger`,icon:`error`,timeout:5e3})};return window.addEventListener(`auth_unauthorized`,t),()=>{window.removeEventListener(`auth_unauthorized`,t)}},[a,e]),{isLoggedIn:t,currentUser:r,checkAuth:s,handleLogout:async()=>{try{K.clearStorage(),await ot()}catch(e){console.error(`Logout failed`,e)}finally{if(K.clearStorage(),typeof window<`u`&&`caches`in window)try{await caches.delete(`dns-worker-logs-v1`)}catch{}try{sessionStorage.removeItem(`dns_worker_session_active`)}catch{}Z(),n(!1),window.location.reload()}}}}function Nr(e){let[t,n]=(0,P.useState)([]),[r,i]=(0,P.useState)(null),[a,o]=(0,P.useState)(!1),[s,c]=(0,P.useState)(!1),[l,d]=(0,P.useState)(``),[f,p]=(0,P.useState)(``),[m,g]=(0,P.useState)(null),_=u(),v=ae(),{t:y}=h(),b=async()=>{try{n(await Bt())}catch(e){console.error(`Failed to fetch profiles`,e)}finally{o(!0)}};return(0,P.useEffect)(()=>{e===!0?b():(n([]),i(null),o(!1))},[e]),(0,P.useEffect)(()=>{e===!0&&v.pathname===`/dash`&&b()},[v.pathname]),{profiles:t,selectedProfile:r,setSelectedProfile:i,showCreateDialog:s,setShowCreateDialog:c,newProfileName:l,setNewProfileName:d,createError:f,setCreateError:p,prefilledRule:m,setPrefilledRule:g,fetchProfiles:b,handleCreateProfile:async()=>{if(l)try{await Ht(l),d(``),c(!1),await b()}catch(e){p(pr(e,y))}},handleDeleteProfile:async(e,t)=>{if(e.stopPropagation(),confirm(y(`common.confirmDelete`)))try{await Ut(t),await b(),r?.id===t&&i(null)}catch(e){console.error(e)}},handleQuickAction:(e,t,n)=>{g({domain:e,type:t,recordType:n});let i=r?.id||v.pathname.split(`/`)[2];i&&_(`/dash/${i}/rules`)},loaded:a}}const Pr=(0,P.forwardRef)(({length:e,value:t,onChange:n,disabled:r=!1,type:i=`text`,error:a=!1,autoFocus:o=!1,onComplete:s},c)=>{let l=(0,P.useRef)([]);(0,P.useImperativeHandle)(c,()=>({focus:()=>{l.current[0]?.focus()}})),(0,P.useEffect)(()=>{l.current=l.current.slice(0,e),o&&setTimeout(()=>{l.current[0]?.focus()},50)},[e,o]);let u=(r,i)=>{let a=r.target.value.replace(/\D/g,``);if(!a){let e=t.split(``);e[i]=``,n(e.join(``));return}let o=t.split(``);o[i]=a[a.length-1];let c=o.join(``);n(c),i<e-1?l.current[i+1]?.focus():c.length===e&&s?.(c)},d=(r,i)=>{if(r.key===`Backspace`){r.preventDefault();let e=t.split(``);e[i]?(e[i]=``,n(e.join(``))):i>0&&(e[i-1]=``,n(e.join(``)),l.current[i-1]?.focus())}else r.key===`ArrowLeft`&&i>0?l.current[i-1]?.focus():r.key===`ArrowRight`&&i<e-1&&l.current[i+1]?.focus()},f=t=>{t.preventDefault();let r=t.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,e);if(r){n(r);let t=Math.min(r.length,e-1);l.current[t]?.focus(),r.length===e&&s?.(r)}};return(0,F.jsx)(`div`,{className:`flex gap-2 justify-center my-2`,children:Array.from({length:e},(e,n)=>t[n]||``).map((e,t)=>(0,F.jsx)(`input`,{ref:e=>{l.current[t]=e},type:i===`password`?`password`:`text`,pattern:`\\d*`,inputMode:`numeric`,maxLength:1,value:e,disabled:r,onChange:e=>u(e,t),onKeyDown:e=>d(e,t),onPaste:f,autoComplete:`one-time-code`,className:`w-10 h-12 text-center text-xl font-bold rounded-lg border-2 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 transition-all duration-150 focus:outline-none focus:ring-2 focus:ring-indigo-500/20 ${a?`border-red-500 focus:border-red-500 focus:ring-red-500/20`:`border-slate-200 dark:border-slate-700 focus:border-indigo-500 dark:focus:border-indigo-400`} ${r?`opacity-60 cursor-not-allowed bg-slate-50 dark:bg-slate-900`:``}`},t))})});Pr.displayName=`DigitInput`;const Fr=({currentUser:e,handleLogout:t,onUnlock:n,children:r})=>{let{t:i}=h(),[a,o]=(0,P.useState)(()=>sessionStorage.getItem(`obex_session_locked`)===`true`),[c,l]=(0,P.useState)(``),[u,d]=(0,P.useState)(!1),[f,p]=(0,P.useState)(``),[m,g]=(0,P.useState)(null),_=(0,P.useRef)(0),v=(0,P.useRef)(null),y=e?.session_lock_timeout??15,b=!!e?.pin_enabled,x=b;(0,P.useEffect)(()=>{if(!x||!b||a||!e)return;_.current=Date.now();let t=()=>{_.current=Date.now()},n=[`mousemove`,`mousedown`,`keypress`,`scroll`,`touchstart`,`click`];n.forEach(e=>{window.addEventListener(e,t)});let r=setInterval(async()=>{if(Date.now()-_.current>=y*60*1e3){o(!0),sessionStorage.setItem(`obex_session_locked`,`true`);try{await lt()}catch(e){console.warn(`Failed to lock session on backend`,e)}}},1e4);return()=>{n.forEach(e=>{window.removeEventListener(e,t)}),clearInterval(r)}},[x,b,a,y,e]),(0,P.useEffect)(()=>{let e=()=>{o(!0),sessionStorage.setItem(`obex_session_locked`,`true`)};return window.addEventListener(`session_paused`,e),()=>{window.removeEventListener(`session_paused`,e)}},[]),(0,P.useEffect)(()=>{a&&setTimeout(()=>{v.current?.focus()},100)},[a]);let S=async r=>{let a=r||c;if(a){d(!0),p(``);try{let r=e?.id||sessionStorage.getItem(`obex_user_id`);if(!r){p(i(`auth.sessionExpired`,`Session expired. Logging out...`)),setTimeout(()=>{t()},1500);return}let{nonce:s,legacy:c}=await st(),u=await br(a,r);c?await ct(u,s):await ct(await xr(u,s),s),o(!1),sessionStorage.removeItem(`obex_session_locked`),l(``),p(``),g(null),_.current=Date.now(),n&&n()}catch(e){if(l(``),e instanceof L)if(e.status===401&&e.bodyText.includes(`too_many_attempts`))p(i(`auth.sessionExpired`,`Too many failed attempts. Session terminated.`)),setTimeout(()=>{t()},1500);else if(e.status===400||e.status===401){let t=3;try{let n=JSON.parse(e.bodyText);n&&typeof n.attemptsRemaining==`number`&&(t=n.attemptsRemaining)}catch(e){console.debug(`Failed to parse attemptsRemaining from body`,e)}g(t),p(i(`auth.pinIncorrect`,`Incorrect PIN`)),setTimeout(()=>{v.current?.focus()},50)}else p(i(`auth.networkError`,`Network error. Please try again.`));else e instanceof Error?p(e.message):p(i(`common.errorNetwork`))}finally{d(!1)}}};return a?(0,F.jsx)(`div`,{className:`fixed inset-0 z-9999 flex flex-col items-center justify-center p-4`,style:{background:`radial-gradient(circle at center, #1b202e 0%, #0d0f14 100%)`,fontFamily:`system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`,userSelect:`none`},children:(0,F.jsx)(`div`,{className:`w-full max-w-md`,children:(0,F.jsxs)(M,{elevation:Ce.FOUR,className:`w-full p-8 rounded-2xl shadow-none! dark:bg-gray-900 border border-gray-100 dark:border-gray-800 flex flex-col items-center`,onClick:()=>v.current?.focus(),children:[(0,F.jsxs)(`div`,{className:`flex flex-col items-center mb-8`,children:[(0,F.jsx)(`img`,{src:X,alt:`DNS Worker Logo`,className:`w-20 h-20 object-contain`}),(()=>{let t=e?.username||sessionStorage.getItem(`obex_username`)||``;return t?(0,F.jsx)(`h3`,{className:`font-bold tracking-tight text-2xl mt-4 text-slate-900 dark:text-slate-100`,children:t}):null})(),(0,F.jsx)(`p`,{className:`text-gray-500 mt-2 text-center text-sm leading-relaxed`,children:i(`auth.sessionLocked`,`Session Locked due to inactivity`)})]}),f&&(0,F.jsxs)(Te,{intent:k.DANGER,className:`w-full mb-6 rounded-xl text-center`,children:[(0,F.jsx)(`div`,{className:`text-sm font-medium`,children:f}),m!==null&&(0,F.jsx)(`div`,{className:`text-xs mt-1 opacity-80`,children:i(`auth.attemptsRemaining`,`{{count}} attempts remaining`,{count:m})})]}),(0,F.jsx)(`div`,{className:`w-full flex justify-center mb-6`,children:(0,F.jsx)(Pr,{ref:v,length:4,value:c,onChange:l,disabled:u,type:`password`,error:!!f,autoFocus:!0,onComplete:e=>S(e)})}),u&&(0,F.jsx)(`div`,{className:`mb-4`,children:(0,F.jsx)(O,{size:24})}),(0,F.jsx)(`div`,{className:`w-full flex justify-center mt-4`,children:(0,F.jsx)(D,{minimal:!0,intent:k.NONE,icon:(0,F.jsx)(s,{size:16}),onClick:e=>{e.stopPropagation(),t()},className:`text-gray-400 hover:text-gray-700 dark:hover:text-gray-200`,children:i(`auth.logoutAndReturn`,`Log Out Session`)})})]})})}):(0,F.jsx)(F.Fragment,{children:r})},Ir=({itemCount:e})=>{let t=(0,P.useRef)(null),[n,r]=(0,P.useState)(-1),[i,a]=(0,P.useState)(!1),o=(0,P.useRef)(!1),s=(0,P.useRef)(0),c=(0,P.useRef)(0),l=(0,P.useRef)(null),u=t=>{let n=Array.from(t.children);if(n.length>=e*3){let r=n[0].offsetTop,i=n[e].offsetTop-r;i>0&&(t.scrollTop>=i*2?t.scrollTop-=i:t.scrollTop<i&&(t.scrollTop+=i))}};return(0,P.useEffect)(()=>{let n=t.current;if(!n)return;requestAnimationFrame(()=>{let t=Array.from(n.children);if(t.length>=e*3){let r=t[0].offsetTop,i=t[e].offsetTop-r;i>0&&n.scrollTop===0&&(n.scrollTop=i)}});let r=e=>{e.button===0&&(o.current=!0,s.current=e.clientY,c.current=n.scrollTop,a(!0))},i=e=>{if(!o.current)return;e.preventDefault();let t=e.clientY-s.current;n.scrollTop=c.current-t,u(n)},d=()=>{o.current&&(o.current=!1,a(!1))},f=e=>{e.touches.length===1&&(o.current=!0,s.current=e.touches[0].clientY,c.current=n.scrollTop,a(!0))},p=e=>{if(!o.current||e.touches.length!==1)return;e.preventDefault();let t=e.touches[0].clientY-s.current;n.scrollTop=c.current-t,u(n)},m=()=>{o.current&&(o.current=!1,a(!1))},h=()=>{a(!0),l.current&&window.clearTimeout(l.current),l.current=window.setTimeout(()=>{a(!1)},1200),u(n)};return n.addEventListener(`mousedown`,r),window.addEventListener(`mousemove`,i),window.addEventListener(`mouseup`,d),n.addEventListener(`touchstart`,f,{passive:!0}),n.addEventListener(`touchmove`,p,{passive:!1}),n.addEventListener(`touchend`,m),n.addEventListener(`touchcancel`,m),n.addEventListener(`wheel`,h,{passive:!0}),()=>{n.removeEventListener(`mousedown`,r),window.removeEventListener(`mousemove`,i),window.removeEventListener(`mouseup`,d),n.removeEventListener(`touchstart`,f),n.removeEventListener(`touchmove`,p),n.removeEventListener(`touchend`,m),n.removeEventListener(`touchcancel`,m),n.removeEventListener(`wheel`,h),l.current&&window.clearTimeout(l.current)}},[e]),(0,P.useEffect)(()=>{let n=t.current;if(!n)return;let a,s=()=>{!i&&!o.current&&(n.scrollTop+=.5,u(n));let t=n.getBoundingClientRect(),c=t.top+t.height/3,l=Array.from(n.children),d=-1;for(let t=0;t<l.length;t++){let n=l[t].getBoundingClientRect();if(n.top<=c&&n.bottom>=c){d=t%e;break}}r(d),a=requestAnimationFrame(s)};return a=requestAnimationFrame(s),()=>cancelAnimationFrame(a)},[i,e]),{containerRef:t,activeIdx:n,isPaused:i,setIsPaused:a}};var Q=[{icon:t,colorClass:`text-blue-500`,titleKey:`intro.item1Title`,descKey:`intro.item1Desc`},{icon:x,colorClass:`text-purple-500`,titleKey:`intro.item2Title`,descKey:`intro.item2Desc`},{icon:ue,colorClass:`text-green-500`,titleKey:`intro.item3Title`,descKey:`intro.item3Desc`},{icon:se,colorClass:`text-orange-500`,titleKey:`intro.item4Title`,descKey:`intro.item4Desc`},{icon:ee,colorClass:`text-red-500`,titleKey:`intro.item5Title`,descKey:`intro.item5Desc`},{icon:fe,colorClass:`text-yellow-500`,titleKey:`intro.item6Title`,descKey:`intro.item6Desc`},{icon:ne,colorClass:`text-cyan-500`,titleKey:`intro.item7Title`,descKey:`intro.item7Desc`},{icon:o,colorClass:`text-indigo-500`,titleKey:`intro.item9Title`,descKey:`intro.item9Desc`},{icon:y,colorClass:`text-pink-500`,titleKey:`intro.item8Title`,descKey:`intro.item8Desc`}];const Lr=()=>{let{t:e}=h(),{containerRef:t,activeIdx:n,setIsPaused:r}=Ir({itemCount:Q.length});return(0,F.jsx)(`div`,{className:`flex flex-col h-full overflow-y-auto no-scrollbar py-[50vh] px-8 lg:px-16 relative select-none cursor-grab active:cursor-grabbing bg-transparent`,ref:t,onMouseEnter:()=>r(!0),onMouseLeave:()=>r(!1),style:{scrollbarWidth:`none`},children:[...Q,...Q,...Q].map((t,r)=>{let i=r%Q.length===n,a=t.icon,o=(r%Q.length+1).toString().padStart(2,`0`);return(0,F.jsx)(`div`,{className:`transition-[opacity,transform,border-color,filter] duration-300 ease-out shrink-0 border-l-4 mb-8 pl-6 transform-gpu ${i?`border-blue-600 scale-[1.03] opacity-100 grayscale-0`:`border-gray-200 dark:border-gray-800 scale-100 opacity-30 grayscale`}`,children:(0,F.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,F.jsxs)(`span`,{className:`font-mono text-xs font-bold tracking-widest transition-colors duration-300 ${i?`text-blue-600`:`text-gray-400`}`,children:[`/`,o]}),(0,F.jsx)(`div`,{className:`transition-transform duration-300 ${i?`rotate-0 scale-110`:`rotate-12 opacity-50`}`,children:(0,F.jsx)(a,{size:24,className:t.colorClass})})]}),(0,F.jsxs)(`div`,{className:`space-y-2`,children:[(0,F.jsx)(`h2`,{className:`text-4xl font-black leading-none tracking-tighter uppercase transition-colors duration-300 ${i?`text-gray-900 dark:text-white`:`text-gray-300 dark:text-gray-700`}`,children:e(t.titleKey)}),(0,F.jsx)(`p`,{className:`max-w-md text-lg font-medium leading-snug transition-colors duration-300 ${i?`text-gray-600 dark:text-gray-400`:`text-transparent`}`,children:e(t.descKey)})]}),i&&(0,F.jsx)(`div`,{className:`flex gap-1 mt-2 transition-all duration-300`,children:(0,F.jsx)(`div`,{className:`h-1 w-2 bg-gray-200 dark:bg-gray-800`})})]})},r)})})},$=({code:e,copyText:t,copiedText:n})=>{let[r,i]=(0,P.useState)(!1);return(0,F.jsxs)(`div`,{className:`relative group my-2 bg-zinc-950 dark:bg-black text-zinc-200 rounded-lg p-4 font-mono text-sm border border-zinc-800/80 overflow-x-auto shadow-inner`,children:[(0,F.jsx)(`pre`,{className:`whitespace-pre`,children:e}),(0,F.jsx)(`button`,{onClick:async()=>{try{await navigator.clipboard.writeText(e),i(!0),setTimeout(()=>i(!1),2e3)}catch(e){console.error(`Failed to copy text:`,e)}},className:`absolute top-2 right-2 px-2.5 py-1 text-xs rounded bg-zinc-850 hover:bg-zinc-800 text-zinc-300 hover:text-white transition duration-200 border border-zinc-700/60 flex items-center gap-1.5`,children:r?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`svg`,{className:`w-3.5 h-3.5 text-emerald-400`,fill:`none`,viewBox:`0 0 24 24`,stroke:`currentColor`,strokeWidth:`2.5`,children:(0,F.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,d:`M5 13l4 4L19 7`})}),(0,F.jsx)(`span`,{children:n})]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`svg`,{className:`w-3.5 h-3.5`,fill:`none`,viewBox:`0 0 24 24`,stroke:`currentColor`,strokeWidth:`2`,children:(0,F.jsx)(`path`,{strokeLinecap:`round`,strokeLinejoin:`round`,d:`M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3`})}),(0,F.jsx)(`span`,{children:t})]})})]})},Rr=()=>{let{t:e}=h();return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`1`}),e(`deploymentGuide.stepDbTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:e(`deploymentGuide.stepDbDesc`)}),(0,F.jsx)($,{code:e(`deploymentGuide.stepDbCommand1`),copyText:e(`deploymentGuide.copy`),copiedText:e(`deploymentGuide.copied`)}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mt-3 mb-2`,children:e(`deploymentGuide.stepDbDesc2`)}),(0,F.jsx)($,{code:e(`deploymentGuide.stepDbCommand2`),copyText:e(`deploymentGuide.copy`),copiedText:e(`deploymentGuide.copied`)})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`2`}),e(`deploymentGuide.stepJwtTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:e(`deploymentGuide.stepJwtDesc`)}),(0,F.jsx)($,{code:e(`deploymentGuide.stepJwtCommand1`),copyText:e(`deploymentGuide.copy`),copiedText:e(`deploymentGuide.copied`)})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`3`}),e(`deploymentGuide.stepDeployTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:e(`deploymentGuide.stepDeployDesc`)}),(0,F.jsx)($,{code:e(`deploymentGuide.stepDeployCommand`),copyText:e(`deploymentGuide.copy`),copiedText:e(`deploymentGuide.copied`)})]})]})},zr=()=>{let{t:e}=h();return(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`1`}),e(`deploymentGuide.stepDbTitle`)]}),(0,F.jsx)(`div`,{className:`text-gray-600 dark:text-zinc-400 text-xs space-y-2 leading-relaxed mb-3`,children:e(`deploymentGuide.stepDbDashDesc`).split(`
|
|
15
|
+
`).map((e,t)=>(0,F.jsx)(`p`,{className:`m-0`,children:e},t))}),(0,F.jsxs)(`a`,{href:e(`deploymentGuide.stepJwtCommand2`),target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1.5 text-xs text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 font-bold transition duration-200`,children:[(0,F.jsx)(`span`,{children:e(`deploymentGuide.stepJwtDesc2`)}),(0,F.jsx)(te,{size:12})]})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`2`}),e(`deploymentGuide.stepJwtTitle`)]}),(0,F.jsx)(`div`,{className:`text-gray-600 dark:text-zinc-400 text-xs space-y-2 leading-relaxed mb-3`,children:e(`deploymentGuide.stepJwtDashDesc`).split(`
|
|
16
|
+
`).map((e,t)=>(0,F.jsx)(`p`,{className:`m-0`,children:e},t))}),(0,F.jsxs)(`a`,{href:e(`deploymentGuide.stepJwtCommand2`),target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1.5 text-xs text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 font-bold transition duration-200`,children:[(0,F.jsx)(`span`,{children:e(`deploymentGuide.stepJwtDesc2`)}),(0,F.jsx)(te,{size:12})]})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`3`}),e(`deploymentGuide.stepDeployTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-0 leading-relaxed`,children:e(`deploymentGuide.stepDeployDashDesc`)})]})]})};var Br=()=>{let e=new Uint32Array(64);window.crypto.getRandomValues(e);let t=``;for(let n=0;n<e.length;n++)t+=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`[e[n]%62];return t};const Vr=()=>{let{t:e}=h(),[t,n]=(0,P.useState)(Br),[r,i]=(0,P.useState)(!1);return(0,F.jsxs)(`div`,{className:`mb-6 border border-zinc-200 dark:border-zinc-800 rounded-xl p-4 bg-zinc-50 dark:bg-zinc-950/60 shadow-inner`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`w-2 h-2 rounded-full bg-emerald-500 animate-pulse`}),e(`deploymentGuide.jwtGeneratorTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-500 dark:text-zinc-400 text-xs mb-3`,children:e(`deploymentGuide.jwtGeneratorDesc`)}),(0,F.jsxs)(`div`,{className:`flex gap-2 items-center`,children:[(0,F.jsx)(`div`,{className:`flex-1 font-mono text-xs p-3 bg-zinc-900 dark:bg-black text-zinc-100 rounded-lg select-all overflow-x-auto border border-zinc-800/80 shadow-inner leading-relaxed break-all whitespace-nowrap scrollbar-thin`,children:t}),(0,F.jsxs)(`div`,{className:`flex flex-col sm:flex-row gap-1.5 shrink-0`,children:[(0,F.jsx)(D,{intent:r?k.SUCCESS:k.NONE,icon:r?`tick`:`duplicate`,text:e(r?`deploymentGuide.copied`:`deploymentGuide.copy`),onClick:async()=>{try{await navigator.clipboard.writeText(t),i(!0),setTimeout(()=>i(!1),2e3)}catch(e){console.error(`Failed to copy text:`,e)}},size:`small`}),(0,F.jsx)(D,{icon:`refresh`,text:e(`deploymentGuide.regenerate`),onClick:()=>{n(Br()),i(!1)},size:`small`})]})]})]})},Hr=({isDbMissing:e,isJwtSecretMissing:t})=>{let{t:n}=h();return(0,F.jsxs)(`div`,{className:`space-y-6`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`1`}),n(`deploymentGuide.serverfullStepInstallTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:n(`deploymentGuide.serverfullStepInstallDesc`)}),(0,F.jsx)($,{code:n(`deploymentGuide.serverfullStepInstallCmd`),copyText:n(`deploymentGuide.copy`),copiedText:n(`deploymentGuide.copied`)}),e&&(0,F.jsxs)(`p`,{className:`text-amber-600 dark:text-amber-400 text-xs mt-2 font-medium`,children:[`⚠️ `,n(`deploymentGuide.serverfullDbMissingHint`)]})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`2`}),n(`deploymentGuide.serverfullStepEnvTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:n(`deploymentGuide.serverfullStepEnvDesc`)}),(0,F.jsx)($,{code:n(`deploymentGuide.serverfullStepEnvCmd`),copyText:n(`deploymentGuide.copy`),copiedText:n(`deploymentGuide.copied`)}),t&&(0,F.jsxs)(`p`,{className:`text-blue-600 dark:text-blue-400 text-xs mt-2 font-medium`,children:[`💡 `,n(`deploymentGuide.serverfullJwtMissingHint`)]})]}),(0,F.jsxs)(`div`,{className:`border-t border-gray-100 dark:border-zinc-800 pt-4`,children:[(0,F.jsxs)(`h3`,{className:`text-sm font-bold text-gray-900 dark:text-zinc-100 mb-1 flex items-center gap-2 mt-0`,children:[(0,F.jsx)(`span`,{className:`flex items-center justify-center w-5 h-5 rounded bg-blue-500/10 text-blue-600 dark:text-blue-400 text-xs font-bold`,children:`3`}),n(`deploymentGuide.serverfullStepServiceTitle`)]}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs mb-2`,children:n(`deploymentGuide.serverfullStepServiceDesc`)}),(0,F.jsx)($,{code:n(`deploymentGuide.serverfullStepServiceCmd`),copyText:n(`deploymentGuide.copy`),copiedText:n(`deploymentGuide.copied`)})]})]})};function Ur(){return(0,P.useMemo)(()=>{let e=typeof window<`u`?window.DNS_WORKER_CONFIG:void 0,t=!!(e?.isServerfull||e?.mode===`serverfull`);return{mode:t?`serverfull`:`cloudflare`,isServerfull:t,isCloudflare:!t}},[])}const Wr=({size:e=`normal`,showIcon:t=!0,className:n})=>{let{isServerfull:r}=Ur(),{t:i}=h(),a=e===`small`;return(0,F.jsxs)(`div`,{className:Le(`inline-flex items-center gap-1.5 font-medium rounded-full transition-colors select-none`,a?`px-2 py-0.5 text-[11px]`:`px-3 py-1 text-xs`,r?`bg-purple-50 text-purple-700 border border-purple-200 dark:bg-purple-950/40 dark:text-purple-300 dark:border-purple-800`:`bg-blue-50 text-blue-700 border border-blue-200 dark:bg-blue-950/40 dark:text-blue-300 dark:border-blue-800`,n),title:r?`Self-Hosted / Node.js Serverfull`:`Cloudflare Workers Serverless`,children:[t&&(r?(0,F.jsx)(p,{size:a?12:14,className:`shrink-0 text-purple-600 dark:text-purple-400`}):(0,F.jsx)(de,{size:a?12:14,className:`shrink-0 text-blue-600 dark:text-blue-400`})),(0,F.jsx)(`span`,{children:i(r?`common.modeServerfull`:`common.modeCloudflare`)})]})},Gr=()=>{let{t:e}=h(),{isServerfull:t}=Ur(),[n,r]=(0,P.useState)(`cli`),[i,a]=(0,P.useState)(!0),o=window.DNS_WORKER_CONFIG?.isDbMissing??!0,s=window.DNS_WORKER_CONFIG?.isJwtSecretMissing??!0;return(0,F.jsxs)(`div`,{className:`flex flex-row min-h-screen bg-white dark:bg-gray-950 overflow-hidden relative`,children:[(0,F.jsx)(`div`,{className:`hidden lg:block w-1/2 h-screen overflow-hidden border-r border-gray-100 dark:border-gray-900`,children:(0,F.jsx)(Lr,{})}),(0,F.jsx)(`div`,{className:`lg:hidden absolute inset-0 z-0 transition-opacity duration-500 cursor-pointer ${i?`opacity-25`:`opacity-70`}`,onClick:()=>a(!0),children:(0,F.jsx)(Lr,{})}),!i&&(0,F.jsx)(`div`,{className:`lg:hidden fixed bottom-12 left-1/2 -translate-x-1/2 z-50 animate-bounce`,children:(0,F.jsx)(D,{large:!0,intent:`primary`,icon:`manual`,text:e(`deploymentGuide.title`),onClick:()=>a(!0),className:`shadow-2xl px-8 py-4 rounded-full`})}),(0,F.jsxs)(`div`,{className:`flex-1 flex items-center justify-center p-4 relative z-10 bg-gray-50/50 dark:bg-gray-900/30 lg:bg-gray-50 lg:dark:bg-gray-900/50 transition-all duration-500 ease-in-out ${i?`translate-x-0 opacity-100`:`max-lg:translate-x-full max-lg:opacity-0 max-lg:pointer-events-none`}`,onClick:e=>{window.innerWidth<1024&&e.target===e.currentTarget&&a(!1)},children:[(0,F.jsx)(`div`,{className:`absolute top-4 right-4 z-50`,children:(0,F.jsx)(Y,{})}),(0,F.jsx)(`div`,{className:`w-full max-w-2xl transition-all duration-200 ease-in-out transform-gpu`,children:(0,F.jsxs)(M,{elevation:Ce.FOUR,className:`w-full max-h-[90vh] flex flex-col p-6 md:p-8 rounded-2xl border border-gray-100 dark:border-gray-800 dark:bg-gray-900 shadow-xl`,onClick:e=>e.stopPropagation(),children:[(0,F.jsxs)(`div`,{className:`flex items-center justify-between gap-4 mb-6 shrink-0`,children:[(0,F.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,F.jsx)(`img`,{src:X,alt:`DNS Worker Logo`,className:`w-10 h-10 object-contain rounded-xl shadow-lg shadow-blue-500/10`}),(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h1`,{className:`text-xl font-extrabold text-gray-900 dark:text-zinc-50 tracking-tight m-0`,children:`DNS Worker`}),(0,F.jsx)(`p`,{className:`text-xs text-gray-500 dark:text-zinc-400 font-medium m-0 mt-0.5`,children:`Advanced Agentic DNS Server`})]})]}),(0,F.jsx)(Wr,{size:`small`})]}),(0,F.jsxs)(`div`,{className:`flex-1 overflow-y-auto pr-1.5 min-h-0 space-y-6 scrollbar-thin`,children:[(0,F.jsxs)(`div`,{children:[(0,F.jsx)(`h2`,{className:`text-lg md:text-xl font-bold text-gray-900 dark:text-zinc-50 mb-1 mt-0`,children:e(t?`deploymentGuide.serverfullTitle`:`deploymentGuide.title`)}),(0,F.jsx)(`p`,{className:`text-gray-600 dark:text-zinc-400 text-xs md:text-sm leading-relaxed m-0`,children:e(t?`deploymentGuide.serverfullSubtitle`:`deploymentGuide.subtitle`)})]}),(0,F.jsxs)(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 gap-4`,children:[(0,F.jsxs)(Te,{intent:o?k.DANGER:k.SUCCESS,icon:o?`warning-sign`:`tick`,title:e(t?`deploymentGuide.serverfullDbStatus`:`deploymentGuide.dbStatus`),className:`flex-1`,children:[(0,F.jsx)(`span`,{className:`font-semibold text-xs`,children:e(t?o?`deploymentGuide.serverfullDbMissing`:`deploymentGuide.serverfullDbConfigured`:o?`deploymentGuide.missing`:`deploymentGuide.configured`)}),t&&o&&(0,F.jsx)(`p`,{className:`text-xs mt-1.5 opacity-90 leading-normal`,children:e(`deploymentGuide.serverfullDbMissingHint`)})]}),(0,F.jsxs)(Te,{intent:s?k.DANGER:k.SUCCESS,icon:s?`warning-sign`:`tick`,title:e(t?`deploymentGuide.serverfullJwtStatus`:`deploymentGuide.jwtStatus`),className:`flex-1`,children:[(0,F.jsx)(`span`,{className:`font-semibold text-xs`,children:e(t?s?`deploymentGuide.serverfullJwtMissing`:`deploymentGuide.serverfullJwtConfigured`:s?`deploymentGuide.missing`:`deploymentGuide.configured`)}),t&&s&&(0,F.jsx)(`p`,{className:`text-xs mt-1.5 opacity-90 leading-normal`,children:e(`deploymentGuide.serverfullJwtMissingHint`)})]})]}),(0,F.jsx)(Vr,{}),t?(0,F.jsx)(`div`,{className:`pt-2`,children:(0,F.jsx)(Hr,{isDbMissing:o,isJwtSecretMissing:s})}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`div`,{children:(0,F.jsxs)(je,{id:`deploy-methods`,selectedTabId:n,onChange:e=>r(e),className:`border-b border-gray-200 dark:border-zinc-800 pb-2`,children:[(0,F.jsx)(ke,{id:`cli`,title:(0,F.jsxs)(`div`,{className:`flex items-center gap-2 font-bold py-1.5 px-1`,children:[(0,F.jsx)(pe,{size:14}),(0,F.jsx)(`span`,{children:e(`deploymentGuide.methodCli`)})]})}),(0,F.jsx)(ke,{id:`dash`,title:(0,F.jsxs)(`div`,{className:`flex items-center gap-2 font-bold py-1.5 px-1`,children:[(0,F.jsx)(de,{size:14}),(0,F.jsx)(`span`,{children:e(`deploymentGuide.methodDash`)})]})})]})}),(0,F.jsx)(`div`,{className:`space-y-6 pb-2`,children:n===`cli`?(0,F.jsx)(Rr,{}):(0,F.jsx)(zr,{})})]})]}),(0,F.jsx)(`div`,{className:`flex items-center justify-end border-t border-gray-200 dark:border-zinc-800 pt-4 shrink-0 mt-4`,children:(0,F.jsx)(D,{large:!0,intent:k.PRIMARY,icon:(0,F.jsx)(g,{size:14,className:`animate-spin-hover`}),text:e(`deploymentGuide.refresh`),onClick:()=>{window.location.reload()}})})]})})]})]})};var Kr=I(()=>T(()=>import(`./AuthView-qoUKd5PS.js`),__vite__mapDeps([16,1,2,3,4,5,6,7,17,18])).then(e=>({default:e.AuthView}))),qr=I(()=>T(()=>import(`./AccountView-p5jSjMAE.js`),__vite__mapDeps([19,1,2,3,4,5,6,7,17,18])).then(e=>({default:e.AccountView})));function Jr(){let[e,t]=(0,P.useState)(!0),r=u(),i=ae(),a=(0,P.useRef)(null),{i18n:o}=h(),{theme:s,setTheme:c}=Ar(),l=jr(),{isLoggedIn:d,currentUser:p,checkAuth:g,handleLogout:_}=Mr(a),{profiles:v,selectedProfile:y,setSelectedProfile:b,showCreateDialog:x,setShowCreateDialog:S,newProfileName:ee,setNewProfileName:te,createError:C,prefilledRule:ne,setPrefilledRule:re,fetchProfiles:ie,handleCreateProfile:oe,handleDeleteProfile:se,handleQuickAction:ce}=Nr(d),[le,ue]=(0,P.useState)(!1),[de,fe]=(0,P.useState)(!1);if((0,P.useEffect)(()=>{Ve(o.language).finally(()=>{fe(!0)})},[o.language]),!de)return(0,F.jsx)(`div`,{className:`h-screen flex items-center justify-center`,children:(0,F.jsx)(O,{size:50})});let pe=window.DNS_WORKER_CONFIG?.isDbMissing,me=window.DNS_WORKER_CONFIG?.isJwtSecretMissing;return pe||me?(0,F.jsx)(Gr,{}):d===null?(0,F.jsxs)(`div`,{className:`h-screen flex items-center justify-center`,children:[(0,F.jsx)(Ue,{}),(0,F.jsx)(O,{size:50})]}):d?(0,F.jsxs)(P.Suspense,{fallback:(0,F.jsx)(`div`,{className:`h-screen flex items-center justify-center`,children:(0,F.jsx)(O,{size:50})}),children:[(0,F.jsxs)(m,{children:[(0,F.jsx)(`title`,{children:l.title}),(0,F.jsx)(`meta`,{name:`description`,content:l.description}),(0,F.jsx)(`html`,{lang:o.language})]}),(0,F.jsx)(Ue,{}),(0,F.jsx)(Pe,{position:`bottom`,ref:a}),(0,F.jsx)(Fr,{currentUser:p,handleLogout:_,onUnlock:ie,children:(0,F.jsxs)(n,{children:[(0,F.jsx)(w,{path:`/`,element:(0,F.jsx)(f,{to:`/dash`,replace:!0})}),(0,F.jsx)(w,{path:`/dash`,element:(0,F.jsx)(Tr,{profiles:v,onSelect:e=>{b(e),r(`/dash/${e.id}/setup`)},onCreate:oe,showCreate:x,setShowCreate:S,newName:ee,setNewName:te,error:C,onDelete:se,handleLogout:_,navigate:r,onRefresh:ie,currentUser:p})}),(0,F.jsx)(w,{path:`/dash/:profileId/*`,element:(0,F.jsx)(kr,{isSidebarOpen:e,setIsSidebarOpen:t,theme:s,setTheme:c,selectedProfile:y,profiles:v,setSelectedProfile:b,location:i,navigate:r,handleLogout:_,currentUser:p,isSaving:le,children:(0,F.jsx)(et,{selectedProfile:y,prefilledRule:ne,setPrefilledRule:re,handleQuickAction:ce,toasterRef:a,currentUser:p,onSavingChange:ue})})}),(0,F.jsx)(w,{path:`/account`,element:(0,F.jsx)(kr,{isSidebarOpen:e,setIsSidebarOpen:t,theme:s,setTheme:c,selectedProfile:y,profiles:v,setSelectedProfile:b,location:i,navigate:r,handleLogout:_,currentUser:p,children:(0,F.jsx)(qr,{})})}),(0,F.jsx)(w,{path:`*`,element:(0,F.jsx)(Ge,{})})]})})]}):(0,F.jsxs)(P.Suspense,{fallback:(0,F.jsx)(`div`,{className:`h-screen flex items-center justify-center`,children:(0,F.jsx)(O,{size:50})}),children:[(0,F.jsxs)(m,{children:[(0,F.jsx)(`title`,{children:l.title}),(0,F.jsx)(`meta`,{name:`description`,content:l.description}),(0,F.jsx)(`html`,{lang:o.language})]}),(0,F.jsx)(Ue,{}),(0,F.jsx)(Pe,{position:`bottom`,ref:a}),(0,F.jsx)(Kr,{onSuccess:g})]})}var Yr=Jr;_e.setLoaderOptions({loader:`split-by-size`}),we.onlyShowFocusOnTabs(),_e.loadAll();var Xr=!1,Zr=`unknown`,Qr=[];function $r(e){Qr.push(e)}function ei(e){Qr.forEach(t=>t(e)),Qr=[]}var ti=window.fetch;window.fetch=async function(e,t){let n=``;typeof e==`string`?n=e:e instanceof URL?n=e.href:e&&typeof e==`object`&&`url`in e&&(n=e.url);let r=n.startsWith(`/api/`)||n.includes(window.location.host+`/api/`);if(r){t||={};let e=new Headers(t.headers),r=t.method?.toUpperCase()||`GET`;if([`POST`,`PUT`,`PATCH`,`DELETE`].includes(r)){let t=document.cookie.match(/(?:^|;\s*)csrf_token=([^;]*)/),n=t?t[1]:null;n&&e.set(`X-CSRF-Token`,n)}let i=Un();i&&!n.includes(`/api/auth/refresh`)&&e.set(`Authorization`,`Bearer ${i}`),t.headers=e}let i=await ti(e,t);if(r&&i.status===403&&!n.includes(`/api/auth/`))try{await i.clone().text()===`session_paused`&&window.dispatchEvent(new Event(`session_paused`))}catch{}if(r&&i.status===401&&(!n.includes(`/api/auth/`)||n.includes(`/api/auth/unlock-session`)||n.includes(`/api/auth/lock-session`))){Xr||(Xr=!0,z().then(e=>{q(e.accessToken),ei(e.accessToken)}).catch(e=>{q(null);let t=`unknown`;if(e instanceof L)if(e.bodyText===`Refresh token missing`)t=`missing`;else try{let n=JSON.parse(e.bodyText);n&&n.reason&&(t=n.reason)}catch{}else t=`network_error`;Zr=t,ei(``)}).finally(()=>{Xr=!1}));let n=await new Promise(e=>{$r(e)});if(n){let r=new Headers(t?.headers);r.set(`Authorization`,`Bearer ${n}`),i=await ti(e,{...t,headers:r})}else window.dispatchEvent(new CustomEvent(`auth_unauthorized`,{detail:{reason:Zr}}))}return i},typeof window<`u`&&`caches`in window&&(sessionStorage.getItem(`dns_worker_session_active`)||(caches.delete(`dns-worker-logs-v1`).catch(()=>{}),sessionStorage.setItem(`dns_worker_session_active`,`true`))),(0,Re.createRoot)(document.getElementById(`root`)).render((0,F.jsx)(Ae,{children:(0,F.jsx)(a,{children:(0,F.jsx)(i,{children:(0,F.jsx)(Yr,{})})})}));export{Ht as $,nt as $t,zn as A,pt as At,xn as B,kt as Bt,Y as C,At as Ct,Wn as D,Tt as Dt,Jn as E,Ft as Et,Tn as F,Ot as Ft,vn as G,Dt as Gt,yn as H,It as Ht,kn as I,Pt as It,an as J,Nt as Jt,Sn as K,gt as Kt,hn as L,yt as Lt,En as M,Mt,Dn as N,jt as Nt,q as O,bt as Ot,On as P,_t as Pt,Kt as Q,L as Qt,wn as R,vt as Rt,X as S,St,Yn as T,ht as Tt,gn as U,Lt as Ut,bn as V,Ct as Vt,Cn as W,mt as Wt,Qt as X,ft as Xt,en as Y,ut as Yt,$t as Z,dt as Zt,mr as _,Xt as _t,ar as a,rt as an,rn as at,Sr as b,tn as bt,ir as c,ct as cn,Gt as ct,pr as d,Zt as dt,tt as en,Yt as et,gr as f,Bt as ft,dr as g,cn as gt,yr as h,sn as ht,Pr as i,ot as in,un as it,K as j,Rt as jt,Vn as k,xt as kt,nr as l,We as ln,dn as lt,hr as m,mn as mt,Ur as n,lt as nn,on as nt,rr as o,z as on,fn as ot,br as p,H as pt,Jt as q,Et as qt,Lr as r,it as rn,nn as rt,sr as s,at as sn,Vt as st,Wr as t,st as tn,Ut as tt,vr as u,ln as ut,cr as v,pn as vt,Zn as w,wt,lr as x,Wt as xt,ur as y,qt as yt,_n as z,zt};
|