Added ignored domains list config

This commit is contained in:
Juan Gilsanz Polo 2024-01-24 15:14:19 +01:00
parent 211eab9f44
commit c12e8c5ad3
6 changed files with 218 additions and 31 deletions

View file

@ -727,5 +727,7 @@
"dnsCacheNumber": "DNS cache size must be a number",
"errors": "Errors",
"redirectHttpsWarning": "If you have enabled \"Redirect to HTTPS automatically\" on your AdGuard Home server, you must select an HTTPS connection and use the HTTPS port of your server.",
"logsSettingsDescription": "Configure query logs"
"logsSettingsDescription": "Configure query logs",
"ignoredDomains": "Ignored domains",
"noIgnoredDomainsAdded": "No domains to ignore added"
}

View file

@ -727,5 +727,7 @@
"dnsCacheNumber": "El tamaño de caché de DNS debe ser un número",
"errors": "Errores",
"redirectHttpsWarning": "Si tienes activado \"Redireccionar a HTTPS automáticamente\" en tu servidor AdGuard Home, debes seleccionar una conexión HTTPS y utilizar el puerto de HTTPS de tu servidor.",
"logsSettingsDescription": "Configura los registros de peticiones"
"logsSettingsDescription": "Configura los registros de peticiones",
"ignoredDomains": "Dominios ignorados",
"noIgnoredDomainsAdded": "No hay añadidos dominios para ignorar"
}

View file

@ -0,0 +1,27 @@
class QueryLogConfig {
final List<String>? ignored;
final int? interval;
final bool? enabled;
final bool? anonymizeClientIp;
QueryLogConfig({
this.ignored,
this.interval,
this.enabled,
this.anonymizeClientIp,
});
factory QueryLogConfig.fromJson(Map<String, dynamic> json) => QueryLogConfig(
ignored: json["ignored"] == null ? [] : List<String>.from(json["ignored"]!.map((x) => x)),
interval: json["interval"],
enabled: json["enabled"],
anonymizeClientIp: json["anonymize_client_ip"],
);
Map<String, dynamic> toJson() => {
"ignored": ignored == null ? [] : List<dynamic>.from(ignored!.map((x) => x)),
"interval": interval,
"enabled": enabled,
"anonymize_client_ip": anonymizeClientIp,
};
}

