Compare commits

...

34 Commits

Author SHA1 Message Date
NaiJi ✨ a698d3e7b2 Add padding to About page 2022-08-26 06:41:16 +03:00
NaiJi ✨ 6014e385bc Implement ServerJobCard 2022-08-26 05:34:25 +03:00
NaiJi ✨ 37d5ee9913 Implement ServerJob cubit 2022-08-25 02:45:02 +03:00
NaiJi ✨ 7bbdaf2ebe Last changes on storage pages and job backend logic 2022-08-24 08:35:49 +03:00
NaiJi ✨ d2c3d28b0f Merge from develop into graphql 2022-08-08 13:33:55 +03:00
NaiJi ✨ c230037351 Implement data_migration page and logic 2022-08-03 05:25:33 +03:00
NaiJi ✨ 96c7d7966a Update cubit for volume management and graphql 2022-08-01 02:10:37 +03:00
NaiJi ✨ 975c3e237b Merge pull request 'update ru clownflare markdown' (#106) from def/selfprivacy.org.app:master into develop
Reviewed-on: kherel/selfprivacy.org.app#106
2022-07-29 08:41:02 +03:00
NaiJi ✨ c747dcd4ae Implement Server Storage card for provider menu
Co-authored-by: Inex Code <inex.code@selfprivacy.org>
2022-07-29 08:38:21 +03:00
def e7ebfdfac6 fix russian lang 2022-07-28 23:41:27 +02:00
def 5305059a3a update ru clownflare markdown 2022-07-28 13:41:17 +02:00
Inex Code dcf120bdbc Merge pull request 'Fix installation Timer errors' (#105) from installation-timer into develop
Reviewed-on: kherel/selfprivacy.org.app#105
2022-07-27 16:05:23 +03:00
NaiJi ✨ c56a0f5976 Implement getApiTokens for graphql API map 2022-07-25 19:08:46 +03:00
NaiJi ✨ 7ead9a29ea Implement basic graphql api map structure 2022-07-25 17:06:55 +03:00
NaiJi ✨ d8ea528621 Add magic sleep before deleteVolume on creation failure
It seems Hetzner fails to accept our deleteVolume request so quickly after failing. We need to wait for about 10 seconds, I guess. The same magic sleep happens on deleteServer pack of requests. Please have patience...
2022-07-25 03:03:19 +03:00
NaiJi ✨ dab2c569ec Fix installation Timer errors
Co-authored-by: Inex Code <inex.code@selfprivacy.org>
2022-07-22 19:22:10 +03:00
Inex Code 2c9dcbe5e6 Merge pull request 'Implement Dns Provider Api Abstractions' (#101) from dns-provider-api into develop
Reviewed-on: kherel/selfprivacy.org.app#101
2022-07-19 15:09:19 +03:00
NaiJi ✨ dac310f913 Implement Dns Provider Api Abstractions 2022-07-14 16:34:08 +03:00
Inex Code 8deb240426 Merge pull request 'Implement Provider Api Abstractions' (#99) from naiji-dev into develop
Reviewed-on: kherel/selfprivacy.org.app#99
2022-07-13 21:46:35 +03:00
NaiJi ✨ 9993b09e7f Turn VolumeApiProvider into a mixin 2022-07-13 14:58:23 +03:00
NaiJi ✨ 37b7e9f839 Implement Provider Api Abstractions 2022-07-12 15:54:16 +03:00
Inex Code f40749ca57 Merge pull request 'volumes-hetzner' (#97) from volumes-hetzner into develop
Reviewed-on: kherel/selfprivacy.org.app#97
2022-07-04 02:16:25 +03:00
NaiJi ✨ 5fd8a68597 Change volume size from Gb to Byte 2022-06-28 21:06:52 +03:00
NaiJi ✨ 0a919907c8 Implement hetzner volumes cubit 2022-06-27 10:07:11 +03:00
NaiJi ✨ 352351663f Implement endpoints for hetzner volumes 2022-06-24 00:34:09 +03:00
Inex Code c4f62e012b Merge pull request 'naiji-dev' (#91) from naiji-dev into develop
Reviewed-on: kherel/selfprivacy.org.app#91
2022-06-23 12:12:37 +03:00
Inex Code 4afd40f5da Merge branch 'develop' into naiji-dev 2022-06-23 12:11:59 +03:00
Inex Code 7dc35306c4 Merge pull request 'Force domain to lowercase on recovery pages' (#95) from recovery-domain into develop
Reviewed-on: kherel/selfprivacy.org.app#95
2022-06-23 01:03:14 +03:00
NaiJi ✨ 7e2319bf21 Force domain to lowercase on recovery pages 2022-06-22 22:42:38 +03:00
NaiJi ✨ 0feb9bc299 Return to main page on backbutton press for some recovery pages 2022-06-15 07:58:02 +03:00
NaiJi ✨ 7870cf9f99 Update pubsec version to 0.6.1 2022-06-15 07:06:57 +03:00
NaiJi ✨ 9d4f7b4786 Add changelog for 0.6.1 2022-06-15 06:27:05 +03:00
NaiJi ✨ 6a22e2db6f Fix revert routing errors 2022-06-15 06:23:54 +03:00
NaiJi ✨ 313cfc7187 Bug fix error processing for installation cubits 2022-06-15 04:55:45 +03:00
115 changed files with 20991 additions and 622 deletions

View File

@ -13,6 +13,7 @@ analyzer:
exclude: exclude:
- lib/generated_plugin_registrant.dart - lib/generated_plugin_registrant.dart
- lib/**.g.dart - lib/**.g.dart
- lib/**.graphql.dart
linter: linter:
# The lint rules applied to this project can be customized in the # The lint rules applied to this project can be customized in the

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,11 +1,14 @@
### Как получить Cloudflare API Token ### Как получить Cloudflare API Token
1. Переходим по [ссылке](https://dash.cloudflare.com/) и авторизуемся в ранее созданном аккаунте. https://dash.cloudflare.com/ 1. Переходим по [ссылке](https://dash.cloudflare.com/) и авторизуемся в ранее созданном аккаунте. https://dash.cloudflare.com/
В правом углу кликаем на иконку профиля (человечек в кружочке). Для мобильной версии сайта, в верхнем левом углу, нажимаем кнопку **Меню** (три горизонтальных полоски), в выпавшем меню, ищем пункт **My Profile**. 2. В правом верхнем углу кликаем на иконку профиля (для мобильной версии сайта: в верхнем левом углу нажимаем кнопку **Меню** с тремя горизонтальными полосками). В выпавшем меню кликаем на пункт **My Profile**.
![My profile](resource:assets/images/pics/myprofile.png)
3. Нам предлагается на выбор, четыре категории настройки: **Preferences**, **Authentication**, **API Tokens**, **Sessions**. Выбираем **API Tokens**. 3. Нам предлагается на выбор, четыре категории настройки: **Preferences**, **Authentication**, **API Tokens**, **Sessions**. Выбираем **API Tokens**.
4. Самым первым пунктом видим кнопку **Create Token**. С полной уверенностью в себе и желанием обрести приватность, нажимаем на неё. 4. Самым первым пунктом видим кнопку **Create Token**. С полной уверенностью в себе и желанием обрести приватность, нажимаем на неё.
5. Спускаемся в самый низ и видим поле **Create Custom Token** и кнопку **Get Started** с правой стороны. Нажимаем. 5. Спускаемся в самый низ и видим поле **Create Custom Token** и кнопку **Get Started** с правой стороны. Нажимаем.
6. В поле **Token Name** даём своему токену имя. Можете покреативить и отнестись к этому как к наименованию домашнего зверька :) 6. В поле **Token Name** даём своему токену имя. Можете покреативить и отнестись к этому как к наименованию домашнего зверька :)
7. Далее, у нас **Permissions**. В первом поле выбираем Zone. Во втором поле, по центру, выбираем **DNS**. В последнем поле выбираем **Edit**. 7. Далее, у нас **Permissions**. В первом поле выбираем **Zone**. Во втором поле, по центру, выбираем **DNS**. В последнем поле выбираем **Edit**.
8. Нажимаем на синюю надпись снизу **+ Add more** (сразу же под левым полем которое мы заполняли ранее). Вуаля, у нас появились новые поля. Заполняем по аналогии с предыдущим пунктом, в первом поле выбираем **Zone**, во-втором тоже **Zone**. А уже в третьем нажимаем на **Read**. Давайте сверим с тем, что у вас получилось:
![Permissions](resource:assets/images/pics/permissions.png)
8. Далее смотрим на **Zone Resources**. Под этой надписью есть строка с двумя полями. В первом должно быть **Include**, а во втором — **Specific Zone**. Как только Вы выберите **Specific Zone**, справа появится ещё одно поле. В нём выбираем наш домен. 8. Далее смотрим на **Zone Resources**. Под этой надписью есть строка с двумя полями. В первом должно быть **Include**, а во втором — **Specific Zone**. Как только Вы выберите **Specific Zone**, справа появится ещё одно поле. В нём выбираем наш домен.
9. Листаем в самый низ и нажимаем на синюю кнопку **Continue to Summary**. 9. Листаем в самый низ и нажимаем на синюю кнопку **Continue to Summary**.
10. Проверяем, всё ли мы правильно выбрали. Должна присутствовать подобная строка: ваш.домен — **DNS:Edit, Zone:Read**. 10. Проверяем, всё ли мы правильно выбрали. Должна присутствовать подобная строка: ваш.домен — **DNS:Edit, Zone:Read**.

View File

@ -52,7 +52,8 @@
"copied_ssh": "SSH copied to clipboard", "copied_ssh": "SSH copied to clipboard",
"delete_ssh_text": "Delete SSH key?", "delete_ssh_text": "Delete SSH key?",
"about_app_page": { "about_app_page": {
"text": "Application version v.{}" "application_version_text": "Application version v.{}",
"api_version_text": "Server API version v.{}"
}, },
"settings": { "settings": {
"title": "Application settings", "title": "Application settings",
@ -162,6 +163,24 @@
"refresh": "Refresh status", "refresh": "Refresh status",
"refetchBackups": "Refetch backup list", "refetchBackups": "Refetch backup list",
"refetchingList": "In a few minutes list will be updated" "refetchingList": "In a few minutes list will be updated"
},
"storage": {
"card_title": "Server Storage",
"status_ok": "Disk usage is OK",
"status_error": "Low disk space",
"disk_usage": "{} GB used",
"disk_total": "{} GB total · {}",
"gb": "{} GB",
"mb": "{} MB",
"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.",
"size": "Size",
"euro": "Euro",
"data_migration_title": "Data migration",
"data_migration_notice": "During migration all services will be turned off.",
"start_migration_button": "Start migration"
} }
}, },
"not_ready_card": { "not_ready_card": {
@ -237,6 +256,15 @@
"bottom_sheet": { "bottom_sheet": {
"1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure" "1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure"
} }
},
"page": {
"up_and_running": "Up and running",
"resource_usage": "Resource usage",
"disk_used": "{} of disk space used",
"users": "Users",
"controlled_by": "Controlled by {}",
"apps": "Apps",
"settings": "Settings and maintenance"
} }
}, },
"users": { "users": {
@ -259,6 +287,9 @@
"1": "Connect a server", "1": "Connect a server",
"2": "A place where your data and SelfPrivacy services will reside:", "2": "A place where your data and SelfPrivacy services will reside:",
"how": "How to obtain API token", "how": "How to obtain API token",
"hetzner_bad_key_error": "Hetzner API key is invalid",
"cloudflare_bad_key_error": "Cloudflare API key is invalid",
"backblaze_bad_key_error": "Backblaze storage information is invalid",
"3": "Connect CloudFlare", "3": "Connect CloudFlare",
"4": "To manage your domain's DNS", "4": "To manage your domain's DNS",
"5": "CloudFlare API Token", "5": "CloudFlare API Token",
@ -281,7 +312,7 @@
"22": "Create master account", "22": "Create master account",
"23": "Enter a nickname and strong password", "23": "Enter a nickname and strong password",
"finish": "Everything is initialized", "finish": "Everything is initialized",
"checks": "Checks have been completed \n{} ouf of {}" "checks": "Checks have been completed \n{} out of {}"
}, },
"recovering": { "recovering": {
"recovery_main_header": "Connect to an existing server", "recovery_main_header": "Connect to an existing server",
@ -373,8 +404,10 @@
}, },
"modals": { "modals": {
"_comment": "messages in modals", "_comment": "messages in modals",
"1": "Server with such name, already exist", "1": "Server with such name, already exist.",
"1_1": "Unexpected error during placement from the provider side.",
"2": "Destroy server and create a new one?", "2": "Destroy server and create a new one?",
"2_2": "Try again?",
"3": "Are you sure?", "3": "Are you sure?",
"4": "Purge all authentication keys?", "4": "Purge all authentication keys?",
"5": "Yes, purge all my tokens", "5": "Yes, purge all my tokens",

View File

@ -52,7 +52,8 @@
"copied_ssh": "SSH ключ cкопирован в буфер", "copied_ssh": "SSH ключ cкопирован в буфер",
"delete_ssh_text": "Удалить SSH ключ?", "delete_ssh_text": "Удалить SSH ключ?",
"about_app_page": { "about_app_page": {
"text": "Версия приложения: v.{}" "application_version_text": "Версия приложения v.{}",
"api_version_text": "Версия API сервера v.{}"
}, },
"settings": { "settings": {
"title": "Настройки приложения", "title": "Настройки приложения",
@ -260,6 +261,9 @@
"1": "Подключите сервер", "1": "Подключите сервер",
"2": "Здесь будут жить наши данные и SelfPrivacy-сервисы", "2": "Здесь будут жить наши данные и SelfPrivacy-сервисы",
"how": "Как получить API Token", "how": "Как получить API Token",
"hetzner_bad_key_error": "Hetzner API ключ неверен",
"cloudflare_bad_key_error": "Cloudflare API ключ неверен",
"backblaze_bad_key_error": "Информация о Backblaze хранилище неверна",
"3": "Подключите CloudFlare", "3": "Подключите CloudFlare",
"4": "Для управления DNS вашего домена", "4": "Для управления DNS вашего домена",
"5": "CloudFlare API Token", "5": "CloudFlare API Token",
@ -371,8 +375,10 @@
}, },
"modals": { "modals": {
"_comment": "messages in modals", "_comment": "messages in modals",
"1": "Сервер с таким именем уже существует", "1": "Сервер с таким именем уже существует.",
"1.1": "Непредвиденная ошибка при создании со стороны провайдера.",
"2": "Уничтожить сервер и создать новый?", "2": "Уничтожить сервер и создать новый?",
"2.2": "Попробовать ещё раз?",
"3": "Подтвердите", "3": "Подтвердите",
"4": "Сбросить все ключи?", "4": "Сбросить все ключи?",
"5": "Да, сбросить", "5": "Да, сбросить",

View File

@ -1,7 +1,17 @@
targets: targets:
$default: $default:
builders: builders:
graphql_codegen:
options:
scalars:
DateTime:
type: DateTime
fromJsonFunctionName: dateTimeFromJson
toJsonFunctionName: dateTimeToJson
import: package:selfprivacy/utils/scalars.dart
clients:
- graphql
json_serializable: json_serializable:
options: options:
create_factory: true create_factory: true
create_to_json: false create_to_json: true

View File

@ -0,0 +1,3 @@
- Fixed routing errors and broken "back" buttons on recovery stages
- Fixed broken validation on api token fields
- Minor improvements

View File

@ -10,6 +10,7 @@ import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
class BlocAndProviderConfig extends StatelessWidget { class BlocAndProviderConfig extends StatelessWidget {
const BlocAndProviderConfig({final super.key, this.child}); const BlocAndProviderConfig({final super.key, this.child});
@ -26,6 +27,7 @@ class BlocAndProviderConfig extends StatelessWidget {
final dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit); final dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
final recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit); final recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
final apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit); final apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit);
final apiVolumesCubit = ApiProviderVolumeCubit(serverInstallationCubit);
return MultiProvider( return MultiProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
@ -60,6 +62,9 @@ class BlocAndProviderConfig extends StatelessWidget {
BlocProvider( BlocProvider(
create: (final _) => apiDevicesCubit..load(), create: (final _) => apiDevicesCubit..load(),
), ),
BlocProvider(
create: (final _) => apiVolumesCubit..load(),
),
BlocProvider( BlocProvider(
create: (final _) => create: (final _) =>
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit), JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit),

View File

@ -0,0 +1,55 @@
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:selfprivacy/config/get_it_config.dart';
abstract class ApiMap {
Future<GraphQLClient> getClient() async {
final httpLink = HttpLink(
'https://api.$rootAddress/graphql',
);
final String token = _getApiToken();
final Link graphQLLink = isWithToken
? AuthLink(
getToken: () async =>
customToken == '' ? 'Bearer $token' : customToken,
).concat(httpLink)
: httpLink;
return GraphQLClient(
cache: GraphQLCache(),
link: graphQLLink,
);
}
Future<GraphQLClient> getSubscriptionClient() async {
final String token = _getApiToken();
final WebSocketLink webSocketLink = WebSocketLink(
'ws://api.$rootAddress/graphql',
config: SocketClientConfig(
autoReconnect: true,
headers: token.isEmpty ? null : {'Authorization': 'Bearer $token'},
),
);
return GraphQLClient(
cache: GraphQLCache(),
link: webSocketLink,
);
}
String _getApiToken() {
String token = '';
final serverDetails = getIt<ApiConfigModel>().serverDetails;
if (serverDetails != null) {
token = getIt<ApiConfigModel>().serverDetails!.apiToken;
}
return token;
}
abstract final String? rootAddress;
abstract final bool hasLogger;
abstract final bool isWithToken;
abstract final String customToken;
}

View File

@ -0,0 +1,69 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
query GetServerDiskVolumes {
storage {
volumes {
freeSpace
model
name
root
serial
totalSpace
type
usages {
title
usedSpace
__typename
... on ServiceStorageUsage {
service {
id
isMovable
displayName
}
}
}
usedSpace
}
}
}
mutation MountVolume($name: String!) {
mountVolume(name: $name) {
...basicMutationReturnFields
}
}
mutation ResizeVolume($name: String!) {
resizeVolume(name: $name) {
...basicMutationReturnFields
}
}
mutation UnmountVolume($name: String!) {
unmountVolume(name: $name) {
...basicMutationReturnFields
}
}
mutation MigrateToBinds($input: MigrateToBindsInput!) {
migrateToBinds(input: $input) {
...basicMutationReturnFields
job {
createdAt
description
error
finishedAt
name
progress
result
status
statusText
uid
updatedAt
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'disk_volumes.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes _$Query$GetServerDiskVolumesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes(
storage: Query$GetServerDiskVolumes$storage.fromJson(
json['storage'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumesToJson(
Query$GetServerDiskVolumes instance) =>
<String, dynamic>{
'storage': instance.storage.toJson(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage _$Query$GetServerDiskVolumes$storageFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage(
volumes: (json['volumes'] as List<dynamic>)
.map((e) => Query$GetServerDiskVolumes$storage$volumes.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storageToJson(
Query$GetServerDiskVolumes$storage instance) =>
<String, dynamic>{
'volumes': instance.volumes.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes
_$Query$GetServerDiskVolumes$storage$volumesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes(
freeSpace: json['freeSpace'] as String,
model: json['model'] as String?,
name: json['name'] as String,
root: json['root'] as bool,
serial: json['serial'] as String?,
totalSpace: json['totalSpace'] as String,
type: json['type'] as String,
usages: (json['usages'] as List<dynamic>)
.map((e) =>
Query$GetServerDiskVolumes$storage$volumes$usages.fromJson(
e as Map<String, dynamic>))
.toList(),
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storage$volumesToJson(
Query$GetServerDiskVolumes$storage$volumes instance) =>
<String, dynamic>{
'freeSpace': instance.freeSpace,
'model': instance.model,
'name': instance.name,
'root': instance.root,
'serial': instance.serial,
'totalSpace': instance.totalSpace,
'type': instance.type,
'usages': instance.usages.map((e) => e.toJson()).toList(),
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes$usages
_$Query$GetServerDiskVolumes$storage$volumes$usagesFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetServerDiskVolumes$storage$volumes$usagesToJson(
Query$GetServerDiskVolumes$storage$volumes$usages instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
};
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsageFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
$__typename: json['__typename'] as String,
service: json['service'] == null
? null
: Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
.fromJson(json['service'] as Map<String, dynamic>),
);
Map<String, dynamic>
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsageToJson(
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage
instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'__typename': instance.$__typename,
'service': instance.service?.toJson(),
};
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$serviceFromJson(
Map<String, dynamic> json) =>
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service(
id: json['id'] as String,
isMovable: json['isMovable'] as bool,
displayName: json['displayName'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$serviceToJson(
Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service
instance) =>
<String, dynamic>{
'id': instance.id,
'isMovable': instance.isMovable,
'displayName': instance.displayName,
'__typename': instance.$__typename,
};
Variables$Mutation$MountVolume _$Variables$Mutation$MountVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MountVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$MountVolumeToJson(
Variables$Mutation$MountVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$MountVolume _$Mutation$MountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$MountVolume(
mountVolume: Mutation$MountVolume$mountVolume.fromJson(
json['mountVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MountVolumeToJson(
Mutation$MountVolume instance) =>
<String, dynamic>{
'mountVolume': instance.mountVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$MountVolume$mountVolume _$Mutation$MountVolume$mountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$MountVolume$mountVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MountVolume$mountVolumeToJson(
Mutation$MountVolume$mountVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$ResizeVolume _$Variables$Mutation$ResizeVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ResizeVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$ResizeVolumeToJson(
Variables$Mutation$ResizeVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$ResizeVolume _$Mutation$ResizeVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$ResizeVolume(
resizeVolume: Mutation$ResizeVolume$resizeVolume.fromJson(
json['resizeVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ResizeVolumeToJson(
Mutation$ResizeVolume instance) =>
<String, dynamic>{
'resizeVolume': instance.resizeVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$ResizeVolume$resizeVolume _$Mutation$ResizeVolume$resizeVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$ResizeVolume$resizeVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ResizeVolume$resizeVolumeToJson(
Mutation$ResizeVolume$resizeVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$UnmountVolume _$Variables$Mutation$UnmountVolumeFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$UnmountVolume(
name: json['name'] as String,
);
Map<String, dynamic> _$Variables$Mutation$UnmountVolumeToJson(
Variables$Mutation$UnmountVolume instance) =>
<String, dynamic>{
'name': instance.name,
};
Mutation$UnmountVolume _$Mutation$UnmountVolumeFromJson(
Map<String, dynamic> json) =>
Mutation$UnmountVolume(
unmountVolume: Mutation$UnmountVolume$unmountVolume.fromJson(
json['unmountVolume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UnmountVolumeToJson(
Mutation$UnmountVolume instance) =>
<String, dynamic>{
'unmountVolume': instance.unmountVolume.toJson(),
'__typename': instance.$__typename,
};
Mutation$UnmountVolume$unmountVolume
_$Mutation$UnmountVolume$unmountVolumeFromJson(Map<String, dynamic> json) =>
Mutation$UnmountVolume$unmountVolume(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UnmountVolume$unmountVolumeToJson(
Mutation$UnmountVolume$unmountVolume instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$MigrateToBinds _$Variables$Mutation$MigrateToBindsFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MigrateToBinds(
input: Input$MigrateToBindsInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$MigrateToBindsToJson(
Variables$Mutation$MigrateToBinds instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$MigrateToBinds _$Mutation$MigrateToBindsFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds(
migrateToBinds: Mutation$MigrateToBinds$migrateToBinds.fromJson(
json['migrateToBinds'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MigrateToBindsToJson(
Mutation$MigrateToBinds instance) =>
<String, dynamic>{
'migrateToBinds': instance.migrateToBinds.toJson(),
'__typename': instance.$__typename,
};
Mutation$MigrateToBinds$migrateToBinds
_$Mutation$MigrateToBinds$migrateToBindsFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds$migrateToBinds(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
job: json['job'] == null
? null
: Mutation$MigrateToBinds$migrateToBinds$job.fromJson(
json['job'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$MigrateToBinds$migrateToBindsToJson(
Mutation$MigrateToBinds$migrateToBinds instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'job': instance.job?.toJson(),
};
Mutation$MigrateToBinds$migrateToBinds$job
_$Mutation$MigrateToBinds$migrateToBinds$jobFromJson(
Map<String, dynamic> json) =>
Mutation$MigrateToBinds$migrateToBinds$job(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MigrateToBinds$migrateToBinds$jobToJson(
Mutation$MigrateToBinds$migrateToBinds$job instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};

View File

@ -0,0 +1,351 @@
type Alert {
message: String!
severity: Severity!
timestamp: DateTime
title: String!
}
type Api {
devices: [ApiDevice!]!
recoveryKey: ApiRecoveryKeyStatus!
version: String!
}
type ApiDevice {
creationDate: DateTime!
isCaller: Boolean!
name: String!
}
type ApiJob {
createdAt: DateTime!
description: String!
error: String
finishedAt: DateTime
name: String!
progress: Int
result: String
status: String!
statusText: String
uid: String!
updatedAt: DateTime!
}
type ApiKeyMutationReturn implements MutationReturnInterface {
code: Int!
key: String
message: String!
success: Boolean!
}
type ApiRecoveryKeyStatus {
creationDate: DateTime
exists: Boolean!
expirationDate: DateTime
usesLeft: Int
valid: Boolean!
}
type AutoUpgradeOptions {
allowReboot: Boolean!
enable: Boolean!
}
input AutoUpgradeSettingsInput {
enableAutoUpgrade: Boolean = null
allowReboot: Boolean = null
}
type AutoUpgradeSettingsMutationReturn implements MutationReturnInterface {
allowReboot: Boolean!
code: Int!
enableAutoUpgrade: Boolean!
message: String!
success: Boolean!
}
"""Date with time (isoformat)"""
scalar DateTime
type DeviceApiTokenMutationReturn implements MutationReturnInterface {
code: Int!
message: String!
success: Boolean!
token: String
}
enum DnsProvider {
CLOUDFLARE
}
type DnsRecord {
content: String!
name: String!
priority: Int
recordType: String!
ttl: Int!
}
type GenericJobButationReturn implements MutationReturnInterface {
code: Int!
job: ApiJob
message: String!
success: Boolean!
}
type GenericMutationReturn implements MutationReturnInterface {
code: Int!
message: String!
success: Boolean!
}
type Job {
getJob(jobId: String!): ApiJob
getJobs: [ApiJob!]!
}
input MigrateToBindsInput {
emailBlockDevice: String!
bitwardenBlockDevice: String!
giteaBlockDevice: String!
nextcloudBlockDevice: String!
pleromaBlockDevice: String!
}
input MoveServiceInput {
serviceId: String!
location: String!
}
type Mutation {
addSshKey(sshInput: SshMutationInput!): UserMutationReturn!
authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn!
changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn!
changeTimezone(timezone: String!): TimezoneMutationReturn!
createUser(user: UserMutationInput!): UserMutationReturn!
deleteDeviceApiToken(device: String!): GenericMutationReturn!
deleteUser(username: String!): GenericMutationReturn!
disableService(serviceId: String!): ServiceMutationReturn!
enableService(serviceId: String!): ServiceMutationReturn!
getNewDeviceApiKey: ApiKeyMutationReturn!
getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn!
invalidateNewDeviceApiKey: GenericMutationReturn!
migrateToBinds(input: MigrateToBindsInput!): GenericJobButationReturn!
mountVolume(name: String!): GenericMutationReturn!
moveService(input: MoveServiceInput!): ServiceJobMutationReturn!
pullRepositoryChanges: GenericMutationReturn!
rebootSystem: GenericMutationReturn!
refreshDeviceApiToken: DeviceApiTokenMutationReturn!
removeJob(jobId: String!): GenericMutationReturn!
removeSshKey(sshInput: SshMutationInput!): UserMutationReturn!
resizeVolume(name: String!): GenericMutationReturn!
restartService(serviceId: String!): ServiceMutationReturn!
runSystemRebuild: GenericMutationReturn!
runSystemRollback: GenericMutationReturn!
runSystemUpgrade: GenericMutationReturn!
startService(serviceId: String!): ServiceMutationReturn!
stopService(serviceId: String!): ServiceMutationReturn!
testMutation: GenericMutationReturn!
unmountVolume(name: String!): GenericMutationReturn!
updateUser(user: UserMutationInput!): UserMutationReturn!
useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn!
}
interface MutationReturnInterface {
code: Int!
message: String!
success: Boolean!
}
type Query {
api: Api!
jobs: Job!
services: Services!
storage: Storage!
system: System!
users: Users!
}
input RecoveryKeyLimitsInput {
expirationDate: DateTime = null
uses: Int = null
}
enum ServerProvider {
HETZNER
}
type Service {
description: String!
displayName: String!
dnsRecords: [DnsRecord!]
id: String!
isEnabled: Boolean!
isMovable: Boolean!
isRequired: Boolean!
status: ServiceStatusEnum!
storageUsage: ServiceStorageUsage!
svgIcon: String!
url: String
}
type ServiceJobMutationReturn implements MutationReturnInterface {
code: Int!
job: ApiJob
message: String!
service: Service
success: Boolean!
}
type ServiceMutationReturn implements MutationReturnInterface {
code: Int!
message: String!
service: Service
success: Boolean!
}
enum ServiceStatusEnum {
ACTIVATING
ACTIVE
DEACTIVATING
FAILED
INACTIVE
OFF
RELOADING
}
type ServiceStorageUsage implements StorageUsageInterface {
service: Service
title: String!
usedSpace: String!
volume: StorageVolume
}
type Services {
allServices: [Service!]!
}
enum Severity {
CRITICAL
ERROR
INFO
SUCCESS
WARNING
}
input SshMutationInput {
username: String!
sshKey: String!
}
type SshSettings {
enable: Boolean!
passwordAuthentication: Boolean!
rootSshKeys: [String!]!
}
type Storage {
volumes: [StorageVolume!]!
}
interface StorageUsageInterface {
title: String!
usedSpace: String!
volume: StorageVolume
}
type StorageVolume {
freeSpace: String!
model: String
name: String!
root: Boolean!
serial: String
totalSpace: String!
type: String!
usages: [StorageUsageInterface!]!
usedSpace: String!
}
type Subscription {
count(target: Int! = 100): Int!
}
type System {
busy: Boolean!
domainInfo: SystemDomainInfo!
info: SystemInfo!
provider: SystemProviderInfo!
settings: SystemSettings!
status: Alert!
workingDirectory: String!
}
type SystemDomainInfo {
domain: String!
hostname: String!
provider: DnsProvider!
requiredDnsRecords: [DnsRecord!]!
}
type SystemInfo {
pythonVersion: String!
systemVersion: String!
usingBinds: Boolean!
}
type SystemProviderInfo {
id: String!
provider: ServerProvider!
}
type SystemSettings {
autoUpgrade: AutoUpgradeOptions!
ssh: SshSettings!
timezone: String!
}
type TimezoneMutationReturn implements MutationReturnInterface {
code: Int!
message: String!
success: Boolean!
timezone: String
}
input UseNewDeviceKeyInput {
key: String!
deviceName: String!
}
input UseRecoveryKeyInput {
key: String!
deviceName: String!
}
type User {
sshKeys: [String!]!
userType: UserType!
username: String!
}
input UserMutationInput {
username: String!
password: String!
}
type UserMutationReturn implements MutationReturnInterface {
code: Int!
message: String!
success: Boolean!
user: User
}
enum UserType {
NORMAL
PRIMARY
ROOT
}
type Users {
allUsers: [User!]!
getUser(username: String!): User
}

View File

@ -0,0 +1,443 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:selfprivacy/utils/scalars.dart';
part 'schema.graphql.g.dart';
@JsonSerializable(explicitToJson: true)
class Input$AutoUpgradeSettingsInput {
Input$AutoUpgradeSettingsInput({this.enableAutoUpgrade, this.allowReboot});
@override
factory Input$AutoUpgradeSettingsInput.fromJson(Map<String, dynamic> json) =>
_$Input$AutoUpgradeSettingsInputFromJson(json);
final bool? enableAutoUpgrade;
final bool? allowReboot;
Map<String, dynamic> toJson() => _$Input$AutoUpgradeSettingsInputToJson(this);
int get hashCode {
final l$enableAutoUpgrade = enableAutoUpgrade;
final l$allowReboot = allowReboot;
return Object.hashAll([l$enableAutoUpgrade, l$allowReboot]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$AutoUpgradeSettingsInput) ||
runtimeType != other.runtimeType) return false;
final l$enableAutoUpgrade = enableAutoUpgrade;
final lOther$enableAutoUpgrade = other.enableAutoUpgrade;
if (l$enableAutoUpgrade != lOther$enableAutoUpgrade) return false;
final l$allowReboot = allowReboot;
final lOther$allowReboot = other.allowReboot;
if (l$allowReboot != lOther$allowReboot) return false;
return true;
}
Input$AutoUpgradeSettingsInput copyWith(
{bool? Function()? enableAutoUpgrade,
bool? Function()? allowReboot}) =>
Input$AutoUpgradeSettingsInput(
enableAutoUpgrade: enableAutoUpgrade == null
? this.enableAutoUpgrade
: enableAutoUpgrade(),
allowReboot: allowReboot == null ? this.allowReboot : allowReboot());
}
@JsonSerializable(explicitToJson: true)
class Input$MigrateToBindsInput {
Input$MigrateToBindsInput(
{required this.emailBlockDevice,
required this.bitwardenBlockDevice,
required this.giteaBlockDevice,
required this.nextcloudBlockDevice,
required this.pleromaBlockDevice});
@override
factory Input$MigrateToBindsInput.fromJson(Map<String, dynamic> json) =>
_$Input$MigrateToBindsInputFromJson(json);
final String emailBlockDevice;
final String bitwardenBlockDevice;
final String giteaBlockDevice;
final String nextcloudBlockDevice;
final String pleromaBlockDevice;
Map<String, dynamic> toJson() => _$Input$MigrateToBindsInputToJson(this);
int get hashCode {
final l$emailBlockDevice = emailBlockDevice;
final l$bitwardenBlockDevice = bitwardenBlockDevice;
final l$giteaBlockDevice = giteaBlockDevice;
final l$nextcloudBlockDevice = nextcloudBlockDevice;
final l$pleromaBlockDevice = pleromaBlockDevice;
return Object.hashAll([
l$emailBlockDevice,
l$bitwardenBlockDevice,
l$giteaBlockDevice,
l$nextcloudBlockDevice,
l$pleromaBlockDevice
]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$MigrateToBindsInput) ||
runtimeType != other.runtimeType) return false;
final l$emailBlockDevice = emailBlockDevice;
final lOther$emailBlockDevice = other.emailBlockDevice;
if (l$emailBlockDevice != lOther$emailBlockDevice) return false;
final l$bitwardenBlockDevice = bitwardenBlockDevice;
final lOther$bitwardenBlockDevice = other.bitwardenBlockDevice;
if (l$bitwardenBlockDevice != lOther$bitwardenBlockDevice) return false;
final l$giteaBlockDevice = giteaBlockDevice;
final lOther$giteaBlockDevice = other.giteaBlockDevice;
if (l$giteaBlockDevice != lOther$giteaBlockDevice) return false;
final l$nextcloudBlockDevice = nextcloudBlockDevice;
final lOther$nextcloudBlockDevice = other.nextcloudBlockDevice;
if (l$nextcloudBlockDevice != lOther$nextcloudBlockDevice) return false;
final l$pleromaBlockDevice = pleromaBlockDevice;
final lOther$pleromaBlockDevice = other.pleromaBlockDevice;
if (l$pleromaBlockDevice != lOther$pleromaBlockDevice) return false;
return true;
}
Input$MigrateToBindsInput copyWith(
{String? emailBlockDevice,
String? bitwardenBlockDevice,
String? giteaBlockDevice,
String? nextcloudBlockDevice,
String? pleromaBlockDevice}) =>
Input$MigrateToBindsInput(
emailBlockDevice: emailBlockDevice == null
? this.emailBlockDevice
: emailBlockDevice,
bitwardenBlockDevice: bitwardenBlockDevice == null
? this.bitwardenBlockDevice
: bitwardenBlockDevice,
giteaBlockDevice: giteaBlockDevice == null
? this.giteaBlockDevice
: giteaBlockDevice,
nextcloudBlockDevice: nextcloudBlockDevice == null
? this.nextcloudBlockDevice
: nextcloudBlockDevice,
pleromaBlockDevice: pleromaBlockDevice == null
? this.pleromaBlockDevice
: pleromaBlockDevice);
}
@JsonSerializable(explicitToJson: true)
class Input$MoveServiceInput {
Input$MoveServiceInput({required this.serviceId, required this.location});
@override
factory Input$MoveServiceInput.fromJson(Map<String, dynamic> json) =>
_$Input$MoveServiceInputFromJson(json);
final String serviceId;
final String location;
Map<String, dynamic> toJson() => _$Input$MoveServiceInputToJson(this);
int get hashCode {
final l$serviceId = serviceId;
final l$location = location;
return Object.hashAll([l$serviceId, l$location]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$MoveServiceInput) || runtimeType != other.runtimeType)
return false;
final l$serviceId = serviceId;
final lOther$serviceId = other.serviceId;
if (l$serviceId != lOther$serviceId) return false;
final l$location = location;
final lOther$location = other.location;
if (l$location != lOther$location) return false;
return true;
}
Input$MoveServiceInput copyWith({String? serviceId, String? location}) =>
Input$MoveServiceInput(
serviceId: serviceId == null ? this.serviceId : serviceId,
location: location == null ? this.location : location);
}
@JsonSerializable(explicitToJson: true)
class Input$RecoveryKeyLimitsInput {
Input$RecoveryKeyLimitsInput({this.expirationDate, this.uses});
@override
factory Input$RecoveryKeyLimitsInput.fromJson(Map<String, dynamic> json) =>
_$Input$RecoveryKeyLimitsInputFromJson(json);
@JsonKey(
fromJson: _nullable$dateTimeFromJson, toJson: _nullable$dateTimeToJson)
final DateTime? expirationDate;
final int? uses;
Map<String, dynamic> toJson() => _$Input$RecoveryKeyLimitsInputToJson(this);
int get hashCode {
final l$expirationDate = expirationDate;
final l$uses = uses;
return Object.hashAll([l$expirationDate, l$uses]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$RecoveryKeyLimitsInput) ||
runtimeType != other.runtimeType) return false;
final l$expirationDate = expirationDate;
final lOther$expirationDate = other.expirationDate;
if (l$expirationDate != lOther$expirationDate) return false;
final l$uses = uses;
final lOther$uses = other.uses;
if (l$uses != lOther$uses) return false;
return true;
}
Input$RecoveryKeyLimitsInput copyWith(
{DateTime? Function()? expirationDate, int? Function()? uses}) =>
Input$RecoveryKeyLimitsInput(
expirationDate:
expirationDate == null ? this.expirationDate : expirationDate(),
uses: uses == null ? this.uses : uses());
}
@JsonSerializable(explicitToJson: true)
class Input$SshMutationInput {
Input$SshMutationInput({required this.username, required this.sshKey});
@override
factory Input$SshMutationInput.fromJson(Map<String, dynamic> json) =>
_$Input$SshMutationInputFromJson(json);
final String username;
final String sshKey;
Map<String, dynamic> toJson() => _$Input$SshMutationInputToJson(this);
int get hashCode {
final l$username = username;
final l$sshKey = sshKey;
return Object.hashAll([l$username, l$sshKey]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$SshMutationInput) || runtimeType != other.runtimeType)
return false;
final l$username = username;
final lOther$username = other.username;
if (l$username != lOther$username) return false;
final l$sshKey = sshKey;
final lOther$sshKey = other.sshKey;
if (l$sshKey != lOther$sshKey) return false;
return true;
}
Input$SshMutationInput copyWith({String? username, String? sshKey}) =>
Input$SshMutationInput(
username: username == null ? this.username : username,
sshKey: sshKey == null ? this.sshKey : sshKey);
}
@JsonSerializable(explicitToJson: true)
class Input$UseNewDeviceKeyInput {
Input$UseNewDeviceKeyInput({required this.key, required this.deviceName});
@override
factory Input$UseNewDeviceKeyInput.fromJson(Map<String, dynamic> json) =>
_$Input$UseNewDeviceKeyInputFromJson(json);
final String key;
final String deviceName;
Map<String, dynamic> toJson() => _$Input$UseNewDeviceKeyInputToJson(this);
int get hashCode {
final l$key = key;
final l$deviceName = deviceName;
return Object.hashAll([l$key, l$deviceName]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$UseNewDeviceKeyInput) ||
runtimeType != other.runtimeType) return false;
final l$key = key;
final lOther$key = other.key;
if (l$key != lOther$key) return false;
final l$deviceName = deviceName;
final lOther$deviceName = other.deviceName;
if (l$deviceName != lOther$deviceName) return false;
return true;
}
Input$UseNewDeviceKeyInput copyWith({String? key, String? deviceName}) =>
Input$UseNewDeviceKeyInput(
key: key == null ? this.key : key,
deviceName: deviceName == null ? this.deviceName : deviceName);
}
@JsonSerializable(explicitToJson: true)
class Input$UseRecoveryKeyInput {
Input$UseRecoveryKeyInput({required this.key, required this.deviceName});
@override
factory Input$UseRecoveryKeyInput.fromJson(Map<String, dynamic> json) =>
_$Input$UseRecoveryKeyInputFromJson(json);
final String key;
final String deviceName;
Map<String, dynamic> toJson() => _$Input$UseRecoveryKeyInputToJson(this);
int get hashCode {
final l$key = key;
final l$deviceName = deviceName;
return Object.hashAll([l$key, l$deviceName]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$UseRecoveryKeyInput) ||
runtimeType != other.runtimeType) return false;
final l$key = key;
final lOther$key = other.key;
if (l$key != lOther$key) return false;
final l$deviceName = deviceName;
final lOther$deviceName = other.deviceName;
if (l$deviceName != lOther$deviceName) return false;
return true;
}
Input$UseRecoveryKeyInput copyWith({String? key, String? deviceName}) =>
Input$UseRecoveryKeyInput(
key: key == null ? this.key : key,
deviceName: deviceName == null ? this.deviceName : deviceName);
}
@JsonSerializable(explicitToJson: true)
class Input$UserMutationInput {
Input$UserMutationInput({required this.username, required this.password});
@override
factory Input$UserMutationInput.fromJson(Map<String, dynamic> json) =>
_$Input$UserMutationInputFromJson(json);
final String username;
final String password;
Map<String, dynamic> toJson() => _$Input$UserMutationInputToJson(this);
int get hashCode {
final l$username = username;
final l$password = password;
return Object.hashAll([l$username, l$password]);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(other is Input$UserMutationInput) || runtimeType != other.runtimeType)
return false;
final l$username = username;
final lOther$username = other.username;
if (l$username != lOther$username) return false;
final l$password = password;
final lOther$password = other.password;
if (l$password != lOther$password) return false;
return true;
}
Input$UserMutationInput copyWith({String? username, String? password}) =>
Input$UserMutationInput(
username: username == null ? this.username : username,
password: password == null ? this.password : password);
}
enum Enum$DnsProvider {
@JsonValue('CLOUDFLARE')
CLOUDFLARE,
$unknown
}
enum Enum$ServerProvider {
@JsonValue('HETZNER')
HETZNER,
$unknown
}
enum Enum$ServiceStatusEnum {
@JsonValue('ACTIVATING')
ACTIVATING,
@JsonValue('ACTIVE')
ACTIVE,
@JsonValue('DEACTIVATING')
DEACTIVATING,
@JsonValue('FAILED')
FAILED,
@JsonValue('INACTIVE')
INACTIVE,
@JsonValue('OFF')
OFF,
@JsonValue('RELOADING')
RELOADING,
$unknown
}
enum Enum$Severity {
@JsonValue('CRITICAL')
CRITICAL,
@JsonValue('ERROR')
ERROR,
@JsonValue('INFO')
INFO,
@JsonValue('SUCCESS')
SUCCESS,
@JsonValue('WARNING')
WARNING,
$unknown
}
enum Enum$UserType {
@JsonValue('NORMAL')
NORMAL,
@JsonValue('PRIMARY')
PRIMARY,
@JsonValue('ROOT')
ROOT,
$unknown
}
const possibleTypesMap = {
'MutationReturnInterface': {
'ApiKeyMutationReturn',
'AutoUpgradeSettingsMutationReturn',
'DeviceApiTokenMutationReturn',
'GenericJobButationReturn',
'GenericMutationReturn',
'ServiceJobMutationReturn',
'ServiceMutationReturn',
'TimezoneMutationReturn',
'UserMutationReturn'
},
'StorageUsageInterface': {'ServiceStorageUsage'}
};
DateTime? _nullable$dateTimeFromJson(dynamic data) =>
data == null ? null : dateTimeFromJson(data);
dynamic _nullable$dateTimeToJson(DateTime? data) =>
data == null ? null : dateTimeToJson(data);

View File

@ -0,0 +1,125 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'schema.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Input$AutoUpgradeSettingsInput _$Input$AutoUpgradeSettingsInputFromJson(
Map<String, dynamic> json) =>
Input$AutoUpgradeSettingsInput(
enableAutoUpgrade: json['enableAutoUpgrade'] as bool?,
allowReboot: json['allowReboot'] as bool?,
);
Map<String, dynamic> _$Input$AutoUpgradeSettingsInputToJson(
Input$AutoUpgradeSettingsInput instance) =>
<String, dynamic>{
'enableAutoUpgrade': instance.enableAutoUpgrade,
'allowReboot': instance.allowReboot,
};
Input$MigrateToBindsInput _$Input$MigrateToBindsInputFromJson(
Map<String, dynamic> json) =>
Input$MigrateToBindsInput(
emailBlockDevice: json['emailBlockDevice'] as String,
bitwardenBlockDevice: json['bitwardenBlockDevice'] as String,
giteaBlockDevice: json['giteaBlockDevice'] as String,
nextcloudBlockDevice: json['nextcloudBlockDevice'] as String,
pleromaBlockDevice: json['pleromaBlockDevice'] as String,
);
Map<String, dynamic> _$Input$MigrateToBindsInputToJson(
Input$MigrateToBindsInput instance) =>
<String, dynamic>{
'emailBlockDevice': instance.emailBlockDevice,
'bitwardenBlockDevice': instance.bitwardenBlockDevice,
'giteaBlockDevice': instance.giteaBlockDevice,
'nextcloudBlockDevice': instance.nextcloudBlockDevice,
'pleromaBlockDevice': instance.pleromaBlockDevice,
};
Input$MoveServiceInput _$Input$MoveServiceInputFromJson(
Map<String, dynamic> json) =>
Input$MoveServiceInput(
serviceId: json['serviceId'] as String,
location: json['location'] as String,
);
Map<String, dynamic> _$Input$MoveServiceInputToJson(
Input$MoveServiceInput instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
'location': instance.location,
};
Input$RecoveryKeyLimitsInput _$Input$RecoveryKeyLimitsInputFromJson(
Map<String, dynamic> json) =>
Input$RecoveryKeyLimitsInput(
expirationDate: _nullable$dateTimeFromJson(json['expirationDate']),
uses: json['uses'] as int?,
);
Map<String, dynamic> _$Input$RecoveryKeyLimitsInputToJson(
Input$RecoveryKeyLimitsInput instance) =>
<String, dynamic>{
'expirationDate': _nullable$dateTimeToJson(instance.expirationDate),
'uses': instance.uses,
};
Input$SshMutationInput _$Input$SshMutationInputFromJson(
Map<String, dynamic> json) =>
Input$SshMutationInput(
username: json['username'] as String,
sshKey: json['sshKey'] as String,
);
Map<String, dynamic> _$Input$SshMutationInputToJson(
Input$SshMutationInput instance) =>
<String, dynamic>{
'username': instance.username,
'sshKey': instance.sshKey,
};
Input$UseNewDeviceKeyInput _$Input$UseNewDeviceKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseNewDeviceKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);
Map<String, dynamic> _$Input$UseNewDeviceKeyInputToJson(
Input$UseNewDeviceKeyInput instance) =>
<String, dynamic>{
'key': instance.key,
'deviceName': instance.deviceName,
};
Input$UseRecoveryKeyInput _$Input$UseRecoveryKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseRecoveryKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);
Map<String, dynamic> _$Input$UseRecoveryKeyInputToJson(
Input$UseRecoveryKeyInput instance) =>
<String, dynamic>{
'key': instance.key,
'deviceName': instance.deviceName,
};
Input$UserMutationInput _$Input$UserMutationInputFromJson(
Map<String, dynamic> json) =>
Input$UserMutationInput(
username: json['username'] as String,
password: json['password'] as String,
);
Map<String, dynamic> _$Input$UserMutationInputToJson(
Input$UserMutationInput instance) =>
<String, dynamic>{
'username': instance.username,
'password': instance.password,
};

View File

@ -0,0 +1,235 @@
import 'package:graphql/client.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/api_map.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/api_token.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
class ServerApi extends ApiMap {
ServerApi({
this.hasLogger = false,
this.isWithToken = true,
this.customToken = '',
}) {
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
rootAddress = serverDomain?.domainName ?? '';
}
@override
bool hasLogger;
@override
bool isWithToken;
@override
String customToken;
@override
String? rootAddress;
Future<bool> _commonBoolRequest(final Function graphQLMethod) async {
QueryResult response;
bool result = false;
try {
response = await graphQLMethod();
if (response.hasException) {
print(response.exception.toString());
result = false;
} else {
result = true;
}
} catch (e) {
print(e);
}
return result;
}
Future<String?> getApiVersion() async {
QueryResult response;
String? apiVersion;
try {
final GraphQLClient client = await getClient();
response = await client.query$GetApiVersion();
if (response.hasException) {
print(response.exception.toString());
}
apiVersion = response.data!['api']['version'];
} catch (e) {
print(e);
}
return apiVersion;
}
Future<List<ApiToken>> getApiTokens() async {
QueryResult response;
List<ApiToken> tokens = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$GetApiTokens();
if (response.hasException) {
print(response.exception.toString());
}
tokens = response.data!['api']['devices']
.map<ApiToken>((final e) => ApiToken.fromJson(e))
.toList();
} catch (e) {
print(e);
}
return tokens;
}
Future<List<ServerDiskVolume>> getServerDiskVolumes() async {
QueryResult response;
List<ServerDiskVolume> volumes = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$GetServerDiskVolumes();
if (response.hasException) {
print(response.exception.toString());
}
volumes = response.data!['storage']['volumes']
.map<ServerDiskVolume>((final e) => ServerDiskVolume.fromJson(e))
.toList();
} catch (e) {
print(e);
}
return volumes;
}
Future<void> mountVolume(final String volumeName) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$MountVolume(name: volumeName);
final mountVolumeMutation =
Options$Mutation$MountVolume(variables: variables);
await client.mutate$MountVolume(mountVolumeMutation);
} catch (e) {
print(e);
}
}
Future<void> unmountVolume(final String volumeName) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$UnmountVolume(name: volumeName);
final unmountVolumeMutation =
Options$Mutation$UnmountVolume(variables: variables);
await client.mutate$UnmountVolume(unmountVolumeMutation);
} catch (e) {
print(e);
}
}
Future<void> resizeVolume(final String volumeName) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$ResizeVolume(name: volumeName);
final resizeVolumeMutation =
Options$Mutation$ResizeVolume(variables: variables);
await client.mutate$ResizeVolume(resizeVolumeMutation);
} catch (e) {
print(e);
}
}
Future<List<ServerJob>> getServerJobs() async {
QueryResult response;
List<ServerJob> jobs = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$GetApiJobs();
if (response.hasException) {
print(response.exception.toString());
}
jobs = response.data!['jobs']
.map<ServerJob>((final e) => ServerJob.fromJson(e))
.toList();
} catch (e) {
print(e);
}
return jobs;
}
Future<void> removeApiJob(final String uid) async {
try {
final GraphQLClient client = await getClient();
//await client.query$GetApiJobsQuery();
} catch (e) {
print(e);
}
}
Future<bool> reboot() async {
try {
final GraphQLClient client = await getClient();
return await _commonBoolRequest(
() async {
await client.mutate$RebootSystem();
},
);
} catch (e) {
return false;
}
}
Future<bool> pullConfigurationUpdate() async {
try {
final GraphQLClient client = await getClient();
return await _commonBoolRequest(
() async {
await client.mutate$PullRepositoryChanges();
},
);
} catch (e) {
return false;
}
}
Future<bool> upgrade() async {
try {
final GraphQLClient client = await getClient();
return await _commonBoolRequest(
() async {
await client.mutate$RunSystemUpgrade();
},
);
} catch (e) {
return false;
}
}
Future<void> switchService(final String uid, final bool needTurnOn) async {
try {
final GraphQLClient client = await getClient();
if (needTurnOn) {
final variables = Variables$Mutation$EnableService(serviceId: uid);
final mutation = Options$Mutation$EnableService(variables: variables);
await client.mutate$EnableService(mutation);
} else {
final variables = Variables$Mutation$DisableService(serviceId: uid);
final mutation = Options$Mutation$DisableService(variables: variables);
await client.mutate$DisableService(mutation);
}
} catch (e) {
print(e);
}
}
Future<void> apply() async {
try {
final GraphQLClient client = await getClient();
await client.mutate$RunSystemRebuild();
} catch (e) {
print(e);
}
}
}

View File

@ -0,0 +1,134 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
query GetApiVersion {
api {
version
}
}
query GetApiJobs {
jobs {
getJobs {
createdAt
description
error
finishedAt
name
progress
result
status
statusText
uid
updatedAt
}
}
}
mutation RemoveJob($jobId: String!) {
removeJob(jobId: $jobId) {
...basicMutationReturnFields
}
}
mutation RunSystemRebuild {
runSystemRebuild {
...basicMutationReturnFields
}
}
mutation RunSystemRollback {
runSystemRollback {
...basicMutationReturnFields
}
}
mutation RunSystemUpgrade {
runSystemUpgrade {
...basicMutationReturnFields
}
}
mutation PullRepositoryChanges {
pullRepositoryChanges {
...basicMutationReturnFields
}
}
mutation RebootSystem {
rebootSystem {
...basicMutationReturnFields
}
}
query GetApiTokens {
api {
devices {
creationDate
isCaller
name
}
}
}
query RecoveryKey {
api {
recoveryKey {
creationDate
exists
expirationDate
usesLeft
valid
}
}
}
mutation GetNewRecoveryApiKey($limits: RecoveryKeyLimitsInput) {
getNewRecoveryApiKey(limits: $limits) {
...basicMutationReturnFields
key
}
}
mutation UseRecoveryApiKey($input: UseRecoveryKeyInput!) {
useRecoveryApiKey(input: $input) {
...basicMutationReturnFields
token
}
}
mutation RefreshDeviceApiToken {
refreshDeviceApiToken {
...basicMutationReturnFields
token
}
}
mutation DeleteDeviceApiToken($device: String!) {
deleteDeviceApiToken(device: $device) {
...basicMutationReturnFields
}
}
mutation GetNewDeviceApiKey {
getNewDeviceApiKey {
...basicMutationReturnFields
key
}
}
mutation InvalidateNewDeviceApiKey {
invalidateNewDeviceApiKey {
...basicMutationReturnFields
}
}
mutation AuthorizeWithNewDeviceApiKey($input: UseNewDeviceKeyInput!) {
authorizeWithNewDeviceApiKey(input: $input) {
...basicMutationReturnFields
token
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,745 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_api.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$GetApiVersion _$Query$GetApiVersionFromJson(Map<String, dynamic> json) =>
Query$GetApiVersion(
api:
Query$GetApiVersion$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiVersionToJson(
Query$GetApiVersion instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiVersion$api _$Query$GetApiVersion$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiVersion$api(
version: json['version'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiVersion$apiToJson(
Query$GetApiVersion$api instance) =>
<String, dynamic>{
'version': instance.version,
'__typename': instance.$__typename,
};
Query$GetApiJobs _$Query$GetApiJobsFromJson(Map<String, dynamic> json) =>
Query$GetApiJobs(
jobs:
Query$GetApiJobs$jobs.fromJson(json['jobs'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobsToJson(Query$GetApiJobs instance) =>
<String, dynamic>{
'jobs': instance.jobs.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiJobs$jobs _$Query$GetApiJobs$jobsFromJson(
Map<String, dynamic> json) =>
Query$GetApiJobs$jobs(
getJobs: (json['getJobs'] as List<dynamic>)
.map((e) =>
Query$GetApiJobs$jobs$getJobs.fromJson(e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobs$jobsToJson(
Query$GetApiJobs$jobs instance) =>
<String, dynamic>{
'getJobs': instance.getJobs.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetApiJobs$jobs$getJobs _$Query$GetApiJobs$jobs$getJobsFromJson(
Map<String, dynamic> json) =>
Query$GetApiJobs$jobs$getJobs(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiJobs$jobs$getJobsToJson(
Query$GetApiJobs$jobs$getJobs instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};
Variables$Mutation$RemoveJob _$Variables$Mutation$RemoveJobFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RemoveJob(
jobId: json['jobId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$RemoveJobToJson(
Variables$Mutation$RemoveJob instance) =>
<String, dynamic>{
'jobId': instance.jobId,
};
Mutation$RemoveJob _$Mutation$RemoveJobFromJson(Map<String, dynamic> json) =>
Mutation$RemoveJob(
removeJob: Mutation$RemoveJob$removeJob.fromJson(
json['removeJob'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveJobToJson(Mutation$RemoveJob instance) =>
<String, dynamic>{
'removeJob': instance.removeJob.toJson(),
'__typename': instance.$__typename,
};
Mutation$RemoveJob$removeJob _$Mutation$RemoveJob$removeJobFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveJob$removeJob(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveJob$removeJobToJson(
Mutation$RemoveJob$removeJob instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemRebuild _$Mutation$RunSystemRebuildFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRebuild(
runSystemRebuild: Mutation$RunSystemRebuild$runSystemRebuild.fromJson(
json['runSystemRebuild'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRebuildToJson(
Mutation$RunSystemRebuild instance) =>
<String, dynamic>{
'runSystemRebuild': instance.runSystemRebuild.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemRebuild$runSystemRebuild
_$Mutation$RunSystemRebuild$runSystemRebuildFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRebuild$runSystemRebuild(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRebuild$runSystemRebuildToJson(
Mutation$RunSystemRebuild$runSystemRebuild instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemRollback _$Mutation$RunSystemRollbackFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRollback(
runSystemRollback: Mutation$RunSystemRollback$runSystemRollback.fromJson(
json['runSystemRollback'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRollbackToJson(
Mutation$RunSystemRollback instance) =>
<String, dynamic>{
'runSystemRollback': instance.runSystemRollback.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemRollback$runSystemRollback
_$Mutation$RunSystemRollback$runSystemRollbackFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemRollback$runSystemRollback(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemRollback$runSystemRollbackToJson(
Mutation$RunSystemRollback$runSystemRollback instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RunSystemUpgrade _$Mutation$RunSystemUpgradeFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemUpgrade(
runSystemUpgrade: Mutation$RunSystemUpgrade$runSystemUpgrade.fromJson(
json['runSystemUpgrade'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemUpgradeToJson(
Mutation$RunSystemUpgrade instance) =>
<String, dynamic>{
'runSystemUpgrade': instance.runSystemUpgrade.toJson(),
'__typename': instance.$__typename,
};
Mutation$RunSystemUpgrade$runSystemUpgrade
_$Mutation$RunSystemUpgrade$runSystemUpgradeFromJson(
Map<String, dynamic> json) =>
Mutation$RunSystemUpgrade$runSystemUpgrade(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RunSystemUpgrade$runSystemUpgradeToJson(
Mutation$RunSystemUpgrade$runSystemUpgrade instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$PullRepositoryChanges _$Mutation$PullRepositoryChangesFromJson(
Map<String, dynamic> json) =>
Mutation$PullRepositoryChanges(
pullRepositoryChanges:
Mutation$PullRepositoryChanges$pullRepositoryChanges.fromJson(
json['pullRepositoryChanges'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$PullRepositoryChangesToJson(
Mutation$PullRepositoryChanges instance) =>
<String, dynamic>{
'pullRepositoryChanges': instance.pullRepositoryChanges.toJson(),
'__typename': instance.$__typename,
};
Mutation$PullRepositoryChanges$pullRepositoryChanges
_$Mutation$PullRepositoryChanges$pullRepositoryChangesFromJson(
Map<String, dynamic> json) =>
Mutation$PullRepositoryChanges$pullRepositoryChanges(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Mutation$PullRepositoryChanges$pullRepositoryChangesToJson(
Mutation$PullRepositoryChanges$pullRepositoryChanges instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$RebootSystem _$Mutation$RebootSystemFromJson(
Map<String, dynamic> json) =>
Mutation$RebootSystem(
rebootSystem: Mutation$RebootSystem$rebootSystem.fromJson(
json['rebootSystem'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RebootSystemToJson(
Mutation$RebootSystem instance) =>
<String, dynamic>{
'rebootSystem': instance.rebootSystem.toJson(),
'__typename': instance.$__typename,
};
Mutation$RebootSystem$rebootSystem _$Mutation$RebootSystem$rebootSystemFromJson(
Map<String, dynamic> json) =>
Mutation$RebootSystem$rebootSystem(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RebootSystem$rebootSystemToJson(
Mutation$RebootSystem$rebootSystem instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$GetApiTokens _$Query$GetApiTokensFromJson(Map<String, dynamic> json) =>
Query$GetApiTokens(
api: Query$GetApiTokens$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokensToJson(Query$GetApiTokens instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$GetApiTokens$api _$Query$GetApiTokens$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokens$api(
devices: (json['devices'] as List<dynamic>)
.map((e) => Query$GetApiTokens$api$devices.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokens$apiToJson(
Query$GetApiTokens$api instance) =>
<String, dynamic>{
'devices': instance.devices.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$GetApiTokens$api$devices _$Query$GetApiTokens$api$devicesFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokens$api$devices(
creationDate: dateTimeFromJson(json['creationDate']),
isCaller: json['isCaller'] as bool,
name: json['name'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetApiTokens$api$devicesToJson(
Query$GetApiTokens$api$devices instance) =>
<String, dynamic>{
'creationDate': dateTimeToJson(instance.creationDate),
'isCaller': instance.isCaller,
'name': instance.name,
'__typename': instance.$__typename,
};
Query$RecoveryKey _$Query$RecoveryKeyFromJson(Map<String, dynamic> json) =>
Query$RecoveryKey(
api: Query$RecoveryKey$api.fromJson(json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKeyToJson(Query$RecoveryKey instance) =>
<String, dynamic>{
'api': instance.api.toJson(),
'__typename': instance.$__typename,
};
Query$RecoveryKey$api _$Query$RecoveryKey$apiFromJson(
Map<String, dynamic> json) =>
Query$RecoveryKey$api(
recoveryKey: Query$RecoveryKey$api$recoveryKey.fromJson(
json['recoveryKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKey$apiToJson(
Query$RecoveryKey$api instance) =>
<String, dynamic>{
'recoveryKey': instance.recoveryKey.toJson(),
'__typename': instance.$__typename,
};
Query$RecoveryKey$api$recoveryKey _$Query$RecoveryKey$api$recoveryKeyFromJson(
Map<String, dynamic> json) =>
Query$RecoveryKey$api$recoveryKey(
creationDate: _nullable$dateTimeFromJson(json['creationDate']),
exists: json['exists'] as bool,
expirationDate: _nullable$dateTimeFromJson(json['expirationDate']),
usesLeft: json['usesLeft'] as int?,
valid: json['valid'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$RecoveryKey$api$recoveryKeyToJson(
Query$RecoveryKey$api$recoveryKey instance) =>
<String, dynamic>{
'creationDate': _nullable$dateTimeToJson(instance.creationDate),
'exists': instance.exists,
'expirationDate': _nullable$dateTimeToJson(instance.expirationDate),
'usesLeft': instance.usesLeft,
'valid': instance.valid,
'__typename': instance.$__typename,
};
Variables$Mutation$GetNewRecoveryApiKey
_$Variables$Mutation$GetNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$GetNewRecoveryApiKey(
limits: json['limits'] == null
? null
: Input$RecoveryKeyLimitsInput.fromJson(
json['limits'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$GetNewRecoveryApiKeyToJson(
Variables$Mutation$GetNewRecoveryApiKey instance) =>
<String, dynamic>{
'limits': instance.limits?.toJson(),
};
Mutation$GetNewRecoveryApiKey _$Mutation$GetNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewRecoveryApiKey(
getNewRecoveryApiKey:
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.fromJson(
json['getNewRecoveryApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$GetNewRecoveryApiKeyToJson(
Mutation$GetNewRecoveryApiKey instance) =>
<String, dynamic>{
'getNewRecoveryApiKey': instance.getNewRecoveryApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey
_$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
key: json['key'] as String?,
);
Map<String, dynamic> _$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKeyToJson(
Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'key': instance.key,
};
Variables$Mutation$UseRecoveryApiKey
_$Variables$Mutation$UseRecoveryApiKeyFromJson(Map<String, dynamic> json) =>
Variables$Mutation$UseRecoveryApiKey(
input: Input$UseRecoveryKeyInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$UseRecoveryApiKeyToJson(
Variables$Mutation$UseRecoveryApiKey instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$UseRecoveryApiKey _$Mutation$UseRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$UseRecoveryApiKey(
useRecoveryApiKey: Mutation$UseRecoveryApiKey$useRecoveryApiKey.fromJson(
json['useRecoveryApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UseRecoveryApiKeyToJson(
Mutation$UseRecoveryApiKey instance) =>
<String, dynamic>{
'useRecoveryApiKey': instance.useRecoveryApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$UseRecoveryApiKey$useRecoveryApiKey
_$Mutation$UseRecoveryApiKey$useRecoveryApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$UseRecoveryApiKey$useRecoveryApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic> _$Mutation$UseRecoveryApiKey$useRecoveryApiKeyToJson(
Mutation$UseRecoveryApiKey$useRecoveryApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};
Mutation$RefreshDeviceApiToken _$Mutation$RefreshDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$RefreshDeviceApiToken(
refreshDeviceApiToken:
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.fromJson(
json['refreshDeviceApiToken'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RefreshDeviceApiTokenToJson(
Mutation$RefreshDeviceApiToken instance) =>
<String, dynamic>{
'refreshDeviceApiToken': instance.refreshDeviceApiToken.toJson(),
'__typename': instance.$__typename,
};
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken
_$Mutation$RefreshDeviceApiToken$refreshDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic>
_$Mutation$RefreshDeviceApiToken$refreshDeviceApiTokenToJson(
Mutation$RefreshDeviceApiToken$refreshDeviceApiToken instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};
Variables$Mutation$DeleteDeviceApiToken
_$Variables$Mutation$DeleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DeleteDeviceApiToken(
device: json['device'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DeleteDeviceApiTokenToJson(
Variables$Mutation$DeleteDeviceApiToken instance) =>
<String, dynamic>{
'device': instance.device,
};
Mutation$DeleteDeviceApiToken _$Mutation$DeleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteDeviceApiToken(
deleteDeviceApiToken:
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.fromJson(
json['deleteDeviceApiToken'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteDeviceApiTokenToJson(
Mutation$DeleteDeviceApiToken instance) =>
<String, dynamic>{
'deleteDeviceApiToken': instance.deleteDeviceApiToken.toJson(),
'__typename': instance.$__typename,
};
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken
_$Mutation$DeleteDeviceApiToken$deleteDeviceApiTokenFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteDeviceApiToken$deleteDeviceApiTokenToJson(
Mutation$DeleteDeviceApiToken$deleteDeviceApiToken instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Mutation$GetNewDeviceApiKey _$Mutation$GetNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewDeviceApiKey(
getNewDeviceApiKey:
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.fromJson(
json['getNewDeviceApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$GetNewDeviceApiKeyToJson(
Mutation$GetNewDeviceApiKey instance) =>
<String, dynamic>{
'getNewDeviceApiKey': instance.getNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey
_$Mutation$GetNewDeviceApiKey$getNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
key: json['key'] as String?,
);
Map<String, dynamic> _$Mutation$GetNewDeviceApiKey$getNewDeviceApiKeyToJson(
Mutation$GetNewDeviceApiKey$getNewDeviceApiKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'key': instance.key,
};
Mutation$InvalidateNewDeviceApiKey _$Mutation$InvalidateNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$InvalidateNewDeviceApiKey(
invalidateNewDeviceApiKey:
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey.fromJson(
json['invalidateNewDeviceApiKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$InvalidateNewDeviceApiKeyToJson(
Mutation$InvalidateNewDeviceApiKey instance) =>
<String, dynamic>{
'invalidateNewDeviceApiKey': instance.invalidateNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey
_$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKeyToJson(
Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$AuthorizeWithNewDeviceApiKey
_$Variables$Mutation$AuthorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$AuthorizeWithNewDeviceApiKey(
input: Input$UseNewDeviceKeyInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$AuthorizeWithNewDeviceApiKeyToJson(
Variables$Mutation$AuthorizeWithNewDeviceApiKey instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$AuthorizeWithNewDeviceApiKey
_$Mutation$AuthorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AuthorizeWithNewDeviceApiKey(
authorizeWithNewDeviceApiKey:
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
.fromJson(json['authorizeWithNewDeviceApiKey']
as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$AuthorizeWithNewDeviceApiKeyToJson(
Mutation$AuthorizeWithNewDeviceApiKey instance) =>
<String, dynamic>{
'authorizeWithNewDeviceApiKey':
instance.authorizeWithNewDeviceApiKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
_$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
token: json['token'] as String?,
);
Map<String, dynamic>
_$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKeyToJson(
Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'token': instance.token,
};

View File

@ -0,0 +1,54 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
query SystemSettings {
system {
settings {
autoUpgrade {
allowReboot
enable
}
ssh {
enable
passwordAuthentication
rootSshKeys
}
timezone
}
}
}
query DomainInfo {
system {
domainInfo {
domain
hostname
provider
requiredDnsRecords {
content
name
priority
recordType
ttl
}
}
}
}
mutation ChangeTimezone($timezone: String!) {
changeTimezone(timezone: $timezone) {
...basicMutationReturnFields
timezone
}
}
mutation ChangeAutoUpgradeSettings($settings: AutoUpgradeSettingsInput!) {
changeAutoUpgradeSettings(settings: $settings) {
...basicMutationReturnFields
allowReboot
enableAutoUpgrade
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,300 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_settings.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$SystemSettings _$Query$SystemSettingsFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings(
system: Query$SystemSettings$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettingsToJson(
Query$SystemSettings instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$SystemSettings$system _$Query$SystemSettings$systemFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system(
settings: Query$SystemSettings$system$settings.fromJson(
json['settings'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$systemToJson(
Query$SystemSettings$system instance) =>
<String, dynamic>{
'settings': instance.settings.toJson(),
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings
_$Query$SystemSettings$system$settingsFromJson(Map<String, dynamic> json) =>
Query$SystemSettings$system$settings(
autoUpgrade:
Query$SystemSettings$system$settings$autoUpgrade.fromJson(
json['autoUpgrade'] as Map<String, dynamic>),
ssh: Query$SystemSettings$system$settings$ssh.fromJson(
json['ssh'] as Map<String, dynamic>),
timezone: json['timezone'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settingsToJson(
Query$SystemSettings$system$settings instance) =>
<String, dynamic>{
'autoUpgrade': instance.autoUpgrade.toJson(),
'ssh': instance.ssh.toJson(),
'timezone': instance.timezone,
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings$autoUpgrade
_$Query$SystemSettings$system$settings$autoUpgradeFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system$settings$autoUpgrade(
allowReboot: json['allowReboot'] as bool,
enable: json['enable'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settings$autoUpgradeToJson(
Query$SystemSettings$system$settings$autoUpgrade instance) =>
<String, dynamic>{
'allowReboot': instance.allowReboot,
'enable': instance.enable,
'__typename': instance.$__typename,
};
Query$SystemSettings$system$settings$ssh
_$Query$SystemSettings$system$settings$sshFromJson(
Map<String, dynamic> json) =>
Query$SystemSettings$system$settings$ssh(
enable: json['enable'] as bool,
passwordAuthentication: json['passwordAuthentication'] as bool,
rootSshKeys: (json['rootSshKeys'] as List<dynamic>)
.map((e) => e as String)
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemSettings$system$settings$sshToJson(
Query$SystemSettings$system$settings$ssh instance) =>
<String, dynamic>{
'enable': instance.enable,
'passwordAuthentication': instance.passwordAuthentication,
'rootSshKeys': instance.rootSshKeys,
'__typename': instance.$__typename,
};
Query$DomainInfo _$Query$DomainInfoFromJson(Map<String, dynamic> json) =>
Query$DomainInfo(
system: Query$DomainInfo$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfoToJson(Query$DomainInfo instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$DomainInfo$system _$Query$DomainInfo$systemFromJson(
Map<String, dynamic> json) =>
Query$DomainInfo$system(
domainInfo: Query$DomainInfo$system$domainInfo.fromJson(
json['domainInfo'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfo$systemToJson(
Query$DomainInfo$system instance) =>
<String, dynamic>{
'domainInfo': instance.domainInfo.toJson(),
'__typename': instance.$__typename,
};
Query$DomainInfo$system$domainInfo _$Query$DomainInfo$system$domainInfoFromJson(
Map<String, dynamic> json) =>
Query$DomainInfo$system$domainInfo(
domain: json['domain'] as String,
hostname: json['hostname'] as String,
provider: $enumDecode(_$Enum$DnsProviderEnumMap, json['provider'],
unknownValue: Enum$DnsProvider.$unknown),
requiredDnsRecords: (json['requiredDnsRecords'] as List<dynamic>)
.map((e) =>
Query$DomainInfo$system$domainInfo$requiredDnsRecords.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$DomainInfo$system$domainInfoToJson(
Query$DomainInfo$system$domainInfo instance) =>
<String, dynamic>{
'domain': instance.domain,
'hostname': instance.hostname,
'provider': _$Enum$DnsProviderEnumMap[instance.provider],
'requiredDnsRecords':
instance.requiredDnsRecords.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
const _$Enum$DnsProviderEnumMap = {
Enum$DnsProvider.CLOUDFLARE: 'CLOUDFLARE',
Enum$DnsProvider.$unknown: r'$unknown',
};
Query$DomainInfo$system$domainInfo$requiredDnsRecords
_$Query$DomainInfo$system$domainInfo$requiredDnsRecordsFromJson(
Map<String, dynamic> json) =>
Query$DomainInfo$system$domainInfo$requiredDnsRecords(
content: json['content'] as String,
name: json['name'] as String,
priority: json['priority'] as int?,
recordType: json['recordType'] as String,
ttl: json['ttl'] as int,
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Query$DomainInfo$system$domainInfo$requiredDnsRecordsToJson(
Query$DomainInfo$system$domainInfo$requiredDnsRecords instance) =>
<String, dynamic>{
'content': instance.content,
'name': instance.name,
'priority': instance.priority,
'recordType': instance.recordType,
'ttl': instance.ttl,
'__typename': instance.$__typename,
};
Variables$Mutation$ChangeTimezone _$Variables$Mutation$ChangeTimezoneFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ChangeTimezone(
timezone: json['timezone'] as String,
);
Map<String, dynamic> _$Variables$Mutation$ChangeTimezoneToJson(
Variables$Mutation$ChangeTimezone instance) =>
<String, dynamic>{
'timezone': instance.timezone,
};
Mutation$ChangeTimezone _$Mutation$ChangeTimezoneFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeTimezone(
changeTimezone: Mutation$ChangeTimezone$changeTimezone.fromJson(
json['changeTimezone'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ChangeTimezoneToJson(
Mutation$ChangeTimezone instance) =>
<String, dynamic>{
'changeTimezone': instance.changeTimezone.toJson(),
'__typename': instance.$__typename,
};
Mutation$ChangeTimezone$changeTimezone
_$Mutation$ChangeTimezone$changeTimezoneFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeTimezone$changeTimezone(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
timezone: json['timezone'] as String?,
);
Map<String, dynamic> _$Mutation$ChangeTimezone$changeTimezoneToJson(
Mutation$ChangeTimezone$changeTimezone instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'timezone': instance.timezone,
};
Variables$Mutation$ChangeAutoUpgradeSettings
_$Variables$Mutation$ChangeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$ChangeAutoUpgradeSettings(
settings: Input$AutoUpgradeSettingsInput.fromJson(
json['settings'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$ChangeAutoUpgradeSettingsToJson(
Variables$Mutation$ChangeAutoUpgradeSettings instance) =>
<String, dynamic>{
'settings': instance.settings.toJson(),
};
Mutation$ChangeAutoUpgradeSettings _$Mutation$ChangeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeAutoUpgradeSettings(
changeAutoUpgradeSettings:
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings.fromJson(
json['changeAutoUpgradeSettings'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$ChangeAutoUpgradeSettingsToJson(
Mutation$ChangeAutoUpgradeSettings instance) =>
<String, dynamic>{
'changeAutoUpgradeSettings': instance.changeAutoUpgradeSettings.toJson(),
'__typename': instance.$__typename,
};
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings
_$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettingsFromJson(
Map<String, dynamic> json) =>
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
allowReboot: json['allowReboot'] as bool,
enableAutoUpgrade: json['enableAutoUpgrade'] as bool,
);
Map<String, dynamic>
_$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettingsToJson(
Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings
instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'allowReboot': instance.allowReboot,
'enableAutoUpgrade': instance.enableAutoUpgrade,
};

View File

@ -0,0 +1,84 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
query AllServices {
services {
allServices {
description
displayName
dnsRecords {
content
name
priority
recordType
ttl
}
id
isEnabled
isMovable
isRequired
status
storageUsage {
title
usedSpace
volume {
name
}
}
svgIcon
url
}
}
}
mutation EnableService($serviceId: String!) {
enableService(serviceId: $serviceId) {
...basicMutationReturnFields
}
}
mutation DisableService($serviceId: String!) {
disableService(serviceId: $serviceId) {
...basicMutationReturnFields
}
}
mutation StopService($serviceId: String!) {
stopService(serviceId: $serviceId) {
...basicMutationReturnFields
}
}
mutation StartService($serviceId: String!) {
startService(serviceId: $serviceId) {
...basicMutationReturnFields
}
}
mutation RestartService($serviceId: String!) {
restartService(serviceId: $serviceId) {
...basicMutationReturnFields
}
}
mutation MoveService($input: MoveServiceInput!) {
moveService(input: $input) {
...basicMutationReturnFields
job {
createdAt
description
error
finishedAt
name
progress
result
status
statusText
uid
updatedAt
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,482 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'services.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Query$AllServices _$Query$AllServicesFromJson(Map<String, dynamic> json) =>
Query$AllServices(
services: Query$AllServices$services.fromJson(
json['services'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServicesToJson(Query$AllServices instance) =>
<String, dynamic>{
'services': instance.services.toJson(),
'__typename': instance.$__typename,
};
Query$AllServices$services _$Query$AllServices$servicesFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services(
allServices: (json['allServices'] as List<dynamic>)
.map((e) => Query$AllServices$services$allServices.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServices$servicesToJson(
Query$AllServices$services instance) =>
<String, dynamic>{
'allServices': instance.allServices.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$AllServices$services$allServices
_$Query$AllServices$services$allServicesFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices(
description: json['description'] as String,
displayName: json['displayName'] as String,
dnsRecords: (json['dnsRecords'] as List<dynamic>?)
?.map((e) =>
Query$AllServices$services$allServices$dnsRecords.fromJson(
e as Map<String, dynamic>))
.toList(),
id: json['id'] as String,
isEnabled: json['isEnabled'] as bool,
isMovable: json['isMovable'] as bool,
isRequired: json['isRequired'] as bool,
status: $enumDecode(_$Enum$ServiceStatusEnumEnumMap, json['status'],
unknownValue: Enum$ServiceStatusEnum.$unknown),
storageUsage:
Query$AllServices$services$allServices$storageUsage.fromJson(
json['storageUsage'] as Map<String, dynamic>),
svgIcon: json['svgIcon'] as String,
url: json['url'] as String?,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServices$services$allServicesToJson(
Query$AllServices$services$allServices instance) =>
<String, dynamic>{
'description': instance.description,
'displayName': instance.displayName,
'dnsRecords': instance.dnsRecords?.map((e) => e.toJson()).toList(),
'id': instance.id,
'isEnabled': instance.isEnabled,
'isMovable': instance.isMovable,
'isRequired': instance.isRequired,
'status': _$Enum$ServiceStatusEnumEnumMap[instance.status],
'storageUsage': instance.storageUsage.toJson(),
'svgIcon': instance.svgIcon,
'url': instance.url,
'__typename': instance.$__typename,
};
const _$Enum$ServiceStatusEnumEnumMap = {
Enum$ServiceStatusEnum.ACTIVATING: 'ACTIVATING',
Enum$ServiceStatusEnum.ACTIVE: 'ACTIVE',
Enum$ServiceStatusEnum.DEACTIVATING: 'DEACTIVATING',
Enum$ServiceStatusEnum.FAILED: 'FAILED',
Enum$ServiceStatusEnum.INACTIVE: 'INACTIVE',
Enum$ServiceStatusEnum.OFF: 'OFF',
Enum$ServiceStatusEnum.RELOADING: 'RELOADING',
Enum$ServiceStatusEnum.$unknown: r'$unknown',
};
Query$AllServices$services$allServices$dnsRecords
_$Query$AllServices$services$allServices$dnsRecordsFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices$dnsRecords(
content: json['content'] as String,
name: json['name'] as String,
priority: json['priority'] as int?,
recordType: json['recordType'] as String,
ttl: json['ttl'] as int,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllServices$services$allServices$dnsRecordsToJson(
Query$AllServices$services$allServices$dnsRecords instance) =>
<String, dynamic>{
'content': instance.content,
'name': instance.name,
'priority': instance.priority,
'recordType': instance.recordType,
'ttl': instance.ttl,
'__typename': instance.$__typename,
};
Query$AllServices$services$allServices$storageUsage
_$Query$AllServices$services$allServices$storageUsageFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices$storageUsage(
title: json['title'] as String,
usedSpace: json['usedSpace'] as String,
volume: json['volume'] == null
? null
: Query$AllServices$services$allServices$storageUsage$volume
.fromJson(json['volume'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic>
_$Query$AllServices$services$allServices$storageUsageToJson(
Query$AllServices$services$allServices$storageUsage instance) =>
<String, dynamic>{
'title': instance.title,
'usedSpace': instance.usedSpace,
'volume': instance.volume?.toJson(),
'__typename': instance.$__typename,
};
Query$AllServices$services$allServices$storageUsage$volume
_$Query$AllServices$services$allServices$storageUsage$volumeFromJson(
Map<String, dynamic> json) =>
Query$AllServices$services$allServices$storageUsage$volume(
name: json['name'] as String,
$__typename: json['__typename'] as String,
);
Map<String,
dynamic> _$Query$AllServices$services$allServices$storageUsage$volumeToJson(
Query$AllServices$services$allServices$storageUsage$volume instance) =>
<String, dynamic>{
'name': instance.name,
'__typename': instance.$__typename,
};
Variables$Mutation$EnableService _$Variables$Mutation$EnableServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$EnableService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$EnableServiceToJson(
Variables$Mutation$EnableService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$EnableService _$Mutation$EnableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$EnableService(
enableService: Mutation$EnableService$enableService.fromJson(
json['enableService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$EnableServiceToJson(
Mutation$EnableService instance) =>
<String, dynamic>{
'enableService': instance.enableService.toJson(),
'__typename': instance.$__typename,
};
Mutation$EnableService$enableService
_$Mutation$EnableService$enableServiceFromJson(Map<String, dynamic> json) =>
Mutation$EnableService$enableService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$EnableService$enableServiceToJson(
Mutation$EnableService$enableService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$DisableService _$Variables$Mutation$DisableServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DisableService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DisableServiceToJson(
Variables$Mutation$DisableService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$DisableService _$Mutation$DisableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$DisableService(
disableService: Mutation$DisableService$disableService.fromJson(
json['disableService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DisableServiceToJson(
Mutation$DisableService instance) =>
<String, dynamic>{
'disableService': instance.disableService.toJson(),
'__typename': instance.$__typename,
};
Mutation$DisableService$disableService
_$Mutation$DisableService$disableServiceFromJson(
Map<String, dynamic> json) =>
Mutation$DisableService$disableService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DisableService$disableServiceToJson(
Mutation$DisableService$disableService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$StopService _$Variables$Mutation$StopServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$StopService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$StopServiceToJson(
Variables$Mutation$StopService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$StopService _$Mutation$StopServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StopService(
stopService: Mutation$StopService$stopService.fromJson(
json['stopService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StopServiceToJson(
Mutation$StopService instance) =>
<String, dynamic>{
'stopService': instance.stopService.toJson(),
'__typename': instance.$__typename,
};
Mutation$StopService$stopService _$Mutation$StopService$stopServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StopService$stopService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StopService$stopServiceToJson(
Mutation$StopService$stopService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$StartService _$Variables$Mutation$StartServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$StartService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$StartServiceToJson(
Variables$Mutation$StartService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$StartService _$Mutation$StartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StartService(
startService: Mutation$StartService$startService.fromJson(
json['startService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StartServiceToJson(
Mutation$StartService instance) =>
<String, dynamic>{
'startService': instance.startService.toJson(),
'__typename': instance.$__typename,
};
Mutation$StartService$startService _$Mutation$StartService$startServiceFromJson(
Map<String, dynamic> json) =>
Mutation$StartService$startService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$StartService$startServiceToJson(
Mutation$StartService$startService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$RestartService _$Variables$Mutation$RestartServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RestartService(
serviceId: json['serviceId'] as String,
);
Map<String, dynamic> _$Variables$Mutation$RestartServiceToJson(
Variables$Mutation$RestartService instance) =>
<String, dynamic>{
'serviceId': instance.serviceId,
};
Mutation$RestartService _$Mutation$RestartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$RestartService(
restartService: Mutation$RestartService$restartService.fromJson(
json['restartService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RestartServiceToJson(
Mutation$RestartService instance) =>
<String, dynamic>{
'restartService': instance.restartService.toJson(),
'__typename': instance.$__typename,
};
Mutation$RestartService$restartService
_$Mutation$RestartService$restartServiceFromJson(
Map<String, dynamic> json) =>
Mutation$RestartService$restartService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RestartService$restartServiceToJson(
Mutation$RestartService$restartService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$MoveService _$Variables$Mutation$MoveServiceFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$MoveService(
input: Input$MoveServiceInput.fromJson(
json['input'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$MoveServiceToJson(
Variables$Mutation$MoveService instance) =>
<String, dynamic>{
'input': instance.input.toJson(),
};
Mutation$MoveService _$Mutation$MoveServiceFromJson(
Map<String, dynamic> json) =>
Mutation$MoveService(
moveService: Mutation$MoveService$moveService.fromJson(
json['moveService'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MoveServiceToJson(
Mutation$MoveService instance) =>
<String, dynamic>{
'moveService': instance.moveService.toJson(),
'__typename': instance.$__typename,
};
Mutation$MoveService$moveService _$Mutation$MoveService$moveServiceFromJson(
Map<String, dynamic> json) =>
Mutation$MoveService$moveService(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
job: json['job'] == null
? null
: Mutation$MoveService$moveService$job.fromJson(
json['job'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$MoveService$moveServiceToJson(
Mutation$MoveService$moveService instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'job': instance.job?.toJson(),
};
Mutation$MoveService$moveService$job
_$Mutation$MoveService$moveService$jobFromJson(Map<String, dynamic> json) =>
Mutation$MoveService$moveService$job(
createdAt: dateTimeFromJson(json['createdAt']),
description: json['description'] as String,
error: json['error'] as String?,
finishedAt: _nullable$dateTimeFromJson(json['finishedAt']),
name: json['name'] as String,
progress: json['progress'] as int?,
result: json['result'] as String?,
status: json['status'] as String,
statusText: json['statusText'] as String?,
uid: json['uid'] as String,
updatedAt: dateTimeFromJson(json['updatedAt']),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$MoveService$moveService$jobToJson(
Mutation$MoveService$moveService$job instance) =>
<String, dynamic>{
'createdAt': dateTimeToJson(instance.createdAt),
'description': instance.description,
'error': instance.error,
'finishedAt': _nullable$dateTimeToJson(instance.finishedAt),
'name': instance.name,
'progress': instance.progress,
'result': instance.result,
'status': instance.status,
'statusText': instance.statusText,
'uid': instance.uid,
'updatedAt': dateTimeToJson(instance.updatedAt),
'__typename': instance.$__typename,
};

View File

@ -0,0 +1,76 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
mutation CreateUser($user: UserMutationInput!) {
createUser(user: $user) {
...basicMutationReturnFields
user {
username
userType
sshKeys
}
}
}
query AllUsers {
users {
allUsers {
userType
username
sshKeys
}
}
}
mutation AddSshKey($sshInput: SshMutationInput!) {
addSshKey(sshInput: $sshInput) {
...basicMutationReturnFields
user {
sshKeys
userType
username
}
}
}
query GetUser($username: String!) {
users {
getUser(username: $username) {
sshKeys
userType
username
}
}
}
mutation RemoveSshKey($sshInput: SshMutationInput!) {
removeSshKey(sshInput: $sshInput) {
...basicMutationReturnFields
user {
sshKeys
userType
username
}
}
}
mutation DeleteUser($username: String!) {
deleteUser(username: $username) {
...basicMutationReturnFields
}
}
mutation UpdateUser($user: UserMutationInput!) {
updateUser(user: $user) {
...basicMutationReturnFields
user {
sshKeys
userType
username
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,471 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'users.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Fragment$basicMutationReturnFields _$Fragment$basicMutationReturnFieldsFromJson(
Map<String, dynamic> json) =>
Fragment$basicMutationReturnFields(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Fragment$basicMutationReturnFieldsToJson(
Fragment$basicMutationReturnFields instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$CreateUser _$Variables$Mutation$CreateUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$CreateUser(
user: Input$UserMutationInput.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$CreateUserToJson(
Variables$Mutation$CreateUser instance) =>
<String, dynamic>{
'user': instance.user.toJson(),
};
Mutation$CreateUser _$Mutation$CreateUserFromJson(Map<String, dynamic> json) =>
Mutation$CreateUser(
createUser: Mutation$CreateUser$createUser.fromJson(
json['createUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$CreateUserToJson(
Mutation$CreateUser instance) =>
<String, dynamic>{
'createUser': instance.createUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$CreateUser$createUser _$Mutation$CreateUser$createUserFromJson(
Map<String, dynamic> json) =>
Mutation$CreateUser$createUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Mutation$CreateUser$createUser$user.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$CreateUser$createUserToJson(
Mutation$CreateUser$createUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Mutation$CreateUser$createUser$user
_$Mutation$CreateUser$createUser$userFromJson(Map<String, dynamic> json) =>
Mutation$CreateUser$createUser$user(
username: json['username'] as String,
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
sshKeys: (json['sshKeys'] as List<dynamic>)
.map((e) => e as String)
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$CreateUser$createUser$userToJson(
Mutation$CreateUser$createUser$user instance) =>
<String, dynamic>{
'username': instance.username,
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'sshKeys': instance.sshKeys,
'__typename': instance.$__typename,
};
const _$Enum$UserTypeEnumMap = {
Enum$UserType.NORMAL: 'NORMAL',
Enum$UserType.PRIMARY: 'PRIMARY',
Enum$UserType.ROOT: 'ROOT',
Enum$UserType.$unknown: r'$unknown',
};
Query$AllUsers _$Query$AllUsersFromJson(Map<String, dynamic> json) =>
Query$AllUsers(
users:
Query$AllUsers$users.fromJson(json['users'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllUsersToJson(Query$AllUsers instance) =>
<String, dynamic>{
'users': instance.users.toJson(),
'__typename': instance.$__typename,
};
Query$AllUsers$users _$Query$AllUsers$usersFromJson(
Map<String, dynamic> json) =>
Query$AllUsers$users(
allUsers: (json['allUsers'] as List<dynamic>)
.map((e) =>
Query$AllUsers$users$allUsers.fromJson(e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllUsers$usersToJson(
Query$AllUsers$users instance) =>
<String, dynamic>{
'allUsers': instance.allUsers.map((e) => e.toJson()).toList(),
'__typename': instance.$__typename,
};
Query$AllUsers$users$allUsers _$Query$AllUsers$users$allUsersFromJson(
Map<String, dynamic> json) =>
Query$AllUsers$users$allUsers(
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
username: json['username'] as String,
sshKeys:
(json['sshKeys'] as List<dynamic>).map((e) => e as String).toList(),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$AllUsers$users$allUsersToJson(
Query$AllUsers$users$allUsers instance) =>
<String, dynamic>{
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'username': instance.username,
'sshKeys': instance.sshKeys,
'__typename': instance.$__typename,
};
Variables$Mutation$AddSshKey _$Variables$Mutation$AddSshKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$AddSshKey(
sshInput: Input$SshMutationInput.fromJson(
json['sshInput'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$AddSshKeyToJson(
Variables$Mutation$AddSshKey instance) =>
<String, dynamic>{
'sshInput': instance.sshInput.toJson(),
};
Mutation$AddSshKey _$Mutation$AddSshKeyFromJson(Map<String, dynamic> json) =>
Mutation$AddSshKey(
addSshKey: Mutation$AddSshKey$addSshKey.fromJson(
json['addSshKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$AddSshKeyToJson(Mutation$AddSshKey instance) =>
<String, dynamic>{
'addSshKey': instance.addSshKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$AddSshKey$addSshKey _$Mutation$AddSshKey$addSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$AddSshKey$addSshKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Mutation$AddSshKey$addSshKey$user.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$AddSshKey$addSshKeyToJson(
Mutation$AddSshKey$addSshKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Mutation$AddSshKey$addSshKey$user _$Mutation$AddSshKey$addSshKey$userFromJson(
Map<String, dynamic> json) =>
Mutation$AddSshKey$addSshKey$user(
sshKeys:
(json['sshKeys'] as List<dynamic>).map((e) => e as String).toList(),
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
username: json['username'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$AddSshKey$addSshKey$userToJson(
Mutation$AddSshKey$addSshKey$user instance) =>
<String, dynamic>{
'sshKeys': instance.sshKeys,
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'username': instance.username,
'__typename': instance.$__typename,
};
Variables$Query$GetUser _$Variables$Query$GetUserFromJson(
Map<String, dynamic> json) =>
Variables$Query$GetUser(
username: json['username'] as String,
);
Map<String, dynamic> _$Variables$Query$GetUserToJson(
Variables$Query$GetUser instance) =>
<String, dynamic>{
'username': instance.username,
};
Query$GetUser _$Query$GetUserFromJson(Map<String, dynamic> json) =>
Query$GetUser(
users:
Query$GetUser$users.fromJson(json['users'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetUserToJson(Query$GetUser instance) =>
<String, dynamic>{
'users': instance.users.toJson(),
'__typename': instance.$__typename,
};
Query$GetUser$users _$Query$GetUser$usersFromJson(Map<String, dynamic> json) =>
Query$GetUser$users(
getUser: json['getUser'] == null
? null
: Query$GetUser$users$getUser.fromJson(
json['getUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetUser$usersToJson(
Query$GetUser$users instance) =>
<String, dynamic>{
'getUser': instance.getUser?.toJson(),
'__typename': instance.$__typename,
};
Query$GetUser$users$getUser _$Query$GetUser$users$getUserFromJson(
Map<String, dynamic> json) =>
Query$GetUser$users$getUser(
sshKeys:
(json['sshKeys'] as List<dynamic>).map((e) => e as String).toList(),
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
username: json['username'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$GetUser$users$getUserToJson(
Query$GetUser$users$getUser instance) =>
<String, dynamic>{
'sshKeys': instance.sshKeys,
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'username': instance.username,
'__typename': instance.$__typename,
};
Variables$Mutation$RemoveSshKey _$Variables$Mutation$RemoveSshKeyFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$RemoveSshKey(
sshInput: Input$SshMutationInput.fromJson(
json['sshInput'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$RemoveSshKeyToJson(
Variables$Mutation$RemoveSshKey instance) =>
<String, dynamic>{
'sshInput': instance.sshInput.toJson(),
};
Mutation$RemoveSshKey _$Mutation$RemoveSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveSshKey(
removeSshKey: Mutation$RemoveSshKey$removeSshKey.fromJson(
json['removeSshKey'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveSshKeyToJson(
Mutation$RemoveSshKey instance) =>
<String, dynamic>{
'removeSshKey': instance.removeSshKey.toJson(),
'__typename': instance.$__typename,
};
Mutation$RemoveSshKey$removeSshKey _$Mutation$RemoveSshKey$removeSshKeyFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveSshKey$removeSshKey(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Mutation$RemoveSshKey$removeSshKey$user.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$RemoveSshKey$removeSshKeyToJson(
Mutation$RemoveSshKey$removeSshKey instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Mutation$RemoveSshKey$removeSshKey$user
_$Mutation$RemoveSshKey$removeSshKey$userFromJson(
Map<String, dynamic> json) =>
Mutation$RemoveSshKey$removeSshKey$user(
sshKeys: (json['sshKeys'] as List<dynamic>)
.map((e) => e as String)
.toList(),
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
username: json['username'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$RemoveSshKey$removeSshKey$userToJson(
Mutation$RemoveSshKey$removeSshKey$user instance) =>
<String, dynamic>{
'sshKeys': instance.sshKeys,
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'username': instance.username,
'__typename': instance.$__typename,
};
Variables$Mutation$DeleteUser _$Variables$Mutation$DeleteUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$DeleteUser(
username: json['username'] as String,
);
Map<String, dynamic> _$Variables$Mutation$DeleteUserToJson(
Variables$Mutation$DeleteUser instance) =>
<String, dynamic>{
'username': instance.username,
};
Mutation$DeleteUser _$Mutation$DeleteUserFromJson(Map<String, dynamic> json) =>
Mutation$DeleteUser(
deleteUser: Mutation$DeleteUser$deleteUser.fromJson(
json['deleteUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteUserToJson(
Mutation$DeleteUser instance) =>
<String, dynamic>{
'deleteUser': instance.deleteUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$DeleteUser$deleteUser _$Mutation$DeleteUser$deleteUserFromJson(
Map<String, dynamic> json) =>
Mutation$DeleteUser$deleteUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$DeleteUser$deleteUserToJson(
Mutation$DeleteUser$deleteUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
};
Variables$Mutation$UpdateUser _$Variables$Mutation$UpdateUserFromJson(
Map<String, dynamic> json) =>
Variables$Mutation$UpdateUser(
user: Input$UserMutationInput.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Variables$Mutation$UpdateUserToJson(
Variables$Mutation$UpdateUser instance) =>
<String, dynamic>{
'user': instance.user.toJson(),
};
Mutation$UpdateUser _$Mutation$UpdateUserFromJson(Map<String, dynamic> json) =>
Mutation$UpdateUser(
updateUser: Mutation$UpdateUser$updateUser.fromJson(
json['updateUser'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UpdateUserToJson(
Mutation$UpdateUser instance) =>
<String, dynamic>{
'updateUser': instance.updateUser.toJson(),
'__typename': instance.$__typename,
};
Mutation$UpdateUser$updateUser _$Mutation$UpdateUser$updateUserFromJson(
Map<String, dynamic> json) =>
Mutation$UpdateUser$updateUser(
code: json['code'] as int,
message: json['message'] as String,
success: json['success'] as bool,
$__typename: json['__typename'] as String,
user: json['user'] == null
? null
: Mutation$UpdateUser$updateUser$user.fromJson(
json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$Mutation$UpdateUser$updateUserToJson(
Mutation$UpdateUser$updateUser instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'success': instance.success,
'__typename': instance.$__typename,
'user': instance.user?.toJson(),
};
Mutation$UpdateUser$updateUser$user
_$Mutation$UpdateUser$updateUser$userFromJson(Map<String, dynamic> json) =>
Mutation$UpdateUser$updateUser$user(
sshKeys: (json['sshKeys'] as List<dynamic>)
.map((e) => e as String)
.toList(),
userType: $enumDecode(_$Enum$UserTypeEnumMap, json['userType'],
unknownValue: Enum$UserType.$unknown),
username: json['username'] as String,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Mutation$UpdateUser$updateUser$userToJson(
Mutation$UpdateUser$updateUser$user instance) =>
<String, dynamic>{
'sshKeys': instance.sshKeys,
'userType': _$Enum$UserTypeEnumMap[instance.userType],
'username': instance.username,
'__typename': instance.$__typename,
};

View File

@ -1,277 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/utils/password_generator.dart';
class HetznerApi extends ApiMap {
HetznerApi({this.hasLogger = false, this.isWithToken = true});
@override
bool hasLogger;
@override
bool isWithToken;
@override
BaseOptions get options {
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().hetznerKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}
if (validateStatus != null) {
options.validateStatus = validateStatus!;
}
return options;
}
@override
String rootAddress = 'https://api.hetzner.cloud/v1';
Future<bool> isValid(final String token) async {
validateStatus = (final int? status) =>
status == HttpStatus.ok || status == HttpStatus.unauthorized;
final Dio client = await getClient();
final Response response = await client.get(
'/servers',
options: Options(
headers: {'Authorization': 'Bearer $token'},
),
);
close(client);
if (response.statusCode == HttpStatus.ok) {
return true;
} else if (response.statusCode == HttpStatus.unauthorized) {
return false;
} else {
throw Exception('code: ${response.statusCode}');
}
}
Future<ServerVolume> createVolume() async {
final Dio client = await getClient();
final Response dbCreateResponse = await client.post(
'/volumes',
data: {
'size': 10,
'name': StringGenerators.dbStorageName(),
'labels': {'labelkey': 'value'},
'location': 'fsn1',
'automount': false,
'format': 'ext4'
},
);
final dbId = dbCreateResponse.data['volume']['id'];
return ServerVolume(
id: dbId,
name: dbCreateResponse.data['volume']['name'],
);
}
Future<ServerHostingDetails?> createServer({
required final String cloudFlareKey,
required final User rootUser,
required final String domainName,
required final ServerVolume dataBase,
}) async {
final Dio client = await getClient();
final String dbPassword = StringGenerators.dbPassword();
final int dbId = dataBase.id;
final String apiToken = StringGenerators.apiToken();
final String hostname = getHostnameFromDomain(domainName);
final String base64Password =
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
print('hostname: $hostname');
/// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
/// check the branch name, it could be "development" or "master".
///
final String userdataString =
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
print(userdataString);
final Map<String, Object> data = {
'name': hostname,
'server_type': 'cx11',
'start_after_create': false,
'image': 'ubuntu-20.04',
'volumes': [dbId],
'networks': [],
'user_data': userdataString,
'labels': {},
'automount': true,
'location': 'fsn1'
};
print('Decoded data: $data');
ServerHostingDetails? serverDetails;
try {
final Response serverCreateResponse = await client.post(
'/servers',
data: data,
);
print(serverCreateResponse.data);
serverDetails = ServerHostingDetails(
id: serverCreateResponse.data['server']['id'],
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
createTime: DateTime.now(),
volume: dataBase,
apiToken: apiToken,
provider: ServerProvider.hetzner,
);
} on DioError catch (e) {
print(e);
rethrow;
} catch (e) {
print(e);
} finally {
client.close();
}
return serverDetails;
}
static String getHostnameFromDomain(final String domain) {
// Replace all non-alphanumeric characters with an underscore
String hostname =
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
if (hostname.endsWith('-')) {
hostname = hostname.substring(0, hostname.length - 1);
}
if (hostname.startsWith('-')) {
hostname = hostname.substring(1);
}
if (hostname.isEmpty) {
hostname = 'selfprivacy-server';
}
return hostname;
}
Future<void> deleteSelfprivacyServerAndAllVolumes({
required final String domainName,
}) async {
final Dio client = await getClient();
final String hostname = getHostnameFromDomain(domainName);
final Response serversReponse = await client.get('/servers');
final List servers = serversReponse.data['servers'];
final Map server = servers.firstWhere((final el) => el['name'] == hostname);
final List volumes = server['volumes'];
final List<Future> laterFutures = <Future>[];
for (final volumeId in volumes) {
await client.post('/volumes/$volumeId/actions/detach');
}
await Future.delayed(const Duration(seconds: 10));
for (final volumeId in volumes) {
laterFutures.add(client.delete('/volumes/$volumeId'));
}
laterFutures.add(client.delete('/servers/${server['id']}'));
await Future.wait(laterFutures);
close(client);
}
Future<ServerHostingDetails> reset() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient();
await client.post('/servers/${server.id}/actions/reset');
close(client);
return server.copyWith(startTime: DateTime.now());
}
Future<ServerHostingDetails> powerOn() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient();
await client.post('/servers/${server.id}/actions/poweron');
close(client);
return server.copyWith(startTime: DateTime.now());
}
Future<Map<String, dynamic>> getMetrics(
final DateTime start,
final DateTime end,
final String type,
) async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Map<String, dynamic> queryParameters = {
'start': start.toUtc().toIso8601String(),
'end': end.toUtc().toIso8601String(),
'type': type
};
final Response res = await client.get(
'/servers/${hetznerServer!.id}/metrics',
queryParameters: queryParameters,
);
close(client);
return res.data;
}
Future<HetznerServerInfo> getInfo() async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Response response = await client.get('/servers/${hetznerServer!.id}');
close(client);
return HetznerServerInfo.fromJson(response.data!['server']);
}
Future<List<HetznerServerInfo>> getServers() async {
final Dio client = await getClient();
final Response response = await client.get('/servers');
close(client);
return (response.data!['servers'] as List)
// ignore: unnecessary_lambdas
.map((final e) => HetznerServerInfo.fromJson(e))
.toList();
}
Future<void> createReverseDns({
required final String ip4,
required final String domainName,
}) async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
try {
await client.post(
'/servers/${hetznerServer!.id}/actions/change_dns_ptr',
data: {
'ip': ip4,
'dns_ptr': domainName,
},
);
} catch (e) {
print(e);
} finally {
close(client);
}
}
}

View File

@ -0,0 +1,48 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
class UnknownApiProviderException implements Exception {
UnknownApiProviderException(this.message);
final String message;
}
class ApiFactoryCreator {
static ServerProviderApiFactory createServerProviderApiFactory(
final ServerProvider provider,
) {
switch (provider) {
case ServerProvider.hetzner:
return HetznerApiFactory();
case ServerProvider.unknown:
throw UnknownApiProviderException('Unknown server provider');
}
}
static DnsProviderApiFactory createDnsProviderApiFactory(
final DnsProvider provider,
) {
switch (provider) {
case DnsProvider.cloudflare:
return CloudflareApiFactory();
case DnsProvider.unknown:
throw UnknownApiProviderException('Unknown DNS provider');
}
}
}
class VolumeApiFactoryCreator {
static VolumeProviderApiFactory createVolumeProviderApiFactory(
final ServerProvider provider,
) {
switch (provider) {
case ServerProvider.hetzner:
return HetznerApiFactory();
case ServerProvider.unknown:
throw UnknownApiProviderException('Unknown volume provider');
}
}
}

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
class BackblazeApiAuth { class BackblazeApiAuth {

View File

@ -2,16 +2,11 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.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/dns_records.dart';
class DomainNotFoundException implements Exception { class CloudflareApi extends DnsProviderApi {
DomainNotFoundException(this.message);
final String message;
}
class CloudflareApi extends ApiMap {
CloudflareApi({ CloudflareApi({
this.hasLogger = false, this.hasLogger = false,
this.isWithToken = true, this.isWithToken = true,
@ -24,6 +19,10 @@ class CloudflareApi extends ApiMap {
final String? customToken; final String? customToken;
@override
RegExp getApiTokenValidation() =>
RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
@override @override
BaseOptions get options { BaseOptions get options {
final BaseOptions options = BaseOptions(baseUrl: rootAddress); final BaseOptions options = BaseOptions(baseUrl: rootAddress);
@ -46,27 +45,37 @@ class CloudflareApi extends ApiMap {
@override @override
String rootAddress = 'https://api.cloudflare.com/client/v4'; String rootAddress = 'https://api.cloudflare.com/client/v4';
Future<bool> isValid(final String token) async { @override
validateStatus = (final status) => Future<bool> isApiTokenValid(final String token) async {
status == HttpStatus.ok || status == HttpStatus.unauthorized; bool isValid = false;
Response? response;
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get( try {
'/user/tokens/verify', response = await client.get(
options: Options(headers: {'Authorization': 'Bearer $token'}), '/user/tokens/verify',
); options: Options(headers: {'Authorization': 'Bearer $token'}),
);
close(client); } catch (e) {
print(e);
if (response.statusCode == HttpStatus.ok) { isValid = false;
return true; } finally {
} else if (response.statusCode == HttpStatus.unauthorized) { close(client);
return false;
} else {
throw Exception('code: ${response.statusCode}');
} }
if (response != null) {
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
}
return isValid;
} }
@override
Future<String> getZoneId(final String domain) async { Future<String> getZoneId(final String domain) async {
validateStatus = (final status) => validateStatus = (final status) =>
status == HttpStatus.ok || status == HttpStatus.forbidden; status == HttpStatus.ok || status == HttpStatus.forbidden;
@ -85,12 +94,13 @@ class CloudflareApi extends ApiMap {
} }
} }
@override
Future<void> removeSimilarRecords({ Future<void> removeSimilarRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
final String? ip4, final String? ip4,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '/zones/$domainZoneId/dns_records'; final String url = '/zones/$domainZoneId/dns_records';
@ -112,11 +122,12 @@ class CloudflareApi extends ApiMap {
close(client); close(client);
} }
@override
Future<List<DnsRecord>> getDnsRecords({ Future<List<DnsRecord>> getDnsRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '/zones/$domainZoneId/dns_records'; final String url = '/zones/$domainZoneId/dns_records';
@ -144,12 +155,13 @@ class CloudflareApi extends ApiMap {
return allRecords; return allRecords;
} }
@override
Future<void> createMultipleDnsRecords({ Future<void> createMultipleDnsRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
final String? ip4, final String? ip4,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4); final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4);
final List<Future> allCreateFutures = <Future>[]; final List<Future> allCreateFutures = <Future>[];
@ -219,11 +231,12 @@ class CloudflareApi extends ApiMap {
]; ];
} }
@override
Future<void> setDkim( Future<void> setDkim(
final String dkimRecordString, final String dkimRecordString,
final ServerDomain cloudFlareDomain, final ServerDomain domain,
) async { ) async {
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '$rootAddress/zones/$domainZoneId/dns_records'; final String url = '$rootAddress/zones/$domainZoneId/dns_records';
final DnsRecord dkimRecord = DnsRecord( final DnsRecord dkimRecord = DnsRecord(
@ -242,6 +255,7 @@ class CloudflareApi extends ApiMap {
client.close(); client.close();
} }
@override
Future<List<String>> domainList() async { Future<List<String>> domainList() async {
final String url = '$rootAddress/zones'; final String url = '$rootAddress/zones';
final Dio client = await getClient(); final Dio client = await getClient();

View File

@ -0,0 +1,15 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
class CloudflareApiFactory extends DnsProviderApiFactory {
@override
DnsProviderApi getDnsProvider({
final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
}) =>
CloudflareApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
customToken: settings.customToken,
);
}

View File

@ -0,0 +1,31 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart';
class DomainNotFoundException implements Exception {
DomainNotFoundException(this.message);
final String message;
}
abstract class DnsProviderApi extends ApiMap {
Future<List<DnsRecord>> getDnsRecords({
required final ServerDomain domain,
});
Future<void> removeSimilarRecords({
required final ServerDomain domain,
final String? ip4,
});
Future<void> createMultipleDnsRecords({
required final ServerDomain domain,
final String? ip4,
});
Future<void> setDkim(
final String dkimRecordString,
final ServerDomain domain,
);
Future<String> getZoneId(final String domain);
Future<List<String>> domainList();
Future<bool> isApiTokenValid(final String token);
RegExp getApiTokenValidation();
}

View File

@ -0,0 +1,17 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
class DnsProviderApiSettings extends ProviderApiSettings {
const DnsProviderApiSettings({
final super.hasLogger = false,
final super.isWithToken = true,
final this.customToken,
});
final String? customToken;
}
abstract class DnsProviderApiFactory {
DnsProviderApi getDnsProvider({
final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
});
}

View File

@ -0,0 +1,5 @@
class ProviderApiSettings {
const ProviderApiSettings({this.hasLogger = false, this.isWithToken = true});
final bool hasLogger;
final bool isWithToken;
}

View File

@ -15,7 +15,7 @@ import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
class ApiResponse<D> { class ApiResponse<D> {
ApiResponse({ ApiResponse({
@ -46,16 +46,20 @@ class ServerApi extends ApiMap {
@override @override
BaseOptions get options { BaseOptions get options {
BaseOptions options = BaseOptions(); BaseOptions options = BaseOptions(
connectTimeout: 10000,
receiveTimeout: 10000,
);
if (isWithToken) { if (isWithToken) {
final ServerDomain? cloudFlareDomain = final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
getIt<ApiConfigModel>().serverDomain; final String domainName = serverDomain!.domainName;
final String domainName = cloudFlareDomain!.domainName;
final String? apiToken = getIt<ApiConfigModel>().serverDetails?.apiToken; final String? apiToken = getIt<ApiConfigModel>().serverDetails?.apiToken;
options = BaseOptions( options = BaseOptions(
baseUrl: 'https://api.$domainName', baseUrl: 'https://api.$domainName',
connectTimeout: 10000,
receiveTimeout: 10000,
headers: { headers: {
'Authorization': 'Bearer $apiToken', 'Authorization': 'Bearer $apiToken',
}, },
@ -65,6 +69,8 @@ class ServerApi extends ApiMap {
if (overrideDomain != null) { if (overrideDomain != null) {
options = BaseOptions( options = BaseOptions(
baseUrl: 'https://api.$overrideDomain', baseUrl: 'https://api.$overrideDomain',
connectTimeout: 10000,
receiveTimeout: 10000,
headers: customToken != null headers: customToken != null
? {'Authorization': 'Bearer $customToken'} ? {'Authorization': 'Bearer $customToken'}
: null, : null,
@ -99,8 +105,8 @@ class ServerApi extends ApiMap {
try { try {
response = await client.get('/services/status'); response = await client.get('/services/status');
res = response.statusCode == HttpStatus.ok; res = response.statusCode == HttpStatus.ok;
} on DioError catch (e) { } catch (e) {
print(e.message); print(e);
} finally { } finally {
close(client); close(client);
} }
@ -619,7 +625,7 @@ class ServerApi extends ApiMap {
} }
} }
Future<String?> getDkim() async { Future<String> getDkim() async {
Response response; Response response;
final Dio client = await getClient(); final Dio client = await getClient();
@ -627,13 +633,13 @@ class ServerApi extends ApiMap {
response = await client.get('/services/mailserver/dkim'); response = await client.get('/services/mailserver/dkim');
} on DioError catch (e) { } on DioError catch (e) {
print(e.message); print(e.message);
return null; throw Exception('No DKIM key found');
} finally { } finally {
close(client); close(client);
} }
if (response.statusCode == null) { if (response.statusCode == null) {
return null; throw Exception('No DKIM key found');
} }
if (response.statusCode == HttpStatus.notFound || response.data == null) { if (response.statusCode == HttpStatus.notFound || response.data == null) {
@ -641,7 +647,7 @@ class ServerApi extends ApiMap {
} }
if (response.statusCode != HttpStatus.ok) { if (response.statusCode != HttpStatus.ok) {
return ''; throw Exception('No DKIM key found');
} }
final Codec<String, String> base64toString = utf8.fuse(base64); final Codec<String, String> base64toString = utf8.fuse(base64);

View File

@ -0,0 +1,546 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/utils/password_generator.dart';
class HetznerApi extends ServerProviderApi with VolumeProviderApi {
HetznerApi({final this.hasLogger = false, final this.isWithToken = true});
@override
bool hasLogger;
@override
bool isWithToken;
@override
BaseOptions get options {
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().hetznerKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}
if (validateStatus != null) {
options.validateStatus = validateStatus!;
}
return options;
}
@override
String rootAddress = 'https://api.hetzner.cloud/v1';
@override
Future<bool> isApiTokenValid(final String token) async {
bool isValid = false;
Response? response;
final Dio client = await getClient();
try {
response = await client.get(
'/servers',
options: Options(
headers: {'Authorization': 'Bearer $token'},
),
);
} catch (e) {
print(e);
isValid = false;
} finally {
close(client);
}
if (response != null) {
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
}
return isValid;
}
@override
RegExp getApiTokenValidation() =>
RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
@override
Future<double?> getPricePerGb() async {
double? price;
final Response dbGetResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get('/pricing');
final volume = dbGetResponse.data['pricing']['volume'];
final volumePrice = volume['price_per_gb_month']['gross'];
price = double.parse(volumePrice);
} catch (e) {
print(e);
} finally {
client.close();
}
return price;
}
@override
Future<ServerVolume?> createVolume() async {
ServerVolume? volume;
final Response dbCreateResponse;
final Dio client = await getClient();
try {
dbCreateResponse = await client.post(
'/volumes',
data: {
'size': 10,
'name': StringGenerators.dbStorageName(),
'labels': {'labelkey': 'value'},
'location': 'fsn1',
'automount': false,
'format': 'ext4'
},
);
final dbId = dbCreateResponse.data['volume']['id'];
final dbSize = dbCreateResponse.data['volume']['size'];
final dbServer = dbCreateResponse.data['volume']['server'];
final dbName = dbCreateResponse.data['volume']['name'];
final dbDevice = dbCreateResponse.data['volume']['linux_device'];
volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
);
} catch (e) {
print(e);
} finally {
client.close();
}
return volume;
}
@override
Future<List<ServerVolume>> getVolumes({final String? status}) async {
final List<ServerVolume> volumes = [];
final Response dbGetResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get(
'/volumes',
queryParameters: {
'status': status,
},
);
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
for (final rawVolume in rawVolumes) {
final int dbId = rawVolume['id'];
final int dbSize = rawVolume['size'];
final dbServer = rawVolume['server'];
final String dbName = rawVolume['name'];
final dbDevice = rawVolume['linux_device'];
final volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
);
volumes.add(volume);
}
} catch (e) {
print(e);
} finally {
client.close();
}
return volumes;
}
@override
Future<ServerVolume?> getVolume(final int id) async {
ServerVolume? volume;
final Response dbGetResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get('/volumes/$id');
final int dbId = dbGetResponse.data['volume']['id'];
final int dbSize = dbGetResponse.data['volume']['size'];
final int dbServer = dbGetResponse.data['volume']['server'];
final String dbName = dbGetResponse.data['volume']['name'];
final dbDevice = dbGetResponse.data['volume']['linux_device'];
volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
);
} catch (e) {
print(e);
} finally {
client.close();
}
return volume;
}
@override
Future<void> deleteVolume(final int id) async {
final Dio client = await getClient();
try {
await client.delete('/volumes/$id');
} catch (e) {
print(e);
} finally {
client.close();
}
}
@override
Future<bool> attachVolume(final int volumeId, final int serverId) async {
bool success = false;
final Response dbPostResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
'/volumes/$volumeId/actions/attach',
data: {
'automount': true,
'server': serverId,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
client.close();
}
return success;
}
@override
Future<bool> detachVolume(final int volumeId) async {
bool success = false;
final Response dbPostResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post('/volumes/$volumeId/actions/detach');
success = dbPostResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
client.close();
}
return success;
}
@override
Future<bool> resizeVolume(final int volumeId, final int sizeGb) async {
bool success = false;
final Response dbPostResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
'/volumes/$volumeId/actions/resize',
data: {
'size': sizeGb,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
client.close();
}
return success;
}
@override
Future<ServerHostingDetails?> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
}) async {
ServerHostingDetails? details;
final ServerVolume? newVolume = await createVolume();
if (newVolume == null) {
return details;
}
details = await createServerWithVolume(
dnsApiToken: dnsApiToken,
rootUser: rootUser,
domainName: domainName,
dataBase: newVolume,
);
return details;
}
Future<ServerHostingDetails?> createServerWithVolume({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
required final ServerVolume dataBase,
}) async {
final Dio client = await getClient();
final String dbPassword = StringGenerators.dbPassword();
final int dbId = dataBase.id;
final String apiToken = StringGenerators.apiToken();
final String hostname = getHostnameFromDomain(domainName);
final String base64Password =
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
print('hostname: $hostname');
/// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
/// check the branch name, it could be "development" or "master".
///
final String userdataString =
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
print(userdataString);
final Map<String, Object> data = {
'name': hostname,
'server_type': 'cx11',
'start_after_create': false,
'image': 'ubuntu-20.04',
'volumes': [dbId],
'networks': [],
'user_data': userdataString,
'labels': {},
'automount': true,
'location': 'fsn1'
};
print('Decoded data: $data');
ServerHostingDetails? serverDetails;
DioError? hetznerError;
bool success = false;
try {
final Response serverCreateResponse = await client.post(
'/servers',
data: data,
);
print(serverCreateResponse.data);
serverDetails = ServerHostingDetails(
id: serverCreateResponse.data['server']['id'],
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
createTime: DateTime.now(),
volume: dataBase,
apiToken: apiToken,
provider: ServerProvider.hetzner,
);
success = true;
} on DioError catch (e) {
print(e);
hetznerError = e;
} catch (e) {
print(e);
} finally {
client.close();
}
if (!success) {
await Future.delayed(const Duration(seconds: 10));
await deleteVolume(dbId);
}
if (hetznerError != null) {
throw hetznerError;
}
return serverDetails;
}
static String getHostnameFromDomain(final String domain) {
// Replace all non-alphanumeric characters with an underscore
String hostname =
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
if (hostname.endsWith('-')) {
hostname = hostname.substring(0, hostname.length - 1);
}
if (hostname.startsWith('-')) {
hostname = hostname.substring(1);
}
if (hostname.isEmpty) {
hostname = 'selfprivacy-server';
}
return hostname;
}
@override
Future<void> deleteServer({
required final String domainName,
}) async {
final Dio client = await getClient();
final String hostname = getHostnameFromDomain(domainName);
final Response serversReponse = await client.get('/servers');
final List servers = serversReponse.data['servers'];
final Map server = servers.firstWhere((final el) => el['name'] == hostname);
final List volumes = server['volumes'];
final List<Future> laterFutures = <Future>[];
for (final volumeId in volumes) {
await client.post('/volumes/$volumeId/actions/detach');
}
await Future.delayed(const Duration(seconds: 10));
for (final volumeId in volumes) {
laterFutures.add(client.delete('/volumes/$volumeId'));
}
laterFutures.add(client.delete('/servers/${server['id']}'));
await Future.wait(laterFutures);
close(client);
}
@override
Future<ServerHostingDetails> restart() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient();
try {
await client.post('/servers/${server.id}/actions/reset');
} catch (e) {
print(e);
} finally {
close(client);
}
return server.copyWith(startTime: DateTime.now());
}
@override
Future<ServerHostingDetails> powerOn() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient();
try {
await client.post('/servers/${server.id}/actions/poweron');
} catch (e) {
print(e);
} finally {
close(client);
}
return server.copyWith(startTime: DateTime.now());
}
Future<Map<String, dynamic>> getMetrics(
final DateTime start,
final DateTime end,
final String type,
) async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Map<String, dynamic> queryParameters = {
'start': start.toUtc().toIso8601String(),
'end': end.toUtc().toIso8601String(),
'type': type
};
final Response res = await client.get(
'/servers/${hetznerServer!.id}/metrics',
queryParameters: queryParameters,
);
close(client);
return res.data;
}
Future<HetznerServerInfo> getInfo() async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Response response = await client.get('/servers/${hetznerServer!.id}');
close(client);
return HetznerServerInfo.fromJson(response.data!['server']);
}
@override
Future<List<ServerBasicInfo>> getServers() async {
List<ServerBasicInfo> servers = [];
final Dio client = await getClient();
try {
final Response response = await client.get('/servers');
servers = (response.data!['servers'] as List)
.map(
(final e) => HetznerServerInfo.fromJson(e),
)
.toList()
.map(
(final HetznerServerInfo server) => ServerBasicInfo(
id: server.id,
name: server.name,
ip: server.publicNet.ipv4.ip,
reverseDns: server.publicNet.ipv4.reverseDns,
created: server.created,
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
),
)
.toList();
} catch (e) {
print(e);
} finally {
close(client);
}
return servers;
}
@override
Future<void> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
}) async {
final Dio client = await getClient();
try {
await client.post(
'/servers/${serverDetails.id}/actions/change_dns_ptr',
data: {
'ip': serverDetails.ip4,
'dns_ptr': domain.domainName,
},
);
} catch (e) {
print(e);
} finally {
close(client);
}
}
}

View File

@ -0,0 +1,26 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
class HetznerApiFactory extends ServerProviderApiFactory
with VolumeProviderApiFactory {
@override
ServerProviderApi getServerProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
@override
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
}

View File

@ -0,0 +1,26 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
abstract class ServerProviderApi extends ApiMap {
Future<List<ServerBasicInfo>> getServers();
Future<ServerHostingDetails> restart();
Future<ServerHostingDetails> powerOn();
Future<void> deleteServer({required final String domainName});
Future<ServerHostingDetails?> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
});
Future<void> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
});
Future<bool> isApiTokenValid(final String token);
RegExp getApiTokenValidation();
}

View File

@ -0,0 +1,15 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
abstract class ServerProviderApiFactory {
ServerProviderApi getServerProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}
mixin VolumeProviderApiFactory {
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}

View File

@ -0,0 +1,13 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
mixin VolumeProviderApi on ApiMap {
Future<ServerVolume?> createVolume();
Future<List<ServerVolume>> getVolumes({final String? status});
Future<ServerVolume?> getVolume(final int id);
Future<bool> attachVolume(final int volumeId, final int serverId);
Future<bool> detachVolume(final int volumeId);
Future<bool> resizeVolume(final int volumeId, final int sizeGb);
Future<void> deleteVolume(final int id);
Future<double?> getPricePerGb();
}

View File

@ -2,8 +2,8 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/backblaze.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/json/backup.dart'; import 'package:selfprivacy/logic/models/json/backup.dart';

View File

@ -1,5 +1,5 @@
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/json/api_token.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart';
@ -16,17 +16,16 @@ class ApiDevicesCubit
@override @override
void load() async { void load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) { if (serverInstallationCubit.state is ServerInstallationFinished) {
final List<ApiToken>? devices = await _getApiTokens(); _refetch();
if (devices != null) {
emit(ApiDevicesState(devices, LoadingStatus.success));
} else {
emit(const ApiDevicesState([], LoadingStatus.error));
}
} }
} }
Future<void> refresh() async { Future<void> refresh() async {
emit(const ApiDevicesState([], LoadingStatus.refreshing)); emit(const ApiDevicesState([], LoadingStatus.refreshing));
_refetch();
}
void _refetch() async {
final List<ApiToken>? devices = await _getApiTokens(); final List<ApiToken>? devices = await _getApiTokens();
if (devices != null) { if (devices != null) {
emit(ApiDevicesState(devices, LoadingStatus.success)); emit(ApiDevicesState(devices, LoadingStatus.success));

View File

@ -1,10 +1,12 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.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/dns_records.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/api_maps/server.dart';
part 'dns_records_state.dart'; part 'dns_records_state.dart';
@ -16,8 +18,12 @@ class DnsRecordsCubit
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing), const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing),
); );
DnsProviderApiFactory? dnsProviderApiFactory =
ApiFactoryCreator.createDnsProviderApiFactory(
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
); // TODO: Remove when provider selection is implemented.
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final CloudflareApi cloudflare = CloudflareApi();
@override @override
Future<void> load() async { Future<void> load() async {
@ -31,14 +37,15 @@ class DnsRecordsCubit
), ),
), ),
); );
print('Loading DNS status');
if (serverInstallationCubit.state is ServerInstallationFinished) { if (serverInstallationCubit.state is ServerInstallationFinished) {
final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress = final String? ipAddress =
serverInstallationCubit.state.serverDetails?.ip4; serverInstallationCubit.state.serverDetails?.ip4;
if (domain != null && ipAddress != null) { if (domain != null && ipAddress != null) {
final List<DnsRecord> records = final List<DnsRecord> records = await dnsProviderApiFactory!
await cloudflare.getDnsRecords(cloudFlareDomain: domain); .getDnsProvider()
.getDnsRecords(domain: domain);
final String? dkimPublicKey = await api.getDkim(); final String? dkimPublicKey = await api.getDkim();
final List<DesiredDnsRecord> desiredRecords = final List<DesiredDnsRecord> desiredRecords =
_getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); _getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey);
@ -116,12 +123,14 @@ class DnsRecordsCubit
final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4;
final String? dkimPublicKey = await api.getDkim(); final String? dkimPublicKey = await api.getDkim();
await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!); final DnsProviderApi dnsProviderApi =
await cloudflare.createMultipleDnsRecords( dnsProviderApiFactory!.getDnsProvider();
cloudFlareDomain: domain, await dnsProviderApi.removeSimilarRecords(domain: domain!);
await dnsProviderApi.createMultipleDnsRecords(
domain: domain,
ip4: ipAddress, ip4: ipAddress,
); );
await cloudflare.setDkim(dkimPublicKey ?? '', domain); await dnsProviderApi.setDkim(dkimPublicKey ?? '', domain);
await load(); await load();
} }

View File

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/backblaze.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -55,10 +55,11 @@ class BackblazeFormCubit extends FormCubit {
} }
if (!isKeyValid) { if (!isKeyValid) {
keyId.setError('bad key'); keyId.setError('initializing.backblaze_bad_key_error'.tr());
applicationKey.setError('bad key'); applicationKey.setError('initializing.backblaze_bad_key_error'.tr());
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -2,13 +2,12 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class CloudFlareFormCubit extends FormCubit { class DnsProviderFormCubit extends FormCubit {
CloudFlareFormCubit(this.initializingCubit) { DnsProviderFormCubit(this.initializingCubit) {
final RegExp regExp = RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); final RegExp regExp = initializingCubit.getDnsProviderApiTokenValidation();
apiKey = FieldCubit( apiKey = FieldCubit(
initalValue: '', initalValue: '',
validations: [ validations: [
@ -30,24 +29,25 @@ class CloudFlareFormCubit extends FormCubit {
} }
final ServerInstallationCubit initializingCubit; final ServerInstallationCubit initializingCubit;
late final FieldCubit<String> apiKey; late final FieldCubit<String> apiKey;
@override @override
FutureOr<bool> asyncValidation() async { FutureOr<bool> asyncValidation() async {
late bool isKeyValid; late bool isKeyValid;
final CloudflareApi apiClient = CloudflareApi(isWithToken: false);
try { try {
isKeyValid = await apiClient.isValid(apiKey.state.value); isKeyValid = await initializingCubit
.isDnsProviderApiTokenValid(apiKey.state.value);
} catch (e) { } catch (e) {
addError(e); addError(e);
isKeyValid = false;
} }
if (!isKeyValid) { if (!isKeyValid) {
apiKey.setError('bad key'); apiKey.setError('initializing.cloudflare_bad_key_error'.tr());
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -1,5 +1,4 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
@ -10,9 +9,10 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
Future<void> load() async { Future<void> load() async {
emit(Loading(LoadingTypes.loadingDomain)); emit(Loading(LoadingTypes.loadingDomain));
final CloudflareApi api = CloudflareApi(); final List<String> list = await serverInstallationCubit
.repository.dnsProviderApiFactory!
final List<String> list = await api.domainList(); .getDnsProvider()
.domainList();
if (list.isEmpty) { if (list.isEmpty) {
emit(Empty()); emit(Empty());
} else if (list.length == 1) { } else if (list.length == 1) {
@ -28,11 +28,13 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
Future<void> saveDomain() async { Future<void> saveDomain() async {
assert(state is Loaded, 'wrong state'); assert(state is Loaded, 'wrong state');
final String domainName = (state as Loaded).domain; final String domainName = (state as Loaded).domain;
final CloudflareApi api = CloudflareApi();
emit(Loading(LoadingTypes.saving)); emit(Loading(LoadingTypes.saving));
final String zoneId = await api.getZoneId(domainName); final String zoneId = await serverInstallationCubit
.repository.dnsProviderApiFactory!
.getDnsProvider()
.getZoneId(domainName);
final ServerDomain domain = ServerDomain( final ServerDomain domain = ServerDomain(
domainName: domainName, domainName: domainName,

View File

@ -2,13 +2,13 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class HetznerFormCubit extends FormCubit { class ProviderFormCubit extends FormCubit {
HetznerFormCubit(this.serverInstallationCubit) { ProviderFormCubit(this.serverInstallationCubit) {
final RegExp regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); final RegExp regExp =
serverInstallationCubit.getServerProviderApiTokenValidation();
apiKey = FieldCubit( apiKey = FieldCubit(
initalValue: '', initalValue: '',
validations: [ validations: [
@ -30,24 +30,25 @@ class HetznerFormCubit extends FormCubit {
} }
final ServerInstallationCubit serverInstallationCubit; final ServerInstallationCubit serverInstallationCubit;
late final FieldCubit<String> apiKey; late final FieldCubit<String> apiKey;
@override @override
FutureOr<bool> asyncValidation() async { FutureOr<bool> asyncValidation() async {
late bool isKeyValid; late bool isKeyValid;
final HetznerApi apiClient = HetznerApi(isWithToken: false);
try { try {
isKeyValid = await apiClient.isValid(apiKey.state.value); isKeyValid = await serverInstallationCubit
.isServerProviderApiTokenValid(apiKey.state.value);
} catch (e) { } catch (e) {
addError(e); addError(e);
isKeyValid = false;
} }
if (!isKeyValid) { if (!isKeyValid) {
apiKey.setError('bad key'); apiKey.setError('initializing.hetzner_bad_key_error'.tr());
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
@ -18,8 +18,9 @@ class RecoveryDomainFormCubit extends FormCubit {
@override @override
FutureOr<void> onSubmit() async { FutureOr<void> onSubmit() async {
initializingCubit initializingCubit.submitDomainForAccessRecovery(
.submitDomainForAccessRecovery(serverDomainField.state.value); serverDomainField.state.value.toLowerCase(),
);
} }
@override @override

View File

@ -41,7 +41,8 @@ class SshFormCubit extends FormCubit {
@override @override
FutureOr<void> onSubmit() { FutureOr<void> onSubmit() {
print(key.state.isValid); print(key.state.isValid);
jobsCubit.addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value)); jobsCubit
.addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value.trim()));
} }
late FieldCubit<String> key; late FieldCubit<String> key;

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; import 'package:selfprivacy/logic/models/hetzner_metrics.dart';

View File

@ -1,11 +1,14 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';
@ -15,20 +18,21 @@ class JobsCubit extends Cubit<JobsState> {
JobsCubit({ JobsCubit({
required this.usersCubit, required this.usersCubit,
required this.servicesCubit, required this.servicesCubit,
}) : super(JobsStateEmpty()); }) : super(const JobsStateEmpty([]));
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final UsersCubit usersCubit; final UsersCubit usersCubit;
final ServicesCubit servicesCubit; final ServicesCubit servicesCubit;
void addJob(final Job job) { void addJob(final ClientJob job) {
final List<Job> newJobsList = <Job>[]; final List<ClientJob> newJobsList = [];
if (state is JobsStateWithJobs) { if (state is JobsStateWithJobs) {
newJobsList.addAll((state as JobsStateWithJobs).jobList); final JobsStateWithJobs jobsState = state as JobsStateWithJobs;
newJobsList.addAll(jobsState.clientJobList);
} }
newJobsList.add(job); newJobsList.add(job);
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr()); getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
emit(JobsStateWithJobs(newJobsList)); emit(JobsStateWithJobs(newJobsList, state.serverJobList));
} }
void removeJob(final String id) { void removeJob(final String id) {
@ -37,51 +41,51 @@ class JobsCubit extends Cubit<JobsState> {
} }
void createOrRemoveServiceToggleJob(final ToggleJob job) { void createOrRemoveServiceToggleJob(final ToggleJob job) {
final List<Job> newJobsList = <Job>[]; final List<ClientJob> newJobsList = <ClientJob>[];
if (state is JobsStateWithJobs) { if (state is JobsStateWithJobs) {
newJobsList.addAll((state as JobsStateWithJobs).jobList); newJobsList.addAll((state as JobsStateWithJobs).clientJobList);
} }
final bool needToRemoveJob = newJobsList final bool needToRemoveJob = newJobsList
.any((final el) => el is ServiceToggleJob && el.type == job.type); .any((final el) => el is ServiceToggleJob && el.type == job.type);
if (needToRemoveJob) { if (needToRemoveJob) {
final Job removingJob = newJobsList.firstWhere( final ClientJob removingJob = newJobsList.firstWhere(
(final el) => el is ServiceToggleJob && el.type == job.type, (final el) => el is ServiceToggleJob && el.type == job.type,
); );
removeJob(removingJob.id); removeJob(removingJob.id);
} else { } else {
newJobsList.add(job); newJobsList.add(job);
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr()); getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
emit(JobsStateWithJobs(newJobsList)); emit(JobsStateWithJobs(newJobsList, state.serverJobList));
} }
} }
void createShhJobIfNotExist(final CreateSSHKeyJob job) { void createShhJobIfNotExist(final CreateSSHKeyJob job) {
final List<Job> newJobsList = <Job>[]; final List<ClientJob> newJobsList = <ClientJob>[];
if (state is JobsStateWithJobs) { if (state is JobsStateWithJobs) {
newJobsList.addAll((state as JobsStateWithJobs).jobList); newJobsList.addAll((state as JobsStateWithJobs).clientJobList);
} }
final bool isExistInJobList = final bool isExistInJobList =
newJobsList.any((final el) => el is CreateSSHKeyJob); newJobsList.any((final el) => el is CreateSSHKeyJob);
if (!isExistInJobList) { if (!isExistInJobList) {
newJobsList.add(job); newJobsList.add(job);
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr()); getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
emit(JobsStateWithJobs(newJobsList)); emit(JobsStateWithJobs(newJobsList, state.serverJobList));
} }
} }
Future<void> rebootServer() async { Future<void> rebootServer() async {
emit(JobsStateLoading()); emit(JobsStateLoading(state.serverJobList));
final bool isSuccessful = await api.reboot(); final bool isSuccessful = await api.reboot();
if (isSuccessful) { if (isSuccessful) {
getIt<NavigationService>().showSnackBar('jobs.rebootSuccess'.tr()); getIt<NavigationService>().showSnackBar('jobs.rebootSuccess'.tr());
} else { } else {
getIt<NavigationService>().showSnackBar('jobs.rebootFailed'.tr()); getIt<NavigationService>().showSnackBar('jobs.rebootFailed'.tr());
} }
emit(JobsStateEmpty()); emit(JobsStateEmpty(state.serverJobList));
} }
Future<void> upgradeServer() async { Future<void> upgradeServer() async {
emit(JobsStateLoading()); emit(JobsStateLoading(state.serverJobList));
final bool isPullSuccessful = await api.pullConfigurationUpdate(); final bool isPullSuccessful = await api.pullConfigurationUpdate();
final bool isSuccessful = await api.upgrade(); final bool isSuccessful = await api.upgrade();
if (isSuccessful) { if (isSuccessful) {
@ -93,15 +97,15 @@ class JobsCubit extends Cubit<JobsState> {
} else { } else {
getIt<NavigationService>().showSnackBar('jobs.upgradeFailed'.tr()); getIt<NavigationService>().showSnackBar('jobs.upgradeFailed'.tr());
} }
emit(JobsStateEmpty()); emit(JobsStateEmpty(state.serverJobList));
} }
Future<void> applyAll() async { Future<void> applyAll() async {
if (state is JobsStateWithJobs) { if (state is JobsStateWithJobs) {
final List<Job> jobs = (state as JobsStateWithJobs).jobList; final List<ClientJob> jobs = (state as JobsStateWithJobs).clientJobList;
emit(JobsStateLoading()); emit(JobsStateLoading(state.serverJobList));
bool hasServiceJobs = false; bool hasServiceJobs = false;
for (final Job job in jobs) { for (final ClientJob job in jobs) {
if (job is CreateUserJob) { if (job is CreateUserJob) {
await usersCubit.createUser(job.user); await usersCubit.createUser(job.user);
} }
@ -122,11 +126,45 @@ class JobsCubit extends Cubit<JobsState> {
await api.pullConfigurationUpdate(); await api.pullConfigurationUpdate();
await api.apply(); await api.apply();
if (hasServiceJobs) { if (hasServiceJobs) {
await servicesCubit.load(); await servicesCubit.load();
} }
emit(JobsStateEmpty()); emit(JobsStateEmpty(state.serverJobList));
} }
} }
Future<void> resetRequestsTimer() async {
const duration = Duration(seconds: 1);
Timer.periodic(
duration,
(final timer) async {
if (timer.tick >= 10) {
final List<ServerJob> serverJobs = await api.getServerJobs();
final List<ServerJob> newServerJobs = [];
for (final ServerJob job in serverJobs) {
if (job.status == 'FINISHED') {
await api.removeApiJob(job.uid);
} else {
newServerJobs.add(job);
}
}
if (state is JobsStateWithJobs) {
emit(
JobsStateWithJobs(
(state as JobsStateWithJobs).clientJobList,
newServerJobs,
),
);
} else {
emit(
JobsStateEmpty(newServerJobs),
);
}
}
},
);
}
} }

View File

@ -1,28 +1,34 @@
part of 'jobs_cubit.dart'; part of 'jobs_cubit.dart';
abstract class JobsState extends Equatable { abstract class JobsState extends Equatable {
const JobsState(this.serverJobList);
final List<ServerJob> serverJobList;
@override @override
List<Object?> get props => []; List<Object?> get props => [serverJobList];
} }
class JobsStateLoading extends JobsState {} class JobsStateLoading extends JobsState {
const JobsStateLoading(super.serverJobList);
}
class JobsStateEmpty extends JobsState {} class JobsStateEmpty extends JobsState {
const JobsStateEmpty(super.serverJobList);
}
class JobsStateWithJobs extends JobsState { class JobsStateWithJobs extends JobsState {
JobsStateWithJobs(this.jobList); const JobsStateWithJobs(this.clientJobList, super.serverJobList);
final List<Job> jobList; final List<ClientJob> clientJobList;
JobsState removeById(final String id) { JobsState removeById(final String id) {
final List<Job> newJobsList = final List<ClientJob> newJobsList =
jobList.where((final element) => element.id != id).toList(); clientJobList.where((final element) => element.id != id).toList();
if (newJobsList.isEmpty) { if (newJobsList.isEmpty) {
return JobsStateEmpty(); return JobsStateEmpty(serverJobList);
} }
return JobsStateWithJobs(newJobsList); return JobsStateWithJobs(newJobsList, serverJobList);
} }
@override @override
List<Object?> get props => jobList; List<Object?> get props => [...super.props, clientJobList];
} }

View File

@ -0,0 +1,114 @@
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
part 'provider_volume_state.dart';
class ApiProviderVolumeCubit
extends ServerInstallationDependendCubit<ApiProviderVolumeState> {
ApiProviderVolumeCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, const ApiProviderVolumeState.initial()) {
final serverDetails = getIt<ApiConfigModel>().serverDetails;
providerApi = serverDetails == null
? null
: VolumeApiFactoryCreator.createVolumeProviderApiFactory(
getIt<ApiConfigModel>().serverDetails!.provider,
);
}
VolumeProviderApiFactory? providerApi;
@override
Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
_refetch();
}
}
Future<double?> getPricePerGb() async =>
providerApi!.getVolumeProvider().getPricePerGb();
Future<void> refresh() async {
emit(const ApiProviderVolumeState([], LoadingStatus.refreshing));
_refetch();
}
Future<void> _refetch() async {
if (providerApi == null) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error));
}
final List<ServerVolume> volumes =
await providerApi!.getVolumeProvider().getVolumes();
if (volumes.isEmpty) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error));
}
emit(ApiProviderVolumeState(volumes, LoadingStatus.success));
}
Future<void> attachVolume(final DiskVolume volume) async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
await providerApi!
.getVolumeProvider()
.attachVolume(volume.providerVolume!.id, server.id);
refresh();
}
Future<void> detachVolume(final DiskVolume volume) async {
await providerApi!
.getVolumeProvider()
.detachVolume(volume.providerVolume!.id);
refresh();
}
Future<bool> resizeVolume(
final DiskVolume volume,
final int newSizeGb,
) async {
final bool resized = await providerApi!.getVolumeProvider().resizeVolume(
volume.providerVolume!.id,
newSizeGb,
);
if (!resized) {
return false;
}
await ServerApi().resizeVolume(volume.name);
refresh();
return true;
}
Future<void> createVolume() async {
final ServerVolume? volume =
await providerApi!.getVolumeProvider().createVolume();
final diskVolume = DiskVolume();
diskVolume.providerVolume = volume;
await attachVolume(diskVolume);
await Future.delayed(const Duration(seconds: 10));
await ServerApi().mountVolume(volume!.name);
refresh();
}
Future<void> deleteVolume(final DiskVolume volume) async {
await providerApi!
.getVolumeProvider()
.deleteVolume(volume.providerVolume!.id);
refresh();
}
@override
void clear() {
emit(const ApiProviderVolumeState.initial());
}
}

View File

@ -0,0 +1,24 @@
part of 'provider_volume_cubit.dart';
class ApiProviderVolumeState extends ServerInstallationDependendState {
const ApiProviderVolumeState(this._volumes, this.status);
const ApiProviderVolumeState.initial()
: this(const [], LoadingStatus.uninitialized);
final List<ServerVolume> _volumes;
final LoadingStatus status;
List<ServerVolume> get volumes => _volumes;
ApiProviderVolumeState copyWith({
final List<ServerVolume>? volumes,
final LoadingStatus? status,
}) =>
ApiProviderVolumeState(
volumes ?? _volumes,
status ?? this.status,
);
@override
List<Object?> get props => [_volumes];
}

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';

View File

@ -1,5 +1,5 @@
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart';

View File

@ -4,10 +4,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
@ -49,13 +53,40 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
} }
RegExp getServerProviderApiTokenValidation() =>
repository.serverProviderApiFactory!
.getServerProvider()
.getApiTokenValidation();
RegExp getDnsProviderApiTokenValidation() => repository.dnsProviderApiFactory!
.getDnsProvider()
.getApiTokenValidation();
Future<bool> isServerProviderApiTokenValid(
final String providerToken,
) async =>
repository.serverProviderApiFactory!
.getServerProvider(
settings: const ProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken);
Future<bool> isDnsProviderApiTokenValid(
final String providerToken,
) async =>
repository.dnsProviderApiFactory!
.getDnsProvider(
settings: const DnsProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken);
void setHetznerKey(final String hetznerKey) async { void setHetznerKey(final String hetznerKey) async {
await repository.saveHetznerKey(hetznerKey); await repository.saveHetznerKey(hetznerKey);
if (state is ServerInstallationRecovery) { if (state is ServerInstallationRecovery) {
emit( emit(
(state as ServerInstallationRecovery).copyWith( (state as ServerInstallationRecovery).copyWith(
hetznerKey: hetznerKey, providerApiToken: hetznerKey,
currentStep: RecoveryStep.serverSelection, currentStep: RecoveryStep.serverSelection,
), ),
); );
@ -63,7 +94,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
emit( emit(
(state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey), (state as ServerInstallationNotFinished).copyWith(
providerApiToken: hetznerKey,
),
); );
} }
@ -117,7 +150,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
Future<void> onSuccess(final ServerHostingDetails serverDetails) async { Future<void> onSuccess(final ServerHostingDetails serverDetails) async {
await repository.createDnsRecords( await repository.createDnsRecords(
serverDetails.ip4, serverDetails,
state.serverDomain!, state.serverDomain!,
onCancel: onCancel, onCancel: onCancel,
); );
@ -167,6 +200,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final ServerHostingDetails server = await repository.startServer( final ServerHostingDetails server = await repository.startServer(
dataState.serverDetails!, dataState.serverDetails!,
); );
await repository.saveServerDetails(server); await repository.saveServerDetails(server);
await repository.saveIsServerStarted(true); await repository.saveIsServerStarted(true);
@ -292,10 +326,22 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final bool isServerWorking = await repository.isHttpServerWorking(); final bool isServerWorking = await repository.isHttpServerWorking();
if (isServerWorking) { if (isServerWorking) {
await repository.createDkimRecord(dataState.serverDomain!); bool dkimCreated = true;
await repository.saveHasFinalChecked(true); try {
await repository.createDkimRecord(dataState.serverDomain!);
emit(dataState.finish()); } catch (e) {
dkimCreated = false;
}
if (dkimCreated) {
await repository.saveHasFinalChecked(true);
emit(dataState.finish());
} else {
runDelayed(
finishCheckIfServerIsOkay,
const Duration(seconds: 60),
dataState,
);
}
} else { } else {
runDelayed( runDelayed(
finishCheckIfServerIsOkay, finishCheckIfServerIsOkay,
@ -417,11 +463,11 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
), ),
); );
break; break;
case RecoveryStep.serverSelection: case RecoveryStep.cloudflareToken:
repository.deleteHetznerKey(); repository.deleteServerDetails();
emit( emit(
dataState.copyWith( dataState.copyWith(
currentStep: RecoveryStep.hetznerToken, currentStep: RecoveryStep.serverSelection,
), ),
); );
break; break;
@ -464,7 +510,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final ServerInstallationRecovery dataState = final ServerInstallationRecovery dataState =
state as ServerInstallationRecovery; state as ServerInstallationRecovery;
final List<ServerBasicInfo> servers = final List<ServerBasicInfo> servers =
await repository.getServersOnHetznerAccount(); await repository.getServersOnProviderAccount();
final Iterable<ServerBasicInfoWithValidators> validated = servers.map( final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
(final ServerBasicInfo server) => (final ServerBasicInfo server) =>
ServerBasicInfoWithValidators.fromServerBasicInfo( ServerBasicInfoWithValidators.fromServerBasicInfo(
@ -491,6 +537,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
volume: ServerVolume( volume: ServerVolume(
id: server.volumeId, id: server.volumeId,
name: 'recovered_volume', name: 'recovered_volume',
sizeByte: 0,
serverId: server.id,
linuxDevice: '',
), ),
apiToken: dataState.serverDetails!.apiToken, apiToken: dataState.serverDetails!.apiToken,
provider: ServerProvider.hetzner, provider: ServerProvider.hetzner,
@ -564,7 +613,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
print('================================'); print('================================');
print('ServerInstallationState changed!'); print('ServerInstallationState changed!');
print('Current type: ${change.nextState.runtimeType}'); print('Current type: ${change.nextState.runtimeType}');
print('Hetzner key: ${change.nextState.hetznerKey}'); print('Hetzner key: ${change.nextState.providerApiToken}');
print('Cloudflare key: ${change.nextState.cloudFlareKey}'); print('Cloudflare key: ${change.nextState.cloudFlareKey}');
print('Domain: ${change.nextState.serverDomain}'); print('Domain: ${change.nextState.serverDomain}');
print('BackblazeCredential: ${change.nextState.backblazeCredential}'); print('BackblazeCredential: ${change.nextState.backblazeCredential}');
@ -597,7 +646,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
await repository.deleteServerRelatedRecords(); await repository.deleteServerRelatedRecords();
emit( emit(
ServerInstallationNotFinished( ServerInstallationNotFinished(
hetznerKey: state.hetznerKey, providerApiToken: state.providerApiToken,
serverDomain: state.serverDomain, serverDomain: state.serverDomain,
cloudFlareKey: state.cloudFlareKey, cloudFlareKey: state.cloudFlareKey,
backblazeCredential: state.backblazeCredential, backblazeCredential: state.backblazeCredential,

View File

@ -9,16 +9,18 @@ import 'package:hive/hive.dart';
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/device_token.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/logic/models/message.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/ui/components/action_button/action_button.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart';
@ -39,9 +41,17 @@ class ServerAuthorizationException implements Exception {
class ServerInstallationRepository { class ServerInstallationRepository {
Box box = Hive.box(BNames.serverInstallationBox); Box box = Hive.box(BNames.serverInstallationBox);
Box<User> usersBox = Hive.box(BNames.usersBox); Box<User> usersBox = Hive.box(BNames.usersBox);
ServerProviderApiFactory? serverProviderApiFactory =
ApiFactoryCreator.createServerProviderApiFactory(
ServerProvider.hetzner, // TODO: HARDCODE FOR NOW!!!
); // TODO: Remove when provider selection is implemented.
DnsProviderApiFactory? dnsProviderApiFactory =
ApiFactoryCreator.createDnsProviderApiFactory(
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
);
Future<ServerInstallationState> load() async { Future<ServerInstallationState> load() async {
final String? hetznerToken = getIt<ApiConfigModel>().hetznerKey; final String? providerApiToken = getIt<ApiConfigModel>().hetznerKey;
final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey; final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain; final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
final BackblazeCredential? backblazeCredential = final BackblazeCredential? backblazeCredential =
@ -49,9 +59,23 @@ class ServerInstallationRepository {
final ServerHostingDetails? serverDetails = final ServerHostingDetails? serverDetails =
getIt<ApiConfigModel>().serverDetails; getIt<ApiConfigModel>().serverDetails;
if (serverDetails != null &&
serverDetails.provider != ServerProvider.unknown) {
serverProviderApiFactory =
ApiFactoryCreator.createServerProviderApiFactory(
serverDetails.provider,
);
}
if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) {
dnsProviderApiFactory = ApiFactoryCreator.createDnsProviderApiFactory(
serverDomain.provider,
);
}
if (box.get(BNames.hasFinalChecked, defaultValue: false)) { if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
return ServerInstallationFinished( return ServerInstallationFinished(
hetznerKey: hetznerToken!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudflareToken!, cloudFlareKey: cloudflareToken!,
serverDomain: serverDomain!, serverDomain: serverDomain!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
@ -68,14 +92,14 @@ class ServerInstallationRepository {
if (box.get(BNames.isRecoveringServer, defaultValue: false) && if (box.get(BNames.isRecoveringServer, defaultValue: false) &&
serverDomain != null) { serverDomain != null) {
return ServerInstallationRecovery( return ServerInstallationRecovery(
hetznerKey: hetznerToken, providerApiToken: providerApiToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
serverDomain: serverDomain, serverDomain: serverDomain,
backblazeCredential: backblazeCredential, backblazeCredential: backblazeCredential,
serverDetails: serverDetails, serverDetails: serverDetails,
rootUser: box.get(BNames.rootUser), rootUser: box.get(BNames.rootUser),
currentStep: _getCurrentRecoveryStep( currentStep: _getCurrentRecoveryStep(
hetznerToken, providerApiToken,
cloudflareToken, cloudflareToken,
serverDomain, serverDomain,
serverDetails, serverDetails,
@ -85,7 +109,7 @@ class ServerInstallationRepository {
} }
return ServerInstallationNotFinished( return ServerInstallationNotFinished(
hetznerKey: hetznerToken, providerApiToken: providerApiToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
serverDomain: serverDomain, serverDomain: serverDomain,
backblazeCredential: backblazeCredential, backblazeCredential: backblazeCredential,
@ -130,20 +154,24 @@ class ServerInstallationRepository {
Future<ServerHostingDetails> startServer( Future<ServerHostingDetails> startServer(
final ServerHostingDetails hetznerServer, final ServerHostingDetails hetznerServer,
) async { ) async {
final HetznerApi hetznerApi = HetznerApi(); ServerHostingDetails serverDetails;
final ServerHostingDetails serverDetails = await hetznerApi.powerOn();
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
serverDetails = await api.powerOn();
return serverDetails; return serverDetails;
} }
Future<String?> getDomainId(final String token, final String domain) async { Future<String?> getDomainId(final String token, final String domain) async {
final CloudflareApi cloudflareApi = CloudflareApi( final DnsProviderApi dnsProviderApi = dnsProviderApiFactory!.getDnsProvider(
isWithToken: false, settings: DnsProviderApiSettings(
customToken: token, isWithToken: false,
customToken: token,
),
); );
try { try {
final String domainId = await cloudflareApi.getZoneId(domain); final String domainId = await dnsProviderApi.getZoneId(domain);
return domainId; return domainId;
} on DomainNotFoundException { } on DomainNotFoundException {
return null; return null;
@ -208,18 +236,14 @@ class ServerInstallationRepository {
required final Future<void> Function(ServerHostingDetails serverDetails) required final Future<void> Function(ServerHostingDetails serverDetails)
onSuccess, onSuccess,
}) async { }) async {
final HetznerApi hetznerApi = HetznerApi(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
late ServerVolume dataBase;
try { try {
dataBase = await hetznerApi.createVolume(); final ServerHostingDetails? serverDetails = await api.createServer(
dnsApiToken: cloudFlareKey,
final ServerHostingDetails? serverDetails = await hetznerApi.createServer(
cloudFlareKey: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
dataBase: dataBase,
); );
if (serverDetails == null) { if (serverDetails == null) {
print('Server is not initialized!'); print('Server is not initialized!');
return; return;
@ -238,17 +262,58 @@ class ServerInstallationRepository {
text: 'basis.delete'.tr(), text: 'basis.delete'.tr(),
isRed: true, isRed: true,
onPressed: () async { onPressed: () async {
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await api.deleteServer(
domainName: domainName, domainName: domainName,
); );
final ServerHostingDetails? serverDetails = ServerHostingDetails? serverDetails;
await hetznerApi.createServer( try {
cloudFlareKey: cloudFlareKey, serverDetails = await api.createServer(
rootUser: rootUser, dnsApiToken: cloudFlareKey,
domainName: domainName, rootUser: rootUser,
dataBase: dataBase, domainName: domainName,
); );
} catch (e) {
print(e);
}
if (serverDetails == null) {
print('Server is not initialized!');
return;
}
await saveServerDetails(serverDetails);
onSuccess(serverDetails);
},
),
ActionButton(
text: 'basis.cancel'.tr(),
onPressed: onCancel,
),
],
),
);
} else if (e.response!.data['error']['code'] == 'resource_unavailable') {
final NavigationService nav = getIt.get<NavigationService>();
nav.showPopUpDialog(
BrandAlert(
title: 'modals.1_1'.tr(),
contentText: 'modals.2_2'.tr(),
actions: [
ActionButton(
text: 'modals.7'.tr(),
isRed: true,
onPressed: () async {
ServerHostingDetails? serverDetails;
try {
serverDetails = await api.createServer(
dnsApiToken: cloudFlareKey,
rootUser: rootUser,
domainName: domainName,
);
} catch (e) {
print(e);
}
if (serverDetails == null) { if (serverDetails == null) {
print('Server is not initialized!'); print('Server is not initialized!');
return; return;
@ -268,25 +333,27 @@ class ServerInstallationRepository {
} }
} }
Future<void> createDnsRecords( Future<bool> createDnsRecords(
final String ip4, final ServerHostingDetails serverDetails,
final ServerDomain cloudFlareDomain, { final ServerDomain domain, {
required final void Function() onCancel, required final void Function() onCancel,
}) async { }) async {
final CloudflareApi cloudflareApi = CloudflareApi(); final DnsProviderApi dnsProviderApi =
dnsProviderApiFactory!.getDnsProvider();
final ServerProviderApi serverApi =
serverProviderApiFactory!.getServerProvider();
await cloudflareApi.removeSimilarRecords( await dnsProviderApi.removeSimilarRecords(
ip4: ip4, ip4: serverDetails.ip4,
cloudFlareDomain: cloudFlareDomain, domain: domain,
); );
try { try {
await cloudflareApi.createMultipleDnsRecords( await dnsProviderApi.createMultipleDnsRecords(
ip4: ip4, ip4: serverDetails.ip4,
cloudFlareDomain: cloudFlareDomain, domain: domain,
); );
} on DioError catch (e) { } on DioError catch (e) {
final HetznerApi hetznerApi = HetznerApi();
final NavigationService nav = getIt.get<NavigationService>(); final NavigationService nav = getIt.get<NavigationService>();
nav.showPopUpDialog( nav.showPopUpDialog(
BrandAlert( BrandAlert(
@ -299,8 +366,8 @@ class ServerInstallationRepository {
text: 'basis.delete'.tr(), text: 'basis.delete'.tr(),
isRed: true, isRed: true,
onPressed: () async { onPressed: () async {
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await serverApi.deleteServer(
domainName: cloudFlareDomain.domainName, domainName: domain.domainName,
); );
onCancel(); onCancel();
@ -313,42 +380,46 @@ class ServerInstallationRepository {
], ],
), ),
); );
return false;
} }
await HetznerApi().createReverseDns( await serverApi.createReverseDns(
ip4: ip4, serverDetails: serverDetails,
domainName: cloudFlareDomain.domainName, domain: domain,
); );
return true;
} }
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async { Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
final CloudflareApi cloudflareApi = CloudflareApi(); final DnsProviderApi dnsProviderApi =
dnsProviderApiFactory!.getDnsProvider();
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final String? dkimRecordString = await api.getDkim(); String dkimRecordString = '';
try {
dkimRecordString = await api.getDkim();
} catch (e) {
print(e);
rethrow;
}
await cloudflareApi.setDkim(dkimRecordString ?? '', cloudFlareDomain); await dnsProviderApi.setDkim(dkimRecordString, cloudFlareDomain);
} }
Future<bool> isHttpServerWorking() async { Future<bool> isHttpServerWorking() async {
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final bool isHttpServerWorking = await api.isHttpServerWorking(); return api.isHttpServerWorking();
try {
await api.getDkim();
} catch (e) {
return false;
}
return isHttpServerWorking;
} }
Future<ServerHostingDetails> restart() async { Future<ServerHostingDetails> restart() async {
final HetznerApi hetznerApi = HetznerApi(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
return hetznerApi.reset(); return api.restart();
} }
Future<ServerHostingDetails> powerOn() async { Future<ServerHostingDetails> powerOn() async {
final HetznerApi hetznerApi = HetznerApi(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
return hetznerApi.powerOn(); return api.powerOn();
} }
Future<ServerRecoveryCapabilities> getRecoveryCapabilities( Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
@ -439,6 +510,9 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
linuxDevice: '',
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -473,6 +547,9 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
linuxDevice: '',
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -507,6 +584,9 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
serverId: 0,
sizeByte: 0,
linuxDevice: '',
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -532,6 +612,9 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
linuxDevice: '',
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -575,21 +658,9 @@ class ServerInstallationRepository {
} }
} }
Future<List<ServerBasicInfo>> getServersOnHetznerAccount() async { Future<List<ServerBasicInfo>> getServersOnProviderAccount() async {
final HetznerApi hetznerApi = HetznerApi(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
final List<HetznerServerInfo> servers = await hetznerApi.getServers(); return api.getServers();
return servers
.map(
(final HetznerServerInfo server) => ServerBasicInfo(
id: server.id,
name: server.name,
ip: server.publicNet.ipv4.ip,
reverseDns: server.publicNet.ipv4.reverseDns,
created: server.created,
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
),
)
.toList();
} }
Future<void> saveServerDetails( Future<void> saveServerDetails(
@ -598,6 +669,11 @@ class ServerInstallationRepository {
await getIt<ApiConfigModel>().storeServerDetails(serverDetails); await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
} }
Future<void> deleteServerDetails() async {
await box.delete(BNames.serverDetails);
getIt<ApiConfigModel>().init();
}
Future<void> saveHetznerKey(final String key) async { Future<void> saveHetznerKey(final String key) async {
print('saved'); print('saved');
await getIt<ApiConfigModel>().storeHetznerKey(key); await getIt<ApiConfigModel>().storeHetznerKey(key);
@ -614,10 +690,20 @@ class ServerInstallationRepository {
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential); await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
} }
Future<void> deleteBackblazeKey() async {
await box.delete(BNames.backblazeCredential);
getIt<ApiConfigModel>().init();
}
Future<void> saveCloudFlareKey(final String key) async { Future<void> saveCloudFlareKey(final String key) async {
await getIt<ApiConfigModel>().storeCloudFlareKey(key); await getIt<ApiConfigModel>().storeCloudFlareKey(key);
} }
Future<void> deleteCloudFlareKey() async {
await box.delete(BNames.cloudFlareKey);
getIt<ApiConfigModel>().init();
}
Future<void> saveDomain(final ServerDomain serverDomain) async { Future<void> saveDomain(final ServerDomain serverDomain) async {
await getIt<ApiConfigModel>().storeServerDomain(serverDomain); await getIt<ApiConfigModel>().storeServerDomain(serverDomain);
} }
@ -652,10 +738,11 @@ class ServerInstallationRepository {
} }
Future<void> deleteServer(final ServerDomain serverDomain) async { Future<void> deleteServer(final ServerDomain serverDomain) async {
final HetznerApi hetznerApi = HetznerApi(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
final CloudflareApi cloudFlare = CloudflareApi(); final DnsProviderApi dnsProviderApi =
dnsProviderApiFactory!.getDnsProvider();
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await api.deleteServer(
domainName: serverDomain.domainName, domainName: serverDomain.domainName,
); );
@ -666,7 +753,7 @@ class ServerInstallationRepository {
await box.put(BNames.isLoading, false); await box.put(BNames.isLoading, false);
await box.put(BNames.serverDetails, null); await box.put(BNames.serverDetails, null);
await cloudFlare.removeSimilarRecords(cloudFlareDomain: serverDomain); await dnsProviderApi.removeSimilarRecords(domain: serverDomain);
} }
Future<void> deleteServerRelatedRecords() async { Future<void> deleteServerRelatedRecords() async {

View File

@ -2,7 +2,7 @@ part of '../server_installation/server_installation_cubit.dart';
abstract class ServerInstallationState extends Equatable { abstract class ServerInstallationState extends Equatable {
const ServerInstallationState({ const ServerInstallationState({
required this.hetznerKey, required this.providerApiToken,
required this.cloudFlareKey, required this.cloudFlareKey,
required this.backblazeCredential, required this.backblazeCredential,
required this.serverDomain, required this.serverDomain,
@ -15,7 +15,7 @@ abstract class ServerInstallationState extends Equatable {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -25,7 +25,7 @@ abstract class ServerInstallationState extends Equatable {
isServerResetedFirstTime, isServerResetedFirstTime,
]; ];
final String? hetznerKey; final String? providerApiToken;
final String? cloudFlareKey; final String? cloudFlareKey;
final BackblazeCredential? backblazeCredential; final BackblazeCredential? backblazeCredential;
final ServerDomain? serverDomain; final ServerDomain? serverDomain;
@ -35,11 +35,11 @@ abstract class ServerInstallationState extends Equatable {
final bool isServerResetedFirstTime; final bool isServerResetedFirstTime;
final bool isServerResetedSecondTime; final bool isServerResetedSecondTime;
bool get isHetznerFilled => hetznerKey != null; bool get isServerProviderFilled => providerApiToken != null;
bool get isCloudFlareFilled => cloudFlareKey != null; bool get isDnsProviderFilled => cloudFlareKey != null;
bool get isBackblazeFilled => backblazeCredential != null; bool get isBackupsProviderFilled => backblazeCredential != null;
bool get isDomainFilled => serverDomain != null; bool get isDomainSelected => serverDomain != null;
bool get isUserFilled => rootUser != null; bool get isPrimaryUserFilled => rootUser != null;
bool get isServerCreated => serverDetails != null; bool get isServerCreated => serverDetails != null;
bool get isFullyInitilized => _fulfilementList.every((final el) => el!); bool get isFullyInitilized => _fulfilementList.every((final el) => el!);
@ -58,11 +58,11 @@ abstract class ServerInstallationState extends Equatable {
List<bool?> get _fulfilementList { List<bool?> get _fulfilementList {
final List<bool> res = [ final List<bool> res = [
isHetznerFilled, isServerProviderFilled,
isCloudFlareFilled, isDnsProviderFilled,
isBackblazeFilled, isBackupsProviderFilled,
isDomainFilled, isDomainSelected,
isUserFilled, isPrimaryUserFilled,
isServerCreated, isServerCreated,
isServerStarted, isServerStarted,
isServerResetedFirstTime, isServerResetedFirstTime,
@ -80,7 +80,7 @@ class TimerState extends ServerInstallationNotFinished {
this.timerStart, this.timerStart,
this.duration, this.duration,
}) : super( }) : super(
hetznerKey: dataState.hetznerKey, providerApiToken: dataState.providerApiToken,
cloudFlareKey: dataState.cloudFlareKey, cloudFlareKey: dataState.cloudFlareKey,
backblazeCredential: dataState.backblazeCredential, backblazeCredential: dataState.backblazeCredential,
serverDomain: dataState.serverDomain, serverDomain: dataState.serverDomain,
@ -124,7 +124,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
required final super.isServerResetedSecondTime, required final super.isServerResetedSecondTime,
required final this.isLoading, required final this.isLoading,
required this.dnsMatches, required this.dnsMatches,
final super.hetznerKey, final super.providerApiToken,
final super.cloudFlareKey, final super.cloudFlareKey,
final super.backblazeCredential, final super.backblazeCredential,
final super.serverDomain, final super.serverDomain,
@ -136,7 +136,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -149,7 +149,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
]; ];
ServerInstallationNotFinished copyWith({ ServerInstallationNotFinished copyWith({
final String? hetznerKey, final String? providerApiToken,
final String? cloudFlareKey, final String? cloudFlareKey,
final BackblazeCredential? backblazeCredential, final BackblazeCredential? backblazeCredential,
final ServerDomain? serverDomain, final ServerDomain? serverDomain,
@ -162,7 +162,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
final Map<String, bool>? dnsMatches, final Map<String, bool>? dnsMatches,
}) => }) =>
ServerInstallationNotFinished( ServerInstallationNotFinished(
hetznerKey: hetznerKey ?? this.hetznerKey, providerApiToken: providerApiToken ?? this.providerApiToken,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
backblazeCredential: backblazeCredential ?? this.backblazeCredential, backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain, serverDomain: serverDomain ?? this.serverDomain,
@ -178,7 +178,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
); );
ServerInstallationFinished finish() => ServerInstallationFinished( ServerInstallationFinished finish() => ServerInstallationFinished(
hetznerKey: hetznerKey!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudFlareKey!, cloudFlareKey: cloudFlareKey!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!, serverDomain: serverDomain!,
@ -193,7 +193,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
class ServerInstallationEmpty extends ServerInstallationNotFinished { class ServerInstallationEmpty extends ServerInstallationNotFinished {
const ServerInstallationEmpty() const ServerInstallationEmpty()
: super( : super(
hetznerKey: null, providerApiToken: null,
cloudFlareKey: null, cloudFlareKey: null,
backblazeCredential: null, backblazeCredential: null,
serverDomain: null, serverDomain: null,
@ -209,7 +209,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
class ServerInstallationFinished extends ServerInstallationState { class ServerInstallationFinished extends ServerInstallationState {
const ServerInstallationFinished({ const ServerInstallationFinished({
required final String super.hetznerKey, required final String super.providerApiToken,
required final String super.cloudFlareKey, required final String super.cloudFlareKey,
required final BackblazeCredential super.backblazeCredential, required final BackblazeCredential super.backblazeCredential,
required final ServerDomain super.serverDomain, required final ServerDomain super.serverDomain,
@ -222,7 +222,7 @@ class ServerInstallationFinished extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -260,7 +260,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
const ServerInstallationRecovery({ const ServerInstallationRecovery({
required this.currentStep, required this.currentStep,
required this.recoveryCapabilities, required this.recoveryCapabilities,
final super.hetznerKey, final super.providerApiToken,
final super.cloudFlareKey, final super.cloudFlareKey,
final super.backblazeCredential, final super.backblazeCredential,
final super.serverDomain, final super.serverDomain,
@ -276,7 +276,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -288,7 +288,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
]; ];
ServerInstallationRecovery copyWith({ ServerInstallationRecovery copyWith({
final String? hetznerKey, final String? providerApiToken,
final String? cloudFlareKey, final String? cloudFlareKey,
final BackblazeCredential? backblazeCredential, final BackblazeCredential? backblazeCredential,
final ServerDomain? serverDomain, final ServerDomain? serverDomain,
@ -298,7 +298,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
final ServerRecoveryCapabilities? recoveryCapabilities, final ServerRecoveryCapabilities? recoveryCapabilities,
}) => }) =>
ServerInstallationRecovery( ServerInstallationRecovery(
hetznerKey: hetznerKey ?? this.hetznerKey, providerApiToken: providerApiToken ?? this.providerApiToken,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
backblazeCredential: backblazeCredential ?? this.backblazeCredential, backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain, serverDomain: serverDomain ?? this.serverDomain,
@ -309,7 +309,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
); );
ServerInstallationFinished finish() => ServerInstallationFinished( ServerInstallationFinished finish() => ServerInstallationFinished(
hetznerKey: hetznerKey!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudFlareKey!, cloudFlareKey: cloudFlareKey!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!, serverDomain: serverDomain!,

View File

@ -0,0 +1,36 @@
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
part 'server_volume_state.dart';
class ApiServerVolumeCubit
extends ServerInstallationDependendCubit<ApiServerVolumeState> {
ApiServerVolumeCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, const ApiServerVolumeState.initial());
final ServerApi serverApi = ServerApi();
@override
Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
_refetch();
}
}
Future<void> _refetch() async {
final List<ServerDiskVolume> volumes =
await serverApi.getServerDiskVolumes();
if (volumes.isNotEmpty) {
emit(ApiServerVolumeState(volumes, LoadingStatus.success));
} else {
emit(const ApiServerVolumeState([], LoadingStatus.error));
}
}
@override
void clear() {
emit(const ApiServerVolumeState.initial());
}
}

View File

@ -0,0 +1,24 @@
part of 'server_volume_cubit.dart';
class ApiServerVolumeState extends ServerInstallationDependendState {
const ApiServerVolumeState(this._volumes, this.status);
const ApiServerVolumeState.initial()
: this(const [], LoadingStatus.uninitialized);
final List<ServerDiskVolume> _volumes;
final LoadingStatus status;
List<ServerDiskVolume> get volumes => _volumes;
ApiServerVolumeState copyWith({
final List<ServerDiskVolume>? volumes,
final LoadingStatus? status,
}) =>
ApiServerVolumeState(
volumes ?? _volumes,
status ?? this.status,
);
@override
List<Object?> get props => [_volumes];
}

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';

View File

@ -3,7 +3,7 @@ import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';

View File

@ -34,7 +34,6 @@ class ApiConfigModel {
Future<void> storeBackblazeCredential(final BackblazeCredential value) async { Future<void> storeBackblazeCredential(final BackblazeCredential value) async {
await _box.put(BNames.backblazeCredential, value); await _box.put(BNames.backblazeCredential, value);
_backblazeCredential = value; _backblazeCredential = value;
} }
@ -64,7 +63,6 @@ class ApiConfigModel {
void init() { void init() {
_hetznerKey = _box.get(BNames.hetznerKey); _hetznerKey = _box.get(BNames.hetznerKey);
_cloudFlareKey = _box.get(BNames.cloudFlareKey); _cloudFlareKey = _box.get(BNames.cloudFlareKey);
_backblazeCredential = _box.get(BNames.backblazeCredential); _backblazeCredential = _box.get(BNames.backblazeCredential);
_serverDomain = _box.get(BNames.serverDomain); _serverDomain = _box.get(BNames.serverDomain);

View File

@ -0,0 +1,9 @@
class DiskSize {
DiskSize({final this.byte = 0});
double asKb() => byte / 1000.0;
double asMb() => byte / 1000000.0;
double asGb() => byte / 1000000000.0;
int byte;
}

View File

@ -55,12 +55,21 @@ class ServerVolume {
ServerVolume({ ServerVolume({
required this.id, required this.id,
required this.name, required this.name,
required this.sizeByte,
required this.serverId,
required this.linuxDevice,
}); });
@HiveField(1) @HiveField(1)
int id; int id;
@HiveField(2) @HiveField(2)
String name; String name;
@HiveField(3, defaultValue: 10737418240) // 10 Gb
int sizeByte;
@HiveField(4, defaultValue: null)
int? serverId;
@HiveField(5, defaultValue: null)
String? linuxDevice;
} }
@HiveType(typeId: 101) @HiveType(typeId: 101)

View File

@ -73,17 +73,26 @@ class ServerVolumeAdapter extends TypeAdapter<ServerVolume> {
return ServerVolume( return ServerVolume(
id: fields[1] as int, id: fields[1] as int,
name: fields[2] as String, name: fields[2] as String,
sizeByte: fields[3] == null ? 10737418240 : fields[3] as int,
serverId: fields[4] as int?,
linuxDevice: fields[5] as String?,
); );
} }
@override @override
void write(BinaryWriter writer, ServerVolume obj) { void write(BinaryWriter writer, ServerVolume obj) {
writer writer
..writeByte(2) ..writeByte(5)
..writeByte(1) ..writeByte(1)
..write(obj.id) ..write(obj.id)
..writeByte(2) ..writeByte(2)
..write(obj.name); ..write(obj.name)
..writeByte(3)
..write(obj.sizeByte)
..writeByte(4)
..write(obj.serverId)
..writeByte(5)
..write(obj.linuxDevice);
} }
@override @override

View File

@ -11,9 +11,9 @@ class UserAdapter extends TypeAdapter<User> {
final int typeId = 1; final int typeId = 1;
@override @override
User read(final BinaryReader reader) { User read(BinaryReader reader) {
final int numOfFields = reader.readByte(); final numOfFields = reader.readByte();
final Map<int, dynamic> fields = <int, dynamic>{ final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
}; };
return User( return User(
@ -26,7 +26,7 @@ class UserAdapter extends TypeAdapter<User> {
} }
@override @override
void write(final BinaryWriter writer, final User obj) { void write(BinaryWriter writer, User obj) {
writer writer
..writeByte(5) ..writeByte(5)
..writeByte(0) ..writeByte(0)
@ -45,7 +45,7 @@ class UserAdapter extends TypeAdapter<User> {
int get hashCode => typeId.hashCode; int get hashCode => typeId.hashCode;
@override @override
bool operator ==(final Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is UserAdapter && other is UserAdapter &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&

View File

@ -7,8 +7,8 @@ import 'package:selfprivacy/utils/password_generator.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
@immutable @immutable
class Job extends Equatable { class ClientJob extends Equatable {
Job({ ClientJob({
required this.title, required this.title,
final String? id, final String? id,
}) : id = id ?? StringGenerators.simpleId(); }) : id = id ?? StringGenerators.simpleId();
@ -20,7 +20,7 @@ class Job extends Equatable {
List<Object> get props => [id, title]; List<Object> get props => [id, title];
} }
class CreateUserJob extends Job { class CreateUserJob extends ClientJob {
CreateUserJob({ CreateUserJob({
required this.user, required this.user,
}) : super(title: '${"jobs.createUser".tr()} ${user.login}'); }) : super(title: '${"jobs.createUser".tr()} ${user.login}');
@ -31,7 +31,7 @@ class CreateUserJob extends Job {
List<Object> get props => [id, title, user]; List<Object> get props => [id, title, user];
} }
class DeleteUserJob extends Job { class DeleteUserJob extends ClientJob {
DeleteUserJob({ DeleteUserJob({
required this.user, required this.user,
}) : super(title: '${"jobs.deleteUser".tr()} ${user.login}'); }) : super(title: '${"jobs.deleteUser".tr()} ${user.login}');
@ -42,7 +42,7 @@ class DeleteUserJob extends Job {
List<Object> get props => [id, title, user]; List<Object> get props => [id, title, user];
} }
class ToggleJob extends Job { class ToggleJob extends ClientJob {
ToggleJob({ ToggleJob({
required this.type, required this.type,
required final super.title, required final super.title,
@ -66,7 +66,7 @@ class ServiceToggleJob extends ToggleJob {
final bool needToTurnOn; final bool needToTurnOn;
} }
class CreateSSHKeyJob extends Job { class CreateSSHKeyJob extends ClientJob {
CreateSSHKeyJob({ CreateSSHKeyJob({
required this.user, required this.user,
required this.publicKey, required this.publicKey,
@ -79,7 +79,7 @@ class CreateSSHKeyJob extends Job {
List<Object> get props => [id, title, user, publicKey]; List<Object> get props => [id, title, user, publicKey];
} }
class DeleteSSHKeyJob extends Job { class DeleteSSHKeyJob extends ClientJob {
DeleteSSHKeyJob({ DeleteSSHKeyJob({
required this.user, required this.user,
required this.publicKey, required this.publicKey,

View File

@ -11,3 +11,9 @@ ApiToken _$ApiTokenFromJson(Map<String, dynamic> json) => ApiToken(
date: DateTime.parse(json['date'] as String), date: DateTime.parse(json['date'] as String),
isCaller: json['is_caller'] as bool, isCaller: json['is_caller'] as bool,
); );
Map<String, dynamic> _$ApiTokenToJson(ApiToken instance) => <String, dynamic>{
'name': instance.name,
'date': instance.date.toIso8601String(),
'is_caller': instance.isCaller,
};

View File

@ -11,12 +11,24 @@ Backup _$BackupFromJson(Map<String, dynamic> json) => Backup(
id: json['short_id'] as String, id: json['short_id'] as String,
); );
Map<String, dynamic> _$BackupToJson(Backup instance) => <String, dynamic>{
'time': instance.time.toIso8601String(),
'short_id': instance.id,
};
BackupStatus _$BackupStatusFromJson(Map<String, dynamic> json) => BackupStatus( BackupStatus _$BackupStatusFromJson(Map<String, dynamic> json) => BackupStatus(
status: $enumDecode(_$BackupStatusEnumEnumMap, json['status']), status: $enumDecode(_$BackupStatusEnumEnumMap, json['status']),
progress: (json['progress'] as num).toDouble(), progress: (json['progress'] as num).toDouble(),
errorMessage: json['error_message'] as String?, errorMessage: json['error_message'] as String?,
); );
Map<String, dynamic> _$BackupStatusToJson(BackupStatus instance) =>
<String, dynamic>{
'status': _$BackupStatusEnumEnumMap[instance.status],
'progress': instance.progress,
'error_message': instance.errorMessage,
};
const _$BackupStatusEnumEnumMap = { const _$BackupStatusEnumEnumMap = {
BackupStatusEnum.noKey: 'NO_KEY', BackupStatusEnum.noKey: 'NO_KEY',
BackupStatusEnum.notInitialized: 'NOT_INITIALIZED', BackupStatusEnum.notInitialized: 'NOT_INITIALIZED',

View File

@ -10,3 +10,9 @@ DeviceToken _$DeviceTokenFromJson(Map<String, dynamic> json) => DeviceToken(
device: json['device'] as String, device: json['device'] as String,
token: json['token'] as String, token: json['token'] as String,
); );
Map<String, dynamic> _$DeviceTokenToJson(DeviceToken instance) =>
<String, dynamic>{
'device': instance.device,
'token': instance.token,
};

View File

@ -19,6 +19,18 @@ HetznerServerInfo _$HetznerServerInfoFromJson(Map<String, dynamic> json) =>
(json['volumes'] as List<dynamic>).map((e) => e as int).toList(), (json['volumes'] as List<dynamic>).map((e) => e as int).toList(),
); );
Map<String, dynamic> _$HetznerServerInfoToJson(HetznerServerInfo instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'status': _$ServerStatusEnumMap[instance.status],
'created': instance.created.toIso8601String(),
'volumes': instance.volumes,
'server_type': instance.serverType,
'datacenter': instance.location,
'public_net': instance.publicNet,
};
const _$ServerStatusEnumMap = { const _$ServerStatusEnumMap = {
ServerStatus.running: 'running', ServerStatus.running: 'running',
ServerStatus.initializing: 'initializing', ServerStatus.initializing: 'initializing',
@ -37,6 +49,12 @@ HetznerPublicNetInfo _$HetznerPublicNetInfoFromJson(
HetznerIp4.fromJson(json['ipv4'] as Map<String, dynamic>), HetznerIp4.fromJson(json['ipv4'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$HetznerPublicNetInfoToJson(
HetznerPublicNetInfo instance) =>
<String, dynamic>{
'ipv4': instance.ipv4,
};
HetznerIp4 _$HetznerIp4FromJson(Map<String, dynamic> json) => HetznerIp4( HetznerIp4 _$HetznerIp4FromJson(Map<String, dynamic> json) => HetznerIp4(
json['id'] as int, json['id'] as int,
json['ip'] as String, json['ip'] as String,
@ -44,6 +62,14 @@ HetznerIp4 _$HetznerIp4FromJson(Map<String, dynamic> json) => HetznerIp4(
json['dns_ptr'] as String, json['dns_ptr'] as String,
); );
Map<String, dynamic> _$HetznerIp4ToJson(HetznerIp4 instance) =>
<String, dynamic>{
'blocked': instance.blocked,
'dns_ptr': instance.reverseDns,
'id': instance.id,
'ip': instance.ip,
};
HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson(
Map<String, dynamic> json) => Map<String, dynamic> json) =>
HetznerServerTypeInfo( HetznerServerTypeInfo(
@ -55,12 +81,27 @@ HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson(
.toList(), .toList(),
); );
Map<String, dynamic> _$HetznerServerTypeInfoToJson(
HetznerServerTypeInfo instance) =>
<String, dynamic>{
'cores': instance.cores,
'memory': instance.memory,
'disk': instance.disk,
'prices': instance.prices,
};
HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) => HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) =>
HetznerPriceInfo( HetznerPriceInfo(
HetznerPriceInfo.getPrice(json['price_hourly'] as Map), HetznerPriceInfo.getPrice(json['price_hourly'] as Map),
HetznerPriceInfo.getPrice(json['price_monthly'] as Map), HetznerPriceInfo.getPrice(json['price_monthly'] as Map),
); );
Map<String, dynamic> _$HetznerPriceInfoToJson(HetznerPriceInfo instance) =>
<String, dynamic>{
'price_hourly': instance.hourly,
'price_monthly': instance.monthly,
};
HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) => HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) =>
HetznerLocation( HetznerLocation(
json['country'] as String, json['country'] as String,
@ -68,3 +109,11 @@ HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) =>
json['description'] as String, json['description'] as String,
json['network_zone'] as String, json['network_zone'] as String,
); );
Map<String, dynamic> _$HetznerLocationToJson(HetznerLocation instance) =>
<String, dynamic>{
'country': instance.country,
'city': instance.city,
'description': instance.description,
'network_zone': instance.zone,
};

View File

@ -17,3 +17,12 @@ RecoveryKeyStatus _$RecoveryKeyStatusFromJson(Map<String, dynamic> json) =>
: DateTime.parse(json['expiration'] as String), : DateTime.parse(json['expiration'] as String),
usesLeft: json['uses_left'] as int?, usesLeft: json['uses_left'] as int?,
); );
Map<String, dynamic> _$RecoveryKeyStatusToJson(RecoveryKeyStatus instance) =>
<String, dynamic>{
'exists': instance.exists,
'date': instance.date?.toIso8601String(),
'expiration': instance.expiration?.toIso8601String(),
'uses_left': instance.usesLeft,
'valid': instance.valid,
};

View File

@ -0,0 +1,28 @@
import 'package:json_annotation/json_annotation.dart';
part 'server_disk_volume.g.dart';
@JsonSerializable()
class ServerDiskVolume {
factory ServerDiskVolume.fromJson(final Map<String, dynamic> json) =>
_$ServerDiskVolumeFromJson(json);
ServerDiskVolume({
required this.freeSpace,
required this.model,
required this.name,
required this.root,
required this.serial,
required this.totalSpace,
required this.type,
required this.usedSpace,
});
final String freeSpace;
final String? model;
final String name;
final bool root;
final String? serial;
final String totalSpace;
final String type;
final String usedSpace;
}

View File

@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_disk_volume.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ServerDiskVolume _$ServerDiskVolumeFromJson(Map<String, dynamic> json) =>
ServerDiskVolume(
freeSpace: json['freeSpace'] as String,
model: json['model'] as String?,
name: json['name'] as String,
root: json['root'] as bool,
serial: json['serial'] as String?,
totalSpace: json['totalSpace'] as String,
type: json['type'] as String,
usedSpace: json['usedSpace'] as String,
);
Map<String, dynamic> _$ServerDiskVolumeToJson(ServerDiskVolume instance) =>
<String, dynamic>{
'freeSpace': instance.freeSpace,
'model': instance.model,
'name': instance.name,
'root': instance.root,
'serial': instance.serial,
'totalSpace': instance.totalSpace,
'type': instance.type,
'usedSpace': instance.usedSpace,
};

View File

@ -0,0 +1,39 @@
import 'package:json_annotation/json_annotation.dart';
part 'server_job.g.dart';
@JsonSerializable()
class ServerJob {
factory ServerJob.fromJson(final Map<String, dynamic> json) =>
_$ServerJobFromJson(json);
ServerJob({
required this.name,
required this.description,
required this.status,
required this.uid,
required this.updatedAt,
required this.createdAt,
final this.error,
final this.progress,
final this.result,
final this.statusText,
final this.finishedAt,
});
final String name;
final String description;
final String status;
final String uid;
@JsonKey(name: 'updated_at')
final String updatedAt;
@JsonKey(name: 'created_at')
final DateTime createdAt;
final String? error;
final int? progress;
final String? result;
@JsonKey(name: 'status_text')
final String? statusText;
@JsonKey(name: 'finished_at')
final String? finishedAt;
}

View File

@ -0,0 +1,35 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_job.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ServerJob _$ServerJobFromJson(Map<String, dynamic> json) => ServerJob(
name: json['name'] as String,
description: json['description'] as String,
status: json['status'] as String,
uid: json['uid'] as String,
updatedAt: json['updated_at'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
error: json['error'] as String?,
progress: json['progress'] as int?,
result: json['result'] as String?,
statusText: json['status_text'] as String?,
finishedAt: json['finished_at'] as String?,
);
Map<String, dynamic> _$ServerJobToJson(ServerJob instance) => <String, dynamic>{
'name': instance.name,
'description': instance.description,
'status': instance.status,
'uid': instance.uid,
'updated_at': instance.updatedAt,
'created_at': instance.createdAt.toIso8601String(),
'error': instance.error,
'progress': instance.progress,
'result': instance.result,
'status_text': instance.statusText,
'finished_at': instance.finishedAt,
};

View File

@ -1 +1 @@
enum StateType { uninitialized, stable, warning } enum StateType { uninitialized, stable, warning, error }

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class BrandOutlinedButton extends StatelessWidget {
const BrandOutlinedButton({
final super.key,
this.onPressed,
this.title,
this.child,
this.disabled = false,
});
final VoidCallback? onPressed;
final String? title;
final Widget? child;
final bool disabled;
@override
Widget build(final BuildContext context) => ConstrainedBox(
constraints: const BoxConstraints(
minHeight: 40,
minWidth: double.infinity,
),
child: OutlinedButton(
onPressed: onPressed,
child: child ??
Text(
title ?? '',
style: Theme.of(context).textTheme.button?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
),
);
}

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
class BrandLinearIndicator extends StatelessWidget {
const BrandLinearIndicator({
required this.value,
required this.color,
required this.backgroundColor,
required this.height,
final super.key,
});
final double value;
final Color color;
final Color backgroundColor;
final double height;
@override
Widget build(final BuildContext context) => Container(
height: height,
width: double.infinity,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(height),
),
alignment: Alignment.centerLeft,
child: FractionallySizedBox(
widthFactor: value,
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(height),
),
),
),
);
}

View File

@ -28,6 +28,12 @@ class IconStatusMask extends StatelessWidget {
case StateType.warning: case StateType.warning:
colors = BrandColors.warningGradientColors; colors = BrandColors.warningGradientColors;
break; break;
case StateType.error:
colors = [
Theme.of(context).colorScheme.error,
Theme.of(context).colorScheme.error,
];
break;
} }
return ShaderMask( return ShaderMask(
shaderCallback: (final bounds) => LinearGradient( shaderCallback: (final bounds) => LinearGradient(

View File

@ -11,6 +11,7 @@ import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
class JobsContent extends StatelessWidget { class JobsContent extends StatelessWidget {
const JobsContent({final super.key}); const JobsContent({final super.key});
@ -67,7 +68,7 @@ class JobsContent extends StatelessWidget {
]; ];
} else if (state is JobsStateWithJobs) { } else if (state is JobsStateWithJobs) {
widgets = [ widgets = [
...state.jobList ...state.clientJobList
.map( .map(
(final j) => Row( (final j) => Row(
children: [ children: [
@ -117,7 +118,15 @@ class JobsContent extends StatelessWidget {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
...widgets ...widgets,
const SizedBox(height: 8),
const Divider(),
const SizedBox(height: 8),
...state.serverJobList.map(
(final job) => ServerJobCard(
serverJob: job,
),
),
], ],
); );
}, },

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
class ServerJobCard extends StatelessWidget {
const ServerJobCard({
required final this.serverJob,
final super.key,
});
final ServerJob serverJob;
@override
Widget build(final BuildContext context) => GestureDetector(
child: BrandCards.big(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
serverJob.name,
style: Theme.of(context).textTheme.bodyMedium,
),
Text(
serverJob.description,
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 8),
BrandLinearIndicator(
value: serverJob.progress == null
? 0.0
: serverJob.progress! / 100.0,
color: Theme.of(context).colorScheme.secondary,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
height: 7.0,
),
const SizedBox(height: 8),
],
),
),
);
}

View File

@ -83,10 +83,14 @@ class _ProgressBarState extends State<ProgressBar> {
height: 5, height: 5,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
gradient: const LinearGradient( color: Theme.of(context).colorScheme.surfaceVariant,
gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: BrandColors.stableGradientColors, colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary
],
), ),
), ),
duration: const Duration( duration: const Duration(
@ -122,15 +126,7 @@ class _ProgressBarState extends State<ProgressBar> {
text: TextSpan( text: TextSpan(
style: progressTextStyleLight, style: progressTextStyleLight,
children: [ children: [
if (checked) TextSpan(text: '${index + 1}.', style: style),
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(bottom: 2, right: 2),
child: Icon(BrandIcons.check, size: 11),
),
)
else
TextSpan(text: '${index + 1}.', style: style),
TextSpan(text: step, style: style) TextSpan(text: step, style: style)
], ],
), ),

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
@ -16,8 +17,13 @@ class AboutPage extends StatelessWidget {
hasBackButton: true, hasBackButton: true,
), ),
), ),
body: const BrandMarkdown( body: ListView(
fileName: 'about', padding: paddingH15V0,
children: const [
BrandMarkdown(
fileName: 'about',
),
],
), ),
), ),
); );

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server.dart';
import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
@ -23,9 +24,16 @@ class InfoPage extends StatelessWidget {
const BrandDivider(), const BrandDivider(),
const SizedBox(height: 10), const SizedBox(height: 10),
FutureBuilder( FutureBuilder(
future: _version(), future: _packageVersion(),
builder: (final context, final snapshot) => BrandText.body1( builder: (final context, final snapshot) => BrandText.body1(
'more.about_app_page.text' 'more.about_app_page.application_version_text'
.tr(args: [snapshot.data.toString()]),
),
),
FutureBuilder(
future: _apiVersion(),
builder: (final context, final snapshot) => BrandText.body1(
'more.about_app_page.api_version_text'
.tr(args: [snapshot.data.toString()]), .tr(args: [snapshot.data.toString()]),
), ),
), ),
@ -34,7 +42,7 @@ class InfoPage extends StatelessWidget {
), ),
); );
Future<String> _version() async { Future<String> _packageVersion() async {
String packageVersion = 'unknown'; String packageVersion = 'unknown';
try { try {
final PackageInfo packageInfo = await PackageInfo.fromPlatform(); final PackageInfo packageInfo = await PackageInfo.fromPlatform();
@ -45,4 +53,15 @@ class InfoPage extends StatelessWidget {
return packageVersion; return packageVersion;
} }
Future<String> _apiVersion() async {
String apiVersion = 'unknown';
try {
apiVersion = await ServerApi().getApiVersion() ?? apiVersion;
} catch (e) {
print(e);
}
return apiVersion;
}
} }

View File

@ -5,6 +5,11 @@ import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
import 'package:selfprivacy/logic/models/provider.dart'; import 'package:selfprivacy/logic/models/provider.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
@ -15,7 +20,9 @@ import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart';
import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/ui/helpers/modals.dart';
import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart'; import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart';
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart'; import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
import 'package:selfprivacy/ui/pages/providers/storage_card.dart';
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart'; import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@ -67,6 +74,17 @@ class _ProvidersPageState extends State<ProvidersPage> {
), ),
) )
.toList(); .toList();
cards.add(
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: StorageCard(
diskStatus: toDiskStatus(
context.read<ApiServerVolumeCubit>().state.volumes,
context.read<ApiProviderVolumeCubit>().state.volumes,
),
),
),
);
return Scaffold( return Scaffold(
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(52), preferredSize: const Size.fromHeight(52),
@ -86,6 +104,60 @@ class _ProvidersPageState extends State<ProvidersPage> {
), ),
); );
} }
DiskStatus toDiskStatus(
final List<ServerDiskVolume> serverVolumes,
final List<ServerVolume> providerVolumes,
) {
final DiskStatus diskStatus = DiskStatus();
diskStatus.isDiskOkay = true;
if (providerVolumes.isEmpty || serverVolumes.isEmpty) {
diskStatus.isDiskOkay = false;
}
diskStatus.diskVolumes = serverVolumes.map((
final ServerDiskVolume volume,
) {
final DiskVolume diskVolume = DiskVolume();
diskVolume.sizeUsed = DiskSize(
byte: volume.usedSpace == 'None' ? 0 : int.parse(volume.usedSpace),
);
diskVolume.sizeTotal = DiskSize(
byte: volume.totalSpace == 'None' ? 0 : int.parse(volume.totalSpace),
);
diskVolume.serverDiskVolume = volume;
for (final ServerVolume providerVolume in providerVolumes) {
if (providerVolume.linuxDevice == null ||
volume.model == null ||
volume.serial == null) {
continue;
}
final String deviceId = providerVolume.linuxDevice!.split('/').last;
if (deviceId.contains(volume.model!) &&
deviceId.contains(volume.serial!)) {
diskVolume.providerVolume = providerVolume;
break;
}
}
diskVolume.name = volume.name;
diskVolume.root = volume.root;
diskVolume.percentage =
volume.usedSpace != 'None' && volume.totalSpace != 'None'
? 1.0 / diskVolume.sizeTotal.byte * diskVolume.sizeUsed.byte
: 0.0;
if (diskVolume.percentage >= 0.8 ||
diskVolume.sizeTotal.asGb() - diskVolume.sizeUsed.asGb() <= 2.0) {
diskStatus.isDiskOkay = false;
}
return diskVolume;
}).toList();
return diskStatus;
}
} }
class _Card extends StatelessWidget { class _Card extends StatelessWidget {
@ -103,9 +175,6 @@ class _Card extends StatelessWidget {
final ServerInstallationState appConfig = final ServerInstallationState appConfig =
context.watch<ServerInstallationCubit>().state; context.watch<ServerInstallationCubit>().state;
final String domainName =
appConfig.isDomainFilled ? appConfig.serverDomain!.domainName : '';
switch (provider.type) { switch (provider.type) {
case ProviderType.server: case ProviderType.server:
title = 'providers.server.card_title'.tr(); title = 'providers.server.card_title'.tr();
@ -121,7 +190,9 @@ class _Card extends StatelessWidget {
break; break;
case ProviderType.domain: case ProviderType.domain:
title = 'providers.domain.screen_title'.tr(); title = 'providers.domain.screen_title'.tr();
message = domainName; message = appConfig.isDomainSelected
? appConfig.serverDomain!.domainName
: '';
stableText = 'providers.domain.status'.tr(); stableText = 'providers.domain.status'.tr();
onTap = () => Navigator.of(context).push( onTap = () => Navigator.of(context).push(
@ -151,12 +222,17 @@ class _Card extends StatelessWidget {
status: provider.state, status: provider.state,
child: Icon(provider.icon, size: 30, color: Colors.white), child: Icon(provider.icon, size: 30, color: Colors.white),
), ),
const SizedBox(height: 10), const SizedBox(height: 16),
BrandText.h2(title), Text(
const SizedBox(height: 10), title,
style: Theme.of(context).textTheme.titleLarge,
),
if (message != null) ...[ if (message != null) ...[
BrandText.body2(message), Text(
const SizedBox(height: 10), message,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
], ],
if (provider.state == StateType.stable) BrandText.body2(stableText), if (provider.state == StateType.stable) BrandText.body2(stableText),
], ],

View File

@ -0,0 +1,100 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class StorageCard extends StatelessWidget {
const StorageCard({
required final this.diskStatus,
final super.key,
});
final DiskStatus diskStatus;
@override
Widget build(final BuildContext context) {
final List<Widget> sections = [];
for (final DiskVolume volume in diskStatus.diskVolumes) {
sections.add(
const SizedBox(height: 16),
);
sections.add(
ServerStorageListItem(
volume: volume,
dense: true,
showIcon: false,
),
);
}
StateType state = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished
? StateType.stable
: StateType.uninitialized;
if (state == StateType.stable && !diskStatus.isDiskOkay) {
state = StateType.error;
}
return GestureDetector(
onTap: () => Navigator.of(context).push(
materialRoute(
ServerStoragePage(
diskStatus: diskStatus,
),
),
),
child: BrandCards.big(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconStatusMask(
status: state,
child: const Icon(
Icons.storage_outlined,
size: 30,
color: Colors.white,
),
),
if (state != StateType.uninitialized)
IconStatusMask(
status: state,
child: Icon(
diskStatus.isDiskOkay
? Icons.check_circle_outline
: Icons.error_outline,
size: 24,
color: Colors.white,
),
),
],
),
const SizedBox(height: 16),
Text(
'providers.storage.card_title'.tr(),
style: Theme.of(context).textTheme.titleLarge,
),
if (state != StateType.uninitialized)
Text(
diskStatus.isDiskOkay
? 'providers.storage.status_ok'.tr()
: 'providers.storage.status_error'.tr(),
style: Theme.of(context).textTheme.bodyLarge,
),
...sections,
const SizedBox(height: 8),
],
),
),
);
}
}

View File

@ -294,7 +294,6 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
} }
_amountController.addListener(_updateErrorStatuses); _amountController.addListener(_updateErrorStatuses);
_expirationController.addListener(_updateErrorStatuses); _expirationController.addListener(_updateErrorStatuses);
return Column( return Column(
@ -321,6 +320,7 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
children: [ children: [
const SizedBox(height: 8), const SizedBox(height: 8),
TextField( TextField(
textInputAction: TextInputAction.next,
enabled: _isAmountToggled, enabled: _isAmountToggled,
controller: _amountController, controller: _amountController,
decoration: InputDecoration( decoration: InputDecoration(
@ -360,6 +360,7 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
children: [ children: [
const SizedBox(height: 8), const SizedBox(height: 8),
TextField( TextField(
textInputAction: TextInputAction.next,
enabled: _isExpirationToggled, enabled: _isExpirationToggled,
controller: _expirationController, controller: _expirationController,
onTap: () { onTap: () {

View File

@ -0,0 +1,47 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage_list_item.dart';
class DataMigrationPage extends StatefulWidget {
const DataMigrationPage({
required this.diskVolumeToResize,
required this.diskStatus,
required this.resizeTarget,
final super.key,
});
final DiskVolume diskVolumeToResize;
final DiskStatus diskStatus;
final DiskSize resizeTarget;
@override
State<DataMigrationPage> createState() => _DataMigrationPageState();
}
class _DataMigrationPageState extends State<DataMigrationPage> {
@override
Widget build(final BuildContext context) {
int a = 0;
return BrandHeroScreen(
hasBackButton: true,
heroTitle: 'providers.storage.data_migration_title'.tr(),
children: [
...widget.diskStatus.diskVolumes
.map(
(final volume) => Column(
children: [
ServerStorageListItem(
volume: volume,
),
const SizedBox(height: 16),
],
),
)
.toList(),
],
);
}
}

View File

@ -0,0 +1,21 @@
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/json/server_disk_volume.dart';
class DiskVolume {
DiskSize sizeUsed = DiskSize();
DiskSize sizeTotal = DiskSize();
String name = '';
bool root = false;
bool isResizable = true;
ServerDiskVolume? serverDiskVolume;
ServerVolume? providerVolume;
/// from 0.0 to 1.0
double percentage = 0.0;
}
class DiskStatus {
bool isDiskOkay = false;
List<DiskVolume> diskVolumes = [];
}

View File

@ -0,0 +1,149 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
class ExtendingVolumePage extends StatefulWidget {
const ExtendingVolumePage({
required this.diskVolumeToResize,
required this.diskStatus,
final super.key,
});
final DiskVolume diskVolumeToResize;
final DiskStatus diskStatus;
@override
State<ExtendingVolumePage> createState() => _ExtendingVolumePageState();
}
class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
bool _isError = false;
double _currentSliderGbValue = -1;
double _euroPerGb = 1.0;
final DiskSize maxSize = DiskSize(byte: 500000000000);
DiskSize minSize = DiskSize();
final TextEditingController _sizeController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
void _updateErrorStatuses() {
_isError = minSize.asGb() > _currentSliderGbValue;
}
@override
Widget build(final BuildContext context) => FutureBuilder(
future: context.read<ApiProviderVolumeCubit>().getPricePerGb(),
builder: (
final BuildContext context,
final AsyncSnapshot<void> snapshot,
) {
if (!snapshot.hasData) {
return BrandHeroScreen(
hasBackButton: true,
heroTitle: 'providers.storage.extending_volume_title'.tr(),
heroSubtitle:
'providers.storage.extending_volume_description'.tr(),
children: const [
SizedBox(height: 16),
],
);
}
_euroPerGb = snapshot.data as double;
_sizeController.text = _currentSliderGbValue.truncate().toString();
_priceController.text =
(_euroPerGb * double.parse(_sizeController.text))
.toStringAsPrecision(2);
minSize = widget.diskVolumeToResize.sizeTotal;
if (_currentSliderGbValue < 0) {
_currentSliderGbValue = minSize.asGb();
}
return BrandHeroScreen(
hasBackButton: true,
heroTitle: 'providers.storage.extending_volume_title'.tr(),
heroSubtitle: 'providers.storage.extending_volume_description'.tr(),
children: [
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 130),
child: TextField(
readOnly: true,
textAlign: TextAlign.start,
textInputAction: TextInputAction.next,
enabled: true,
controller: _sizeController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
errorText: _isError ? ' ' : null,
labelText: 'providers.storage.size'.tr(),
),
),
),
const SizedBox(width: 16),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 130),
child: TextField(
readOnly: true,
textAlign: TextAlign.start,
textInputAction: TextInputAction.next,
enabled: true,
controller: _priceController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
errorText: _isError ? ' ' : null,
labelText: 'providers.storage.euro'.tr(),
),
),
),
],
),
const SizedBox(height: 16),
Slider(
min: minSize.asGb(),
value: _currentSliderGbValue,
max: maxSize.asGb(),
onChanged: (final double value) {
setState(() {
_currentSliderGbValue = value;
_updateErrorStatuses();
});
},
),
const SizedBox(height: 16),
FilledButton(
title: 'providers.storage.extend_volume_button.title'.tr(),
onPressed: null,
disabled: _isError,
),
const SizedBox(height: 16),
const Divider(
height: 1.0,
),
const SizedBox(height: 16),
const Align(
alignment: Alignment.centerLeft,
child: Icon(
Icons.info_outlined,
size: 24,
),
),
const SizedBox(height: 16),
Text('providers.storage.extending_volume_price_info'.tr()),
const SizedBox(height: 16),
],
);
},
);
}

View File

@ -0,0 +1,92 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ServerStoragePage extends StatefulWidget {
const ServerStoragePage({required this.diskStatus, final super.key});
final DiskStatus diskStatus;
@override
State<ServerStoragePage> createState() => _ServerStoragePageState();
}
class _ServerStoragePageState extends State<ServerStoragePage> {
@override
Widget build(final BuildContext context) {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
if (!isReady) {
return BrandHeroScreen(
hasBackButton: true,
heroTitle: 'providers.storage.card_title'.tr(),
children: const [],
);
}
return BrandHeroScreen(
hasBackButton: true,
heroTitle: 'providers.storage.card_title'.tr(),
children: [
// ...sections,
...widget.diskStatus.diskVolumes
.map(
(final volume) => Column(
children: [
ServerStorageSection(
volume: volume,
diskStatus: widget.diskStatus,
),
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 16),
],
),
)
.toList(),
const SizedBox(height: 8),
],
);
}
}
class ServerStorageSection extends StatelessWidget {
const ServerStorageSection({
required this.volume,
required this.diskStatus,
final super.key,
});
final DiskVolume volume;
final DiskStatus diskStatus;
@override
Widget build(final BuildContext context) => Column(
children: [
ServerStorageListItem(
volume: volume,
),
if (volume.isResizable) ...[
const SizedBox(height: 16),
BrandOutlinedButton(
title: 'providers.storage.extend_volume_button.title'.tr(),
onPressed: () => Navigator.of(context).push(
materialRoute(
ExtendingVolumePage(
diskVolumeToResize: volume,
diskStatus: diskStatus,
),
),
),
),
],
],
);
}

View File

@ -0,0 +1,74 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
import 'package:selfprivacy/ui/pages/server_storage/disk_status.dart';
class ServerStorageListItem extends StatelessWidget {
const ServerStorageListItem({
required this.volume,
final this.showIcon = true,
final this.dense = false,
final super.key,
});
final DiskVolume volume;
final bool showIcon;
final bool dense;
@override
Widget build(final BuildContext context) {
final TextStyle? titleStyle = dense
? Theme.of(context).textTheme.titleMedium
: Theme.of(context).textTheme.titleLarge;
final TextStyle? subtitleStyle = dense
? Theme.of(context).textTheme.bodySmall
: Theme.of(context).textTheme.bodyMedium;
return Row(
children: [
if (showIcon)
const Icon(
Icons.storage_outlined,
size: 24,
color: Colors.white,
),
if (showIcon) const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'providers.storage.disk_usage'.tr(
args: [
volume.sizeUsed.asGb().toStringAsPrecision(3),
],
),
style: titleStyle,
),
const SizedBox(height: 4),
BrandLinearIndicator(
value: volume.percentage,
color: volume.root
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.secondary,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
height: 14.0,
),
const SizedBox(height: 4),
Text(
'providers.storage.disk_total'.tr(
args: [
volume.sizeTotal.asGb().toStringAsPrecision(3),
volume.name,
],
),
style: subtitleStyle,
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,56 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
class ServiceStorageConsumptionListItem extends StatelessWidget {
const ServiceStorageConsumptionListItem({
required this.title,
required this.percentage,
required this.storageConsumptionText,
required this.color,
required this.icon,
final super.key,
});
final String title;
final double percentage;
final String storageConsumptionText;
final Color color;
final IconData icon;
@override
Widget build(final BuildContext context) => Row(
children: [
Icon(
icon,
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
storageConsumptionText,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
const SizedBox(height: 4),
BrandLinearIndicator(
value: percentage,
color: color,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
height: 7.0,
),
],
),
),
],
);
}

Some files were not shown because too many files have changed in this diff Show More