Compare commits
No commits in common. "f72f86d8aee612e9cdc48661efa4d5e112be0400" and "9d083a1d93badf1419051dda6768fe24c3bdc073" have entirely different histories.
f72f86d8ae
...
9d083a1d93
@ -1,9 +1,8 @@
|
|||||||
const addMountTitle = 'New Mount Settings';
|
const String addMountTitle = 'New Mount Settings';
|
||||||
const appTitle = 'Repertory Management Portal';
|
const String appTitle = 'Repertory Management Portal';
|
||||||
const databaseTypeList = ['rocksdb', 'sqlite'];
|
const databaseTypeList = ['rocksdb', 'sqlite'];
|
||||||
const downloadTypeList = ['default', 'direct', 'ring_buffer'];
|
const downloadTypeList = ['default', 'direct', 'ring_buffer'];
|
||||||
const eventLevelList = ['critical', 'error', 'warn', 'info', 'debug', 'trace'];
|
const eventLevelList = ['critical', 'error', 'warn', 'info', 'debug', 'trace'];
|
||||||
const padding = 15.0;
|
|
||||||
const protocolTypeList = ['http', 'https'];
|
const protocolTypeList = ['http', 'https'];
|
||||||
const providerTypeList = ['Encrypt', 'Remote', 'S3', 'Sia'];
|
const providerTypeList = ['Encrypt', 'Remote', 'S3', 'Sia'];
|
||||||
const ringBufferSizeList = ['128', '256', '512', '1024', '2048'];
|
const ringBufferSizeList = ['128', '256', '512', '1024', '2048'];
|
||||||
|
@ -1,60 +1,32 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:repertory/constants.dart' as constants;
|
|
||||||
|
|
||||||
typedef Validator = bool Function(String);
|
typedef Validator = bool Function(String);
|
||||||
|
|
||||||
// ignore: prefer_function_declarations_over_variables
|
bool containsRestrictedChar(String value) {
|
||||||
final Validator noRestrictedChars = (value) {
|
const invalidChars = [
|
||||||
return [
|
'!',
|
||||||
'!',
|
'"',
|
||||||
'"',
|
'\$',
|
||||||
'\$',
|
'&',
|
||||||
'&',
|
'\'',
|
||||||
"'",
|
'(',
|
||||||
'(',
|
')',
|
||||||
')',
|
'*',
|
||||||
'*',
|
';',
|
||||||
';',
|
'<',
|
||||||
'<',
|
'>',
|
||||||
'>',
|
'?',
|
||||||
'?',
|
'[',
|
||||||
'[',
|
']',
|
||||||
']',
|
'`',
|
||||||
'`',
|
'{',
|
||||||
'{',
|
'}',
|
||||||
'}',
|
'|',
|
||||||
'|',
|
];
|
||||||
].firstWhereOrNull((char) => value.contains(char)) ==
|
return invalidChars.firstWhereOrNull((char) => value.contains(char)) != null;
|
||||||
null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ignore: prefer_function_declarations_over_variables
|
|
||||||
final Validator notEmptyValidator = (value) => value.isNotEmpty;
|
|
||||||
|
|
||||||
// ignore: prefer_function_declarations_over_variables
|
|
||||||
final Validator portIsValid = (value) {
|
|
||||||
int? intValue = int.tryParse(value);
|
|
||||||
if (intValue == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (intValue > 0 && intValue < 65536);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ignore: prefer_function_declarations_over_variables
|
|
||||||
final Validator trimNotEmptyValidator = (value) => value.trim().isNotEmpty;
|
|
||||||
|
|
||||||
createUriValidator<Validator>({host, port}) {
|
|
||||||
return (value) =>
|
|
||||||
Uri.tryParse('http://${host ?? value}:${port ?? value}/') != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createHostNameOrIpValidators() => <Validator>[
|
|
||||||
trimNotEmptyValidator,
|
|
||||||
createUriValidator(port: 9000),
|
|
||||||
];
|
|
||||||
|
|
||||||
Map<String, dynamic> createDefaultSettings(String mountType) {
|
Map<String, dynamic> createDefaultSettings(String mountType) {
|
||||||
switch (mountType) {
|
switch (mountType) {
|
||||||
case 'Encrypt':
|
case 'Encrypt':
|
||||||
@ -108,49 +80,49 @@ String getBaseUri() {
|
|||||||
List<Validator> getSettingValidators(String settingPath) {
|
List<Validator> getSettingValidators(String settingPath) {
|
||||||
switch (settingPath) {
|
switch (settingPath) {
|
||||||
case 'ApiAuth':
|
case 'ApiAuth':
|
||||||
return [notEmptyValidator];
|
return [(value) => value.isNotEmpty];
|
||||||
case 'DatabaseType':
|
|
||||||
return [(value) => constants.databaseTypeList.contains(value)];
|
|
||||||
case 'PreferredDownloadType':
|
|
||||||
return [(value) => constants.downloadTypeList.contains(value)];
|
|
||||||
case 'EventLevel':
|
|
||||||
return [(value) => constants.eventLevelList.contains(value)];
|
|
||||||
case 'EncryptConfig.EncryptionToken':
|
case 'EncryptConfig.EncryptionToken':
|
||||||
return [notEmptyValidator];
|
return [(value) => value.isNotEmpty];
|
||||||
case 'EncryptConfig.Path':
|
case 'EncryptConfig.Path':
|
||||||
return [trimNotEmptyValidator, noRestrictedChars];
|
return [
|
||||||
|
(value) => value.trim().isNotEmpty,
|
||||||
|
(value) => !containsRestrictedChar(value),
|
||||||
|
];
|
||||||
case 'HostConfig.ApiPassword':
|
case 'HostConfig.ApiPassword':
|
||||||
return [notEmptyValidator];
|
return [(value) => value.isNotEmpty];
|
||||||
case 'HostConfig.ApiPort':
|
case 'HostConfig.ApiPort':
|
||||||
return [portIsValid];
|
return [
|
||||||
|
(value) {
|
||||||
|
int? intValue = int.tryParse(value);
|
||||||
|
if (intValue == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (intValue > 0 && intValue < 65536);
|
||||||
|
},
|
||||||
|
(value) => Uri.tryParse('http://localhost:$value/') != null,
|
||||||
|
];
|
||||||
case 'HostConfig.HostNameOrIp':
|
case 'HostConfig.HostNameOrIp':
|
||||||
return createHostNameOrIpValidators();
|
return [
|
||||||
|
(value) => value.trim().isNotEmpty,
|
||||||
|
(value) => Uri.tryParse('http://$value:9000/') != null,
|
||||||
|
];
|
||||||
case 'HostConfig.Protocol':
|
case 'HostConfig.Protocol':
|
||||||
return [(value) => constants.protocolTypeList.contains(value)];
|
return [(value) => value == "http" || value == "https"];
|
||||||
case 'RemoteConfig.ApiPort':
|
|
||||||
return [notEmptyValidator, portIsValid];
|
|
||||||
case 'RemoteConfig.EncryptionToken':
|
case 'RemoteConfig.EncryptionToken':
|
||||||
return [notEmptyValidator];
|
return [(value) => value.isNotEmpty];
|
||||||
case 'RemoteConfig.HostNameOrIp':
|
|
||||||
return createHostNameOrIpValidators();
|
|
||||||
case 'RemoteMount.ApiPort':
|
|
||||||
return [notEmptyValidator, portIsValid];
|
|
||||||
case 'RemoteMount.EncryptionToken':
|
case 'RemoteMount.EncryptionToken':
|
||||||
return [notEmptyValidator];
|
return [(value) => value.isNotEmpty];
|
||||||
case 'RemoteMount.HostNameOrIp':
|
|
||||||
return createHostNameOrIpValidators();
|
|
||||||
case 'RingBufferFileSize':
|
|
||||||
return [(value) => constants.ringBufferSizeList.contains(value)];
|
|
||||||
case 'S3Config.AccessKey':
|
case 'S3Config.AccessKey':
|
||||||
return [trimNotEmptyValidator];
|
return [(value) => value.trim().isNotEmpty];
|
||||||
case 'S3Config.Bucket':
|
case 'S3Config.Bucket':
|
||||||
return [trimNotEmptyValidator];
|
return [(value) => value.trim().isNotEmpty];
|
||||||
case 'S3Config.SecretKey':
|
case 'S3Config.SecretKey':
|
||||||
return [trimNotEmptyValidator];
|
return [(value) => value.trim().isNotEmpty];
|
||||||
case 'S3Config.URL':
|
case 'S3Config.URL':
|
||||||
return [trimNotEmptyValidator, (value) => Uri.tryParse(value) != null];
|
return [(value) => Uri.tryParse(value) != null];
|
||||||
case 'SiaConfig.Bucket':
|
case 'SiaConfig.Bucket':
|
||||||
return [trimNotEmptyValidator];
|
return [(value) => value.trim().isNotEmpty];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
@ -174,19 +146,18 @@ bool validateSettings(
|
|||||||
String? rootKey,
|
String? rootKey,
|
||||||
}) {
|
}) {
|
||||||
settings.forEach((key, value) {
|
settings.forEach((key, value) {
|
||||||
final settingKey = rootKey == null ? key : '$rootKey.$key';
|
final checkKey = rootKey == null ? key : '$rootKey.$key';
|
||||||
if (value is Map) {
|
if (value is Map) {
|
||||||
validateSettings(
|
validateSettings(
|
||||||
value as Map<String, dynamic>,
|
value as Map<String, dynamic>,
|
||||||
failed,
|
failed,
|
||||||
rootKey: settingKey,
|
rootKey: checkKey,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
for (var validator in getSettingValidators(settingKey)) {
|
for (var validator in getSettingValidators(checkKey)) {
|
||||||
if (validator(value.toString())) {
|
if (!validator(value.toString())) {
|
||||||
continue;
|
failed.add(checkKey);
|
||||||
}
|
}
|
||||||
failed.add(settingKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:repertory/constants.dart' as constants;
|
import 'package:repertory/constants.dart';
|
||||||
import 'package:repertory/helpers.dart';
|
import 'package:repertory/helpers.dart';
|
||||||
import 'package:repertory/models/mount.dart';
|
import 'package:repertory/models/mount.dart';
|
||||||
import 'package:repertory/models/mount_list.dart';
|
import 'package:repertory/models/mount_list.dart';
|
||||||
@ -18,6 +17,8 @@ class AddMountScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AddMountScreenState extends State<AddMountScreen> {
|
class _AddMountScreenState extends State<AddMountScreen> {
|
||||||
|
static const _padding = 15.0;
|
||||||
|
|
||||||
Mount? _mount;
|
Mount? _mount;
|
||||||
final _mountNameController = TextEditingController();
|
final _mountNameController = TextEditingController();
|
||||||
String _mountType = "";
|
String _mountType = "";
|
||||||
@ -49,7 +50,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(constants.padding),
|
padding: const EdgeInsets.all(_padding),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -57,44 +58,43 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Card(
|
Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(constants.padding),
|
padding: const EdgeInsets.all(_padding),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text('Provider Type'),
|
const Text('Provider Type'),
|
||||||
const SizedBox(width: constants.padding),
|
const SizedBox(width: _padding),
|
||||||
DropdownButton<String>(
|
DropdownButton<String>(
|
||||||
value: _mountType,
|
value: _mountType,
|
||||||
onChanged: (mountType) => _handleChange(mountType ?? ''),
|
onChanged: (mountType) => _handleChange(mountType ?? ''),
|
||||||
items:
|
items:
|
||||||
constants.providerTypeList
|
providerTypeList.map<DropdownMenuItem<String>>((
|
||||||
.map<DropdownMenuItem<String>>((item) {
|
item,
|
||||||
return DropdownMenuItem<String>(
|
) {
|
||||||
value: item,
|
return DropdownMenuItem<String>(
|
||||||
child: Text(item),
|
value: item,
|
||||||
);
|
child: Text(item),
|
||||||
})
|
);
|
||||||
.toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_mountType.isNotEmpty)
|
if (_mountType.isNotEmpty) const SizedBox(height: _padding),
|
||||||
const SizedBox(height: constants.padding),
|
|
||||||
if (_mountType.isNotEmpty)
|
if (_mountType.isNotEmpty)
|
||||||
Card(
|
Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(constants.padding),
|
padding: const EdgeInsets.all(_padding),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text('Configuration Name'),
|
const Text('Configuration Name'),
|
||||||
const SizedBox(width: constants.padding),
|
const SizedBox(width: _padding),
|
||||||
TextField(
|
TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: _mountNameController,
|
controller: _mountNameController,
|
||||||
@ -112,7 +112,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(constants.padding),
|
padding: const EdgeInsets.all(_padding),
|
||||||
child: MountSettingsWidget(
|
child: MountSettingsWidget(
|
||||||
isAdd: true,
|
isAdd: true,
|
||||||
mount: _mount!,
|
mount: _mount!,
|
||||||
@ -123,60 +123,33 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_mount != null)
|
if (_mount != null)
|
||||||
Builder(
|
ElevatedButton.icon(
|
||||||
builder: (context) {
|
onPressed: () {
|
||||||
return ElevatedButton.icon(
|
List<String> failed = [];
|
||||||
onPressed: () {
|
if (!validateSettings(_settings[_mountType]!, failed)) {
|
||||||
final mountList = Provider.of<MountList>(
|
for (var key in failed) {
|
||||||
context,
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
listen: false,
|
SnackBar(
|
||||||
);
|
content: Text(
|
||||||
|
"'$key' is not valid",
|
||||||
List<String> failed = [];
|
textAlign: TextAlign.center,
|
||||||
if (!validateSettings(_settings[_mountType]!, failed)) {
|
|
||||||
for (var key in failed) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
"'$key' is not valid",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final existingMount = mountList.items.firstWhereOrNull(
|
|
||||||
(item) =>
|
|
||||||
item.name.toLowerCase() ==
|
|
||||||
_mountNameController.text.toLowerCase(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingMount != null) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
"'${_mountNameController.text}' already exists",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mountList.add(
|
|
||||||
_mountType,
|
|
||||||
_mountNameController.text,
|
|
||||||
_settings[_mountType]!,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Navigator.pop(context);
|
Provider.of<MountList>(context, listen: false).add(
|
||||||
},
|
_mountType,
|
||||||
label: const Text('Add'),
|
_mountNameController.text,
|
||||||
icon: Icon(Icons.add),
|
_settings[_mountType]!,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
label: const Text('Add'),
|
||||||
|
icon: Icon(Icons.add),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:repertory/constants.dart' as constants;
|
import 'package:repertory/constants.dart';
|
||||||
import 'package:repertory/helpers.dart' show Validator, getSettingValidators;
|
import 'package:repertory/helpers.dart' show Validator, getSettingValidators;
|
||||||
import 'package:repertory/models/mount.dart';
|
import 'package:repertory/models/mount.dart';
|
||||||
import 'package:settings_ui/settings_ui.dart';
|
import 'package:settings_ui/settings_ui.dart';
|
||||||
@ -26,6 +26,8 @@ class MountSettingsWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
||||||
|
static const _padding = 15.0;
|
||||||
|
|
||||||
void _addBooleanSetting(list, root, key, value, isAdvanced) {
|
void _addBooleanSetting(list, root, key, value, isAdvanced) {
|
||||||
if (!isAdvanced || widget.showAdvanced) {
|
if (!isAdvanced || widget.showAdvanced) {
|
||||||
list.add(
|
list.add(
|
||||||
@ -262,7 +264,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
obscuringCharacter: '*',
|
obscuringCharacter: '*',
|
||||||
onChanged: (value) => updatedValue1 = value,
|
onChanged: (value) => updatedValue1 = value,
|
||||||
),
|
),
|
||||||
const SizedBox(height: constants.padding),
|
const SizedBox(height: _padding),
|
||||||
TextField(
|
TextField(
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
controller: TextEditingController(text: updatedValue2),
|
controller: TextEditingController(text: updatedValue2),
|
||||||
@ -407,7 +409,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
widget.settings,
|
widget.settings,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
constants.databaseTypeList,
|
databaseTypeList,
|
||||||
Icons.dataset,
|
Icons.dataset,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@ -454,7 +456,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
widget.settings,
|
widget.settings,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
constants.eventLevelList,
|
eventLevelList,
|
||||||
Icons.event,
|
Icons.event,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -526,7 +528,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
widget.settings,
|
widget.settings,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
constants.downloadTypeList,
|
downloadTypeList,
|
||||||
Icons.download,
|
Icons.download,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -551,7 +553,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
widget.settings,
|
widget.settings,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
constants.ringBufferSizeList,
|
ringBufferSizeList,
|
||||||
512,
|
512,
|
||||||
Icons.animation,
|
Icons.animation,
|
||||||
false,
|
false,
|
||||||
@ -731,7 +733,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
|||||||
widget.settings[key],
|
widget.settings[key],
|
||||||
subKey,
|
subKey,
|
||||||
subValue,
|
subValue,
|
||||||
constants.protocolTypeList,
|
protocolTypeList,
|
||||||
Icons.http,
|
Icons.http,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user