chore: Merge endpoint-errors into master

Reviewed-on: kherel/selfprivacy.org.app#149
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
pull/151/head
NaiJi ✨ 2022-12-09 16:35:51 +02:00
commit 51ca8bce27
25 changed files with 572 additions and 300 deletions

View File

@ -273,6 +273,7 @@
"place_where_data": "A place where your data and SelfPrivacy services will reside:",
"how": "How to obtain API token",
"provider_bad_key_error": "Provider API key is invalid",
"could_not_connect": "Counldn't connect to the provider.",
"choose_location_type": "Choose your server location and type:",
"back_to_locations": "Go back to available locations!",
"no_locations_found": "No available locations found. Make sure your account is accessible.",

View File

@ -272,6 +272,7 @@
"place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:",
"how": "Как получить API Token",
"provider_bad_key_error": "API ключ провайдера неверен",
"could_not_connect": "Не удалось соединиться с провайдером.",
"choose_location_type": "Выберите локацию и тип вашего сервера:",
"back_to_locations": "Назад к доступным локациям!",
"no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.",

View File

@ -0,0 +1,15 @@
class APIGenericResult<T> {
APIGenericResult({
required this.success,
required this.data,
this.message,
this.code,
});
/// Whether was a response successfully received,
/// doesn't represent success of the request if `data<T>` is `bool`
final bool success;
final String? message;
final T data;
final int? code;
}

View File

