mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-17 22:29:15 +00:00
Merge 'master' into 'soft-reset'
This commit is contained in:
commit
5c473d96a9
|
@ -355,9 +355,9 @@
|
|||
"fallback_select_provider_console": "Hostinq konsoluna giriş.",
|
||||
"authorization_failed": "Bu açarla daxil olmaq alınmadı",
|
||||
"fallback_select_provider_console_hint": "Məsələn: Hetzner.",
|
||||
"server_provider_connected": "Server provayderinizə qoşulur",
|
||||
"server_provider_connected_description": "Əlaqə quruldu. {} girişi ilə nişanınızı daxil edin:",
|
||||
"server_provider_connected_placeholder": "Server provayderi nişanı",
|
||||
"provider_connected": "{} qoşulur",
|
||||
"provider_connected_description": "Əlaqə quruldu. {} girişi ilə nişanınızı daxil edin:",
|
||||
"provider_connected_placeholder": "{} nişanı",
|
||||
"confirm_server": "Serveri təsdiqləyin",
|
||||
"confirm_server_description": "Server tapdım! Onun olduğunu təsdiq edin:",
|
||||
"confirm_server_accept": "Bəli, odur",
|
||||
|
@ -368,11 +368,7 @@
|
|||
"domain_not_available_on_token": "Daxil edilmiş işarənin tələb olunan domenə girişi yoxdur.",
|
||||
"modal_confirmation_dns_valid": "Əks DNS düzgündür",
|
||||
"modal_confirmation_dns_invalid": "Əks DNS fərqli domenə işarə edir",
|
||||
"modal_confirmation_ip_valid": "IP DNS qeydində göstərilənə uyğun gəlir",
|
||||
"confirm_cloudflare": "Cloudflare-ə qoşulur",
|
||||
"confirm_cloudflare_description": "{} hüququ olan Cloudflare tokenini daxil edin:",
|
||||
"confirm_backblaze": "Backblaze-ə qoşulur",
|
||||
"confirm_backblaze_description": "Ehtiyat saxlama hüququ olan Backblaze tokenini daxil edin:"
|
||||
"modal_confirmation_ip_valid": "IP DNS qeydində göstərilənə uyğun gəlir"
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
|
|
@ -97,8 +97,8 @@
|
|||
"fallback_select_root_ssh": "Каранёвы доступ да сервера праз SSH.",
|
||||
"authorization_failed": "Не ўдалося ўвайсці з гэтым ключом",
|
||||
"fallback_select_provider_console_hint": "Напрыклад, Hetzner.",
|
||||
"server_provider_connected": "Падлучэнне да вашага сервернага правайдэра",
|
||||
"server_provider_connected_placeholder": "Токен сервернага правайдэра",
|
||||
"provider_connected": "Падлучэнне да вашага {}",
|
||||
"provider_connected_placeholder": "Токен {}",
|
||||
"confirm_server": "Пацвердзіце сервер",
|
||||
"confirm_server_description": "Знайшлі сервер! Пацьвердзіце, што гэта ён:",
|
||||
"confirm_server_accept": "Да, гэта ён",
|
||||
|
@ -109,16 +109,12 @@
|
|||
"modal_confirmation_dns_valid": "Зваротны DNS карэктны",
|
||||
"modal_confirmation_dns_invalid": "Зваротны DNS паказвае на іншы дамен",
|
||||
"modal_confirmation_ip_invalid": "IP не супадае з паказаным у DNS запісу",
|
||||
"confirm_backblaze": "Падключэнне да Backblaze",
|
||||
"confirm_backblaze_description": "Увядзіце токен Backblaze, які мае правы на сховішча рэзервовых копій:",
|
||||
"fallback_select_provider_console": "Доступ да кансолі хостынгу.",
|
||||
"server_provider_connected_description": "Сувязь устаноўлена. Увядзіце свой токен з доступам да {}:",
|
||||
"provider_connected_description": "Сувязь устаноўлена. Увядзіце свой токен з доступам да {}:",
|
||||
"choose_server": "Выберыце сервер",
|
||||
"no_servers": "На вашым акаўнце няма даступных сэрвэраў.",
|
||||
"modal_confirmation_description": "Падлучэнне да няправільнага сервера можа прывесці да дэструктыўных наступстваў.",
|
||||
"modal_confirmation_ip_valid": "IP супадае з паказаным у DNS запісу",
|
||||
"confirm_cloudflare": "Падключэнне да DNS Правайдэра",
|
||||
"confirm_cloudflare_description": "Увядзіце токен DNS Правайдэра, які мае правы на {}:"
|
||||
"modal_confirmation_ip_valid": "IP супадае з паказаным у DNS запісу"
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
@ -514,4 +510,4 @@
|
|||
"support": {
|
||||
"title": "Падтрымка SelfPrivacy"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -369,9 +369,9 @@
|
|||
"fallback_select_root_ssh": "Kořenový přístup SSH k serveru.",
|
||||
"fallback_select_provider_console": "Přístup ke konzole serveru mého prodiveru.",
|
||||
"authorization_failed": "Nelze se přihlásit pomocí tohoto klíče",
|
||||
"server_provider_connected": "Připojení k poskytovateli serveru",
|
||||
"server_provider_connected_description": "Komunikace navázána. Zadejte svůj token s přístupem k {}:",
|
||||
"server_provider_connected_placeholder": "Token poskytovatele serveru",
|
||||
"provider_connected": "Připojení k poskytovateli {}",
|
||||
"provider_connected_description": "Komunikace navázána. Zadejte svůj token s přístupem k {}:",
|
||||
"provider_connected_placeholder": "Token {}",
|
||||
"confirm_server": "Potvrzení serveru",
|
||||
"confirm_server_accept": "Ano! To je ono",
|
||||
"confirm_server_decline": "Výběr jiného serveru",
|
||||
|
@ -383,17 +383,13 @@
|
|||
"modal_confirmation_dns_valid": "Reverzní DNS je platný",
|
||||
"modal_confirmation_dns_invalid": "Reverzní DNS ukazuje na jinou doménu",
|
||||
"modal_confirmation_ip_invalid": "IP není stejná jako v záznamu DNS",
|
||||
"confirm_cloudflare": "Připojení k poskytovateli DNS",
|
||||
"confirm_cloudflare_description": "Zadejte token DNS poskytovatele, který má práva k {}:",
|
||||
"confirm_backblaze": "Připojení k službě Backblaze",
|
||||
"generic_error": "Operace se nezdařila, zkuste to prosím znovu.",
|
||||
"domain_recovery_description": "Zadejte doménu serveru, ke které chcete získat přístup:",
|
||||
"method_device_description": "Otevřete aplikaci v jiném zařízení a přejděte na stránku zařízení. Stisknutím tlačítka \"Přidat zařízení\" získáte token.",
|
||||
"fallback_select_provider_console_hint": "Například: Hetzner.",
|
||||
"confirm_server_description": "Našel jsem váš server! Potvrďte, že je to ten správný:",
|
||||
"domain_not_available_on_token": "Vybraná doména není na tomto tokenu k dispozici.",
|
||||
"modal_confirmation_ip_valid": "IP je stejná jako v záznamu DNS",
|
||||
"confirm_backblaze_description": "Zadejte token Backblaze s přístupem k úložišti záloh:"
|
||||
"modal_confirmation_ip_valid": "IP je stejná jako v záznamu DNS"
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
@ -514,4 +510,4 @@
|
|||
"ignore_tls": "Nekontrolujte certifikáty TLS",
|
||||
"ignore_tls_description": "Aplikace nebude při připojování k serveru ověřovat certifikáty TLS."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -411,12 +411,9 @@
|
|||
"domain_recovery_description": "Geben Sie eine Serverdomäne ein, für die Sie Zugriff erhalten möchten:",
|
||||
"method_device_description": "Öffnen Sie die Anwendung auf einem anderen Gerät und gehen Sie dann zur Geräteseite. Drücken Sie auf „Gerät hinzufügen“, um Ihren Token zu erhalten.",
|
||||
"fallback_select_token_copy": "Kopie des Authentifizierungstokens von einer anderen Version der Anwendung.",
|
||||
"server_provider_connected_description": "Kommunikation hergestellt. Eingabe Ihres Tokens Token mit Zugriff auf {}:",
|
||||
"provider_connected_description": "Kommunikation hergestellt. Eingabe Ihres Tokens Token mit Zugriff auf {}:",
|
||||
"choose_server_description": "Wir konnten nicht herausfinden, mit welchem Server Sie sich verbinden möchten.",
|
||||
"modal_confirmation_dns_invalid": "Reverse DNS zeigt auf eine andere Domain",
|
||||
"confirm_cloudflare_description": "Geben Sie das DNS-Token des Anbieters ein, der Rechte für {} hat:",
|
||||
"confirm_backblaze": "Mit Backblaze verbinden",
|
||||
"confirm_backblaze_description": "Geben Sie ein Backblaze-Token mit Zugriff auf den Sicherungsspeicher ein:",
|
||||
"generic_error": "Vorgang fehlgeschlagen, bitte versuchen Sie es erneut.",
|
||||
"recovery_main_header": "Verbindung zu einem vorhandenen Server herstellen",
|
||||
"domain_recover_placeholder": "Ihre Domain",
|
||||
|
@ -434,8 +431,8 @@
|
|||
"fallback_select_provider_console": "Zugang zur Serverkonsole meines Providers.",
|
||||
"authorization_failed": "Anmeldung mit diesem Schlüssel nicht möglich",
|
||||
"fallback_select_provider_console_hint": "Zum Beispiel: Hetzner.",
|
||||
"server_provider_connected": "Verbinden Sie sich mit Ihrem Serveranbieter",
|
||||
"server_provider_connected_placeholder": "Token des Serveranbieters",
|
||||
"provider_connected": "Verbinden Sie sich mit Ihrem {}",
|
||||
"provider_connected_placeholder": "Token des {}",
|
||||
"confirm_server": "Server bestätigen",
|
||||
"confirm_server_description": "Server gefunden! Bestätigen Sie, dass es das Richtige ist:",
|
||||
"confirm_server_accept": "Ja! Das ist es",
|
||||
|
@ -447,8 +444,7 @@
|
|||
"modal_confirmation_description": "Wenn Sie sich mit einem falschen Server verbinden, können Sie alle Ihre Daten verlieren.",
|
||||
"modal_confirmation_dns_valid": "Reverse DNS ist gültig",
|
||||
"modal_confirmation_ip_valid": "Die IP ist die gleiche wie im DNS-Eintrag",
|
||||
"modal_confirmation_ip_invalid": "Die IP ist nicht dieselbe wie im DNS-Eintrag",
|
||||
"confirm_cloudflare": "Verbindung zu einem DNS-Anbieter herstellen"
|
||||
"modal_confirmation_ip_invalid": "Die IP ist nicht dieselbe wie im DNS-Eintrag"
|
||||
},
|
||||
"recovery_key": {
|
||||
"key_connection_error": "Es konnte keine Verbindung zum Server hergestellt werden.",
|
||||
|
@ -514,4 +510,4 @@
|
|||
"ignore_tls": "Überprüfen Sie keine TLS-Zertifikate",
|
||||
"ignore_tls_description": "Die Anwendung validiert TLS-Zertifikate nicht, wenn sie eine Verbindung zum Server herstellt."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -124,7 +124,8 @@
|
|||
"disk": "Disk local",
|
||||
"monthly_cost": "Monthly cost",
|
||||
"location": "Location",
|
||||
"provider": "Provider",
|
||||
"server_provider": "Server Provider",
|
||||
"dns_provider": "DNS Provider",
|
||||
"core_count": {
|
||||
"one": "{} core",
|
||||
"two": "{} cores",
|
||||
|
@ -218,7 +219,7 @@
|
|||
"extend_volume_button": "Extend volume",
|
||||
"extending_volume_title": "Extending volume",
|
||||
"extending_volume_description": "Resizing volume will allow you to store more data on your server without extending the server itself. Volume can only be extended: shrinking is not possible.",
|
||||
"extending_volume_price_info": "Price includes VAT and is estimated from pricing data provided by Hetzner. Server will be rebooted after resizing.",
|
||||
"extending_volume_price_info": "Price includes VAT and is estimated from pricing data provided by your server provider. Server will be rebooted after resizing.",
|
||||
"extending_volume_error": "Couldn't initialize volume extending.",
|
||||
"size": "Size",
|
||||
"data_migration_title": "Data migration",
|
||||
|
@ -404,9 +405,9 @@
|
|||
"fallback_select_provider_console": "Access to the server console of my provider.",
|
||||
"authorization_failed": "Couldn't log in with this key",
|
||||
"fallback_select_provider_console_hint": "For example: Hetzner.",
|
||||
"server_provider_connected": "Connect to your Server Provider",
|
||||
"server_provider_connected_description": "Communication established. Enter you token with access to {}:",
|
||||
"server_provider_connected_placeholder": "Server Provider token",
|
||||
"provider_connected": "Connect to {}",
|
||||
"provider_connected_description": "Enter your token with access to {}:",
|
||||
"provider_connected_placeholder": "{} token",
|
||||
"confirm_server": "Confirm server",
|
||||
"confirm_server_description": "Found your server! Confirm it is the right one:",
|
||||
"confirm_server_accept": "Yes! That's it",
|
||||
|
@ -420,11 +421,7 @@
|
|||
"modal_confirmation_dns_valid": "Reverse DNS is valid",
|
||||
"modal_confirmation_dns_invalid": "Reverse DNS points to another domain",
|
||||
"modal_confirmation_ip_valid": "IP is the same as in DNS record",
|
||||
"modal_confirmation_ip_invalid": "IP is not the same as in DNS record",
|
||||
"confirm_cloudflare": "Connect to your DNS Provider",
|
||||
"confirm_cloudflare_description": "Enter a token of your DNS Provider with access to {}:",
|
||||
"confirm_backblaze": "Connect to Backblaze",
|
||||
"confirm_backblaze_description": "Enter a Backblaze token with access to backup storage:"
|
||||
"modal_confirmation_ip_invalid": "IP is not the same as in DNS record"
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
@ -542,4 +539,4 @@
|
|||
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
|
||||
"cubit_statuses": "Cubit loading statuses"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -433,7 +433,6 @@
|
|||
"recovering": {
|
||||
"confirm_server_decline": "Wybierz inny serwer",
|
||||
"domain_not_available_on_token": "Wprowadzony token nie ma dostępu do żądanej domeny.",
|
||||
"confirm_cloudflare_description": "Wprowadź token DNS dostawcy, który ma uprawnienia do {}:",
|
||||
"method_recovery_input_description": "Wprowadź swój token odzyskiwania",
|
||||
"fallback_select_provider_console": "Dostęp do konsoli serwera mojego dostawcy.",
|
||||
"confirm_server_description": "Znalazłem twój serwer! Potwierdź, że jest to właściwe:",
|
||||
|
@ -461,17 +460,14 @@
|
|||
"modal_confirmation_dns_invalid": "Odwrócony DNS wskazuje na inną domenę",
|
||||
"modal_confirmation_ip_valid": "IP jest takie samo jak w rekordzie DNS",
|
||||
"modal_confirmation_ip_invalid": "IP nie jest zgodne z tym w rekordzie DNS",
|
||||
"confirm_cloudflare": "Łączenie z dostawcą DNS",
|
||||
"confirm_backblaze": "Podłączanie do Backblaze",
|
||||
"confirm_backblaze_description": "Wpisz token Backblaze, który ma prawa do magazynu kopii zapasowych:",
|
||||
"fallback_select_description": "Które z nich posiadasz? Wybierz pierwszą, która pasuje:",
|
||||
"fallback_select_token_copy": "Kopia tokena autoryzacyjnego z innej wersji aplikacji.",
|
||||
"fallback_select_root_ssh": "Dostęp Root do serwera poprzez SSH.",
|
||||
"authorization_failed": "Nie udało się zalogować za pomocą tego klucza",
|
||||
"fallback_select_provider_console_hint": "Na przykład: Hetzner.",
|
||||
"server_provider_connected": "Połączenie z dostawcą serwera",
|
||||
"server_provider_connected_description": "Połączenie ustanowione. Podaj swój token z dostępem do {}:",
|
||||
"server_provider_connected_placeholder": "Token dostawcy serwera"
|
||||
"provider_connected": "Połączenie z dostawcą {}",
|
||||
"provider_connected_description": "Połączenie ustanowione. Podaj swój token z dostępem do {}:",
|
||||
"provider_connected_placeholder": "{} Token "
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
@ -513,4 +509,4 @@
|
|||
"cubit_statuses": "Aktualny stan qubitów ładujących",
|
||||
"ignore_tls": "Używane podczas konfigurowania nowego serwera."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -214,7 +214,7 @@
|
|||
"extend_volume_button": "Расширить хранилище",
|
||||
"extending_volume_title": "Расширение хранилища",
|
||||
"extending_volume_description": "Изменение размера хранилища позволит вам держать больше данных на вашем сервере без расширения самого сервера. Объем можно только увеличить: уменьшить нельзя.",
|
||||
"extending_volume_price_info": "Цена включает НДС и рассчитана на основе данных о ценах, предоставленных Hetzner. Сервер будет перезагружен во время процесса.",
|
||||
"extending_volume_price_info": "Цена включает НДС и рассчитана на основе данных о ценах, предоставленных вашим провайдером. Сервер будет перезагружен во время процесса.",
|
||||
"extending_volume_error": "Не удалось начать расширение хранилища.",
|
||||
"size": "Размер",
|
||||
"data_migration_title": "Миграция данных",
|
||||
|
@ -414,13 +414,9 @@
|
|||
"modal_confirmation_dns_invalid": "Обратный DNS указывает на другой домен",
|
||||
"modal_confirmation_ip_valid": "IP совпадает с указанным в DNS записи",
|
||||
"modal_confirmation_ip_invalid": "IP не совпадает с указанным в DNS записи",
|
||||
"confirm_cloudflare": "Подключение к DNS Провайдеру",
|
||||
"confirm_cloudflare_description": "Введите токен DNS Провайдера, который имеет права на {}:",
|
||||
"confirm_backblaze_description": "Введите токен Backblaze, который имеет права на хранилище резервных копий:",
|
||||
"confirm_backblaze": "Подключение к Backblaze",
|
||||
"server_provider_connected": "Подключение к вашему серверному провайдеру",
|
||||
"server_provider_connected_description": "Связь установлена. Введите свой токен с доступом к {}:",
|
||||
"server_provider_connected_placeholder": "Токен серверного провайдера"
|
||||
"provider_connected": "Подключение к вашему {}",
|
||||
"provider_connected_description": "Связь установлена. Введите свой токен с доступом к {}:",
|
||||
"provider_connected_placeholder": "{} Токен"
|
||||
},
|
||||
"devices": {
|
||||
"main_screen": {
|
||||
|
@ -538,4 +534,4 @@
|
|||
"ignore_tls_description": "Приложение не будет проверять сертификаты TLS при подключении к серверу.",
|
||||
"ignore_tls": "Не проверять сертификаты TLS"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -388,11 +388,10 @@
|
|||
"recovery_main_header": "Pripojiť sa k existujúcemu serveru",
|
||||
"method_select_other_device": "Mám prístup na inom zariadení",
|
||||
"method_device_description": "Otvorte aplikáciu na inom zariadení a otvorte obrazovku správy zariadenia. Kliknutím na „Pridať zariadenie“ získate autorizačný token.",
|
||||
"server_provider_connected": "Pripojiť sa k poskytovateľovi servera",
|
||||
"provider_connected": "Pripojiť sa k poskytovateľovi {}",
|
||||
"choose_server": "Vyberte si svoj server",
|
||||
"domain_not_available_on_token": "Vybraná doména nie je na tomto tokene dostupná.",
|
||||
"modal_confirmation_ip_valid": "IP je rovnaká ako v DNS zázname",
|
||||
"confirm_backblaze_description": "Zadajte token Backblaze, ktorý má práva na úložisko záloh:",
|
||||
"generic_error": "Operácia zlyhala, skúste to znova.",
|
||||
"domain_recovery_description": "Zadajte doménu servera, pre ktorú chcete získať prístup:",
|
||||
"domain_recover_placeholder": "Vaša doména",
|
||||
|
@ -410,8 +409,8 @@
|
|||
"fallback_select_provider_console": "Prístup ku konzole servera môjho poskytovateľa.",
|
||||
"authorization_failed": "Pomocou tohto kľúča sa nepodarilo prihlásiť",
|
||||
"fallback_select_provider_console_hint": "Napríklad Hetzner.",
|
||||
"server_provider_connected_description": "Spojenie bolo nadviazané. Zadajte svoj token s prístupom k {}:",
|
||||
"server_provider_connected_placeholder": "Token poskytovateľa servera",
|
||||
"provider_connected_description": "Spojenie bolo nadviazané. Zadajte svoj token s prístupom k {}:",
|
||||
"provider_connected_placeholder": "{} Token",
|
||||
"confirm_server": "Potvrďte server",
|
||||
"confirm_server_description": "Našiel sa server! Potvrďte, že je to on:",
|
||||
"confirm_server_accept": "Áno, to je on",
|
||||
|
@ -422,10 +421,7 @@
|
|||
"modal_confirmation_description": "Ak sa pripojíte k nesprávnemu serveru, môžete stratiť všetky svoje údaje.",
|
||||
"modal_confirmation_dns_valid": "Reverzný DNS je platný",
|
||||
"modal_confirmation_dns_invalid": "Reverzné DNS ukazuje na inú doménu",
|
||||
"modal_confirmation_ip_invalid": "IP nie je rovnaká ako v DNS zázname",
|
||||
"confirm_cloudflare": "Pripojenie k službe CloudFlare",
|
||||
"confirm_cloudflare_description": "Zadajte token Cloudflare, ktorý má práva na {}:",
|
||||
"confirm_backblaze": "Pripojenie ku Backblaze"
|
||||
"modal_confirmation_ip_invalid": "IP nie je rovnaká ako v DNS zázname"
|
||||
},
|
||||
"devices": {
|
||||
"add_new_device_screen": {
|
||||
|
|
|
@ -171,9 +171,9 @@
|
|||
"fallback_select_provider_console": "Доступ до серверної консолі мого продiвера.",
|
||||
"authorization_failed": "Не можу авторизуватись за цим ключем",
|
||||
"fallback_select_provider_console_hint": "Наприклад: Hetzner.",
|
||||
"server_provider_connected": "Підключіться до провайдера сервера",
|
||||
"server_provider_connected_description": "Зв'язок встановлений. Введіть свій токен з доступом до {}:",
|
||||
"server_provider_connected_placeholder": "Токен провайдера сервера",
|
||||
"provider_connected": "Підключіться до {}",
|
||||
"provider_connected_description": "Зв'язок встановлений. Введіть свій токен з доступом до {}:",
|
||||
"provider_connected_placeholder": "{} Токен",
|
||||
"confirm_server": "Підтвердити сервер",
|
||||
"confirm_server_description": "Знайдено ваш сервер! Підтвердіть, що він правильний:",
|
||||
"confirm_server_accept": "Так! Це воно",
|
||||
|
@ -187,11 +187,7 @@
|
|||
"modal_confirmation_dns_valid": "Зворотна DNS дійсна",
|
||||
"modal_confirmation_dns_invalid": "Зворотна DNS вказує на інший домен",
|
||||
"modal_confirmation_ip_valid": "IP той же, що і в записі DNS",
|
||||
"modal_confirmation_ip_invalid": "IP не такий, як в DNS-записі",
|
||||
"confirm_cloudflare": "Підключення до CloudFlare",
|
||||
"confirm_cloudflare_description": "Введіть токен CloudFlare з доступом до {}:",
|
||||
"confirm_backblaze": "Підкючитися до Backblaze",
|
||||
"confirm_backblaze_description": "Введіть токен Backblaze із доступом до сховища резервних копій:"
|
||||
"modal_confirmation_ip_invalid": "IP не такий, як в DNS-записі"
|
||||
},
|
||||
"resource_chart": {
|
||||
"month": "Місяць",
|
||||
|
|
|
@ -4,8 +4,7 @@ import 'package:dio/dio.dart';
|
|||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||
import 'package:selfprivacy/logic/models/json/cloudflare_dns_info.dart';
|
||||
|
||||
class CloudflareApi extends RestApiMap {
|
||||
CloudflareApi({
|
||||
|
@ -92,9 +91,9 @@ class CloudflareApi extends RestApiMap {
|
|||
);
|
||||
}
|
||||
|
||||
Future<GenericResult<List>> getDomains() async {
|
||||
Future<GenericResult<List<CloudflareZone>>> getZones() async {
|
||||
final String url = '$rootAddress/zones';
|
||||
List domains = [];
|
||||
List<CloudflareZone> domains = [];
|
||||
|
||||
late final Response? response;
|
||||
final Dio client = await getClient();
|
||||
|
@ -103,7 +102,11 @@ class CloudflareApi extends RestApiMap {
|
|||
url,
|
||||
queryParameters: {'per_page': 50},
|
||||
);
|
||||
domains = response.data['result'];
|
||||
domains = response.data['result']!
|
||||
.map<CloudflareZone>(
|
||||
(final json) => CloudflareZone.fromJson(json),
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return GenericResult(
|
||||
|
@ -125,18 +128,17 @@ class CloudflareApi extends RestApiMap {
|
|||
}
|
||||
|
||||
Future<GenericResult<void>> createMultipleDnsRecords({
|
||||
required final ServerDomain domain,
|
||||
required final List<DnsRecord> records,
|
||||
required final String zoneId,
|
||||
required final List<CloudflareDnsRecord> records,
|
||||
}) async {
|
||||
final String domainZoneId = domain.zoneId;
|
||||
final List<Future> allCreateFutures = <Future>[];
|
||||
|
||||
final Dio client = await getClient();
|
||||
try {
|
||||
for (final DnsRecord record in records) {
|
||||
for (final CloudflareDnsRecord record in records) {
|
||||
allCreateFutures.add(
|
||||
client.post(
|
||||
'/zones/$domainZoneId/dns_records',
|
||||
'/zones/$zoneId/dns_records',
|
||||
data: record.toJson(),
|
||||
),
|
||||
);
|
||||
|
@ -160,11 +162,10 @@ class CloudflareApi extends RestApiMap {
|
|||
}
|
||||
|
||||
Future<GenericResult<void>> removeSimilarRecords({
|
||||
required final ServerDomain domain,
|
||||
required final List records,
|
||||
required final String zoneId,
|
||||
required final List<CloudflareDnsRecord> records,
|
||||
}) async {
|
||||
final String domainZoneId = domain.zoneId;
|
||||
final String url = '/zones/$domainZoneId/dns_records';
|
||||
final String url = '/zones/$zoneId/dns_records';
|
||||
|
||||
final Dio client = await getClient();
|
||||
try {
|
||||
|
@ -172,7 +173,7 @@ class CloudflareApi extends RestApiMap {
|
|||
|
||||
for (final record in records) {
|
||||
allDeleteFutures.add(
|
||||
client.delete('$url/${record["id"]}'),
|
||||
client.delete('$url/${record.id}'),
|
||||
);
|
||||
}
|
||||
await Future.wait(allDeleteFutures);
|
||||
|
@ -190,26 +191,21 @@ class CloudflareApi extends RestApiMap {
|
|||
return GenericResult(success: true, data: null);
|
||||
}
|
||||
|
||||
Future<GenericResult<List>> getDnsRecords({
|
||||
required final ServerDomain domain,
|
||||
Future<GenericResult<List<CloudflareDnsRecord>>> getDnsRecords({
|
||||
required final String zoneId,
|
||||
}) async {
|
||||
Response response;
|
||||
final String domainName = domain.domainName;
|
||||
final String domainZoneId = domain.zoneId;
|
||||
final List allRecords = [];
|
||||
|
||||
final String url = '/zones/$domainZoneId/dns_records';
|
||||
List<CloudflareDnsRecord> allRecords = [];
|
||||
|
||||
final String url = '/zones/$zoneId/dns_records';
|
||||
final Dio client = await getClient();
|
||||
try {
|
||||
response = await client.get(url);
|
||||
final List records = response.data['result'] ?? [];
|
||||
|
||||
for (final record in records) {
|
||||
if (record['zone_name'] == domainName) {
|
||||
allRecords.add(record);
|
||||
}
|
||||
}
|
||||
allRecords = response.data['result']!
|
||||
.map<CloudflareDnsRecord>(
|
||||
(final json) => CloudflareDnsRecord.fromJson(json),
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return GenericResult(
|
||||
|
@ -223,30 +219,4 @@ class CloudflareApi extends RestApiMap {
|
|||
|
||||
return GenericResult(data: allRecords, success: true);
|
||||
}
|
||||
|
||||
Future<GenericResult<List<dynamic>>> getZones(final String domain) async {
|
||||
List zones = [];
|
||||
|
||||
late final Response? response;
|
||||
final Dio client = await getClient();
|
||||
try {
|
||||
response = await client.get(
|
||||
'/zones',
|
||||
queryParameters: {'name': domain},
|
||||
);
|
||||
zones = response.data['result'];
|
||||
} catch (e) {
|
||||
print(e);
|
||||
GenericResult(
|
||||
success: false,
|
||||
data: zones,
|
||||
code: response?.statusCode,
|
||||
message: response?.statusMessage,
|
||||
);
|
||||
} finally {
|
||||
close(client);
|
||||
}
|
||||
|
||||
return GenericResult(success: true, data: zones);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ class DigitalOceanApi extends RestApiMap {
|
|||
@override
|
||||
String get rootAddress => 'https://api.digitalocean.com/v2';
|
||||
String get infectProviderName => 'digitalocean';
|
||||
String get displayProviderName => 'Digital Ocean';
|
||||
|
||||
Future<GenericResult<List>> getServers() async {
|
||||
List servers = [];
|
||||
|
|
|
@ -46,7 +46,6 @@ class HetznerApi extends RestApiMap {
|
|||
@override
|
||||
String get rootAddress => 'https://api.hetzner.cloud/v1';
|
||||
String get infectProviderName => 'hetzner';
|
||||
String get displayProviderName => 'Hetzner';
|
||||
|
||||
Future<GenericResult<List<HetznerServerInfo>>> getServers() async {
|
||||
List<HetznerServerInfo> servers = [];
|
||||
|
|
|
@ -25,23 +25,17 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
|||
Future<void> saveDomain() async {
|
||||
assert(state is Loaded, 'wrong state');
|
||||
final String domainName = (state as Loaded).domain;
|
||||
|
||||
emit(Loading(LoadingTypes.saving));
|
||||
|
||||
final dnsProvider = ProvidersController.currentDnsProvider!;
|
||||
final GenericResult<String?> zoneIdResult =
|
||||
await dnsProvider.getZoneId(domainName);
|
||||
|
||||
if (zoneIdResult.success || zoneIdResult.data != null) {
|
||||
final ServerDomain domain = ServerDomain(
|
||||
domainName: domainName,
|
||||
zoneId: zoneIdResult.data!,
|
||||
provider: dnsProvider.type,
|
||||
);
|
||||
final ServerDomain domain = ServerDomain(
|
||||
domainName: domainName,
|
||||
provider: dnsProvider.type,
|
||||
);
|
||||
|
||||
serverInstallationCubit.setDomain(domain);
|
||||
emit(DomainSet());
|
||||
}
|
||||
serverInstallationCubit.setDomain(domain);
|
||||
emit(DomainSet());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,20 +9,38 @@ class ServerDetailsRepository {
|
|||
ServerApi server = ServerApi();
|
||||
|
||||
Future<ServerDetailsRepositoryDto> load() async {
|
||||
final serverProviderApi = ProvidersController.currentServerProvider;
|
||||
final settings = await server.getSystemSettings();
|
||||
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
|
||||
final metadata = await serverProviderApi?.getMetadata(serverId);
|
||||
|
||||
return ServerDetailsRepositoryDto(
|
||||
autoUpgradeSettings: settings.autoUpgradeSettings,
|
||||
metadata: metadata!.data,
|
||||
metadata: await metadata,
|
||||
serverTimezone: TimeZoneSettings.fromString(
|
||||
settings.timezone,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<ServerMetadataEntity>> get metadata async {
|
||||
List<ServerMetadataEntity> data = [];
|
||||
|
||||
final serverProviderApi = ProvidersController.currentServerProvider;
|
||||
final dnsProviderApi = ProvidersController.currentDnsProvider;
|
||||
if (serverProviderApi != null && dnsProviderApi != null) {
|
||||
final serverId = getIt<ApiConfigModel>().serverDetails?.id ?? 0;
|
||||
final metadataResult = await serverProviderApi.getMetadata(serverId);
|
||||
metadataResult.data.add(
|
||||
ServerMetadataEntity(
|
||||
trId: 'server.dns_provider',
|
||||
value: dnsProviderApi.type.displayName,
|
||||
type: MetadataType.other,
|
||||
),
|
||||
);
|
||||
|
||||
data = metadataResult.data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<void> setAutoUpgradeSettings(
|
||||
final AutoUpgradeSettings settings,
|
||||
) async {
|
||||
|
|
|
@ -467,7 +467,6 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
final ServerDomain serverDomain = ServerDomain(
|
||||
domainName: domain,
|
||||
provider: DnsProviderType.unknown,
|
||||
zoneId: '',
|
||||
);
|
||||
final ServerRecoveryCapabilities recoveryCapabilities =
|
||||
await repository.getRecoveryCapabilities(serverDomain);
|
||||
|
@ -532,13 +531,29 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
.showSnackBar('recovering.generic_error'.tr());
|
||||
return;
|
||||
}
|
||||
await repository.saveServerDetails(serverDetails);
|
||||
final newServerDetails = ServerHostingDetails(
|
||||
provider: serverProvider,
|
||||
apiToken: serverDetails.apiToken,
|
||||
createTime: serverDetails.createTime,
|
||||
id: serverDetails.id,
|
||||
ip4: serverDetails.ip4,
|
||||
volume: serverDetails.volume,
|
||||
startTime: serverDetails.startTime,
|
||||
);
|
||||
final newServerDomain = ServerDomain(
|
||||
domainName: serverDomain.domainName,
|
||||
zoneId: serverDomain.zoneId,
|
||||
provider: dnsProvider,
|
||||
);
|
||||
await repository.saveServerDetails(newServerDetails);
|
||||
await repository.saveDnsProviderType(dnsProvider);
|
||||
await repository.saveDomain(newServerDomain);
|
||||
setServerProviderType(serverProvider);
|
||||
setDnsProviderType(dnsProvider);
|
||||
emit(
|
||||
dataState.copyWith(
|
||||
serverDetails: serverDetails,
|
||||
serverDetails: newServerDetails,
|
||||
serverDomain: newServerDomain,
|
||||
currentStep: RecoveryStep.serverProviderToken,
|
||||
),
|
||||
);
|
||||
|
@ -681,9 +696,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
if (serverDomain == null) {
|
||||
return;
|
||||
}
|
||||
final String? zoneId =
|
||||
await repository.getDomainId(token, serverDomain.domainName);
|
||||
if (zoneId == null) {
|
||||
final isTokenValid =
|
||||
await repository.validateDnsToken(token, serverDomain.domainName);
|
||||
if (!isTokenValid) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar('recovering.domain_not_available_on_token'.tr());
|
||||
return;
|
||||
|
@ -695,16 +710,13 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
await repository.saveDomain(
|
||||
ServerDomain(
|
||||
domainName: serverDomain.domainName,
|
||||
zoneId: zoneId,
|
||||
provider: dnsProviderType,
|
||||
),
|
||||
);
|
||||
// await repository.setDnsApiToken(token);
|
||||
emit(
|
||||
dataState.copyWith(
|
||||
serverDomain: ServerDomain(
|
||||
domainName: serverDomain.domainName,
|
||||
zoneId: zoneId,
|
||||
provider: dnsProviderType,
|
||||
),
|
||||
dnsApiToken: token,
|
||||
|
|
|
@ -193,17 +193,23 @@ class ServerInstallationRepository {
|
|||
return server;
|
||||
}
|
||||
|
||||
Future<String?> getDomainId(final String token, final String domain) async {
|
||||
Future<bool> validateDnsToken(
|
||||
final String token,
|
||||
final String domain,
|
||||
) async {
|
||||
final result =
|
||||
await ProvidersController.currentDnsProvider!.tryInitApiByToken(token);
|
||||
if (!result.success) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
await setDnsApiToken(token);
|
||||
return (await ProvidersController.currentDnsProvider!.getZoneId(
|
||||
domain,
|
||||
))
|
||||
.data;
|
||||
final domainResult =
|
||||
await ProvidersController.currentDnsProvider!.domainList();
|
||||
if (!domainResult.success || domainResult.data.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return domain == domainResult.data[0];
|
||||
}
|
||||
|
||||
Future<Map<String, bool>> isDnsAddressesMatch(
|
||||
|
|
|
@ -96,14 +96,9 @@ enum ServerProviderType {
|
|||
}
|
||||
}
|
||||
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case ServerProviderType.hetzner:
|
||||
return 'Hetzner Cloud';
|
||||
case ServerProviderType.digitalOcean:
|
||||
return 'Digital Ocean';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
String get displayName => switch (this) {
|
||||
digitalOcean => 'Digital Ocean',
|
||||
hetzner => 'Hetzner Cloud',
|
||||
unknown => 'Unknown',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,21 +7,16 @@ part 'server_domain.g.dart';
|
|||
class ServerDomain {
|
||||
ServerDomain({
|
||||
required this.domainName,
|
||||
required this.zoneId,
|
||||
required this.provider,
|
||||
});
|
||||
|
||||
@HiveField(0)
|
||||
final String domainName;
|
||||
|
||||
@HiveField(1)
|
||||
final String zoneId;
|
||||
// @HiveField(1)
|
||||
|
||||
@HiveField(2, defaultValue: DnsProviderType.cloudflare)
|
||||
final DnsProviderType provider;
|
||||
|
||||
@override
|
||||
String toString() => '$domainName: $zoneId';
|
||||
}
|
||||
|
||||
@HiveType(typeId: 100)
|
||||
|
@ -54,4 +49,11 @@ enum DnsProviderType {
|
|||
desec => 'DESEC',
|
||||
unknown => 'UNKNOWN',
|
||||
};
|
||||
|
||||
String get displayName => switch (this) {
|
||||
digitalOcean => 'Digital Ocean DNS',
|
||||
cloudflare => 'Cloudflare',
|
||||
desec => 'deSEC',
|
||||
unknown => 'Unknown',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ class ServerDomainAdapter extends TypeAdapter<ServerDomain> {
|
|||
};
|
||||
return ServerDomain(
|
||||
domainName: fields[0] as String,
|
||||
zoneId: fields[1] as String,
|
||||
provider: fields[2] == null
|
||||
? DnsProviderType.cloudflare
|
||||
: fields[2] as DnsProviderType,
|
||||
|
@ -28,11 +27,9 @@ class ServerDomainAdapter extends TypeAdapter<ServerDomain> {
|
|||
@override
|
||||
void write(BinaryWriter writer, ServerDomain obj) {
|
||||
writer
|
||||
..writeByte(3)
|
||||
..writeByte(2)
|
||||
..writeByte(0)
|
||||
..write(obj.domainName)
|
||||
..writeByte(1)
|
||||
..write(obj.zoneId)
|
||||
..writeByte(2)
|
||||
..write(obj.provider);
|
||||
}
|
||||
|
|
86
lib/logic/models/json/cloudflare_dns_info.dart
Normal file
86
lib/logic/models/json/cloudflare_dns_info.dart
Normal file
|
@ -0,0 +1,86 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'cloudflare_dns_info.g.dart';
|
||||
|
||||
/// https://developers.cloudflare.com/api/operations/zones-get
|
||||
@JsonSerializable()
|
||||
class CloudflareZone {
|
||||
CloudflareZone({
|
||||
required this.id,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
/// Zone identifier
|
||||
///
|
||||
/// `<= 32 characters`
|
||||
///
|
||||
/// Example: 023e105f4ecef8ad9ca31a8372d0c353
|
||||
final String id;
|
||||
|
||||
/// The domain name
|
||||
///
|
||||
/// `<= 253 characters`
|
||||
///
|
||||
/// Example: example.com
|
||||
final String name;
|
||||
|
||||
static CloudflareZone fromJson(final Map<String, dynamic> json) =>
|
||||
_$CloudflareZoneFromJson(json);
|
||||
}
|
||||
|
||||
/// https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-list-dns-records
|
||||
@JsonSerializable()
|
||||
class CloudflareDnsRecord {
|
||||
CloudflareDnsRecord({
|
||||
required this.type,
|
||||
required this.name,
|
||||
required this.content,
|
||||
required this.zoneName,
|
||||
this.ttl = 3600,
|
||||
this.priority = 10,
|
||||
this.id,
|
||||
});
|
||||
|
||||
/// Record identifier
|
||||
///
|
||||
/// `<= 32 characters`
|
||||
/// Example: 023e105f4ecef8ad9ca31a8372d0c353
|
||||
final String? id;
|
||||
|
||||
/// Record type.
|
||||
///
|
||||
/// Example: A
|
||||
final String type;
|
||||
|
||||
/// DNS record name (or @ for the zone apex) in Punycode.
|
||||
///
|
||||
/// `<= 255 characters`
|
||||
///
|
||||
/// Example: example.com
|
||||
final String? name;
|
||||
|
||||
/// Valid DNS Record string content.
|
||||
///
|
||||
/// Example: A valid IPv4 address "198.51.100.4"
|
||||
final String? content;
|
||||
|
||||
/// The domain of the record.
|
||||
///
|
||||
/// Example: example.com
|
||||
@JsonKey(name: 'zone_name')
|
||||
final String zoneName;
|
||||
|
||||
/// Time To Live (TTL) of the DNS record in seconds. Setting to 1 means 'automatic'.
|
||||
///
|
||||
/// Value must be between 60 and 86400, with the minimum reduced to 30 for Enterprise zones.
|
||||
final int ttl;
|
||||
|
||||
/// Required for MX, SRV and URI records; unused by other record types. Records with lower priorities are preferred.
|
||||
///
|
||||
/// `>= 0 <= 65535`
|
||||
final int priority;
|
||||
|
||||
static CloudflareDnsRecord fromJson(final Map<String, dynamic> json) =>
|
||||
_$CloudflareDnsRecordFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$CloudflareDnsRecordToJson(this);
|
||||
}
|
42
lib/logic/models/json/cloudflare_dns_info.g.dart
Normal file
42
lib/logic/models/json/cloudflare_dns_info.g.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'cloudflare_dns_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CloudflareZone _$CloudflareZoneFromJson(Map<String, dynamic> json) =>
|
||||
CloudflareZone(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CloudflareZoneToJson(CloudflareZone instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
CloudflareDnsRecord _$CloudflareDnsRecordFromJson(Map<String, dynamic> json) =>
|
||||
CloudflareDnsRecord(
|
||||
type: json['type'] as String,
|
||||
name: json['name'] as String?,
|
||||
content: json['content'] as String?,
|
||||
zoneName: json['zone_name'] as String,
|
||||
ttl: json['ttl'] as int? ?? 3600,
|
||||
priority: json['priority'] as int? ?? 10,
|
||||
id: json['id'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CloudflareDnsRecordToJson(
|
||||
CloudflareDnsRecord instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'type': instance.type,
|
||||
'name': instance.name,
|
||||
'content': instance.content,
|
||||
'zone_name': instance.zoneName,
|
||||
'ttl': instance.ttl,
|
||||
'priority': instance.priority,
|
||||
};
|
|
@ -1,12 +1,16 @@
|
|||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/json/cloudflare_dns_info.dart';
|
||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||
import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart';
|
||||
|
||||
class ApiAdapter {
|
||||
ApiAdapter({final bool isWithToken = true})
|
||||
: _api = CloudflareApi(
|
||||
ApiAdapter({
|
||||
final bool isWithToken = true,
|
||||
this.cachedDomain = '',
|
||||
this.cachedZoneId = '',
|
||||
}) : _api = CloudflareApi(
|
||||
isWithToken: isWithToken,
|
||||
);
|
||||
|
||||
|
@ -17,6 +21,8 @@ class ApiAdapter {
|
|||
);
|
||||
|
||||
final CloudflareApi _api;
|
||||
final String cachedZoneId;
|
||||
final String cachedDomain;
|
||||
}
|
||||
|
||||
class CloudflareDnsProvider extends DnsProvider {
|
||||
|
@ -47,7 +53,7 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
@override
|
||||
Future<GenericResult<List<String>>> domainList() async {
|
||||
List<String> domains = [];
|
||||
final result = await _adapter.api().getDomains();
|
||||
final result = await _adapter.api().getZones();
|
||||
if (result.data.isEmpty || !result.success) {
|
||||
return GenericResult(
|
||||
success: result.success,
|
||||
|
@ -59,10 +65,17 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
|
||||
domains = result.data
|
||||
.map<String>(
|
||||
(final el) => el['name'] as String,
|
||||
(final el) => el.name,
|
||||
)
|
||||
.toList();
|
||||
|
||||
/// TODO: Remove when domain selection for more than one domain on account is implemented, move cachedZoneId writing to domain saving method
|
||||
_adapter = ApiAdapter(
|
||||
isWithToken: true,
|
||||
cachedDomain: result.data[0].name,
|
||||
cachedZoneId: result.data[0].id,
|
||||
);
|
||||
|
||||
return GenericResult(
|
||||
success: true,
|
||||
data: domains,
|
||||
|
@ -73,11 +86,27 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
Future<GenericResult<void>> createDomainRecords({
|
||||
required final ServerDomain domain,
|
||||
final String? ip4,
|
||||
}) {
|
||||
}) async {
|
||||
final syncZoneIdResult = await syncZoneId(domain.domainName);
|
||||
if (!syncZoneIdResult.success) {
|
||||
return syncZoneIdResult;
|
||||
}
|
||||
|
||||
final records = getProjectDnsRecords(domain.domainName, ip4);
|
||||
return _adapter.api().createMultipleDnsRecords(
|
||||
domain: domain,
|
||||
records: records,
|
||||
zoneId: _adapter.cachedZoneId,
|
||||
records: records
|
||||
.map<CloudflareDnsRecord>(
|
||||
(final rec) => CloudflareDnsRecord(
|
||||
content: rec.content,
|
||||
name: rec.name,
|
||||
type: rec.type,
|
||||
zoneName: domain.domainName,
|
||||
id: null,
|
||||
ttl: rec.ttl,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -86,7 +115,13 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
required final ServerDomain domain,
|
||||
final String? ip4,
|
||||
}) async {
|
||||
final result = await _adapter.api().getDnsRecords(domain: domain);
|
||||
final syncZoneIdResult = await syncZoneId(domain.domainName);
|
||||
if (!syncZoneIdResult.success) {
|
||||
return syncZoneIdResult;
|
||||
}
|
||||
|
||||
final result =
|
||||
await _adapter.api().getDnsRecords(zoneId: _adapter.cachedZoneId);
|
||||
if (result.data.isEmpty || !result.success) {
|
||||
return GenericResult(
|
||||
success: result.success,
|
||||
|
@ -97,7 +132,7 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
}
|
||||
|
||||
return _adapter.api().removeSimilarRecords(
|
||||
domain: domain,
|
||||
zoneId: _adapter.cachedZoneId,
|
||||
records: result.data,
|
||||
);
|
||||
}
|
||||
|
@ -106,8 +141,19 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
Future<GenericResult<List<DnsRecord>>> getDnsRecords({
|
||||
required final ServerDomain domain,
|
||||
}) async {
|
||||
final syncZoneIdResult = await syncZoneId(domain.domainName);
|
||||
if (!syncZoneIdResult.success) {
|
||||
return GenericResult(
|
||||
success: syncZoneIdResult.success,
|
||||
data: [],
|
||||
code: syncZoneIdResult.code,
|
||||
message: syncZoneIdResult.message,
|
||||
);
|
||||
}
|
||||
|
||||
final List<DnsRecord> records = [];
|
||||
final result = await _adapter.api().getDnsRecords(domain: domain);
|
||||
final result =
|
||||
await _adapter.api().getDnsRecords(zoneId: _adapter.cachedZoneId);
|
||||
if (result.data.isEmpty || !result.success) {
|
||||
return GenericResult(
|
||||
success: result.success,
|
||||
|
@ -120,11 +166,10 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
for (final rawRecord in result.data) {
|
||||
records.add(
|
||||
DnsRecord(
|
||||
name: rawRecord['name'],
|
||||
type: rawRecord['type'],
|
||||
content: rawRecord['content'],
|
||||
ttl: rawRecord['ttl'],
|
||||
proxied: rawRecord['proxied'],
|
||||
name: rawRecord.name,
|
||||
type: rawRecord.type,
|
||||
content: rawRecord.content,
|
||||
ttl: rawRecord.ttl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -139,11 +184,26 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
Future<GenericResult<void>> setDnsRecord(
|
||||
final DnsRecord record,
|
||||
final ServerDomain domain,
|
||||
) async =>
|
||||
_adapter.api().createMultipleDnsRecords(
|
||||
domain: domain,
|
||||
records: [record],
|
||||
);
|
||||
) async {
|
||||
final syncZoneIdResult = await syncZoneId(domain.domainName);
|
||||
if (!syncZoneIdResult.success) {
|
||||
return syncZoneIdResult;
|
||||
}
|
||||
|
||||
return _adapter.api().createMultipleDnsRecords(
|
||||
zoneId: _adapter.cachedZoneId,
|
||||
records: [
|
||||
CloudflareDnsRecord(
|
||||
content: record.content,
|
||||
id: null,
|
||||
name: record.name,
|
||||
type: record.type,
|
||||
zoneName: domain.domainName,
|
||||
ttl: record.ttl,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GenericResult<List<DesiredDnsRecord>>> validateDnsRecords(
|
||||
|
@ -336,10 +396,39 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GenericResult<void>> syncZoneId(final String domain) async {
|
||||
if (domain == _adapter.cachedDomain && _adapter.cachedZoneId.isNotEmpty) {
|
||||
return GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
}
|
||||
|
||||
final getZoneIdResult = await getZoneId(domain);
|
||||
if (!getZoneIdResult.success || getZoneIdResult.data == null) {
|
||||
return GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
code: getZoneIdResult.code,
|
||||
message: getZoneIdResult.message,
|
||||
);
|
||||
}
|
||||
|
||||
_adapter = ApiAdapter(
|
||||
isWithToken: true,
|
||||
cachedDomain: domain,
|
||||
cachedZoneId: getZoneIdResult.data!,
|
||||
);
|
||||
|
||||
return GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<GenericResult<String?>> getZoneId(final String domain) async {
|
||||
String? id;
|
||||
final result = await _adapter.api().getZones(domain);
|
||||
final result = await _adapter.api().getZones();
|
||||
if (result.data.isEmpty || !result.success) {
|
||||
return GenericResult(
|
||||
success: result.success,
|
||||
|
@ -349,8 +438,12 @@ class CloudflareDnsProvider extends DnsProvider {
|
|||
);
|
||||
}
|
||||
|
||||
id = result.data[0]['id'];
|
||||
for (final availableDomain in result.data) {
|
||||
if (availableDomain.name == domain) {
|
||||
id = availableDomain.id;
|
||||
}
|
||||
}
|
||||
|
||||
return GenericResult(success: true, data: id);
|
||||
return GenericResult(success: id != null, data: id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -408,11 +408,4 @@ class DesecDnsProvider extends DnsProvider {
|
|||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GenericResult<String?>> getZoneId(final String domain) async =>
|
||||
GenericResult(
|
||||
data: domain,
|
||||
success: true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -369,11 +369,4 @@ class DigitalOceanDnsProvider extends DnsProvider {
|
|||
),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<GenericResult<String?>> getZoneId(final String domain) async =>
|
||||
GenericResult(
|
||||
data: domain,
|
||||
success: true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -73,12 +73,4 @@ abstract class DnsProvider {
|
|||
final String? ip4,
|
||||
final String? dkimPublicKey,
|
||||
);
|
||||
|
||||
/// Tries to access zone of requested domain.
|
||||
///
|
||||
/// If a DNS provider doesn't support zones,
|
||||
/// will return domain without any changes.
|
||||
///
|
||||
/// If success, returns an initializing string of zone id.
|
||||
Future<GenericResult<String?>> getZoneId(final String domain);
|
||||
}
|
||||
|
|
|
@ -748,8 +748,8 @@ class DigitalOceanServerProvider extends ServerProvider {
|
|||
),
|
||||
ServerMetadataEntity(
|
||||
type: MetadataType.other,
|
||||
trId: 'server.provider',
|
||||
value: _adapter.api().displayProviderName,
|
||||
trId: 'server.server_provider',
|
||||
value: type.displayName,
|
||||
),
|
||||
];
|
||||
} catch (e) {
|
||||
|
|
|
@ -46,6 +46,7 @@ class HetznerServerProvider extends ServerProvider {
|
|||
|
||||
ApiAdapter _adapter;
|
||||
final Currency currency = Currency.fromType(CurrencyType.eur);
|
||||
int? cachedCoreAmount;
|
||||
|
||||
@override
|
||||
ServerProviderType get type => ServerProviderType.hetzner;
|
||||
|
@ -287,6 +288,8 @@ class HetznerServerProvider extends ServerProvider {
|
|||
provider: ServerProviderType.hetzner,
|
||||
);
|
||||
|
||||
cachedCoreAmount = serverResult.data!.serverType.cores;
|
||||
|
||||
final createDnsResult = await _adapter.api().createReverseDns(
|
||||
serverId: serverDetails.id,
|
||||
ip4: serverDetails.ip4,
|
||||
|
@ -752,8 +755,8 @@ class HetznerServerProvider extends ServerProvider {
|
|||
),
|
||||
ServerMetadataEntity(
|
||||
type: MetadataType.other,
|
||||
trId: 'server.provider',
|
||||
value: _adapter.api().displayProviderName,
|
||||
trId: 'server.server_provider',
|
||||
value: type.displayName,
|
||||
),
|
||||
];
|
||||
} catch (e) {
|
||||
|
@ -775,7 +778,7 @@ class HetznerServerProvider extends ServerProvider {
|
|||
) async {
|
||||
ServerMetrics? metrics;
|
||||
|
||||
List<TimeSeriesData> serializeTimeSeries(
|
||||
List<TimeSeriesData> serializeTimeNetworkSeries(
|
||||
final Map<String, dynamic> json,
|
||||
final String type,
|
||||
) {
|
||||
|
@ -785,6 +788,47 @@ class HetznerServerProvider extends ServerProvider {
|
|||
.toList();
|
||||
}
|
||||
|
||||
if (cachedCoreAmount == null) {
|
||||
final serversResult = await _adapter.api().getServers();
|
||||
if (serversResult.data.isEmpty || !serversResult.success) {
|
||||
return GenericResult(
|
||||
success: false,
|
||||
data: metrics,
|
||||
code: serversResult.code,
|
||||
message: serversResult.message,
|
||||
);
|
||||
}
|
||||
|
||||
for (final server in serversResult.data) {
|
||||
if (server.id == serverId) {
|
||||
cachedCoreAmount = server.serverType.cores;
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedCoreAmount == null) {
|
||||
return GenericResult(
|
||||
success: false,
|
||||
data: metrics,
|
||||
message: "Couldn't find active server to cache core amount",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<TimeSeriesData> serializeTimeCpuSeries(
|
||||
final Map<String, dynamic> json,
|
||||
final String type,
|
||||
) {
|
||||
final List list = json['time_series'][type]['values'];
|
||||
return list
|
||||
.map(
|
||||
(final el) => TimeSeriesData(
|
||||
el[0],
|
||||
double.parse(el[1]) / cachedCoreAmount!,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
final cpuResult = await _adapter.api().getMetrics(
|
||||
serverId,
|
||||
start,
|
||||
|
@ -818,15 +862,15 @@ class HetznerServerProvider extends ServerProvider {
|
|||
}
|
||||
|
||||
metrics = ServerMetrics(
|
||||
cpu: serializeTimeSeries(
|
||||
cpu: serializeTimeCpuSeries(
|
||||
cpuResult.data,
|
||||
'cpu',
|
||||
),
|
||||
bandwidthIn: serializeTimeSeries(
|
||||
bandwidthIn: serializeTimeNetworkSeries(
|
||||
netResult.data,
|
||||
'network.0.bandwidth.in',
|
||||
),
|
||||
bandwidthOut: serializeTimeSeries(
|
||||
bandwidthOut: serializeTimeNetworkSeries(
|
||||
netResult.data,
|
||||
'network.0.bandwidth.out',
|
||||
),
|
||||
|
|
|
@ -52,57 +52,59 @@ class _CopyEncryptionKeyModalState extends State<CopyEncryptionKeyModal> {
|
|||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Stack(
|
||||
children: [
|
||||
SelectableText(
|
||||
encryptionKey,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Stack(
|
||||
children: [
|
||||
SelectableText(
|
||||
encryptionKey,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(
|
||||
() {
|
||||
isKeyVisible = !isKeyVisible;
|
||||
},
|
||||
);
|
||||
},
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
opacity: isKeyVisible ? 0 : 1,
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.visibility_outlined),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'backup.backups_encryption_key_show'.tr(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(
|
||||
() {
|
||||
isKeyVisible = !isKeyVisible;
|
||||
},
|
||||
);
|
||||
},
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
opacity: isKeyVisible ? 0 : 1,
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.visibility_outlined),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'backup.backups_encryption_key_show'.tr(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
FilledButton.icon(
|
||||
onPressed: () {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
|
@ -85,9 +83,7 @@ class CpuChart extends StatelessWidget {
|
|||
],
|
||||
minY: 0,
|
||||
// Maximal value of data by 100 step
|
||||
maxY:
|
||||
((data.map((final e) => e.value).reduce(max) - 1) / 100).ceil() *
|
||||
100.0,
|
||||
maxY: 100,
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'
|
|||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||
import 'package:selfprivacy/logic/models/price.dart';
|
||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||
|
||||
|
@ -165,16 +166,11 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
|
|||
height: 1.0,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Icon(
|
||||
Icons.info_outlined,
|
||||
size: 24,
|
||||
),
|
||||
InfoBox(
|
||||
text: 'storage.extending_volume_price_info'.tr(),
|
||||
isWarning: false,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('storage.extending_volume_price_info'.tr()),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
|
|
@ -23,8 +23,10 @@ class RecoveryConfirmBackblaze extends StatelessWidget {
|
|||
context.watch<BackblazeFormCubit>().state;
|
||||
|
||||
return BrandHeroScreen(
|
||||
heroTitle: 'recovering.confirm_backblaze'.tr(),
|
||||
heroSubtitle: 'recovering.confirm_backblaze_description'.tr(),
|
||||
heroTitle: 'recovering.provider_connected'.tr(args: ['Backblaze']),
|
||||
heroSubtitle: 'recovering.provider_connected_description'.tr(
|
||||
args: ['Backblaze'],
|
||||
),
|
||||
hasBackButton: true,
|
||||
ignoreBreakpoints: true,
|
||||
hasSupportDrawer: true,
|
||||
|
|
|
@ -21,10 +21,14 @@ class RecoveryConfirmDns extends StatelessWidget {
|
|||
builder: (final BuildContext context) {
|
||||
final FormCubitState formCubitState =
|
||||
context.watch<DnsProviderFormCubit>().state;
|
||||
|
||||
final String providerDisplayName =
|
||||
appConfig.state.serverDomain?.provider.displayName ??
|
||||
'DNS Provider';
|
||||
return BrandHeroScreen(
|
||||
heroTitle: 'recovering.confirm_cloudflare'.tr(),
|
||||
heroSubtitle: 'recovering.confirm_cloudflare_description'.tr(
|
||||
heroTitle: 'recovering.provider_connected'.tr(
|
||||
args: [providerDisplayName],
|
||||
),
|
||||
heroSubtitle: 'recovering.provider_connected_description'.tr(
|
||||
args: [appConfig.state.serverDomain?.domainName ?? 'your domain'],
|
||||
),
|
||||
hasBackButton: true,
|
||||
|
@ -38,7 +42,9 @@ class RecoveryConfirmDns extends StatelessWidget {
|
|||
formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: 'initializing.cloudflare_api_token'.tr(),
|
||||
labelText: 'recovering.provider_connected_placeholder'.tr(
|
||||
args: [providerDisplayName],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
|
|
@ -20,8 +20,13 @@ class RecoveryServerProviderConnected extends StatelessWidget {
|
|||
ServerProviderFormCubit(appConfig),
|
||||
child: Builder(
|
||||
builder: (final BuildContext context) => BrandHeroScreen(
|
||||
heroTitle: 'recovering.server_provider_connected'.tr(),
|
||||
heroSubtitle: 'recovering.server_provider_connected_description'.tr(
|
||||
heroTitle: 'recovering.provider_connected'.tr(
|
||||
args: [
|
||||
appConfig.state.serverDetails?.provider.displayName ??
|
||||
'Server Provider'
|
||||
],
|
||||
),
|
||||
heroSubtitle: 'recovering.provider_connected_description'.tr(
|
||||
args: [appConfig.state.serverDomain?.domainName ?? 'your domain'],
|
||||
),
|
||||
hasBackButton: true,
|
||||
|
@ -36,8 +41,12 @@ class RecoveryServerProviderConnected extends StatelessWidget {
|
|||
formFieldCubit: context.read<ServerProviderFormCubit>().apiKey,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText:
|
||||
'recovering.server_provider_connected_placeholder'.tr(),
|
||||
labelText: 'recovering.provider_connected_placeholder'.tr(
|
||||
args: [
|
||||
appConfig.state.serverDetails?.provider.displayName ??
|
||||
'Server Provider'
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
|
Loading…
Reference in a new issue