Merge pull request 'Release 0.7.0' (#116) from graphql into master

Reviewed-on: kherel/selfprivacy.org.app#116
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
pull/117/head
Inex Code 2022-09-19 04:45:13 +03:00
commit 81c97e5249
205 changed files with 32600 additions and 3870 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

View File

@ -26,7 +26,9 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion flutter.compileSdkVersion namespace 'org.selfprivacy.app'
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
sourceSets { sourceSets {
@ -48,7 +50,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "pro.kherel.selfprivacy" applicationId "org.selfprivacy.app"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
@ -62,6 +64,21 @@ android {
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
} }
flavorDimensions "default"
productFlavors {
fdroid {
applicationId "pro.kherel.selfprivacy"
}
production {
applicationIdSuffix ""
}
nightly {
applicationIdSuffix ".nightly"
versionCode project.getVersionCode()
versionName "nightly-" + project.getVersionCode()
}
}
} }
flutter { flutter {

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pro.kherel.selfprivacy"> package="org.selfprivacy.app">
<!-- Flutter needs it to communicate with the running application <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pro.kherel.selfprivacy"> package="org.selfprivacy.app">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that <!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method. calls FlutterMain.startInitialization(this); in its onCreate method.

View File

@ -1,4 +1,4 @@
package pro.kherel.selfprivacy package org.selfprivacy.app
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pro.kherel.selfprivacy"> package="org.selfprivacy.app">
<!-- Flutter needs it to communicate with the running application <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->

View File

@ -1,5 +1,18 @@
buildscript { buildscript {
ext.kotlin_version = '1.6.10' ext.kotlin_version = '1.6.10'
ext.getVersionCode = { ->
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-list', '--first-parent', '--count', 'origin/master'
standardOutput = stdout
}
return Integer.parseInt(stdout.toString().trim())
}
catch (ignored) {
return -1
}
}
repositories { repositories {
google() google()
jcenter() jcenter()

View File

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableR8=true

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",
@ -99,7 +100,33 @@
"chart": { "chart": {
"month": "Month", "month": "Month",
"day": "Day", "day": "Day",
"hour": "Hour" "hour": "Hour",
"cpu_title": "CPU Usage",
"network_title": "Network Usage",
"in": "In",
"out": "Out"
},
"resource_usage": "Resource usage",
"settings": {
"allow_autoupgrade": "Allow auto-upgrade",
"allow_autoupgrade_hint": "Allow automatic packages upgrades on server",
"reboot_after_upgrade": "Reboot after upgrade",
"reboot_after_upgrade_hint": "Reboot without prompt after applying changes on server",
"server_timezone": "Server timezone",
"select_timezone": "Select timezone"
},
"info": {
"server_id": "Server ID",
"status": "Status",
"cpu": "CPU",
"ram": "Memory",
"disk": "Disk local",
"monthly_cost": "Monthly cost",
"location": "Location",
"core_count": {
"one": "{} core",
"other": "{} cores"
}
} }
}, },
"domain": { "domain": {
@ -162,6 +189,28 @@
"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": "{} used",
"disk_total": "{} total · {}",
"gb": "{} GB",
"mb": "{} MB",
"kb": "{} KB",
"extend_volume_button": "Extend volume",
"extending_volume_title": "Extending volume",
"extending_volume_description": "Resizing volume will allow you to store more data on your server without extending the server itself. Volume can only be extended: shrinking is not possible.",
"extending_volume_price_info": "Price includes VAT and is estimated from pricing data provided by Hetzner. Server will be rebooted after resizing.",
"extending_volume_error": "Couldn't initialize volume extending.",
"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",
"migration_process": "Migrating...",
"migration_done": "Finish"
} }
}, },
"not_ready_card": { "not_ready_card": {
@ -172,8 +221,16 @@
"in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work." "in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work."
}, },
"services": { "services": {
"_comment": "Вкладка сервисы", "_comment": "Services",
"title": "Your personal, private and independent services.", "title": "Your personal, private and independent services.",
"service_page": {
"open_in_browser": "Open in browser",
"restart": "Restart service",
"disable": "Disable service",
"enable": "Enable service",
"move": "Move to another volume",
"uses": "Uses {usage} on {volume}"
},
"mail": { "mail": {
"title": "E-Mail", "title": "E-Mail",
"subtitle": "E-Mail for company and family.", "subtitle": "E-Mail for company and family.",
@ -237,12 +294,22 @@
"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": {
"_comment": "'Users' tab", "_comment": "'Users' tab",
"add_new_user": "Add a first user", "add_new_user": "Add a first user",
"new_user": "New user", "new_user": "New user",
"delete_user": "Delete user",
"not_ready": "Please connect server, domain and DNS in the Providers tab, to be able to add a first user", "not_ready": "Please connect server, domain and DNS in the Providers tab, to be able to add a first user",
"nobody_here": "Nobody here", "nobody_here": "Nobody here",
"login": "Login", "login": "Login",
@ -252,13 +319,24 @@
"delete_confirm_question": "Are you sure?", "delete_confirm_question": "Are you sure?",
"reset_password": "Reset password", "reset_password": "Reset password",
"account": "Account", "account": "Account",
"send_registration_data": "Share login credentials" "send_registration_data": "Share login credentials",
"could_not_fetch_users": "Couldn't fetch users list",
"could_not_fetch_description": "Please check your internet connection and try again",
"refresh_users": "Refresh users list",
"could_not_create_user": "Couldn't create user",
"could_not_delete_user": "Couldn't delete user",
"could_not_add_ssh_key": "Couldn't add SSH key",
"email_login": "Email login",
"no_sso_notice": "Only email and SSH accounts are created for this user. Single Sign On for all services is coming soon."
}, },
"initializing": { "initializing": {
"_comment": "initializing page", "_comment": "initializing page",
"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",
@ -275,13 +353,14 @@
"15": "Server created. DNS checks and server boot in progress...", "15": "Server created. DNS checks and server boot in progress...",
"16": "Until the next check: ", "16": "Until the next check: ",
"17": "Check", "17": "Check",
"18": "How to obtain Hetzner API Token:'",
"19": "1 Go via this link ", "19": "1 Go via this link ",
"20": "\n", "20": "\n",
"21": "One more restart to apply your security certificates.", "21": "One more restart to apply your security certificates.",
"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 +452,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",
@ -408,7 +489,9 @@
"upgradeServer": "Upgrade server", "upgradeServer": "Upgrade server",
"rebootServer": "Reboot server", "rebootServer": "Reboot server",
"create_ssh_key": "Create SSH key for {}", "create_ssh_key": "Create SSH key for {}",
"delete_ssh_key": "Delete SSH key for {}" "delete_ssh_key": "Delete SSH key for {}",
"server_jobs": "Jobs on the server",
"resetUserPassword": "Reset password of user"
}, },
"validations": { "validations": {
"required": "Required.", "required": "Required.",

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": "Настройки приложения",
@ -99,7 +100,35 @@
"chart": { "chart": {
"month": "Месяц", "month": "Месяц",
"day": "День", "day": "День",
"hour": "Час" "hour": "Час",
"cpu_title": "Использование процессора",
"network_title": "Использование сети",
"in": "Получено",
"out": "Отправлено"
},
"settings": {
"allow_autoupgrade": "Разрешить авто-обноления",
"allow_autoupgrade_hint": "Разрешить автоматичесую установку обновлений на сервер",
"reboot_after_upgrade": "Перезагружать после обновлений",
"reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений",
"server_timezone": "Часовой пояс сервера",
"select_timezone": "Выберите часовой пояс"
},
"info": {
"server_id": "ID сервера",
"status": "Статус",
"cpu": "Процессор",
"ram": "Операивная память",
"disk": "Диск",
"monthly_cost": "Ежемесячная стоимость",
"location": "Размещение",
"core_count": {
"one": "{} ядро",
"two": "{} ядра",
"few": "{} ядра",
"many": "{} ядер",
"other": "{} ядер"
}
} }
}, },
"domain": { "domain": {
@ -162,7 +191,28 @@
"refresh": "Обновить статус", "refresh": "Обновить статус",
"refetchBackups": "Обновить список копий", "refetchBackups": "Обновить список копий",
"refetchingList": "Через несколько минут список будет обновлён" "refetchingList": "Через несколько минут список будет обновлён"
},
"storage": {
"card_title": "Хранилище",
"status_ok": "Проблем на диске не обнаружено",
"status_error": "Заканчивается место на диске",
"disk_usage": "{} использовано",
"disk_total": "{} всего · {}",
"gb": "{} GB",
"mb": "{} MB",
"kb": "{} KB",
"extend_volume_button": "Расширить хранилище",
"extending_volume_title": "Расширение хранилища",
"extending_volume_description": "Изменение размера хранилища позволит вам держать больше данных на вашем сервере без расширения самого сервера. Объем можно только увеличить: уменьшить нельзя.",
"extending_volume_price_info": "Цена включает НДС и рассчитана на основе данных о ценах, предоставленных Hetzner. Сервер будет перезагружен во время процесса.",
"extending_volume_error": "Не удалось начать расширение хранилища.",
"size": "Размер",
"euro": "Евро",
"data_migration_title": "Миграция данных",
"data_migration_notice": "На время миграции данных все сервисы будут выключены.",
"start_migration_button": "Начать миграцию",
"migration_process": "Мигрируем...",
"migration_done": "Завершить"
} }
}, },
"not_ready_card": { "not_ready_card": {
@ -175,6 +225,14 @@
"services": { "services": {
"_comment": "Вкладка сервисы", "_comment": "Вкладка сервисы",
"title": "Ваши личные, приватные и независимые сервисы:", "title": "Ваши личные, приватные и независимые сервисы:",
"service_page": {
"open_in_browser": "Открыть в браузере",
"restart": "Перезапустить сервис",
"disable": "Выключить сервис",
"enable": "Включить сервис",
"move": "Переместить на другой диск",
"uses": "Использует {usage} на {volume}"
},
"mail": { "mail": {
"title": "Почта", "title": "Почта",
"subtitle": "Электронная почта для семьи или компании.", "subtitle": "Электронная почта для семьи или компании.",
@ -238,6 +296,15 @@
"bottom_sheet": { "bottom_sheet": {
"1": "Создать подключиться к VPN-серверу. Движок для безопасной и масштабируемой инфраструктуры VPN" "1": "Создать подключиться к VPN-серверу. Движок для безопасной и масштабируемой инфраструктуры VPN"
} }
},
"page": {
"up_and_running": "Запущен и работает",
"resource_usage": "Потребление ресурсов",
"disk_used": "{} использовано места на диске",
"users": "Пользователи",
"controlled_by": "Контролируется {}",
"apps": "Приложения",
"settings": "Настройки"
} }
}, },
"users": { "users": {
@ -260,6 +327,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",
@ -318,6 +388,10 @@
"domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.", "domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.",
"modal_confirmation_title": "Это действительно ваш сервер?", "modal_confirmation_title": "Это действительно ваш сервер?",
"modal_confirmation_description": "Подключение к неправильному серверу может привести к деструктивным последствиям.", "modal_confirmation_description": "Подключение к неправильному серверу может привести к деструктивным последствиям.",
"modal_confirmation_dns_valid": "Обратный DNS корректен",
"modal_confirmation_dns_invalid": "Обратный DNS указывает на другой домен",
"modal_confirmation_ip_valid": "IP совпадает с указанным в DNS записи",
"modal_confirmation_ip_invalid": "IP не совпадает с указанным в DNS записи",
"confirm_cloudflare": "Подключение к Cloudflare", "confirm_cloudflare": "Подключение к Cloudflare",
"confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:", "confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:",
"confirm_backblze": "Подключение к Backblaze", "confirm_backblze": "Подключение к Backblaze",
@ -371,8 +445,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": "Да, сбросить",
@ -406,7 +482,9 @@
"upgradeServer": "Обновить сервер", "upgradeServer": "Обновить сервер",
"rebootServer": "Перезагрузить сервер", "rebootServer": "Перезагрузить сервер",
"create_ssh_key": "Создать SSH ключ для {}", "create_ssh_key": "Создать SSH ключ для {}",
"delete_ssh_key": "Удалить SSH ключ для {}" "delete_ssh_key": "Удалить SSH ключ для {}",
"server_jobs": "Задачи на сервере",
"resetUserPassword": "Сбросить пароль пользователя"
}, },
"validations": { "validations": {
"required": "Обязательное поле.", "required": "Обязательное поле.",

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

@ -0,0 +1,9 @@
- Server storage management
- New screen for service management
- New users management screen
- User passwords can be changed
- Server auto upgrade settings can be changed now
- Server timezone can be changed
- Fixed bugs related to users list synchronization
- UI fixes
- Minor bug fixes

View File

@ -2,14 +2,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart'; import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_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/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; 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/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_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/server_jobs/server_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_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 +30,12 @@ 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);
final apiServerVolumesCubit =
ApiServerVolumeCubit(serverInstallationCubit, apiVolumesCubit);
final serverJobsCubit = ServerJobsCubit(serverInstallationCubit);
final serverDetailsCubit = ServerDetailsCubit(serverInstallationCubit);
return MultiProvider( return MultiProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
@ -38,7 +48,9 @@ class BlocAndProviderConfig extends StatelessWidget {
create: (final _) => serverInstallationCubit, create: (final _) => serverInstallationCubit,
lazy: false, lazy: false,
), ),
BlocProvider(create: (final _) => ProvidersCubit()), BlocProvider(
create: (final _) => ProvidersCubit(),
),
BlocProvider( BlocProvider(
create: (final _) => usersCubit..load(), create: (final _) => usersCubit..load(),
lazy: false, lazy: false,
@ -61,8 +73,22 @@ class BlocAndProviderConfig extends StatelessWidget {
create: (final _) => apiDevicesCubit..load(), create: (final _) => apiDevicesCubit..load(),
), ),
BlocProvider( BlocProvider(
create: (final _) => create: (final _) => apiVolumesCubit..load(),
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit), ),
BlocProvider(
create: (final _) => apiServerVolumesCubit..load(),
),
BlocProvider(
create: (final _) => serverJobsCubit..load(),
),
BlocProvider(
create: (final _) => serverDetailsCubit..load(),
),
BlocProvider(
create: (final _) => JobsCubit(
usersCubit: usersCubit,
servicesCubit: servicesCubit,
),
), ),
], ],
child: child, child: child,

View File

@ -1,7 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/ui/components/error/error.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
@ -14,15 +11,8 @@ class SimpleBlocObserver extends BlocObserver {
final Object error, final Object error,
final StackTrace stackTrace, final StackTrace stackTrace,
) { ) {
final NavigatorState navigator = getIt.get<NavigationService>().navigator!; getIt<NavigationService>().showSnackBar(
'Bloc error: ${error.toString()}',
navigator.push(
materialRoute(
BrandError(
error: error,
stackTrace: stackTrace,
),
),
); );
super.onError(bloc, error, stackTrace); super.onError(bloc, error, stackTrace);
} }

View File

@ -21,6 +21,7 @@ class HiveConfig {
Hive.registerAdapter(DnsProviderAdapter()); Hive.registerAdapter(DnsProviderAdapter());
Hive.registerAdapter(ServerProviderAdapter()); Hive.registerAdapter(ServerProviderAdapter());
Hive.registerAdapter(UserTypeAdapter());
await Hive.openBox(BNames.appSettingsBox); await Hive.openBox(BNames.appSettingsBox);

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,756 @@
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;
}
CopyWith$Input$AutoUpgradeSettingsInput<Input$AutoUpgradeSettingsInput>
get copyWith => CopyWith$Input$AutoUpgradeSettingsInput(this, (i) => i);
}
abstract class CopyWith$Input$AutoUpgradeSettingsInput<TRes> {
factory CopyWith$Input$AutoUpgradeSettingsInput(
Input$AutoUpgradeSettingsInput instance,
TRes Function(Input$AutoUpgradeSettingsInput) then) =
_CopyWithImpl$Input$AutoUpgradeSettingsInput;
factory CopyWith$Input$AutoUpgradeSettingsInput.stub(TRes res) =
_CopyWithStubImpl$Input$AutoUpgradeSettingsInput;
TRes call({bool? enableAutoUpgrade, bool? allowReboot});
}
class _CopyWithImpl$Input$AutoUpgradeSettingsInput<TRes>
implements CopyWith$Input$AutoUpgradeSettingsInput<TRes> {
_CopyWithImpl$Input$AutoUpgradeSettingsInput(this._instance, this._then);
final Input$AutoUpgradeSettingsInput _instance;
final TRes Function(Input$AutoUpgradeSettingsInput) _then;
static const _undefined = {};
TRes call(
{Object? enableAutoUpgrade = _undefined,
Object? allowReboot = _undefined}) =>
_then(Input$AutoUpgradeSettingsInput(
enableAutoUpgrade: enableAutoUpgrade == _undefined
? _instance.enableAutoUpgrade
: (enableAutoUpgrade as bool?),
allowReboot: allowReboot == _undefined
? _instance.allowReboot
: (allowReboot as bool?)));
}
class _CopyWithStubImpl$Input$AutoUpgradeSettingsInput<TRes>
implements CopyWith$Input$AutoUpgradeSettingsInput<TRes> {
_CopyWithStubImpl$Input$AutoUpgradeSettingsInput(this._res);
TRes _res;
call({bool? enableAutoUpgrade, bool? allowReboot}) => _res;
}
@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;
}
CopyWith$Input$MigrateToBindsInput<Input$MigrateToBindsInput> get copyWith =>
CopyWith$Input$MigrateToBindsInput(this, (i) => i);
}
abstract class CopyWith$Input$MigrateToBindsInput<TRes> {
factory CopyWith$Input$MigrateToBindsInput(Input$MigrateToBindsInput instance,
TRes Function(Input$MigrateToBindsInput) then) =
_CopyWithImpl$Input$MigrateToBindsInput;
factory CopyWith$Input$MigrateToBindsInput.stub(TRes res) =
_CopyWithStubImpl$Input$MigrateToBindsInput;
TRes call(
{String? emailBlockDevice,
String? bitwardenBlockDevice,
String? giteaBlockDevice,
String? nextcloudBlockDevice,
String? pleromaBlockDevice});
}
class _CopyWithImpl$Input$MigrateToBindsInput<TRes>
implements CopyWith$Input$MigrateToBindsInput<TRes> {
_CopyWithImpl$Input$MigrateToBindsInput(this._instance, this._then);
final Input$MigrateToBindsInput _instance;
final TRes Function(Input$MigrateToBindsInput) _then;
static const _undefined = {};
TRes call(
{Object? emailBlockDevice = _undefined,
Object? bitwardenBlockDevice = _undefined,
Object? giteaBlockDevice = _undefined,
Object? nextcloudBlockDevice = _undefined,
Object? pleromaBlockDevice = _undefined}) =>
_then(Input$MigrateToBindsInput(
emailBlockDevice:
emailBlockDevice == _undefined || emailBlockDevice == null
? _instance.emailBlockDevice
: (emailBlockDevice as String),
bitwardenBlockDevice:
bitwardenBlockDevice == _undefined || bitwardenBlockDevice == null
? _instance.bitwardenBlockDevice
: (bitwardenBlockDevice as String),
giteaBlockDevice:
giteaBlockDevice == _undefined || giteaBlockDevice == null
? _instance.giteaBlockDevice
: (giteaBlockDevice as String),
nextcloudBlockDevice:
nextcloudBlockDevice == _undefined || nextcloudBlockDevice == null
? _instance.nextcloudBlockDevice
: (nextcloudBlockDevice as String),
pleromaBlockDevice:
pleromaBlockDevice == _undefined || pleromaBlockDevice == null
? _instance.pleromaBlockDevice
: (pleromaBlockDevice as String)));
}
class _CopyWithStubImpl$Input$MigrateToBindsInput<TRes>
implements CopyWith$Input$MigrateToBindsInput<TRes> {
_CopyWithStubImpl$Input$MigrateToBindsInput(this._res);
TRes _res;
call(
{String? emailBlockDevice,
String? bitwardenBlockDevice,
String? giteaBlockDevice,
String? nextcloudBlockDevice,
String? pleromaBlockDevice}) =>
_res;
}
@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;
}
CopyWith$Input$MoveServiceInput<Input$MoveServiceInput> get copyWith =>
CopyWith$Input$MoveServiceInput(this, (i) => i);
}
abstract class CopyWith$Input$MoveServiceInput<TRes> {
factory CopyWith$Input$MoveServiceInput(Input$MoveServiceInput instance,
TRes Function(Input$MoveServiceInput) then) =
_CopyWithImpl$Input$MoveServiceInput;
factory CopyWith$Input$MoveServiceInput.stub(TRes res) =
_CopyWithStubImpl$Input$MoveServiceInput;
TRes call({String? serviceId, String? location});
}
class _CopyWithImpl$Input$MoveServiceInput<TRes>
implements CopyWith$Input$MoveServiceInput<TRes> {
_CopyWithImpl$Input$MoveServiceInput(this._instance, this._then);
final Input$MoveServiceInput _instance;
final TRes Function(Input$MoveServiceInput) _then;
static const _undefined = {};
TRes call({Object? serviceId = _undefined, Object? location = _undefined}) =>
_then(Input$MoveServiceInput(
serviceId: serviceId == _undefined || serviceId == null
? _instance.serviceId
: (serviceId as String),
location: location == _undefined || location == null
? _instance.location
: (location as String)));
}
class _CopyWithStubImpl$Input$MoveServiceInput<TRes>
implements CopyWith$Input$MoveServiceInput<TRes> {
_CopyWithStubImpl$Input$MoveServiceInput(this._res);
TRes _res;
call({String? serviceId, String? location}) => _res;
}
@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;
}
CopyWith$Input$RecoveryKeyLimitsInput<Input$RecoveryKeyLimitsInput>
get copyWith => CopyWith$Input$RecoveryKeyLimitsInput(this, (i) => i);
}
abstract class CopyWith$Input$RecoveryKeyLimitsInput<TRes> {
factory CopyWith$Input$RecoveryKeyLimitsInput(
Input$RecoveryKeyLimitsInput instance,
TRes Function(Input$RecoveryKeyLimitsInput) then) =
_CopyWithImpl$Input$RecoveryKeyLimitsInput;
factory CopyWith$Input$RecoveryKeyLimitsInput.stub(TRes res) =
_CopyWithStubImpl$Input$RecoveryKeyLimitsInput;
TRes call({DateTime? expirationDate, int? uses});
}
class _CopyWithImpl$Input$RecoveryKeyLimitsInput<TRes>
implements CopyWith$Input$RecoveryKeyLimitsInput<TRes> {
_CopyWithImpl$Input$RecoveryKeyLimitsInput(this._instance, this._then);
final Input$RecoveryKeyLimitsInput _instance;
final TRes Function(Input$RecoveryKeyLimitsInput) _then;
static const _undefined = {};
TRes call({Object? expirationDate = _undefined, Object? uses = _undefined}) =>
_then(Input$RecoveryKeyLimitsInput(
expirationDate: expirationDate == _undefined
? _instance.expirationDate
: (expirationDate as DateTime?),
uses: uses == _undefined ? _instance.uses : (uses as int?)));
}
class _CopyWithStubImpl$Input$RecoveryKeyLimitsInput<TRes>
implements CopyWith$Input$RecoveryKeyLimitsInput<TRes> {
_CopyWithStubImpl$Input$RecoveryKeyLimitsInput(this._res);
TRes _res;
call({DateTime? expirationDate, int? uses}) => _res;
}
@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;
}
CopyWith$Input$SshMutationInput<Input$SshMutationInput> get copyWith =>
CopyWith$Input$SshMutationInput(this, (i) => i);
}
abstract class CopyWith$Input$SshMutationInput<TRes> {
factory CopyWith$Input$SshMutationInput(Input$SshMutationInput instance,
TRes Function(Input$SshMutationInput) then) =
_CopyWithImpl$Input$SshMutationInput;
factory CopyWith$Input$SshMutationInput.stub(TRes res) =
_CopyWithStubImpl$Input$SshMutationInput;
TRes call({String? username, String? sshKey});
}
class _CopyWithImpl$Input$SshMutationInput<TRes>
implements CopyWith$Input$SshMutationInput<TRes> {
_CopyWithImpl$Input$SshMutationInput(this._instance, this._then);
final Input$SshMutationInput _instance;
final TRes Function(Input$SshMutationInput) _then;
static const _undefined = {};
TRes call({Object? username = _undefined, Object? sshKey = _undefined}) =>
_then(Input$SshMutationInput(
username: username == _undefined || username == null
? _instance.username
: (username as String),
sshKey: sshKey == _undefined || sshKey == null
? _instance.sshKey
: (sshKey as String)));
}
class _CopyWithStubImpl$Input$SshMutationInput<TRes>
implements CopyWith$Input$SshMutationInput<TRes> {
_CopyWithStubImpl$Input$SshMutationInput(this._res);
TRes _res;
call({String? username, String? sshKey}) => _res;
}
@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;
}
CopyWith$Input$UseNewDeviceKeyInput<Input$UseNewDeviceKeyInput>
get copyWith => CopyWith$Input$UseNewDeviceKeyInput(this, (i) => i);
}
abstract class CopyWith$Input$UseNewDeviceKeyInput<TRes> {
factory CopyWith$Input$UseNewDeviceKeyInput(
Input$UseNewDeviceKeyInput instance,
TRes Function(Input$UseNewDeviceKeyInput) then) =
_CopyWithImpl$Input$UseNewDeviceKeyInput;
factory CopyWith$Input$UseNewDeviceKeyInput.stub(TRes res) =
_CopyWithStubImpl$Input$UseNewDeviceKeyInput;
TRes call({String? key, String? deviceName});
}
class _CopyWithImpl$Input$UseNewDeviceKeyInput<TRes>
implements CopyWith$Input$UseNewDeviceKeyInput<TRes> {
_CopyWithImpl$Input$UseNewDeviceKeyInput(this._instance, this._then);
final Input$UseNewDeviceKeyInput _instance;
final TRes Function(Input$UseNewDeviceKeyInput) _then;
static const _undefined = {};
TRes call({Object? key = _undefined, Object? deviceName = _undefined}) =>
_then(Input$UseNewDeviceKeyInput(
key: key == _undefined || key == null
? _instance.key
: (key as String),
deviceName: deviceName == _undefined || deviceName == null
? _instance.deviceName
: (deviceName as String)));
}
class _CopyWithStubImpl$Input$UseNewDeviceKeyInput<TRes>
implements CopyWith$Input$UseNewDeviceKeyInput<TRes> {
_CopyWithStubImpl$Input$UseNewDeviceKeyInput(this._res);
TRes _res;
call({String? key, String? deviceName}) => _res;
}
@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;
}
CopyWith$Input$UseRecoveryKeyInput<Input$UseRecoveryKeyInput> get copyWith =>
CopyWith$Input$UseRecoveryKeyInput(this, (i) => i);
}
abstract class CopyWith$Input$UseRecoveryKeyInput<TRes> {
factory CopyWith$Input$UseRecoveryKeyInput(Input$UseRecoveryKeyInput instance,
TRes Function(Input$UseRecoveryKeyInput) then) =
_CopyWithImpl$Input$UseRecoveryKeyInput;
factory CopyWith$Input$UseRecoveryKeyInput.stub(TRes res) =
_CopyWithStubImpl$Input$UseRecoveryKeyInput;
TRes call({String? key, String? deviceName});
}
class _CopyWithImpl$Input$UseRecoveryKeyInput<TRes>
implements CopyWith$Input$UseRecoveryKeyInput<TRes> {
_CopyWithImpl$Input$UseRecoveryKeyInput(this._instance, this._then);
final Input$UseRecoveryKeyInput _instance;
final TRes Function(Input$UseRecoveryKeyInput) _then;
static const _undefined = {};
TRes call({Object? key = _undefined, Object? deviceName = _undefined}) =>
_then(Input$UseRecoveryKeyInput(
key: key == _undefined || key == null
? _instance.key
: (key as String),
deviceName: deviceName == _undefined || deviceName == null
? _instance.deviceName
: (deviceName as String)));
}
class _CopyWithStubImpl$Input$UseRecoveryKeyInput<TRes>
implements CopyWith$Input$UseRecoveryKeyInput<TRes> {
_CopyWithStubImpl$Input$UseRecoveryKeyInput(this._res);
TRes _res;
call({String? key, String? deviceName}) => _res;
}
@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;
}
CopyWith$Input$UserMutationInput<Input$UserMutationInput> get copyWith =>
CopyWith$Input$UserMutationInput(this, (i) => i);
}
abstract class CopyWith$Input$UserMutationInput<TRes> {
factory CopyWith$Input$UserMutationInput(Input$UserMutationInput instance,
TRes Function(Input$UserMutationInput) then) =
_CopyWithImpl$Input$UserMutationInput;
factory CopyWith$Input$UserMutationInput.stub(TRes res) =
_CopyWithStubImpl$Input$UserMutationInput;
TRes call({String? username, String? password});
}
class _CopyWithImpl$Input$UserMutationInput<TRes>
implements CopyWith$Input$UserMutationInput<TRes> {
_CopyWithImpl$Input$UserMutationInput(this._instance, this._then);
final Input$UserMutationInput _instance;
final TRes Function(Input$UserMutationInput) _then;
static const _undefined = {};
TRes call({Object? username = _undefined, Object? password = _undefined}) =>
_then(Input$UserMutationInput(
username: username == _undefined || username == null
? _instance.username
: (username as String),
password: password == _undefined || password == null
? _instance.password
: (password as String)));
}
class _CopyWithStubImpl$Input$UserMutationInput<TRes>
implements CopyWith$Input$UserMutationInput<TRes> {
_CopyWithStubImpl$Input$UserMutationInput(this._res);
TRes _res;
call({String? username, String? password}) => _res;
}
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,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,61 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
query SystemSettings {
system {
settings {
autoUpgrade {
allowReboot
enable
}
ssh {
enable
passwordAuthentication
}
timezone
}
}
}
query SystemIsUsingBinds {
system {
info {
usingBinds
}
}
}
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,340 @@
// 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,
$__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,
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds _$Query$SystemIsUsingBindsFromJson(
Map<String, dynamic> json) =>
Query$SystemIsUsingBinds(
system: Query$SystemIsUsingBinds$system.fromJson(
json['system'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBindsToJson(
Query$SystemIsUsingBinds instance) =>
<String, dynamic>{
'system': instance.system.toJson(),
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds$system _$Query$SystemIsUsingBinds$systemFromJson(
Map<String, dynamic> json) =>
Query$SystemIsUsingBinds$system(
info: Query$SystemIsUsingBinds$system$info.fromJson(
json['info'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBinds$systemToJson(
Query$SystemIsUsingBinds$system instance) =>
<String, dynamic>{
'info': instance.info.toJson(),
'__typename': instance.$__typename,
};
Query$SystemIsUsingBinds$system$info
_$Query$SystemIsUsingBinds$system$infoFromJson(Map<String, dynamic> json) =>
Query$SystemIsUsingBinds$system$info(
usingBinds: json['usingBinds'] as bool,
$__typename: json['__typename'] as String,
);
Map<String, dynamic> _$Query$SystemIsUsingBinds$system$infoToJson(
Query$SystemIsUsingBinds$system$info instance) =>
<String, dynamic>{
'usingBinds': instance.usingBinds,
'__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,72 @@
fragment basicMutationReturnFields on MutationReturnInterface{
code
message
success
}
fragment userFields on User{
username
userType
sshKeys
}
query AllUsers {
users {
allUsers {
...userFields
}
rootUser: getUser(username: "root") {
...userFields
}
}
}
query GetUser($username: String!) {
users {
getUser(username: $username) {
...userFields
}
}
}
mutation CreateUser($user: UserMutationInput!) {
createUser(user: $user) {
...basicMutationReturnFields
user {
...userFields
}
}
}
mutation DeleteUser($username: String!) {
deleteUser(username: $username) {
...basicMutationReturnFields
}
}
mutation UpdateUser($user: UserMutationInput!) {
updateUser(user: $user) {
...basicMutationReturnFields
user {
...userFields
}
}
}
mutation AddSshKey($sshInput: SshMutationInput!) {
addSshKey(sshInput: $sshInput) {
...basicMutationReturnFields
user {
...userFields
}
}
}
mutation RemoveSshKey($sshInput: SshMutationInput!) {
removeSshKey(sshInput: $sshInput) {
...basicMutationReturnFields
user {
...userFields
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,366 @@
// 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,
};
Fragment$userFields _$Fragment$userFieldsFromJson(Map<String, dynamic> json) =>
Fragment$userFields(
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> _$Fragment$userFieldsToJson(
Fragment$userFields 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) => Fragment$userFields.fromJson(e as Map<String, dynamic>))
.toList(),
rootUser: json['rootUser'] == null
? null
: Fragment$userFields.fromJson(
json['rootUser'] as Map<String, dynamic>),
$__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(),
'rootUser': instance.rootUser?.toJson(),
'__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
: Fragment$userFields.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,
};
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
: Fragment$userFields.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(),
};
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
: Fragment$userFields.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(),
};
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
: Fragment$userFields.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(),
};
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
: Fragment$userFields.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(),
};

View File

@ -0,0 +1,45 @@
part of 'server.dart';
mixin JobsApi on ApiMap {
Future<List<ServerJob>> getServerJobs() async {
QueryResult<Query$GetApiJobs> response;
List<ServerJob> jobsList = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$GetApiJobs();
if (response.hasException) {
print(response.exception.toString());
}
jobsList = jobsList = response.parsedData?.jobs.getJobs
.map<ServerJob>((final job) => ServerJob.fromGraphQL(job))
.toList() ??
[];
} catch (e) {
print(e);
}
return jobsList;
}
Future<GenericMutationResult> removeApiJob(final String uid) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$RemoveJob(jobId: uid);
final mutation = Options$Mutation$RemoveJob(variables: variables);
final response = await client.mutate$RemoveJob(mutation);
return GenericMutationResult(
success: response.parsedData?.removeJob.success ?? false,
code: response.parsedData?.removeJob.code ?? 0,
message: response.parsedData?.removeJob.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
}

View File

@ -0,0 +1,196 @@
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/disk_volumes.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/hive/user.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';
import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/ssh_settings.dart';
import 'package:selfprivacy/logic/models/system_settings.dart';
part 'jobs_api.dart';
part 'server_actions_api.dart';
part 'services_api.dart';
part 'users_api.dart';
part 'volume_api.dart';
class GenericMutationResult {
GenericMutationResult({
required this.success,
required this.code,
this.message,
});
final bool success;
final int code;
final String? message;
}
class GenericJobMutationReturn extends GenericMutationResult {
GenericJobMutationReturn({
required final super.success,
required final super.code,
final super.message,
this.job,
});
final ServerJob? job;
}
class ServerApi extends ApiMap
with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi {
ServerApi({
this.hasLogger = false,
this.isWithToken = true,
this.customToken = '',
});
@override
bool hasLogger;
@override
bool isWithToken;
@override
String customToken;
@override
String? get rootAddress => getIt<ApiConfigModel>().serverDomain?.domainName;
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<bool> isUsingBinds() async {
QueryResult response;
bool usesBinds = false;
try {
final GraphQLClient client = await getClient();
response = await client.query$SystemIsUsingBinds();
if (response.hasException) {
print(response.exception.toString());
}
usesBinds = response.data!['system']['info']['usingBinds'];
} catch (e) {
print(e);
}
return usesBinds;
}
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<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> setAutoUpgradeSettings(
final AutoUpgradeSettings settings,
) async {
try {
final GraphQLClient client = await getClient();
final input = Input$AutoUpgradeSettingsInput(
allowReboot: settings.allowReboot,
enableAutoUpgrade: settings.enable,
);
final variables = Variables$Mutation$ChangeAutoUpgradeSettings(
settings: input,
);
final mutation = Options$Mutation$ChangeAutoUpgradeSettings(
variables: variables,
);
await client.mutate$ChangeAutoUpgradeSettings(mutation);
} catch (e) {
print(e);
}
}
Future<void> setTimezone(final String timezone) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$ChangeTimezone(
timezone: timezone,
);
final mutation = Options$Mutation$ChangeTimezone(
variables: variables,
);
await client.mutate$ChangeTimezone(mutation);
} catch (e) {
print(e);
}
}
Future<SystemSettings> getSystemSettings() async {
QueryResult<Query$SystemSettings> response;
SystemSettings settings = SystemSettings(
autoUpgradeSettings: AutoUpgradeSettings(
allowReboot: false,
enable: false,
),
sshSettings: SshSettings(
enable: false,
passwordAuthentication: false,
),
timezone: 'Unknown',
);
try {
final GraphQLClient client = await getClient();
response = await client.query$SystemSettings();
if (response.hasException) {
print(response.exception.toString());
}
settings = SystemSettings.fromGraphQL(response.parsedData!.system);
} catch (e) {
print(e);
}
return settings;
}
}

View File

@ -0,0 +1,64 @@
part of 'server.dart';
mixin ServerActionsApi on ApiMap {
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<bool> reboot() async {
try {
final GraphQLClient client = await getClient();
return await _commonBoolRequest(
() async => client.mutate$RebootSystem(),
);
} catch (e) {
return false;
}
}
Future<bool> pullConfigurationUpdate() async {
try {
final GraphQLClient client = await getClient();
return await _commonBoolRequest(
() async => client.mutate$PullRepositoryChanges(),
);
} catch (e) {
return false;
}
}
Future<bool> upgrade() async {
try {
final GraphQLClient client = await getClient();
return _commonBoolRequest(
() async => client.mutate$RunSystemUpgrade(),
);
} catch (e) {
return false;
}
}
Future<void> apply() async {
try {
final GraphQLClient client = await getClient();
await client.mutate$RunSystemRebuild();
} catch (e) {
print(e);
}
}
}

View File

@ -0,0 +1,159 @@
part of 'server.dart';
mixin ServicesApi on ApiMap {
Future<List<Service>> getAllServices() async {
QueryResult<Query$AllServices> response;
List<Service> services = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$AllServices();
if (response.hasException) {
print(response.exception.toString());
}
services = response.parsedData?.services.allServices
.map<Service>((final service) => Service.fromGraphQL(service))
.toList() ??
[];
} catch (e) {
print(e);
}
return services;
}
Future<GenericMutationResult> enableService(final String serviceId) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$EnableService(serviceId: serviceId);
final mutation = Options$Mutation$EnableService(variables: variables);
final response = await client.mutate$EnableService(mutation);
return GenericMutationResult(
success: response.parsedData?.enableService.success ?? false,
code: response.parsedData?.enableService.code ?? 0,
message: response.parsedData?.enableService.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericMutationResult> disableService(final String serviceId) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$DisableService(serviceId: serviceId);
final mutation = Options$Mutation$DisableService(variables: variables);
final response = await client.mutate$DisableService(mutation);
return GenericMutationResult(
success: response.parsedData?.disableService.success ?? false,
code: response.parsedData?.disableService.code ?? 0,
message: response.parsedData?.disableService.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericMutationResult> stopService(final String serviceId) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$StopService(serviceId: serviceId);
final mutation = Options$Mutation$StopService(variables: variables);
final response = await client.mutate$StopService(mutation);
return GenericMutationResult(
success: response.parsedData?.stopService.success ?? false,
code: response.parsedData?.stopService.code ?? 0,
message: response.parsedData?.stopService.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericMutationResult> startService(final String serviceId) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$StartService(serviceId: serviceId);
final mutation = Options$Mutation$StartService(variables: variables);
final response = await client.mutate$StartService(mutation);
return GenericMutationResult(
success: response.parsedData?.startService.success ?? false,
code: response.parsedData?.startService.code ?? 0,
message: response.parsedData?.startService.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericMutationResult> restartService(final String serviceId) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$RestartService(serviceId: serviceId);
final mutation = Options$Mutation$RestartService(variables: variables);
final response = await client.mutate$RestartService(mutation);
return GenericMutationResult(
success: response.parsedData?.restartService.success ?? false,
code: response.parsedData?.restartService.code ?? 0,
message: response.parsedData?.restartService.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericJobMutationReturn> moveService(
final String serviceId,
final String destination,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$MoveService(
input: Input$MoveServiceInput(
serviceId: serviceId,
location: destination,
),
);
final mutation = Options$Mutation$MoveService(variables: variables);
final response = await client.mutate$MoveService(mutation);
final jobJson = response.parsedData?.moveService.job?.toJson();
return GenericJobMutationReturn(
success: response.parsedData?.moveService.success ?? false,
code: response.parsedData?.moveService.code ?? 0,
message: response.parsedData?.moveService.message,
job: jobJson != null ? ServerJob.fromJson(jobJson) : null,
);
} catch (e) {
print(e);
return GenericJobMutationReturn(
success: false,
code: 0,
message: e.toString(),
job: null,
);
}
}
}

View File

@ -0,0 +1,203 @@
part of 'server.dart';
class UserMutationResult extends GenericMutationResult {
UserMutationResult({
required final super.success,
required final super.code,
final super.message,
this.user,
});
final User? user;
}
mixin UsersApi on ApiMap {
Future<List<User>> getAllUsers() async {
QueryResult<Query$AllUsers> response;
List<User> users = [];
try {
final GraphQLClient client = await getClient();
response = await client.query$AllUsers();
if (response.hasException) {
print(response.exception.toString());
}
users = response.parsedData?.users.allUsers
.map<User>((final user) => User.fromGraphQL(user))
.toList() ??
[];
final rootUser = response.parsedData?.users.rootUser;
if (rootUser != null) {
users.add(User.fromGraphQL(rootUser));
}
} catch (e) {
print(e);
}
return users;
}
Future<User?> getUser(final String login) async {
QueryResult<Query$GetUser> response;
User? user;
try {
final GraphQLClient client = await getClient();
final variables = Variables$Query$GetUser(username: login);
response = await client
.query$GetUser(Options$Query$GetUser(variables: variables));
if (response.hasException) {
print(response.exception.toString());
}
final responseUser = response.parsedData?.users.getUser;
if (responseUser != null) {
user = User.fromGraphQL(responseUser);
}
} catch (e) {
print(e);
}
return user;
}
Future<UserMutationResult> createUser(
final String username,
final String password,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$CreateUser(
user: Input$UserMutationInput(username: username, password: password),
);
final mutation = Options$Mutation$CreateUser(variables: variables);
final response = await client.mutate$CreateUser(mutation);
return UserMutationResult(
success: response.parsedData?.createUser.success ?? false,
code: response.parsedData?.createUser.code ?? 500,
message: response.parsedData?.createUser.message,
user: response.parsedData?.createUser.user != null
? User.fromGraphQL(response.parsedData!.createUser.user!)
: null,
);
} catch (e) {
print(e);
return UserMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<GenericMutationResult> deleteUser(
final String username,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$DeleteUser(username: username);
final mutation = Options$Mutation$DeleteUser(variables: variables);
final response = await client.mutate$DeleteUser(mutation);
return GenericMutationResult(
success: response.parsedData?.deleteUser.success ?? false,
code: response.parsedData?.deleteUser.code ?? 500,
message: response.parsedData?.deleteUser.message,
);
} catch (e) {
print(e);
return GenericMutationResult(
success: false,
code: 500,
message: e.toString(),
);
}
}
Future<UserMutationResult> updateUser(
final String username,
final String password,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$UpdateUser(
user: Input$UserMutationInput(username: username, password: password),
);
final mutation = Options$Mutation$UpdateUser(variables: variables);
final response = await client.mutate$UpdateUser(mutation);
return UserMutationResult(
success: response.parsedData?.updateUser.success ?? false,
code: response.parsedData?.updateUser.code ?? 500,
message: response.parsedData?.updateUser.message,
user: response.parsedData?.updateUser.user != null
? User.fromGraphQL(response.parsedData!.updateUser.user!)
: null,
);
} catch (e) {
print(e);
return UserMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<UserMutationResult> addSshKey(
final String username,
final String sshKey,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$AddSshKey(
sshInput: Input$SshMutationInput(
username: username,
sshKey: sshKey,
),
);
final mutation = Options$Mutation$AddSshKey(variables: variables);
final response = await client.mutate$AddSshKey(mutation);
return UserMutationResult(
success: response.parsedData?.addSshKey.success ?? false,
code: response.parsedData?.addSshKey.code ?? 500,
message: response.parsedData?.addSshKey.message,
user: response.parsedData?.addSshKey.user != null
? User.fromGraphQL(response.parsedData!.addSshKey.user!)
: null,
);
} catch (e) {
print(e);
return UserMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
Future<UserMutationResult> removeSshKey(
final String username,
final String sshKey,
) async {
try {
final GraphQLClient client = await getClient();
final variables = Variables$Mutation$RemoveSshKey(
sshInput: Input$SshMutationInput(
username: username,
sshKey: sshKey,
),
);
final mutation = Options$Mutation$RemoveSshKey(variables: variables);
final response = await client.mutate$RemoveSshKey(mutation);
return UserMutationResult(
success: response.parsedData?.removeSshKey.success ?? false,
code: response.parsedData?.removeSshKey.code ?? 500,
message: response.parsedData?.removeSshKey.message,
user: response.parsedData?.removeSshKey.user != null
? User.fromGraphQL(response.parsedData!.removeSshKey.user!)
: null,
);
} catch (e) {
print(e);
return UserMutationResult(
success: false,
code: 0,
message: e.toString(),
);
}
}
}

View File

@ -0,0 +1,109 @@
part of 'server.dart';
class MigrateToBindsMutationReturn extends GenericMutationResult {
MigrateToBindsMutationReturn({
required final super.success,
required final super.code,
final super.message,
this.jobUid,
});
final String? jobUid;
}
mixin VolumeApi on ApiMap {
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<MigrateToBindsMutationReturn> migrateToBinds(
final Map<String, String> serviceToDisk,
) async {
MigrateToBindsMutationReturn? mutation;
try {
final GraphQLClient client = await getClient();
final input = Input$MigrateToBindsInput(
bitwardenBlockDevice: serviceToDisk['bitwarden']!,
emailBlockDevice: serviceToDisk['mailserver']!,
giteaBlockDevice: serviceToDisk['gitea']!,
nextcloudBlockDevice: serviceToDisk['nextcloud']!,
pleromaBlockDevice: serviceToDisk['pleroma']!,
);
final variables = Variables$Mutation$MigrateToBinds(input: input);
final migrateMutation =
Options$Mutation$MigrateToBinds(variables: variables);
final QueryResult<Mutation$MigrateToBinds> result =
await client.mutate$MigrateToBinds(
migrateMutation,
);
mutation = mutation = MigrateToBindsMutationReturn(
success: result.parsedData!.migrateToBinds.success,
code: result.parsedData!.migrateToBinds.code,
message: result.parsedData!.migrateToBinds.message,
jobUid: result.parsedData!.migrateToBinds.job?.uid,
);
} catch (e) {
print(e);
mutation = MigrateToBindsMutationReturn(
success: false,
code: 0,
message: e.toString(),
jobUid: null,
);
}
return mutation;
}
}

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,110 +45,133 @@ 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;
} }
Future<String> getZoneId(final String domain) async { @override
validateStatus = (final status) => Future<String?> getZoneId(final String domain) async {
status == HttpStatus.ok || status == HttpStatus.forbidden; String? zoneId;
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get( try {
'/zones', final Response response = await client.get(
queryParameters: {'name': domain}, '/zones',
); queryParameters: {'name': domain},
);
close(client); zoneId = response.data['result'][0]['id'];
} catch (e) {
if (response.data['result'].isEmpty) { print(e);
throw DomainNotFoundException('No domains found'); } finally {
} else { close(client);
return response.data['result'][0]['id'];
} }
return zoneId;
} }
@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';
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get(url); try {
final Response response = await client.get(url);
final List records = response.data['result'] ?? []; final List records = response.data['result'] ?? [];
final List<Future> allDeleteFutures = <Future>[]; final List<Future> allDeleteFutures = <Future>[];
for (final record in records) { for (final record in records) {
if (record['zone_name'] == domainName) { if (record['zone_name'] == domainName) {
allDeleteFutures.add( allDeleteFutures.add(
client.delete('$url/${record["id"]}'), client.delete('$url/${record["id"]}'),
); );
}
} }
await Future.wait(allDeleteFutures);
} catch (e) {
print(e);
} finally {
close(client);
} }
await Future.wait(allDeleteFutures);
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; Response response;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainName = domain.domainName;
final String domainZoneId = domain.zoneId;
final List<DnsRecord> allRecords = <DnsRecord>[];
final String url = '/zones/$domainZoneId/dns_records'; final String url = '/zones/$domainZoneId/dns_records';
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get(url); try {
response = await client.get(url);
final List records = response.data['result'] ?? [];
final List records = response.data['result'] ?? []; for (final record in records) {
final List<DnsRecord> allRecords = <DnsRecord>[]; if (record['zone_name'] == domainName) {
allRecords.add(
for (final record in records) { DnsRecord(
if (record['zone_name'] == domainName) { name: record['name'],
allRecords.add( type: record['type'],
DnsRecord( content: record['content'],
name: record['name'], ttl: record['ttl'],
type: record['type'], proxied: record['proxied'],
content: record['content'], ),
ttl: record['ttl'], );
proxied: record['proxied'], }
),
);
} }
} catch (e) {
print(e);
} finally {
close(client);
} }
close(client);
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 +241,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(
@ -234,26 +257,38 @@ class CloudflareApi extends ApiMap {
); );
final Dio client = await getClient(); final Dio client = await getClient();
await client.post( try {
url, await client.post(
data: dkimRecord.toJson(), url,
); data: dkimRecord.toJson(),
);
client.close(); } catch (e) {
print(e);
} finally {
close(client);
}
} }
@override
Future<List<String>> domainList() async { Future<List<String>> domainList() async {
final String url = '$rootAddress/zones'; final String url = '$rootAddress/zones';
List<String> domains = [];
final Dio client = await getClient(); final Dio client = await getClient();
try {
final Response response = await client.get(
url,
queryParameters: {'per_page': 50},
);
domains = response.data['result']
.map<String>((final el) => el['name'] as String)
.toList();
} catch (e) {
print(e);
} finally {
close(client);
}
final Response response = await client.get( return domains;
url,
queryParameters: {'per_page': 50},
);
close(client);
return response.data['result']
.map<String>((final el) => el['name'] as String)
.toList();
} }
} }

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

@ -4,19 +4,17 @@ 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/rest_maps/api_map.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_bucket.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.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/api_token.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/backup.dart'; import 'package:selfprivacy/logic/models/json/backup.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/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';
class ApiResponse<D> { class ApiResponse<D> {
ApiResponse({ ApiResponse({
required this.statusCode, required this.statusCode,
@ -46,16 +44,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 +67,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 +103,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);
} }
@ -126,6 +130,7 @@ class ServerApi extends ApiMap {
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
data: User( data: User(
login: user.login, login: user.login,
type: UserType.normal,
password: user.password, password: user.password,
isFoundOnServer: false, isFoundOnServer: false,
), ),
@ -152,6 +157,7 @@ class ServerApi extends ApiMap {
statusCode: code, statusCode: code,
data: User( data: User(
login: user.login, login: user.login,
type: UserType.normal,
password: user.password, password: user.password,
isFoundOnServer: isFoundOnServer, isFoundOnServer: isFoundOnServer,
), ),
@ -403,11 +409,11 @@ class ServerApi extends ApiMap {
} }
return { return {
ServiceTypes.passwordManager: response.data['bitwarden'] == 0, ServiceTypes.bitwarden: response.data['bitwarden'] == 0,
ServiceTypes.git: response.data['gitea'] == 0, ServiceTypes.gitea: response.data['gitea'] == 0,
ServiceTypes.cloud: response.data['nextcloud'] == 0, ServiceTypes.nextcloud: response.data['nextcloud'] == 0,
ServiceTypes.vpn: response.data['ocserv'] == 0, ServiceTypes.ocserv: response.data['ocserv'] == 0,
ServiceTypes.socialNetwork: response.data['pleroma'] == 0, ServiceTypes.pleroma: response.data['pleroma'] == 0,
}; };
} }
@ -558,51 +564,21 @@ class ServerApi extends ApiMap {
return result; return result;
} }
Future<AutoUpgradeSettings> getAutoUpgradeSettings() async {
Response response;
AutoUpgradeSettings settings = const AutoUpgradeSettings(
enable: false,
allowReboot: false,
);
final Dio client = await getClient();
try {
response = await client.get('/system/configuration/autoUpgrade');
if (response.data != null) {
settings = AutoUpgradeSettings.fromJson(response.data);
}
} on DioError catch (e) {
print(e.message);
} finally {
close(client);
}
return settings;
}
Future<void> updateAutoUpgradeSettings(
final AutoUpgradeSettings settings,
) async {
final Dio client = await getClient();
try {
await client.put(
'/system/configuration/autoUpgrade',
data: settings.toJson(),
);
} on DioError catch (e) {
print(e.message);
} finally {
close(client);
}
}
Future<TimeZoneSettings> getServerTimezone() async { Future<TimeZoneSettings> getServerTimezone() async {
// I am not sure how to initialize TimeZoneSettings with default value... TimeZoneSettings settings = TimeZoneSettings();
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = try {
await client.get('/system/configuration/timezone'); final Response response = await client.get(
close(client); '/system/configuration/timezone',
);
settings = TimeZoneSettings.fromString(response.data);
} catch (e) {
print(e);
} finally {
close(client);
}
return TimeZoneSettings.fromString(response.data); return settings;
} }
Future<void> updateServerTimezone(final TimeZoneSettings settings) async { Future<void> updateServerTimezone(final TimeZoneSettings settings) async {
@ -621,36 +597,23 @@ class ServerApi extends ApiMap {
Future<String?> getDkim() async { Future<String?> getDkim() async {
Response response; Response response;
String? dkim;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
response = await client.get('/services/mailserver/dkim'); response = await client.get('/services/mailserver/dkim');
final Codec<String, String> base64toString = utf8.fuse(base64);
dkim = base64toString
.decode(response.data)
.split('(')[1]
.split(')')[0]
.replaceAll('"', '');
} on DioError catch (e) { } on DioError catch (e) {
print(e.message); print(e.message);
return null;
} finally { } finally {
close(client); close(client);
} }
if (response.statusCode == null) { return dkim;
return null;
}
if (response.statusCode == HttpStatus.notFound || response.data == null) {
throw Exception('No DKIM key found');
}
if (response.statusCode != HttpStatus.ok) {
return '';
}
final Codec<String, String> base64toString = utf8.fuse(base64);
return base64toString
.decode(response.data)
.split('(')[1]
.split(')')[0]
.replaceAll('"', '');
} }
Future<ApiResponse<RecoveryKeyStatus?>> getRecoveryTokenStatus() async { Future<ApiResponse<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
@ -914,15 +877,15 @@ extension UrlServerExt on ServiceTypes {
// return ''; // external service // return ''; // external service
// case ServiceTypes.video: // case ServiceTypes.video:
// return ''; // jitsi meet not working // return ''; // jitsi meet not working
case ServiceTypes.passwordManager: case ServiceTypes.bitwarden:
return 'bitwarden'; return 'bitwarden';
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return 'nextcloud'; return 'nextcloud';
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return 'pleroma'; return 'pleroma';
case ServiceTypes.git: case ServiceTypes.gitea:
return 'gitea'; return 'gitea';
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return 'ocserv'; return 'ocserv';
default: default:
throw Exception('wrong state'); throw Exception('wrong state');

View File

@ -0,0 +1,558 @@
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'] * 1024 * 1024 * 1024;
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;
Map<String, dynamic> metrics = {};
final Dio client = await getClient();
try {
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,
);
metrics = res.data;
} catch (e) {
print(e);
} finally {
close(client);
}
return metrics;
}
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']
.map<HetznerServerInfo>(
(final e) => HetznerServerInfo.fromJson(e),
)
.toList()
.where(
(final server) => server.publicNet.ipv4 != null,
)
.map<ServerBasicInfo>(
(final 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);
}
print(servers);
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

@ -20,97 +20,104 @@ enum InitializingSteps {
checkSystemDnsAndDkimSet, checkSystemDnsAndDkimSet,
} }
enum Period { hour, day, month } enum Period {
hour,
day,
month;
int get stepPeriodInSeconds {
switch (this) {
case Period.hour:
return 18;
case Period.day:
return 432;
case Period.month:
return 6480;
}
}
}
enum ServiceTypes { enum ServiceTypes {
mail, mailserver,
messenger, bitwarden,
passwordManager, jitsi,
video, nextcloud,
cloud, pleroma,
socialNetwork, gitea,
git, ocserv,
vpn,
} }
extension ServiceTypesExt on ServiceTypes { extension ServiceTypesExt on ServiceTypes {
String get title { String get title {
switch (this) { switch (this) {
case ServiceTypes.mail: case ServiceTypes.mailserver:
return 'services.mail.title'.tr(); return 'services.mail.title'.tr();
case ServiceTypes.messenger: case ServiceTypes.bitwarden:
return 'services.messenger.title'.tr();
case ServiceTypes.passwordManager:
return 'services.password_manager.title'.tr(); return 'services.password_manager.title'.tr();
case ServiceTypes.video: case ServiceTypes.jitsi:
return 'services.video.title'.tr(); return 'services.video.title'.tr();
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return 'services.cloud.title'.tr(); return 'services.cloud.title'.tr();
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return 'services.social_network.title'.tr(); return 'services.social_network.title'.tr();
case ServiceTypes.git: case ServiceTypes.gitea:
return 'services.git.title'.tr(); return 'services.git.title'.tr();
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return 'services.vpn.title'.tr(); return 'services.vpn.title'.tr();
} }
} }
String get subtitle { String get subtitle {
switch (this) { switch (this) {
case ServiceTypes.mail: case ServiceTypes.mailserver:
return 'services.mail.subtitle'.tr(); return 'services.mail.subtitle'.tr();
case ServiceTypes.messenger: case ServiceTypes.bitwarden:
return 'services.messenger.subtitle'.tr();
case ServiceTypes.passwordManager:
return 'services.password_manager.subtitle'.tr(); return 'services.password_manager.subtitle'.tr();
case ServiceTypes.video: case ServiceTypes.jitsi:
return 'services.video.subtitle'.tr(); return 'services.video.subtitle'.tr();
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return 'services.cloud.subtitle'.tr(); return 'services.cloud.subtitle'.tr();
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return 'services.social_network.subtitle'.tr(); return 'services.social_network.subtitle'.tr();
case ServiceTypes.git: case ServiceTypes.gitea:
return 'services.git.subtitle'.tr(); return 'services.git.subtitle'.tr();
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return 'services.vpn.subtitle'.tr(); return 'services.vpn.subtitle'.tr();
} }
} }
String get loginInfo { String get loginInfo {
switch (this) { switch (this) {
case ServiceTypes.mail: case ServiceTypes.mailserver:
return 'services.mail.login_info'.tr(); return 'services.mail.login_info'.tr();
case ServiceTypes.messenger: case ServiceTypes.bitwarden:
return 'services.messenger.login_info'.tr();
case ServiceTypes.passwordManager:
return 'services.password_manager.login_info'.tr(); return 'services.password_manager.login_info'.tr();
case ServiceTypes.video: case ServiceTypes.jitsi:
return 'services.video.login_info'.tr(); return 'services.video.login_info'.tr();
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return 'services.cloud.login_info'.tr(); return 'services.cloud.login_info'.tr();
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return 'services.social_network.login_info'.tr(); return 'services.social_network.login_info'.tr();
case ServiceTypes.git: case ServiceTypes.gitea:
return 'services.git.login_info'.tr(); return 'services.git.login_info'.tr();
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return ''; return '';
} }
} }
String get subdomain { String get subdomain {
switch (this) { switch (this) {
case ServiceTypes.passwordManager: case ServiceTypes.bitwarden:
return 'password'; return 'password';
case ServiceTypes.video: case ServiceTypes.jitsi:
return 'meet'; return 'meet';
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return 'cloud'; return 'cloud';
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return 'social'; return 'social';
case ServiceTypes.git: case ServiceTypes.gitea:
return 'git'; return 'git';
case ServiceTypes.vpn: case ServiceTypes.ocserv:
case ServiceTypes.messenger:
default: default:
return ''; return '';
} }
@ -118,21 +125,19 @@ extension ServiceTypesExt on ServiceTypes {
IconData get icon { IconData get icon {
switch (this) { switch (this) {
case ServiceTypes.mail: case ServiceTypes.mailserver:
return BrandIcons.envelope; return BrandIcons.envelope;
case ServiceTypes.messenger: case ServiceTypes.bitwarden:
return BrandIcons.messanger;
case ServiceTypes.passwordManager:
return BrandIcons.key; return BrandIcons.key;
case ServiceTypes.video: case ServiceTypes.jitsi:
return BrandIcons.webcam; return BrandIcons.webcam;
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return BrandIcons.upload; return BrandIcons.upload;
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return BrandIcons.social; return BrandIcons.social;
case ServiceTypes.git: case ServiceTypes.gitea:
return BrandIcons.git; return BrandIcons.git;
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return Icons.vpn_lock_outlined; return Icons.vpn_lock_outlined;
} }
} }

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,15 +1,17 @@
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/server_api/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';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';
part 'jobs_state.dart'; part 'client_jobs_state.dart';
class JobsCubit extends Cubit<JobsState> { class JobsCubit extends Cubit<JobsState> {
JobsCubit({ JobsCubit({
@ -21,10 +23,11 @@ class JobsCubit extends Cubit<JobsState> {
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());
@ -37,14 +40,14 @@ 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);
@ -56,9 +59,9 @@ class JobsCubit extends Cubit<JobsState> {
} }
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);
@ -98,10 +101,11 @@ class JobsCubit extends Cubit<JobsState> {
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());
bool hasServiceJobs = false; bool hasServiceJobs = false;
for (final Job job in jobs) { for (final ClientJob job in jobs) {
// TODO: Rewrite to polymorphism
if (job is CreateUserJob) { if (job is CreateUserJob) {
await usersCubit.createUser(job.user); await usersCubit.createUser(job.user);
} }
@ -110,7 +114,7 @@ class JobsCubit extends Cubit<JobsState> {
} }
if (job is ServiceToggleJob) { if (job is ServiceToggleJob) {
hasServiceJobs = true; hasServiceJobs = true;
await api.switchService(job.type, job.needToTurnOn); await api.switchService(job.type.name, job.needToTurnOn);
} }
if (job is CreateSSHKeyJob) { if (job is CreateSSHKeyJob) {
await usersCubit.addSshKey(job.user, job.publicKey); await usersCubit.addSshKey(job.user, job.publicKey);
@ -118,10 +122,17 @@ class JobsCubit extends Cubit<JobsState> {
if (job is DeleteSSHKeyJob) { if (job is DeleteSSHKeyJob) {
await usersCubit.deleteSshKey(job.user, job.publicKey); await usersCubit.deleteSshKey(job.user, job.publicKey);
} }
if (job is ResetUserPasswordJob) {
await usersCubit.changeUserPassword(job.user, job.user.password!);
}
if (job is RebuildServerJob) {
await upgradeServer();
}
} }
await api.pullConfigurationUpdate(); await api.pullConfigurationUpdate();
await api.apply(); await api.apply();
if (hasServiceJobs) { if (hasServiceJobs) {
await servicesCubit.load(); await servicesCubit.load();
} }

View File

@ -1,4 +1,4 @@
part of 'jobs_cubit.dart'; part of 'client_jobs_cubit.dart';
abstract class JobsState extends Equatable { abstract class JobsState extends Equatable {
@override @override
@ -10,13 +10,11 @@ class JobsStateLoading extends JobsState {}
class JobsStateEmpty extends JobsState {} class JobsStateEmpty extends JobsState {}
class JobsStateWithJobs extends JobsState { class JobsStateWithJobs extends JobsState {
JobsStateWithJobs(this.jobList); JobsStateWithJobs(this.clientJobList);
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();
} }
@ -24,5 +22,5 @@ class JobsStateWithJobs extends JobsState {
} }
@override @override
List<Object?> get props => jobList; List<Object?> get props => clientJobList;
} }

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();
} }
@ -130,7 +139,7 @@ class DnsRecordsCubit
final String? ipAddress, final String? ipAddress,
final String? dkimPublicKey, final String? dkimPublicKey,
) { ) {
if (domainName == null || ipAddress == null || dkimPublicKey == null) { if (domainName == null || ipAddress == null) {
return []; return [];
} }
return [ return [
@ -195,13 +204,14 @@ class DnsRecordsCubit
type: 'TXT', type: 'TXT',
category: DnsRecordsCategory.email, category: DnsRecordsCategory.email,
), ),
DesiredDnsRecord( if (dkimPublicKey != null)
name: 'selector._domainkey.$domainName', DesiredDnsRecord(
content: dkimPublicKey, name: 'selector._domainkey.$domainName',
description: 'providers.domain.record_description.dkim', content: dkimPublicKey,
type: 'TXT', description: 'providers.domain.record_description.dkim',
category: DnsRecordsCategory.email, type: 'TXT',
), category: DnsRecordsCategory.email,
),
]; ];
} }
} }

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,20 +28,24 @@ 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( if (zoneId != null) {
domainName: domainName, final ServerDomain domain = ServerDomain(
zoneId: zoneId, domainName: domainName,
provider: DnsProvider.cloudflare, zoneId: zoneId,
); provider: DnsProvider.cloudflare,
);
serverInstallationCubit.setDomain(domain); serverInstallationCubit.setDomain(domain);
emit(DomainSet()); emit(DomainSet());
}
} }
} }

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

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.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';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
class RootUserFormCubit extends FormCubit { class RootUserFormCubit extends FormCubit {
@ -22,6 +22,7 @@ class RootUserFormCubit extends FormCubit {
FutureOr<void> onSubmit() async { FutureOr<void> onSubmit() async {
final User user = User( final User user = User(
login: userName.state.value, login: userName.state.value,
type: UserType.primary,
password: password.state.value, password: password.state.value,
); );
serverInstallationCubit.setRootUser(user); serverInstallationCubit.setRootUser(user);

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

@ -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/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
@ -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,37 +1,60 @@
import 'dart:async'; import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/utils/password_generator.dart'; import 'package:selfprivacy/utils/password_generator.dart';
class UserFormCubit extends FormCubit { class UserFormCubit extends FormCubit {
UserFormCubit({ UserFormCubit({
required this.jobsCubit, required this.jobsCubit,
required final FieldCubitFactory fieldFactory, required final FieldCubitFactory fieldFactory,
final User? user, final this.initialUser,
}) { }) {
final bool isEdit = user != null; if (initialUser == null) {
login = fieldFactory.createUserLoginField();
login.setValue('');
password = fieldFactory.createUserPasswordField();
password.setValue(
StringGenerators.userPassword(),
);
login = fieldFactory.createUserLoginField(); super.addFields([login, password]);
login.setValue(isEdit ? user.login : ''); } else {
password = fieldFactory.createUserPasswordField(); login = fieldFactory.createRequiredStringField();
password.setValue( login.setValue(initialUser!.login);
isEdit ? (user.password ?? '') : StringGenerators.userPassword(), password = fieldFactory.createUserPasswordField();
); password.setValue(
initialUser?.password ?? '',
);
super.addFields([login, password]); super.addFields([login, password]);
}
} }
@override @override
FutureOr<void> onSubmit() { FutureOr<void> onSubmit() {
final User user = User( print('onSubmit');
login: login.state.value, print('initialUser: $initialUser');
password: password.state.value, print('login: ${login.state.value}');
); print('password: ${password.state.value}');
jobsCubit.addJob(CreateUserJob(user: user)); if (initialUser == null) {
final User user = User(
login: login.state.value,
type: UserType.normal,
password: password.state.value,
);
jobsCubit.addJob(CreateUserJob(user: user));
} else {
final User user = User(
login: initialUser?.login ?? login.state.value,
type: initialUser?.type ?? UserType.normal,
password: password.state.value,
);
jobsCubit.addJob(ResetUserPasswordJob(user: user));
}
} }
late FieldCubit<String> login; late FieldCubit<String> login;
@ -42,4 +65,5 @@ class UserFormCubit extends FormCubit {
} }
final JobsCubit jobsCubit; final JobsCubit jobsCubit;
final User? initialUser;
} }

View File

@ -39,12 +39,20 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
} }
void load(final Period period) async { void load(final Period period) async {
final HetznerMetricsLoaded newState = await repository.getMetrics(period); try {
timer = Timer( final HetznerMetricsLoaded newState = await repository.getMetrics(period);
Duration(seconds: newState.stepInSeconds.toInt()), timer = Timer(
() => load(newState.period), Duration(seconds: newState.stepInSeconds.toInt()),
); () => load(newState.period),
);
emit(newState); emit(newState);
} on StateError {
print('Tried to emit Hetzner metrics when cubit is closed');
} on MetricsLoadException {
timer = Timer(
Duration(seconds: state.period.stepPeriodInSeconds),
() => load(state.period),
);
}
} }
} }

