Merge remote-tracking branch 'upstream/dev' into fix-dev

This commit is contained in:
Lokowitz
2025-12-09 11:40:04 +00:00
36 changed files with 1022 additions and 453 deletions

View File

@@ -181,7 +181,7 @@
"baseDomain": "Базов домейн",
"subdomnainDescription": "Поддомейнът, където ресурсът ще бъде достъпен.",
"resourceRawSettings": "TCP/UDP настройки",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Конфигурирайте как ресурсът ще бъде достъпен чрез TCP/UDP",
"protocol": "Протокол",
"protocolSelect": "Изберете протокол",
"resourcePortNumber": "Номер на порт",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Настройките за прокси бяха успешно актуализирани",
"proxyErrorUpdate": "Неуспешно актуализиране на настройки на прокси",
"proxyErrorUpdateDescription": "Възникна грешка при актуализиране на настройки на прокси",
"targetAddr": "Host",
"targetAddr": "Хост",
"targetPort": "Порт",
"targetProtocol": "Протокол",
"targetTlsSettings": "Конфигурация на защитена връзка",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Актуализации на продукта",
"productUpdateEmpty": "Няма актуализации",
"dismissAll": "Отхвърляне на всички",
"pangolinUpdateAvailable": "Налична е нова версия",
"pangolinUpdateAvailable": "Актуализация е налична",
"pangolinUpdateAvailableInfo": "Версия {version} е готова за инсталиране",
"pangolinUpdateAvailableReleaseNotes": "Преглед на бележките за издание",
"pangolinUpdateAvailableReleaseNotes": "Преглед на бележките за изданието",
"newtUpdateAvailable": "Ново обновление",
"newtUpdateAvailableInfo": "Нова версия на Newt е налична. Моля, обновете до последната версия за най-добро изживяване.",
"domainPickerEnterDomain": "Домейн",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 дни",
"logRetention90Days": "90 дни",
"logRetentionForever": "Завинаги",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Край на следващата година",
"actionLogsDescription": "Прегледайте историята на действията, извършени в тази организация",
"accessLogsDescription": "Прегледайте заявките за удостоверяване на достъпа до ресурсите в тази организация",
"licenseRequiredToUse": "Необходим е лиценз Enterprise, за да се използва тази функция.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Това ще генерира нови удостоверителни данни и незабавно ще прекъсне връзката на отдалечения възел. Отдалеченият възел ще трябва да се рестартира с новите удостоверителни данни.",
"remoteExitNodeRegenerateCredentialsConfirmation": "Сигурни ли сте, че искате да генерирате новите удостоверителни данни за този отдалечен възел?",
"remoteExitNodeRegenerateCredentialsWarning": "Това ще генерира нови удостоверителни данни. Отдалеченият възел ще остане свързан, докато не го рестартирате ръчно и използвате новите удостоверителни данни.",
"agent": "Agent"
"agent": "Агент"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "Základní doména",
"subdomnainDescription": "Subdoména, kde bude zdroj přístupný.",
"resourceRawSettings": "Nastavení TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Nakonfigurujte, jak bude dokument přístupný přes TCP/UDP",
"protocol": "Protokol",
"protocolSelect": "Vybrat protokol",
"resourcePortNumber": "Číslo portu",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Nastavení proxy bylo úspěšně aktualizováno",
"proxyErrorUpdate": "Aktualizace nastavení proxy se nezdařila",
"proxyErrorUpdateDescription": "Došlo k chybě při aktualizaci nastavení proxy",
"targetAddr": "Host",
"targetAddr": "Hostitel",
"targetPort": "Přístav",
"targetProtocol": "Protokol",
"targetTlsSettings": "Nastavení bezpečného připojení",
@@ -1320,7 +1320,7 @@
"productUpdateTitle": "Aktualizace produktu",
"productUpdateEmpty": "Žádné aktualizace",
"dismissAll": "Odmítnout vše",
"pangolinUpdateAvailable": "K dispozici je nová verze",
"pangolinUpdateAvailable": "Dostupná aktualizace",
"pangolinUpdateAvailableInfo": "Verze {version} je připravena k instalaci",
"pangolinUpdateAvailableReleaseNotes": "Zobrazit poznámky k vydání",
"newtUpdateAvailable": "Dostupná aktualizace",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 dní",
"logRetention90Days": "90 dní",
"logRetentionForever": "Navždy",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Konec následujícího roku",
"actionLogsDescription": "Zobrazit historii akcí provedených v této organizaci",
"accessLogsDescription": "Zobrazit žádosti o ověření přístupu pro zdroje v této organizaci",
"licenseRequiredToUse": "Pro použití této funkce je vyžadována licence pro podnikání.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Basis-Domain",
"subdomnainDescription": "Die Subdomäne, auf die die Ressource zugegriffen werden soll.",
"resourceRawSettings": "TCP/UDP Einstellungen",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Konfigurieren, wie auf die Ressource über TCP/UDP zugegriffen wird",
"protocol": "Protokoll",
"protocolSelect": "Wählen Sie ein Protokoll",
"resourcePortNumber": "Portnummer",
@@ -1102,7 +1102,7 @@
"actionDeleteIdpOrg": "IDP-Organisationsrichtlinie löschen",
"actionListIdpOrgs": "IDP-Organisationen auflisten",
"actionUpdateIdpOrg": "IDP-Organisation aktualisieren",
"actionCreateClient": "Client anlegen",
"actionCreateClient": "Endgerät anlegen",
"actionDeleteClient": "Client löschen",
"actionUpdateClient": "Client aktualisieren",
"actionListClients": "Clients auflisten",
@@ -1320,7 +1320,7 @@
"productUpdateTitle": "Produkt-Updates",
"productUpdateEmpty": "Keine Updates",
"dismissAll": "Alle verwerfen",
"pangolinUpdateAvailable": "Neue Version verfügbar",
"pangolinUpdateAvailable": "Update verfügbar",
"pangolinUpdateAvailableInfo": "Version {version} ist bereit zur Installation",
"pangolinUpdateAvailableReleaseNotes": "Versionshinweise anzeigen",
"newtUpdateAvailable": "Update verfügbar",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 Tage",
"logRetention90Days": "90 Tage",
"logRetentionForever": "Für immer",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Ende des folgenden Jahres",
"actionLogsDescription": "Verlauf der in dieser Organisation durchgeführten Aktionen anzeigen",
"accessLogsDescription": "Zugriffsauth-Anfragen für Ressourcen in dieser Organisation anzeigen",
"licenseRequiredToUse": "Um diese Funktion nutzen zu können, ist eine Enterprise-Lizenz erforderlich.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Dominio base",
"subdomnainDescription": "El subdominio al que el recurso será accesible.",
"resourceRawSettings": "Configuración TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Configurar cómo se accederá al recurso a través de TCP/UDP",
"protocol": "Protocolo",
"protocolSelect": "Seleccionar un protocolo",
"resourcePortNumber": "Número de puerto",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "La configuración del proxy se ha actualizado correctamente",
"proxyErrorUpdate": "Error al actualizar la configuración del proxy",
"proxyErrorUpdateDescription": "Se ha producido un error al actualizar la configuración del proxy",
"targetAddr": "Host",
"targetAddr": "Anfitrión",
"targetPort": "Puerto",
"targetProtocol": "Protocolo",
"targetTlsSettings": "Configuración de conexión segura",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Actualizaciones de producto",
"productUpdateEmpty": "Sin actualizaciones",
"dismissAll": "Descartar todo",
"pangolinUpdateAvailable": "Nueva versión disponible",
"pangolinUpdateAvailable": "Actualización disponible",
"pangolinUpdateAvailableInfo": "La versión {version} está lista para instalar",
"pangolinUpdateAvailableReleaseNotes": "Ver notas del lanzamiento",
"pangolinUpdateAvailableReleaseNotes": "Ver notas de lanzamiento",
"newtUpdateAvailable": "Nueva actualización disponible",
"newtUpdateAvailableInfo": "Hay una nueva versión de Newt disponible. Actualice a la última versión para la mejor experiencia.",
"domainPickerEnterDomain": "Dominio",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 días",
"logRetention90Days": "90 días",
"logRetentionForever": "Para siempre",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Fin del año siguiente",
"actionLogsDescription": "Ver un historial de acciones realizadas en esta organización",
"accessLogsDescription": "Ver solicitudes de acceso a los recursos de esta organización",
"licenseRequiredToUse": "Se requiere una licencia Enterprise para utilizar esta función.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Esto regenerará las credenciales y desconectará inmediatamente el nodo de salida remoto. El nodo de salida remoto tendrá que reiniciarse con las nuevas credenciales.",
"remoteExitNodeRegenerateCredentialsConfirmation": "¿Estás seguro de que quieres regenerar las credenciales para este nodo de salida remoto?",
"remoteExitNodeRegenerateCredentialsWarning": "Esto regenerará las credenciales. El nodo de salida remoto permanecerá conectado hasta que lo reinicie manualmente y utilice las nuevas credenciales.",
"agent": "Agent"
"agent": "Agente"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "Domaine racine",
"subdomnainDescription": "Le sous-domaine où la ressource sera accessible.",
"resourceRawSettings": "Paramètres TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Configurer comment la ressource sera accédée via TCP/UDP",
"protocol": "Protocole",
"protocolSelect": "Choisir un protocole",
"resourcePortNumber": "Numéro de port",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Les paramètres du proxy ont été mis à jour avec succès",
"proxyErrorUpdate": "Échec de la mise à jour des paramètres du proxy",
"proxyErrorUpdateDescription": "Une erreur s'est produite lors de la mise à jour des paramètres du proxy",
"targetAddr": "Host",
"targetAddr": "Hôte",
"targetPort": "Port",
"targetProtocol": "Protocole",
"targetTlsSettings": "Configuration sécurisée de connexion",
@@ -1322,7 +1322,7 @@
"dismissAll": "Tout cacher",
"pangolinUpdateAvailable": "Mise à jour disponible",
"pangolinUpdateAvailableInfo": "La version {version} est prête à être installée",
"pangolinUpdateAvailableReleaseNotes": "Voir les notes de version",
"pangolinUpdateAvailableReleaseNotes": "Voir les notes de publication",
"newtUpdateAvailable": "Mise à jour disponible",
"newtUpdateAvailableInfo": "Une nouvelle version de Newt est disponible. Veuillez mettre à jour vers la dernière version pour une meilleure expérience.",
"domainPickerEnterDomain": "Domaine",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 jours",
"logRetention90Days": "90 jours",
"logRetentionForever": "Pour toujours",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Fin de l'année suivante",
"actionLogsDescription": "Voir l'historique des actions effectuées dans cette organisation",
"accessLogsDescription": "Voir les demandes d'authentification d'accès aux ressources de cette organisation",
"licenseRequiredToUse": "Une licence Entreprise est nécessaire pour utiliser cette fonctionnalité.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Dominio Base",
"subdomnainDescription": "Il sottodominio in cui la risorsa sarà accessibile.",
"resourceRawSettings": "Impostazioni TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Configura come accedere alla risorsa tramite TCP/UDP",
"protocol": "Protocollo",
"protocolSelect": "Seleziona un protocollo",
"resourcePortNumber": "Numero Porta",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Aggiornamenti Prodotto",
"productUpdateEmpty": "Nessun aggiornamento",
"dismissAll": "Ignora tutto",
"pangolinUpdateAvailable": "Nuova versione disponibile",
"pangolinUpdateAvailable": "Aggiornamento Disponibile",
"pangolinUpdateAvailableInfo": "La versione {version} è pronta per l'installazione",
"pangolinUpdateAvailableReleaseNotes": "Visualizza note di rilascio",
"pangolinUpdateAvailableReleaseNotes": "Visualizza Note Di Rilascio",
"newtUpdateAvailable": "Aggiornamento Disponibile",
"newtUpdateAvailableInfo": "È disponibile una nuova versione di Newt. Si prega di aggiornare all'ultima versione per la migliore esperienza.",
"domainPickerEnterDomain": "Dominio",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 giorni",
"logRetention90Days": "90 giorni",
"logRetentionForever": "Per Sempre",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Fine dell'anno successivo",
"actionLogsDescription": "Visualizza una cronologia delle azioni eseguite in questa organizzazione",
"accessLogsDescription": "Visualizza le richieste di autenticazione di accesso per le risorse in questa organizzazione",
"licenseRequiredToUse": "Per utilizzare questa funzione è necessaria una licenza Enterprise.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Questo rigenererà le credenziali e disconnetterà immediatamente il nodo di uscita remoto. Il nodo di uscita remoto dovrà essere riavviato con le nuove credenziali.",
"remoteExitNodeRegenerateCredentialsConfirmation": "Sei sicuro di voler rigenerare le credenziali per questo nodo di uscita remoto?",
"remoteExitNodeRegenerateCredentialsWarning": "Questo rigenererà le credenziali. Il nodo di uscita remoto rimarrà connesso finché non lo riavvierai manualmente e userai le nuove credenziali.",
"agent": "Agent"
"agent": "Agente"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "기본 도메인",
"subdomnainDescription": "리소스에 접근할 수 있는 하위 도메인입니다.",
"resourceRawSettings": "TCP/UDP 설정",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "TCP/UDP를 통해 리소스에 접근하는 방법을 구성하세요.",
"protocol": "프로토콜",
"protocolSelect": "프로토콜 선택",
"resourcePortNumber": "포트 번호",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "프록시 설정이 성공적으로 업데이트되었습니다",
"proxyErrorUpdate": "프록시 설정 업데이트에 실패했습니다.",
"proxyErrorUpdateDescription": "프록시 설정을 업데이트하는 동안 오류가 발생했습니다",
"targetAddr": "Host",
"targetAddr": "호스트",
"targetPort": "포트",
"targetProtocol": "프로토콜",
"targetTlsSettings": "보안 연결 구성",
@@ -1320,7 +1320,7 @@
"productUpdateTitle": "제품 업데이트",
"productUpdateEmpty": "업데이트 없음",
"dismissAll": "모두 해제",
"pangolinUpdateAvailable": "새 버전 사용 가능",
"pangolinUpdateAvailable": "업데이트 가능",
"pangolinUpdateAvailableInfo": "버전 {version}을(를) 설치할 준비가 되었습니다",
"pangolinUpdateAvailableReleaseNotes": "릴리스 노트 보기",
"newtUpdateAvailable": "업데이트 가능",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 일",
"logRetention90Days": "90 일",
"logRetentionForever": "영구",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "다음 연도 말",
"actionLogsDescription": "이 조직에서 수행된 작업의 기록을 봅니다",
"accessLogsDescription": "이 조직의 자원에 대한 접근 인증 요청을 확인합니다",
"licenseRequiredToUse": "이 기능을 사용하려면 Enterprise 라이선스가 필요합니다.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "이 과정은 자격 증명을 다시 생성하고 원격 종료 노드와의 연결을 즉시 해제합니다. 원격 종료 노드는 새 자격 증명으로 다시 시작되어야 합니다.",
"remoteExitNodeRegenerateCredentialsConfirmation": "이 원격 종료 노드에 대한 자격 증명을 다시 생성하시겠습니까?",
"remoteExitNodeRegenerateCredentialsWarning": "이 과정은 자격 증명을 다시 생성합니다. 수동으로 다시 시작하고 새 자격 증명을 사용하기 전까지 원격 종료 노드는 연결된 상태로 유지됩니다.",
"agent": "Agent"
"agent": "에이전트"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "Grunndomene",
"subdomnainDescription": "Underdomenet hvor ressursen vil være tilgjengelig.",
"resourceRawSettings": "TCP/UDP-innstillinger",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Konfigurer hvordan ressursen vil bli tilgjengelig over TCP/UDP",
"protocol": "Protokoll",
"protocolSelect": "Velg en protokoll",
"resourcePortNumber": "Portnummer",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Proxy innstillinger har blitt oppdatert",
"proxyErrorUpdate": "En feil oppsto under oppdatering av proxyinnstillinger",
"proxyErrorUpdateDescription": "En feil oppsto under oppdatering av proxyinnstillinger",
"targetAddr": "Host",
"targetAddr": "Vert",
"targetPort": "Port",
"targetProtocol": "Protokoll",
"targetTlsSettings": "Sikker tilkoblings-konfigurasjon",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Oppdateringer om produktet",
"productUpdateEmpty": "Ingen oppdateringer",
"dismissAll": "Avvis alle",
"pangolinUpdateAvailable": "Ny versjon tilgjengelig",
"pangolinUpdateAvailable": "Oppdatering tilgjengelig",
"pangolinUpdateAvailableInfo": "Versjon {version} er klar til å installere",
"pangolinUpdateAvailableReleaseNotes": "Se utgivelsnotater",
"pangolinUpdateAvailableReleaseNotes": "Se utgivelsesnotater",
"newtUpdateAvailable": "Oppdatering tilgjengelig",
"newtUpdateAvailableInfo": "En ny versjon av Newt er tilgjengelig. Vennligst oppdater til den nyeste versjonen for den beste opplevelsen.",
"domainPickerEnterDomain": "Domene",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 dager",
"logRetention90Days": "90 dager",
"logRetentionForever": "Alltid",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Slutt på neste år",
"actionLogsDescription": "Vis historikk for handlinger som er utført i denne organisasjonen",
"accessLogsDescription": "Vis autoriseringsforespørsler for ressurser i denne organisasjonen",
"licenseRequiredToUse": "En Enterprise lisens er påkrevd for å bruke denne funksjonen.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Basis domein",
"subdomnainDescription": "Het subdomein waar de bron toegankelijk zal zijn.",
"resourceRawSettings": "TCP/UDP instellingen",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Stel in hoe de bron wordt benaderd via TCP/UDP",
"protocol": "Protocol",
"protocolSelect": "Selecteer een protocol",
"resourcePortNumber": "Nummer van poort",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Proxyinstellingen zijn succesvol bijgewerkt",
"proxyErrorUpdate": "Bijwerken van proxy-instellingen mislukt",
"proxyErrorUpdateDescription": "Fout opgetreden tijdens het bijwerken van de proxy-instellingen",
"targetAddr": "Host",
"targetAddr": "Hostnaam",
"targetPort": "Poort",
"targetProtocol": "Protocol",
"targetTlsSettings": "HTTPS & TLS instellingen",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Update Producten",
"productUpdateEmpty": "Geen updates",
"dismissAll": "Alles afwijzen",
"pangolinUpdateAvailable": "Nieuwe versie beschikbaar",
"pangolinUpdateAvailable": "Update beschikbaar",
"pangolinUpdateAvailableInfo": "Versie {version} is klaar om te installeren",
"pangolinUpdateAvailableReleaseNotes": "Bekijk release notities",
"pangolinUpdateAvailableReleaseNotes": "Uitgaveopmerkingen bekijken",
"newtUpdateAvailable": "Update beschikbaar",
"newtUpdateAvailableInfo": "Er is een nieuwe versie van Newt beschikbaar. Update naar de nieuwste versie voor de beste ervaring.",
"domainPickerEnterDomain": "Domein",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 dagen",
"logRetention90Days": "90 dagen",
"logRetentionForever": "Voor altijd",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Einde van volgend jaar",
"actionLogsDescription": "Bekijk een geschiedenis van acties die worden uitgevoerd in deze organisatie",
"accessLogsDescription": "Toegangsverificatieverzoeken voor resources in deze organisatie bekijken",
"licenseRequiredToUse": "Een Enterprise-licentie is vereist om deze functie te gebruiken.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Bazowa domena",
"subdomnainDescription": "Poddomena, w której zasób będzie dostępny.",
"resourceRawSettings": "Ustawienia TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Skonfiguruj jak zasób będzie dostępny przez TCP/UDP",
"protocol": "Protokół",
"protocolSelect": "Wybierz protokół",
"resourcePortNumber": "Numer portu",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Aktualizacje produktu",
"productUpdateEmpty": "Brak aktualizacji",
"dismissAll": "Zamknij wszystkie",
"pangolinUpdateAvailable": "Dostępna jest nowa wersja",
"pangolinUpdateAvailable": "Dostępna aktualizacja",
"pangolinUpdateAvailableInfo": "Wersja {version} jest gotowa do zainstalowania",
"pangolinUpdateAvailableReleaseNotes": "Zobacz notatki o wydaniu",
"pangolinUpdateAvailableReleaseNotes": "Zobacz informacje o wydaniu",
"newtUpdateAvailable": "Dostępna aktualizacja",
"newtUpdateAvailableInfo": "Nowa wersja Newt jest dostępna. Prosimy o aktualizację do najnowszej wersji dla najlepszej pracy.",
"domainPickerEnterDomain": "Domena",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 dni",
"logRetention90Days": "90 dni",
"logRetentionForever": "Na zawsze",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Koniec następnego roku",
"actionLogsDescription": "Zobacz historię działań wykonywanych w tej organizacji",
"accessLogsDescription": "Wyświetl prośby o autoryzację dostępu do zasobów w tej organizacji",
"licenseRequiredToUse": "Licencja Enterprise jest wymagana do korzystania z tej funkcji.",