@ -22,13 +22,13 @@ mixin JobsApi on ApiMap {
return jobsList;
}
Future<GenericMutationResult<bool>> removeApiJob(final String uid) async {
Future<APIGenericResult<bool>> 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(
return APIGenericResult(
data: response.parsedData?.removeJob.success ?? false,
success: true,
code: response.parsedData?.removeJob.code ?? 0,
@ -36,7 +36,7 @@ mixin JobsApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: false,
success: false,
code: 0,

View File

@ -1,5 +1,6 @@
import 'package:graphql/client.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_generic_result.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';
@ -22,36 +23,14 @@ import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/ssh_settings.dart';
import 'package:selfprivacy/logic/models/system_settings.dart';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
part 'jobs_api.dart';
part 'server_actions_api.dart';
part 'services_api.dart';
part 'users_api.dart';
part 'volume_api.dart';
class GenericResult<T> {
GenericResult({
required this.success,
required this.data,
this.message,
});
/// Whether was a response successfully received,
/// doesn't represent success of the request if `data<T>` is `bool`
final bool success;
final String? message;
final T data;
}
class GenericMutationResult<T> extends GenericResult<T> {
GenericMutationResult({
required super.success,
required this.code,
required super.data,
super.message,
});
final int code;
}
class ServerApi extends ApiMap
with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi {
ServerApi({
@ -206,7 +185,7 @@ class ServerApi extends ApiMap
return settings;
}
Future<GenericResult<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
Future<APIGenericResult<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
RecoveryKeyStatus? key;
QueryResult<Query$RecoveryKey> response;
String? error;
@ -223,18 +202,18 @@ class ServerApi extends ApiMap
print(e);
}
return GenericResult<RecoveryKeyStatus?>(
return APIGenericResult<RecoveryKeyStatus?>(
success: error == null,
data: key,
message: error,
);
}
Future<GenericResult<String>> generateRecoveryToken(
Future<APIGenericResult<String>> generateRecoveryToken(
final DateTime? expirationDate,
final int? numberOfUses,
) async {
GenericResult<String> key;
APIGenericResult<String> key;
QueryResult<Mutation$GetNewRecoveryApiKey> response;
try {
@ -255,19 +234,19 @@ class ServerApi extends ApiMap
);
if (response.hasException) {
print(response.exception.toString());
key = GenericResult<String>(
key = APIGenericResult<String>(
success: false,
data: '',
message: response.exception.toString(),
);
}
key = GenericResult<String>(
key = APIGenericResult<String>(
success: true,
data: response.parsedData!.getNewRecoveryApiKey.key!,
);
} catch (e) {
print(e);
key = GenericResult<String>(
key = APIGenericResult<String>(
success: false,
data: '',
message: e.toString(),
@ -300,8 +279,8 @@ class ServerApi extends ApiMap
return records;
}
Future<GenericResult<List<ApiToken>>> getApiTokens() async {
GenericResult<List<ApiToken>> tokens;
Future<APIGenericResult<List<ApiToken>>> getApiTokens() async {
APIGenericResult<List<ApiToken>> tokens;
QueryResult<Query$GetApiTokens> response;
try {
@ -310,7 +289,7 @@ class ServerApi extends ApiMap
if (response.hasException) {
final message = response.exception.toString();
print(message);
tokens = GenericResult<List<ApiToken>>(
tokens = APIGenericResult<List<ApiToken>>(
success: false,
data: [],
message: message,
@ -324,13 +303,13 @@ class ServerApi extends ApiMap
ApiToken.fromGraphQL(device),
)
.toList();
tokens = GenericResult<List<ApiToken>>(
tokens = APIGenericResult<List<ApiToken>>(
success: true,
data: parsed,
);
} catch (e) {
print(e);
tokens = GenericResult<List<ApiToken>>(
tokens = APIGenericResult<List<ApiToken>>(
success: false,
data: [],
message: e.toString(),
@ -340,8 +319,8 @@ class ServerApi extends ApiMap
return tokens;
}
Future<GenericResult<void>> deleteApiToken(final String name) async {
GenericResult<void> returnable;
Future<APIGenericResult<void>> deleteApiToken(final String name) async {
APIGenericResult<void> returnable;
QueryResult<Mutation$DeleteDeviceApiToken> response;
try {
@ -358,19 +337,19 @@ class ServerApi extends ApiMap
);
if (response.hasException) {
print(response.exception.toString());
returnable = GenericResult<void>(
returnable = APIGenericResult<void>(
success: false,
data: null,
message: response.exception.toString(),
);
}
returnable = GenericResult<void>(
returnable = APIGenericResult<void>(
success: true,
data: null,
);
} catch (e) {
print(e);
returnable = GenericResult<void>(
returnable = APIGenericResult<void>(
success: false,
data: null,
message: e.toString(),
@ -380,8 +359,8 @@ class ServerApi extends ApiMap
return returnable;
}
Future<GenericResult<String>> createDeviceToken() async {
GenericResult<String> token;
Future<APIGenericResult<String>> createDeviceToken() async {
APIGenericResult<String> token;
QueryResult<Mutation$GetNewDeviceApiKey> response;
try {
@ -393,19 +372,19 @@ class ServerApi extends ApiMap
);
if (response.hasException) {
print(response.exception.toString());
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: response.exception.toString(),
);
}
token = GenericResult<String>(
token = APIGenericResult<String>(
success: true,
data: response.parsedData!.getNewDeviceApiKey.key!,
);
} catch (e) {
print(e);
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: e.toString(),
@ -417,10 +396,10 @@ class ServerApi extends ApiMap
Future<bool> isHttpServerWorking() async => (await getApiVersion()) != null;
Future<GenericResult<String>> authorizeDevice(
Future<APIGenericResult<String>> authorizeDevice(
final DeviceToken deviceToken,
) async {
GenericResult<String> token;
APIGenericResult<String> token;
QueryResult<Mutation$AuthorizeWithNewDeviceApiKey> response;
try {
@ -442,19 +421,19 @@ class ServerApi extends ApiMap
);
if (response.hasException) {
print(response.exception.toString());
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: response.exception.toString(),
);
}
token = GenericResult<String>(
token = APIGenericResult<String>(
success: true,
data: response.parsedData!.authorizeWithNewDeviceApiKey.token!,
);
} catch (e) {
print(e);
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: e.toString(),
@ -464,10 +443,10 @@ class ServerApi extends ApiMap
return token;
}
Future<GenericResult<String>> useRecoveryToken(
Future<APIGenericResult<String>> useRecoveryToken(
final DeviceToken deviceToken,
) async {
GenericResult<String> token;
APIGenericResult<String> token;
QueryResult<Mutation$UseRecoveryApiKey> response;
try {
@ -489,19 +468,19 @@ class ServerApi extends ApiMap
);
if (response.hasException) {
print(response.exception.toString());
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: response.exception.toString(),
);
}
token = GenericResult<String>(
token = APIGenericResult<String>(
success: true,
data: response.parsedData!.useRecoveryApiKey.token!,
);
} catch (e) {
print(e);
token = GenericResult<String>(
token = APIGenericResult<String>(
success: false,
data: '',
message: e.toString(),

View File

@ -20,7 +20,7 @@ mixin ServicesApi on ApiMap {
return services;
}
Future<GenericMutationResult<bool>> enableService(
Future<APIGenericResult<bool>> enableService(
final String serviceId,
) async {
try {
@ -28,7 +28,7 @@ mixin ServicesApi on ApiMap {
final variables = Variables$Mutation$EnableService(serviceId: serviceId);
final mutation = Options$Mutation$EnableService(variables: variables);
final response = await client.mutate$EnableService(mutation);
return GenericMutationResult(
return APIGenericResult(
data: response.parsedData?.enableService.success ?? false,
success: true,
code: response.parsedData?.enableService.code ?? 0,
@ -36,7 +36,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: false,
success: false,
code: 0,
@ -45,7 +45,7 @@ mixin ServicesApi on ApiMap {
}
}
Future<GenericMutationResult<void>> disableService(
Future<APIGenericResult<void>> disableService(
final String serviceId,
) async {
try {
@ -53,7 +53,7 @@ mixin ServicesApi on ApiMap {
final variables = Variables$Mutation$DisableService(serviceId: serviceId);
final mutation = Options$Mutation$DisableService(variables: variables);
final response = await client.mutate$DisableService(mutation);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: response.parsedData?.disableService.success ?? false,
code: response.parsedData?.disableService.code ?? 0,
@ -61,7 +61,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: false,
code: 0,
@ -70,7 +70,7 @@ mixin ServicesApi on ApiMap {
}
}
Future<GenericMutationResult<bool>> stopService(
Future<APIGenericResult<bool>> stopService(
final String serviceId,
) async {
try {
@ -78,7 +78,7 @@ mixin ServicesApi on ApiMap {
final variables = Variables$Mutation$StopService(serviceId: serviceId);
final mutation = Options$Mutation$StopService(variables: variables);
final response = await client.mutate$StopService(mutation);
return GenericMutationResult(
return APIGenericResult(
data: response.parsedData?.stopService.success ?? false,
success: true,
code: response.parsedData?.stopService.code ?? 0,
@ -86,7 +86,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: false,
success: false,
code: 0,
@ -95,13 +95,13 @@ mixin ServicesApi on ApiMap {
}
}
Future<GenericMutationResult> startService(final String serviceId) async {
Future<APIGenericResult> 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(
return APIGenericResult(
data: null,
success: response.parsedData?.startService.success ?? false,
code: response.parsedData?.startService.code ?? 0,
@ -109,7 +109,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: false,
code: 0,
@ -118,7 +118,7 @@ mixin ServicesApi on ApiMap {
}
}
Future<GenericMutationResult<bool>> restartService(
Future<APIGenericResult<bool>> restartService(
final String serviceId,
) async {
try {
@ -126,7 +126,7 @@ mixin ServicesApi on ApiMap {
final variables = Variables$Mutation$RestartService(serviceId: serviceId);
final mutation = Options$Mutation$RestartService(variables: variables);
final response = await client.mutate$RestartService(mutation);
return GenericMutationResult(
return APIGenericResult(
data: response.parsedData?.restartService.success ?? false,
success: true,
code: response.parsedData?.restartService.code ?? 0,
@ -134,7 +134,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: false,
success: false,
code: 0,
@ -143,7 +143,7 @@ mixin ServicesApi on ApiMap {
}
}
Future<GenericMutationResult<ServerJob?>> moveService(
Future<APIGenericResult<ServerJob?>> moveService(
final String serviceId,
final String destination,
) async {
@ -158,7 +158,7 @@ mixin ServicesApi on ApiMap {
final mutation = Options$Mutation$MoveService(variables: variables);
final response = await client.mutate$MoveService(mutation);
final jobJson = response.parsedData?.moveService.job?.toJson();
return GenericMutationResult(
return APIGenericResult(
success: true,
code: response.parsedData?.moveService.code ?? 0,
message: response.parsedData?.moveService.message,
@ -166,7 +166,7 @@ mixin ServicesApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
success: false,
code: 0,
message: e.toString(),

View File

@ -45,7 +45,7 @@ mixin UsersApi on ApiMap {
return user;
}
Future<GenericMutationResult<User?>> createUser(
Future<APIGenericResult<User?>> createUser(
final String username,
final String password,
) async {
@ -56,7 +56,7 @@ mixin UsersApi on ApiMap {
);
final mutation = Options$Mutation$CreateUser(variables: variables);
final response = await client.mutate$CreateUser(mutation);
return GenericMutationResult(
return APIGenericResult(
success: true,
code: response.parsedData?.createUser.code ?? 500,
message: response.parsedData?.createUser.message,
@ -66,7 +66,7 @@ mixin UsersApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
success: false,
code: 0,
message: e.toString(),
@ -75,7 +75,7 @@ mixin UsersApi on ApiMap {
}
}
Future<GenericMutationResult<bool>> deleteUser(
Future<APIGenericResult<bool>> deleteUser(
final String username,
) async {
try {
@ -83,7 +83,7 @@ mixin UsersApi on ApiMap {
final variables = Variables$Mutation$DeleteUser(username: username);
final mutation = Options$Mutation$DeleteUser(variables: variables);
final response = await client.mutate$DeleteUser(mutation);
return GenericMutationResult(
return APIGenericResult(
data: response.parsedData?.deleteUser.success ?? false,
success: true,
code: response.parsedData?.deleteUser.code ?? 500,
@ -91,7 +91,7 @@ mixin UsersApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: false,
success: false,
code: 500,
@ -100,7 +100,7 @@ mixin UsersApi on ApiMap {
}
}
Future<GenericMutationResult<User?>> updateUser(
Future<APIGenericResult<User?>> updateUser(
final String username,
final String password,
) async {
@ -111,7 +111,7 @@ mixin UsersApi on ApiMap {
);
final mutation = Options$Mutation$UpdateUser(variables: variables);
final response = await client.mutate$UpdateUser(mutation);
return GenericMutationResult(
return APIGenericResult(
success: true,
code: response.parsedData?.updateUser.code ?? 500,
message: response.parsedData?.updateUser.message,
@ -121,7 +121,7 @@ mixin UsersApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: false,
code: 0,
@ -130,7 +130,7 @@ mixin UsersApi on ApiMap {
}
}
Future<GenericMutationResult<User?>> addSshKey(
Future<APIGenericResult<User?>> addSshKey(
final String username,
final String sshKey,
) async {
@ -144,7 +144,7 @@ mixin UsersApi on ApiMap {
);
final mutation = Options$Mutation$AddSshKey(variables: variables);
final response = await client.mutate$AddSshKey(mutation);
return GenericMutationResult(
return APIGenericResult(
success: true,
code: response.parsedData?.addSshKey.code ?? 500,
message: response.parsedData?.addSshKey.message,
@ -154,7 +154,7 @@ mixin UsersApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: false,
code: 0,
@ -163,7 +163,7 @@ mixin UsersApi on ApiMap {
}
}
Future<GenericMutationResult<User?>> removeSshKey(
Future<APIGenericResult<User?>> removeSshKey(
final String username,
final String sshKey,
) async {
@ -177,7 +177,7 @@ mixin UsersApi on ApiMap {
);
final mutation = Options$Mutation$RemoveSshKey(variables: variables);
final response = await client.mutate$RemoveSshKey(mutation);
return GenericMutationResult(
return APIGenericResult(
success: response.parsedData?.removeSshKey.success ?? false,
code: response.parsedData?.removeSshKey.code ?? 500,
message: response.parsedData?.removeSshKey.message,
@ -187,7 +187,7 @@ mixin UsersApi on ApiMap {
);
} catch (e) {
print(e);
return GenericMutationResult(
return APIGenericResult(
data: null,
success: false,
code: 0,

View File

@ -57,10 +57,10 @@ mixin VolumeApi on ApiMap {
}
}
Future<GenericMutationResult<String?>> migrateToBinds(
Future<APIGenericResult<String?>> migrateToBinds(
final Map<String, String> serviceToDisk,
) async {
GenericMutationResult<String?>? mutation;
APIGenericResult<String?>? mutation;
try {
final GraphQLClient client = await getClient();
@ -78,7 +78,7 @@ mixin VolumeApi on ApiMap {
await client.mutate$MigrateToBinds(
migrateMutation,
);
mutation = mutation = GenericMutationResult(
mutation = mutation = APIGenericResult(
success: true,
code: result.parsedData!.migrateToBinds.code,
message: result.parsedData!.migrateToBinds.message,
@ -86,7 +86,7 @@ mixin VolumeApi on ApiMap {
);
} catch (e) {
print(e);
mutation = GenericMutationResult(
mutation = APIGenericResult(
success: false,
code: 0,
message: e.toString(),

View File

@ -2,9 +2,12 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
class BackblazeApiAuth {
BackblazeApiAuth({required this.authorizationToken, required this.apiUrl});
@ -71,28 +74,43 @@ class BackblazeApi extends ApiMap {
);
}
Future<bool> isValid(final String encodedApiKey) async {
Future<APIGenericResult<bool>> isApiTokenValid(
final String encodedApiKey,
) async {
final Dio client = await getClient();
bool isTokenValid = false;
try {
final Response response = await client.get(
'b2_authorize_account',
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
options: Options(
followRedirects: false,
validateStatus: (final status) =>
status != null && (status >= 200 || status == 401),
headers: {'Authorization': 'Basic $encodedApiKey'},
),
);
if (response.statusCode == HttpStatus.ok) {
if (response.data['allowed']['capabilities'].contains('listBuckets')) {
return true;
}
return false;
isTokenValid = response.data['allowed']['capabilities'].contains('listBuckets');
} else if (response.statusCode == HttpStatus.unauthorized) {
return false;
isTokenValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
} on DioError {
return false;
} on DioError catch (e) {
print(e);
return APIGenericResult(
data: false,
success: false,
message: e.toString(),
);
} finally {
close(client);
}
return APIGenericResult(
data: isTokenValid,
success: true,
);
}
// Create bucket

View File

@ -46,33 +46,50 @@ class CloudflareApi extends DnsProviderApi {
String rootAddress = 'https://api.cloudflare.com/client/v4';
@override
Future<bool> isApiTokenValid(final String token) async {
Future<APIGenericResult<bool>> isApiTokenValid(final String token) async {
bool isValid = false;
Response? response;
String message = '';
final Dio client = await getClient();
try {
response = await client.get(
'/user/tokens/verify',
options: Options(headers: {'Authorization': 'Bearer $token'}),
options: Options(
followRedirects: false,
validateStatus: (final status) =>
status != null && (status >= 200 || status == 401),
headers: {'Authorization': 'Bearer $token'},
),
);
} catch (e) {
print(e);
isValid = false;
message = e.toString();
} 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}');
}
if (response == null) {
return APIGenericResult(
data: isValid,
success: false,
message: message,
);
}
return isValid;
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
return APIGenericResult(
data: isValid,
success: true,
message: response.statusMessage,
);
}
@override
@ -96,7 +113,7 @@ class CloudflareApi extends DnsProviderApi {
}
@override
Future<void> removeSimilarRecords({
Future<APIGenericResult<void>> removeSimilarRecords({
required final ServerDomain domain,
final String? ip4,
}) async {
@ -122,9 +139,16 @@ class CloudflareApi extends DnsProviderApi {
await Future.wait(allDeleteFutures);
} catch (e) {
print(e);
return APIGenericResult(
success: false,
data: null,
message: e.toString(),
);
} finally {
close(client);
}
return APIGenericResult(success: true, data: null);
}
@override
@ -166,7 +190,7 @@ class CloudflareApi extends DnsProviderApi {
}
@override
Future<void> createMultipleDnsRecords({
Future<APIGenericResult<void>> createMultipleDnsRecords({
required final ServerDomain domain,
final String? ip4,
}) async {
@ -189,9 +213,18 @@ class CloudflareApi extends DnsProviderApi {
} on DioError catch (e) {
print(e.message);
rethrow;
} catch (e) {
print(e);
return APIGenericResult(
success: false,
data: null,
message: e.toString(),
);
} finally {
close(client);
}
return APIGenericResult(success: true, data: null);
}
List<DnsRecord> projectDnsRecords(

View File

@ -1,7 +1,10 @@
import 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
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';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
class DomainNotFoundException implements Exception {
DomainNotFoundException(this.message);
final String message;
@ -11,11 +14,11 @@ abstract class DnsProviderApi extends ApiMap {
Future<List<DnsRecord>> getDnsRecords({
required final ServerDomain domain,
});
Future<void> removeSimilarRecords({
Future<APIGenericResult<void>> removeSimilarRecords({
required final ServerDomain domain,
final String? ip4,
});
Future<void> createMultipleDnsRecords({
Future<APIGenericResult<void>> createMultipleDnsRecords({
required final ServerDomain domain,
final String? ip4,
});
@ -26,6 +29,6 @@ abstract class DnsProviderApi extends ApiMap {
Future<String?> getZoneId(final String domain);
Future<List<String>> domainList();
Future<bool> isApiTokenValid(final String token);
Future<APIGenericResult<bool>> isApiTokenValid(final String token);
RegExp getApiTokenValidation();
}

View File

@ -59,35 +59,50 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
String get displayProviderName => 'Digital Ocean';
@override
Future<bool> isApiTokenValid(final String token) async {
Future<APIGenericResult<bool>> isApiTokenValid(final String token) async {
bool isValid = false;
Response? response;
String message = '';
final Dio client = await getClient();
try {
response = await client.get(
'/account',
options: Options(
followRedirects: false,
validateStatus: (final status) =>
status != null && (status >= 200 || status == 401),
headers: {'Authorization': 'Bearer $token'},
),
);
} catch (e) {
print(e);
isValid = false;
message = e.toString();
} 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}');
}
if (response == null) {
return APIGenericResult(
data: isValid,
success: false,
message: message,
);
}
return isValid;
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
return APIGenericResult(
data: isValid,
success: true,
message: response.statusMessage,
);
}
/// Hardcoded on their documentation and there is no pricing API at all
@ -99,10 +114,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
);
@override
Future<ServerVolume?> createVolume() async {
Future<APIGenericResult<ServerVolume?>> createVolume() async {
ServerVolume? volume;
final Response createVolumeResponse;
Response? createVolumeResponse;
final Dio client = await getClient();
try {
final List<ServerVolume> volumes = await getVolumes();
@ -131,11 +146,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
);
} catch (e) {
print(e);
return APIGenericResult(
data: null,
success: false,
message: e.toString(),
);
} finally {
client.close();
}
return volume;
return APIGenericResult(
data: volume,
success: true,
code: createVolumeResponse.statusCode,
message: createVolumeResponse.statusMessage,
);
}
@override
@ -204,13 +229,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<bool> attachVolume(
Future<APIGenericResult<bool>> attachVolume(
final ServerVolume volume,
final int serverId,
) async {
bool success = false;
final Response attachVolumeResponse;
Response? attachVolumeResponse;
final Dio client = await getClient();
try {
attachVolumeResponse = await client.post(
@ -226,11 +251,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
attachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
return APIGenericResult(
data: false,
success: false,
message: e.toString(),
);
} finally {
close(client);
}
return success;
return APIGenericResult(
data: success,
success: true,
code: attachVolumeResponse.statusCode,
message: attachVolumeResponse.statusMessage,
);
}
@override
@ -308,7 +343,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<ServerHostingDetails?> createServer({
Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
@ -330,6 +365,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log";
print(userdataString);
Response? serverCreateResponse;
final Dio client = await getClient();
try {
final Map<String, Object> data = {
@ -341,14 +377,15 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
};
print('Decoded data: $data');
final Response serverCreateResponse = await client.post(
serverCreateResponse = await client.post(
'/droplets',
data: data,
);
final int serverId = serverCreateResponse.data['droplet']['id'];
final ServerVolume? newVolume = await createVolume();
final bool attachedVolume = await attachVolume(newVolume!, serverId);
final ServerVolume? newVolume = (await createVolume()).data;
final bool attachedVolume =
(await attachVolume(newVolume!, serverId)).data;
String? ipv4;
int attempts = 0;
@ -376,11 +413,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
} catch (e) {
print(e);
return APIGenericResult(
success: false,
data: null,
message: e.toString(),
);
} finally {
close(client);
}
return serverDetails;
return APIGenericResult(
data: serverDetails,
success: true,
code: serverCreateResponse.statusCode,
message: serverCreateResponse.statusMessage,
);
}
@override
@ -694,7 +741,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<List<ServerProviderLocation>> getAvailableLocations() async {
Future<APIGenericResult<List<ServerProviderLocation>>>
getAvailableLocations() async {
List<ServerProviderLocation> locations = [];
final Dio client = await getClient();
@ -715,15 +763,20 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
.toList();
} catch (e) {
print(e);
return APIGenericResult(
data: [],
success: false,
message: e.toString(),
);
} finally {
close(client);
}
return locations;
return APIGenericResult(data: locations, success: true);
}
@override
Future<List<ServerType>> getServerTypesByLocation({
Future<APIGenericResult<List<ServerType>>> getServerTypesByLocation({
required final ServerProviderLocation location,
}) async {
final List<ServerType> types = [];
@ -756,19 +809,26 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
} catch (e) {
print(e);
return APIGenericResult(
data: [],
success: false,
message: e.toString(),
);
} finally {
close(client);
}
return types;
return APIGenericResult(data: types, success: true);
}
@override
Future<void> createReverseDns({
Future<APIGenericResult<void>> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
}) async {
/// TODO remove from provider interface
const bool success = true;
return APIGenericResult(success: success, data: null);
}
@override

View File

@ -60,35 +60,50 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
String get displayProviderName => 'Hetzner';
@override
Future<bool> isApiTokenValid(final String token) async {
Future<APIGenericResult<bool>> isApiTokenValid(final String token) async {
bool isValid = false;
Response? response;
String message = '';
final Dio client = await getClient();
try {
response = await client.get(
'/servers',
options: Options(
followRedirects: false,
validateStatus: (final status) =>
status != null && (status >= 200 || status == 401),
headers: {'Authorization': 'Bearer $token'},
),
);
} catch (e) {
print(e);
isValid = false;
message = e.toString();
} 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}');
}
if (response == null) {
return APIGenericResult(
data: isValid,
success: false,
message: message,
);
}
return isValid;
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
return APIGenericResult(
data: isValid,
success: true,
message: response.statusMessage,
);
}
@override
@ -125,10 +140,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<ServerVolume?> createVolume() async {
Future<APIGenericResult<ServerVolume?>> createVolume() async {
ServerVolume? volume;
final Response createVolumeResponse;
Response? createVolumeResponse;
final Dio client = await getClient();
try {
createVolumeResponse = await client.post(
@ -156,11 +171,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
);
} catch (e) {
print(e);
return APIGenericResult(
data: null,
success: false,
message: e.toString(),
);
} finally {
client.close();
}
return volume;
return APIGenericResult(
data: volume,
success: true,
code: createVolumeResponse.statusCode,
message: createVolumeResponse.statusMessage,
);
}
@override
@ -244,13 +269,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<bool> attachVolume(
Future<APIGenericResult<bool>> attachVolume(
final ServerVolume volume,
final int serverId,
) async {
bool success = false;
final Response attachVolumeResponse;
Response? attachVolumeResponse;
final Dio client = await getClient();
try {
attachVolumeResponse = await client.post(
@ -268,7 +293,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
client.close();
}
return success;
return APIGenericResult(
data: success,
success: true,
code: attachVolumeResponse?.statusCode,
message: attachVolumeResponse?.statusMessage,
);
}
@override
@ -320,31 +350,33 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<ServerHostingDetails?> createServer({
Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
required final String serverType,
}) async {
ServerHostingDetails? details;
final APIGenericResult<ServerVolume?> newVolumeResponse =
await createVolume();
final ServerVolume? newVolume = await createVolume();
if (newVolume == null) {
return details;
if (!newVolumeResponse.success || newVolumeResponse.data == null) {
return APIGenericResult(
data: null,
success: false,
message: newVolumeResponse.message,
code: newVolumeResponse.code,
);
}
details = await createServerWithVolume(
return createServerWithVolume(
dnsApiToken: dnsApiToken,
rootUser: rootUser,
domainName: domainName,
volume: newVolume,
volume: newVolumeResponse.data!,
serverType: serverType,
);
return details;
}
Future<ServerHostingDetails?> createServerWithVolume({
Future<APIGenericResult<ServerHostingDetails?>> createServerWithVolume({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
@ -366,6 +398,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
final String userdataString =
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName 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";
Response? serverCreateResponse;
ServerHostingDetails? serverDetails;
DioError? hetznerError;
bool success = false;
@ -385,7 +418,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
};
print('Decoded data: $data');
final Response serverCreateResponse = await client.post(
serverCreateResponse = await client.post(
'/servers',
data: data,
);
@ -413,11 +446,19 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
await deleteVolume(volume);
}
if (hetznerError != null) {
throw hetznerError;
String? apiResultMessage = serverCreateResponse?.statusMessage;
if (hetznerError != null &&
hetznerError.response!.data['error']['code'] == 'uniqueness_error') {
apiResultMessage = 'uniqueness_error';
}
return serverDetails;
return APIGenericResult(
data: serverDetails,
success: success && hetznerError == null,
code: serverCreateResponse?.statusCode ??
hetznerError?.response?.statusCode,
message: apiResultMessage,
);
}
static String getHostnameFromDomain(final String domain) {
@ -692,7 +733,8 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}
@override
Future<List<ServerProviderLocation>> getAvailableLocations() async {
Future<APIGenericResult<List<ServerProviderLocation>>>
getAvailableLocations() async {
List<ServerProviderLocation> locations = [];
final Dio client = await getClient();
@ -713,15 +755,20 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
.toList();
} catch (e) {
print(e);
return APIGenericResult(
success: false,
data: [],
message: e.toString(),
);
} finally {
close(client);
}
return locations;
return APIGenericResult(success: true, data: locations);
}
@override
Future<List<ServerType>> getServerTypesByLocation({
Future<APIGenericResult<List<ServerType>>> getServerTypesByLocation({
required final ServerProviderLocation location,
}) async {
final List<ServerType> types = [];
@ -754,15 +801,20 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}
} catch (e) {
print(e);
return APIGenericResult(
data: [],
success: false,
message: e.toString(),
);
} finally {
close(client);
}
return types;
return APIGenericResult(data: types, success: true);
}
@override
Future<void> createReverseDns({
Future<APIGenericResult<void>> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
}) async {
@ -777,8 +829,15 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
);
} catch (e) {
print(e);
return APIGenericResult(
success: false,
data: null,
message: e.toString(),
);
} finally {
close(client);
}
return APIGenericResult(success: true, data: null);
}
}

View File

@ -1,3 +1,4 @@
import 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
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';
@ -8,6 +9,8 @@ import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/server_provider_location.dart';
import 'package:selfprivacy/logic/models/server_type.dart';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
class ProviderApiTokenValidation {
ProviderApiTokenValidation({
required this.length,
@ -19,8 +22,9 @@ class ProviderApiTokenValidation {
abstract class ServerProviderApi extends ApiMap {
Future<List<ServerBasicInfo>> getServers();
Future<List<ServerProviderLocation>> getAvailableLocations();
Future<List<ServerType>> getServerTypesByLocation({
Future<APIGenericResult<List<ServerProviderLocation>>>
getAvailableLocations();
Future<APIGenericResult<List<ServerType>>> getServerTypesByLocation({
required final ServerProviderLocation location,
});
@ -28,18 +32,18 @@ abstract class ServerProviderApi extends ApiMap {
Future<ServerHostingDetails> powerOn();
Future<void> deleteServer({required final String domainName});
Future<ServerHostingDetails?> createServer({
Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
required final String serverType,
});
Future<void> createReverseDns({
Future<APIGenericResult<void>> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
});
Future<bool> isApiTokenValid(final String token);
Future<APIGenericResult<bool>> isApiTokenValid(final String token);
ProviderApiTokenValidation getApiTokenValidation();
Future<List<ServerMetadataEntity>> getMetadata(final int serverId);
Future<ServerMetrics?> getMetrics(

View File

@ -1,12 +1,18 @@
import 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/price.dart';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
mixin VolumeProviderApi on ApiMap {
Future<ServerVolume?> createVolume();
Future<APIGenericResult<ServerVolume?>> createVolume();
Future<List<ServerVolume>> getVolumes({final String? status});
Future<bool> attachVolume(final ServerVolume volume, final int serverId);
Future<APIGenericResult<bool>> attachVolume(
final ServerVolume volume,
final int serverId,
);
Future<bool> detachVolume(final ServerVolume volume);
Future<bool> resizeVolume(final ServerVolume volume, final DiskSize size);
Future<void> deleteVolume(final ServerVolume volume);

View File

@ -35,7 +35,7 @@ class ApiDevicesCubit
}
Future<List<ApiToken>?> _getApiTokens() async {
final GenericResult<List<ApiToken>> response = await api.getApiTokens();
final APIGenericResult<List<ApiToken>> response = await api.getApiTokens();
if (response.success) {
return response.data;
} else {
@ -44,7 +44,8 @@ class ApiDevicesCubit
}
Future<void> deleteDevice(final ApiToken device) async {
final GenericResult<void> response = await api.deleteApiToken(device.name);
final APIGenericResult<void> response =
await api.deleteApiToken(device.name);
if (response.success) {
emit(
ApiDevicesState(
@ -59,7 +60,7 @@ class ApiDevicesCubit
}
Future<String?> getNewDeviceKey() async {
final GenericResult<String> response = await api.createDeviceToken();
final APIGenericResult<String> response = await api.createDeviceToken();
if (response.success) {
return response.data;
} else {

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/config/get_it_config.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/models/hive/backblaze_credential.dart';
@ -7,7 +8,6 @@ import 'package:easy_localization/easy_localization.dart';
class BackblazeFormCubit extends FormCubit {
BackblazeFormCubit(this.serverInstallationCubit) {
//var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]");
keyId = FieldCubit(
initalValue: '',
validations: [
@ -40,7 +40,7 @@ class BackblazeFormCubit extends FormCubit {
@override
FutureOr<bool> asyncValidation() async {
late bool isKeyValid;
late APIGenericResult<bool> backblazeResponse;
final BackblazeApi apiClient = BackblazeApi(isWithToken: false);
try {
@ -48,18 +48,30 @@ class BackblazeFormCubit extends FormCubit {
keyId.state.value,
applicationKey.state.value,
);
isKeyValid = await apiClient.isValid(encodedApiKey);
backblazeResponse = await apiClient.isApiTokenValid(encodedApiKey);
} catch (e) {
addError(e);
isKeyValid = false;
backblazeResponse = APIGenericResult(
success: false,
data: false,
message: e.toString(),
);
}
if (!isKeyValid) {
keyId.setError('initializing.backblaze_bad_key_error'.tr());
applicationKey.setError('initializing.backblaze_bad_key_error'.tr());
if (!backblazeResponse.success) {
getIt<NavigationService>().showSnackBar(
'initializing.could_not_connect'.tr(),
);
keyId.setError('');
applicationKey.setError('');
return false;
}
return true;
if (!backblazeResponse.data) {
keyId.setError('initializing.backblaze_bad_key_error'.tr());
applicationKey.setError('initializing.backblaze_bad_key_error'.tr());
}
return backblazeResponse.data;
}
}

View File

@ -28,21 +28,24 @@ class DnsProviderFormCubit extends FormCubit {
@override
FutureOr<bool> asyncValidation() async {
late bool isKeyValid;
bool? isKeyValid;
try {
isKeyValid = await initializingCubit
.isDnsProviderApiTokenValid(apiKey.state.value);
} catch (e) {
addError(e);
isKeyValid = false;
}
if (isKeyValid == null) {
apiKey.setError('');
return false;
}
if (!isKeyValid) {
apiKey.setError('initializing.cloudflare_bad_key_error'.tr());
return false;
}
return true;
return isKeyValid;
}
}

View File

@ -29,21 +29,24 @@ class ProviderFormCubit extends FormCubit {
@override
FutureOr<bool> asyncValidation() async {
late bool isKeyValid;
bool? isKeyValid;
try {
isKeyValid = await serverInstallationCubit
.isServerProviderApiTokenValid(apiKey.state.value);
} catch (e) {
addError(e);
isKeyValid = false;
}
if (isKeyValid == null) {
apiKey.setError('');
return false;
}
if (!isKeyValid) {
apiKey.setError('initializing.provider_bad_key_error'.tr());
return false;
}
return true;
return isKeyValid;
}
}

View File

@ -113,10 +113,11 @@ class ApiProviderVolumeCubit
}
Future<void> createVolume() async {
final ServerVolume? volume = await ApiController
.currentVolumeProviderApiFactory!
.getVolumeProvider()
.createVolume();
final ServerVolume? volume = (await ApiController
.currentVolumeProviderApiFactory!
.getVolumeProvider()
.createVolume())
.data;
final diskVolume = DiskVolume(providerVolume: volume);
await attachVolume(diskVolume);

View File

@ -32,7 +32,7 @@ class RecoveryKeyCubit
}
Future<RecoveryKeyStatus?> _getRecoveryKeyStatus() async {
final GenericResult<RecoveryKeyStatus?> response =
final APIGenericResult<RecoveryKeyStatus?> response =
await api.getRecoveryTokenStatus();
if (response.success) {
return response.data;
@ -57,7 +57,7 @@ class RecoveryKeyCubit
final DateTime? expirationDate,
final int? numberOfUses,
}) async {
final GenericResult<String> response =
final APIGenericResult<String> response =
await api.generateRecoveryToken(expirationDate, numberOfUses);
if (response.success) {
refresh();

View File

@ -76,18 +76,29 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
.getDnsProvider()
.getApiTokenValidation();
Future<bool> isServerProviderApiTokenValid(
Future<bool?> isServerProviderApiTokenValid(
final String providerToken,
) async =>
ApiController.currentServerProviderApiFactory!
.getServerProvider(
settings: const ServerProviderApiSettings(
isWithToken: false,
),
)
.isApiTokenValid(providerToken);
) async {
final APIGenericResult<bool> apiResponse =
await ApiController.currentServerProviderApiFactory!
.getServerProvider(
settings: const ServerProviderApiSettings(
isWithToken: false,
),
)
.isApiTokenValid(providerToken);
Future<bool> isDnsProviderApiTokenValid(
if (!apiResponse.success) {
getIt<NavigationService>().showSnackBar(
'initializing.could_not_connect'.tr(),
);
return null;
}
return apiResponse.data;
}
Future<bool?> isDnsProviderApiTokenValid(
final String providerToken,
) async {
if (ApiController.currentDnsProviderApiFactory == null) {
@ -100,11 +111,21 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
);
}
return ApiController.currentDnsProviderApiFactory!
.getDnsProvider(
settings: const DnsProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken);
final APIGenericResult<bool> apiResponse =
await ApiController.currentDnsProviderApiFactory!
.getDnsProvider(
settings: const DnsProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken);
if (!apiResponse.success) {
getIt<NavigationService>().showSnackBar(
'initializing.could_not_connect'.tr(),
);
return null;
}
return apiResponse.data;
}
Future<List<ServerProviderLocation>> fetchAvailableLocations() async {
@ -112,9 +133,18 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
return [];
}
return ApiController.currentServerProviderApiFactory!
final APIGenericResult apiResult = await ApiController
.currentServerProviderApiFactory!
.getServerProvider()
.getAvailableLocations();
if (!apiResult.success) {
getIt<NavigationService>().showSnackBar(
'initializing.could_not_connect'.tr(),
);
}
return apiResult.data;
}
Future<List<ServerType>> fetchAvailableTypesByLocation(
@ -124,9 +154,18 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
return [];
}
return ApiController.currentServerProviderApiFactory!
final APIGenericResult apiResult = await ApiController
.currentServerProviderApiFactory!
.getServerProvider()
.getServerTypesByLocation(location: location);
if (!apiResult.success) {
getIt<NavigationService>().showSnackBar(
'initializing.could_not_connect'.tr(),
);
}
return apiResult.data;
}
void setServerProviderKey(final String serverProviderKey) async {

View File

@ -75,13 +75,12 @@ class ServerInstallationRepository {
);
}
if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) {
ApiController.initDnsProviderApiFactory(
DnsProviderApiFactorySettings(
provider: serverDomain.provider,
),
);
}
// No other DNS provider is supported for now, so it's fine.
ApiController.initDnsProviderApiFactory(
DnsProviderApiFactorySettings(
provider: DnsProvider.cloudflare,
),
);
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
return ServerInstallationFinished(
@ -247,22 +246,52 @@ class ServerInstallationRepository {
}) async {
final ServerProviderApi api =
ApiController.currentServerProviderApiFactory!.getServerProvider();
void showInstallationErrorPopUp() {
showPopUpAlert(
alertTitle: 'modals.unexpected_error'.tr(),
description: 'modals.try_again'.tr(),
actionButtonTitle: 'modals.yes'.tr(),
actionButtonOnPressed: () async {
ServerHostingDetails? serverDetails;
try {
final APIGenericResult createResult = await api.createServer(
dnsApiToken: cloudFlareKey,
rootUser: rootUser,
domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!,
);
serverDetails = createResult.data;
} catch (e) {
print(e);
}
if (serverDetails == null) {
print('Server is not initialized!');
return;
}
await saveServerDetails(serverDetails);
onSuccess(serverDetails);
},
cancelButtonOnPressed: onCancel,
);
}
try {
final ServerHostingDetails? serverDetails = await api.createServer(
final APIGenericResult<ServerHostingDetails?> createServerResult =
await api.createServer(
dnsApiToken: cloudFlareKey,
rootUser: rootUser,
domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!,
);
if (serverDetails == null) {
print('Server is not initialized!');
return;
if (createServerResult.data == null) {
const String e = 'Server is not initialized!';
print(e);
}
saveServerDetails(serverDetails);
onSuccess(serverDetails);
} on DioError catch (e) {
if (e.response!.data['error']['code'] == 'uniqueness_error') {
if (createServerResult.message == 'uniqueness_error') {
showPopUpAlert(
alertTitle: 'modals.already_exists'.tr(),
description: 'modals.destroy_server'.tr(),
@ -274,39 +303,13 @@ class ServerInstallationRepository {
ServerHostingDetails? serverDetails;
try {
serverDetails = await api.createServer(
dnsApiToken: cloudFlareKey,
rootUser: rootUser,
domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!,
);
} catch (e) {
print(e);
}
if (serverDetails == null) {
print('Server is not initialized!');
return;
}
await saveServerDetails(serverDetails);
onSuccess(serverDetails);
},
cancelButtonOnPressed: onCancel,
);
} else {
showPopUpAlert(
alertTitle: 'modals.unexpected_error'.tr(),
description: 'modals.try_again'.tr(),
actionButtonTitle: 'modals.yes'.tr(),
actionButtonOnPressed: () async {
ServerHostingDetails? serverDetails;
try {
serverDetails = await api.createServer(
final APIGenericResult createResult = await api.createServer(
dnsApiToken: cloudFlareKey,
rootUser: rootUser,
domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!,
);
serverDetails = createResult.data;
} catch (e) {
print(e);
}
@ -320,7 +323,14 @@ class ServerInstallationRepository {
},
cancelButtonOnPressed: onCancel,
);
return;
}
saveServerDetails(createServerResult.data!);
onSuccess(createServerResult.data!);
} catch (e) {
print(e);
showInstallationErrorPopUp();
}
}
@ -334,21 +344,9 @@ class ServerInstallationRepository {
final ServerProviderApi serverApi =
ApiController.currentServerProviderApiFactory!.getServerProvider();
await dnsProviderApi.removeSimilarRecords(
ip4: serverDetails.ip4,
domain: domain,
);
try {
await dnsProviderApi.createMultipleDnsRecords(
ip4: serverDetails.ip4,
domain: domain,
);
} on DioError catch (e) {
void showDomainErrorPopUp(final String error) {
showPopUpAlert(
alertTitle: e.response!.data['errors'][0]['code'] == 1038
? 'modals.you_cant_use_this_api'.tr()
: 'domain.error'.tr(),
alertTitle: error,
description: 'modals.delete_server_volume'.tr(),
cancelButtonOnPressed: onCancel,
actionButtonTitle: 'basis.delete'.tr(),
@ -359,14 +357,50 @@ class ServerInstallationRepository {
onCancel();
},
);
}
final APIGenericResult removingResult =
await dnsProviderApi.removeSimilarRecords(
ip4: serverDetails.ip4,
domain: domain,
);
if (!removingResult.success) {
showDomainErrorPopUp('domain.error'.tr());
return false;
}
await serverApi.createReverseDns(
bool createdSuccessfully = false;
String errorMessage = 'domain.error'.tr();
try {
final APIGenericResult createResult =
await dnsProviderApi.createMultipleDnsRecords(
ip4: serverDetails.ip4,
domain: domain,
);
createdSuccessfully = createResult.success;
} on DioError catch (e) {
if (e.response!.data['errors'][0]['code'] == 1038) {
errorMessage = 'modals.you_cant_use_this_api'.tr();
}
}
if (!createdSuccessfully) {
showDomainErrorPopUp(errorMessage);
return false;
}
final APIGenericResult createReverseResult =
await serverApi.createReverseDns(
serverDetails: serverDetails,
domain: domain,
);
if (!createReverseResult.success) {
showDomainErrorPopUp(errorMessage);
return false;
}
return true;
}
@ -479,7 +513,7 @@ class ServerInstallationRepository {
overrideDomain: serverDomain.domainName,
);
final String serverIp = await getServerIpFromDomain(serverDomain);
final GenericResult<String> result = await serverApi.authorizeDevice(
final APIGenericResult<String> result = await serverApi.authorizeDevice(
DeviceToken(device: await getDeviceName(), token: newDeviceKey),
);
@ -516,7 +550,7 @@ class ServerInstallationRepository {
overrideDomain: serverDomain.domainName,
);
final String serverIp = await getServerIpFromDomain(serverDomain);
final GenericResult<String> result = await serverApi.useRecoveryToken(
final APIGenericResult<String> result = await serverApi.useRecoveryToken(
DeviceToken(device: await getDeviceName(), token: recoveryKey),
);
@ -577,9 +611,9 @@ class ServerInstallationRepository {
);
}
}
final GenericResult<String> deviceAuthKey =
final APIGenericResult<String> deviceAuthKey =
await serverApi.createDeviceToken();
final GenericResult<String> result = await serverApi.authorizeDevice(
final APIGenericResult<String> result = await serverApi.authorizeDevice(
DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data),
);

View File

@ -78,7 +78,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
return;
}
// If API returned error, do nothing
final GenericMutationResult<User?> result =
final APIGenericResult<User?> result =
await api.createUser(user.login, password);
if (result.data == null) {
getIt<NavigationService>()
@ -101,7 +101,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
return;
}
final List<User> loadedUsers = List<User>.from(state.users);
final GenericMutationResult result = await api.deleteUser(user.login);
final APIGenericResult result = await api.deleteUser(user.login);
if (result.success && result.data) {
loadedUsers.removeWhere((final User u) => u.login == user.login);
await box.clear();
@ -128,7 +128,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
.showSnackBar('users.could_not_change_password'.tr());
return;
}
final GenericMutationResult<User?> result =
final APIGenericResult<User?> result =
await api.updateUser(user.login, newPassword);
if (result.data == null) {
getIt<NavigationService>().showSnackBar(
@ -138,7 +138,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
}
Future<void> addSshKey(final User user, final String publicKey) async {
final GenericMutationResult<User?> result =
final APIGenericResult<User?> result =
await api.addSshKey(user.login, publicKey);
if (result.data != null) {
final User updatedUser = result.data!;
@ -157,7 +157,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
}
Future<void> deleteSshKey(final User user, final String publicKey) async {
final GenericMutationResult<User?> result =
final APIGenericResult<User?> result =
await api.removeSshKey(user.login, publicKey);
if (result.data != null) {
final User updatedUser = result.data!;

View File

@ -77,7 +77,7 @@ class InitializingPage extends StatelessWidget {
'Domain',
'User',
'Server',
'Check',
'Installation',
],
activeIndex: cubit.state.porgressBar,
),