View File

@ -1,9 +1,14 @@
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';
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart'; import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
class MetricsLoadException implements Exception {
MetricsLoadException(this.message);
final String message;
}
class HetznerMetricsRepository { class HetznerMetricsRepository {
Future<HetznerMetricsLoaded> getMetrics(final Period period) async { Future<HetznerMetricsLoaded> getMetrics(final Period period) async {
final DateTime end = DateTime.now(); final DateTime end = DateTime.now();
@ -21,7 +26,7 @@ class HetznerMetricsRepository {
break; break;
} }
final HetznerApi api = HetznerApi(hasLogger: true); final HetznerApi api = HetznerApi(hasLogger: false);
final List<Map<String, dynamic>> results = await Future.wait([ final List<Map<String, dynamic>> results = await Future.wait([
api.getMetrics(start, end, 'cpu'), api.getMetrics(start, end, 'cpu'),
@ -31,6 +36,10 @@ class HetznerMetricsRepository {
final cpuMetricsData = results[0]['metrics']; final cpuMetricsData = results[0]['metrics'];
final networkMetricsData = results[1]['metrics']; final networkMetricsData = results[1]['metrics'];
if (cpuMetricsData == null || networkMetricsData == null) {
throw MetricsLoadException('Metrics data is null');
}
return HetznerMetricsLoaded( return HetznerMetricsLoaded(
period: period, period: period,
start: start, start: start,

View File

@ -0,0 +1,141 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/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/logic/models/disk_status.dart';
part 'provider_volume_state.dart';
class ApiProviderVolumeCubit
extends ServerInstallationDependendCubit<ApiProviderVolumeState> {
ApiProviderVolumeCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, const ApiProviderVolumeState.initial());
VolumeProviderApiFactory? providerApi;
final ServerApi serverApi = ServerApi();
@override
Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final serverDetails = getIt<ApiConfigModel>().serverDetails;
providerApi = serverDetails == null
? null
: VolumeApiFactoryCreator.createVolumeProviderApiFactory(
getIt<ApiConfigModel>().serverDetails!.provider,
);
_refetch();
}
}
Future<double?> getPricePerGb() async =>
providerApi!.getVolumeProvider().getPricePerGb();
Future<void> refresh() async {
emit(const ApiProviderVolumeState([], LoadingStatus.refreshing, false));
_refetch();
}
Future<void> _refetch() async {
if (providerApi == null) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
}
final List<ServerVolume> volumes =
await providerApi!.getVolumeProvider().getVolumes();
if (volumes.isEmpty) {
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
}
emit(ApiProviderVolumeState(volumes, LoadingStatus.success, false));
}
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,
final Function() callback,
) async {
getIt<NavigationService>().showSnackBar(
'Starting resize',
);
emit(state.copyWith(isResizing: true));
final bool resized = await providerApi!.getVolumeProvider().resizeVolume(
volume.providerVolume!.id,
newSizeGb,
);
if (!resized) {
getIt<NavigationService>().showSnackBar(
'providers.storage.extending_volume_error'.tr(),
);
emit(state.copyWith(isResizing: false));
return false;
}
getIt<NavigationService>().showSnackBar(
'Hetzner resized, waiting 10 seconds',
);
await Future.delayed(const Duration(seconds: 10));
await ServerApi().resizeVolume(volume.name);
getIt<NavigationService>().showSnackBar(
'Server api resized, waiting 20 seconds',
);
await Future.delayed(const Duration(seconds: 20));
getIt<NavigationService>().showSnackBar(
'Restarting server',
);
await refresh();
emit(state.copyWith(isResizing: false));
await callback();
await serverApi.reboot();
return true;
}
Future<void> createVolume() async {
final ServerVolume? volume =
await providerApi!.getVolumeProvider().createVolume();
final 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,27 @@
part of 'provider_volume_cubit.dart';
class ApiProviderVolumeState extends ServerInstallationDependendState {
const ApiProviderVolumeState(this._volumes, this.status, this.isResizing);
const ApiProviderVolumeState.initial()
: this(const [], LoadingStatus.uninitialized, false);
final List<ServerVolume> _volumes;
final LoadingStatus status;
final bool isResizing;
List<ServerVolume> get volumes => _volumes;
ApiProviderVolumeState copyWith({
final List<ServerVolume>? volumes,
final LoadingStatus? status,
final bool? isResizing,
}) =>
ApiProviderVolumeState(
volumes ?? _volumes,
status ?? this.status,
isResizing ?? this.isResizing,
);
@override
List<Object?> get props => [_volumes, status, isResizing];
}

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,33 +1,48 @@
import 'package:flutter_bloc/flutter_bloc.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/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/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';
part 'server_detailed_info_state.dart'; part 'server_detailed_info_state.dart';
class ServerDetailsCubit extends Cubit<ServerDetailsState> { class ServerDetailsCubit
ServerDetailsCubit() : super(ServerDetailsInitial()); extends ServerInstallationDependendCubit<ServerDetailsState> {
ServerDetailsCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, ServerDetailsInitial());
ServerDetailsRepository repository = ServerDetailsRepository(); ServerDetailsRepository repository = ServerDetailsRepository();
void check() async { void check() async {
final bool isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null; final bool isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null;
if (isReadyToCheck) { try {
emit(ServerDetailsLoading()); if (isReadyToCheck) {
final ServerDetailsRepositoryDto data = await repository.load(); emit(ServerDetailsLoading());
emit( final ServerDetailsRepositoryDto data = await repository.load();
Loaded( emit(
serverInfo: data.hetznerServerInfo, Loaded(
autoUpgradeSettings: data.autoUpgradeSettings, serverInfo: data.hetznerServerInfo,
serverTimezone: data.serverTimezone, autoUpgradeSettings: data.autoUpgradeSettings,
checkTime: DateTime.now(), serverTimezone: data.serverTimezone,
), checkTime: DateTime.now(),
); ),
} else { );
emit(ServerDetailsNotReady()); } else {
emit(ServerDetailsNotReady());
}
} on StateError {
print('Tried to emit server info state when cubit is closed');
} }
} }
@override
void clear() {
emit(ServerDetailsNotReady());
}
@override
void load() async {
check();
}
} }

View File

@ -1,21 +1,37 @@
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/graphql_maps/server_api/server.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/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';
class ServerDetailsRepository { class ServerDetailsRepository {
HetznerApi hetznerAPi = HetznerApi(); HetznerApi hetzner = HetznerApi();
ServerApi selfprivacyServer = ServerApi(); ServerApi server = ServerApi();
Future<ServerDetailsRepositoryDto> load() async { Future<ServerDetailsRepositoryDto> load() async {
print('load'); final settings = await server.getSystemSettings();
return ServerDetailsRepositoryDto( return ServerDetailsRepositoryDto(
autoUpgradeSettings: await selfprivacyServer.getAutoUpgradeSettings(), autoUpgradeSettings: settings.autoUpgradeSettings,
hetznerServerInfo: await hetznerAPi.getInfo(), hetznerServerInfo: await hetzner.getInfo(),
serverTimezone: await selfprivacyServer.getServerTimezone(), serverTimezone: TimeZoneSettings.fromString(
settings.timezone,
),
); );
} }
Future<void> setAutoUpgradeSettings(
final AutoUpgradeSettings settings,
) async {
await server.setAutoUpgradeSettings(settings);
}
Future<void> setTimezone(
final String timezone,
) async {
if (timezone.isNotEmpty) {
await server.setTimezone(timezone);
}
}
} }
class ServerDetailsRepositoryDto { class ServerDetailsRepositoryDto {

View File

@ -1,6 +1,6 @@
part of 'server_detailed_info_cubit.dart'; part of 'server_detailed_info_cubit.dart';
abstract class ServerDetailsState extends Equatable { abstract class ServerDetailsState extends ServerInstallationDependendState {
const ServerDetailsState(); const ServerDetailsState();
@override @override

View File

@ -4,6 +4,8 @@ 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/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';
@ -49,13 +51,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 +92,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
emit( emit(
(state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey), (state as ServerInstallationNotFinished).copyWith(
providerApiToken: hetznerKey,
),
); );
} }
@ -117,7 +148,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 +198,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 +324,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 +461,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 +508,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 +535,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,
@ -561,24 +608,6 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
@override @override
void onChange(final Change<ServerInstallationState> change) { void onChange(final Change<ServerInstallationState> change) {
super.onChange(change); super.onChange(change);
print('================================');
print('ServerInstallationState changed!');
print('Current type: ${change.nextState.runtimeType}');
print('Hetzner key: ${change.nextState.hetznerKey}');
print('Cloudflare key: ${change.nextState.cloudFlareKey}');
print('Domain: ${change.nextState.serverDomain}');
print('BackblazeCredential: ${change.nextState.backblazeCredential}');
if (change.nextState is ServerInstallationRecovery) {
print(
'Recovery Step: ${(change.nextState as ServerInstallationRecovery).currentStep}',
);
print(
'Recovery Capabilities: ${(change.nextState as ServerInstallationRecovery).recoveryCapabilities}',
);
}
if (change.nextState is TimerState) {
print('Timer: ${(change.nextState as TimerState).duration}');
}
} }
void clearAppConfig() { void clearAppConfig() {
@ -597,7 +626,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,23 +9,24 @@ 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.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/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.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: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';
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
class IpNotFoundException implements Exception { class IpNotFoundException implements Exception {
IpNotFoundException(this.message); IpNotFoundException(this.message);
final String message; final String message;
@ -39,9 +40,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 +58,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 +91,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 +108,7 @@ class ServerInstallationRepository {
} }
return ServerInstallationNotFinished( return ServerInstallationNotFinished(
hetznerKey: hetznerToken, providerApiToken: providerApiToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
serverDomain: serverDomain, serverDomain: serverDomain,
backblazeCredential: backblazeCredential, backblazeCredential: backblazeCredential,
@ -130,24 +153,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 { final String? domainId = await dnsProviderApi.getZoneId(domain);
final String domainId = await cloudflareApi.getZoneId(domain); return domainId;
return domainId;
} on DomainNotFoundException {
return null;
}
} }
Future<Map<String, bool>> isDnsAddressesMatch( Future<Map<String, bool>> isDnsAddressesMatch(
@ -208,18 +231,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 +257,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 +328,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 +361,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 +375,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 +505,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 +542,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 +579,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 +607,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,
@ -550,6 +628,7 @@ class ServerInstallationRepository {
final ServerApi serverApi = ServerApi(); final ServerApi serverApi = ServerApi();
const User fallbackUser = User( const User fallbackUser = User(
isFoundOnServer: false, isFoundOnServer: false,
type: UserType.primary,
note: "Couldn't find main user on server, API is outdated", note: "Couldn't find main user on server, API is outdated",
login: 'UNKNOWN', login: 'UNKNOWN',
sshKeys: [], sshKeys: [],
@ -569,27 +648,16 @@ class ServerInstallationRepository {
return User( return User(
isFoundOnServer: true, isFoundOnServer: true,
login: users.data[0], login: users.data[0],
type: UserType.primary,
); );
} on FormatException { } on FormatException {
return fallbackUser; return fallbackUser;
} }
} }
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 +666,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 +687,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 +735,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 +750,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,117 @@
import 'dart:async';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
export 'package:provider/provider.dart';
part 'server_jobs_state.dart';
class ServerJobsCubit
extends ServerInstallationDependendCubit<ServerJobsState> {
ServerJobsCubit(final ServerInstallationCubit serverInstallationCubit)
: super(
serverInstallationCubit,
ServerJobsState(),
);
Timer? timer;
final ServerApi api = ServerApi();
@override
void clear() async {
emit(
ServerJobsState(),
);
if (timer != null && timer!.isActive) {
timer!.cancel();
timer = null;
}
}
@override
void load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final List<ServerJob> jobs = await api.getServerJobs();
emit(
ServerJobsState(
serverJobList: jobs,
),
);
timer = Timer(const Duration(seconds: 5), () => reload(useTimer: true));
}
}
Future<void> migrateToBinds(final Map<String, String> serviceToDisk) async {
final result = await api.migrateToBinds(serviceToDisk);
if (!result.success || result.jobUid == null) {
getIt<NavigationService>().showSnackBar(result.message!);
return;
}
emit(
ServerJobsState(
migrationJobUid: result.jobUid,
),
);
}
ServerJob? getServerJobByUid(final String uid) {
ServerJob? job;
try {
job = state.serverJobList.firstWhere(
(final ServerJob job) => job.uid == uid,
);
} catch (e) {
print(e);
}
return job;
}
Future<void> removeServerJob(final String uid) async {
final result = await api.removeApiJob(uid);
if (!result.success) {
getIt<NavigationService>().showSnackBar(result.message!);
return;
}
emit(
ServerJobsState(
serverJobList: [
for (final ServerJob job in state.serverJobList)
if (job.uid != uid) job
],
),
);
print('removed job $uid');
}
Future<void> removeAllFinishedJobs() async {
final List<ServerJob> finishedJobs = state.serverJobList
.where(
(final ServerJob job) =>
job.status == JobStatusEnum.finished ||
job.status == JobStatusEnum.error,
)
.toList();
for (final ServerJob job in finishedJobs) {
await removeServerJob(job.uid);
}
}
Future<void> reload({final bool useTimer = false}) async {
final List<ServerJob> jobs = await api.getServerJobs();
emit(
ServerJobsState(
serverJobList: jobs,
),
);
if (useTimer) {
timer = Timer(const Duration(seconds: 5), () => reload(useTimer: true));
}
}
}

View File

@ -0,0 +1,37 @@
part of 'server_jobs_cubit.dart';
class ServerJobsState extends ServerInstallationDependendState {
ServerJobsState({
final serverJobList = const <ServerJob>[],
this.migrationJobUid,
}) {
_serverJobList = serverJobList;
}
late final List<ServerJob> _serverJobList;
final String? migrationJobUid;
List<ServerJob> get serverJobList {
final List<ServerJob> list = _serverJobList;
list.sort((final a, final b) => b.createdAt.compareTo(a.createdAt));
return list;
}
bool get hasRemovableJobs => serverJobList.any(
(final job) =>
job.status == JobStatusEnum.finished ||
job.status == JobStatusEnum.error,
);
@override
List<Object?> get props => [migrationJobUid, _serverJobList];
ServerJobsState copyWith({
final List<ServerJob>? serverJobList,
final String? migrationJobUid,
}) =>
ServerJobsState(
serverJobList: serverJobList ?? _serverJobList,
migrationJobUid: migrationJobUid ?? this.migrationJobUid,
);
}

View File

@ -0,0 +1,78 @@
import 'dart:async';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/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/cubit/provider_volumes/provider_volume_cubit.dart';
import 'package:selfprivacy/logic/models/disk_status.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,
this.providerVolumeCubit,
) : super(serverInstallationCubit, ApiServerVolumeState.initial()) {
_providerVolumeSubscription =
providerVolumeCubit.stream.listen(checkProviderVolumes);
}
final ServerApi serverApi = ServerApi();
@override
Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
reload();
}
}
late StreamSubscription<ApiProviderVolumeState> _providerVolumeSubscription;
final ApiProviderVolumeCubit providerVolumeCubit;
void checkProviderVolumes(final ApiProviderVolumeState state) {
emit(
ApiServerVolumeState(
this.state._volumes,
this.state.status,
this.state.usesBinds,
DiskStatus.fromVolumes(this.state._volumes, state.volumes),
),
);
return;
}
Future<void> reload() async {
final volumes = await serverApi.getServerDiskVolumes();
final usesBinds = await serverApi.isUsingBinds();
var status = LoadingStatus.error;
if (volumes.isNotEmpty) {
status = LoadingStatus.success;
}
emit(
ApiServerVolumeState(
volumes,
status,
usesBinds,
DiskStatus.fromVolumes(
volumes,
providerVolumeCubit.state.volumes,
),
),
);
}
@override
void clear() {
emit(ApiServerVolumeState.initial());
}
@override
Future<void> close() {
_providerVolumeSubscription.cancel();
return super.close();
}
}

View File

@ -0,0 +1,42 @@
part of 'server_volume_cubit.dart';
class ApiServerVolumeState extends ServerInstallationDependendState {
const ApiServerVolumeState(
this._volumes,
this.status,
this.usesBinds,
this._diskStatus,
);
ApiServerVolumeState.initial()
: this(const [], LoadingStatus.uninitialized, null, DiskStatus());
final List<ServerDiskVolume> _volumes;
final DiskStatus _diskStatus;
final bool? usesBinds;
final LoadingStatus status;
List<DiskVolume> get volumes => _diskStatus.diskVolumes;
DiskStatus get diskStatus => _diskStatus;
DiskVolume getVolume(final String volumeName) => volumes.firstWhere(
(final volume) => volume.name == volumeName,
orElse: () => DiskVolume(),
);
ApiServerVolumeState copyWith({
final List<ServerDiskVolume>? volumes,
final LoadingStatus? status,
final bool? usesBinds,
final DiskStatus? diskStatus,
}) =>
ApiServerVolumeState(
volumes ?? _volumes,
status ?? this.status,
usesBinds ?? this.usesBinds,
diskStatus ?? _diskStatus,
);
@override
List<Object?> get props => [_volumes, status, usesBinds];
}

View File

@ -1,31 +1,72 @@
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'dart:async';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/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/service.dart';
part 'services_state.dart'; part 'services_state.dart';
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> { class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
ServicesCubit(final ServerInstallationCubit serverInstallationCubit) ServicesCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, ServicesState.allOff()); : super(serverInstallationCubit, const ServicesState.empty());
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
Timer? timer;
@override @override
Future<void> load() async { Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) { if (serverInstallationCubit.state is ServerInstallationFinished) {
final Map<ServiceTypes, bool> statuses = await api.servicesPowerCheck(); final List<Service> services = await api.getAllServices();
emit( emit(
ServicesState( ServicesState(
isPasswordManagerEnable: statuses[ServiceTypes.passwordManager]!, services: services,
isCloudEnable: statuses[ServiceTypes.cloud]!, lockedServices: const [],
isGitEnable: statuses[ServiceTypes.git]!,
isSocialNetworkEnable: statuses[ServiceTypes.socialNetwork]!,
isVpnEnable: statuses[ServiceTypes.vpn]!,
), ),
); );
timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true));
} }
} }
Future<void> reload({final bool useTimer = false}) async {
final List<Service> services = await api.getAllServices();
emit(
state.copyWith(
services: services,
),
);
if (useTimer) {
timer = Timer(const Duration(seconds: 60), () => reload(useTimer: true));
}
}
Future<void> restart(final String serviceId) async {
emit(state.copyWith(lockedServices: [...state.lockedServices, serviceId]));
await api.restartService(serviceId);
await Future.delayed(const Duration(seconds: 2));
reload();
await Future.delayed(const Duration(seconds: 10));
emit(
state.copyWith(
lockedServices: state.lockedServices
.where((final element) => element != serviceId)
.toList(),
),
);
reload();
}
Future<void> moveService(
final String serviceId,
final String destination,
) async {
await api.moveService(serviceId, destination);
}
@override @override
void clear() async { void clear() async {
emit(ServicesState.allOff()); emit(const ServicesState.empty());
if (timer != null && timer!.isActive) {
timer!.cancel();
timer = null;
}
} }
} }

View File

@ -1,93 +1,91 @@
part of 'services_cubit.dart'; part of 'services_cubit.dart';
class ServicesState extends ServerInstallationDependendState { class ServicesState extends ServerInstallationDependendState {
factory ServicesState.allOn() => const ServicesState(
isPasswordManagerEnable: true,
isCloudEnable: true,
isGitEnable: true,
isSocialNetworkEnable: true,
isVpnEnable: true,
);
factory ServicesState.allOff() => const ServicesState(
isPasswordManagerEnable: false,
isCloudEnable: false,
isGitEnable: false,
isSocialNetworkEnable: false,
isVpnEnable: false,
);
const ServicesState({ const ServicesState({
required this.isPasswordManagerEnable, required this.services,
required this.isCloudEnable, required this.lockedServices,
required this.isGitEnable,
required this.isSocialNetworkEnable,
required this.isVpnEnable,
}); });
final bool isPasswordManagerEnable; const ServicesState.empty()
final bool isCloudEnable; : this(services: const [], lockedServices: const []);
final bool isGitEnable;
final bool isSocialNetworkEnable;
final bool isVpnEnable;
ServicesState enableList( final List<Service> services;
final List<ServiceTypes> list, final List<String> lockedServices;
) =>
ServicesState(
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
? true
: isPasswordManagerEnable,
isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable,
isGitEnable:
list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable,
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork)
? true
: isPasswordManagerEnable,
isVpnEnable:
list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable,
);
ServicesState disableList( bool isServiceLocked(final String serviceId) =>
final List<ServiceTypes> list, lockedServices.contains(serviceId);
) =>
ServicesState( bool get isPasswordManagerEnable => services
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager) .firstWhere(
? false (final service) => service.id == 'bitwarden',
: isPasswordManagerEnable, orElse: () => Service.empty,
isCloudEnable: )
list.contains(ServiceTypes.cloud) ? false : isCloudEnable, .isEnabled;
isGitEnable: bool get isCloudEnable => services
list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable, .firstWhere(
isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork) (final service) => service.id == 'nextcloud',
? false orElse: () => Service.empty,
: isPasswordManagerEnable, )
isVpnEnable: .isEnabled;
list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable, bool get isGitEnable => services
); .firstWhere(
(final service) => service.id == 'gitea',
orElse: () => Service.empty,
)
.isEnabled;
bool get isSocialNetworkEnable => services
.firstWhere(
(final service) => service.id == 'pleroma',
orElse: () => Service.empty,
)
.isEnabled;
bool get isVpnEnable => services
.firstWhere(
(final service) => service.id == 'ocserv',
orElse: () => Service.empty,
)
.isEnabled;
Service? getServiceById(final String id) {
final service = services.firstWhere(
(final service) => service.id == id,
orElse: () => Service.empty,
);
if (service.id == 'empty') {
return null;
}
return service;
}
@override @override
List<Object> get props => [ List<Object> get props => [
isPasswordManagerEnable, services,
isCloudEnable, lockedServices,
isGitEnable,
isSocialNetworkEnable,
isVpnEnable
]; ];
bool isEnableByType(final ServiceTypes type) { bool isEnableByType(final ServiceTypes type) {
switch (type) { switch (type) {
case ServiceTypes.passwordManager: case ServiceTypes.bitwarden:
return isPasswordManagerEnable; return isPasswordManagerEnable;
case ServiceTypes.cloud: case ServiceTypes.nextcloud:
return isCloudEnable; return isCloudEnable;
case ServiceTypes.socialNetwork: case ServiceTypes.pleroma:
return isSocialNetworkEnable; return isSocialNetworkEnable;
case ServiceTypes.git: case ServiceTypes.gitea:
return isGitEnable; return isGitEnable;
case ServiceTypes.vpn: case ServiceTypes.ocserv:
return isVpnEnable; return isVpnEnable;
default: default:
throw Exception('wrong state'); throw Exception('wrong state');
} }
} }
ServicesState copyWith({
final List<Service>? services,
final List<String>? lockedServices,
}) =>
ServicesState(
services: services ?? this.services,
lockedServices: lockedServices ?? this.lockedServices,
);
} }

