mirror of
https://github.com/fosrl/pangolin.git
synced 2026-04-04 08:56:37 +00:00
Compare commits
89 Commits
1.17.0-s.0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3436105bec | ||
|
|
d948d2ec33 | ||
|
|
4b3375ab8e | ||
|
|
6b8a3c8d77 | ||
|
|
ba9794c067 | ||
|
|
6ce165bfd5 | ||
|
|
eb4b2daaab | ||
|
|
8cbc8dec89 | ||
|
|
e89e60d50b | ||
|
|
c45308f234 | ||
|
|
40205c40c5 | ||
|
|
f3fe2dd33b | ||
|
|
8edcc45033 | ||
|
|
91471a4aca | ||
|
|
ae2c37a2f6 | ||
|
|
c8208f0a88 | ||
|
|
e11dfbd29c | ||
|
|
b375d20598 | ||
|
|
c4b82c69f8 | ||
|
|
c9a00420a0 | ||
|
|
36ef9cd442 | ||
|
|
5e08779ab0 | ||
|
|
16a0e1ce7b | ||
|
|
8b03484ade | ||
|
|
9da9974adf | ||
|
|
6f80cf3db2 | ||
|
|
76d8f44779 | ||
|
|
700c92efcb | ||
|
|
d17e0c9f50 | ||
|
|
f00b9794f5 | ||
|
|
daff59c93f | ||
|
|
aa8954366c | ||
|
|
87464d53bd | ||
|
|
e04f17c9aa | ||
|
|
b25e3499d8 | ||
|
|
2e6f74a6f8 | ||
|
|
8eee0ca5a5 | ||
|
|
c2ebc0a0ff | ||
|
|
03c905a7af | ||
|
|
035644eaf7 | ||
|
|
8ce45a1acd | ||
|
|
16e7233a3e | ||
|
|
a331dd3fb4 | ||
|
|
e3e2938b28 | ||
|
|
73e96b1b28 | ||
|
|
b8194295ec | ||
|
|
382a46dfff | ||
|
|
f85cfc4c68 | ||
|
|
0b2aceafe0 | ||
|
|
059db34a53 | ||
|
|
bc1ea86b4e | ||
|
|
9f2ced1933 | ||
|
|
013cff9b6e | ||
|
|
aa19437031 | ||
|
|
e848ef848b | ||
|
|
bb6605337f | ||
|
|
8df8383468 | ||
|
|
a7e9de3ac4 | ||
|
|
8df41f514e | ||
|
|
c2bf50b121 | ||
|
|
4e7dcbd7b5 | ||
|
|
f1a0bc97e3 | ||
|
|
a57dfd1d12 | ||
|
|
c0a8304b91 | ||
|
|
ab7b968e28 | ||
|
|
f10b40c3b0 | ||
|
|
7878ac9c76 | ||
|
|
0752951842 | ||
|
|
06bb6636a1 | ||
|
|
2fdd332a31 | ||
|
|
98b1e9546a | ||
|
|
184aa65c6d | ||
|
|
70b3a432a4 | ||
|
|
fb4fc75bd8 | ||
|
|
0479ed9e7f | ||
|
|
8f3fbb94d2 | ||
|
|
e8c35bec1c | ||
|
|
728e7252eb | ||
|
|
1218507f7d | ||
|
|
a2dff0a35d | ||
|
|
f411180908 | ||
|
|
231a19b679 | ||
|
|
58a87a986a | ||
|
|
61a78ef352 | ||
|
|
e28e5ebb4e | ||
|
|
19cef8c453 | ||
|
|
1290d6cd5c | ||
|
|
ad301074db | ||
|
|
30a756d254 |
@@ -86,6 +86,8 @@ entryPoints:
|
|||||||
http:
|
http:
|
||||||
tls:
|
tls:
|
||||||
certResolver: "letsencrypt"
|
certResolver: "letsencrypt"
|
||||||
|
middlewares:
|
||||||
|
- crowdsec@file
|
||||||
encodedCharacters:
|
encodedCharacters:
|
||||||
allowEncodedSlash: true
|
allowEncodedSlash: true
|
||||||
allowEncodedQuestionMark: true
|
allowEncodedQuestionMark: true
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Ключът за осигуряване е актуализиран",
|
"provisioningKeysUpdated": "Ключът за осигуряване е актуализиран",
|
||||||
"provisioningKeysUpdatedDescription": "Вашите промени бяха запазени.",
|
"provisioningKeysUpdatedDescription": "Вашите промени бяха запазени.",
|
||||||
"provisioningKeysBannerTitle": "Ключове за осигуряване на сайта",
|
"provisioningKeysBannerTitle": "Ключове за осигуряване на сайта",
|
||||||
"provisioningKeysBannerDescription": "Генерирайте ключ за осигуряване и го използвайте с Newt конектора за автоматично създаване на сайтове при първото стартиране — няма нужда от създаване на отделни идентификационни данни за всеки сайт.",
|
"provisioningKeysBannerDescription": "Генерирайте ключ за осигуряване и го използвайте със съединителя Newt за автоматично създаване на сайтове при първоначално стартиране - не е необходимо да се създават отделни идентификационни данни за всеки сайт.",
|
||||||
"provisioningKeysBannerButtonText": "Научете повече",
|
"provisioningKeysBannerButtonText": "Научете повече",
|
||||||
"pendingSitesBannerTitle": "Чакащи сайтове",
|
"pendingSitesBannerTitle": "Чакащи сайтове",
|
||||||
"pendingSitesBannerDescription": "Сайтовете, които се свързват чрез ключ за осигуряване, се появяват тук за преглед. Одобрете всеки сайт, преди да стане активен и да получи достъп до вашите ресурси.",
|
"pendingSitesBannerDescription": "Сайтовете, които се свързват с ключ за осигуряване, ще се появят тук за преглед.",
|
||||||
"pendingSitesBannerButtonText": "Научете повече",
|
"pendingSitesBannerButtonText": "Научете повече",
|
||||||
"apiKeysSettings": "Настройки на {apiKeyName}",
|
"apiKeysSettings": "Настройки на {apiKeyName}",
|
||||||
"userTitle": "Управление на всички потребители",
|
"userTitle": "Управление на всички потребители",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Моля, въведете валиден номер на порт",
|
"targetErrorInvalidPortDescription": "Моля, въведете валиден номер на порт",
|
||||||
"targetErrorNoSite": "Няма избран сайт",
|
"targetErrorNoSite": "Няма избран сайт",
|
||||||
"targetErrorNoSiteDescription": "Моля, изберете сайт за целта",
|
"targetErrorNoSiteDescription": "Моля, изберете сайт за целта",
|
||||||
|
"targetTargetsCleared": "Мишените са премахнати",
|
||||||
|
"targetTargetsClearedDescription": "Всички цели са били премахнати от този ресурс",
|
||||||
"targetCreated": "Целта е създадена",
|
"targetCreated": "Целта е създадена",
|
||||||
"targetCreatedDescription": "Целта беше успешно създадена",
|
"targetCreatedDescription": "Целта беше успешно създадена",
|
||||||
"targetErrorCreate": "Неуспешно създаване на целта",
|
"targetErrorCreate": "Неуспешно създаване на целта",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Предприятие, 50 потребители, 50 сайта и приоритетна поддръжка."
|
"description": "Предприятие, 50 потребители, 50 сайта и приоритетна поддръжка."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Само за лична употреба (безплатен лиценз — без плащане)",
|
"personalUseOnly": "Само за лична употреба (безплатен лиценз - без проверка)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Продължете към плащане"
|
"continueToCheckout": "Продължете към плащане"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Машинни клиенти",
|
"machineClients": "Машинни клиенти",
|
||||||
"install": "Инсталирай",
|
"install": "Инсталирай",
|
||||||
"run": "Изпълни",
|
"run": "Изпълни",
|
||||||
|
"envFile": "Файл за среда",
|
||||||
|
"serviceFile": "Файл за услуга",
|
||||||
|
"enableAndStart": "Активиране и стартиране",
|
||||||
"clientNameDescription": "Показваното име на клиента, което може да се промени по-късно.",
|
"clientNameDescription": "Показваното име на клиента, което може да се промени по-късно.",
|
||||||
"clientAddress": "Клиентски адрес (Разширено)",
|
"clientAddress": "Клиентски адрес (Разширено)",
|
||||||
"setupFailedToFetchSubnet": "Неуспешно извличане на подмрежа по подразбиране",
|
"setupFailedToFetchSubnet": "Неуспешно извличане на подмрежа по подразбиране",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Без удостоверяване",
|
"httpDestAuthNoneTitle": "Без удостоверяване",
|
||||||
"httpDestAuthNoneDescription": "Изпращане на заявки без заглавие за удостоверяване.",
|
"httpDestAuthNoneDescription": "Изпращане на заявки без заглавие за удостоверяване.",
|
||||||
"httpDestAuthBearerTitle": "Bearer Токен",
|
"httpDestAuthBearerTitle": "Bearer Токен",
|
||||||
"httpDestAuthBearerDescription": "Добавя заглавие за удостоверяване Bearer <token> към всяка заявка.",
|
"httpDestAuthBearerDescription": "Добавя заглавие Authorization: Bearer '<token>' към всяка заявка.",
|
||||||
"httpDestAuthBearerPlaceholder": "Вашият API ключ или токен",
|
"httpDestAuthBearerPlaceholder": "Вашият API ключ или токен",
|
||||||
"httpDestAuthBasicTitle": "Основно удостоверяване",
|
"httpDestAuthBasicTitle": "Основно удостоверяване",
|
||||||
"httpDestAuthBasicDescription": "Добавя заглавие за удостоверяване Basic <credentials> към всяка заявка. Осигурете идентификационни данни като потребителско име:парола.",
|
"httpDestAuthBasicDescription": "Добавя заглавие Authorization: Basic '<credentials>'. Осигурете идентификационни данни като потребителско име:парола.",
|
||||||
"httpDestAuthBasicPlaceholder": "потребителско име:парола",
|
"httpDestAuthBasicPlaceholder": "потребителско име:парола",
|
||||||
"httpDestAuthCustomTitle": "Персонализирано заглавие",
|
"httpDestAuthCustomTitle": "Персонализирано заглавие",
|
||||||
"httpDestAuthCustomDescription": "Посочете персонализирано име и стойност на заглавието за удостоверяване (например X-API-Key).",
|
"httpDestAuthCustomDescription": "Посочете персонализирано име и стойност на заглавието за удостоверяване (например X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Zajišťovací klíč byl aktualizován",
|
"provisioningKeysUpdated": "Zajišťovací klíč byl aktualizován",
|
||||||
"provisioningKeysUpdatedDescription": "Vaše změny byly uloženy.",
|
"provisioningKeysUpdatedDescription": "Vaše změny byly uloženy.",
|
||||||
"provisioningKeysBannerTitle": "Klíče pro poskytování webu",
|
"provisioningKeysBannerTitle": "Klíče pro poskytování webu",
|
||||||
"provisioningKeysBannerDescription": "Vygenerujte konfigurační klíč a používejte jej pomocí nového konektoru k automatickému vytváření stránek při prvním startu – není třeba nastavovat samostatné přihlašovací údaje pro každý web.",
|
"provisioningKeysBannerDescription": "Vygenerujte klíč pro zřízení a použijte ho s Newt konektorem k automatickému vytvoření stránek při prvním spuštění – není potřeba nastavit samostatné přihlašovací údaje pro každou stránku.",
|
||||||
"provisioningKeysBannerButtonText": "Zjistit více",
|
"provisioningKeysBannerButtonText": "Zjistit více",
|
||||||
"pendingSitesBannerTitle": "Nevyřízené weby",
|
"pendingSitesBannerTitle": "Nevyřízené weby",
|
||||||
"pendingSitesBannerDescription": "Zde se zobrazují stránky, které se připojují pomocí doplňovacího klíče. Schválte každý web předtím, než bude aktivní, a získejte přístup k vašim zdrojům.",
|
"pendingSitesBannerDescription": "Stránky, které se připojují pomocí klíče pro zřízení, se zde objeví ke kontrole.",
|
||||||
"pendingSitesBannerButtonText": "Zjistit více",
|
"pendingSitesBannerButtonText": "Zjistit více",
|
||||||
"apiKeysSettings": "Nastavení {apiKeyName}",
|
"apiKeysSettings": "Nastavení {apiKeyName}",
|
||||||
"userTitle": "Spravovat všechny uživatele",
|
"userTitle": "Spravovat všechny uživatele",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Zadejte platné číslo portu",
|
"targetErrorInvalidPortDescription": "Zadejte platné číslo portu",
|
||||||
"targetErrorNoSite": "Není vybrán žádný web",
|
"targetErrorNoSite": "Není vybrán žádný web",
|
||||||
"targetErrorNoSiteDescription": "Vyberte prosím web pro cíl",
|
"targetErrorNoSiteDescription": "Vyberte prosím web pro cíl",
|
||||||
|
"targetTargetsCleared": "Cíle vymazány",
|
||||||
|
"targetTargetsClearedDescription": "Všechny cíle byly odstraněny z tohoto zdroje",
|
||||||
"targetCreated": "Cíl byl vytvořen",
|
"targetCreated": "Cíl byl vytvořen",
|
||||||
"targetCreatedDescription": "Cíl byl úspěšně vytvořen",
|
"targetCreatedDescription": "Cíl byl úspěšně vytvořen",
|
||||||
"targetErrorCreate": "Nepodařilo se vytvořit cíl",
|
"targetErrorCreate": "Nepodařilo se vytvořit cíl",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Podnikové funkce, 50 uživatelů, 50 míst a prioritní podpory."
|
"description": "Podnikové funkce, 50 uživatelů, 50 míst a prioritní podpory."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Pouze osobní použití (bezplatná licence – bez platby)",
|
"personalUseOnly": "Pouze pro osobní použití (zdarma licence - bez ověření)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Pokračovat do pokladny"
|
"continueToCheckout": "Pokračovat do pokladny"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Strojoví klienti",
|
"machineClients": "Strojoví klienti",
|
||||||
"install": "Instalovat",
|
"install": "Instalovat",
|
||||||
"run": "Spustit",
|
"run": "Spustit",
|
||||||
|
"envFile": "Konfigurační soubor prostředí",
|
||||||
|
"serviceFile": "Služební soubor",
|
||||||
|
"enableAndStart": "Povolit a spustit",
|
||||||
"clientNameDescription": "Zobrazované jméno klienta, které lze později změnit.",
|
"clientNameDescription": "Zobrazované jméno klienta, které lze později změnit.",
|
||||||
"clientAddress": "Adresa klienta (Rozšířeno)",
|
"clientAddress": "Adresa klienta (Rozšířeno)",
|
||||||
"setupFailedToFetchSubnet": "Nepodařilo se načíst výchozí podsíť",
|
"setupFailedToFetchSubnet": "Nepodařilo se načíst výchozí podsíť",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Žádné ověření",
|
"httpDestAuthNoneTitle": "Žádné ověření",
|
||||||
"httpDestAuthNoneDescription": "Odešle žádosti bez záhlaví autorizace.",
|
"httpDestAuthNoneDescription": "Odešle žádosti bez záhlaví autorizace.",
|
||||||
"httpDestAuthBearerTitle": "Token na doručitele",
|
"httpDestAuthBearerTitle": "Token na doručitele",
|
||||||
"httpDestAuthBearerDescription": "Přidá autorizaci: Hlavička Bearer <token> ke každému požadavku.",
|
"httpDestAuthBearerDescription": "Přidává hlavičku Authorization: Bearer '<token>' k každému požadavku.",
|
||||||
"httpDestAuthBearerPlaceholder": "Váš API klíč nebo token",
|
"httpDestAuthBearerPlaceholder": "Váš API klíč nebo token",
|
||||||
"httpDestAuthBasicTitle": "Základní ověření",
|
"httpDestAuthBasicTitle": "Základní ověření",
|
||||||
"httpDestAuthBasicDescription": "Přidá autorizaci: Základní <credentials> hlavička. Poskytněte přihlašovací údaje jako uživatelské jméno:password.",
|
"httpDestAuthBasicDescription": "Přidává hlavičku Authorization: Basic '<credentials>'. Poskytněte přihlašovací údaje ve formátu uživatelské jméno:heslo.",
|
||||||
"httpDestAuthBasicPlaceholder": "uživatelské jméno:heslo",
|
"httpDestAuthBasicPlaceholder": "uživatelské jméno:heslo",
|
||||||
"httpDestAuthCustomTitle": "Vlastní záhlaví",
|
"httpDestAuthCustomTitle": "Vlastní záhlaví",
|
||||||
"httpDestAuthCustomDescription": "Zadejte název a hodnotu vlastního HTTP hlavičky pro ověření (např. X-API-Key).",
|
"httpDestAuthCustomDescription": "Zadejte název a hodnotu vlastního HTTP hlavičky pro ověření (např. X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Bereitstellungsschlüssel aktualisiert",
|
"provisioningKeysUpdated": "Bereitstellungsschlüssel aktualisiert",
|
||||||
"provisioningKeysUpdatedDescription": "Ihre Änderungen wurden gespeichert.",
|
"provisioningKeysUpdatedDescription": "Ihre Änderungen wurden gespeichert.",
|
||||||
"provisioningKeysBannerTitle": "Website-Bereitstellungsschlüssel",
|
"provisioningKeysBannerTitle": "Website-Bereitstellungsschlüssel",
|
||||||
"provisioningKeysBannerDescription": "Generieren Sie einen Bereitstellungsschlüssel und verwenden Sie ihn mit dem Newt-Konnektor, um beim ersten Start automatisch Sites zu erstellen – keine Notwendigkeit, separate Anmeldeinformationen für jede Seite einzurichten.",
|
"provisioningKeysBannerDescription": "Generieren Sie einen Bereitstellungsschlüssel und verwenden Sie ihn mit dem Newt-Connector, um Standorte beim ersten Start automatisch zu erstellen - keine Notwendigkeit, separate Anmeldedaten für jede Seite einzurichten.",
|
||||||
"provisioningKeysBannerButtonText": "Mehr erfahren",
|
"provisioningKeysBannerButtonText": "Mehr erfahren",
|
||||||
"pendingSitesBannerTitle": "Ausstehende Seiten",
|
"pendingSitesBannerTitle": "Ausstehende Seiten",
|
||||||
"pendingSitesBannerDescription": "Sites, die sich mit einem Bereitstellungsschlüssel verbinden, erscheinen hier zur Überprüfung. Bestätigen Sie jede Site, bevor sie aktiv wird und erhalten Zugriff auf Ihre Ressourcen.",
|
"pendingSitesBannerDescription": "Websites, die mit einem Bereitstellungsschlüssel verbunden sind, erscheinen hier zur Überprüfung.",
|
||||||
"pendingSitesBannerButtonText": "Mehr erfahren",
|
"pendingSitesBannerButtonText": "Mehr erfahren",
|
||||||
"apiKeysSettings": "{apiKeyName} Einstellungen",
|
"apiKeysSettings": "{apiKeyName} Einstellungen",
|
||||||
"userTitle": "Alle Benutzer verwalten",
|
"userTitle": "Alle Benutzer verwalten",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Bitte geben Sie eine gültige Portnummer ein",
|
"targetErrorInvalidPortDescription": "Bitte geben Sie eine gültige Portnummer ein",
|
||||||
"targetErrorNoSite": "Kein Standort ausgewählt",
|
"targetErrorNoSite": "Kein Standort ausgewählt",
|
||||||
"targetErrorNoSiteDescription": "Bitte wähle einen Standort für das Ziel aus",
|
"targetErrorNoSiteDescription": "Bitte wähle einen Standort für das Ziel aus",
|
||||||
|
"targetTargetsCleared": "Ziele gelöscht",
|
||||||
|
"targetTargetsClearedDescription": "Alle Ziele wurden aus dieser Ressource entfernt",
|
||||||
"targetCreated": "Ziel erstellt",
|
"targetCreated": "Ziel erstellt",
|
||||||
"targetCreatedDescription": "Ziel wurde erfolgreich erstellt",
|
"targetCreatedDescription": "Ziel wurde erfolgreich erstellt",
|
||||||
"targetErrorCreate": "Fehler beim Erstellen des Ziels",
|
"targetErrorCreate": "Fehler beim Erstellen des Ziels",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Enterprise Features, 50 Benutzer, 50 Sites und Prioritätsunterstützung."
|
"description": "Enterprise Features, 50 Benutzer, 50 Sites und Prioritätsunterstützung."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Nur persönliche Nutzung (kostenlose Lizenz — keine Kasse)",
|
"personalUseOnly": "Nur persönliche Nutzung (kostenlose Lizenz - kein Checkout)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Weiter zur Kasse"
|
"continueToCheckout": "Weiter zur Kasse"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Maschinen-Clients",
|
"machineClients": "Maschinen-Clients",
|
||||||
"install": "Installieren",
|
"install": "Installieren",
|
||||||
"run": "Ausführen",
|
"run": "Ausführen",
|
||||||
|
"envFile": "Umgebungsdatei",
|
||||||
|
"serviceFile": "Servicedatei",
|
||||||
|
"enableAndStart": "Aktivieren und Starten",
|
||||||
"clientNameDescription": "Der Anzeigename des Clients, der später geändert werden kann.",
|
"clientNameDescription": "Der Anzeigename des Clients, der später geändert werden kann.",
|
||||||
"clientAddress": "Clientadresse (Erweitert)",
|
"clientAddress": "Clientadresse (Erweitert)",
|
||||||
"setupFailedToFetchSubnet": "Fehler beim Abrufen des Standard-Subnetzes",
|
"setupFailedToFetchSubnet": "Fehler beim Abrufen des Standard-Subnetzes",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Keine Authentifizierung",
|
"httpDestAuthNoneTitle": "Keine Authentifizierung",
|
||||||
"httpDestAuthNoneDescription": "Sendet Anfragen ohne Autorisierungs-Header.",
|
"httpDestAuthNoneDescription": "Sendet Anfragen ohne Autorisierungs-Header.",
|
||||||
"httpDestAuthBearerTitle": "Bären-Token",
|
"httpDestAuthBearerTitle": "Bären-Token",
|
||||||
"httpDestAuthBearerDescription": "Fügt eine Berechtigung hinzu: Bearer <token> Header zu jeder Anfrage.",
|
"httpDestAuthBearerDescription": "Fügt jedem Anfrage-Header eine \"Authorization: Bearer '<token>'\" hinzu.",
|
||||||
"httpDestAuthBearerPlaceholder": "Ihr API-Schlüssel oder Token",
|
"httpDestAuthBearerPlaceholder": "Ihr API-Schlüssel oder Token",
|
||||||
"httpDestAuthBasicTitle": "Einfacher Auth",
|
"httpDestAuthBasicTitle": "Einfacher Auth",
|
||||||
"httpDestAuthBasicDescription": "Fügt eine Autorisierung hinzu: Basic <credentials> Kopfzeile hinzu. Geben Sie Anmeldedaten als Benutzername:password an.",
|
"httpDestAuthBasicDescription": "Fügt einen \"Authorization: Basic '<credentials>'\"-Header hinzu. Geben Sie die Anmeldedaten als Benutzername:Passwort an.",
|
||||||
"httpDestAuthBasicPlaceholder": "benutzername:password",
|
"httpDestAuthBasicPlaceholder": "benutzername:password",
|
||||||
"httpDestAuthCustomTitle": "Eigene Kopfzeile",
|
"httpDestAuthCustomTitle": "Eigene Kopfzeile",
|
||||||
"httpDestAuthCustomDescription": "Geben Sie einen eigenen HTTP-Header-Namen und einen Wert für die Authentifizierung an (z.B. X-API-Key).",
|
"httpDestAuthCustomDescription": "Geben Sie einen eigenen HTTP-Header-Namen und einen Wert für die Authentifizierung an (z.B. X-API-Key).",
|
||||||
|
|||||||
@@ -2850,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "No Authentication",
|
"httpDestAuthNoneTitle": "No Authentication",
|
||||||
"httpDestAuthNoneDescription": "Sends requests without an Authorization header.",
|
"httpDestAuthNoneDescription": "Sends requests without an Authorization header.",
|
||||||
"httpDestAuthBearerTitle": "Bearer Token",
|
"httpDestAuthBearerTitle": "Bearer Token",
|
||||||
"httpDestAuthBearerDescription": "Adds an Authorization: Bearer <token> header to each request.",
|
"httpDestAuthBearerDescription": "Adds an Authorization: Bearer '<token>' header to each request.",
|
||||||
"httpDestAuthBearerPlaceholder": "Your API key or token",
|
"httpDestAuthBearerPlaceholder": "Your API key or token",
|
||||||
"httpDestAuthBasicTitle": "Basic Auth",
|
"httpDestAuthBasicTitle": "Basic Auth",
|
||||||
"httpDestAuthBasicDescription": "Adds an Authorization: Basic <credentials> header. Provide credentials as username:password.",
|
"httpDestAuthBasicDescription": "Adds an Authorization: Basic '<credentials>' header. Provide credentials as username:password.",
|
||||||
"httpDestAuthBasicPlaceholder": "username:password",
|
"httpDestAuthBasicPlaceholder": "username:password",
|
||||||
"httpDestAuthCustomTitle": "Custom Header",
|
"httpDestAuthCustomTitle": "Custom Header",
|
||||||
"httpDestAuthCustomDescription": "Specify a custom HTTP header name and value for authentication (e.g. X-API-Key).",
|
"httpDestAuthCustomDescription": "Specify a custom HTTP header name and value for authentication (e.g. X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Clave de aprovisionamiento actualizada",
|
"provisioningKeysUpdated": "Clave de aprovisionamiento actualizada",
|
||||||
"provisioningKeysUpdatedDescription": "Sus cambios han sido guardados.",
|
"provisioningKeysUpdatedDescription": "Sus cambios han sido guardados.",
|
||||||
"provisioningKeysBannerTitle": "Claves de aprovisionamiento del sitio",
|
"provisioningKeysBannerTitle": "Claves de aprovisionamiento del sitio",
|
||||||
"provisioningKeysBannerDescription": "Generar una clave de aprovisionamiento y usarla con el conector Newt para crear automáticamente sitios en el primer inicio — no es necesario configurar credenciales separadas para cada sitio.",
|
"provisioningKeysBannerDescription": "Genere una clave de aprovisionamiento y utilícela con el conector Newt para crear automáticamente sitios en el primer inicio: no es necesario configurar credenciales separadas para cada sitio.",
|
||||||
"provisioningKeysBannerButtonText": "Saber más",
|
"provisioningKeysBannerButtonText": "Saber más",
|
||||||
"pendingSitesBannerTitle": "Sitios pendientes",
|
"pendingSitesBannerTitle": "Sitios pendientes",
|
||||||
"pendingSitesBannerDescription": "Los sitios que se conectan usando una clave de aprovisionamiento aparecen aquí para su revisión. Aprobar cada sitio antes de que se active y obtenga acceso a sus recursos.",
|
"pendingSitesBannerDescription": "Los sitios que se conectan utilizando una clave de aprovisionamiento aparecerán aquí para su revisión.",
|
||||||
"pendingSitesBannerButtonText": "Saber más",
|
"pendingSitesBannerButtonText": "Saber más",
|
||||||
"apiKeysSettings": "Ajustes {apiKeyName}",
|
"apiKeysSettings": "Ajustes {apiKeyName}",
|
||||||
"userTitle": "Administrar todos los usuarios",
|
"userTitle": "Administrar todos los usuarios",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Por favor, introduzca un número de puerto válido",
|
"targetErrorInvalidPortDescription": "Por favor, introduzca un número de puerto válido",
|
||||||
"targetErrorNoSite": "Ningún sitio seleccionado",
|
"targetErrorNoSite": "Ningún sitio seleccionado",
|
||||||
"targetErrorNoSiteDescription": "Por favor, seleccione un sitio para el objetivo",
|
"targetErrorNoSiteDescription": "Por favor, seleccione un sitio para el objetivo",
|
||||||
|
"targetTargetsCleared": "Objetivos eliminados",
|
||||||
|
"targetTargetsClearedDescription": "Todos los objetivos han sido eliminados de este recurso",
|
||||||
"targetCreated": "Objetivo creado",
|
"targetCreated": "Objetivo creado",
|
||||||
"targetCreatedDescription": "El objetivo se ha creado correctamente",
|
"targetCreatedDescription": "El objetivo se ha creado correctamente",
|
||||||
"targetErrorCreate": "Error al crear el objetivo",
|
"targetErrorCreate": "Error al crear el objetivo",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Características de la empresa, 50 usuarios, 50 sitios y soporte prioritario."
|
"description": "Características de la empresa, 50 usuarios, 50 sitios y soporte prioritario."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Solo uso personal (licencia gratuita, sin pago)",
|
"personalUseOnly": "Solo uso personal (licencia gratuita - sin salida)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Continuar con el pago"
|
"continueToCheckout": "Continuar con el pago"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Clientes de la máquina",
|
"machineClients": "Clientes de la máquina",
|
||||||
"install": "Instalar",
|
"install": "Instalar",
|
||||||
"run": "Ejecutar",
|
"run": "Ejecutar",
|
||||||
|
"envFile": "Archivo de Entorno",
|
||||||
|
"serviceFile": "Archivo de Servicio",
|
||||||
|
"enableAndStart": "Habilitar y empezar",
|
||||||
"clientNameDescription": "El nombre mostrado del cliente que se puede cambiar más adelante.",
|
"clientNameDescription": "El nombre mostrado del cliente que se puede cambiar más adelante.",
|
||||||
"clientAddress": "Dirección del cliente (Avanzado)",
|
"clientAddress": "Dirección del cliente (Avanzado)",
|
||||||
"setupFailedToFetchSubnet": "No se pudo obtener la subred por defecto",
|
"setupFailedToFetchSubnet": "No se pudo obtener la subred por defecto",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Sin autenticación",
|
"httpDestAuthNoneTitle": "Sin autenticación",
|
||||||
"httpDestAuthNoneDescription": "Envía solicitudes sin un encabezado de autorización.",
|
"httpDestAuthNoneDescription": "Envía solicitudes sin un encabezado de autorización.",
|
||||||
"httpDestAuthBearerTitle": "Tóken de portador",
|
"httpDestAuthBearerTitle": "Tóken de portador",
|
||||||
"httpDestAuthBearerDescription": "Añade una autorización: portador <token> encabezado a cada solicitud.",
|
"httpDestAuthBearerDescription": "Añade un encabezado Authorization: Bearer '<token>' a cada solicitud.",
|
||||||
"httpDestAuthBearerPlaceholder": "Tu clave o token API",
|
"httpDestAuthBearerPlaceholder": "Tu clave o token API",
|
||||||
"httpDestAuthBasicTitle": "Auth Básica",
|
"httpDestAuthBasicTitle": "Auth Básica",
|
||||||
"httpDestAuthBasicDescription": "Añade una Autorización: encabezado básico <credentials> . Proporcione credenciales como nombre de usuario: contraseña.",
|
"httpDestAuthBasicDescription": "Añade un encabezado Authorization: Basic '<credenciales>'. Proporcione las credenciales como nombredeusuario:contraseña.",
|
||||||
"httpDestAuthBasicPlaceholder": "usuario:contraseña",
|
"httpDestAuthBasicPlaceholder": "usuario:contraseña",
|
||||||
"httpDestAuthCustomTitle": "Cabecera personalizada",
|
"httpDestAuthCustomTitle": "Cabecera personalizada",
|
||||||
"httpDestAuthCustomDescription": "Especifique un nombre de cabecera HTTP personalizado y un valor para la autenticación (por ejemplo, X-API-Key).",
|
"httpDestAuthCustomDescription": "Especifique un nombre de cabecera HTTP personalizado y un valor para la autenticación (por ejemplo, X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Clé de provisioning mise à jour",
|
"provisioningKeysUpdated": "Clé de provisioning mise à jour",
|
||||||
"provisioningKeysUpdatedDescription": "Vos modifications ont été enregistrées.",
|
"provisioningKeysUpdatedDescription": "Vos modifications ont été enregistrées.",
|
||||||
"provisioningKeysBannerTitle": "Clés de provisioning du site",
|
"provisioningKeysBannerTitle": "Clés de provisioning du site",
|
||||||
"provisioningKeysBannerDescription": "Générez une clé de provisioning et utilisez-la avec le connecteur Newt pour créer automatiquement des sites au premier démarrage — pas besoin de configurer des identifiants distincts pour chaque site.",
|
"provisioningKeysBannerDescription": "Générez une clé de provisionnement et utilisez-la avec le connecteur Newt pour créer automatiquement des sites lors du premier démarrage - sans besoin de configurer des identifiants séparés pour chaque site.",
|
||||||
"provisioningKeysBannerButtonText": "En savoir plus",
|
"provisioningKeysBannerButtonText": "En savoir plus",
|
||||||
"pendingSitesBannerTitle": "Sites en attente",
|
"pendingSitesBannerTitle": "Sites en attente",
|
||||||
"pendingSitesBannerDescription": "Les sites qui se connectent à l'aide d'une clé de provisioning apparaissent ici pour être revus. Approuver chaque site avant qu'il ne devienne actif et qu'il accède à vos ressources.",
|
"pendingSitesBannerDescription": "Les sites qui se connectent en utilisant une clé de provisionnement apparaissent ici pour révision.",
|
||||||
"pendingSitesBannerButtonText": "En savoir plus",
|
"pendingSitesBannerButtonText": "En savoir plus",
|
||||||
"apiKeysSettings": "Paramètres de {apiKeyName}",
|
"apiKeysSettings": "Paramètres de {apiKeyName}",
|
||||||
"userTitle": "Gérer tous les utilisateurs",
|
"userTitle": "Gérer tous les utilisateurs",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Veuillez entrer un numéro de port valide",
|
"targetErrorInvalidPortDescription": "Veuillez entrer un numéro de port valide",
|
||||||
"targetErrorNoSite": "Aucun site sélectionné",
|
"targetErrorNoSite": "Aucun site sélectionné",
|
||||||
"targetErrorNoSiteDescription": "Veuillez sélectionner un site pour la cible",
|
"targetErrorNoSiteDescription": "Veuillez sélectionner un site pour la cible",
|
||||||
|
"targetTargetsCleared": "Cibles effacées",
|
||||||
|
"targetTargetsClearedDescription": "Toutes les cibles ont été retirées de cette ressource",
|
||||||
"targetCreated": "Cible créée",
|
"targetCreated": "Cible créée",
|
||||||
"targetCreatedDescription": "La cible a été créée avec succès",
|
"targetCreatedDescription": "La cible a été créée avec succès",
|
||||||
"targetErrorCreate": "Impossible de créer la cible",
|
"targetErrorCreate": "Impossible de créer la cible",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Fonctionnalités d'entreprise, 50 utilisateurs, 50 sites et une prise en charge prioritaire."
|
"description": "Fonctionnalités d'entreprise, 50 utilisateurs, 50 sites et une prise en charge prioritaire."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Utilisation personnelle uniquement (licence gratuite — sans checkout)",
|
"personalUseOnly": "Usage personnel uniquement (licence gratuite - pas de validation)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Continuer vers le paiement"
|
"continueToCheckout": "Continuer vers le paiement"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Clients Machines",
|
"machineClients": "Clients Machines",
|
||||||
"install": "Installer",
|
"install": "Installer",
|
||||||
"run": "Exécuter",
|
"run": "Exécuter",
|
||||||
|
"envFile": "Fichier Environnement",
|
||||||
|
"serviceFile": "Fichier de Service",
|
||||||
|
"enableAndStart": "Activer et Démarrer",
|
||||||
"clientNameDescription": "Le nom d'affichage du client qui peut être modifié plus tard.",
|
"clientNameDescription": "Le nom d'affichage du client qui peut être modifié plus tard.",
|
||||||
"clientAddress": "Adresse du client (Avancé)",
|
"clientAddress": "Adresse du client (Avancé)",
|
||||||
"setupFailedToFetchSubnet": "Impossible de récupérer le sous-réseau par défaut",
|
"setupFailedToFetchSubnet": "Impossible de récupérer le sous-réseau par défaut",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Aucune authentification",
|
"httpDestAuthNoneTitle": "Aucune authentification",
|
||||||
"httpDestAuthNoneDescription": "Envoie des requêtes sans en-tête d'autorisation.",
|
"httpDestAuthNoneDescription": "Envoie des requêtes sans en-tête d'autorisation.",
|
||||||
"httpDestAuthBearerTitle": "Jeton de Porteur",
|
"httpDestAuthBearerTitle": "Jeton de Porteur",
|
||||||
"httpDestAuthBearerDescription": "Ajoute un en-tête Authorization: Bearer <token> à chaque requête.",
|
"httpDestAuthBearerDescription": "Ajoute un en-tête Authorization: Bearer '<token>' à chaque requête.",
|
||||||
"httpDestAuthBearerPlaceholder": "Votre clé API ou votre jeton",
|
"httpDestAuthBearerPlaceholder": "Votre clé API ou votre jeton",
|
||||||
"httpDestAuthBasicTitle": "Authentification basique",
|
"httpDestAuthBasicTitle": "Authentification basique",
|
||||||
"httpDestAuthBasicDescription": "Ajoute une autorisation : en-tête de base <credentials> . Fournissez des informations d'identification comme nom d'utilisateur:mot de passe.",
|
"httpDestAuthBasicDescription": "Ajoute un en-tête Authorization: Basic '<credentials>'. Fournissez les identifiants sous la forme nom d'utilisateur:mot de passe.",
|
||||||
"httpDestAuthBasicPlaceholder": "nom d'utilisateur:mot de passe",
|
"httpDestAuthBasicPlaceholder": "nom d'utilisateur:mot de passe",
|
||||||
"httpDestAuthCustomTitle": "En-tête personnalisé",
|
"httpDestAuthCustomTitle": "En-tête personnalisé",
|
||||||
"httpDestAuthCustomDescription": "Spécifiez un nom d'en-tête HTTP personnalisé et une valeur pour l'authentification (par exemple X-API-Key).",
|
"httpDestAuthCustomDescription": "Spécifiez un nom d'en-tête HTTP personnalisé et une valeur pour l'authentification (par exemple X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Chiave di accantonamento aggiornata",
|
"provisioningKeysUpdated": "Chiave di accantonamento aggiornata",
|
||||||
"provisioningKeysUpdatedDescription": "Le tue modifiche sono state salvate.",
|
"provisioningKeysUpdatedDescription": "Le tue modifiche sono state salvate.",
|
||||||
"provisioningKeysBannerTitle": "Chiavi Di Provvedimento Sito",
|
"provisioningKeysBannerTitle": "Chiavi Di Provvedimento Sito",
|
||||||
"provisioningKeysBannerDescription": "Generare una chiave di provisioning e usarla con il connettore Newt per creare automaticamente siti al primo avvio — non è necessario impostare credenziali separate per ogni sito.",
|
"provisioningKeysBannerDescription": "Genera una chiave di provisioning e usala con il connettore Newt per creare automaticamente i siti al primo avvio - non è necessario configurare credenziali separate per ogni sito.",
|
||||||
"provisioningKeysBannerButtonText": "Scopri di più",
|
"provisioningKeysBannerButtonText": "Scopri di più",
|
||||||
"pendingSitesBannerTitle": "Siti In Attesa",
|
"pendingSitesBannerTitle": "Siti In Attesa",
|
||||||
"pendingSitesBannerDescription": "I siti che si connettono utilizzando una chiave di provisioning appaiono qui per la revisione. Approva ogni sito prima che diventi attivo e ottenga l'accesso alle tue risorse.",
|
"pendingSitesBannerDescription": "I siti che si connettono utilizzando una chiave di provisioning vengono visualizzati qui per la revisione.",
|
||||||
"pendingSitesBannerButtonText": "Scopri di più",
|
"pendingSitesBannerButtonText": "Scopri di più",
|
||||||
"apiKeysSettings": "Impostazioni {apiKeyName}",
|
"apiKeysSettings": "Impostazioni {apiKeyName}",
|
||||||
"userTitle": "Gestisci Tutti Gli Utenti",
|
"userTitle": "Gestisci Tutti Gli Utenti",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Inserisci un numero di porta valido",
|
"targetErrorInvalidPortDescription": "Inserisci un numero di porta valido",
|
||||||
"targetErrorNoSite": "Nessun sito selezionato",
|
"targetErrorNoSite": "Nessun sito selezionato",
|
||||||
"targetErrorNoSiteDescription": "Si prega di selezionare un sito per l'obiettivo",
|
"targetErrorNoSiteDescription": "Si prega di selezionare un sito per l'obiettivo",
|
||||||
|
"targetTargetsCleared": "Obiettivi cancellati",
|
||||||
|
"targetTargetsClearedDescription": "Tutti gli obiettivi sono stati rimossi da questa risorsa",
|
||||||
"targetCreated": "Destinazione creata",
|
"targetCreated": "Destinazione creata",
|
||||||
"targetCreatedDescription": "L'obiettivo è stato creato con successo",
|
"targetCreatedDescription": "L'obiettivo è stato creato con successo",
|
||||||
"targetErrorCreate": "Impossibile creare l'obiettivo",
|
"targetErrorCreate": "Impossibile creare l'obiettivo",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Funzionalità aziendali, 50 utenti, 50 siti e supporto prioritario."
|
"description": "Funzionalità aziendali, 50 utenti, 50 siti e supporto prioritario."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Solo uso personale (licenza gratuita — nessun checkout)",
|
"personalUseOnly": "Uso personale esclusivo (licenza gratuita - nessun pagamento)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Continua al Checkout"
|
"continueToCheckout": "Continua al Checkout"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Machine Clients",
|
"machineClients": "Machine Clients",
|
||||||
"install": "Installa",
|
"install": "Installa",
|
||||||
"run": "Esegui",
|
"run": "Esegui",
|
||||||
|
"envFile": "File di ambiente",
|
||||||
|
"serviceFile": "File di servizio",
|
||||||
|
"enableAndStart": "Abilita e avvia",
|
||||||
"clientNameDescription": "Il nome visualizzato del client che può essere modificato in seguito.",
|
"clientNameDescription": "Il nome visualizzato del client che può essere modificato in seguito.",
|
||||||
"clientAddress": "Indirizzo Client (Avanzato)",
|
"clientAddress": "Indirizzo Client (Avanzato)",
|
||||||
"setupFailedToFetchSubnet": "Recupero della sottorete predefinita non riuscito",
|
"setupFailedToFetchSubnet": "Recupero della sottorete predefinita non riuscito",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Nessuna Autenticazione",
|
"httpDestAuthNoneTitle": "Nessuna Autenticazione",
|
||||||
"httpDestAuthNoneDescription": "Invia richieste senza intestazione autorizzazione.",
|
"httpDestAuthNoneDescription": "Invia richieste senza intestazione autorizzazione.",
|
||||||
"httpDestAuthBearerTitle": "Token Del Portatore",
|
"httpDestAuthBearerTitle": "Token Del Portatore",
|
||||||
"httpDestAuthBearerDescription": "Aggiunge un'intestazione Autorizzazione: Bearer <token> ad ogni richiesta.",
|
"httpDestAuthBearerDescription": "Aggiunge un'intestazione Authorization: Bearer '<token>' a ogni richiesta.",
|
||||||
"httpDestAuthBearerPlaceholder": "La tua chiave API o token",
|
"httpDestAuthBearerPlaceholder": "La tua chiave API o token",
|
||||||
"httpDestAuthBasicTitle": "Autenticazione Base",
|
"httpDestAuthBasicTitle": "Autenticazione Base",
|
||||||
"httpDestAuthBasicDescription": "Aggiunge un'autorizzazione: intestazione di base <credentials> . Fornisce le credenziali come username:password.",
|
"httpDestAuthBasicDescription": "Aggiunge un'intestazione Authorization: Basic '<credentials>'. Fornire le credenziali come username:password.",
|
||||||
"httpDestAuthBasicPlaceholder": "username:password",
|
"httpDestAuthBasicPlaceholder": "username:password",
|
||||||
"httpDestAuthCustomTitle": "Intestazione Personalizzata",
|
"httpDestAuthCustomTitle": "Intestazione Personalizzata",
|
||||||
"httpDestAuthCustomDescription": "Specifica un nome e un valore di intestazione HTTP personalizzati per l'autenticazione (ad esempio X-API-Key).",
|
"httpDestAuthCustomDescription": "Specifica un nome e un valore di intestazione HTTP personalizzati per l'autenticazione (ad esempio X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "프로비저닝 키가 업데이트되었습니다",
|
"provisioningKeysUpdated": "프로비저닝 키가 업데이트되었습니다",
|
||||||
"provisioningKeysUpdatedDescription": "변경 사항이 저장되었습니다.",
|
"provisioningKeysUpdatedDescription": "변경 사항이 저장되었습니다.",
|
||||||
"provisioningKeysBannerTitle": "사이트 프로비저닝 키",
|
"provisioningKeysBannerTitle": "사이트 프로비저닝 키",
|
||||||
"provisioningKeysBannerDescription": "프로비저닝 키를 생성하여 Newt 커넥터와 함께 사용해 첫 실행 시 자동으로 사이트를 생성하세요 — 각 사이트마다 별도의 인증을 설정할 필요가 없습니다.",
|
"provisioningKeysBannerDescription": "프로비저닝 키를 생성하고 Newt 커넥터와 함께 사용하여 첫 시작 시 사이트를 자동 생성 - 각 사이트에 대한 별도 자격 증명이 필요 없습니다.",
|
||||||
"provisioningKeysBannerButtonText": "자세히 알아보기",
|
"provisioningKeysBannerButtonText": "자세히 알아보기",
|
||||||
"pendingSitesBannerTitle": "대기중인 사이트",
|
"pendingSitesBannerTitle": "대기중인 사이트",
|
||||||
"pendingSitesBannerDescription": "프로비저닝 키를 사용하여 연결하는 사이트는 검토 대기 중입니다. 사이트가 활성화되어 리소스에 액세스하기 전에 각 사이트를 승인하세요.",
|
"pendingSitesBannerDescription": "프로비저닝 키를 사용하여 연결된 사이트가 검토를 위해 여기에 표시됩니다.",
|
||||||
"pendingSitesBannerButtonText": "자세히 알아보기",
|
"pendingSitesBannerButtonText": "자세히 알아보기",
|
||||||
"apiKeysSettings": "{apiKeyName} 설정",
|
"apiKeysSettings": "{apiKeyName} 설정",
|
||||||
"userTitle": "모든 사용자 관리",
|
"userTitle": "모든 사용자 관리",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "유효한 포트 번호를 입력하세요.",
|
"targetErrorInvalidPortDescription": "유효한 포트 번호를 입력하세요.",
|
||||||
"targetErrorNoSite": "선택된 사이트 없음",
|
"targetErrorNoSite": "선택된 사이트 없음",
|
||||||
"targetErrorNoSiteDescription": "대상을 위해 사이트를 선택하세요.",
|
"targetErrorNoSiteDescription": "대상을 위해 사이트를 선택하세요.",
|
||||||
|
"targetTargetsCleared": "대상이 제거됨",
|
||||||
|
"targetTargetsClearedDescription": "이 리소스에서 모든 대상이 제거되었습니다",
|
||||||
"targetCreated": "대상 생성",
|
"targetCreated": "대상 생성",
|
||||||
"targetCreatedDescription": "대상이 성공적으로 생성되었습니다.",
|
"targetCreatedDescription": "대상이 성공적으로 생성되었습니다.",
|
||||||
"targetErrorCreate": "대상 생성 실패",
|
"targetErrorCreate": "대상 생성 실패",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "기업 기능, 50명의 사용자, 50개의 사이트, 우선 지원."
|
"description": "기업 기능, 50명의 사용자, 50개의 사이트, 우선 지원."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "개인 사용 전용 (무료 라이센스 — 체크아웃 없음)",
|
"personalUseOnly": "개인용으로만 사용 (무료 라이선스 - 결제 없음)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "결제로 진행"
|
"continueToCheckout": "결제로 진행"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "기계 클라이언트",
|
"machineClients": "기계 클라이언트",
|
||||||
"install": "설치",
|
"install": "설치",
|
||||||
"run": "실행",
|
"run": "실행",
|
||||||
|
"envFile": "환경 파일",
|
||||||
|
"serviceFile": "서비스 파일",
|
||||||
|
"enableAndStart": "활성화 및 시작",
|
||||||
"clientNameDescription": "나중에 변경할 수 있는 클라이언트의 표시 이름입니다.",
|
"clientNameDescription": "나중에 변경할 수 있는 클라이언트의 표시 이름입니다.",
|
||||||
"clientAddress": "클라이언트 주소(고급)",
|
"clientAddress": "클라이언트 주소(고급)",
|
||||||
"setupFailedToFetchSubnet": "기본값 로드 실패",
|
"setupFailedToFetchSubnet": "기본값 로드 실패",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "인증 없음",
|
"httpDestAuthNoneTitle": "인증 없음",
|
||||||
"httpDestAuthNoneDescription": "Authorization 헤더 없이 요청을 보냅니다.",
|
"httpDestAuthNoneDescription": "Authorization 헤더 없이 요청을 보냅니다.",
|
||||||
"httpDestAuthBearerTitle": "Bearer 토큰",
|
"httpDestAuthBearerTitle": "Bearer 토큰",
|
||||||
"httpDestAuthBearerDescription": "모든 요청에 Authorization: Bearer <token> 헤더를 추가합니다.",
|
"httpDestAuthBearerDescription": "각 요청에 Authorization: Bearer '<token>' 헤더를 추가합니다.",
|
||||||
"httpDestAuthBearerPlaceholder": "API 키 또는 토큰",
|
"httpDestAuthBearerPlaceholder": "API 키 또는 토큰",
|
||||||
"httpDestAuthBasicTitle": "기본 인증",
|
"httpDestAuthBasicTitle": "기본 인증",
|
||||||
"httpDestAuthBasicDescription": "Authorization: Basic <credentials> 헤더를 추가합니다. 자격 증명은 username:password 형식으로 제공하세요.",
|
"httpDestAuthBasicDescription": "Authorization: Basic '<credentials>' 헤더를 추가합니다. 자격 증명은 사용자 이름:비밀번호로 제공합니다.",
|
||||||
"httpDestAuthBasicPlaceholder": "사용자 이름:비밀번호",
|
"httpDestAuthBasicPlaceholder": "사용자 이름:비밀번호",
|
||||||
"httpDestAuthCustomTitle": "사용자 정의 헤더",
|
"httpDestAuthCustomTitle": "사용자 정의 헤더",
|
||||||
"httpDestAuthCustomDescription": "인증을 위한 사용자 정의 HTTP 헤더 이름 및 값을 지정하세요 (예: X-API-Key).",
|
"httpDestAuthCustomDescription": "인증을 위한 사용자 정의 HTTP 헤더 이름 및 값을 지정하세요 (예: X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Foreslå nøkkel oppdatert",
|
"provisioningKeysUpdated": "Foreslå nøkkel oppdatert",
|
||||||
"provisioningKeysUpdatedDescription": "Dine endringer er lagret.",
|
"provisioningKeysUpdatedDescription": "Dine endringer er lagret.",
|
||||||
"provisioningKeysBannerTitle": "Sidens bestemmende nøkler",
|
"provisioningKeysBannerTitle": "Sidens bestemmende nøkler",
|
||||||
"provisioningKeysBannerDescription": "Generer en foreløpig nøkkel og bruk den med Nyhetskontakten for å automatisk opprette sider ved første oppstart — trenger ikke å sette opp separat innloggingsinformasjon for hver side.",
|
"provisioningKeysBannerDescription": "Generer en provisjonsnøkkel og bruk den med Newt-kontakten for automatisk opprettelse av nettsteder ved første oppstart - ingen behov for å sette opp separate legitimasjoner for hvert nettsted.",
|
||||||
"provisioningKeysBannerButtonText": "Lær mer",
|
"provisioningKeysBannerButtonText": "Lær mer",
|
||||||
"pendingSitesBannerTitle": "Ventende nettsteder",
|
"pendingSitesBannerTitle": "Ventende nettsteder",
|
||||||
"pendingSitesBannerDescription": "Nettsteder som kobler deg til ved hjelp av en bestemmelsestekst, vises her for gjennomgang. Godkjenn hvert nettsted før det blir aktivt og får tilgang til ressursene dine.",
|
"pendingSitesBannerDescription": "Nettsteder som kobler seg til ved bruk av en provisjonsnøkkel vises her for vurdering.",
|
||||||
"pendingSitesBannerButtonText": "Lær mer",
|
"pendingSitesBannerButtonText": "Lær mer",
|
||||||
"apiKeysSettings": "{apiKeyName} Innstillinger",
|
"apiKeysSettings": "{apiKeyName} Innstillinger",
|
||||||
"userTitle": "Administrer alle brukere",
|
"userTitle": "Administrer alle brukere",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Vennligst skriv inn et gyldig portnummer",
|
"targetErrorInvalidPortDescription": "Vennligst skriv inn et gyldig portnummer",
|
||||||
"targetErrorNoSite": "Ingen nettsted valgt",
|
"targetErrorNoSite": "Ingen nettsted valgt",
|
||||||
"targetErrorNoSiteDescription": "Velg et nettsted for målet",
|
"targetErrorNoSiteDescription": "Velg et nettsted for målet",
|
||||||
|
"targetTargetsCleared": "Mål ryddet",
|
||||||
|
"targetTargetsClearedDescription": "Alle mål har blitt fjernet fra denne ressursen",
|
||||||
"targetCreated": "Mål opprettet",
|
"targetCreated": "Mål opprettet",
|
||||||
"targetCreatedDescription": "Målet har blitt opprettet",
|
"targetCreatedDescription": "Målet har blitt opprettet",
|
||||||
"targetErrorCreate": "Kunne ikke opprette målet",
|
"targetErrorCreate": "Kunne ikke opprette målet",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Enterprise features, 50 brukere, 50 nettsteder og prioritetsstøtte."
|
"description": "Enterprise features, 50 brukere, 50 nettsteder og prioritetsstøtte."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Kun personlig bruk (gratis lisens - ingen utsjekking)",
|
"personalUseOnly": "Kun personlig bruk (gratis lisens - ingen kasse)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Fortsett til kassen"
|
"continueToCheckout": "Fortsett til kassen"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Maskinklienter",
|
"machineClients": "Maskinklienter",
|
||||||
"install": "Installer",
|
"install": "Installer",
|
||||||
"run": "Kjør",
|
"run": "Kjør",
|
||||||
|
"envFile": "Miljøfil",
|
||||||
|
"serviceFile": "Tjenestefil",
|
||||||
|
"enableAndStart": "Aktiver og start",
|
||||||
"clientNameDescription": "Visningsnavnet til klienten som kan endres senere.",
|
"clientNameDescription": "Visningsnavnet til klienten som kan endres senere.",
|
||||||
"clientAddress": "Klientadresse (avansert)",
|
"clientAddress": "Klientadresse (avansert)",
|
||||||
"setupFailedToFetchSubnet": "Kunne ikke hente standard undernett",
|
"setupFailedToFetchSubnet": "Kunne ikke hente standard undernett",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Ingen godkjenning",
|
"httpDestAuthNoneTitle": "Ingen godkjenning",
|
||||||
"httpDestAuthNoneDescription": "Sender forespørsler uten autorisasjonsoverskrift.",
|
"httpDestAuthNoneDescription": "Sender forespørsler uten autorisasjonsoverskrift.",
|
||||||
"httpDestAuthBearerTitle": "Bærer Symbol",
|
"httpDestAuthBearerTitle": "Bærer Symbol",
|
||||||
"httpDestAuthBearerDescription": "Legger til en autorisasjon: Bearer <token> header til hver forespørsel.",
|
"httpDestAuthBearerDescription": "Legger til en Autorisasjon: Bearer '<token>' header til hver forespørsel.",
|
||||||
"httpDestAuthBearerPlaceholder": "Din API-nøkkel eller token",
|
"httpDestAuthBearerPlaceholder": "Din API-nøkkel eller token",
|
||||||
"httpDestAuthBasicTitle": "Standard Auth",
|
"httpDestAuthBasicTitle": "Standard Auth",
|
||||||
"httpDestAuthBasicDescription": "Legger til en godkjenning: Grunnleggende <credentials> overskrift. Angi legitimasjon som brukernavn:passord.",
|
"httpDestAuthBasicDescription": "Legger til en Autorisasjon: Basic '<credentials>' header. Gi legitimasjon som brukernavn:passord.",
|
||||||
"httpDestAuthBasicPlaceholder": "brukernavn:passord",
|
"httpDestAuthBasicPlaceholder": "brukernavn:passord",
|
||||||
"httpDestAuthCustomTitle": "Egendefinert topptekst",
|
"httpDestAuthCustomTitle": "Egendefinert topptekst",
|
||||||
"httpDestAuthCustomDescription": "Angi et egendefinert HTTP headers navn og verdi for autentisering (f.eks X-API-Key).",
|
"httpDestAuthCustomDescription": "Angi et egendefinert HTTP headers navn og verdi for autentisering (f.eks X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Provisie sleutel bijgewerkt",
|
"provisioningKeysUpdated": "Provisie sleutel bijgewerkt",
|
||||||
"provisioningKeysUpdatedDescription": "Uw wijzigingen zijn opgeslagen.",
|
"provisioningKeysUpdatedDescription": "Uw wijzigingen zijn opgeslagen.",
|
||||||
"provisioningKeysBannerTitle": "Bewerkingssleutels voor websites",
|
"provisioningKeysBannerTitle": "Bewerkingssleutels voor websites",
|
||||||
"provisioningKeysBannerDescription": "Genereer een provisioning-sleutel en gebruik deze met de Newt-connector om automatisch sites aan te maken bij het opstarten van de eerste opstart- het is niet nodig om afzonderlijke inloggegevens in te stellen voor elke site.",
|
"provisioningKeysBannerDescription": "Genereer een inrichtingssleutel en gebruik deze met de Newt-connector om automatisch sites te maken bij de eerste opstart - er is geen behoefte om aparte inloggegevens voor elke site in te stellen.",
|
||||||
"provisioningKeysBannerButtonText": "Meer informatie",
|
"provisioningKeysBannerButtonText": "Meer informatie",
|
||||||
"pendingSitesBannerTitle": "Openstaande sites",
|
"pendingSitesBannerTitle": "Openstaande sites",
|
||||||
"pendingSitesBannerDescription": "Sites die met elkaar verbinden met behulp van een provisioning-sleutel verschijnen hier voor beoordeling. Accepteer elke site voordat deze actief wordt en krijgt toegang tot uw bronnen.",
|
"pendingSitesBannerDescription": "Sites die verbinding maken met een inrichtingssleutel verschijnen hier voor beoordeling.",
|
||||||
"pendingSitesBannerButtonText": "Meer informatie",
|
"pendingSitesBannerButtonText": "Meer informatie",
|
||||||
"apiKeysSettings": "{apiKeyName} instellingen",
|
"apiKeysSettings": "{apiKeyName} instellingen",
|
||||||
"userTitle": "Alle gebruikers beheren",
|
"userTitle": "Alle gebruikers beheren",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Voer een geldig poortnummer in",
|
"targetErrorInvalidPortDescription": "Voer een geldig poortnummer in",
|
||||||
"targetErrorNoSite": "Geen site geselecteerd",
|
"targetErrorNoSite": "Geen site geselecteerd",
|
||||||
"targetErrorNoSiteDescription": "Selecteer een site voor het doel",
|
"targetErrorNoSiteDescription": "Selecteer een site voor het doel",
|
||||||
|
"targetTargetsCleared": "Doelen gewist",
|
||||||
|
"targetTargetsClearedDescription": "Alle doelen zijn verwijderd van deze bron",
|
||||||
"targetCreated": "Doel aangemaakt",
|
"targetCreated": "Doel aangemaakt",
|
||||||
"targetCreatedDescription": "Doel is succesvol aangemaakt",
|
"targetCreatedDescription": "Doel is succesvol aangemaakt",
|
||||||
"targetErrorCreate": "Kan doel niet aanmaken",
|
"targetErrorCreate": "Kan doel niet aanmaken",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Enterprise functies, 50 gebruikers, 50 sites en prioriteit ondersteuning."
|
"description": "Enterprise functies, 50 gebruikers, 50 sites en prioriteit ondersteuning."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Alleen persoonlijk gebruik (gratis licentie - geen afrekenen)",
|
"personalUseOnly": "Alleen voor persoonlijk gebruik (gratis licentie - geen afrekening)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Doorgaan naar afrekenen"
|
"continueToCheckout": "Doorgaan naar afrekenen"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Machine Clienten",
|
"machineClients": "Machine Clienten",
|
||||||
"install": "Installeren",
|
"install": "Installeren",
|
||||||
"run": "Uitvoeren",
|
"run": "Uitvoeren",
|
||||||
|
"envFile": "Omgevingsbestand",
|
||||||
|
"serviceFile": "Servicebestand",
|
||||||
|
"enableAndStart": "Inschakelen en Starten",
|
||||||
"clientNameDescription": "De weergavenaam van de client die later gewijzigd kan worden.",
|
"clientNameDescription": "De weergavenaam van de client die later gewijzigd kan worden.",
|
||||||
"clientAddress": "Klant adres (Geavanceerd)",
|
"clientAddress": "Klant adres (Geavanceerd)",
|
||||||
"setupFailedToFetchSubnet": "Kan standaard subnet niet ophalen",
|
"setupFailedToFetchSubnet": "Kan standaard subnet niet ophalen",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Geen authenticatie",
|
"httpDestAuthNoneTitle": "Geen authenticatie",
|
||||||
"httpDestAuthNoneDescription": "Stuurt verzoeken zonder toestemmingskop.",
|
"httpDestAuthNoneDescription": "Stuurt verzoeken zonder toestemmingskop.",
|
||||||
"httpDestAuthBearerTitle": "Betere Token",
|
"httpDestAuthBearerTitle": "Betere Token",
|
||||||
"httpDestAuthBearerDescription": "Voegt een machtiging toe: Drager <token> header aan elke aanvraag.",
|
"httpDestAuthBearerDescription": "Voegt een Authorization: Bearer '<token>' header toe aan elk verzoek.",
|
||||||
"httpDestAuthBearerPlaceholder": "Uw API-sleutel of -token",
|
"httpDestAuthBearerPlaceholder": "Uw API-sleutel of -token",
|
||||||
"httpDestAuthBasicTitle": "Basis authenticatie",
|
"httpDestAuthBasicTitle": "Basis authenticatie",
|
||||||
"httpDestAuthBasicDescription": "Voegt een Authorizatie toe: Basis <credentials> kop. Geef inloggegevens op als gebruikersnaam:wachtwoord.",
|
"httpDestAuthBasicDescription": "Voegt een Authorization: Basic '<credentials>' header toe. Verstrek inloggegevens als gebruikersnaam:wachtwoord.",
|
||||||
"httpDestAuthBasicPlaceholder": "Gebruikersnaam:wachtwoord",
|
"httpDestAuthBasicPlaceholder": "Gebruikersnaam:wachtwoord",
|
||||||
"httpDestAuthCustomTitle": "Aangepaste koptekst",
|
"httpDestAuthCustomTitle": "Aangepaste koptekst",
|
||||||
"httpDestAuthCustomDescription": "Specificeer een aangepaste HTTP header naam en waarde voor authenticatie (bijv. X-API-Key).",
|
"httpDestAuthCustomDescription": "Specificeer een aangepaste HTTP header naam en waarde voor authenticatie (bijv. X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Klucz zaopatrzenia zaktualizowany",
|
"provisioningKeysUpdated": "Klucz zaopatrzenia zaktualizowany",
|
||||||
"provisioningKeysUpdatedDescription": "Twoje zmiany zostały zapisane.",
|
"provisioningKeysUpdatedDescription": "Twoje zmiany zostały zapisane.",
|
||||||
"provisioningKeysBannerTitle": "Klucze Zaopatrzenia witryny",
|
"provisioningKeysBannerTitle": "Klucze Zaopatrzenia witryny",
|
||||||
"provisioningKeysBannerDescription": "Wygeneruj klucz tworzenia rezerw i użyj go z konektorem Newt do automatycznego tworzenia witryn przy pierwszym uruchomieniu — nie ma potrzeby ustawiania oddzielnych poświadczeń dla każdej witryny.",
|
"provisioningKeysBannerDescription": "Wygeneruj klucz provisioning i użyj go z konektorem Newt do automatycznego tworzenia witryn przy pierwszym uruchomieniu - nie ma potrzeby konfigurowania oddzielnych poświadczeń dla każdej witryny.",
|
||||||
"provisioningKeysBannerButtonText": "Dowiedz się więcej",
|
"provisioningKeysBannerButtonText": "Dowiedz się więcej",
|
||||||
"pendingSitesBannerTitle": "Witryny oczekujące",
|
"pendingSitesBannerTitle": "Witryny oczekujące",
|
||||||
"pendingSitesBannerDescription": "Witryny, które łączą się przy użyciu klucza zaopatrzenia, pojawiają się tutaj, aby przejrzeć. Zatwierdź każdą witrynę, zanim stanie się aktywna i uzyska dostęp do twoich zasobów.",
|
"pendingSitesBannerDescription": "Witryny, które łączą się za pomocą klucza provisioning, pojawią się tutaj do przeglądu.",
|
||||||
"pendingSitesBannerButtonText": "Dowiedz się więcej",
|
"pendingSitesBannerButtonText": "Dowiedz się więcej",
|
||||||
"apiKeysSettings": "Ustawienia {apiKeyName}",
|
"apiKeysSettings": "Ustawienia {apiKeyName}",
|
||||||
"userTitle": "Zarządzaj wszystkimi użytkownikami",
|
"userTitle": "Zarządzaj wszystkimi użytkownikami",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Wprowadź prawidłowy numer portu",
|
"targetErrorInvalidPortDescription": "Wprowadź prawidłowy numer portu",
|
||||||
"targetErrorNoSite": "Nie wybrano witryny",
|
"targetErrorNoSite": "Nie wybrano witryny",
|
||||||
"targetErrorNoSiteDescription": "Wybierz witrynę docelową",
|
"targetErrorNoSiteDescription": "Wybierz witrynę docelową",
|
||||||
|
"targetTargetsCleared": "Cele wyczyszczone",
|
||||||
|
"targetTargetsClearedDescription": "Wszystkie cele zostały usunięte z tego zasobu",
|
||||||
"targetCreated": "Cel utworzony",
|
"targetCreated": "Cel utworzony",
|
||||||
"targetCreatedDescription": "Cel został utworzony pomyślnie",
|
"targetCreatedDescription": "Cel został utworzony pomyślnie",
|
||||||
"targetErrorCreate": "Nie udało się utworzyć celu",
|
"targetErrorCreate": "Nie udało się utworzyć celu",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Cechy przedsiębiorstw, 50 użytkowników, 50 obiektów i wsparcie priorytetowe."
|
"description": "Cechy przedsiębiorstw, 50 użytkowników, 50 obiektów i wsparcie priorytetowe."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Wyłącznie do użytku osobistego (bezpłatna licencja – brak zamówień)",
|
"personalUseOnly": "Tylko do użytku osobistego (darmowa licencja - bez płatności)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Przejdź do zamówienia"
|
"continueToCheckout": "Przejdź do zamówienia"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Klienci maszyn",
|
"machineClients": "Klienci maszyn",
|
||||||
"install": "Zainstaluj",
|
"install": "Zainstaluj",
|
||||||
"run": "Uruchom",
|
"run": "Uruchom",
|
||||||
|
"envFile": "Plik środowiska",
|
||||||
|
"serviceFile": "Plik serwisu",
|
||||||
|
"enableAndStart": "Włącz i Uruchom",
|
||||||
"clientNameDescription": "Wyświetlana nazwa klienta, która może zostać zmieniona później.",
|
"clientNameDescription": "Wyświetlana nazwa klienta, która może zostać zmieniona później.",
|
||||||
"clientAddress": "Adres klienta (Zaawansowany)",
|
"clientAddress": "Adres klienta (Zaawansowany)",
|
||||||
"setupFailedToFetchSubnet": "Nie udało się pobrać domyślnej podsieci",
|
"setupFailedToFetchSubnet": "Nie udało się pobrać domyślnej podsieci",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Brak uwierzytelniania",
|
"httpDestAuthNoneTitle": "Brak uwierzytelniania",
|
||||||
"httpDestAuthNoneDescription": "Wysyła żądania bez nagłówka autoryzacji.",
|
"httpDestAuthNoneDescription": "Wysyła żądania bez nagłówka autoryzacji.",
|
||||||
"httpDestAuthBearerTitle": "Token Bearer",
|
"httpDestAuthBearerTitle": "Token Bearer",
|
||||||
"httpDestAuthBearerDescription": "Dodaje autoryzację: nagłówek Bearer <token> do każdego żądania.",
|
"httpDestAuthBearerDescription": "Dodaje nagłówek Authorization: Bearer '<token>' do każdego żądania.",
|
||||||
"httpDestAuthBearerPlaceholder": "Twój klucz API lub token",
|
"httpDestAuthBearerPlaceholder": "Twój klucz API lub token",
|
||||||
"httpDestAuthBasicTitle": "Podstawowa Autoryzacja",
|
"httpDestAuthBasicTitle": "Podstawowa Autoryzacja",
|
||||||
"httpDestAuthBasicDescription": "Dodaje Autoryzacja: Nagłówek Basic <credentials> . Podaj poświadczenia jako nazwę użytkownika: hasło.",
|
"httpDestAuthBasicDescription": "Dodaje nagłówek Authorization: Basic '<credentials>'. Podaj poświadczenia w formacie użytkownik:hasło.",
|
||||||
"httpDestAuthBasicPlaceholder": "Nazwa użytkownika:hasło",
|
"httpDestAuthBasicPlaceholder": "Nazwa użytkownika:hasło",
|
||||||
"httpDestAuthCustomTitle": "Niestandardowy nagłówek",
|
"httpDestAuthCustomTitle": "Niestandardowy nagłówek",
|
||||||
"httpDestAuthCustomDescription": "Określ niestandardową nazwę nagłówka HTTP i wartość dla uwierzytelniania (np. X-API-Key).",
|
"httpDestAuthCustomDescription": "Określ niestandardową nazwę nagłówka HTTP i wartość dla uwierzytelniania (np. X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Chave de provisionamento atualizada",
|
"provisioningKeysUpdated": "Chave de provisionamento atualizada",
|
||||||
"provisioningKeysUpdatedDescription": "Suas alterações foram salvas.",
|
"provisioningKeysUpdatedDescription": "Suas alterações foram salvas.",
|
||||||
"provisioningKeysBannerTitle": "Chaves de provisionamento do site",
|
"provisioningKeysBannerTitle": "Chaves de provisionamento do site",
|
||||||
"provisioningKeysBannerDescription": "Gerar uma chave de provisionamento e usá-la com o conector de Newt para criar automaticamente sites na primeira inicialização — não é necessário configurar credenciais separadas para cada site.",
|
"provisioningKeysBannerDescription": "Gere uma chave de provisionamento e use-a com o conector Newt para criar sites automaticamente na primeira inicialização - sem necessidade de configurar credenciais separadas para cada site.",
|
||||||
"provisioningKeysBannerButtonText": "Saiba mais",
|
"provisioningKeysBannerButtonText": "Saiba mais",
|
||||||
"pendingSitesBannerTitle": "Sites pendentes",
|
"pendingSitesBannerTitle": "Sites pendentes",
|
||||||
"pendingSitesBannerDescription": "Sites que conectam usando uma chave de provisionamento aparecem aqui para revisão. Aprovar cada site antes de se tornar ativo e ganhar acesso a seus recursos.",
|
"pendingSitesBannerDescription": "Sites que se conectam usando uma chave de provisionamento aparecem aqui para revisão.",
|
||||||
"pendingSitesBannerButtonText": "Saiba mais",
|
"pendingSitesBannerButtonText": "Saiba mais",
|
||||||
"apiKeysSettings": "Configurações de {apiKeyName}",
|
"apiKeysSettings": "Configurações de {apiKeyName}",
|
||||||
"userTitle": "Gerir Todos os Utilizadores",
|
"userTitle": "Gerir Todos os Utilizadores",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Por favor, digite um número de porta válido",
|
"targetErrorInvalidPortDescription": "Por favor, digite um número de porta válido",
|
||||||
"targetErrorNoSite": "Nenhum site selecionado",
|
"targetErrorNoSite": "Nenhum site selecionado",
|
||||||
"targetErrorNoSiteDescription": "Selecione um site para o destino",
|
"targetErrorNoSiteDescription": "Selecione um site para o destino",
|
||||||
|
"targetTargetsCleared": "Alvos limpos",
|
||||||
|
"targetTargetsClearedDescription": "Todos os alvos foram removidos deste recurso",
|
||||||
"targetCreated": "Destino criado",
|
"targetCreated": "Destino criado",
|
||||||
"targetCreatedDescription": "O alvo foi criado com sucesso",
|
"targetCreatedDescription": "O alvo foi criado com sucesso",
|
||||||
"targetErrorCreate": "Falha ao criar destino",
|
"targetErrorCreate": "Falha ao criar destino",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Recursos de empresa, 50 usuários, 50 sites e apoio prioritário."
|
"description": "Recursos de empresa, 50 usuários, 50 sites e apoio prioritário."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Apenas uso pessoal (licença gratuita — sem check-out)",
|
"personalUseOnly": "Uso pessoal apenas (licença gratuita - sem checkout)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Continuar com checkout"
|
"continueToCheckout": "Continuar com checkout"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Clientes de máquina",
|
"machineClients": "Clientes de máquina",
|
||||||
"install": "Instale",
|
"install": "Instale",
|
||||||
"run": "Executar",
|
"run": "Executar",
|
||||||
|
"envFile": "Arquivo de Ambiente",
|
||||||
|
"serviceFile": "Arquivo de Serviço",
|
||||||
|
"enableAndStart": "Ativar e Iniciar",
|
||||||
"clientNameDescription": "O nome de exibição do cliente que pode ser alterado mais tarde.",
|
"clientNameDescription": "O nome de exibição do cliente que pode ser alterado mais tarde.",
|
||||||
"clientAddress": "Endereço do Cliente (Avançado)",
|
"clientAddress": "Endereço do Cliente (Avançado)",
|
||||||
"setupFailedToFetchSubnet": "Falha ao buscar a subrede padrão",
|
"setupFailedToFetchSubnet": "Falha ao buscar a subrede padrão",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Sem Autenticação",
|
"httpDestAuthNoneTitle": "Sem Autenticação",
|
||||||
"httpDestAuthNoneDescription": "Envia pedidos sem um cabeçalho de autorização.",
|
"httpDestAuthNoneDescription": "Envia pedidos sem um cabeçalho de autorização.",
|
||||||
"httpDestAuthBearerTitle": "Token do portador",
|
"httpDestAuthBearerTitle": "Token do portador",
|
||||||
"httpDestAuthBearerDescription": "Adiciona uma autorização: Bearer <token> header a cada requisição.",
|
"httpDestAuthBearerDescription": "Adiciona um cabeçalho Authorization: Bearer '<token>' a cada solicitação.",
|
||||||
"httpDestAuthBearerPlaceholder": "Sua chave de API ou token",
|
"httpDestAuthBearerPlaceholder": "Sua chave de API ou token",
|
||||||
"httpDestAuthBasicTitle": "Autenticação básica",
|
"httpDestAuthBasicTitle": "Autenticação básica",
|
||||||
"httpDestAuthBasicDescription": "Adiciona uma Autorização: cabeçalho <credentials> básico. Forneça credenciais como nome de usuário:senha.",
|
"httpDestAuthBasicDescription": "Adiciona um cabeçalho Authorization: Basic '<credentials>'. Forneça as credenciais como username:password.",
|
||||||
"httpDestAuthBasicPlaceholder": "Usuário:password",
|
"httpDestAuthBasicPlaceholder": "Usuário:password",
|
||||||
"httpDestAuthCustomTitle": "Cabeçalho personalizado",
|
"httpDestAuthCustomTitle": "Cabeçalho personalizado",
|
||||||
"httpDestAuthCustomDescription": "Especifique um nome e valor de cabeçalho HTTP personalizado para autenticação (por exemplo, X-API-Key).",
|
"httpDestAuthCustomDescription": "Especifique um nome e valor de cabeçalho HTTP personalizado para autenticação (por exemplo, X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Ключ подготовки обновлен",
|
"provisioningKeysUpdated": "Ключ подготовки обновлен",
|
||||||
"provisioningKeysUpdatedDescription": "Ваши изменения были сохранены.",
|
"provisioningKeysUpdatedDescription": "Ваши изменения были сохранены.",
|
||||||
"provisioningKeysBannerTitle": "Ключи подготовки сайта",
|
"provisioningKeysBannerTitle": "Ключи подготовки сайта",
|
||||||
"provisioningKeysBannerDescription": "Генерировать подготовительный ключ и использовать его вместе с Новым коннектором для автоматического создания сайтов при первом запуске — нет необходимости настраивать отдельные учетные данные для каждого сайта.",
|
"provisioningKeysBannerDescription": "Создайте ключ настройки и используйте его с соединителем Newt для автоматического создания сайтов при первом запуске — нет необходимости настраивать отдельные учетные данные для каждого сайта.",
|
||||||
"provisioningKeysBannerButtonText": "Узнать больше",
|
"provisioningKeysBannerButtonText": "Узнать больше",
|
||||||
"pendingSitesBannerTitle": "Ожидающие сайты",
|
"pendingSitesBannerTitle": "Ожидающие сайты",
|
||||||
"pendingSitesBannerDescription": "Сайты, связанные с использованием ключа подготовки, появляются здесь для проверки. Одобрите каждый сайт, прежде чем он станет активным и получит доступ к вашим ресурсам.",
|
"pendingSitesBannerDescription": "Сайты, подключающиеся с помощью ключа настройки, отображаются здесь для проверки.",
|
||||||
"pendingSitesBannerButtonText": "Узнать больше",
|
"pendingSitesBannerButtonText": "Узнать больше",
|
||||||
"apiKeysSettings": "Настройки {apiKeyName}",
|
"apiKeysSettings": "Настройки {apiKeyName}",
|
||||||
"userTitle": "Управление всеми пользователями",
|
"userTitle": "Управление всеми пользователями",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Пожалуйста, введите правильный номер порта",
|
"targetErrorInvalidPortDescription": "Пожалуйста, введите правильный номер порта",
|
||||||
"targetErrorNoSite": "Сайт не выбран",
|
"targetErrorNoSite": "Сайт не выбран",
|
||||||
"targetErrorNoSiteDescription": "Пожалуйста, выберите сайт для цели",
|
"targetErrorNoSiteDescription": "Пожалуйста, выберите сайт для цели",
|
||||||
|
"targetTargetsCleared": "Цели очищены",
|
||||||
|
"targetTargetsClearedDescription": "Все цели удалены из этого ресурса",
|
||||||
"targetCreated": "Цель создана",
|
"targetCreated": "Цель создана",
|
||||||
"targetCreatedDescription": "Цель была успешно создана",
|
"targetCreatedDescription": "Цель была успешно создана",
|
||||||
"targetErrorCreate": "Не удалось создать цель",
|
"targetErrorCreate": "Не удалось создать цель",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Функции предприятия, 50 пользователей, 50 сайтов, а также приоритетная поддержка."
|
"description": "Функции предприятия, 50 пользователей, 50 сайтов, а также приоритетная поддержка."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Только для личного пользования (бесплатная лицензия — без оформления)",
|
"personalUseOnly": "Только для личного использования (бесплатная лицензия - без оформления на кассе)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Продолжить оформление заказа"
|
"continueToCheckout": "Продолжить оформление заказа"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Машинные клиенты",
|
"machineClients": "Машинные клиенты",
|
||||||
"install": "Установить",
|
"install": "Установить",
|
||||||
"run": "Запустить",
|
"run": "Запустить",
|
||||||
|
"envFile": "Файл окружения",
|
||||||
|
"serviceFile": "Сервисный файл",
|
||||||
|
"enableAndStart": "Включить и запустить",
|
||||||
"clientNameDescription": "Отображаемое имя клиента, которое может быть изменено позже.",
|
"clientNameDescription": "Отображаемое имя клиента, которое может быть изменено позже.",
|
||||||
"clientAddress": "Адрес клиента (Дополнительно)",
|
"clientAddress": "Адрес клиента (Дополнительно)",
|
||||||
"setupFailedToFetchSubnet": "Не удалось получить подсеть по умолчанию",
|
"setupFailedToFetchSubnet": "Не удалось получить подсеть по умолчанию",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Нет аутентификации",
|
"httpDestAuthNoneTitle": "Нет аутентификации",
|
||||||
"httpDestAuthNoneDescription": "Отправляет запросы без заголовка авторизации.",
|
"httpDestAuthNoneDescription": "Отправляет запросы без заголовка авторизации.",
|
||||||
"httpDestAuthBearerTitle": "Жетон носителя",
|
"httpDestAuthBearerTitle": "Жетон носителя",
|
||||||
"httpDestAuthBearerDescription": "Добавляет заголовок Authorization: Bearer <token> к каждому запросу.",
|
"httpDestAuthBearerDescription": "Добавляет заголовок Authorization: Bearer '<token>' к каждому запросу.",
|
||||||
"httpDestAuthBearerPlaceholder": "Ваш ключ API или токен",
|
"httpDestAuthBearerPlaceholder": "Ваш ключ API или токен",
|
||||||
"httpDestAuthBasicTitle": "Базовая авторизация",
|
"httpDestAuthBasicTitle": "Базовая авторизация",
|
||||||
"httpDestAuthBasicDescription": "Добавляет Authorization: Basic <credentials> header. Предоставьте учетные данные в качестве имени пользователя:password.",
|
"httpDestAuthBasicDescription": "Добавляет заголовок Authorization: Basic '<credentials>'. Укажите учетные данные в формате username:password.",
|
||||||
"httpDestAuthBasicPlaceholder": "имя пользователя:пароль",
|
"httpDestAuthBasicPlaceholder": "имя пользователя:пароль",
|
||||||
"httpDestAuthCustomTitle": "Пользовательский заголовок",
|
"httpDestAuthCustomTitle": "Пользовательский заголовок",
|
||||||
"httpDestAuthCustomDescription": "Укажите пользовательское имя заголовка HTTP и значение для аутентификации (например, X-API-Key).",
|
"httpDestAuthCustomDescription": "Укажите пользовательское имя заголовка HTTP и значение для аутентификации (например, X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "Tedarik anahtarı güncellendi",
|
"provisioningKeysUpdated": "Tedarik anahtarı güncellendi",
|
||||||
"provisioningKeysUpdatedDescription": "Değişiklikleriniz kaydedildi.",
|
"provisioningKeysUpdatedDescription": "Değişiklikleriniz kaydedildi.",
|
||||||
"provisioningKeysBannerTitle": "Site Tedarik Anahtarları",
|
"provisioningKeysBannerTitle": "Site Tedarik Anahtarları",
|
||||||
"provisioningKeysBannerDescription": "Tedarik anahtarı oluşturun ve ilk başlangıçta siteleri otomatik olarak oluşturmak için Newt konektörüyle kullanın — her site için ayrı kimlik bilgileri ayarlamaya gerek yoktur.",
|
"provisioningKeysBannerDescription": "Bir sağlama anahtarı oluşturun ve ilk başlangıçta siteleri otomatik olarak oluşturmak için Newt bağlayıcısını kullanın - her site için ayrı kimlik bilgileri ayarlamaya gerek yok.",
|
||||||
"provisioningKeysBannerButtonText": "Daha fazla bilgi",
|
"provisioningKeysBannerButtonText": "Daha fazla bilgi",
|
||||||
"pendingSitesBannerTitle": "Bekleyen Siteler",
|
"pendingSitesBannerTitle": "Bekleyen Siteler",
|
||||||
"pendingSitesBannerDescription": "Tedarik anahtarı kullanarak bağlanan siteler burada incelenmek için görünür. Aktif hale gelmeden ve kaynaklarınıza erişim kazanmadan önce her siteyi onaylayın.",
|
"pendingSitesBannerDescription": "Bir sağlama anahtarı kullanarak bağlanan siteler, inceleme için burada görünür.",
|
||||||
"pendingSitesBannerButtonText": "Daha fazla bilgi",
|
"pendingSitesBannerButtonText": "Daha fazla bilgi",
|
||||||
"apiKeysSettings": "{apiKeyName} Ayarları",
|
"apiKeysSettings": "{apiKeyName} Ayarları",
|
||||||
"userTitle": "Tüm Kullanıcıları Yönet",
|
"userTitle": "Tüm Kullanıcıları Yönet",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "Lütfen geçerli bir port numarası girin",
|
"targetErrorInvalidPortDescription": "Lütfen geçerli bir port numarası girin",
|
||||||
"targetErrorNoSite": "Hiçbir site seçili değil",
|
"targetErrorNoSite": "Hiçbir site seçili değil",
|
||||||
"targetErrorNoSiteDescription": "Lütfen hedef için bir site seçin",
|
"targetErrorNoSiteDescription": "Lütfen hedef için bir site seçin",
|
||||||
|
"targetTargetsCleared": "Hedefler temizlendi",
|
||||||
|
"targetTargetsClearedDescription": "Bu kaynaktan tüm hedefler kaldırıldı",
|
||||||
"targetCreated": "Hedef oluşturuldu",
|
"targetCreated": "Hedef oluşturuldu",
|
||||||
"targetCreatedDescription": "Hedef başarıyla oluşturuldu",
|
"targetCreatedDescription": "Hedef başarıyla oluşturuldu",
|
||||||
"targetErrorCreate": "Hedef oluşturma başarısız oldu",
|
"targetErrorCreate": "Hedef oluşturma başarısız oldu",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "Kurumsal özellikler, 50 kullanıcı, 50 site ve öncelikli destek."
|
"description": "Kurumsal özellikler, 50 kullanıcı, 50 site ve öncelikli destek."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "Yalnızca kişisel kullanım (ücretsiz lisans — ödeme yapılmaz)",
|
"personalUseOnly": "Kişisel kullanım için (ücretsiz lisans - ödeme yok)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "Ödemeye Devam Et"
|
"continueToCheckout": "Ödemeye Devam Et"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "Makine İstemcileri",
|
"machineClients": "Makine İstemcileri",
|
||||||
"install": "Yükle",
|
"install": "Yükle",
|
||||||
"run": "Çalıştır",
|
"run": "Çalıştır",
|
||||||
|
"envFile": "Ortam Dosyası",
|
||||||
|
"serviceFile": "Servis Dosyası",
|
||||||
|
"enableAndStart": "Etkinleştir ve Başlat",
|
||||||
"clientNameDescription": "Daha sonra değiştirilebilecek istemcinin görünen adı.",
|
"clientNameDescription": "Daha sonra değiştirilebilecek istemcinin görünen adı.",
|
||||||
"clientAddress": "İstemci Adresi (Gelişmiş)",
|
"clientAddress": "İstemci Adresi (Gelişmiş)",
|
||||||
"setupFailedToFetchSubnet": "Varsayılan alt ağ alınamadı",
|
"setupFailedToFetchSubnet": "Varsayılan alt ağ alınamadı",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "Kimlik Doğrulama Yok",
|
"httpDestAuthNoneTitle": "Kimlik Doğrulama Yok",
|
||||||
"httpDestAuthNoneDescription": "Yetkilendirme başlığı olmadan istekler gönderir.",
|
"httpDestAuthNoneDescription": "Yetkilendirme başlığı olmadan istekler gönderir.",
|
||||||
"httpDestAuthBearerTitle": "Taşıyıcı Jetonu",
|
"httpDestAuthBearerTitle": "Taşıyıcı Jetonu",
|
||||||
"httpDestAuthBearerDescription": "Her isteğe bir Yetkilendirme: Taşıyıcı <token> başlığı ekler.",
|
"httpDestAuthBearerDescription": "Her isteğe bir Yetkilendirme: Taşıyıcı '<token>' üst bilgisi ekler.",
|
||||||
"httpDestAuthBearerPlaceholder": "API anahtarınız veya jetonunuz",
|
"httpDestAuthBearerPlaceholder": "API anahtarınız veya jetonunuz",
|
||||||
"httpDestAuthBasicTitle": "Temel Kimlik Doğrulama",
|
"httpDestAuthBasicTitle": "Temel Kimlik Doğrulama",
|
||||||
"httpDestAuthBasicDescription": "Authorization: Temel <belirtecikler> başlığı ekler. Yetkilendirmeleri kullanıcı adı:şifre olarak sağlayın.",
|
"httpDestAuthBasicDescription": "Bir Yetkilendirme: Temel '<credentials>' üst bilgisi ekler. Kimlik bilgilerini kullanıcı adı:şifre olarak sağlayın.",
|
||||||
"httpDestAuthBasicPlaceholder": "kullanıcı adı:şifre",
|
"httpDestAuthBasicPlaceholder": "kullanıcı adı:şifre",
|
||||||
"httpDestAuthCustomTitle": "Özel Başlık",
|
"httpDestAuthCustomTitle": "Özel Başlık",
|
||||||
"httpDestAuthCustomDescription": "Kimlik doğrulama için özel bir HTTP başlık adı ve değer belirtin (örn. X-API-Key).",
|
"httpDestAuthCustomDescription": "Kimlik doğrulama için özel bir HTTP başlık adı ve değer belirtin (örn. X-API-Key).",
|
||||||
|
|||||||
@@ -371,10 +371,10 @@
|
|||||||
"provisioningKeysUpdated": "置备密钥已更新",
|
"provisioningKeysUpdated": "置备密钥已更新",
|
||||||
"provisioningKeysUpdatedDescription": "您的更改已保存。",
|
"provisioningKeysUpdatedDescription": "您的更改已保存。",
|
||||||
"provisioningKeysBannerTitle": "站点置备密钥",
|
"provisioningKeysBannerTitle": "站点置备密钥",
|
||||||
"provisioningKeysBannerDescription": "生成一个预配键并使用它来在首次启动时自动创建站点——无需为每个站点设置单独的凭证。",
|
"provisioningKeysBannerDescription": "生成一个供应密钥,并将其与 Newt 连接器一起使用,以在首次启动时自动创建站点 - 无需为每个站点设置单独的凭据。",
|
||||||
"provisioningKeysBannerButtonText": "了解更多",
|
"provisioningKeysBannerButtonText": "了解更多",
|
||||||
"pendingSitesBannerTitle": "待定站点",
|
"pendingSitesBannerTitle": "待定站点",
|
||||||
"pendingSitesBannerDescription": "使用预配键连接的站点会出现在这里供审核。在站点开始运行之前批准并获取对您资源的访问权限。",
|
"pendingSitesBannerDescription": "使用供应密钥连接的站点将在此显示以供审核。",
|
||||||
"pendingSitesBannerButtonText": "了解更多",
|
"pendingSitesBannerButtonText": "了解更多",
|
||||||
"apiKeysSettings": "{apiKeyName} 设置",
|
"apiKeysSettings": "{apiKeyName} 设置",
|
||||||
"userTitle": "管理所有用户",
|
"userTitle": "管理所有用户",
|
||||||
@@ -624,6 +624,8 @@
|
|||||||
"targetErrorInvalidPortDescription": "请输入有效的端口号",
|
"targetErrorInvalidPortDescription": "请输入有效的端口号",
|
||||||
"targetErrorNoSite": "没有选择站点",
|
"targetErrorNoSite": "没有选择站点",
|
||||||
"targetErrorNoSiteDescription": "请选择目标站点",
|
"targetErrorNoSiteDescription": "请选择目标站点",
|
||||||
|
"targetTargetsCleared": "目标已清除",
|
||||||
|
"targetTargetsClearedDescription": "所有目标已从此资源中移除",
|
||||||
"targetCreated": "目标已创建",
|
"targetCreated": "目标已创建",
|
||||||
"targetCreatedDescription": "目标已成功创建",
|
"targetCreatedDescription": "目标已成功创建",
|
||||||
"targetErrorCreate": "创建目标失败",
|
"targetErrorCreate": "创建目标失败",
|
||||||
@@ -2346,7 +2348,7 @@
|
|||||||
"description": "企业特征、50个用户、50个站点和优先支持。"
|
"description": "企业特征、50个用户、50个站点和优先支持。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"personalUseOnly": "仅供个人使用 (免费许可证-无签出)",
|
"personalUseOnly": "仅限个人使用(免费许可 - 无需结账)",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"continueToCheckout": "继续签出"
|
"continueToCheckout": "继续签出"
|
||||||
},
|
},
|
||||||
@@ -2607,6 +2609,9 @@
|
|||||||
"machineClients": "机器客户端",
|
"machineClients": "机器客户端",
|
||||||
"install": "安装",
|
"install": "安装",
|
||||||
"run": "运行",
|
"run": "运行",
|
||||||
|
"envFile": "环境文件",
|
||||||
|
"serviceFile": "服务文件",
|
||||||
|
"enableAndStart": "启用并启动",
|
||||||
"clientNameDescription": "可以稍后更改的客户端的显示名称。",
|
"clientNameDescription": "可以稍后更改的客户端的显示名称。",
|
||||||
"clientAddress": "客户端地址 (高级)",
|
"clientAddress": "客户端地址 (高级)",
|
||||||
"setupFailedToFetchSubnet": "获取默认子网失败",
|
"setupFailedToFetchSubnet": "获取默认子网失败",
|
||||||
@@ -2845,10 +2850,10 @@
|
|||||||
"httpDestAuthNoneTitle": "无身份验证",
|
"httpDestAuthNoneTitle": "无身份验证",
|
||||||
"httpDestAuthNoneDescription": "在没有授权头的情况下发送请求。",
|
"httpDestAuthNoneDescription": "在没有授权头的情况下发送请求。",
|
||||||
"httpDestAuthBearerTitle": "持有者令牌",
|
"httpDestAuthBearerTitle": "持有者令牌",
|
||||||
"httpDestAuthBearerDescription": "添加授权:每个请求的标题为 <token>。",
|
"httpDestAuthBearerDescription": "在每个请求中添加授权:Bearer “<token>” 头。",
|
||||||
"httpDestAuthBearerPlaceholder": "您的 API 密钥或令牌",
|
"httpDestAuthBearerPlaceholder": "您的 API 密钥或令牌",
|
||||||
"httpDestAuthBasicTitle": "基本认证",
|
"httpDestAuthBasicTitle": "基本认证",
|
||||||
"httpDestAuthBasicDescription": "添加授权:基本 <credentials> 头。提供用户名:密码的凭据。",
|
"httpDestAuthBasicDescription": "添加一个Authorization: Basic \"<凭据>\" 标头。 以用户名:密码形式提供凭据。",
|
||||||
"httpDestAuthBasicPlaceholder": "用户名:密码",
|
"httpDestAuthBasicPlaceholder": "用户名:密码",
|
||||||
"httpDestAuthCustomTitle": "自定义标题",
|
"httpDestAuthCustomTitle": "自定义标题",
|
||||||
"httpDestAuthCustomDescription": "指定自定义 HTTP 头名称和身份验证值 (例如,X-API 键)。",
|
"httpDestAuthCustomDescription": "指定自定义 HTTP 头名称和身份验证值 (例如,X-API 键)。",
|
||||||
|
|||||||
@@ -479,10 +479,7 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
||||||
const anySitesOnline = targets.some(
|
const anySitesOnline = targets.some(
|
||||||
(target) =>
|
(target) => target.site.online
|
||||||
target.site.online ||
|
|
||||||
target.site.type === "local" ||
|
|
||||||
target.site.type === "wireguard"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -495,7 +492,7 @@ export async function getTraefikConfig(
|
|||||||
if (target.health == "unhealthy") {
|
if (target.health == "unhealthy") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any sites are online, exclude offline sites
|
// If any sites are online, exclude offline sites
|
||||||
if (anySitesOnline && !target.site.online) {
|
if (anySitesOnline && !target.site.online) {
|
||||||
return false;
|
return false;
|
||||||
@@ -610,10 +607,7 @@ export async function getTraefikConfig(
|
|||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = targets.some(
|
const anySitesOnline = targets.some(
|
||||||
(target) =>
|
(target) => target.site.online
|
||||||
target.site.online ||
|
|
||||||
target.site.type === "local" ||
|
|
||||||
target.site.type === "wireguard"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
@@ -621,7 +615,7 @@ export async function getTraefikConfig(
|
|||||||
if (!target.enabled) {
|
if (!target.enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any sites are online, exclude offline sites
|
// If any sites are online, exclude offline sites
|
||||||
if (anySitesOnline && !target.site.online) {
|
if (anySitesOnline && !target.site.online) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import {
|
|||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { and, eq, gt, desc, max, sql } from "drizzle-orm";
|
import { and, eq, gt, desc, max, sql } from "drizzle-orm";
|
||||||
|
import { decrypt } from "@server/lib/crypto";
|
||||||
|
import config from "@server/lib/config";
|
||||||
import {
|
import {
|
||||||
LogType,
|
LogType,
|
||||||
LOG_TYPES,
|
LOG_TYPES,
|
||||||
@@ -272,19 +274,20 @@ export class LogStreamingManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse config – skip destination if config is unparseable
|
// Decrypt and parse config – skip destination if either step fails
|
||||||
let config: HttpConfig;
|
let configFromDb: HttpConfig;
|
||||||
try {
|
try {
|
||||||
config = JSON.parse(dest.config) as HttpConfig;
|
const decryptedConfig = decrypt(dest.config, config.getRawConfig().server.secret!);
|
||||||
|
configFromDb = JSON.parse(decryptedConfig) as HttpConfig;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`LogStreamingManager: destination ${dest.destinationId} has invalid JSON config`,
|
`LogStreamingManager: destination ${dest.destinationId} has invalid or undecryptable config`,
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = this.createProvider(dest.type, config);
|
const provider = this.createProvider(dest.type, configFromDb);
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`LogStreamingManager: unsupported destination type "${dest.type}" ` +
|
`LogStreamingManager: unsupported destination type "${dest.type}" ` +
|
||||||
|
|||||||
@@ -671,10 +671,7 @@ export async function getTraefikConfig(
|
|||||||
|
|
||||||
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
// TODO: HOW TO HANDLE ^^^^^^ BETTER
|
||||||
const anySitesOnline = targets.some(
|
const anySitesOnline = targets.some(
|
||||||
(target) =>
|
(target) => target.site.online
|
||||||
target.site.online ||
|
|
||||||
target.site.type === "local" ||
|
|
||||||
target.site.type === "wireguard"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -802,10 +799,7 @@ export async function getTraefikConfig(
|
|||||||
servers: (() => {
|
servers: (() => {
|
||||||
// Check if any sites are online
|
// Check if any sites are online
|
||||||
const anySitesOnline = targets.some(
|
const anySitesOnline = targets.some(
|
||||||
(target) =>
|
(target) => target.site.online
|
||||||
target.site.online ||
|
|
||||||
target.site.type === "local" ||
|
|
||||||
target.site.type === "wireguard"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import createHttpError from "http-errors";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
import { encrypt } from "@server/lib/crypto";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z.strictObject({
|
const paramsSchema = z.strictObject({
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
@@ -87,7 +89,10 @@ export async function createEventStreamingDestination(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, config, enabled } = parsedBody.data;
|
const { type, config: configToSet, enabled } = parsedBody.data;
|
||||||
|
|
||||||
|
const key = config.getRawConfig().server.secret!;
|
||||||
|
const encryptedConfig = encrypt(configToSet, key);
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
@@ -96,7 +101,7 @@ export async function createEventStreamingDestination(
|
|||||||
.values({
|
.values({
|
||||||
orgId,
|
orgId,
|
||||||
type,
|
type,
|
||||||
config,
|
config: encryptedConfig,
|
||||||
enabled,
|
enabled,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
import { decrypt } from "@server/lib/crypto";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z.strictObject({
|
const paramsSchema = z.strictObject({
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
@@ -121,9 +123,22 @@ export async function listEventStreamingDestinations(
|
|||||||
.from(eventStreamingDestinations)
|
.from(eventStreamingDestinations)
|
||||||
.where(eq(eventStreamingDestinations.orgId, orgId));
|
.where(eq(eventStreamingDestinations.orgId, orgId));
|
||||||
|
|
||||||
|
const key = config.getRawConfig().server.secret!;
|
||||||
|
const decryptedList = list.map((dest) => {
|
||||||
|
try {
|
||||||
|
return { ...dest, config: decrypt(dest.config, key) };
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
`listEventStreamingDestinations: failed to decrypt config for destination ${dest.destinationId}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return { ...dest, config: "" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return response<ListEventStreamingDestinationsResponse>(res, {
|
return response<ListEventStreamingDestinationsResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
destinations: list,
|
destinations: decryptedList,
|
||||||
pagination: {
|
pagination: {
|
||||||
total: count,
|
total: count,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import logger from "@server/logger";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import { encrypt } from "@server/lib/crypto";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
|
||||||
const paramsSchema = z
|
const paramsSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -110,14 +111,17 @@ export async function updateEventStreamingDestination(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, config, enabled, sendAccessLogs, sendActionLogs, sendConnectionLogs, sendRequestLogs } = parsedBody.data;
|
const { type, config: configToUpdate, enabled, sendAccessLogs, sendActionLogs, sendConnectionLogs, sendRequestLogs } = parsedBody.data;
|
||||||
|
|
||||||
const updateData: Record<string, unknown> = {
|
const updateData: Record<string, unknown> = {
|
||||||
updatedAt: Date.now()
|
updatedAt: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type !== undefined) updateData.type = type;
|
if (type !== undefined) updateData.type = type;
|
||||||
if (config !== undefined) updateData.config = config;
|
if (configToUpdate !== undefined) {
|
||||||
|
const key = config.getRawConfig().server.secret!;
|
||||||
|
updateData.config = encrypt(configToUpdate, key);
|
||||||
|
}
|
||||||
if (enabled !== undefined) updateData.enabled = enabled;
|
if (enabled !== undefined) updateData.enabled = enabled;
|
||||||
if (sendAccessLogs !== undefined) updateData.sendAccessLogs = sendAccessLogs;
|
if (sendAccessLogs !== undefined) updateData.sendAccessLogs = sendAccessLogs;
|
||||||
if (sendActionLogs !== undefined) updateData.sendActionLogs = sendActionLogs;
|
if (sendActionLogs !== undefined) updateData.sendActionLogs = sendActionLogs;
|
||||||
|
|||||||
@@ -171,9 +171,8 @@ export async function flushSiteBandwidthToDb(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PostgreSQL: batch UPDATE … FROM (VALUES …) — single round-trip per chunk.
|
// PostgreSQL: batch UPDATE … FROM (VALUES …) — single round-trip per chunk.
|
||||||
const valuesList = chunk.map(
|
const valuesList = chunk.map(([publicKey, { bytesIn, bytesOut }]) =>
|
||||||
([publicKey, { bytesIn, bytesOut }]) =>
|
sql`(${publicKey}::text, ${bytesIn}::real, ${bytesOut}::real)`
|
||||||
sql`(${publicKey}, ${bytesIn}, ${bytesOut})`
|
|
||||||
);
|
);
|
||||||
const valuesClause = sql.join(valuesList, sql`, `);
|
const valuesClause = sql.join(valuesList, sql`, `);
|
||||||
return dbQueryRows<{ orgId: string; pubKey: string }>(sql`
|
return dbQueryRows<{ orgId: string; pubKey: string }>(sql`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { sites, clients, olms } from "@server/db";
|
import { sites, clients, olms } from "@server/db";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { inArray } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +21,7 @@ import logger from "@server/logger";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const FLUSH_INTERVAL_MS = 10_000; // Flush every 10 seconds
|
const FLUSH_INTERVAL_MS = 10_000; // Flush every 10 seconds
|
||||||
const MAX_RETRIES = 2;
|
const MAX_RETRIES = 5;
|
||||||
const BASE_DELAY_MS = 50;
|
const BASE_DELAY_MS = 50;
|
||||||
|
|
||||||
// ── Site (newt) pings ──────────────────────────────────────────────────
|
// ── Site (newt) pings ──────────────────────────────────────────────────
|
||||||
@@ -36,6 +36,14 @@ const pendingOlmArchiveResets: Set<string> = new Set();
|
|||||||
|
|
||||||
let flushTimer: NodeJS.Timeout | null = null;
|
let flushTimer: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guard that prevents two flush cycles from running concurrently.
|
||||||
|
* setInterval does not await async callbacks, so without this a slow flush
|
||||||
|
* (e.g. due to DB latency) would overlap with the next scheduled cycle and
|
||||||
|
* the two concurrent bulk UPDATEs would deadlock each other.
|
||||||
|
*/
|
||||||
|
let isFlushing = false;
|
||||||
|
|
||||||
// ── Public API ─────────────────────────────────────────────────────────
|
// ── Public API ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +80,12 @@ export function recordClientPing(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush all accumulated site pings to the database.
|
* Flush all accumulated site pings to the database.
|
||||||
|
*
|
||||||
|
* Each batch of up to BATCH_SIZE rows is written with a **single** UPDATE
|
||||||
|
* statement. We use the maximum timestamp across the batch so that `lastPing`
|
||||||
|
* reflects the most recent ping seen for any site in the group. This avoids
|
||||||
|
* the multi-statement transaction that previously created additional
|
||||||
|
* row-lock ordering hazards.
|
||||||
*/
|
*/
|
||||||
async function flushSitePingsToDb(): Promise<void> {
|
async function flushSitePingsToDb(): Promise<void> {
|
||||||
if (pendingSitePings.size === 0) {
|
if (pendingSitePings.size === 0) {
|
||||||
@@ -83,55 +97,35 @@ async function flushSitePingsToDb(): Promise<void> {
|
|||||||
const pingsToFlush = new Map(pendingSitePings);
|
const pingsToFlush = new Map(pendingSitePings);
|
||||||
pendingSitePings.clear();
|
pendingSitePings.clear();
|
||||||
|
|
||||||
// Sort by siteId for consistent lock ordering (prevents deadlocks)
|
const entries = Array.from(pingsToFlush.entries());
|
||||||
const sortedEntries = Array.from(pingsToFlush.entries()).sort(
|
|
||||||
([a], [b]) => a - b
|
|
||||||
);
|
|
||||||
|
|
||||||
const BATCH_SIZE = 50;
|
const BATCH_SIZE = 50;
|
||||||
for (let i = 0; i < sortedEntries.length; i += BATCH_SIZE) {
|
for (let i = 0; i < entries.length; i += BATCH_SIZE) {
|
||||||
const batch = sortedEntries.slice(i, i + BATCH_SIZE);
|
const batch = entries.slice(i, i + BATCH_SIZE);
|
||||||
|
|
||||||
|
// Use the latest timestamp in the batch so that `lastPing` always
|
||||||
|
// moves forward. Using a single timestamp for the whole batch means
|
||||||
|
// we only ever need one UPDATE statement (no transaction).
|
||||||
|
const maxTimestamp = Math.max(...batch.map(([, ts]) => ts));
|
||||||
|
const siteIds = batch.map(([id]) => id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await withRetry(async () => {
|
await withRetry(async () => {
|
||||||
// Group by timestamp for efficient bulk updates
|
await db
|
||||||
const byTimestamp = new Map<number, number[]>();
|
.update(sites)
|
||||||
for (const [siteId, timestamp] of batch) {
|
.set({
|
||||||
const group = byTimestamp.get(timestamp) || [];
|
online: true,
|
||||||
group.push(siteId);
|
lastPing: maxTimestamp
|
||||||
byTimestamp.set(timestamp, group);
|
})
|
||||||
}
|
.where(inArray(sites.siteId, siteIds));
|
||||||
|
|
||||||
if (byTimestamp.size === 1) {
|
|
||||||
const [timestamp, siteIds] = Array.from(
|
|
||||||
byTimestamp.entries()
|
|
||||||
)[0];
|
|
||||||
await db
|
|
||||||
.update(sites)
|
|
||||||
.set({
|
|
||||||
online: true,
|
|
||||||
lastPing: timestamp
|
|
||||||
})
|
|
||||||
.where(inArray(sites.siteId, siteIds));
|
|
||||||
} else {
|
|
||||||
await db.transaction(async (tx) => {
|
|
||||||
for (const [timestamp, siteIds] of byTimestamp) {
|
|
||||||
await tx
|
|
||||||
.update(sites)
|
|
||||||
.set({
|
|
||||||
online: true,
|
|
||||||
lastPing: timestamp
|
|
||||||
})
|
|
||||||
.where(inArray(sites.siteId, siteIds));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, "flushSitePingsToDb");
|
}, "flushSitePingsToDb");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Failed to flush site ping batch (${batch.length} sites), re-queuing for next cycle`,
|
`Failed to flush site ping batch (${batch.length} sites), re-queuing for next cycle`,
|
||||||
{ error }
|
{ error }
|
||||||
);
|
);
|
||||||
|
// Re-queue only if the preserved timestamp is newer than any
|
||||||
|
// update that may have landed since we snapshotted.
|
||||||
for (const [siteId, timestamp] of batch) {
|
for (const [siteId, timestamp] of batch) {
|
||||||
const existing = pendingSitePings.get(siteId);
|
const existing = pendingSitePings.get(siteId);
|
||||||
if (!existing || existing < timestamp) {
|
if (!existing || existing < timestamp) {
|
||||||
@@ -144,6 +138,8 @@ async function flushSitePingsToDb(): Promise<void> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush all accumulated client (OLM) pings to the database.
|
* Flush all accumulated client (OLM) pings to the database.
|
||||||
|
*
|
||||||
|
* Same single-UPDATE-per-batch approach as `flushSitePingsToDb`.
|
||||||
*/
|
*/
|
||||||
async function flushClientPingsToDb(): Promise<void> {
|
async function flushClientPingsToDb(): Promise<void> {
|
||||||
if (pendingClientPings.size === 0 && pendingOlmArchiveResets.size === 0) {
|
if (pendingClientPings.size === 0 && pendingOlmArchiveResets.size === 0) {
|
||||||
@@ -159,51 +155,25 @@ async function flushClientPingsToDb(): Promise<void> {
|
|||||||
|
|
||||||
// ── Flush client pings ─────────────────────────────────────────────
|
// ── Flush client pings ─────────────────────────────────────────────
|
||||||
if (pingsToFlush.size > 0) {
|
if (pingsToFlush.size > 0) {
|
||||||
const sortedEntries = Array.from(pingsToFlush.entries()).sort(
|
const entries = Array.from(pingsToFlush.entries());
|
||||||
([a], [b]) => a - b
|
|
||||||
);
|
|
||||||
|
|
||||||
const BATCH_SIZE = 50;
|
const BATCH_SIZE = 50;
|
||||||
for (let i = 0; i < sortedEntries.length; i += BATCH_SIZE) {
|
for (let i = 0; i < entries.length; i += BATCH_SIZE) {
|
||||||
const batch = sortedEntries.slice(i, i + BATCH_SIZE);
|
const batch = entries.slice(i, i + BATCH_SIZE);
|
||||||
|
|
||||||
|
const maxTimestamp = Math.max(...batch.map(([, ts]) => ts));
|
||||||
|
const clientIds = batch.map(([id]) => id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await withRetry(async () => {
|
await withRetry(async () => {
|
||||||
const byTimestamp = new Map<number, number[]>();
|
await db
|
||||||
for (const [clientId, timestamp] of batch) {
|
.update(clients)
|
||||||
const group = byTimestamp.get(timestamp) || [];
|
.set({
|
||||||
group.push(clientId);
|
lastPing: maxTimestamp,
|
||||||
byTimestamp.set(timestamp, group);
|
online: true,
|
||||||
}
|
archived: false
|
||||||
|
})
|
||||||
if (byTimestamp.size === 1) {
|
.where(inArray(clients.clientId, clientIds));
|
||||||
const [timestamp, clientIds] = Array.from(
|
|
||||||
byTimestamp.entries()
|
|
||||||
)[0];
|
|
||||||
await db
|
|
||||||
.update(clients)
|
|
||||||
.set({
|
|
||||||
lastPing: timestamp,
|
|
||||||
online: true,
|
|
||||||
archived: false
|
|
||||||
})
|
|
||||||
.where(inArray(clients.clientId, clientIds));
|
|
||||||
} else {
|
|
||||||
await db.transaction(async (tx) => {
|
|
||||||
for (const [timestamp, clientIds] of byTimestamp) {
|
|
||||||
await tx
|
|
||||||
.update(clients)
|
|
||||||
.set({
|
|
||||||
lastPing: timestamp,
|
|
||||||
online: true,
|
|
||||||
archived: false
|
|
||||||
})
|
|
||||||
.where(
|
|
||||||
inArray(clients.clientId, clientIds)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, "flushClientPingsToDb");
|
}, "flushClientPingsToDb");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -260,7 +230,12 @@ export async function flushPingsToDb(): Promise<void> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple retry wrapper with exponential backoff for transient errors
|
* Simple retry wrapper with exponential backoff for transient errors
|
||||||
* (connection timeouts, unexpected disconnects).
|
* (deadlocks, connection timeouts, unexpected disconnects).
|
||||||
|
*
|
||||||
|
* PostgreSQL deadlocks (40P01) are always safe to retry: the database
|
||||||
|
* guarantees exactly one winner per deadlock pair, so the loser just needs
|
||||||
|
* to try again. MAX_RETRIES is intentionally higher than typical connection
|
||||||
|
* retry budgets to give deadlock victims enough chances to succeed.
|
||||||
*/
|
*/
|
||||||
async function withRetry<T>(
|
async function withRetry<T>(
|
||||||
operation: () => Promise<T>,
|
operation: () => Promise<T>,
|
||||||
@@ -277,7 +252,8 @@ async function withRetry<T>(
|
|||||||
const jitter = Math.random() * baseDelay;
|
const jitter = Math.random() * baseDelay;
|
||||||
const delay = baseDelay + jitter;
|
const delay = baseDelay + jitter;
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Transient DB error in ${context}, retrying attempt ${attempt}/${MAX_RETRIES} after ${delay.toFixed(0)}ms`
|
`Transient DB error in ${context}, retrying attempt ${attempt}/${MAX_RETRIES} after ${delay.toFixed(0)}ms`,
|
||||||
|
{ code: error?.code ?? error?.cause?.code }
|
||||||
);
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
continue;
|
continue;
|
||||||
@@ -288,14 +264,14 @@ async function withRetry<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect transient connection errors that are safe to retry.
|
* Detect transient errors that are safe to retry.
|
||||||
*/
|
*/
|
||||||
function isTransientError(error: any): boolean {
|
function isTransientError(error: any): boolean {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
|
|
||||||
const message = (error.message || "").toLowerCase();
|
const message = (error.message || "").toLowerCase();
|
||||||
const causeMessage = (error.cause?.message || "").toLowerCase();
|
const causeMessage = (error.cause?.message || "").toLowerCase();
|
||||||
const code = error.code || "";
|
const code = error.code || error.cause?.code || "";
|
||||||
|
|
||||||
// Connection timeout / terminated
|
// Connection timeout / terminated
|
||||||
if (
|
if (
|
||||||
@@ -308,12 +284,17 @@ function isTransientError(error: any): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostgreSQL deadlock
|
// PostgreSQL deadlock detected — always safe to retry (one winner guaranteed)
|
||||||
if (code === "40P01" || message.includes("deadlock")) {
|
if (code === "40P01" || message.includes("deadlock")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECONNRESET, ECONNREFUSED, EPIPE
|
// PostgreSQL serialization failure
|
||||||
|
if (code === "40001") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECONNRESET, ECONNREFUSED, EPIPE, ETIMEDOUT
|
||||||
if (
|
if (
|
||||||
code === "ECONNRESET" ||
|
code === "ECONNRESET" ||
|
||||||
code === "ECONNREFUSED" ||
|
code === "ECONNREFUSED" ||
|
||||||
@@ -337,12 +318,26 @@ export function startPingAccumulator(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flushTimer = setInterval(async () => {
|
flushTimer = setInterval(async () => {
|
||||||
|
// Skip this tick if the previous flush is still in progress.
|
||||||
|
// setInterval does not await async callbacks, so without this guard
|
||||||
|
// two flush cycles can run concurrently and deadlock each other on
|
||||||
|
// overlapping bulk UPDATE statements.
|
||||||
|
if (isFlushing) {
|
||||||
|
logger.debug(
|
||||||
|
"Ping accumulator: previous flush still in progress, skipping cycle"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFlushing = true;
|
||||||
try {
|
try {
|
||||||
await flushPingsToDb();
|
await flushPingsToDb();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Unhandled error in ping accumulator flush", {
|
logger.error("Unhandled error in ping accumulator flush", {
|
||||||
error
|
error
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
isFlushing = false;
|
||||||
}
|
}
|
||||||
}, FLUSH_INTERVAL_MS);
|
}, FLUSH_INTERVAL_MS);
|
||||||
|
|
||||||
@@ -364,7 +359,22 @@ export async function stopPingAccumulator(): Promise<void> {
|
|||||||
flushTimer = null;
|
flushTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final flush to persist any remaining pings
|
// Final flush to persist any remaining pings.
|
||||||
|
// Wait for any in-progress flush to finish first so we don't race.
|
||||||
|
if (isFlushing) {
|
||||||
|
logger.debug(
|
||||||
|
"Ping accumulator: waiting for in-progress flush before stopping…"
|
||||||
|
);
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
const poll = setInterval(() => {
|
||||||
|
if (!isFlushing) {
|
||||||
|
clearInterval(poll);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await flushPingsToDb();
|
await flushPingsToDb();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -379,4 +389,4 @@ export async function stopPingAccumulator(): Promise<void> {
|
|||||||
*/
|
*/
|
||||||
export function getPendingPingCount(): number {
|
export function getPendingPingCount(): number {
|
||||||
return pendingSitePings.size + pendingClientPings.size;
|
return pendingSitePings.size + pendingClientPings.size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { build } from "@server/build";
|
|||||||
import { usageService } from "@server/lib/billing/usageService";
|
import { usageService } from "@server/lib/billing/usageService";
|
||||||
import { FeatureId } from "@server/lib/billing";
|
import { FeatureId } from "@server/lib/billing";
|
||||||
import { INSPECT_MAX_BYTES } from "buffer";
|
import { INSPECT_MAX_BYTES } from "buffer";
|
||||||
import { v } from "@faker-js/faker/dist/airline-Dz1uGqgJ";
|
import { getNextAvailableClientSubnet } from "@server/lib/ip";
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
provisioningKey: z.string().nonempty(),
|
provisioningKey: z.string().nonempty(),
|
||||||
@@ -152,6 +152,11 @@ export async function registerNewt(
|
|||||||
createHttpError(HttpCode.NOT_FOUND, "Organization not found")
|
createHttpError(HttpCode.NOT_FOUND, "Organization not found")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!org.subnet) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Organization subnet not found")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// SaaS billing check
|
// SaaS billing check
|
||||||
if (build == "saas") {
|
if (build == "saas") {
|
||||||
@@ -190,6 +195,20 @@ export async function registerNewt(
|
|||||||
let newSiteId: number | undefined;
|
let newSiteId: number | undefined;
|
||||||
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
|
|
||||||
|
const newClientAddress = await getNextAvailableClientSubnet(orgId);
|
||||||
|
if (!newClientAddress) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"No available subnet found"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let clientAddress = newClientAddress.split("/")[0];
|
||||||
|
clientAddress = `${clientAddress}/${org.subnet!.split("/")[1]}`; // we want the block size of the whole org
|
||||||
|
|
||||||
// Create the site (type "newt", name = niceId)
|
// Create the site (type "newt", name = niceId)
|
||||||
const [newSite] = await trx
|
const [newSite] = await trx
|
||||||
.insert(sites)
|
.insert(sites)
|
||||||
@@ -197,6 +216,7 @@ export async function registerNewt(
|
|||||||
orgId,
|
orgId,
|
||||||
name: name || niceId,
|
name: name || niceId,
|
||||||
niceId,
|
niceId,
|
||||||
|
address: clientAddress,
|
||||||
type: "newt",
|
type: "newt",
|
||||||
dockerSocketEnabled: true,
|
dockerSocketEnabled: true,
|
||||||
status: keyRecord.approveNewSites ? "approved" : "pending",
|
status: keyRecord.approveNewSites ? "approved" : "pending",
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ export default function ConnectionLogsPage() {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const clientType = row.original.clientType === "olm" ? "machine" : "user";
|
const clientType = row.original.userId ? "user" : "machine";
|
||||||
if (row.original.clientName && row.original.clientNiceId) {
|
if (row.original.clientName && row.original.clientNiceId) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -106,7 +106,9 @@ function DestinationCard({
|
|||||||
{/* URL preview */}
|
{/* URL preview */}
|
||||||
<p className="text-xs text-muted-foreground truncate">
|
<p className="text-xs text-muted-foreground truncate">
|
||||||
{cfg.url || (
|
{cfg.url || (
|
||||||
<span className="italic">{t("streamingNoUrlConfigured")}</span>
|
<span className="italic">
|
||||||
|
{t("streamingNoUrlConfigured")}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -160,7 +162,9 @@ function AddDestinationCard({ onClick }: { onClick: () => void }) {
|
|||||||
<div className="flex items-center justify-center w-9 h-9 rounded-md border-2 border-dashed border-current">
|
<div className="flex items-center justify-center w-9 h-9 rounded-md border-2 border-dashed border-current">
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm font-medium">{t("streamingAddDestination")}</span>
|
<span className="text-sm font-medium">
|
||||||
|
{t("streamingAddDestination")}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -186,7 +190,9 @@ function DestinationTypePicker({
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const [selected, setSelected] = useState<DestinationType>("http");
|
const [selected, setSelected] = useState<DestinationType>("http");
|
||||||
|
|
||||||
const destinationTypeOptions: ReadonlyArray<StrategyOption<DestinationType>> = [
|
const destinationTypeOptions: ReadonlyArray<
|
||||||
|
StrategyOption<DestinationType>
|
||||||
|
> = [
|
||||||
{
|
{
|
||||||
id: "http",
|
id: "http",
|
||||||
title: t("streamingHttpWebhookTitle"),
|
title: t("streamingHttpWebhookTitle"),
|
||||||
@@ -233,13 +239,19 @@ function DestinationTypePicker({
|
|||||||
<Credenza open={open} onOpenChange={onOpenChange}>
|
<Credenza open={open} onOpenChange={onOpenChange}>
|
||||||
<CredenzaContent className="sm:max-w-lg">
|
<CredenzaContent className="sm:max-w-lg">
|
||||||
<CredenzaHeader>
|
<CredenzaHeader>
|
||||||
<CredenzaTitle>{t("streamingAddDestination")}</CredenzaTitle>
|
<CredenzaTitle>
|
||||||
|
{t("streamingAddDestination")}
|
||||||
|
</CredenzaTitle>
|
||||||
<CredenzaDescription>
|
<CredenzaDescription>
|
||||||
{t("streamingTypePickerDescription")}
|
{t("streamingTypePickerDescription")}
|
||||||
</CredenzaDescription>
|
</CredenzaDescription>
|
||||||
</CredenzaHeader>
|
</CredenzaHeader>
|
||||||
<CredenzaBody>
|
<CredenzaBody>
|
||||||
<div className={isPaywalled ? "pointer-events-none opacity-50" : ""}>
|
<div
|
||||||
|
className={
|
||||||
|
isPaywalled ? "pointer-events-none opacity-50" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
<StrategySelect
|
<StrategySelect
|
||||||
options={destinationTypeOptions}
|
options={destinationTypeOptions}
|
||||||
value={selected}
|
value={selected}
|
||||||
@@ -301,10 +313,7 @@ export default function StreamingDestinationsPage() {
|
|||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t("streamingFailedToLoad"),
|
title: t("streamingFailedToLoad"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(e, t("streamingUnexpectedError"))
|
||||||
e,
|
|
||||||
t("streamingUnexpectedError")
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -341,10 +350,7 @@ export default function StreamingDestinationsPage() {
|
|||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t("streamingFailedToUpdate"),
|
title: t("streamingFailedToUpdate"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(e, t("streamingUnexpectedError"))
|
||||||
e,
|
|
||||||
t("streamingUnexpectedError")
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTogglingIds((prev) => {
|
setTogglingIds((prev) => {
|
||||||
@@ -375,10 +381,7 @@ export default function StreamingDestinationsPage() {
|
|||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: t("streamingFailedToDelete"),
|
title: t("streamingFailedToDelete"),
|
||||||
description: formatAxiosError(
|
description: formatAxiosError(e, t("streamingUnexpectedError"))
|
||||||
e,
|
|
||||||
t("streamingUnexpectedError")
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setDeleting(false);
|
setDeleting(false);
|
||||||
@@ -459,13 +462,14 @@ export default function StreamingDestinationsPage() {
|
|||||||
if (!v) setDeleteTarget(null);
|
if (!v) setDeleteTarget(null);
|
||||||
}}
|
}}
|
||||||
string={
|
string={
|
||||||
parseHttpConfig(deleteTarget.config).name || t("streamingDeleteDialogThisDestination")
|
parseHttpConfig(deleteTarget.config).name ||
|
||||||
|
t("streamingDeleteDialogThisDestination")
|
||||||
}
|
}
|
||||||
title={t("streamingDeleteTitle")}
|
title={t("streamingDeleteTitle")}
|
||||||
dialog={
|
dialog={
|
||||||
<p className="text-sm text-muted-foreground">
|
<p>
|
||||||
{t("streamingDeleteDialogAreYouSure")}{" "}
|
{t("streamingDeleteDialogAreYouSure")}{" "}
|
||||||
<span className="font-semibold text-foreground">
|
<span>
|
||||||
{parseHttpConfig(deleteTarget.config).name ||
|
{parseHttpConfig(deleteTarget.config).name ||
|
||||||
t("streamingDeleteDialogThisDestination")}
|
t("streamingDeleteDialogThisDestination")}
|
||||||
</span>
|
</span>
|
||||||
@@ -478,4 +482,4 @@ export default function StreamingDestinationsPage() {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user