Skip to content

Commit ba09a14

Browse files
committed
Merge branch 'master' into ADG-10291
2 parents 18acf8f + 4ca8533 commit ba09a14

75 files changed

Lines changed: 1531 additions & 634 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,42 @@ See also the [v0.107.65 GitHub milestone][ms-v0.107.65].
1717
1818
NOTE: Add new changes BELOW THIS COMMENT.
1919
-->
20+
21+
### Added
22+
23+
- A separate checkbox in the Web UI to enable or disable the global DNS response cache without losing the configured cache size.
24+
25+
- A new `"cache_enabled"` field to the HTTP API (`GET /control/dns_info` and `POST /control/dns_config`). See `openapi/openapi.yaml` for the full description.
26+
27+
### Changed
28+
29+
#### Configuration changes
30+
31+
In this release, the schema version has changed from 29 to 30.
32+
33+
- Added a new boolean field `dns.cache_enabled` to the configuration. This field explicitly controls whether DNS caching is enabled, replacing the previous implicit logic based on `dns.cache_size`.
34+
35+
```yaml
36+
# BEFORE:
37+
'dns':
38+
#
39+
'cache_size': 123456
40+
41+
# AFTER:
42+
'dns':
43+
#
44+
'cache_enabled': true
45+
'cache_size': 123456
46+
```
47+
48+
To roll back this change, set the schema_version back to `29`.
49+
50+
### Fixed
51+
52+
- Disabled state of Top clients action button in web UI ([#7923]).
53+
54+
[#7923]: https://github.com/AdguardTeam/AdGuardHome/issues/7923
55+
2056
<!--
2157
NOTE: Add new changes ABOVE THIS COMMENT.
2258
-->

client/src/__locales/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,10 @@
655655
"safe_search": "Safe Search",
656656
"blocklist": "Blocklist",
657657
"milliseconds_abbreviation": "ms",
658+
"cache_enabled": "Enable cache",
659+
"cache_enabled_desc": "Store DNS responses locally.",
658660
"cache_size": "Cache size",
661+
"cache_size_validation": "The cache size must be greater than zero when enabled.",
659662
"cache_size_desc": "DNS cache size (in bytes). To disable caching, set to 0.",
660663
"cache_ttl_min_override": "Override minimum TTL",
661664
"cache_ttl_max_override": "Override maximum TTL",

client/src/components/Logs/Logs.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,8 @@
337337
}
338338

339339
.button-action--arrow-option:disabled {
340-
display: none;
340+
opacity: 0.5;
341+
cursor: default;
341342
}
342343

343344
.tooltip-custom__container .button-action--arrow-option {

client/src/components/Settings/Dns/Cache/Form.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const INPUTS_FIELDS = [
3232
];
3333

3434
type FormData = {
35+
cache_enabled: boolean;
3536
cache_size: number;
3637
cache_ttl_min: number;
3738
cache_ttl_max: number;
@@ -54,21 +55,25 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
5455
handleSubmit,
5556
watch,
5657
control,
57-
formState: { isSubmitting, isDirty },
58+
formState: { isSubmitting },
5859
} = useForm<FormData>({
5960
mode: 'onBlur',
6061
defaultValues: {
62+
cache_enabled: initialValues?.cache_enabled || false,
6163
cache_size: initialValues?.cache_size || 0,
6264
cache_ttl_min: initialValues?.cache_ttl_min || 0,
6365
cache_ttl_max: initialValues?.cache_ttl_max || 0,
6466
cache_optimistic: initialValues?.cache_optimistic || false,
6567
},
6668
});
6769

70+
const cache_enabled = watch('cache_enabled');
71+
const cache_size = watch('cache_size');
6872
const cache_ttl_min = watch('cache_ttl_min');
6973
const cache_ttl_max = watch('cache_ttl_max');
7074

7175
const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max;
76+
const cacheSizeZeroWhenEnabled = cache_enabled && cache_size === 0;
7277

7378
const handleClearCache = () => {
7479
if (window.confirm(t('confirm_dns_cache_clear'))) {
@@ -79,6 +84,24 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
7984
return (
8085
<form onSubmit={handleSubmit(onSubmit)}>
8186
<div className="row">
87+
<div className="col-12 col-md-7">
88+
<div className="form__group form__group--settings">
89+
<Controller
90+
name="cache_enabled"
91+
control={control}
92+
render={({ field }) => (
93+
<Checkbox
94+
{...field}
95+
data-testid="dns_cache_enabled"
96+
title={t('cache_enabled')}
97+
subtitle={t('cache_enabled_desc')}
98+
disabled={processingSetConfig}
99+
/>
100+
)}
101+
/>
102+
</div>
103+
</div>
104+
82105
{INPUTS_FIELDS.map(({ name, title, description, placeholder }) => (
83106
<div className="col-12" key={name}>
84107
<div className="col-12 col-md-7 p-0">
@@ -102,6 +125,12 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
102125
setValueAs: (value) => replaceZeroWithEmptyString(value),
103126
})}
104127
/>
128+
129+
{name === CACHE_CONFIG_FIELDS.cache_size && cacheSizeZeroWhenEnabled && (
130+
<span className="form__message form__message--error">
131+
{t('cache_size_validation')}
132+
</span>
133+
)}
105134
</div>
106135
</div>
107136
</div>
@@ -133,7 +162,7 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
133162
type="submit"
134163
data-testid="dns_save"
135164
className="btn btn-success btn-standard btn-large"
136-
disabled={isSubmitting || !isDirty || processingSetConfig || minExceedsMax}>
165+
disabled={isSubmitting || processingSetConfig || minExceedsMax || cacheSizeZeroWhenEnabled}>
137166
{t('save_btn')}
138167
</button>
139168

client/src/components/Settings/Dns/Cache/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { RootState } from '../../../../initialState';
1313
const CacheConfig = () => {
1414
const { t } = useTranslation();
1515
const dispatch = useDispatch();
16-
const { cache_size, cache_ttl_max, cache_ttl_min, cache_optimistic } = useSelector(
16+
const { cache_enabled, cache_size, cache_ttl_max, cache_ttl_min, cache_optimistic } = useSelector(
1717
(state: RootState) => state.dnsConfig,
1818
shallowEqual,
1919
);
@@ -32,6 +32,7 @@ const CacheConfig = () => {
3232
<div className="form">
3333
<Form
3434
initialValues={{
35+
cache_enabled,
3536
cache_size: replaceZeroWithEmptyString(cache_size),
3637
cache_ttl_max: replaceZeroWithEmptyString(cache_ttl_max),
3738
cache_ttl_min: replaceZeroWithEmptyString(cache_ttl_min),

client/src/initialState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ export type DnsConfigData = {
322322
ratelimit_subnet_len_ipv6?: number;
323323
edns_cs_use_custom?: boolean;
324324
edns_cs_custom_ip?: string;
325+
cache_enabled?: boolean;
325326
cache_size?: number;
326327
cache_ttl_max?: number;
327328
cache_ttl_min?: number;

internal/agh/agh.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Package agh contains common entities and interfaces of AdGuard Home.
2+
package agh
3+
4+
import (
5+
"context"
6+
)
7+
8+
// ConfigModifier defines an interface for updating the global configuration.
9+
type ConfigModifier interface {
10+
// Apply applies changes to the global configuration.
11+
Apply(ctx context.Context)
12+
}
13+
14+
// EmptyConfigModifier is an empty [ConfigModifier] implementation that does
15+
// nothing.
16+
type EmptyConfigModifier struct{}
17+
18+
// type check
19+
var _ ConfigModifier = EmptyConfigModifier{}
20+
21+
// Apply implements the [ConfigModifier] for EmptyConfigModifier.
22+
func (em EmptyConfigModifier) Apply(ctx context.Context) {}

internal/aghtest/interface.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66
"net/netip"
77
"time"
88

9+
"github.com/AdguardTeam/AdGuardHome/internal/agh"
910
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
10-
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
11+
nextagh "github.com/AdguardTeam/AdGuardHome/internal/next/agh"
1112
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
1213
"github.com/AdguardTeam/AdGuardHome/internal/whois"
1314
"github.com/AdguardTeam/dnsproxy/upstream"
@@ -53,31 +54,32 @@ func (w *FSWatcher) Add(name string) (err error) {
5354
return w.OnAdd(name)
5455
}
5556

56-
// Package agh
57+
// Package nextagh
5758

58-
// ServiceWithConfig is a fake [agh.ServiceWithConfig] implementation for tests.
59+
// ServiceWithConfig is a fake [nextagh.ServiceWithConfig] implementation for
60+
// tests.
5961
type ServiceWithConfig[ConfigType any] struct {
6062
OnStart func(ctx context.Context) (err error)
6163
OnShutdown func(ctx context.Context) (err error)
6264
OnConfig func() (c ConfigType)
6365
}
6466

6567
// type check
66-
var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
68+
var _ nextagh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)
6769

68-
// Start implements the [agh.ServiceWithConfig] interface for
70+
// Start implements the [nextagh.ServiceWithConfig] interface for
6971
// *ServiceWithConfig.
7072
func (s *ServiceWithConfig[_]) Start(ctx context.Context) (err error) {
7173
return s.OnStart(ctx)
7274
}
7375

74-
// Shutdown implements the [agh.ServiceWithConfig] interface for
76+
// Shutdown implements the [nextagh.ServiceWithConfig] interface for
7577
// *ServiceWithConfig.
7678
func (s *ServiceWithConfig[_]) Shutdown(ctx context.Context) (err error) {
7779
return s.OnShutdown(ctx)
7880
}
7981

80-
// Config implements the [agh.ServiceWithConfig] interface for
82+
// Config implements the [nextagh.ServiceWithConfig] interface for
8183
// *ServiceWithConfig.
8284
func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
8385
return s.OnConfig()
@@ -178,3 +180,16 @@ func (u *UpstreamMock) Exchange(req *dns.Msg) (resp *dns.Msg, err error) {
178180
func (u *UpstreamMock) Close() (err error) {
179181
return u.OnClose()
180182
}
183+
184+
// ConfigModifier is a fake [agh.ConfigModifier] implementation for tests.
185+
type ConfigModifier struct {
186+
OnApply func(ctx context.Context)
187+
}
188+
189+
// type check
190+
var _ agh.ConfigModifier = (*ConfigModifier)(nil)
191+
192+
// Apply implements the [ConfigModifier] interface for *ConfigModifier.
193+
func (m *ConfigModifier) Apply(ctx context.Context) {
194+
m.OnApply(ctx)
195+
}

internal/configmigrate/configmigrate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
package configmigrate
33

44
// LastSchemaVersion is the most recent schema version.
5-
const LastSchemaVersion uint = 29
5+
const LastSchemaVersion uint = 30

internal/configmigrate/migrator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func (m *Migrator) upgradeConfigSchema(current, target uint, diskConf yobj) (err
125125
26: migrateTo27,
126126
27: migrateTo28,
127127
28: m.migrateTo29,
128+
29: m.migrateTo30,
128129
}
129130

130131
for i, migrate := range upgrades[current:target] {

0 commit comments

Comments
 (0)