View file

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/settings/logs_settings/logs_settings.dart';
import 'package:adguard_home_manager/widgets/section_label.dart';
import 'package:adguard_home_manager/widgets/custom_checkbox_list_tile.dart';
class LogsConfigOptions extends StatelessWidget {
@ -13,6 +16,8 @@ class LogsConfigOptions extends StatelessWidget {
final void Function(double?) updateRetentionTime;
final void Function() onClear;
final void Function() onConfirm;
final List<DomainListItemController> ignoredDomainsControllers;
final void Function(List<DomainListItemController>) updateIgnoredDomainsControllers;
const LogsConfigOptions({
super.key,
@ -24,11 +29,15 @@ class LogsConfigOptions extends StatelessWidget {
required this.retentionTime,
required this.updateRetentionTime,
required this.onClear,
required this.onConfirm
required this.onConfirm,
required this.ignoredDomainsControllers,
required this.updateIgnoredDomainsControllers
});
@override
Widget build(BuildContext context) {
const Uuid uuid = Uuid();
final List<String> dropdownItemTranslation = [
AppLocalizations.of(context)!.hours6,
AppLocalizations.of(context)!.hours24,
@ -37,7 +46,28 @@ class LogsConfigOptions extends StatelessWidget {
AppLocalizations.of(context)!.days90,
];
return Column(
void validateDomain(String value, String id) {
final domainRegex = RegExp(r'^([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+$');
bool error = false;
if (domainRegex.hasMatch(value)) {
error = false;
}
else {
error = true;
}
updateIgnoredDomainsControllers(
ignoredDomainsControllers.map((entry) {
if (entry.id != id) return entry;
return DomainListItemController(
id: id,
controller: entry.controller,
error: error
);
}).toList()
);
}
return ListView(
children: [
const SizedBox(height: 16),
Padding(
@ -100,13 +130,84 @@ class LogsConfigOptions extends StatelessWidget {
borderRadius: BorderRadius.circular(20),
),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: onClear,
icon: const Icon(Icons.delete_rounded),
label: Text(AppLocalizations.of(context)!.clearLogs),
Padding(
padding: const EdgeInsets.only(top: 24, bottom: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SectionLabel(
label: AppLocalizations.of(context)!.ignoredDomains,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 0),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: IconButton(
onPressed: () => updateIgnoredDomainsControllers([
...ignoredDomainsControllers,
DomainListItemController(
id: uuid.v4(),
controller: TextEditingController(),
error: false
),
]),
icon: const Icon(Icons.add)
),
)
],
),
),
if (ignoredDomainsControllers.isNotEmpty) ...ignoredDomainsControllers.map((controller) => Padding(
padding: const EdgeInsets.only(
top: 12, bottom: 12, left: 24, right: 10
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: TextFormField(
controller: controller.controller,
onChanged: (v) => validateDomain(v, controller.id),
decoration: InputDecoration(
prefixIcon: const Icon(Icons.link_rounded),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
labelText: AppLocalizations.of(context)!.domain,
errorText: controller.error
? AppLocalizations.of(context)!.invalidDomain
: null
),
),
),
const SizedBox(width: 12),
Padding(
padding: controller.error
? const EdgeInsets.only(bottom: 24)
: const EdgeInsets.all(0),
child: IconButton(
onPressed: () => updateIgnoredDomainsControllers(
ignoredDomainsControllers.where((e) => e.id != controller.id).toList()
),
icon: const Icon(Icons.remove_circle_outline_outlined)
),
)
],
),
)),
if (ignoredDomainsControllers.isEmpty) Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Text(
AppLocalizations.of(context)!.noIgnoredDomainsAdded,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
],
);
}
}

View file

@ -1,15 +1,29 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/settings/logs_settings/config_widgets.dart';
import 'package:adguard_home_manager/models/querylog_config.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class DomainListItemController {
final String id;
final TextEditingController controller;
bool error;
DomainListItemController({
required this.id,
required this.controller,
required this.error
});
}
class LogsSettings extends StatefulWidget {
const LogsSettings({super.key});
@ -18,9 +32,12 @@ class LogsSettings extends StatefulWidget {
}
class _LogsSettingsState extends State<LogsSettings> {
final Uuid uuid = const Uuid();
bool generalSwitch = false;
bool anonymizeClientIp = false;
double? retentionTime;
List<DomainListItemController> _ignoredDomainsControllers = [];
List<int> retentionItems = [
21600000,
@ -37,14 +54,22 @@ class _LogsSettingsState extends State<LogsSettings> {
final result = await serversProvider.apiClient2!.getQueryLogInfo();
if (mounted) {
if (!mounted) return;
if (result.successful == true) {
final data = result.content as QueryLogConfig;
setState(() {
generalSwitch = result.content['enabled'];
anonymizeClientIp = result.content['anonymize_client_ip'];
retentionTime = result.content['interval'] != null
? double.parse(result.content['interval'].toString())
generalSwitch = data.enabled ?? false;
anonymizeClientIp = data.anonymizeClientIp ?? false;
retentionTime = data.interval != null
? double.parse(data.interval.toString())
: null;
if (data.ignored != null) {
_ignoredDomainsControllers = data.ignored!.map((e) => DomainListItemController(
id: uuid.v4(),
controller: TextEditingController(text: e),
error: false
)).toList();
}
loadStatus = LoadStatus.loaded;
});
}
@ -52,7 +77,6 @@ class _LogsSettingsState extends State<LogsSettings> {
setState(() => loadStatus = LoadStatus.error);
}
}
}
@override
void initState() {
@ -65,6 +89,10 @@ class _LogsSettingsState extends State<LogsSettings> {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final validValues = _ignoredDomainsControllers.where(
(d) => d.controller.text == "" || d.error == true
).isEmpty;
void clearQueries() async {
ProcessModal processModal = ProcessModal();
processModal.open(AppLocalizations.of(context)!.updatingSettings);
@ -99,7 +127,8 @@ class _LogsSettingsState extends State<LogsSettings> {
data: {
"enabled": generalSwitch,
"interval": retentionTime,
"anonymize_client_ip": anonymizeClientIp
"anonymize_client_ip": anonymizeClientIp,
"ignored": _ignoredDomainsControllers.map((e) => e.controller.text).toList()
}
);
@ -128,10 +157,24 @@ class _LogsSettingsState extends State<LogsSettings> {
title: Text(AppLocalizations.of(context)!.logsSettings),
actions: [
if (loadStatus == LoadStatus.loaded) IconButton(
onPressed: updateConfig,
onPressed: validValues ? () => updateConfig() : null,
icon: const Icon(Icons.save_rounded),
tooltip: AppLocalizations.of(context)!.save,
),
if (loadStatus == LoadStatus.loaded) PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: clearQueries,
child: Row(
children: [
const Icon(Icons.delete_rounded),
const SizedBox(width: 8),
Text(AppLocalizations.of(context)!.clearLogs),
],
)
)
],
),
const SizedBox(width: 8)
],
),
@ -151,7 +194,9 @@ class _LogsSettingsState extends State<LogsSettings> {
retentionTime: retentionTime,
updateRetentionTime: (v) => setState(() => retentionTime = v),
onClear: clearQueries,
onConfirm: updateConfig
onConfirm: updateConfig,
ignoredDomainsControllers: _ignoredDomainsControllers,
updateIgnoredDomainsControllers: (v) => setState(() => _ignoredDomainsControllers = v),
);
case LoadStatus.error:

View file

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:adguard_home_manager/models/blocked_services.dart';
import 'package:adguard_home_manager/models/querylog_config.dart';
import 'package:adguard_home_manager/models/dns_info.dart';
import 'package:adguard_home_manager/models/encryption.dart';
import 'package:adguard_home_manager/models/dhcp.dart';
@ -627,10 +628,19 @@ class ApiClientV2 {
Future<ApiResponse> getQueryLogInfo() async {
final result = await HttpRequestClient.get(urlPath: '/querylog/config', server: server);
if (result.successful) {
try {
return ApiResponse(
successful: true,
content: jsonDecode(result.body!)
content: QueryLogConfig.fromJson(jsonDecode(result.body!))
);
} catch (e, stackTrace) {
Sentry.captureException(
e,
stackTrace: stackTrace,
hint: Hint.withMap({ "statusCode": result.statusCode.toString() })
);
return const ApiResponse(successful: false);
}
}
else {
return const ApiResponse(successful: false);