View File

@@ -181,7 +181,7 @@
"baseDomain": "Domínio Base",
"subdomnainDescription": "O subdomínio onde o recurso será acessível.",
"resourceRawSettings": "Configurações TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Configurar como o recurso será acessado sobre TCP/UDP",
"protocol": "Protocolo",
"protocolSelect": "Selecione um protocolo",
"resourcePortNumber": "Número da Porta",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Configurações de proxy atualizadas com sucesso",
"proxyErrorUpdate": "Falha ao atualizar configurações de proxy",
"proxyErrorUpdateDescription": "Ocorreu um erro ao atualizar as configurações de proxy",
"targetAddr": "Host",
"targetAddr": "Servidor",
"targetPort": "Porta",
"targetProtocol": "Protocolo",
"targetTlsSettings": "Configuração de conexão segura",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Atualizações de Produto",
"productUpdateEmpty": "Não há atualizações",
"dismissAll": "Recusar tudo",
"pangolinUpdateAvailable": "Nova versão disponível",
"pangolinUpdateAvailable": "Atualização disponível",
"pangolinUpdateAvailableInfo": "A versão {version} está pronta para ser instalada",
"pangolinUpdateAvailableReleaseNotes": "Ver notas de lançamento",
"pangolinUpdateAvailableReleaseNotes": "Ver notas de versão",
"newtUpdateAvailable": "Nova Atualização Disponível",
"newtUpdateAvailableInfo": "Uma nova versão do Newt está disponível. Atualize para a versão mais recente para uma melhor experiência.",
"domainPickerEnterDomain": "Domínio",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 dias",
"logRetention90Days": "90 dias",
"logRetentionForever": "Permanentemente",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Fim do ano seguinte",
"actionLogsDescription": "Visualizar histórico de ações realizadas nesta organização",
"accessLogsDescription": "Ver solicitações de autenticação de recursos nesta organização",
"licenseRequiredToUse": "É necessária uma licença empresarial para usar esse recurso.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Isto irá regenerar as credenciais e desconectar imediatamente o nó de saída remota. O nó de saída remota precisará ser reiniciado com as novas credenciais.",
"remoteExitNodeRegenerateCredentialsConfirmation": "Você tem certeza que deseja regenerar as credenciais para este nó de saída remota?",
"remoteExitNodeRegenerateCredentialsWarning": "Isto irá regenerar as credenciais. O nó de saída remota permanecerá conectado até que você o reinicie manualmente e use as novas credenciais.",
"agent": "Agent"
"agent": "Representante"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "Базовый домен",
"subdomnainDescription": "Поддомен, в котором ресурс будет доступен.",
"resourceRawSettings": "Настройки TCP/UDP",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Настройка доступа к ресурсу по TCP/UDP",
"protocol": "Протокол",
"protocolSelect": "Выберите протокол",
"resourcePortNumber": "Номер порта",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "Настройки прокси успешно обновлены",
"proxyErrorUpdate": "Не удалось обновить настройки прокси",
"proxyErrorUpdateDescription": "Произошла ошибка при обновлении настроек прокси",
"targetAddr": "Host",
"targetAddr": "Хост",
"targetPort": "Порт",
"targetProtocol": "Протокол",
"targetTlsSettings": "Конфигурация безопасного соединения",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Обновления продуктов",
"productUpdateEmpty": "Нет обновлений",
"dismissAll": "Отклонить все",
"pangolinUpdateAvailable": "Доступна новая версия",
"pangolinUpdateAvailable": "Доступно обновление",
"pangolinUpdateAvailableInfo": "Версия {version} готова к установке",
"pangolinUpdateAvailableReleaseNotes": "Просмотреть заметки о выпуске",
"pangolinUpdateAvailableReleaseNotes": "Просмотреть примечания к выпуску",
"newtUpdateAvailable": "Доступно обновление",
"newtUpdateAvailableInfo": "Доступна новая версия Newt. Пожалуйста, обновитесь до последней версии для лучшего опыта.",
"domainPickerEnterDomain": "Домен",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 дней",
"logRetention90Days": "90 дней",
"logRetentionForever": "Всегда",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Конец следующего года",
"actionLogsDescription": "Просмотр истории действий, выполненных в этой организации",
"accessLogsDescription": "Просмотр запросов авторизации доступа к ресурсам этой организации",
"licenseRequiredToUse": "Для использования этой функции требуется лицензия предприятия.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Это позволит восстановить учётные данные и немедленно отключить удаленный узел выхода. Удаленный узел выхода должен быть перезапущен с новыми учетными данными.",
"remoteExitNodeRegenerateCredentialsConfirmation": "Вы уверены, что хотите восстановить учетные данные для этого удаленного выхода узла?",
"remoteExitNodeRegenerateCredentialsWarning": "Это позволит восстановить учетные данные. Удалённый узел останется подключенным, пока вы не перезапустите его вручную и воспользуетесь новыми учетными данными.",
"agent": "Agent"
"agent": "Агент"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "Temel Alan Adı",
"subdomnainDescription": "Kaynağınızın erişilebileceği alt alan adı.",
"resourceRawSettings": "TCP/UDP Ayarları",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "Kaynaklara TCP/UDP üzerinden nasıl erişileceğini yapılandırın",
"protocol": "Protokol",
"protocolSelect": "Bir protokol seçin",
"resourcePortNumber": "Port Numarası",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "Ürün Güncellemeleri",
"productUpdateEmpty": "Güncelleme yok",
"dismissAll": "Hepsini Kapat",
"pangolinUpdateAvailable": "Yeni sürüm mevcut",
"pangolinUpdateAvailable": "Güncelleme Mevcut",
"pangolinUpdateAvailableInfo": "Sürüm {version} yüklenmeye hazır",
"pangolinUpdateAvailableReleaseNotes": "Sürüm notlarını görüntüleyin",
"pangolinUpdateAvailableReleaseNotes": "Yayın Notlarını Görüntüle",
"newtUpdateAvailable": "Güncelleme Mevcut",
"newtUpdateAvailableInfo": "Newt'in yeni bir versiyonu mevcut. En iyi deneyim için lütfen en son sürüme güncelleyin.",
"domainPickerEnterDomain": "Alan Adı",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 gün",
"logRetention90Days": "90 gün",
"logRetentionForever": "Sonsuza kadar",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "Bir sonraki yılın sonu",
"actionLogsDescription": "Bu organizasyondaki eylemler geçmişini görüntüleyin",
"accessLogsDescription": "Bu organizasyondaki kaynaklar için erişim kimlik doğrulama isteklerini görüntüleyin",
"licenseRequiredToUse": "Bu özelliği kullanmak için bir kurumsal lisans gereklidir.",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "Bu, kimlik bilgilerini yeniden oluşturacak ve hemen uzak çıkış düğümünün bağlantısını kesecek. Uzak çıkış düğümü, yeni kimlik bilgileri ile yeniden başlatılmalıdır.",
"remoteExitNodeRegenerateCredentialsConfirmation": "Bu uzak çıkış düğümü için kimlik bilgilerini yeniden oluşturmak istediğinizden emin misiniz?",
"remoteExitNodeRegenerateCredentialsWarning": "Bu, kimlik bilgilerini yeniden oluşturacak. Uzak çıkış düğümü, manuel olarak yeniden başlatılana ve yeni kimlik bilgiler kullanılana kadar bağlı kalacak.",
"agent": "Agent"
"agent": "Aracı"
}

