refactor: Implement better error handling on create server stage

Replace try-catch hell with APIGenericResult chain
pull/149/head
NaiJi ✨ 2022-12-02 22:40:08 +04:00
parent 29b0bf2397
commit e0b32404be
14 changed files with 197 additions and 131 deletions

View File

@ -3,6 +3,7 @@ class APIGenericResult<T> {
required this.success, required this.success,
required this.data, required this.data,
this.message, this.message,
this.code,
}); });
/// Whether was a response successfully received, /// Whether was a response successfully received,
@ -10,4 +11,5 @@ class APIGenericResult<T> {
final bool success; final bool success;
final String? message; final String? message;
final T data; final T data;
final int? code;
} }

View File

@ -22,13 +22,13 @@ mixin JobsApi on ApiMap {
return jobsList; return jobsList;
} }
Future<GenericMutationResult<bool>> removeApiJob(final String uid) async { Future<APIGenericResult<bool>> removeApiJob(final String uid) async {
try { try {
final GraphQLClient client = await getClient(); final GraphQLClient client = await getClient();
final variables = Variables$Mutation$RemoveJob(jobId: uid); final variables = Variables$Mutation$RemoveJob(jobId: uid);
final mutation = Options$Mutation$RemoveJob(variables: variables); final mutation = Options$Mutation$RemoveJob(variables: variables);
final response = await client.mutate$RemoveJob(mutation); final response = await client.mutate$RemoveJob(mutation);
return GenericMutationResult( return APIGenericResult(
data: response.parsedData?.removeJob.success ?? false, data: response.parsedData?.removeJob.success ?? false,
success: true, success: true,
code: response.parsedData?.removeJob.code ?? 0, code: response.parsedData?.removeJob.code ?? 0,
@ -36,7 +36,7 @@ mixin JobsApi on ApiMap {
); );
} catch (e) { } catch (e) {
print(e); print(e);
return GenericMutationResult( return APIGenericResult(
data: false, data: false,
success: false, success: false,
code: 0, code: 0,

View File

@ -31,16 +31,6 @@ part 'services_api.dart';
part 'users_api.dart'; part 'users_api.dart';
part 'volume_api.dart'; part 'volume_api.dart';
class GenericMutationResult<T> extends APIGenericResult<T> {
GenericMutationResult({
required super.success,
required this.code,
required super.data,
super.message,
});
final int code;
}
class ServerApi extends ApiMap class ServerApi extends ApiMap
with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi { with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi {
ServerApi({ ServerApi({

View File

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

View File

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

View File

@ -114,10 +114,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
); );
@override @override
Future<ServerVolume?> createVolume() async { Future<APIGenericResult<ServerVolume?>> createVolume() async {
ServerVolume? volume; ServerVolume? volume;
final Response createVolumeResponse; Response? createVolumeResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
final List<ServerVolume> volumes = await getVolumes(); final List<ServerVolume> volumes = await getVolumes();
@ -146,11 +146,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
); );
} catch (e) { } catch (e) {
print(e); print(e);
return APIGenericResult(
data: null,
success: false,
message: e.toString(),
);
} finally { } finally {
client.close(); client.close();
} }
return volume; return APIGenericResult(
data: volume,
success: true,
code: createVolumeResponse.statusCode,
message: createVolumeResponse.statusMessage,
);
} }
@override @override
@ -219,13 +229,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> attachVolume( Future<APIGenericResult<bool>> attachVolume(
final ServerVolume volume, final ServerVolume volume,
final int serverId, final int serverId,
) async { ) async {
bool success = false; bool success = false;
final Response attachVolumeResponse; Response? attachVolumeResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
attachVolumeResponse = await client.post( attachVolumeResponse = await client.post(
@ -241,11 +251,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
attachVolumeResponse.data['action']['status'].toString() != 'error'; attachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) { } catch (e) {
print(e); print(e);
return APIGenericResult(
data: false,
success: false,
message: e.toString(),
);
} finally { } finally {
close(client); close(client);
} }
return success; return APIGenericResult(
data: success,
success: true,
code: attachVolumeResponse.statusCode,
message: attachVolumeResponse.statusMessage,
);
} }
@override @override
@ -323,7 +343,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<ServerHostingDetails?> createServer({ Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken, required final String dnsApiToken,
required final User rootUser, required final User rootUser,
required final String domainName, required final String domainName,
@ -345,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"; "#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); print(userdataString);
Response? serverCreateResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
final Map<String, Object> data = { final Map<String, Object> data = {
@ -356,14 +377,15 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}; };
print('Decoded data: $data'); print('Decoded data: $data');
final Response serverCreateResponse = await client.post( serverCreateResponse = await client.post(
'/droplets', '/droplets',
data: data, data: data,
); );
final int serverId = serverCreateResponse.data['droplet']['id']; final int serverId = serverCreateResponse.data['droplet']['id'];
final ServerVolume? newVolume = await createVolume(); final ServerVolume? newVolume = (await createVolume()).data;
final bool attachedVolume = await attachVolume(newVolume!, serverId); final bool attachedVolume =
(await attachVolume(newVolume!, serverId)).data;
String? ipv4; String? ipv4;
int attempts = 0; int attempts = 0;
@ -391,11 +413,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
} catch (e) { } catch (e) {
print(e); print(e);
return APIGenericResult(
success: false,
data: null,
message: e.toString(),
);
} finally { } finally {
close(client); close(client);
} }
return serverDetails; return APIGenericResult(
data: serverDetails,
success: true,
code: serverCreateResponse.statusCode,
message: serverCreateResponse.statusMessage,
);
} }
@override @override

View File

@ -140,10 +140,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<ServerVolume?> createVolume() async { Future<APIGenericResult<ServerVolume?>> createVolume() async {
ServerVolume? volume; ServerVolume? volume;
final Response createVolumeResponse; Response? createVolumeResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
createVolumeResponse = await client.post( createVolumeResponse = await client.post(
@ -171,11 +171,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
); );
} catch (e) { } catch (e) {
print(e); print(e);
return APIGenericResult(
data: null,
success: false,
message: e.toString(),
);
} finally { } finally {
client.close(); client.close();
} }
return volume; return APIGenericResult(
data: volume,
success: true,
code: createVolumeResponse.statusCode,
message: createVolumeResponse.statusMessage,
);
} }
@override @override
@ -259,13 +269,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> attachVolume( Future<APIGenericResult<bool>> attachVolume(
final ServerVolume volume, final ServerVolume volume,
final int serverId, final int serverId,
) async { ) async {
bool success = false; bool success = false;
final Response attachVolumeResponse; Response? attachVolumeResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
attachVolumeResponse = await client.post( attachVolumeResponse = await client.post(
@ -283,7 +293,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
client.close(); client.close();
} }
return success; return APIGenericResult(
data: success,
success: true,
code: attachVolumeResponse?.statusCode,
message: attachVolumeResponse?.statusMessage,
);
} }
@override @override
@ -335,31 +350,33 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<ServerHostingDetails?> createServer({ Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken, required final String dnsApiToken,
required final User rootUser, required final User rootUser,
required final String domainName, required final String domainName,
required final String serverType, required final String serverType,
}) async { }) async {
ServerHostingDetails? details; final APIGenericResult<ServerVolume?> newVolumeResponse =
await createVolume();
final ServerVolume? newVolume = await createVolume(); if (!newVolumeResponse.success || newVolumeResponse.data == null) {
if (newVolume == null) { return APIGenericResult(
return details; data: null,
success: false,
message: newVolumeResponse.message,
code: newVolumeResponse.code,
);
} }
return createServerWithVolume(
details = await createServerWithVolume(
dnsApiToken: dnsApiToken, dnsApiToken: dnsApiToken,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
volume: newVolume, volume: newVolumeResponse.data!,
serverType: serverType, serverType: serverType,
); );
return details;
} }
Future<ServerHostingDetails?> createServerWithVolume({ Future<APIGenericResult<ServerHostingDetails?>> createServerWithVolume({
required final String dnsApiToken, required final String dnsApiToken,
required final User rootUser, required final User rootUser,
required final String domainName, required final String domainName,
@ -381,6 +398,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
final String userdataString = 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"; "#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; ServerHostingDetails? serverDetails;
DioError? hetznerError; DioError? hetznerError;
bool success = false; bool success = false;
@ -400,7 +418,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
}; };
print('Decoded data: $data'); print('Decoded data: $data');
final Response serverCreateResponse = await client.post( serverCreateResponse = await client.post(
'/servers', '/servers',
data: data, data: data,
); );
@ -428,11 +446,19 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
await deleteVolume(volume); await deleteVolume(volume);
} }
if (hetznerError != null) { String? apiResultMessage = serverCreateResponse?.statusMessage;
throw hetznerError; 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) { static String getHostnameFromDomain(final String domain) {

View File

@ -32,7 +32,7 @@ abstract class ServerProviderApi extends ApiMap {
Future<ServerHostingDetails> powerOn(); Future<ServerHostingDetails> powerOn();
Future<void> deleteServer({required final String domainName}); Future<void> deleteServer({required final String domainName});
Future<ServerHostingDetails?> createServer({ Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken, required final String dnsApiToken,
required final User rootUser, required final User rootUser,
required final String domainName, required final String domainName,

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/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/price.dart'; import 'package:selfprivacy/logic/models/price.dart';
export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
mixin VolumeProviderApi on ApiMap { mixin VolumeProviderApi on ApiMap {
Future<ServerVolume?> createVolume(); Future<APIGenericResult<ServerVolume?>> createVolume();
Future<List<ServerVolume>> getVolumes({final String? status}); 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> detachVolume(final ServerVolume volume);
Future<bool> resizeVolume(final ServerVolume volume, final DiskSize size); Future<bool> resizeVolume(final ServerVolume volume, final DiskSize size);
Future<void> deleteVolume(final ServerVolume volume); Future<void> deleteVolume(final ServerVolume volume);

View File

@ -3,7 +3,6 @@ import 'package:cubit_form/cubit_form.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/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/get_it/navigation.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';

View File

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

View File

@ -246,22 +246,52 @@ class ServerInstallationRepository {
}) async { }) async {
final ServerProviderApi api = final ServerProviderApi api =
ApiController.currentServerProviderApiFactory!.getServerProvider(); 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 { try {
final ServerHostingDetails? serverDetails = await api.createServer( final APIGenericResult<ServerHostingDetails?> createServerResult =
await api.createServer(
dnsApiToken: cloudFlareKey, dnsApiToken: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!, serverType: getIt<ApiConfigModel>().serverType!,
); );
if (serverDetails == null) { if (createServerResult.data == null) {
print('Server is not initialized!'); const String e = 'Server is not initialized!';
return; print(e);
} }
saveServerDetails(serverDetails);
onSuccess(serverDetails); if (createServerResult.message == 'uniqueness_error') {
} on DioError catch (e) {
if (e.response!.data['error']['code'] == 'uniqueness_error') {
showPopUpAlert( showPopUpAlert(
alertTitle: 'modals.already_exists'.tr(), alertTitle: 'modals.already_exists'.tr(),
description: 'modals.destroy_server'.tr(), description: 'modals.destroy_server'.tr(),
@ -273,39 +303,13 @@ class ServerInstallationRepository {
ServerHostingDetails? serverDetails; ServerHostingDetails? serverDetails;
try { try {
serverDetails = await api.createServer( final APIGenericResult createResult = 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(
dnsApiToken: cloudFlareKey, dnsApiToken: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
serverType: getIt<ApiConfigModel>().serverType!, serverType: getIt<ApiConfigModel>().serverType!,
); );
serverDetails = createResult.data;
} catch (e) { } catch (e) {
print(e); print(e);
} }
@ -320,6 +324,12 @@ class ServerInstallationRepository {
cancelButtonOnPressed: onCancel, cancelButtonOnPressed: onCancel,
); );
} }
saveServerDetails(createServerResult.data!);
onSuccess(createServerResult.data!);
} catch (e) {
print(e);
showInstallationErrorPopUp();
} }
} }

View File

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