Merge 'master' into 'soft-reset'

pull/269/head
NaiJi ✨ 2023-08-07 08:03:24 -03:00
commit 5c473d96a9
33 changed files with 523 additions and 312 deletions

View File

@ -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": {

View File

@ -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"
}
}
}

View File

@ -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."
}
}
}

View File

@ -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."
}
}
}

View File

@ -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"
}
}
}

View File

@ -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."
}
}
}

View File

@ -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"
}
}
}

View File

@ -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": {

View File

@ -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": "Місяць",

View File

@ -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);
}
}

View File

@ -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 = [];

View File

@ -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 = [];

View File

@ -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());
}
}

View File

@ -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 {

View File

@ -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,

View File

@ -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(

View File

@ -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',
};
}

View File

@ -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',
};
}

View File

@ -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);
}

View 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);
}

View 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,
};

View File

@ -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);
}
}

View File

@ -408,11 +408,4 @@ class DesecDnsProvider extends DnsProvider {
),
];
}
@override
Future<GenericResult<String?>> getZoneId(final String domain) async =>
GenericResult(
data: domain,
success: true,
);
}

View File

@ -369,11 +369,4 @@ class DigitalOceanDnsProvider extends DnsProvider {
),
];
}
@override
Future<GenericResult<String?>> getZoneId(final String domain) async =>
GenericResult(
data: domain,
success: true,
);
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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',
),

View File

@ -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: () {

View File

@ -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)),

View File

@ -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),
],
);
},

View File

@ -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,

View File

@ -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),

View File

@ -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),