Added logs config

This commit is contained in:
Juan Gilsanz Polo 2022-10-15 23:08:01 +02:00
parent 2e4872a6c5
commit d56307592e
7 changed files with 403 additions and 161 deletions

View file

@ -401,5 +401,12 @@
"days30": "30 days", "days30": "30 days",
"days90": "90 days", "days90": "90 days",
"retentionTime": "Retention time", "retentionTime": "Retention time",
"selectOneItem": "Select one item" "selectOneItem": "Select one item",
"logSettingsNotLoaded": "Log settings couldn't be loaded.",
"updatingSettings": "Updating settings...",
"logsConfigUpdated": "Logs settings updated successfully",
"logsConfigNotUpdated": "Logs settings couldn't be updated",
"deletingLogs": "Clearing logs...",
"logsCleared": "Logs cleared successfully",
"logsNotCleared": "Logs could not be cleared"
} }

View file

@ -401,5 +401,12 @@
"days30": "30 días", "days30": "30 días",
"days90": "90 días", "days90": "90 días",
"retentionTime": "Tiempo de retención", "retentionTime": "Tiempo de retención",
"selectOneItem": "Selecciona un elemento" "selectOneItem": "Selecciona un elemento",
"logSettingsNotLoaded": "Los ajustes de registros no han podido ser cargados.",
"updatingSettings": "Actualizando ajustes...",
"logsConfigUpdated": "Configuración de registros actualizada correctamente",
"logsConfigNotUpdated": "La configuración de registros no ha podido ser actualizada",
"deletingLogs": "Borrando registros...",
"logsCleared": "Registros borrados correctamente",
"logsNotCleared": "No se han podido borrar los registros"
} }

View file

@ -38,7 +38,7 @@ class Log {
final bool answerDnssec; final bool answerDnssec;
final bool cached; final bool cached;
final String client; final String client;
final ClientInfo clientInfo; final ClientInfo? clientInfo;
final String clientProto; final String clientProto;
final String elapsedMs; final String elapsedMs;
final Question question; final Question question;
@ -56,7 +56,7 @@ class Log {
required this.answerDnssec, required this.answerDnssec,
required this.cached, required this.cached,
required this.client, required this.client,
required this.clientInfo, this.clientInfo,
required this.clientProto, required this.clientProto,
required this.elapsedMs, required this.elapsedMs,
required this.question, required this.question,
@ -75,7 +75,7 @@ class Log {
answerDnssec: json["answer_dnssec"], answerDnssec: json["answer_dnssec"],
cached: json["cached"], cached: json["cached"],
client: json["client"], client: json["client"],
clientInfo: ClientInfo.fromJson(json["client_info"]), clientInfo: json["client_info"] != null ? ClientInfo.fromJson(json["client_info"]) : null,
clientProto: json["client_proto"], clientProto: json["client_proto"],
elapsedMs: json["elapsedMs"], elapsedMs: json["elapsedMs"],
question: Question.fromJson(json["question"]), question: Question.fromJson(json["question"]),
@ -94,7 +94,7 @@ class Log {
"answer_dnssec": answerDnssec, "answer_dnssec": answerDnssec,
"cached": cached, "cached": cached,
"client": client, "client": client,
"client_info": clientInfo.toJson(), "client_info": clientInfo?.toJson(),
"client_proto": clientProto, "client_proto": clientProto,
"elapsedMs": elapsedMs, "elapsedMs": elapsedMs,
"question": question.toJson(), "question": question.toJson(),

View file

@ -173,10 +173,10 @@ class LogDetailsModal extends StatelessWidget {
title: AppLocalizations.of(context)!.deviceIp, title: AppLocalizations.of(context)!.deviceIp,
subtitle: log.client subtitle: log.client
), ),
if (log.clientInfo.name != '') LogListTile( if (log.clientInfo != null && log.clientInfo!.name != '') LogListTile(
icon: Icons.abc_rounded, icon: Icons.abc_rounded,
title: AppLocalizations.of(context)!.deviceName, title: AppLocalizations.of(context)!.deviceName,
subtitle: log.clientInfo.name subtitle: log.clientInfo!.name
), ),
], ],
) )

View file

