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.
Files changed (51) hide show
  1. package/README.md +3 -3
  2. package/dist/serverfull.mjs +73 -69
  3. package/dist/serverfull.mjs.map +4 -4
  4. package/package.json +3 -1
  5. package/static/assets/AccountView-p5jSjMAE.js +4 -0
  6. package/static/assets/{AnalyticsView-C_g_jz14.js → AnalyticsView-BywwDzah.js} +2 -2
  7. package/static/assets/AuthView-qoUKd5PS.js +2 -0
  8. package/static/assets/{DestinationMap-jSW7gimP.js → DestinationMap-9Yc54Mdj.js} +1 -1
  9. package/static/assets/FilteringView-Bs3__NpV.js +1 -0
  10. package/static/assets/LogsView-B-U9oaxB.js +5 -0
  11. package/static/assets/{QRCodeCanvas-Cu_-aNyM.js → QRCodeCanvas-DQ-xNuwF.js} +2 -2
  12. package/static/assets/{RulesView-CfVeQv0-.js → RulesView-B2kL1QhI.js} +1 -1
  13. package/static/assets/SettingsView-DTGI6eSd.js +1 -0
  14. package/static/assets/SetupView-DE9MM-32.js +1 -0
  15. package/static/assets/{SwipeableDrawer-BrmEzhuP.js → SwipeableDrawer-B81ANpsc.js} +1 -1
  16. package/static/assets/{TrendChart-Dc5yJndv.js → TrendChart-CCR1W3fC.js} +3 -3
  17. package/static/assets/browser-Dc5iKjY4.js +1 -0
  18. package/static/assets/date-CpvuBM37.js +1 -0
  19. package/static/assets/{de-DE-CA3VPcoz.js → de-DE-kDnumQOF.js} +1 -1
  20. package/static/assets/{es-ES-CHvRWwd-.js → es-ES-CqKS7vHl.js} +1 -1
  21. package/static/assets/{fr-FR-CqGOuepx.js → fr-FR-BRcdpT35.js} +1 -1
  22. package/static/assets/getFlagEmoji-DRubsb7D.js +1 -0
  23. package/static/assets/index-Bheo8y3N.css +2 -0
  24. package/static/assets/index-dXN_pYn1.js +16 -0
  25. package/static/assets/{ja-JP-BJSbUSmh.js → ja-JP-C5jk6SuH.js} +1 -1
  26. package/static/assets/{ko-KR-Chzg6jwO.js → ko-KR-z38NtMFa.js} +1 -1
  27. package/static/assets/{ru-RU-CSyCGGPn.js → ru-RU-Bicx52lz.js} +1 -1
  28. package/static/assets/services-BVnEJBDq.js +1 -0
  29. package/static/assets/vendor-i18next-BKUzkAKP.js +1 -0
  30. package/static/assets/vendor-react-BmoHsW1x.js +11 -0
  31. package/static/assets/{vendor-ui-core-sfZn2C08.js → vendor-ui-core-CY_WyMIf.js} +1 -1
  32. package/static/assets/{vendor-utils-CnB7BG7m.js → vendor-utils-BWSB0jGn.js} +1 -1
  33. package/static/assets/{vi-VN-D7rcA1IX.js → vi-VN-CvS_mTAu.js} +1 -1
  34. package/static/assets/{zh-CN-CSPdaOkF.js → zh-CN-B0nRgJTD.js} +1 -1
  35. package/static/assets/{zh-TW-DETA1X3X.js → zh-TW-lc_SSoVI.js} +1 -1
  36. package/static/index.html +6 -6
  37. package/static/sw.js +1 -1
  38. package/static/assets/AccountView-CGRwz6ae.js +0 -4
  39. package/static/assets/AuthView-BQzmEyvO.js +0 -2
  40. package/static/assets/FilteringView-CQ7sX3Rp.js +0 -1
  41. package/static/assets/LogsView-eLfZMRVo.js +0 -5
  42. package/static/assets/SettingsView-agJiYEt5.js +0 -1
  43. package/static/assets/SetupView-CTgCUAgk.js +0 -1
  44. package/static/assets/browser-DPi2BX_1.js +0 -1
  45. package/static/assets/date-IKj2stuv.js +0 -1
  46. package/static/assets/getFlagEmoji-_Wxhq0lT.js +0 -1
  47. package/static/assets/index-919BqnYI.css +0 -2
  48. package/static/assets/index-CbTjGMTZ.js +0 -16
  49. package/static/assets/services-Cwvmit1q.js +0 -1
  50. package/static/assets/vendor-i18next-cO7FDf_e.js +0 -1
  51. package/static/assets/vendor-react-CHk5ngkq.js +0 -11
