diff --git a/blueprint.yaml b/blueprint.yaml index 0a524f12..adc25055 100644 --- a/blueprint.yaml +++ b/blueprint.yaml @@ -31,6 +31,7 @@ proxy-resources: # - owen@pangolin.net # whitelist-users: # - owen@pangolin.net + # auto-login-idp: 1 headers: - name: X-Example-Header value: example-value diff --git a/docker-compose.example.yml b/docker-compose.example.yml index 21a5134f..84a5140b 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -35,7 +35,7 @@ services: - 80:80 # Port for traefik because of the network_mode traefik: - image: traefik:v3.5 + image: traefik:v3.6 container_name: traefik restart: unless-stopped network_mode: service:gerbil # Ports appear on the gerbil service @@ -52,4 +52,4 @@ networks: default: driver: bridge name: pangolin - enable_ipv6: true \ No newline at end of file + enable_ipv6: true diff --git a/install/config/docker-compose.yml b/install/config/docker-compose.yml index b507e914..90613b2a 100644 --- a/install/config/docker-compose.yml +++ b/install/config/docker-compose.yml @@ -35,7 +35,7 @@ services: - 80:80 {{end}} traefik: - image: docker.io/traefik:v3.5 + image: docker.io/traefik:v3.6 container_name: traefik restart: unless-stopped {{if .InstallGerbil}} @@ -59,4 +59,4 @@ networks: default: driver: bridge name: pangolin -{{if .EnableIPv6}} enable_ipv6: true{{end}} \ No newline at end of file +{{if .EnableIPv6}} enable_ipv6: true{{end}} diff --git a/messages/de-DE.json b/messages/de-DE.json index 15a56f2d..29dfab10 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -1080,11 +1080,11 @@ "actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen", "actionListIdpOrgs": "IDP-Organisationen auflisten", "actionUpdateIdpOrg": "IDP-Organisation aktualisieren", - "actionCreateClient": "Client anlegen", - "actionDeleteClient": "Client löschen", - "actionUpdateClient": "Client aktualisieren", + "actionCreateClient": "Kunde erstellen", + "actionDeleteClient": "Kunde löschen", + "actionUpdateClient": "Kunde aktualisieren", "actionListClients": "Clients auflisten", - "actionGetClient": "Clients abrufen", + "actionGetClient": "Kunde holen", "actionCreateSiteResource": "Site-Ressource erstellen", "actionDeleteSiteResource": "Site-Ressource löschen", "actionGetSiteResource": "Site-Ressource abrufen", @@ -1432,14 +1432,14 @@ }, "siteRequired": "Standort ist erforderlich.", "olmTunnel": "Olm-Tunnel", - "olmTunnelDescription": "Nutzen Sie Olm für die Kundenverbindung", + "olmTunnelDescription": "Nutzen Sie Olm für die Client-Verbindung", "errorCreatingClient": "Fehler beim Erstellen des Clients", "clientDefaultsNotFound": "Standardeinstellungen des Clients nicht gefunden", "createClient": "Client erstellen", "createClientDescription": "Erstellen Sie einen neuen Client für die Verbindung zu Ihren Standorten.", "seeAllClients": "Alle Clients anzeigen", - "clientInformation": "Client Informationen", - "clientNamePlaceholder": "Client Name", + "clientInformation": "Client-Informationen", + "clientNamePlaceholder": "Client-Name", "address": "Adresse", "subnetPlaceholder": "Subnetz", "addressDescription": "Die Adresse, die dieser Client für die Verbindung verwenden wird.", @@ -2110,7 +2110,6 @@ "selectedResources": "Ausgewählte Ressourcen", "enableSelected": "Ausgewählte aktivieren", "disableSelected": "Ausgewählte deaktivieren", - "checkSelectedStatus": "Status der Auswahl überprüfen", "credentials": "Zugangsdaten", "savecredentials": "Zugangsdaten speichern", "regeneratecredentials": "Re-Key", @@ -2136,5 +2135,6 @@ "niceIdUpdateErrorDescription": "Beim Aktualisieren der Nizza-ID ist ein Fehler aufgetreten.", "niceIdCannotBeEmpty": "Nizza-ID darf nicht leer sein", "enterIdentifier": "Identifikator eingeben", - "identifier": "Identifier" + "identifier": "Identifier", + "checkSelectedStatus": "Status der Auswahl überprüfen" } diff --git a/messages/en-US.json b/messages/en-US.json index 88a6595d..3efc5787 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -528,6 +528,8 @@ "targetCreatedDescription": "Target has been created successfully", "targetErrorCreate": "Failed to create target", "targetErrorCreateDescription": "An error occurred while creating the target", + "tlsServerName": "TLS Server Name", + "tlsServerNameDescription": "The TLS server name to use for SNI", "save": "Save", "proxyAdditional": "Additional Proxy Settings", "proxyAdditionalDescription": "Configure how the resource handles proxy settings", @@ -2123,9 +2125,9 @@ "olmUpdateAvailableInfo": "An updated version of Olm is available. Please update to the latest version for the best experience.", "client": "Client", "proxyProtocol": "Proxy Protocol Settings", - "proxyProtocolDescription": "Configure Proxy Protocol to preserve client IP addresses for TCP/UDP services.", + "proxyProtocolDescription": "Configure Proxy Protocol to preserve client IP addresses for TCP services.", "enableProxyProtocol": "Enable Proxy Protocol", - "proxyProtocolInfo": "Preserve client IP addresses for TCP/UDP backends", + "proxyProtocolInfo": "Preserve client IP addresses for TCP backends", "proxyProtocolVersion": "Proxy Protocol Version", "version1": " Version 1 (Recommended)", "version2": "Version 2", diff --git a/messages/zh-TW.json b/messages/zh-TW.json new file mode 100644 index 00000000..a7e11f60 --- /dev/null +++ b/messages/zh-TW.json @@ -0,0 +1,2099 @@ +{ + "setupCreate": "創建您的第一個組織、網站和資源", + "setupNewOrg": "新建組織", + "setupCreateOrg": "創建組織", + "setupCreateResources": "創建資源", + "setupOrgName": "組織名稱", + "orgDisplayName": "這是您組織的顯示名稱。", + "orgId": "組織ID", + "setupIdentifierMessage": "這是您組織的唯一標識符。這是與顯示名稱分開的。", + "setupErrorIdentifier": "組織ID 已被使用。請另選一個。", + "componentsErrorNoMemberCreate": "您目前不是任何組織的成員。創建組織以開始操作。", + "componentsErrorNoMember": "您目前不是任何組織的成員。", + "welcome": "歡迎使用 Pangolin", + "welcomeTo": "歡迎來到", + "componentsCreateOrg": "創建組織", + "componentsMember": "您屬於{count, plural, =0 {沒有組織} one {一個組織} other {# 個組織}}。", + "componentsInvalidKey": "檢測到無效或過期的許可證金鑰。按照許可證條款操作以繼續使用所有功能。", + "dismiss": "忽略", + "componentsLicenseViolation": "許可證超限:該伺服器使用了 {usedSites} 個站點,已超過授權的 {maxSites} 個。請遵守許可證條款以繼續使用全部功能。", + "componentsSupporterMessage": "感謝您的支持!您現在是 Pangolin 的 {tier} 用戶。", + "inviteErrorNotValid": "很抱歉,但看起來你試圖訪問的邀請尚未被接受或不再有效。", + "inviteErrorUser": "很抱歉,但看起來你想要訪問的邀請不是這個用戶。", + "inviteLoginUser": "請確保您以正確的用戶登錄。", + "inviteErrorNoUser": "很抱歉,但看起來你想訪問的邀請不是一個存在的用戶。", + "inviteCreateUser": "請先創建一個帳戶。", + "goHome": "返回首頁", + "inviteLogInOtherUser": "以不同的用戶登錄", + "createAnAccount": "創建帳戶", + "inviteNotAccepted": "邀請未接受", + "authCreateAccount": "創建一個帳戶以開始", + "authNoAccount": "沒有帳戶?", + "email": "電子郵件地址", + "password": "密碼", + "confirmPassword": "確認密碼", + "createAccount": "創建帳戶", + "viewSettings": "查看設置", + "delete": "刪除", + "name": "名稱", + "online": "在線", + "offline": "離線的", + "site": "站點", + "dataIn": "數據輸入", + "dataOut": "數據輸出", + "connectionType": "連接類型", + "tunnelType": "隧道類型", + "local": "本地的", + "edit": "編輯", + "siteConfirmDelete": "確認刪除站點", + "siteDelete": "刪除站點", + "siteMessageRemove": "一旦移除,站點將無法訪問。與站點相關的所有目標也將被移除。", + "siteQuestionRemove": "您確定要從組織中刪除該站點嗎?", + "siteManageSites": "管理站點", + "siteDescription": "允許通過安全隧道連接到您的網路", + "siteCreate": "創建站點", + "siteCreateDescription2": "按照下面的步驟創建和連接一個新站點", + "siteCreateDescription": "創建一個新站點開始連接您的資源", + "close": "關閉", + "siteErrorCreate": "創建站點出錯", + "siteErrorCreateKeyPair": "找不到金鑰對或站點預設值", + "siteErrorCreateDefaults": "未找到站點預設值", + "method": "方法", + "siteMethodDescription": "這是您將如何顯示連接。", + "siteLearnNewt": "學習如何在您的系統上安裝 Newt", + "siteSeeConfigOnce": "您只能看到一次配置。", + "siteLoadWGConfig": "正在載入 WireGuard 配置...", + "siteDocker": "擴展 Docker 部署詳細資訊", + "toggle": "切換", + "dockerCompose": "Docker 配置", + "dockerRun": "停靠欄", + "siteLearnLocal": "本地站點不需要隧道連接,點擊了解更多", + "siteConfirmCopy": "我已經複製了配置資訊", + "searchSitesProgress": "搜索站點...", + "siteAdd": "添加站點", + "siteInstallNewt": "安裝 Newt", + "siteInstallNewtDescription": "在您的系統中運行 Newt", + "WgConfiguration": "WireGuard 配置", + "WgConfigurationDescription": "使用以下配置連接到您的網路", + "operatingSystem": "操作系統", + "commands": "命令", + "recommended": "推薦", + "siteNewtDescription": "為獲得最佳用戶體驗,請使用 Newt。其底層採用 WireGuard 技術,可直接通過 Pangolin 控制台,使用區域網路地址訪問您私有網路中的資源。", + "siteRunsInDocker": "在 Docker 中運行", + "siteRunsInShell": "在 macOS 、 Linux 和 Windows 的 Shell 中運行", + "siteErrorDelete": "刪除站點出錯", + "siteErrorUpdate": "更新站點失敗", + "siteErrorUpdateDescription": "更新站點時出錯。", + "siteUpdated": "站點已更新", + "siteUpdatedDescription": "網站已更新。", + "siteGeneralDescription": "配置此站點的常規設置", + "siteSettingDescription": "配置您網站上的設置", + "siteSetting": "{siteName} 設置", + "siteNewtTunnel": "Newt 隧道 (推薦)", + "siteNewtTunnelDescription": "最簡單的方式來連接到您的網路。不需要任何額外設置。", + "siteWg": "基本 WireGuard", + "siteWgDescription": "使用任何 WireGuard 用戶端來建立隧道。需要手動配置 NAT。", + "siteWgDescriptionSaas": "使用任何WireGuard用戶端建立隧道。需要手動配置NAT。僅適用於自託管節點。", + "siteLocalDescription": "僅限本地資源。不需要隧道。", + "siteLocalDescriptionSaas": "僅本地資源。沒有隧道。僅在遠程節點上可用。", + "siteSeeAll": "查看所有站點", + "siteTunnelDescription": "確定如何連接到您的網站", + "siteNewtCredentials": "Newt 憑據", + "siteNewtCredentialsDescription": "這是 Newt 伺服器的身份驗證憑據", + "siteCredentialsSave": "保存您的憑據", + "siteCredentialsSaveDescription": "您只能看到一次。請確保將其複製並保存到一個安全的地方。", + "siteInfo": "站點資訊", + "status": "狀態", + "shareTitle": "管理共享連結", + "shareDescription": "創建可共享的連結,允許暫時或永久訪問您的資源", + "shareSearch": "搜索共享連結...", + "shareCreate": "創建共享連結", + "shareErrorDelete": "刪除連結失敗", + "shareErrorDeleteMessage": "刪除連結時出錯", + "shareDeleted": "連結已刪除", + "shareDeletedDescription": "連結已刪除", + "shareTokenDescription": "您的訪問令牌可以透過兩種方式傳遞:作為查詢參數或請求頭。 每次驗證訪問請求都必須從用戶端傳遞。", + "accessToken": "訪問令牌", + "usageExamples": "用法範例", + "tokenId": "令牌 ID", + "requestHeades": "請求頭", + "queryParameter": "查詢參數", + "importantNote": "重要提示", + "shareImportantDescription": "出於安全考慮,建議盡可能在使用請求頭傳遞參數,因為查詢參數可能會被瀏覽器歷史記錄或伺服器日誌記錄。", + "token": "令牌", + "shareTokenSecurety": "請妥善保管您的訪問令牌,不要將其暴露在公開訪問的區域或用戶端代碼中。", + "shareErrorFetchResource": "獲取資源失敗", + "shareErrorFetchResourceDescription": "獲取資源時出錯", + "shareErrorCreate": "無法創建共享連結", + "shareErrorCreateDescription": "創建共享連結時出錯", + "shareCreateDescription": "任何具有此連結的人都可以訪問資源", + "shareTitleOptional": "標題 (可選)", + "expireIn": "過期時間", + "neverExpire": "永不過期", + "shareExpireDescription": "過期時間是連結可以使用並提供對資源的訪問時間。 此時間後,連結將不再工作,使用此連結的用戶將失去對資源的訪問。", + "shareSeeOnce": "您只能看到一次此連結。請確保複製它。", + "shareAccessHint": "任何具有此連結的人都可以訪問該資源。小心地分享它。", + "shareTokenUsage": "查看訪問令牌使用情況", + "createLink": "創建連結", + "resourcesNotFound": "找不到資源", + "resourceSearch": "搜索資源", + "openMenu": "打開菜單", + "resource": "資源", + "title": "標題", + "created": "已創建", + "expires": "過期時間", + "never": "永不過期", + "shareErrorSelectResource": "請選擇一個資源", + "resourceTitle": "管理資源", + "resourceDescription": "為您的私人應用程式創建安全代理", + "resourcesSearch": "搜索資源...", + "resourceAdd": "添加資源", + "resourceErrorDelte": "刪除資源時出錯", + "authentication": "認證", + "protected": "受到保護", + "notProtected": "未受到保護", + "resourceMessageRemove": "一旦刪除,資源將不再可訪問。與該資源相關的所有目標也將被刪除。", + "resourceQuestionRemove": "您確定要從組織中刪除資源嗎?", + "resourceHTTP": "HTTPS 資源", + "resourceHTTPDescription": "使用子域或根域名通過 HTTPS 向您的應用程式提出代理請求。", + "resourceRaw": "TCP/UDP 資源", + "resourceRawDescription": "使用 TCP/UDP 使用埠號向您的應用提出代理請求。", + "resourceCreate": "創建資源", + "resourceCreateDescription": "按照下面的步驟創建新資源", + "resourceSeeAll": "查看所有資源", + "resourceInfo": "資源資訊", + "resourceNameDescription": "這是資源的顯示名稱。", + "siteSelect": "選擇站點", + "siteSearch": "搜索站點", + "siteNotFound": "未找到站點。", + "selectCountry": "選擇國家", + "searchCountries": "搜索國家...", + "noCountryFound": "找不到國家。", + "siteSelectionDescription": "此站點將為目標提供連接。", + "resourceType": "資源類型", + "resourceTypeDescription": "確定如何訪問您的資源", + "resourceHTTPSSettings": "HTTPS 設置", + "resourceHTTPSSettingsDescription": "配置如何通過 HTTPS 訪問您的資源", + "domainType": "域類型", + "subdomain": "子域名", + "baseDomain": "根域名", + "subdomnainDescription": "您的資源可以訪問的子域名。", + "resourceRawSettings": "TCP/UDP 設置", + "resourceRawSettingsDescription": "配置如何通過 TCP/UDP 訪問您的資源。 您映射資源到主機Pangolin伺服器上的埠,這樣您就可以訪問伺服器-公共-ip:mapped埠的資源。", + "protocol": "協議", + "protocolSelect": "選擇協議", + "resourcePortNumber": "埠號", + "resourcePortNumberDescription": "代理請求的外部埠號。", + "cancel": "取消", + "resourceConfig": "配置片段", + "resourceConfigDescription": "複製並黏貼這些配置片段以設置您的 TCP/UDP 資源", + "resourceAddEntrypoints": "Traefik: 添加入口點", + "resourceExposePorts": "Gerbil:在 Docker Compose 中顯示埠", + "resourceLearnRaw": "學習如何配置 TCP/UDP 資源", + "resourceBack": "返回資源", + "resourceGoTo": "轉到資源", + "resourceDelete": "刪除資源", + "resourceDeleteConfirm": "確認刪除資源", + "visibility": "可見性", + "enabled": "已啟用", + "disabled": "已禁用", + "general": "概覽", + "generalSettings": "常規設置", + "proxy": "代理伺服器", + "internal": "內部設置", + "rules": "規則", + "resourceSettingDescription": "配置您資源上的設置", + "resourceSetting": "{resourceName} 設置", + "alwaysAllow": "一律允許", + "alwaysDeny": "一律拒絕", + "passToAuth": "傳遞至認證", + "orgSettingsDescription": "配置您組織的一般設定", + "orgGeneralSettings": "組織設置", + "orgGeneralSettingsDescription": "管理您的機構詳細資訊和配置", + "saveGeneralSettings": "保存常規設置", + "saveSettings": "保存設置", + "orgDangerZone": "危險區域", + "orgDangerZoneDescription": "一旦刪除該組織,將無法恢復,請務必確認。", + "orgDelete": "刪除組織", + "orgDeleteConfirm": "確認刪除組織", + "orgMessageRemove": "此操作不可逆,這將刪除所有相關數據。", + "orgMessageConfirm": "要確認,請在下面輸入組織名稱。", + "orgQuestionRemove": "您確定要刪除組織嗎?", + "orgUpdated": "組織已更新", + "orgUpdatedDescription": "組織已更新。", + "orgErrorUpdate": "更新組織失敗", + "orgErrorUpdateMessage": "更新組織時出錯。", + "orgErrorFetch": "獲取組織失敗", + "orgErrorFetchMessage": "列出您的組織時出錯", + "orgErrorDelete": "刪除組織失敗", + "orgErrorDeleteMessage": "刪除組織時出錯。", + "orgDeleted": "組織已刪除", + "orgDeletedMessage": "組織及其數據已被刪除。", + "orgMissing": "缺少組織 ID", + "orgMissingMessage": "沒有組織ID,無法重新生成邀請。", + "accessUsersManage": "管理用戶", + "accessUsersDescription": "邀請用戶並位他們添加角色以管理訪問您的組織", + "accessUsersSearch": "搜索用戶...", + "accessUserCreate": "創建用戶", + "accessUserRemove": "刪除用戶", + "username": "使用者名稱", + "identityProvider": "身份提供商", + "role": "角色", + "nameRequired": "名稱是必填項", + "accessRolesManage": "管理角色", + "accessRolesDescription": "配置角色來管理訪問您的組織", + "accessRolesSearch": "搜索角色...", + "accessRolesAdd": "添加角色", + "accessRoleDelete": "刪除角色", + "description": "描述", + "inviteTitle": "打開邀請", + "inviteDescription": "管理您給其他用戶的邀請", + "inviteSearch": "搜索邀請...", + "minutes": "分鐘", + "hours": "小時", + "days": "天", + "weeks": "周", + "months": "月", + "years": "年", + "day": "{count, plural, other {# 天}}", + "apiKeysTitle": "API 金鑰", + "apiKeysConfirmCopy2": "您必須確認您已複製 API 金鑰。", + "apiKeysErrorCreate": "創建 API 金鑰出錯", + "apiKeysErrorSetPermission": "設置權限出錯", + "apiKeysCreate": "生成 API 金鑰", + "apiKeysCreateDescription": "為您的組織生成一個新的 API 金鑰", + "apiKeysGeneralSettings": "權限", + "apiKeysGeneralSettingsDescription": "確定此 API 金鑰可以做什麼", + "apiKeysList": "您的 API 金鑰", + "apiKeysSave": "保存您的 API 金鑰", + "apiKeysSaveDescription": "該資訊僅會顯示一次,請確保將其複製到安全的位置。", + "apiKeysInfo": "您的 API 金鑰是:", + "apiKeysConfirmCopy": "我已複製 API 金鑰", + "generate": "生成", + "done": "完成", + "apiKeysSeeAll": "查看所有 API 金鑰", + "apiKeysPermissionsErrorLoadingActions": "載入 API 金鑰操作時出錯", + "apiKeysPermissionsErrorUpdate": "設置權限出錯", + "apiKeysPermissionsUpdated": "權限已更新", + "apiKeysPermissionsUpdatedDescription": "權限已更新。", + "apiKeysPermissionsGeneralSettings": "權限", + "apiKeysPermissionsGeneralSettingsDescription": "確定此 API 金鑰可以做什麼", + "apiKeysPermissionsSave": "保存權限", + "apiKeysPermissionsTitle": "權限", + "apiKeys": "API 金鑰", + "searchApiKeys": "搜索 API 金鑰...", + "apiKeysAdd": "生成 API 金鑰", + "apiKeysErrorDelete": "刪除 API 金鑰出錯", + "apiKeysErrorDeleteMessage": "刪除 API 金鑰出錯", + "apiKeysQuestionRemove": "您確定要從組織中刪除 API 金鑰嗎?", + "apiKeysMessageRemove": "一旦刪除,此API金鑰將無法被使用。", + "apiKeysDeleteConfirm": "確認刪除 API 金鑰", + "apiKeysDelete": "刪除 API 金鑰", + "apiKeysManage": "管理 API 金鑰", + "apiKeysDescription": "API 金鑰用於認證集成 API", + "apiKeysSettings": "{apiKeyName} 設置", + "userTitle": "管理所有用戶", + "userDescription": "查看和管理系統中的所有用戶", + "userAbount": "關於用戶管理", + "userAbountDescription": "此表格顯示系統中所有根用戶對象。每個用戶可能屬於多個組織。 從組織中刪除用戶不會刪除其根用戶對象 - 他們將保留在系統中。 要從系統中完全刪除用戶,您必須使用此表格中的刪除操作刪除其根用戶對象。", + "userServer": "伺服器用戶", + "userSearch": "搜索伺服器用戶...", + "userErrorDelete": "刪除用戶時出錯", + "userDeleteConfirm": "確認刪除用戶", + "userDeleteServer": "從伺服器刪除用戶", + "userMessageRemove": "該用戶將被從所有組織中刪除並完全從伺服器中刪除。", + "userQuestionRemove": "您確定要從伺服器永久刪除用戶嗎?", + "licenseKey": "許可證金鑰", + "valid": "有效", + "numberOfSites": "站點數量", + "licenseKeySearch": "搜索許可證金鑰...", + "licenseKeyAdd": "添加許可證金鑰", + "type": "類型", + "licenseKeyRequired": "需要許可證金鑰", + "licenseTermsAgree": "您必須同意許可條款", + "licenseErrorKeyLoad": "載入許可證金鑰失敗", + "licenseErrorKeyLoadDescription": "載入許可證金鑰時出錯。", + "licenseErrorKeyDelete": "刪除許可證金鑰失敗", + "licenseErrorKeyDeleteDescription": "刪除許可證金鑰時出錯。", + "licenseKeyDeleted": "許可證金鑰已刪除", + "licenseKeyDeletedDescription": "許可證金鑰已被刪除。", + "licenseErrorKeyActivate": "啟用許可證金鑰失敗", + "licenseErrorKeyActivateDescription": "啟用許可證金鑰時出錯。", + "licenseAbout": "關於許可協議", + "communityEdition": "社區版", + "licenseAboutDescription": "這是針對商業環境中使用Pangolin的商業和企業用戶。 如果您正在使用 Pangolin 供個人使用,您可以忽略此部分。", + "licenseKeyActivated": "授權金鑰已啟用", + "licenseKeyActivatedDescription": "已成功啟用許可證金鑰。", + "licenseErrorKeyRecheck": "重新檢查許可證金鑰失敗", + "licenseErrorKeyRecheckDescription": "重新檢查許可證金鑰時出錯。", + "licenseErrorKeyRechecked": "重新檢查許可證金鑰", + "licenseErrorKeyRecheckedDescription": "已重新檢查所有許可證金鑰", + "licenseActivateKey": "啟用許可證金鑰", + "licenseActivateKeyDescription": "輸入一個許可金鑰來啟用它。", + "licenseActivate": "啟用許可證", + "licenseAgreement": "通過檢查此框,您確認您已經閱讀並同意與您的許可證金鑰相關的許可條款。", + "fossorialLicense": "查看Fossorial Commercial License和訂閱條款", + "licenseMessageRemove": "這將刪除許可證金鑰和它授予的所有相關權限。", + "licenseMessageConfirm": "要確認,請在下面輸入許可證金鑰。", + "licenseQuestionRemove": "您確定要刪除許可證金鑰?", + "licenseKeyDelete": "刪除許可證金鑰", + "licenseKeyDeleteConfirm": "確認刪除許可證金鑰", + "licenseTitle": "管理許可證狀態", + "licenseTitleDescription": "查看和管理系統中的許可證金鑰", + "licenseHost": "主機許可證", + "licenseHostDescription": "管理主機的主許可證金鑰。", + "licensedNot": "未授權", + "hostId": "主機 ID", + "licenseReckeckAll": "重新檢查所有金鑰", + "licenseSiteUsage": "站點使用情況", + "licenseSiteUsageDecsription": "查看使用此許可的站點數量。", + "licenseNoSiteLimit": "使用未經許可主機的站點數量沒有限制。", + "licensePurchase": "購買許可證", + "licensePurchaseSites": "購買更多站點", + "licenseSitesUsedMax": "使用了 {usedSites}/{maxSites} 個站點", + "licenseSitesUsed": "{count, plural, =0 {# 站點} one {# 站點} other {# 站點}}", + "licensePurchaseDescription": "請選擇您希望 {selectedMode, select, license {直接購買許可證,您可以隨時增加更多站點。} other {為現有許可證購買更多站點}}", + "licenseFee": "許可證費用", + "licensePriceSite": "每個站點的價格", + "total": "總計", + "licenseContinuePayment": "繼續付款", + "pricingPage": "定價頁面", + "pricingPortal": "前往付款頁面", + "licensePricingPage": "關於最新的價格和折扣,請訪問 ", + "invite": "邀請", + "inviteRegenerate": "重新生成邀請", + "inviteRegenerateDescription": "撤銷以前的邀請並創建一個新的邀請", + "inviteRemove": "移除邀請", + "inviteRemoveError": "刪除邀請失敗", + "inviteRemoveErrorDescription": "刪除邀請時出錯。", + "inviteRemoved": "邀請已刪除", + "inviteRemovedDescription": "為 {email} 創建的邀請已刪除", + "inviteQuestionRemove": "您確定要刪除邀請嗎?", + "inviteMessageRemove": "一旦刪除,這個邀請將不再有效。", + "inviteMessageConfirm": "要確認,請在下面輸入邀請的電子郵件地址。", + "inviteQuestionRegenerate": "您確定要重新邀請 {email} 嗎?這將會撤銷掉之前的邀請", + "inviteRemoveConfirm": "確認刪除邀請", + "inviteRegenerated": "重新生成邀請", + "inviteSent": "邀請郵件已成功發送至 {email}。", + "inviteSentEmail": "發送電子郵件通知給用戶", + "inviteGenerate": "已為 {email} 創建新的邀請。", + "inviteDuplicateError": "重複的邀請", + "inviteDuplicateErrorDescription": "此用戶的邀請已存在。", + "inviteRateLimitError": "超出速率限制", + "inviteRateLimitErrorDescription": "您超過了每小時3次再生的限制。請稍後再試。", + "inviteRegenerateError": "重新生成邀請失敗", + "inviteRegenerateErrorDescription": "重新生成邀請時出錯。", + "inviteValidityPeriod": "有效期", + "inviteValidityPeriodSelect": "選擇有效期", + "inviteRegenerateMessage": "邀請已重新生成。用戶必須訪問下面的連結才能接受邀請。", + "inviteRegenerateButton": "重新生成", + "expiresAt": "到期於", + "accessRoleUnknown": "未知角色", + "placeholder": "占位符", + "userErrorOrgRemove": "刪除用戶失敗", + "userErrorOrgRemoveDescription": "刪除用戶時出錯。", + "userOrgRemoved": "用戶已刪除", + "userOrgRemovedDescription": "已將 {email} 從組織中移除。", + "userQuestionOrgRemove": "您確定要從組織中刪除此用戶嗎?", + "userMessageOrgRemove": "一旦刪除,這個用戶將不再能夠訪問組織。 你總是可以稍後重新邀請他們,但他們需要再次接受邀請。", + "userRemoveOrgConfirm": "確認刪除用戶", + "userRemoveOrg": "從組織中刪除用戶", + "users": "用戶", + "accessRoleMember": "成員", + "accessRoleOwner": "所有者", + "userConfirmed": "已確認", + "idpNameInternal": "內部設置", + "emailInvalid": "無效的電子郵件地址", + "inviteValidityDuration": "請選擇持續時間", + "accessRoleSelectPlease": "請選擇一個角色", + "usernameRequired": "必須輸入使用者名稱", + "idpSelectPlease": "請選擇身份提供商", + "idpGenericOidc": "通用的 OAuth2/OIDC 提供商。", + "accessRoleErrorFetch": "獲取角色失敗", + "accessRoleErrorFetchDescription": "獲取角色時出錯", + "idpErrorFetch": "獲取身份提供者失敗", + "idpErrorFetchDescription": "獲取身份提供者時出錯", + "userErrorExists": "用戶已存在", + "userErrorExistsDescription": "此用戶已經是組織成員。", + "inviteError": "邀請用戶失敗", + "inviteErrorDescription": "邀請用戶時出錯", + "userInvited": "用戶邀請", + "userInvitedDescription": "用戶已被成功邀請。", + "userErrorCreate": "創建用戶失敗", + "userErrorCreateDescription": "創建用戶時出錯", + "userCreated": "用戶已創建", + "userCreatedDescription": "用戶已成功創建。", + "userTypeInternal": "內部用戶", + "userTypeInternalDescription": "邀請用戶直接加入您的組織。", + "userTypeExternal": "外部用戶", + "userTypeExternalDescription": "創建一個具有外部身份提供商的用戶。", + "accessUserCreateDescription": "按照下面的步驟創建一個新用戶", + "userSeeAll": "查看所有用戶", + "userTypeTitle": "用戶類型", + "userTypeDescription": "確定如何創建用戶", + "userSettings": "用戶資訊", + "userSettingsDescription": "輸入新用戶的詳細資訊", + "inviteEmailSent": "發送邀請郵件給用戶", + "inviteValid": "有效", + "selectDuration": "選擇持續時間", + "accessRoleSelect": "選擇角色", + "inviteEmailSentDescription": "一封電子郵件已經發送給用戶,帶有下面的訪問連結。他們必須訪問該連結才能接受邀請。", + "inviteSentDescription": "用戶已被邀請。他們必須訪問下面的連結才能接受邀請。", + "inviteExpiresIn": "邀請將在{days, plural, other {# 天}}後過期。", + "idpTitle": "身份提供商", + "idpSelect": "為外部用戶選擇身份提供商", + "idpNotConfigured": "沒有配置身份提供者。請在創建外部用戶之前配置身份提供者。", + "usernameUniq": "這必須匹配所選身份提供者中存在的唯一使用者名稱。", + "emailOptional": "電子郵件(可選)", + "nameOptional": "名稱(可選)", + "accessControls": "訪問控制", + "userDescription2": "管理此用戶的設置", + "accessRoleErrorAdd": "添加用戶到角色失敗", + "accessRoleErrorAddDescription": "添加用戶到角色時出錯。", + "userSaved": "用戶已保存", + "userSavedDescription": "用戶已更新。", + "autoProvisioned": "自動設置", + "autoProvisionedDescription": "允許此用戶由身份提供商自動管理", + "accessControlsDescription": "管理此用戶在組織中可以訪問和做什麼", + "accessControlsSubmit": "保存訪問控制", + "roles": "角色", + "accessUsersRoles": "管理用戶和角色", + "accessUsersRolesDescription": "邀請用戶並將他們添加到角色以管理訪問您的組織", + "key": "關鍵字", + "createdAt": "創建於", + "proxyErrorInvalidHeader": "無效的自訂主機 Header。使用域名格式,或將空保存為取消自訂 Header。", + "proxyErrorTls": "無效的 TLS 伺服器名稱。使用域名格式,或保存空以刪除 TLS 伺服器名稱。", + "proxyEnableSSL": "啟用 SSL", + "proxyEnableSSLDescription": "啟用 SSL/TLS 加密以確保您目標的 HTTPS 連接。", + "target": "Target", + "configureTarget": "配置目標", + "targetErrorFetch": "獲取目標失敗", + "targetErrorFetchDescription": "獲取目標時出錯", + "siteErrorFetch": "獲取資源失敗", + "siteErrorFetchDescription": "獲取資源時出錯", + "targetErrorDuplicate": "重複的目標", + "targetErrorDuplicateDescription": "具有這些設置的目標已存在", + "targetWireGuardErrorInvalidIp": "無效的目標IP", + "targetWireGuardErrorInvalidIpDescription": "目標IP必須在站點子網內", + "targetsUpdated": "目標已更新", + "targetsUpdatedDescription": "目標和設置更新成功", + "targetsErrorUpdate": "更新目標失敗", + "targetsErrorUpdateDescription": "更新目標時出錯", + "targetTlsUpdate": "TLS 設置已更新", + "targetTlsUpdateDescription": "您的 TLS 設置已成功更新", + "targetErrorTlsUpdate": "更新 TLS 設置失敗", + "targetErrorTlsUpdateDescription": "更新 TLS 設置時出錯", + "proxyUpdated": "代理設置已更新", + "proxyUpdatedDescription": "您的代理設置已成功更新", + "proxyErrorUpdate": "更新代理設置失敗", + "proxyErrorUpdateDescription": "更新代理設置時出錯", + "targetAddr": "IP / 域名", + "targetPort": "埠", + "targetProtocol": "協議", + "targetTlsSettings": "安全連接配置", + "targetTlsSettingsDescription": "配置資源的 SSL/TLS 設置", + "targetTlsSettingsAdvanced": "高級TLS設置", + "targetTlsSni": "TLS 伺服器名稱", + "targetTlsSniDescription": "SNI使用的 TLS 伺服器名稱。留空使用預設值。", + "targetTlsSubmit": "保存設置", + "targets": "目標配置", + "targetsDescription": "設置目標來路由流量到您的後端服務", + "targetStickySessions": "啟用置頂會話", + "targetStickySessionsDescription": "將連接保持在同一個後端目標的整個會話中。", + "methodSelect": "選擇方法", + "targetSubmit": "添加目標", + "targetNoOne": "此資源沒有任何目標。添加目標來配置向您後端發送請求的位置。", + "targetNoOneDescription": "在上面添加多個目標將啟用負載平衡。", + "targetsSubmit": "保存目標", + "addTarget": "添加目標", + "targetErrorInvalidIp": "無效的 IP 地址", + "targetErrorInvalidIpDescription": "請輸入有效的IP位址或主機名", + "targetErrorInvalidPort": "無效的埠", + "targetErrorInvalidPortDescription": "請輸入有效的埠號", + "targetErrorNoSite": "沒有選擇站點", + "targetErrorNoSiteDescription": "請選擇目標站點", + "targetCreated": "目標已創建", + "targetCreatedDescription": "目標已成功創建", + "targetErrorCreate": "創建目標失敗", + "targetErrorCreateDescription": "創建目標時出錯", + "save": "保存", + "proxyAdditional": "附加代理設置", + "proxyAdditionalDescription": "配置你的資源如何處理代理設置", + "proxyCustomHeader": "自訂主機 Header", + "proxyCustomHeaderDescription": "代理請求時設置的 Header。留空則使用預設值。", + "proxyAdditionalSubmit": "保存代理設置", + "subnetMaskErrorInvalid": "子網掩碼無效。必須在 0 和 32 之間。", + "ipAddressErrorInvalidFormat": "無效的 IP 地址格式", + "ipAddressErrorInvalidOctet": "無效的 IP 地址", + "path": "路徑", + "matchPath": "匹配路徑", + "ipAddressRange": "IP 範圍", + "rulesErrorFetch": "獲取規則失敗", + "rulesErrorFetchDescription": "獲取規則時出錯", + "rulesErrorDuplicate": "複製規則", + "rulesErrorDuplicateDescription": "帶有這些設置的規則已存在", + "rulesErrorInvalidIpAddressRange": "無效的 CIDR", + "rulesErrorInvalidIpAddressRangeDescription": "請輸入一個有效的 CIDR 值", + "rulesErrorInvalidUrl": "無效的 URL 路徑", + "rulesErrorInvalidUrlDescription": "請輸入一個有效的 URL 路徑值", + "rulesErrorInvalidIpAddress": "無效的 IP", + "rulesErrorInvalidIpAddressDescription": "請輸入一個有效的IP位址", + "rulesErrorUpdate": "更新規則失敗", + "rulesErrorUpdateDescription": "更新規則時出錯", + "rulesUpdated": "啟用規則", + "rulesUpdatedDescription": "規則已更新", + "rulesMatchIpAddressRangeDescription": "以 CIDR 格式輸入地址(如:103.21.244.0/22)", + "rulesMatchIpAddress": "輸入IP位址(例如,103.21.244.12)", + "rulesMatchUrl": "輸入一個 URL 路徑或模式(例如/api/v1/todos 或 /api/v1/*)", + "rulesErrorInvalidPriority": "無效的優先度", + "rulesErrorInvalidPriorityDescription": "請輸入一個有效的優先度", + "rulesErrorDuplicatePriority": "重複的優先度", + "rulesErrorDuplicatePriorityDescription": "請輸入唯一的優先度", + "ruleUpdated": "規則已更新", + "ruleUpdatedDescription": "規則更新成功", + "ruleErrorUpdate": "操作失敗", + "ruleErrorUpdateDescription": "保存過程中發生錯誤", + "rulesPriority": "優先權", + "rulesAction": "行為", + "rulesMatchType": "匹配類型", + "value": "值", + "rulesAbout": "關於規則", + "rulesAboutDescription": "規則使您能夠依據特定條件控制資源訪問權限。您可以創建基於 IP 地址或 URL 路徑的規則,以允許或拒絕訪問。", + "rulesActions": "行動", + "rulesActionAlwaysAllow": "總是允許:繞過所有身份驗證方法", + "rulesActionAlwaysDeny": "總是拒絕:阻止所有請求;無法嘗試驗證", + "rulesActionPassToAuth": "傳遞至認證:允許嘗試身份驗證方法", + "rulesMatchCriteria": "匹配條件", + "rulesMatchCriteriaIpAddress": "匹配一個指定的 IP 地址", + "rulesMatchCriteriaIpAddressRange": "在 CIDR 符號中匹配一系列IP位址", + "rulesMatchCriteriaUrl": "匹配一個 URL 路徑或模式", + "rulesEnable": "啟用規則", + "rulesEnableDescription": "啟用或禁用此資源的規則評估", + "rulesResource": "資源規則配置", + "rulesResourceDescription": "配置規則來控制對您資源的訪問", + "ruleSubmit": "添加規則", + "rulesNoOne": "沒有規則。使用表單添加規則。", + "rulesOrder": "規則按優先順序評定。", + "rulesSubmit": "保存規則", + "resourceErrorCreate": "創建資源時出錯", + "resourceErrorCreateDescription": "創建資源時出錯", + "resourceErrorCreateMessage": "創建資源時發生錯誤:", + "resourceErrorCreateMessageDescription": "發生意外錯誤", + "sitesErrorFetch": "獲取站點出錯", + "sitesErrorFetchDescription": "獲取站點時出錯", + "domainsErrorFetch": "獲取域名出錯", + "domainsErrorFetchDescription": "獲取域時出錯", + "none": "無", + "unknown": "未知", + "resources": "資源", + "resourcesDescription": "資源是您私有網路中運行的應用程式的代理。您可以為私有網路中的任何 HTTP/HTTPS 或 TCP/UDP 服務創建資源。每個資源都必須連接到一個站點,以通過加密的 WireGuard 隧道實現私密且安全的連接。", + "resourcesWireGuardConnect": "採用 WireGuard 提供的加密安全連接", + "resourcesMultipleAuthenticationMethods": "配置多個身份驗證方法", + "resourcesUsersRolesAccess": "基於用戶和角色的訪問控制", + "resourcesErrorUpdate": "切換資源失敗", + "resourcesErrorUpdateDescription": "更新資源時出錯", + "access": "訪問權限", + "shareLink": "{resource} 的分享連結", + "resourceSelect": "選擇資源", + "shareLinks": "分享連結", + "share": "分享連結", + "shareDescription2": "創建資源共享連結。連結提供對資源的臨時或無限制訪問。 當您創建連結時,您可以配置連結的到期時間。", + "shareEasyCreate": "輕鬆創建和分享", + "shareConfigurableExpirationDuration": "可配置的過期時間", + "shareSecureAndRevocable": "安全和可撤銷的", + "nameMin": "名稱長度必須大於 {len} 字元。", + "nameMax": "名稱長度必須小於 {len} 字元。", + "sitesConfirmCopy": "請確認您已經複製了配置。", + "unknownCommand": "未知命令", + "newtErrorFetchReleases": "無法獲取版本資訊: {err}", + "newtErrorFetchLatest": "無法獲取最新版資訊: {err}", + "newtEndpoint": "Newt 端點", + "newtId": "Newt ID", + "newtSecretKey": "Newt 私鑰", + "architecture": "架構", + "sites": "站點", + "siteWgAnyClients": "使用任何 WireGuard 用戶端連接。您必須使用對等IP解決您的內部資源。", + "siteWgCompatibleAllClients": "與所有 WireGuard 用戶端相容", + "siteWgManualConfigurationRequired": "需要手動配置", + "userErrorNotAdminOrOwner": "用戶不是管理員或所有者", + "pangolinSettings": "設置 - Pangolin", + "accessRoleYour": "您的角色:", + "accessRoleSelect2": "選擇角色", + "accessUserSelect": "選擇一個用戶", + "otpEmailEnter": "輸入電子郵件", + "otpEmailEnterDescription": "在輸入欄位輸入後按 Enter 鍵添加電子郵件。", + "otpEmailErrorInvalid": "無效的信箱地址。通配符(*)必須占據整個開頭部分。", + "otpEmailSmtpRequired": "需要先配置 SMTP", + "otpEmailSmtpRequiredDescription": "必須在伺服器上啟用 SMTP 才能使用一次性密碼驗證。", + "otpEmailTitle": "一次性密碼", + "otpEmailTitleDescription": "資源訪問需要基於電子郵件的身份驗證", + "otpEmailWhitelist": "電子郵件白名單", + "otpEmailWhitelistList": "白名單郵件", + "otpEmailWhitelistListDescription": "只有擁有這些電子郵件地址的用戶才能訪問此資源。 他們將被提示輸入一次性密碼發送到他們的電子郵件。 通配符 (*@example.com) 可以用來允許來自一個域名的任何電子郵件地址。", + "otpEmailWhitelistSave": "保存白名單", + "passwordAdd": "添加密碼", + "passwordRemove": "刪除密碼", + "pincodeAdd": "添加 PIN 碼", + "pincodeRemove": "移除 PIN 碼", + "resourceAuthMethods": "身份驗證方法", + "resourceAuthMethodsDescriptions": "允許透過額外的認證方法訪問資源", + "resourceAuthSettingsSave": "保存成功", + "resourceAuthSettingsSaveDescription": "已保存身份驗證設置", + "resourceErrorAuthFetch": "獲取數據失敗", + "resourceErrorAuthFetchDescription": "獲取數據時出錯", + "resourceErrorPasswordRemove": "刪除資源密碼出錯", + "resourceErrorPasswordRemoveDescription": "刪除資源密碼時出錯", + "resourceErrorPasswordSetup": "設置資源密碼出錯", + "resourceErrorPasswordSetupDescription": "設置資源密碼時出錯", + "resourceErrorPincodeRemove": "刪除資源固定碼時出錯", + "resourceErrorPincodeRemoveDescription": "刪除資源PIN碼時出錯", + "resourceErrorPincodeSetup": "設置資源 PIN 碼時出錯", + "resourceErrorPincodeSetupDescription": "設置資源 PIN 碼時發生錯誤", + "resourceErrorUsersRolesSave": "設置角色失敗", + "resourceErrorUsersRolesSaveDescription": "設置角色時出錯", + "resourceErrorWhitelistSave": "保存白名單失敗", + "resourceErrorWhitelistSaveDescription": "保存白名單時出錯", + "resourcePasswordSubmit": "啟用密碼保護", + "resourcePasswordProtection": "密碼保護 {status}", + "resourcePasswordRemove": "已刪除資源密碼", + "resourcePasswordRemoveDescription": "已成功刪除資源密碼", + "resourcePasswordSetup": "設置資源密碼", + "resourcePasswordSetupDescription": "已成功設置資源密碼", + "resourcePasswordSetupTitle": "設置密碼", + "resourcePasswordSetupTitleDescription": "設置密碼來保護此資源", + "resourcePincode": "PIN 碼", + "resourcePincodeSubmit": "啟用 PIN 碼保護", + "resourcePincodeProtection": "PIN 碼保護 {status}", + "resourcePincodeRemove": "資源 PIN 碼已刪除", + "resourcePincodeRemoveDescription": "已成功刪除資源 PIN 碼", + "resourcePincodeSetup": "資源 PIN 碼已設置", + "resourcePincodeSetupDescription": "資源 PIN 碼已成功設置", + "resourcePincodeSetupTitle": "設置 PIN 碼", + "resourcePincodeSetupTitleDescription": "設置 PIN 碼來保護此資源", + "resourceRoleDescription": "管理員總是可以訪問此資源。", + "resourceUsersRoles": "用戶和角色", + "resourceUsersRolesDescription": "配置用戶和角色可以訪問此資源", + "resourceUsersRolesSubmit": "保存用戶和角色", + "resourceWhitelistSave": "保存成功", + "resourceWhitelistSaveDescription": "白名單設置已保存", + "ssoUse": "使用平台 SSO", + "ssoUseDescription": "對於所有啟用此功能的資源,現有用戶只需登錄一次。", + "proxyErrorInvalidPort": "無效的埠號", + "subdomainErrorInvalid": "無效的子域", + "domainErrorFetch": "獲取域名失敗", + "domainErrorFetchDescription": "獲取域名時出錯", + "resourceErrorUpdate": "更新資源失敗", + "resourceErrorUpdateDescription": "更新資源時出錯", + "resourceUpdated": "資源已更新", + "resourceUpdatedDescription": "資源已成功更新", + "resourceErrorTransfer": "轉移資源失敗", + "resourceErrorTransferDescription": "轉移資源時出錯", + "resourceTransferred": "資源已傳輸", + "resourceTransferredDescription": "資源已成功傳輸", + "resourceErrorToggle": "切換資源失敗", + "resourceErrorToggleDescription": "更新資源時出錯", + "resourceVisibilityTitle": "可見性", + "resourceVisibilityTitleDescription": "完全啟用或禁用資源可見性", + "resourceGeneral": "常規設置", + "resourceGeneralDescription": "配置此資源的常規設置", + "resourceEnable": "啟用資源", + "resourceTransfer": "轉移資源", + "resourceTransferDescription": "將此資源轉移到另一個站點", + "resourceTransferSubmit": "轉移資源", + "siteDestination": "目標站點", + "searchSites": "搜索站點", + "accessRoleCreate": "創建角色", + "accessRoleCreateDescription": "創建一個新角色來分組用戶並管理他們的權限。", + "accessRoleCreateSubmit": "創建角色", + "accessRoleCreated": "角色已創建", + "accessRoleCreatedDescription": "角色已成功創建。", + "accessRoleErrorCreate": "創建角色失敗", + "accessRoleErrorCreateDescription": "創建角色時出錯。", + "accessRoleErrorNewRequired": "需要新角色", + "accessRoleErrorRemove": "刪除角色失敗", + "accessRoleErrorRemoveDescription": "刪除角色時出錯。", + "accessRoleName": "角色名稱", + "accessRoleQuestionRemove": "您即將刪除 {name} 角色。 此操作無法撤銷。", + "accessRoleRemove": "刪除角色", + "accessRoleRemoveDescription": "從組織中刪除角色", + "accessRoleRemoveSubmit": "刪除角色", + "accessRoleRemoved": "角色已刪除", + "accessRoleRemovedDescription": "角色已成功刪除。", + "accessRoleRequiredRemove": "刪除此角色之前,請選擇一個新角色來轉移現有成員。", + "manage": "管理", + "sitesNotFound": "未找到站點。", + "pangolinServerAdmin": "伺服器管理員 - Pangolin", + "licenseTierProfessional": "專業許可證", + "licenseTierEnterprise": "企業許可證", + "licenseTierPersonal": "個人許可證", + "licensed": "已授權", + "yes": "是", + "no": "否", + "sitesAdditional": "其他站點", + "licenseKeys": "許可證金鑰", + "sitestCountDecrease": "減少站點數量", + "sitestCountIncrease": "增加站點數量", + "idpManage": "管理身份提供商", + "idpManageDescription": "查看和管理系統中的身份提供商", + "idpDeletedDescription": "身份提供商刪除成功", + "idpOidc": "OAuth2/OIDC", + "idpQuestionRemove": "您確定要永久刪除身份提供者嗎?", + "idpMessageRemove": "這將刪除身份提供者和所有相關的配置。透過此提供者進行身份驗證的用戶將無法登錄。", + "idpMessageConfirm": "要確認,請在下面輸入身份提供者的名稱。", + "idpConfirmDelete": "確認刪除身份提供商", + "idpDelete": "刪除身份提供商", + "idp": "身份提供商", + "idpSearch": "搜索身份提供者...", + "idpAdd": "添加身份提供商", + "idpClientIdRequired": "用戶端 ID 是必需的。", + "idpClientSecretRequired": "用戶端金鑰是必需的。", + "idpErrorAuthUrlInvalid": "身份驗證 URL 必須是有效的 URL。", + "idpErrorTokenUrlInvalid": "令牌 URL 必須是有效的 URL。", + "idpPathRequired": "標識符路徑是必需的。", + "idpScopeRequired": "授權範圍是必需的。", + "idpOidcDescription": "配置 OpenID 連接身份提供商", + "idpCreatedDescription": "身份提供商創建成功", + "idpCreate": "創建身份提供商", + "idpCreateDescription": "配置用戶身份驗證的新身份提供商", + "idpSeeAll": "查看所有身份提供商", + "idpSettingsDescription": "配置身份提供者的基本資訊", + "idpDisplayName": "此身份提供商的顯示名稱", + "idpAutoProvisionUsers": "自動提供用戶", + "idpAutoProvisionUsersDescription": "如果啟用,用戶將在首次登錄時自動在系統中創建,並且能夠映射用戶到角色和組織。", + "licenseBadge": "EE", + "idpType": "提供者類型", + "idpTypeDescription": "選擇您想要配置的身份提供者類型", + "idpOidcConfigure": "OAuth2/OIDC 配置", + "idpOidcConfigureDescription": "配置 OAuth2/OIDC 供應商端點和憑據", + "idpClientId": "用戶端ID", + "idpClientIdDescription": "來自您身份提供商的 OAuth2 用戶端 ID", + "idpClientSecret": "用戶端金鑰", + "idpClientSecretDescription": "來自身份提供商的 OAuth2 用戶端金鑰", + "idpAuthUrl": "授權 URL", + "idpAuthUrlDescription": "OAuth2 授權端點的 URL", + "idpTokenUrl": "令牌 URL", + "idpTokenUrlDescription": "OAuth2 令牌端點的 URL", + "idpOidcConfigureAlert": "重要提示", + "idpOidcConfigureAlertDescription": "創建身份提供方後,您需要在其設置中配置回調 URL。回調 URL 會在創建成功後提供。", + "idpToken": "令牌配置", + "idpTokenDescription": "配置如何從 ID 令牌中提取用戶資訊", + "idpJmespathAbout": "關於 JMESPath", + "idpJmespathAboutDescription": "以下路徑使用 JMESPath 語法從 ID 令牌中提取值。", + "idpJmespathAboutDescriptionLink": "了解更多 JMESPath 資訊", + "idpJmespathLabel": "標識符路徑", + "idpJmespathLabelDescription": "ID 令牌中用戶標識符的路徑", + "idpJmespathEmailPathOptional": "信箱路徑(可選)", + "idpJmespathEmailPathOptionalDescription": "ID 令牌中用戶信箱的路徑", + "idpJmespathNamePathOptional": "使用者名稱路徑(可選)", + "idpJmespathNamePathOptionalDescription": "ID 令牌中使用者名稱的路徑", + "idpOidcConfigureScopes": "作用域(Scopes)", + "idpOidcConfigureScopesDescription": "以空格分隔的 OAuth2 請求作用域列表", + "idpSubmit": "創建身份提供商", + "orgPolicies": "組織策略", + "idpSettings": "{idpName} 設置", + "idpCreateSettingsDescription": "配置身份提供商的設置", + "roleMapping": "角色映射", + "orgMapping": "組織映射", + "orgPoliciesSearch": "搜索組織策略...", + "orgPoliciesAdd": "添加組織策略", + "orgRequired": "組織是必填項", + "error": "錯誤", + "success": "成功", + "orgPolicyAddedDescription": "策略添加成功", + "orgPolicyUpdatedDescription": "策略更新成功", + "orgPolicyDeletedDescription": "已成功刪除策略", + "defaultMappingsUpdatedDescription": "默認映射更新成功", + "orgPoliciesAbout": "關於組織政策", + "orgPoliciesAboutDescription": "組織策略用於根據用戶的 ID 令牌來控制對組織的訪問。 您可以指定 JMESPath 表達式來提取角色和組織資訊從 ID 令牌中提取資訊。", + "orgPoliciesAboutDescriptionLink": "欲了解更多資訊,請參閱文件。", + "defaultMappingsOptional": "默認映射(可選)", + "defaultMappingsOptionalDescription": "當沒有為某個組織定義組織的政策時,使用默認映射。 您可以指定默認角色和組織映射回到這裡。", + "defaultMappingsRole": "默認角色映射", + "defaultMappingsRoleDescription": "此表達式的結果必須返回組織中定義的角色名稱作為字串。", + "defaultMappingsOrg": "默認組織映射", + "defaultMappingsOrgDescription": "此表達式必須返回 組織ID 或 true 才能允許用戶訪問組織。", + "defaultMappingsSubmit": "保存默認映射", + "orgPoliciesEdit": "編輯組織策略", + "org": "組織", + "orgSelect": "選擇組織", + "orgSearch": "搜索", + "orgNotFound": "找不到組織。", + "roleMappingPathOptional": "角色映射路徑(可選)", + "orgMappingPathOptional": "組織映射路徑(可選)", + "orgPolicyUpdate": "更新策略", + "orgPolicyAdd": "添加策略", + "orgPolicyConfig": "配置組織訪問權限", + "idpUpdatedDescription": "身份提供商更新成功", + "redirectUrl": "重定向網址", + "redirectUrlAbout": "關於重定向網址", + "redirectUrlAboutDescription": "這是用戶在驗證後將被重定向到的URL。您需要在身份提供商設置中配置此URL。", + "pangolinAuth": "認證 - Pangolin", + "verificationCodeLengthRequirements": "您的驗證碼必須是 8 個字元。", + "errorOccurred": "發生錯誤", + "emailErrorVerify": "驗證電子郵件失敗:", + "emailVerified": "電子郵件驗證成功!重定向您...", + "verificationCodeErrorResend": "無法重新發送驗證碼:", + "verificationCodeResend": "驗證碼已重新發送", + "verificationCodeResendDescription": "我們已將驗證碼重新發送到您的電子郵件地址。請檢查您的收件箱。", + "emailVerify": "驗證電子郵件", + "emailVerifyDescription": "輸入驗證碼發送到您的電子郵件地址。", + "verificationCode": "驗證碼", + "verificationCodeEmailSent": "我們向您的電子郵件地址發送了驗證碼。", + "submit": "提交", + "emailVerifyResendProgress": "正在重新發送...", + "emailVerifyResend": "沒有收到代碼?點擊此處重新發送", + "passwordNotMatch": "密碼不匹配", + "signupError": "註冊時出錯", + "pangolinLogoAlt": "Pangolin 標誌", + "inviteAlready": "看起來您已被邀請!", + "inviteAlreadyDescription": "要接受邀請,您必須登錄或創建一個帳戶。", + "signupQuestion": "已經有一個帳戶?", + "login": "登錄", + "resourceNotFound": "找不到資源", + "resourceNotFoundDescription": "您要訪問的資源不存在。", + "pincodeRequirementsLength": "PIN碼必須是 6 位數字", + "pincodeRequirementsChars": "PIN 必須只包含數字", + "passwordRequirementsLength": "密碼必須至少 1 個字元長", + "passwordRequirementsTitle": "密碼要求:", + "passwordRequirementLength": "至少 8 個字元長", + "passwordRequirementUppercase": "至少一個大寫字母", + "passwordRequirementLowercase": "至少一個小寫字母", + "passwordRequirementNumber": "至少一個數字", + "passwordRequirementSpecial": "至少一個特殊字元", + "passwordRequirementsMet": "✓ 密碼滿足所有要求", + "passwordStrength": "密碼強度", + "passwordStrengthWeak": "弱", + "passwordStrengthMedium": "中", + "passwordStrengthStrong": "強", + "passwordRequirements": "要求:", + "passwordRequirementLengthText": "8+ 個字元", + "passwordRequirementUppercaseText": "大寫字母 (A-Z)", + "passwordRequirementLowercaseText": "小寫字母 (a-z)", + "passwordRequirementNumberText": "數字 (0-9)", + "passwordRequirementSpecialText": "特殊字元 (!@#$%...)", + "passwordsDoNotMatch": "密碼不匹配", + "otpEmailRequirementsLength": "OTP 必須至少 1 個字元長", + "otpEmailSent": "OTP 已發送", + "otpEmailSentDescription": "OTP 已經發送到您的電子郵件", + "otpEmailErrorAuthenticate": "通過電子郵件身份驗證失敗", + "pincodeErrorAuthenticate": "Pincode 驗證失敗", + "passwordErrorAuthenticate": "密碼驗證失敗", + "poweredBy": "支持者:", + "authenticationRequired": "需要身份驗證", + "authenticationMethodChoose": "請選擇您偏好的方式來訪問 {name}", + "authenticationRequest": "您必須通過身份驗證才能訪問 {name}", + "user": "用戶", + "pincodeInput": "6 位數字 PIN 碼", + "pincodeSubmit": "使用 PIN 登錄", + "passwordSubmit": "使用密碼登錄", + "otpEmailDescription": "一次性代碼將發送到此電子郵件。", + "otpEmailSend": "發送一次性代碼", + "otpEmail": "一次性密碼 (OTP)", + "otpEmailSubmit": "提交 OTP", + "backToEmail": "回到電子郵件", + "noSupportKey": "伺服器當前未使用支持者金鑰,歡迎支持本項目!", + "accessDenied": "訪問被拒絕", + "accessDeniedDescription": "當前帳戶無權訪問此資源。如認為這是錯誤,請與管理員聯繫。", + "accessTokenError": "檢查訪問令牌時出錯", + "accessGranted": "已授予訪問", + "accessUrlInvalid": "訪問 URL 無效", + "accessGrantedDescription": "您已獲准訪問此資源,正在為您跳轉...", + "accessUrlInvalidDescription": "此共享訪問URL無效。請聯絡資源所有者獲取新URL。", + "tokenInvalid": "無效的令牌", + "pincodeInvalid": "無效的代碼", + "passwordErrorRequestReset": "請求重設失敗:", + "passwordErrorReset": "重設密碼失敗:", + "passwordResetSuccess": "密碼重設成功!返回登錄...", + "passwordReset": "重設密碼", + "passwordResetDescription": "按照步驟重設您的密碼", + "passwordResetSent": "我們將發送一個驗證碼到這個電子郵件地址。", + "passwordResetCode": "驗證碼", + "passwordResetCodeDescription": "請檢查您的電子郵件以獲取驗證碼。", + "passwordNew": "新密碼", + "passwordNewConfirm": "確認新密碼", + "changePassword": "更改密碼", + "changePasswordDescription": "更新您的帳戶密碼", + "oldPassword": "當前密碼", + "newPassword": "新密碼", + "confirmNewPassword": "確認新密碼", + "changePasswordError": "更改密碼失敗", + "changePasswordErrorDescription": "更改您的密碼時出錯", + "changePasswordSuccess": "密碼修改成功", + "changePasswordSuccessDescription": "您的密碼已成功更新", + "passwordExpiryRequired": "需要密碼過期", + "passwordExpiryDescription": "該機構要求您每 {maxDays} 天更改一次密碼。", + "changePasswordNow": "現在更改密碼", + "pincodeAuth": "驗證器代碼", + "pincodeSubmit2": "提交代碼", + "passwordResetSubmit": "請求重設", + "passwordBack": "回到密碼", + "loginBack": "返回登錄", + "signup": "註冊", + "loginStart": "登錄以開始", + "idpOidcTokenValidating": "正在驗證 OIDC 令牌", + "idpOidcTokenResponse": "驗證 OIDC 令牌響應", + "idpErrorOidcTokenValidating": "驗證 OIDC 令牌出錯", + "idpConnectingTo": "連接到{name}", + "idpConnectingToDescription": "正在驗證您的身份", + "idpConnectingToProcess": "正在連接...", + "idpConnectingToFinished": "已連接", + "idpErrorConnectingTo": "無法連接到 {name},請聯絡管理員協助處理。", + "idpErrorNotFound": "找不到 IdP", + "inviteInvalid": "無效邀請", + "inviteInvalidDescription": "邀請連結無效。", + "inviteErrorWrongUser": "邀請不是該用戶的", + "inviteErrorUserNotExists": "用戶不存在。請先創建帳戶。", + "inviteErrorLoginRequired": "您必須登錄才能接受邀請", + "inviteErrorExpired": "邀請可能已過期", + "inviteErrorRevoked": "邀請可能已被吊銷了", + "inviteErrorTypo": "邀請連結中可能有一個類型", + "pangolinSetup": "認證 - Pangolin", + "orgNameRequired": "組織名稱是必需的", + "orgIdRequired": "組織ID是必需的", + "orgErrorCreate": "創建組織時出錯", + "pageNotFound": "找不到頁面", + "pageNotFoundDescription": "哎呀!您正在尋找的頁面不存在。", + "overview": "概覽", + "home": "首頁", + "accessControl": "訪問控制", + "settings": "設置", + "usersAll": "所有用戶", + "license": "許可協議", + "pangolinDashboard": "儀錶板 - Pangolin", + "noResults": "未找到任何結果。", + "terabytes": "{count} TB", + "gigabytes": "{count} GB", + "megabytes": "{count} MB", + "tagsEntered": "已輸入的標籤", + "tagsEnteredDescription": "這些是您輸入的標籤。", + "tagsWarnCannotBeLessThanZero": "最大標籤和最小標籤不能小於 0", + "tagsWarnNotAllowedAutocompleteOptions": "標記不允許為每個自動完成選項", + "tagsWarnInvalid": "無效的標籤,每個有效標籤", + "tagWarnTooShort": "標籤 {tagText} 太短", + "tagWarnTooLong": "標籤 {tagText} 太長", + "tagsWarnReachedMaxNumber": "已達到允許標籤的最大數量", + "tagWarnDuplicate": "未添加重複標籤 {tagText}", + "supportKeyInvalid": "無效金鑰", + "supportKeyInvalidDescription": "您的支持者金鑰無效。", + "supportKeyValid": "有效的金鑰", + "supportKeyValidDescription": "您的支持者金鑰已被驗證。感謝您的支持!", + "supportKeyErrorValidationDescription": "驗證支持者金鑰失敗。", + "supportKey": "支持開發和通過一個 Pangolin !", + "supportKeyDescription": "購買支持者鑰匙,幫助我們繼續為社區發展 Pangolin 。 您的貢獻使我們能夠投入更多的時間來維護和添加所有人的新功能。 我們永遠不會用這個來支付牆上的功能。這與任何商業版是分開的。", + "supportKeyPet": "您還可以領養並見到屬於自己的 Pangolin!", + "supportKeyPurchase": "付款通過 GitHub 進行處理,之後您可以在以下位置獲取您的金鑰:", + "supportKeyPurchaseLink": "我們的網站", + "supportKeyPurchase2": "並在這裡兌換。", + "supportKeyLearnMore": "了解更多。", + "supportKeyOptions": "請選擇最適合您的選項。", + "supportKetOptionFull": "完全支持者", + "forWholeServer": "適用於整個伺服器", + "lifetimePurchase": "終身購買", + "supporterStatus": "支持者狀態", + "buy": "購買", + "supportKeyOptionLimited": "有限支持者", + "forFiveUsers": "適用於 5 或更少用戶", + "supportKeyRedeem": "兌換支持者金鑰", + "supportKeyHideSevenDays": "隱藏 7 天", + "supportKeyEnter": "輸入支持者金鑰", + "supportKeyEnterDescription": "見到你自己的 Pangolin!", + "githubUsername": "GitHub 使用者名稱", + "supportKeyInput": "支持者金鑰", + "supportKeyBuy": "購買支持者金鑰", + "logoutError": "註銷錯誤", + "signingAs": "登錄為", + "serverAdmin": "伺服器管理員", + "managedSelfhosted": "託管自託管", + "otpEnable": "啟用雙因子認證", + "otpDisable": "禁用雙因子認證", + "logout": "登出", + "licenseTierProfessionalRequired": "需要專業版", + "licenseTierProfessionalRequiredDescription": "此功能僅在專業版可用。", + "actionGetOrg": "獲取組織", + "updateOrgUser": "更新組織用戶", + "createOrgUser": "創建組織用戶", + "actionUpdateOrg": "更新組織", + "actionUpdateUser": "更新用戶", + "actionGetUser": "獲取用戶", + "actionGetOrgUser": "獲取組織用戶", + "actionListOrgDomains": "列出組織域", + "actionCreateSite": "創建站點", + "actionDeleteSite": "刪除站點", + "actionGetSite": "獲取站點", + "actionListSites": "站點列表", + "actionApplyBlueprint": "應用藍圖", + "setupToken": "設置令牌", + "setupTokenDescription": "從伺服器控制台輸入設定令牌。", + "setupTokenRequired": "需要設置令牌", + "actionUpdateSite": "更新站點", + "actionListSiteRoles": "允許站點角色列表", + "actionCreateResource": "創建資源", + "actionDeleteResource": "刪除資源", + "actionGetResource": "獲取資源", + "actionListResource": "列出資源", + "actionUpdateResource": "更新資源", + "actionListResourceUsers": "列出資源用戶", + "actionSetResourceUsers": "設置資源用戶", + "actionSetAllowedResourceRoles": "設置允許的資源角色", + "actionListAllowedResourceRoles": "列出允許的資源角色", + "actionSetResourcePassword": "設置資源密碼", + "actionSetResourcePincode": "設置資源粉碼", + "actionSetResourceEmailWhitelist": "設置資源電子郵件白名單", + "actionGetResourceEmailWhitelist": "獲取資源電子郵件白名單", + "actionCreateTarget": "創建目標", + "actionDeleteTarget": "刪除目標", + "actionGetTarget": "獲取目標", + "actionListTargets": "列表目標", + "actionUpdateTarget": "更新目標", + "actionCreateRole": "創建角色", + "actionDeleteRole": "刪除角色", + "actionGetRole": "獲取角色", + "actionListRole": "角色列表", + "actionUpdateRole": "更新角色", + "actionListAllowedRoleResources": "列表允許的角色資源", + "actionInviteUser": "邀請用戶", + "actionRemoveUser": "刪除用戶", + "actionListUsers": "列出用戶", + "actionAddUserRole": "添加用戶角色", + "actionGenerateAccessToken": "生成訪問令牌", + "actionDeleteAccessToken": "刪除訪問令牌", + "actionListAccessTokens": "訪問令牌", + "actionCreateResourceRule": "創建資源規則", + "actionDeleteResourceRule": "刪除資源規則", + "actionListResourceRules": "列出資源規則", + "actionUpdateResourceRule": "更新資源規則", + "actionListOrgs": "列出組織", + "actionCheckOrgId": "檢查組織ID", + "actionCreateOrg": "創建組織", + "actionDeleteOrg": "刪除組織", + "actionListApiKeys": "列出 API 金鑰", + "actionListApiKeyActions": "列出 API 金鑰動作", + "actionSetApiKeyActions": "設置 API 金鑰允許的操作", + "actionCreateApiKey": "創建 API 金鑰", + "actionDeleteApiKey": "刪除 API 金鑰", + "actionCreateIdp": "創建 IDP", + "actionUpdateIdp": "更新 IDP", + "actionDeleteIdp": "刪除 IDP", + "actionListIdps": "列出 IDP", + "actionGetIdp": "獲取 IDP", + "actionCreateIdpOrg": "創建 IDP 組織策略", + "actionDeleteIdpOrg": "刪除 IDP 組織策略", + "actionListIdpOrgs": "列出 IDP 組織", + "actionUpdateIdpOrg": "更新 IDP 組織", + "actionCreateClient": "創建用戶端", + "actionDeleteClient": "刪除用戶端", + "actionUpdateClient": "更新用戶端", + "actionListClients": "列出用戶端", + "actionGetClient": "獲取用戶端", + "actionCreateSiteResource": "創建站點資源", + "actionDeleteSiteResource": "刪除站點資源", + "actionGetSiteResource": "獲取站點資源", + "actionListSiteResources": "列出站點資源", + "actionUpdateSiteResource": "更新站點資源", + "actionListInvitations": "邀請列表", + "noneSelected": "未選擇", + "orgNotFound2": "未找到組織。", + "searchProgress": "搜索中...", + "create": "創建", + "orgs": "組織", + "loginError": "登錄時出錯", + "passwordForgot": "忘記密碼?", + "otpAuth": "兩步驗證", + "otpAuthDescription": "從您的身份驗證程序中輸入代碼或您的單次備份代碼。", + "otpAuthSubmit": "提交代碼", + "idpContinue": "或者繼續", + "otpAuthBack": "返回登錄", + "navbar": "導航菜單", + "navbarDescription": "應用程式的主導航菜單", + "navbarDocsLink": "文件", + "otpErrorEnable": "無法啟用 2FA", + "otpErrorEnableDescription": "啟用 2FA 時出錯", + "otpSetupCheckCode": "請輸入您的 6 位數字代碼", + "otpSetupCheckCodeRetry": "無效的代碼。請重試。", + "otpSetup": "啟用兩步驗證", + "otpSetupDescription": "用額外的保護層來保護您的帳戶", + "otpSetupScanQr": "用您的身份驗證程序掃描此二維碼或手動輸入金鑰:", + "otpSetupSecretCode": "驗證器代碼", + "otpSetupSuccess": "啟用兩步驗證", + "otpSetupSuccessStoreBackupCodes": "您的帳戶現在更加安全。不要忘記保存您的備份代碼。", + "otpErrorDisable": "無法禁用 2FA", + "otpErrorDisableDescription": "禁用 2FA 時出錯", + "otpRemove": "禁用兩步驗證", + "otpRemoveDescription": "為您的帳戶禁用兩步驗證", + "otpRemoveSuccess": "雙重身份驗證已禁用", + "otpRemoveSuccessMessage": "您的帳戶已禁用雙重身份驗證。您可以隨時再次啟用它。", + "otpRemoveSubmit": "禁用兩步驗證", + "paginator": "第 {current} 頁,共 {last} 頁", + "paginatorToFirst": "轉到第一頁", + "paginatorToPrevious": "轉到上一頁", + "paginatorToNext": "轉到下一頁", + "paginatorToLast": "轉到最後一頁", + "copyText": "複製文本", + "copyTextFailed": "複製文本失敗: ", + "copyTextClipboard": "複製到剪貼簿", + "inviteErrorInvalidConfirmation": "無效確認", + "passwordRequired": "必須填寫密碼", + "allowAll": "允許所有", + "permissionsAllowAll": "允許所有權限", + "githubUsernameRequired": "必須填寫 GitHub 使用者名稱", + "supportKeyRequired": "必須填寫支持者金鑰", + "passwordRequirementsChars": "密碼至少需要 8 個字元", + "language": "語言", + "verificationCodeRequired": "必須輸入代碼", + "userErrorNoUpdate": "沒有要更新的用戶", + "siteErrorNoUpdate": "沒有要更新的站點", + "resourceErrorNoUpdate": "沒有可更新的資源", + "authErrorNoUpdate": "沒有要更新的身份驗證資訊", + "orgErrorNoUpdate": "沒有要更新的組織", + "orgErrorNoProvided": "未提供組織", + "apiKeysErrorNoUpdate": "沒有要更新的 API 金鑰", + "sidebarOverview": "概覽", + "sidebarHome": "首頁", + "sidebarSites": "站點", + "sidebarResources": "資源", + "sidebarAccessControl": "訪問控制", + "sidebarUsers": "用戶", + "sidebarInvitations": "邀請", + "sidebarRoles": "角色", + "sidebarShareableLinks": "分享連結", + "sidebarApiKeys": "API 金鑰", + "sidebarSettings": "設置", + "sidebarAllUsers": "所有用戶", + "sidebarIdentityProviders": "身份提供商", + "sidebarLicense": "證書", + "sidebarClients": "用戶端", + "sidebarDomains": "域", + "sidebarBluePrints": "藍圖", + "blueprints": "藍圖", + "blueprintsDescription": "應用聲明配置並查看先前運行的", + "blueprintAdd": "添加藍圖", + "blueprintGoBack": "查看所有藍圖", + "blueprintCreate": "創建藍圖", + "blueprintCreateDescription2": "按照下面的步驟創建和應用新的藍圖", + "blueprintDetails": "藍圖詳細資訊", + "blueprintDetailsDescription": "查看應用藍圖的結果和發生的任何錯誤", + "blueprintInfo": "藍圖資訊", + "message": "留言", + "blueprintContentsDescription": "定義描述您基礎設施的 YAML 內容", + "blueprintErrorCreateDescription": "應用藍圖時出錯", + "blueprintErrorCreate": "創建藍圖時出錯", + "searchBlueprintProgress": "搜索藍圖...", + "appliedAt": "應用於", + "source": "來源", + "contents": "目錄", + "parsedContents": "解析內容 (只讀)", + "enableDockerSocket": "啟用 Docker 藍圖", + "enableDockerSocketDescription": "啟用 Docker Socket 標籤擦除藍圖標籤。套接字路徑必須提供給新的。", + "enableDockerSocketLink": "了解更多", + "viewDockerContainers": "查看停靠容器", + "containersIn": "{siteName} 中的容器", + "selectContainerDescription": "選擇任何容器作為目標的主機名。點擊埠使用埠。", + "containerName": "名稱", + "containerImage": "圖片", + "containerState": "狀態", + "containerNetworks": "網路", + "containerHostnameIp": "主機名/IP", + "containerLabels": "標籤", + "containerLabelsCount": "{count, plural, other {# 標籤}}", + "containerLabelsTitle": "容器標籤", + "containerLabelEmpty": "<為空>", + "containerPorts": "埠", + "containerPortsMore": "+{count} 更多", + "containerActions": "行動", + "select": "選擇", + "noContainersMatchingFilters": "沒有找到匹配當前過濾器的容器。", + "showContainersWithoutPorts": "顯示沒有埠的容器", + "showStoppedContainers": "顯示已停止的容器", + "noContainersFound": "未找到容器。請確保 Docker 容器正在運行。", + "searchContainersPlaceholder": "在 {count} 個容器中搜索...", + "searchResultsCount": "{count, plural, other {# 個結果}}", + "filters": "篩選器", + "filterOptions": "過濾器選項", + "filterPorts": "埠", + "filterStopped": "已停止", + "clearAllFilters": "清除所有過濾器", + "columns": "列", + "toggleColumns": "切換列", + "refreshContainersList": "刷新容器列表", + "searching": "搜索中...", + "noContainersFoundMatching": "未找到與 \"{filter}\" 匹配的容器。", + "light": "淺色", + "dark": "深色", + "system": "系統", + "theme": "主題", + "subnetRequired": "子網是必填項", + "initialSetupTitle": "初始伺服器設置", + "initialSetupDescription": "創建初始伺服器管理員帳戶。 只能存在一個伺服器管理員。 您可以隨時更改這些憑據。", + "createAdminAccount": "創建管理員帳戶", + "setupErrorCreateAdmin": "創建伺服器管理員帳戶時發生錯誤。", + "certificateStatus": "證書狀態", + "loading": "載入中", + "restart": "重啟", + "domains": "域", + "domainsDescription": "管理您的組織域", + "domainsSearch": "搜索域...", + "domainAdd": "添加域", + "domainAddDescription": "在您的組織中註冊新域", + "domainCreate": "創建域", + "domainCreatedDescription": "域創建成功", + "domainDeletedDescription": "成功刪除域", + "domainQuestionRemove": "您確定要從您的帳戶中刪除域名嗎?", + "domainMessageRemove": "移除後,該域將不再與您的帳戶關聯。", + "domainConfirmDelete": "確認刪除域", + "domainDelete": "刪除域", + "domain": "域", + "selectDomainTypeNsName": "域委派(NS)", + "selectDomainTypeNsDescription": "此域及其所有子域。當您希望控制整個域區域時使用此選項。", + "selectDomainTypeCnameName": "單個域(CNAME)", + "selectDomainTypeCnameDescription": "僅此特定域。用於單個子域或特定域條目。", + "selectDomainTypeWildcardName": "通配符域", + "selectDomainTypeWildcardDescription": "此域名及其子域名。", + "domainDelegation": "單個域", + "selectType": "選擇一個類型", + "actions": "操作", + "refresh": "刷新", + "refreshError": "刷新數據失敗", + "verified": "已驗證", + "pending": "待定", + "sidebarBilling": "計費", + "billing": "計費", + "orgBillingDescription": "管理您的帳單資訊和訂閱", + "github": "GitHub", + "pangolinHosted": "Pangolin 託管", + "fossorial": "Fossorial", + "completeAccountSetup": "完成帳戶設定", + "completeAccountSetupDescription": "設置您的密碼以開始", + "accountSetupSent": "我們將發送帳號設定代碼到該電子郵件地址。", + "accountSetupCode": "設置代碼", + "accountSetupCodeDescription": "請檢查您的信箱以獲取設置代碼。", + "passwordCreate": "創建密碼", + "passwordCreateConfirm": "確認密碼", + "accountSetupSubmit": "發送設置代碼", + "completeSetup": "完成設置", + "accountSetupSuccess": "帳號設定完成!歡迎來到 Pangolin!", + "documentation": "文件", + "saveAllSettings": "保存所有設置", + "settingsUpdated": "設置已更新", + "settingsUpdatedDescription": "所有設置已成功更新", + "settingsErrorUpdate": "設置更新失敗", + "settingsErrorUpdateDescription": "更新設置時發生錯誤", + "sidebarCollapse": "摺疊", + "sidebarExpand": "展開", + "newtUpdateAvailable": "更新可用", + "newtUpdateAvailableInfo": "新版本的 Newt 已可用。請更新到最新版本以獲得最佳體驗。", + "domainPickerEnterDomain": "域名", + "domainPickerPlaceholder": "example.com", + "domainPickerDescription": "輸入資源的完整域名以查看可用選項。", + "domainPickerDescriptionSaas": "輸入完整域名、子域或名稱以查看可用選項。", + "domainPickerTabAll": "所有", + "domainPickerTabOrganization": "組織", + "domainPickerTabProvided": "提供的", + "domainPickerSortAsc": "A-Z", + "domainPickerSortDesc": "Z-A", + "domainPickerCheckingAvailability": "檢查可用性...", + "domainPickerNoMatchingDomains": "未找到匹配的域名。嘗試不同的域名或檢查您組織的域名設置。", + "domainPickerOrganizationDomains": "組織域", + "domainPickerProvidedDomains": "提供的域", + "domainPickerSubdomain": "子域:{subdomain}", + "domainPickerNamespace": "命名空間:{namespace}", + "domainPickerShowMore": "顯示更多", + "regionSelectorTitle": "選擇區域", + "regionSelectorInfo": "選擇區域以幫助提升您所在地的性能。您不必與伺服器在相同的區域。", + "regionSelectorPlaceholder": "選擇一個區域", + "regionSelectorComingSoon": "即將推出", + "billingLoadingSubscription": "正在載入訂閱...", + "billingFreeTier": "免費層", + "billingWarningOverLimit": "警告:您已超出一個或多個使用限制。在您修改訂閱或調整使用情況之前,您的站點將無法連接。", + "billingUsageLimitsOverview": "使用限制概覽", + "billingMonitorUsage": "監控您的使用情況以對比已配置的限制。如需提高限制請聯絡我們 support@pangolin.net。", + "billingDataUsage": "數據使用情況", + "billingOnlineTime": "站點在線時間", + "billingUsers": "活躍用戶", + "billingDomains": "活躍域", + "billingRemoteExitNodes": "活躍自託管節點", + "billingNoLimitConfigured": "未配置限制", + "billingEstimatedPeriod": "估計結算週期", + "billingIncludedUsage": "包含的使用量", + "billingIncludedUsageDescription": "您當前訂閱計劃中包含的使用量", + "billingFreeTierIncludedUsage": "免費層使用額度", + "billingIncluded": "包含", + "billingEstimatedTotal": "預計總額:", + "billingNotes": "備註", + "billingEstimateNote": "這是根據您當前使用情況的估算。", + "billingActualChargesMayVary": "實際費用可能會有變化。", + "billingBilledAtEnd": "您將在結算週期結束時被計費。", + "billingModifySubscription": "修改訂閱", + "billingStartSubscription": "開始訂閱", + "billingRecurringCharge": "週期性收費", + "billingManageSubscriptionSettings": "管理您的訂閱設置和偏好", + "billingNoActiveSubscription": "您沒有活躍的訂閱。開始訂閱以增加使用限制。", + "billingFailedToLoadSubscription": "無法載入訂閱", + "billingFailedToLoadUsage": "無法載入使用情況", + "billingFailedToGetCheckoutUrl": "無法獲取結帳網址", + "billingPleaseTryAgainLater": "請稍後再試。", + "billingCheckoutError": "結帳錯誤", + "billingFailedToGetPortalUrl": "無法獲取門戶網址", + "billingPortalError": "門戶錯誤", + "billingDataUsageInfo": "當連接到雲端時,您將為透過安全隧道傳輸的所有數據收取費用。 這包括您所有站點的進出流量。 當您達到上限時,您的站點將斷開連接,直到您升級計劃或減少使用。使用節點時不收取數據。", + "billingOnlineTimeInfo": "您要根據您的網站連接到雲端的時間長短收取費用。 例如,44,640 分鐘等於一個 24/7 全月運行的網站。 當您達到上限時,您的站點將斷開連接,直到您升級計劃或減少使用。使用節點時不收取費用。", + "billingUsersInfo": "根據您組織中的活躍用戶數量收費。按日計算帳單。", + "billingDomainInfo": "根據組織中活躍域的數量收費。按日計算帳單。", + "billingRemoteExitNodesInfo": "根據您組織中已管理節點的數量收費。按日計算帳單。", + "domainNotFound": "域未找到", + "domainNotFoundDescription": "此資源已禁用,因為該域不再在我們的系統中存在。請為此資源設置一個新域。", + "failed": "失敗", + "createNewOrgDescription": "創建一個新組織", + "organization": "組織", + "port": "埠", + "securityKeyManage": "管理安全金鑰", + "securityKeyDescription": "添加或刪除用於無密碼認證的安全金鑰", + "securityKeyRegister": "註冊新的安全金鑰", + "securityKeyList": "您的安全金鑰", + "securityKeyNone": "尚未註冊安全金鑰", + "securityKeyNameRequired": "名稱為必填項", + "securityKeyRemove": "刪除", + "securityKeyLastUsed": "上次使用:{date}", + "securityKeyNameLabel": "名稱", + "securityKeyRegisterSuccess": "安全金鑰註冊成功", + "securityKeyRegisterError": "註冊安全金鑰失敗", + "securityKeyRemoveSuccess": "安全金鑰刪除成功", + "securityKeyRemoveError": "刪除安全金鑰失敗", + "securityKeyLoadError": "載入安全金鑰失敗", + "securityKeyLogin": "使用安全金鑰繼續", + "securityKeyAuthError": "使用安全金鑰認證失敗", + "securityKeyRecommendation": "考慮在其他設備上註冊另一個安全金鑰,以確保不會被鎖定在您的帳戶之外。", + "registering": "註冊中...", + "securityKeyPrompt": "請使用您的安全金鑰驗證身份。確保您的安全金鑰已連接並準備好。", + "securityKeyBrowserNotSupported": "您的瀏覽器不支持安全金鑰。請使用像 Chrome、Firefox 或 Safari 這樣的現代瀏覽器。", + "securityKeyPermissionDenied": "請允許訪問您的安全金鑰以繼續登錄。", + "securityKeyRemovedTooQuickly": "請保持您的安全金鑰連接,直到登錄過程完成。", + "securityKeyNotSupported": "您的安全金鑰可能不相容。請嘗試不同的安全金鑰。", + "securityKeyUnknownError": "使用安全金鑰時出現問題。請再試一次。", + "twoFactorRequired": "註冊安全金鑰需要兩步驗證。", + "twoFactor": "兩步驗證", + "twoFactorAuthentication": "兩步驗證", + "twoFactorDescription": "這個組織需要雙重身份驗證。", + "enableTwoFactor": "啟用兩步驗證", + "organizationSecurityPolicy": "組織安全政策", + "organizationSecurityPolicyDescription": "此機構擁有安全要求,您必須先滿足才能訪問", + "securityRequirements": "安全要求", + "allRequirementsMet": "已滿足所有要求", + "completeRequirementsToContinue": "完成下面的要求以繼續訪問此組織", + "youCanNowAccessOrganization": "您現在可以訪問此組織", + "reauthenticationRequired": "會話長度", + "reauthenticationDescription": "該機構要求您每 {maxDays} 天登錄一次。", + "reauthenticationDescriptionHours": "該機構要求您每 {maxHours} 小時登錄一次。", + "reauthenticateNow": "再次登錄", + "adminEnabled2FaOnYourAccount": "管理員已為 {email} 啟用兩步驗證。請完成設置以繼續。", + "securityKeyAdd": "添加安全金鑰", + "securityKeyRegisterTitle": "註冊新安全金鑰", + "securityKeyRegisterDescription": "連接您的安全金鑰並輸入名稱以便識別", + "securityKeyTwoFactorRequired": "要求兩步驗證", + "securityKeyTwoFactorDescription": "請輸入你的兩步驗證代碼以註冊安全金鑰", + "securityKeyTwoFactorRemoveDescription": "請輸入你的兩步驗證代碼以移除安全金鑰", + "securityKeyTwoFactorCode": "雙因素代碼", + "securityKeyRemoveTitle": "移除安全金鑰", + "securityKeyRemoveDescription": "輸入您的密碼以移除安全金鑰 \"{name}\"", + "securityKeyNoKeysRegistered": "沒有註冊安全金鑰", + "securityKeyNoKeysDescription": "添加安全金鑰以加強您的帳戶安全", + "createDomainRequired": "必須輸入域", + "createDomainAddDnsRecords": "添加 DNS 記錄", + "createDomainAddDnsRecordsDescription": "將以下 DNS 記錄添加到您的域名提供商以完成設置。", + "createDomainNsRecords": "NS 記錄", + "createDomainRecord": "記錄", + "createDomainType": "類型:", + "createDomainName": "名稱:", + "createDomainValue": "值:", + "createDomainCnameRecords": "CNAME 記錄", + "createDomainARecords": "A記錄", + "createDomainRecordNumber": "記錄 {number}", + "createDomainTxtRecords": "TXT 記錄", + "createDomainSaveTheseRecords": "保存這些記錄", + "createDomainSaveTheseRecordsDescription": "務必保存這些 DNS 記錄,因為您將無法再次查看它們。", + "createDomainDnsPropagation": "DNS 傳播", + "createDomainDnsPropagationDescription": "DNS 更改可能需要一些時間才能在網路上傳播。這可能需要從幾分鐘到 48 小時,具體取決於您的 DNS 提供商和 TTL 設置。", + "resourcePortRequired": "非 HTTP 資源必須輸入埠號", + "resourcePortNotAllowed": "HTTP 資源不應設置埠號", + "billingPricingCalculatorLink": "價格計算機", + "signUpTerms": { + "IAgreeToThe": "我同意", + "termsOfService": "服務條款", + "and": "和", + "privacyPolicy": "隱私政策" + }, + "siteRequired": "需要站點。", + "olmTunnel": "Olm 隧道", + "olmTunnelDescription": "使用 Olm 進行用戶端連接", + "errorCreatingClient": "創建用戶端出錯", + "clientDefaultsNotFound": "未找到用戶端預設值", + "createClient": "創建用戶端", + "createClientDescription": "創建一個新用戶端來連接您的站點", + "seeAllClients": "查看所有用戶端", + "clientInformation": "用戶端資訊", + "clientNamePlaceholder": "用戶端名稱", + "address": "地址", + "subnetPlaceholder": "子網", + "addressDescription": "此用戶端將用於連接的地址", + "selectSites": "選擇站點", + "sitesDescription": "用戶端將與所選站點進行連接", + "clientInstallOlm": "安裝 Olm", + "clientInstallOlmDescription": "在您的系統上運行 Olm", + "clientOlmCredentials": "Olm 憑據", + "clientOlmCredentialsDescription": "這是 Olm 伺服器的身份驗證方式", + "olmEndpoint": "Olm 端點", + "olmId": "Olm ID", + "olmSecretKey": "Olm 私鑰", + "clientCredentialsSave": "保存您的憑據", + "clientCredentialsSaveDescription": "該資訊僅會顯示一次,請確保將其複製到安全位置。", + "generalSettingsDescription": "配置此用戶端的常規設置", + "clientUpdated": "用戶端已更新", + "clientUpdatedDescription": "用戶端已更新。", + "clientUpdateFailed": "更新用戶端失敗", + "clientUpdateError": "更新用戶端時出錯。", + "sitesFetchFailed": "獲取站點失敗", + "sitesFetchError": "獲取站點時出錯。", + "olmErrorFetchReleases": "獲取 Olm 發布版本時出錯。", + "olmErrorFetchLatest": "獲取最新 Olm 發布版本時出錯。", + "remoteSubnets": "遠程子網", + "enterCidrRange": "輸入 CIDR 範圍", + "remoteSubnetsDescription": "添加可以通過用戶端遠端存取該站點的 CIDR 範圍。使用類似 10.0.0.0/24 的格式。這僅適用於 VPN 用戶端連接。", + "resourceEnableProxy": "啟用公共代理", + "resourceEnableProxyDescription": "啟用到此資源的公共代理。這允許外部網路通過開放埠訪問資源。需要 Traefik 配置。", + "externalProxyEnabled": "外部代理已啟用", + "addNewTarget": "添加新目標", + "targetsList": "目標列表", + "advancedMode": "高級模式", + "targetErrorDuplicateTargetFound": "找到重複的目標", + "healthCheckHealthy": "正常", + "healthCheckUnhealthy": "不正常", + "healthCheckUnknown": "未知", + "healthCheck": "健康檢查", + "configureHealthCheck": "配置健康檢查", + "configureHealthCheckDescription": "為 {target} 設置健康監控", + "enableHealthChecks": "啟用健康檢查", + "enableHealthChecksDescription": "監視此目標的健康狀況。如果需要,您可以監視一個不同的終點。", + "healthScheme": "方法", + "healthSelectScheme": "選擇方法", + "healthCheckPath": "路徑", + "healthHostname": "IP / 主機", + "healthPort": "埠", + "healthCheckPathDescription": "用於檢查健康狀態的路徑。", + "healthyIntervalSeconds": "正常間隔", + "unhealthyIntervalSeconds": "不正常間隔", + "IntervalSeconds": "正常間隔", + "timeoutSeconds": "超時", + "timeIsInSeconds": "時間以秒為單位", + "retryAttempts": "重試次數", + "expectedResponseCodes": "期望響應代碼", + "expectedResponseCodesDescription": "HTTP 狀態碼表示健康狀態。如留空,200-300 被視為健康。", + "customHeaders": "自訂 Headers", + "customHeadersDescription": "Header 斷行分隔:Header 名稱:值", + "headersValidationError": "Header 必須是格式:Header 名稱:值。", + "saveHealthCheck": "保存健康檢查", + "healthCheckSaved": "健康檢查已保存", + "healthCheckSavedDescription": "健康檢查配置已成功保存。", + "healthCheckError": "健康檢查錯誤", + "healthCheckErrorDescription": "保存健康檢查配置時出錯", + "healthCheckPathRequired": "健康檢查路徑為必填項", + "healthCheckMethodRequired": "HTTP 方法為必填項", + "healthCheckIntervalMin": "檢查間隔必須至少為 5 秒", + "healthCheckTimeoutMin": "超時必須至少為 1 秒", + "healthCheckRetryMin": "重試次數必須至少為 1 次", + "httpMethod": "HTTP 方法", + "selectHttpMethod": "選擇 HTTP 方法", + "domainPickerSubdomainLabel": "子域名", + "domainPickerBaseDomainLabel": "根域名", + "domainPickerSearchDomains": "搜索域名...", + "domainPickerNoDomainsFound": "未找到域名", + "domainPickerLoadingDomains": "載入域名...", + "domainPickerSelectBaseDomain": "選擇根域名...", + "domainPickerNotAvailableForCname": "不適用於 CNAME 域", + "domainPickerEnterSubdomainOrLeaveBlank": "輸入子域名或留空以使用根域名。", + "domainPickerEnterSubdomainToSearch": "輸入一個子域名以搜索並從可用免費域名中選擇。", + "domainPickerFreeDomains": "免費域名", + "domainPickerSearchForAvailableDomains": "搜索可用域名", + "domainPickerNotWorkSelfHosted": "注意:自託管實例當前不提供免費的域名。", + "resourceDomain": "域名", + "resourceEditDomain": "編輯域名", + "siteName": "站點名稱", + "proxyPort": "埠", + "resourcesTableProxyResources": "代理資源", + "resourcesTableClientResources": "用戶端資源", + "resourcesTableNoProxyResourcesFound": "未找到代理資源。", + "resourcesTableNoInternalResourcesFound": "未找到內部資源。", + "resourcesTableDestination": "目標", + "resourcesTableTheseResourcesForUseWith": "這些資源供...使用", + "resourcesTableClients": "用戶端", + "resourcesTableAndOnlyAccessibleInternally": "且僅在與用戶端連接時可內部訪問。", + "editInternalResourceDialogEditClientResource": "編輯用戶端資源", + "editInternalResourceDialogUpdateResourceProperties": "更新 {resourceName} 的資源屬性和目標配置。", + "editInternalResourceDialogResourceProperties": "資源屬性", + "editInternalResourceDialogName": "名稱", + "editInternalResourceDialogProtocol": "協議", + "editInternalResourceDialogSitePort": "站點埠", + "editInternalResourceDialogTargetConfiguration": "目標配置", + "editInternalResourceDialogCancel": "取消", + "editInternalResourceDialogSaveResource": "保存資源", + "editInternalResourceDialogSuccess": "成功", + "editInternalResourceDialogInternalResourceUpdatedSuccessfully": "內部資源更新成功", + "editInternalResourceDialogError": "錯誤", + "editInternalResourceDialogFailedToUpdateInternalResource": "更新內部資源失敗", + "editInternalResourceDialogNameRequired": "名稱為必填項", + "editInternalResourceDialogNameMaxLength": "名稱長度必須小於 255 個字元", + "editInternalResourceDialogProxyPortMin": "代理埠必須至少為 1", + "editInternalResourceDialogProxyPortMax": "代理埠必須小於 65536", + "editInternalResourceDialogInvalidIPAddressFormat": "無效的 IP 位址格式", + "editInternalResourceDialogDestinationPortMin": "目標埠必須至少為 1", + "editInternalResourceDialogDestinationPortMax": "目標埠必須小於 65536", + "createInternalResourceDialogNoSitesAvailable": "暫無可用站點", + "createInternalResourceDialogNoSitesAvailableDescription": "您需要至少配置一個子網的 Newt 站點來創建內部資源。", + "createInternalResourceDialogClose": "關閉", + "createInternalResourceDialogCreateClientResource": "創建用戶端資源", + "createInternalResourceDialogCreateClientResourceDescription": "創建一個新資源,該資源將可供連接到所選站點的用戶端訪問。", + "createInternalResourceDialogResourceProperties": "資源屬性", + "createInternalResourceDialogName": "名稱", + "createInternalResourceDialogSite": "站點", + "createInternalResourceDialogSelectSite": "選擇站點...", + "createInternalResourceDialogSearchSites": "搜索站點...", + "createInternalResourceDialogNoSitesFound": "未找到站點。", + "createInternalResourceDialogProtocol": "協議", + "createInternalResourceDialogTcp": "TCP", + "createInternalResourceDialogUdp": "UDP", + "createInternalResourceDialogSitePort": "站點埠", + "createInternalResourceDialogSitePortDescription": "使用此埠在連接到用戶端時訪問站點上的資源。", + "createInternalResourceDialogTargetConfiguration": "目標配置", + "createInternalResourceDialogDestinationIPDescription": "站點網路上資源的 IP 或主機名地址。", + "createInternalResourceDialogDestinationPortDescription": "資源在目標 IP 上可訪問的埠。", + "createInternalResourceDialogCancel": "取消", + "createInternalResourceDialogCreateResource": "創建資源", + "createInternalResourceDialogSuccess": "成功", + "createInternalResourceDialogInternalResourceCreatedSuccessfully": "內部資源創建成功", + "createInternalResourceDialogError": "錯誤", + "createInternalResourceDialogFailedToCreateInternalResource": "創建內部資源失敗", + "createInternalResourceDialogNameRequired": "名稱為必填項", + "createInternalResourceDialogNameMaxLength": "名稱長度必須小於 255 個字元", + "createInternalResourceDialogPleaseSelectSite": "請選擇一個站點", + "createInternalResourceDialogProxyPortMin": "代理埠必須至少為 1", + "createInternalResourceDialogProxyPortMax": "代理埠必須小於 65536", + "createInternalResourceDialogInvalidIPAddressFormat": "無效的 IP 位址格式", + "createInternalResourceDialogDestinationPortMin": "目標埠必須至少為 1", + "createInternalResourceDialogDestinationPortMax": "目標埠必須小於 65536", + "siteConfiguration": "配置", + "siteAcceptClientConnections": "接受用戶端連接", + "siteAcceptClientConnectionsDescription": "允許其他設備透過此 Newt 實例使用用戶端作為閘道器連接。", + "siteAddress": "站點地址", + "siteAddressDescription": "指定主機的 IP 位址以供用戶端連接。這是 Pangolin 網路中站點的內部地址,供用戶端訪問。必須在 Org 子網內。", + "autoLoginExternalIdp": "自動使用外部 IDP 登錄", + "autoLoginExternalIdpDescription": "立即將用戶重定向到外部 IDP 進行身份驗證。", + "selectIdp": "選擇 IDP", + "selectIdpPlaceholder": "選擇一個 IDP...", + "selectIdpRequired": "在啟用自動登錄時,請選擇一個 IDP。", + "autoLoginTitle": "重定向中", + "autoLoginDescription": "正在將您重定向到外部身份提供商進行身份驗證。", + "autoLoginProcessing": "準備身份驗證...", + "autoLoginRedirecting": "重定向到登錄...", + "autoLoginError": "自動登錄錯誤", + "autoLoginErrorNoRedirectUrl": "未從身份提供商收到重定向 URL。", + "autoLoginErrorGeneratingUrl": "生成身份驗證 URL 失敗。", + "remoteExitNodeManageRemoteExitNodes": "遠程節點", + "remoteExitNodeDescription": "自我主機一個或多個遠程節點來擴展您的網路連接並減少對雲的依賴性", + "remoteExitNodes": "節點", + "searchRemoteExitNodes": "搜索節點...", + "remoteExitNodeAdd": "添加節點", + "remoteExitNodeErrorDelete": "刪除節點時出錯", + "remoteExitNodeQuestionRemove": "您確定要從組織中刪除該節點嗎?", + "remoteExitNodeMessageRemove": "一旦刪除,該節點將不再能夠訪問。", + "remoteExitNodeConfirmDelete": "確認刪除節點", + "remoteExitNodeDelete": "刪除節點", + "sidebarRemoteExitNodes": "遠程節點", + "remoteExitNodeCreate": { + "title": "創建節點", + "description": "創建一個新節點來擴展您的網路連接", + "viewAllButton": "查看所有節點", + "strategy": { + "title": "創建策略", + "description": "選擇此選項以手動配置您的節點或生成新憑據。", + "adopt": { + "title": "採納節點", + "description": "如果您已經擁有該節點的憑據,請選擇此項。" + }, + "generate": { + "title": "生成金鑰", + "description": "如果您想為節點生成新金鑰,請選擇此選項" + } + }, + "adopt": { + "title": "採納現有節點", + "description": "輸入您想要採用的現有節點的憑據", + "nodeIdLabel": "節點 ID", + "nodeIdDescription": "您想要採用的現有節點的 ID", + "secretLabel": "金鑰", + "secretDescription": "現有節點的秘密金鑰", + "submitButton": "採用節點" + }, + "generate": { + "title": "生成的憑據", + "description": "使用這些生成的憑據來配置您的節點", + "nodeIdTitle": "節點 ID", + "secretTitle": "金鑰", + "saveCredentialsTitle": "將憑據添加到配置中", + "saveCredentialsDescription": "將這些憑據添加到您的自託管 Pangolin 節點設定檔中以完成連接。", + "submitButton": "創建節點" + }, + "validation": { + "adoptRequired": "在通過現有節點時需要節點ID和金鑰" + }, + "errors": { + "loadDefaultsFailed": "無法載入預設值", + "defaultsNotLoaded": "預設值未載入", + "createFailed": "創建節點失敗" + }, + "success": { + "created": "節點創建成功" + } + }, + "remoteExitNodeSelection": "節點選擇", + "remoteExitNodeSelectionDescription": "為此本地站點選擇要路由流量的節點", + "remoteExitNodeRequired": "必須為本地站點選擇節點", + "noRemoteExitNodesAvailable": "無可用節點", + "noRemoteExitNodesAvailableDescription": "此組織沒有可用的節點。首先創建一個節點來使用本地站點。", + "exitNode": "出口節點", + "country": "國家", + "rulesMatchCountry": "當前基於源 IP", + "managedSelfHosted": { + "title": "託管自託管", + "description": "更可靠、維護成本更低的自架 Pangolin 伺服器,並附帶額外的附加功能", + "introTitle": "託管式自架 Pangolin", + "introDescription": "這是一種部署選擇,為那些希望簡潔和額外可靠的人設計,同時仍然保持他們的數據的私密性和自我託管性。", + "introDetail": "通過此選項,您仍然運行您自己的 Pangolin 節點 — — 您的隧道、SSL 終止,並且流量在您的伺服器上保持所有狀態。 不同之處在於,管理和監測是通過我們的雲層儀錶板進行的,該儀錶板開啟了一些好處:", + "benefitSimplerOperations": { + "title": "簡單的操作", + "description": "無需運行您自己的郵件伺服器或設置複雜的警報。您將從方框中獲得健康檢查和下限提醒。" + }, + "benefitAutomaticUpdates": { + "title": "自動更新", + "description": "雲儀錶板快速演化,所以您可以獲得新的功能和錯誤修復,而不必每次手動拉取新的容器。" + }, + "benefitLessMaintenance": { + "title": "減少維護時間", + "description": "沒有要管理的資料庫遷移、備份或額外的基礎設施。我們在雲端處理這個問題。" + }, + "benefitCloudFailover": { + "title": "雲端故障轉移", + "description": "如果您的節點發生故障,您的隧道可以暫時故障轉移到我們的雲端存取點,直到您將節點恢復線上狀態。" + }, + "benefitHighAvailability": { + "title": "高可用率(PoPs)", + "description": "您還可以將多個節點添加到您的帳戶中以獲取冗餘和更好的性能。" + }, + "benefitFutureEnhancements": { + "title": "將來的改進", + "description": "我們正在計劃添加更多的分析、警報和管理工具,使你的部署更加有力。" + }, + "docsAlert": { + "text": "在我們中更多地了解管理下的自託管選項", + "documentation": "文件" + }, + "convertButton": "將此節點轉換為管理自託管的" + }, + "internationaldomaindetected": "檢測到國際域", + "willbestoredas": "儲存為:", + "roleMappingDescription": "確定當用戶啟用自動配送時如何分配他們的角色。", + "selectRole": "選擇角色", + "roleMappingExpression": "表達式", + "selectRolePlaceholder": "選擇角色", + "selectRoleDescription": "選擇一個角色,從此身份提供商分配給所有用戶", + "roleMappingExpressionDescription": "輸入一個 JMESPath 表達式來從 ID 令牌提取角色資訊", + "idpTenantIdRequired": "租戶 ID 是必需的", + "invalidValue": "無效的值", + "idpTypeLabel": "身份提供者類型", + "roleMappingExpressionPlaceholder": "例如: contains(group, 'admin' &'Admin' || 'Member'", + "idpGoogleConfiguration": "Google 配置", + "idpGoogleConfigurationDescription": "配置您的 Google OAuth2 憑據", + "idpGoogleClientIdDescription": "您的 Google OAuth2 用戶端 ID", + "idpGoogleClientSecretDescription": "您的 Google OAuth2 用戶端金鑰", + "idpAzureConfiguration": "Azure Entra ID 配置", + "idpAzureConfigurationDescription": "配置您的 Azure Entra ID OAuth2 憑據", + "idpTenantId": "租戶 ID", + "idpTenantIdPlaceholder": "您的租戶 ID", + "idpAzureTenantIdDescription": "您的 Azure 租戶ID (在 Azure Active Directory 概覽中發現)", + "idpAzureClientIdDescription": "您的 Azure 應用程式註冊用戶端 ID", + "idpAzureClientSecretDescription": "您的 Azure 應用程式註冊用戶端金鑰", + "idpGoogleTitle": "Google", + "idpGoogleAlt": "Google", + "idpAzureTitle": "Azure Entra ID", + "idpAzureAlt": "Azure", + "idpGoogleConfigurationTitle": "Google 配置", + "idpAzureConfigurationTitle": "Azure Entra ID 配置", + "idpTenantIdLabel": "租戶 ID", + "idpAzureClientIdDescription2": "您的 Azure 應用程式註冊用戶端 ID", + "idpAzureClientSecretDescription2": "您的 Azure 應用程式註冊用戶端金鑰", + "idpGoogleDescription": "Google OAuth2/OIDC 提供商", + "idpAzureDescription": "Microsoft Azure OAuth2/OIDC provider", + "subnet": "子網", + "subnetDescription": "此組織網路配置的子網。", + "authPage": "認證頁面", + "authPageDescription": "配置您的組織認證頁面", + "authPageDomain": "認證頁面域", + "noDomainSet": "沒有域設置", + "changeDomain": "更改域", + "selectDomain": "選擇域", + "restartCertificate": "重新啟動證書", + "editAuthPageDomain": "編輯認證頁面域", + "setAuthPageDomain": "設置認證頁面域", + "failedToFetchCertificate": "獲取證書失敗", + "failedToRestartCertificate": "重新啟動證書失敗", + "addDomainToEnableCustomAuthPages": "為您的組織添加域名以啟用自訂認證頁面", + "selectDomainForOrgAuthPage": "選擇組織認證頁面的域", + "domainPickerProvidedDomain": "提供的域", + "domainPickerFreeProvidedDomain": "免費提供的域", + "domainPickerVerified": "已驗證", + "domainPickerUnverified": "未驗證", + "domainPickerInvalidSubdomainStructure": "此子域包含無效的字元或結構。當您保存時,它將被自動清除。", + "domainPickerError": "錯誤", + "domainPickerErrorLoadDomains": "載入組織域名失敗", + "domainPickerErrorCheckAvailability": "檢查域可用性失敗", + "domainPickerInvalidSubdomain": "無效的子域", + "domainPickerInvalidSubdomainRemoved": "輸入 \"{sub}\" 已被移除,因為其無效。", + "domainPickerInvalidSubdomainCannotMakeValid": "\"{sub}\" 無法為 {domain} 變為有效。", + "domainPickerSubdomainSanitized": "子域已淨化", + "domainPickerSubdomainCorrected": "\"{sub}\" 已被更正為 \"{sanitized}\"", + "orgAuthSignInTitle": "登錄到您的組織", + "orgAuthChooseIdpDescription": "選擇您的身份提供商以繼續", + "orgAuthNoIdpConfigured": "此機構沒有配置任何身份提供者。您可以使用您的 Pangolin 身份登錄。", + "orgAuthSignInWithPangolin": "使用 Pangolin 登錄", + "subscriptionRequiredToUse": "需要訂閱才能使用此功能。", + "idpDisabled": "身份提供者已禁用。", + "orgAuthPageDisabled": "組織認證頁面已禁用。", + "domainRestartedDescription": "域驗證重新啟動成功", + "resourceAddEntrypointsEditFile": "編輯文件:config/traefik/traefik_config.yml", + "resourceExposePortsEditFile": "編輯文件:docker-compose.yml", + "emailVerificationRequired": "需要電子郵件驗證。 請通過 {dashboardUrl}/auth/login 再次登錄以完成此步驟。 然後,回到這裡。", + "twoFactorSetupRequired": "需要設置雙因素身份驗證。 請通過 {dashboardUrl}/auth/login 再次登錄以完成此步驟。 然後,回到這裡。", + "additionalSecurityRequired": "需要額外的安全", + "organizationRequiresAdditionalSteps": "這個組織需要額外的安全步驟才能訪問資源。", + "completeTheseSteps": "完成這些步驟", + "enableTwoFactorAuthentication": "啟用兩步驗證", + "completeSecuritySteps": "完成安全步驟", + "securitySettings": "安全設定", + "securitySettingsDescription": "配置您組織的安全策略", + "requireTwoFactorForAllUsers": "所有用戶需要兩步驗證", + "requireTwoFactorDescription": "如果啟用,此組織的所有內部用戶必須啟用雙重身份驗證才能訪問組織。", + "requireTwoFactorDisabledDescription": "此功能需要有效的許可證(企業)或活動訂閱(SaS)", + "requireTwoFactorCannotEnableDescription": "您必須為您的帳戶啟用雙重身份驗證才能對所有用戶", + "maxSessionLength": "最大會話長度", + "maxSessionLengthDescription": "設置用戶會話的最長時間。此後用戶需要重新驗證。", + "maxSessionLengthDisabledDescription": "此功能需要有效的許可證(企業)或活動訂閱(SaS)", + "selectSessionLength": "選擇會話長度", + "unenforced": "未執行", + "1Hour": "1 小時", + "3Hours": "3 小時", + "6Hours": "6 小時", + "12Hours": "12 小時", + "1DaySession": "1天", + "3Days": "3 天", + "7Days": "7 天", + "14Days": "14 天", + "30DaysSession": "30 天", + "90DaysSession": "90 天", + "180DaysSession": "180天", + "passwordExpiryDays": "密碼過期", + "editPasswordExpiryDescription": "設置用戶需要更改密碼之前的天數。", + "selectPasswordExpiry": "選擇密碼過期", + "30Days": "30 天", + "1Day": "1天", + "60Days": "60天", + "90Days": "90 天", + "180Days": "180天", + "1Year": "1 年", + "subscriptionBadge": "需要訂閱", + "securityPolicyChangeWarning": "安全政策更改警告", + "securityPolicyChangeDescription": "您即將更改安全政策設置。保存後,您可能需要重新認證以遵守這些政策更新。 所有不符合要求的用戶也需要重新認證。", + "securityPolicyChangeConfirmMessage": "我確認", + "securityPolicyChangeWarningText": "這將影響組織中的所有用戶", + "authPageErrorUpdateMessage": "更新身份驗證頁面設置時出錯", + "authPageErrorUpdate": "無法更新認證頁面", + "authPageUpdated": "身份驗證頁面更新成功", + "healthCheckNotAvailable": "本地的", + "rewritePath": "重寫路徑", + "rewritePathDescription": "在轉發到目標之前,可以選擇重寫路徑。", + "continueToApplication": "繼續應用", + "checkingInvite": "正在檢查邀請", + "setResourceHeaderAuth": "設置 ResourceHeaderAuth", + "resourceHeaderAuthRemove": "移除 Header 身份驗證", + "resourceHeaderAuthRemoveDescription": "已成功刪除 Header 身份驗證。", + "resourceErrorHeaderAuthRemove": "刪除 Header 身份驗證失敗", + "resourceErrorHeaderAuthRemoveDescription": "無法刪除資源的 Header 身份驗證。", + "resourceHeaderAuthProtectionEnabled": "Header 認證已啟用", + "resourceHeaderAuthProtectionDisabled": "Header 身份驗證已禁用", + "headerAuthRemove": "刪除 Header 認證", + "headerAuthAdd": "添加頁首認證", + "resourceErrorHeaderAuthSetup": "設置頁首認證失敗", + "resourceErrorHeaderAuthSetupDescription": "無法設置資源的 Header 身份驗證。", + "resourceHeaderAuthSetup": "Header 認證設置成功", + "resourceHeaderAuthSetupDescription": "Header 認證已成功設置。", + "resourceHeaderAuthSetupTitle": "設置 Header 身份驗證", + "resourceHeaderAuthSetupTitleDescription": "使用 HTTP 頭身份驗證來設置基本身份驗證資訊(使用者名稱和密碼)。使用 https://username:password@resource.example.com 訪問它", + "resourceHeaderAuthSubmit": "設置 Header 身份驗證", + "actionSetResourceHeaderAuth": "設置 Header 身份驗證", + "enterpriseEdition": "企業版", + "unlicensed": "未授權", + "beta": "測試版", + "manageClients": "管理用戶端", + "manageClientsDescription": "用戶端是可以連接到您的站點的設備", + "licenseTableValidUntil": "有效期至", + "saasLicenseKeysSettingsTitle": "企業許可證", + "saasLicenseKeysSettingsDescription": "為自我託管的 Pangolin 實例生成和管理企業許可證金鑰", + "sidebarEnterpriseLicenses": "許可協議", + "generateLicenseKey": "生成許可證金鑰", + "generateLicenseKeyForm": { + "validation": { + "emailRequired": "請輸入一個有效的電子郵件地址", + "useCaseTypeRequired": "請選擇一個使用的案例類型", + "firstNameRequired": "必填名", + "lastNameRequired": "姓氏是必填項", + "primaryUseRequired": "請描述您的主要使用", + "jobTitleRequiredBusiness": "企業使用必須有職位頭銜。", + "industryRequiredBusiness": "商業使用需要工業", + "stateProvinceRegionRequired": "州/省/地區是必填項", + "postalZipCodeRequired": "郵政編碼是必需的", + "companyNameRequiredBusiness": "企業使用需要公司名稱", + "countryOfResidenceRequiredBusiness": "商業使用必須是居住國", + "countryRequiredPersonal": "國家需要個人使用", + "agreeToTermsRequired": "您必須同意條款", + "complianceConfirmationRequired": "您必須確認遵守 Fossorial Commercial License" + }, + "useCaseOptions": { + "personal": { + "title": "個人使用", + "description": "個人非商業用途,如學習、個人項目或實驗。" + }, + "business": { + "title": "商業使用", + "description": "供組織、公司或商業或創收活動使用。" + } + }, + "steps": { + "emailLicenseType": { + "title": "電子郵件和許可證類型", + "description": "輸入您的電子郵件並選擇您的許可證類型" + }, + "personalInformation": { + "title": "個人資訊", + "description": "告訴我們自己的資訊" + }, + "contactInformation": { + "title": "聯繫資訊", + "description": "您的聯繫資訊" + }, + "termsGenerate": { + "title": "條款並生成", + "description": "審閱並接受條款生成您的許可證" + } + }, + "alerts": { + "commercialUseDisclosure": { + "title": "使用情況披露", + "description": "選擇能準確反映您預定用途的許可等級。 個人許可證允許對個人、非商業性或小型商業活動免費使用軟體,年收入毛額不到 100,000 美元。 超出這些限度的任何用途,包括在企業、組織內的用途。 或其他創收環境——需要有效的企業許可證和支付適用的許可證費用。 所有用戶,不論是個人還是企業,都必須遵守寄養商業許可證條款。" + }, + "trialPeriodInformation": { + "title": "試用期資訊", + "description": "此許可證金鑰使企業特性能夠持續 7 天的評價。 在評估期過後繼續訪問付費功能需要在有效的個人或企業許可證下啟用。對於企業許可證,請聯絡 Sales@pangolin.net。" + } + }, + "form": { + "useCaseQuestion": "您是否正在使用 Pangolin 進行個人或商業使用?", + "firstName": "名字", + "lastName": "名字", + "jobTitle": "工作頭銜:", + "primaryUseQuestion": "您主要計劃使用 Pangolin 嗎?", + "industryQuestion": "您的行業是什麼?", + "prospectiveUsersQuestion": "您期望有多少預期用戶?", + "prospectiveSitesQuestion": "您期望有多少站點(隧道)?", + "companyName": "公司名稱", + "countryOfResidence": "居住國", + "stateProvinceRegion": "州/省/地區", + "postalZipCode": "郵政編碼", + "companyWebsite": "公司網站", + "companyPhoneNumber": "公司電話號碼", + "country": "國家", + "phoneNumberOptional": "電話號碼 (可選)", + "complianceConfirmation": "我確認我提供的資料是準確的,我遵守了寄養商業許可證。 報告不準確的資訊或錯誤的產品使用是違反許可證的行為,可能導致您的金鑰被撤銷。" + }, + "buttons": { + "close": "關閉", + "previous": "上一個", + "next": "下一個", + "generateLicenseKey": "生成許可證金鑰" + }, + "toasts": { + "success": { + "title": "許可證金鑰生成成功", + "description": "您的許可證金鑰已經生成並準備使用。" + }, + "error": { + "title": "生成許可證金鑰失敗", + "description": "生成許可證金鑰時出錯。" + } + } + }, + "priority": "優先權", + "priorityDescription": "先評估更高優先度線路。優先度 = 100 意味著自動排序(系統決定). 使用另一個數字強制執行手動優先度。", + "instanceName": "實例名稱", + "pathMatchModalTitle": "配置路徑匹配", + "pathMatchModalDescription": "根據傳入請求的路徑設置匹配方式。", + "pathMatchType": "匹配類型", + "pathMatchPrefix": "前綴", + "pathMatchExact": "精準的", + "pathMatchRegex": "正則表達式", + "pathMatchValue": "路徑值", + "clear": "清空", + "saveChanges": "保存更改", + "pathMatchRegexPlaceholder": "^/api/.*", + "pathMatchDefaultPlaceholder": "/路徑", + "pathMatchPrefixHelp": "範例: /api 匹配/api, /api/users 等。", + "pathMatchExactHelp": "範例:/api 匹配僅限/api", + "pathMatchRegexHelp": "例如:^/api/.* 匹配/api/why", + "pathRewriteModalTitle": "配置路徑重寫", + "pathRewriteModalDescription": "在轉發到目標之前變換匹配的路徑。", + "pathRewriteType": "重寫類型", + "pathRewritePrefixOption": "前綴 - 替換前綴", + "pathRewriteExactOption": "精確-替換整個路徑", + "pathRewriteRegexOption": "正則表達式 - 替換模式", + "pathRewriteStripPrefixOption": "刪除前綴 - 刪除前綴", + "pathRewriteValue": "重寫值", + "pathRewriteRegexPlaceholder": "/new/$1", + "pathRewriteDefaultPlaceholder": "/new-path", + "pathRewritePrefixHelp": "用此值替換匹配的前綴", + "pathRewriteExactHelp": "當路徑匹配時用此值替換整個路徑", + "pathRewriteRegexHelp": "使用抓取組,如$1,$2來替換", + "pathRewriteStripPrefixHelp": "留空以脫離前綴或提供新的前綴", + "pathRewritePrefix": "前綴", + "pathRewriteExact": "精準的", + "pathRewriteRegex": "正則表達式", + "pathRewriteStrip": "帶狀圖", + "pathRewriteStripLabel": "條形圖", + "sidebarEnableEnterpriseLicense": "啟用企業許可證", + "cannotbeUndone": "無法撤消。", + "toConfirm": "確認", + "deleteClientQuestion": "您確定要從站點和組織中刪除客戶嗎?", + "clientMessageRemove": "一旦刪除,用戶端將無法連接到站點。", + "sidebarLogs": "日誌", + "request": "請求", + "logs": "日誌", + "logsSettingsDescription": "監視從此 orginization 中收集的日誌", + "searchLogs": "搜索日誌...", + "action": "行動", + "actor": "執行者", + "timestamp": "時間戳", + "accessLogs": "訪問日誌", + "exportCsv": "導出 CSV", + "actorId": "執行者 ID", + "allowedByRule": "根據規則允許", + "allowedNoAuth": "無認證", + "validAccessToken": "有效訪問令牌", + "validHeaderAuth": "有效的 Header 身份驗證", + "validPincode": "有效的 Pincode", + "validPassword": "有效密碼", + "validEmail": "有效的 email", + "validSSO": "有效的 SSO", + "resourceBlocked": "資源被阻止", + "droppedByRule": "被規則刪除", + "noSessions": "無會話", + "temporaryRequestToken": "臨時請求令牌", + "noMoreAuthMethods": "無有效授權", + "ip": "IP", + "reason": "原因", + "requestLogs": "請求日誌", + "host": "主機", + "location": "地點", + "actionLogs": "操作日誌", + "sidebarLogsRequest": "請求日誌", + "sidebarLogsAccess": "訪問日誌", + "sidebarLogsAction": "操作日誌", + "logRetention": "日誌保留", + "logRetentionDescription": "管理不同類型的日誌為這個機構保留多長時間或禁用這些日誌", + "requestLogsDescription": "查看此機構資源的詳細請求日誌", + "logRetentionRequestLabel": "請求日誌保留", + "logRetentionRequestDescription": "保留請求日誌的時間", + "logRetentionAccessLabel": "訪問日誌保留", + "logRetentionAccessDescription": "保留訪問日誌的時間", + "logRetentionActionLabel": "動作日誌保留", + "logRetentionActionDescription": "保留操作日誌的時間", + "logRetentionDisabled": "已禁用", + "logRetention3Days": "3 天", + "logRetention7Days": "7 天", + "logRetention14Days": "14 天", + "logRetention30Days": "30 天", + "logRetention90Days": "90 天", + "logRetentionForever": "永遠的", + "actionLogsDescription": "查看此機構執行的操作歷史", + "accessLogsDescription": "查看此機構資源的訪問認證請求", + "licenseRequiredToUse": "需要企業許可證才能使用此功能。", + "certResolver": "證書解決器", + "certResolverDescription": "選擇用於此資源的證書解析器。", + "selectCertResolver": "選擇證書解析", + "enterCustomResolver": "輸入自訂解析器", + "preferWildcardCert": "喜歡通配符證書", + "unverified": "未驗證", + "domainSetting": "域設置", + "domainSettingDescription": "配置您的域的設置", + "preferWildcardCertDescription": "嘗試生成通配符證書(需要正確配置的證書解析器)。", + "recordName": "記錄名稱", + "auto": "自動操作", + "TTL": "TTL", + "howToAddRecords": "如何添加記錄", + "dnsRecord": "DNS 記錄", + "required": "必填", + "domainSettingsUpdated": "域設置更新成功", + "orgOrDomainIdMissing": "缺少機構或域 ID", + "loadingDNSRecords": "正在載入 DNS 記錄...", + "olmUpdateAvailableInfo": "有最新版本的 Olm 可用。請更新到最新版本以獲取最佳體驗。", + "client": "用戶端:", + "proxyProtocol": "代理協議設置", + "proxyProtocolDescription": "配置代理協議以保留 TCP/UDP 服務的用戶端 IP 位址。", + "enableProxyProtocol": "啟用代理協議", + "proxyProtocolInfo": "為 TCP/UDP 後端保留用戶端 IP 位址", + "proxyProtocolVersion": "代理協議版本", + "version1": " 版本 1 (推薦)", + "version2": "版本 2", + "versionDescription": "版本 1 是基於文本和廣泛支持的版本。版本 2 是二進制和更有效率但不那麼相容。", + "warning": "警告", + "proxyProtocolWarning": "您的後端應用程式必須配置為接受代理協議連接。如果您的後端不支持代理協議,啟用這將會中斷所有連接。 請務必從 Traefik 配置您的後端到信任代理協議標題。", + "restarting": "正在重啟...", + "manual": "手動模式", + "messageSupport": "消息支持", + "supportNotAvailableTitle": "支持不可用", + "supportNotAvailableDescription": "支持現在不可用。您可以發送電子郵件到 support@pangolin.net。", + "supportRequestSentTitle": "支持請求已發送", + "supportRequestSentDescription": "您的消息已成功發送。", + "supportRequestFailedTitle": "發送請求失敗", + "supportRequestFailedDescription": "發送您的支持請求時出錯。", + "supportSubjectRequired": "主題是必填項", + "supportSubjectMaxLength": "主題必須是 255 個或更少的字元", + "supportMessageRequired": "消息是必填項", + "supportReplyTo": "回復給", + "supportSubject": "議題", + "supportSubjectPlaceholder": "輸入主題", + "supportMessage": "留言", + "supportMessagePlaceholder": "輸入您的消息", + "supportSending": "正在發送...", + "supportSend": "發送", + "supportMessageSent": "消息已發送!", + "supportWillContact": "我們很快就會聯繫起來!", + "selectLogRetention": "選擇保留日誌", + "showColumns": "顯示列", + "hideColumns": "隱藏列", + "columnVisibility": "列可見性", + "toggleColumn": "切換 {columnName} 列", + "allColumns": "全部列", + "defaultColumns": "默認列", + "customizeView": "自訂視圖", + "viewOptions": "查看選項", + "selectAll": "選擇所有", + "selectNone": "沒有選擇", + "selectedResources": "選定的資源", + "enableSelected": "啟用選中的", + "disableSelected": "禁用選中的", + "checkSelectedStatus": "檢查選中的狀態" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a7bbfd4e..919730b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "eslint-config-next": "16.0.3", "express": "5.1.0", "express-rate-limit": "8.2.1", - "glob": "11.0.3", + "glob": "11.1.0", "helmet": "8.1.0", "http-errors": "2.0.0", "i": "^0.3.7", @@ -82,7 +82,7 @@ "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.10", - "npm": "^11.6.2", + "npm": "^11.6.4", "nprogress": "^0.2.0", "oslo": "1.2.1", "pg": "^8.16.2", @@ -2315,6 +2315,7 @@ "@noble/ciphers": "^1.0.0" } }, +<<<<<<< HEAD "node_modules/@emnapi/core": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", @@ -2346,6 +2347,8 @@ "tslib": "^2.4.0" } }, +======= +>>>>>>> dev "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -3611,154 +3614,6 @@ "node": ">=18" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", - "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", - "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.3" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", - "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", - "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", - "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", - "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", - "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", - "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", @@ -3776,132 +3631,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", - "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", - "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", - "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", - "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", - "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.3" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", - "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.3" - } - }, "node_modules/@img/sharp-linux-x64": { "version": "0.34.4", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", @@ -3925,132 +3654,6 @@ "@img/sharp-libvips-linux-x64": "1.2.3" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", - "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", - "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.3" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", - "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.5.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", - "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", - "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", - "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@ioredis/commands": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", @@ -4201,18 +3804,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@next/env": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz", @@ -4228,6 +3819,7 @@ "fast-glob": "3.3.1" } }, +<<<<<<< HEAD "node_modules/@next/swc-darwin-arm64": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", @@ -4292,6 +3884,8 @@ "node": ">= 10" } }, +======= +>>>>>>> dev "node_modules/@next/swc-linux-x64-gnu": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", @@ -4308,6 +3902,7 @@ "node": ">= 10" } }, +<<<<<<< HEAD "node_modules/@next/swc-linux-x64-musl": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", @@ -4356,6 +3951,8 @@ "node": ">= 10" } }, +======= +>>>>>>> dev "node_modules/@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", @@ -4423,134 +4020,6 @@ "@node-rs/argon2-win32-x64-msvc": "2.0.2" } }, - "node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-2.0.2.tgz", - "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-android-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-2.0.2.tgz", - "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-arm64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-2.0.2.tgz", - "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-darwin-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-2.0.2.tgz", - "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-freebsd-x64": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-2.0.2.tgz", - "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-2.0.2.tgz", - "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-2.0.2.tgz", - "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-2.0.2.tgz", - "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-2.0.2.tgz", @@ -4567,86 +4036,6 @@ "node": ">= 10" } }, - "node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-2.0.2.tgz", - "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-2.0.2.tgz", - "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-2.0.2.tgz", - "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-2.0.2.tgz", - "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-2.0.2.tgz", - "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/bcrypt": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz", @@ -4676,134 +4065,6 @@ "@node-rs/bcrypt-win32-x64-msvc": "1.9.0" } }, - "node_modules/@node-rs/bcrypt-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz", - "integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz", - "integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@node-rs/bcrypt-linux-x64-gnu": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz", @@ -4820,119 +4081,6 @@ "node": ">= 10" } }, - "node_modules/@node-rs/bcrypt-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", - "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@node-rs/bcrypt-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-rs/bcrypt-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -8301,74 +7449,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-arm64": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", - "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-darwin-x64": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", - "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", - "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", - "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-gnu": { "version": "15.5.2", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", @@ -8386,57 +7466,6 @@ "node": ">= 10" } }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", - "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", - "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-email/preview-server/node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", - "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@react-email/preview-server/node_modules/@radix-ui/react-slot": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", @@ -9754,6 +8783,7 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, +<<<<<<< HEAD "node_modules/@swc/core": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.3.tgz", @@ -9872,6 +8902,8 @@ "node": ">=10" } }, +======= +>>>>>>> dev "node_modules/@swc/core-linux-x64-gnu": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.3.tgz", @@ -9888,6 +8920,7 @@ "node": ">=10" } }, +<<<<<<< HEAD "node_modules/@swc/core-linux-x64-musl": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.3.tgz", @@ -9952,6 +8985,8 @@ "node": ">=10" } }, +======= +>>>>>>> dev "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -10028,125 +9063,6 @@ "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", - "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", - "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", - "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", - "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", - "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", - "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { "version": "4.1.17", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", @@ -10164,6 +9080,7 @@ "node": ">= 10" } }, +<<<<<<< HEAD "node_modules/@tailwindcss/oxide-linux-x64-musl": { "version": "4.1.17", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", @@ -10245,6 +9162,8 @@ "node": ">= 10" } }, +======= +>>>>>>> dev "node_modules/@tailwindcss/postcss": { "version": "4.1.17", "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", @@ -10374,16 +9293,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/better-sqlite3": { "version": "7.6.12", "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.12.tgz", @@ -11302,175 +10211,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", @@ -11484,74 +10224,6 @@ "linux" ] }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -12344,6 +11016,7 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/express" +<<<<<<< HEAD } }, "node_modules/body-parser/node_modules/iconv-lite": { @@ -12360,6 +11033,8 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/express" +======= +>>>>>>> dev } }, "node_modules/boolbase": { @@ -13265,6 +11940,18 @@ "node": ">= 10" } }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/d3-ease": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", @@ -16102,13 +14789,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, - "node_modules/fs-monkey": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", - "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", - "license": "Unlicense", - "optional": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -16296,14 +14976,14 @@ "license": "MIT" }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -16623,15 +15303,19 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ieee754": { @@ -17557,153 +16241,6 @@ "lightningcss-win32-x64-msvc": "1.30.2" } }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", @@ -17725,69 +16262,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -18018,29 +16492,6 @@ "node": ">= 0.8" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memfs-browser": { - "version": "3.5.10302", - "resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz", - "integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==", - "license": "Unlicense", - "optional": true, - "dependencies": { - "memfs": "3.5.3" - } - }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -18451,6 +16902,58 @@ "integrity": "sha512-hscCKUv+5GQ0CCNbvqZ8gaxnAGToCgDTbL++jgCq8SCk/ljtZDEeQZcMk46Nm6Ynn49Q/JKF4Npo/Sq1mpbusA==", "license": "MIT" }, +<<<<<<< HEAD +======= + "node_modules/next-intl/node_modules/@swc/core": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.3.tgz", + "integrity": "sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.3", + "@swc/core-darwin-x64": "1.15.3", + "@swc/core-linux-arm-gnueabihf": "1.15.3", + "@swc/core-linux-arm64-gnu": "1.15.3", + "@swc/core-linux-arm64-musl": "1.15.3", + "@swc/core-linux-x64-gnu": "1.15.3", + "@swc/core-linux-x64-musl": "1.15.3", + "@swc/core-win32-arm64-msvc": "1.15.3", + "@swc/core-win32-ia32-msvc": "1.15.3", + "@swc/core-win32-x64-msvc": "1.15.3" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/next-intl/node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, +>>>>>>> dev "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -20921,26 +19424,6 @@ "@node-rs/bcrypt": "1.9.0" } }, - "node_modules/oslo/node_modules/@emnapi/core": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", - "integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/oslo/node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz", @@ -20966,134 +19449,6 @@ "@node-rs/argon2-win32-x64-msvc": "1.7.0" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz", - "integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz", - "integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz", - "integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz", @@ -21110,99 +19465,6 @@ "node": ">= 10" } }, - "node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^0.45.0", - "@emnapi/runtime": "^0.45.0", - "@tybys/wasm-util": "^0.8.1", - "memfs-browser": "^3.4.13000" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/oslo/node_modules/@tybys/wasm-util": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz", - "integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -21978,6 +20240,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", +<<<<<<< HEAD "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -21998,12 +20261,18 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", +======= +>>>>>>> dev "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" }, "funding": { "type": "opencollective", diff --git a/package.json b/package.json index 85218c9d..41ed801d 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "eslint-config-next": "16.0.3", "express": "5.1.0", "express-rate-limit": "8.2.1", - "glob": "11.0.3", + "glob": "11.1.0", "helmet": "8.1.0", "http-errors": "2.0.0", "i": "^0.3.7", @@ -105,7 +105,7 @@ "node-cache": "5.1.2", "node-fetch": "3.3.2", "nodemailer": "7.0.10", - "npm": "^11.6.2", + "npm": "^11.6.4", "nprogress": "^0.2.0", "oslo": "1.2.1", "pg": "^8.16.2", diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index b46e2844..f59f50a4 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -176,7 +176,8 @@ export const targetHealthCheck = pgTable("targetHealthCheck", { hcFollowRedirects: boolean("hcFollowRedirects").default(true), hcMethod: varchar("hcMethod").default("GET"), hcStatus: integer("hcStatus"), // http code - hcHealth: text("hcHealth").default("unknown") // "unknown", "healthy", "unhealthy" + hcHealth: text("hcHealth").default("unknown"), // "unknown", "healthy", "unhealthy" + hcTlsServerName: text("hcTlsServerName"), }); export const exitNodes = pgTable("exitNodes", { diff --git a/server/db/sqlite/schema/schema.ts b/server/db/sqlite/schema/schema.ts index 36867257..e9500141 100644 --- a/server/db/sqlite/schema/schema.ts +++ b/server/db/sqlite/schema/schema.ts @@ -195,7 +195,8 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", { }).default(true), hcMethod: text("hcMethod").default("GET"), hcStatus: integer("hcStatus"), // http code - hcHealth: text("hcHealth").default("unknown") // "unknown", "healthy", "unhealthy" + hcHealth: text("hcHealth").default("unknown"), // "unknown", "healthy", "unhealthy" + hcTlsServerName: text("hcTlsServerName"), }); export const exitNodes = sqliteTable("exitNodes", { diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index 57c3bfe9..9761f57d 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -221,6 +221,7 @@ export async function updateProxyResources( domainId: domain ? domain.domainId : null, enabled: resourceEnabled, sso: resourceData.auth?.["sso-enabled"] || false, + skipToIdpId: resourceData.auth?.["auto-login-idp"] || null, ssl: resourceSsl, setHostHeader: resourceData["host-header"] || null, tlsServerName: resourceData["tls-server-name"] || null, @@ -610,6 +611,7 @@ export async function updateProxyResources( domainId: domain ? domain.domainId : null, enabled: resourceEnabled, sso: resourceData.auth?.["sso-enabled"] || false, + skipToIdpId: resourceData.auth?.["auto-login-idp"] || null, setHostHeader: resourceData["host-header"] || null, tlsServerName: resourceData["tls-server-name"] || null, ssl: resourceSsl, diff --git a/server/lib/blueprints/types.ts b/server/lib/blueprints/types.ts index a5ee5700..75a0ae17 100644 --- a/server/lib/blueprints/types.ts +++ b/server/lib/blueprints/types.ts @@ -59,6 +59,7 @@ export const AuthSchema = z.object({ }), "sso-users": z.array(z.email()).optional().default([]), "whitelist-users": z.array(z.email()).optional().default([]), + "auto-login-idp": z.int().positive().optional(), }); export const RuleSchema = z.object({ diff --git a/server/lib/consts.ts b/server/lib/consts.ts index c2cf6698..b014f849 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.12.1"; +export const APP_VERSION = "1.12.3"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/lib/lock.ts b/server/lib/lock.ts new file mode 100644 index 00000000..7eea8908 --- /dev/null +++ b/server/lib/lock.ts @@ -0,0 +1,111 @@ +export class LockManager { + /** + * Acquire a distributed lock using Redis SET with NX and PX options + * @param lockKey - Unique identifier for the lock + * @param ttlMs - Time to live in milliseconds + * @returns Promise - true if lock acquired, false otherwise + */ + async acquireLock( + lockKey: string, + ttlMs: number = 30000 + ): Promise { + return true; + } + + /** + * Release a lock using Lua script to ensure atomicity + * @param lockKey - Unique identifier for the lock + */ + async releaseLock(lockKey: string): Promise {} + + /** + * Force release a lock regardless of owner (use with caution) + * @param lockKey - Unique identifier for the lock + */ + async forceReleaseLock(lockKey: string): Promise {} + + /** + * Check if a lock exists and get its info + * @param lockKey - Unique identifier for the lock + * @returns Promise<{exists: boolean, ownedByMe: boolean, ttl: number}> + */ + async getLockInfo(lockKey: string): Promise<{ + exists: boolean; + ownedByMe: boolean; + ttl: number; + owner?: string; + }> { + return { exists: true, ownedByMe: true, ttl: 0 }; + } + + /** + * Extend the TTL of an existing lock owned by this worker + * @param lockKey - Unique identifier for the lock + * @param ttlMs - New TTL in milliseconds + * @returns Promise - true if extended successfully + */ + async extendLock(lockKey: string, ttlMs: number): Promise { + return true; + } + + /** + * Attempt to acquire lock with retries and exponential backoff + * @param lockKey - Unique identifier for the lock + * @param ttlMs - Time to live in milliseconds + * @param maxRetries - Maximum number of retry attempts + * @param baseDelayMs - Base delay between retries in milliseconds + * @returns Promise - true if lock acquired + */ + async acquireLockWithRetry( + lockKey: string, + ttlMs: number = 30000, + maxRetries: number = 5, + baseDelayMs: number = 100 + ): Promise { + return true; + } + + /** + * Execute a function while holding a lock + * @param lockKey - Unique identifier for the lock + * @param fn - Function to execute while holding the lock + * @param ttlMs - Lock TTL in milliseconds + * @returns Promise - Result of the executed function + */ + async withLock( + lockKey: string, + fn: () => Promise, + ttlMs: number = 30000 + ): Promise { + const acquired = await this.acquireLock(lockKey, ttlMs); + + if (!acquired) { + throw new Error(`Failed to acquire lock: ${lockKey}`); + } + + try { + return await fn(); + } finally { + await this.releaseLock(lockKey); + } + } + + /** + * Clean up expired locks - Redis handles this automatically, but this method + * can be used to get statistics about locks + * @returns Promise<{activeLocksCount: number, locksOwnedByMe: number}> + */ + async getLockStatistics(): Promise<{ + activeLocksCount: number; + locksOwnedByMe: number; + }> { + return { activeLocksCount: 0, locksOwnedByMe: 0 }; + } + + /** + * Close the Redis connection + */ + async disconnect(): Promise {} +} + +export const lockManager = new LockManager(); diff --git a/server/private/lib/lock.ts b/server/private/lib/lock.ts new file mode 100644 index 00000000..4a12063b --- /dev/null +++ b/server/private/lib/lock.ts @@ -0,0 +1,363 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { config } from "@server/lib/config"; +import logger from "@server/logger"; +import { redis } from "#private/lib/redis"; + +export class LockManager { + /** + * Acquire a distributed lock using Redis SET with NX and PX options + * @param lockKey - Unique identifier for the lock + * @param ttlMs - Time to live in milliseconds + * @returns Promise - true if lock acquired, false otherwise + */ + async acquireLock( + lockKey: string, + ttlMs: number = 30000 + ): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return true; + } + + const lockValue = `${ + config.getRawConfig().gerbil.exit_node_name + }:${Date.now()}`; + const redisKey = `lock:${lockKey}`; + + try { + // Use SET with NX (only set if not exists) and PX (expire in milliseconds) + // This is atomic and handles both setting and expiration + const result = await redis.set( + redisKey, + lockValue, + "PX", + ttlMs, + "NX" + ); + + if (result === "OK") { + logger.debug( + `Lock acquired: ${lockKey} by ${ + config.getRawConfig().gerbil.exit_node_name + }` + ); + return true; + } + + // Check if the existing lock is from this worker (reentrant behavior) + const existingValue = await redis.get(redisKey); + if ( + existingValue && + existingValue.startsWith( + `${config.getRawConfig().gerbil.exit_node_name}:` + ) + ) { + // Extend the lock TTL since it's the same worker + await redis.pexpire(redisKey, ttlMs); + logger.debug( + `Lock extended: ${lockKey} by ${ + config.getRawConfig().gerbil.exit_node_name + }` + ); + return true; + } + + return false; + } catch (error) { + logger.error(`Failed to acquire lock ${lockKey}:`, error); + return false; + } + } + + /** + * Release a lock using Lua script to ensure atomicity + * @param lockKey - Unique identifier for the lock + */ + async releaseLock(lockKey: string): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return; + } + + const redisKey = `lock:${lockKey}`; + + // Lua script to ensure we only delete the lock if it belongs to this worker + const luaScript = ` + local key = KEYS[1] + local worker_prefix = ARGV[1] + local current_value = redis.call('GET', key) + + if current_value and string.find(current_value, worker_prefix, 1, true) == 1 then + return redis.call('DEL', key) + else + return 0 + end + `; + + try { + const result = (await redis.eval( + luaScript, + 1, + redisKey, + `${config.getRawConfig().gerbil.exit_node_name}:` + )) as number; + + if (result === 1) { + logger.debug( + `Lock released: ${lockKey} by ${ + config.getRawConfig().gerbil.exit_node_name + }` + ); + } else { + logger.warn( + `Lock not released - not owned by worker: ${lockKey} by ${ + config.getRawConfig().gerbil.exit_node_name + }` + ); + } + } catch (error) { + logger.error(`Failed to release lock ${lockKey}:`, error); + } + } + + /** + * Force release a lock regardless of owner (use with caution) + * @param lockKey - Unique identifier for the lock + */ + async forceReleaseLock(lockKey: string): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return; + } + + const redisKey = `lock:${lockKey}`; + + try { + const result = await redis.del(redisKey); + if (result === 1) { + logger.debug(`Lock force released: ${lockKey}`); + } + } catch (error) { + logger.error(`Failed to force release lock ${lockKey}:`, error); + } + } + + /** + * Check if a lock exists and get its info + * @param lockKey - Unique identifier for the lock + * @returns Promise<{exists: boolean, ownedByMe: boolean, ttl: number}> + */ + async getLockInfo(lockKey: string): Promise<{ + exists: boolean; + ownedByMe: boolean; + ttl: number; + owner?: string; + }> { + if (!redis || !redis.status || redis.status !== "ready") { + return { exists: false, ownedByMe: true, ttl: 0 }; + } + + const redisKey = `lock:${lockKey}`; + + try { + const [value, ttl] = await Promise.all([ + redis.get(redisKey), + redis.pttl(redisKey) + ]); + + const exists = value !== null; + const ownedByMe = + exists && + value!.startsWith(`${config.getRawConfig().gerbil.exit_node_name}:`); + const owner = exists ? value!.split(":")[0] : undefined; + + return { + exists, + ownedByMe, + ttl: ttl > 0 ? ttl : 0, + owner + }; + } catch (error) { + logger.error(`Failed to get lock info ${lockKey}:`, error); + return { exists: false, ownedByMe: false, ttl: 0 }; + } + } + + /** + * Extend the TTL of an existing lock owned by this worker + * @param lockKey - Unique identifier for the lock + * @param ttlMs - New TTL in milliseconds + * @returns Promise - true if extended successfully + */ + async extendLock(lockKey: string, ttlMs: number): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return true; + } + + const redisKey = `lock:${lockKey}`; + + // Lua script to extend TTL only if lock is owned by this worker + const luaScript = ` + local key = KEYS[1] + local worker_prefix = ARGV[1] + local ttl = tonumber(ARGV[2]) + local current_value = redis.call('GET', key) + + if current_value and string.find(current_value, worker_prefix, 1, true) == 1 then + return redis.call('PEXPIRE', key, ttl) + else + return 0 + end + `; + + try { + const result = (await redis.eval( + luaScript, + 1, + redisKey, + `${config.getRawConfig().gerbil.exit_node_name}:`, + ttlMs.toString() + )) as number; + + if (result === 1) { + logger.debug( + `Lock extended: ${lockKey} by ${ + config.getRawConfig().gerbil.exit_node_name + } for ${ttlMs}ms` + ); + return true; + } + return false; + } catch (error) { + logger.error(`Failed to extend lock ${lockKey}:`, error); + return false; + } + } + + /** + * Attempt to acquire lock with retries and exponential backoff + * @param lockKey - Unique identifier for the lock + * @param ttlMs - Time to live in milliseconds + * @param maxRetries - Maximum number of retry attempts + * @param baseDelayMs - Base delay between retries in milliseconds + * @returns Promise - true if lock acquired + */ + async acquireLockWithRetry( + lockKey: string, + ttlMs: number = 30000, + maxRetries: number = 5, + baseDelayMs: number = 100 + ): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return true; + } + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + const acquired = await this.acquireLock(lockKey, ttlMs); + + if (acquired) { + return true; + } + + if (attempt < maxRetries) { + // Exponential backoff with jitter + const delay = + baseDelayMs * Math.pow(2, attempt) + Math.random() * 100; + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + + logger.warn( + `Failed to acquire lock ${lockKey} after ${maxRetries + 1} attempts` + ); + return false; + } + + /** + * Execute a function while holding a lock + * @param lockKey - Unique identifier for the lock + * @param fn - Function to execute while holding the lock + * @param ttlMs - Lock TTL in milliseconds + * @returns Promise - Result of the executed function + */ + async withLock( + lockKey: string, + fn: () => Promise, + ttlMs: number = 30000 + ): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return await fn(); + } + + const acquired = await this.acquireLock(lockKey, ttlMs); + + if (!acquired) { + throw new Error(`Failed to acquire lock: ${lockKey}`); + } + + try { + return await fn(); + } finally { + await this.releaseLock(lockKey); + } + } + + /** + * Clean up expired locks - Redis handles this automatically, but this method + * can be used to get statistics about locks + * @returns Promise<{activeLocksCount: number, locksOwnedByMe: number}> + */ + async getLockStatistics(): Promise<{ + activeLocksCount: number; + locksOwnedByMe: number; + }> { + if (!redis || !redis.status || redis.status !== "ready") { + return { activeLocksCount: 0, locksOwnedByMe: 0 }; + } + + try { + const keys = await redis.keys("lock:*"); + let locksOwnedByMe = 0; + + if (keys.length > 0) { + const values = await redis.mget(...keys); + locksOwnedByMe = values.filter( + (value) => + value && + value.startsWith( + `${config.getRawConfig().gerbil.exit_node_name}:` + ) + ).length; + } + + return { + activeLocksCount: keys.length, + locksOwnedByMe + }; + } catch (error) { + logger.error("Failed to get lock statistics:", error); + return { activeLocksCount: 0, locksOwnedByMe: 0 }; + } + } + + /** + * Close the Redis connection + */ + async disconnect(): Promise { + if (!redis || !redis.status || redis.status !== "ready") { + return; + } + await redis.quit(); + } +} + +export const lockManager = new LockManager(); diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index 24416c5b..a61f37b2 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -1743,7 +1743,12 @@ hybridRouter.post( tls: logEntry.tls })); - await db.insert(requestAuditLog).values(logEntries); + // batch them into inserts of 100 to avoid exceeding parameter limits + const batchSize = 100; + for (let i = 0; i < logEntries.length; i += batchSize) { + const batch = logEntries.slice(i, i + batchSize); + await db.insert(requestAuditLog).values(batch); + } return response(res, { data: null, diff --git a/server/routers/newt/handleNewtRegisterMessage.ts b/server/routers/newt/handleNewtRegisterMessage.ts index 372f3677..f4d963a1 100644 --- a/server/routers/newt/handleNewtRegisterMessage.ts +++ b/server/routers/newt/handleNewtRegisterMessage.ts @@ -1,8 +1,8 @@ -import { db, exitNodeOrgs, newts } from "@server/db"; +import { db, ExitNode, exitNodeOrgs, newts, Transaction } from "@server/db"; import { MessageHandler } from "@server/routers/ws"; import { exitNodes, Newt, resources, sites, Target, targets } from "@server/db"; import { targetHealthCheck } from "@server/db"; -import { eq, and, sql, inArray } from "drizzle-orm"; +import { eq, and, sql, inArray, ne } from "drizzle-orm"; import { addPeer, deletePeer } from "../gerbil/peers"; import logger from "@server/logger"; import config from "@server/lib/config"; @@ -17,6 +17,7 @@ import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes"; import { fetchContainers } from "./dockerSocket"; +import { lockManager } from "#dynamic/lib/lock"; export type ExitNodePingResult = { exitNodeId: number; @@ -151,27 +152,8 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { return; } - const sitesQuery = await db - .select({ - subnet: sites.subnet - }) - .from(sites) - .where(eq(sites.exitNodeId, exitNodeId)); + const newSubnet = await getUniqueSubnetForSite(exitNode); - const blockSize = config.getRawConfig().gerbil.site_block_size; - const subnets = sitesQuery - .map((site) => site.subnet) - .filter( - (subnet) => - subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) - ) - .filter((subnet) => subnet !== null); - subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); - const newSubnet = findNextAvailableCidr( - subnets, - blockSize, - exitNode.address - ); if (!newSubnet) { logger.error( `No available subnets found for the new exit node id ${exitNodeId} and site id ${siteId}` @@ -272,7 +254,8 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { hcUnhealthyInterval: targetHealthCheck.hcUnhealthyInterval, hcTimeout: targetHealthCheck.hcTimeout, hcHeaders: targetHealthCheck.hcHeaders, - hcMethod: targetHealthCheck.hcMethod + hcMethod: targetHealthCheck.hcMethod, + hcTlsServerName: targetHealthCheck.hcTlsServerName, }) .from(targets) .innerJoin(resources, eq(targets.resourceId, resources.resourceId)) @@ -344,7 +327,8 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { hcUnhealthyInterval: target.hcUnhealthyInterval, // in seconds hcTimeout: target.hcTimeout, // in seconds hcHeaders: hcHeadersSend, - hcMethod: target.hcMethod + hcMethod: target.hcMethod, + hcTlsServerName: target.hcTlsServerName, }; }); @@ -376,3 +360,39 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => { excludeSender: false // Include sender in broadcast }; }; + +async function getUniqueSubnetForSite( + exitNode: ExitNode, + trx: Transaction | typeof db = db +): Promise { + const lockKey = `subnet-allocation:${exitNode.exitNodeId}`; + + return await lockManager.withLock( + lockKey, + async () => { + const sitesQuery = await trx + .select({ + subnet: sites.subnet + }) + .from(sites) + .where(eq(sites.exitNodeId, exitNode.exitNodeId)); + + const blockSize = config.getRawConfig().gerbil.site_block_size; + const subnets = sitesQuery + .map((site) => site.subnet) + .filter( + (subnet) => + subnet && /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/.test(subnet) + ) + .filter((subnet) => subnet !== null); + subnets.push(exitNode.address.replace(/\/\d+$/, `/${blockSize}`)); + const newSubnet = findNextAvailableCidr( + subnets, + blockSize, + exitNode.address + ); + return newSubnet; + }, + 5000 // 5 second lock TTL - subnet allocation should be quick + ); +} diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index 97e4030d..32145fcb 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -66,7 +66,8 @@ export async function addTargets( hcUnhealthyInterval: hc.hcUnhealthyInterval, // in seconds hcTimeout: hc.hcTimeout, // in seconds hcHeaders: hcHeadersSend, - hcMethod: hc.hcMethod + hcMethod: hc.hcMethod, + hcTlsServerName: hc.hcTlsServerName, }; }); diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 81a35451..2ec8d3dc 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -198,6 +198,62 @@ export async function createSite( } } + if (subnet && exitNodeId) { + //make sure the subnet is in the range of the exit node if provided + const [exitNode] = await db + .select() + .from(exitNodes) + .where(eq(exitNodes.exitNodeId, exitNodeId)); + + if (!exitNode) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Exit node not found") + ); + } + + if (!exitNode.address) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Exit node has no subnet defined" + ) + ); + } + + const subnetIp = subnet.split("/")[0]; + + if (!isIpInCidr(subnetIp, exitNode.address)) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Subnet is not in the CIDR range of the exit node address." + ) + ); + } + + // lets also make sure there is no overlap with other sites on the exit node + const sitesQuery = await db + .select({ + subnet: sites.subnet + }) + .from(sites) + .where( + and( + eq(sites.exitNodeId, exitNodeId), + eq(sites.subnet, subnet) + ) + ); + + if (sitesQuery.length > 0) { + return next( + createHttpError( + HttpCode.CONFLICT, + `Subnet ${subnet} overlaps with an existing site on this exit node. Please restart site creation.` + ) + ); + } + } + const niceId = await getUniqueSiteName(orgId); let newSite: Site; diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 6cf29da3..2c09b5a6 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -48,6 +48,7 @@ const createTargetSchema = z.strictObject({ hcFollowRedirects: z.boolean().optional().nullable(), hcMethod: z.string().min(1).optional().nullable(), hcStatus: z.int().optional().nullable(), + hcTlsServerName: z.string().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z .enum(["exact", "prefix", "regex"]) @@ -247,7 +248,8 @@ export async function createTarget( hcFollowRedirects: targetData.hcFollowRedirects ?? null, hcMethod: targetData.hcMethod ?? null, hcStatus: targetData.hcStatus ?? null, - hcHealth: "unknown" + hcHealth: "unknown", + hcTlsServerName: targetData.hcTlsServerName ?? null }) .returning(); diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index e97d577d..356276cb 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -57,6 +57,7 @@ function queryTargets(resourceId: number) { hcMethod: targetHealthCheck.hcMethod, hcStatus: targetHealthCheck.hcStatus, hcHealth: targetHealthCheck.hcHealth, + hcTlsServerName: targetHealthCheck.hcTlsServerName, path: targets.path, pathMatchType: targets.pathMatchType, rewritePath: targets.rewritePath, diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 1889154c..4a60e6cf 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -42,6 +42,7 @@ const updateTargetBodySchema = z.strictObject({ hcFollowRedirects: z.boolean().optional().nullable(), hcMethod: z.string().min(1).optional().nullable(), hcStatus: z.int().optional().nullable(), + hcTlsServerName: z.string().optional().nullable(), path: z.string().optional().nullable(), pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(), rewritePath: z.string().optional().nullable(), @@ -217,7 +218,8 @@ export async function updateTarget( hcHeaders: hcHeaders, hcFollowRedirects: parsedBody.data.hcFollowRedirects, hcMethod: parsedBody.data.hcMethod, - hcStatus: parsedBody.data.hcStatus + hcStatus: parsedBody.data.hcStatus, + hcTlsServerName: parsedBody.data.hcTlsServerName, }) .where(eq(targetHealthCheck.targetId, targetId)) .returning(); diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx index 0f261034..ba3499b5 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx @@ -464,6 +464,7 @@ export default function ReverseProxyTargets(props: { hcStatus: null, hcMode: null, hcUnhealthyInterval: null, + hcTlsServerName: null, siteType: sites.length > 0 ? sites[0].type : null, new: true, updated: false @@ -629,7 +630,8 @@ export default function ReverseProxyTargets(props: { hcHealth: "unknown", hcStatus: null, hcMode: null, - hcUnhealthyInterval: null + hcUnhealthyInterval: null, + hcTlsServerName: null, }; setTargets([...targets, newTarget]); @@ -730,7 +732,8 @@ export default function ReverseProxyTargets(props: { hcMethod: target.hcMethod || null, hcStatus: target.hcStatus || null, hcUnhealthyInterval: target.hcUnhealthyInterval || null, - hcMode: target.hcMode || null + hcMode: target.hcMode || null, + hcTlsServerName: target.hcTlsServerName, }; // Only include path-related fields for HTTP resources @@ -1831,7 +1834,9 @@ export default function ReverseProxyTargets(props: { hcMode: selectedTargetForHealthCheck.hcMode || "http", hcUnhealthyInterval: selectedTargetForHealthCheck.hcUnhealthyInterval || - 30 + 30, + hcTlsServerName: selectedTargetForHealthCheck.hcTlsServerName || + undefined, }} onChanges={async (config) => { if (selectedTargetForHealthCheck) { diff --git a/src/app/[orgId]/settings/resources/proxy/create/page.tsx b/src/app/[orgId]/settings/resources/proxy/create/page.tsx index ed00ace7..5fe98c95 100644 --- a/src/app/[orgId]/settings/resources/proxy/create/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/create/page.tsx @@ -297,6 +297,7 @@ export default function Page() { hcStatus: null, hcMode: null, hcUnhealthyInterval: null, + hcTlsServerName: null, siteType: sites.length > 0 ? sites[0].type : null, new: true, updated: false @@ -454,7 +455,8 @@ export default function Page() { hcHealth: "unknown", hcStatus: null, hcMode: null, - hcUnhealthyInterval: null + hcUnhealthyInterval: null, + hcTlsServerName: null }; setTargets([...targets, newTarget]); @@ -576,7 +578,8 @@ export default function Page() { target.hcFollowRedirects || null, hcStatus: target.hcStatus || null, hcUnhealthyInterval: target.hcUnhealthyInterval || null, - hcMode: target.hcMode || null + hcMode: target.hcMode || null, + hcTlsServerName: target.hcTlsServerName }; // Only include path-related fields for HTTP resources @@ -1809,7 +1812,10 @@ export default function Page() { "http", hcUnhealthyInterval: selectedTargetForHealthCheck.hcUnhealthyInterval || - 30 + 30, + hcTlsServerName: + selectedTargetForHealthCheck.hcTlsServerName || + undefined }} onChanges={async (config) => { if (selectedTargetForHealthCheck) { diff --git a/src/components/HealthCheckDialog.tsx b/src/components/HealthCheckDialog.tsx index 1bdef85e..c9590802 100644 --- a/src/components/HealthCheckDialog.tsx +++ b/src/components/HealthCheckDialog.tsx @@ -51,6 +51,7 @@ type HealthCheckConfig = { hcFollowRedirects: boolean; hcMode: string; hcUnhealthyInterval: number; + hcTlsServerName: string; }; type HealthCheckDialogProps = { @@ -109,7 +110,8 @@ export default function HealthCheckDialog({ ), hcFollowRedirects: z.boolean(), hcMode: z.string(), - hcUnhealthyInterval: z.int().positive().min(5) + hcUnhealthyInterval: z.int().positive().min(5), + hcTlsServerName: z.string() }); const form = useForm>({ @@ -147,7 +149,8 @@ export default function HealthCheckDialog({ : "", hcFollowRedirects: initialConfig?.hcFollowRedirects, hcMode: initialConfig?.hcMode, - hcUnhealthyInterval: initialConfig?.hcUnhealthyInterval + hcUnhealthyInterval: initialConfig?.hcUnhealthyInterval, + hcTlsServerName: initialConfig?.hcTlsServerName ?? "" }); }, [open]); @@ -554,6 +557,37 @@ export default function HealthCheckDialog({ )} /> + {/*TLS Server Name (SNI)*/} + ( + + + {t("tlsServerName")} + + + { + field.onChange(e); + handleFieldChange( + "hcTlsServerName", + e.target.value + ); + }} + /> + + + {t( + "tlsServerNameDescription" + )} + + + + )} + /> + {/* Custom Headers */} diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 3644b06d..84ed8bf7 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,4 +1,4 @@ export type Locale = (typeof locales)[number]; -export const locales = ['en-US', 'es-ES', 'fr-FR', 'de-DE', 'nl-NL', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN', 'ko-KR', 'bg-BG', 'cs-CZ', 'ru-RU', 'nb-NO'] as const; +export const locales = ['en-US', 'es-ES', 'fr-FR', 'de-DE', 'nl-NL', 'it-IT', 'pl-PL', 'pt-PT', 'tr-TR', 'zh-CN', 'ko-KR', 'bg-BG', 'cs-CZ', 'ru-RU', 'nb-NO', 'zh-TW'] as const; export const defaultLocale: Locale = 'en-US'; \ No newline at end of file