@ -8,6 +8,8 @@ import 'package:adguard_home_manager/screens/logs/logs_filters_modal.dart';
import 'package:adguard_home_manager/screens/logs/logs_config_modal.dart'; import 'package:adguard_home_manager/screens/logs/logs_config_modal.dart';
import 'package:adguard_home_manager/screens/logs/log_tile.dart'; import 'package:adguard_home_manager/screens/logs/log_tile.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/models/applied_filters.dart'; import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
@ -135,8 +137,67 @@ class _LogsWidgetState extends State<LogsWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final logsProvider = Provider.of<LogsProvider>(context); final logsProvider = Provider.of<LogsProvider>(context);
void updateConfig(Map<String, dynamic> data) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.updatingSettings);
final result = await updateQueryLogParameters(server: serversProvider.selectedServer!, data: data);
processModal.close();
if (result['result'] == 'success') {
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsConfigUpdated,
color: Colors.green
);
}
else {
appConfigProvider.addLog(result['log']);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsConfigNotUpdated,
color: Colors.red
);
}
}
void clearQueries() async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.updatingSettings);
final result = await clearLogs(server: serversProvider.selectedServer!);
processModal.close();
if (result['result'] == 'success') {
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsCleared,
color: Colors.green
);
}
else {
appConfigProvider.addLog(result['log']);
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsNotCleared,
color: Colors.red
);
}
}
void openFilersModal() { void openFilersModal() {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -269,14 +330,6 @@ class _LogsWidgetState extends State<LogsWidget> {
} }
} }
void updateConfig(Map<String, dynamic> data) async {
}
void clearQueries() async {
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(AppLocalizations.of(context)!.logs), title: Text(AppLocalizations.of(context)!.logs),

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/services/http_requests.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/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
@ -57,10 +58,30 @@ class _LogsConfigModalWidgetState extends State<LogsConfigModalWidget> {
List<Map<String, dynamic>> retentionItems = []; List<Map<String, dynamic>> retentionItems = [];
int loadStatus = 0;
void loadData() async {
final result = await getQueryLogInfo(server: widget.serversProvider.selectedServer!);
if (mounted) {
if (result['result'] == 'success') {
setState(() {
generalSwitch = result['data']['enabled'];
anonymizeClientIp = result['data']['anonymize_client_ip'];
retentionTime = result['data']['interval'].toString();
loadStatus = 1;
});
}
else {
setState(() => loadStatus = 2);
}
}
}
@override @override
void initState() { void initState() {
retentionItems = [ retentionItems = [
{ {
'label': AppLocalizations.of(widget.context)!.hours6, 'label': AppLocalizations.of(widget.context)!.hours6,
'value': 0.25 'value': 0.25
}, },
@ -82,11 +103,206 @@ class _LogsConfigModalWidgetState extends State<LogsConfigModalWidget> {
}, },
]; ];
loadData();
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget generateBody() {
switch (loadStatus) {
case 0:
return const Center(
child: CircularProgressIndicator(),
);
case 1:
return Column(
children: [
const Padding(
padding: EdgeInsets.only(top: 28),
child: Icon(
Icons.settings,
size: 26,
),
),
const SizedBox(height: 20),
Text(
AppLocalizations.of(context)!.logsSettings,
style: const TextStyle(
fontSize: 24
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material(
color: Theme.of(context).primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(28),
child: InkWell(
onTap: () => setState(() => generalSwitch = !generalSwitch),
borderRadius: BorderRadius.circular(28),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 8
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.enableLog,
style: const TextStyle(
fontSize: 18,
),
),
Switch(
value: generalSwitch,
onChanged: (value) => setState(() => generalSwitch = value),
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
),
const SizedBox(height: 20),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () => setState(() => anonymizeClientIp = !anonymizeClientIp),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.anonymizeClientIp,
style: const TextStyle(
fontSize: 16
),
),
Switch(
value: anonymizeClientIp,
onChanged: (value) => setState(() => anonymizeClientIp = value),
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
child: DropdownButtonFormField(
items: retentionItems.map<DropdownMenuItem<String>>((Map<String, dynamic> item) {
return DropdownMenuItem<String>(
value: item['value'].toString(),
child: Text(item['label']),
);
}).toList(),
value: retentionTime,
onChanged: (value) => setState(() => retentionTime = value),
decoration: InputDecoration(
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
label: Text(AppLocalizations.of(context)!.retentionTime)
),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
widget.onClear();
},
child: Text(AppLocalizations.of(context)!.clearLogs)
),
Row(
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
),
const SizedBox(width: 20),
TextButton(
onPressed: retentionTime != ''
? () {
Navigator.pop(context);
widget.onConfirm({
"enabled": generalSwitch,
"interval": double.parse(retentionTime!),
"anonymize_client_ip": anonymizeClientIp
});
}
: null,
child: Text(
AppLocalizations.of(context)!.confirm,
style: TextStyle(
color: retentionTime != ''
? Theme.of(context).primaryColor
: Colors.grey
),
)
),
],
)
],
),
)
],
),
)
],
);
case 2:
return SizedBox(
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icons.error,
color: Colors.red,
size: 50,
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
AppLocalizations.of(context)!.logSettingsNotLoaded,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 22,
color: Colors.grey,
),
),
)
],
),
);
default:
return const SizedBox();
}
}
return Container( return Container(
height: 450, height: 450,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -96,150 +312,7 @@ class _LogsConfigModalWidgetState extends State<LogsConfigModalWidget> {
), ),
color: Theme.of(context).dialogBackgroundColor color: Theme.of(context).dialogBackgroundColor
), ),
child: Column( child: generateBody()
children: [
const Padding(
padding: EdgeInsets.only(top: 28),
child: Icon(
Icons.settings,
size: 26,
),
),
const SizedBox(height: 20),
Text(
AppLocalizations.of(context)!.logsSettings,
style: const TextStyle(
fontSize: 24
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Material(
color: Theme.of(context).primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(28),
child: InkWell(
onTap: () => setState(() => generalSwitch = !generalSwitch),
borderRadius: BorderRadius.circular(28),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 8
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.enableLog,
style: const TextStyle(
fontSize: 18,
),
),
Switch(
value: generalSwitch,
onChanged: (value) => setState(() => generalSwitch = value),
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
),
const SizedBox(height: 20),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () => setState(() => anonymizeClientIp = !anonymizeClientIp),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.anonymizeClientIp,
style: const TextStyle(
fontSize: 16
),
),
Switch(
value: anonymizeClientIp,
onChanged: (value) => setState(() => anonymizeClientIp = value),
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
child: DropdownButtonFormField(
items: retentionItems.map<DropdownMenuItem<String>>((Map<String, dynamic> item) {
return DropdownMenuItem<String>(
value: item['value'].toString(),
child: Text(item['label']),
);
}).toList(),
onChanged: (value) => setState(() => retentionTime = value),
decoration: InputDecoration(
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
label: Text(AppLocalizations.of(context)!.retentionTime)
),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: widget.onClear,
child: Text(AppLocalizations.of(context)!.clearLogs)
),
Row(
children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
),
const SizedBox(width: 20),
TextButton(
onPressed: retentionTime != ''
? () => widget.onConfirm({
"enabled": generalSwitch,
"interval": double.parse(retentionTime!),
"anonymize_client_ip": anonymizeClientIp
})
: null,
child: Text(
AppLocalizations.of(context)!.confirm,
style: TextStyle(
color: retentionTime != ''
? Theme.of(context).primaryColor
: Colors.grey
),
)
),
],
)
],
),
)
],
),
)
],
),
); );
} }
} }