@@ -1,4 +1,4 @@
1
- const e={account:`帳號`,logout:`登出`,selectProfile:`選擇配置`,add:`添加`,newProfileName:`新配置名稱...`,profileNameFormatTip:`配置名稱提示:1-30個字元,且不能與已有配置重名`,profileLimitExceeded:`配置數量已達上限(最多{{max}}個)`,profileNameExists:`該配置名稱已存在`,profileNameFormatError:`配置名稱格式不正確`,create:`創建`,welcome:`歡迎`,createProfileToStart:`創建一個配置以開始。`,loading:`載入中...`,errorNetwork:`網路錯誤`,confirmDelete:`確定要刪除此配置嗎?`,confirmLogout:`確定注銷登入嗎?`,notFound:`404 - 頁面未找到`,notFoundDesc:`您嘗試訪問的頁面不存在或已被移除。`,backToHome:`返回首頁`,apac:`亞太`,mainlandChina:`中國大陸`,HongKong:`香港`,Singapore:`新加坡`,Tokyo:`東京`,NorthAmerica:`北美`,nae:`北美東部`,naw:`北美西部`,eu:`歐洲`,invalidFormat:`無效文件格式`,import:`匯入`,importSuccess:`配置成功匯入`,importError:`配置匯入失敗,請確保格式正確且稍後再試`,save:`儲存`,saveSuccess:`設定已儲存`,close:`關閉`,cancel:`取消`,continue:`繼續`,back:`返回`,copy:`複製`,copied:`已複製`,copyKey:`複製新恢復密鑰`,confirm:`確認`,delete:`刪除`,rename:`重新命名`,disable:`停用`,confirmDisable:`確認停用`,modeServerfull:`Serverfull 模式`,modeCloudflare:`Cloudflare Worker 模式`},t={setup:`安裝`,rules:`規則`,stats:`統計`,logs:`日誌`,filter:`過濾項`,settings:`設定`},n={light:`淺色`,dark:`深色`,system:`跟隨系統`},r={keepLoggedIn:`保持登入 {{days}} 天`,login:`登入 DNS Worker`,signup:`創建帳戶以開始`,welcomeBack:`歡迎回來,請輸入憑據`,protectInternet:`保護您的網際網路第一跳`,error:`錯誤`,username:`用戶名`,usernamePlaceholder:`您的用戶名`,password:`密碼`,passwordPlaceholder:`您的密碼`,loginBtn:`登入`,signupBtn:`註冊`,totpVerification:`TOTP 驗證`,noAccount:`沒有帳戶?立即註冊`,haveAccount:`已有帳戶?返回登入`,formatTipUsername:`用戶名格式提示:僅限 5 到 32 位 Unix 用戶名格式(小寫字母或底線開頭,包含小寫字母、數字、底線及中線)`,formatTipPassword:`密碼格式提示:12-100位字元`,authFailed:`認證失敗,請檢查用戶名或密碼`,networkError:`網路錯誤,請稍後再試`,turnstileRequired:`請完成人機身份驗證`,turnstileError:`驗證服務加載失敗,請檢查 Site Key 或域名配置`,verifying:`正在進行安全驗證...`,passwordLeaked:`您的密碼已被洩露,為了您的帳戶安全,請使用隨機密碼/強密碼。`,usernameExists:`該用戶名已被註冊,請嘗試其他用戶名。`,skip:`跳過`,showPassword:`顯示密碼`,hidePassword:`隱藏密碼`,geolocationRequired:`地理資訊獲取失敗,請重新進入。`,unauthorizedDefault:`您的登入會話已失效,請重新登入。`,unauthorizedGeo:`檢測到地理位置變更(超出距離限制),當前會話已被強制註銷。`,unauthorizedExpired:`您的登入已過期,請重新登入。`,unauthorizedReuse:`安全警報:檢測到重複登入,會話已終止。`,unauthorizedRevoked:`您的會話已被註銷或刪除,請重新登入。`,sessionLockTitle:`空閒會話鎖定`,sessionLockDesc:`當應用程式空閒時,使用 4 位數字 PIN 鎖定介面,以防止未授權的物理存取來保護活動會話。`,enableSessionLock:`啟用會話鎖定`,inactivityTimeout:`空閒逾時時間`,timeout1m:`1 分鐘`,timeout2m:`2 分鐘`,timeout5m:`5 分鐘`,timeout15m:`15 分鐘`,timeout30m:`30 分鐘`,timeout60m:`60 分鐘`,changePin:`修改 PIN 碼`,disablePin:`停用會話鎖定`,configurePin:`設定 PIN 碼`,newPin:`新 4 位 PIN 碼`,pinHelper:`僅限數字,例如:1234`,confirmPin:`確認新 PIN 碼`,verifyIdentity:`驗證身份`,usePassword:`使用密碼驗證`,use2fa:`使用 TOTP 驗證碼`,totpCode:`TOTP 驗證碼`,currentPassword:`目前密碼`,disablePinWarning:`停用 PIN 碼將徹底關閉空閒會话鎖定功能。`,pinFormatTip:`PIN 碼必須為 4 位數字`,pinMatchTip:`兩次輸入的 PIN 碼不符合`,passwordRequired:`需要輸入密碼以驗證身份`,sessionLocked:`由於長時間未活動,會話已鎖定`,sessionExpired:`嘗試次數過多,會話已終結。`,pinIncorrect:`PIN 碼錯誤`,attemptsRemaining:`還剩 {{count}} 次嘗試機會`,logoutAndReturn:`登出會話`,lockNow:`立即鎖定`,cfErrorWithDetails:`Cloudflare 邊緣節點異常 (錯誤代碼 {{code}}: {{desc}})。服務暫時不可用,請稍後重試。{{rayId}}`,cfErrorCodeOnly:`Cloudflare 邊緣服務異常 (錯誤代碼 {{code}})。服務暫時不可用,請稍後重試。{{rayId}}`,cfErrorGeneric:`Cloudflare 邊緣服務異常,服務暫時不可用,請稍後重試。{{rayId}}`,htmlErrorGeneric:`伺服器傳回異常網頁回應 (HTTP {{status}}),請稍後重試。`,tooManyAttempts:`請求過於頻繁或嘗試次數過多,已被臨時限制,請稍後再試。`,dbQuotaExceeded:`資料庫每日免費讀取配額已用盡,請等待次日自動重置或升級配額。`,noUsersRegistered:`系統尚無帳號,請註冊成為管理員。`,registerAdminTitle:`註冊管理員帳號`,registerAdminDesc:`系統尚無帳號,首位註冊用戶將自動成為系統管理員。`,goToRegisterAdmin:`註冊管理員`,dbNotInitialized:`資料庫未初始化或缺少資料表,請在終端執行 npm run db:migrate:prod 套用資料庫遷移。`,verifyWithPasskey:`使用通行密鑰驗證`,orUseTotp:`或使用數字驗證器`,passwordRequiredFirst:`請先輸入密碼`,passkeyVerification:`通行密鑰驗證`,passkeyPromptDesc:`使用已註冊的裝置生物識別或硬體安全金鑰進行驗證`,forgotPassword:`忘記密碼?`,forgotPasswordTitle:`重設密碼`,forgotPasswordDesc:`透過多因素驗證(通行金鑰或數位驗證碼)安全找回您的帳號。`,noMfaContactAdmin:`該帳號未設定任何多因素驗證(Passkey 或 TOTP),無法自助重設密碼。請聯絡系統管理員為您重設密碼。`,chooseMfaForReset:`請選擇一種驗證方式以核驗您的身分:`,authViaPasskey:`使用通行金鑰驗證`,authViaPasskeyDesc:`使用指紋、臉孔或硬體安全金鑰一鍵完成驗證`,authViaTotp:`使用數位驗證器 (TOTP)`,authViaTotpDesc:`輸入身分驗證器應用程式產生的 6 位動態驗證碼`,authViaRecoveryKey:`使用緊急復原金鑰`,authViaRecoveryKeyDesc:`輸入 10 位緊急復原金鑰`,verifyAndProceed:`核驗並繼續`,setNewPassword:`設定新密碼`,setNewPasswordDesc:`身分核驗成功,請為您的帳號設定一個新密碼。`,newPassword:`新密碼`,confirmNewPassword:`確認新密碼`,passwordMismatch:`兩次輸入的新密碼不相符`,saveAndReset:`儲存並重設密碼`,resetSuccessTitle:`密碼重設成功`,resetSuccessDesc:`您的密碼已更新,所有現有登入工作階段已登出。請使用新密碼重新登入。`,goToLogin:`前往登入`,mfaSetupTitle:`強化帳號安全`,mfaSetupDesc:`設定多因素驗證 (MFA) 可有效保護您的帳號,並解鎖無密碼快速登入與密碼自助找回功能。`,mfaRecommended:`推薦`,passkeySignupDesc:`使用指紋、臉孔或安全金鑰,免去繁瑣輸入,兼顧極速體驗與頂級安全防護。`,bindPasskeyNow:`立即綁定通行金鑰`,mfaUniversal:`通用`,totpSignupDesc:`使用 Google Authenticator、1Password 等身分驗證器產生 6 位動態驗證碼。`,setupTotpBtn:`設定數位驗證器`,skipMfaSetup:`暫不設定,直接完成`,backToMfaOptions:`返回選擇其他方式`,otherOptions:`其他選項`,next:`下一步`,mfaVerification:`多因素認證`,totpPrompt:`請輸入身份驗證應用程式中的 6 位動態驗證碼`,recoveryPrompt:`請輸入 30 位應急恢復密鑰(5 組,每組 6 位數字)`,recoveryChecksumError:`校驗碼錯誤:該組 6 位數無法被 11 整除`,recoveryChecksumErrorDesc:`第 {{groups}} 組校驗碼錯誤,請核對後重試`,backToPasskey:`返回通行密鑰驗證`,recoveryKeyRequired:`請輸入應急恢復密鑰`,totpFormatError:`請輸入有效的 6 位驗證碼`,registrationDisabledTitle:`註冊已停用`,registrationDisabledDesc:`目前系統已暫停開放新使用者註冊,請聯絡系統管理員取得帳號。`,backToLogin:`返回登入`,jwtSecretPreset:`檢測到正在使用預設的 JWT_SECRET,請在環境變數中修改並替換為自訂安全金鑰後重新部署。`,jwtSecretMissing:`缺少 JWT_SECRET,請在環境變數中配置安全金鑰後重新部署。`,jwtSecretInvalid:`JWT_SECRET 金鑰格式無效,請檢查設定。`,orUseOtherMfa:`或使用其他驗證方式`,totpUseRecovery:`使用緊急恢復密鑰`,totpUseApp:`使用驗證器 App`,mfaMethodTotp:`數位驗證器 (TOTP)`,invalidTotp:`TOTP 驗證碼無效,請重試`},i={title:`安裝指南`,subtitle:`配置裝置以使用 DNS Worker 主掌您的網際網路安全。`,regionOptimization:`接入地區優化 (Windows)`,otherRegion:`其他地區`,copied:`已複製到剪貼簿`,verifyConnection:`驗證您的連接`,verifying:`正在驗證...`,connected:`連接正常`,notConnected:`或未連接`,profileMatch:`您正透過此配置進行過濾。`,profileMismatch:`您正使用 Obex,但配置 ID 不匹配。`,verifyHint:`完成下方配置後,點擊按鈕驗證。`,refreshStatus:`重新整理狀態`,egressIp:`出口 IP 地址`,currentLocation:`當前位置`,edgeServer:`邊緣伺服器`,serverLocation:`伺服器位置`,dohUrlTitle:`當前配置 DoH URL`,accessPointTitle:`Access DoH URL`,defaultAccessPointName:`裝置-1`,moreAccessPoints:`更多存取點 ↗`,accessPointName:`存取點名稱`,accessPointNameFormatTip:`存取點名稱提示:1-30 位字元,只允許字母、數字、下劃線和中劃線`,accessPointLimitExceeded:`存取點數量已達上限(最多{{max}}個)`,accessPointToken:`訪問密鑰`,addAccessPoint:`新增存取點`,renameAccessPoint:`重命名`,rotateToken:`輪換密鑰`,rotateTokenConfirmDesc:`輪換密鑰將使所有使用舊密鑰的裝置立即斷開連接。確定要繼續嗎?`,deleteAccessPoint:`刪除`,deleteAccessPointConfirm:`確定要刪除此存取點嗎?`,manageAccessPoints:`管理存取點`,searchAccessPoints:`搜尋存取點...`,accessPointNameExists:`存取點名稱已存在`,accessPointCreated:`存取點創建成功`,accessPointRenamed:`存取點重命名成功`,accessPointDeleted:`存取點刪除成功`,tokenRotated:`訪問密鑰輪換成功`,whatIsDoh:`何為 DoH?`,dohDesc:`DNS over HTTPS (DoH) 是一種透過加密的 HTTPS 協議進行域名解析技術。`,dohBenefit1:`減少營運商等網路中間人的劫持與監聽`,dohBenefit2:`提升域名解析及網際網路訪問安全性`,learnMore:`瞭解更多`,copyUrl:`複製 URL`,browsers:`瀏覽器`,browserTitle:`瀏覽器配置 (Chrome/Edge)`,browserSteps:`設定 > 隱私和安全 > 安全 > 使用安全 DNS > 自訂 > 粘貼 URL。`,browserTips:`或者將此網址複製到地址欄:`,apple:`Apple`,appleTitle:`macOS / iOS / iPadOS / tvOS`,appleDesc:`Apple 裝置支援透過設定描述檔快速配置。`,appleWarning:`設定描述檔不可在 "封閉模式 (Lockdown Mode)" 下安裝,安裝前請確認系統不處於相關狀態。`,downloadConfig:`下載設定描述檔`,appleInstallHint:`下載後請前往「設定」>「已下載的設定描述檔」進行安裝`,windows:`MS Windows`,windowsTitle:`Windows 11 設定`,windowsStep0:`設定 > 網路與網際網路`,windowsStep1:`屬性 > DNS 伺服器 > 編輯 > 手動。`,windowsStep2:`選擇並填入 DNS IP:`,windowsStep3:`啟用 DoH (手動模板) 並粘貼 URL。`,windowsStep4:`關閉 回退到明文。`,android:`Android`,androidTitle:`Android / 私有 DNS (DoT & DoQ)`,androidDesc:`支援系統原生的「私有 DNS (DoT)」或使用第三方 App 設定 DoH。`,androidDotTitle:`私有 DNS (DoT / DoQ)`,androidDotDesc:`設定 > 網路和網際網路 > 私有 DNS > 提供者主機名 (亦支援各類現代 DoQ 客戶端):`,androidWildcardHint:`(通配符證書,路由至此配置)`,androidSingleDomainHint:`(單域名證書,路由至默認配置)`,androidIpNotice:`檢測到當前通過 IP 或本地地址訪問面板。Android 原生私有 DNS 僅接受域名,請在 .env 中配置 SERVERFULL_DOT_DOMAIN (例如 dns.example.com) 後刷新。`,androidWarning:`註:系統原生的「私有 DNS」使用 DoT 協議。若部署在 Cloudflare Workers,建議使用 Intra 等 App 設定 DoH;若運行在 Serverfull 模式並配置了 TLS 證書,可直接使用上述 DoT 主機名。`,dynamicFromDomain:`動態 (來自 "{{domain}}")`,dynamicFromDomainV6:`動態 IPv6 (來自 "{{domain}}")`,linux:`Linux`,linuxTitle:`Linux (Cloudflared DoH 代理)`,linuxDesc:`在本地執行輕量級 DNS 代理,透過 DoH 將解析請求加密轉發至 Obex。`,linuxStep1:`執行以下腳本安裝 cloudflared 並建立 systemd 服務(監聽 127.0.0.1:53)。`,linuxStep2:`將系統 DNS 修改為 127.0.0.1。`,routeros:`MikroTik`,routerosTitle:`MikroTik RouterOS`,routerosDesc:`將其設定為路由器的 DoH 伺服器,即可讓網路中的所有裝置使用此解析器。`,routerosStep1:`在 RouterOS 終端機中執行(RouterOS 7):`,routerosNote:`經路由器轉發的查詢在日誌中會顯示為路由器,而非個別裝置。如需依裝置查看日誌,請直接在裝置上設定。`,routerosDocs:`MikroTik DNS 文件`,accessPointsCardTitle:`存取點`,currentAccessPoint:`目前存取點`,selectAccessPointToConfigure:`選擇存取点以設定`,quic:`QUIC & HTTP/3`,quicTitle:`DNS over QUIC (DoQ) & HTTP/3 (DoH3)`,quicDesc:`原生支援 RFC 9250 (DNS over QUIC) 與 RFC 9460 (HTTP/3 & Encrypted Client Hello),具備 0-RTT 極速交握與防 SNI 窺探能力。`,doqTitle:`DNS over QUIC (DoQ - RFC 9250)`,doqAppHint:`適用於 AdGuard、Clash Meta (Mihomo)、Sing-box 等現代用戶端:`,doqWildcardHint:`(萬用字元憑證,路由至此設定)`,doqSingleDomainHint:`(單一網域憑證,路由至預設設定)`,doqIpNotice:`DoQ 需要 TLS 憑證與有效網域,請在設定中填入 SERVERFULL_DOT_DOMAIN 後重新整理。`,h3Title:`HTTP/3 (DoH3) & ECH (Encrypted Client Hello)`,h3Desc:`支援現代瀏覽器 (Chrome/Firefox/Safari) 透過 Alt-Svc 及 RFC 9460 HTTPS 記錄自動協商 HTTP/3 和 ECH 加密:`,ddrHint:`服務已整合 RFC 9460 Section 8 DDR (Discovery of Designated Resolvers),支援用戶端透過 _dns 網域查詢自動協商並升級至 DoQ / DoT 與 ECH。`,e2eeDesc:`使用專屬後量子混合金鑰 (P256-MLKEM768) 在邊緣即刻加密 DNS 日誌。雲端無法窺視,資料僅在通過您的硬體 Passkey 授權的客戶端本地解密並分析。`,detecting:`正在檢測...`,dotUnavailableCfTitle:`DoT / DoQ 在 Cloudflare Worker 模式下無法使用`,dotUnavailableCfDesc:`目前運行於 Cloudflare Worker (Serverless) 模式。受邊緣無伺服器執行環境網路限制,不支援監聽原生 TCP/UDP 853 連接埠。若需使用 DoT 或 DoQ,請部署在獨立伺服器 (Serverfull 模式) 下;在目前模式下請使用 DoH (DNS over HTTPS)。`},a={title:`自訂規則`,subtitle:`手動控制特定域名的解析行為(屏蔽、放行或重新導向)。`,action:`行動`,typeBlock:`攔截 (BLOCK)`,typeAllow:`放行 (ALLOW)`,typeRedirect:`重新導向 (REDIRECT)`,pattern:`域名模式 (Pattern)`,patternPlaceholder:`example.com 或 *.test.com`,addRule:`添加規則`,redirectSettings:`重新導向記錄設定`,aRecord:`A 記錄 (IPv4)`,aaaaRecord:`AAAA 記錄 (IPv6)`,cnameRecord:`CNAME 記錄`,txtRecord:`TXT 記錄`,optional:`(選填)`,ipv4Placeholder:`例如: 1.2.3.4`,ipv6Placeholder:`例如: 2001:db8::1`,cnamePlaceholder:`例如: target.com`,txtPlaceholder:`輸入文本內容...`,saveRedirectRule:`儲存並添加重新導向規則`,noRulesTitle:`無自訂規則`,noRulesDesc:`您還沒有添加任何手動規則。`,tableAction:`行動`,tablePattern:`模式`,tableDetails:`解析詳情`,tableOps:`操作`,labelBlock:`攔截`,labelAllow:`放行`,labelRedirect:`重新導向`,detailBlock:`解析至`,detailAllow:`轉發至上游服務`,patternEmpty:`域名模式不能為空`,duplicatePattern:`該域名的規則已存在`,addSuccess:`規則新增成功`,addFailed:`新增規則失敗`,updateSuccess:`規則更新成功`,updateFailed:`更新規則失敗`,tableCreatedAt:`新增時間`,clickToSort:`點擊排序`,editRule:`編輯規則`,cancel:`取消`,saveChanges:`儲存變更`,delete:`刪除`,close:`關閉`},o={customRange:`自訂時間範圍`,startTime:`開始時間`,endTime:`結束時間`,apply:`應用`,custom:`自訂`,totalQueries:`總查詢`,blocked:`攔截`,redirected:`重新導向`,blockRate:`攔截率`,activeDevices:`活躍裝置`,activeIPs:`活躍 IP 地址`,queryTrend:`查詢趨勢`,noRecords:`此時間段內暫無記錄`,topAllowed:`常用域名 (Allowed)`,topBlocked:`攔截頻率最高 (Blocked)`,noData:`暫無數據`,clientActivity:`客戶端活躍度`,ipAddress:`IP 地址`,location:`歸屬地`,queries:`查詢數`,destinationDistribution:`流量目的地分佈`,requests:`請求`,topIsps:`主要營運商`,pressToShowMore:`按下顯示更多`,maximize:`放大`,minimize:`縮小`,loading:`載入中...`},s={title:`查詢日誌`,subtitle:`查看本配置 DNS 解析日誌和攔截記錄。`,realtime:`即時`,allStatus:`全部狀態`,onlyPass:`放行`,onlyBlock:`攔截`,onlyRedirect:`重新導向`,searchPlaceholder:`搜尋域名...`,noResults:`未找到結果`,noRecords:`尚無記錄`,noResultsDesc:`沒有找到匹配 "{{query}}" 的域名記錄。`,noRecordsDesc:`選定的時間範圍內沒有收到任何 DNS 查詢。`,tableTime:`時間`,tableDomain:`域名`,tableAnswer:`解析結果`,tableSource:`來源`,tableType:`類型`,tableStatus:`狀態`,tableReason:`原因`,loadedAll:`已載入全部 {{count}} 條記錄`,realtimeLoadMoreTip:`關閉「即時」模式以載入更多`,filterAccessPoint:`存取點`,allAccessPoint:`全部存取點`,allCountry:`全部國家`,filterCountry:`目的地國家`,allIsp:`全部營運商`,filterIsp:`營運商`,timeRange:`時間範圍`,clearFilters:`清除篩選`,details:`查詢詳情`,detailAccessPoint:`存取點`,basicInfo:`基礎資訊`,detailDomain:`域名`,detailType:`類型`,detailTime:`時間`,detailLatency:`耗時`,detailProfile:`所用配置`,detailStatus:`狀態`,detailUpstream:`上游伺服器`,detailReason:`原因`,detailNoReason:`無 (已放行)`,detailECS:`ECS`,resolutionResult:`解析結果`,noResult:`無結果`,networkDetails:`網路詳情`,clientSource:`客戶端 (源)`,destination:`目的地`,quickActions:`快捷操作`,actionAllow:`放行`,actionBlock:`攔截`,actionRedirect:`重新導向`,statusPass:`放行`,statusBlock:`攔截`,statusRedirect:`重新導向`,export:`匯出日誌`,rewritten:`已重寫`,logDetails:`查詢詳情`,searchCountry:`搜尋國家...`,noMatchingCountries:`未找到相符國家`,searchIsp:`搜尋電信業者...`,noMatchingIsps:`未找到相符電信業者`,loadingIsps:`正在載入電信業者...`,loggingDisabledTitle:`日誌記錄已關閉`,loggingDisabledDesc:`目前設定的日誌留存已設為「關閉」,系統不記錄任何查詢日誌。如需查看,請在設定中啟用日誌留存。`,detailIsClientEncrypted:`是否使用者端加密`,encryptedLocked:`是 (未解鎖)`,encryptedDecrypted:`是 (已本機解密)`,encryptedYes:`是`,notEncrypted:`否`,encryptedRecord:`[加密記錄]`,exportSuccess:`日誌匯出成功`,exportError:`日誌匯出失敗`},c={title:`外部攔截清單`,subtitle:`訂閱第三方維護的域名屏蔽清單 (如 AdGuard 等)。`,formatDisclaimer:`本系統只支援有限的 AdGuard 格式列表和純域名列表。僅支援基礎的攔截規則(如 ||example.com^),會繞過 "@@" 白名單,暫不支援 "$" 重寫規則以及 "*" 省略規則。系統會自動匹配域名的所有子域名。`,syncAll:`同步所有清单`,urlPlaceholder:`輸入攔截清單 URL...`,addSubscription:`添加訂閱`,supportedFormats:`支援的清單格式:`,formatAdGuard:`相容 ||domain.com^ 等過濾規則。`,formatHosts:`相容標準 hosts 文件 (例如 0.0.0.0 domain.com)。`,noListsTitle:`尚未添加清單`,noListsDesc:`您還沒有訂閱任何外部攔截清單。`,tableUrl:`清單 URL`,tableLastSync:`上次同步`,tableStatus:`狀態`,tableOps:`操作`,neverSynced:`從未`,enabled:`正常`,disabled:`同步失敗`,statusNormal:`正常`,statusDisabled:`已停用`,statusOutdated:`過時`,statusMissing:`缺失`,presetOisdBig:`OISD Big (廣告/惡意軟體等攔截)`,presetOisdNsfw:`OISD NSFW (色情/震撼/成人內容攔截)`,presetAdGuard:`AdGuard AdServers (廣告伺服器)`,presetStevenBlack:`StevenBlack Hosts (綜合攔截)`,syncTaskStarted:`同步任務已啟動`,syncCheckComplete:`同步檢查已完成`,syncTaskFailed:`同步任務失敗`,syncing:`正在同步...`,syncSuccess:`清單同步成功`,syncError:`清單同步失敗`,syncErrorDesc:`無法下載或解析此攔截列表。請檢查連結是否正確,並確保該連結可以正常存取。`,deleteConfirm:`確定要刪除此清單訂閱嗎?`,deleteSuccess:`清單刪除成功`,deleteError:`清單刪除失敗`,addSuccess:`清單新增成功`,addError:`清單新增失敗`,listEnabled:`清單已啟用`,listEnabledHint:`停用後將停止使用此清單進行過濾,但不會刪除該清單,其訂閱與同步紀錄會保留。`,listEnabledToast:`已啟用清單 - 正在重建過濾器`,listDisabledToast:`已停用清單 - 正在重建過濾器`,checkDomain:`檢查網域`,checkDomainHint:`清單以精簡過濾器的形式儲存,因此無法瀏覽其中的項目。您可以立即針對此清單測試任意網域。`,check:`檢查`,checkBlocked:`已被此清單封鎖`,checkNotBlocked:`未被此清單封鎖`,checkViaParent:`(比對到上層網域)`,checkNotSynced:`此清單尚未完成同步,因此暫無可供檢查的內容。`,checkListDisabled:`此清單目前已停用,因此現在不會過濾任何內容。`,checkProbabilisticRate:`過濾器屬於機率性:大約每 {{odds}} 次比對中有 1 次為誤判。新增例外規則也會涵蓋其所有子網域。`,checkProbabilistic:`過濾器屬於機率性,因此少部分比對屬於誤判。新增例外規則也會涵蓋其所有子網域。`,checkAllowAction:`仍然允許`,checkAllowed:`已新增例外 - 該網域現在可以解析`,allowRuleAdded:`已為 {{domain}} 新增例外`,listDetails:`清單詳情`},l={title:`配置設定`,subtitle:`管理您的 DNS 解析策略和進階選項。`,saveChanges:`儲存所有更改`,saveSuccess:`設定已成功儲存`,saveError:`儲存設定失敗,請稍後再試`,upstreamTitle:`上游服務`,dohUrl:`DoH URL`,dnsIP:`DNS IP`,classicDns:`經典 DNS`,httpsOnly:`僅支援 HTTPS`,upstreamDesc:`所有未被攔截的查詢將由本應用轉發至此伺服器。支援 DoH (HTTPS)、DoT (tls://)、DNS Stamp (sdns://) 或經典 DNS。經典 DNS 透過 TCP 轉發。`,defaultPolicyTitle:`默認過濾行為`,onNoMatch:`未匹配規則時`,allowAll:`允許所有查詢`,blockAll:`攔截所有查詢`,logRetentionTitle:`日誌留存`,retentionDuration:`留存時長`,retentionDesc:`日誌將自動清除。`,skipLogOnPass:`不記錄放行紀錄`,skipLogOnPassDesc:`啟用後不記錄正常允許的 DNS 查詢,僅記錄被攔截、重新導向或解析失敗的紀錄。可大幅減少儲存空間與寫入操作。`,ecsTitle:`EDNS 客戶端子網 (ECS)`,ecsUpstreamHint:`並非所有上游 DNS 都支援 ECS。對於 ControlD 等遞迴解析器,ECS 僅影響其內部路由,不會傳遞給上游權威伺服器。`,sendEcs:`呈遞 ECS`,customSubnet:`自訂子網`,customIpv4:`自訂 IPv4 子網`,customIpv6:`自訂 IPv6 子网`,ipv4Hint:`(例如 1.2.3.0/24)`,ipv6Hint:`(例如 2001:db8::/32)`,testToolTitle:`即時解析測試工具`,testDomain:`測試域名`,recordType:`記錄類型`,runTest:`執行測試`,domainPlaceholder:`例如 google.com`,hitRule:`命中規則 / 原因`,defaultAllow:`默認放行策略`,parseLatency:`解析耗時`,sourceIp:`源 IP 地址`,answerSection:`解析結果回顯 (Answer Section)`,noRecordsReturned:`本次查詢未返回任何記錄。`,diagnostics:`診斷資訊`,errorDetail:`錯誤詳情`,upstreamResponse:`上游回應內容`,presetCloudflareSecurity:`Cloudflare Security (快速/去惡意域名/去釣魚域名)`,presetCloudflareFamilies:`Cloudflare Families (快速/去惡意域名/去成人內容)`,presetQuad9:`Quad9 (去惡意域名/無日誌/不呈遞 ECS)`,presetQuad9Ecs:`Quad9 ECS (去惡意域名/無日誌/呈遞 ECS)`,presetControlDFree:`ControlD Free (去廣告/去跟蹤/去惡意域名/去釣魚域名)`,presetControlDUncensored:`ControlD Uncensored (抗審查)`,presetAdGuard:`AdGuard (去廣告/去跟蹤)`,presetAdGuardFamily:`AdGuard Family protection (去廣告/去跟蹤/去成人內容/安全搜尋)`,retention10m:`10 分鐘`,retention1h:`1 小時`,retention24h:`24 小時`,retention7d:`7 天`,retention30d:`30 天`,retention90d:`90 天 (最長)`,blockModeTitle:`攔截響應模式`,blockModeDesc:`當域名被攔截時返回的内容`,default:`默認`,NXDOMAIN:`域名不存在`,NODATA:`空回答`,customIP:`自訂 IP`,export:`匯出配置`,exportSuccess:`配置成功匯出`,exportError:`配置匯出失敗,請稍後再試`,nameUpdateSuccess:`配置名稱已更新`,saving:`正在儲存...`,echTitle:`重寫 ECH`,enableEch:`啟用重寫 ECH`,echDesc:`針對由 Cloudflare 代理的網域名稱,當查詢 HTTPS (Type 65) 或 SVCB (Type 64) 記錄時,自動重寫注入 ECH 參數與表層 SNI 以保護存取隱私。`,echFrontingTitle:`表層 SNI (ECH Fronting)`,echFrontingDesc:`TLS 握手外層可見的偽裝網域`,echFrontingCustomPlaceholder:`輸入自訂表層網域,例如 crypto.cloudflare.com`,echPreferred:`首選`,whatIsEch:`何為 ECH (Encrypted Client Hello)?`,echHelpDesc:`ECH (加密客戶端問候) 是 TLS 1.3 的重要隱私擴充,它將 TLS 握手中的真實目標網域名稱 (Inner SNI) 全程加密,僅向網路中間人暴露表層偽裝網域名稱 (Outer SNI / ECH Fronting)。`,echHelpBenefit1:`防止中間人及 ISP 窺探您正在存取的具體網頁與子網域名稱`,echHelpBenefit2:`配合 DoH 與自訂表層 SNI,消除明文 SNI 阻斷與識別風險`,retentionDisabled:`關閉 (隱私需要)`,retentionDisabledDesc:`已關閉日誌記錄。系統將不會記錄任何 DNS 查詢日誌並立即清空歷史日誌。`,localRetentionTitle:`本地儲存與留存`,localRetentionDuration:`本地保留時長`,localRetentionDesc:`日誌將持久化保存在當前瀏覽器本地的 SQLite 資料庫 (OPFS) 中,提供秒級本地搜尋並節省雲端儲存費用。超出此時長的本地日誌將自動清除。`,localRetention30d:`30 天`,localRetention90d:`90 天 (推薦)`,localRetention180d:`180 天`,localRetention365d:`365 天 (1 年)`,localRetentionUnlimited:`永久保留 (直到手動清除)`,localEngineStorage:`儲存引擎`,localEngineOpfs:`OPFS 本地持久化`,localEngineMemory:`記憶體暫存 (無痕/隱私模式)`,localCachedCount:`本地已快取日誌`,localCachedCountUnit:`{{count}} 筆記錄`,clearLocalData:`清空本地日誌`,clearLocalDataConfirm:`確定要清空此配置在本地保存的所有日誌嗎?(雲端日誌不受影響)`,clearLocalDataSuccess:`本地日誌已成功清空`,clearLocalDataError:`清空本地日誌失敗`,e2eeTitle:`日誌端到端加密 (E2EE)`,e2eeEnabled:`已啟用`,e2eeDisabled:`未啟用`,e2eeDesc:`使用專屬後量子混合金鑰 (P256-MLKEM768) 在邊緣即刻加密 DNS 日誌。雲端無法窺視,資料僅在通過您的硬體 Passkey 授權的客戶端本地解密並分析。`,e2eePasskeyRequired:`端到端加密需要硬體 Passkey (WebAuthn) 支援。檢測到您尚未在帳號中綁定 Passkey,請先在「帳號設定」中添加 Passkey。`,e2eeGoToAccount:`前往帳號設定添加 Passkey`,e2eeEnableSwitch:`啟用日誌端到端加密`,e2eeKeyType:`加密演算法`,e2eeDeviceState:`當前裝置狀態`,e2eeUnlocked:`已在此裝置解鎖`,e2eeUnlockButton:`驗證 Passkey 解鎖`,e2eeProtectedPasskeys:`已授權 Passkey 憑證`,e2eeDevicesUnit:`個憑證`,e2eeEnableSuccess:`端到端加密已成功啟用`,e2eeEnableError:`啟用端到端加密失敗`,e2eeDisableSuccess:`端到端加密已關閉`,e2eeDisableError:`關閉端到端加密失敗`,e2eeUnlockSuccess:`私鑰已成功在此裝置解鎖`,e2eeUnlockError:`驗證 Passkey 解鎖失敗`,e2eeDisableConfirm:`確定要關閉端到端加密嗎?關閉後新產生的 DNS 日誌將以明文儲存。(已加密的歷史日誌仍需私鑰解密)`,e2eeUnlockPrompt:`此配置已啟用端到端加密,請驗證 Passkey 解鎖查看完整明文日誌。`,e2eeUnlockNow:`解鎖加密日誌`,e2eeUnlockRecovery:`使用修復金鑰解鎖`,securityTitle:`安全與存取`,rotateKeyTitle:`存取憑據 (URL)`,rotateKeyDesc:`為此設定檔重新產生新的存取 URL。目前使用舊 URL 的所有裝置將立即中斷與 DNS Worker 的連線,需使用新 URL 重新設定。`,rotateKeyBtn:`輪換存取憑據`,copied:`已複製到剪貼簿`,nameUpdateFailed:`更新失敗`},u=JSON.parse(`{"title":"帳號設定","subtitle":"管理您的安全憑據和系統用戶。","e2ee":{"title":"端到端加密 (E2EE)","desc":"採用客戶端後量子混合加密 (P256-MLKEM768 + AES-256-GCM / NIST FIPS 203) 硬體信封加密技術。資料在邊緣即刻加密,伺服端僅儲存不可逆密文,只有已授權硬體 Passkey 的設備可在本機解密並分析。","pqcTag":"PQC / NIST FIPS 203","upgradePqcBtn":"升級到後量子 (PQC)","upgradePqcSuccess":"已成功升級到後量子混合加密 (P256-MLKEM768)","upgradePqcError":"升級到後量子加密失敗","enabled":"已啟用","disabled":"未啟用","passkeyRequired":"端到端加密需要硬體 Passkey (WebAuthn) 支援。請在上方「雙重認證 (MFA)」設定中先新增至少一個 Passkey 憑證。","scopesTitle":"資料加密範圍","scopesSubtitle":"選擇透過硬體 Passkey 進行端到端加密保護的資料類型。","scopeLogsTitle":"DNS 查詢日誌","scopeLogsDesc":"在邊緣加密 DNS 請求的網域名稱、客戶端 IP、解析紀錄及上游資訊,防止雲端日誌被窺探或留存明文。","scopeRulesTitle":"自訂規則與重新導向","scopeRulesDesc":"端到端加密自訂封鎖/允許規則與 URL 重新導向設定。","scopeClientsTitle":"裝置別名與客戶端元資料","scopeClientsDesc":"端到端加密存取點裝置名稱、IP 別名與拓撲資訊。","statusActive":"已保護","statusPlanned":"規劃中","profileSelect":"設定 Profile","keyType":"加密演算法","deviceState":"目前裝置狀態","unlocked":"已在此裝置解鎖","unlockButton":"驗證 Passkey 解鎖","protectedPasskeys":"已授權 Passkey 憑證","devicesUnit":"個憑證","enableSuccess":"端到端加密已成功啟用","enableError":"啟用端到端加密失敗","disableSuccess":"端到端加密已關閉","disableError":"關閉端到端加密失敗","unlockSuccess":"私鑰已成功在此裝置解鎖","unlockError":"驗證 Passkey 解鎖失敗","disableConfirm":"確定要關閉端到端加密嗎?關閉後新產生的 DNS 日誌將以明文儲存。(已加密的歷史日誌仍需私鑰解密)","noKeypairTitle":"尚未產生端到端加密金鑰對","noKeypairDesc":"端到端加密需要為您的帳戶產生專屬的後量子 (P256-MLKEM768) 金鑰對。私密金鑰將透過通行金鑰 (Passkey) 或修復金鑰 (Recovery Key) 進行硬體/信封加密保護,伺服器無法解密您的 DNS 記錄。","initPasskeyBtn":"驗證現有 Passkey 產生金鑰對","addPasskeyBtn":"新增 Passkey 以支援硬體加密","initRecoveryBtn":"透過修復金鑰產生金鑰對","initWithRecoveryTitle":"透過修復金鑰產生金鑰對","initWithRecoveryDesc":"請輸入您目前的 30 位緊急修復金鑰。系統將在本地產生後量子 (P256-MLKEM768) 金鑰對並透過該金鑰信封加密。使用後修復金鑰將自動輪換以確保安全。","keypairGenSuccess":"端到端加密金鑰對已成功產生!","keypairGenError":"產生端到端加密金鑰對失敗","unlockRecoveryButton":"使用修復金鑰解鎖","unlockWithRecoveryTitle":"使用修復金鑰解鎖","unlockWithRecoveryDesc":"請輸入 30 位緊急修復金鑰。注意:根據單次使用規則,修復金鑰驗證成功後將立即自動輪換。","recoveryKeyInputLabel":"修復金鑰","unlockConfirm":"解鎖私密金鑰","recoveryUnlockFailed":"修復金鑰無效或解析失敗","generateKeypairBtn":"產生金鑰對","enableSwitch":"啟用查詢日誌端到端加密","enableSwitchDesc":"在用戶端邊緣加密 DNS 請求的網域名稱、用戶端 IP、解析記錄及上游資訊後再寫入雲端持久化儲存。","trustBoundaryTitle":"安全與信任邊界提示","trustBoundaryDesc":"所有 DNS 查詢流量在網路解析時必然會經過託管伺服器(Cloudflare Workers 邊緣節點或自建主機執行階段),伺服器在執行階段記憶體中處理解析後將記錄加密寫入。本功能僅用於保護雲端持久化資料庫(D1 / 資料倉儲)免受靜態資料洩漏或離線分析,無法向正在處理轉發請求的託管伺服器隱藏即時網路流量。","initFailed":"初始化端對端加密失敗","lockDevice":"鎖定此裝置","lockSuccess":"已鎖定,私鑰已從本機清除"},"jwtSecretWarningTitle":"安全提示:JWT 金鑰強度較弱","jwtSecretWarningDesc":"目前 JWT_SECRET 長度小於 32 位元字元,存在潛在安全隱患。請先設定環境變數 KEK_v1 完成憑證信封加密遷移,然後再輪換 JWT_SECRET,以防止既有加密的 TOTP 或復原金鑰無法解密。","roleAdmin":"系統管理員","roleUser":"用戶","personalInfo":"個人資訊","username":"用戶名","userId":"用戶 ID","changePassword":"修改密碼","currentPassword":"當前密碼","newPassword":"新密碼","updatePassword":"更新密碼","userManagement":"用戶管理 (管理員)","createUser":"創建用戶","role":"角色","createdAt":"創建時間","lastActive":"最後活動時間","lastResolve":"最後解析時間","actions":"操作","dangerZone":"危險區域","clearLogs":"清空所有日誌","clearLogsDesc":"這將刪除您名下所有 DNS 配置生成的查詢日誌。此操作不可恢復。","clearLogsBtn":"立即清空日誌","deleteAccount":"註銷帳號","deleteAccountDesc":"永久刪除您的帳號及所有關聯數據。此操作將導致您失去所有解析配置。","deleteAccountBtn":"註銷並刪除帳號","createNewUser":"創建新用戶","initialPassword":"初始密碼","userRole":"用戶角色","cancel":"取消","createNow":"立即創建","formatTipUsername":"用戶名格式提示:僅限 5 至 32 位 Unix 用戶名格式","formatTipPassword":"密碼格式提示:12-100位字元","passwordSuccess":"密碼修改成功","updateFailed":"修改失敗","confirmDeleteUser":"確定要刪除此用戶嗎?其擁有的所有配置也將被移除。","confirmClearLogs":"確定要清空您名下所有配置的查詢日誌嗎?此操作不可撤銷。","clearLogsSuccess":"日誌已成功清空","confirmDeleteAccount":"警告:確定要註銷並刪除本帳號嗎?\\n\\n閣下所有 DNS 配置、自訂規則和查詢日誌將被永久刪除。此操作不可逆!","inactivityPolicy":"活躍度政策","inactivityDesc":"為保護用戶隱私並節省資源,超過 30 天無解析記錄的帳戶,其所有關聯查詢日誌及 DNS 配置將被自動清除。帳戶本身在 90 天無登錄後將予移除。","systemSettings":"系統設定 (管理員)","adminOnlyView":"僅管理員可見","featureToggle":"功能開關","enableTurnstileSignup":"註冊頁面啟用人機驗證","enableTurnstileLogin":"登入頁面啟用人機驗證","totp":{"title":"數位驗證器 (TOTP)","enabled":"啟用","disabled":"停用","setupDesc":"為帳號新增額外的安全層。支持 Google Authenticator、Authy 或任何 TOTP 相容應用。","setupBtn":"設定數位驗證器","scanQR":"使用驗證器應用掃描此二維碼:","orEnterManually":"或手動輸入密鑰:","enterCode":"請輸入驗證器代碼進行確認:","enterCodeHint":"輸入 6 位驗證碼以確認密鑰已正確錄入。","activate":"啟用數位驗證器","skipPassword":"無密碼登入 — 登入時只顯示 TOTP 輸入框(隐藏密碼)","disable":"停用數位驗證器","disableTitle":"停用數位驗證器","disableWarning":"停用數位驗證器後,若未設定通行金鑰,帳號將僅受密碼保護。確定要停用嗎?","confirmDisable":"確認停用","recoveryKeysTitle":"請保存您的恢復密鑰","recoveryKeysWarning":"請將恢復金鑰妥善保存(如密碼管理器)。每個金鑰只能使用一次。","copyKeys":"複製全部密鑰","copied":"已複製!","done":"完成,我已保存密鑰","activeDesc":"數位驗證器已啟用,正在為您的帳號提供 6 位動態驗證碼保護。","invalidCode":"請輸入 6 位驗證碼"},"sessions":{"session":"會話","title":"活躍會話","device":"裝置","ip":"IP 位址","loginTime":"登入時間","expires":"過期時間","action":"操作","current":"目前會話","revoke":"撤銷","revokeFailed":"撤銷會話失敗。","empty":"未找到活躍會話。","unknownDevice":"未知裝置","revokeOthers":"撤銷其他會話","confirmRevokeOthers":"撤銷其他會話","revokeOthersConfirmText":"確定要撤銷所有其他活躍會話嗎?除當前設備外,其他已登錄設備將立即退出。"},"activity":{"title":"帳號活動記錄","action":"事件","time":"時間","session":"會話","ip":"IP 位址","empty":"暫無活動記錄。","loadMore":"載入更多","signup":"註冊","loginSuccess":"登入成功","loginFail":"登入失敗","logout":"登出","pwChangeSuccess":"密碼已修改","pwChangeFail":"密碼修改失敗","totpVerifySuccess":"TOTP 驗證通過","totpVerifyFail":"TOTP 驗證失敗","totpSetup":"設定數位驗證器","totpRemoved":"停用數位驗證器","recoveryKeyUsed":"使用了恢復密鑰","sessionRevoked":"會話已撤銷","reasons":{"user_active":"主動登出","user_revoke":"撤銷會話","expired":"會話已過期","geolocation_mismatch":"異地存取限制","geolocation_missing":"缺少地理位置資訊","token_reuse":"憑證重複使用(安全警告)"},"passkeyRegistered":"新增通行密鑰","passkeyDeleted":"刪除通行密鑰","passkeyVerifySuccess":"通行密鑰驗證通過","passkeyVerifyFail":"通行密鑰驗證失敗","recoveryKeyRotated":"應急恢復金鑰已輪換","device":"裝置"},"passkey":{"title":"通行密鑰 (Passkey)","addBtn":"新增通行密鑰","desc":"使用生物識別(Touch ID / Face ID / Windows Hello)或硬體安全金鑰(YubiKey)進行快捷且防釣魚的多因素認證。","empty":"尚未新增任何通行密鑰。","addTitle":"新增通行密鑰","nameLabel":"通行密鑰名稱","namePlaceholder":"例如:my_passkey","promptNotice":"點擊繼續後,瀏覽器將彈出系統提示,引導您完成生物識別或硬體安全金鑰註冊。","continue":"繼續","renameTitle":"重命名通行密鑰","deleteTitle":"刪除通行密鑰","deleteConfirm":"確定要刪除通行密鑰 \\"{{name}}\\" 嗎?刪除後將無法使用該通行密鑰進行多因素認證。","defaultName":"通行密鑰","notSupported":"您的瀏覽器或目前環境不支援通行密鑰 (WebAuthn)。","regFailed":"通行密鑰註冊失敗","count":"{{count}} 個已設定","none":"未設定","created":"新增時間","lastUsed":"最後使用","neverUsed":"從未在登入中使用","updated":"通行密鑰更新成功","registered":"通行密鑰新增成功","deleted":"通行密鑰已刪除","appNameLabel":"網域名稱","appNameHelper":"通行密鑰將綁定至此網域","formatTip":"通行密鑰名稱提示:1-30 位字元,只允許字母、數字、下劃線和中劃線","verifyPrompt":"請使用裝置的生物辨識(人臉、指紋、Windows Hello)或安全金鑰進行身分驗證。","changePwNotice":"提交修改時將調用通行金鑰 (Passkey) 生物辨識或硬體金鑰核驗身分。","submitNotice":"提交時將調用通行金鑰 (Passkey) 生物辨識或硬體金鑰核驗身分。"},"mfaNone":"無","resetUserPassword":"重設密碼","resetPasswordTitle":"重設使用者密碼","resetPasswordWarning":"重設後,該使用者的所有現有工作階段將立即登出,使用者需使用新密碼重新登入。","resetPasswordSuccess":"密碼已成功重設,使用者舊登入工作階段已登出。","confirmReset":"確認重設","mfa":{"title":"多因素驗證 (MFA)","enabled":"已設定","notConfigured":"未設定","desc":"通行金鑰 (Passkey) 與數位驗證器 (TOTP) 均可作為多因素驗證方式,設定任選其一即可保障帳號安全並支援無密碼登入與密碼重設。","passwordlessTitle":"無密碼登入 (Passwordless Login)","passwordlessDesc":"啟用後,登入時無需輸入密碼,直接透過通行金鑰 (Passkey) 或數位驗證碼 (TOTP) 完成身分核驗。","unconfiguredHint":"目前帳號尚未設定任何多因素驗證。設定通行金鑰或數位驗證器後,可啟用無密碼快速登入並在忘記密碼時安全找回帳號。","password":"密碼","passkey":"通行密鑰 (Passkey)","totp":"身份驗證器 (TOTP)"},"recoveryKey":{"title":"緊急修復金鑰","desc":"緊急修復金鑰用於在遺失所有通行金鑰或驗證器時重新登入帳戶或重設密碼。為保證零知識安全,修復金鑰採用單向不可逆密碼學雜湊儲存,無法在網頁端直接解密檢視,僅支援輪換。使用後金鑰將立即自動輪換。","configured":"已設定","notConfigured":"未設定","rotateBtn":"輪換修復金鑰","yourKey":"您的 30 位緊急修復金鑰","securityTip":"請妥善保管此修復金鑰。擁有此金鑰者可以重獲您的帳戶存取權限。","rotateConfirm":"輪換修復金鑰將立即使原修復金鑰失效,並重新衍生信封加密金鑰。僅允許透過現有通行金鑰 (Passkey) 或原修復金鑰完成輪換驗證。是否繼續?","confirmRotateBtn":"確認進入輪換驗證","rotateVerifyTitle":"驗證身分以輪換修復金鑰","rotateSuccess":"修復金鑰已成功輪換!請立即妥善離線儲存新金鑰,舊金鑰已失效。","oneWayHash":"單向不可逆雜湊","oneWayTooltip":"採用 SHA-256 單向密碼學雜湊保護,伺服器不儲存明文且不可解密,僅支援身分輪換驗證。","newKeyGenerated":"已產生全新 30 位修復金鑰(請立即儲存)","oneWayWarning":"注意:此金鑰僅在此顯示一次,關閉後將無法再次檢視!伺服器僅儲存不可逆雜湊,請務必將其妥善複製並離線安全儲存。","autoRotatedTitle":"修復金鑰已自動輪換","autoRotatedNotice":"根據修復金鑰單次使用規則,您的原修復金鑰已失效,系統已為您產生全新 30 位緊急修復金鑰。請務必立即複製並妥善離線儲存,關閉後將無法再次檢視!","savedAndClose":"我已妥善儲存並繼續","savedAndContinue":"我已妥善儲存並進入控制台","descOneWay":"緊急恢復密鑰是在您遺失所有通行密鑰 (Passkey) 或身份驗證器時,恢復帳號存取及端對端解密資料的唯一憑據。為確保零知識安全,恢復密鑰採用單向不可逆雜湊儲存,無法在網頁端檢視明文,僅支援透過原恢復密鑰或現有 Passkey 進行輪換。密鑰一旦被使用將立即自動輪換。","keyRequired":"請輸入緊急恢復密鑰","verifyBtn":"恢復密鑰","verifyPromptLabel":"原恢復密鑰 (Original Recovery Key)"},"verifyIdentity":"安全身分核驗","verifyIdentityDesc":"為了保護您的帳戶安全,執行此敏感操作前請先驗證您的身分。","usePasskeyInstead":"使用通行金鑰驗證","changePasswordWithPasskey":"使用通行金鑰核驗並修改密碼","enableRegistration":"開放新使用者註冊","timezone":"時區","timezoneAuto":"自動(跟隨瀏覽器預設)","totpCode":"驗證器動態口令"}`),d={item1Title:`Obex 保護性 DNS`,item1Desc:`基於 Cloudflare Workers & D1 的 DNS 解析器,盡力提高速度與安全。`,item2Title:`DoH 隱私保護`,item2Desc:`透過 DNS over HTTPS (RFC 8484) 加密隧道防止劫持並增強隱私。`,item3Title:`全球邊緣網路`,item3Desc:`依托 Cloudflare 龐大的邊缘網路,獲得極速解析與極低延遲體驗。`,item4Title:`精細化過濾`,item4Desc:`全面支援黑白名單、AdGuard 規則以及自訂 DNS 記錄重新導向。`,item5Title:`即時統計分析`,item5Desc:`透過詳細的查詢日誌和綜合統計儀表盤,直观展現網路健康狀況。`,item6Title:`現代 UI 與暗黑模式`,item6Desc:`為工程師和家庭用戶設計的高密度管理面板,基於由 Palantir 開發的 Blueprint UI。`,item7Title:`雲原生基礎架構`,item7Desc:`無伺服器架構確保零維護和私人 DNS 的無限擴充性。`,item8Title:`多配置管理`,item8Desc:`為不同裝置或網路環境創建並管理多個獨立配置。`,item9Title:`多存取點管理`,item9Desc:`為不同裝置配置專屬的 DNS 存取點,實現日誌和統計數據的精细化隔離與分析。`},f={title:`DNS Worker 部署與配置指南`,subtitle:`您的系統實例尚未完全配置。請按照以下步驟完成部署以啟用 DNS Worker。`,dbStatus:`D1 資料庫綁定 (env.DB)`,jwtStatus:`JWT 金鑰 Secrets (env.JWT_SECRET)`,configured:`已配置`,missing:`缺失 / 未配置`,refresh:`重新檢測並重新整理`,methodCli:`CLI 命令行部署`,methodDash:`Cloudflare 控制台部署`,copy:`複製`,copied:`已複製!`,stepDbTitle:`1. 建立與綁定 D1 資料庫`,stepDbDesc:`在 Cloudflare 中建立 D1 資料庫實例,在終端機執行以下命令:`,stepDbCommand1:`wrangler d1 create dns_worker_db`,stepDbDesc2:`複製終端機輸出的配置資訊,並貼到專案根目錄的 wrangler.toml 裡的 [[d1_databases]] 中:`,stepDbCommand2:`[[d1_databases]]
1
+ const e={account:`帳號`,logout:`登出`,selectProfile:`選擇配置`,add:`添加`,newProfileName:`新配置名稱...`,profileNameFormatTip:`配置名稱提示:1-30個字元,且不能與已有配置重名`,profileLimitExceeded:`配置數量已達上限(最多{{max}}個)`,profileNameExists:`該配置名稱已存在`,profileNameFormatError:`配置名稱格式不正確`,create:`創建`,welcome:`歡迎`,createProfileToStart:`創建一個配置以開始。`,loading:`載入中...`,errorNetwork:`網路錯誤`,confirmDelete:`確定要刪除此配置嗎?`,confirmLogout:`確定注銷登入嗎?`,notFound:`404 - 頁面未找到`,notFoundDesc:`您嘗試訪問的頁面不存在或已被移除。`,backToHome:`返回首頁`,apac:`亞太`,mainlandChina:`中國大陸`,HongKong:`香港`,Singapore:`新加坡`,Tokyo:`東京`,NorthAmerica:`北美`,nae:`北美東部`,naw:`北美西部`,eu:`歐洲`,invalidFormat:`無效文件格式`,import:`匯入`,importSuccess:`配置成功匯入`,importError:`配置匯入失敗,請確保格式正確且稍後再試`,save:`儲存`,saveSuccess:`設定已儲存`,close:`關閉`,cancel:`取消`,continue:`繼續`,back:`返回`,copy:`複製`,copied:`已複製`,copyKey:`複製新恢復密鑰`,confirm:`確認`,delete:`刪除`,rename:`重新命名`,disable:`停用`,confirmDisable:`確認停用`,modeServerfull:`Serverfull 模式`,modeCloudflare:`Cloudflare Worker 模式`},t={setup:`安裝`,rules:`規則`,stats:`統計`,logs:`日誌`,filter:`過濾項`,settings:`設定`},n={light:`淺色`,dark:`深色`,system:`跟隨系統`},r={keepLoggedIn:`保持登入 {{days}} 天`,login:`登入 DNS Worker`,signup:`創建帳戶以開始`,welcomeBack:`歡迎回來,請輸入憑據`,protectInternet:`保護您的網際網路第一跳`,error:`錯誤`,username:`用戶名`,usernamePlaceholder:`您的用戶名`,password:`密碼`,passwordPlaceholder:`您的密碼`,loginBtn:`登入`,signupBtn:`註冊`,totpVerification:`TOTP 驗證`,noAccount:`沒有帳戶?立即註冊`,haveAccount:`已有帳戶?返回登入`,formatTipUsername:`用戶名格式提示:僅限 5 到 32 位 Unix 用戶名格式(小寫字母或底線開頭,包含小寫字母、數字、底線及中線)`,formatTipPassword:`密碼格式提示:12-100位字元`,authFailed:`認證失敗,請檢查用戶名或密碼`,networkError:`網路錯誤,請稍後再試`,turnstileRequired:`請完成人機身份驗證`,turnstileError:`驗證服務加載失敗,請檢查 Site Key 或域名配置`,verifying:`正在進行安全驗證...`,passwordLeaked:`您的密碼已被洩露,為了您的帳戶安全,請使用隨機密碼/強密碼。`,usernameExists:`該用戶名已被註冊,請嘗試其他用戶名。`,skip:`跳過`,showPassword:`顯示密碼`,hidePassword:`隱藏密碼`,geolocationRequired:`地理資訊獲取失敗,請重新進入。`,unauthorizedDefault:`您的登入會話已失效,請重新登入。`,unauthorizedGeo:`檢測到地理位置變更(超出距離限制),當前會話已被強制註銷。`,unauthorizedExpired:`您的登入已過期,請重新登入。`,unauthorizedReuse:`安全警報:檢測到重複登入,會話已終止。`,unauthorizedRevoked:`您的會話已被註銷或刪除,請重新登入。`,sessionLockTitle:`空閒會話鎖定`,sessionLockDesc:`當應用程式空閒時,使用 4 位數字 PIN 鎖定介面,以防止未授權的物理存取來保護活動會話。`,enableSessionLock:`啟用會話鎖定`,inactivityTimeout:`空閒逾時時間`,timeout1m:`1 分鐘`,timeout2m:`2 分鐘`,timeout5m:`5 分鐘`,timeout15m:`15 分鐘`,timeout30m:`30 分鐘`,timeout60m:`60 分鐘`,changePin:`修改 PIN 碼`,disablePin:`停用會話鎖定`,configurePin:`設定 PIN 碼`,newPin:`新 4 位 PIN 碼`,pinHelper:`僅限數字,例如:1234`,confirmPin:`確認新 PIN 碼`,verifyIdentity:`驗證身份`,usePassword:`使用密碼驗證`,use2fa:`使用 TOTP 驗證碼`,totpCode:`TOTP 驗證碼`,currentPassword:`目前密碼`,disablePinWarning:`停用 PIN 碼將徹底關閉空閒會话鎖定功能。`,pinFormatTip:`PIN 碼必須為 4 位數字`,pinMatchTip:`兩次輸入的 PIN 碼不符合`,passwordRequired:`需要輸入密碼以驗證身份`,sessionLocked:`由於長時間未活動,會話已鎖定`,sessionExpired:`嘗試次數過多,會話已終結。`,pinIncorrect:`PIN 碼錯誤`,attemptsRemaining:`還剩 {{count}} 次嘗試機會`,logoutAndReturn:`登出會話`,lockNow:`立即鎖定`,cfErrorWithDetails:`Cloudflare 邊緣節點異常 (錯誤代碼 {{code}}: {{desc}})。服務暫時不可用,請稍後重試。{{rayId}}`,cfErrorCodeOnly:`Cloudflare 邊緣服務異常 (錯誤代碼 {{code}})。服務暫時不可用,請稍後重試。{{rayId}}`,cfErrorGeneric:`Cloudflare 邊緣服務異常,服務暫時不可用,請稍後重試。{{rayId}}`,htmlErrorGeneric:`伺服器傳回異常網頁回應 (HTTP {{status}}),請稍後重試。`,tooManyAttempts:`請求過於頻繁或嘗試次數過多,已被臨時限制,請稍後再試。`,dbQuotaExceeded:`資料庫每日免費讀取配額已用盡,請等待次日自動重置或升級配額。`,noUsersRegistered:`系統尚無帳號,請註冊成為管理員。`,registerAdminTitle:`註冊管理員帳號`,registerAdminDesc:`系統尚無帳號,首位註冊用戶將自動成為系統管理員。`,goToRegisterAdmin:`註冊管理員`,dbNotInitialized:`資料庫未初始化或缺少資料表,請在終端執行 npm run db:migrate:prod 套用資料庫遷移。`,verifyWithPasskey:`使用通行密鑰驗證`,orUseTotp:`或使用數字驗證器`,passwordRequiredFirst:`請先輸入密碼`,passkeyVerification:`通行密鑰驗證`,passkeyPromptDesc:`使用已註冊的裝置生物識別或硬體安全金鑰進行驗證`,forgotPassword:`忘記密碼?`,forgotPasswordTitle:`重設密碼`,forgotPasswordDesc:`透過多因素驗證(通行金鑰或數位驗證碼)安全找回您的帳號。`,noMfaContactAdmin:`該帳號未設定任何多因素驗證(Passkey 或 TOTP),無法自助重設密碼。請聯絡系統管理員為您重設密碼。`,chooseMfaForReset:`請選擇一種驗證方式以核驗您的身分:`,authViaPasskey:`使用通行金鑰驗證`,authViaPasskeyDesc:`使用指紋、臉孔或硬體安全金鑰一鍵完成驗證`,authViaTotp:`使用數位驗證器 (TOTP)`,authViaTotpDesc:`輸入身分驗證器應用程式產生的 6 位動態驗證碼`,authViaRecoveryKey:`使用緊急復原金鑰`,authViaRecoveryKeyDesc:`輸入 10 位緊急復原金鑰`,verifyAndProceed:`核驗並繼續`,setNewPassword:`設定新密碼`,setNewPasswordDesc:`身分核驗成功,請為您的帳號設定一個新密碼。`,newPassword:`新密碼`,confirmNewPassword:`確認新密碼`,passwordMismatch:`兩次輸入的新密碼不相符`,saveAndReset:`儲存並重設密碼`,resetSuccessTitle:`密碼重設成功`,resetSuccessDesc:`您的密碼已更新,所有現有登入工作階段已登出。請使用新密碼重新登入。`,goToLogin:`前往登入`,mfaSetupTitle:`強化帳號安全`,mfaSetupDesc:`設定多因素驗證 (MFA) 可有效保護您的帳號,並解鎖無密碼快速登入與密碼自助找回功能。`,mfaRecommended:`推薦`,passkeySignupDesc:`使用指紋、臉孔或安全金鑰,免去繁瑣輸入,兼顧極速體驗與頂級安全防護。`,bindPasskeyNow:`立即綁定通行金鑰`,mfaUniversal:`通用`,totpSignupDesc:`使用 Google Authenticator、1Password 等身分驗證器產生 6 位動態驗證碼。`,setupTotpBtn:`設定數位驗證器`,skipMfaSetup:`暫不設定,直接完成`,backToMfaOptions:`返回選擇其他方式`,otherOptions:`其他選項`,next:`下一步`,mfaVerification:`多因素認證`,totpPrompt:`請輸入身份驗證應用程式中的 6 位動態驗證碼`,recoveryPrompt:`請輸入 30 位應急恢復密鑰(5 組,每組 6 位數字)`,recoveryChecksumError:`校驗碼錯誤:該組 6 位數無法被 11 整除`,recoveryChecksumErrorDesc:`第 {{groups}} 組校驗碼錯誤,請核對後重試`,backToPasskey:`返回通行密鑰驗證`,recoveryKeyRequired:`請輸入應急恢復密鑰`,totpFormatError:`請輸入有效的 6 位驗證碼`,registrationDisabledTitle:`註冊已停用`,registrationDisabledDesc:`目前系統已暫停開放新使用者註冊,請聯絡系統管理員取得帳號。`,backToLogin:`返回登入`,jwtSecretPreset:`檢測到正在使用預設的 JWT_SECRET,請在環境變數中修改並替換為自訂安全金鑰後重新部署。`,jwtSecretMissing:`缺少 JWT_SECRET,請在環境變數中配置安全金鑰後重新部署。`,jwtSecretInvalid:`JWT_SECRET 金鑰格式無效,請檢查設定。`,orUseOtherMfa:`或使用其他驗證方式`,totpUseRecovery:`使用緊急恢復密鑰`,totpUseApp:`使用驗證器 App`,mfaMethodTotp:`數位驗證器 (TOTP)`,invalidTotp:`TOTP 驗證碼無效,請重試`},i={title:`安裝指南`,subtitle:`配置裝置以使用 DNS Worker 主掌您的網際網路安全。`,regionOptimization:`接入地區優化 (Windows)`,otherRegion:`其他地區`,copied:`已複製到剪貼簿`,verifyConnection:`驗證您的連接`,verifying:`正在驗證...`,connected:`連接正常`,notConnected:`或未連接`,profileMatch:`您正透過此配置進行過濾。`,profileMismatch:`您正使用 Obex,但配置 ID 不匹配。`,verifyHint:`完成下方配置後,點擊按鈕驗證。`,refreshStatus:`重新整理狀態`,egressIp:`出口 IP 地址`,currentLocation:`當前位置`,edgeServer:`邊緣伺服器`,serverLocation:`伺服器位置`,dohUrlTitle:`當前配置 DoH URL`,accessPointTitle:`Access DoH URL`,defaultAccessPointName:`裝置-1`,moreAccessPoints:`更多存取點 ↗`,accessPointName:`存取點名稱`,accessPointNameFormatTip:`存取點名稱提示:1-30 位字元,只允許字母、數字、下劃線和中劃線`,accessPointLimitExceeded:`存取點數量已達上限(最多{{max}}個)`,accessPointToken:`訪問密鑰`,addAccessPoint:`新增存取點`,renameAccessPoint:`重命名`,rotateToken:`輪換密鑰`,rotateTokenConfirmDesc:`輪換密鑰將使所有使用舊密鑰的裝置立即斷開連接。確定要繼續嗎?`,deleteAccessPoint:`刪除`,deleteAccessPointConfirm:`確定要刪除此存取點嗎?`,manageAccessPoints:`管理存取點`,searchAccessPoints:`搜尋存取點...`,accessPointNameExists:`存取點名稱已存在`,accessPointCreated:`存取點創建成功`,accessPointRenamed:`存取點重命名成功`,accessPointDeleted:`存取點刪除成功`,tokenRotated:`訪問密鑰輪換成功`,whatIsDoh:`何為 DoH?`,dohDesc:`DNS over HTTPS (DoH) 是一種透過加密的 HTTPS 協議進行域名解析技術。`,dohBenefit1:`減少營運商等網路中間人的劫持與監聽`,dohBenefit2:`提升域名解析及網際網路訪問安全性`,learnMore:`瞭解更多`,copyUrl:`複製 URL`,browsers:`瀏覽器`,browserTitle:`瀏覽器配置 (Chrome/Edge)`,browserSteps:`設定 > 隱私和安全 > 安全 > 使用安全 DNS > 自訂 > 粘貼 URL。`,browserTips:`或者將此網址複製到地址欄:`,apple:`Apple`,appleTitle:`macOS / iOS / iPadOS / tvOS`,appleDesc:`Apple 裝置支援透過設定描述檔快速配置。`,appleWarning:`設定描述檔不可在 "封閉模式 (Lockdown Mode)" 下安裝,安裝前請確認系統不處於相關狀態。`,downloadConfig:`下載設定描述檔`,appleInstallHint:`下載後請前往「設定」>「已下載的設定描述檔」進行安裝`,windows:`MS Windows`,windowsTitle:`Windows 11 設定`,windowsStep0:`設定 > 網路與網際網路`,windowsStep1:`屬性 > DNS 伺服器 > 編輯 > 手動。`,windowsStep2:`選擇並填入 DNS IP:`,windowsStep3:`啟用 DoH (手動模板) 並粘貼 URL。`,windowsStep4:`關閉 回退到明文。`,android:`Android`,androidTitle:`Android / 私有 DNS (DoT & DoQ)`,androidDesc:`支援系統原生的「私有 DNS (DoT)」或使用第三方 App 設定 DoH。`,androidDotTitle:`私有 DNS (DoT / DoQ)`,androidDotDesc:`設定 > 網路和網際網路 > 私有 DNS > 提供者主機名 (亦支援各類現代 DoQ 客戶端):`,androidWildcardHint:`(通配符證書,路由至此配置)`,androidSingleDomainHint:`(單域名證書,路由至默認配置)`,androidIpNotice:`檢測到當前通過 IP 或本地地址訪問面板。Android 原生私有 DNS 僅接受域名,請在 .env 中配置 SERVERFULL_DOT_DOMAIN (例如 dns.example.com) 後刷新。`,androidWarning:`註:系統原生的「私有 DNS」使用 DoT 協議。若部署在 Cloudflare Workers,建議使用 Intra 等 App 設定 DoH;若運行在 Serverfull 模式並配置了 TLS 證書,可直接使用上述 DoT 主機名。`,dynamicFromDomain:`動態 (來自 "{{domain}}")`,dynamicFromDomainV6:`動態 IPv6 (來自 "{{domain}}")`,linux:`Linux`,linuxTitle:`Linux (Cloudflared DoH 代理)`,linuxDesc:`在本地執行輕量級 DNS 代理,透過 DoH 將解析請求加密轉發至 Obex。`,linuxStep1:`執行以下腳本安裝 cloudflared 並建立 systemd 服務(監聽 127.0.0.1:53)。`,linuxStep2:`將系統 DNS 修改為 127.0.0.1。`,routeros:`MikroTik`,routerosTitle:`MikroTik RouterOS`,routerosDesc:`將其設定為路由器的 DoH 伺服器,即可讓網路中的所有裝置使用此解析器。`,routerosStep1:`在 RouterOS 終端機中執行(RouterOS 7):`,routerosNote:`經路由器轉發的查詢在日誌中會顯示為路由器,而非個別裝置。如需依裝置查看日誌,請直接在裝置上設定。`,routerosDocs:`MikroTik DNS 文件`,accessPointsCardTitle:`存取點`,currentAccessPoint:`目前存取點`,selectAccessPointToConfigure:`選擇存取点以設定`,quic:`QUIC & HTTP/3`,quicTitle:`DNS over QUIC (DoQ) & HTTP/3 (DoH3)`,quicDesc:`原生支援 RFC 9250 (DNS over QUIC) 與 RFC 9460 (HTTP/3 & Encrypted Client Hello),具備 0-RTT 極速交握與防 SNI 窺探能力。`,doqTitle:`DNS over QUIC (DoQ - RFC 9250)`,doqAppHint:`適用於 AdGuard、Clash Meta (Mihomo)、Sing-box 等現代用戶端:`,doqWildcardHint:`(萬用字元憑證,路由至此設定)`,doqSingleDomainHint:`(單一網域憑證,路由至預設設定)`,doqIpNotice:`DoQ 需要 TLS 憑證與有效網域,請在設定中填入 SERVERFULL_DOT_DOMAIN 後重新整理。`,h3Title:`HTTP/3 (DoH3) & ECH (Encrypted Client Hello)`,h3Desc:`支援現代瀏覽器 (Chrome/Firefox/Safari) 透過 Alt-Svc 及 RFC 9460 HTTPS 記錄自動協商 HTTP/3 和 ECH 加密:`,ddrHint:`服務已整合 RFC 9460 Section 8 DDR (Discovery of Designated Resolvers),支援用戶端透過 _dns 網域查詢自動協商並升級至 DoQ / DoT 與 ECH。`,e2eeDesc:`使用專屬後量子混合金鑰 (P256-MLKEM768) 在邊緣即刻加密 DNS 日誌。雲端無法窺視,資料僅在通過您的硬體 Passkey 授權的客戶端本地解密並分析。`,detecting:`正在檢測...`,dotUnavailableCfTitle:`DoT / DoQ 在 Cloudflare Worker 模式下無法使用`,dotUnavailableCfDesc:`目前運行於 Cloudflare Worker (Serverless) 模式。受邊緣無伺服器執行環境網路限制,不支援監聽原生 TCP/UDP 853 連接埠。若需使用 DoT 或 DoQ,請部署在獨立伺服器 (Serverfull 模式) 下;在目前模式下請使用 DoH (DNS over HTTPS)。`},a={title:`自訂規則`,subtitle:`手動控制特定域名的解析行為(屏蔽、放行或重新導向)。`,action:`行動`,typeBlock:`攔截 (BLOCK)`,typeAllow:`放行 (ALLOW)`,typeRedirect:`重新導向 (REDIRECT)`,pattern:`域名模式 (Pattern)`,patternPlaceholder:`example.com 或 *.test.com`,addRule:`添加規則`,redirectSettings:`重新導向記錄設定`,aRecord:`A 記錄 (IPv4)`,aaaaRecord:`AAAA 記錄 (IPv6)`,cnameRecord:`CNAME 記錄`,txtRecord:`TXT 記錄`,optional:`(選填)`,ipv4Placeholder:`例如: 1.2.3.4`,ipv6Placeholder:`例如: 2001:db8::1`,cnamePlaceholder:`例如: target.com`,txtPlaceholder:`輸入文本內容...`,saveRedirectRule:`儲存並添加重新導向規則`,noRulesTitle:`無自訂規則`,noRulesDesc:`您還沒有添加任何手動規則。`,tableAction:`行動`,tablePattern:`模式`,tableDetails:`解析詳情`,tableOps:`操作`,labelBlock:`攔截`,labelAllow:`放行`,labelRedirect:`重新導向`,detailBlock:`解析至`,detailAllow:`轉發至上游服務`,patternEmpty:`域名模式不能為空`,duplicatePattern:`該域名的規則已存在`,addSuccess:`規則新增成功`,addFailed:`新增規則失敗`,updateSuccess:`規則更新成功`,updateFailed:`更新規則失敗`,tableCreatedAt:`新增時間`,clickToSort:`點擊排序`,editRule:`編輯規則`,cancel:`取消`,saveChanges:`儲存變更`,delete:`刪除`,close:`關閉`},o={customRange:`自訂時間範圍`,startTime:`開始時間`,endTime:`結束時間`,apply:`應用`,custom:`自訂`,totalQueries:`總查詢`,blocked:`攔截`,redirected:`重新導向`,blockRate:`攔截率`,activeDevices:`活躍裝置`,activeIPs:`活躍 IP 地址`,queryTrend:`查詢趨勢`,noRecords:`此時間段內暫無記錄`,topAllowed:`常用域名 (Allowed)`,topBlocked:`攔截頻率最高 (Blocked)`,noData:`暫無數據`,clientActivity:`客戶端活躍度`,ipAddress:`IP 地址`,location:`歸屬地`,queries:`查詢數`,destinationDistribution:`流量目的地分佈`,requests:`請求`,topIsps:`主要營運商`,pressToShowMore:`按下顯示更多`,maximize:`放大`,minimize:`縮小`,loading:`載入中...`},s={title:`查詢日誌`,subtitle:`查看本配置 DNS 解析日誌和攔截記錄。`,realtime:`即時`,allStatus:`全部狀態`,onlyPass:`放行`,onlyBlock:`攔截`,onlyRedirect:`重新導向`,searchPlaceholder:`搜尋域名...`,noResults:`未找到結果`,noRecords:`尚無記錄`,noResultsDesc:`沒有找到匹配 "{{query}}" 的域名記錄。`,noRecordsDesc:`選定的時間範圍內沒有收到任何 DNS 查詢。`,tableTime:`時間`,tableDomain:`域名`,tableAnswer:`解析結果`,tableSource:`來源`,tableType:`類型`,tableStatus:`狀態`,tableReason:`原因`,loadedAll:`已載入全部 {{count}} 條記錄`,realtimeLoadMoreTip:`關閉「即時」模式以載入更多`,filterAccessPoint:`存取點`,allAccessPoint:`全部存取點`,allCountry:`全部國家`,filterCountry:`目的地國家`,allIsp:`全部營運商`,filterIsp:`營運商`,timeRange:`時間範圍`,clearFilters:`清除篩選`,details:`查詢詳情`,detailAccessPoint:`存取點`,basicInfo:`基礎資訊`,detailDomain:`域名`,detailType:`類型`,detailTime:`時間`,detailLatency:`耗時`,detailProfile:`所用配置`,detailStatus:`狀態`,detailUpstream:`上游伺服器`,detailReason:`原因`,detailNoReason:`無 (已放行)`,detailECS:`ECS`,resolutionResult:`解析結果`,noResult:`無結果`,networkDetails:`網路詳情`,clientSource:`客戶端 (源)`,destination:`目的地`,quickActions:`快捷操作`,actionAllow:`放行`,actionBlock:`攔截`,actionRedirect:`重新導向`,statusPass:`放行`,statusBlock:`攔截`,statusRedirect:`重新導向`,export:`匯出日誌`,rewritten:`已重寫`,logDetails:`查詢詳情`,searchCountry:`搜尋國家...`,noMatchingCountries:`未找到相符國家`,searchIsp:`搜尋電信業者...`,noMatchingIsps:`未找到相符電信業者`,loadingIsps:`正在載入電信業者...`,loggingDisabledTitle:`日誌記錄已關閉`,loggingDisabledDesc:`目前設定的日誌留存已設為「關閉」,系統不記錄任何查詢日誌。如需查看,請在設定中啟用日誌留存。`,detailIsClientEncrypted:`是否使用者端加密`,encryptedLocked:`是 (未解鎖)`,encryptedDecrypted:`是 (已本機解密)`,encryptedYes:`是`,notEncrypted:`否`,encryptedRecord:`[加密記錄]`,exportSuccess:`日誌匯出成功`,exportError:`日誌匯出失敗`},c={title:`外部攔截清單`,subtitle:`訂閱第三方維護的域名屏蔽清單 (如 AdGuard 等)。`,formatDisclaimer:`本系統只支援有限的 AdGuard 格式列表和純域名列表。僅支援基礎的攔截規則(如 ||example.com^),會繞過 "@@" 白名單,暫不支援 "$" 重寫規則以及 "*" 省略規則。系統會自動匹配域名的所有子域名。`,syncAll:`同步所有清单`,urlPlaceholder:`輸入攔截清單 URL...`,addSubscription:`添加訂閱`,supportedFormats:`支援的清單格式:`,formatAdGuard:`相容 ||domain.com^ 等過濾規則。`,formatHosts:`相容標準 hosts 文件 (例如 0.0.0.0 domain.com)。`,noListsTitle:`尚未添加清單`,noListsDesc:`您還沒有訂閱任何外部攔截清單。`,tableUrl:`清單 URL`,tableLastSync:`上次同步`,tableStatus:`狀態`,tableOps:`操作`,neverSynced:`從未`,enabled:`正常`,disabled:`同步失敗`,statusNormal:`正常`,statusDisabled:`已停用`,statusOutdated:`過時`,statusMissing:`缺失`,presetOisdBig:`OISD Big (廣告/惡意軟體等攔截)`,presetOisdNsfw:`OISD NSFW (色情/震撼/成人內容攔截)`,presetAdGuard:`AdGuard AdServers (廣告伺服器)`,presetStevenBlack:`StevenBlack Hosts (綜合攔截)`,syncTaskStarted:`同步任務已啟動`,syncCheckComplete:`同步檢查已完成`,syncTaskFailed:`同步任務失敗`,syncing:`正在同步...`,syncSuccess:`清單同步成功`,syncError:`清單同步失敗`,syncErrorDesc:`無法下載或解析此攔截列表。請檢查連結是否正確,並確保該連結可以正常存取。`,deleteConfirm:`確定要刪除此清單訂閱嗎?`,deleteSuccess:`清單刪除成功`,deleteError:`清單刪除失敗`,addSuccess:`清單新增成功`,addError:`清單新增失敗`,listEnabled:`清單已啟用`,listEnabledHint:`停用後將停止使用此清單進行過濾,但不會刪除該清單,其訂閱與同步紀錄會保留。`,listEnabledToast:`已啟用清單 - 正在重建過濾器`,listDisabledToast:`已停用清單 - 正在重建過濾器`,checkDomain:`檢查網域`,checkDomainHint:`清單以精簡過濾器的形式儲存,因此無法瀏覽其中的項目。您可以立即針對此清單測試任意網域。`,check:`檢查`,checkBlocked:`已被此清單封鎖`,checkNotBlocked:`未被此清單封鎖`,checkViaParent:`(比對到上層網域)`,checkNotSynced:`此清單尚未完成同步,因此暫無可供檢查的內容。`,checkListDisabled:`此清單目前已停用,因此現在不會過濾任何內容。`,checkProbabilisticRate:`過濾器屬於機率性:大約每 {{odds}} 次比對中有 1 次為誤判。新增例外規則也會涵蓋其所有子網域。`,checkProbabilistic:`過濾器屬於機率性,因此少部分比對屬於誤判。新增例外規則也會涵蓋其所有子網域。`,checkAllowAction:`仍然允許`,checkAllowed:`已新增例外 - 該網域現在可以解析`,allowRuleAdded:`已為 {{domain}} 新增例外`,listDetails:`清單詳情`},l={title:`配置設定`,subtitle:`管理您的 DNS 解析策略和進階選項。`,saveChanges:`儲存所有更改`,saveSuccess:`設定已成功儲存`,saveError:`儲存設定失敗,請稍後再試`,upstreamTitle:`上游服務`,dohUrl:`DoH URL`,dnsIP:`DNS IP`,classicDns:`經典 DNS`,httpsOnly:`僅支援 HTTPS`,upstreamDesc:`所有未被攔截的查詢將由本應用轉發至此伺服器。支援 DoH (HTTPS)、DoT (tls://)、DNS Stamp (sdns://) 或經典 DNS。經典 DNS 透過 TCP 轉發。`,defaultPolicyTitle:`默認過濾行為`,onNoMatch:`未匹配規則時`,allowAll:`允許所有查詢`,blockAll:`攔截所有查詢`,logRetentionTitle:`日誌留存`,retentionDuration:`留存時長`,retentionDesc:`日誌將自動清除。`,skipLogOnPass:`不記錄放行紀錄`,skipLogOnPassDesc:`啟用後不記錄正常允許的 DNS 查詢,僅記錄被攔截、重新導向或解析失敗的紀錄。可大幅減少儲存空間與寫入操作。`,ecsTitle:`EDNS 客戶端子網 (ECS)`,ecsUpstreamHint:`並非所有上游 DNS 都支援 ECS。對於 ControlD 等遞迴解析器,ECS 僅影響其內部路由,不會傳遞給上游權威伺服器。`,sendEcs:`呈遞 ECS`,customSubnet:`自訂子網`,customIpv4:`自訂 IPv4 子網`,customIpv6:`自訂 IPv6 子网`,ipv4Hint:`(例如 1.2.3.0/24)`,ipv6Hint:`(例如 2001:db8::/32)`,testToolTitle:`即時解析測試工具`,testDomain:`測試域名`,recordType:`記錄類型`,runTest:`執行測試`,domainPlaceholder:`例如 google.com`,hitRule:`命中規則 / 原因`,defaultAllow:`默認放行策略`,parseLatency:`解析耗時`,sourceIp:`源 IP 地址`,answerSection:`解析結果回顯 (Answer Section)`,noRecordsReturned:`本次查詢未返回任何記錄。`,diagnostics:`診斷資訊`,errorDetail:`錯誤詳情`,upstreamResponse:`上游回應內容`,presetCloudflareSecurity:`Cloudflare Security (快速/去惡意域名/去釣魚域名)`,presetCloudflareFamilies:`Cloudflare Families (快速/去惡意域名/去成人內容)`,presetQuad9:`Quad9 (去惡意域名/無日誌/不呈遞 ECS)`,presetQuad9Ecs:`Quad9 ECS (去惡意域名/無日誌/呈遞 ECS)`,presetControlDFree:`ControlD Free (去廣告/去跟蹤/去惡意域名/去釣魚域名)`,presetControlDUncensored:`ControlD Uncensored (抗審查)`,presetAdGuard:`AdGuard (去廣告/去跟蹤)`,presetAdGuardFamily:`AdGuard Family protection (去廣告/去跟蹤/去成人內容/安全搜尋)`,retention10m:`10 分鐘`,retention1h:`1 小時`,retention24h:`24 小時`,retention7d:`7 天`,retention30d:`30 天`,retention90d:`90 天`,retention180d:`180 天`,retention360d:`360 天`,retentionMaxSuffix:`(最長)`,daysUnit:`天`,blockModeTitle:`攔截響應模式`,blockModeDesc:`當域名被攔截時返回的内容`,default:`默認`,NXDOMAIN:`域名不存在`,NODATA:`空回答`,customIP:`自訂 IP`,export:`匯出配置`,exportSuccess:`配置成功匯出`,exportError:`配置匯出失敗,請稍後再試`,nameUpdateSuccess:`配置名稱已更新`,saving:`正在儲存...`,echTitle:`重寫 ECH`,enableEch:`啟用重寫 ECH`,echDesc:`針對由 Cloudflare 代理的網域名稱,當查詢 HTTPS (Type 65) 或 SVCB (Type 64) 記錄時,自動重寫注入 ECH 參數與表層 SNI 以保護存取隱私。`,echFrontingTitle:`表層 SNI (ECH Fronting)`,echFrontingDesc:`TLS 握手外層可見的偽裝網域`,echFrontingCustomPlaceholder:`輸入自訂表層網域,例如 crypto.cloudflare.com`,echPreferred:`首選`,whatIsEch:`何為 ECH (Encrypted Client Hello)?`,echHelpDesc:`ECH (加密客戶端問候) 是 TLS 1.3 的重要隱私擴充,它將 TLS 握手中的真實目標網域名稱 (Inner SNI) 全程加密,僅向網路中間人暴露表層偽裝網域名稱 (Outer SNI / ECH Fronting)。`,echHelpBenefit1:`防止中間人及 ISP 窺探您正在存取的具體網頁與子網域名稱`,echHelpBenefit2:`配合 DoH 與自訂表層 SNI,消除明文 SNI 阻斷與識別風險`,retentionDisabled:`關閉 (隱私需要)`,retentionDisabledDesc:`已關閉日誌記錄。系統將不會記錄任何 DNS 查詢日誌並立即清空歷史日誌。`,localRetentionTitle:`本地儲存與留存`,localRetentionDuration:`本地保留時長`,localRetentionDesc:`日誌將持久化保存在當前瀏覽器本地的 SQLite 資料庫 (OPFS) 中,提供秒級本地搜尋並節省雲端儲存費用。超出此時長的本地日誌將自動清除。`,localRetention30d:`30 天`,localRetention90d:`90 天 (推薦)`,localRetention180d:`180 天`,localRetention365d:`365 天 (1 年)`,localRetentionUnlimited:`永久保留 (直到手動清除)`,localEngineStorage:`儲存引擎`,localEngineOpfs:`OPFS 本地持久化`,localEngineMemory:`記憶體暫存 (無痕/隱私模式)`,localCachedCount:`本地已快取日誌`,localCachedCountUnit:`{{count}} 筆記錄`,clearLocalData:`清空本地日誌`,clearLocalDataConfirm:`確定要清空此配置在本地保存的所有日誌嗎?(雲端日誌不受影響)`,clearLocalDataSuccess:`本地日誌已成功清空`,clearLocalDataError:`清空本地日誌失敗`,e2eeTitle:`日誌端到端加密 (E2EE)`,e2eeEnabled:`已啟用`,e2eeDisabled:`未啟用`,e2eeDesc:`使用專屬後量子混合金鑰 (P256-MLKEM768) 在邊緣即刻加密 DNS 日誌。雲端無法窺視,資料僅在通過您的硬體 Passkey 授權的客戶端本地解密並分析。`,e2eePasskeyRequired:`端到端加密需要硬體 Passkey (WebAuthn) 支援。檢測到您尚未在帳號中綁定 Passkey,請先在「帳號設定」中添加 Passkey。`,e2eeGoToAccount:`前往帳號設定添加 Passkey`,e2eeEnableSwitch:`啟用日誌端到端加密`,e2eeKeyType:`加密演算法`,e2eeDeviceState:`當前裝置狀態`,e2eeUnlocked:`已在此裝置解鎖`,e2eeUnlockButton:`驗證 Passkey 解鎖`,e2eeProtectedPasskeys:`已授權 Passkey 憑證`,e2eeDevicesUnit:`個憑證`,e2eeEnableSuccess:`端到端加密已成功啟用`,e2eeEnableError:`啟用端到端加密失敗`,e2eeDisableSuccess:`端到端加密已關閉`,e2eeDisableError:`關閉端到端加密失敗`,e2eeUnlockSuccess:`私鑰已成功在此裝置解鎖`,e2eeUnlockError:`驗證 Passkey 解鎖失敗`,e2eeDisableConfirm:`確定要關閉端到端加密嗎?關閉後新產生的 DNS 日誌將以明文儲存。(已加密的歷史日誌仍需私鑰解密)`,e2eeUnlockPrompt:`此配置已啟用端到端加密,請驗證 Passkey 解鎖查看完整明文日誌。`,e2eeUnlockNow:`解鎖加密日誌`,e2eeUnlockRecovery:`使用修復金鑰解鎖`,securityTitle:`安全與存取`,rotateKeyTitle:`存取憑據 (URL)`,rotateKeyDesc:`為此設定檔重新產生新的存取 URL。目前使用舊 URL 的所有裝置將立即中斷與 DNS Worker 的連線,需使用新 URL 重新設定。`,rotateKeyBtn:`輪換存取憑據`,copied:`已複製到剪貼簿`,nameUpdateFailed:`更新失敗`},u=JSON.parse(`{"title":"帳號設定","subtitle":"管理您的安全憑據和系統用戶。","e2ee":{"title":"端到端加密 (E2EE)","desc":"採用客戶端後量子混合加密 (P256-MLKEM768 + AES-256-GCM / NIST FIPS 203) 硬體信封加密技術。資料在邊緣即刻加密,伺服端僅儲存不可逆密文,只有已授權硬體 Passkey 的設備可在本機解密並分析。","pqcTag":"PQC / NIST FIPS 203","upgradePqcBtn":"升級到後量子 (PQC)","upgradePqcSuccess":"已成功升級到後量子混合加密 (P256-MLKEM768)","upgradePqcError":"升級到後量子加密失敗","enabled":"已啟用","disabled":"未啟用","passkeyRequired":"端到端加密需要硬體 Passkey (WebAuthn) 支援。請在上方「雙重認證 (MFA)」設定中先新增至少一個 Passkey 憑證。","scopesTitle":"資料加密範圍","scopesSubtitle":"選擇透過硬體 Passkey 進行端到端加密保護的資料類型。","scopeLogsTitle":"DNS 查詢日誌","scopeLogsDesc":"在邊緣加密 DNS 請求的網域名稱、客戶端 IP、解析紀錄及上游資訊,防止雲端日誌被窺探或留存明文。","scopeRulesTitle":"自訂規則與重新導向","scopeRulesDesc":"端到端加密自訂封鎖/允許規則與 URL 重新導向設定。","scopeClientsTitle":"裝置別名與客戶端元資料","scopeClientsDesc":"端到端加密存取點裝置名稱、IP 別名與拓撲資訊。","statusActive":"已保護","statusPlanned":"規劃中","profileSelect":"設定 Profile","keyType":"加密演算法","deviceState":"目前裝置狀態","unlocked":"已在此裝置解鎖","unlockButton":"驗證 Passkey 解鎖","protectedPasskeys":"已授權 Passkey 憑證","devicesUnit":"個憑證","enableSuccess":"端到端加密已成功啟用","enableError":"啟用端到端加密失敗","disableSuccess":"端到端加密已關閉","disableError":"關閉端到端加密失敗","unlockSuccess":"私鑰已成功在此裝置解鎖","unlockError":"驗證 Passkey 解鎖失敗","disableConfirm":"確定要關閉端到端加密嗎?關閉後新產生的 DNS 日誌將以明文儲存。(已加密的歷史日誌仍需私鑰解密)","noKeypairTitle":"尚未產生端到端加密金鑰對","noKeypairDesc":"端到端加密需要為您的帳戶產生專屬的後量子 (P256-MLKEM768) 金鑰對。私密金鑰將透過通行金鑰 (Passkey) 或修復金鑰 (Recovery Key) 進行硬體/信封加密保護,伺服器無法解密您的 DNS 記錄。","initPasskeyBtn":"驗證現有 Passkey 產生金鑰對","addPasskeyBtn":"新增 Passkey 以支援硬體加密","initRecoveryBtn":"透過修復金鑰產生金鑰對","initWithRecoveryTitle":"透過修復金鑰產生金鑰對","initWithRecoveryDesc":"請輸入您目前的 30 位緊急修復金鑰。系統將在本地產生後量子 (P256-MLKEM768) 金鑰對並透過該金鑰信封加密。使用後修復金鑰將自動輪換以確保安全。","keypairGenSuccess":"端到端加密金鑰對已成功產生!","keypairGenError":"產生端到端加密金鑰對失敗","unlockRecoveryButton":"使用修復金鑰解鎖","unlockWithRecoveryTitle":"使用修復金鑰解鎖","unlockWithRecoveryDesc":"請輸入 30 位緊急修復金鑰。注意:根據單次使用規則,修復金鑰驗證成功後將立即自動輪換。","recoveryKeyInputLabel":"修復金鑰","unlockConfirm":"解鎖私密金鑰","recoveryUnlockFailed":"修復金鑰無效或解析失敗","generateKeypairBtn":"產生金鑰對","enableSwitch":"啟用查詢日誌端到端加密","enableSwitchDesc":"在用戶端邊緣加密 DNS 請求的網域名稱、用戶端 IP、解析記錄及上游資訊後再寫入雲端持久化儲存。","trustBoundaryTitle":"安全與信任邊界提示","trustBoundaryDesc":"所有 DNS 查詢流量在網路解析時必然會經過託管伺服器(Cloudflare Workers 邊緣節點或自建主機執行階段),伺服器在執行階段記憶體中處理解析後將記錄加密寫入。本功能僅用於保護雲端持久化資料庫(D1 / 資料倉儲)免受靜態資料洩漏或離線分析,無法向正在處理轉發請求的託管伺服器隱藏即時網路流量。","initFailed":"初始化端對端加密失敗","lockDevice":"鎖定此裝置","lockSuccess":"已鎖定,私鑰已從本機清除","enrollPasskeyBtn":"關聯當前通行金鑰","enrollPasskeySuccess":"已成功將通行金鑰關聯至端到端加密!","enrollPasskeyFailed":"關聯通行金鑰失敗","resetKeypairBtn":"重置金鑰對","confirmResetBtn":"確認重置並重新生成","resetKeypairTitle":"重置端到端加密金鑰對","resetKeypairDesc":"如果您的恢復金鑰不匹配或無法解密,且未關聯通行金鑰,重置金鑰對將從服務端清除舊金鑰,並為您重新生成全新的後量子 (P256-MLKEM768) 金鑰對。","resetKeypairPasskeyNotice":"確認後將立即調用您當前已配置的通行金鑰 (Passkey) 完成新金鑰對的加密與授權。","resetKeypairRecoveryNotice":"確認後將引導您輸入當前恢復金鑰以完成新金鑰對的生成與加密。","resetAndInitPasskeySuccess":"金鑰對已成功重置並使用通行金鑰重新加密!","resetSuccessEnterRecovery":"舊金鑰已清除,請使用恢復金鑰初始化新金鑰對。","resetFailed":"重置金鑰對失敗"},"jwtSecretWarningTitle":"安全提示:JWT 金鑰強度較弱","jwtSecretWarningDesc":"目前 JWT_SECRET 長度小於 32 位元字元,存在潛在安全隱患。請先設定環境變數 KEK_v1 完成憑證信封加密遷移,然後再輪換 JWT_SECRET,以防止既有加密的 TOTP 或復原金鑰無法解密。","roleAdmin":"系統管理員","roleUser":"用戶","personalInfo":"個人資訊","username":"用戶名","userId":"用戶 ID","changePassword":"修改密碼","currentPassword":"當前密碼","newPassword":"新密碼","updatePassword":"更新密碼","userManagement":"用戶管理 (管理員)","createUser":"創建用戶","role":"角色","createdAt":"創建時間","lastActive":"最後活動時間","lastResolve":"最後解析時間","actions":"操作","dangerZone":"危險區域","clearLogs":"清空所有日誌","clearLogsDesc":"這將刪除您名下所有 DNS 配置生成的查詢日誌。此操作不可恢復。","currentDbSize":"當前資料庫大小","clearLogsBtn":"立即清空日誌","deleteAccount":"註銷帳號","deleteAccountDesc":"永久刪除您的帳號及所有關聯數據。此操作將導致您失去所有解析配置。","deleteAccountBtn":"註銷並刪除帳號","createNewUser":"創建新用戶","initialPassword":"初始密碼","userRole":"用戶角色","cancel":"取消","createNow":"立即創建","formatTipUsername":"用戶名格式提示:僅限 5 至 32 位 Unix 用戶名格式","formatTipPassword":"密碼格式提示:12-100位字元","passwordSuccess":"密碼修改成功","updateFailed":"修改失敗","confirmDeleteUser":"確定要刪除此用戶嗎?其擁有的所有配置也將被移除。","confirmClearLogs":"確定要清空您名下所有配置的查詢日誌嗎?此操作不可撤銷。","clearLogsSuccess":"日誌已成功清空","confirmDeleteAccount":"警告:確定要註銷並刪除本帳號嗎?\\n\\n閣下所有 DNS 配置、自訂規則和查詢日誌將被永久刪除。此操作不可逆!","inactivityPolicy":"活躍度政策","inactivityDesc":"為保護用戶隱私並節省資源,超過 30 天無解析記錄的帳戶,其所有關聯查詢日誌及 DNS 配置將被自動清除。帳戶本身在 90 天無登錄後將予移除。","systemSettings":"系統設定 (管理員)","adminOnlyView":"僅管理員可見","featureToggle":"功能開關","enableTurnstileSignup":"註冊頁面啟用人機驗證","enableTurnstileLogin":"登入頁面啟用人機驗證","totp":{"title":"數位驗證器 (TOTP)","enabled":"啟用","disabled":"停用","setupDesc":"為帳號新增額外的安全層。支持 Google Authenticator、Authy 或任何 TOTP 相容應用。","setupBtn":"設定數位驗證器","scanQR":"使用驗證器應用掃描此二維碼:","orEnterManually":"或手動輸入密鑰:","enterCode":"請輸入驗證器代碼進行確認:","enterCodeHint":"輸入 6 位驗證碼以確認密鑰已正確錄入。","activate":"啟用數位驗證器","skipPassword":"無密碼登入 — 登入時只顯示 TOTP 輸入框(隐藏密碼)","disable":"停用數位驗證器","disableTitle":"停用數位驗證器","disableWarning":"停用數位驗證器後,若未設定通行金鑰,帳號將僅受密碼保護。確定要停用嗎?","confirmDisable":"確認停用","recoveryKeysTitle":"請保存您的恢復密鑰","recoveryKeysWarning":"請將恢復金鑰妥善保存(如密碼管理器)。每個金鑰只能使用一次。","copyKeys":"複製全部密鑰","copied":"已複製!","done":"完成,我已保存密鑰","activeDesc":"數位驗證器已啟用,正在為您的帳號提供 6 位動態驗證碼保護。","invalidCode":"請輸入 6 位驗證碼","testBtn":"測試動態口令","testTitle":"測試數位驗證器 (TOTP)","testDesc":"請輸入身分驗證器 App 目前顯示的 6 位驗證碼,以驗證其設定是否正確並已準確同步。","testSuccess":"驗證成功!您的身分驗證器設定正確並能正常產生驗證碼。","testFailed":"驗證碼錯誤或裝置時鐘未同步","verificationCode":"6 位驗證碼","verifyBtn":"立即驗證"},"sessions":{"session":"會話","title":"活躍會話","device":"裝置","ip":"IP 位址","loginTime":"登入時間","expires":"過期時間","action":"操作","current":"目前會話","revoke":"撤銷","revokeFailed":"撤銷會話失敗。","empty":"未找到活躍會話。","unknownDevice":"未知裝置","revokeOthers":"撤銷其他會話","confirmRevokeOthers":"撤銷其他會話","revokeOthersConfirmText":"確定要撤銷所有其他活躍會話嗎?除當前設備外,其他已登錄設備將立即退出。"},"activity":{"title":"帳號活動記錄","action":"事件","time":"時間","session":"會話","ip":"IP 位址","empty":"暫無活動記錄。","loadMore":"載入更多","signup":"註冊","loginSuccess":"登入成功","loginFail":"登入失敗","logout":"登出","pwChangeSuccess":"密碼已修改","pwChangeFail":"密碼修改失敗","totpVerifySuccess":"TOTP 驗證通過","totpVerifyFail":"TOTP 驗證失敗","totpSetup":"設定數位驗證器","totpRemoved":"停用數位驗證器","recoveryKeyUsed":"使用了恢復密鑰","sessionRevoked":"會話已撤銷","reasons":{"user_active":"主動登出","user_revoke":"撤銷會話","expired":"會話已過期","geolocation_mismatch":"異地存取限制","geolocation_missing":"缺少地理位置資訊","token_reuse":"憑證重複使用(安全警告)"},"passkeyRegistered":"新增通行密鑰","passkeyDeleted":"刪除通行密鑰","passkeyVerifySuccess":"通行密鑰驗證通過","passkeyVerifyFail":"通行密鑰驗證失敗","recoveryKeyRotated":"應急恢復金鑰已輪換","device":"裝置"},"passkey":{"title":"通行密鑰 (Passkey)","addBtn":"新增通行密鑰","desc":"使用生物識別(Touch ID / Face ID / Windows Hello)或硬體安全金鑰(YubiKey)進行快捷且防釣魚的多因素認證。","empty":"尚未新增任何通行密鑰。","addTitle":"新增通行密鑰","nameLabel":"通行密鑰名稱","namePlaceholder":"例如:my_passkey","promptNotice":"點擊繼續後,瀏覽器將彈出系統提示,引導您完成生物識別或硬體安全金鑰註冊。","continue":"繼續","renameTitle":"重命名通行密鑰","deleteTitle":"刪除通行密鑰","deleteConfirm":"確定要刪除通行密鑰 \\"{{name}}\\" 嗎?刪除後將無法使用該通行密鑰進行多因素認證。","defaultName":"通行密鑰","notSupported":"您的瀏覽器或目前環境不支援通行密鑰 (WebAuthn)。","regFailed":"通行密鑰註冊失敗","count":"{{count}} 個已設定","none":"未設定","created":"新增時間","lastUsed":"最後使用","neverUsed":"從未在登入中使用","updated":"通行密鑰更新成功","registered":"通行密鑰新增成功","deleted":"通行密鑰已刪除","appNameLabel":"網域名稱","appNameHelper":"通行密鑰將綁定至此網域","formatTip":"通行密鑰名稱提示:1-30 位字元,只允許字母、數字、下劃線和中劃線","verifyPrompt":"請使用裝置的生物辨識(人臉、指紋、Windows Hello)或安全金鑰進行身分驗證。","changePwNotice":"提交修改時將調用通行金鑰 (Passkey) 生物辨識或硬體金鑰核驗身分。","submitNotice":"提交時將調用通行金鑰 (Passkey) 生物辨識或硬體金鑰核驗身分。","testBtn":"測試通行金鑰","testThis":"測試此通行金鑰","testSuccess":"通行金鑰驗證成功:{{name}}","testCancelled":"通行金鑰驗證已取消","testFailed":"通行金鑰驗證失敗"},"mfaNone":"無","resetUserPassword":"重設密碼","resetPasswordTitle":"重設使用者密碼","resetPasswordWarning":"重設後,該使用者的所有現有工作階段將立即登出,使用者需使用新密碼重新登入。","resetPasswordSuccess":"密碼已成功重設,使用者舊登入工作階段已登出。","confirmReset":"確認重設","mfa":{"title":"多因素驗證 (MFA)","enabled":"已設定","notConfigured":"未設定","desc":"通行金鑰 (Passkey) 與數位驗證器 (TOTP) 均可作為多因素驗證方式,設定任選其一即可保障帳號安全並支援無密碼登入與密碼重設。","passwordlessTitle":"無密碼登入 (Passwordless Login)","passwordlessDesc":"啟用後,登入時無需輸入密碼,直接透過通行金鑰 (Passkey) 或數位驗證碼 (TOTP) 完成身分核驗。","unconfiguredHint":"目前帳號尚未設定任何多因素驗證。設定通行金鑰或數位驗證器後,可啟用無密碼快速登入並在忘記密碼時安全找回帳號。","password":"密碼","passkey":"通行密鑰 (Passkey)","totp":"身份驗證器 (TOTP)"},"recoveryKey":{"title":"緊急修復金鑰","desc":"緊急修復金鑰用於在遺失所有通行金鑰或驗證器時重新登入帳戶或重設密碼。為保證零知識安全,修復金鑰採用單向不可逆密碼學雜湊儲存,無法在網頁端直接解密檢視,僅支援輪換。使用後金鑰將立即自動輪換。","configured":"已設定","notConfigured":"未設定","rotateBtn":"輪換修復金鑰","yourKey":"您的 30 位緊急修復金鑰","securityTip":"請妥善保管此修復金鑰。擁有此金鑰者可以重獲您的帳戶存取權限。","rotateConfirm":"輪換修復金鑰將立即使原修復金鑰失效,並重新衍生信封加密金鑰。僅允許透過現有通行金鑰 (Passkey) 或原修復金鑰完成輪換驗證。是否繼續?","confirmRotateBtn":"確認進入輪換驗證","rotateVerifyTitle":"驗證身分以輪換修復金鑰","rotateSuccess":"修復金鑰已成功輪換!請立即妥善離線儲存新金鑰,舊金鑰已失效。","oneWayHash":"單向不可逆雜湊","oneWayTooltip":"採用 SHA-256 單向密碼學雜湊保護,伺服器不儲存明文且不可解密,僅支援身分輪換驗證。","newKeyGenerated":"已產生全新 30 位修復金鑰(請立即儲存)","oneWayWarning":"注意:此金鑰僅在此顯示一次,關閉後將無法再次檢視!伺服器僅儲存不可逆雜湊,請務必將其妥善複製並離線安全儲存。","autoRotatedTitle":"修復金鑰已自動輪換","autoRotatedNotice":"根據修復金鑰單次使用規則,您的原修復金鑰已失效,系統已為您產生全新 30 位緊急修復金鑰。請務必立即複製並妥善離線儲存,關閉後將無法再次檢視!","savedAndClose":"我已妥善儲存並繼續","savedAndContinue":"我已妥善儲存並進入控制台","descOneWay":"緊急恢復密鑰是在您遺失所有通行密鑰 (Passkey) 或身份驗證器時,恢復帳號存取及端對端解密資料的唯一憑據。為確保零知識安全,恢復密鑰採用單向不可逆雜湊儲存,無法在網頁端檢視明文,僅支援透過原恢復密鑰或現有 Passkey 進行輪換。密鑰一旦被使用將立即自動輪換。","keyRequired":"請輸入緊急恢復密鑰","verifyBtn":"恢復密鑰","verifyPromptLabel":"原恢復密鑰 (Original Recovery Key)","rotateSuccessE2eeWarning":"恢復金鑰已成功輪換!注意:由於端到端加密此前處於鎖定狀態且未能解密,舊 E2EE 密文仍需使用輪換前的舊恢復金鑰解鎖,或可在下方端到端加密卡片中點擊「重置金鑰對」。"},"verifyIdentity":"安全身分核驗","verifyIdentityDesc":"為了保護您的帳戶安全,執行此敏感操作前請先驗證您的身分。","usePasskeyInstead":"使用通行金鑰驗證","changePasswordWithPasskey":"使用通行金鑰核驗並修改密碼","enableRegistration":"開放新使用者註冊","timezone":"時區","timezoneAuto":"自動(跟隨瀏覽器預設)","totpCode":"驗證器動態口令"}`),d={item1Title:`Obex 保護性 DNS`,item1Desc:`基於 Cloudflare Workers & D1 的 DNS 解析器,盡力提高速度與安全。`,item2Title:`DoH 隱私保護`,item2Desc:`透過 DNS over HTTPS (RFC 8484) 加密隧道防止劫持並增強隱私。`,item3Title:`全球邊緣網路`,item3Desc:`依托 Cloudflare 龐大的邊缘網路,獲得極速解析與極低延遲體驗。`,item4Title:`精細化過濾`,item4Desc:`全面支援黑白名單、AdGuard 規則以及自訂 DNS 記錄重新導向。`,item5Title:`即時統計分析`,item5Desc:`透過詳細的查詢日誌和綜合統計儀表盤,直观展現網路健康狀況。`,item6Title:`現代 UI 與暗黑模式`,item6Desc:`為工程師和家庭用戶設計的高密度管理面板,基於由 Palantir 開發的 Blueprint UI。`,item7Title:`雲原生基礎架構`,item7Desc:`無伺服器架構確保零維護和私人 DNS 的無限擴充性。`,item8Title:`多配置管理`,item8Desc:`為不同裝置或網路環境創建並管理多個獨立配置。`,item9Title:`多存取點管理`,item9Desc:`為不同裝置配置專屬的 DNS 存取點,實現日誌和統計數據的精细化隔離與分析。`},f={title:`DNS Worker 部署與配置指南`,subtitle:`您的系統實例尚未完全配置。請按照以下步驟完成部署以啟用 DNS Worker。`,dbStatus:`D1 資料庫綁定 (env.DB)`,jwtStatus:`JWT 金鑰 Secrets (env.JWT_SECRET)`,configured:`已配置`,missing:`缺失 / 未配置`,refresh:`重新檢測並重新整理`,methodCli:`CLI 命令行部署`,methodDash:`Cloudflare 控制台部署`,copy:`複製`,copied:`已複製!`,stepDbTitle:`1. 建立與綁定 D1 資料庫`,stepDbDesc:`在 Cloudflare 中建立 D1 資料庫實例,在終端機執行以下命令:`,stepDbCommand1:`wrangler d1 create dns_worker_db`,stepDbDesc2:`複製終端機輸出的配置資訊,並貼到專案根目錄的 wrangler.toml 裡的 [[d1_databases]] 中:`,stepDbCommand2:`[[d1_databases]]
2
2
  binding = "DB"
3
3
  database_name = "dns_worker_db"
4
4
  database_id = "<您的_D1_DATABASE_ID>"
package/static/index.html CHANGED
@@ -8,16 +8,16 @@
8
8
  <link rel="shortcut icon" href="/favicon.ico">
9
9
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10
10
  <title>DNS Worker</title>
11
- <script type="module" crossorigin src="/assets/index-CbTjGMTZ.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-dXN_pYn1.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C4Xdq4VV.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-16-BgwRBCeU.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-20-X5NPN60g.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-other-Bme3ywH9.js">
16
- <link rel="modulepreload" crossorigin href="/assets/vendor-react-CHk5ngkq.js">
17
- <link rel="modulepreload" crossorigin href="/assets/vendor-ui-core-sfZn2C08.js">
18
- <link rel="modulepreload" crossorigin href="/assets/vendor-utils-CnB7BG7m.js">
19
- <link rel="modulepreload" crossorigin href="/assets/vendor-i18next-cO7FDf_e.js">
20
- <link rel="stylesheet" crossorigin href="/assets/index-919BqnYI.css">
16
+ <link rel="modulepreload" crossorigin href="/assets/vendor-react-BmoHsW1x.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/vendor-ui-core-CY_WyMIf.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/vendor-utils-BWSB0jGn.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/vendor-i18next-BKUzkAKP.js">
20
+ <link rel="stylesheet" crossorigin href="/assets/index-Bheo8y3N.css">
21
21
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
22
22
 
23
23
  <body>
package/static/sw.js CHANGED
@@ -1 +1 @@
1
- const e=`dns-worker-icons-v2`,t=`/api/icon/`,n=1440*60*1e3;15*n,30*n;function r(){return new Response(`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>`,{headers:{"Content-Type":`image/svg+xml`,"Cache-Control":`public, max-age=86400`}})}async function i(e){try{return await fetch(e,{mode:`cors`})}catch{return fetch(e,{mode:`no-cors`})}}function a(e){return e.startsWith(t)}async function o(e,t,n){let r=typeof t==`string`?t:t.url,i=new Request(r+`?sw-cached-at`),a=new Response(Date.now().toString());await e.put(t,n),await e.put(i,a)}async function s(e,t){try{let n=new Request(t.url+`?sw-cached-at`),r=await e.match(n);if(!r)return 1/0;let i=await r.text(),a=parseInt(i,10);return isNaN(a)?1/0:Date.now()-a}catch{return 1/0}}async function c(t){let n=await caches.open(e),a=await n.match(t.request);if(a){let e=await s(n,t.request);if(e<1296e6)return a;if(e<2592e6)return i(t.request.url).then(e=>{e&&(e.ok||e.status===0)&&o(n,t.request,e)}).catch(()=>{}),a}try{let e=await i(t.request.url);return e&&(e.ok||e.status===0)?(await o(n,t.request,e.clone()),e):a??r()}catch{return a??r()}}function l(n=[]){caches.open(e).then(e=>{n.forEach(n=>{let r=`${t}${n.replace(/^\*\./,``)}.ico`,a=new Request(r);e.match(a).then(t=>{t||i(r).then(t=>{t&&(t.ok||t.status===0)&&o(e,a,t)}).catch(()=>{})})})})}async function u(){try{let t=await caches.open(e),n=await t.keys(),r=Date.now(),i=n.filter(e=>e.url.includes(`?sw-cached-at`));for(let e of i){let n=await t.match(e);if(n){let i=await n.text(),a=parseInt(i,10);if(!isNaN(a)&&r-a>2592e6){let n=e.url.split(`?`)[0];await t.delete(new Request(n)),await t.delete(e)}}}}catch(e){console.error(`Failed to execute service worker cache cleanup:`,e)}}async function d(){try{let e=await caches.keys();for(let t of e)t.startsWith(`dns-worker-icons-`)&&t!==`dns-worker-icons-v2`&&await caches.delete(t)}catch(e){console.error(`Failed to clean up old caches:`,e)}}const f=`dns-worker-logs-v1`;function p(e){try{let t=new URL(e);return/^\/api\/profiles\/[^/]+\/logs\/\d+$/.test(t.pathname)}catch{return!1}}async function m(e){let t=await caches.open(f),n=await t.match(e.request);if(n)return n;try{let n=await fetch(e.request);return n.ok&&await o(t,e.request,n.clone()),n}catch{return new Response(JSON.stringify({error:`Network error fetching log details`}),{status:503,headers:{"Content-Type":`application/json`}})}}async function h(){try{let e=await caches.open(f),t=await e.keys(),n=Date.now(),r=t.filter(e=>e.url.includes(`?sw-cached-at`));for(let t of r){let r=await e.match(t);if(r){let i=await r.text(),a=parseInt(i,10);if(!isNaN(a)&&n-a>864e5){let n=t.url.split(`?`)[0];await e.delete(new Request(n)),await e.delete(t)}}}}catch(e){console.error(`Failed to execute service worker log cache cleanup:`,e)}}[{"revision":"237335bb4b54a5c99e2df044caa514b2","url":"sqlite3-opfs-async-proxy.js"},{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"e2ea163d199c3d6e3ead1902f471cdcc","url":"index.html"},{"revision":null,"url":"assets/zh-TW-DETA1X3X.js"},{"revision":null,"url":"assets/zh-CN-CSPdaOkF.js"},{"revision":null,"url":"assets/vi-VN-D7rcA1IX.js"},{"revision":null,"url":"assets/vendor-utils-CnB7BG7m.js"},{"revision":null,"url":"assets/vendor-ui-core-sfZn2C08.js"},{"revision":null,"url":"assets/vendor-react-CHk5ngkq.js"},{"revision":null,"url":"assets/vendor-icons-other-Bme3ywH9.js"},{"revision":null,"url":"assets/vendor-icons-20-X5NPN60g.js"},{"revision":null,"url":"assets/vendor-icons-16-BgwRBCeU.js"},{"revision":null,"url":"assets/vendor-i18next-cO7FDf_e.js"},{"revision":null,"url":"assets/sqlite3-worker1-DZ2OAEkd.js"},{"revision":null,"url":"assets/sqlite3-opfs-async-proxy.js"},{"revision":null,"url":"assets/sqlite3-opfs-async-proxy-Ck-yCayi.js"},{"revision":null,"url":"assets/sqlite3-Con_VOcu.wasm"},{"revision":null,"url":"assets/sqlite.worker-C50yoTbX.js"},{"revision":null,"url":"assets/splitPathsBySizeLoader-BgBwSA38.js"},{"revision":null,"url":"assets/services-Cwvmit1q.js"},{"revision":null,"url":"assets/ru-RU-CSyCGGPn.js"},{"revision":null,"url":"assets/rolldown-runtime-C4Xdq4VV.js"},{"revision":null,"url":"assets/paths-cc-gUzxh.js"},{"revision":null,"url":"assets/paths-CfqdBnl8.js"},{"revision":null,"url":"assets/ko-KR-Chzg6jwO.js"},{"revision":null,"url":"assets/ja-JP-BJSbUSmh.js"},{"revision":null,"url":"assets/index-CbTjGMTZ.js"},{"revision":null,"url":"assets/index-919BqnYI.css"},{"revision":null,"url":"assets/getFlagEmoji-_Wxhq0lT.js"},{"revision":null,"url":"assets/fr-FR-CqGOuepx.js"},{"revision":null,"url":"assets/formatCountryName-CSBD8pP8.js"},{"revision":null,"url":"assets/es-ES-CHvRWwd-.js"},{"revision":null,"url":"assets/de-DE-CA3VPcoz.js"},{"revision":null,"url":"assets/date-IKj2stuv.js"},{"revision":null,"url":"assets/browser-DPi2BX_1.js"},{"revision":null,"url":"assets/allPathsLoader-DM2JAMeC.js"},{"revision":null,"url":"assets/allPaths-BEI3QvIM.js"},{"revision":null,"url":"assets/TrendChart-Dc5yJndv.js"},{"revision":null,"url":"assets/SwipeableDrawer-BrmEzhuP.js"},{"revision":null,"url":"assets/SetupView-CTgCUAgk.js"},{"revision":null,"url":"assets/SettingsView-agJiYEt5.js"},{"revision":null,"url":"assets/RulesView-CfVeQv0-.js"},{"revision":null,"url":"assets/QRCodeCanvas-Cu_-aNyM.js"},{"revision":null,"url":"assets/LogsView-eLfZMRVo.js"},{"revision":null,"url":"assets/FilteringView-CQ7sX3Rp.js"},{"revision":null,"url":"assets/DestinationMap-jSW7gimP.js"},{"revision":null,"url":"assets/AuthView-BQzmEyvO.js"},{"revision":null,"url":"assets/AnalyticsView-C_g_jz14.js"},{"revision":null,"url":"assets/AccountView-CGRwz6ae.js"},{"revision":"edbf27fdbbc78a5a7269553eed5447b1","url":"manifest.webmanifest"}],self.addEventListener(`install`,()=>{self.skipWaiting()}),self.addEventListener(`activate`,e=>{e.waitUntil(Promise.all([self.clients.claim(),u(),d(),h()]))}),self.addEventListener(`fetch`,e=>{let t=e.request.url;a(t)&&e.respondWith(c(e)),p(t)&&e.respondWith(m(e))}),self.addEventListener(`message`,e=>{e.data&&e.data.type===`PREFETCH_ICONS`&&l(e.data.domains)});
1
+ const e=`dns-worker-icons-v2`,t=`/api/icon/`,n=1440*60*1e3;15*n,30*n;function r(){return new Response(`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9ca3af" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>`,{headers:{"Content-Type":`image/svg+xml`,"Cache-Control":`public, max-age=86400`}})}async function i(e){try{return await fetch(e,{mode:`cors`})}catch{return fetch(e,{mode:`no-cors`})}}function a(e){return e.startsWith(t)}async function o(e,t,n){let r=typeof t==`string`?t:t.url,i=new Request(r+`?sw-cached-at`),a=new Response(Date.now().toString());await e.put(t,n),await e.put(i,a)}async function s(e,t){try{let n=new Request(t.url+`?sw-cached-at`),r=await e.match(n);if(!r)return 1/0;let i=await r.text(),a=parseInt(i,10);return isNaN(a)?1/0:Date.now()-a}catch{return 1/0}}async function c(t){let n=await caches.open(e),a=await n.match(t.request);if(a){let e=await s(n,t.request);if(e<1296e6)return a;if(e<2592e6)return i(t.request.url).then(e=>{e&&(e.ok||e.status===0)&&o(n,t.request,e)}).catch(()=>{}),a}try{let e=await i(t.request.url);return e&&(e.ok||e.status===0)?(await o(n,t.request,e.clone()),e):a??r()}catch{return a??r()}}function l(n=[]){caches.open(e).then(e=>{n.forEach(n=>{let r=`${t}${n.replace(/^\*\./,``)}.ico`,a=new Request(r);e.match(a).then(t=>{t||i(r).then(t=>{t&&(t.ok||t.status===0)&&o(e,a,t)}).catch(()=>{})})})})}async function u(){try{let t=await caches.open(e),n=await t.keys(),r=Date.now(),i=n.filter(e=>e.url.includes(`?sw-cached-at`));for(let e of i){let n=await t.match(e);if(n){let i=await n.text(),a=parseInt(i,10);if(!isNaN(a)&&r-a>2592e6){let n=e.url.split(`?`)[0];await t.delete(new Request(n)),await t.delete(e)}}}}catch(e){console.error(`Failed to execute service worker cache cleanup:`,e)}}async function d(){try{let e=await caches.keys();for(let t of e)t.startsWith(`dns-worker-icons-`)&&t!==`dns-worker-icons-v2`&&await caches.delete(t)}catch(e){console.error(`Failed to clean up old caches:`,e)}}const f=`dns-worker-logs-v1`;function p(e){try{let t=new URL(e);return/^\/api\/profiles\/[^/]+\/logs\/\d+$/.test(t.pathname)}catch{return!1}}async function m(e){let t=await caches.open(f),n=await t.match(e.request);if(n)return n;try{let n=await fetch(e.request);return n.ok&&await o(t,e.request,n.clone()),n}catch{return new Response(JSON.stringify({error:`Network error fetching log details`}),{status:503,headers:{"Content-Type":`application/json`}})}}async function h(){try{let e=await caches.open(f),t=await e.keys(),n=Date.now(),r=t.filter(e=>e.url.includes(`?sw-cached-at`));for(let t of r){let r=await e.match(t);if(r){let i=await r.text(),a=parseInt(i,10);if(!isNaN(a)&&n-a>864e5){let n=t.url.split(`?`)[0];await e.delete(new Request(n)),await e.delete(t)}}}}catch(e){console.error(`Failed to execute service worker log cache cleanup:`,e)}}[{"revision":"237335bb4b54a5c99e2df044caa514b2","url":"sqlite3-opfs-async-proxy.js"},{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"bd8aeccc80994cf85007786588df3223","url":"index.html"},{"revision":null,"url":"assets/zh-TW-lc_SSoVI.js"},{"revision":null,"url":"assets/zh-CN-B0nRgJTD.js"},{"revision":null,"url":"assets/vi-VN-CvS_mTAu.js"},{"revision":null,"url":"assets/vendor-utils-BWSB0jGn.js"},{"revision":null,"url":"assets/vendor-ui-core-CY_WyMIf.js"},{"revision":null,"url":"assets/vendor-react-BmoHsW1x.js"},{"revision":null,"url":"assets/vendor-icons-other-Bme3ywH9.js"},{"revision":null,"url":"assets/vendor-icons-20-X5NPN60g.js"},{"revision":null,"url":"assets/vendor-icons-16-BgwRBCeU.js"},{"revision":null,"url":"assets/vendor-i18next-BKUzkAKP.js"},{"revision":null,"url":"assets/sqlite3-worker1-DZ2OAEkd.js"},{"revision":null,"url":"assets/sqlite3-opfs-async-proxy.js"},{"revision":null,"url":"assets/sqlite3-opfs-async-proxy-Ck-yCayi.js"},{"revision":null,"url":"assets/sqlite3-Con_VOcu.wasm"},{"revision":null,"url":"assets/sqlite.worker-C50yoTbX.js"},{"revision":null,"url":"assets/splitPathsBySizeLoader-BgBwSA38.js"},{"revision":null,"url":"assets/services-BVnEJBDq.js"},{"revision":null,"url":"assets/ru-RU-Bicx52lz.js"},{"revision":null,"url":"assets/rolldown-runtime-C4Xdq4VV.js"},{"revision":null,"url":"assets/paths-cc-gUzxh.js"},{"revision":null,"url":"assets/paths-CfqdBnl8.js"},{"revision":null,"url":"assets/ko-KR-z38NtMFa.js"},{"revision":null,"url":"assets/ja-JP-C5jk6SuH.js"},{"revision":null,"url":"assets/index-dXN_pYn1.js"},{"revision":null,"url":"assets/index-Bheo8y3N.css"},{"revision":null,"url":"assets/getFlagEmoji-DRubsb7D.js"},{"revision":null,"url":"assets/fr-FR-BRcdpT35.js"},{"revision":null,"url":"assets/formatCountryName-CSBD8pP8.js"},{"revision":null,"url":"assets/es-ES-CqKS7vHl.js"},{"revision":null,"url":"assets/de-DE-kDnumQOF.js"},{"revision":null,"url":"assets/date-CpvuBM37.js"},{"revision":null,"url":"assets/browser-Dc5iKjY4.js"},{"revision":null,"url":"assets/allPathsLoader-DM2JAMeC.js"},{"revision":null,"url":"assets/allPaths-BEI3QvIM.js"},{"revision":null,"url":"assets/TrendChart-CCR1W3fC.js"},{"revision":null,"url":"assets/SwipeableDrawer-B81ANpsc.js"},{"revision":null,"url":"assets/SetupView-DE9MM-32.js"},{"revision":null,"url":"assets/SettingsView-DTGI6eSd.js"},{"revision":null,"url":"assets/RulesView-B2kL1QhI.js"},{"revision":null,"url":"assets/QRCodeCanvas-DQ-xNuwF.js"},{"revision":null,"url":"assets/LogsView-B-U9oaxB.js"},{"revision":null,"url":"assets/FilteringView-Bs3__NpV.js"},{"revision":null,"url":"assets/DestinationMap-9Yc54Mdj.js"},{"revision":null,"url":"assets/AuthView-qoUKd5PS.js"},{"revision":null,"url":"assets/AnalyticsView-BywwDzah.js"},{"revision":null,"url":"assets/AccountView-p5jSjMAE.js"},{"revision":"edbf27fdbbc78a5a7269553eed5447b1","url":"manifest.webmanifest"}],self.addEventListener(`install`,()=>{self.skipWaiting()}),self.addEventListener(`activate`,e=>{e.waitUntil(Promise.all([self.clients.claim(),u(),d(),h()]))}),self.addEventListener(`fetch`,e=>{let t=e.request.url;a(t)&&e.respondWith(c(e)),p(t)&&e.respondWith(m(e))}),self.addEventListener(`message`,e=>{e.data&&e.data.type===`PREFETCH_ICONS`&&l(e.data.domains)});
@@ -1,4 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/date-IKj2stuv.js","assets/index-CbTjGMTZ.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-CHk5ngkq.js","assets/vendor-ui-core-sfZn2C08.js","assets/vendor-utils-CnB7BG7m.js","assets/vendor-i18next-cO7FDf_e.js","assets/index-919BqnYI.css"])))=>i.map(i=>d[i]);
2
- import{i as e}from"./rolldown-runtime-C4Xdq4VV.js";import{$ as t,A as n,B as r,C as i,D as a,E as o,J as s,M as c,Q as l,R as u,S as d,Tt as f,W as p,X as m,Y as h,Zt as g,b as _,ht as v,it as y,j as b,k as x,lt as S,mt as C,rt as w,x as T,y as E,z as D}from"./vendor-react-CHk5ngkq.js";import{S as O,b as k,x as A}from"./vendor-icons-other-Bme3ywH9.js";import{r as j}from"./vendor-i18next-cO7FDf_e.js";import{B as M,C as N,D as P,F as ee,H as F,L as I,M as te,N as L,O as R,P as ne,R as z,S as B,T as re,V,_ as H,d as ie,f as U,h as ae,j as W,p as G,r as K,t as oe,u as se,v as ce,x as q,z as J}from"./vendor-ui-core-sfZn2C08.js";import{_t as le}from"./vendor-utils-CnB7BG7m.js";import{$t as ue,At as de,Bt as fe,Dt as pe,Et as me,F as he,Ft as ge,Gt as _e,Ht as ve,I as ye,It as be,Jt as xe,Kt as Se,L as Ce,M as we,Mt as Te,N as Ee,Nt as De,Ot as Oe,P as ke,Pt as Ae,R as je,Rt as Me,Tt as Ne,Ut as Pe,Vt as Fe,Wt as Ie,d as Y,f as Le,i as Re,j as X,jt as ze,kt as Be,l as Ve,m as He,o as Ue,on as We,p as Ge,qt as Ke,s as qe,v as Je,w as Z,z as Ye,zt as Xe}from"./index-CbTjGMTZ.js";import{t as Ze}from"./QRCodeCanvas-Cu_-aNyM.js";var Q=e(O(),1),$=e(k(),1);const Qe=({recoveryKeys:e,copied:t,onCopy:r,onDone:i})=>{let{t:a}=g();return(0,$.jsxs)(`div`,{className:`p-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-800`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2 mb-4`,children:[(0,$.jsx)(n,{size:20,className:`text-green-500`}),(0,$.jsx)(W,{style:{margin:0},children:a(`account.totp.recoveryKeysTitle`,`Save Your Recovery Keys`)})]}),(0,$.jsx)(B,{intent:V.WARNING,className:`mb-4`,children:a(`account.totp.recoveryKeysWarning`,`Store these keys safely. Each key can only be used once.`)}),(0,$.jsx)(`div`,{className:e.length===1?`space-y-2 mb-4`:`grid grid-cols-2 gap-2 mb-4`,children:e.map((e,t)=>(0,$.jsx)(`code`,{className:`block font-mono text-sm sm:text-base font-semibold bg-gray-100 dark:bg-gray-800 text-blue-600 dark:text-blue-400 px-4 py-3 rounded-xl text-center tracking-widest break-all select-all border border-gray-200 dark:border-gray-700`,children:e},t))}),(0,$.jsxs)(`div`,{className:`flex gap-2`,children:[(0,$.jsx)(L,{fill:!0,icon:(0,$.jsx)(S,{size:14}),text:t?a(`account.totp.copied`,`Copied!`):a(`account.totp.copyKeys`,`Copy All Keys`),intent:t?V.SUCCESS:V.NONE,onClick:r}),(0,$.jsx)(L,{fill:!0,intent:V.PRIMARY,text:a(`account.totp.done`,`Done, I've saved them`),onClick:i})]})]})},$e=({onRefresh:e})=>{let{t}=g(),[n,r]=(0,Q.useState)([]),[i,a]=(0,Q.useState)(!0),[o,s]=(0,Q.useState)(!0),[c,l]=(0,Q.useState)(null),[u,d]=(0,Q.useState)(!1),[f,p]=(0,Q.useState)(!1),[m,h]=(0,Q.useState)(``),[_,v]=(0,Q.useState)(!1),[y,b]=(0,Q.useState)(!1),[x,S]=(0,Q.useState)(``),[C,w]=(0,Q.useState)(null),[T,E]=(0,Q.useState)(``),[D,O]=(0,Q.useState)(!1),[k,A]=(0,Q.useState)(!1),[j,M]=(0,Q.useState)(``),[N,P]=(0,Q.useState)(null),[ee,F]=(0,Q.useState)(!1),[I,te]=(0,Q.useState)(null),L=(0,Q.useCallback)(async()=>{a(!0);try{r(await ge()||[])}catch{}finally{a(!1)}},[]);return(0,Q.useEffect)(()=>{s(we()),L()},[L]),{passkeys:n,loading:i,supported:o,recoveryKeys:c,setRecoveryKeys:l,copiedKeys:u,handleCopyRecoveryKeys:()=>{c&&navigator.clipboard.writeText(c.join(`
3
- `)).then(()=>{d(!0),setTimeout(()=>d(!1),2e3)})},isAddOpen:f,setIsAddOpen:p,passkeyName:m,setPasskeyName:h,nameFocused:_,setNameFocused:v,registering:y,addError:x,setAddError:S,handleOpenAdd:()=>{h(``),S(``),v(!1),p(!0)},handleRegister:async n=>{n.preventDefault();let r=m.trim();if(!Je(r)){S(t(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`));return}b(!0),S(``);try{let t=await xe({name:r,credential:await ke(await Ae())}),n=t?.passkey?.id||t?.id;if(n&&X.isUnlocked())try{await X.wrapCurrentKeyForPasskey(n)}catch(e){console.warn(`[Passkeys] Failed to wrap existing E2EE key for new passkey:`,e)}p(!1),h(``),await L(),e?.(),t?.recovery_keys&&t.recovery_keys.length>0&&l(t.recovery_keys)}catch(e){console.error(`Passkey registration failed:`,e),S(e instanceof Error?e.message:t(`account.passkey.regFailed`,`Registration failed`))}finally{b(!1)}},editingPasskey:C,setEditingPasskey:w,editName:T,setEditName:E,renameFocused:D,setRenameFocused:O,renaming:k,renameError:j,setRenameError:M,handleOpenRename:e=>{w(e),E(e.name),M(``),O(!1)},handleRenameSubmit:async e=>{e.preventDefault();let n=T.trim();if(!(!C||!n)){if(!Je(n)){M(t(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`));return}A(!0),M(``);try{await Me(C.id,n),w(null),await L()}catch(e){M(e instanceof Error?e.message:t(`common.errorNetwork`))}finally{A(!1)}}},deletingId:N,deleteConfirmOpen:ee,setDeleteConfirmOpen:F,targetToDelete:I,handleOpenDelete:e=>{te(e),F(!0)},handleDeleteSubmit:async()=>{if(I){P(I.id);try{await Be(I.id),F(!1),te(null),await L(),e?.()}catch(e){let n=e instanceof Error?e.message:t(`common.errorNetwork`);alert(n)}finally{P(null)}}},fetchPasskeys:L}},et=({passkeys:e,loading:n,deletingId:a,onOpenRename:o,onOpenDelete:s})=>{let{t:c}=g();return n?(0,$.jsx)(`div`,{className:`py-6 text-center`,children:(0,$.jsx)(ne,{size:24})}):e.length===0?(0,$.jsxs)(`div`,{className:`py-6 text-center text-sm text-gray-500 dark:text-gray-400 border border-dashed border-gray-200 dark:border-gray-800 rounded-lg`,children:[(0,$.jsx)(t,{size:28,className:`mx-auto mb-2 opacity-40`}),(0,$.jsx)(`p`,{children:c(`account.passkey.empty`,`No passkeys added yet.`)})]}):(0,$.jsx)(`div`,{className:`divide-y divide-gray-100 dark:divide-gray-800 border border-gray-200 dark:border-gray-800 rounded-lg overflow-hidden`,children:e.map(e=>(0,$.jsxs)(`div`,{className:`p-3 sm:p-4 flex items-center justify-between gap-3 hover:bg-gray-50 dark:hover:bg-gray-900/40 transition-colors`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-3 min-w-0`,children:[(0,$.jsx)(`div`,{className:`p-2 bg-purple-50 dark:bg-purple-950/40 text-purple-600 dark:text-purple-400 rounded-lg shrink-0`,children:(0,$.jsx)(t,{size:18})}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`font-semibold text-sm truncate text-gray-900 dark:text-gray-100`,children:e.name}),(0,$.jsxs)(`div`,{className:`text-xs text-gray-500 dark:text-gray-400 flex flex-wrap gap-x-3 gap-y-1 mt-0.5`,children:[(0,$.jsxs)(`span`,{children:[c(`account.passkey.created`,`Added`),`:`,` `,Z(new Date(e.created_at*1e3))]}),e.last_used_at?(0,$.jsxs)(`span`,{children:[c(`account.passkey.lastUsed`,`Last used`),`:`,` `,Z(new Date(e.last_used_at*1e3))]}):(0,$.jsx)(`span`,{children:c(`account.passkey.neverUsed`,`Never used`)})]})]})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-1 shrink-0`,children:[(0,$.jsx)(L,{minimal:!0,small:!0,icon:(0,$.jsx)(r,{size:14}),title:c(`common.rename`,`Rename`),onClick:()=>o(e)}),(0,$.jsx)(L,{minimal:!0,small:!0,intent:V.DANGER,icon:(0,$.jsx)(i,{size:14}),title:c(`common.delete`,`Delete`),loading:a===e.id,onClick:()=>s(e)})]})]},e.id))})},tt=({isOpen:e,onClose:t,passkeyName:n,setPasskeyName:r,nameFocused:i,setNameFocused:a,registering:o,addError:s,setAddError:c,onSubmit:l})=>{let{t:u}=g();return(0,$.jsx)(R,{isOpen:e,onClose:()=>!o&&t(),title:u(`account.passkey.addTitle`,`Add New Passkey`),className:`max-w-md`,children:(0,$.jsxs)(`form`,{onSubmit:l,children:[(0,$.jsxs)(`div`,{className:I,children:[s&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-4`,children:s}),(0,$.jsx)(G,{label:u(`account.passkey.nameLabel`,`Passkey Name`),labelFor:`passkey-name-input`,helperText:u(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`),children:(0,$.jsx)(ce,{content:u(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`),isOpen:i,position:M.TOP,intent:V.PRIMARY,className:`w-full`,children:(0,$.jsx)(`div`,{className:`w-full block`,children:(0,$.jsx)(U,{id:`passkey-name-input`,autoFocus:!0,placeholder:u(`account.passkey.namePlaceholder`,`e.g. my_passkey`),value:n,onChange:e=>{r(e.target.value),s&&c(``)},onFocus:()=>a(!0),onBlur:()=>a(!1),disabled:o})})})}),(0,$.jsx)(`p`,{className:`text-xs text-gray-500 dark:text-gray-400`,children:u(`account.passkey.promptNotice`,`After clicking continue, your browser will prompt you to authenticate via Key, face scan, PIN, or hardware key.`)})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{text:u(`common.cancel`,`Cancel`),onClick:t,disabled:o}),(0,$.jsx)(L,{intent:V.PRIMARY,type:`submit`,text:u(`account.passkey.continue`,`Continue`),loading:o,disabled:!Je(n.trim())||o})]})})]})})},nt=({isOpen:e,onClose:t,editName:n,setEditName:r,renameFocused:i,setRenameFocused:a,renaming:o,renameError:s,setRenameError:c,onSubmit:l})=>{let{t:u}=g();return(0,$.jsx)(R,{isOpen:e,onClose:()=>!o&&t(),title:u(`account.passkey.renameTitle`,`Rename Passkey`),className:`max-w-md`,children:(0,$.jsxs)(`form`,{onSubmit:l,children:[(0,$.jsxs)(`div`,{className:I,children:[s&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-4`,children:s}),(0,$.jsx)(G,{label:u(`account.passkey.appNameLabel`,`Application (Domain)`),children:(0,$.jsx)(U,{readOnly:!0,disabled:!0,leftIcon:`globe`,value:window.location.hostname})}),(0,$.jsx)(G,{label:u(`account.passkey.nameLabel`,`Passkey Name`),helperText:u(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`),children:(0,$.jsx)(ce,{content:u(`account.passkey.formatTip`,`1-30 characters, letters, numbers, hyphens and underscores allowed`),isOpen:i,position:M.TOP,intent:V.PRIMARY,className:`w-full`,children:(0,$.jsx)(`div`,{className:`w-full block`,children:(0,$.jsx)(U,{autoFocus:!0,placeholder:u(`account.passkey.namePlaceholder`,`e.g. my_passkey`),value:n,onChange:e=>{r(e.target.value),s&&c(``)},onFocus:()=>a(!0),onBlur:()=>a(!1),disabled:o})})})})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{text:u(`common.cancel`,`Cancel`),onClick:t,disabled:o}),(0,$.jsx)(L,{intent:V.PRIMARY,type:`submit`,text:u(`common.save`,`Save`),loading:o,disabled:!Je(n.trim())||o})]})})]})})},rt=({isOpen:e,onClose:t,targetToDelete:n,deleting:r,onConfirm:i})=>{let{t:a}=g();return(0,$.jsxs)(R,{isOpen:e,onClose:()=>!r&&t(),title:a(`account.passkey.deleteTitle`,`Delete Passkey`),className:`max-w-md`,children:[(0,$.jsx)(`div`,{className:I,children:(0,$.jsx)(`p`,{children:a(`account.passkey.deleteConfirm`,`Are you sure you want to delete passkey "{{name}}"? You will no longer be able to use this passkey for MFA.`,{name:n?.name})})}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{text:a(`common.cancel`,`Cancel`),onClick:t,disabled:r}),(0,$.jsx)(L,{intent:V.DANGER,text:a(`common.delete`,`Delete`),onClick:i,loading:r})]})})]})},it=({onRefresh:e})=>{let{t}=g(),{passkeys:n,loading:r,supported:i,recoveryKeys:a,setRecoveryKeys:o,copiedKeys:s,handleCopyRecoveryKeys:c,isAddOpen:u,setIsAddOpen:d,passkeyName:f,setPasskeyName:p,nameFocused:m,setNameFocused:h,registering:_,addError:v,setAddError:y,handleOpenAdd:x,handleRegister:S,editingPasskey:C,setEditingPasskey:w,editName:T,setEditName:E,renameFocused:O,setRenameFocused:k,renaming:A,renameError:j,setRenameError:M,handleOpenRename:N,handleRenameSubmit:P,deletingId:ee,deleteConfirmOpen:F,setDeleteConfirmOpen:I,targetToDelete:te,handleOpenDelete:R,handleDeleteSubmit:ne}=$e({onRefresh:e});return a?(0,$.jsx)(Qe,{recoveryKeys:a,copied:s,onCopy:c,onDone:()=>o(null)}):(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between mb-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(l,{size:20,className:`text-purple-500`}),(0,$.jsx)(W,{style:{margin:0},children:t(`account.passkey.title`,`Passkeys`)}),(0,$.jsx)(K,{intent:n.length>0?V.SUCCESS:V.NONE,minimal:!0,round:!0,children:n.length>0?t(`account.passkey.count`,{count:n.length,defaultValue:`${n.length} configured`}):t(`account.passkey.none`,`None configured`)})]}),i&&(0,$.jsx)(L,{intent:V.PRIMARY,icon:(0,$.jsx)(D,{size:16}),text:t(`account.passkey.addBtn`,`Add Passkey`),onClick:x})]}),(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400 mb-4`,children:t(`account.passkey.desc`,`Authenticate securely with biometrics (Touch ID, Face ID, Windows Hello) or a hardware security key (YubiKey) as an MFA factor.`)}),!i&&(0,$.jsx)(B,{intent:V.WARNING,icon:(0,$.jsx)(b,{size:16}),className:`mb-4`,children:t(`account.passkey.notSupported`,`Your browser or device does not support Passkeys (WebAuthn).`)}),(0,$.jsx)(et,{passkeys:n,loading:r,deletingId:ee,onOpenRename:N,onOpenDelete:R}),(0,$.jsx)(tt,{isOpen:u,onClose:()=>d(!1),passkeyName:f,setPasskeyName:p,nameFocused:m,setNameFocused:h,registering:_,addError:v,setAddError:y,onSubmit:S}),(0,$.jsx)(nt,{isOpen:!!C,onClose:()=>w(null),editName:T,setEditName:E,renameFocused:O,setRenameFocused:k,renaming:A,renameError:j,setRenameError:M,onSubmit:P}),(0,$.jsx)(rt,{isOpen:F,onClose:()=>I(!1),targetToDelete:te,deleting:!!ee,onConfirm:ne})]})},at=({user:e,disableDialogOpen:t,setDisableDialogOpen:n,disablePassword:r,setDisablePassword:i,disableError:a,setDisableError:s,disableLoading:c,onDisable:l})=>{let{t:u}=g(),[d,f]=Q.useState(!1);return(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(o,{size:20,className:`text-blue-500`}),(0,$.jsx)(W,{style:{margin:0},children:u(`account.totp.title`,`Authenticator App (TOTP)`)}),(0,$.jsx)(K,{intent:V.SUCCESS,minimal:!0,round:!0,children:u(`account.totp.enabled`,`Enabled`)})]}),(0,$.jsx)(L,{intent:V.DANGER,outlined:!0,icon:(0,$.jsx)(x,{size:14}),text:u(`account.totp.disable`,`Disable Authenticator App (TOTP)`),onClick:()=>n(!0)})]}),(0,$.jsx)(`div`,{className:`space-y-4`,children:(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400`,children:u(`account.totp.activeDesc`,`Authenticator app (TOTP) is active and generating 6-digit verification codes for your account.`)})}),(0,$.jsx)(R,{isOpen:t,onClose:()=>{n(!1),i(``),s(``),f(!1)},title:u(`account.totp.disableTitle`,`Disable Authenticator App (TOTP)`),icon:`shield`,children:(0,$.jsxs)(`div`,{className:`p-6 space-y-4`,children:[(0,$.jsx)(B,{intent:V.WARNING,children:u(`account.totp.disableWarning`,`After disabling TOTP, if no Passkeys are configured, your account will only be protected by password.`)}),a&&(0,$.jsx)(B,{intent:V.DANGER,children:a}),!e.totp_skip_password&&(0,$.jsxs)(`form`,{onSubmit:l,className:`space-y-4`,children:[(0,$.jsx)(G,{label:u(`account.currentPassword`),children:(0,$.jsx)(U,{type:d?`text`:`password`,leftIcon:`lock`,value:r,onChange:e=>i(e.target.value),rightElement:(0,$.jsx)(L,{minimal:!0,icon:d?`eye-open`:`eye-off`,onClick:()=>f(!d),title:d?u(`auth.hidePassword`,`Hide password`):u(`auth.showPassword`,`Show password`)}),required:!0})}),(0,$.jsxs)(`div`,{className:`flex justify-end gap-2`,children:[(0,$.jsx)(L,{text:u(`account.cancel`),onClick:()=>n(!1)}),(0,$.jsx)(L,{intent:V.DANGER,text:u(`account.totp.confirmDisable`,`Disable TOTP`),type:`submit`,loading:c})]})]}),e.totp_skip_password&&(0,$.jsxs)(`div`,{className:`flex justify-end gap-2`,children:[(0,$.jsx)(L,{text:u(`account.cancel`),onClick:()=>n(!1)}),(0,$.jsx)(L,{intent:V.DANGER,text:u(`account.totp.confirmDisable`,`Disable TOTP`),loading:c,onClick:l})]})]})})]})},ot=({setupData:e,setupToken:t,setSetupToken:n,setupError:r,setupLoading:i,onStartSetup:s,onConfirmSetup:c,onCancel:l})=>{let{t:u}=g();return(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-2 mb-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(o,{size:20,className:`text-blue-500`}),(0,$.jsx)(W,{style:{margin:0},children:u(`account.totp.title`,`Authenticator App (TOTP)`)}),(0,$.jsx)(K,{minimal:!0,round:!0,intent:V.NONE,children:u(`account.totp.disabled`,`Disabled`)})]}),!e&&(0,$.jsx)(L,{intent:V.PRIMARY,icon:(0,$.jsx)(a,{size:14}),text:u(`account.totp.setupBtn`,`Set Up Authenticator App (TOTP)`),loading:i,onClick:s})]}),e?(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsx)(`p`,{className:`text-sm font-medium`,children:u(`account.totp.scanQR`,`Scan this QR code with your authenticator app:`)}),(0,$.jsxs)(`div`,{className:`flex flex-col items-center gap-3`,children:[(0,$.jsx)(Ze,{uri:e.uri}),(0,$.jsxs)(`div`,{className:`text-center`,children:[(0,$.jsx)(`p`,{className:`text-xs text-gray-500 mb-1`,children:u(`account.totp.orEnterManually`,`Or enter the key manually:`)}),(0,$.jsx)(`code`,{className:`text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded select-all`,children:e.secret})]})]}),(0,$.jsx)(H,{}),r&&(0,$.jsx)(B,{intent:V.DANGER,children:r}),(0,$.jsxs)(`form`,{onSubmit:c,className:`space-y-3`,children:[(0,$.jsx)(G,{label:u(`account.totp.enterCode`,`Enter the 6-digit code to confirm:`),helperText:u(`account.totp.enterCodeHint`,`This verifies the key was scanned correctly.`),children:(0,$.jsx)(Re,{length:6,value:t,onChange:n,disabled:i})}),(0,$.jsxs)(`div`,{className:`flex gap-2`,children:[(0,$.jsx)(L,{text:u(`account.cancel`),onClick:l}),(0,$.jsx)(L,{intent:V.SUCCESS,type:`submit`,loading:i,text:u(`account.totp.activate`,`Activate`),disabled:t.length<6})]})]})]}):(0,$.jsxs)(`div`,{className:`space-y-3`,children:[(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400`,children:u(`account.totp.setupDesc`,`Add an extra layer of security. Use Google Authenticator, Authy, or any TOTP app.`)}),r&&(0,$.jsx)(B,{intent:V.DANGER,children:r})]})]})},st=({user:e,onRefresh:t})=>{let{t:n}=g(),[r,i]=(0,Q.useState)(null),[a,o]=(0,Q.useState)(``),[s,c]=(0,Q.useState)(null),[l,u]=(0,Q.useState)(!1),[d,f]=(0,Q.useState)(``),[p,m]=(0,Q.useState)(!1),[h,_]=(0,Q.useState)(!1),[v,y]=(0,Q.useState)(``),[b,x]=(0,Q.useState)(!1),[S,C]=(0,Q.useState)(``),[w,T]=(0,Q.useState)(!1);return s?(0,$.jsx)(Qe,{recoveryKeys:s,copied:p,onCopy:()=>{s&&navigator.clipboard.writeText(s.join(`
4
- `)).then(()=>{m(!0),setTimeout(()=>m(!1),2e3)})},onDone:()=>c(null)}):e.totp_enabled?(0,$.jsx)(at,{user:e,settingsLoading:w,onToggleSkipPassword:async e=>{T(!0);try{await Ke(e),t()}catch{}finally{T(!1)}},disableDialogOpen:h,setDisableDialogOpen:_,disablePassword:v,setDisablePassword:y,disableError:S,setDisableError:C,disableLoading:b,onDisable:async r=>{r.preventDefault(),x(!0),C(``);try{let n=v;e.password_version===2&&(n=await Le(v,e.username)),await de(n),_(!1),y(``),t()}catch(e){C(e.message||n(`common.errorNetwork`))}finally{x(!1)}}}):(0,$.jsx)(ot,{setupData:r,setupToken:a,setSetupToken:o,setupError:d,setupLoading:l,onStartSetup:async()=>{u(!0),f(``);try{i(await Pe())}catch(e){f(e.message||n(`common.errorNetwork`))}finally{u(!1)}},onConfirmSetup:async e=>{if(e.preventDefault(),r){u(!0),f(``);try{let e=a.replace(/\s/g,``),n=crypto.randomUUID(),s=new TextEncoder().encode(e+n),l=await crypto.subtle.digest(`SHA-256`,s),u=Array.from(new Uint8Array(l)).map(e=>e.toString(16).padStart(2,`0`)).join(``);c((await pe({secret:r.secret,totpTokenHash:u,salt:n})).recovery_keys),i(null),o(``),t()}catch(e){f(e.message||n(`common.errorNetwork`))}finally{u(!1)}}},onCancel:()=>{i(null),o(``),f(``)}})},ct=({method:e,onSelectMethod:t,hasPasskey:r,hasRecoveryKey:i,hasTotp:a,hasPassword:o})=>{let{t:s}=g();return(0,$.jsx)(`div`,{className:`flex justify-center mb-5 isolate`,style:{isolation:`isolate`},children:(0,$.jsx)(`div`,{className:`w-full bg-gray-100/70 dark:bg-gray-800/70 p-1 rounded-lg`,children:(0,$.jsxs)(N,{fill:!0,variant:`minimal`,style:{isolation:`isolate`},children:[r&&(0,$.jsx)(L,{small:!0,active:e===`passkey`,intent:e===`passkey`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(l,{size:14}),text:s(`account.mfa.passkey`,`Passkey`),onClick:()=>t(`passkey`)}),i&&(0,$.jsx)(L,{small:!0,active:e===`recovery_key`,intent:e===`recovery_key`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(l,{size:14}),text:s(`account.recoveryKey.verifyBtn`,`Recovery Key`),onClick:()=>t(`recovery_key`)}),a&&(0,$.jsx)(L,{small:!0,active:e===`totp`,intent:e===`totp`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(n,{size:14}),text:s(`account.mfa.totp`,`TOTP`),onClick:()=>t(`totp`)}),o&&(0,$.jsx)(L,{small:!0,active:e===`password`,intent:e===`password`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(m,{size:14}),text:s(`account.mfa.password`,`Password`),onClick:()=>t(`password`)})]})})})},lt=({loading:e,onVerify:t})=>{let{t:n}=g();return(0,$.jsxs)(`div`,{className:`text-center py-4 space-y-4`,children:[(0,$.jsx)(`div`,{className:`flex justify-center`,children:(0,$.jsx)(`div`,{className:`p-4 rounded-full bg-blue-50 dark:bg-blue-900/30 text-blue-500`,children:(0,$.jsx)(l,{size:48})})}),(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-300`,children:n(`account.passkey.verifyPrompt`,`Authenticate using your device's biometric sensor (Face ID, Touch ID, Windows Hello) or security key.`)}),(0,$.jsx)(L,{fill:!0,large:!0,intent:V.PRIMARY,loading:e,onClick:t,icon:(0,$.jsx)(l,{size:16}),text:n(`account.usePasskeyInstead`,`Authenticate with Passkey`)})]})},ut=({totpCode:e,setTotpCode:t,loading:n,onVerify:r})=>{let{t:i}=g();return(0,$.jsxs)(`div`,{className:`py-2 space-y-4`,children:[(0,$.jsx)(G,{label:i(`account.totpCode`,`Authenticator Code`),children:(0,$.jsx)(Re,{length:6,value:e,onChange:e=>{t(e),e.length===6&&r(e)},disabled:n})}),(0,$.jsx)(L,{fill:!0,intent:V.PRIMARY,loading:n,disabled:e.length!==6,onClick:()=>r(),text:i(`common.confirm`,`Confirm`)})]})},dt=({password:e,setPassword:t,showPassword:n,setShowPassword:r,loading:i,onSubmit:a})=>{let{t:o}=g();return(0,$.jsxs)(`form`,{onSubmit:a,className:`py-2 space-y-4`,children:[(0,$.jsx)(G,{label:o(`account.currentPassword`,`Current Password`),children:(0,$.jsx)(U,{leftIcon:`lock`,type:n?`text`:`password`,value:e,onChange:e=>t(e.target.value),autoFocus:!0,rightElement:(0,$.jsx)(L,{minimal:!0,icon:n?`eye-open`:`eye-off`,onClick:()=>r(!n)})})}),(0,$.jsx)(L,{fill:!0,intent:V.PRIMARY,type:`submit`,loading:i,text:o(`common.confirm`,`Confirm`)})]})},ft=({recoveryKey:e,setRecoveryKey:t,loading:n,onSubmit:r})=>{let{t:i}=g();return(0,$.jsxs)(`form`,{onSubmit:r,className:`py-2 space-y-4`,children:[(0,$.jsx)(G,{label:i(`account.recoveryKey.verifyPromptLabel`,`原恢复密钥 (Original Recovery Key)`),children:(0,$.jsx)(U,{leftIcon:`key`,type:`text`,placeholder:`123456-789012-345678-901234-567890`,value:e,onChange:e=>t(e.target.value),autoFocus:!0,className:`font-mono text-xs`})}),(0,$.jsx)(L,{fill:!0,intent:V.PRIMARY,type:`submit`,loading:n,disabled:!e.trim(),text:i(`common.confirm`,`Confirm`)})]})};var pt=(e,t)=>{let n=t||[`passkey`,`totp`,`password`];return n.includes(`passkey`)&&e?.passkeys_count&&e.passkeys_count>0?`passkey`:n.includes(`recovery_key`)?`recovery_key`:n.includes(`totp`)&&e?.totp_enabled?`totp`:n.includes(`password`)?`password`:n[0]||`password`};const mt=({isOpen:e,onClose:t,user:n,title:r,onVerify:i,allowedMethods:a})=>{let{t:o}=g(),s=a||[`passkey`,`totp`,`password`],c=!!(n?.passkeys_count&&n.passkeys_count>0)&&s.includes(`passkey`),l=!!n?.totp_enabled&&s.includes(`totp`),u=s.includes(`password`),d=s.includes(`recovery_key`),f=pt(n,a),[p,m]=(0,Q.useState)(f),[h,_]=(0,Q.useState)(``),[v,y]=(0,Q.useState)(!1),[b,x]=(0,Q.useState)(``),[S,C]=(0,Q.useState)(``),[w,T]=(0,Q.useState)(!1),[E,D]=(0,Q.useState)(``),[O,k]=(0,Q.useState)(e);!O&&e?(k(!0),m(f),_(``),y(!1),x(``),C(``),D(``)):O&&!e&&k(!1),(0,Q.useEffect)(()=>{p===`passkey`&&!c?m(d?`recovery_key`:l?`totp`:`password`):p===`totp`&&!l?m(c?`passkey`:d?`recovery_key`:`password`):p===`recovery_key`&&!d&&m(c?`passkey`:l?`totp`:`password`)},[c,l,d,p]);let A=async e=>{if(e&&e.preventDefault(),!h){D(o(`auth.passwordRequired`,`Password is required`));return}T(!0),D(``);try{let e=h;n?.password_version===2&&n?.username&&(e=await Le(h,n.username)),await i({password:e}),t()}catch(e){D(Y(e,o))}finally{T(!1)}},j=async()=>{T(!0),D(``);try{await i({passkeyAssertion:await Ee(await De())}),t()}catch(e){D(Y(e,o))}finally{T(!1)}},M=async e=>{let n=e||b;if(n.length!==6){D(o(`account.totp.invalidCode`,`Please enter a 6-digit code`));return}T(!0),D(``);try{let e=crypto.randomUUID(),r=new TextEncoder().encode(n+e),a=await crypto.subtle.digest(`SHA-256`,r);await i({totpTokenHash:Array.from(new Uint8Array(a)).map(e=>e.toString(16).padStart(2,`0`)).join(``),totpSalt:e}),t()}catch(e){D(Y(e,o))}finally{T(!1)}},N=async e=>{e&&e.preventDefault();let n=S.trim();if(!n){D(o(`account.recoveryKey.keyRequired`,`Recovery key is required`));return}T(!0),D(``);try{await i({recoveryKey:n}),t()}catch(e){D(Y(e,o))}finally{T(!1)}},P=(c?1:0)+(d?1:0)+(l?1:0)+(u?1:0);return(0,$.jsx)(R,{isOpen:e,onClose:t,title:r||o(`account.verifyIdentity`,`Security Verification`),icon:`shield`,className:`max-w-md w-full`,children:(0,$.jsxs)(`div`,{className:I,children:[(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400 mb-4`,children:o(`account.verifyIdentityDesc`,`To protect your account security, please verify your identity before proceeding.`)}),P>1&&(0,$.jsx)(ct,{method:p,onSelectMethod:e=>{m(e),D(``)},hasPasskey:c,hasRecoveryKey:d,hasTotp:l,hasPassword:u}),E&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-4`,children:E}),p===`recovery_key`&&(0,$.jsx)(ft,{recoveryKey:S,setRecoveryKey:C,loading:w,onSubmit:N}),p===`passkey`&&(0,$.jsx)(lt,{loading:w,onVerify:j}),p===`totp`&&(0,$.jsx)(ut,{totpCode:b,setTotpCode:x,loading:w,onVerify:M}),p===`password`&&(0,$.jsx)(dt,{password:h,setPassword:_,showPassword:v,setShowPassword:y,loading:w,onSubmit:A})]})})},ht=({user:e,onRefresh:t})=>{let{t:r}=g(),[i,a]=(0,Q.useState)(!1),[o,s]=(0,Q.useState)(!1),[c,d]=(0,Q.useState)(null),[f,p]=(0,Q.useState)(!1),[h,_]=(0,Q.useState)(!1),[x,C]=(0,Q.useState)(null),T=!!(e.has_recovery_keys||e.recovery_keys_encrypted),E=()=>{C(null),s(!0)},D=()=>{s(!1),a(!0)},O=async e=>{let n=await Fe(e);if(d(n.recovery_key),p(!1),X.isUnlocked())try{await X.wrapCurrentKeyForRecovery(n.recovery_key)}catch(e){console.warn(`[RecoveryKey] Failed to wrap E2EE key with new recovery key:`,e)}else if(e.recoveryKey)try{await X.unlockWithRecoveryKey(e.recoveryKey),await X.wrapCurrentKeyForRecovery(n.recovery_key)}catch(e){console.warn(`[RecoveryKey] Failed to unlock and re-wrap with new recovery key:`,e)}else if(e.passkeyAssertion)try{await X.unlockUser(),await X.wrapCurrentKeyForRecovery(n.recovery_key)}catch(e){console.warn(`[RecoveryKey] Failed to unlock via Passkey and re-wrap:`,e)}C({text:r(`account.recoveryKey.rotateSuccess`,`恢复密钥已成功轮换!请立即妥善离线保存新密钥,旧密钥已失效。`),intent:V.SUCCESS}),t()},k=e=>{navigator.clipboard.writeText(e),_(!0),setTimeout(()=>_(!1),2e3)};return(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsxs)(`div`,{className:`flex flex-col sm:flex-row sm:items-center justify-between gap-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,$.jsx)(l,{size:18,className:`text-amber-500`}),(0,$.jsx)(W,{style:{margin:0},children:r(`account.recoveryKey.title`,`紧急恢复密钥`)}),(0,$.jsx)(K,{intent:T?V.SUCCESS:V.NONE,minimal:!0,round:!0,children:T?r(`account.recoveryKey.configured`,`已配置`):r(`account.recoveryKey.notConfigured`,`未配置`)}),(0,$.jsx)(ce,{content:r(`account.recoveryKey.oneWayTooltip`,`采用 SHA-256 单向密码学散列保护,服务端不存储明文且不可解密,仅支持身份轮换验证。`),position:M.TOP,children:(0,$.jsx)(K,{intent:V.PRIMARY,minimal:!0,round:!0,icon:(0,$.jsx)(n,{size:12,style:{display:`inline-flex`,alignItems:`center`,marginRight:`4px`}}),children:r(`account.recoveryKey.oneWayHash`,`单向不可逆散列`)})})]}),(0,$.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,$.jsx)(L,{small:!0,intent:V.WARNING,icon:(0,$.jsx)(u,{size:14}),text:r(`account.recoveryKey.rotateBtn`,`轮换恢复密钥`),onClick:E})})]}),(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400`,children:r(`account.recoveryKey.descOneWay`,`紧急恢复密钥是在您丢失所有通行密钥 (Passkey) 或身份验证器时,恢复账号访问及端到端解密数据的唯一凭据。为确保零知识安全,恢复密钥采用单向不可逆散列存储,无法在网页端查看明文,仅支持通过原恢复密钥或现有 Passkey 进行轮换。密钥一旦被使用将立即自动轮换。`)}),x&&(0,$.jsx)(B,{intent:x.intent,className:`my-2`,children:x.text}),c&&(0,$.jsxs)(q,{elevation:F.ZERO,className:`bg-amber-50/50 dark:bg-amber-950/20 border border-amber-300 dark:border-amber-800 p-4 space-y-3`,children:[(0,$.jsxs)(`div`,{className:`flex flex-col sm:flex-row sm:items-center justify-between gap-2`,children:[(0,$.jsxs)(`span`,{className:`text-xs font-semibold text-amber-800 dark:text-amber-300 uppercase tracking-wider flex items-center gap-1.5`,children:[(0,$.jsx)(b,{size:14}),r(`account.recoveryKey.newKeyGenerated`,`已生成全新 30 位恢复密钥(请立即保存)`)]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-1.5 flex-wrap`,children:[(0,$.jsx)(L,{minimal:!0,small:!0,icon:f?(0,$.jsx)(w,{size:14}):(0,$.jsx)(y,{size:14}),onClick:()=>p(!f),title:f?r(`auth.showPassword`,`显示`):r(`auth.hidePassword`,`隐藏`)}),(0,$.jsx)(L,{minimal:!0,small:!0,icon:h?(0,$.jsx)(v,{size:14,className:`text-green-500`}):(0,$.jsx)(S,{size:14}),onClick:()=>k(c),title:r(`common.copy`,`复制`),text:h?r(`common.copied`,`已复制`):r(`common.copy`,`复制`)}),(0,$.jsx)(L,{minimal:!0,small:!0,icon:`cross`,onClick:()=>d(null),title:r(`common.close`,`关闭`)})]})]}),(0,$.jsx)(`div`,{className:`p-3 bg-white dark:bg-gray-900 rounded border border-amber-200 dark:border-amber-800/80 font-mono text-center text-lg sm:text-xl font-bold tracking-widest select-all text-gray-900 dark:text-gray-100`,children:f?`••••••-••••••-••••••-••••••-••••••`:c}),(0,$.jsxs)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400 flex items-center gap-1`,children:[(0,$.jsx)(m,{size:14,className:`shrink-0`}),r(`account.recoveryKey.oneWayWarning`,`注意:此密钥仅在此显示一次,关闭后将无法再次查看!服务端仅保存不可逆散列,请务必将其妥善复制并离线安全保存。`)]})]}),(0,$.jsx)(P,{isOpen:o,onConfirm:D,onCancel:()=>s(!1),cancelButtonText:r(`common.cancel`,`取消`),confirmButtonText:r(`account.recoveryKey.confirmRotateBtn`,`确认进入轮换验证`),intent:V.DANGER,icon:`warning-sign`,children:(0,$.jsx)(`p`,{children:r(`account.recoveryKey.rotateConfirm`,`轮换恢复密钥将立即使原恢复密钥失效,并重新派生信封加密密钥。仅允许通过现有通行密钥 (Passkey) 或原恢复密钥完成轮换验证。是否继续?`)})}),(0,$.jsx)(mt,{isOpen:i,onClose:()=>a(!1),user:e,allowedMethods:[`passkey`,`recovery_key`],title:r(`account.recoveryKey.rotateVerifyTitle`,`验证身份以轮换恢复密钥`),onVerify:O})]})},gt=({user:e,onRefresh:r})=>{let{t:i}=g(),[a,o]=(0,Q.useState)(!1),s=!!(e.mfa_enabled??(e.totp_enabled||e.passkeys_count&&e.passkeys_count>0)),c=async e=>{o(!0);try{await _e(e),r()}catch(e){alert(e.message||i(`common.errorNetwork`,`Network error`))}finally{o(!1)}};return(0,$.jsxs)(q,{elevation:F.ONE,className:`space-y-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`flex items-center justify-between mb-3`,children:(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(n,{size:20,className:s?`text-green-500`:`text-gray-400`}),(0,$.jsx)(W,{style:{margin:0},children:i(`account.mfa.title`,`Multi-Factor Authentication (MFA)`)}),(0,$.jsx)(K,{intent:s?V.SUCCESS:V.NONE,minimal:!0,round:!0,children:s?i(`account.mfa.enabled`,`Configured`):i(`account.mfa.notConfigured`,`Not Configured`)})]})}),(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400 mb-4`,children:i(`account.mfa.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.`)}),s?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(H,{className:`my-3`}),(0,$.jsxs)(`div`,{className:`flex flex-col sm:flex-row sm:items-center justify-between gap-2 py-1`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`div`,{className:`font-medium text-sm text-gray-900 dark:text-gray-100`,children:i(`account.mfa.passwordlessTitle`,`Passwordless Login`)}),(0,$.jsx)(`div`,{className:`text-xs text-gray-500 dark:text-gray-400`,children:i(`account.mfa.passwordlessDesc`,`When enabled, logging in will skip the password requirement and verify your identity directly via Passkey or TOTP.`)})]}),(0,$.jsx)(ae,{checked:!!e.totp_skip_password,onChange:e=>c(e.currentTarget.checked),disabled:a,className:`mb-0 shrink-0`})]})]}):(0,$.jsx)(B,{intent:V.PRIMARY,icon:(0,$.jsx)(t,{size:16,style:{display:`inline-flex`,alignItems:`center`,marginRight:`8px`}}),children:i(`account.mfa.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.`)})]}),(0,$.jsx)(H,{}),(0,$.jsx)(it,{onRefresh:r}),(0,$.jsx)(H,{}),(0,$.jsx)(st,{user:e,onRefresh:r}),(0,$.jsx)(H,{}),(0,$.jsx)(ht,{user:e,onRefresh:r})]})},_t={signup:{label:`account.activity.signup`,icon:(0,$.jsx)(T,{size:14}),intent:V.SUCCESS},login_success:{label:`account.activity.loginSuccess`,icon:(0,$.jsx)(h,{size:14}),intent:V.SUCCESS},login_fail:{label:`account.activity.loginFail`,icon:(0,$.jsx)(d,{size:14}),intent:V.DANGER},logout:{label:`account.activity.logout`,icon:(0,$.jsx)(s,{size:14}),intent:V.NONE},password_change_success:{label:`account.activity.pwChangeSuccess`,icon:(0,$.jsx)(l,{size:14}),intent:V.SUCCESS},password_change_fail:{label:`account.activity.pwChangeFail`,icon:(0,$.jsx)(d,{size:14}),intent:V.DANGER},totp_verify_success:{label:`account.activity.totpVerifySuccess`,icon:(0,$.jsx)(n,{size:14}),intent:V.SUCCESS},totp_verify_fail:{label:`account.activity.totpVerifyFail`,icon:(0,$.jsx)(d,{size:14}),intent:V.DANGER},totp_setup:{label:`account.activity.totpSetup`,icon:(0,$.jsx)(a,{size:14}),intent:V.PRIMARY},totp_removed:{label:`account.activity.totpRemoved`,icon:(0,$.jsx)(x,{size:14}),intent:V.WARNING},recovery_key_used:{label:`account.activity.recoveryKeyUsed`,icon:(0,$.jsx)(l,{size:14}),intent:V.WARNING},recovery_key_rotated:{label:`account.activity.recoveryKeyRotated`,icon:(0,$.jsx)(l,{size:14}),intent:V.WARNING},session_revoked:{label:`account.activity.sessionRevoked`,icon:(0,$.jsx)(s,{size:14}),intent:V.WARNING},passkey_registered:{label:`account.activity.passkeyRegistered`,icon:(0,$.jsx)(l,{size:14}),intent:V.PRIMARY},passkey_deleted:{label:`account.activity.passkeyDeleted`,icon:(0,$.jsx)(x,{size:14}),intent:V.WARNING},passkey_verify_success:{label:`account.activity.passkeyVerifySuccess`,icon:(0,$.jsx)(n,{size:14}),intent:V.SUCCESS},passkey_verify_fail:{label:`account.activity.passkeyVerifyFail`,icon:(0,$.jsx)(d,{size:14}),intent:V.DANGER}},vt=e=>{if(!e)return`desktop`;let t=e.toLowerCase();return t.includes(`mobile`)||t.includes(`android`)||t.includes(`iphone`)||t.includes(`tablet`)||t.includes(`ipad`)?`mobile-phone`:`desktop`},yt=(e,t)=>{if(!e)return t;let n=e;return n.includes(`Edg/`)?`Edge`:n.includes(`Chrome/`)&&!n.includes(`Edg/`)?`Chrome`:n.includes(`Firefox/`)?`Firefox`:n.includes(`Safari/`)&&!n.includes(`Chrome/`)?`Safari`:t},bt=({userAgent:e})=>{let{t}=g();if(!e)return(0,$.jsx)(`span`,{className:`text-gray-500`,children:`—`});let n=yt(e,t(`account.sessions.unknownDevice`,`Unknown Device`));return(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(ee,{icon:vt(e),className:`text-gray-400`}),(0,$.jsx)(`span`,{children:n})]}),(0,$.jsx)(`div`,{className:`text-xs text-gray-400 mt-1 max-w-xs truncate`,title:e,children:e})]})},xt=({hoveredSessionId:e,setHoveredSessionId:t})=>{let{t:n}=g(),[r,i]=(0,Q.useState)([]),[a,o]=(0,Q.useState)(!0),[s,c]=(0,Q.useState)(!0),[l,d]=(0,Q.useState)(!1),p=(0,Q.useCallback)(async e=>{o(!0);try{let t=new URLSearchParams({limit:`20`});e&&t.set(`before`,String(e));let n=await ze(t.toString());i(e?e=>[...e,...n]:n),c(n.length===20)}catch{}finally{o(!1)}},[]);(0,Q.useEffect)(()=>{p()},[p]);let m=()=>{let e=r[r.length-1];e&&p(e.timestamp)},h=e=>_t[e]??{label:e,icon:(0,$.jsx)(f,{size:14}),intent:V.NONE};return(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between cursor-pointer`,onClick:()=>d(!l),children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(f,{size:20,className:`text-purple-500`}),(0,$.jsx)(W,{style:{margin:0},children:n(`account.activity.title`,`Account Activity`)}),r.length>0&&(0,$.jsxs)(K,{minimal:!0,round:!0,intent:V.NONE,children:[r.length,s?`+`:``]})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(L,{minimal:!0,icon:(0,$.jsx)(u,{size:14}),onClick:e=>{e.stopPropagation(),p()}}),(0,$.jsx)(C,{size:16,className:`text-gray-400 transition-transform ${l?`rotate-180`:``}`})]})]}),l&&(0,$.jsx)(`div`,{className:`mt-4`,children:a&&r.length===0?(0,$.jsx)(`div`,{className:`flex justify-center py-8`,children:(0,$.jsx)(ne,{size:24})}):r.length===0?(0,$.jsx)(`p`,{className:`text-sm text-gray-500 text-center py-6`,children:n(`account.activity.empty`,`No activity recorded yet.`)}):(0,$.jsxs)(`div`,{className:`overflow-x-auto`,children:[(0,$.jsxs)(se,{interactive:!0,striped:!0,className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{children:n(`account.activity.action`,`Action`)}),(0,$.jsx)(`th`,{children:n(`account.activity.time`,`Time`)}),(0,$.jsx)(`th`,{children:n(`account.activity.session`,`Session`)}),(0,$.jsx)(`th`,{children:n(`account.activity.ip`,`IP Address`)}),(0,$.jsx)(`th`,{children:n(`account.activity.device`,`Device`)})]})}),(0,$.jsx)(`tbody`,{children:r.map(r=>{let i=h(r.action),a=``,o=r.session_id_hash||``;if(r.extra)try{let e=JSON.parse(r.extra);if(e.reason){let t=`account.activity.reasons.${e.reason}`,r=n(t);a=r&&r!==t?r:e.reason.replace(/[_-]+/g,` `).replace(/\b\w/g,e=>e.toUpperCase())}}catch{}return(0,$.jsxs)(`tr`,{onMouseEnter:()=>{o&&t?.(o)},onMouseLeave:()=>{o&&t?.(null)},className:e&&e===o?`bg-gray-100/80 dark:bg-slate-800/80`:``,children:[(0,$.jsx)(`td`,{className:`align-middle`,children:(0,$.jsxs)(`div`,{className:`flex flex-col items-start gap-1`,children:[(0,$.jsx)(K,{minimal:!0,intent:i.intent,icon:i.icon,children:n(i.label,r.action)}),a&&(0,$.jsx)(`span`,{className:`text-xs text-gray-500 italic ml-1`,children:a})]})}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500 whitespace-nowrap align-middle`,children:Z(new Date(r.timestamp*1e3))}),(0,$.jsx)(`td`,{className:`font-mono text-xs text-gray-500 align-middle`,children:o?o.slice(0,8):`—`}),(0,$.jsx)(`td`,{className:`font-mono text-xs text-gray-500 align-middle`,children:r.ip_address||`—`}),(0,$.jsx)(`td`,{className:`align-middle`,children:(0,$.jsx)(bt,{userAgent:r.user_agent||null})})]},r.id)})})]}),s&&(0,$.jsx)(`div`,{className:`flex justify-center mt-3`,children:(0,$.jsx)(L,{minimal:!0,loading:a,text:n(`account.activity.loadMore`,`Load More`),onClick:m})})]})})]})},St=({hoveredSessionId:e,setHoveredSessionId:t})=>{let{t:n}=g(),[r,i]=(0,Q.useState)([]),[a,o]=(0,Q.useState)(!0),[c,l]=(0,Q.useState)(null),[d,f]=(0,Q.useState)(!1),[m,h]=(0,Q.useState)(!1),_=r.filter(e=>!e.is_current).length,v=(0,Q.useCallback)(async()=>{o(!0);try{let e=await be(),t=Date.now();i((e||[]).filter(e=>e.expires_at*1e3>t))}catch{}finally{o(!1)}},[]);(0,Q.useEffect)(()=>{v()},[v]);let y=async e=>{l(e);try{(await fe(e)).is_current?window.location.reload():i(t=>t.filter(t=>t.id!==e))}catch(e){alert(e.message||n(`account.sessions.revokeFailed`,`Failed to revoke session.`))}finally{l(null)}};return(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between mb-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(p,{size:20,className:`text-green-500`}),(0,$.jsx)(W,{style:{margin:0},children:n(`account.sessions.title`,`Active Sessions`)})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[_>0&&(0,$.jsx)(L,{small:!0,minimal:!0,intent:V.DANGER,icon:(0,$.jsx)(s,{size:14}),text:n(`account.sessions.revokeOthers`,`Revoke Other Sessions`),loading:d,onClick:()=>h(!0)}),(0,$.jsx)(L,{minimal:!0,icon:(0,$.jsx)(u,{size:14}),onClick:()=>v()})]})]}),a?(0,$.jsx)(`div`,{className:`flex justify-center py-4`,children:(0,$.jsx)(ne,{size:24})}):r.length===0?(0,$.jsx)(`p`,{className:`text-sm text-gray-500 text-center py-4`,children:n(`account.sessions.empty`,`No active sessions found.`)}):(0,$.jsx)(`div`,{className:`overflow-x-auto`,children:(0,$.jsxs)(se,{interactive:!0,striped:!0,className:`w-full text-sm`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{children:n(`account.sessions.device`,`Device`)}),(0,$.jsx)(`th`,{children:n(`account.sessions.session`,`Session`)}),(0,$.jsx)(`th`,{children:n(`account.sessions.ip`,`IP Address`)}),(0,$.jsx)(`th`,{children:n(`account.sessions.loginTime`,`Login Time`)}),(0,$.jsx)(`th`,{children:n(`account.sessions.expires`,`Expires`)}),(0,$.jsx)(`th`,{children:n(`account.sessions.action`,`Action`)})]})}),(0,$.jsx)(`tbody`,{children:r.map(r=>(0,$.jsxs)(`tr`,{onMouseEnter:()=>t?.(r.id),onMouseLeave:()=>t?.(null),className:e===r.id?`bg-gray-100/80 dark:bg-slate-800/80`:``,children:[(0,$.jsx)(`td`,{children:(0,$.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,$.jsx)(bt,{userAgent:r.user_agent}),r.is_current&&(0,$.jsx)(K,{minimal:!0,intent:V.SUCCESS,className:`mt-0.5`,children:n(`account.sessions.current`,`Current`)})]})}),(0,$.jsx)(`td`,{className:`font-mono text-xs text-gray-500 align-middle`,children:r.id?r.id.slice(0,8):`—`}),(0,$.jsx)(`td`,{className:`font-mono text-xs text-gray-500 align-middle`,children:r.ip_address||`—`}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500 align-middle`,children:r.created_at?Z(new Date(r.created_at*1e3)):`—`}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500 align-middle`,children:Z(new Date(r.expires_at*1e3))}),(0,$.jsx)(`td`,{className:`align-middle`,children:(0,$.jsx)(L,{small:!0,minimal:!0,intent:V.DANGER,disabled:r.is_current,icon:(0,$.jsx)(s,{size:14}),text:n(`account.sessions.revoke`,`Revoke`),loading:c===r.id,onClick:()=>y(r.id)})})]},r.id))})]})}),(0,$.jsx)(P,{isOpen:m,cancelButtonText:n(`common.cancel`,`Cancel`),confirmButtonText:n(`account.sessions.confirmRevokeOthers`,`Revoke Other Sessions`),intent:V.DANGER,icon:`log-out`,onCancel:()=>h(!1),onConfirm:async()=>{f(!0);try{await Xe(),i(e=>e.filter(e=>e.is_current)),h(!1)}catch(e){alert(e.message||n(`account.sessions.revokeFailed`,`Failed to revoke session.`))}finally{f(!1)}},loading:d,children:(0,$.jsx)(`p`,{children:n(`account.sessions.revokeOthersConfirmText`,`Are you sure you want to revoke all other active sessions? All other logged-in devices will be signed out immediately.`)})})]})},Ct=({users:e,currentUserId:t,onRefresh:r,registrationEnabled:a=!0,onRefreshSettings:o})=>{let{t:s}=g(),[c,l]=(0,Q.useState)(!1),[u,d]=(0,Q.useState)(``),[f,p]=(0,Q.useState)(``),[m,h]=(0,Q.useState)(`user`),[_,v]=(0,Q.useState)(!1),[y,b]=(0,Q.useState)(!1),[x,S]=(0,Q.useState)(a),[C,w]=(0,Q.useState)(!1);(0,Q.useEffect)(()=>{S(a)},[a]);let E=async e=>{S(e),w(!0);try{await Ye({registration_enabled:String(e)}),o?.()}catch(t){console.error(t),S(!e),alert(t.message||s(`common.errorNetwork`,`Network error`))}finally{w(!1)}},D=async()=>{if(!Ve.test(u)){alert(s(`account.formatTipUsername`));return}if(!Ue.test(f)){alert(s(`account.formatTipPassword`));return}v(!0);try{await he({username:u,password:await Le(f,u),role:m}),l(!1),d(``),p(``),b(!1),r()}catch(e){console.error(e),alert(e.message||s(`common.errorNetwork`))}finally{v(!1)}},O=async e=>{if(confirm(s(`account.confirmDeleteUser`)))try{await ye(e),r()}catch(e){console.error(e)}};return(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`flex flex-col sm:flex-row sm:items-center justify-between gap-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(n,{size:20,className:`text-red-500`}),(0,$.jsx)(W,{style:{margin:0},children:s(`account.userManagement`)})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-4 flex-wrap`,children:[(0,$.jsx)(ae,{label:s(`account.enableRegistration`,`开放新用户注册`),checked:x,disabled:C,onChange:e=>E(e.currentTarget.checked),className:`mb-0`}),(0,$.jsx)(L,{className:`whitespace-nowrap`,icon:(0,$.jsx)(T,{size:16}),intent:V.SUCCESS,text:s(`account.createUser`),onClick:()=>l(!0)})]})]}),(0,$.jsx)(q,{elevation:F.ONE,className:`p-0 overflow-hidden overflow-x-auto`,children:(0,$.jsxs)(se,{interactive:!0,striped:!0,className:`w-full`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{children:s(`account.username`)}),(0,$.jsx)(`th`,{children:s(`account.role`)}),(0,$.jsx)(`th`,{children:`MFA`}),(0,$.jsx)(`th`,{children:`ID`}),(0,$.jsx)(`th`,{children:s(`account.createdAt`)}),(0,$.jsx)(`th`,{children:s(`account.lastActive`)}),(0,$.jsx)(`th`,{children:s(`account.lastResolve`)}),(0,$.jsx)(`th`,{className:`text-right`,children:s(`account.actions`)})]})}),(0,$.jsx)(`tbody`,{children:e.map(e=>(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`td`,{className:`font-bold`,children:e.username}),(0,$.jsx)(`td`,{children:(0,$.jsx)(K,{minimal:!0,intent:e.role===`admin`?V.DANGER:V.NONE,children:e.role===`admin`?s(`account.roleAdmin`):s(`account.roleUser`)})}),(0,$.jsx)(`td`,{children:(0,$.jsxs)(`div`,{className:`flex flex-wrap gap-1`,children:[!!e.totp_enabled&&(0,$.jsx)(K,{minimal:!0,intent:V.SUCCESS,children:`TOTP`}),!!(e.passkeys_count&&e.passkeys_count>0)&&(0,$.jsx)(K,{minimal:!0,intent:V.PRIMARY,children:`Passkey`}),!e.totp_enabled&&!(e.passkeys_count&&e.passkeys_count>0)&&(0,$.jsx)(K,{minimal:!0,intent:V.NONE,style:{color:`#8a9ba8`},children:s(`account.mfaNone`,`无`)})]})}),(0,$.jsx)(`td`,{children:(0,$.jsx)(`code`,{className:`text-xs`,children:e.id})}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500`,children:e.created_at?Z(new Date(e.created_at*1e3)):`-`}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500`,children:e.last_active_at?Z(new Date(e.last_active_at*1e3)):`-`}),(0,$.jsx)(`td`,{className:`text-xs text-gray-500`,children:e.last_resolve_at?Z(new Date(e.last_resolve_at*1e3)):`-`}),(0,$.jsx)(`td`,{className:`text-right`,children:(0,$.jsx)(`div`,{className:`inline-flex items-center gap-1`,children:(0,$.jsx)(L,{minimal:!0,intent:V.DANGER,icon:(0,$.jsx)(i,{size:14}),disabled:e.id===t,onClick:()=>O(e.id)})})})]},e.id))})]})}),(0,$.jsx)(R,{isOpen:c,onClose:()=>{l(!1),b(!1)},title:s(`account.createNewUser`),icon:`user`,children:(0,$.jsxs)(`div`,{className:`p-6 space-y-4`,children:[(0,$.jsx)(G,{label:s(`account.username`),children:(0,$.jsx)(U,{value:u,onChange:e=>d(e.target.value),placeholder:s(`auth.usernamePlaceholder`)})}),(0,$.jsx)(G,{label:s(`account.initialPassword`),children:(0,$.jsx)(U,{type:y?`text`:`password`,value:f,onChange:e=>p(e.target.value),placeholder:s(`auth.passwordPlaceholder`),rightElement:(0,$.jsx)(L,{minimal:!0,icon:y?`eye-open`:`eye-off`,onClick:()=>b(!y),title:y?s(`auth.hidePassword`,`Hide password`):s(`auth.showPassword`,`Show password`)})})}),(0,$.jsx)(G,{label:s(`account.userRole`),children:(0,$.jsx)(ie,{fill:!0,value:m,onChange:e=>h(e.target.value),options:[{label:s(`account.roleUser`),value:`user`},{label:s(`account.roleAdmin`),value:`admin`}]})}),(0,$.jsxs)(`div`,{className:`flex justify-end gap-2 mt-6`,children:[(0,$.jsx)(L,{text:s(`account.cancel`),onClick:()=>l(!1)}),(0,$.jsx)(L,{intent:V.PRIMARY,text:s(`account.createNow`),loading:_,onClick:D})]})]})})]})},wt=({initialSettings:e,onRefresh:t})=>{let{t:n}=g(),[r,i]=(0,Q.useState)(e),[a,o]=(0,Q.useState)(!1),[s,l]=(0,Q.useState)(!1);return Q.useEffect(()=>{i(e)},[e]),(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(c,{size:20,className:`text-gray-500`}),(0,$.jsx)(W,{style:{margin:0},children:n(`account.systemSettings`,`System Settings`)})]}),(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-4`,children:[(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsx)(W,{className:`text-sm font-bold opacity-70 text-blue-500`,children:`Cloudflare Turnstile`}),(0,$.jsx)(G,{label:`Site Key`,children:(0,$.jsx)(U,{value:r.turnstile_site_key||``,onChange:e=>i({...r,turnstile_site_key:e.target.value}),placeholder:`0x000...`})}),(0,$.jsx)(G,{label:`Secret Key`,children:(0,$.jsx)(U,{type:s?`text`:`password`,value:r.turnstile_secret_key||``,onChange:e=>i({...r,turnstile_secret_key:e.target.value}),placeholder:`0x000...`,rightElement:(0,$.jsx)(L,{minimal:!0,icon:s?`eye-open`:`eye-off`,onClick:()=>l(!s),title:s?n(`auth.hidePassword`,`Hide password`):n(`auth.showPassword`,`Show password`)})})})]}),(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsx)(W,{className:`text-sm font-bold opacity-70 text-green-500`,children:n(`account.featureToggle`,`Feature Toggle`)}),(0,$.jsx)(ae,{label:n(`account.enableTurnstileSignup`,`Enable verification on Signup`),checked:r.turnstile_enabled_signup===`true`,onChange:e=>i({...r,turnstile_enabled_signup:String(e.currentTarget.checked)})}),(0,$.jsx)(ae,{label:n(`account.enableTurnstileLogin`,`Enable verification on Login`),checked:r.turnstile_enabled_login===`true`,onChange:e=>i({...r,turnstile_enabled_login:String(e.currentTarget.checked)})})]})]}),(0,$.jsx)(H,{className:`my-4`}),(0,$.jsx)(`div`,{className:`flex justify-end`,children:(0,$.jsx)(L,{intent:V.PRIMARY,icon:`floppy-disk`,text:n(`common.save`,`Save`),loading:a,onClick:async()=>{o(!0);try{await Ye({turnstile_site_key:r.turnstile_site_key||``,turnstile_secret_key:r.turnstile_secret_key||``,turnstile_enabled_signup:r.turnstile_enabled_signup||`false`,turnstile_enabled_login:r.turnstile_enabled_login||`false`}),alert(n(`common.saveSuccess`,`Settings saved`)),t()}catch(e){console.error(e)}finally{o(!1)}}})})]})]})},Tt=({isAdmin:e})=>{let{t}=g();return(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(W,{className:`text-red-500 flex items-center gap-2`,children:[(0,$.jsx)(i,{size:20}),` `,t(`account.dangerZone`)]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-6`,children:[(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsx)(W,{children:t(`account.clearLogs`)}),(0,$.jsx)(`p`,{className:`text-xs opacity-60 mb-4`,children:t(`account.clearLogsDesc`)}),(0,$.jsx)(L,{fill:!0,intent:V.DANGER,text:t(`account.clearLogsBtn`),icon:`trash`,onClick:async()=>{if(confirm(t(`account.confirmClearLogs`)))try{await Ne(),alert(t(`account.clearLogsSuccess`))}catch(e){console.error(e)}}})]}),!e&&(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsx)(W,{children:t(`account.deleteAccount`)}),(0,$.jsx)(`p`,{className:`text-xs opacity-60 mb-4`,children:t(`account.deleteAccountDesc`)}),(0,$.jsx)(L,{fill:!0,intent:V.DANGER,text:t(`account.deleteAccountBtn`),icon:`delete`,onClick:async()=>{if(confirm(t(`account.confirmDeleteAccount`)))try{await Oe(),window.location.href=`/`}catch(e){console.error(e),alert(e.message||t(`common.errorNetwork`))}}})]})]}),(0,$.jsx)(B,{intent:V.WARNING,icon:`info-sign`,title:t(`account.inactivityPolicy`),children:(0,$.jsx)(`p`,{className:`text-sm`,children:t(`account.inactivityDesc`)})})]})},Et=[{label:j(`account.timezoneAuto`,`Auto (Browser Default)`),value:``},{label:`Pacific/Midway (Midway Island) (Standard: UTC-11)`,value:`Pacific/Midway`},{label:`Pacific/Honolulu (Honolulu, Hawaii) (Standard: UTC-10)`,value:`Pacific/Honolulu`},{label:`America/Anchorage (Anchorage, Alaska) (Standard: UTC-9 / DST: UTC-8)`,value:`America/Anchorage`},{label:`America/Los_Angeles (Pacific Time) (Standard: UTC-8 / DST: UTC-7)`,value:`America/Los_Angeles`},{label:`America/Denver (Mountain Time) (Standard: UTC-7 / DST: UTC-6)`,value:`America/Denver`},{label:`America/Chicago (Central Time) (Standard: UTC-6 / DST: UTC-5)`,value:`America/Chicago`},{label:`America/New_York (Eastern Time) (Standard: UTC-5 / DST: UTC-4)`,value:`America/New_York`},{label:`America/Halifax (Atlantic Time) (Standard: UTC-4 / DST: UTC-3)`,value:`America/Halifax`},{label:`America/Argentina/Buenos_Aires (Buenos Aires) (Standard: UTC-3)`,value:`America/Argentina/Buenos_Aires`},{label:`America/Noronha (Fernando de Noronha) (Standard: UTC-2)`,value:`America/Noronha`},{label:`Atlantic/Azores (Azores) (Standard: UTC-1 / DST: UTC+0)`,value:`Atlantic/Azores`},{label:`UTC (Coordinated Universal Time) (Standard: UTC+0)`,value:`UTC`},{label:`Europe/London (London) (Standard: UTC+0 / DST: UTC+1)`,value:`Europe/London`},{label:`Europe/Paris (Paris, Berlin, Rome) (Standard: UTC+1 / DST: UTC+2)`,value:`Europe/Paris`},{label:`Europe/Kyiv (Kyiv, Helsinki, Cairo) (Standard: UTC+2 / DST: UTC+3)`,value:`Europe/Kyiv`},{label:`Europe/Moscow (Moscow) (Standard: UTC+3)`,value:`Europe/Moscow`},{label:`Asia/Dubai (Dubai, Baku) (Standard: UTC+4)`,value:`Asia/Dubai`},{label:`Asia/Karachi (Karachi, Tashkent) (Standard: UTC+5)`,value:`Asia/Karachi`},{label:`Asia/Dhaka (Dhaka, Almaty) (Standard: UTC+6)`,value:`Asia/Dhaka`},{label:`Asia/Bangkok (Bangkok, Jakarta) (Standard: UTC+7)`,value:`Asia/Bangkok`},{label:`Asia/Singapore (Beijing, Shanghai, Singapore, Taipei) (Standard: UTC+8)`,value:`Asia/Singapore`},{label:`Asia/Tokyo (Tokyo, Seoul) (Standard: UTC+9)`,value:`Asia/Tokyo`},{label:`Australia/Sydney (Sydney, Melbourne) (Standard: UTC+10 / DST: UTC+11)`,value:`Australia/Sydney`},{label:`Pacific/Noumea (Nouméa) (Standard: UTC+11)`,value:`Pacific/Noumea`},{label:`Pacific/Auckland (Auckland, Suva) (Standard: UTC+12 / DST: UTC+13)`,value:`Pacific/Auckland`}],Dt=({me:e,isEditingUsername:t,setIsEditingUsername:n,editUsername:i,setEditUsername:a,usernameLoading:o,usernameFocused:s,setUsernameFocused:c,onUpdateUsername:l,onUpdateTimezone:u})=>{let{t:d}=g();return(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2 mb-4`,children:[(0,$.jsx)(_,{size:20,className:`text-blue-500`}),(0,$.jsx)(W,{style:{margin:0},children:d(`account.personalInfo`)})]}),(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsx)(G,{label:d(`account.username`),children:(0,$.jsx)(`div`,{className:`flex gap-2`,children:t?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(ce,{content:d(`account.formatTipUsername`),isOpen:s,position:M.TOP,intent:V.PRIMARY,className:`w-full`,children:(0,$.jsx)(`div`,{className:`w-full block`,children:(0,$.jsx)(U,{fill:!0,value:i,onChange:e=>a(e.target.value),onFocus:()=>c(!0),onBlur:()=>c(!1),autoFocus:!0})})}),(0,$.jsxs)(N,{children:[(0,$.jsx)(L,{icon:(0,$.jsx)(v,{size:16}),intent:V.SUCCESS,loading:o,onClick:l}),(0,$.jsx)(L,{icon:(0,$.jsx)(E,{size:16}),onClick:()=>{n(!1),a(e?.username||``)}})]})]}):(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(U,{fill:!0,value:e?.username,readOnly:!0}),(0,$.jsx)(L,{icon:(0,$.jsx)(r,{size:16}),onClick:()=>n(!0)})]})})}),(0,$.jsx)(G,{label:d(`account.userId`),children:(0,$.jsx)(U,{leftIcon:`id-number`,value:e?.id,disabled:!0})}),(0,$.jsx)(G,{label:d(`account.timezone`,`Timezone`),children:(0,$.jsx)(ie,{fill:!0,value:e?.timezone||``,onChange:e=>u?.(e.target.value||null),options:Et})})]})]})},Ot=({me:e,onRefresh:t})=>{let{t:r}=g(),i=!!(e?.passkeys_count&&e.passkeys_count>0),a=!!e?.totp_enabled,[o,s]=(0,Q.useState)(`password`),[c,u]=(0,Q.useState)(``),[d,f]=(0,Q.useState)(!1),[p,h]=(0,Q.useState)(``),[_,v]=(0,Q.useState)(``),[y,b]=(0,Q.useState)(!1),[x,S]=(0,Q.useState)(!1),[C,w]=(0,Q.useState)(!1),[T,E]=(0,Q.useState)(null);return(0,Q.useEffect)(()=>{o===`passkey`&&!i?s(a?`totp`:`password`):o===`totp`&&!a&&s(i?`passkey`:`password`)},[i,a,o]),(0,$.jsxs)(q,{elevation:F.ONE,className:`isolate`,style:{isolation:`isolate`},children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2 mb-4`,children:[(0,$.jsx)(l,{size:20,className:`text-orange-500`}),(0,$.jsx)(W,{style:{margin:0},children:r(`account.changePassword`,`Change Password`)})]}),(i||a)&&(0,$.jsx)(`div`,{className:`mb-4 isolate`,style:{isolation:`isolate`},children:(0,$.jsx)(`div`,{className:`w-full bg-gray-100/70 dark:bg-gray-800/70 p-1 rounded-lg`,children:(0,$.jsxs)(N,{fill:!0,variant:`minimal`,style:{isolation:`isolate`},children:[(0,$.jsx)(L,{small:!0,active:o===`password`,intent:o===`password`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(m,{size:14}),text:r(`account.mfa.password`,`Password`),onClick:()=>{s(`password`),E(null)}}),i&&(0,$.jsx)(L,{small:!0,active:o===`passkey`,intent:o===`passkey`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(l,{size:14}),text:r(`account.mfa.passkey`,`Passkey`),onClick:()=>{s(`passkey`),E(null)}}),a&&(0,$.jsx)(L,{small:!0,active:o===`totp`,intent:o===`totp`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(n,{size:14}),text:r(`account.mfa.totp`,`TOTP`),onClick:()=>{s(`totp`),E(null)}})]})})}),T&&(0,$.jsx)(B,{intent:T.intent,className:`mb-4`,children:T.text}),(0,$.jsxs)(`form`,{onSubmit:async n=>{if(n.preventDefault(),!Ue.test(_)){E({text:r(`account.formatTipPassword`,`Password must be 12-100 characters and contain letters, numbers, and symbols.`),intent:V.DANGER});return}if(!e?.username){E({text:r(`auth.sessionExpired`,`Session expired. Please log in again.`),intent:V.DANGER});return}w(!0),E(null);try{let n=await Le(_,e.username);if(o===`passkey`)await Se({passkeyAssertion:await Ee(await De()),newPassword:n});else if(o===`totp`){if(p.length!==6){E({text:r(`account.totp.invalidCode`,`Please enter a 6-digit code`),intent:V.DANGER}),w(!1);return}let e=crypto.randomUUID(),t=new TextEncoder().encode(p+e),i=await crypto.subtle.digest(`SHA-256`,t);await Se({totpTokenHash:Array.from(new Uint8Array(i)).map(e=>e.toString(16).padStart(2,`0`)).join(``),totpSalt:e,newPassword:n})}else{if(!c){E({text:r(`auth.passwordRequired`,`Current password is required`),intent:V.DANGER}),w(!1);return}let t=c;(e.password_version??1)===2&&(t=await Le(c,e.username)),await Se({oldPassword:t,newPassword:n})}E({text:r(`account.passwordSuccess`,`Password updated successfully!`),intent:V.SUCCESS}),u(``),h(``),v(``),t&&t()}catch(e){E({text:Y(e,r),intent:V.DANGER})}finally{w(!1)}},className:`space-y-4`,children:[o===`password`&&(0,$.jsx)(G,{label:r(`account.currentPassword`,`Current Password`),children:(0,$.jsx)(U,{leftIcon:`lock`,type:d?`text`:`password`,value:c,onChange:e=>u(e.target.value),rightElement:(0,$.jsx)(L,{minimal:!0,icon:d?`eye-open`:`eye-off`,onClick:()=>f(!d),title:r(d?`auth.hidePassword`:`auth.showPassword`)}),required:!0})}),o===`totp`&&(0,$.jsx)(G,{label:r(`account.totpCode`,`Authenticator Code`),children:(0,$.jsx)(Re,{length:6,value:p,onChange:h,disabled:C})}),o===`passkey`&&(0,$.jsx)(B,{intent:V.PRIMARY,children:(0,$.jsx)(`span`,{className:`text-xs`,children:r(`account.passkey.changePwNotice`,`You will be prompted to verify via biometric authentication or your security key when submitting.`)})}),(0,$.jsx)(G,{label:r(`account.newPassword`,`New Password`),children:(0,$.jsx)(ce,{content:r(`account.formatTipPassword`,`Password must be 12-100 characters and contain letters, numbers, and symbols.`),isOpen:x,position:M.TOP,intent:V.PRIMARY,className:`w-full`,children:(0,$.jsx)(`div`,{className:`w-full block`,children:(0,$.jsx)(U,{leftIcon:`lock`,type:y?`text`:`password`,value:_,onChange:e=>v(e.target.value),onFocus:()=>S(!0),onBlur:()=>S(!1),rightElement:(0,$.jsx)(L,{minimal:!0,icon:y?`eye-open`:`eye-off`,onClick:()=>b(!y),title:r(y?`auth.hidePassword`:`auth.showPassword`)}),required:!0})})})}),(0,$.jsx)(L,{fill:!0,intent:o===`passkey`?V.PRIMARY:V.WARNING,type:`submit`,loading:C,icon:o===`passkey`?(0,$.jsx)(l,{size:16}):void 0,text:o===`passkey`?r(`account.changePasswordWithPasskey`,`Verify with Passkey & Update`):r(`account.updatePassword`,`Update Password`)})]})]})},kt=({isOpen:e,onClose:t,user:r,onSuccess:i})=>{let{t:a}=g(),o=!!(r?.passkeys_count&&r.passkeys_count>0),s=!!r?.totp_enabled,[c,u]=(0,Q.useState)(`password`),[d,f]=(0,Q.useState)(``),[p,h]=(0,Q.useState)(``),[_,v]=(0,Q.useState)(``),[y,b]=(0,Q.useState)(!1),[x,S]=(0,Q.useState)(``),[C,w]=(0,Q.useState)(!1),[T,E]=(0,Q.useState)(``),D=(0,Q.useRef)(null),O=(0,Q.useRef)(null);(0,Q.useEffect)(()=>{c===`passkey`&&!o?u(s?`totp`:`password`):c===`totp`&&!s&&u(o?`passkey`:`password`)},[o,s,c]);let k=async(e,t)=>{if(e&&e.preventDefault(),d.length!==4||!qe.test(d)){E(a(`auth.pinFormatTip`,`PIN must be exactly 4 digits`));return}if(d!==p){E(a(`auth.pinMatchTip`,`PINs do not match`));return}w(!0),E(``);try{let e=r?.id||sessionStorage.getItem(`dns_worker_user_id`);if(!e){E(a(`auth.sessionExpired`,`Session expired. Logging out...`)),setTimeout(()=>{A()},1500);return}let n=await Ge(d,e),o={};if(c===`passkey`)o.passkeyAssertion=await Ee(await De());else if(c===`totp`){let e=(t||x).replace(/\s/g,``);if(e.length!==6){E(a(`account.totp.invalidCode`,`Please enter a 6-digit code`)),w(!1);return}let n=crypto.randomUUID();o.totpTokenHash=await He(e,n),o.totpSalt=n}else{if(!_){E(a(`auth.passwordRequired`,`Password is required for verification`)),w(!1);return}let e=_;r?.password_version===2&&r?.username&&(e=await Le(_,r.username)),o.password=e}await ve(n,o),f(``),h(``),v(``),S(``),i()}catch(e){E(Y(e,a))}finally{w(!1)}},A=()=>{E(``),f(``),h(``),v(``),b(!1),S(``),u(`password`),t()};return(0,$.jsx)(R,{isOpen:e,onClose:A,title:r?.pin_enabled?a(`auth.changePin`,`Change PIN`):a(`auth.configurePin`,`Configure PIN`),icon:`key`,className:`pb-0`,style:{width:`420px`},children:(0,$.jsxs)(`form`,{onSubmit:k,children:[(0,$.jsxs)(`div`,{className:I,children:[T&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-4`,children:T}),(0,$.jsx)(G,{label:a(`auth.newPin`,`New 4-Digit PIN`),labelFor:`new-pin-input`,helperText:a(`auth.pinHelper`,`Digits only, e.g., 1234`),children:(0,$.jsx)(Re,{length:4,value:d,onChange:f,type:`password`,disabled:C,autoFocus:!0,onComplete:()=>D.current?.focus()})}),(0,$.jsx)(G,{label:a(`auth.confirmPin`,`Confirm New PIN`),labelFor:`confirm-pin-input`,children:(0,$.jsx)(Re,{ref:D,length:4,value:p,onChange:h,type:`password`,disabled:C,onComplete:()=>{c===`totp`?O.current?.focus():c===`password`&&document.getElementById(`verify-pw-input`)?.focus()}})}),(0,$.jsx)(H,{className:`my-4`}),(0,$.jsxs)(`div`,{className:`mb-3 space-y-2`,children:[(0,$.jsx)(`span`,{className:`font-semibold text-sm block`,children:a(`auth.verifyIdentity`,`Verify Identity`)}),(o||s)&&(0,$.jsx)(`div`,{className:`w-full bg-gray-100/70 dark:bg-gray-800/70 p-1 rounded-lg isolate`,style:{isolation:`isolate`},children:(0,$.jsxs)(N,{fill:!0,variant:`minimal`,style:{isolation:`isolate`},children:[(0,$.jsx)(L,{small:!0,active:c===`password`,intent:c===`password`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(m,{size:14}),text:a(`account.mfa.password`,`Password`),onClick:()=>{u(`password`),E(``)}}),o&&(0,$.jsx)(L,{small:!0,active:c===`passkey`,intent:c===`passkey`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(l,{size:14}),text:a(`account.mfa.passkey`,`Passkey`),onClick:()=>{u(`passkey`),E(``)}}),s&&(0,$.jsx)(L,{small:!0,active:c===`totp`,intent:c===`totp`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(n,{size:14}),text:a(`account.mfa.totp`,`TOTP`),onClick:()=>{u(`totp`),E(``)}})]})})]}),c===`password`&&(0,$.jsx)(G,{label:a(`auth.currentPassword`,`Current Password`),labelFor:`verify-pw-input`,children:(0,$.jsx)(U,{id:`verify-pw-input`,type:y?`text`:`password`,placeholder:a(`auth.passwordPlaceholder`,`Enter current password`),value:_,onChange:e=>v(e.target.value),leftIcon:`lock`,rightElement:(0,$.jsx)(L,{minimal:!0,icon:y?`eye-open`:`eye-off`,onClick:()=>b(!y),title:y?a(`auth.hidePassword`,`Hide password`):a(`auth.showPassword`,`Show password`)}),required:!0})}),c===`totp`&&(0,$.jsx)(G,{label:a(`auth.totpCode`,`TOTP Code`),labelFor:`verify-totp-input`,children:(0,$.jsx)(Re,{ref:O,length:6,value:x,onChange:S,disabled:C,onComplete:e=>k(void 0,e),autoFocus:!0})}),c===`passkey`&&(0,$.jsx)(B,{intent:V.PRIMARY,icon:(0,$.jsx)(l,{size:16}),children:(0,$.jsx)(`span`,{className:`text-xs`,children:a(`account.passkey.submitNotice`,a(`account.passkey.changePwNotice`,`You will be prompted to verify via biometric authentication or your security key when submitting.`))})})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{onClick:A,children:a(`common.cancel`,`Cancel`)}),(0,$.jsx)(L,{type:`submit`,intent:V.PRIMARY,loading:C,children:a(`common.save`,`Save`)})]})})]})})},At=({isOpen:e,onClose:t,user:r,onSuccess:i})=>{let{t:a}=g(),o=!!(r?.passkeys_count&&r.passkeys_count>0),s=!!r?.totp_enabled,[c,u]=(0,Q.useState)(`password`),[d,f]=(0,Q.useState)(``),[p,h]=(0,Q.useState)(!1),[_,v]=(0,Q.useState)(``),[y,x]=(0,Q.useState)(!1),[S,C]=(0,Q.useState)(``);(0,Q.useEffect)(()=>{c===`passkey`&&!o?u(s?`totp`:`password`):c===`totp`&&!s&&u(o?`passkey`:`password`)},[o,s,c]);let w=async(e,t)=>{e&&e.preventDefault(),x(!0),C(``);try{let e={};if(c===`passkey`)e.passkeyAssertion=await Ee(await De());else if(c===`totp`){let n=(t||_).replace(/\s/g,``);if(n.length!==6){C(a(`account.totp.invalidCode`,`Please enter a 6-digit code`)),x(!1);return}let r=crypto.randomUUID();e.totpTokenHash=await He(n,r),e.totpSalt=r}else{if(!d){C(a(`auth.passwordRequired`,`Password is required for verification`)),x(!1);return}let t=d;r?.password_version===2&&r?.username&&(t=await Le(d,r.username)),e.password=t}await me(e),f(``),v(``),i()}catch(e){C(Y(e,a))}finally{x(!1)}},T=()=>{C(``),f(``),h(!1),v(``),u(`password`),t()};return(0,$.jsx)(R,{isOpen:e,onClose:T,title:a(`auth.disablePin`,`Disable Session Lock`),icon:`trash`,className:`pb-0`,style:{width:`420px`},children:(0,$.jsxs)(`form`,{onSubmit:w,children:[(0,$.jsxs)(`div`,{className:I,children:[S&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-4`,children:S}),(0,$.jsx)(B,{intent:V.WARNING,className:`mb-4`,icon:(0,$.jsx)(b,{size:20}),children:a(`auth.disablePinWarning`,`Disabling the PIN will disable the inactivity session locking feature completely.`)}),(0,$.jsxs)(`div`,{className:`mb-3 space-y-2`,children:[(0,$.jsx)(`span`,{className:`font-semibold text-sm block`,children:a(`auth.verifyIdentity`,`Verify Identity`)}),(o||s)&&(0,$.jsx)(`div`,{className:`w-full bg-gray-100/70 dark:bg-gray-800/70 p-1 rounded-lg isolate`,style:{isolation:`isolate`},children:(0,$.jsxs)(N,{fill:!0,variant:`minimal`,style:{isolation:`isolate`},children:[(0,$.jsx)(L,{small:!0,active:c===`password`,intent:c===`password`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(m,{size:14}),text:a(`account.mfa.password`,`Password`),onClick:()=>{u(`password`),C(``)}}),o&&(0,$.jsx)(L,{small:!0,active:c===`passkey`,intent:c===`passkey`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(l,{size:14}),text:a(`account.mfa.passkey`,`Passkey`),onClick:()=>{u(`passkey`),C(``)}}),s&&(0,$.jsx)(L,{small:!0,active:c===`totp`,intent:c===`totp`?V.PRIMARY:V.NONE,icon:(0,$.jsx)(n,{size:14}),text:a(`account.mfa.totp`,`TOTP`),onClick:()=>{u(`totp`),C(``)}})]})})]}),c===`password`&&(0,$.jsx)(G,{label:a(`auth.currentPassword`,`Current Password`),labelFor:`disable-pw-input`,children:(0,$.jsx)(U,{id:`disable-pw-input`,type:p?`text`:`password`,placeholder:a(`auth.passwordPlaceholder`,`Enter current password`),value:d,onChange:e=>f(e.target.value),leftIcon:`lock`,rightElement:(0,$.jsx)(L,{minimal:!0,icon:p?`eye-open`:`eye-off`,onClick:()=>h(!p),title:p?a(`auth.hidePassword`,`Hide password`):a(`auth.showPassword`,`Show password`)}),required:!0})}),c===`totp`&&(0,$.jsx)(G,{label:a(`auth.totpCode`,`TOTP Code`),labelFor:`disable-totp-input`,children:(0,$.jsx)(Re,{length:6,value:_,onChange:v,disabled:y,onComplete:e=>w(void 0,e),autoFocus:!0})}),c===`passkey`&&(0,$.jsx)(B,{intent:V.PRIMARY,icon:(0,$.jsx)(l,{size:16}),children:(0,$.jsx)(`span`,{className:`text-xs`,children:a(`account.passkey.submitNotice`,a(`account.passkey.changePwNotice`,`You will be prompted to verify via biometric authentication or your security key when submitting.`))})})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{onClick:T,children:a(`common.cancel`,`Cancel`)}),(0,$.jsx)(L,{type:`submit`,intent:V.DANGER,loading:y,children:a(`common.disable`,`Disable`)})]})})]})})},jt=({user:e,onRefresh:t})=>{let{t:r}=g(),[i,a]=(0,Q.useState)(!1),[o,s]=(0,Q.useState)(!1),[c,l]=(0,Q.useState)(!1),u=!!e?.pin_enabled,d=e?.session_lock_timeout||15,f=async e=>{let n=Number(e.currentTarget.value);a(!0);try{await Ie({session_lock_timeout:n}),t()}catch(e){console.error(`Failed to change session lock timeout`,e)}finally{a(!1)}},p=()=>{s(!0)};return(0,$.jsxs)(q,{elevation:F.ONE,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between mb-4`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(n,{size:20,className:`text-indigo-600`}),(0,$.jsx)(W,{className:`m-0!`,children:r(`auth.sessionLockTitle`,`Idle Session Lock`)})]}),!u&&(0,$.jsx)(L,{icon:`key`,intent:V.PRIMARY,onClick:p,disabled:i,children:r(`auth.configurePin`,`Configure 4-Digit PIN`)})]}),(0,$.jsx)(`p`,{className:`text-gray-500 text-sm mb-4`,children:r(`auth.sessionLockDesc`,`Lock the interface with a 4-digit PIN when the application is idle to protect active sessions from unauthorized physical access.`)}),(0,$.jsx)(`div`,{className:`flex flex-col gap-4 mb-4`,children:(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsx)(`span`,{children:r(`auth.inactivityTimeout`,`Inactivity Timeout`)}),(0,$.jsx)(ie,{value:d,onChange:f,disabled:!u||i,options:[{label:r(`auth.timeout1m`,`1 Minute`),value:1},{label:r(`auth.timeout2m`,`2 Minutes`),value:2},{label:r(`auth.timeout5m`,`5 Minutes`),value:5},{label:r(`auth.timeout15m`,`15 Minutes`),value:15},{label:r(`auth.timeout30m`,`30 Minutes`),value:30},{label:r(`auth.timeout60m`,`60 Minutes`),value:60}]})]})}),u&&(0,$.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[(0,$.jsx)(L,{icon:`edit`,onClick:p,disabled:i,children:r(`auth.changePin`,`Change PIN`)}),(0,$.jsx)(L,{icon:`trash`,intent:V.DANGER,onClick:()=>{l(!0)},disabled:i,children:r(`auth.disablePin`,`Disable PIN & Lock`)}),(0,$.jsx)(L,{icon:`lock`,intent:V.WARNING,onClick:async()=>{a(!0);try{await ue(),window.dispatchEvent(new Event(`session_paused`))}catch(e){console.error(`Failed to lock session immediately`,e)}finally{a(!1)}},disabled:i,children:r(`auth.lockNow`,`Lock Now`)})]}),(0,$.jsx)(kt,{isOpen:o,onClose:()=>s(!1),user:e,onSuccess:()=>{s(!1),t()}}),(0,$.jsx)(At,{isOpen:c,onClose:()=>l(!1),user:e,onSuccess:()=>{l(!1),t()}})]})};function Mt({user:e,onRefresh:t,toasterRef:n}){let{t:r}=g(),[i,a]=(0,Q.useState)(null),[o,s]=(0,Q.useState)(!0),[c,l]=(0,Q.useState)(!1),[u,d]=(0,Q.useState)(!1),[f,p]=(0,Q.useState)(!1),[m,h]=(0,Q.useState)(!1),[_,v]=(0,Q.useState)(``),[y,b]=(0,Q.useState)(``),[x,S]=(0,Q.useState)(!1),[C,w]=(0,Q.useState)(``),[T,E]=(0,Q.useState)(``),[D,O]=(0,Q.useState)(null),[k,A]=(0,Q.useState)(!1),j=(0,Q.useCallback)(async()=>{s(!0);try{a(await X.getUserStatus()),p(X.isUnlocked())}catch(e){console.error(`[useE2eeCardState] Failed to fetch E2EE status:`,e)}finally{s(!1)}},[]);(0,Q.useEffect)(()=>{j()},[j]);let M=e.passkeys_count&&e.passkeys_count>0||(i?.userPasskeyCount||0)>0,N=!!i?.enabled;return{status:i,loading:o,processing:c,isUnlocked:f,isPqc:(0,Q.useMemo)(()=>{if(!i?.publicKey)return!1;try{let e=JSON.parse(i.publicKey);return e.alg===`P256-MLKEM768`||!!e.pqc_pk}catch{return!1}},[i?.publicKey]),isLogsE2eeEnabled:N,hasPasskey:M,isDisableAlertOpen:u,setIsDisableAlertOpen:d,isRecoveryDialogOpen:m,setIsRecoveryDialogOpen:h,recoveryKeyInput:_,setRecoveryKeyInput:v,recoveryUnlockError:y,setRecoveryUnlockError:b,initRecoveryDialogOpen:x,setInitRecoveryDialogOpen:S,initRecoveryKeyInput:C,setInitRecoveryKeyInput:w,initRecoveryError:T,setInitRecoveryError:E,rotatedKey:D,setRotatedKey:O,copiedKey:k,handleToggleLogs:async e=>{if(e){l(!0);try{await X.enableUserE2ee(),n.current?.show({message:r(`account.e2ee.enableSuccess`),intent:V.SUCCESS,icon:`lock`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Failed to enable E2EE:`,e),n.current?.show({message:e.message||r(`account.e2ee.enableError`),intent:V.DANGER,icon:`error`})}finally{l(!1)}}else d(!0)},handleConfirmDisable:async()=>{l(!0);try{await X.disableUserE2ee(),n.current?.show({message:r(`account.e2ee.disableSuccess`),intent:V.WARNING,icon:`unlock`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Failed to disable E2EE:`,e),n.current?.show({message:e.message||r(`account.e2ee.disableError`),intent:V.DANGER,icon:`error`})}finally{l(!1),d(!1)}},handleUnlock:async()=>{l(!0);try{await X.unlockUser()&&(p(!0),n.current?.show({message:r(`account.e2ee.unlockSuccess`),intent:V.SUCCESS,icon:`tick`}))}catch(e){console.error(`[useE2eeCardState] Failed to unlock private key:`,e),n.current?.show({message:e.message||r(`account.e2ee.unlockError`),intent:V.DANGER,icon:`error`})}finally{l(!1)}},handleUnlockWithRecoveryKey:async e=>{e.preventDefault();let i=_.trim();if(i){l(!0),b(``);try{await X.unlockWithRecoveryKey(i);try{let e=await Fe({recoveryKey:i});await X.wrapCurrentKeyForRecovery(e.recovery_key),O(e.recovery_key)}catch(e){console.warn(`[useE2eeCardState] Recovery key auto-rotation failed:`,e)}p(!0),h(!1),v(``),n.current?.show({message:r(`account.e2ee.unlockSuccess`),intent:V.SUCCESS,icon:`tick`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Recovery unlock failed:`,e),b(e.message||r(`account.e2ee.recoveryUnlockFailed`))}finally{l(!1)}}},handleGenerateKeyWithPasskey:async()=>{l(!0);try{await X.enableUserE2ee(),n.current?.show({message:r(`account.e2ee.keypairGenSuccess`),intent:V.SUCCESS,icon:`tick`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Generate key with Passkey failed:`,e),n.current?.show({message:e.message||r(`account.e2ee.keypairGenError`),intent:V.DANGER,icon:`error`})}finally{l(!1)}},handleUpgradeToPqc:async()=>{l(!0);try{await X.rotateUserE2eeKey(),n.current?.show({message:r(`account.e2ee.upgradePqcSuccess`,`已成功升级到后量子混合加密 (P256-MLKEM768)`),intent:V.SUCCESS,icon:`tick`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Upgrade to PQC failed:`,e),n.current?.show({message:e.message||r(`account.e2ee.upgradePqcError`,`升级到后量子加密失败`),intent:V.DANGER,icon:`error`})}finally{l(!1)}},handleInitWithRecoveryKey:async e=>{e.preventDefault();let i=C.trim();if(i){l(!0),E(``);try{await X.initUserE2eeWithRecoveryKey(i);try{let e=await Fe({recoveryKey:i});await X.wrapCurrentKeyForRecovery(e.recovery_key),O(e.recovery_key)}catch(e){console.warn(`[useE2eeCardState] Recovery key auto-rotation failed:`,e)}S(!1),w(``),p(!0),n.current?.show({message:r(`account.e2ee.keypairGenSuccess`),intent:V.SUCCESS,icon:`tick`}),await j(),t?.()}catch(e){console.error(`[useE2eeCardState] Init keypair with recovery key failed:`,e),E(e.message||r(`account.e2ee.initFailed`))}finally{l(!1)}}},handleLockDevice:()=>{X.clearStorage(),p(!1),n.current?.show({message:r(`account.e2ee.lockSuccess`,`已锁定,私钥已从本地清除`),intent:V.NONE})},handleCopyRotatedKey:e=>{navigator.clipboard.writeText(e),A(!0),setTimeout(()=>A(!1),2e3)}}}const Nt=({isLogsE2eeEnabled:e})=>{let{t}=g();return(0,$.jsxs)(`div`,{className:`flex items-center justify-between mb-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(m,{size:20,className:e?`text-emerald-500`:`text-gray-400`}),(0,$.jsx)(W,{style:{margin:0},children:t(`account.e2ee.title`,`E2EE`)}),(0,$.jsx)(K,{intent:e?V.SUCCESS:V.NONE,minimal:!0,round:!0,children:t(e?`account.e2ee.enabled`:`account.e2ee.disabled`)})]}),(0,$.jsx)(re,{placement:`bottom-end`,usePortal:!0,content:(0,$.jsxs)(`div`,{className:`p-4 max-w-sm`,children:[(0,$.jsx)(te,{children:t(`account.e2ee.trustBoundaryTitle`)}),(0,$.jsx)(`p`,{className:`text-sm text-gray-700 dark:text-gray-300 m-0`,children:t(`account.e2ee.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.`)})]}),children:(0,$.jsx)(L,{icon:`help`,variant:`minimal`,intent:V.NONE})})]})},Pt=({hasPasskey:e,processing:n,onGenerateKeyWithPasskey:r,onOpenInitRecoveryDialog:i})=>{let{t:a}=g();return(0,$.jsx)(B,{intent:V.PRIMARY,icon:`shield`,className:`my-3 text-xs`,children:(0,$.jsxs)(`div`,{className:`space-y-3`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`h5`,{className:`font-semibold text-sm mb-1 text-blue-900 dark:text-blue-100`,children:a(`account.e2ee.noKeypairTitle`)}),(0,$.jsx)(`p`,{className:`text-xs text-blue-800 dark:text-blue-200 m-0`,children:a(`account.e2ee.noKeypairDesc`)})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap pt-1`,children:[e?(0,$.jsx)(L,{intent:V.PRIMARY,small:!0,icon:(0,$.jsx)(t,{size:14}),text:a(`account.e2ee.initPasskeyBtn`),loading:n,onClick:r}):(0,$.jsx)(L,{intent:V.PRIMARY,small:!0,icon:(0,$.jsx)(l,{size:14}),text:a(`account.e2ee.addPasskeyBtn`),onClick:()=>{document.getElementById(`passkeys-section`)?.scrollIntoView({behavior:`smooth`})}}),(0,$.jsx)(L,{intent:V.NONE,small:!0,icon:(0,$.jsx)(l,{size:14}),text:a(`account.e2ee.initRecoveryBtn`),onClick:i})]})]})})},Ft=({status:e,isLogsE2eeEnabled:r,isUnlocked:i,isPqc:a,hasPasskey:o,processing:s,loading:c,onToggleLogs:l,onUpgradeToPqc:d,onLockDevice:f,onUnlockPasskey:p,onOpenRecoveryUnlockDialog:m})=>{let{t:h}=g();return(0,$.jsxs)(`div`,{className:`space-y-4`,children:[(0,$.jsx)(H,{className:`my-2`}),(0,$.jsxs)(`div`,{className:`flex flex-col sm:flex-row sm:items-center justify-between gap-4 p-4 rounded-lg border border-gray-200 dark:border-gray-800 bg-gray-50/70 dark:bg-gray-900/50`,children:[(0,$.jsxs)(`div`,{className:`space-y-1`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(`span`,{className:`font-semibold text-sm text-gray-900 dark:text-gray-100`,children:h(`account.e2ee.enableSwitch`)}),(0,$.jsx)(K,{intent:r?V.SUCCESS:V.NONE,minimal:!0,round:!0,children:h(r?`account.e2ee.enabled`:`account.e2ee.disabled`)})]}),(0,$.jsx)(`p`,{className:`text-xs text-gray-500 dark:text-gray-400 m-0`,children:h(`account.e2ee.enableSwitchDesc`)})]}),(0,$.jsx)(`div`,{className:`shrink-0 flex items-center`,children:(0,$.jsx)(ae,{checked:r,disabled:s||c,onChange:e=>l(e.currentTarget.checked),large:!0,className:`mb-0`})})]}),(0,$.jsxs)(`div`,{className:`pt-2 border-t border-gray-100 dark:border-gray-800/80 space-y-2 text-xs`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`span`,{className:`opacity-70 flex items-center gap-1.5`,children:[(0,$.jsx)(t,{size:13}),h(`account.e2ee.keyType`),`:`]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,$.jsx)(`span`,{className:`font-mono`,children:a?`P256-MLKEM768 + AES-256-GCM`:`ECDH P-256 + AES-256-GCM`}),a?(0,$.jsx)(K,{minimal:!0,intent:V.PRIMARY,className:`text-[10px]`,children:h(`account.e2ee.pqcTag`,`PQC / NIST FIPS 203`)}):(0,$.jsx)(L,{small:!0,intent:V.PRIMARY,minimal:!0,icon:(0,$.jsx)(u,{size:12}),text:h(`account.e2ee.upgradePqcBtn`,`升级到后量子 (PQC)`),loading:s,onClick:d})]})]}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`span`,{className:`opacity-70 flex items-center gap-1.5`,children:[(0,$.jsx)(n,{size:13}),h(`account.e2ee.deviceState`),`:`]}),i?(0,$.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,$.jsx)(K,{minimal:!0,intent:V.SUCCESS,className:`text-[10px]`,children:h(`account.e2ee.unlocked`)}),(0,$.jsx)(L,{small:!0,minimal:!0,intent:V.NONE,icon:`lock`,text:h(`account.e2ee.lockDevice`,`锁定此设备`),onClick:f})]}):(0,$.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap justify-end`,children:[o&&(0,$.jsx)(L,{small:!0,minimal:!0,intent:V.PRIMARY,icon:(0,$.jsx)(u,{size:12}),text:h(`account.e2ee.unlockButton`),loading:s,onClick:p}),(0,$.jsx)(L,{small:!0,minimal:!0,intent:V.NONE,icon:(0,$.jsx)(t,{size:12}),text:h(`account.e2ee.unlockRecoveryButton`),onClick:m})]})]}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,$.jsxs)(`span`,{className:`opacity-70`,children:[h(`account.e2ee.protectedPasskeys`),`:`]}),(0,$.jsxs)(`span`,{className:`font-mono`,children:[e?.wrappedPasskeys?.length||0,` `,h(`account.e2ee.devicesUnit`)]})]})]})]})},It=({isOpen:e,processing:t,onConfirm:n,onCancel:r})=>{let{t:i}=g();return(0,$.jsx)(P,{isOpen:e,confirmButtonText:i(`common.confirmDisable`),cancelButtonText:i(`common.cancel`),intent:V.DANGER,icon:`unlock`,onConfirm:n,onCancel:r,loading:t,children:(0,$.jsx)(`p`,{children:i(`account.e2ee.disableConfirm`)})})},Lt=({isOpen:e,onClose:t,recoveryKeyInput:n,setRecoveryKeyInput:r,recoveryUnlockError:i,processing:a,onSubmit:o})=>{let{t:s}=g();return(0,$.jsx)(R,{isOpen:e,onClose:t,title:s(`account.e2ee.unlockWithRecoveryTitle`),icon:`key`,className:`dark:bg-gray-900`,children:(0,$.jsxs)(`form`,{onSubmit:o,children:[(0,$.jsxs)(`div`,{className:I,children:[(0,$.jsx)(`p`,{className:`text-xs text-gray-500 dark:text-gray-400 mb-3`,children:s(`account.e2ee.unlockWithRecoveryDesc`)}),i&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-3 text-xs`,children:i}),(0,$.jsx)(G,{label:s(`account.e2ee.recoveryKeyInputLabel`),labelFor:`recovery-key-input`,children:(0,$.jsx)(U,{id:`recovery-key-input`,placeholder:`123456-789012-345678-901234-567890`,value:n,onChange:e=>r(e.target.value),leftIcon:`key`,className:`font-mono text-xs`,autoFocus:!0})})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{onClick:t,text:s(`common.cancel`)}),(0,$.jsx)(L,{type:`submit`,intent:V.PRIMARY,loading:a,disabled:!n.trim(),text:s(`account.e2ee.unlockConfirm`)})]})})]})})},Rt=({isOpen:e,onClose:t,initRecoveryKeyInput:n,setInitRecoveryKeyInput:r,initRecoveryError:i,processing:a,onSubmit:o})=>{let{t:s}=g();return(0,$.jsx)(R,{isOpen:e,onClose:t,title:s(`account.e2ee.initWithRecoveryTitle`),icon:`key`,className:`dark:bg-gray-900`,children:(0,$.jsxs)(`form`,{onSubmit:o,children:[(0,$.jsxs)(`div`,{className:I,children:[(0,$.jsx)(`p`,{className:`text-xs text-gray-500 dark:text-gray-400 mb-3`,children:s(`account.e2ee.initWithRecoveryDesc`)}),i&&(0,$.jsx)(B,{intent:V.DANGER,className:`mb-3 text-xs`,children:i}),(0,$.jsx)(G,{label:s(`account.e2ee.recoveryKeyInputLabel`),labelFor:`init-recovery-key-input`,children:(0,$.jsx)(U,{id:`init-recovery-key-input`,placeholder:`123456-789012-345678-901234-567890`,value:n,onChange:e=>r(e.target.value),leftIcon:`key`,className:`font-mono text-xs`,autoFocus:!0})})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsxs)(`div`,{className:J,children:[(0,$.jsx)(L,{onClick:t,text:s(`common.cancel`)}),(0,$.jsx)(L,{type:`submit`,intent:V.PRIMARY,loading:a,disabled:!n.trim(),text:s(`account.e2ee.generateKeypairBtn`)})]})})]})})},zt=({rotatedKey:e,onClose:t,copiedKey:n,onCopyKey:r})=>{let{t:i}=g();return(0,$.jsxs)(R,{isOpen:!!e,onClose:t,title:i(`account.recoveryKey.autoRotatedTitle`),icon:`warning-sign`,isCloseButtonShown:!1,canOutsideClickClose:!1,className:`dark:bg-gray-900`,children:[(0,$.jsxs)(`div`,{className:I,children:[(0,$.jsx)(B,{intent:V.WARNING,icon:(0,$.jsx)(b,{size:16}),className:`mb-4 text-xs`,children:i(`account.recoveryKey.autoRotatedNotice`)}),e&&(0,$.jsxs)(`div`,{className:`space-y-3`,children:[(0,$.jsx)(`div`,{className:`p-3 bg-gray-100 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700 font-mono text-center text-lg font-bold tracking-widest select-all text-gray-900 dark:text-gray-100`,children:e}),(0,$.jsx)(`div`,{className:`flex justify-center`,children:(0,$.jsx)(L,{intent:V.PRIMARY,icon:n?(0,$.jsx)(v,{size:14,className:`text-green-400`}):(0,$.jsx)(S,{size:14}),text:i(n?`common.copied`:`common.copyKey`),onClick:()=>r(e)})})]})]}),(0,$.jsx)(`div`,{className:z,children:(0,$.jsx)(`div`,{className:J,children:(0,$.jsx)(L,{intent:V.SUCCESS,onClick:t,text:i(`account.recoveryKey.savedAndClose`)})})})]})},Bt=({user:e,onRefresh:t})=>{let{t:n}=g(),r=(0,Q.useRef)(null),{status:i,loading:a,processing:o,isUnlocked:s,isPqc:c,isLogsE2eeEnabled:l,hasPasskey:u,isDisableAlertOpen:d,setIsDisableAlertOpen:f,isRecoveryDialogOpen:p,setIsRecoveryDialogOpen:m,recoveryKeyInput:h,setRecoveryKeyInput:_,recoveryUnlockError:v,initRecoveryDialogOpen:y,setInitRecoveryDialogOpen:b,initRecoveryKeyInput:x,setInitRecoveryKeyInput:S,initRecoveryError:C,setInitRecoveryError:w,rotatedKey:T,setRotatedKey:E,copiedKey:D,handleToggleLogs:O,handleConfirmDisable:k,handleUnlock:A,handleUnlockWithRecoveryKey:j,handleGenerateKeyWithPasskey:M,handleUpgradeToPqc:N,handleInitWithRecoveryKey:P,handleLockDevice:ee,handleCopyRotatedKey:I}=Mt({user:e,onRefresh:t,toasterRef:r});return(0,$.jsxs)(q,{elevation:F.ONE,className:`space-y-6`,children:[(0,$.jsx)(oe,{ref:r}),(0,$.jsxs)(`div`,{children:[(0,$.jsx)(Nt,{isLogsE2eeEnabled:l}),(0,$.jsx)(`p`,{className:`text-sm text-gray-600 dark:text-gray-400 mb-3`,children:n(`account.e2ee.desc`)}),a?(0,$.jsx)(`div`,{className:`flex items-center justify-center p-6`,children:(0,$.jsx)(ne,{size:20})}):i?.hasKeys?(0,$.jsx)(Ft,{status:i,isLogsE2eeEnabled:l,isUnlocked:s,isPqc:c,hasPasskey:u,processing:o,loading:a,onToggleLogs:O,onUpgradeToPqc:N,onLockDevice:ee,onUnlockPasskey:A,onOpenRecoveryUnlockDialog:()=>{_(``),m(!0)}}):(0,$.jsx)(Pt,{hasPasskey:u,processing:o,onGenerateKeyWithPasskey:M,onOpenInitRecoveryDialog:()=>{w(``),S(``),b(!0)}})]}),(0,$.jsx)(It,{isOpen:d,processing:o,onConfirm:k,onCancel:()=>f(!1)}),(0,$.jsx)(Lt,{isOpen:p,onClose:()=>m(!1),recoveryKeyInput:h,setRecoveryKeyInput:_,recoveryUnlockError:v,processing:o,onSubmit:j}),(0,$.jsx)(Rt,{isOpen:y,onClose:()=>b(!1),initRecoveryKeyInput:x,setInitRecoveryKeyInput:S,initRecoveryError:C,processing:o,onSubmit:P}),(0,$.jsx)(zt,{rotatedKey:T,onClose:()=>E(null),copiedKey:D,onCopyKey:I})]})},Vt=()=>{let[e,t]=(0,Q.useState)(null),[r,i]=(0,Q.useState)([]),[a,o]=(0,Q.useState)(!0),[s,c]=(0,Q.useState)(null),{t:l,i18n:u}=g(),f=We(),[p,m]=(0,Q.useState)(!1),[h,_]=(0,Q.useState)(``),[v,y]=(0,Q.useState)(!1),[b,x]=(0,Q.useState)(!1),[S,C]=(0,Q.useState)({}),w=async()=>{try{i(await je())}catch(e){console.error(e)}},T=async()=>{try{C(await Ce())}catch(e){console.error(e)}},E=(0,Q.useCallback)(async()=>{try{let e=await Te();if(t(e),_(e.username),window.dispatchEvent(new Event(`user_updated`)),e.timezone){let{setSystemTimeZone:t}=await A(async()=>{let{setSystemTimeZone:e}=await import(`./date-IKj2stuv.js`);return{setSystemTimeZone:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10]));t(e.timezone)}if(e.locale){let{setSystemLocale:t}=await A(async()=>{let{setSystemLocale:e}=await import(`./date-IKj2stuv.js`);return{setSystemLocale:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10]));t(e.locale),u.changeLanguage(e.locale)}e.role===`admin`&&(w(),T())}catch(e){console.error(e)}finally{o(!1)}},[]);return(0,Q.useEffect)(()=>{E()},[E]),a?(0,$.jsx)(`div`,{className:`p-8 text-center`,children:l(`common.loading`)}):(0,$.jsxs)(`div`,{className:le(`max-w-5xl mx-auto space-y-8`,f?`p-0`:`px-8`),children:[(0,$.jsxs)(`div`,{className:`flex justify-between items-end`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsx)(`h2`,{className:`bp6-heading`,children:l(`account.title`)}),(0,$.jsx)(`p`,{className:`bp6-text-muted`,children:l(`account.subtitle`)})]}),e&&(0,$.jsx)(K,{large:!0,round:!0,intent:e.role===`admin`?V.DANGER:V.PRIMARY,icon:(0,$.jsx)(n,{size:16}),children:e.role===`admin`?l(`account.roleAdmin`):l(`account.roleUser`)})]}),e?.role===`admin`&&e?.jwt_secret_warning&&(0,$.jsx)(B,{intent:V.WARNING,icon:(0,$.jsx)(d,{size:18}),title:l(`account.jwtSecretWarningTitle`),children:(0,$.jsx)(`div`,{className:`text-sm leading-relaxed mt-1`,children:l(`account.jwtSecretWarningDesc`)})}),(0,$.jsxs)(`div`,{className:`grid grid-cols-1 md:grid-cols-2 gap-6`,children:[(0,$.jsx)(Dt,{me:e,isEditingUsername:p,setIsEditingUsername:m,editUsername:h,setEditUsername:_,usernameLoading:v,usernameFocused:b,setUsernameFocused:x,onUpdateUsername:async()=>{if(h===e?.username){m(!1);return}if(!Ve.test(h)){alert(l(`account.formatTipUsername`));return}y(!0);try{await Ie({username:h}),t(e=>e?{...e,username:h}:null),m(!1)}catch(e){console.error(e),alert(e.message||l(`common.errorNetwork`))}finally{y(!1)}},onUpdateTimezone:async e=>{try{await Ie({timezone:e||``}),t(t=>t?{...t,timezone:e}:null);let{setSystemTimeZone:n}=await A(async()=>{let{setSystemTimeZone:e}=await import(`./date-IKj2stuv.js`);return{setSystemTimeZone:e}},__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10]));n(e||``)}catch(e){console.error(e),alert(e.message||l(`common.errorNetwork`))}}}),(0,$.jsx)(Ot,{me:e,onRefresh:E})]}),e&&(0,$.jsx)(gt,{user:e,onRefresh:E}),e&&(0,$.jsx)(Bt,{user:e,onRefresh:E}),e&&(0,$.jsx)(jt,{user:e,onRefresh:E}),e&&(0,$.jsx)(St,{hoveredSessionId:s,setHoveredSessionId:c}),e&&(0,$.jsx)(xt,{hoveredSessionId:s,setHoveredSessionId:c}),e?.role===`admin`&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(H,{}),(0,$.jsx)(`div`,{className:`space-y-4`,children:(0,$.jsx)(Ct,{users:r,currentUserId:e.id,onRefresh:w,registrationEnabled:S.registration_enabled!==`false`,onRefreshSettings:T})}),(0,$.jsx)(H,{}),(0,$.jsx)(`div`,{className:`space-y-4`,children:(0,$.jsx)(wt,{initialSettings:S,onRefresh:T})})]}),(0,$.jsx)(H,{}),(0,$.jsx)(`div`,{className:`space-y-4`,children:(0,$.jsx)(Tt,{isAdmin:e?.role===`admin`})})]})};export{Vt as AccountView};