View File

@@ -181,7 +181,7 @@
"baseDomain": "根域名",
"subdomnainDescription": "可访问资源的子域。",
"resourceRawSettings": "TCP/UDP 设置",
"resourceRawSettingsDescription": "Configure how the resource will be accessed over TCP/UDP",
"resourceRawSettingsDescription": "配置如何通过 TCP/UDP 访问资源",
"protocol": "协议",
"protocolSelect": "选择协议",
"resourcePortNumber": "端口号",
@@ -499,7 +499,7 @@
"proxyUpdatedDescription": "已成功更新代理设置",
"proxyErrorUpdate": "更新代理设置失败",
"proxyErrorUpdateDescription": "更新代理设置时出错",
"targetAddr": "Host",
"targetAddr": "主机",
"targetPort": "端口",
"targetProtocol": "协议",
"targetTlsSettings": "安全连接配置",
@@ -1320,9 +1320,9 @@
"productUpdateTitle": "产品更新",
"productUpdateEmpty": "无更新",
"dismissAll": "关闭所有",
"pangolinUpdateAvailable": "新版本可用",
"pangolinUpdateAvailable": "可用更新",
"pangolinUpdateAvailableInfo": "版本 {version} 已准备就绪",
"pangolinUpdateAvailableReleaseNotes": "查看发布笔记",
"pangolinUpdateAvailableReleaseNotes": "查看发布说明",
"newtUpdateAvailable": "更新可用",
"newtUpdateAvailableInfo": "新版本的 Newt 已可用。请更新到最新版本以获得最佳体验。",
"domainPickerEnterDomain": "域名",
@@ -2108,7 +2108,7 @@
"logRetention30Days": "30 天",
"logRetention90Days": "90 天",
"logRetentionForever": "永远的",
"logRetentionEndOfFollowingYear": "End of following year",
"logRetentionEndOfFollowingYear": "下一年结束",
"actionLogsDescription": "查看此机构执行的操作历史",
"accessLogsDescription": "查看此机构资源的访问认证请求",
"licenseRequiredToUse": "需要企业许可证才能使用此功能。",
@@ -2270,5 +2270,5 @@
"remoteExitNodeRegenerateAndDisconnectWarning": "这将重新生成凭据并立即断开远程退出节点。远程退出节点将需要用新的凭据重启。",
"remoteExitNodeRegenerateCredentialsConfirmation": "您确定要重新生成此远程退出节点的凭据吗?",
"remoteExitNodeRegenerateCredentialsWarning": "这将重新生成凭据。远程退出节点将保持连接,直到您手动重启它并使用新凭据。",
"agent": "Agent"
"agent": "代理"
}

