From cc2006f61600858acdd7ba3a079b3b9fd9fc3873 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 29 Jan 2025 18:08:39 +0300 Subject: [PATCH 1/7] Pull request #2337: ADG-9565 Merge in DNS/adguard-home from ADG-9565 to master Squashed commit of the following: commit ef92c2e8918c76d3fec40dbc5b838cf73f84b5c1 Merge: a4364ff7e 91270d0b6 Author: Ildar Kamalov Date: Wed Jan 29 13:42:29 2025 +0300 Merge branch 'master' into ADG-9565 commit a4364ff7ed5ed7f6b0cadd85e52d1005451f2b1b Author: Ildar Kamalov Date: Wed Jan 29 13:39:14 2025 +0300 ADG-9565 format large numbers in the upstream table and query log --- CHANGELOG.md | 6 +++++ .../components/Dashboard/UpstreamAvgTime.tsx | 3 ++- client/src/helpers/helpers.tsx | 22 +++++++++++-------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c216f35..f68e4872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ See also the [v0.107.57 GitHub milestone][ms-v0.107.57]. NOTE: Add new changes BELOW THIS COMMENT. --> +### Fixed + +- The formatting of large numbers in the upstream table and query log ([#7590]). + +[#7590]: https://github.com/AdguardTeam/AdGuardHome/issues/7590 + diff --git a/client/src/components/Dashboard/UpstreamAvgTime.tsx b/client/src/components/Dashboard/UpstreamAvgTime.tsx index 48ed9e6a..8f5b4cab 100644 --- a/client/src/components/Dashboard/UpstreamAvgTime.tsx +++ b/client/src/components/Dashboard/UpstreamAvgTime.tsx @@ -10,6 +10,7 @@ import Card from '../ui/Card'; import DomainCell from './DomainCell'; import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants'; +import { formatNumber } from '../../helpers/helpers'; interface TimeCellProps { value?: string | number; @@ -20,7 +21,7 @@ const TimeCell = ({ value }: TimeCellProps) => { return '–'; } - const valueInMilliseconds = round(Number(value) * 1000); + const valueInMilliseconds = formatNumber(round(Number(value) * 1000)); return (
diff --git a/client/src/helpers/helpers.tsx b/client/src/helpers/helpers.tsx index d04676c4..60851b36 100644 --- a/client/src/helpers/helpers.tsx +++ b/client/src/helpers/helpers.tsx @@ -453,7 +453,7 @@ export const getParamsForClientsSearch = (data: any, param: any, additionalParam }); return { - clients: Array.from(clients).map(id => ({ id })), + clients: Array.from(clients).map((id) => ({ id })), }; }; @@ -670,9 +670,16 @@ export const countClientsStatistics = (ids: any, autoClients: any) => { * @param {function} t translate * @returns {string} */ -export const formatElapsedMs = (elapsedMs: any, t: any) => { - const formattedElapsedMs = parseInt(elapsedMs, 10) || parseFloat(elapsedMs).toFixed(2); - return `${formattedElapsedMs} ${t('milliseconds_abbreviation')}`; +export const formatElapsedMs = (elapsedMs: string, t: (key: string) => string) => { + const parsedElapsedMs = parseInt(elapsedMs, 10); + + if (Number.isNaN(parsedElapsedMs)) { + return elapsedMs; + } + + const formattedMs = formatNumber(parsedElapsedMs); + + return `${formattedMs} ${t('milliseconds_abbreviation')}`; }; /** @@ -754,12 +761,9 @@ type NestedObject = { order: number; }; -export const getObjectKeysSorted = < - T extends Record, - K extends keyof NestedObject ->( +export const getObjectKeysSorted = , K extends keyof NestedObject>( object: T, - sortKey: K + sortKey: K, ): string[] => { return Object.entries(object) .sort(([, a], [, b]) => (a[sortKey] as number) - (b[sortKey] as number)) From d3dea0f46cda81dadf9cc597969f459e016368b8 Mon Sep 17 00:00:00 2001 From: Stanislav Chzhen Date: Thu, 30 Jan 2025 15:08:01 +0300 Subject: [PATCH 2/7] Pull request 2334: AGDNS-2627-query-statistics Merge in DNS/adguard-home from AGDNS-2627-query-statistics to master Squashed commit of the following: commit 9bfdadfddda04404742683d69f6455a56ae68763 Author: Stanislav Chzhen Date: Thu Jan 30 14:40:06 2025 +0300 chlog: fix typo commit 73764b79bda4dd26695160ab7cee6f222ed0e3a9 Author: Stanislav Chzhen Date: Thu Jan 30 14:14:56 2025 +0300 chlog: fix typo commit 5900b360feb12ced95d10fd11510d81450cd5443 Author: Stanislav Chzhen Date: Thu Jan 30 14:10:34 2025 +0300 all: upd chlog commit 4a9d79a2596839d16f18e45dd13939cdc011361e Merge: 76d037122 cc2006f61 Author: Stanislav Chzhen Date: Thu Jan 30 14:01:46 2025 +0300 Merge branch 'master' into AGDNS-2627-query-statistics commit 76d0371220c3a6268e0fb0e775a01ab5a609e443 Author: Stanislav Chzhen Date: Mon Jan 27 20:32:44 2025 +0300 all: upd chlog commit 496cbba94ec8c1684001f8ed0245b51a73d5bffe Author: Stanislav Chzhen Date: Thu Jan 23 19:44:26 2025 +0300 all: imp code commit 4795a2c8fbf49cede106641e97892c008dccd2e9 Author: Stanislav Chzhen Date: Tue Jan 21 17:48:00 2025 +0300 all: query statistics --- CHANGELOG.md | 4 ++++ go.mod | 2 +- go.sum | 4 ++-- internal/dnsforward/stats.go | 24 ++++++++++++++++-------- internal/stats/stats_test.go | 13 +++++++++---- internal/stats/unit.go | 21 ++++++++++++--------- 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f68e4872..923c6e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ See also the [v0.107.57 GitHub milestone][ms-v0.107.57]. NOTE: Add new changes BELOW THIS COMMENT. --> +### Changed + +- The *Fastest IP adddress* upstream mode now collects statistics for the all upstream DNS servers. + ### Fixed - The formatting of large numbers in the upstream table and query log ([#7590]). diff --git a/go.mod b/go.mod index 1ed09910..7e2d5a55 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome go 1.23.5 require ( - github.com/AdguardTeam/dnsproxy v0.73.5 + github.com/AdguardTeam/dnsproxy v0.75.0 github.com/AdguardTeam/golibs v0.31.0 github.com/AdguardTeam/urlfilter v0.20.0 github.com/NYTimes/gziphandler v1.1.1 diff --git a/go.sum b/go.sum index ed6ca788..d93deb23 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/AdguardTeam/dnsproxy v0.73.5 h1:3EiVaTfmuW6PcJGbqloR6ZdHACsrYkm9z0eH8ZQTZnQ= -github.com/AdguardTeam/dnsproxy v0.73.5/go.mod h1:Oqw+k7LyjDObfYzXYCkpgtirbzbUrmotr92jrb3N09I= +github.com/AdguardTeam/dnsproxy v0.75.0 h1:v8/Oq/xPYzNoALR7SEUZEIbKmjnPcXLVhJLFVbrozEc= +github.com/AdguardTeam/dnsproxy v0.75.0/go.mod h1:O2qoXwF4BUBFui7OMUiWSYwapEDcYxKWeur4+jfy9nM= github.com/AdguardTeam/golibs v0.31.0 h1:Z0oPfLTLw6iZmpE58dePy2Bel0MaX+lnDwtFEE5EmIo= github.com/AdguardTeam/golibs v0.31.0/go.mod h1:wIkZ9o2UnppeW6/YD7yJB71dYbMhiuC1Fh/I2ElW7GQ= github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs= diff --git a/internal/dnsforward/stats.go b/internal/dnsforward/stats.go index ffcbc6ef..50818b40 100644 --- a/internal/dnsforward/stats.go +++ b/internal/dnsforward/stats.go @@ -122,9 +122,14 @@ func (s *Server) logQuery(dctx *dnsContext, ip net.IP, processingTime time.Durat if pctx.Upstream != nil { p.Upstream = pctx.Upstream.Address() - } else if cachedUps := pctx.CachedUpstreamAddr; cachedUps != "" { - p.Upstream = pctx.CachedUpstreamAddr - p.Cached = true + } + + if qs := pctx.QueryStatistics(); qs != nil { + ms := qs.Main() + if len(ms) == 1 && ms[0].IsCached { + p.Upstream = ms[0].Address + p.Cached = true + } } s.queryLog.Add(p) @@ -134,15 +139,18 @@ func (s *Server) logQuery(dctx *dnsContext, ip net.IP, processingTime time.Durat func (s *Server) updateStats(dctx *dnsContext, clientIP string, processingTime time.Duration) { pctx := dctx.proxyCtx + var upstreamStats []*proxy.UpstreamStatistics + qs := pctx.QueryStatistics() + if qs != nil { + upstreamStats = append(upstreamStats, qs.Main()...) + upstreamStats = append(upstreamStats, qs.Fallback()...) + } + e := &stats.Entry{ + UpstreamStats: upstreamStats, Domain: aghnet.NormalizeDomain(pctx.Req.Question[0].Name), Result: stats.RNotFiltered, ProcessingTime: processingTime, - UpstreamTime: pctx.QueryDuration, - } - - if pctx.Upstream != nil { - e.Upstream = pctx.Upstream.Address() } if clientID := dctx.clientID; clientID != "" { diff --git a/internal/stats/stats_test.go b/internal/stats/stats_test.go index dbf857d6..06aa36f3 100644 --- a/internal/stats/stats_test.go +++ b/internal/stats/stats_test.go @@ -13,6 +13,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/stats" + "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" @@ -78,15 +79,19 @@ func TestStats(t *testing.T) { Client: cliIPStr, Result: stats.RFiltered, ProcessingTime: time.Microsecond * 123456, - Upstream: respUpstream, - UpstreamTime: time.Microsecond * 222222, + UpstreamStats: []*proxy.UpstreamStatistics{{ + Address: respUpstream, + QueryDuration: time.Microsecond * 222222, + }}, }, { Domain: reqDomain, Client: cliIPStr, Result: stats.RNotFiltered, ProcessingTime: time.Microsecond * 123456, - Upstream: respUpstream, - UpstreamTime: time.Microsecond * 222222, + UpstreamStats: []*proxy.UpstreamStatistics{{ + Address: respUpstream, + QueryDuration: time.Microsecond * 222222, + }}, }} wantData := &stats.StatsResp{ diff --git a/internal/stats/unit.go b/internal/stats/unit.go index cef7fca2..5eef6f3c 100644 --- a/internal/stats/unit.go +++ b/internal/stats/unit.go @@ -10,6 +10,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" + "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/logutil/slogutil" "go.etcd.io/bbolt" @@ -62,8 +63,9 @@ type Entry struct { // Domain is the domain name requested. Domain string - // Upstream is the upstream DNS server. - Upstream string + // UpstreamStats contains the DNS query statistics for both the upstream and + // fallback DNS servers. + UpstreamStats []*proxy.UpstreamStatistics // Result is the result of processing the request. Result Result @@ -71,9 +73,6 @@ type Entry struct { // ProcessingTime is the duration of the request processing from the start // of the request including timeouts. ProcessingTime time.Duration - - // UpstreamTime is the duration of the successful request to the upstream. - UpstreamTime time.Duration } // validate returns an error if entry is not valid. @@ -329,10 +328,14 @@ func (u *unit) add(e *Entry) { u.timeSum += pt u.nTotal++ - if e.Upstream != "" { - u.upstreamsResponses[e.Upstream]++ - ut := uint64(e.UpstreamTime.Microseconds()) - u.upstreamsTimeSum[e.Upstream] += ut + for _, s := range e.UpstreamStats { + if s.IsCached || s.Error != nil { + continue + } + + addr := s.Address + u.upstreamsResponses[addr]++ + u.upstreamsTimeSum[addr] += uint64(s.QueryDuration.Microseconds()) } } From 8b6d785c72c966c9d388ec9c5e73f1ac44923913 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 5 Feb 2025 17:52:45 +0300 Subject: [PATCH 3/7] fix encryption validation --- .gitignore | 8 +- .../components/Settings/Encryption/Form.tsx | 74 +++++++++---------- .../components/Settings/Encryption/index.tsx | 40 +++++++--- .../components/ui/Controls/Checkbox/index.tsx | 7 +- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 5fd6dc60..a9598645 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,10 @@ /agh-backup/ /bin/ /build/* +/client/blob-report/ +/client/playwright-report/ +/client/playwright/.cache/ +/client/test-results/ /data/ /dist/ /filtering/tests/filtering.TestLotsOfRules*.pprof @@ -33,9 +37,5 @@ AdGuardHome.exe AdGuardHome.yaml* coverage.txt node_modules/ -/client/blob-report/ -/client/playwright-report/ -/client/playwright/.cache/ -/client/test-results/ !/build/gitkeep diff --git a/client/src/components/Settings/Encryption/Form.tsx b/client/src/components/Settings/Encryption/Form.tsx index 7968cc3e..be03433d 100644 --- a/client/src/components/Settings/Encryption/Form.tsx +++ b/client/src/components/Settings/Encryption/Form.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; @@ -92,19 +92,7 @@ export type EncryptionFormValues = { type Props = { initialValues: EncryptionFormValues; - processingConfig: boolean; - processingValidate: boolean; - status_key?: string; - not_after?: string; - warning_validation?: string; - valid_chain?: boolean; - valid_key?: boolean; - valid_cert?: boolean; - valid_pair?: boolean; - dns_names?: string[]; - key_type?: string; - issuer?: string; - subject?: string; + encryption: EncryptionData; onSubmit: (values: EncryptionFormValues) => void; debouncedConfigValidation: (values: EncryptionFormValues) => void; setTlsConfig: (values: Partial) => void; @@ -130,24 +118,28 @@ const defaultValues = { export const Form = ({ initialValues, - processingConfig, - processingValidate, - not_after, - valid_chain, - valid_key, - valid_cert, - valid_pair, - dns_names, - key_type, - issuer, - subject, - warning_validation, + encryption, onSubmit, setTlsConfig, + debouncedConfigValidation, validateTlsConfig, }: Props) => { const { t } = useTranslation(); - const previousValuesRef = useRef(initialValues); + + const { + not_after, + valid_chain, + valid_key, + valid_cert, + valid_pair, + dns_names, + key_type, + issuer, + subject, + warning_validation, + processingConfig, + processingValidate, + } = encryption; const { control, @@ -166,8 +158,6 @@ export const Form = ({ mode: 'onBlur', }); - const watchedValues = watch(); - const { enabled: isEnabled, serve_plain_dns: servePlainDns, @@ -178,16 +168,11 @@ export const Form = ({ private_key_saved: privateKeySaved, certificate_path: certificatePath, certificate_source: certificateSource, - } = watchedValues; + } = watch(); - useEffect(() => { - const previousValues = previousValuesRef.current; - - if (JSON.stringify(previousValues) !== JSON.stringify(watchedValues)) { - // TODO(ik) onChange TLS config validation - previousValuesRef.current = watchedValues; - } - }, [watchedValues]); + const handleBlur = () => { + debouncedConfigValidation(getValues()); + }; const isSavingDisabled = () => { const processing = isSubmitting || processingConfig || processingValidate; @@ -243,7 +228,9 @@ export const Form = ({ } + render={({ field }) => ( + + )} />
@@ -288,6 +275,7 @@ export const Form = ({ placeholder={t('encryption_server_enter')} error={fieldState.error?.message} disabled={!isEnabled} + onBlur={handleBlur} /> )} /> @@ -337,6 +325,7 @@ export const Form = ({ const { value } = e.target; field.onChange(toNumber(value)); }} + onBlur={handleBlur} /> )} /> @@ -368,6 +357,7 @@ export const Form = ({ const { value } = e.target; field.onChange(toNumber(value)); }} + onBlur={handleBlur} /> )} /> @@ -399,6 +389,7 @@ export const Form = ({ const { value } = e.target; field.onChange(toNumber(value)); }} + onBlur={handleBlur} /> )} /> @@ -457,6 +448,7 @@ export const Form = ({ placeholder={t('encryption_certificates_input')} disabled={!isEnabled} error={fieldState.error?.message} + onBlur={handleBlur} /> )} /> @@ -471,6 +463,7 @@ export const Form = ({ placeholder={t('encryption_certificate_path')} error={fieldState.error?.message} disabled={!isEnabled} + onBlur={handleBlur} /> )} /> @@ -527,6 +520,7 @@ export const Form = ({ } field.onChange(checked); }} + onBlur={handleBlur} /> )} /> @@ -540,6 +534,7 @@ export const Form = ({ placeholder={t('encryption_key_input')} disabled={!isEnabled || privateKeySaved} error={fieldState.error?.message} + onBlur={handleBlur} /> )} /> @@ -555,6 +550,7 @@ export const Form = ({ placeholder={t('encryption_private_key_path')} error={fieldState.error?.message} disabled={!isEnabled} + onBlur={handleBlur} /> )} /> diff --git a/client/src/components/Settings/Encryption/index.tsx b/client/src/components/Settings/Encryption/index.tsx index 67c99842..a8c5dcda 100644 --- a/client/src/components/Settings/Encryption/index.tsx +++ b/client/src/components/Settings/Encryption/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { debounce } from 'lodash'; import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants'; @@ -18,19 +18,37 @@ type Props = { export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => { const { t } = useTranslation(); - useEffect(() => { - if (encryption.enabled) { - validateTlsConfig(encryption); - } - }, [encryption, validateTlsConfig]); - const initialValues = useMemo((): EncryptionFormValues => { - const { certificate_chain, private_key, private_key_saved } = encryption; + const { + enabled, + serve_plain_dns, + server_name, + force_https, + port_https, + port_dns_over_tls, + port_dns_over_quic, + certificate_chain, + private_key, + certificate_path, + private_key_path, + private_key_saved, + } = encryption; const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH; const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH; return { - ...encryption, + enabled, + serve_plain_dns, + server_name, + force_https, + port_https, + port_dns_over_tls, + port_dns_over_quic, + certificate_chain, + private_key, + certificate_path, + private_key_path, + private_key_saved, certificate_source, key_source, }; @@ -75,7 +93,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop } }, []); - const debouncedConfigValidation = useCallback(debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]); + const debouncedConfigValidation = useMemo(() => debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]); return (
@@ -94,7 +112,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop debouncedConfigValidation={debouncedConfigValidation} setTlsConfig={setTlsConfig} validateTlsConfig={validateTlsConfig} - {...encryption} + encryption={encryption} /> )} diff --git a/client/src/components/ui/Controls/Checkbox/index.tsx b/client/src/components/ui/Controls/Checkbox/index.tsx index 32a97fba..09ed43fa 100644 --- a/client/src/components/ui/Controls/Checkbox/index.tsx +++ b/client/src/components/ui/Controls/Checkbox/index.tsx @@ -12,10 +12,14 @@ type Props = { className?: string; error?: string; onChange: (value: boolean) => void; + onBlur?: () => void; }; export const Checkbox = forwardRef( - ({ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, ...rest }, ref) => ( + ( + { title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, onBlur, ...rest }, + ref, + ) => ( <>