mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-05-14 14:02:48 +00:00
Added ignored domains list config
This commit is contained in:
parent
211eab9f44
commit
c12e8c5ad3
6 changed files with 218 additions and 31 deletions
|
@ -727,5 +727,7 @@
|
||||||
"dnsCacheNumber": "DNS cache size must be a number",
|
"dnsCacheNumber": "DNS cache size must be a number",
|
||||||
"errors": "Errors",
|
"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.",
|
"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"
|
||||||
}
|
}
|
|
@ -727,5 +727,7 @@
|
||||||
"dnsCacheNumber": "El tamaño de caché de DNS debe ser un número",
|
"dnsCacheNumber": "El tamaño de caché de DNS debe ser un número",
|
||||||
"errors": "Errores",
|
"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.",
|
"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"
|
||||||
}
|
}
|
27
lib/models/querylog_config.dart
Normal file
27
lib/models/querylog_config.dart
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.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';
|
import 'package:adguard_home_manager/widgets/custom_checkbox_list_tile.dart';
|
||||||
|
|
||||||
class LogsConfigOptions extends StatelessWidget {
|
class LogsConfigOptions extends StatelessWidget {
|
||||||
|
@ -13,6 +16,8 @@ class LogsConfigOptions extends StatelessWidget {
|
||||||
final void Function(double?) updateRetentionTime;
|
final void Function(double?) updateRetentionTime;
|
||||||
final void Function() onClear;
|
final void Function() onClear;
|
||||||
final void Function() onConfirm;
|
final void Function() onConfirm;
|
||||||
|
final List<DomainListItemController> ignoredDomainsControllers;
|
||||||
|
final void Function(List<DomainListItemController>) updateIgnoredDomainsControllers;
|
||||||
|
|
||||||
const LogsConfigOptions({
|
const LogsConfigOptions({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -24,11 +29,15 @@ class LogsConfigOptions extends StatelessWidget {
|
||||||
required this.retentionTime,
|
required this.retentionTime,
|
||||||
required this.updateRetentionTime,
|
required this.updateRetentionTime,
|
||||||
required this.onClear,
|
required this.onClear,
|
||||||
required this.onConfirm
|
required this.onConfirm,
|
||||||
|
required this.ignoredDomainsControllers,
|
||||||
|
required this.updateIgnoredDomainsControllers
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
const Uuid uuid = Uuid();
|
||||||
|
|
||||||
final List<String> dropdownItemTranslation = [
|
final List<String> dropdownItemTranslation = [
|
||||||
AppLocalizations.of(context)!.hours6,
|
AppLocalizations.of(context)!.hours6,
|
||||||
AppLocalizations.of(context)!.hours24,
|
AppLocalizations.of(context)!.hours24,
|
||||||
|
@ -37,7 +46,28 @@ class LogsConfigOptions extends StatelessWidget {
|
||||||
AppLocalizations.of(context)!.days90,
|
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: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -100,13 +130,84 @@ class LogsConfigOptions extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
Padding(
|
||||||
ElevatedButton.icon(
|
padding: const EdgeInsets.only(top: 24, bottom: 8),
|
||||||
onPressed: onClear,
|
child: Row(
|
||||||
icon: const Icon(Icons.delete_rounded),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
label: Text(AppLocalizations.of(context)!.clearLogs),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.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/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/classes/process_modal.dart';
|
||||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
import 'package:adguard_home_manager/constants/enums.dart';
|
import 'package:adguard_home_manager/constants/enums.dart';
|
||||||
import 'package:adguard_home_manager/providers/servers_provider.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 {
|
class LogsSettings extends StatefulWidget {
|
||||||
const LogsSettings({super.key});
|
const LogsSettings({super.key});
|
||||||
|
|
||||||
|
@ -18,9 +32,12 @@ class LogsSettings extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LogsSettingsState extends State<LogsSettings> {
|
class _LogsSettingsState extends State<LogsSettings> {
|
||||||
|
final Uuid uuid = const Uuid();
|
||||||
|
|
||||||
bool generalSwitch = false;
|
bool generalSwitch = false;
|
||||||
bool anonymizeClientIp = false;
|
bool anonymizeClientIp = false;
|
||||||
double? retentionTime;
|
double? retentionTime;
|
||||||
|
List<DomainListItemController> _ignoredDomainsControllers = [];
|
||||||
|
|
||||||
List<int> retentionItems = [
|
List<int> retentionItems = [
|
||||||
21600000,
|
21600000,
|
||||||
|
@ -37,14 +54,22 @@ class _LogsSettingsState extends State<LogsSettings> {
|
||||||
|
|
||||||
final result = await serversProvider.apiClient2!.getQueryLogInfo();
|
final result = await serversProvider.apiClient2!.getQueryLogInfo();
|
||||||
|
|
||||||
if (mounted) {
|
if (!mounted) return;
|
||||||
if (result.successful == true) {
|
if (result.successful == true) {
|
||||||
|
final data = result.content as QueryLogConfig;
|
||||||
setState(() {
|
setState(() {
|
||||||
generalSwitch = result.content['enabled'];
|
generalSwitch = data.enabled ?? false;
|
||||||
anonymizeClientIp = result.content['anonymize_client_ip'];
|
anonymizeClientIp = data.anonymizeClientIp ?? false;
|
||||||
retentionTime = result.content['interval'] != null
|
retentionTime = data.interval != null
|
||||||
? double.parse(result.content['interval'].toString())
|
? double.parse(data.interval.toString())
|
||||||
: null;
|
: null;
|
||||||
|
if (data.ignored != null) {
|
||||||
|
_ignoredDomainsControllers = data.ignored!.map((e) => DomainListItemController(
|
||||||
|
id: uuid.v4(),
|
||||||
|
controller: TextEditingController(text: e),
|
||||||
|
error: false
|
||||||
|
)).toList();
|
||||||
|
}
|
||||||
loadStatus = LoadStatus.loaded;
|
loadStatus = LoadStatus.loaded;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -52,7 +77,6 @@ class _LogsSettingsState extends State<LogsSettings> {
|
||||||
setState(() => loadStatus = LoadStatus.error);
|
setState(() => loadStatus = LoadStatus.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -65,6 +89,10 @@ class _LogsSettingsState extends State<LogsSettings> {
|
||||||
final serversProvider = Provider.of<ServersProvider>(context);
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
final validValues = _ignoredDomainsControllers.where(
|
||||||
|
(d) => d.controller.text == "" || d.error == true
|
||||||
|
).isEmpty;
|
||||||
|
|
||||||
void clearQueries() async {
|
void clearQueries() async {
|
||||||
ProcessModal processModal = ProcessModal();
|
ProcessModal processModal = ProcessModal();
|
||||||
processModal.open(AppLocalizations.of(context)!.updatingSettings);
|
processModal.open(AppLocalizations.of(context)!.updatingSettings);
|
||||||
|
@ -99,7 +127,8 @@ class _LogsSettingsState extends State<LogsSettings> {
|
||||||
data: {
|
data: {
|
||||||
"enabled": generalSwitch,
|
"enabled": generalSwitch,
|
||||||
"interval": retentionTime,
|
"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),
|
title: Text(AppLocalizations.of(context)!.logsSettings),
|
||||||
actions: [
|
actions: [
|
||||||
if (loadStatus == LoadStatus.loaded) IconButton(
|
if (loadStatus == LoadStatus.loaded) IconButton(
|
||||||
onPressed: updateConfig,
|
onPressed: validValues ? () => updateConfig() : null,
|
||||||
icon: const Icon(Icons.save_rounded),
|
icon: const Icon(Icons.save_rounded),
|
||||||
tooltip: AppLocalizations.of(context)!.save,
|
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)
|
const SizedBox(width: 8)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -151,7 +194,9 @@ class _LogsSettingsState extends State<LogsSettings> {
|
||||||
retentionTime: retentionTime,
|
retentionTime: retentionTime,
|
||||||
updateRetentionTime: (v) => setState(() => retentionTime = v),
|
updateRetentionTime: (v) => setState(() => retentionTime = v),
|
||||||
onClear: clearQueries,
|
onClear: clearQueries,
|
||||||
onConfirm: updateConfig
|
onConfirm: updateConfig,
|
||||||
|
ignoredDomainsControllers: _ignoredDomainsControllers,
|
||||||
|
updateIgnoredDomainsControllers: (v) => setState(() => _ignoredDomainsControllers = v),
|
||||||
);
|
);
|
||||||
|
|
||||||
case LoadStatus.error:
|
case LoadStatus.error:
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/models/blocked_services.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/dns_info.dart';
|
||||||
import 'package:adguard_home_manager/models/encryption.dart';
|
import 'package:adguard_home_manager/models/encryption.dart';
|
||||||
import 'package:adguard_home_manager/models/dhcp.dart';
|
import 'package:adguard_home_manager/models/dhcp.dart';
|
||||||
|
@ -627,10 +628,19 @@ class ApiClientV2 {
|
||||||
Future<ApiResponse> getQueryLogInfo() async {
|
Future<ApiResponse> getQueryLogInfo() async {
|
||||||
final result = await HttpRequestClient.get(urlPath: '/querylog/config', server: server);
|
final result = await HttpRequestClient.get(urlPath: '/querylog/config', server: server);
|
||||||
if (result.successful) {
|
if (result.successful) {
|
||||||
|
try {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
successful: true,
|
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 {
|
else {
|
||||||
return const ApiResponse(successful: false);
|
return const ApiResponse(successful: false);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue