partial logon support
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good
This commit is contained in:
@@ -246,8 +246,8 @@ bool validateSettings(
|
||||
|
||||
Future<Map<String, dynamic>> convertAllToString(
|
||||
Map<String, dynamic> settings,
|
||||
SecureKey key,
|
||||
) async {
|
||||
String? password;
|
||||
Future<Map<String, dynamic>> convert(Map<String, dynamic> settings) async {
|
||||
for (var entry in settings.entries) {
|
||||
if (entry.value is Map<String, dynamic>) {
|
||||
@@ -262,14 +262,7 @@ Future<Map<String, dynamic>> convertAllToString(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (password == null) {
|
||||
password = await promptPassword();
|
||||
if (password == null) {
|
||||
throw NullPasswordException();
|
||||
}
|
||||
}
|
||||
|
||||
settings[entry.key] = encryptValue(entry.value, password!);
|
||||
settings[entry.key] = encryptValue(entry.value, key);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -286,7 +279,7 @@ Future<Map<String, dynamic>> convertAllToString(
|
||||
return convert(settings);
|
||||
}
|
||||
|
||||
String encryptValue(String value, String password) {
|
||||
String encryptValue(String value, SecureKey key) {
|
||||
if (value.isEmpty) {
|
||||
return value;
|
||||
}
|
||||
@@ -296,17 +289,12 @@ String encryptValue(String value, String password) {
|
||||
return value;
|
||||
}
|
||||
|
||||
final keyHash = sodium.crypto.genericHash(
|
||||
outLen: sodium.crypto.aeadXChaCha20Poly1305IETF.keyBytes,
|
||||
message: Uint8List.fromList(password.toCharArray()),
|
||||
);
|
||||
|
||||
final crypto = sodium.crypto.aeadXChaCha20Poly1305IETF;
|
||||
|
||||
final nonce = sodium.secureRandom(crypto.nonceBytes).extractBytes();
|
||||
final data = crypto.encrypt(
|
||||
additionalData: Uint8List.fromList('repertory'.toCharArray()),
|
||||
key: SecureKey.fromList(sodium, keyHash),
|
||||
key: key,
|
||||
message: Uint8List.fromList(value.toCharArray()),
|
||||
nonce: nonce,
|
||||
);
|
||||
|
@@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/screens/add_mount_screen.dart';
|
||||
import 'package:repertory/screens/auth_screen.dart';
|
||||
import 'package:repertory/screens/edit_mount_screen.dart';
|
||||
import 'package:repertory/screens/edit_settings_screen.dart';
|
||||
import 'package:repertory/screens/home_screen.dart';
|
||||
@@ -17,8 +19,15 @@ void main() async {
|
||||
debugPrint('$e');
|
||||
}
|
||||
|
||||
final auth = Auth();
|
||||
runApp(
|
||||
ChangeNotifierProvider(create: (_) => MountList(), child: const MyApp()),
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (_) => auth),
|
||||
ChangeNotifierProvider(create: (_) => MountList(auth)),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,12 +64,18 @@ class _MyAppState extends State<MyApp> {
|
||||
title: constants.appTitle,
|
||||
initialRoute: '/',
|
||||
routes: {
|
||||
'/': (context) => const HomeScreen(title: constants.appTitle),
|
||||
'/add':
|
||||
(context) => const AddMountScreen(title: constants.addMountTitle),
|
||||
'/settings':
|
||||
'/':
|
||||
(context) =>
|
||||
const EditSettingsScreen(title: constants.appSettingsTitle),
|
||||
const AuthCheck(child: HomeScreen(title: constants.appTitle)),
|
||||
'/add':
|
||||
(context) => const AuthCheck(
|
||||
child: AddMountScreen(title: constants.addMountTitle),
|
||||
),
|
||||
'/auth': (context) => const AuthScreen(title: constants.appTitle),
|
||||
'/settings':
|
||||
(context) => const AuthCheck(
|
||||
child: EditSettingsScreen(title: constants.appSettingsTitle),
|
||||
),
|
||||
},
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name != '/edit') {
|
||||
@@ -70,10 +85,12 @@ class _MyAppState extends State<MyApp> {
|
||||
final mount = settings.arguments as Mount;
|
||||
return MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return EditMountScreen(
|
||||
mount: mount,
|
||||
title:
|
||||
'${mount.provider} [${formatMountName(mount.type, mount.name)}] Settings',
|
||||
return AuthCheck(
|
||||
child: EditMountScreen(
|
||||
mount: mount,
|
||||
title:
|
||||
'${mount.provider} [${formatMountName(mount.type, mount.name)}] Settings',
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -81,3 +98,22 @@ class _MyAppState extends State<MyApp> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthCheck extends StatelessWidget {
|
||||
final Widget child;
|
||||
const AuthCheck({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Auth>(
|
||||
builder: (context, auth, __) {
|
||||
if (!auth.authenticated) {
|
||||
Navigator.of(context).pushReplacementNamed('/auth');
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
55
web/repertory/lib/models/auth.dart
Normal file
55
web/repertory/lib/models/auth.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:sodium_libs/sodium_libs.dart';
|
||||
|
||||
class Auth with ChangeNotifier {
|
||||
bool _authenticated = false;
|
||||
SecureKey? _key;
|
||||
String _user = "";
|
||||
|
||||
bool get authenticated => _authenticated;
|
||||
SecureKey get key => _key!;
|
||||
|
||||
Future<void> authenticate(String user, String password) async {
|
||||
final sodium = constants.sodium;
|
||||
if (sodium == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final keyHash = sodium.crypto.genericHash(
|
||||
outLen: sodium.crypto.aeadXChaCha20Poly1305IETF.keyBytes,
|
||||
message: Uint8List.fromList(password.toCharArray()),
|
||||
);
|
||||
|
||||
_authenticated = true;
|
||||
_key = SecureKey.fromList(sodium, keyHash);
|
||||
_user = user;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<String> createAuth() async {
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse(Uri.encodeFull('${getBaseUri()}/api/v1/nonce')),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final nonce = jsonDecode(response.body)["nonce"];
|
||||
debugPrint('nonce: $nonce');
|
||||
return encryptValue('${_user}_$nonce', key);
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
@@ -3,16 +3,18 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/types/mount_config.dart';
|
||||
|
||||
class Mount with ChangeNotifier {
|
||||
final Auth _auth;
|
||||
final MountConfig mountConfig;
|
||||
final MountList? _mountList;
|
||||
bool _isMounting = false;
|
||||
bool _isRefreshing = false;
|
||||
|
||||
Mount(this.mountConfig, this._mountList, {isAdd = false}) {
|
||||
Mount(this._auth, this.mountConfig, this._mountList, {isAdd = false}) {
|
||||
if (isAdd) {
|
||||
return;
|
||||
}
|
||||
@@ -29,9 +31,12 @@ class Mount with ChangeNotifier {
|
||||
|
||||
Future<void> _fetch() async {
|
||||
try {
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
Uri.encodeFull('${getBaseUri()}/api/v1/mount?name=$name&type=$type'),
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/mount?auth=$auth&name=$name&type=$type',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -57,10 +62,11 @@ class Mount with ChangeNotifier {
|
||||
|
||||
Future<void> _fetchStatus() async {
|
||||
try {
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/mount_status?name=$name&type=$type',
|
||||
'${getBaseUri()}/api/v1/mount_status?auth=$auth&name=$name&type=$type',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -87,10 +93,11 @@ class Mount with ChangeNotifier {
|
||||
|
||||
Future<String?> getMountLocation() async {
|
||||
try {
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/mount_location?name=$name&type=$type',
|
||||
'${getBaseUri()}/api/v1/mount_location?auth=$auth&name=$name&type=$type',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -120,10 +127,11 @@ class Mount with ChangeNotifier {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
}
|
||||
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.post(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/mount?unmount=$unmount&name=$name&type=$type&location=$location',
|
||||
'${getBaseUri()}/api/v1/mount?auth=$auth&unmount=$unmount&name=$name&type=$type&location=$location',
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -167,10 +175,11 @@ class Mount with ChangeNotifier {
|
||||
|
||||
Future<void> setValue(String key, String value) async {
|
||||
try {
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.put(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/set_value_by_name?name=$name&type=$type&key=$key&value=$value',
|
||||
'${getBaseUri()}/api/v1/set_value_by_name?auth=$auth&name=$name&type=$type&key=$key&value=$value',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@@ -6,16 +6,21 @@ import 'package:flutter/material.dart' show ModalRoute;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/types/mount_config.dart';
|
||||
|
||||
class MountList with ChangeNotifier {
|
||||
MountList() {
|
||||
final Auth _auth;
|
||||
|
||||
MountList(this._auth) {
|
||||
_fetch();
|
||||
}
|
||||
|
||||
List<Mount> _mountList = [];
|
||||
|
||||
Auth get auth => _auth;
|
||||
|
||||
UnmodifiableListView<Mount> get items =>
|
||||
UnmodifiableListView<Mount>(_mountList);
|
||||
|
||||
@@ -46,8 +51,9 @@ class MountList with ChangeNotifier {
|
||||
|
||||
Future<void> _fetch() async {
|
||||
try {
|
||||
final auth = await _auth.createAuth();
|
||||
final response = await http.get(
|
||||
Uri.parse('${getBaseUri()}/api/v1/mount_list'),
|
||||
Uri.parse('${getBaseUri()}/api/v1/mount_list?auth=$auth'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 404) {
|
||||
@@ -64,7 +70,10 @@ class MountList with ChangeNotifier {
|
||||
jsonDecode(response.body).forEach((type, value) {
|
||||
nextList.addAll(
|
||||
value
|
||||
.map((name) => Mount(MountConfig(type: type, name: name), this))
|
||||
.map(
|
||||
(name) =>
|
||||
Mount(_auth, MountConfig(type: type, name: name), this),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
});
|
||||
@@ -107,11 +116,15 @@ class MountList with ChangeNotifier {
|
||||
}
|
||||
|
||||
try {
|
||||
final map = await convertAllToString(jsonDecode(jsonEncode(mountConfig)));
|
||||
final auth = await _auth.createAuth();
|
||||
final map = await convertAllToString(
|
||||
jsonDecode(jsonEncode(mountConfig)),
|
||||
_auth.key,
|
||||
);
|
||||
final response = await http.post(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/add_mount?name=$name&type=$type&config=${jsonEncode(map)}',
|
||||
'${getBaseUri()}/api/v1/add_mount?auth=$auth&name=$name&type=$type&config=${jsonEncode(map)}',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/types/mount_config.dart';
|
||||
@@ -49,147 +50,158 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Card(
|
||||
margin: EdgeInsets.all(0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Provider Type'),
|
||||
const SizedBox(width: constants.padding),
|
||||
DropdownButton<String>(
|
||||
autofocus: true,
|
||||
value: _mountType,
|
||||
onChanged: (mountType) => _handleChange(mountType ?? ''),
|
||||
items:
|
||||
constants.providerTypeList
|
||||
.map<DropdownMenuItem<String>>((item) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: item,
|
||||
child: Text(item),
|
||||
);
|
||||
})
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_mountType.isNotEmpty && _mountType != 'Remote')
|
||||
const SizedBox(height: constants.padding),
|
||||
if (_mountType.isNotEmpty && _mountType != 'Remote')
|
||||
Card(
|
||||
margin: EdgeInsets.all(0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Configuration Name'),
|
||||
const SizedBox(width: constants.padding),
|
||||
TextField(
|
||||
autofocus: true,
|
||||
controller: _mountNameController,
|
||||
keyboardType: TextInputType.text,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||
],
|
||||
onChanged: (_) => _handleChange(_mountType),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_mount != null) const SizedBox(height: constants.padding),
|
||||
if (_mount != null)
|
||||
Expanded(
|
||||
child: Card(
|
||||
child: Consumer<Auth>(
|
||||
builder: (context, auth, _) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Card(
|
||||
margin: EdgeInsets.all(0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: MountSettingsWidget(
|
||||
isAdd: true,
|
||||
mount: _mount!,
|
||||
settings: _settings[_mountType]!,
|
||||
showAdvanced: _showAdvanced,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Provider Type'),
|
||||
const SizedBox(width: constants.padding),
|
||||
DropdownButton<String>(
|
||||
autofocus: true,
|
||||
value: _mountType,
|
||||
onChanged:
|
||||
(mountType) =>
|
||||
_handleChange(auth, mountType ?? ''),
|
||||
items:
|
||||
constants.providerTypeList
|
||||
.map<DropdownMenuItem<String>>((item) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: item,
|
||||
child: Text(item),
|
||||
);
|
||||
})
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_mount != null) const SizedBox(height: constants.padding),
|
||||
if (_mount != null)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final mountList = Provider.of<MountList>(context);
|
||||
if (_mountType.isNotEmpty && _mountType != 'Remote')
|
||||
const SizedBox(height: constants.padding),
|
||||
if (_mountType.isNotEmpty && _mountType != 'Remote')
|
||||
Card(
|
||||
margin: EdgeInsets.all(0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Configuration Name'),
|
||||
const SizedBox(width: constants.padding),
|
||||
TextField(
|
||||
autofocus: true,
|
||||
controller: _mountNameController,
|
||||
keyboardType: TextInputType.text,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||
],
|
||||
onChanged: (_) => _handleChange(auth, _mountType),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_mount != null) const SizedBox(height: constants.padding),
|
||||
if (_mount != null)
|
||||
Expanded(
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: MountSettingsWidget(
|
||||
isAdd: true,
|
||||
mount: _mount!,
|
||||
settings: _settings[_mountType]!,
|
||||
showAdvanced: _showAdvanced,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_mount != null) const SizedBox(height: constants.padding),
|
||||
if (_mount != null)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
return ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final mountList = Provider.of<MountList>(context);
|
||||
|
||||
List<String> failed = [];
|
||||
if (!validateSettings(_settings[_mountType]!, failed)) {
|
||||
for (var key in failed) {
|
||||
displayErrorMessage(
|
||||
context,
|
||||
"Setting '$key' is not valid",
|
||||
List<String> failed = [];
|
||||
if (!validateSettings(
|
||||
_settings[_mountType]!,
|
||||
failed,
|
||||
)) {
|
||||
for (var key in failed) {
|
||||
displayErrorMessage(
|
||||
context,
|
||||
"Setting '$key' is not valid",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mountList.hasConfigName(
|
||||
_mountNameController.text,
|
||||
)) {
|
||||
return displayErrorMessage(
|
||||
context,
|
||||
"Configuration name '${_mountNameController.text}' already exists",
|
||||
);
|
||||
}
|
||||
|
||||
if (_mountType == "Sia" || _mountType == "S3") {
|
||||
final bucket =
|
||||
_settings[_mountType]!["${_mountType}Config"]["Bucket"]
|
||||
as String;
|
||||
if (mountList.hasBucketName(_mountType, bucket)) {
|
||||
return displayErrorMessage(
|
||||
context,
|
||||
"Bucket '$bucket' already exists",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final success = await mountList.add(
|
||||
_mountType,
|
||||
_mountType == 'Remote'
|
||||
? '${_settings[_mountType]!['RemoteConfig']['HostNameOrIp']}_${_settings[_mountType]!['RemoteConfig']['ApiPort']}'
|
||||
: _mountNameController.text,
|
||||
_settings[_mountType]!,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mountList.hasConfigName(_mountNameController.text)) {
|
||||
return displayErrorMessage(
|
||||
context,
|
||||
"Configuration name '${_mountNameController.text}' already exists",
|
||||
);
|
||||
}
|
||||
if (!success || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mountType == "Sia" || _mountType == "S3") {
|
||||
final bucket =
|
||||
_settings[_mountType]!["${_mountType}Config"]["Bucket"]
|
||||
as String;
|
||||
if (mountList.hasBucketName(_mountType, bucket)) {
|
||||
return displayErrorMessage(
|
||||
context,
|
||||
"Bucket '$bucket' already exists",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final success = await mountList.add(
|
||||
_mountType,
|
||||
_mountType == 'Remote'
|
||||
? '${_settings[_mountType]!['RemoteConfig']['HostNameOrIp']}_${_settings[_mountType]!['RemoteConfig']['ApiPort']}'
|
||||
: _mountNameController.text,
|
||||
_settings[_mountType]!,
|
||||
Navigator.pop(context);
|
||||
},
|
||||
label: const Text('Add'),
|
||||
icon: const Icon(Icons.add),
|
||||
);
|
||||
|
||||
if (!success || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
label: const Text('Add'),
|
||||
icon: const Icon(Icons.add),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleChange(String mountType) {
|
||||
void _handleChange(Auth auth, String mountType) {
|
||||
setState(() {
|
||||
final changed = _mountType != mountType;
|
||||
|
||||
@@ -204,6 +216,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
|
||||
(_mountNameController.text.isEmpty)
|
||||
? null
|
||||
: Mount(
|
||||
auth,
|
||||
MountConfig(
|
||||
name: _mountNameController.text,
|
||||
settings: _settings[mountType],
|
||||
|
94
web/repertory/lib/screens/auth_screen.dart
Normal file
94
web/repertory/lib/screens/auth_screen.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/models/auth.dart';
|
||||
|
||||
class AuthScreen extends StatefulWidget {
|
||||
final String title;
|
||||
const AuthScreen({super.key, required this.title});
|
||||
|
||||
@override
|
||||
State<AuthScreen> createState() => _AuthScreenState();
|
||||
}
|
||||
|
||||
class _AuthScreenState extends State<AuthScreen> {
|
||||
bool _enabled = true;
|
||||
final _passwordController = TextEditingController();
|
||||
final _userController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Consumer<Auth>(
|
||||
builder: (context, auth, _) {
|
||||
if (auth.authenticated) {
|
||||
Navigator.of(context).pushReplacementNamed('/');
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
'Logon to Repertory Portal',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Username'),
|
||||
controller: _userController,
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
TextField(
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(labelText: 'Password'),
|
||||
controller: _passwordController,
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
ElevatedButton(
|
||||
onPressed:
|
||||
_enabled
|
||||
? () async {
|
||||
setState(() => _enabled = false);
|
||||
await auth.authenticate(
|
||||
_userController.text,
|
||||
_passwordController.text,
|
||||
);
|
||||
setState(() => _enabled = true);
|
||||
}
|
||||
: null,
|
||||
child: const Text('Login'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setState(fn);
|
||||
}
|
||||
}
|
@@ -2,7 +2,9 @@ import 'dart:convert' show jsonDecode, jsonEncode;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/widgets/ui_settings.dart';
|
||||
|
||||
class EditSettingsScreen extends StatefulWidget {
|
||||
@@ -41,8 +43,9 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
|
||||
|
||||
Future<Map<String, dynamic>> _grabSettings() async {
|
||||
try {
|
||||
final auth = await Provider.of<Auth>(context, listen: false).createAuth();
|
||||
final response = await http.get(
|
||||
Uri.parse('${getBaseUri()}/api/v1/settings'),
|
||||
Uri.parse('${getBaseUri()}/api/v1/settings?auth=$auth'),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
|
@@ -7,6 +7,7 @@ import 'package:repertory/helpers.dart'
|
||||
getChanged,
|
||||
getSettingDescription,
|
||||
getSettingValidators;
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/settings.dart';
|
||||
@@ -622,7 +623,8 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
||||
widget.settings,
|
||||
);
|
||||
if (settings.isNotEmpty) {
|
||||
convertAllToString(settings).then((map) {
|
||||
final authProvider = Provider.of<Auth>(context, listen: false);
|
||||
convertAllToString(settings, authProvider.key).then((map) {
|
||||
map.forEach((key, value) {
|
||||
if (value is Map<String, dynamic>) {
|
||||
value.forEach((subKey, subValue) {
|
||||
|
@@ -2,6 +2,7 @@ import 'dart:convert' show jsonEncode;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/helpers.dart'
|
||||
show
|
||||
convertAllToString,
|
||||
@@ -11,6 +12,7 @@ import 'package:repertory/helpers.dart'
|
||||
getSettingDescription,
|
||||
getSettingValidators,
|
||||
trimNotEmptyValidator;
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/settings.dart';
|
||||
import 'package:settings_ui/settings_ui.dart';
|
||||
|
||||
@@ -103,13 +105,15 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
|
||||
void dispose() {
|
||||
final settings = getChanged(widget.origSettings, widget.settings);
|
||||
if (settings.isNotEmpty) {
|
||||
convertAllToString(settings)
|
||||
final authProvider = Provider.of<Auth>(context, listen: false);
|
||||
convertAllToString(settings, authProvider.key)
|
||||
.then((map) async {
|
||||
try {
|
||||
final auth = await authProvider.createAuth();
|
||||
final response = await http.put(
|
||||
Uri.parse(
|
||||
Uri.encodeFull(
|
||||
'${getBaseUri()}/api/v1/settings?data=${jsonEncode(map)}',
|
||||
'${getBaseUri()}/api/v1/settings?auth=$auth&data=${jsonEncode(map)}',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Reference in New Issue
Block a user