View file

@ -1463,3 +1463,105 @@ Future addDnsRewriteRule({
return result; return result;
} }
} }
Future getQueryLogInfo({
required Server server,
}) async {
final result = await apiRequest(
urlPath: '/querylog_info',
method: 'get',
server: server,
type: 'get_query_log_info'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return {
'result': 'success',
'data': jsonDecode(result['body'])
};
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'get_query_log_info',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'].toString(),
resBody: result['body'],
)
};
}
}
else {
return result;
}
}
Future updateQueryLogParameters({
required Server server,
required Map<String, dynamic> data,
}) async {
final result = await apiRequest(
urlPath: '/querylog_config',
method: 'post',
server: server,
body: data,
type: 'update_query_log_config'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return { 'result': 'success' };
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'update_query_log_config',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'].toString(),
resBody: result['body'],
)
};
}
}
else {
return result;
}
}
Future clearLogs({
required Server server,
}) async {
final result = await apiRequest(
urlPath: '/querylog_clear',
method: 'post',
server: server,
body: {},
type: 'clear_query_logs'
);
if (result['hasResponse'] == true) {
if (result['statusCode'] == 200) {
return { 'result': 'success' };
}
else {
return {
'result': 'error',
'log': AppLog(
type: 'clear_query_logs',
dateTime: DateTime.now(),
message: 'error_code_not_expected',
statusCode: result['statusCode'].toString(),
resBody: result['body'],
)
};
}
}
else {
return result;
}
}