fix: Make improvements by Code Review

pull/140/head
NaiJi ✨ 2022-11-23 11:55:28 +04:00
parent a70e793360
commit 8c6b56f61d
11 changed files with 192 additions and 181 deletions

View File

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

View File

@ -7,19 +7,21 @@ import 'package:selfprivacy/logic/api_maps/staging_options.dart';
abstract class ApiMap {
Future<GraphQLClient> getClient() async {
final HttpClient httpClient = HttpClient();
IOClient? ioClient;
if (StagingOptions.stagingAcme) {
final HttpClient httpClient = HttpClient();
httpClient.badCertificateCallback = (
final cert,
final host,
final port,
) =>
true;
ioClient = IOClient(httpClient);
}
final httpLink = HttpLink(
'https://api.$rootAddress/graphql',
httpClient: IOClient(httpClient),
httpClient: ioClient,
);
final String token = _getApiToken();

View File

@ -90,7 +90,7 @@ class ServerApi extends ApiMap
}
Future<ServerProvider> getServerProviderType() async {
QueryResult response;
QueryResult<Query$SystemServerProvider> response;
ServerProvider providerType = ServerProvider.unknown;
try {
@ -99,15 +99,9 @@ class ServerApi extends ApiMap
if (response.hasException) {
print(response.exception.toString());
}
final rawProviderValue = response.data!['system']['provider']['provider'];
switch (rawProviderValue) {
case 'HETZNER':
providerType = ServerProvider.hetzner;
break;
case 'DIGITALOCEAN':
providerType = ServerProvider.digitalOcean;
break;
}
providerType = ServerProvider.fromGraphQL(
response.parsedData!.system.provider.provider,
);
} catch (e) {
print(e);
}

View File

@ -56,7 +56,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
String get infectProviderName => 'digitalocean';
@override
String get appearanceProviderName => 'Digital Ocean';
String get displayProviderName => 'Digital Ocean';
@override
Future<bool> isApiTokenValid(final String token) async {
@ -102,32 +102,32 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
Future<ServerVolume?> createVolume() async {
ServerVolume? volume;
final Response dbCreateResponse;
final Response createVolumeResponse;
final Dio client = await getClient();
try {
final List<ServerVolume> volumes = await getVolumes();
await Future.delayed(const Duration(seconds: 6));
dbCreateResponse = await client.post(
createVolumeResponse = await client.post(
'/volumes',
data: {
'size_gigabytes': 10,
'name': 'volume${StringGenerators.dbStorageName()}',
'name': 'volume${StringGenerators.storageName()}',
'labels': {'labelkey': 'value'},
'region': region,
'filesystem_type': 'ext4',
},
);
final dbId = dbCreateResponse.data['volume']['id'];
final dbSize = dbCreateResponse.data['volume']['size_gigabytes'];
final dbName = dbCreateResponse.data['volume']['name'];
final volumeId = createVolumeResponse.data['volume']['id'];
final volumeSize = createVolumeResponse.data['volume']['size_gigabytes'];
final volumeName = createVolumeResponse.data['volume']['name'];
volume = ServerVolume(
id: volumes.length,
name: dbName,
sizeByte: dbSize,
name: volumeName,
sizeByte: volumeSize,
serverId: null,
linuxDevice: 'scsi-0DO_Volume_$dbName',
uuid: dbId,
linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName',
uuid: volumeId,
);
} catch (e) {
print(e);
@ -142,29 +142,29 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
Future<List<ServerVolume>> getVolumes({final String? status}) async {
final List<ServerVolume> volumes = [];
final Response dbGetResponse;
final Response getVolumesResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get(
getVolumesResponse = await client.get(
'/volumes',
queryParameters: {
'status': status,
},
);
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
final List<dynamic> rawVolumes = getVolumesResponse.data['volumes'];
int id = 0;
for (final rawVolume in rawVolumes) {
final dbId = rawVolume['id'];
final int dbSize = rawVolume['size_gigabytes'] * 1024 * 1024 * 1024;
final dbDropletIds = rawVolume['droplet_ids'];
final String dbName = rawVolume['name'];
final volumeId = rawVolume['id'];
final int volumeSize = rawVolume['size_gigabytes'] * 1024 * 1024 * 1024;
final volumeDropletIds = rawVolume['droplet_ids'];
final String volumeName = rawVolume['name'];
final volume = ServerVolume(
id: id++,
name: dbName,
sizeByte: dbSize,
serverId: dbDropletIds.isNotEmpty ? dbDropletIds[0] : null,
linuxDevice: 'scsi-0DO_Volume_$dbName',
uuid: dbId,
name: volumeName,
sizeByte: volumeSize,
serverId: volumeDropletIds.isNotEmpty ? volumeDropletIds[0] : null,
linuxDevice: 'scsi-0DO_Volume_$volumeName',
uuid: volumeId,
);
volumes.add(volume);
}
@ -178,24 +178,24 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
}
Future<ServerVolume?> getVolume(final String volumeUuid) async {
ServerVolume? neededVolume;
ServerVolume? requestedVolume;
final List<ServerVolume> volumes = await getVolumes();
for (final volume in volumes) {
if (volume.uuid == volumeUuid) {
neededVolume = volume;
requestedVolume = volume;
}
}
return neededVolume;
return requestedVolume;
}
@override
Future<void> deleteVolume(final ServerVolume volume) async {
final Dio client = await getClient();
try {
await client.delete('/volumes/$volume.uuid');
await client.delete('/volumes/${volume.uuid}');
} catch (e) {
print(e);
} finally {
@ -210,10 +210,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
) async {
bool success = false;
final Response dbPostResponse;
final Response attachVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
attachVolumeResponse = await client.post(
'/volumes/actions',
data: {
'type': 'attach',
@ -222,7 +222,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
'droplet_id': serverId,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
attachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -236,10 +237,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
Future<bool> detachVolume(final ServerVolume volume) async {
bool success = false;
final Response dbPostResponse;
final Response detachVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
detachVolumeResponse = await client.post(
'/volumes/actions',
data: {
'type': 'detach',
@ -248,7 +249,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
'region': region,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
detachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -265,10 +267,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
) async {
bool success = false;
final Response dbPostResponse;
final Response resizeVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
resizeVolumeResponse = await client.post(
'/volumes/actions',
data: {
'type': 'resize',
@ -277,7 +279,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
'region': region,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
resizeVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -450,7 +453,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
return server.copyWith(startTime: DateTime.now());
}
/// Digital Ocean returns a map of lists of /proc/state values,
/// Digital Ocean returns a map of lists of /proc/stat values,
/// so here we are trying to implement average CPU
/// load calculation for each point in time on a given interval.
///
@ -500,63 +503,63 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
const int step = 15;
final Dio client = await getClient();
//try {
Response response = await client.get(
'/monitoring/metrics/droplet/bandwidth',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
'interface': 'public',
'direction': 'inbound',
},
);
try {
Response response = await client.get(
'/monitoring/metrics/droplet/bandwidth',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
'interface': 'public',
'direction': 'inbound',
},
);
final List inbound = response.data['data']['result'][0]['values'];
final List inbound = response.data['data']['result'][0]['values'];
response = await client.get(
'/monitoring/metrics/droplet/bandwidth',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
'interface': 'public',
'direction': 'outbound',
},
);
response = await client.get(
'/monitoring/metrics/droplet/bandwidth',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
'interface': 'public',
'direction': 'outbound',
},
);
final List outbound = response.data['data']['result'][0]['values'];
final List outbound = response.data['data']['result'][0]['values'];
response = await client.get(
'/monitoring/metrics/droplet/cpu',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
},
);
response = await client.get(
'/monitoring/metrics/droplet/cpu',
queryParameters: {
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
'host_id': '$serverId',
},
);
metrics = ServerMetrics(
bandwidthIn: inbound
.map(
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
)
.toList(),
bandwidthOut: outbound
.map(
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
)
.toList(),
cpu: calculateCpuLoadMetrics(response.data['data']['result']),
start: start,
end: end,
stepsInSecond: step,
);
/* } catch (e) {
metrics = ServerMetrics(
bandwidthIn: inbound
.map(
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
)
.toList(),
bandwidthOut: outbound
.map(
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
)
.toList(),
cpu: calculateCpuLoadMetrics(response.data['data']['result']),
start: start,
end: end,
stepsInSecond: step,
);
} catch (e) {
print(e);
} finally {
close(client);
}*/
}
return metrics;
}
@ -604,7 +607,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
ServerMetadataEntity(
type: MetadataType.other,
name: 'server.provider'.tr(),
value: appearanceProviderName,
value: displayProviderName,
),
];
} catch (e) {

View File

@ -57,7 +57,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
String get infectProviderName => 'hetzner';
@override
String get appearanceProviderName => 'Hetzner';
String get displayProviderName => 'Hetzner';
@override
Future<bool> isApiTokenValid(final String token) async {
@ -102,12 +102,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
Future<Price?> getPricePerGb() async {
double? price;
final Response dbGetResponse;
final Response pricingResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get('/pricing');
pricingResponse = await client.get('/pricing');
final volume = dbGetResponse.data['pricing']['volume'];
final volume = pricingResponse.data['pricing']['volume'];
final volumePrice = volume['price_per_gb_month']['gross'];
price = double.parse(volumePrice);
} catch (e) {
@ -128,31 +128,31 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
Future<ServerVolume?> createVolume() async {
ServerVolume? volume;
final Response dbCreateResponse;
final Response createVolumeResponse;
final Dio client = await getClient();
try {
dbCreateResponse = await client.post(
createVolumeResponse = await client.post(
'/volumes',
data: {
'size': 10,
'name': StringGenerators.dbStorageName(),
'name': StringGenerators.storageName(),
'labels': {'labelkey': 'value'},
'location': region,
'automount': false,
'format': 'ext4'
},
);
final dbId = dbCreateResponse.data['volume']['id'];
final dbSize = dbCreateResponse.data['volume']['size'];
final dbServer = dbCreateResponse.data['volume']['server'];
final dbName = dbCreateResponse.data['volume']['name'];
final dbDevice = dbCreateResponse.data['volume']['linux_device'];
final volumeId = createVolumeResponse.data['volume']['id'];
final volumeSize = createVolumeResponse.data['volume']['size'];
final volumeServer = createVolumeResponse.data['volume']['server'];
final volumeName = createVolumeResponse.data['volume']['name'];
final volumeDevice = createVolumeResponse.data['volume']['linux_device'];
volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
id: volumeId,
name: volumeName,
sizeByte: volumeSize,
serverId: volumeServer,
linuxDevice: volumeDevice,
);
} catch (e) {
print(e);
@ -167,28 +167,28 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
Future<List<ServerVolume>> getVolumes({final String? status}) async {
final List<ServerVolume> volumes = [];
final Response dbGetResponse;
final Response getVolumesResonse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get(
getVolumesResonse = await client.get(
'/volumes',
queryParameters: {
'status': status,
},
);
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
final List<dynamic> rawVolumes = getVolumesResonse.data['volumes'];
for (final rawVolume in rawVolumes) {
final int dbId = rawVolume['id'];
final int dbSize = rawVolume['size'] * 1024 * 1024 * 1024;
final dbServer = rawVolume['server'];
final String dbName = rawVolume['name'];
final dbDevice = rawVolume['linux_device'];
final int volumeId = rawVolume['id'];
final int volumeSize = rawVolume['size'] * 1024 * 1024 * 1024;
final volumeServer = rawVolume['server'];
final String volumeName = rawVolume['name'];
final volumeDevice = rawVolume['linux_device'];
final volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
id: volumeId,
name: volumeName,
sizeByte: volumeSize,
serverId: volumeServer,
linuxDevice: volumeDevice,
);
volumes.add(volume);
}
@ -206,21 +206,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
) async {
ServerVolume? volume;
final Response dbGetResponse;
final Response getVolumeResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get('/volumes/$volumeId');
final int dbId = dbGetResponse.data['volume']['id'];
final int dbSize = dbGetResponse.data['volume']['size'];
final int dbServer = dbGetResponse.data['volume']['server'];
final String dbName = dbGetResponse.data['volume']['name'];
final dbDevice = dbGetResponse.data['volume']['linux_device'];
getVolumeResponse = await client.get('/volumes/$volumeId');
final int responseVolumeId = getVolumeResponse.data['volume']['id'];
final int volumeSize = getVolumeResponse.data['volume']['size'];
final int volumeServer = getVolumeResponse.data['volume']['server'];
final String volumeName = getVolumeResponse.data['volume']['name'];
final volumeDevice = getVolumeResponse.data['volume']['linux_device'];
volume = ServerVolume(
id: dbId,
name: dbName,
sizeByte: dbSize,
serverId: dbServer,
linuxDevice: dbDevice,
id: responseVolumeId,
name: volumeName,
sizeByte: volumeSize,
serverId: volumeServer,
linuxDevice: volumeDevice,
);
} catch (e) {
print(e);
@ -250,17 +250,18 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
) async {
bool success = false;
final Response dbPostResponse;
final Response attachVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
attachVolumeResponse = await client.post(
'/volumes/${volume.id}/actions/attach',
data: {
'automount': true,
'server': serverId,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
attachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -274,13 +275,14 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
Future<bool> detachVolume(final ServerVolume volume) async {
bool success = false;
final Response dbPostResponse;
final Response detachVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
detachVolumeResponse = await client.post(
'/volumes/${volume.id}/actions/detach',
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
detachVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -297,16 +299,17 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
) async {
bool success = false;
final Response dbPostResponse;
final Response resizeVolumeResponse;
final Dio client = await getClient();
try {
dbPostResponse = await client.post(
resizeVolumeResponse = await client.post(
'/volumes/${volume.id}/actions/resize',
data: {
'size': size.gibibyte,
},
);
success = dbPostResponse.data['action']['status'].toString() != 'error';
success =
resizeVolumeResponse.data['action']['status'].toString() != 'error';
} catch (e) {
print(e);
} finally {
@ -334,7 +337,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
dnsApiToken: dnsApiToken,
rootUser: rootUser,
domainName: domainName,
dataBase: newVolume,
volume: newVolume,
serverType: serverType,
);
@ -345,13 +348,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
required final ServerVolume dataBase,
required final ServerVolume volume,
required final String serverType,
}) async {
final Dio client = await getClient();
final String dbPassword = StringGenerators.dbPassword();
final int dbId = dataBase.id;
final int volumeId = volume.id;
final String apiToken = StringGenerators.apiToken();
final String hostname = getHostnameFromDomain(domainName);
@ -373,7 +376,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
'server_type': serverType,
'start_after_create': false,
'image': 'ubuntu-20.04',
'volumes': [dbId],
'volumes': [volumeId],
'networks': [],
'user_data': userdataString,
'labels': {},
@ -391,7 +394,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
id: serverCreateResponse.data['server']['id'],
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
createTime: DateTime.now(),
volume: dataBase,
volume: volume,
apiToken: apiToken,
provider: ServerProvider.hetzner,
);
@ -407,7 +410,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
if (!success) {
await Future.delayed(const Duration(seconds: 10));
await deleteVolume(dataBase);
await deleteVolume(volume);
}
if (hetznerError != null) {
@ -621,7 +624,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
ServerMetadataEntity(
type: MetadataType.other,
name: 'server.provider'.tr(),
value: appearanceProviderName,
value: displayProviderName,
),
];
} catch (e) {

View File

@ -54,5 +54,5 @@ abstract class ServerProviderApi extends ApiMap {
/// Actual provider name to render on information page for user,
/// for example 'Digital Ocean' for Digital Ocean
String get appearanceProviderName;
String get displayProviderName;
}

View File

@ -1,4 +1,5 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
part 'server_details.g.dart';
@ -82,5 +83,16 @@ enum ServerProvider {
@HiveField(1)
hetzner,
@HiveField(2)
digitalOcean,
digitalOcean;
factory ServerProvider.fromGraphQL(final Enum$ServerProvider provider) {
switch (provider) {
case Enum$ServerProvider.HETZNER:
return hetzner;
case Enum$ServerProvider.DIGITALOCEAN:
return digitalOcean;
default:
return unknown;
}
}
}

View File

@ -1,12 +1,20 @@
enum MetadataType {
id,
status,
cpu,
ram,
cost,
location,
import 'package:flutter/material.dart';
other,
enum MetadataType {
id(icon: Icons.numbers_outlined),
status(icon: Icons.mode_standby_outlined),
cpu(icon: Icons.memory_outlined),
ram(icon: Icons.memory_outlined),
cost(icon: Icons.payments_outlined),
location(icon: Icons.location_on_outlined),
other(icon: Icons.info_outlined);
const MetadataType({
required this.icon,
});
final IconData icon;
}
class ServerMetadataEntity {

View File

@ -10,7 +10,6 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';

View File

@ -1,16 +1,6 @@
part of 'server_details_screen.dart';
class _TextDetails extends StatelessWidget {
final Map<MetadataType, IconData> metadataToIcon = const {
MetadataType.id: Icons.numbers_outlined,
MetadataType.status: Icons.mode_standby_outlined,
MetadataType.cpu: Icons.memory_outlined,
MetadataType.ram: Icons.memory_outlined,
MetadataType.cost: Icons.euro_outlined,
MetadataType.location: Icons.location_on_outlined,
MetadataType.other: Icons.info_outlined,
};
@override
Widget build(final BuildContext context) {
final details = context.watch<ServerDetailsCubit>().state;
@ -36,7 +26,7 @@ class _TextDetails extends StatelessWidget {
...details.metadata
.map(
(final metadata) => ListTileOnSurfaceVariant(
leadingIcon: metadataToIcon[metadata.type],
leadingIcon: metadata.type.icon,
title: metadata.name,
subtitle: metadata.value,
),

View File

@ -101,7 +101,7 @@ class StringGenerators {
hasSymbols: true,
);
static StringGeneratorFunction dbStorageName = () => getRandomString(
static StringGeneratorFunction storageName = () => getRandomString(
6,
hasLowercaseLetters: true,
hasUppercaseLetters: false,