View File

@ -1,10 +1,11 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.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/graphql_maps/server_api/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/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/api_maps/server.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';
part 'users_state.dart'; part 'users_state.dart';
@ -15,8 +16,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
serverInstallationCubit, serverInstallationCubit,
const UsersState( const UsersState(
<User>[], <User>[],
User(login: 'root'), false,
User(login: 'loading...'),
), ),
); );
Box<User> box = Hive.box<User>(BNames.usersBox); Box<User> box = Hive.box<User>(BNames.usersBox);
@ -26,173 +26,43 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
@override @override
Future<void> load() async { Future<void> load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) { if (serverInstallationCubit.state is! ServerInstallationFinished) {
final List<User> loadedUsers = box.values.toList(); return;
final primaryUser = serverInstallationBox.get( }
BNames.rootUser, final List<User> loadedUsers = box.values.toList();
defaultValue: const User(login: 'loading...'), if (loadedUsers.isNotEmpty) {
);
final List<String> rootKeys = [
...serverInstallationBox.get(BNames.rootKeys, defaultValue: [])
];
if (loadedUsers.isNotEmpty) {
emit(
UsersState(
loadedUsers,
User(login: 'root', sshKeys: rootKeys),
primaryUser,
),
);
}
final ApiResponse<List<String>> usersFromServer =
await api.getUsersList();
if (usersFromServer.isSuccess) {
final List<User> updatedList =
mergeLocalAndServerUsers(loadedUsers, usersFromServer.data);
emit(
UsersState(
updatedList,
User(login: 'root', sshKeys: rootKeys),
primaryUser,
),
);
}
final List<User> usersWithSshKeys = await loadSshKeys(state.users);
// Update the users it the box
box.clear();
box.addAll(usersWithSshKeys);
final User rootUserWithSshKeys =
(await loadSshKeys([state.rootUser])).first;
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final User primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit( emit(
UsersState( UsersState(
usersWithSshKeys, loadedUsers,
rootUserWithSshKeys, false,
primaryUserWithSshKeys,
),
);
}
}
List<User> mergeLocalAndServerUsers(
final List<User> localUsers,
final List<String> serverUsers,
) {
// If local user not exists on server, add it with isFoundOnServer = false
// If server user not exists on local, add it
final List<User> mergedUsers = [];
final List<String> serverUsersCopy = List.from(serverUsers);
for (final User localUser in localUsers) {
if (serverUsersCopy.contains(localUser.login)) {
mergedUsers.add(
User(
login: localUser.login,
isFoundOnServer: true,
password: localUser.password,
sshKeys: localUser.sshKeys,
),
);
serverUsersCopy.remove(localUser.login);
} else {
mergedUsers.add(
User(
login: localUser.login,
isFoundOnServer: false,
password: localUser.password,
note: localUser.note,
),
);
}
}
for (final String serverUser in serverUsersCopy) {
mergedUsers.add(
User(
login: serverUser,
isFoundOnServer: true,
), ),
); );
} }
return mergedUsers; refresh();
}
Future<List<User>> loadSshKeys(final List<User> users) async {
final List<User> updatedUsers = [];
for (final User user in users) {
if (user.isFoundOnServer ||
user.login == 'root' ||
user.login == state.primaryUser.login) {
final ApiResponse<List<String>> sshKeys =
await api.getUserSshKeys(user);
print('sshKeys for $user: ${sshKeys.data}');
if (sshKeys.isSuccess) {
updatedUsers.add(
User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: sshKeys.data,
note: user.note,
),
);
} else {
updatedUsers.add(
User(
login: user.login,
isFoundOnServer: true,
password: user.password,
note: user.note,
),
);
}
} else {
updatedUsers.add(
User(
login: user.login,
isFoundOnServer: false,
password: user.password,
note: user.note,
),
);
}
}
return updatedUsers;
} }
Future<void> refresh() async { Future<void> refresh() async {
List<User> updatedUsers = List<User>.from(state.users); if (serverInstallationCubit.state is! ServerInstallationFinished) {
final ApiResponse<List<String>> usersFromServer = await api.getUsersList(); return;
if (usersFromServer.isSuccess) { }
updatedUsers = emit(state.copyWith(isLoading: true));
mergeLocalAndServerUsers(updatedUsers, usersFromServer.data); final List<User> usersFromServer = await api.getAllUsers();
if (usersFromServer.isNotEmpty) {
emit(
UsersState(
usersFromServer,
false,
),
);
// Update the users it the box
await box.clear();
await box.addAll(usersFromServer);
} else {
getIt<NavigationService>()
.showSnackBar('users.could_not_fetch_users'.tr());
emit(state.copyWith(isLoading: false));
} }
final List<User> usersWithSshKeys = await loadSshKeys(updatedUsers);
box.clear();
box.addAll(usersWithSshKeys);
final User rootUserWithSshKeys =
(await loadSshKeys([state.rootUser])).first;
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
final User primaryUserWithSshKeys =
(await loadSshKeys([state.primaryUser])).first;
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
emit(
UsersState(
usersWithSshKeys,
rootUserWithSshKeys,
primaryUserWithSshKeys,
),
);
return;
} }
Future<void> createUser(final User user) async { Future<void> createUser(final User user) async {
@ -201,18 +71,24 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
.any((final User u) => u.login == user.login && u.isFoundOnServer)) { .any((final User u) => u.login == user.login && u.isFoundOnServer)) {
return; return;
} }
// If user is root or primary user, do nothing final String? password = user.password;
if (user.login == 'root' || user.login == state.primaryUser.login) { if (password == null) {
getIt<NavigationService>()
.showSnackBar('users.could_not_create_user'.tr());
return; return;
} }
// If API returned error, do nothing // If API returned error, do nothing
final ApiResponse<User> result = await api.createUser(user); final UserMutationResult result =
if (!result.isSuccess) { await api.createUser(user.login, password);
final User? createdUser = result.user;
if (!result.success || createdUser == null) {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'users.could_not_create_user'.tr());
return; return;
} }
final List<User> loadedUsers = List<User>.from(state.users); final List<User> loadedUsers = List<User>.from(state.users);
loadedUsers.add(result.data); loadedUsers.add(createdUser);
await box.clear(); await box.clear();
await box.addAll(loadedUsers); await box.addAll(loadedUsers);
emit(state.copyWith(users: loadedUsers)); emit(state.copyWith(users: loadedUsers));
@ -220,142 +96,69 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
Future<void> deleteUser(final User user) async { Future<void> deleteUser(final User user) async {
// If user is primary or root, don't delete // If user is primary or root, don't delete
if (user.login == state.primaryUser.login || user.login == 'root') { if (user.type != UserType.normal) {
getIt<NavigationService>()
.showSnackBar('users.could_not_delete_user'.tr());
return; return;
} }
final List<User> loadedUsers = List<User>.from(state.users); final List<User> loadedUsers = List<User>.from(state.users);
final bool result = await api.deleteUser(user); final GenericMutationResult result = await api.deleteUser(user.login);
if (result) { if (result.success) {
loadedUsers.removeWhere((final User u) => u.login == user.login); loadedUsers.removeWhere((final User u) => u.login == user.login);
await box.clear(); await box.clear();
await box.addAll(loadedUsers); await box.addAll(loadedUsers);
emit(state.copyWith(users: loadedUsers)); emit(state.copyWith(users: loadedUsers));
} else {
getIt<NavigationService>()
.showSnackBar(result.message ?? 'users.could_not_delete_user'.tr());
}
}
Future<void> changeUserPassword(
final User user,
final String newPassword,
) async {
if (user.type == UserType.root) {
getIt<NavigationService>()
.showSnackBar('users.could_not_change_password'.tr());
return;
}
final UserMutationResult result =
await api.updateUser(user.login, newPassword);
if (!result.success) {
getIt<NavigationService>().showSnackBar(
result.message ?? 'users.could_not_change_password'.tr(),
);
} }
} }
Future<void> addSshKey(final User user, final String publicKey) async { Future<void> addSshKey(final User user, final String publicKey) async {
// If adding root key, use api.addRootSshKey final UserMutationResult result =
// Otherwise, use api.addUserSshKey await api.addSshKey(user.login, publicKey);
if (user.login == 'root') { if (result.success) {
final ApiResponse<void> result = await api.addRootSshKey(publicKey); final User updatedUser = result.user!;
if (result.isSuccess) { final int index =
// Add ssh key to the array of root keys state.users.indexWhere((final User u) => u.login == user.login);
final List<String> rootKeys = serverInstallationBox await box.putAt(index, updatedUser);
.get(BNames.rootKeys, defaultValue: []) as List<String>; emit(
rootKeys.add(publicKey); state.copyWith(
serverInstallationBox.put(BNames.rootKeys, rootKeys); users: box.values.toList(),
emit( ),
state.copyWith( );
rootUser: User(
login: state.rootUser.login,
isFoundOnServer: true,
password: state.rootUser.password,
sshKeys: rootKeys,
note: state.rootUser.note,
),
),
);
}
} else { } else {
final ApiResponse<void> result = await api.addUserSshKey(user, publicKey); getIt<NavigationService>()
if (result.isSuccess) { .showSnackBar(result.message ?? 'users.could_not_add_ssh_key'.tr());
// If it is primary user, update primary user
if (user.login == state.primaryUser.login) {
final List<String> primaryUserKeys =
List<String>.from(state.primaryUser.sshKeys);
primaryUserKeys.add(publicKey);
final User updatedUser = User(
login: state.primaryUser.login,
isFoundOnServer: true,
password: state.primaryUser.password,
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
serverInstallationBox.put(BNames.rootUser, updatedUser);
emit(
state.copyWith(
primaryUser: updatedUser,
),
);
} else {
// If it is not primary user, update user
final List<String> userKeys = List<String>.from(user.sshKeys);
userKeys.add(publicKey);
final User updatedUser = User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: userKeys,
note: user.note,
);
await box.putAt(box.values.toList().indexOf(user), updatedUser);
emit(
state.copyWith(
users: box.values.toList(),
),
);
}
}
} }
} }
Future<void> deleteSshKey(final User user, final String publicKey) async { Future<void> deleteSshKey(final User user, final String publicKey) async {
// All keys are deleted via api.deleteUserSshKey final UserMutationResult result =
await api.removeSshKey(user.login, publicKey);
final ApiResponse<void> result = if (result.success) {
await api.deleteUserSshKey(user, publicKey); final User updatedUser = result.user!;
if (result.isSuccess) { final int index =
// If it is root user, delete key from root keys state.users.indexWhere((final User u) => u.login == user.login);
// If it is primary user, update primary user await box.putAt(index, updatedUser);
// If it is not primary user, update user
if (user.login == 'root') {
final List<String> rootKeys = serverInstallationBox
.get(BNames.rootKeys, defaultValue: []) as List<String>;
rootKeys.remove(publicKey);
serverInstallationBox.put(BNames.rootKeys, rootKeys);
emit(
state.copyWith(
rootUser: User(
login: state.rootUser.login,
isFoundOnServer: true,
password: state.rootUser.password,
sshKeys: rootKeys,
note: state.rootUser.note,
),
),
);
return;
}
if (user.login == state.primaryUser.login) {
final List<String> primaryUserKeys =
List<String>.from(state.primaryUser.sshKeys);
primaryUserKeys.remove(publicKey);
final User updatedUser = User(
login: state.primaryUser.login,
isFoundOnServer: true,
password: state.primaryUser.password,
sshKeys: primaryUserKeys,
note: state.primaryUser.note,
);
serverInstallationBox.put(BNames.rootUser, updatedUser);
emit(
state.copyWith(
primaryUser: updatedUser,
),
);
return;
}
final List<String> userKeys = List<String>.from(user.sshKeys);
userKeys.remove(publicKey);
final User updatedUser = User(
login: user.login,
isFoundOnServer: true,
password: user.password,
sshKeys: userKeys,
note: user.note,
);
await box.putAt(box.values.toList().indexOf(user), updatedUser);
emit( emit(
state.copyWith( state.copyWith(
users: box.values.toList(), users: box.values.toList(),
@ -369,8 +172,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
emit( emit(
const UsersState( const UsersState(
<User>[], <User>[],
User(login: 'root'), false,
User(login: 'loading...'),
), ),
); );
} }

View File

@ -1,30 +1,34 @@
part of 'users_cubit.dart'; part of 'users_cubit.dart';
class UsersState extends ServerInstallationDependendState { class UsersState extends ServerInstallationDependendState {
const UsersState(this.users, this.rootUser, this.primaryUser); const UsersState(this.users, this.isLoading);
final List<User> users; final List<User> users;
final User rootUser; final bool isLoading;
final User primaryUser;
User get rootUser =>
users.firstWhere((final user) => user.type == UserType.root);
User get primaryUser =>
users.firstWhere((final user) => user.type == UserType.primary);
List<User> get normalUsers =>
users.where((final user) => user.type == UserType.normal).toList();
@override @override
List<Object> get props => [users, rootUser, primaryUser]; List<Object> get props => [users, isLoading];
UsersState copyWith({ UsersState copyWith({
final List<User>? users, final List<User>? users,
final User? rootUser, final bool? isLoading,
final User? primaryUser,
}) => }) =>
UsersState( UsersState(
users ?? this.users, users ?? this.users,
rootUser ?? this.rootUser, isLoading ?? this.isLoading,
primaryUser ?? this.primaryUser,
); );
bool isLoginRegistered(final String login) => bool isLoginRegistered(final String login) =>
users.any((final User user) => user.login == login) || users.any((final User user) => user.login == login);
login == rootUser.login ||
login == primaryUser.login;
bool get isEmpty => users.isEmpty; bool get isEmpty => users.isEmpty;
} }

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,18 @@
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
class AutoUpgradeSettings {
AutoUpgradeSettings({
required this.enable,
required this.allowReboot,
});
AutoUpgradeSettings.fromGraphQL(
final Query$SystemSettings$system$settings$autoUpgrade autoUpgrade,
) : this(
enable: autoUpgrade.enable,
allowReboot: autoUpgrade.allowReboot,
);
final bool enable;
final bool allowReboot;
}

View File

@ -0,0 +1,38 @@
import 'package:easy_localization/easy_localization.dart';
class DiskSize {
const DiskSize({final this.byte = 0});
DiskSize.fromKibibyte(final double kibibyte)
: this(byte: (kibibyte * 1024).round());
DiskSize.fromMebibyte(final double mebibyte)
: this(byte: (mebibyte * 1024 * 1024).round());
DiskSize.fromGibibyte(final double gibibyte)
: this(byte: (gibibyte * 1024 * 1024 * 1024).round());
final int byte;
double get kibibyte => byte / 1024.0;
double get mebibyte => byte / 1024.0 / 1024.0;
double get gibibyte => byte / 1024.0 / 1024.0 / 1024.0;
DiskSize operator +(final DiskSize other) =>
DiskSize(byte: byte + other.byte);
DiskSize operator -(final DiskSize other) =>
DiskSize(byte: byte - other.byte);
DiskSize operator *(final double other) =>
DiskSize(byte: (byte * other).round());
@override
String toString() {
if (byte < 1024) {
return '${byte.toStringAsFixed(0)} ${tr('bytes')}';
} else if (byte < 1024 * 1024) {
return 'providers.storage.kb'.tr(args: [kibibyte.toStringAsFixed(1)]);
} else if (byte < 1024 * 1024 * 1024) {
return 'providers.storage.mb'.tr(args: [mebibyte.toStringAsFixed(1)]);
} else {
return 'providers.storage.gb'.tr(args: [gibibyte.toStringAsFixed(1)]);
}
}
}

View File

@ -0,0 +1,122 @@
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 {
DiskVolume({
this.name = '',
this.sizeTotal = const DiskSize(byte: 0),
this.sizeUsed = const DiskSize(byte: 0),
this.root = false,
this.isResizable = false,
this.serverDiskVolume,
this.providerVolume,
});
DiskVolume.fromServerDiscVolume(
final ServerDiskVolume volume,
final ServerVolume? providerVolume,
) : this(
name: volume.name,
sizeTotal: DiskSize(
byte:
volume.totalSpace == 'None' ? 0 : int.parse(volume.totalSpace),
),
sizeUsed: DiskSize(
byte: volume.usedSpace == 'None' ? 0 : int.parse(volume.usedSpace),
),
root: volume.root,
isResizable: providerVolume != null,
serverDiskVolume: volume,
providerVolume: providerVolume,
);
/// Get the display name of the volume
///
/// If it is sda1 and root the name is "System disk"
/// If there is a mapping to providerVolume, the name is "Expandable volume"
/// Otherwise the name is the name of the volume
String get displayName {
if (root) {
return 'System disk';
} else if (providerVolume != null) {
return 'Expandable volume';
} else {
return name;
}
}
DiskSize sizeUsed;
DiskSize sizeTotal;
String name;
bool root;
bool isResizable;
ServerDiskVolume? serverDiskVolume;
ServerVolume? providerVolume;
/// from 0.0 to 1.0
double get percentage =>
sizeTotal.byte == 0 ? 0 : sizeUsed.byte / sizeTotal.byte;
bool get isDiskOkay =>
percentage < 0.8 && sizeTotal.gibibyte - sizeUsed.gibibyte > 2.0;
DiskVolume copyWith({
final DiskSize? sizeUsed,
final DiskSize? sizeTotal,
final String? name,
final bool? root,
final bool? isResizable,
final ServerDiskVolume? serverDiskVolume,
final ServerVolume? providerVolume,
}) =>
DiskVolume(
sizeUsed: sizeUsed ?? this.sizeUsed,
sizeTotal: sizeTotal ?? this.sizeTotal,
name: name ?? this.name,
root: root ?? this.root,
isResizable: isResizable ?? this.isResizable,
serverDiskVolume: serverDiskVolume ?? this.serverDiskVolume,
providerVolume: providerVolume ?? this.providerVolume,
);
}
class DiskStatus {
DiskStatus.fromVolumes(
final List<ServerDiskVolume> serverVolumes,
final List<ServerVolume> providerVolumes,
) {
diskVolumes = serverVolumes.map((
final ServerDiskVolume volume,
) {
ServerVolume? providerVolume;
for (final ServerVolume iterableProviderVolume in providerVolumes) {
if (iterableProviderVolume.linuxDevice == null ||
volume.model == null ||
volume.serial == null) {
continue;
}
final String deviceId =
iterableProviderVolume.linuxDevice!.split('/').last;
if (deviceId.contains(volume.model!) &&
deviceId.contains(volume.serial!)) {
providerVolume = iterableProviderVolume;
break;
}
}
final DiskVolume diskVolume =
DiskVolume.fromServerDiscVolume(volume, providerVolume);
return diskVolume;
}).toList();
}
DiskStatus() {
diskVolumes = [];
}
bool get isDiskOkay => diskVolumes.every((final volume) => volume.isDiskOkay);
List<DiskVolume> diskVolumes = [];
}

View File

@ -7,7 +7,8 @@
5. ServerVolume 5. ServerVolume
6. BackblazeBucket 6. BackblazeBucket
## Enums ## Enums
100. DnsProvider 100. DnsProvider
101. ServerProvider 101. ServerProvider
102. UserType

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

@ -2,6 +2,8 @@ import 'dart:ui';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
import 'package:selfprivacy/utils/color_utils.dart'; import 'package:selfprivacy/utils/color_utils.dart';
part 'user.g.dart'; part 'user.g.dart';
@ -10,12 +12,21 @@ part 'user.g.dart';
class User extends Equatable { class User extends Equatable {
const User({ const User({
required this.login, required this.login,
required this.type,
this.password, this.password,
this.sshKeys = const [], this.sshKeys = const [],
this.isFoundOnServer = true, this.isFoundOnServer = true,
this.note, this.note,
}); });
User.fromGraphQL(final Fragment$userFields user)
: this(
login: user.username,
type: UserType.fromGraphQL(user.userType),
sshKeys: user.sshKeys,
isFoundOnServer: true,
);
@HiveField(0) @HiveField(0)
final String login; final String login;
@ -31,6 +42,9 @@ class User extends Equatable {
@HiveField(4) @HiveField(4)
final String? note; final String? note;
@HiveField(5, defaultValue: UserType.normal)
final UserType type;
@override @override
List<Object?> get props => [login, password, sshKeys, isFoundOnServer, note]; List<Object?> get props => [login, password, sshKeys, isFoundOnServer, note];
@ -40,3 +54,26 @@ class User extends Equatable {
String toString() => String toString() =>
'$login, ${isFoundOnServer ? 'found' : 'not found'}, ${sshKeys.length} ssh keys, note: $note'; '$login, ${isFoundOnServer ? 'found' : 'not found'}, ${sshKeys.length} ssh keys, note: $note';
} }
@HiveType(typeId: 102)
enum UserType {
@HiveField(0)
root,
@HiveField(1)
primary,
@HiveField(2)
normal;
factory UserType.fromGraphQL(final Enum$UserType type) {
switch (type) {
case Enum$UserType.ROOT:
return root;
case Enum$UserType.PRIMARY:
return primary;
case Enum$UserType.NORMAL:
return normal;
case Enum$UserType.$unknown:
return normal;
}
}
}

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