Compare commits

..

No commits in common. "f72f86d8aee612e9cdc48661efa4d5e112be0400" and "9d083a1d93badf1419051dda6768fe24c3bdc073" have entirely different histories.

4 changed files with 108 additions and 163 deletions

View File

@ -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'];

View File

@ -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);
} }
} }
}); });

View File

@ -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),
), ),
], ],
), ),

View File

@ -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,
); );