refactor(volume): Make volume interfaces work through volume entities, not IDs

routes-refactor
NaiJi ✨ 2022-10-17 23:58:29 +00:00
parent d19531232c
commit e4ed69d151
4 changed files with 40 additions and 77 deletions

View File

@ -167,16 +167,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
return volumes; return volumes;
} }
@override Future<ServerVolume?> getVolume(final String volumeUuid) async {
/// volumeId is storage's UUID for Digital Ocean
Future<ServerVolume?> getVolume(final String volumeId) async {
ServerVolume? neededVolume; ServerVolume? neededVolume;
final List<ServerVolume> volumes = await getVolumes(); final List<ServerVolume> volumes = await getVolumes();
for (final volume in volumes) { for (final volume in volumes) {
if (volume.uuid == volumeId) { if (volume.uuid == volumeUuid) {
neededVolume = volume; neededVolume = volume;
} }
} }
@ -185,12 +182,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<void> deleteVolume(final ServerVolume volume) async {
/// volumeId is storage's UUID for Digital Ocean
Future<void> deleteVolume(final String volumeId) async {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
await client.delete('/volumes/$volumeId'); await client.delete('/volumes/$volume.uuid');
} catch (e) { } catch (e) {
print(e); print(e);
} finally { } finally {
@ -199,12 +194,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> attachVolume(final ServerVolume volume, final int serverId) async {
/// volumeId is storage's UUID for Digital Ocean
Future<bool> attachVolume(final String volumeId, final int serverId) async {
bool success = false; bool success = false;
final ServerVolume? volumeToAttach = await getVolume(volumeId); final ServerVolume? volumeToAttach = await getVolume(volume.uuid!);
if (volumeToAttach == null) { if (volumeToAttach == null) {
return success; return success;
} }
@ -233,12 +226,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> detachVolume(final ServerVolume volume) async {
/// volumeId is storage's UUID for Digital Ocean
Future<bool> detachVolume(final String volumeId) async {
bool success = false; bool success = false;
final ServerVolume? volumeToAttach = await getVolume(volumeId); final ServerVolume? volumeToAttach = await getVolume(volume.uuid!);
if (volumeToAttach == null) { if (volumeToAttach == null) {
return success; return success;
} }
@ -266,9 +257,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> resizeVolume(final ServerVolume volume, final int sizeGb) async {
/// volumeId is storage's UUID for Digital Ocean
Future<bool> resizeVolume(final String volumeId, final int sizeGb) async {
bool success = false; bool success = false;
final Response dbPostResponse; final Response dbPostResponse;
@ -303,21 +292,19 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
ServerHostingDetails? serverDetails; ServerHostingDetails? serverDetails;
final String dbPassword = StringGenerators.dbPassword(); final String dbPassword = StringGenerators.dbPassword();
final String apiToken = StringGenerators.apiToken(); final String apiToken = StringGenerators.apiToken();
final String hostname = getHostnameFromDomain(domainName);
final String base64Password = final String base64Password =
base64.encode(utf8.encode(rootUser.password ?? 'PASS')); base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
final String userdataString = final String userdataString =
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=digital-ocean 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/master/nixos-infect | PROVIDER=digital-ocean NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$domainName bash 2>&1 | tee /tmp/infect.log";
print(userdataString); print(userdataString);
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
final Map<String, Object> data = { final Map<String, Object> data = {
'name': hostname, 'name': domainName,
'size': serverType, 'size': serverType,
'image': 'ubuntu-20-04-x64', 'image': 'ubuntu-20-04-x64',
'user_data': userdataString, 'user_data': userdataString,
@ -333,7 +320,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
final int serverId = serverCreateResponse.data['server']['id']; final int serverId = serverCreateResponse.data['server']['id'];
final ServerVolume? newVolume = await createVolume(); final ServerVolume? newVolume = await createVolume();
final bool attachedVolume = final bool attachedVolume =
await attachVolume(newVolume!.uuid!, serverId); await attachVolume(newVolume!, serverId);
if (attachedVolume) { if (attachedVolume) {
serverDetails = ServerHostingDetails( serverDetails = ServerHostingDetails(
@ -354,44 +341,25 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
return serverDetails; return serverDetails;
} }
static String getHostnameFromDomain(final String domain) {
// Replace all non-alphanumeric characters with an underscore
String hostname =
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
if (hostname.endsWith('-')) {
hostname = hostname.substring(0, hostname.length - 1);
}
if (hostname.startsWith('-')) {
hostname = hostname.substring(1);
}
if (hostname.isEmpty) {
hostname = 'selfprivacy-server';
}
return hostname;
}
@override @override
Future<void> deleteServer({ Future<void> deleteServer({
required final String domainName, required final String domainName,
}) async { }) async {
final Dio client = await getClient(); final Dio client = await getClient();
final String hostname = getHostnameFromDomain(domainName);
final ServerBasicInfo serverToRemove = (await getServers()).firstWhere( final ServerBasicInfo serverToRemove = (await getServers()).firstWhere(
(final el) => el.name == hostname, (final el) => el.name == domainName,
); );
final ServerVolume volumeToRemove = (await getVolumes()).firstWhere( final ServerVolume volumeToRemove = (await getVolumes()).firstWhere(
(final el) => el.serverId == serverToRemove.id, (final el) => el.serverId == serverToRemove.id,
); );
final List<Future> laterFutures = <Future>[]; final List<Future> laterFutures = <Future>[];
await detachVolume(volumeToRemove.uuid!); await detachVolume(volumeToRemove);
await Future.delayed(const Duration(seconds: 10)); await Future.delayed(const Duration(seconds: 10));
try { try {
laterFutures.add(deleteVolume(volumeToRemove.uuid!)); laterFutures.add(deleteVolume(volumeToRemove));
laterFutures.add(client.delete('/droplets/$serverToRemove.id')); laterFutures.add(client.delete('/droplets/$serverToRemove.id'));
await Future.wait(laterFutures); await Future.wait(laterFutures);
} catch (e) { } catch (e) {
@ -479,22 +447,18 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
final Response response = await client.get('/servers'); final Response response = await client.get('/droplets');
servers = response.data!['servers'] servers = response.data!['droplets']
.map<HetznerServerInfo>(
(final e) => HetznerServerInfo.fromJson(e),
)
.toList()
.where( .where(
(final server) => server.publicNet.ipv4 != null, (final server) => server['networks']['v4'].isNotEmpty,
) )
.map<ServerBasicInfo>( .map<ServerBasicInfo>(
(final server) => ServerBasicInfo( (final server) => ServerBasicInfo(
id: server.id, id: server['id'],
name: server.name, reverseDns: server['name'],
ip: server.publicNet.ipv4.ip, created: DateTime.now(),
reverseDns: server.publicNet.ipv4.reverseDns, ip: server['networks']['v4'][0],
created: server.created, name: server['name'],
), ),
) )
.toList(); .toList();

View File

@ -217,10 +217,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<void> deleteVolume(final String volumeId) async { Future<void> deleteVolume(final ServerVolume volume) async {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
await client.delete('/volumes/$volumeId'); await client.delete('/volumes/$volume.id');
} catch (e) { } catch (e) {
print(e); print(e);
} finally { } finally {
@ -229,14 +229,14 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> attachVolume(final String volumeId, final int serverId) async { Future<bool> attachVolume(final ServerVolume volume, final int serverId) async {
bool success = false; bool success = false;
final Response dbPostResponse; final Response dbPostResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
dbPostResponse = await client.post( dbPostResponse = await client.post(
'/volumes/$volumeId/actions/attach', '/volumes/$volume.id/actions/attach',
data: { data: {
'automount': true, 'automount': true,
'server': serverId, 'server': serverId,
@ -253,13 +253,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> detachVolume(final String volumeId) async { Future<bool> detachVolume(final ServerVolume volume) async {
bool success = false; bool success = false;
final Response dbPostResponse; final Response dbPostResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
dbPostResponse = await client.post('/volumes/$volumeId/actions/detach'); dbPostResponse = await client.post('/volumes/$volume.id/actions/detach');
success = dbPostResponse.data['action']['status'].toString() != 'error'; success = dbPostResponse.data['action']['status'].toString() != 'error';
} catch (e) { } catch (e) {
print(e); print(e);
@ -271,14 +271,14 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<bool> resizeVolume(final String volumeId, final int sizeGb) async { Future<bool> resizeVolume(final ServerVolume volume, final int sizeGb) async {
bool success = false; bool success = false;
final Response dbPostResponse; final Response dbPostResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
dbPostResponse = await client.post( dbPostResponse = await client.post(
'/volumes/$volumeId/actions/resize', '/volumes/$volume.id/actions/resize',
data: { data: {
'size': sizeGb, 'size': sizeGb,
}, },
@ -383,7 +383,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
if (!success) { if (!success) {
await Future.delayed(const Duration(seconds: 10)); await Future.delayed(const Duration(seconds: 10));
await deleteVolume(dbId.toString()); await deleteVolume(dataBase);
} }
if (hetznerError != null) { if (hetznerError != null) {

View File

@ -5,10 +5,9 @@ import 'package:selfprivacy/logic/models/price.dart';
mixin VolumeProviderApi on ApiMap { mixin VolumeProviderApi on ApiMap {
Future<ServerVolume?> createVolume(); Future<ServerVolume?> createVolume();
Future<List<ServerVolume>> getVolumes({final String? status}); Future<List<ServerVolume>> getVolumes({final String? status});
Future<ServerVolume?> getVolume(final String volumeId); Future<bool> attachVolume(final ServerVolume volume, final int serverId);
Future<bool> attachVolume(final String volumeId, final int serverId); Future<bool> detachVolume(final ServerVolume volume);
Future<bool> detachVolume(final String volumeId); Future<bool> resizeVolume(final ServerVolume volume, final int sizeGb);
Future<bool> resizeVolume(final String volumeId, final int sizeGb); Future<void> deleteVolume(final ServerVolume volume);
Future<void> deleteVolume(final String volumeId);
Future<Price?> getPricePerGb(); Future<Price?> getPricePerGb();
} }

View File

@ -66,14 +66,14 @@ class ApiProviderVolumeCubit
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!; final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
await providerApi! await providerApi!
.getVolumeProvider() .getVolumeProvider()
.attachVolume(volume.providerVolume!.id.toString(), server.id); .attachVolume(volume.providerVolume!, server.id);
refresh(); refresh();
} }
Future<void> detachVolume(final DiskVolume volume) async { Future<void> detachVolume(final DiskVolume volume) async {
await providerApi! await providerApi!
.getVolumeProvider() .getVolumeProvider()
.detachVolume(volume.providerVolume!.id.toString()); .detachVolume(volume.providerVolume!);
refresh(); refresh();
} }
@ -87,7 +87,7 @@ class ApiProviderVolumeCubit
); );
emit(state.copyWith(isResizing: true)); emit(state.copyWith(isResizing: true));
final bool resized = await providerApi!.getVolumeProvider().resizeVolume( final bool resized = await providerApi!.getVolumeProvider().resizeVolume(
volume.providerVolume!.id.toString(), volume.providerVolume!,
newSizeGb, newSizeGb,
); );
@ -137,7 +137,7 @@ class ApiProviderVolumeCubit
Future<void> deleteVolume(final DiskVolume volume) async { Future<void> deleteVolume(final DiskVolume volume) async {
await providerApi! await providerApi!
.getVolumeProvider() .getVolumeProvider()
.deleteVolume(volume.providerVolume!.id.toString()); .deleteVolume(volume.providerVolume!);
refresh(); refresh();
} }