640
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"license": "SEE LICENSE IN LICENSE AND README.md",
"dependencies": {
"@asteasolutions/zod-to-openapi": "8.2.0",
"@aws-sdk/client-s3": "3.946.0",
"@aws-sdk/client-s3": "3.947.0",
"@faker-js/faker": "10.1.0",
"@headlessui/react": "2.2.9",
"@hookform/resolvers": "5.2.2",
@@ -60,7 +60,7 @@
"date-fns": "4.1.0",
"drizzle-orm": "0.45.0",
"eslint": "9.39.1",
"eslint-config-next": "16.0.7",
"eslint-config-next": "16.0.8",
"express": "5.2.1",
"express-rate-limit": "8.2.1",
"glob": "13.0.0",
@@ -131,7 +131,7 @@
"@types/jmespath": "0.15.2",
"@types/js-yaml": "4.0.9",
"@types/jsonwebtoken": "9.0.10",
"@types/node": "24.10.1",
"@types/node": "24.10.2",
"@types/nodemailer": "7.0.4",
"@types/nprogress": "0.2.3",
"@types/pg": "8.15.6",
@@ -152,7 +152,7 @@
"tsc-alias": "1.8.16",
"tsx": "4.21.0",
"typescript": "5.9.3",
"typescript-eslint": "8.48.1"
"typescript-eslint": "8.49.0"
}
},
"node_modules/@alloc/quick-lru": {
@@ -396,32 +396,32 @@
}
},
"node_modules/@aws-sdk/client-s3": {
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.946.0.tgz",
"integrity": "sha512-Y3ww3yd1wzmS2r3qgH3jg4MxCTdeNrae2J1BmdV+IW/2R2gFWJva5U5GbS6KUSUxanJBRG7gd8uOIi1b0EMOng==",
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.947.0.tgz",
"integrity": "sha512-ICgnI8D3ccIX9alsLksPFY2bX5CAIbyB+q19sXJgPhzCJ5kWeQ6LQ5xBmRVT5kccmsVGbbJdhnLXHyiN5LZsWg==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha1-browser": "5.2.0",
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.946.0",
"@aws-sdk/credential-provider-node": "3.946.0",
"@aws-sdk/core": "3.947.0",
"@aws-sdk/credential-provider-node": "3.947.0",
"@aws-sdk/middleware-bucket-endpoint": "3.936.0",
"@aws-sdk/middleware-expect-continue": "3.936.0",
"@aws-sdk/middleware-flexible-checksums": "3.946.0",
"@aws-sdk/middleware-flexible-checksums": "3.947.0",
"@aws-sdk/middleware-host-header": "3.936.0",
"@aws-sdk/middleware-location-constraint": "3.936.0",
"@aws-sdk/middleware-logger": "3.936.0",
"@aws-sdk/middleware-recursion-detection": "3.936.0",
"@aws-sdk/middleware-sdk-s3": "3.946.0",
"@aws-sdk/middleware-sdk-s3": "3.947.0",
"@aws-sdk/middleware-ssec": "3.936.0",
"@aws-sdk/middleware-user-agent": "3.946.0",
"@aws-sdk/middleware-user-agent": "3.947.0",
"@aws-sdk/region-config-resolver": "3.936.0",
"@aws-sdk/signature-v4-multi-region": "3.946.0",
"@aws-sdk/signature-v4-multi-region": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@aws-sdk/util-endpoints": "3.936.0",
"@aws-sdk/util-user-agent-browser": "3.936.0",
"@aws-sdk/util-user-agent-node": "3.946.0",
"@aws-sdk/util-user-agent-node": "3.947.0",
"@smithy/config-resolver": "^4.4.3",
"@smithy/core": "^3.18.7",
"@smithy/eventstream-serde-browser": "^4.2.5",
@@ -461,6 +461,388 @@
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.947.0.tgz",
"integrity": "sha512-sDwcO8SP290WSErY1S8pz8hTafeghKmmWjNVks86jDK30wx62CfazOTeU70IpWgrUBEygyXk/zPogHsUMbW2Rg==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.947.0",
"@aws-sdk/middleware-host-header": "3.936.0",
"@aws-sdk/middleware-logger": "3.936.0",
"@aws-sdk/middleware-recursion-detection": "3.936.0",
"@aws-sdk/middleware-user-agent": "3.947.0",
"@aws-sdk/region-config-resolver": "3.936.0",
"@aws-sdk/types": "3.936.0",
"@aws-sdk/util-endpoints": "3.936.0",
"@aws-sdk/util-user-agent-browser": "3.936.0",
"@aws-sdk/util-user-agent-node": "3.947.0",
"@smithy/config-resolver": "^4.4.3",
"@smithy/core": "^3.18.7",
"@smithy/fetch-http-handler": "^5.3.6",
"@smithy/hash-node": "^4.2.5",
"@smithy/invalid-dependency": "^4.2.5",
"@smithy/middleware-content-length": "^4.2.5",
"@smithy/middleware-endpoint": "^4.3.14",
"@smithy/middleware-retry": "^4.4.14",
"@smithy/middleware-serde": "^4.2.6",
"@smithy/middleware-stack": "^4.2.5",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/node-http-handler": "^4.4.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/url-parser": "^4.2.5",
"@smithy/util-base64": "^4.3.0",
"@smithy/util-body-length-browser": "^4.2.0",
"@smithy/util-body-length-node": "^4.2.1",
"@smithy/util-defaults-mode-browser": "^4.3.13",
"@smithy/util-defaults-mode-node": "^4.2.16",
"@smithy/util-endpoints": "^3.2.5",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-retry": "^4.2.5",
"@smithy/util-utf8": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz",
"integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "3.936.0",
"@aws-sdk/xml-builder": "3.930.0",
"@smithy/core": "^3.18.7",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/signature-v4": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/util-base64": "^4.3.0",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-utf8": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.947.0.tgz",
"integrity": "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.947.0.tgz",
"integrity": "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/fetch-http-handler": "^5.3.6",
"@smithy/node-http-handler": "^4.4.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/util-stream": "^4.5.6",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.947.0.tgz",
"integrity": "sha512-A2ZUgJUJZERjSzvCi2NR/hBVbVkTXPD0SdKcR/aITb30XwF+n3T963b+pJl90qhOspoy7h0IVYNR7u5Nr9tJdQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/credential-provider-env": "3.947.0",
"@aws-sdk/credential-provider-http": "3.947.0",
"@aws-sdk/credential-provider-login": "3.947.0",
"@aws-sdk/credential-provider-process": "3.947.0",
"@aws-sdk/credential-provider-sso": "3.947.0",
"@aws-sdk/credential-provider-web-identity": "3.947.0",
"@aws-sdk/nested-clients": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/credential-provider-imds": "^4.2.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-login": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.947.0.tgz",
"integrity": "sha512-u7M3hazcB7aJiVwosNdJRbIJDzbwQ861NTtl6S0HmvWpixaVb7iyhJZWg8/plyUznboZGBm7JVEdxtxv3u0bTA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/nested-clients": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.947.0.tgz",
"integrity": "sha512-S0Zqebr71KyrT6J4uYPhwV65g4V5uDPHnd7dt2W34FcyPu+hVC7Hx4MFmsPyVLeT5cMCkkZvmY3kAoEzgUPJJg==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/credential-provider-env": "3.947.0",
"@aws-sdk/credential-provider-http": "3.947.0",
"@aws-sdk/credential-provider-ini": "3.947.0",
"@aws-sdk/credential-provider-process": "3.947.0",
"@aws-sdk/credential-provider-sso": "3.947.0",
"@aws-sdk/credential-provider-web-identity": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/credential-provider-imds": "^4.2.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.947.0.tgz",
"integrity": "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.947.0.tgz",
"integrity": "sha512-NktnVHTGaUMaozxycYrepvb3yfFquHTQ53lt6hBEVjYBzK3C4tVz0siUpr+5RMGLSiZ5bLBp2UjJPgwx4i4waQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/client-sso": "3.947.0",
"@aws-sdk/core": "3.947.0",
"@aws-sdk/token-providers": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.947.0.tgz",
"integrity": "sha512-gokm/e/YHiHLrZgLq4j8tNAn8RJDPbIcglFRKgy08q8DmAqHQ8MXAKW3eS0QjAuRXU9mcMmUo1NrX6FRNBCCPw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/nested-clients": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-sdk-s3": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.947.0.tgz",
"integrity": "sha512-DS2tm5YBKhPW2PthrRBDr6eufChbwXe0NjtTZcYDfUCXf0OR+W6cIqyKguwHMJ+IyYdey30AfVw9/Lb5KB8U8A==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@aws-sdk/util-arn-parser": "3.893.0",
"@smithy/core": "^3.18.7",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/signature-v4": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/util-config-provider": "^4.2.0",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-stream": "^4.5.6",
"@smithy/util-utf8": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.947.0.tgz",
"integrity": "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@aws-sdk/util-endpoints": "3.936.0",
"@smithy/core": "^3.18.7",
"@smithy/protocol-http": "^5.3.5",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.947.0.tgz",
"integrity": "sha512-DjRJEYNnHUTu9kGPPQDTSXquwSEd6myKR4ssI4FaYLFhdT3ldWpj73yYt807H3tdmhS7vPmdVqchSJnjurUQAw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.947.0",
"@aws-sdk/middleware-host-header": "3.936.0",
"@aws-sdk/middleware-logger": "3.936.0",
"@aws-sdk/middleware-recursion-detection": "3.936.0",
"@aws-sdk/middleware-user-agent": "3.947.0",
"@aws-sdk/region-config-resolver": "3.936.0",
"@aws-sdk/types": "3.936.0",
"@aws-sdk/util-endpoints": "3.936.0",
"@aws-sdk/util-user-agent-browser": "3.936.0",
"@aws-sdk/util-user-agent-node": "3.947.0",
"@smithy/config-resolver": "^4.4.3",
"@smithy/core": "^3.18.7",
"@smithy/fetch-http-handler": "^5.3.6",
"@smithy/hash-node": "^4.2.5",
"@smithy/invalid-dependency": "^4.2.5",
"@smithy/middleware-content-length": "^4.2.5",
"@smithy/middleware-endpoint": "^4.3.14",
"@smithy/middleware-retry": "^4.4.14",
"@smithy/middleware-serde": "^4.2.6",
"@smithy/middleware-stack": "^4.2.5",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/node-http-handler": "^4.4.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/url-parser": "^4.2.5",
"@smithy/util-base64": "^4.3.0",
"@smithy/util-body-length-browser": "^4.2.0",
"@smithy/util-body-length-node": "^4.2.1",
"@smithy/util-defaults-mode-browser": "^4.3.13",
"@smithy/util-defaults-mode-node": "^4.2.16",
"@smithy/util-endpoints": "^3.2.5",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-retry": "^4.2.5",
"@smithy/util-utf8": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.947.0.tgz",
"integrity": "sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/middleware-sdk-s3": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/protocol-http": "^5.3.5",
"@smithy/signature-v4": "^5.3.5",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.947.0.tgz",
"integrity": "sha512-X/DyB8GuK44rsE89Tn5+s542B3PhGbXQSgV8lvqHDzvicwCt0tWny6790st6CPETrVVV2K3oJMfG5U3/jAmaZA==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.947.0",
"@aws-sdk/nested-clients": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/property-provider": "^4.2.5",
"@smithy/shared-ini-file-loader": "^4.4.0",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.947.0.tgz",
"integrity": "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/middleware-user-agent": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/types": "^4.9.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"aws-crt": ">=1.0.0"
},
"peerDependenciesMeta": {
"aws-crt": {
"optional": true
}
}
},
"node_modules/@aws-sdk/client-sesv2": {
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.946.0.tgz",
@@ -517,6 +899,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.946.0.tgz",
"integrity": "sha512-kGAs5iIVyUz4p6TX3pzG5q3cNxXnVpC4pwRC6DCSaSv9ozyPjc2d74FsK4fZ+J+ejtvCdJk72uiuQtWJc86Wuw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
@@ -566,6 +949,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz",
"integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "3.936.0",
@@ -590,6 +974,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.946.0.tgz",
"integrity": "sha512-P4l+K6wX1tf8LmWUvZofdQ+BgCNyk6Tb9u1H10npvqpuCD+dCM4pXIBq3PQcv/juUBOvLGGREo+Govuh3lfD0Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -606,6 +991,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.946.0.tgz",
"integrity": "sha512-/zeOJ6E7dGZQ/l2k7KytEoPJX0APIhwt0A79hPf/bUpMF4dDs2P6JmchDrotk0a0Y/MIdNF8sBQ/MEOPnBiYoQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -627,6 +1013,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.946.0.tgz",
"integrity": "sha512-Pdgcra3RivWj/TuZmfFaHbqsvvgnSKO0CxlRUMMr0PgBiCnUhyl+zBktdNOeGsOPH2fUzQpYhcUjYUgVSdcSDQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -652,6 +1039,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.946.0.tgz",
"integrity": "sha512-5iqLNc15u2Zx+7jOdQkIbP62N7n2031tw5hkmIG0DLnozhnk64osOh2CliiOE9x3c4P9Pf4frAwgyy9GzNTk2g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -671,6 +1059,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.946.0.tgz",
"integrity": "sha512-I7URUqnBPng1a5y81OImxrwERysZqMBREG6svhhGeZgxmqcpAZ8z5ywILeQXdEOCuuES8phUp/ojzxFjPXp/eA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/credential-provider-env": "3.946.0",
@@ -694,6 +1083,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.946.0.tgz",
"integrity": "sha512-GtGHX7OGqIeVQ3DlVm5RRF43Qmf3S1+PLJv9svrdvAhAdy2bUb044FdXXqrtSsIfpzTKlHgQUiRo5MWLd35Ntw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -711,6 +1101,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.946.0.tgz",
"integrity": "sha512-LeGSSt2V5iwYey1ENGY75RmoDP3bA2iE/py8QBKW8EDA8hn74XBLkprhrK5iccOvU3UGWY8WrEKFAFGNjJOL9g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/client-sso": "3.946.0",
@@ -730,6 +1121,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.946.0.tgz",
"integrity": "sha512-ocBCvjWfkbjxElBI1QUxOnHldsNhoU0uOICFvuRDAZAoxvypJHN3m5BJkqb7gqorBbcv3LRgmBdEnWXOAvq+7Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -778,15 +1170,15 @@
}
},
"node_modules/@aws-sdk/middleware-flexible-checksums": {
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.946.0.tgz",
"integrity": "sha512-HJA7RIWsnxcChyZ1hNF/3JICkYCqDonxoeG8FkrmLRBknZ8WVdJiPD420/UwrWaa5F2MuTDA92jxk77rI09h1w==",
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.947.0.tgz",
"integrity": "sha512-kXXxS2raNESNO+zR0L4YInVjhcGGNI2Mx0AE1ThRhDkAt2se3a+rGf9equ9YvOqA1m8Jl/GSI8cXYvSxXmS9Ag==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/crc32": "5.2.0",
"@aws-crypto/crc32c": "5.2.0",
"@aws-crypto/util": "5.2.0",
"@aws-sdk/core": "3.946.0",
"@aws-sdk/core": "3.947.0",
"@aws-sdk/types": "3.936.0",
"@smithy/is-array-buffer": "^4.2.0",
"@smithy/node-config-provider": "^4.3.5",
@@ -801,6 +1193,30 @@
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": {
"version": "3.947.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.947.0.tgz",
"integrity": "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==",
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/types": "3.936.0",
"@aws-sdk/xml-builder": "3.930.0",
"@smithy/core": "^3.18.7",
"@smithy/node-config-provider": "^4.3.5",
"@smithy/property-provider": "^4.2.5",
"@smithy/protocol-http": "^5.3.5",
"@smithy/signature-v4": "^5.3.5",
"@smithy/smithy-client": "^4.9.10",
"@smithy/types": "^4.9.0",
"@smithy/util-base64": "^4.3.0",
"@smithy/util-middleware": "^4.2.5",
"@smithy/util-utf8": "^4.2.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aws-sdk/middleware-host-header": {
"version": "3.936.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz",
@@ -864,6 +1280,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.946.0.tgz",
"integrity": "sha512-0UTFmFd8PX2k/jLu/DBmR+mmLQWAtUGHYps9Rjx3dcXNwaMLaa/39NoV3qn7Dwzfpqc6JZlZzBk+NDOCJIHW9g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -903,6 +1320,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.946.0.tgz",
"integrity": "sha512-7QcljCraeaWQNuqmOoAyZs8KpZcuhPiqdeeKoRd397jVGNRehLFsZbIMOvwaluUDFY11oMyXOkQEERe1Zo2fCw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -921,6 +1339,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.946.0.tgz",
"integrity": "sha512-rjAtEguukeW8mlyEQMQI56vxFoyWlaNwowmz1p1rav948SUjtrzjHAp4TOQWhibb7AR7BUTHBCgIcyCRjBEf4g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
@@ -986,6 +1405,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.946.0.tgz",
"integrity": "sha512-61FZ685lKiJuQ06g6U7K3PL9EwKCxNm51wNlxyKV57nnl1GrLD0NC8O3/hDNkCQLNBArT9y3IXl2H7TtIxP8Jg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/middleware-sdk-s3": "3.946.0",
@@ -1003,6 +1423,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.946.0.tgz",
"integrity": "sha512-a5c+rM6CUPX2ExmUZ3DlbLlS5rQr4tbdoGcgBsjnAHiYx8MuMNAI+8M7wfjF13i2yvUQj5WEIddvLpayfEZj9g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/core": "3.946.0",
@@ -1086,6 +1507,7 @@
"version": "3.946.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.946.0.tgz",
"integrity": "sha512-a2UwwvzbK5AxHKUBupfg4s7VnkqRAHjYsuezHnKCniczmT4HZfP1NnfwwvLKEH8qaTrwenxjKSfq4UWmWkvG+Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@aws-sdk/middleware-user-agent": "3.946.0",
@@ -3401,9 +3823,9 @@
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.7.tgz",
"integrity": "sha512-hFrTNZcMEG+k7qxVxZJq3F32Kms130FAhG8lvw2zkKBgAcNOJIxlljNiCjGygvBshvaGBdf88q2CqWtnqezDHA==",
"version": "16.0.8",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.8.tgz",
"integrity": "sha512-1miV0qXDcLUaOdHridVPCh4i39ElRIAraseVIbb3BEqyZ5ol9sPyjTP/GNTPV5rBxqxjF6/vv5zQTVbhiNaLqA==",
"license": "MIT",
"dependencies": {
"fast-glob": "3.3.1"
@@ -7066,6 +7488,39 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/@react-email/preview-server/node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@react-email/preview-server/node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.25.0"
},
"peerDependencies": {
"react": "^19.0.0"
}
},
"node_modules/@react-email/preview-server/node_modules/scheduler": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/@react-email/render": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@react-email/render/-/render-2.0.0.tgz",
@@ -9088,9 +9543,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"version": "24.10.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz",
"integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -9263,17 +9718,16 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz",
"integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz",
"integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==",
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.48.1",
"@typescript-eslint/type-utils": "8.48.1",
"@typescript-eslint/utils": "8.48.1",
"@typescript-eslint/visitor-keys": "8.48.1",
"graphemer": "^1.4.0",
"@typescript-eslint/scope-manager": "8.49.0",
"@typescript-eslint/type-utils": "8.49.0",
"@typescript-eslint/utils": "8.49.0",
"@typescript-eslint/visitor-keys": "8.49.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
@@ -9286,7 +9740,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.48.1",
"@typescript-eslint/parser": "^8.49.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -9301,15 +9755,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz",
"integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz",
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.48.1",
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/typescript-estree": "8.48.1",
"@typescript-eslint/visitor-keys": "8.48.1",
"@typescript-eslint/scope-manager": "8.49.0",
"@typescript-eslint/types": "8.49.0",
"@typescript-eslint/typescript-estree": "8.49.0",
"@typescript-eslint/visitor-keys": "8.49.0",
"debug": "^4.3.4"
},
"engines": {
@@ -9325,13 +9779,13 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz",
"integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz",
"integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.48.1",
"@typescript-eslint/types": "^8.48.1",
"@typescript-eslint/tsconfig-utils": "^8.49.0",
"@typescript-eslint/types": "^8.49.0",
"debug": "^4.3.4"
},
"engines": {
@@ -9346,13 +9800,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz",
"integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz",
"integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/visitor-keys": "8.48.1"
"@typescript-eslint/types": "8.49.0",
"@typescript-eslint/visitor-keys": "8.49.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9363,9 +9817,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz",
"integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz",
"integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9379,14 +9833,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz",
"integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz",
"integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/typescript-estree": "8.48.1",
"@typescript-eslint/utils": "8.48.1",
"@typescript-eslint/types": "8.49.0",
"@typescript-eslint/typescript-estree": "8.49.0",
"@typescript-eslint/utils": "8.49.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -9403,9 +9857,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz",
"integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz",
"integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==",
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9416,15 +9870,15 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz",
"integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz",
"integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.48.1",
"@typescript-eslint/tsconfig-utils": "8.48.1",
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/visitor-keys": "8.48.1",
"@typescript-eslint/project-service": "8.49.0",
"@typescript-eslint/tsconfig-utils": "8.49.0",
"@typescript-eslint/types": "8.49.0",
"@typescript-eslint/visitor-keys": "8.49.0",
"debug": "^4.3.4",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
@@ -9467,15 +9921,15 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz",
"integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz",
"integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==",
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.48.1",
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/typescript-estree": "8.48.1"
"@typescript-eslint/scope-manager": "8.49.0",
"@typescript-eslint/types": "8.49.0",
"@typescript-eslint/typescript-estree": "8.49.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -9490,12 +9944,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz",
"integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz",
"integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.48.1",
"@typescript-eslint/types": "8.49.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -13044,12 +13498,12 @@
}
},
"node_modules/eslint-config-next": {
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.7.tgz",
"integrity": "sha512-WubFGLFHfk2KivkdRGfx6cGSFhaQqhERRfyO8BRx+qiGPGp7WLKcPvYC4mdx1z3VhVRcrfFzczjjTrbJZOpnEQ==",
"version": "16.0.8",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.8.tgz",
"integrity": "sha512-8J5cOAboXIV3f8OD6BOyj7Fik6n/as7J4MboiUSExWruf/lCu1OPR3ZVSdnta6WhzebrmAATEmNSBZsLWA6kbg==",
"license": "MIT",
"dependencies": {
"@next/eslint-plugin-next": "16.0.7",
"@next/eslint-plugin-next": "16.0.8",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
@@ -14243,12 +14697,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"license": "MIT"
},
"node_modules/has-bigints": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@@ -22335,15 +22783,15 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.48.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz",
"integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==",
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz",
"integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==",
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.48.1",
"@typescript-eslint/parser": "8.48.1",
"@typescript-eslint/typescript-estree": "8.48.1",
"@typescript-eslint/utils": "8.48.1"
"@typescript-eslint/eslint-plugin": "8.49.0",
"@typescript-eslint/parser": "8.49.0",
"@typescript-eslint/typescript-estree": "8.49.0",
"@typescript-eslint/utils": "8.49.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@@ -166,7 +166,7 @@ export async function calculateUserClientsForOrgs(
];
// Get next available subnet
const newSubnet = await getNextAvailableClientSubnet(orgId);
const newSubnet = await getNextAvailableClientSubnet(orgId, transaction);
if (!newSubnet) {
logger.warn(
`Skipping org ${orgId} for OLM ${olm.olmId} (user ${userId}): no available subnet found`

View File

@@ -244,7 +244,8 @@ export function isIpInCidr(ip: string, cidr: string): boolean {
}
export async function getNextAvailableClientSubnet(
orgId: string
orgId: string,
transaction: Transaction | typeof db = db
): Promise<string> {
const [org] = await db.select().from(orgs).where(eq(orgs.orgId, orgId));

View File

@@ -701,11 +701,45 @@ async function handleSubnetProxyTargetUpdates(
}
for (const client of removedClients) {
// Check if this client still has access to another resource on this site with the same destination
const destinationStillInUse = await trx
.select()
.from(siteResources)
.innerJoin(
clientSiteResourcesAssociationsCache,
eq(
clientSiteResourcesAssociationsCache.siteResourceId,
siteResources.siteResourceId
)
)
.where(
and(
eq(
clientSiteResourcesAssociationsCache.clientId,
client.clientId
),
eq(siteResources.siteId, siteResource.siteId),
eq(
siteResources.destination,
siteResource.destination
),
ne(
siteResources.siteResourceId,
siteResource.siteResourceId
)
)
);
// Only remove remote subnet if no other resource uses the same destination
const remoteSubnetsToRemove = destinationStillInUse.length > 0
? []
: generateRemoteSubnets([siteResource]);
olmJobs.push(
removePeerData(
client.clientId,
siteResource.siteId,
generateRemoteSubnets([siteResource]),
remoteSubnetsToRemove,
generateAliasConfig([siteResource])
)
);
@@ -1213,12 +1247,46 @@ async function handleMessagesForClientResources(
}
try {
// Check if this client still has access to another resource on this site with the same destination
const destinationStillInUse = await trx
.select()
.from(siteResources)
.innerJoin(
clientSiteResourcesAssociationsCache,
eq(
clientSiteResourcesAssociationsCache.siteResourceId,
siteResources.siteResourceId
)
)
.where(
and(
eq(
clientSiteResourcesAssociationsCache.clientId,
client.clientId
),
eq(siteResources.siteId, resource.siteId),
eq(
siteResources.destination,
resource.destination
),
ne(
siteResources.siteResourceId,
resource.siteResourceId
)
)
);
// Only remove remote subnet if no other resource uses the same destination
const remoteSubnetsToRemove = destinationStillInUse.length > 0
? []
: generateRemoteSubnets([resource]);
// Remove peer data from olm
olmJobs.push(
removePeerData(
client.clientId,
resource.siteId,
generateRemoteSubnets([resource]),
remoteSubnetsToRemove,
generateAliasConfig([resource])
)
);

View File

@@ -189,7 +189,7 @@ export async function getTraefikConfig(
);
if (!validation.isValid) {
logger.error(
logger.debug(
`Invalid path rewrite configuration for resource ${resourceId}: ${validation.error}`
);
return;

View File

@@ -1,13 +0,0 @@
/*
* 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.
*/

View File

@@ -55,9 +55,9 @@ const processMessage = async (
try {
const message: WSMessage = JSON.parse(data.toString());
logger.debug(
`Processing message from ${clientType.toUpperCase()} ID: ${clientId}, type: ${message.type}`
);
// logger.debug(
// `Processing message from ${clientType.toUpperCase()} ID: ${clientId}, type: ${message.type}`
// );
if (!message.type || typeof message.type !== "string") {
throw new Error("Invalid message format: missing or invalid type");

View File

@@ -13,10 +13,12 @@ import { maxmindLookup } from "@server/db/maxmind";
import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
const bodySchema = z.object({
deviceName: z.string().optional(),
applicationName: z.string().min(1, "Application name is required")
}).strict();
const bodySchema = z
.object({
deviceName: z.string().optional(),
applicationName: z.string().min(1, "Application name is required")
})
.strict();
export type StartDeviceWebAuthBody = z.infer<typeof bodySchema>;
@@ -34,14 +36,12 @@ function generateDeviceCode(): string {
// Helper function to hash device code before storing in database
function hashDeviceCode(code: string): string {
return encodeHexLowerCase(
sha256(new TextEncoder().encode(code))
);
return encodeHexLowerCase(sha256(new TextEncoder().encode(code)));
}
// Helper function to extract IP from request
function extractIpFromRequest(req: Request): string | undefined {
const ip = req.ip || req.socket.remoteAddress;
const ip = req.ip;
if (!ip) {
return undefined;
}
@@ -75,10 +75,10 @@ async function getCityFromIp(ip: string): Promise<string | undefined> {
return undefined;
}
// MaxMind CountryResponse doesn't include city by default
// If city data is available, it would be in result.city?.names?.en
// But since we're using CountryResponse type, we'll just return undefined
// The user said "don't do this if not easy", so we'll skip city for now
if (result.country) {
return result.country.names?.en || result.country.iso_code;
}
return undefined;
} catch (error) {
logger.debug("Failed to get city from IP", error);

View File

@@ -1,5 +1,5 @@
import { sendToClient } from "#dynamic/routers/ws";
import { db, olms } from "@server/db";
import { db, olms, Transaction } from "@server/db";
import { Alias, SubnetProxyTarget } from "@server/lib/ip";
import logger from "@server/logger";
import { eq } from "drizzle-orm";

View File

@@ -14,12 +14,55 @@ import { build } from "@server/build";
// Track sites that are already offline to avoid unnecessary queries
const offlineSites = new Set<string>();
// Retry configuration for deadlock handling
const MAX_RETRIES = 3;
const BASE_DELAY_MS = 50;
interface PeerBandwidth {
publicKey: string;
bytesIn: number;
bytesOut: number;
}
/**
* Check if an error is a deadlock error
*/
function isDeadlockError(error: any): boolean {
return (
error?.code === "40P01" ||
error?.cause?.code === "40P01" ||
(error?.message && error.message.includes("deadlock"))
);
}
/**
* Execute a function with retry logic for deadlock handling
*/
async function withDeadlockRetry<T>(
operation: () => Promise<T>,
context: string
): Promise<T> {
let attempt = 0;
while (true) {
try {
return await operation();
} catch (error: any) {
if (isDeadlockError(error) && attempt < MAX_RETRIES) {
attempt++;
const baseDelay = Math.pow(2, attempt - 1) * BASE_DELAY_MS;
const jitter = Math.random() * baseDelay;
const delay = baseDelay + jitter;
logger.warn(
`Deadlock detected in ${context}, retrying attempt ${attempt}/${MAX_RETRIES} after ${delay.toFixed(0)}ms`
);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
export const receiveBandwidth = async (
req: Request,
res: Response,
@@ -60,201 +103,208 @@ export async function updateSiteBandwidth(
const currentTime = new Date();
const oneMinuteAgo = new Date(currentTime.getTime() - 60000); // 1 minute ago
// logger.debug(`Received data: ${JSON.stringify(bandwidthData)}`);
// Sort bandwidth data by publicKey to ensure consistent lock ordering across all instances
// This is critical for preventing deadlocks when multiple instances update the same sites
const sortedBandwidthData = [...bandwidthData].sort((a, b) =>
a.publicKey.localeCompare(b.publicKey)
);
await db.transaction(async (trx) => {
// First, handle sites that are actively reporting bandwidth
const activePeers = bandwidthData.filter((peer) => peer.bytesIn > 0); // Bytesout will have data as it tries to send keep alive messages
// First, handle sites that are actively reporting bandwidth
const activePeers = sortedBandwidthData.filter((peer) => peer.bytesIn > 0);
if (activePeers.length > 0) {
// Remove any active peers from offline tracking since they're sending data
activePeers.forEach((peer) => offlineSites.delete(peer.publicKey));
// Aggregate usage data by organization (collected outside transaction)
const orgUsageMap = new Map<string, number>();
const orgUptimeMap = new Map<string, number>();
// Aggregate usage data by organization
const orgUsageMap = new Map<string, number>();
const orgUptimeMap = new Map<string, number>();
if (activePeers.length > 0) {
// Remove any active peers from offline tracking since they're sending data
activePeers.forEach((peer) => offlineSites.delete(peer.publicKey));
// Update all active sites with bandwidth data and get the site data in one operation
const updatedSites = [];
for (const peer of activePeers) {
const [updatedSite] = await trx
.update(sites)
.set({
megabytesOut: sql`${sites.megabytesOut} + ${peer.bytesIn}`,
megabytesIn: sql`${sites.megabytesIn} + ${peer.bytesOut}`,
lastBandwidthUpdate: currentTime.toISOString(),
online: true
})
.where(eq(sites.pubKey, peer.publicKey))
.returning({
online: sites.online,
orgId: sites.orgId,
siteId: sites.siteId,
lastBandwidthUpdate: sites.lastBandwidthUpdate
});
// Update each active site individually with retry logic
// This reduces transaction scope and allows retries per-site
for (const peer of activePeers) {
try {
const updatedSite = await withDeadlockRetry(async () => {
const [result] = await db
.update(sites)
.set({
megabytesOut: sql`${sites.megabytesOut} + ${peer.bytesIn}`,
megabytesIn: sql`${sites.megabytesIn} + ${peer.bytesOut}`,
lastBandwidthUpdate: currentTime.toISOString(),
online: true
})
.where(eq(sites.pubKey, peer.publicKey))
.returning({
online: sites.online,
orgId: sites.orgId,
siteId: sites.siteId,
lastBandwidthUpdate: sites.lastBandwidthUpdate
});
return result;
}, `update active site ${peer.publicKey}`);
if (updatedSite) {
if (exitNodeId) {
if (
await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId,
trx
)
) {
// not allowed
const notAllowed = await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId
);
if (notAllowed) {
logger.warn(
`Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}`
);
// THIS SHOULD TRIGGER THE TRANSACTION TO FAIL?
throw new Error("Exit node not allowed");
// Skip this site but continue processing others
continue;
}
}
updatedSites.push({ ...updatedSite, peer });
}
}
// Calculate org usage aggregations using the updated site data
for (const { peer, ...site } of updatedSites) {
// Aggregate bandwidth usage for the org
const totalBandwidth = peer.bytesIn + peer.bytesOut;
const currentOrgUsage = orgUsageMap.get(site.orgId) || 0;
orgUsageMap.set(site.orgId, currentOrgUsage + totalBandwidth);
// Add 10 seconds of uptime for each active site
const currentOrgUptime = orgUptimeMap.get(site.orgId) || 0;
orgUptimeMap.set(site.orgId, currentOrgUptime + 10 / 60); // Store in minutes and jut add 10 seconds
}
if (calcUsageAndLimits) {
// REMOTE EXIT NODES DO NOT COUNT TOWARDS USAGE
// Process all usage updates sequentially by organization to reduce deadlock risk
const allOrgIds = new Set([...orgUsageMap.keys(), ...orgUptimeMap.keys()]);
for (const orgId of allOrgIds) {
try {
// Process bandwidth usage for this org
const totalBandwidth = orgUsageMap.get(orgId);
if (totalBandwidth) {
const bandwidthUsage = await usageService.add(
orgId,
FeatureId.EGRESS_DATA_MB,
totalBandwidth,
trx
);
if (bandwidthUsage) {
usageService
.checkLimitSet(
orgId,
true,
FeatureId.EGRESS_DATA_MB,
bandwidthUsage,
trx
)
.catch((error: any) => {
logger.error(
`Error checking bandwidth limits for org ${orgId}:`,
error
);
});
}
}
// Process uptime usage for this org
const totalUptime = orgUptimeMap.get(orgId);
if (totalUptime) {
const uptimeUsage = await usageService.add(
orgId,
FeatureId.SITE_UPTIME,
totalUptime,
trx
);
if (uptimeUsage) {
usageService
.checkLimitSet(
orgId,
true,
FeatureId.SITE_UPTIME,
uptimeUsage,
trx
)
.catch((error: any) => {
logger.error(
`Error checking uptime limits for org ${orgId}:`,
error
);
});
}
}
} catch (error) {
logger.error(
`Error processing usage for org ${orgId}:`,
error
);
// Don't break the loop, continue with other orgs
}
// Aggregate bandwidth usage for the org
const totalBandwidth = peer.bytesIn + peer.bytesOut;
const currentOrgUsage = orgUsageMap.get(updatedSite.orgId) || 0;
orgUsageMap.set(updatedSite.orgId, currentOrgUsage + totalBandwidth);
// Add 10 seconds of uptime for each active site
const currentOrgUptime = orgUptimeMap.get(updatedSite.orgId) || 0;
orgUptimeMap.set(updatedSite.orgId, currentOrgUptime + 10 / 60);
}
} catch (error) {
logger.error(
`Failed to update bandwidth for site ${peer.publicKey}:`,
error
);
// Continue with other sites
}
}
}
// Handle sites that reported zero bandwidth but need online status updated
const zeroBandwidthPeers = bandwidthData.filter(
(peer) => peer.bytesIn === 0 && !offlineSites.has(peer.publicKey) // Bytesout will have data as it tries to send keep alive messages
);
// Process usage updates outside of site update transactions
// This separates the concerns and reduces lock contention
if (calcUsageAndLimits && (orgUsageMap.size > 0 || orgUptimeMap.size > 0)) {
// Sort org IDs to ensure consistent lock ordering
const allOrgIds = [...new Set([...orgUsageMap.keys(), ...orgUptimeMap.keys()])].sort();
if (zeroBandwidthPeers.length > 0) {
const zeroBandwidthSites = await trx
.select()
.from(sites)
.where(
inArray(
sites.pubKey,
zeroBandwidthPeers.map((p) => p.publicKey)
)
);
for (const site of zeroBandwidthSites) {
let newOnlineStatus = site.online;
// Check if site should go offline based on last bandwidth update WITH DATA
if (site.lastBandwidthUpdate) {
const lastUpdateWithData = new Date(
site.lastBandwidthUpdate
for (const orgId of allOrgIds) {
try {
// Process bandwidth usage for this org
const totalBandwidth = orgUsageMap.get(orgId);
if (totalBandwidth) {
const bandwidthUsage = await usageService.add(
orgId,
FeatureId.EGRESS_DATA_MB,
totalBandwidth
);
if (lastUpdateWithData < oneMinuteAgo) {
newOnlineStatus = false;
if (bandwidthUsage) {
// Fire and forget - don't block on limit checking
usageService
.checkLimitSet(
orgId,
true,
FeatureId.EGRESS_DATA_MB,
bandwidthUsage
)
.catch((error: any) => {
logger.error(
`Error checking bandwidth limits for org ${orgId}:`,
error
);
});
}
} else {
// No previous data update recorded, set to offline
newOnlineStatus = false;
}
// Always update lastBandwidthUpdate to show this instance is receiving reports
// Only update online status if it changed
if (site.online !== newOnlineStatus) {
const [updatedSite] = await trx
.update(sites)
.set({
online: newOnlineStatus
})
.where(eq(sites.siteId, site.siteId))
.returning();
// Process uptime usage for this org
const totalUptime = orgUptimeMap.get(orgId);
if (totalUptime) {
const uptimeUsage = await usageService.add(
orgId,
FeatureId.SITE_UPTIME,
totalUptime
);
if (uptimeUsage) {
// Fire and forget - don't block on limit checking
usageService
.checkLimitSet(
orgId,
true,
FeatureId.SITE_UPTIME,
uptimeUsage
)
.catch((error: any) => {
logger.error(
`Error checking uptime limits for org ${orgId}:`,
error
);
});
}
}
} catch (error) {
logger.error(
`Error processing usage for org ${orgId}:`,
error
);
// Continue with other orgs
}
}
}
// Handle sites that reported zero bandwidth but need online status updated
const zeroBandwidthPeers = sortedBandwidthData.filter(
(peer) => peer.bytesIn === 0 && !offlineSites.has(peer.publicKey)
);
if (zeroBandwidthPeers.length > 0) {
// Fetch all zero bandwidth sites in one query
const zeroBandwidthSites = await db
.select()
.from(sites)
.where(
inArray(
sites.pubKey,
zeroBandwidthPeers.map((p) => p.publicKey)
)
);
// Sort by siteId to ensure consistent lock ordering
const sortedZeroBandwidthSites = zeroBandwidthSites.sort(
(a, b) => a.siteId - b.siteId
);
for (const site of sortedZeroBandwidthSites) {
let newOnlineStatus = site.online;
// Check if site should go offline based on last bandwidth update WITH DATA
if (site.lastBandwidthUpdate) {
const lastUpdateWithData = new Date(site.lastBandwidthUpdate);
if (lastUpdateWithData < oneMinuteAgo) {
newOnlineStatus = false;
}
} else {
// No previous data update recorded, set to offline
newOnlineStatus = false;
}
// Only update online status if it changed
if (site.online !== newOnlineStatus) {
try {
const updatedSite = await withDeadlockRetry(async () => {
const [result] = await db
.update(sites)
.set({
online: newOnlineStatus
})
.where(eq(sites.siteId, site.siteId))
.returning();
return result;
}, `update offline status for site ${site.siteId}`);
if (updatedSite && exitNodeId) {
if (
await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId,
trx
)
) {
// not allowed
const notAllowed = await checkExitNodeOrg(
exitNodeId,
updatedSite.orgId
);
if (notAllowed) {
logger.warn(
`Exit node ${exitNodeId} is not allowed for org ${updatedSite.orgId}`
);
// THIS SHOULD TRIGGER THE TRANSACTION TO FAIL?
throw new Error("Exit node not allowed");
}
}
@@ -262,8 +312,14 @@ export async function updateSiteBandwidth(
if (!newOnlineStatus && site.pubKey) {
offlineSites.add(site.pubKey);
}
} catch (error) {
logger.error(
`Failed to update offline status for site ${site.siteId}:`,
error
);
// Continue with other sites
}
}
}
});
}
}

View File

@@ -15,6 +15,7 @@ import {
import { verifyPassword } from "@server/auth/password";
import logger from "@server/logger";
import config from "@server/lib/config";
import { APP_VERSION } from "@server/lib/consts";
export const newtGetTokenBodySchema = z.object({
newtId: z.string(),
@@ -94,9 +95,10 @@ export async function getNewtToken(
const resToken = generateSessionToken();
await createNewtSession(resToken, existingNewt.newtId);
return response<{ token: string }>(res, {
return response<{ token: string; serverVersion: string }>(res, {
data: {
token: resToken
token: resToken,
serverVersion: APP_VERSION
},
success: true,
error: false,

View File

@@ -22,6 +22,7 @@ import {
import { verifyPassword } from "@server/auth/password";
import logger from "@server/logger";
import config from "@server/lib/config";
import { APP_VERSION } from "@server/lib/consts";
export const olmGetTokenBodySchema = z.object({
olmId: z.string(),
@@ -205,10 +206,12 @@ export async function getOlmToken(
return response<{
token: string;
exitNodes: { publicKey: string; endpoint: string }[];
serverVersion: string;
}>(res, {
data: {
token: resToken,
exitNodes: exitNodesHpData
exitNodes: exitNodesHpData,
serverVersion: APP_VERSION
},
success: true,
error: false,

View File

@@ -1,5 +1,5 @@
import { build } from "@server/build";
import { db, sessionTransferToken } from "@server/db";
import { db, deviceWebAuthCodes, sessionTransferToken } from "@server/db";
import {
emailVerificationCodes,
newtSessions,
@@ -89,4 +89,12 @@ export async function clearStaleData() {
logger.warn("Error clearing expired sessionTransferToken:", e);
}
}
try {
await db
.delete(deviceWebAuthCodes)
.where(lt(deviceWebAuthCodes.expiresAt, new Date().getTime()));
} catch (e) {
logger.warn("Error clearing expired deviceWebAuthCodes:", e);
}
}

View File

@@ -198,27 +198,25 @@ export default function CredentialsPage() {
</SettingsSectionBody>
{build !== "oss" && (
<SettingsSectionFooter>
<div className="flex gap-2">
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("remoteExitNodeRegenerateAndDisconnect")}
</Button>
</div>
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("remoteExitNodeRegenerateAndDisconnect")}
</Button>
</SettingsSectionFooter>
)}
</SettingsSection>

View File

@@ -182,27 +182,25 @@ export default function CredentialsPage() {
</SettingsSectionBody>
{build !== "oss" && (
<SettingsSectionFooter>
<div className="flex gap-2">
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("clientRegenerateAndDisconnect")}
</Button>
</div>
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("clientRegenerateAndDisconnect")}
</Button>
</SettingsSectionFooter>
)}
</SettingsSection>
@@ -229,9 +227,7 @@ export default function CredentialsPage() {
)}
</p>
<p>
{t(
"clientRegenerateAndDisconnectWarning"
)}
{t("clientRegenerateAndDisconnectWarning")}
</p>
</>
) : (
@@ -241,9 +237,7 @@ export default function CredentialsPage() {
"clientRegenerateCredentialsConfirmation"
)}
</p>
<p>
{t("clientRegenerateCredentialsWarning")}
</p>
<p>{t("clientRegenerateCredentialsWarning")}</p>
</>
)}
</div>

View File

@@ -136,7 +136,7 @@ export default function Page() {
All: [
{
title: t("install"),
command: `curl -fsSL https://pangolin.net/get-olm.sh | bash`
command: `curl -fsSL https://static.pangolin.net/get-olm.sh | bash`
},
{
title: t("run"),

View File

@@ -931,9 +931,10 @@ export default function ReverseProxyTargets(props: {
openHealthCheckDialog(row.original)
}
>
<Settings className="h-4 w-4" />
<div className="flex items-center gap-1">
{getStatusIcon(status)}
<div
className={`flex items-center gap-1 ${status === "healthy" ? "text-green-500" : status === "unhealthy" ? "text-destructive" : ""}`}
>
<Settings className="h-4 w-4" />
{getStatusText(status)}
</div>
</Button>

View File

@@ -265,27 +265,25 @@ export default function CredentialsPage() {
</SettingsSectionBody>
{build !== "oss" && (
<SettingsSectionFooter>
<div className="flex gap-2">
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("siteRegenerateAndDisconnect")}
</Button>
</div>
<Button
variant="outline"
onClick={() => {
setShouldDisconnect(false);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("regenerateCredentialsButton")}
</Button>
<Button
onClick={() => {
setShouldDisconnect(true);
setModalOpen(true);
}}
disabled={isSecurityFeatureDisabled()}
>
{t("siteRegenerateAndDisconnect")}
</Button>
</SettingsSectionFooter>
)}
</SettingsSection>

View File

@@ -233,7 +233,7 @@ export default function Page() {
All: [
{
title: t("install"),
command: `curl -fsSL https://pangolin.net/get-newt.sh | bash`
command: `curl -fsSL https://static.pangolin.net/get-newt.sh | bash`
},
{
title: t("run"),

View File

@@ -20,6 +20,7 @@ import {
import { useTranslations } from "next-intl";
import { Transition } from "@headlessui/react";
import * as React from "react";
import { gt, valid } from "semver";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Button } from "./ui/button";
import { Badge } from "./ui/badge";
@@ -72,11 +73,15 @@ export default function ProductUpdates({
if (!data) return null;
const latestVersion = data?.latestVersion?.data?.pangolin.latestVersion;
const currentVersion = env.app.version;
const showNewVersionPopup = Boolean(
data?.latestVersion?.data &&
ignoredVersionUpdate !==
data.latestVersion.data?.pangolin.latestVersion &&
env.app.version !== data.latestVersion.data?.pangolin.latestVersion
latestVersion &&
valid(latestVersion) &&
valid(currentVersion) &&
ignoredVersionUpdate !== latestVersion &&
gt(latestVersion, currentVersion)
);
const filteredUpdates = data.updates.filter(

View File

@@ -66,7 +66,7 @@ export function SettingsSectionFooter({
children: React.ReactNode;
}) {
return (
<div className="flex justify-end space-x-2 mt-auto pt-6">
<div className="flex flex-col md:flex-row justify-end space-y-2 md:space-y-0 md:space-x-2 mt-auto pt-6">
{children}
</div>
);

View File