diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 90d01d8..ee98548 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -355,5 +355,25 @@ "settingsNotSaved": "Settings couldn't be saved", "restoringConfig": "Restoring configuration...", "configRestored": "Configuration restored successfully", - "configNotRestored": "The configuration couldn't be restored" + "configNotRestored": "The configuration couldn't be restored", + "dhcpStatic": "DHCP static leases", + "noDhcpStaticLeases": "No DHCP static leases found", + "deleting": "Deleting...", + "staticLeaseDeleted": "DHCP static lease deleted successfully", + "staticLeaseNotDeleted": "The DHCP static lease could not be deleted", + "deleteStaticLease": "Delete static lease", + "deleteStaticLeaseDescription": "The DHCP static lease will be deleted. This action cannot be reverted.", + "addStaticLease": "Add static lease", + "macAddress": "MAC address", + "macAddressNotValid": "MAC address not valid", + "hostName": "Host name", + "hostNameError": "Host name cannot be empty", + "creating": "Creating...", + "staticLeaseCreated": "DHCP static lease created successfully", + "staticLeaseNotCreated": "The DHCP static lease couldn't be created", + "staticLeaseExists": "The DHCP static lease already exists", + "restoreLeases": "Restore leases", + "restoringLeases": "Restoring leases...", + "leasesRestored": "Leases restored successfully", + "leasesNotRestored": "The leases couldn't be restored" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 708f841..6891d92 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -355,5 +355,25 @@ "settingsNotSaved": "No se ha podido guardar la configuración", "restoringConfig": "Restaurando configuración...", "configRestored": "Configuración restaurada correctamente", - "configNotRestored": "La configuración no ha podido ser restaurada" + "configNotRestored": "La configuración no ha podido ser restaurada", + "dhcpStatic": "Asignaciones DHCP estáticas", + "noDhcpStaticLeases": "No se han encontrado asignaciones DHCP estáticas", + "deleting": "Eliminando...", + "staticLeaseDeleted": "Asignación DHCP estática eliminada correctamente", + "staticLeaseNotDeleted": "La asignación DHCP estática no pudo ser eliminada", + "deleteStaticLease": "Eliminar asignación estática", + "deleteStaticLeaseDescription": "La asignación DHCP estática será eliminada. Esta acción no puede ser revertida.", + "addStaticLease": "Añadir asignación estática", + "macAddress": "Dirección MAC", + "macAddressNotValid": "Dirección MAC no válida", + "hostName": "Nombre del host", + "hostNameError": "Nombre del host no puede estar vacío", + "creating": "Creando...", + "staticLeaseCreated": "Asignación DHCP estática creada correctamente", + "staticLeaseNotCreated": "No se ha podido crear la asignación DHCP estática", + "staticLeaseExists": "La asignación DHCP estática ya existe", + "restoreLeases": "Restaurar asignaciones", + "restoringLeases": "Restaurando asignaciones...", + "leasesRestored": "Asignaciones restauradas correctamente", + "leasesNotRestored": "Las asignaciones no pudieron ser restauradas" } \ No newline at end of file diff --git a/lib/models/dhcp.dart b/lib/models/dhcp.dart index 768339a..4910e68 100644 --- a/lib/models/dhcp.dart +++ b/lib/models/dhcp.dart @@ -68,8 +68,8 @@ class DhcpStatus { String interfaceName; IpVersion v4; IpVersion v6; - List leases; - List staticLeases; + List leases; + List staticLeases; bool enabled; DhcpStatus({ @@ -85,8 +85,8 @@ class DhcpStatus { interfaceName: json["interface_name"], v4: IpVersion.fromJson(json["v4"]), v6: IpVersion.fromJson(json["v6"]), - leases: List.from(json["leases"].map((x) => x)), - staticLeases: List.from(json["static_leases"].map((x) => x)), + leases: List.from(json["leases"].map((x) => Lease.fromJson(x))), + staticLeases: List.from(json["static_leases"].map((x) => Lease.fromJson(x))), enabled: json["enabled"], ); @@ -94,8 +94,8 @@ class DhcpStatus { "interface_name": interfaceName, "v4": v4.toJson(), "v6": v6.toJson(), - "leases": List.from(leases.map((x) => x)), - "static_leases": List.from(staticLeases.map((x) => x)), + "leases": List.from(leases.map((x) => x)), + "static_leases": List.from(staticLeases.map((x) => x)), "enabled": enabled, }; } @@ -130,4 +130,28 @@ class IpVersion { "range_end": rangeEnd, "lease_duration": leaseDuration, }; -} \ No newline at end of file +} + +class Lease { + final String mac; + final String hostname; + final String ip; + + Lease({ + required this.mac, + required this.hostname, + required this.ip, + }); + + factory Lease.fromJson(Map json) => Lease( + mac: json["mac"], + hostname: json["hostname"], + ip: json["ip"], + ); + + Map toJson() => { + "mac": mac, + "hostname": hostname, + "ip": ip, + }; +} diff --git a/lib/providers/servers_provider.dart b/lib/providers/servers_provider.dart index 0892dfe..26fa5ea 100644 --- a/lib/providers/servers_provider.dart +++ b/lib/providers/servers_provider.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:sqflite/sqflite.dart'; import 'package:adguard_home_manager/models/filtering.dart'; +import 'package:adguard_home_manager/models/dhcp.dart'; import 'package:adguard_home_manager/models/filtering_status.dart'; import 'package:adguard_home_manager/models/clients_allowed_blocked.dart'; import 'package:adguard_home_manager/models/clients.dart'; @@ -31,6 +32,11 @@ class ServersProvider with ChangeNotifier { data: null ); + final DhcpModel _dhcp = DhcpModel( + loadStatus: 0, // 0 = loading, 1 = loaded, 2 = error + data: null + ); + FilteringStatus? _filteringStatus; List get serversList { @@ -61,6 +67,10 @@ class ServersProvider with ChangeNotifier { return _filtering; } + DhcpModel get dhcp { + return _dhcp; + } + void setDbInstance(Database db) { _dbInstance = db; } @@ -134,6 +144,18 @@ class ServersProvider with ChangeNotifier { _filtering.data!.blockedServices = blockedServices; notifyListeners(); } + + void setDhcpData(DhcpData data) { + _dhcp.data = data; + notifyListeners(); + } + + void setDhcpLoadStatus(int status, bool notify) { + _dhcp.loadStatus = status; + if (notify == true) { + notifyListeners(); + } + } Future createServer(Server server) async { final saved = await saveServerIntoDb(server); diff --git a/lib/screens/settings/dhcp/add_static_lease_modal.dart b/lib/screens/settings/dhcp/add_static_lease_modal.dart new file mode 100644 index 0000000..10793ab --- /dev/null +++ b/lib/screens/settings/dhcp/add_static_lease_modal.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/models/dhcp.dart'; + +class AddStaticLeaseModal extends StatefulWidget { + final void Function(Lease) onConfirm; + + const AddStaticLeaseModal({ + Key? key, + required this.onConfirm, + }) : super(key: key); + + @override + State createState() => _AddStaticLeaseModalState(); +} + +class _AddStaticLeaseModalState extends State { + final TextEditingController macController = TextEditingController(); + String? macError; + final TextEditingController ipController = TextEditingController(); + String? ipError; + final TextEditingController hostNameController = TextEditingController(); + String? hostNameError; + + bool validData = false; + + void validateMac(String value) { + final RegExp macRegex = RegExp(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$'); + if (macRegex.hasMatch(value)) { + setState(() => macError = null); + } + else { + setState(() => macError = AppLocalizations.of(context)!.macAddressNotValid); + } + validateData(); + } + + void validateIp(String value) { + RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$'); + if (ipAddress.hasMatch(value) == true) { + setState(() => ipError = null); + } + else { + setState(() => ipError = AppLocalizations.of(context)!.ipNotValid); + } + validateData(); + } + + void validateData() { + if ( + macController.text != '' && + macError == null && + ipController.text != '' && + ipError == null && + hostNameController.text != '' && + hostNameError == null + ) { + setState(() => validData = true); + } + else { + setState(() => validData = false); + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: MediaQuery.of(context).viewInsets, + child: Container( + height: MediaQuery.of(context).viewInsets.bottom > 0 + ? MediaQuery.of(context).size.height-MediaQuery.of(context).viewInsets.bottom-25 + : 550, + decoration: BoxDecoration( + color: Theme.of(context).dialogBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(28), + topRight: Radius.circular(28) + ) + ), + child: Column( + children: [ + SizedBox( + height: MediaQuery.of(context).viewInsets.bottom > 0 + ? MediaQuery.of(context).size.height-425 + : MediaQuery.of(context).size.height-400, + child: ListView( + shrinkWrap: true, + primary: false, + children: [ + const Padding( + padding: EdgeInsets.only(top: 28), + child: Icon( + Icons.add, + size: 26, + ), + ), + const SizedBox(height: 20), + Text( + AppLocalizations.of(context)!.addStaticLease, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 24 + ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 28), + child: TextFormField( + controller: macController, + onChanged: validateMac, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.smartphone_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: macError, + labelText: AppLocalizations.of(context)!.macAddress, + ), + ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 28), + child: TextFormField( + controller: ipController, + onChanged: validateIp, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.link_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: ipError, + labelText: AppLocalizations.of(context)!.ipAddress, + ), + ), + ), + const SizedBox(height: 30), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 28), + child: TextFormField( + controller: hostNameController, + onChanged: (value) { + if (value != '') { + setState(() => hostNameError = null); + } + else { + setState(() => hostNameError = AppLocalizations.of(context)!.hostNameError); + } + validateData(); + }, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.badge_rounded), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: hostNameError, + labelText: AppLocalizations.of(context)!.hostName, + ), + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(AppLocalizations.of(context)!.cancel), + ), + const SizedBox(width: 20), + TextButton( + onPressed: validData == true + ? () { + Navigator.pop(context); + widget.onConfirm( + Lease( + mac: macController.text, + hostname: hostNameController.text, + ip: ipController.text + ) + ); + } + : null, + child: Text( + AppLocalizations.of(context)!.confirm, + style: TextStyle( + color: validData == true + ? Theme.of(context).primaryColor + : Colors.grey + ), + ), + ), + ], + ), + ) + ], + ), + ) + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/dhcp/delete_static_lease_modal.dart b/lib/screens/settings/dhcp/delete_static_lease_modal.dart new file mode 100644 index 0000000..630079b --- /dev/null +++ b/lib/screens/settings/dhcp/delete_static_lease_modal.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class DeleteStaticLeaseModal extends StatelessWidget { + final void Function() onConfirm; + + const DeleteStaticLeaseModal({ + Key? key, + required this.onConfirm + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Column( + children: [ + const Icon( + Icons.delete_rounded, + size: 26, + ), + const SizedBox(height: 20), + Text( + AppLocalizations.of(context)!.deleteStaticLease, + textAlign: TextAlign.center, + ) + ], + ), + content: Text(AppLocalizations.of(context)!.deleteStaticLeaseDescription), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(AppLocalizations.of(context)!.cancel) + ), + TextButton( + onPressed: () { + onConfirm(); + Navigator.pop(context); + }, + child: Text(AppLocalizations.of(context)!.confirm) + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/dhcp/dhcp.dart b/lib/screens/settings/dhcp/dhcp.dart index b7f972d..c66e2a7 100644 --- a/lib/screens/settings/dhcp/dhcp.dart +++ b/lib/screens/settings/dhcp/dhcp.dart @@ -6,6 +6,7 @@ import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:adguard_home_manager/screens/settings/section_label.dart'; +import 'package:adguard_home_manager/screens/settings/dhcp/dhcp_static.dart'; import 'package:adguard_home_manager/screens/settings/dhcp/select_interface_modal.dart'; import 'package:adguard_home_manager/functions/snackbar.dart'; @@ -45,8 +46,6 @@ class DhcpWidget extends StatefulWidget { } class _DhcpWidgetState extends State { - DhcpModel dhcp = DhcpModel(loadStatus: 0); - NetworkInterface? selectedInterface; bool enabled = false; @@ -72,14 +71,15 @@ class _DhcpWidgetState extends State { bool dataValid = false; void loadDhcpStatus() async { + widget.serversProvider.setDhcpLoadStatus(0, false); + final result = await getDhcpData(server: widget.serversProvider.selectedServer!); if (mounted) { if (result['result'] == 'success') { + widget.serversProvider.setDhcpLoadStatus(1, true); + widget.serversProvider.setDhcpData(result['data']); setState(() { - dhcp.loadStatus = 1; - dhcp.data = result['data']; - if (result['data'].dhcpStatus.interfaceName != '') { selectedInterface = result['data'].networkInterfaces.firstWhere((interface) => interface.name == result['data'].dhcpStatus.interfaceName); @@ -94,7 +94,7 @@ class _DhcpWidgetState extends State { }); } else { - setState(() => dhcp.loadStatus = 2); + widget.serversProvider.setDhcpLoadStatus(2, true); } } checkDataValid(); @@ -287,6 +287,41 @@ class _DhcpWidgetState extends State { }); } + void restoreLeases() async { + Future.delayed(const Duration(seconds: 0), () async { + ProcessModal processModal = ProcessModal(context: context); + processModal.open(AppLocalizations.of(context)!.restoringLeases); + + final result = await restoreAllLeases(server: serversProvider.selectedServer!); + + processModal.close(); + + if (result['result'] == 'success') { + DhcpData data = serversProvider.dhcp.data!; + data.dhcpStatus.staticLeases = []; + data.dhcpStatus.leases = []; + serversProvider.setDhcpData(data); + + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.leasesRestored, + color: Colors.green + ); + } + else { + appConfigProvider.addLog(result['log']); + + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.leasesNotRestored, + color: Colors.red + ); + } + }); + } + void selectInterface() { Future.delayed(const Duration(seconds: 0), () { showFlexibleBottomSheet( @@ -298,7 +333,7 @@ class _DhcpWidgetState extends State { anchors: [0.95], context: context, builder: (ctx, controller, offset) => SelectInterfaceModal( - interfaces: dhcp.data!.networkInterfaces, + interfaces: serversProvider.dhcp.data!.networkInterfaces, scrollController: controller, onSelect: (interface) => setState(() { clearAll(); @@ -311,7 +346,7 @@ class _DhcpWidgetState extends State { } Widget generateBody() { - switch (dhcp.loadStatus) { + switch (serversProvider.dhcp.loadStatus) { case 0: return SizedBox( width: double.maxFinite, @@ -564,7 +599,36 @@ class _DhcpWidgetState extends State { keyboardType: TextInputType.number, ), ), - ] + ], + const SizedBox(height: 20), + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => DhcpStatic( + items: serversProvider.dhcp.data!.dhcpStatus.staticLeases + ) + )); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + AppLocalizations.of(context)!.dhcpStatic, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16 + ), + ), + const Icon(Icons.arrow_forward_rounded) + ], + ), + ), + ), + ), ], ); } @@ -589,7 +653,7 @@ class _DhcpWidgetState extends State { ElevatedButton( onPressed: selectInterface, child: Text(AppLocalizations.of(context)!.selectInterface) - ) + ), ], ); } @@ -647,6 +711,16 @@ class _DhcpWidgetState extends State { ], ) ), + PopupMenuItem( + onTap: restoreLeases, + child: Row( + children: [ + const Icon(Icons.settings_backup_restore_rounded), + const SizedBox(width: 10), + Text(AppLocalizations.of(context)!.restoreLeases) + ], + ) + ), PopupMenuItem( onTap: restoreConfig, child: Row( @@ -656,7 +730,7 @@ class _DhcpWidgetState extends State { Text(AppLocalizations.of(context)!.restoreConfiguration) ], ) - ) + ), ] ), const SizedBox(width: 10) diff --git a/lib/screens/settings/dhcp/dhcp_static.dart b/lib/screens/settings/dhcp/dhcp_static.dart new file mode 100644 index 0000000..c9d2e72 --- /dev/null +++ b/lib/screens/settings/dhcp/dhcp_static.dart @@ -0,0 +1,168 @@ +// ignore_for_file: use_build_context_synchronously + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:animations/animations.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/screens/settings/dhcp/delete_static_lease_modal.dart'; +import 'package:adguard_home_manager/screens/settings/dhcp/add_static_lease_modal.dart'; + +import 'package:adguard_home_manager/providers/app_config_provider.dart'; +import 'package:adguard_home_manager/functions/snackbar.dart'; +import 'package:adguard_home_manager/services/http_requests.dart'; +import 'package:adguard_home_manager/classes/process_modal.dart'; +import 'package:adguard_home_manager/models/dhcp.dart'; +import 'package:adguard_home_manager/providers/servers_provider.dart'; + +class DhcpStatic extends StatelessWidget { + final List items; + + const DhcpStatic({ + Key? key, + required this.items, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final serversProvider = Provider.of(context); + final appConfigProvider = Provider.of(context); + + void deleteLease(Lease lease) async { + ProcessModal processModal = ProcessModal(context: context); + processModal.open(AppLocalizations.of(context)!.deleting); + + final result = await deleteStaticLease(server: serversProvider.selectedServer!, data: { + "mac": lease.mac, + "ip": lease.ip, + "hostname": lease.hostname + }); + + processModal.close(); + + if (result['result'] == 'success') { + DhcpData data = serversProvider.dhcp.data!; + data.dhcpStatus.staticLeases = data.dhcpStatus.staticLeases.where((l) => l.mac != lease.mac).toList(); + serversProvider.setDhcpData(data); + + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.staticLeaseDeleted, + color: Colors.green + ); + } + else { + appConfigProvider.addLog(result['log']); + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.staticLeaseNotDeleted, + color: Colors.red + ); + } + } + + void createLease(Lease lease) async { + ProcessModal processModal = ProcessModal(context: context); + processModal.open(AppLocalizations.of(context)!.creating); + + final result = await createStaticLease(server: serversProvider.selectedServer!, data: { + "mac": lease.mac, + "ip": lease.ip, + "hostname": lease.hostname, + }); + + processModal.close(); + + if (result['result'] == 'success') { + DhcpData data = serversProvider.dhcp.data!; + data.dhcpStatus.staticLeases.add(lease); + serversProvider.setDhcpData(data); + + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.staticLeaseCreated, + color: Colors.green + ); + } + else if (result['result'] == 'error' && result['message'] == 'already_exists' ) { + appConfigProvider.addLog(result['log']); + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.staticLeaseExists, + color: Colors.red + ); + } + else { + appConfigProvider.addLog(result['log']); + showSnacbkar( + context: context, + appConfigProvider: appConfigProvider, + label: AppLocalizations.of(context)!.staticLeaseNotCreated, + color: Colors.red + ); + } + } + + void openAddStaticLease() { + showModalBottomSheet( + context: context, + builder: (context) => AddStaticLeaseModal( + onConfirm: createLease + ), + backgroundColor: Colors.transparent, + isScrollControlled: true + ); + } + + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.dhcpStatic), + ), + body: items.isNotEmpty + ? ListView.builder( + padding: const EdgeInsets.only(top: 0), + itemCount: items.length, + itemBuilder: (context, index) => ListTile( + isThreeLine: true, + title: Text(items[index].ip), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(items[index].mac), + Text(items[index].hostname), + ], + ), + trailing: IconButton( + onPressed: () { + showModal( + context: context, + builder: (context) => DeleteStaticLeaseModal( + onConfirm: () => deleteLease(items[index]) + ) + ); + }, + icon: const Icon(Icons.delete) + ), + ), + ) + : Center( + child: Text( + AppLocalizations.of(context)!.noDhcpStaticLeases, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.grey, + fontSize: 22 + ), + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: openAddStaticLease, + child: const Icon(Icons.add), + ), + ); + } +} \ No newline at end of file diff --git a/lib/services/http_requests.dart b/lib/services/http_requests.dart index ab2556d..98f57ae 100644 --- a/lib/services/http_requests.dart +++ b/lib/services/http_requests.dart @@ -1227,4 +1227,118 @@ Future resetDhcpConfig({ else { return result; } +} + +Future deleteStaticLease({ + required Server server, + required Map data +}) async { + final result = await apiRequest( + urlPath: '/dhcp/remove_static_lease', + method: 'post', + server: server, + body: data, + type: 'remove_static_lease' + ); + + if (result['hasResponse'] == true) { + if (result['statusCode'] == 200) { + return {'result': 'success'}; + } + else { + return { + 'result': 'error', + 'log': AppLog( + type: 'remove_static_lease', + dateTime: DateTime.now(), + message: 'error_code_not_expected', + statusCode: result['statusCode'].toString(), + resBody: result['body'], + ) + }; + } + } + else { + return result; + } +} + +Future createStaticLease({ + required Server server, + required Map data +}) async { + final result = await apiRequest( + urlPath: '/dhcp/add_static_lease', + method: 'post', + server: server, + body: data, + type: 'add_static_lease' + ); + + if (result['hasResponse'] == true) { + if (result['statusCode'] == 200) { + return {'result': 'success'}; + } + else if (result['statusCode'] == 400 && result['body'].contains('static lease already exists')) { + return { + 'result': 'error', + 'message': 'already_exists', + 'log': AppLog( + type: 'add_static_lease', + dateTime: DateTime.now(), + message: 'already_exists', + statusCode: result['statusCode'].toString(), + resBody: result['body'], + ) + }; + } + else { + return { + 'result': 'error', + 'log': AppLog( + type: 'add_static_lease', + dateTime: DateTime.now(), + message: 'error_code_not_expected', + statusCode: result['statusCode'].toString(), + resBody: result['body'], + ) + }; + } + } + else { + return result; + } +} + +Future restoreAllLeases({ + required Server server, +}) async { + final result = await apiRequest( + urlPath: '/dhcp/reset_leases', + method: 'post', + server: server, + body: {}, + type: 'restore_all_leases' + ); + + if (result['hasResponse'] == true) { + if (result['statusCode'] == 200) { + return {'result': 'success'}; + } + else { + return { + 'result': 'error', + 'log': AppLog( + type: 'restore_all_leases', + dateTime: DateTime.now(), + message: 'error_code_not_expected', + statusCode: result['statusCode'].toString(), + resBody: result['body'], + ) + }; + } + } + else { + return result; + } } \ No newline at end of file