mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-20 13:59:12 +00:00
Added create connection
This commit is contained in:
parent
a97ae20631
commit
3acfc7c5a5
10 changed files with 411 additions and 21 deletions
|
@ -14,5 +14,17 @@
|
|||
"general": "General",
|
||||
"connection": "Connection",
|
||||
"authentication": "Authentication",
|
||||
"other": "Other"
|
||||
"other": "Other",
|
||||
"invalidPort": "Invalid port",
|
||||
"invalidPath": "Invalid path",
|
||||
"invalidIpDomain": "Invalid IP or domain",
|
||||
"ipDomainNotEmpty": "IP or domain cannot be empty",
|
||||
"nameNotEmpty": "Name cannot be empty",
|
||||
"invalidUsernamePassword": "Invalid username or password",
|
||||
"tooManyAttempts": "Too many attempts. Try again later.",
|
||||
"cantReachServer": "Can't reach server. Check connection data.",
|
||||
"sslError": "SSL error",
|
||||
"unknownError": "Unknown error",
|
||||
"connectionNotCreated": "Connection couldn't be created",
|
||||
"connecting": "Connecting..."
|
||||
}
|
|
@ -14,5 +14,17 @@
|
|||
"general": "General",
|
||||
"connection": "Conexión",
|
||||
"authentication": "Autenticación",
|
||||
"other": "Otros"
|
||||
"other": "Otros",
|
||||
"invalidPort": "Puerto no válido",
|
||||
"invalidPath": "Ruta no válida",
|
||||
"invalidIpDomain": "IP o dominio no válido",
|
||||
"ipDomainNotEmpty": "IP o dominio no puede estar vacío",
|
||||
"nameNotEmpty": "Name cannot be empty",
|
||||
"invalidUsernamePassword": "Usuario o contraseña no válidos.",
|
||||
"tooManyAttempts": "Demasiados intentos. Prueba de nuevo más tarde.",
|
||||
"cantReachServer": "No se puede alcanzar el servidor. Comprueba los datos de conexión.",
|
||||
"sslError": "Error de SSL",
|
||||
"unknownError": "Error desconocido",
|
||||
"connectionNotCreated": "No se pudo crear la conexión",
|
||||
"connecting": "Conectando..."
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
class Server {
|
||||
final String name;
|
||||
final String connectionMethod;
|
||||
final String domain;
|
||||
final String? path;
|
||||
final int? port;
|
||||
final String user;
|
||||
final String password;
|
||||
final bool defaultServer;
|
||||
final String id;
|
||||
String name;
|
||||
String connectionMethod;
|
||||
String domain;
|
||||
String? path;
|
||||
int? port;
|
||||
String user;
|
||||
String password;
|
||||
bool defaultServer;
|
||||
|
||||
const Server({
|
||||
Server({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.connectionMethod,
|
||||
required this.domain,
|
||||
|
|
|
@ -27,10 +27,87 @@ class ServersProvider with ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<bool> createServer(Server server) async {
|
||||
final saved = await saveServerIntoDb(server);
|
||||
if (saved == true) {
|
||||
if (server.defaultServer == true) {
|
||||
final defaultServer = await setDefaultServer(server);
|
||||
if (defaultServer == true) {
|
||||
_serversList.add(server);
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_serversList.add(server);
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> setDefaultServer(Server server) async {
|
||||
final updated = await setDefaultServerDb(server.id);
|
||||
if (updated == true) {
|
||||
List<Server> newServers = _serversList.map((s) {
|
||||
if (s.id == server.id) {
|
||||
s.defaultServer = true;
|
||||
return s;
|
||||
}
|
||||
else {
|
||||
s.defaultServer = false;
|
||||
return s;
|
||||
}
|
||||
}).toList();
|
||||
_serversList = newServers;
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> saveServerIntoDb(Server server) async {
|
||||
try {
|
||||
return await _dbInstance!.transaction((txn) async {
|
||||
await txn.rawInsert(
|
||||
'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer) VALUES ("${server.id}", "${server.name}", "${server.connectionMethod}", "${server.domain}", "${server.path}", ${server.port}, "${server.user}", "${server.password}", 0)',
|
||||
);
|
||||
return true;
|
||||
});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> setDefaultServerDb(String id) async {
|
||||
try {
|
||||
return await _dbInstance!.transaction((txn) async {
|
||||
await txn.rawUpdate(
|
||||
'UPDATE servers SET defaultServer = 0 WHERE defaultServer = 1',
|
||||
);
|
||||
await txn.rawUpdate(
|
||||
'UPDATE servers SET defaultServer = 1 WHERE id = "$id"',
|
||||
);
|
||||
return true;
|
||||
});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void saveFromDb(List<Map<String, dynamic>>? data) async {
|
||||
if (data != null) {
|
||||
for (var server in data) {
|
||||
final Server serverObj = Server(
|
||||
id: server['id'],
|
||||
name: server['name'],
|
||||
connectionMethod: server['connectionMethod'],
|
||||
domain: server['domain'],
|
||||
|
@ -38,10 +115,10 @@ class ServersProvider with ChangeNotifier {
|
|||
port: server['port'],
|
||||
user: server['user'],
|
||||
password: server['password'],
|
||||
defaultServer: convertFromIntToBool(server['isDefaultServer'])!,
|
||||
defaultServer: convertFromIntToBool(server['defaultServer'])!,
|
||||
);
|
||||
_serversList.add(serverObj);
|
||||
if (convertFromIntToBool(server['isDefaultServer']) == true) {
|
||||
if (convertFromIntToBool(server['defaultServer']) == true) {
|
||||
_selectedServer = serverObj;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
|
||||
class Connect extends StatelessWidget {
|
||||
const Connect({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final serversProvider = Provider.of<ServersProvider>(context);
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ Future<Map<String, dynamic>> loadDb() async {
|
|||
'adguard_home_manager.db',
|
||||
version: 1,
|
||||
onCreate: (Database db, int version) async {
|
||||
await db.execute("CREATE TABLE servers (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER)");
|
||||
await db.execute("CREATE TABLE servers (id TEXT PRIMARY KEY, name TEXT, connectionMethod TEXT, domain TEXT, path TEXT, port INTEGER, user TEXT, password TEXT, defaultServer INTEGER)");
|
||||
},
|
||||
onUpgrade: (Database db, int oldVersion, int newVersion) async {
|
||||
|
||||
|
|
47
lib/services/http_requests.dart
Normal file
47
lib/services/http_requests.dart
Normal file
|
@ -0,0 +1,47 @@
|
|||
// ignore_for_file: depend_on_referenced_packages
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:adguard_home_manager/models/server.dart';
|
||||
|
||||
Future login(Server server) async {
|
||||
try {
|
||||
final result = await http.post(
|
||||
Uri.parse("${server.connectionMethod}://${server.domain}${server.path ?? ""}${server.port != null ? ':${server.port}' : ""}/control/login"),
|
||||
body: jsonEncode({
|
||||
"name": server.user,
|
||||
"password": server.password
|
||||
})
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
return {'result': 'success'};
|
||||
}
|
||||
else if (result.statusCode == 400) {
|
||||
return {
|
||||
'result': 'error',
|
||||
'message': 'invalid_username_password'
|
||||
};
|
||||
}
|
||||
else if (result.statusCode == 429) {
|
||||
return {
|
||||
'result': 'error',
|
||||
'message': 'many_attempts'
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {'result': 'error'};
|
||||
}
|
||||
} on SocketException {
|
||||
return {'result': 'no_connection'};
|
||||
} on TimeoutException {
|
||||
return {'result': 'no_connection'};
|
||||
} on HandshakeException {
|
||||
return {'result': 'ssl_error'};
|
||||
} catch (e) {
|
||||
return {'result': 'error'};
|
||||
}
|
||||
}
|
|
@ -1,8 +1,15 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/custom_radio_toggle.dart';
|
||||
|
||||
import 'package:adguard_home_manager/services/http_requests.dart';
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
import 'package:adguard_home_manager/models/server.dart';
|
||||
import 'package:adguard_home_manager/config/system_overlay_style.dart';
|
||||
|
||||
class AddServerModal extends StatefulWidget {
|
||||
|
@ -13,7 +20,10 @@ class AddServerModal extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _AddServerModalState extends State<AddServerModal> {
|
||||
final uuid = const Uuid();
|
||||
|
||||
final TextEditingController nameController = TextEditingController();
|
||||
String? nameError;
|
||||
|
||||
String connectionType = "http";
|
||||
|
||||
|
@ -32,6 +42,10 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
|
||||
bool defaultServer = false;
|
||||
|
||||
bool allDataValid = false;
|
||||
|
||||
bool isConnecting = false;
|
||||
|
||||
Widget sectionLabel(String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
@ -53,12 +67,14 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
required TextEditingController controller,
|
||||
String? error,
|
||||
required IconData icon,
|
||||
TextInputType? keyboardType
|
||||
TextInputType? keyboardType,
|
||||
Function(String)? onChanged,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(icon),
|
||||
errorText: error,
|
||||
|
@ -74,8 +90,168 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
);
|
||||
}
|
||||
|
||||
void checkDataValid() {
|
||||
if (
|
||||
nameController.text != '' &&
|
||||
ipDomainController.text != '' &&
|
||||
ipDomainError == null &&
|
||||
pathError == null &&
|
||||
portError == null &&
|
||||
userController.text != '' &&
|
||||
passwordController.text != ''
|
||||
) {
|
||||
setState(() {
|
||||
allDataValid = true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
allDataValid = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void validatePort(String? value) {
|
||||
if (value != null && value != '') {
|
||||
if (int.tryParse(value) != null && int.parse(value) <= 65535) {
|
||||
setState(() {
|
||||
portError = null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
portError = AppLocalizations.of(context)!.invalidPort;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
portError = null;
|
||||
});
|
||||
}
|
||||
checkDataValid();
|
||||
}
|
||||
|
||||
void validateSubroute(String? value) {
|
||||
if (value != null && value != '') {
|
||||
RegExp subrouteRegexp = RegExp(r'^\/\b([A-Za-z0-9_\-~/]*)[^\/|\.|\:]$');
|
||||
if (subrouteRegexp.hasMatch(value) == true) {
|
||||
setState(() {
|
||||
pathError = null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
pathError = AppLocalizations.of(context)!.invalidPath;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
pathError = null;
|
||||
});
|
||||
}
|
||||
checkDataValid();
|
||||
}
|
||||
|
||||
void validateAddress(String? value) {
|
||||
if (value != null && value != '') {
|
||||
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$');
|
||||
RegExp domain = RegExp(r'^((?!-))(xn--)?[a-z0-9][a-z0-9-_]{0,61}[a-z0-9]{0,1}\.(xn--)?([a-z0-9\-]{1,61}|[a-z0-9-]{1,30}\.[a-z]{2,})$');
|
||||
if (ipAddress.hasMatch(value) == true || domain.hasMatch(value) == true) {
|
||||
setState(() {
|
||||
ipDomainError = null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
ipDomainError = AppLocalizations.of(context)!.invalidIpDomain;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
ipDomainError = AppLocalizations.of(context)!.ipDomainNotEmpty;
|
||||
});
|
||||
}
|
||||
checkDataValid();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final serversProvider = Provider.of<ServersProvider>(context, listen: false);
|
||||
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
void connect() async {
|
||||
final Server serverObj = Server(
|
||||
id: uuid.v4(),
|
||||
name: nameController.text,
|
||||
connectionMethod: connectionType,
|
||||
domain: ipDomainController.text,
|
||||
port: int.parse(portController.text),
|
||||
user: userController.text,
|
||||
password: passwordController.text,
|
||||
defaultServer: defaultServer
|
||||
);
|
||||
final result = await login(serverObj);
|
||||
if (result['result'] == 'success') {
|
||||
final serverCreated = await serversProvider.createServer(serverObj);
|
||||
if (serverCreated == true) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.connectionNotCreated),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (result['result'] == 'error' && result['message'] == 'invalid_username_password') {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.invalidUsernamePassword),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (result['result'] == 'error' && result['message'] == 'many_attempts') {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.tooManyAttempts),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (result['result'] == 'error' && result['message'] == 'no_connection') {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.cantReachServer),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (result['result'] == 'error' && result['message'] == 'ssl_error') {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.sslError),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.unknownError),
|
||||
backgroundColor: Colors.red,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Scaffold(
|
||||
|
@ -89,7 +265,9 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
padding: const EdgeInsets.only(right: 10),
|
||||
child: IconButton(
|
||||
tooltip: AppLocalizations.of(context)!.connect,
|
||||
onPressed: () => {},
|
||||
onPressed: allDataValid == true
|
||||
? () => connect()
|
||||
: null,
|
||||
icon: const Icon(Icons.login_rounded)
|
||||
),
|
||||
),
|
||||
|
@ -102,7 +280,11 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
textField(
|
||||
label: AppLocalizations.of(context)!.name,
|
||||
controller: nameController,
|
||||
icon: Icons.badge_rounded
|
||||
icon: Icons.badge_rounded,
|
||||
error: nameError,
|
||||
onChanged: (value) => value == ''
|
||||
? setState(() => nameError = AppLocalizations.of(context)!.nameNotEmpty)
|
||||
: setState(() => nameError = null)
|
||||
),
|
||||
sectionLabel(AppLocalizations.of(context)!.connection),
|
||||
Row(
|
||||
|
@ -128,14 +310,16 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
controller: ipDomainController,
|
||||
icon: Icons.link_rounded,
|
||||
error: ipDomainError,
|
||||
keyboardType: TextInputType.url
|
||||
keyboardType: TextInputType.url,
|
||||
onChanged: validateAddress
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
textField(
|
||||
label: AppLocalizations.of(context)!.path,
|
||||
controller: pathController,
|
||||
icon: Icons.route_rounded,
|
||||
error: pathError
|
||||
error: pathError,
|
||||
onChanged: validateSubroute
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
textField(
|
||||
|
@ -143,20 +327,23 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
controller: portController,
|
||||
icon: Icons.numbers_rounded,
|
||||
error: portError,
|
||||
keyboardType: TextInputType.number
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: validatePort
|
||||
),
|
||||
sectionLabel(AppLocalizations.of(context)!.authentication),
|
||||
textField(
|
||||
label: AppLocalizations.of(context)!.username,
|
||||
controller: userController,
|
||||
icon: Icons.person_rounded,
|
||||
onChanged: (_) => checkDataValid()
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
textField(
|
||||
label: AppLocalizations.of(context)!.password,
|
||||
controller: passwordController,
|
||||
icon: Icons.lock_rounded,
|
||||
keyboardType: TextInputType.visiblePassword
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
onChanged: (_) => checkDataValid()
|
||||
),
|
||||
sectionLabel(AppLocalizations.of(context)!.other),
|
||||
Material(
|
||||
|
@ -187,6 +374,39 @@ class _AddServerModalState extends State<AddServerModal> {
|
|||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedOpacity(
|
||||
opacity: isConnecting == true ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeInOut,
|
||||
child: IgnorePointer(
|
||||
ignoring: isConnecting == true ? false : true,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Container(
|
||||
width: mediaQuery.size.width,
|
||||
height: mediaQuery.size.height,
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.7),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.connecting,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 26
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
14
pubspec.lock
14
pubspec.lock
|
@ -43,6 +43,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -355,6 +362,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -42,6 +42,7 @@ dependencies:
|
|||
dynamic_color: ^1.5.4
|
||||
animations: ^2.0.5
|
||||
device_info_plus: ^4.1.2
|
||||
uuid: ^3.0.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Add table
Reference in a new issue