Compare commits

..

167 Commits

Author SHA1 Message Date
Stanislav Chzhen
0a6c8b4198 Pull request 2409: upd-proxy
Merge in DNS/adguard-home from upd-proxy to master

Squashed commit of the following:

commit 2d9f5f1c3c48102bc82806721b63a7095332cc0b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue May 13 17:51:14 2025 +0300

    all: upd proxy
2025-05-13 18:16:12 +03:00
Ainar Garipov
ae840c9c96 Pull request 2405: AGDNS-2374-updater-slog
Squashed commit of the following:

commit 89c3df471964b674b7ddafeb22566e5be9b56a13
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon May 12 18:59:39 2025 +0300

    updater: imp log

commit d78ba4368027ddcbb41c10fbf09d43fe0721dc4c
Merge: 68410954c 187b759fc
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon May 12 18:53:33 2025 +0300

    Merge branch 'master' into AGDNS-2374-updater-slog

commit 68410954c80d76b2adafe4ed28fafdd6b6b6daae
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 30 15:54:30 2025 +0300

    updater: imp docs

commit 99a705218fb849bb59dee5b801c5279a501bcf98
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 30 15:40:30 2025 +0300

    updater: imp docs, logs

commit 2a83ee3ebf9610a2703d99ec6a6b327a315f6cce
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 29 21:01:02 2025 +0300

    updater: use slog
2025-05-13 14:42:33 +03:00
Stanislav Chzhen
187b759fc6 Pull request 2404: 7801-fix-cmd-update
Closes #7801.

Squashed commit of the following:

commit f6e924e939eb9487e2c7743f04bb217e758ef253
Merge: 9caa54933 8c8323ae6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed May 7 16:22:09 2025 +0300

    Merge branch 'master' into 7801-fix-cmd-update

commit 9caa5493302af11b8d522feb2cf6e6f0facaec53
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 29 20:52:09 2025 +0300

    home: imp code

commit 765ea0023972e326c54f0c17ba79f3feca8ff803
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 29 20:16:55 2025 +0300

    all: fix cmd update
2025-05-07 16:34:46 +03:00
Dimitry Kolyshev
8c8323ae68 Pull request: AGDNS-2818-upd-golibs
Merge in DNS/adguard-home from AGDNS-2818-upd-golibs to master

Squashed commit of the following:

commit f2a41b85ec27b306407b3fa96778b266dc8232e9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed May 7 09:52:32 2025 +0300

    client: imp code

commit b4b668f7386c4abf0bbcf255c6c7b1edc5050727
Merge: c6f89e0b5 b5c47054a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed May 7 09:47:54 2025 +0300

    Merge remote-tracking branch 'origin/master' into AGDNS-2818-upd-golibs
    
    # Conflicts:
    #	go.mod
    #	go.sum

commit c6f89e0b5dcaf8842187e72c22cf9109a1c4edc9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 30 14:10:33 2025 +0300

    client: imp code

commit cf07b1802fb0f1aa005af86c2bd59485683582d2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 30 10:31:44 2025 +0300

    client: imp code

commit a10d4b1b9265f6eecd7a40746de058b961d6fdd7
Merge: 447a79ca6 e5d0f0b11
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Apr 30 10:24:00 2025 +0300

    Merge remote-tracking branch 'origin/master' into AGDNS-2818-upd-golibs

commit 447a79ca6eb296a339b1e8c57edddcfcce3efdc2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 28 13:02:40 2025 +0300

    all: upd golibs

commit 693ef4f39d628a97dbe94e1a0c5d2078f31b7e63
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 28 12:47:39 2025 +0300

    all: upd golibs

commit a4f90eac8547eea74bfdaa8b1bb23e0502638777
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Apr 28 10:25:33 2025 +0300

    all: upd golibs
2025-05-07 16:07:25 +03:00
Eugene Burkov
b5c47054ab Pull request 2407: Update i18n
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit cb4a2379ee2543391ab85c6dd29bffc083544b2c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue May 6 17:25:50 2025 +0300

    client: upd i18n
2025-05-06 17:50:19 +03:00
Stanislav Chzhen
4776255604 Pull request 2402: AG-40703-fix-custom-cache
Merge in DNS/adguard-home from AG-40703-fix-custom-cache to master

Squashed commit of the following:

commit e9b9aa34d6969e87cc151573912c2f22a1b81cea
Merge: b8ec40b3d e5d0f0b11
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue May 6 16:58:37 2025 +0300

    Merge branch 'master' into AG-40703-fix-custom-cache

commit b8ec40b3dd9f59124bbf5cfc2b303a37750f7497
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue May 6 16:20:48 2025 +0300

    all: upd proxy

commit 026624543c319c022cf5d57d958cc5127cf2a629
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 28 15:46:28 2025 +0300

    all: fix custom cache
2025-05-06 17:07:57 +03:00
Stanislav Chzhen
e5d0f0b119 Pull request 2401: AGDNS-2817-fix-tls-test
Merge in DNS/adguard-home from AGDNS-2817-fix-tls-test to master

Squashed commit of the following:

commit 8cd435e05b6bfb988be90475b28db6703a763b2e
Merge: b9544ba8b af7c2e3a9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 29 19:54:17 2025 +0300

    Merge branch 'master' into AGDNS-2817-fix-tls-test

commit b9544ba8b9097637a3b217142f092a242c819a63
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 28 15:28:05 2025 +0300

    home: fix tls test
2025-04-29 20:03:03 +03:00
Ainar Garipov
af7c2e3a9d Pull request 2403: 7790-fix-cache-label
Closes #7790.

Squashed commit of the following:

commit 9e871bcd8b0dfefbad41cd4e9893f918c9a4b090
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Apr 29 14:48:58 2025 +0300

    client: fix cache label
2025-04-29 14:59:39 +03:00
Stanislav Chzhen
2c46bc92fe Pull request 2399: AGDNS-2686-fix-custom-upstream-cache
Merge in DNS/adguard-home from AGDNS-2686-fix-custom-upstream-cache to master

Squashed commit of the following:

commit 11ad20a225e0e21a59552dc885fbcb2d3acc1cef
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 18:51:35 2025 +0300

    client: imp docs

commit e6d73f2d7a9f2ea181b321dd0029cf6c42ddeba5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 17:36:05 2025 +0300

    all: imp chlog

commit b8fdd884b801db28f03efb00bd871df2332cf40a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 17:05:11 2025 +0300

    client: fix dhcp clients cache

commit 1760699fcb8e61580a48e61037b805b8aa8ca8b4
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 15:37:50 2025 +0300

    all: upd chlog

commit c6f049c200736032e2d78a2023db7d8cc6c32917
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 15:33:53 2025 +0300

    client: imp tests

commit 7432de722292ef74bbdf5fbd875ea67d55b29040
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 24 15:32:26 2025 +0300

    client: fix custom upstream cache
2025-04-25 21:33:36 +03:00
Stanislav Chzhen
61a1403e4e Pull request 2378: AGDNS-2750-find-client
Merge in DNS/adguard-home from AGDNS-2750-find-client to master

Squashed commit of the following:

commit 98f1a8ca4622b6f502a5092273b9724203fe0bd8
Merge: 9270222d8 4ccc2a213
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 23 17:53:20 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 9270222d8e9e03038e9434b54496cbb6164463cd
Merge: 6468ceec8 c7c62ad3b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:58 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6468ceec82d30084771a53ff6720a8c11c68bf2f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:52 2025 +0300

    home: imp docs

commit 3fd4735a0d6db4fdf2d46f3da9794a687fdcaa8b
Merge: 1311a5869 a8fdf1c55
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:43:36 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 1311a58695de00f20c9704378ee6e964a44d1c59
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:42:41 2025 +0300

    home: imp code

commit b1f2c4c883c9476c5135140abac31f8ae6609b4f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 16 16:47:59 2025 +0300

    home: imp code

commit d0a5abd66587c1ad602c2ccf6c8a45a3dfe39a5c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 15 14:58:31 2025 +0300

    client: imp naming

commit 5accdca325551237f003f1c416891b488fe5290b
Merge: 6a00232f7 4d258972d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:40:40 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6a00232f76a0fe5ce781aa01637b6e04ace7250d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:30:32 2025 +0300

    home: imp code

commit 8633886457c6aab75f5676494b1f49d9811e9ab9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 11 15:29:25 2025 +0300

    all: imp code

commit d6f16879e7b054a5ffac59131d2a6eff1da659c0
Merge: 58236fdec 6d282ae71
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:35:23 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 58236fdec5b64e83a44680ff8a89badc18ec81f1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:23:01 2025 +0300

    all: upd ci

commit 3c4d946d7970987677d4ac984394e18987a29f9a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:16:03 2025 +0300

    all: upd go

commit cc1c97734506a9ffbe70fd3c676284e58a21ba46
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 20:58:56 2025 +0300

    all: imp code

commit 8f061c933152481a4c80eef2af575efd4919d82b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 9 16:49:11 2025 +0300

    all: imp docs

commit 8d19355f1c519211a56cec3f23d527922d4f2ee0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 21:35:06 2025 +0300

    all: imp code

commit f1e853f57e5d54d13bedcdab4f8e21e112f3a356
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 2 14:57:40 2025 +0300

    all: imp code

commit 6a6ac7f899f29ddc90a583c80562233e646ba1d6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:51:56 2025 +0300

    client: imp tests

commit 52040ee7393d0483c682f2f37d7b70f12f9cf621
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:28:18 2025 +0300

    all: imp code

commit 1e09208dbd2d35c3f6b2ade169324e23d1a643a5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 26 15:33:02 2025 +0300

    all: imp code

... and 2 more commits
2025-04-23 18:10:52 +03:00
Stanislav Chzhen
4ccc2a2138 Pull request 2398: 7781-fix-serve-plain-dns
Updates #7781.

Squashed commit of the following:

commit 5dff0be1763e7da7dd655bf1e34dfa8402ad96e8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 22 18:15:42 2025 +0300

    home: fix serve plain dns
2025-04-22 20:31:15 +03:00
Eugene Burkov
72425b80a3 Pull request 2397: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 10ff5bce544ef796eb22739c7d20d8bf92b0109f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 22 15:45:45 2025 +0300

    all: upd chlog
2025-04-22 17:39:45 +03:00
Eugene Burkov
c7c62ad3b6 Pull request 2395: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit c4bba4531813bdeb79536f7f601ade80a16a9163
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 21 18:17:06 2025 +0300

    client: upd filters
2025-04-21 18:46:47 +03:00
Stanislav Chzhen
003e7ce0d5 Pull request 2393: 7773-fix-unencrypted_doh
Updates #7773.

Squashed commit of the following:

commit d9ca09c1d9b251998107fc87bd6daeb5999ea803
Merge: b67a71a7a a8fdf1c55
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 15:56:57 2025 +0300

    Merge branch 'master' into 7773-fix-unencrypted_doh

commit b67a71a7a9686d36cbf64a3f7561886bff7d9c5c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 16:01:49 2025 +0300

    home: imp docs

commit dab9b0582ff1ebc4637d5ec1ea3bc81190ed4066
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 15:09:36 2025 +0300

    home: fix unencrypted doh
2025-04-21 16:05:16 +03:00
Stanislav Chzhen
a8fdf1c553 Pull request 2386: AGDNS-2743-aghuser-session
Merge in DNS/adguard-home from AGDNS-2743-aghuser-session to master

Squashed commit of the following:

commit 74fd4bc11eaf784880855fa2c710a747428db146
Merge: 844e865f6 7d479baba
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 18:14:36 2025 +0300

    Merge branch 'master' into AGDNS-2743-aghuser-session

commit 844e865f647efb4de7f057c392894c8f65bab422
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 15:18:44 2025 +0300

    aghuser: imp fmt

commit 584288e0a3ddbe6d7ae31c80c22b8f397cfd0cae
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 17 20:16:54 2025 +0300

    aghuser: imp tests

commit ea4c8735585f6d30d6dedf2a40a8dd6b07609d07
Merge: c3fd8fe5e 3521e8ed9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 17 20:10:06 2025 +0300

    Merge branch 'master' into AGDNS-2743-aghuser-session

commit c3fd8fe5eabaf2022a971197c018e140c254006d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 17 15:23:45 2025 +0300

    aghuser: imp tests

commit dfd9aba337227a8d3edc6f5a68f3f039afd1ca0b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 16 21:40:14 2025 +0300

    aghuser: imp code

commit b6e75223bf7960f3a2e94c1a3ed7cc33539b9806
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 21:49:20 2025 +0300

    aghuser: imp code

commit 56d6f9d478eec399c376992ffb0f1ca5b797986d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 11 16:58:11 2025 +0300

    aghuser: user db

commit 6fdc2f60bf7f93e72d917abb12af8e4867143b6d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 14:11:22 2025 +0300

    all: upd scripts

commit 575946756f3f622360c5feafe3e721eee010e230
Merge: 7e1fac4ec 1cc6c00e4
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 14:05:46 2025 +0300

    Merge branch 'master' into AGDNS-2743-aghuser-session

commit 7e1fac4ecb1bde0013bca3f6b64e82d81a78c9c3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 14:05:35 2025 +0300

    aghuser: session storage

commit acfb040f0bdff501c7304ea100b9faf1c07291ae
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 8 15:54:24 2025 +0300

    aghuser: session
2025-04-18 18:34:10 +03:00
Eugene Burkov
7d479baba6 Pull request 2392: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit c866dd80f8717cfbe886e56b88dd4857c9a305be
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 21:19:55 2025 +0300

    all: upd golibs

commit 3f48ed7909c190228ab78f1aba0453669f65a2ec
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 21:11:41 2025 +0300

    all: upd tools

commit c33cbdd1b7ea12caed672e7b9838e1a531805f19
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 21:09:20 2025 +0300

    client: upd i18n
2025-04-17 21:39:18 +03:00
Eugene Burkov
feb9c886d8 Pull request 2389: AG-38975 Update proxy
Merge in DNS/adguard-home from AG-38975-upd-proxy to master

Squashed commit of the following:

commit 94fc1fb9bb1c004fea17d52f586af263b6918694
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 20:45:12 2025 +0300

    home: fix typo

commit b1cbf45dcc54c6d4dc7de86868c189114caf7cb1
Merge: c2c868b7d 3521e8ed9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 20:44:14 2025 +0300

    Merge branch 'master' into AG-38975-upd-proxy

commit c2c868b7d938eee6e4e4a19d43b2ed734155c7da
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 17 20:19:23 2025 +0300

    all: upd to tag

commit 1680a9d79f34bd42f0b64712b2e7e968890e7c6e
Merge: df42dec3a 4d258972d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 16 14:08:46 2025 +0300

    Merge branch 'master' into AG-38975-upd-proxy

commit df42dec3a302af0e6e301365a943763cd87d752d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Apr 16 13:43:29 2025 +0300

    all: add thanks

commit f212d6a9ee3d614bd6db776204c65298f0359a87
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 14 17:57:33 2025 +0300

    dnsforward: use bool

commit 055072e29dee0673cf38b198476e252bdbade8d9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Apr 11 18:35:38 2025 +0300

    stats: imp doc

commit 6554101a73564b7786cd77d250344ba141c0cbb5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 10 19:32:34 2025 +0300

    all: log changes

commit da8ecf17778be9481232222c8bb7682beea475c6
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Apr 10 19:07:04 2025 +0300

    all: upd proxy, add config
2025-04-17 21:03:21 +03:00
Stanislav Chzhen
3521e8ed9f Pull request 2382: AGDNS-2714-tls-config
Merge in DNS/adguard-home from AGDNS-2714-tls-config to master

Squashed commit of the following:

commit 073e5ec367db02690e9527602a1da6bfd29321a0
Merge: 18f38c9d4 4d258972d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 16 18:25:23 2025 +0300

    Merge branch 'master' into AGDNS-2714-tls-config

commit 18f38c9d44337752c6d0f09142658f374de0979f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 11 15:02:00 2025 +0300

    dnsforward: imp docs

commit ed56d3c2bc239bdc9af000d847721c4c43d173a3
Merge: 3ef281ea2 1cc6c00e4
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 17:25:08 2025 +0300

    Merge branch 'master' into AGDNS-2714-tls-config

commit 3ef281ea28dc1fcab0a1291fb3221e6324077a10
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 17:24:29 2025 +0300

    all: imp docs

commit b75f2874a816d4814d218c3b062d532f02e26ca5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 17:16:59 2025 +0300

    dnsforward: imp code

commit 8ab17b96bca957a172062faaa23b72d5c7ed4d0d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 4 21:26:37 2025 +0300

    all: imp code

commit 1abce97b50fe0406dd1ec85b96a0f99b633325cc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 2 18:22:15 2025 +0300

    home: imp code

commit debf710f4ebbdfe3e4d2f15b1adcf6b86f8dfc0d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 14:52:21 2025 +0300

    home: imp code

commit 4aa26f15b721f2a3f32da29b3f664a02bc5a8608
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 14:16:16 2025 +0300

    all: imp code

commit 1a3e72f7a1276f9f797caf9b615f8a552cc9e988
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 31 21:22:40 2025 +0300

    all: imp code

commit 776ab824aef18ea27b59c02ebfc8620c715a867e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 27 14:00:33 2025 +0300

    home: tls config mu

commit 9ebf912f530181043df5c583e82291484996429a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 26 18:58:47 2025 +0300

    all: tls config
2025-04-16 18:57:04 +03:00
Eugene Miroshkin
4d258972d1 Pull request #2367: ADG-9679 add playwright test from python
Merge in DNS/adguard-home from ADG-9679 to master

Squashed commit of the following:

commit d2a759b4636b7ec931bfba625827c8b91c60c7e7
Merge: a5e7eea16 9726171f0
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Apr 14 16:05:01 2025 +0200

    Merge remote-tracking branch 'origin/master' into ADG-9679

commit a5e7eea16e6c29d25290ee79b1918df8af59bb51
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Apr 14 13:56:26 2025 +0200

    vitest version bump

commit 26620d1923d92b3a6eb9b80a364748f2f6f66030
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:39:11 2025 +0200

    formatting

commit dbab03d1316241eaff0fc9c99d58a1933e415d2b
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:37:55 2025 +0200

    rollback experiments

commit 4427d984177786f7d905915cf8080166b45d7b46
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:33:28 2025 +0200

    checking dir structure

commit 2cf7eed247d2869ed285dbee0bf32cf1d8df7e86
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:21:11 2025 +0200

    fixed docker image builder

commit 8bd06f412fad9dd09df0e076879bd2cbd2f30d1a
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:18:33 2025 +0200

    home-js-builder version bump

commit 2a83bfeb322a20ec4278e359b18d0466966ec043
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 15:17:38 2025 +0200

    try to remove installation dependencies for e2e test (build is already available)

commit 163e4581e83152f99058b798484468009e8e88b0
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 13:52:27 2025 +0200

    Revert "changed nslookup to dig in e2e tests"
    
    This reverts commit ecb68200ea28e295f504338cc59c711b5540022b.

commit 15f7c5e2c77e230da77a0f9de0bd9cce8451da95
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Apr 10 13:45:40 2025 +0200

    js-home-builder version bump

commit ecb68200ea28e295f504338cc59c711b5540022b
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Apr 9 15:07:39 2025 +0200

    changed nslookup to dig in e2e tests

commit 77c94a60be8839f3e0ad9d02e7dbb2ebd802d3d6
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Wed Apr 9 11:09:15 2025 +0300

    revert timeouts

commit 9dfebc8bcaf2cd3258b39fcbd0f67ab51c2eb46d
Merge: 912f4cb7b 1cc6c00e4
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Wed Apr 9 11:02:19 2025 +0300

    merge master

commit 912f4cb7b71f02866244fe447ca0e7fbd2a015bb
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Wed Apr 9 10:48:59 2025 +0300

    cleanup code

commit 9da200ebca5b001f4952f33d819d90c1938920ee
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Thu Apr 3 17:39:20 2025 +0300

    update tests

commit 794e0bd0a92a41c4d3827b716eeab584a25bd3ed
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Thu Mar 13 18:15:58 2025 +0300

    cleanup

commit 9a523b4e255dd24c0f640bc279924ed2c13509a9
Author: Eugene Miroshkin <e.miroshkin@adguard.com>
Date:   Thu Mar 13 18:04:34 2025 +0300

    ADG-9679 add playwright test from python
2025-04-14 18:03:45 +03:00
Eugene Burkov
9726171f0f Pull request 2391: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 6bae7efe20df984f2abd010add83fcc94a76d848
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Apr 14 14:54:23 2025 +0300

    all: upd chlog
2025-04-14 15:13:22 +03:00
Ainar Garipov
6d282ae716 Pull request 2390: 7588-fix-old-docker
Updates #7588.

Squashed commit of the following:

commit 9add0323ad9fa4ce98a114dd43aa21ec6940ce11
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 10 20:11:39 2025 +0300

    all: fix old docker buildx
2025-04-10 20:26:22 +03:00
Ainar Garipov
6a99c39d11 Pull request 2388: 7588-upd-docker
Updates #7588.

Squashed commit of the following:

commit ce282e4e079e0d18e2acf0fe412c849dfe8ce8d6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 10 19:31:52 2025 +0300

    scripts: imp build-docker

commit 054fa74d4fe3951129d43e524f713bab610ad86e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 10 17:24:01 2025 +0300

    all: upd base docker
2025-04-10 20:01:14 +03:00
Eugene Burkov
1cc6c00e4b Pull request 2385: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit 05de6cd84465ead8f2c6d94ba22766c37cd51a34
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 8 15:59:00 2025 +0300

    all: fix docs

commit d87020e1674e83043841dc7a95b7e609533ba848
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 8 15:38:08 2025 +0300

    client: upd i18n

commit ad054dedffbbe9ef2782358298afe9ffb6b98ad2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 8 15:24:09 2025 +0300

    all: upd deps

commit 69f13bfb14414fed8d8ef1976628d4558a4207cc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Apr 8 14:53:30 2025 +0300

    all: upd go, tools
2025-04-08 17:20:47 +03:00
Stanislav Chzhen
1cb6634d67 Pull request 2383: AGDNS-2743-aghuser
Merge in DNS/adguard-home from AGDNS-2743-aghuser to master

Squashed commit of the following:

commit e3920df62be1625a3cfcc314a4aab3d1a378ca53
Merge: 70ce647f4 106785aab
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 19:44:15 2025 +0300

    Merge branch 'master' into AGDNS-2743-aghuser

commit 70ce647f47921f2bb34a561d63de2041f31e6bce
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 4 18:17:09 2025 +0300

    aghuser: imp docs

commit 87f6984248189de4a3dc0f2a245775141ea974d0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 2 19:03:03 2025 +0300

    aghuser: imp code

commit 636ecae85d1fce1657b5699a29451a9079d40222
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 17:30:54 2025 +0300

    all: add tests

commit 5c842e94111123cf988332ccd1eb6754fa45585d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 27 21:44:25 2025 +0300

    all: aghuser
2025-04-07 20:41:42 +03:00
Stanislav Chzhen
106785aab0 Pull request 2384: 7734-fix-dhcp-client-filtering
Updates #7734.

Squashed commit of the following:

commit 11e091895bdebc1717bee47827322af56edea3a7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 15:24:07 2025 +0300

    client: fix querylog search

commit 028cf11c480e9831f3925e600d1876ae719d5800
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 4 15:45:19 2025 +0300

    all: upd chlog

commit ee1a70c160d3d7eca5c363708f57916d6df98146
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 4 15:33:34 2025 +0300

    client: fix dhcp client filtering
2025-04-07 19:03:30 +03:00
Ainar Garipov
295b2fefb1 Pull request 2380: ADG-9744-node-20
Squashed commit of the following:

commit 16e43b10edc8218f3d1ccec711d965c654dd851d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 28 12:32:58 2025 +0300

    all: upd chlog

commit 2a8dcab14e9cf43fe941f67f215efe2801f2aa65
Merge: 95a4ea8cc 8b4768aad
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 28 12:32:05 2025 +0300

    Merge branch 'master' into ADG-9744-node-20

commit 95a4ea8ccbcc8cb5911b6c33007e36a04b03e616
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 24 14:06:25 2025 +0300

    bamboo-specs: fix test

commit c535028cd16bf2100b45828fe7f7693b938aaeb5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Mar 24 14:02:47 2025 +0300

    all: imp npm cache

commit bfc9d11153446b865b9756f35753e791d4040595
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 21 19:06:37 2025 +0300

    all: fix specs

commit 7127498d6aa0e0ca4585d79a15ed5f3c5e643e3a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Mar 21 18:50:57 2025 +0300

    all: upd node; rm outdated openapi lint
2025-03-28 14:54:02 +03:00
Stanislav Chzhen
8b4768aadd Pull request 2371: AGDNS-2714-tls-manager
Merge in DNS/adguard-home from AGDNS-2714-tls-manager to master

Squashed commit of the following:

commit 5c7cd1fa6d8a9bc1fd0f891818589b48bee641dc
Merge: 381f7666b 810ae9483
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 26 14:13:49 2025 +0300

    Merge branch 'master' into AGDNS-2714-tls-manager

commit 381f7666b063d225b114976a280e65df736495fe
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 25 19:53:12 2025 +0300

    home: imp code

commit 20be72abd449fcc76417381edf7d375248a11e9e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 25 19:19:51 2025 +0300

    home: imp code

commit b5a06e6a15b0f8511819267133a551a56e051499
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 24 21:45:41 2025 +0300

    home: imp code

commit a6a5ba727ebbc59d6de4d3762ac196d2cf194875
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 20 21:06:34 2025 +0300

    home: imp docs

commit 71d379bafc3f42377ce72add2cab3a56a796941d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 20 20:47:15 2025 +0300

    all: upd chlog

commit be69a5b85d4cd4295a9b68e1c2c2205179a3e7f2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 19 20:14:20 2025 +0300

    home: imp docs

commit 85b28db73b59b90365ff23fc5fc90dc1a10cc152
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 19 20:07:59 2025 +0300

    home: imp code

commit c11e4c9e500f7ead96a84575dac08e198569c14d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 19 19:11:59 2025 +0300

    home: imp code

commit 60eff2c66369ca8705a6bb859b5a65d3e6d0df5e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 21:27:49 2025 +0300

    home: imp code

commit fa9d57b2834fe3df85630d95b9eb022f1db372b1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 21:14:56 2025 +0300

    home: imp docs

commit 3f561b64750ab57ef83793522a0b313225245e1e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 20:59:59 2025 +0300

    home: imp code

commit 927296c49f861d102dad8d24e8b67e6204a6c17a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 18:19:22 2025 +0300

    home: imp naming

commit e35f742e42a7304993a924928b51f2452634e258
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 17:53:17 2025 +0300

    home: tls manager web api

commit 85a4de7931fea68464fe36c1fb27686eb5b50066
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 18 15:06:34 2025 +0300

    home: tls manager config

commit 515b26d6bd6d837d3db937354f74d895b5793206
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 22:15:25 2025 +0300

    home: tls manager
2025-03-26 14:26:57 +03:00
Ainar Garipov
810ae94832 Pull request 2381: 7729-login-label
Closes #7729.

Squashed commit of the following:

commit 110137ce9a958cc4e36d82d4c6fc38c4ec16bde7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 25 15:50:25 2025 +0300

    client: fix login form labels
2025-03-25 16:51:08 +03:00
Eugene Burkov
fd337967f4 Pull request #2370: AGDNS-2746 Update proxy
Merge in DNS/adguard-home from bind-retry to master

Squashed commit of the following:

commit 42b782368e9eee924b43667ac4f4329fec8ba144
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 21 16:08:25 2025 +0300

    all: upd deps

commit 962d5c381f30c20e6bed6b58922069372ea0428d
Merge: 2e8fdea1b 4adf2bcf0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 21 16:06:46 2025 +0300

    Merge branch 'master' into bind-retry

commit 2e8fdea1ba8244e20f23ca1bf0ef80a347be5a7a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 21 16:06:29 2025 +0300

    all: revert changes

commit 135897b879e29f943d1d2d7d18242579d5b59f22
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 14 15:56:26 2025 +0300

    all: upd proxy
2025-03-21 18:45:06 +03:00
Eugene Burkov
4adf2bcf00 Pull request 2379: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 2daf656ed9d13b2770015f513d797849fab0565a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 21 14:30:02 2025 +0300

    all: move link

commit 026ffc320d3502b4627e3b943ecf2e308f3447de
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 21 14:24:49 2025 +0300

    all: upd chlog
2025-03-21 14:48:12 +03:00
Ainar Garipov
1eb1b1108c Pull request 2376: 7708-fix-client-addr
Updates #7708.

Squashed commit of the following:

commit 4dd61516c683e3c7eea7beed7e001f37726cae75
Merge: b0763a90b 85e6bf54c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 20 14:03:11 2025 +0300

    Merge branch 'master' into 7708-fix-client-addr

commit b0763a90b70d2f1da6d31e2c5215509d4010220b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 20 13:14:44 2025 +0300

    all: fix client rules
2025-03-20 15:50:04 +03:00
Igor 🐧 Lobanov
85e6bf54c9 Pull request 2375: ADG-9850 query-log-search-form-fix
Updates #7704.

Squashed commit of the following:

commit 16ef53ea6bb687139bc62ffee0a8b87fee676f1c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Mar 20 13:24:48 2025 +0300

    all: upd chlog

commit d0c0935f30683841aaa79ffc149fe5e31938724b
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Mar 19 19:02:13 2025 +0100

    fixed query log search form
2025-03-20 13:57:31 +03:00
Eugene Burkov
cb5de5c653 Pull request 2374: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 913df6009cd12af393f66456760898a622bf2b9b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Mar 19 19:36:50 2025 +0300

    all: upd chlog
2025-03-19 19:54:48 +03:00
Igor 🐧 Lobanov
403fa9b1fe Pull request #2373: rc-v0.107.58-hotfix
Merge in DNS/adguard-home from rc-v0.107.58-hotfix to master

Squashed commit of the following:

commit 1837650f36fff704a4e1fa38c0c560f4e14a183d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Mar 19 13:41:16 2025 +0100

    fixed search field and get parameters sync in logs
2025-03-19 16:42:51 +03:00
Eugene Burkov
2f32b97d2f Pull request 2372: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit 8b29c642c3b4f4d3d4a28cbe3ebc02bb941d0b82
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 18 14:02:23 2025 +0300

    all: fix changelog fmt

commit ef72e9d0fd761796409978cc0d10d38683f5966c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 18 14:01:01 2025 +0300

    client: upd trackers

commit bcf9bfac54004f35ba9b85800b90af4cf9c81e8a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Mar 18 13:59:50 2025 +0300

    client: upd i18n
2025-03-18 14:11:11 +03:00
Stanislav Chzhen
19d2fd47e2 Pull request 2369: AGDNS-2714-tls-manager-logger
Merge in DNS/adguard-home from AGDNS-2714-tls-manager-logger to master

Squashed commit of the following:

commit 4642b731c2609fffd8bbd92e073916d930c3d50f
Merge: 986aebff7 f82dee17f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 18:48:23 2025 +0300

    Merge branch 'master' into AGDNS-2714-tls-manager-logger

commit 986aebff793a1d3146a1575abff0eb8878739ff6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 15:42:59 2025 +0300

    home: imp code

commit 3c985959debca14cbaad938a9849ad84e0daa4bc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 13 19:38:06 2025 +0300

    home: tls manager logger
2025-03-17 19:14:52 +03:00
Stanislav Chzhen
f82dee17f0 Pull request 2365: AGDNS-2714-tls-manager-tests
Merge in DNS/adguard-home from AGDNS-2714-tls-manager-tests to master

Squashed commit of the following:

commit 2a3c6558a4098eb6b531e792884e5ca2bc2dd362
Merge: 85d72559c 1a3853d52
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 18:07:49 2025 +0300

    Merge branch 'master' into AGDNS-2714-tls-manager-tests

commit 85d72559c371d4f14b40077d9aec69afa8dc7e73
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 17:55:41 2025 +0300

    home: imp tests

commit 9ad19e3cee255b157992e4045f4e27fa5aa54325
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 16:21:47 2025 +0300

    home: imp code

commit 8a05bc01998206bf6f3be8b3f0bd8f283158aeab
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 17 15:08:58 2025 +0300

    home: imp tests

commit 85173f986d4c58d8ec8cfbc799317623d9dfdf31
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 13 18:18:56 2025 +0300

    home: add tests

commit add531ea17fd771c071e073757f6021f324c9c75
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 11 19:55:51 2025 +0300

    home: tls manager tests
2025-03-17 18:16:33 +03:00
Stanislav Chzhen
1a3853d52a Pull request 2353: AGDNS-2688-check-host
Merge in DNS/adguard-home from AGDNS-2688-check-host to master

Squashed commit of the following:

commit bd9ed498b0e36fa044e6921fa946062ac40fe616
Merge: 8dffd94a3 c41af2763
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 14 13:42:34 2025 +0300

    Merge branch 'master' into AGDNS-2688-check-host

commit 8dffd94a3bc700cf014cbb16aee9c6339bdc7ffa
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 12 17:12:56 2025 +0300

    filtering: imp code

commit d9a01c8fa60c70e3fd19c40c1a58aec00ae64a6a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 11 20:33:18 2025 +0300

    all: imp code

commit f1aca5f2eb71a1d8bb155a309c618e7a80f8fde5
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Mar 11 16:05:32 2025 +0300

    ADG-9783 update check form

commit a8ebb0401dbaa08fdd04171b1ac66b87d0228c7b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 16:41:55 2025 +0300

    dnsforward: imp docs

commit 36f5db9075cc525c13905e0318dfbc4089355523
Merge: 9a746ee9a 66fba942c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 16:09:22 2025 +0300

    Merge branch 'master' into AGDNS-2688-check-host

commit 9a746ee9a05895676a60980eb4bd1381fe8d8e4b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 16:06:48 2025 +0300

    all: imp docs

commit 0a25e1e8f3536053e30049497bb42a58c6a153d6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 6 21:48:44 2025 +0300

    all: imp code

commit ec618bc484190dde52a0dc57d144bade8dfc22e2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 6 17:38:35 2025 +0300

    all: imp code

commit 979c5cfd4c34e2aac46ea11b7fcba8d2929966b8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 5 21:22:54 2025 +0300

    all: add tests

commit ce0d6117ad7f341edcc018a68acedaa0b718bef1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 4 15:13:06 2025 +0300

    all: check host
2025-03-14 13:51:45 +03:00
Stanislav Chzhen
c41af2763f Pull request 2366: AG-40702-fix-cache-clear
Merge in DNS/adguard-home from AG-40702-fix-cache-clear to master

Squashed commit of the following:

commit 542a7946a9a92f64a56b1736e7411f83a42b3def
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 12 18:16:35 2025 +0300

    all: fix cache clear
2025-03-12 20:23:54 +03:00
Stanislav Chzhen
ee91a6084f Pull request 2361: imp-test-file-names
Merge in DNS/adguard-home from imp-test-file-names to master

Squashed commit of the following:

commit a0827efdf633fba012c5eb0e0f69eaabf7629724
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 21:41:46 2025 +0300

    all: imp tests

commit 21fc274d9276ce0442572261ea39a1c018490870
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 19:40:40 2025 +0300

    all: imp test file names
2025-03-11 19:40:14 +03:00
Eugene Burkov
ac8a0ec570 Pull request 2360: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit a9ec07f3810d94f1119d727a317db05a5f0d2d81
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 10 18:26:30 2025 +0300

    client: upd data

commit f1cba36a0048e47f28a9ed481d51529e82e24283
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Mar 10 18:18:46 2025 +0300

    all: upd proxy
2025-03-10 18:38:11 +03:00
Stanislav Chzhen
3255efcaf3 Pull request 2354: AGDNS-2690-global-context-tls
Merge in DNS/adguard-home from AGDNS-2690-global-context-tls to master

Squashed commit of the following:

commit ae1d9e6f3f3b8abefbc5e776eb256577f7fbbb0f
Merge: 6f30f488a bf9be98c7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 18:15:24 2025 +0300

    Merge branch 'master' into AGDNS-2690-global-context-tls

commit 6f30f488aa2305e518000dc6c1028ede83bf1cc6
Merge: baa187ab0 66fba942c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 15:08:47 2025 +0300

    Merge branch 'master' into AGDNS-2690-global-context-tls

commit baa187ab0b6db7f41e49dece7b4d0430409e7cae
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 10 15:08:39 2025 +0300

    home: imp docs

commit 96a09389c5049a84bb30ed285cc5e1df9aaa438f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 6 20:15:05 2025 +0300

    home: imp docs

commit 1cd007707af4a7a5160c8fe21b20b84543d59e5a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Mar 6 18:54:07 2025 +0300

    home: imp docs

commit ad3d2b6616c2c3aba566a2158ffc597e5802929f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 4 19:38:45 2025 +0300

    home: global context tls
2025-03-10 18:24:41 +03:00
Eugene Burkov
bf9be98c71 Pull request 2357: 7555-fix-ra-packets
Merge in DNS/adguard-home from 7555-fix-ra-packets to master

* commit '0ed2cd04b22a7b447380ee863bc908603720877d':
  dhcpd: imp docs
  all: fix changelog
  dhcpd: imp docs
  all: imp code & test, log changes
  Change comment message to match implementation
  dhcpd: Fix slaac configuration which was not working due to invalid icmp packet
2025-03-10 16:30:15 +03:00
Eugene Burkov
0ed2cd04b2 Merge branch 'master' into 7555-fix-ra-packets 2025-03-10 16:17:54 +03:00
Ildar Kamalov
66fba942c8 Pull request: ADG-9775 fix disabled button for autofilled login form
Merge in DNS/adguard-home from ADG-9775 to master

Squashed commit of the following:

commit 3544ba8d463d80c69285fba4af1332e80cd2f0c8
Merge: 9631f6c4d 7f9cef948
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Mar 10 13:55:27 2025 +0300

    Merge branch 'master' into ADG-9775

commit 9631f6c4d4e32a5f241ff30b0db9f51fcc7635dc
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Mar 10 12:30:25 2025 +0300

    add changelog

commit 49fa7fe189f69758011cbdf8f78744bef5fcd0a0
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Mar 7 17:02:45 2025 +0300

    ADG-9775 fix disabled button for autofilled login form
2025-03-10 14:25:56 +03:00
Eugene Burkov
7f9cef948c Pull request 2358: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit fb5e87e0cb5617d031a2dac932304917722b1a89
Merge: af4ef937e 64994c7fc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 7 18:30:39 2025 +0300

    Merge branch 'master' into upd-all

commit af4ef937ee9ae1046cda083a4b0cb6b41ca3dc8c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Mar 7 12:50:48 2025 +0300

    all: log changes, revert trackers

commit ca1197dc69bb845daa5ec3f25d58d995f3d330ef
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 6 19:33:24 2025 +0300

    client: upd i18n

commit d6aa69668633afcabdf956a3e82b920077ddbc75
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 6 19:24:57 2025 +0300

    client: upd vetted filters, companiesdb, blocked services

commit ed6f706c8eafe248b1e851bb0b123f1c46795414
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 6 19:20:32 2025 +0300

    ipset: add bench results

commit 89c1fbe257163aebcddd1abf85b8e4d87536e0ef
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Mar 6 19:14:34 2025 +0300

    all: upd go, tools
2025-03-07 18:51:31 +03:00
Eugene Burkov
3be90f7b75 Merge branch 'master' into 7555-fix-ra-packets 2025-03-07 18:32:14 +03:00
Ildar Kamalov
64994c7fcb Pull request: ADG-9715 formatting of elapsed times less than one millisecond
Merge in DNS/adguard-home from ADG-9715 to master

Squashed commit of the following:

commit d972608ad5429810e0f5ad430f11e037cd05ee40
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Mar 6 12:46:29 2025 +0300

    ADG-9715 formatting of elapsed times less than one millisecond
2025-03-07 16:42:37 +03:00
Eugene Burkov
4b0db6d397 dhcpd: imp docs 2025-03-07 13:01:34 +03:00
Eugene Burkov
777b310a4b all: fix changelog 2025-03-06 16:12:10 +03:00
Eugene Burkov
148a407f6c Merge branch 'master' into 7555-fix-ra-packets 2025-03-06 16:03:25 +03:00
Eugene Burkov
ed947a048e dhcpd: imp docs 2025-03-06 15:54:02 +03:00
Eugene Burkov
74640fb06c all: imp code & test, log changes 2025-03-06 15:15:06 +03:00
Stanislav Chzhen
0d2163c1d6 Pull request 2355: AGDNS-2686-client-manager-clock
Merge in DNS/adguard-home from AGDNS-2686-client-manager-clock to master

Squashed commit of the following:

commit 1d3cafa7f1036a72b766feaee1db00398e51c364
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Mar 4 20:52:39 2025 +0300

    all: client manager clock
2025-03-04 21:18:22 +03:00
Stanislav Chzhen
318bd2901a Pull request 2346: AGDNS-2686-client-upstream-manager
Merge in DNS/adguard-home from AGDNS-2686-client-upstream-manager to master

Squashed commit of the following:

commit 563cb583f01c26434fa04d0e37dcbe2ba15c0912
Merge: f4b0caf5c 61fe269cb
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 3 19:07:35 2025 +0300

    Merge branch 'master' into AGDNS-2686-client-upstream-manager

commit f4b0caf5c8bc48ee8be97f031cd1aa1399eb461c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 27 21:52:51 2025 +0300

    client: imp docs

commit e7d74931b1cc9b62eeadbe1168ae5781d57d6c73
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 26 21:44:04 2025 +0300

    client: imp code

commit 1cba38c1bc3b6b5afb7829c230c4e831f789647e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 26 18:06:17 2025 +0300

    client: fix typo

commit 65b6b1e8c0fde47f367c428a78fefc4c63bc45f9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 26 17:52:02 2025 +0300

    all: imp code, docs

commit ed158ef09fc26bc9c57c91dbfa04d89fede583d0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 26 14:34:50 2025 +0300

    client: imp code

commit ab897f64c8751ea158408521116d5b689e6d39a9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 25 18:26:16 2025 +0300

    all: upd chlog

commit a2c30e3ede6fb61f6d23fd392cc3035dc96f77af
Merge: bdb08ee0e d8ce5b453
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 25 17:40:32 2025 +0300

    Merge branch 'master' into AGDNS-2686-client-upstream-manager

commit bdb08ee0e6122de727f2749a44f5df7e29d0eee2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 25 17:16:31 2025 +0300

    all: imp tests

commit 00f0eb60474a2297567acf5a3a27e8b5c2d99229
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 20 21:37:58 2025 +0300

    all: imp code, docs

commit 13934176636dd70a17e53bc1956d6cf51602760a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 19 15:58:11 2025 +0300

    all: client upstream manager
2025-03-03 19:19:46 +03:00
Stanislav Chzhen
61fe269cb8 Pull request 2352: AGDNS-2690-signal-handler
Merge in DNS/adguard-home from AGDNS-2690-signal-handler to master

Squashed commit of the following:

commit b6822142312ac814e7c206218bb079a4f364192a
Merge: f597ea1fd 8b2ab8ea8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Mar 3 18:10:27 2025 +0300

    Merge branch 'master' into AGDNS-2690-signal-handler

commit f597ea1fd27c1153f2b08a339fd17361807627e6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 27 20:45:55 2025 +0300

    all: fix logger

commit 582e315ecac45e49e156bddb5755f5fb7114111c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 27 18:40:57 2025 +0300

    home: imp code

commit 1e0f48d32a654b71a9c0a9e3cf3afceab00bc7e5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 27 15:26:19 2025 +0300

    home: imp docs

commit aa43bbc1b20392429d606de5cfd1b1fed940755a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 24 17:24:33 2025 +0300

    home: signal handler
2025-03-03 18:23:01 +03:00
Ildar Kamalov
8b2ab8ea87 Pull request 2322: ADG-9415
Merge in DNS/adguard-home from ADG-9415 to master

Squashed commit of the following:

commit 76bf99499a
Merge: 29529970a 0389515ee
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Feb 26 18:31:41 2025 +0300

    Merge branch 'master' into ADG-9415

commit 29529970a3
Merge: b49790daf 782a1a982
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Feb 24 15:44:38 2025 +0300

    Merge branch 'master' into ADG-9415

commit b49790daf8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 15:30:18 2025 +0300

    fix default lease duration value

commit cb307472ec
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 10:35:26 2025 +0300

    fix default response status

commit 115e743e1a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 24 10:32:46 2025 +0300

    fix upstream description

commit 26b0eddaca
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 18 17:40:41 2025 +0300

    use const for test config file

commit 58faa7c537
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 18 17:31:04 2025 +0300

    fix install config

commit 0a3346d911
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Feb 17 15:25:23 2025 +0300

    fix install check config

commit 17c4c26ea8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 17:18:20 2025 +0300

    fix query log

commit 14a2685ae3
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 15:52:36 2025 +0300

    fix dhcp initial values

commit e7a8db7afd
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 14:37:24 2025 +0300

    fix encryption form values

commit 1c8917f7ac
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 14:07:29 2025 +0300

    fix blocked services submit

commit 4dfa536cea
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Feb 14 13:50:47 2025 +0300

    dns config ip validation

commit 4fee83fe13
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Feb 12 17:49:54 2025 +0300

    add playwright warning

commit 8c2f36e7a6
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 18:36:18 2025 +0300

    fix config file name

commit 83db5f33dc
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 16:16:43 2025 +0300

    temp config file

commit 9080c1620f
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 15:01:46 2025 +0300

    update readme

commit ee1520307f
Merge: fd12e33c0 2fe2d254b
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Feb 11 14:44:06 2025 +0300

    Merge branch 'master' into ADG-9415

commit fd12e33c06
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Feb 10 10:29:43 2025 +0100

    added typecheck on build, fixed eslint

commit b3849eebc4
Merge: 225167a8b 9bf3ee128
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Feb 10 09:43:32 2025 +0100

    Merge branch 'ADG-9415' of https://bit.int.agrd.dev/scm/dns/adguard-home into ADG-9415

... and 94 more commits
2025-02-26 19:37:52 +03:00
Ainar Garipov
0389515ee3 Pull request 2351: 7583-clients-format-2
Updates #7583.

* commit 'cd5dd1eb167d9bca014b2ff45f6826e7969d7b5c':
  refactor: fix conditional check for content formatting in statistics column
2025-02-26 14:31:49 +03:00
Ainar Garipov
cd5dd1eb16 Merge branch 'master' into 7583-clients-format-2 2025-02-24 16:42:06 +03:00
Ainar Garipov
d8ce5b453c Pull request 2349: 7583-clients-format
Updates #7583.

* commit 'e9d4e76828261622dfb424c760246a21b73040e8':
  all: upd chlog
  refactor: Format value requests_count on client table
  refactor: Format value clientstats
2025-02-24 16:17:15 +03:00
Ainar Garipov
782a1a9820 Pull request 2350: 7550-riscv-install
Updates #7550.

* commit '54cc8f506f660e97a8b9e3cd370e123d73986de4':
  correct indention in install.sh
  add riscv64 to install.sh
2025-02-20 21:12:55 +03:00
Ainar Garipov
54cc8f506f Merge branch 'master' into 7550-riscv-install 2025-02-20 20:37:56 +03:00
bankjirapan
64fe768725 refactor: fix conditional check for content formatting in statistics column 2025-02-20 23:07:59 +07:00
Ainar Garipov
e9d4e76828 all: upd chlog 2025-02-20 18:55:24 +03:00
Ainar Garipov
4d2eba0f29 Merge branch 'master' into 7583-clients-format 2025-02-20 18:54:03 +03:00
Ainar Garipov
4c65b03844 Pull request 2348: upd-chlog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 3937386d8afdf5e04cddec5e0bd5ac47dad7034d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Feb 20 18:14:30 2025 +0300

    all: upd chlog
2025-02-20 18:26:53 +03:00
Stanislav Chzhen
1e0873aa71 Pull request 2347: AGDNS-2690-global-context
Merge in DNS/adguard-home from AGDNS-2690-global-context to master

Squashed commit of the following:

commit 58d5999e5d9112b3391f988ed76e87eff2919d6b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 19 18:51:41 2025 +0300

    home: imp naming

commit cfb371df59c816be1022d499cc41ffaf2b72d124
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 19 18:42:52 2025 +0300

    home: global context
2025-02-19 19:02:56 +03:00
Ainar Garipov
a5b073d070 Pull request 2344: fix-chlog
Merge in DNS/adguard-home from fix-chlog to master

Squashed commit of the following:

commit 3f209c99936f40ec67acd70e64f53368adac83dd
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 18 19:45:25 2025 +0300

    all: fix chlog
2025-02-18 19:54:36 +03:00
Ainar Garipov
9c5a7a5544 Pull request 2343: upd-i18n
Merge in DNS/adguard-home from upd-i18n to master

Squashed commit of the following:

commit da6c09136c3e2495a6117f92752f18b190ce4f7b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Feb 18 17:53:36 2025 +0300

    client: upd i18n
2025-02-18 18:17:47 +03:00
Ainar Garipov
da8132e127 Pull request 2342: 7627-dhcp-client-hostname
Updates #7627.

Squashed commit of the following:

commit 5eb677f5b91d2664094d759b4b07c64c1889c57b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 14 17:21:35 2025 +0300

    client: fix test

commit e7c6ee81ef9aa6627538c82f150f4707df92d820
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 14 17:00:00 2025 +0300

    client: imp test

commit b59ca8b3ca4d00007d5c48b96fcc630342414ae7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 14 16:53:02 2025 +0300

    client: fix dhcp
2025-02-18 14:09:14 +03:00
Stanislav Chzhen
2fe2d254b5 Pull request 2338: AGDNS-2665-upstream-timeout
Merge in DNS/adguard-home from AGDNS-2665-upstream-timeout to master

Squashed commit of the following:

commit b4041c97f2ccd371a9cff8312bda72f4c18496b9
Merge: d939b3b3e b92a3cfb8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 7 16:15:19 2025 +0300

    Merge branch 'master' into AGDNS-2665-upstream-timeout

commit d939b3b3ef2345d315c05a3730e81e1f1ea9f79f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 7 16:10:06 2025 +0300

    client: imp i18n

commit 52089adb6c28d8c47f5a916d9df941c5fddea1fc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Feb 6 18:39:53 2025 +0300

    all: fix typo

commit aca08b1873c9f39b8c1da0439a9057117e1dacce
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Feb 6 15:19:30 2025 +0300

    client: rearrange upstream timeout settings

commit 48e75fabe2f65aeac7156a020cce3d4c2412e5f9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 4 20:48:53 2025 +0300

    github: upd actions cache

commit 64bac193fc834a975e6913bb86a6bf27e54c382a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Feb 4 20:28:54 2025 +0300

    all: imp code

commit 95c73698985dc528a4af6ff0d3b8a301da1f0d4a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 3 16:25:35 2025 +0300

    all: upstream timeout
2025-02-10 16:07:11 +03:00
Ainar Garipov
b92a3cfb89 Pull request 2339: upd-deps
Merge in DNS/adguard-home from upd-deps to master

Squashed commit of the following:

commit 40d509625c0099c34e6ff14f69b0ab1535b9c51f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 7 13:19:24 2025 +0300

    all: upd go, deps
2025-02-07 13:49:29 +03:00
Stanislav Chzhen
d3dea0f46c 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 <s.chzhen@adguard.com>
Date:   Thu Jan 30 14:40:06 2025 +0300

    chlog: fix typo

commit 73764b79bda4dd26695160ab7cee6f222ed0e3a9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jan 30 14:14:56 2025 +0300

    chlog: fix typo

commit 5900b360feb12ced95d10fd11510d81450cd5443
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jan 30 14:10:34 2025 +0300

    all: upd chlog

commit 4a9d79a2596839d16f18e45dd13939cdc011361e
Merge: 76d037122 cc2006f61
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jan 30 14:01:46 2025 +0300

    Merge branch 'master' into AGDNS-2627-query-statistics

commit 76d0371220c3a6268e0fb0e775a01ab5a609e443
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jan 27 20:32:44 2025 +0300

    all: upd chlog

commit 496cbba94ec8c1684001f8ed0245b51a73d5bffe
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jan 23 19:44:26 2025 +0300

    all: imp code

commit 4795a2c8fbf49cede106641e97892c008dccd2e9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jan 21 17:48:00 2025 +0300

    all: query statistics
2025-01-30 15:08:01 +03:00
Ildar Kamalov
cc2006f616 Pull request #2337: ADG-9565
Merge in DNS/adguard-home from ADG-9565 to master

Squashed commit of the following:

commit ef92c2e891
Merge: a4364ff7e 91270d0b6
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Jan 29 13:42:29 2025 +0300

    Merge branch 'master' into ADG-9565

commit a4364ff7ed
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Jan 29 13:39:14 2025 +0300

    ADG-9565 format large numbers in the upstream table and query log
2025-01-29 18:08:39 +03:00
Ainar Garipov
91270d0b61 Pull request 2336: 7600-fix-time-in-enable-protection-timer
Closes #7599.
Updates #7600.

* commit 'a4ea6c22335dff3a0f9cfba1ccf9db65a1e6db71':
  fix: time in `enable_protection_timer` translation
2025-01-28 21:39:16 +03:00
Ainar Garipov
a4ea6c2233 Merge branch 'master' into 7600-fix-time-in-enable-protection-timer 2025-01-28 21:30:43 +03:00
Eugene Burkov
c7b65fa522 Pull request 2335: Update changelog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit ea263dbf3e2d409ae9a982b89ee7976de96138b1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jan 28 20:53:44 2025 +0300

    all: upd chlog
2025-01-28 21:10:48 +03:00
Jan Pieper
1a95161784 fix: time in enable_protection_timer translation 2025-01-28 09:53:58 +01:00
bankjirapan
84d72cb842 refactor: Format value requests_count on client table 2025-01-21 14:45:31 +07:00
bankjirapan
acbad67f47 refactor: Format value clientstats 2025-01-21 14:16:22 +07:00
Stanislav Chzhen
6633ad6304 Pull request 2333: fix-test
Merge in DNS/adguard-home from fix-test to master

Squashed commit of the following:

commit cddaed40273e81677eec33e2ededfd4556c5b99f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jan 20 18:33:43 2025 +0300

    filtering: fix test
2025-01-20 18:57:51 +03:00
Eugene Burkov
dfa28af907 Pull request 2331: Update all
Merge in DNS/adguard-home from upd-all to master

Squashed commit of the following:

commit 6610425055168c577adb8fe6ccb0557ed867c059
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 20 12:53:21 2025 +0300

    client: upd i18n

commit a1d669e3ec7cab80c4bc5fff0fbba9ff7dfdd136
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jan 20 12:46:05 2025 +0300

    client: upd trackers
2025-01-20 13:08:43 +03:00
Eugene Burkov
8f75c6ac9d Pull request 2330: Upd Go
Merge in DNS/adguard-home from upd-go to master

Squashed commit of the following:

commit 58aad666b56d7100c7b5291fb8a100fe635e6d62
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jan 17 19:02:06 2025 +0300

    all: upd go
2025-01-17 19:24:56 +03:00
Ildar Kamalov
446f21a511 Pull request 2328: ADG-9458 fix request count link in the clients table
Updates #7513.

Squashed commit of the following:

commit 2b7f6bd4d0e1d8855faacf08cc0dff3e4e6d2bbd
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:28:11 2025 +0300

    fix

commit 86f3f32272975a199a8fb3867f3e776f71704916
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:27:34 2025 +0300

    fix

commit 786c56828f95c66275a19d816759bacc137041f8
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jan 9 15:26:38 2025 +0300

    ADG-9458 fix request count link in the clients table
2025-01-10 16:21:34 +03:00
dervomsee
0211a952ea correct indention in install.sh 2025-01-08 08:10:52 +01:00
dervomsee
f3161d0c05 add riscv64 to install.sh 2025-01-05 22:08:45 +01:00
Gunjan Gupta
0c72cde4c3 Change comment message to match implementation
Signed-off-by: Gunjan Gupta <viraniac@gmail.com>
2025-01-04 01:50:00 +05:30
Gunjan Gupta
b1657c2b2a dhcpd: Fix slaac configuration which was not working due to invalid icmp packet
Signed-off-by: Gunjan Gupta <viraniac@gmail.com>
2025-01-04 01:12:28 +05:30
Stanislav Chzhen
0b25119c52 Pull request #2327: AGDNS-2622-fix-race
Merge in DNS/adguard-home from AGDNS-2622-fix-race to master

Squashed commit of the following:

commit 28170e67655048d7356d6cfce07e04017a933163
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 23 17:24:28 2024 +0300

    all: upd proxy

commit 3bd9458220e04041c5135fa97d55e6e6e6c29071
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Dec 20 21:19:20 2024 +0300

    all: upd proxy
2024-12-23 19:12:55 +03:00
Ainar Garipov
20e56b7171 Pull request 2326: more-md-lint
Merge in DNS/adguard-home from more-md-lint to master

Squashed commit of the following:

commit 39e7ea3b441ebf48c5b0d5c2b5b85620515bbea3
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 19 17:03:36 2024 +0300

    all: imp docs more

commit 7aa08036b239d7eb19f674a6c4bfaf1325ff4bff
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 19 16:08:13 2024 +0300

    all: add more docs to lint
2024-12-19 17:17:40 +03:00
Eugene Burkov
261c1599a5 Pull request #2325: 7510 Fix openapi spec
Merge in DNS/adguard-home from 7510-fix-openapi to master

Squashed commit of the following:

commit b67d6f964081cf12bea5c3406eba64a432d02e81
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 15:28:46 2024 +0300

    openapi: fmt openapi chlog

commit 738becf0af96b3bfc4b549be9ad84cbce0d9cf39
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 14:44:36 2024 +0300

    openapi: log changes

commit a398712ed1b5696f944f15404e340e002822c89f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 19 14:34:11 2024 +0300

    openapi: fix spec
2024-12-19 15:38:18 +03:00
Ainar Garipov
21945c6058 Pull request 2324: 7505-format-values
Closes #7329.
Updates #7505.

* commit '5c15d1bb16044bbaff1ff29e8bbe3ce2ff3bc982':
  all: upd chlog
  Format values in General Statistics
2024-12-17 16:55:02 +03:00
Ainar Garipov
5c15d1bb16 Merge branch 'master' into 7505-format-values 2024-12-17 16:37:01 +03:00
Ainar Garipov
efe2ed1304 all: upd chlog 2024-12-17 16:20:12 +03:00
Stanislav Chzhen
fe07786d2d Pull request #2323: AGDNS-2598-clients-search
Merge in DNS/adguard-home from AGDNS-2598-clients-search to master

Squashed commit of the following:

commit 9df3c19acad16203ccaa7752902bce8bc835c8fb
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 16 19:11:43 2024 +0300

    home: imp code

commit 7bf8f0a516b57fab6c19c24e4a156c87d9c4d03f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 16 18:34:06 2024 +0300

    all: imp code

commit 2dd1c941232ceeaef4c506717096a8b8e8555e6e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Dec 13 17:35:11 2024 +0300

    all: clients search
2024-12-17 15:08:31 +03:00
Hermholtz
2b55e56306 Format values in General Statistics 2024-12-14 15:43:12 +01:00
Ainar Garipov
dedbadafc4 Pull request 2321: upd-go-deps
Merge in DNS/adguard-home from upd-go-deps to master

Squashed commit of the following:

commit 2c0b63da2ec8bbf19bc7dbb03c0166f6f9a5d822
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Dec 12 14:42:31 2024 +0300

    all: upd go deps, tools
2024-12-12 16:20:13 +03:00
Ainar Garipov
d3cc2dc930 Pull request 2319: websvc-patch
Merge in DNS/adguard-home from websvc-patch to master

Squashed commit of the following:

commit b2a10faf12b16f13f617b3ed3ef3e81cb0479ff8
Merge: 38f749106 8f53f6505
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 11 17:33:15 2024 +0300

    Merge branch 'master' into websvc-patch

commit 38f7491069d90d4080e7ad98b09bf9ce19138195
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Dec 10 19:57:54 2024 +0300

    next: add json merge patch utils
2024-12-11 17:41:00 +03:00
Ainar Garipov
8f53f6505b Pull request 2320: upd-chlog
Merge in DNS/adguard-home from upd-chlog to master

Squashed commit of the following:

commit 305ae5823fa2f61b5f58759a29330a65599c19ba
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Dec 11 17:19:51 2024 +0300

    all: upd and fmt chlog
2024-12-11 17:32:22 +03:00
Stanislav Chzhen
dab608292a Pull request 2314: AGDNS-2374-slog-home-webapi
Squashed commit of the following:

commit 7c457d92b5a2d931c22388d37ad30f10dc0ecd89
Merge: bcd3d29df 11dfc7a3e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Dec 5 18:24:08 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-home-webapi

commit bcd3d29dfde4af7d223e2d2468dd01635f799bb9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Dec 5 18:24:01 2024 +0300

    all: imp code

commit f3af1bf3dd476733d0286eeea9f6dd5ebbdb4950
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Dec 4 18:33:35 2024 +0300

    home: imp code

commit 035477513f433346c7bf3215083c317466b4b734
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Dec 3 19:01:55 2024 +0300

    home: imp code

commit 5368d8de50d266b438a4909d81b5fec76b5ca65c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Dec 3 17:25:37 2024 +0300

    home: imp code

commit fce1bf475f33f6fe45252e2276ea8ebf2a82a8b2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Dec 2 18:20:31 2024 +0300

    home: slog webapi
2024-12-06 16:01:58 +03:00
Ildar Kamalov
11dfc7a3e8 Pull request 2316: AGDNS-2239 fix setup guide list styles
Squashed commit of the following:

commit 52cc651e3b6a5fe7c46ad1fb41865f2dd1f84258
Merge: 324c4e102 c234e5dc3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Dec 5 15:28:39 2024 +0300

    Merge branch 'master' into AGDNS-2239

commit 324c4e1024e6d1c4fc72eeae2baeee6565512a6c
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Dec 4 17:03:46 2024 +0300

    fix mobile styles

commit 739ccfa6f1c99562c81c2ba947b3c91fb82ea572
Author: Ildar Kamalov <ik@adguard.com>
Date:   Wed Dec 4 16:45:48 2024 +0300

    AGDNS-2239 fix guide list padding
2024-12-05 15:36:40 +03:00
Eugene Burkov
c234e5dc31 Pull request 2317: Update all
Squashed commit of the following:

commit 832a5ac0c9d588428c7d030978769e510e011645
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 17:38:20 2024 +0300

    client: upd i18n

commit 6fa7591109085b169b79bd771808922a1a53f875
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 17:20:30 2024 +0300

    client: upd trackers

commit f4b692d37c1cc4d784b4a76f85198f4a1cfa7f83
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Dec 4 16:55:40 2024 +0300

    all: upd tools, go
2024-12-04 17:57:37 +03:00
Eugene Burkov
3895cfb4f0 Pull request 2312: 7400 Windows permcheck
Updates #7400.

Squashed commit of the following:

commit f50d7c200de545dc6c8ef70b39208f522033fb90
Merge: 47040a14c 37b16bcf7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 18:09:23 2024 +0300

    Merge branch 'master' into 7400-chown-permcheck

commit 47040a14cd50bf50429f44eba0acdcf736412b61
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 14:26:43 2024 +0300

    permcheck: fix nil entries

commit e1d21c576d75a903b88db3b7beb82348cdcf60c9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 2 15:37:58 2024 +0300

    permcheck: fix nil owner

commit b1fc67c4d189293d0aee90c1905f7f387840643b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 18:07:15 2024 +0300

    permcheck: imp doc

commit 0b6a71326e249f0923e389aa1f6f164b02802a24
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 17:16:24 2024 +0300

    permcheck: imp code

commit 7dfbeda179d0ddb81db54fa4e0dcff189b400215
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 14:28:17 2024 +0300

    permcheck: imp code

commit 3a5b6aced948a2d09fdae823fc986266c9984b3d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 19:21:03 2024 +0300

    all: imp code, docs

commit c076c9366934303fa8c5909bd13770e367dca72e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 15:14:06 2024 +0300

    permcheck: imp code, docs

commit 09e4ae1ba12e195454f1db11fa2f5c9e8e170f06
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 27 19:19:11 2024 +0300

    all: implement windows permcheck

commit b75ed7d4d30e289b8a99e68e6a5e94ab74cf49cb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Nov 25 18:01:47 2024 +0300

    all: revert permissions
2024-12-03 18:26:00 +03:00
Ainar Garipov
37b16bcf79 Pull request 2315: 7479-readme-fix
Closes #7477.

* commit '4d470f94840c46d22f6dc1454a84a8fdc11eda6b':
  Update wiki-start in README.md
2024-12-03 14:22:32 +03:00
Margaret Fero
4d470f9484 Update wiki-start in README.md
When I clicked the link aliased as wiki-start, the article had a banner at the top redirecting readers to a newer version on the wiki. This commit replaces the link to the outdated version with the link it redirects to, to save future visitors a click.
2024-12-02 00:45:18 -08:00
Stanislav Chzhen
a8431297f7 Pull request 2313: 7357-upd-proxy
Updates #7357.

Squashed commit of the following:

commit 599dee0e5dd6d0af0e06ac97207a2677c32b03da
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 29 14:52:16 2024 +0300

    all: upd golibs

commit eb90c49d37bbe59ecb4867075d4b6fde70871fa2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 29 14:32:57 2024 +0300

    all: upd proxy

commit 12b8e51c8738a4c94b257d9b4640bc63f9fa5d70
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 20:04:58 2024 +0300

    all: upd proxy

commit 77fcabcf60f72c092a0a49075d1415ce370cdbb6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 17:13:30 2024 +0300

    all: upd chlog

commit 714f93d612abd705732cf76bee365272d15a11ce
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 28 17:02:34 2024 +0300

    all: upd proxy
2024-11-29 15:57:06 +03:00
Eugene Burkov
9789e5b0fe Pull request 2311: 7465 Fix nil dereference
Squashed commit of the following:

commit e4943c2b064ff0647d0e3bd2e6431bb94f151ba5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 27 17:05:32 2024 +0300

    home: fix nil dereference
2024-11-27 17:16:45 +03:00
Eugene Burkov
4a49c4db96 Pull request 2307: AGDNS-2556 Custom updater URL
Squashed commit of the following:

commit 73f946138ccb4f89141f192b6cb1a21887604ab4
Merge: c58847bfb d578c713f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 17:42:29 2024 +0300

    Merge branch 'master' into AGDNS-2556-custom-update-url

commit c58847bfb08131263e1cff4813eb4a466f613d91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 17:34:11 2024 +0300

    home: imp logging

commit 0d451621d76fdf2c363d223eb29c4442d8f36dc8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 15:12:04 2024 +0300

    home: rename config field

commit c7f3822929e9199f8f411f1a0ad072c643feb42f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 26 15:07:09 2024 +0300

    all: enable updater for some cases

commit 872cd3a18c876076ea643624336cfc0a4296a81d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 19:09:18 2024 +0300

    updater: imp test

commit c9efb412e7411b769df54b7247fe168047fb9799
Merge: c989eef71 abb738013
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 17:51:46 2024 +0300

    Merge branch 'master' into AGDNS-2556-custom-update-url

commit c989eef715ae7edd98d7b2d5df06fd3d04153209
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 17:46:34 2024 +0300

    all: imp code

commit 0452d8b356e6d0b73b097d43b97b7027fcca752d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 15:37:21 2024 +0300

    all: add custom url to updater
2024-11-26 20:35:16 +03:00
Stanislav Chzhen
d578c713ff Pull request 2310: 6818-fix-goroutine-leak
Updates #6818.

Squashed commit of the following:

commit 3027fc4a615beba9138449d65994f1630b5a6cbf
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 25 16:53:33 2024 +0300

    all: fixed goroutine leak
2024-11-26 13:34:16 +03:00
Stanislav Chzhen
d4ca14806e Pull request 2309: fix-safesearch-test
Squashed commit of the following:

commit a5b6c831d86ee5264c0918bf12ddef7c78f78f38
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 25 13:45:17 2024 +0300

    dnsforward: imp tests

commit 1ca353423ca4dcc7616475fbf12c9b5b9450a5ea
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 20:08:28 2024 +0300

    dnsforward: imp tests

commit 82851aa825818159b5b1583e2716d6656d633b03
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 19:54:53 2024 +0300

    dnsforward: imp tests

commit efabbe318e7520eba65b595f11e2fb8effaeec13
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 19:36:27 2024 +0300

    dnsforward: fix safesearch test
2024-11-25 14:31:21 +03:00
Stanislav Chzhen
abb738013a Pull request 2308: AGDNS-2374-slog-client-storage
Squashed commit of the following:

commit 670ce6f7267ae6447c179ba491fc18c01cffa1ab
Merge: b3029a041 098cbab7e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 17:09:08 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-client-storage

commit b3029a041aa0f4b536dd899f162fcee2b7d5127b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 22 16:53:18 2024 +0300

    all: slog client storage
2024-11-22 17:18:47 +03:00
Eugene Burkov
098cbab7e6 Pull request 2305: 7400 Disable permcheck
Updates #7400.

Squashed commit of the following:

commit f6508d395288dfa5ed0b9aa2e714bc1eba72d243
Merge: aa7119648 d96e65cb0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 15:43:27 2024 +0300

    Merge branch 'master' into 7400-disable-perm

commit aa7119648bae3f2ff6af96215c3c8eab70b379ea
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:51:37 2024 +0300

    next: add flag

commit c16b90918f5a9493876e3d16776fb18f67cbc09b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:42:47 2024 +0300

    home: fix help

commit 2e096c0e320599b9472f55a47c1abbc95e1a9f8e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:37:30 2024 +0300

    all: imp code, log changes

commit 368598819fc339cc1dbf788d092af9e4c5191f30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:12:18 2024 +0300

    home: add permcheck option
2024-11-22 17:03:09 +03:00
Stanislav Chzhen
d96e65cb0c Pull request 2304: AGDNS-2374-slog-querylog
Squashed commit of the following:

commit e98e5efaaf5388551322933321df0707ad7b2a9c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Nov 21 13:15:51 2024 +0300

    all: imp code

commit fbe728c9aa03a325c2733c214412f9071faba5ed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 20:57:15 2024 +0300

    all: imp code

commit ef715c58cb6621236424f55268390aa3f997f883
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 16:39:35 2024 +0300

    all: imp code

commit cbb993f7ae4311b2a73ace7066a5dabf190291be
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Nov 18 14:03:42 2024 +0300

    all: imp code

commit 8d88d799303c7e3d15322fee87780fedb408ea13
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Nov 15 15:57:07 2024 +0300

    all: slog querylog
2024-11-21 20:19:39 +03:00
Ainar Garipov
1d6d85cff4 Pull request 2303: AGDNS-2505-upd-next
Squashed commit of the following:

commit 586b0eb180afc22d06d673756dd916c17a173361
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Nov 12 19:58:56 2024 +0300

    next: upd more

commit d729aa150f7ac367255830cceca40b8880c51015
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Nov 12 16:53:15 2024 +0300

    next/websvc: upd more

commit 0c64e6cfc66b9212f077b2de7450586fd4d02802
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Nov 11 21:08:51 2024 +0300

    next: upd more

commit 05eec75222360708621c99d3eeac7c8d9f2a5080
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 8 19:20:02 2024 +0300

    next: upd code
2024-11-13 15:44:21 +03:00
Ainar Garipov
ac5a96fada Pull request 2302: upd-all
Squashed commit of the following:

commit f920006277f39b74c803139af2a9039aa45effae
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 8 16:14:41 2024 +0300

    all: fix pre-commit; upd dnsproxy

commit 391f79b244348c6075f5ba0fccfb8882791bf3f1
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 18:53:28 2024 +0300

    scripts: imp install

commit 35324db80b591831c32b7ea45930eefee82a6320
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 18:20:23 2024 +0300

    all: imp docs, scripts

commit d2724cfaefdb8659efbdb5bf181a28721a909f07
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 7 17:26:23 2024 +0300

    all: upd go, deps, tools, scripts
2024-11-08 17:18:16 +03:00
Eugene Burkov
6673bb175a Pull request 2301: Update all
Squashed commit of the following:

commit df783c76335d8733eb385c047c491ed1bb8b0e51
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:44:55 2024 +0300

    client: upd i18n

commit b72d4564baa8f8bab819d045aec29f9ab977538d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:06:27 2024 +0300

    client: upd filters and trackers
2024-11-06 19:26:57 +03:00
Eugene Burkov
b1a0f4fa44 Pull request 2299: AG-29637 Sign release
Squashed commit of the following:

commit 265097a29ed8e89933c08cb8094297ac89a8b140
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:03:19 2024 +0300

    all: fix darwin find, log changes

commit 298b31c4078239ce83fdf7a3a741062e56f35b9d
Merge: 82c487518 d06b18a49
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 18:01:28 2024 +0300

    Merge branch 'master' into AG-29637-sign-release

commit 82c487518bd21a995921a7d5265da6884aac9e9f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 17:34:12 2024 +0300

    scripts: imp fmt manually

commit 4e33ec1a77dd239df8b5eeec16843e517797d486
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 6 14:42:09 2024 +0300

    scripts: imp fmt, sign releases
2024-11-06 18:45:21 +03:00
Ainar Garipov
d06b18a493 Pull request 2300: upd-chlog
Squashed commit of the following:

commit 20d68fb4bbb5874c816e4cfc8ff40ab16e7c8c46
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Nov 6 17:40:08 2024 +0300

    all: upd chlog
2024-11-06 17:56:48 +03:00
Eugene Burkov
80ec01dd49 Pull request 2298: 7314 Fix updater
Updates #7314.

Squashed commit of the following:

commit be214937fc391cf8c0c31e52a29b0adbdcd81f08
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:42:25 2024 +0300

    updater: upd doc

commit bdb9df94eecb8b1845b1e65b39325f163541ee91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:41:26 2024 +0300

    all: rename const

commit 7fbabeb939ba37aba11852af3c4095363e4ce304
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:34:37 2024 +0300

    all: add binary const

commit c9f1a9f747c9ec440a9ea5754eed6974eff4169d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 5 18:20:44 2024 +0300

    updater: fix binary perm
2024-11-05 19:21:29 +03:00
Ainar Garipov
47dfa44cf6 Pull request 2297: AG-20945-filter-storage
Squashed commit of the following:

commit 2611fd57815910c035621879094ab954389f31b4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 16:29:06 2024 +0300

    dnsforward: imp test

commit 5efcfda937da2ce229ae1a3c57d7a4de35ee7caf
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 15:54:18 2024 +0300

    rulelist: imp docs, tests

commit 7a759c46997ed69e931a0a7c4f25820c52660a3f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 1 14:36:08 2024 +0300

    all: add filtering storage; upd golibs
2024-11-05 12:25:39 +03:00
Ainar Garipov
1d2026bf7e Pull request 2295: upd-all
Squashed commit of the following:

commit c1f3375abc42809928ff80bede77f4099d2e7c1c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Oct 29 14:34:14 2024 +0300

    all: upd deps, lists, tools
2024-10-29 14:49:25 +03:00
Eugene Burkov
e77de2e67d Pull request 2294: AGDNS-2455 Windows permissions
Closes #7314.

Squashed commit of the following:

commit f8b6ffeec2f0f96c947cf896c75d05efaca77caf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Oct 29 14:14:41 2024 +0300

    all: fix chlog

commit 9417b7dc510296c096f234e2f340dad5a6faf627
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 19:41:30 2024 +0300

    aghos: imp doc

commit b91f0e72a70a8e1392bd07b50714d8b83cc4e33e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 19:26:15 2024 +0300

    all: rm bin

commit 9008ee93b181794c5082894bfa5ce4c76153f93d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 18:23:54 2024 +0300

    all: revert permcheck

commit bcc85d50f5f39269713979c6509a9acd220570b8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 17:48:55 2024 +0300

    all: use aghos more

commit 993e351712fbf004a6f96e06061ba2321c1c46e1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Oct 28 16:24:56 2024 +0300

    all: fix more bugs

commit a22b0d265eb0fa747e136363558b97de54e593b8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 18:30:52 2024 +0300

    all: fix bugs

commit a2309f812ad3fd83d26c373b67756ea3074f4854
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 17:05:08 2024 +0300

    all: fix chlog, imp api

commit 42c3f8e91c49998068bc208166de20efe49c3dcb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 16:04:47 2024 +0300

    scripts: fix docs

commit 9e781ff18db58ed9be35e259ecf3c669a4d41e02
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 16:03:19 2024 +0300

    scripts: imp docs

commit 1dbc7849828cc4933bb5edc3257f158ac292d48e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 15:55:16 2024 +0300

    all: use new functions, add tests

commit dcbabaf4e37149a73969c52c9bfac2b9d9127a67
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Oct 25 13:23:50 2024 +0300

    aghos: add stat

commit 72d7c0f881835725e65db63ac2dd1c5f7a409036
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Oct 24 17:10:30 2024 +0300

    aghos: add windows functions
2024-10-29 14:28:59 +03:00
Stanislav Chzhen
e529d29e8a Pull request 2291: AGDNS-2374-slog-client
Squashed commit of the following:

commit e8e6dba18b8f44392bd88999e481723a00aa3042
Merge: 929283702 41cce6259
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 22 13:46:26 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-client

commit 929283702a6a82163906e624f965b934e3b8074e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 15 14:30:00 2024 +0300

    client: imp tests

commit f29d8edb89e3f05e0bd9c3b0eccc0587882a3ed3
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 15:03:08 2024 +0300

    client: imp docs

commit 0b4311ac26c704bbfa0edfc51767c9fc74f959a2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 11 19:12:50 2024 +0300

    all: imp code

commit 1ad99ee3cb915bb19eff8b893deae1dd4d64b190
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 10 20:59:46 2024 +0300

    all: slog client
2024-10-22 13:57:54 +03:00
Stanislav Chzhen
41cce62597 Pull request 2292: 7338-stats-log-msg
Updates #7338.

Squashed commit of the following:

commit a3ff2af22113d5d31f29f6626fb5b1897ca0d7b3
Merge: 9558de14e 5b45f6d50
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:48:26 2024 +0300

    Merge branch 'master' into 7338-stats-log-msg

commit 9558de14ef7786cd37481fd13975b3c469c98083
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 14:11:13 2024 +0300

    all: imp stats log msg
2024-10-14 18:14:09 +03:00
Stanislav Chzhen
5b45f6d508 Pull request 2293: fix-snap-build
Squashed commit of the following:

commit 44e617662007cf9dac5d11f67d8ea091564f6c2c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:15:51 2024 +0300

    all: fix typo

commit ed26f49be92161fcd7e6ffd3cf2c12c560ad2ab7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 17:14:05 2024 +0300

    all: fix snap build

commit 97d186d513c3ea600a8634cd5521b163bfd83394
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 14 16:47:11 2024 +0300

    all: fix snap build
2024-10-14 17:47:47 +03:00
Stanislav Chzhen
73ff401b0f Pull request 2290: 7250-custom-client-cache
Updates #7250.

Squashed commit of the following:

commit 062660f17e164b92a3254c5b79543080c6884dd5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 17:42:17 2024 +0300

    all: upd chlog

commit 66fa039ede456454f26b3bfedefabfe71beda3ab
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 17:30:58 2024 +0300

    home: custom client cache
2024-10-10 16:33:03 +03:00
Stanislav Chzhen
6363f8a2e7 Pull request 2286: AGDNS-2374-slog-safesearch
Squashed commit of the following:

commit 1909dfed99b8815c1215c709efcae77a70b52ea3
Merge: 3856fda5f 2c64ab5a5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 9 16:21:38 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-safesearch

commit 3856fda5f38a89d2df86bd8701e79d7f3fc02bb7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Oct 8 20:04:34 2024 +0300

    home: imp code

commit de774009aa82bf45022fd9c359296e7ab45bf93d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Oct 7 16:41:58 2024 +0300

    all: imp code

commit 038bae59d51497de1db7153e00e779db30f79721
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 20:24:48 2024 +0300

    all: imp code

commit 792975e248bb04bce5a8ec767441fcf253c6d00f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 15:46:40 2024 +0300

    all: slog safesearch
2024-10-09 16:31:03 +03:00
Stanislav Chzhen
2c64ab5a51 Pull request 2289: upd-urlfilter
Updates #6818.

Squashed commit of the following:

commit d03b518457ef14d1c565a46c1dbfce1a47bf301d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 4 18:58:10 2024 +0300

    all: upd chlog

commit 80ee9146998d622c173a9559b8d6af139b750d23
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Oct 4 18:50:24 2024 +0300

    all: upd urlfilter
2024-10-04 19:10:32 +03:00
Stanislav Chzhen
df097341c3 Pull request 2287: 7315-fix-client-storage-panic
Closes #7315.

Squashed commit of the following:

commit 94c8473a14d98b45b5086de432bc68dd55eb654b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 19:09:23 2024 +0300

    all: upd chlog

commit 82b772b1dde19277832d44f9b22c47d22f90bf7e
Merge: e7f34cc43 4919630cc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 19:00:53 2024 +0300

    Merge branch 'master' into 7315-fix-client-storage-panic

commit e7f34cc435f53b80aca328a62e27c6aaa8fe6462
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 17:55:39 2024 +0300

    home: fix client storage panic
2024-10-04 18:10:56 +03:00
Stanislav Chzhen
4919630ccc Pull request 2288: upd-chlog
Squashed commit of the following:

commit f064da09e4fa644c085b4e21f18bb27c65549b92
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Oct 3 18:33:58 2024 +0300

    all: imp chlog more

commit 662ba0f56995e9f955446b134f7c71e9d4d59ccd
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Oct 3 18:25:09 2024 +0300

    all: upd chlog
2024-10-03 18:47:28 +03:00
Ainar Garipov
b32b8f9c7e Pull request 2285: fix-test
Squashed commit of the following:

commit 006391c9c089a2d9fe7ad7157e609898be2ee225
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Oct 3 14:15:10 2024 +0300

    dnsforward: imp test more

commit ce743a35eff5271cb13a892373536ae815a74ebb
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 21:13:02 2024 +0300

    dnsforward: fix test
2024-10-03 14:56:07 +03:00
Ainar Garipov
355cec1d7b Pull request 2284: AG-32257-file-permission-mitigation
Squashed commit of the following:

commit 6e0e61ec2e95a563b04a622f46c6bbe2b2e12711
Merge: e3cccc01a 5b5b39713
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 20:51:29 2024 +0300

    Merge branch 'master' into AG-32257-file-permission-mitigation

commit e3cccc01a9cbd382cec0fcd7f3685e43acb48424
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 19:57:32 2024 +0300

    dnsforward: imp test

commit 16ecebbc2fd2f4afe2bf475774af1786fa7a02c0
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 19:22:10 2024 +0300

    configmigrate: imp tests

commit da8777c3a7c81e17c0d08cfff4e3a9c8d2bbd649
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 18:58:46 2024 +0300

    all: imp types, tests

commit 58822a0ef8aa2d944a667d1ba77fe23ff52af424
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 18:28:37 2024 +0300

    all: imp chlog

commit 8ce81f918cc5cf43972e2045532a48c829257a2f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 18:09:57 2024 +0300

    all: improve permissions, add safe_fs_patterns
2024-10-02 21:00:15 +03:00
Stanislav Chzhen
5b5b397132 Pull request 2283: AG-27492-fix-safesearch-cache
Squashed commit of the following:

commit c93ee78c05fae78c99796acfdfcfb4755469e37f
Merge: 8019871a9 557898788
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 2 20:41:52 2024 +0300

    Merge branch 'master' into AG-27492-fix-safesearch-cache

commit 8019871a93b1326cdc3475f49198bc28f194a097
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Oct 2 15:31:37 2024 +0300

    home: fix safesearch cache
2024-10-02 20:48:58 +03:00
Ainar Garipov
5578987884 Pull request 2282: all: upd go, tools, deps
Squashed commit of the following:

commit 760afd59cba826bdc1686506ca7a9243dcb35ab7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Oct 2 14:52:29 2024 +0300

    all: upd go, tools, deps
2024-10-02 15:16:32 +03:00
Ainar Garipov
54a975f584 Pull request 2280: upd-all
Squashed commit of the following:

commit 8d93f660087f5a08c3775f69a79978da6176e41a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Sep 30 19:49:47 2024 +0300

    all: imp chlog

commit 858765e7a5e5e668db43cc4e4753a71d022c3034
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Sep 30 19:36:39 2024 +0300

    all: upd i18n, lists, tools
2024-09-30 20:03:10 +03:00
Stanislav Chzhen
d40de33316 Pull request 2273: AG-27492-client-storage-runtime-sources
Squashed commit of the following:

commit 3191224d6de3d679ed7c3a35a383ebed27d3c181
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 26 18:20:04 2024 +0300

    client: imp tests

commit 6cc4ed53a206657c5626a5894190a6851ebb3b01
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 26 18:04:36 2024 +0300

    client: imp code

commit 79272b299ad16534ba4919e1f0616a875d7a15cb
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 26 16:10:06 2024 +0300

    all: imp code

commit 0a001fffbe4dece198f9343b998d606e85f94703
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Sep 24 20:05:47 2024 +0300

    all: imp tests

commit 80f7e98d302f78332a4e297f243ad1c4a958cadd
Merge: df7492e9d e338214ad
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Sep 24 19:10:13 2024 +0300

    Merge branch 'master' into AG-27492-client-storage-runtime-sources

commit df7492e9de063e3c1b582e9f014554bc6cba7e90
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Sep 24 19:06:37 2024 +0300

    all: imp code

commit 23896ae5a6ee3f2d3a54dc15536d1c9349ba46b5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 19 21:04:34 2024 +0300

    client: fix typo

commit ba0ba2478ccd50d764d1a6a2d5c4b4b35b9b2a1c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 19 21:02:13 2024 +0300

    all: imp code

commit f7315be742422dda8d0adede6666b119761e67f4
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 12 14:35:38 2024 +0300

    home: imp code

commit f63d0e80fb34f0d2799be7d51138ad1200449fc5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 12 14:15:49 2024 +0300

    all: imp code

commit 9feda414b6e469d911c9428d9ce1c5043d27cc93
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Sep 10 17:53:42 2024 +0300

    all: imp code

commit fafd7cbb52d36bea260c9d922b7c4204f73e7a66
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Sep 9 21:13:05 2024 +0300

    all: imp code

commit 2d2b8e02163e1bd03bf46d46ef7dbb6a50c4e844
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 5 20:55:10 2024 +0300

    client: add tests

commit 4d394e6f2162f797c409073cb52e1e0de93a9d5e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 29 20:40:38 2024 +0300

    all: client storage runtime sources
2024-09-30 14:17:42 +03:00
Stanislav Chzhen
e338214ad5 Pull request 2279: AG-27492-clients-runtime-sources-dhcp
Squashed commit of the following:

commit 51fb3e3d07752a33310b05d99726b248d3897335
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 19 17:01:03 2024 +0300

    all: upd chlog

commit 3ab945bb442c921ffbfa14c1dd2825f8ed307999
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 19 16:49:00 2024 +0300

    home: clients runtime sources dhcp
2024-09-19 20:01:16 +03:00
Eugene Burkov
a74c32f742 Pull request 2277: AG-29637 Sign Windows
Squashed commit of the following:

commit d22a4cb262c984241863d8dec1e498d83733ac6f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 15:19:01 2024 +0300

    all: resolve tmp todos

commit 4574b050bae921ec9ebed5f90f96f571ca7800cd
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 14:55:44 2024 +0300

    bamboo: checkout later

commit 3036a46566c78350f1335cdd9f17f28c837b679f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 14:35:36 2024 +0300

    bamboo: list files

commit eb675abfc0415907e41e08c8c2bc565162697478
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 14:28:14 2024 +0300

    bamboo: work with vcs properly

commit 0c34b4dcfd836f0f1c01cbde50cfc505eb46a5ff
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 14:15:06 2024 +0300

    bamboo: add repo name var

commit 15da8e294f6ee43643787264492facd881bf7713
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 11 14:06:26 2024 +0300

    bamboo: upd api key

commit b1d353dbc3b1b29596f15fa2c6fcb1d7d5f57d72
Merge: 3309f0703 cbae07e8e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 19:29:29 2024 +0300

    Merge branch 'master' into AG-29637-sign-windows

commit 3309f07031331d6f72170a7bb91c35e0a2e50c46
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 19:09:44 2024 +0300

    all: only sign beta

commit f61af53a70b3abd15717f341f07b58091eb4a988
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 15:32:31 2024 +0300

    all: sign windows
2024-09-11 19:39:54 +03:00
Eugene Burkov
cbae07e8e6 Pull request 2278: AG-29637 Checkout publisher
Squashed commit of the following:

commit fec1efed4680076b33141a72e36904c27a04677d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 19:14:59 2024 +0300

    bamboo: add publisher
2024-09-10 19:25:31 +03:00
Eugene Burkov
6fe4b9440d Pull request 2276: Update Go & tools
Squashed commit of the following:

commit 74629880756659d22989a69bd7631a246207d3b7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 14:35:51 2024 +0300

    all: fix docs

commit 238ff1d418f1aef2b0056be96ce83484341c3fea
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 10 14:28:27 2024 +0300

    all: upd go & tools
2024-09-10 15:14:48 +03:00
Stanislav Chzhen
b443cf35c4 Pull request 2275: AGDNS-2374-slog-stats
Squashed commit of the following:

commit 45b2fc6a05a4f7775d2b6fa056c81d53d4f402d7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Sep 5 18:07:06 2024 +0300

    all: imp code

commit 022c90496a46b0a0423dd2cb1c02a3473ba5d224
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Sep 4 19:32:35 2024 +0300

    stats: imp code

commit bb3c0c8002c34bec7440cd93b7833f7022eef0d8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Sep 4 19:10:36 2024 +0300

    all: imp code

commit 363a16f6bb2faa1d9b890b4967684129208af62e
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Sep 4 17:45:31 2024 +0300

    all: imp code

commit a3c96e3d211cc5e11ba09e334748f65a44b8960a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Sep 2 20:44:11 2024 +0300

    stats: imp code

commit 2c0ffd91fddd286254b53be790146a2931b7b55b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Sep 2 19:47:11 2024 +0300

    all: slog stats
2024-09-09 13:31:54 +03:00
Stanislav Chzhen
76344f9785 Pull request 2274: AGDNS-2374-slog-scripts
Squashed commit of the following:

commit 257bd542f78d6e06a6b4783a050b573240a2b5ca
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Sep 2 17:33:12 2024 +0300

    scripts: slog
2024-09-02 18:03:37 +03:00
Stanislav Chzhen
aab6769fa2 Pull request 2272: AGDNS-2374-slog-rdns-whois
Squashed commit of the following:

commit 695db57ec872ac8fa1b3a3b2c095bbe4c5c782cc
Merge: 6b3fda0a3 0b8bf1345
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 29 16:08:39 2024 +0300

    Merge branch 'master' into AGDNS-2374-slog-rdns-whois

commit 6b3fda0a37dfb309cc0909cd86dcb86e6b22afef
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Aug 27 20:40:01 2024 +0300

    all: imp code

commit 0b1f022094e268f51391825c9d5ff9ff1ea27af0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Aug 26 20:35:06 2024 +0300

    all: slog rdns whois
2024-08-29 16:20:05 +03:00
Stanislav Chzhen
0b8bf13453 Pull request 2271: AGDNS-2374-slog-arpdb
Squashed commit of the following:

commit 355136e6e2f3e77b483d97fbc01fbef562c319eb
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Aug 27 18:09:51 2024 +0300

    arpdb: imp docs

commit 27383833033216ab938b0896dbc39e5af8d4dde9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Aug 26 19:11:10 2024 +0300

    all: slog arpdb
2024-08-27 20:42:10 +03:00
Stanislav Chzhen
738958d90a Pull request 2270: AGDNS-2374-slog-ipset
Squashed commit of the following:

commit 51ff7d8c49d174d057b4f508f3e113e1ca86bd1a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 22 13:50:10 2024 +0300

    dnsforward: imp code

commit a1c0011273fc83ec1b509a9d930bca5e278e1e2c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 21:53:01 2024 +0300

    dnsforward: imp code

commit a64fd6b3f037712927a583d04296fcaf821f6442
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 21:28:48 2024 +0300

    dnsforward: imp code

commit 37ccae4e923a7e688e79a135b0e49a746e9b2a06
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 20:23:58 2024 +0300

    all: imp code

commit 03c69ab2729eb424d768def986cba83731ad3e3b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 19:08:30 2024 +0300

    all: imp code

commit 72adfb101fcdb42635702c1f1c4e13ddcc95bfdc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 16:42:44 2024 +0300

    all: slog ipset
2024-08-26 13:30:00 +03:00
Stanislav Chzhen
30c0bbe5cc Pull request 2265: AG-27492-client-runtime-storage
Squashed commit of the following:

commit a164bace2e0333cf95622f34df7b0e79eac69f41
Merge: 6567cd330 184f476bd
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 21 16:14:55 2024 +0300

    Merge branch 'master' into AG-27492-client-runtime-storage

commit 6567cd330ce76d4744f5eb990c09efdb5481aea9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Aug 20 16:45:43 2024 +0300

    all: imp code

commit 243123a404bb5279a27de18391fa58a9d3e6149b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 15 19:15:54 2024 +0300

    all: add tests

commit 6489996878ab6bf2ec93c359599ae29e58e938a0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Aug 5 15:12:05 2024 +0300

    all: client runtime storage
2024-08-21 16:27:28 +03:00
Eugene Burkov
184f476bdc Pull request 2269: ADG-8932 Upd all
Squashed commit of the following:

commit 00fc45877776ed7d1c59be26330f6f16d784ead2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 20 16:21:25 2024 +0300

    all: imp lint

commit b04d9cd334a92faf21787e7e1ebf20d5e5fd0bee
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 20 14:40:18 2024 +0300

    all: upd all

commit f151f8c3139a0d8ac8cc5cf4926710b8d3f98846
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Aug 16 13:12:36 2024 +0300

    all: upd proxy
2024-08-20 18:38:04 +03:00
Dimitry Kolyshev
cdf970fcbf Pull request: 5704-riscv
Updates #5704.

Squashed commit of the following:

commit f111e2033afff7e25bbd87df67b83f709446c6cd
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Aug 8 11:27:41 2024 +0300

    docs: chlog

commit 0d56423056075844cef2035481d759dbb1b50b35
Merge: 866239a49 1a6ec30bd
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Aug 8 11:26:14 2024 +0300

    Merge remote-tracking branch 'origin/master' into 5704-riscv

commit 866239a490eb90b058e41e83ca45242534d61ba1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Aug 6 16:00:05 2024 +0500

    home: riscv arch

commit 60693828579a3ed4949234fc77bbf973981c8db2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Aug 6 15:56:23 2024 +0500

    scripts: riscv arch

commit ea7cb3a0bdbf25dfce1521de590d92e3b2c89252
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Aug 6 14:35:14 2024 +0500

    scripts: riscv arch
2024-08-08 13:38:26 +03:00
Dimitry Kolyshev
1a6ec30bd7 Pull request: AGDNS-2342-upd-golibs
Squashed commit of the following:

commit 82140ab77775b32ea10d7cd011f2bdc2410f9b92
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Aug 6 11:25:13 2024 +0500

    all: upd golibs
2024-08-07 15:49:32 +03:00
Eugene Burkov
edfa8c147f Pull request 2263: AGDNS-2280 Upd dnsproxy, golibs
Squashed commit of the following:

commit 8d83eebba851e8e09bb08b1c94a247cb049a1b75
Merge: c6574a33c b6ed76965
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 5 16:59:50 2024 +0300

    Merge branch 'master' into AGDNS-2280-upd-golibs

commit c6574a33c62171190199c8c07118d0ecd2174801
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 31 19:56:58 2024 +0300

    all: upd proxy, golibs
2024-08-05 17:12:33 +03:00
Dimitry Kolyshev
b6ed769652 Pull request: 5009-ecosia-safesearch
Squashed commit of the following:

commit 787b5d40394889510c24f4abc3a08410ecc96e5e
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 15:09:18 2024 +0300

    configmigrate: revert

commit a036638c05967298d13ef435dc5b377103cfe163
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:37:25 2024 +0300

    docs

commit a3b2e8de4bab7214dd6b260655bba9f45eee6bd2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:18:25 2024 +0300

    locales

commit a01a22019ef02ae77e9006dd9444da462419908f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 09:02:14 2024 +0300

    filtering: imp code

commit bc268cdd526c82cb605b1a474d51b79f593cd3da
Merge: 5ad88d914 bc6d20ff1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 31 08:00:05 2024 +0300

    Merge remote-tracking branch 'origin/master' into 5009-ecosia-safesearch

commit 5ad88d914cf9b03f399efd481ae39ebd56243e66
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 30 13:49:51 2024 +0300

    all: ecosia safesearch
2024-08-02 09:10:19 +03:00
Dimitry Kolyshev
bc6d20ff10 Pull request: 7155-imp-google-safesearch
* commit '9ea4838c07c5e3431260e275bf5c5dc35601fe6c':
  docs: changelog
  chore: update Google DNS list
2024-07-31 07:58:55 +03:00
Dimitry Kolyshev
9ea4838c07 Merge remote-tracking branch 'origin/master' into 7155-imp-google-safesearch
# Conflicts:
#	CHANGELOG.md
2024-07-30 17:19:45 +03:00
Dimitry Kolyshev
0e459a7369 Pull request: 7154-imp-bing-safesearch
* commit '2af8595363866f6fd3b55f6de29fd235f1078864':
  docs: changelog
  fix: enforce Bing safe search from Edge sidebar
2024-07-30 17:18:55 +03:00
Dimitry Kolyshev
c48cc980fc docs: changelog 2024-07-30 13:21:22 +03:00
Dimitry Kolyshev
2af8595363 docs: changelog 2024-07-30 13:15:03 +03:00
Cédrik
af0f43c0f8 chore: update Google DNS list
Add 3 new domains that appear in https://www.google.com/supported_domains but not in this list.
For the technically minded, generate your own list with:
curl -fsSL https://www.google.com/supported_domains | sort | sed -E 's%^(.*)$%|www\1^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com%'
:-)
2024-07-27 16:19:10 +00:00
Cédrik
d860764498 fix: enforce Bing safe search from Edge sidebar
Edge sidebar uses a different search endpoint, as per https://support.microsoft.com/en-us/topic/blocking-adult-content-with-safesearch-or-blocking-chat-946059ed-992b-46a0-944a-28e8fb8f1814;
2024-07-27 15:56:33 +00:00
Igor Lobanov
bf1101b460 Pull request #2258: ADG-8813 query log client column style fix
Merge in DNS/adguard-home from ADG-8813 to master

Squashed commit of the following:

commit ce36a973aed56960aa20720ab576ced9cc3a51b8
Merge: de0ea6edd 5c2ecfaa4
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Jul 22 11:05:59 2024 +0200

    Merge remote-tracking branch 'origin/master' into ADG-8813

commit de0ea6eddf3e409d0caabf4dbcd4e4dcce47c969
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:27:00 2024 +0200

    changelog fix

commit 598e3ce1748a1f5e10ef31bbac002ff2579fb849
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:24:16 2024 +0200

    fixed changelog

commit c4378e31a58a1a291e5933c854a39724e90a720d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Thu Jul 18 16:18:35 2024 +0200

    changelog

commit f9608325e869f7fa0798b46e041c30b100df3a3e
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jul 17 16:12:11 2024 +0200

    query log client column style fix
2024-07-22 12:12:28 +03:00
Eugene Burkov
5c2ecfaa41 Pull request 2259: Upd proxy
Squashed commit of the following:

commit e0d4bc34ee9c9cb8ba058dce904f389087487526
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jul 19 15:54:26 2024 +0300

    all: upd proxy
2024-07-19 16:33:25 +03:00
Eugene Burkov
f29a1cf23a Pull request 2257: 7113 Fix changelog
Updates #7113.

Squashed commit of the following:

commit aea0433099604cd23fd03cf920a4cae9031f1d92
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 15 15:22:17 2024 +0300

    all: fix chlog
2024-07-15 16:04:42 +03:00
Eugene Burkov
42c7cd6f8e Pull request 2256: 4923 Better interfaces
Updates #4923.

Squashed commit of the following:

commit 0e40b41aa1e517a62d6076c4e7a57c607792ef01
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 10 15:28:16 2024 +0300

    dhcpsvc: imp code, docs

commit 5463fdde473f84caaca229b53027e8183d5c6bdc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:31:20 2024 +0300

    dhcpsvc: imp ifaces
2024-07-10 16:17:56 +03:00
Dimitry Kolyshev
c0a33ce708 Pull request: upd-dnsproxy
Squashed commit of the following:

commit 463811748fa5a1f52e084c782e94f268b00b3abc
Merge: 3de62244e 130560b10
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 15:06:01 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 3de62244ee10fce9fb97c73c2955479883ce34eb
Merge: e2de50bf9 e269260fb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:13:40 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit e2de50bf9cf4eddaa0d87c20c8c1605bf4630fce
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Jul 10 09:11:25 2024 +0300

    home: todos

commit 58fe497eecf614ba61e81f55504eb3ec5e537e10
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 13:29:19 2024 +0300

    home: imp code

commit 4db7cdc0c48533932b7c6de073dff9b0d1606fa9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jul 9 11:31:12 2024 +0300

    all: imp code

commit 7e8d3b50e76634b83077bfb13a312adcb6d41189
Merge: 559c3b79d 9a6dd0dc5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:56:14 2024 +0300

    Merge remote-tracking branch 'origin/master' into upd-dnsproxy

commit 559c3b79d7752021e9e75daf9f78af64ba0114fd
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:54:03 2024 +0300

    dnsforward: imp code

commit ba4a7e1c70f91ea2b004b164f2687a7a3107b0e8
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Jul 8 10:49:46 2024 +0300

    aghos: imp code

commit cdf9ccd371317f49c78fa06795d6ba2d360ac40f
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 16:19:27 2024 +0300

    all: partial revert slog logger usage

commit f16cddbb8c63cefa0efc107e1e9fc43922c4aab6
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 13:01:37 2024 +0300

    all: upd dnsproxy

commit 5932c8d102d2b8e5f5aee1c8646aa774e2274501
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:49:37 2024 +0300

    dnsforward: slog logger

commit 3d7f734ac98b74ad3fa149498b881f30ff6b4805
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jul 5 11:05:14 2024 +0300

    all: slog logger

commit 9a74d5d98b6ee9d186eba3bc89de0d3736e4a649
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:16:21 2024 +0300

    all: upd dnsproxy

commit 537bdacec88f16ab1d6d6cc3748d39df00976dea
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 12:10:30 2024 +0300

    all: upd dnsproxy

commit 38e10dee48c8dc55606e0d99dd9cdf7719786f3a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jul 4 10:37:50 2024 +0300

    dnsforward: upstream mode
2024-07-10 15:18:46 +03:00
Eugene Burkov
130560b104 Pull request 2255: 4923 Fix tests
Updates #4923.

Squashed commit of the following:

commit 064d4aa30ba73c57a6ede8cced348fc7daf1aab8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:51:41 2024 +0300

    dhcpsvc: close db

commit d08b70983de237a4a96e8a32360a8533c69d6868
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 20:37:42 2024 +0300

    dhcpsvc: fix test paths
2024-07-10 13:03:36 +03:00
Eugene Burkov
e269260fbe Pull request 2254: 4923 gopacket DHCP vol.9
Updates #4923.

Squashed commit of the following:

commit 05322419156d18502f3f937e789df02d78971b30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 18:18:35 2024 +0300

    dhcpsvc: imp docs

commit 083da3671320f7774db9c5b854e663162da9d214
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 9 15:28:52 2024 +0300

    dhcpsvc: imp code, tests

commit 22e37e587e29c781abd4f2486f282dcfbffb4e6b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 8 19:31:43 2024 +0300

    dhcpsvc: imp tests

commit 83ec7c54ef1e689a1f887e78e3055522539222d5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jul 8 18:56:21 2024 +0300

    dhcpsvc: add db
2024-07-09 20:04:24 +03:00
Ainar Garipov
9a6dd0dc55 Pull request 2253: upg-chlog
Squashed commit of the following:

commit 3548d7a3e8a44afd1bc5fd8de7bf28fb9941e033
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:46:37 2024 +0300

    all: imp chlog better

commit c465b408e9a8da536dd97d884e391a03e1db293b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:41:11 2024 +0300

    all: imp chlog

commit 6b2de2834e9ece13cf3343107dafeea3b206dac9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Jul 5 15:33:05 2024 +0300

    all: upd chlog
2024-07-05 15:55:53 +03:00
Igor Lobanov
9a29aa9232 Pull request #2251: ADG-8776 clear query log filter fix
Merge in DNS/adguard-home from ADG-8776 to master

Squashed commit of the following:

commit d9f3acf7298c1cd39c980b9d6743624e18cdd8bf
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jul 3 17:55:18 2024 +0200

    clear query log filter fix
2024-07-04 12:43:47 +03:00
467 changed files with 48507 additions and 46311 deletions

View File

@@ -1,8 +1,8 @@
'name': 'build'
'env':
'GO_VERSION': '1.22.5'
'NODE_VERSION': '16'
'GO_VERSION': '1.24.2'
'NODE_VERSION': '20'
'on':
'push':
@@ -39,7 +39,7 @@
'with':
'node-version': '${{ env.NODE_VERSION }}'
- 'name': 'Set up Go modules cache'
'uses': 'actions/cache@v2'
'uses': 'actions/cache@v4'
'with':
'path': '~/go/pkg/mod'
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
@@ -48,7 +48,7 @@
'id': 'npm-cache'
'run': 'echo "::set-output name=dir::$( npm config get cache )"'
- 'name': 'Set up npm cache'
'uses': 'actions/cache@v2'
'uses': 'actions/cache@v4'
'with':
'path': '${{ steps.npm-cache.outputs.dir }}'
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
@@ -80,7 +80,7 @@
'with':
'node-version': '${{ env.NODE_VERSION }}'
- 'name': 'Set up Go modules cache'
'uses': 'actions/cache@v2'
'uses': 'actions/cache@v4'
'with':
'path': '~/go/pkg/mod'
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
@@ -89,13 +89,13 @@
'id': 'npm-cache'
'run': 'echo "::set-output name=dir::$(npm config get cache)"'
- 'name': 'Set up npm cache'
'uses': 'actions/cache@v2'
'uses': 'actions/cache@v4'
'with':
'path': '${{ steps.npm-cache.outputs.dir }}'
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
'restore-keys': '${{ runner.os }}-node-'
- 'name': 'Set up Snapcraft'
'run': 'sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft'
'run': 'sudo snap install snapcraft --classic'
- 'name': 'Set up QEMU'
'uses': 'docker/setup-qemu-action@v1'
- 'name': 'Set up Docker Buildx'

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.22.5'
'GO_VERSION': '1.24.2'
'on':
'push':

11
.gitignore vendored
View File

@@ -1,3 +1,8 @@
# This comment is used to simplify checking local copies of the file. Bump
# this number every time a significant change is made to this file.
#
# AdGuard-Project-Version: 1
# Please, DO NOT put your text editors' temporary files here. The more are
# added, the harder it gets to maintain and manage projects' gitignores. Put
# them into your global gitignore file instead.
@@ -8,11 +13,16 @@
# bottom to make sure they take effect.
*.db
*.log
*.out
*.snap
*.test
/agh-backup/
/bin/
/build/*
/client/blob-report/
/client/playwright-report/
/client/playwright/.cache/
/client/test-results/
/data/
/dist/
/filtering/tests/filtering.TestLotsOfRules*.pprof
@@ -21,6 +31,7 @@
/launchpad_credentials
/querylog.json*
/snapcraft_login
/test-reports/
AdGuardHome
AdGuardHome.exe
AdGuardHome.yaml*

25
.markdownlint.json Normal file
View File

@@ -0,0 +1,25 @@
{
"ul-indent": {
"indent": 4
},
"ul-style": {
"style": "dash"
},
"emphasis-style": {
"style": "asterisk"
},
"no-duplicate-heading": {
"siblings_only": true
},
"no-inline-html": {
"allowed_elements": [
"a"
]
},
"no-trailing-spaces": {
"br_spaces": 0
},
"line-length": false,
"no-bare-urls": false,
"link-fragments": false
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Keep the Makefile POSIX-compliant. We currently allow hyphens in
# target names, but that may change in the future.
#
# See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html.
# See https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html.
.POSIX:
# This comment is used to simplify checking local copies of the
# Makefile. Bump this number every time a significant change is made to
# this Makefile.
#
# AdGuard-Project-Version: 4
# AdGuard-Project-Version: 9
# Don't name these macros "GO" etc., because GNU Make apparently makes
# them exported environment variables with the literal value of
@@ -22,22 +22,22 @@ VERBOSE.MACRO = $${VERBOSE:-0}
CHANNEL = development
CLIENT_DIR = client
COMMIT = $$( git rev-parse --short HEAD )
DEPLOY_SCRIPT_PATH = not/a/real/path
DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn
GOTOOLCHAIN = go1.22.5
GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off
GOTOOLCHAIN = go1.24.2
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
NPM_FLAGS = --prefix $(CLIENT_DIR)
NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress --ignore-engines\
--ignore-optional --ignore-platform --ignore-scripts
NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress
RACE = 0
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
SIGN = 1
SIGNER_API_KEY = not-a-real-key
VERSION = v0.0.0
YARN = yarn
NEXTAPI = 0
@@ -58,21 +58,29 @@ BUILD_RELEASE_DEPS_1 = go-deps
ENV = env\
CHANNEL='$(CHANNEL)'\
COMMIT='$(COMMIT)'\
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
DIST_DIR='$(DIST_DIR)'\
GO="$(GO.MACRO)"\
GOAMD64="$(GOAMD64)"\
GOAMD64='$(GOAMD64)'\
GOPROXY='$(GOPROXY)'\
GOSUMDB='$(GOSUMDB)'\
GOTELEMETRY='$(GOTELEMETRY)'\
GOTOOLCHAIN='$(GOTOOLCHAIN)'\
GPG_KEY='$(GPG_KEY)'\
GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\
NEXTAPI='$(NEXTAPI)'\
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
RACE='$(RACE)'\
REVISION='$(REVISION)'\
SIGN='$(SIGN)'\
NEXTAPI='$(NEXTAPI)'\
SIGNER_API_KEY='$(SIGNER_API_KEY)' \
VERBOSE="$(VERBOSE.MACRO)"\
VERSION="$(VERSION)"\
# Keep the line above blank.
ENV_MISC = env\
PATH="$${PWD}/bin:$$("$(GO.MACRO)" env GOPATH)/bin:$${PATH}"\
VERBOSE="$(VERBOSE.MACRO)"\
VERSION='$(VERSION)'\
# Keep the line above blank.
@@ -80,6 +88,8 @@ ENV = env\
# full build.
build: deps quick-build
init: ; git config core.hooksPath ./scripts/hooks
quick-build: js-build go-build
deps: js-deps go-deps
@@ -93,39 +103,39 @@ build-docker: ; $(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
clean: ; $(ENV) "$(SHELL)" ./scripts/make/clean.sh
init: ; git config core.hooksPath ./scripts/hooks
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
js-test: ; $(NPM) $(NPM_FLAGS) run test
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
go-fuzz: ; $(ENV) "$(SHELL)" ./scripts/make/go-fuzz.sh
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.sh
go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
js-typecheck: ; $(NPM) $(NPM_FLAGS) run typecheck
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
js-test: ; $(NPM) $(NPM_FLAGS) run test
js-test-e2e: ; $(NPM) $(NPM_FLAGS) run test:e2e
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
go-env: ; $(ENV) "$(GO.MACRO)" env
go-fuzz: ; $(ENV) "$(SHELL)" ./scripts/make/go-fuzz.sh
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.sh
# TODO(a.garipov): Think about making RACE='1' the default for all
# targets.
go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh
go-upd-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-upd-tools.sh
go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh
go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
go-upd-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-upd-tools.sh
go-check: go-tools go-lint go-test
# A quick check to make sure that all supported operating systems can be
# typechecked and built successfully.
# A quick check to make sure that all operating systems relevant to the
# development of the project can be typechecked and built successfully.
go-os-check:
env GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
env GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
env GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
env GOOS='linux' "$(GO.MACRO)" vet ./internal/...
env GOOS='windows' "$(GO.MACRO)" vet ./internal/...
openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start
$(ENV) GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='linux' "$(GO.MACRO)" vet ./internal/...
$(ENV) GOOS='windows' "$(GO.MACRO)" vet ./internal/...
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
# TODO(a.garipov): Re-add openapi-lint.

View File

@@ -114,7 +114,7 @@ If you're running **Linux,** there's a secure and easy way to install AdGuard Ho
[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
[Snap Store]: https://snapcraft.io/adguard-home
[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
[wiki-start]: https://adguard-dns.io/kb/adguard-home/getting-started/
### <a href="#guides" id="guides" name="guides">Guides</a>
@@ -205,9 +205,9 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
- [Go](https://golang.org/dl/) v1.22 or later;
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
- [npm](https://www.npmjs.com/) v8 or later;
- [Go](https://golang.org/dl/) v1.24 or later;
- [Node.js](https://nodejs.org/en/download/) v20.19 or later;
- [npm](https://www.npmjs.com/) v10.8 or later;
### <a href="#building" id="building" name="building">Building</a>
@@ -290,6 +290,22 @@ When you need to debug the frontend without recompiling the production version e
[targ-docker]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-dockersh-build-a-multi-architecture-docker-image
[targ-release]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-releasesh-build-a-release-for-all-platforms
#### <a href="#e2e-frontend-tests" id="e2e-frontend-tests" name="e2e-frontend-tests">End-to-End (E2E) Frontend Tests</a>
AdGuard Home uses [Playwright](https://playwright.dev) for E2E testing. Tests are located in `tests/e2e`.
**Running Tests:**
- `npm run test:e2e` run all tests (headless).
- `npm run test:e2e:interactive` run tests interactively.
- `npm run test:e2e:debug` run tests in debug mode.
- `npm run test:e2e:codegen` generate new test code.
**Setup:**
1. Run `npm install` to install dependencies.
2. Run `npx playwright install` to set up required browsers.
> **Warning:** Playwright will download and install its own browser binaries for testing, which may differ from the browsers installed on your system.
## <a href="#contributing" id="contributing" name="contributing">Contributing</a>
You are welcome to fork this repository, make your changes and [submit a pull request][pr]. Please make sure you follow our [code guidelines][guide] though.

View File

@@ -7,8 +7,8 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.2--1'
'stages':
- 'Build frontend':
@@ -50,7 +50,7 @@
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'BF'
'other':
'clean-working-dir': true
@@ -91,6 +91,11 @@
'tasks':
- 'checkout':
'force-clean-build': true
- 'checkout':
'repository': 'bamboo-deploy-publisher'
# The paths are always relative to the working directory.
'path': 'bamboo-deploy-publisher'
'force-clean-build': true
- 'script':
'interpreter': 'SHELL'
'scripts':
@@ -99,6 +104,9 @@
set -e -f -u -x
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
# Run the build with the specified channel.
echo "${bamboo.gpgSecretKeyPart1}${bamboo.gpgSecretKeyPart2}"\
| awk '{ gsub(/\\n/, "\n"); print; }'\
@@ -107,6 +115,8 @@
make\
CHANNEL=${bamboo.channel}\
GPG_KEY_PASSPHRASE=${bamboo.gpgPassword}\
DEPLOY_SCRIPT_PATH="./bamboo-deploy-publisher/deploy.sh"\
SIGNER_API_KEY="${bamboo.adguardHomeWinSignerSecretApiKey}"\
FRONTEND_PREBUILT=1\
PARALLELISM=1\
VERBOSE=2\
@@ -132,29 +142,32 @@
# Install Qemu, create builder.
docker version -f '{{ .Server.Experimental }}'
docker buildx rm buildx-builder || :
docker buildx create --name buildx-builder --driver docker-container\
--use
docker buildx create \
--name buildx-builder \
--driver docker-container \
--use
docker buildx inspect --bootstrap
# Login to DockerHub.
docker login -u="${bamboo.dockerHubUsername}"\
-p="${bamboo.dockerHubPassword}"
docker login -u="${bamboo.dockerHubUsername}" \
-p="${bamboo.dockerHubPassword}"
# Boot the builder.
docker buildx inspect --bootstrap
# Print Docker info.
docker info
docker buildx version
# Prepare and push the build.
env\
CHANNEL="${bamboo.channel}"\
COMMIT="${bamboo.repository.revision.number}"\
DIST_DIR='dist'\
DOCKER_IMAGE_NAME='adguard/adguardhome'\
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true"\
VERBOSE='1'\
sh ./scripts/make/build-docker.sh
env \
CHANNEL="${bamboo.channel}" \
REVISION="${bamboo.repository.revision.number}" \
DIST_DIR='dist' \
DOCKER_IMAGE_NAME='adguard/adguardhome' \
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true" \
VERBOSE='1' \
sh ./scripts/make/build-docker.sh
'environment':
DOCKER_CLI_EXPERIMENTAL=enabled
'final-tasks':
@@ -265,8 +278,8 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.2--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -281,5 +294,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.2--1'

View File

@@ -5,8 +5,8 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.2--1'
'channel': 'development'
'stages':
@@ -29,11 +29,17 @@
jobs:
- 'Artifact'
- 'E2E':
manual: false
final: false
jobs:
- 'Test e2e'
'Test frontend':
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'JSTEST'
'other':
'clean-working-dir': true
@@ -48,12 +54,13 @@
set -e -f -u -x
make VERBOSE=1 js-deps js-lint js-test
make VERBOSE=1 js-deps js-typecheck js-lint js-test
'final-tasks':
- 'clean'
'requirements':
- 'adg-docker': 'true'
# TODO(e.burkov): Add the linting stage for markdown docs and shell scripts.
'Test backend':
'docker':
'image': '${bamboo.dockerGo}'
@@ -96,7 +103,7 @@
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'BF'
'other':
'clean-working-dir': true
@@ -164,6 +171,38 @@
'requirements':
- 'adg-docker': 'true'
'Test e2e':
'artifact-subscriptions':
- 'artifact': 'AdGuardHome_linux_amd64'
- 'artifact': 'AdGuardHome frontend'
'docker':
'image': '${bamboo.dockerFrontend}'
'volumes':
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
'key': 'E2ETEST'
'other':
'clean-working-dir': true
'tasks':
- 'checkout':
'force-clean-build': true
- 'script':
'interpreter': 'SHELL'
'scripts':
- |
#!/bin/sh
set -e -f -u -x
export CI=true
tar -xzf dist/AdGuardHome_linux_amd64.tar.gz -C /tmp
mv /tmp/AdGuardHome/AdGuardHome ./AdGuardHome
make VERBOSE=1 js-deps js-test-e2e
'requirements':
- 'adg-docker': 'true'
'branches':
'create': 'for-pull-request'
'delete':
@@ -194,6 +233,6 @@
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'dockerFrontend': 'adguard/home-js-builder:3.1'
'dockerGo': 'adguard/go-builder:1.24.2--1'
'channel': 'candidate'

22
client/.eslintrc.json vendored
View File

@@ -1,5 +1,7 @@
{
"plugins": ["prettier"],
"plugins": [
"prettier"
],
"extends": [
"airbnb-base",
"prettier",
@@ -21,12 +23,23 @@
},
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
"extensions": [
".js",
".jsx",
".ts",
".tsx"
]
}
}
},
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
],
"import/extensions": [
"error",
"ignorePackages",
@@ -43,7 +56,10 @@
"no-console": [
"warn",
{
"allow": ["warn", "error"]
"allow": [
"warn",
"error"
]
}
],
"import/no-extraneous-dependencies": [

View File

@@ -1,6 +0,0 @@
export default {
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': 'babel-jest',
},
};

8829
client/package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

23
client/package.json vendored
View File

@@ -7,11 +7,14 @@
"build-prod": "cross-env BUILD_ENV=prod webpack --config webpack.prod.js",
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
"watch:hot": "cross-env BUILD_ENV=dev webpack-dev-server --config webpack.dev.js",
"lint": "echo 'Lint temporarily disabled'",
"lint-new": "eslint './src/**/*.(ts|tsx)'",
"lint:fix": "eslint './src/**/*.(ts|tsx)' --fix",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint --ext .ts,.tsx src",
"lint:fix": "eslint --ext .ts,.tsx src --fix",
"test": "vitest --run",
"test:watch": "vitest --watch",
"test:e2e": "npx playwright test tests/e2e",
"test:e2e:interactive": "npx playwright test --ui",
"test:e2e:debug": "npx playwright test --debug",
"test:e2e:codegen": "npx playwright codegen",
"typecheck": "tsc --noEmit",
"typecheck:watch": "tsc --noEmit --watch"
},
@@ -20,6 +23,7 @@
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",
@@ -34,6 +38,7 @@
"react": "^16.13.1",
"react-click-outside": "^3.0.1",
"react-dom": "^16.13.1",
"react-hook-form": "^7.54.0",
"react-i18next": "^11.7.2",
"react-modal": "^3.11.2",
"react-popper-tooltip": "^2.11.1",
@@ -46,7 +51,6 @@
"react-transition-group": "^4.4.5",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-form": "^8.3.10",
"redux-thunk": "^2.3.0",
"ts-migrate": "^0.1.35",
"url-polyfill": "^1.1.12"
@@ -60,15 +64,15 @@
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
"@types/jest": "^29.5.12",
"@playwright/test": "1.50.1",
"@types/lodash": "^4.17.4",
"@types/node": "^22.13.10",
"@types/react": "^17.0.80",
"@types/react-dom": "^18.3.0",
"@types/react-redux": "^7.1.33",
"@types/react-router-dom": "^5.3.3",
"@types/react-table": "^7.7.20",
"@types/redux-actions": "^2.6.5",
"@types/redux-form": "^8.3.10",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.10.0",
"babel-loader": "^9.1.3",
@@ -85,8 +89,6 @@
"eslint-plugin-react-hooks": "^4.6.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.6.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jscodeshift": "^0.15.2",
"mini-css-extract-plugin": "^2.9.0",
"path": "^0.12.7",
@@ -97,6 +99,7 @@
"stylelint": "^16.5.0",
"ts-loader": "^9.5.1",
"url-loader": "^4.1.1",
"vitest": "^3.1.1",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",

52
client/playwright.config.ts vendored Normal file
View File

@@ -0,0 +1,52 @@
import { defineConfig, devices } from '@playwright/test';
import path from 'path';
import { CONFIG_FILE_PATH } from './tests/constants';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
globalSetup: path.resolve('./tests/e2e/globalSetup.ts'),
globalTeardown: path.resolve('./tests/e2e/globalTeardown.ts'),
timeout: 5000,
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://127.0.0.1:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
launchOptions: {
headless: true,
},
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
stdout: process.env.CI ? 'pipe' : 'ignore',
command: `${!process.env.CI ? 'sudo ' : ''}./AdGuardHome --local-frontend -v -c ${CONFIG_FILE_PATH}`,
url: 'http://127.0.0.1:3000',
cwd: '..',
reuseExistingServer: !process.env.CI,
timeout: 10000,
},
});

View File

@@ -291,7 +291,7 @@
"custom_ip": "عنوان IP مخصص",
"blocking_ipv4": "حجب عنوان IPv4",
"blocking_ipv6": "حجب عنوان IPv6",
"blocked_response_ttl": "زمن حظر الاستجابة",
"blocked_response_ttl": "حظر استجابة TTL",
"blocked_response_ttl_desc": "تحديد عدد الثواني التي يجب على العملاء تخزين الاستجابة التي تمت تصفيتها مؤقتًا",
"form_enter_blocked_response_ttl": "أدخل وقت الاستجابة المحظورة TTL (بالثواني)",
"dnscrypt": "DNSCrypt",
@@ -734,10 +734,10 @@
"thursday": "الخميس",
"friday": "الجمعة",
"saturday": "السبت",
"sunday_short": "الاحد",
"sunday_short": "الأحد",
"monday_short": "الإثنين",
"tuesday_short": "الثلاثاء",
"wednesday_short": "الاربعاء",
"wednesday_short": "الأربعاء",
"thursday_short": "الخميس",
"friday_short": "الجمعة",
"saturday_short": "السبت",

View File

@@ -1,24 +1,24 @@
{
"client_settings": "Налады кліентаў",
"example_upstream_reserved": "upstream <0>для канкрэтных даменаў</0>;",
"example_multiple_upstreams_reserved": "некалькі DNS-сервераў <0>для канкрэтных даменаў</0>;",
"example_multiple_upstreams_reserved": "некалькі сервер DNSаў <0>для канкрэтных даменаў</0>;",
"example_upstream_comment": "каментар.",
"upstream_parallel": "Ужыць адначасныя запыты да ўсіх сервераў для паскарэння апрацоўкі запыту",
"parallel_requests": "Паралельныя запыты",
"load_balancing": "Размеркаванне нагрузкі",
"load_balancing_desc": "Запытвайце па адным серверы за раз. AdGuard Home будзе выкарыстоўваць выпадковы алгарытм для выбару сервера, так што самы хуткі сервер будзе выкарыстоўвацца часцей.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адрасы DNS-сервераў, якія выкарыстоўваюцца для вырашэння IP-адрасоў распознавальнікаў DoH/DoT, якія вы ўказваеце ў якасці перадачы. Каментары не дапускаюцца.",
"fallback_dns_title": "Рэзервовыя DNS-серверы",
"fallback_dns_desc": "Спіс рэзервовых DNS-сервераў, якія выкарыстоўваюцца, калі вышэйшыя DNS-серверы не адказваюць. Сінтаксіс такі ж, як і ў галоўным полі ўверх.",
"bootstrap_dns": "Bootstrap сервер DNSы",
"bootstrap_dns_desc": "IP-адрасы сервер DNSаў, якія выкарыстоўваюцца для вырашэння IP-адрасоў распознавальнікаў DoH/DoT, якія вы ўказваеце ў якасці перадачы. Каментары не дапускаюцца.",
"fallback_dns_title": "Рэзервовыя сервер DNSы",
"fallback_dns_desc": "Спіс рэзервовых сервер DNSаў, якія выкарыстоўваюцца, калі вышэйшыя сервер DNSы не адказваюць. Сінтаксіс такі ж, як і ў галоўным полі ўверх.",
"fallback_dns_placeholder": "Увядзіце па адным рэзервовым серверы DNS у радку",
"local_ptr_title": "Прыватныя DNS-серверы",
"local_ptr_title": "Прыватныя сервер DNSы",
"local_ptr_desc": "DNS-серверы, якія AdGuard Home выкарыстоўвае для лакальных PTR-запытаў. Гэтыя серверы выкарыстоўваюцца, каб атрымаць даменавыя імёны кліентаў з прыватнымі IP-адрасамі, напрыклад «192.168.12.34», з дапамогай rDNS. Калі спіс пусты, AdGuard Home выкарыстоўвае прадвызначаныя DNS-серверы вашай АС.",
"local_ptr_default_resolver": "Па змаўчанні AdGuard Home выкарыстоўвае наступныя зваротныя DNS-рэзолверы: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home не змог вызначыць прыдатныя прыватныя адваротныя DNS-рэзолверы для гэтай сістэмы.",
"local_ptr_placeholder": "Увядзіце па адным адрасе на радок",
"resolve_clients_title": "Уключыць запытванне даменавых імёнаў для кліентаў",
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны DNS-сервер для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны сервер DNS для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
"use_private_ptr_resolvers_title": "Ужываць прыватныя адваротныя DNS-рэзолверы",
"use_private_ptr_resolvers_desc": "Пасылаць адваротныя DNS-запыты для лакальна абслугоўных адрасоў на паказаныя серверы. Калі адключана, AdGuard Home будзе адказваць NXDOMAIN на ўсе падобныя PTR-запыты, апроч запытаў пра кліентаў, ужо вядомых па DHCP, /etc/hosts і гэтак далей.",
"check_dhcp_servers": "Праверыць DHCP-серверы",
@@ -101,13 +101,13 @@
"compact": "Компактный",
"nothing_found": "Нічога не знойдзена",
"faq": "FAQ",
"version": "версія",
"version": "Версія",
"address": "Адрас",
"protocol": "Пратакол",
"on": "УКЛ",
"off": "Выкл",
"copyright": "Усе правы захаваныя",
"homepage": "Галоўная",
"homepage": "Хатняя старонка",
"report_an_issue": "Паведаміць пра праблему",
"privacy_policy": "Палітыка прыватнасці",
"enable_protection": "Уключыць абарону",
@@ -165,8 +165,8 @@
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі",
"encryption_settings": "Налады шыфравання",
"dhcp_settings": "Налады DHCP",
"upstream_dns": "Upstream DNS-серверы",
"upstream_dns_help": "Увядзіце адрасы сервераў па адным у радку. <a>Даведацца больш </a> пра наладжванне DNS-сервераў.",
"upstream_dns": "Upstream сервер DNSы",
"upstream_dns_help": "Увядзіце адрасы сервераў па адным у радку. <a>Даведацца больш </a> пра наладжванне сервер DNSаў.",
"upstream_dns_configured_in_file": "Наладжаны ў {{path}}",
"test_upstream_btn": "Тэст upstream сервераў",
"upstreams": "Upstreams",
@@ -182,7 +182,7 @@
"enabled_save_search_toast": "Уключаны бяспечны пошук",
"updated_save_search_toast": "Налады бяспечнага пошуку абноўлены",
"enabled_table_header": "УКЛ.",
"name_table_header": "Імя",
"name_table_header": "Назва",
"list_url_table_header": "URL-адрас спіса",
"rules_count_table_header": "Колькасць правілаў:",
"last_time_updated_table_header": "Апошняе абнаўленне",
@@ -196,7 +196,7 @@
"no_whitelist_added": "Белыя спісы не дададзены",
"add_blocklist": "Дадаць чорны спіс",
"add_allowlist": "Дадаць белы спіс",
"cancel_btn": "Адмена",
"cancel_btn": "Скасаваць",
"enter_name_hint": "Увядзіце імя",
"enter_url_or_path_hint": "Увядзіце URL-адрас ці абсалютны шлях да спіса",
"check_updates_btn": "Праверыць абнаўленні",
@@ -219,7 +219,7 @@
"example_meaning_host_block": "адказаць 127.0.0.1 для example.org (але не для яго паддаменаў);",
"example_comment": "! Так можна дадаваць апісанне.",
"example_comment_meaning": "каментар;",
"example_comment_hash": "# І вось так таксама.",
"example_comment_hash": "# Таксама каментарый.",
"example_regex_meaning": "блакаваць доступ да даменаў, якія адпавядаюць зададзенаму рэгулярнаму выразу.",
"example_upstream_regular": "звычайны DNS (наўзверх UDP);",
"example_upstream_regular_port": "звычайны DNS (праз UDP, імя хаста);",
@@ -233,13 +233,13 @@
"example_upstream_tcp_port": "звычайны DNS (праз TCP, імя хаста);",
"example_upstream_tcp_hostname": "звычайны DNS (праз TCP, імя хаста);",
"all_lists_up_to_date_toast": "Усе спісы ўжо абноўлены",
"updated_upstream_dns_toast": "Upstream DNS-серверы абноўлены",
"updated_upstream_dns_toast": "Upstream сервер DNSы абноўлены",
"dns_test_ok_toast": "Паказаныя серверы DNS працуюць карэктна",
"dns_test_not_ok_toast": "Сервер «{{key}}»: немагчыма выкарыстоўваць, праверце слушнасць напісання",
"dns_test_parsing_error_toast": "Раздзел {{section}}: радок {{line}}: немагчыма выкарыстоўваць, праверце слушнасць напісання",
"dns_test_warning_toast": "Upstream «{{key}}» не адказвае на тэставыя запыты і можа не працаваць належным чынам",
"unblock": "Адблакаваць",
"block": "Заблакаваць",
"block": "Заблакіраваць",
"disallow_this_client": "Забараніць доступ гэтаму кліенту",
"allow_this_client": "Дазволіць доступ гэтаму кліенту",
"block_for_this_client_only": "Заблакаваць толькі для гэтага кліента",
@@ -259,7 +259,7 @@
"no_logs_found": "Логі не знойдзены",
"refresh_btn": "Абнавіць",
"previous_btn": "Назад",
"next_btn": "Наперад",
"next_btn": "Далей",
"loading_table_status": "Загрузка...",
"page_table_footer_text": "Старонка",
"rows_table_footer_text": "радкоў",
@@ -280,7 +280,7 @@
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
"dns_config": "Налады DNS-сервера",
"dns_config": "Налады сервер DNSа",
"dns_cache_config": "Налада кэша DNS",
"dns_cache_config_desc": "Тут можна наладзіць кэш DNS",
"blocking_mode": "Рэжым блакавання",
@@ -342,14 +342,14 @@
"unknown_filter": "Невядомы фільтр {{filterId}}",
"known_tracker": "Вядомы трэкер",
"install_welcome_title": "Сардэчна запрашаем у AdGuard Home!",
"install_welcome_desc": "AdGuard Home гэта DNS-сервер, што блакуе рэкламу і трэкінг. Яго мэта даць вам магчымасць кантраляваць усю ваша сеціва і ўсе падлучаныя прылады. Ён не патрабуе ўсталёўкі кліенцкіх праграм.",
"install_welcome_desc": "AdGuard Home гэта сервер DNS, што блакуе рэкламу і трэкінг. Яго мэта даць вам магчымасць кантраляваць усю ваша сеціва і ўсе падлучаныя прылады. Ён не патрабуе ўсталёўкі кліенцкіх праграм.",
"install_settings_title": "Ўэб-інтэрфейс адміністравання",
"install_settings_listen": "Інтэрфейс сеціва",
"install_settings_port": "Порт",
"install_settings_interface_link": "Ваш ўэб-інтэрфейс адміністравання AdGuard Home будзе даступны па наступных адрасах:",
"form_error_port": "Увядзіце карэктны нумар порта",
"install_settings_dns": "DNS-сервер",
"install_settings_dns_desc": "Вам будзе трэба наладзіць свае прылады ці роўтар на выкарыстанне DNS-сервера на адным з наступных адрасоў:",
"install_settings_dns_desc": "Вам будзе трэба наладзіць свае прылады ці роўтар на выкарыстанне сервер DNSа на адным з наступных адрасоў:",
"install_settings_all_interfaces": "Усе інтэрфейсы",
"install_auth_title": "Аўтарызацыя",
"install_auth_desc": "Настойліва рэкамендуецца наладзіць аўтэнтыфікацыю паролем для ўэб-інтэрфейсу AdGuard Home. Нават калі ён даступны толькі ў вашай лакальнай сетцы, важна абараніць яго ад неабмежаванага доступу.",
@@ -365,17 +365,17 @@
"install_submit_desc": "Працэдура налады завершана і вы гатовы пачаць выкарыстанне AdGuard Home.",
"install_devices_router": "Роўтар",
"install_devices_router_desc": "Такая наладка аўтаматычна пакрые ўсе прылады, што выкарыстоўваюць ваш хатні роўтар, і вам не трэба будзе наладжваць кожнае з іх у асобнасці.",
"install_devices_address": "DNS-сервер AdGuard Home даступны па наступных адрасах",
"install_devices_address": "сервер DNS AdGuard Home даступны па наступных адрасах",
"install_devices_router_list_1": "Адкрыйце налады вашага роўтара. Звычайна вы можаце адкрыць іх у вашым браўзары, напрыклад, http://192.168.0.1/ ці http://192.168.1.1/. Вас могуць папрасіць увесці пароль. Калі вы не помніце яго, пароль часта можна скінуць, націснуўшы на кнопку на самым роўтары. Некаторыя роўтары патрабуюць адмысловага дадатку, які ў гэтым выпадку павінен быць ужо ўсталявана на ваш кампутар ці тэлефон.",
"install_devices_router_list_2": "Знайдзіце налады DHCP ці DNS. Знайдзіце літары «DNS» поруч з тэкставым полем, у якое можна ўвесці два ці тры шэрагі лічбаў, падзеленых на 4 групы ад адной до трох лічбаў.",
"install_devices_router_list_3": "Увядзіце туды адрас вашага AdGuard Home.",
"install_devices_router_list_4": "Вы не можаце ўсталяваць уласны DNS-сервер на некаторых тыпах маршрутызатараў. У гэтым выпадку можа дапамагчы налада AdGuard Home у якасці <a href='#dhcp'>DHCP-сервера</a>. У адваротным выпадку вам трэба звярнуцца да кіраўніцтва па наладзе DNS-сервераў для вашай пэўнай мадэлі маршрутызатара.",
"install_devices_router_list_4": "Вы не можаце ўсталяваць уласны сервер DNS на некаторых тыпах маршрутызатараў. У гэтым выпадку можа дапамагчы налада AdGuard Home у якасці <a href='#dhcp'>DHCP-сервера</a>. У адваротным выпадку вам трэба звярнуцца да кіраўніцтва па наладзе сервер DNSаў для вашай пэўнай мадэлі маршрутызатара.",
"install_devices_windows_list_1": "Адкрыйце Панэль кіравання праз меню «Пуск» ці праз пошук Windows.",
"install_devices_windows_list_2": "Перайдзіце ў «Сеціва і інтэрнэт», а потым у «Цэнтр кіравання сеціва і агульным доступам».",
"install_devices_windows_list_3": "У левым боку экрана клікніце «Змена параметраў адаптара».",
"install_devices_windows_list_4": "Пстрыкніце правай кнопкай мышы ваша актыўнае злучэнне і абярыце Уласцівасці.",
"install_devices_windows_list_5": "Знайдзіце ў спісе пункт «IP версіі 4 (TCP/IPv4)», вылучыце яго і потым ізноў націсніце «Уласцівасці».",
"install_devices_windows_list_6": "Абярыце «Выкарыстаць наступныя адрасы DNS-сервераў» і ўвядзіце адрас AdGuard Home.",
"install_devices_windows_list_6": "Абярыце «Выкарыстаць наступныя адрасы сервер DNSаў» і ўвядзіце адрас AdGuard Home.",
"install_devices_macos_list_1": "Клікніце па абразку Apple і перайдзіце ў Сістэмныя налады.",
"install_devices_macos_list_2": "Клікніце па іконцы Сеціва.",
"install_devices_macos_list_3": "Абярыце першае падлучэнне ў спісе і націсніце кнопку «Дадаткова».",
@@ -415,7 +415,7 @@
"encryption_key": "Прыватны ключ",
"encryption_key_input": "Скапіюйце сюды прыватны ключ у PEM-кадоўцы.",
"encryption_enable": "Уключыць шыфраванне (HTTPS, DNS-over-HTTPS і DNS-over-TLS)",
"encryption_enable_desc": "Калі шыфраванне ўлучана, ўэб-інтэрфейс AdGuard Home будзе працаваць па HTTPS, а DNS-сервер будзе таксама працаваць па DNS-over-HTTPS і DNS-over-TLS.",
"encryption_enable_desc": "Калі шыфраванне ўлучана, ўэб-інтэрфейс AdGuard Home будзе працаваць па HTTPS, а сервер DNS будзе таксама працаваць па DNS-over-HTTPS і DNS-over-TLS.",
"encryption_chain_valid": "Ланцужок сертыфікатаў валідны",
"encryption_chain_invalid": "Ланцужок сертыфікатаў не валідны",
"encryption_key_valid": "Валідны {{type}} прыватны ключ",
@@ -435,8 +435,8 @@
"update_announcement": "AdGuard Home {{version}} ужо даступная! <0>Націсніце сюды</0>, каб даведацца больш.",
"setup_guide": "Інструкцыя па наладзе",
"dns_addresses": "Адрасы DNS",
"dns_start": "DNS-сервер запускаецца",
"dns_status_error": "Памылка праверкі стану DNS-сервера",
"dns_start": "сервер DNS запускаецца",
"dns_status_error": "Памылка праверкі стану сервер DNSа",
"down": "Уніз",
"fix": "Выправіць",
"dns_providers": "<0>Спіс вядомых DNS-правайдараў</0> на выбар.",
@@ -449,7 +449,7 @@
"settings_global": "Глабальныя",
"settings_custom": "Свае",
"table_client": "Кліент",
"table_name": "Імя",
"table_name": "Назва",
"save_btn": "Захаваць",
"client_add": "Дадаць кліента",
"client_new": "Новы кліент",
@@ -475,7 +475,7 @@
"auto_clients_title": "Кліенты (runtime)",
"auto_clients_desc": "Інфармацыя аб IP-адрасах прылад, якія выкарыстоўваюць або могуць выкарыстоўваць AdGuard Home. Гэтая інфармацыя збіраецца з некалькіх крыніц, уключаючы файлы хостаў, зваротны DNS і г.д.",
"access_title": "Налады доступу",
"access_desc": "Тут вы можаце наладзіць правілы доступу да DNS-серверу AdGuard Home",
"access_desc": "Тут вы можаце наладзіць правілы доступу да сервер DNSу AdGuard Home",
"access_allowed_title": "Дазволеныя кліенты",
"access_allowed_desc": "Спіс CIDR, IP-адрасоў або <a>ClientID</a>. Калі ў гэтым спісе ёсць запісы, AdGuard Home будзе прымаць запыты толькі ад гэтых кліентаў.",
"access_disallowed_title": "Забароненыя кліенты",
@@ -596,7 +596,7 @@
"disable_ipv6_desc": "Ігнараваць усе запыты DNS для адрасоў IPv6 (тып AAAA) і выдаленне дадзеных IPv6 з адказаў тыпу HTTPS.",
"fastest_addr": "Найхуткі IP-адрас",
"fastest_addr_desc": "Апытайце ўсе DNS-серверы і вярніце самы хуткі IP-адрас сярод усіх адказаў. Гэта замарудзіць выкананне DNS-запытаў, бо нам давядзецца чакаць адказаў ад усіх DNS-сервераў, але палепшыць агульную ўзаемасувязь.",
"autofix_warning_text": "Пры націску «Выправіць» AdGuard Home наладзіць вашу сістэму на выкарыстанне DNS-сервера AdGuard Home.",
"autofix_warning_text": "Пры націску «Выправіць» AdGuard Home наладзіць вашу сістэму на выкарыстанне сервер DNSа AdGuard Home.",
"autofix_warning_list": "Будуць выконвацца наступныя заданні: <0>Дэактываваць сістэмны DNSStubListener</0> <0>Усталяваць адрас сервера DNS на 127.0.0.1</0> <0>Стварыць сімвалічную спасылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Спыніць DNSStubListener (перазагрузіць сістэмную службу)</0>.",
"autofix_warning_result": "У выніку ўсе DNS-запыты ад вашай сістэмы будуць па змаўчанні апрацоўвацца AdGuard Home.\n",
"tags_title": "Тэгі",
@@ -634,12 +634,12 @@
"validated_with_dnssec": "Проверено с помощью DNSSEC",
"all_queries": "Усе запыты",
"show_blocked_responses": "Заблакавана",
"show_whitelisted_responses": "Белы спіс",
"show_whitelisted_responses": "У белым спісе",
"show_processed_responses": "Апрацавана",
"blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
"blocked_threats": "Заблакавана пагроз",
"allowed": "Дазволены",
"allowed": "У белым спісе",
"filtered": "Адфільтраваныя",
"rewritten": "Перапісаныя",
"safe_search": "Бяспечны пошук",
@@ -738,7 +738,7 @@
"thursday_short": "Чц.",
"friday_short": "Пт.",
"saturday_short": "Сб.",
"upstream_dns_cache_configuration": "Канфігурацыя кэша upstream DNS-сервераў",
"upstream_dns_cache_configuration": "Канфігурацыя кэша upstream сервер DNSаў",
"enable_upstream_dns_cache": "Ўключыць кэшаванне для карыстацкай канфігурацыі upstream-сервераў гэтага кліента",
"dns_cache_size": "Памер кэша DNS, у байтах"
}

View File

@@ -45,6 +45,7 @@
"filter": "Филтър",
"query_log": "История на заявките",
"compact": "Compact",
"nothing_found": "Нищо не е намерено",
"faq": "ЧЗВ",
"version": "версия",
"address": "Адрес",
@@ -65,14 +66,12 @@
"stats_malware_phishing": "вируси/атаки",
"stats_adult": "сайтове за възрастни",
"stats_query_domain": "Най-отваряни страници",
"for_last_24_hours": "за последните 24 часа",
"no_domains_found": "Няма намерени резултати",
"requests_count": "Сума на заявките",
"top_blocked_domains": "Най-блокирани страници",
"top_clients": "Най-активни IP адреси",
"no_clients_found": "Нямa намерени адреси",
"general_statistics": "Обща статисика",
"number_of_dns_query_24_hours": "Сума на DNS заявки за последните 24 часа",
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
@@ -156,9 +155,12 @@
"rule_added_to_custom_filtering_toast": "Добавено до местни правила за филтриране: {{rule}}",
"default": "По подразбиране",
"custom_ip": "Персонализиран IP",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-пред-HTTPS",
"dns_over_quic": "DNS-over-QUIC",
"plain_dns": "Обикновен DNS",
"theme_light": "Светла тема",
"theme_dark": "Тъмна тема",
"source_label": "Източник",
"found_in_known_domain_db": "Намерен в списъците с домейни.",
"category_label": "Категория",
@@ -282,6 +284,14 @@
"blocklist": "Черен списък",
"filter_category_general": "General",
"filter_category_security": "Сигурност",
"filter_category_other": "Друго",
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това.",
"parental_control": "Родителски контрол"
"parental_control": "Родителски контрол",
"sunday_short": "Нд",
"monday_short": "Пон",
"tuesday_short": "Вт",
"wednesday_short": "Ср",
"thursday_short": "Чт",
"friday_short": "Пт",
"saturday_short": "Съб"
}

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
"parallel_requests": "Paralelní požadavky",
"load_balancing": "Optimalizace vytížení",
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas.<br/>AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy DNS serverů používaných k překladu IP adres řešitelů DoH/DoT, které zadáte jako odchozí servery. Komentáře nejsou povoleny.",
"fallback_dns_title": "Záložní DNS servery",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použít službu AdGuard Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home zkontroluje, zda doména obsahuje materiály pro dospělé. Používá stejné API přátelské k ochraně osobních údajů jako služba Bezpečnost prohlížení.",
"enforce_safe_search": "Použít bezpečné vyhledávání",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nebyly specifikovány žádné servery",
"general_settings": "Obecná nastavení",
"dns_settings": "Nastavení DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "TTL blokované odezvy",
"blocked_response_ttl_desc": "Určuje, na kolik sekund by měli klienti ukládat filtrovanou odezvu do mezipaměti",
"form_enter_blocked_response_ttl": "Zadejte TTL blokované odezvy (v sekundách)",
"upstream_timeout": "Časový limit odchozího serveru",
"upstream_timeout_desc": "Určuje počet sekund čekání na odpověď od odchozího serveru",
"form_enter_upstream_timeout": "Zadejte dobu časového limitu odchozího serveru v sekundách",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS skrze HTTPS",
"dns_over_tls": "DNS skrze TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Zakázat řešení IPv6 adres",
"disable_ipv6_desc": "Odstranění všech dotazů DNS na adresy IPv6 (typ AAAA) a odstranění náznaků IPv6 z odpovědí HTTPS.",
"fastest_addr": "Nejrychlejší IP adresa",
"fastest_addr_desc": "Dotazovat všechny DNS servery a vrátit nejrychlejší IP adresu ze všech odpovědí. To zpomalí dotazy DNS, protože AdGuard Home musí čekat na odpovědi ze všech serverů DNS, ale celková konektivita se zlepší.",
"fastest_addr_desc": "Počká na odpovědi <b>všech</b> serverů DNS, změří rychlost připojení TCP pro každý server a vrátí IP adresu serveru s nejvyšší rychlostí připojení.<br/>Tento režim může výrazně zpomalit dotazy DNS, pokud jeden nebo více odchozích serverů neodpovídá. Ujistěte se, že vaše odchozí servery jsou stabilní a že časový limit odchozích serverů je nízký.",
"autofix_warning_text": "Pokud kliknete na „Opravit“, AdGuard Home nakonfiguruje váš systém tak, aby používal DNS server AdGuard Home.",
"autofix_warning_list": "Jsou prováděny následující úlohy: <0>Deaktivace systému DNSStubListener</0> <0>Nastavení adresy serveru DNS na 127.0.0.1</0> <0>Nahrazení cíle symbolického odkazu z /etc/resolv.conf do /run/systemd/resolve/resolv.conf</0> <0>Zastavení služby DNSStubListener (znovu načtení služby systemd-resolved)</0>",
"autofix_warning_result": "Výsledkem je, že všechny požadavky DNS z vašeho systému jsou ve výchozím nastavení zpracovány službou AdGuard Home.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Důvod: {{reason}}",
"check_service": "Název služby: {{service}}",
"check_hostname": "Název hostitele nebo domény",
"check_client_id": "Identifikátor klienta (ClientID nebo IP adresa)",
"check_enter_client_id": "Zadejte identifikátor klienta",
"check_dns_record": "Vyberte typ DNS záznamu",
"service_name": "Název služby",
"check_not_found": "Nenalezeno ve Vašich seznamech filtrů",
"client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?",
@@ -649,7 +656,7 @@
"blocklist": "Zakázaný",
"milliseconds_abbreviation": "ms",
"cache_size": "Velikost mezipaměti",
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech). Chcete-li ukládání do mezipaměti zakázat, ponechte prázdné.",
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech). Chcete-li ukládání do mezipaměti zakázat, nastavte 0.",
"cache_ttl_min_override": "Přepsat minimální hodnotu TTL",
"cache_ttl_max_override": "Přepsat maximální hodnotu TTL",
"enter_cache_size": "Zadejte velikost mezipaměti (v bajtech)",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
"parallel_requests": "Parallelle forespørgsler",
"load_balancing": "Belastningsfordeling",
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
"load_balancing_desc": "Forespørg én upstream-server ad gangen.<br/>AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
"bootstrap_dns": "Bootstrap DNS-servere",
"bootstrap_dns_desc": "IP-adresser på DNS-servere, som bruges til at opløse IP-adresser på de DoH/DoT-opløsere, som angives som upstreams. Kommentarer er ikke tilladt.",
"fallback_dns_title": "Reserve DNS-servere",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Brug AdGuards forældrekontrolwebtjeneste",
"use_adguard_parental_hint": "AdGuard Home vil tjekke, om domænet indeholder voksenindhold vha. den samme fortrolighedsvenlige API som browsingsikkerhedswebtjenesten.",
"enforce_safe_search": "Brug sikker søgning",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Yandex og Pixabay.",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Ingen servere angivet",
"general_settings": "Generelle indstillinger",
"dns_settings": "DNS-indstillinger",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Blokeret svar TTL",
"blocked_response_ttl_desc": "Angiver, i hvor mange sekunder klienterne skal cache-lagre et filtreret svar",
"form_enter_blocked_response_ttl": "Angiv blokeringssvar TTL (sekunder)",
"upstream_timeout": "Upstream-timeout",
"upstream_timeout_desc": "Angiver antallet af sekunder, der skal ventes på et svar fra upstream-serveren",
"form_enter_upstream_timeout": "Angiv varigheden af upstream-server timeout i sekunder",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Deaktivér IPv6-adresseopløsning",
"disable_ipv6_desc": "Drop alle DNS-forespørgsler for IPv6-adresser (type AAAA), og fjern IPv6-tips fra HTTPS-svar.",
"fastest_addr": "Hurtigste IP-adresse",
"fastest_addr_desc": "Forespørger alle DNS-servere og returner den hurtigste IP-adresse blandt alle svar. Dette vil gøre DNS-forespørgslerne langsommere grundet afventning af svar fra alle DNS-servere, men forbedrer samlet set forbindelsen.",
"fastest_addr_desc": "Vent på svar fra <b>alle</b> DNS-servere, mål TCP-forbindelseshastigheden for hver server, og returner IP-adressen på serveren med den hurtigste forbindelseshastighed.<br/>Denne tilstand kan sinke DNS-forespørgsler, betydeligt hvis en eller flere upstream-servere ikke svarer. Sørg for, at upstream-serverene er stabile, og at upstream-timeouten er lav.",
"autofix_warning_text": "Klikker du på \"Reparér\", opsætter AdGuard Home dit system til brug med AdGuard Home DNS-server.",
"autofix_warning_list": "Den vil udføre disse opgaver: <0>Deaktivere system DNSStubListener</0> <0>Opsætte DNS-serveradressen til 127.0.0.1</0> <0>Erstatte symbolsk linkmål for /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (genindlæs systemd-opløst tjeneste)</0>",
"autofix_warning_result": "Det betyder, at alle DNS-forespørgsler fra dit system som standard behandles af AdGuard Home.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Årsag: {{reason}}",
"check_service": "Tjenestenavn: {{service}}",
"check_hostname": "Værts- eller domænenavn",
"check_client_id": "Klientidentifikator (ClientID eller IP-adresse)",
"check_enter_client_id": "Angiv klientidentifikator",
"check_dns_record": "Vælg DNS-posttype",
"service_name": "Tjenestenavn",
"check_not_found": "Ikke fundet i dine filterlister",
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Sortliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache-størrelse",
"cache_size_desc": "DNS cache-størrelse (i bytes). Lad stå tomt for at deaktivere cache.",
"cache_size_desc": "DNS cache-størrelse (i bytes). Sæt til 0 for at deaktivere cache.",
"cache_ttl_min_override": "Tilsidesæt minimum TTL",
"cache_ttl_max_override": "Tilsidesæt maksimal TTL",
"enter_cache_size": "Angiv cache-størrelse (bytes)",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
"parallel_requests": "Paralleles Abfragen",
"load_balancing": "Lastverteilung",
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt.<br/> AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
"bootstrap_dns": "Bootstrap-DNS-Server",
"bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.",
"fallback_dns_title": "Fallback-DNS-Server",
@@ -97,9 +97,9 @@
"settings": "Einstellungen",
"filters": "Filter",
"filter": "Filter",
"query_log": "Anfragenprotokoll",
"query_log": "Abfrageprotokoll",
"compact": "Kompakt",
"nothing_found": "Nichts gefunden\n",
"nothing_found": "Nichts gefunden",
"faq": "FAQ",
"version": "Version",
"address": "Adresse",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard Webservice für Kindersicherung verwenden",
"use_adguard_parental_hint": "AdGuard Home wird prüfen, ob die Domain jugendgefährdende Inhalte enthält. Zum Schutz Ihrer Privatsphäre wird die selbe API wie für den Webservice für Internetsicherheit verwendet.",
"enforce_safe_search": "Sichere Suche verwenden",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Yandex und Pixabay.",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex und Pixabay.",
"no_servers_specified": "Keine Server festgelegt",
"general_settings": "Allgemeine Einstellungen",
"dns_settings": "DNS-Einstellungen",
@@ -199,7 +199,7 @@
"cancel_btn": "Abbrechen",
"enter_name_hint": "Name eingeben",
"enter_url_or_path_hint": "URL oder absoluten Pfad der Liste eingeben",
"check_updates_btn": "Nach Aktualisierungen suchen",
"check_updates_btn": "Nach Updates suchen",
"new_blocklist": "Neue Sperrliste",
"new_allowlist": "Neue Positivliste",
"edit_blocklist": "Sperrliste bearbeiten",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Gültigkeitsdauer der blockierten Antwort",
"blocked_response_ttl_desc": "Gibt an, wie viele Sekunden lang die Clients eine gefilterte Antwort zwischenspeichern sollen",
"form_enter_blocked_response_ttl": "Geben Sie die Gültigkeitsdauer für blockierte Antworten ein (in Sekunden)",
"upstream_timeout": "Upstream-Timeout",
"upstream_timeout_desc": "Gibt die Anzahl der Sekunden an, die auf eine Antwort des Upstream-Servers gewartet werden soll",
"form_enter_upstream_timeout": "Geben Sie die Timeout-Dauer des Upstream-Servers in Sekunden ein",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -563,7 +566,7 @@
"ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)",
"ignore_domains_title": "Ignorierte Domains",
"ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen",
"ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Anfragenprotokoll aufgenommen",
"ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Abfrageprotokoll aufgenommen",
"interval_hours": "{{count}} Stunde",
"interval_hours_plural": "{{count}} Stunden",
"filters_configuration": "Filterkonfiguration",
@@ -598,7 +601,7 @@
"disable_ipv6": "IPv6 deaktivieren",
"disable_ipv6_desc": "Alle DNS-Anfragen für IPv6-Adressen (Typ AAAA) verwerfen und IPv6-Hinweise aus HTTPS-Antworten entfernen.",
"fastest_addr": "Schnellste IP-Adresse",
"fastest_addr_desc": "Fragen Sie alle DNS-Server ab und geben Sie die schnellste IP-Adresse unter allen Antworten zurück. Dies verlangsamt DNS-Abfragen, da AdGuard Home auf Antworten von allen DNS-Servern warten muss, verbessert jedoch die Gesamtkonnektivität.",
"fastest_addr_desc": "Auf Antworten von <b>allen</b> DNS-Servern warten, die TCP-Verbindungsgeschwindigkeit für jeden Server messen und die IP-Adresse des Servers mit der schnellsten Verbindungsgeschwindigkeit zurückgeben.<br/>Dieser Modus kann DNS-Anfragen erheblich verlangsamen, wenn ein oder mehrere Server nicht antworten. Stellen Sie sicher, dass Ihre Server stabil laufen und das Upstream-Timeout niedrig ist.",
"autofix_warning_text": "Wenn Sie auf „Beheben“ klicken, konfiguriert AdGuardHome Ihr System für die Verwendung des AdGuardHome-DNS-Servers.",
"autofix_warning_list": "Es werden folgende Aufgaben ausgeführt: <0>Deaktivieren des DNSStubListener-Systems</0> <0>Festlegen der DNS-Server-Adresse auf 127.0.0.1</0> <0>Ersetzen des symbolischen Linkziels von /etc/resolv.conf auf /run/systemd/resolve/resolv.conf</0> <0>Anhalten des DNSStubListener (systemseitig aufgelöster Dienst wird nachladen)</0>",
"autofix_warning_result": "Als Folge daraus werden alle DNS-Anforderungen von Ihrem System standardmäßig von AdGuardHome verarbeitet.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Grund: {{reason}}",
"check_service": "Dienstname: {{service}}",
"check_hostname": "Hostname oder Domainname",
"check_client_id": "Client-Kennung (ClientID oder IP-Adresse)",
"check_enter_client_id": "Client-Kennung eingeben",
"check_dns_record": "DNS-Datensatztyp auswählen",
"service_name": "Name des Dienstes",
"check_not_found": "Nicht in Ihren Filterlisten enthalten",
"client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?",
@@ -649,7 +656,7 @@
"blocklist": "Sperrliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Größe des Cache",
"cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes)",
"cache_size_desc": "Größe des DNS-Cache (in Bytes). Um das Caching zu deaktivieren, setzen Sie den Wert auf 0.",
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
"enter_cache_size": "Größe des Cache (Bytes) eingeben",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
"parallel_requests": "Parallel requests",
"load_balancing": "Load-balancing",
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
"load_balancing_desc": "Query one upstream server at a time.<br/>AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
"bootstrap_dns": "Bootstrap DNS servers",
"bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.",
"fallback_dns_title": "Fallback DNS servers",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Use AdGuard parental control web service",
"use_adguard_parental_hint": "AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.",
"enforce_safe_search": "Use Safe Search",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "No servers specified",
"general_settings": "General settings",
"dns_settings": "DNS settings",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Blocked response TTL",
"blocked_response_ttl_desc": "Specifies for how many seconds the clients should cache a filtered response",
"form_enter_blocked_response_ttl": "Enter blocked response TTL (seconds)",
"upstream_timeout": "Upstream timeout",
"upstream_timeout_desc": "Specifies the number of seconds to wait for a response from the upstream server",
"form_enter_upstream_timeout": "Enter the upstream server timeout duration in seconds",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Disable resolving of IPv6 addresses",
"disable_ipv6_desc": "Drop all DNS queries for IPv6 addresses (type AAAA) and remove IPv6 hints from HTTPS responses.",
"fastest_addr": "Fastest IP address",
"fastest_addr_desc": "Query all DNS servers and return the fastest IP address among all responses. This slows down DNS queries as AdGuard Home has to wait for responses from all DNS servers, but improves the overall connectivity.",
"fastest_addr_desc": "Wait for responses from <b>all</b> DNS servers, measure the TCP connection speed for each server, and return the IP address of the server with the fastest connection speed.<br/>This mode can significantly slow down DNS queries, if one or more upstream servers are not responding. Make sure that your upstream servers are stable and your upstream timeout is low.",
"autofix_warning_text": "If you click \"Fix\", AdGuard Home will configure your system to use AdGuard Home DNS server.",
"autofix_warning_list": "It will perform these tasks: <0>Deactivate system DNSStubListener</0> <0>Set DNS server address to 127.0.0.1</0> <0>Replace symbolic link target of /etc/resolv.conf with /run/systemd/resolve/resolv.conf</0> <0>Stop DNSStubListener (reload systemd-resolved service)</0>",
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuard Home by default.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Reason: {{reason}}",
"check_service": "Service name: {{service}}",
"check_hostname": "Hostname or domain name",
"check_client_id": "Client identifier (ClientID or IP address)",
"check_enter_client_id": "Enter client identifier",
"check_dns_record": "Select DNS record type",
"service_name": "Service name",
"check_not_found": "Not found in your filter lists",
"client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Blocklist",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache size",
"cache_size_desc": "DNS cache size (in bytes). To disable caching, leave empty.",
"cache_size_desc": "DNS cache size (in bytes). To disable caching, set to 0.",
"cache_ttl_min_override": "Override minimum TTL",
"cache_ttl_max_override": "Override maximum TTL",
"enter_cache_size": "Enter cache size (bytes)",

View File

@@ -1,16 +1,16 @@
{
"client_settings": "Configuración de clientes",
"example_upstream_reserved": "un DNS de subida <0>para un dominio específico</0>.",
"example_multiple_upstreams_reserved": "múltiples upstreams <0>para dominios específicos</0>;",
"example_upstream_reserved": "un proveedor DNS <0>para un dominio específico</0>.",
"example_multiple_upstreams_reserved": "múltiples proveedores DNS <0>para dominios específicos</0>.",
"example_upstream_comment": "un comentario.",
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los proveedores DNS.",
"parallel_requests": "Consultas paralelas",
"load_balancing": "Balanceo de carga",
"load_balancing_desc": "Consulta un servidor upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
"load_balancing_desc": "Consulta un proveedor DNS a la vez.<br/>AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo promedio de búsqueda.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"bootstrap_dns_desc": "Direcciones IP de los servidores DNS utilizados para resolver las direcciones IP de los resolutores DoH/DoT que especifiques como proveedores DNS. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos",
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los proveedores DNS no responden. La sintaxis es la misma que en el campo de los principales proveedores DNS anterior.",
"fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea",
"local_ptr_title": "Servidores DNS inversos y privados",
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
@@ -18,9 +18,9 @@
"local_ptr_no_default_resolver": "AdGuard Home no pudo determinar los resolutores DNS inversos y privados adecuados para este sistema.",
"local_ptr_placeholder": "Ingresa una dirección IP por línea",
"resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes",
"resolve_clients_desc": "Resolve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, servidores DNS de subida para clientes con direcciones IP públicas).",
"resolve_clients_desc": "Resuelve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, proveedores DNS para clientes con direcciones IP públicas).",
"use_private_ptr_resolvers_title": "Usar resolutores DNS inversos y privados",
"use_private_ptr_resolvers_desc": "Resolver las peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones privadas utilizando servidores upstream privados, DHCP, /etc/hosts, etc. Si se desactiva, AdGuard Home responde a todas estas consultas con NXDOMAIN.",
"use_private_ptr_resolvers_desc": "Resuelve peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones IP privadas a través de proveedores DNS privados, DHCP, /etc/hosts, etc. Si se deshabilita, AdGuard Home responde a todas estas peticiones con NXDOMAIN.",
"check_dhcp_servers": "Comprobar si hay servidores DHCP",
"save_config": "Guardar configuración",
"enabled_dhcp": "Servidor DHCP habilitado",
@@ -132,8 +132,8 @@
"top_clients": "Clientes más frecuentes",
"no_clients_found": "No se han encontrado clientes",
"general_statistics": "Estadísticas generales",
"top_upstreams": "DNS de subida más frecuentes",
"no_upstreams_data_found": "No se han encontrado datos de DNS de subida",
"top_upstreams": "Proveedores DNS más frecuentes",
"no_upstreams_data_found": "No se han encontrado datos de proveedores DNS",
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
"number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora",
@@ -144,7 +144,7 @@
"enforced_save_search": "Búsquedas seguras forzadas",
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
"average_processing_time": "Tiempo promedio de procesamiento",
"average_upstream_response_time": "Tiempo promedio de respuesta upstream",
"average_upstream_response_time": "Tiempo promedio de respuesta del proveedor DNS",
"response_time": "Tiempo de respuesta",
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
@@ -154,10 +154,10 @@
"use_adguard_parental": "Usar el control parental de AdGuard",
"use_adguard_parental_hint": "AdGuard Home comprobará si el dominio contiene materiales para adultos. Utiliza la misma API amigable con la privacidad del servicio web de seguridad de navegación.",
"enforce_safe_search": "Usar búsqueda segura",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Yandex y Pixabay.",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex y Pixabay.",
"no_servers_specified": "No hay servidores especificados",
"general_settings": "Configuración general",
"dns_settings": "Configuración del DNS",
"dns_settings": "Configuración DNS",
"dns_blocklists": "Listas de bloqueo DNS",
"dns_allowlists": "Listas de permitido DNS",
"dns_blocklists_desc": "AdGuard Home bloqueará los dominios que coincidan con las listas de bloqueo.",
@@ -165,12 +165,12 @@
"custom_filtering_rules": "Reglas de filtrado personalizado",
"encryption_settings": "Configuración de cifrado",
"dhcp_settings": "Configuración DHCP",
"upstream_dns": "Servidores DNS de subida",
"upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los servidores DNS de subida.",
"upstream_dns": "Proveedores DNS",
"upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los proveedores DNS.",
"upstream_dns_configured_in_file": "Configurado en {{path}}",
"test_upstream_btn": "Probar DNS de subida",
"upstreams": "DNS de subida",
"upstream": "DNS de subida",
"test_upstream_btn": "Probar proveedores DNS",
"upstreams": "Proveedores DNS",
"upstream": "Proveedor DNS",
"apply_btn": "Aplicar",
"disabled_filtering_toast": "Filtrado deshabilitado",
"enabled_filtering_toast": "Filtrado habilitado",
@@ -233,11 +233,11 @@
"example_upstream_tcp_port": "DNS regular (mediante TCP, con puerto).",
"example_upstream_tcp_hostname": "DNS regular (mediante TCP, nombre del host).",
"all_lists_up_to_date_toast": "Todas las listas ya están actualizadas",
"updated_upstream_dns_toast": "Servidores DNS de subida guardados correctamente",
"updated_upstream_dns_toast": "Proveedores DNS guardados correctamente",
"dns_test_ok_toast": "Los servidores DNS especificados funcionan correctamente",
"dns_test_not_ok_toast": "Servidor \"{{key}}\": no se puede utilizar, por favor revisa si lo has escrito correctamente",
"dns_test_parsing_error_toast": "No se pudo utilizar la sección {{section}}: línea {{line}}:, verifica si la escribiste correctamente",
"dns_test_warning_toast": "DNS de subida \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente",
"dns_test_warning_toast": "Proveedor DNS \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente",
"unblock": "Desbloquear",
"block": "Bloquear",
"disallow_this_client": "No permitir a este cliente",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Respuesta TTL bloqueada",
"blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
"form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)",
"upstream_timeout": "Tiempo de espera del proveedor DNS",
"upstream_timeout_desc": "Especifica el número de segundos que se debe esperar para recibir una respuesta del proveedor DNS",
"form_enter_upstream_timeout": "Ingresa la duración de tiempo de espera del proveedor DNS en segundos",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS mediante HTTPS",
"dns_over_tls": "DNS mediante TLS",
@@ -308,7 +311,7 @@
"form_enter_rate_limit": "Ingresa el límite de cantidad",
"rate_limit": "Límite de cantidad",
"edns_enable": "Habilitar subred de cliente EDNS",
"edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del DNS de subida y registra los valores enviados por los clientes en el registro de consultas.",
"edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del proveedor DNS y registra los valores enviados por los clientes en el registro de consultas.",
"edns_use_custom_ip": "Usar IP personalizada para EDNS",
"edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS",
"rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.",
@@ -332,7 +335,7 @@
"theme_auto": "Auto",
"theme_light": "Claro",
"theme_dark": "Oscuro",
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración del DNS</0>.",
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración DNS</0>.",
"tracker_source": "Fuente del rastreador",
"source_label": "Fuente",
"found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.",
@@ -593,12 +596,12 @@
"example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.",
"rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA",
"rewrite_domain_name": "Nombre de dominio: añade un registro CNAME",
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del DNS de subida",
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del DNS de subida",
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del proveedor DNS",
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del proveedor DNS",
"disable_ipv6": "Deshabilitar resolución de direcciones IPv6",
"disable_ipv6_desc": "Descarta todas las consultas de DNS para direcciones IPv6 (tipo AAAA) y elimina las sugerencias de IPv6 de las respuestas HTTPS.",
"fastest_addr": "Dirección IP más rápida",
"fastest_addr_desc": "Consulta todos los servidores DNS y devuelve la dirección IP más rápida de todas las respuestas. Esto ralentiza las consultas DNS ya que AdGuard Home tiene que esperar las respuestas de todos los servidores DNS, pero mejora la conectividad general.",
"fastest_addr_desc": "Espera respuestas de <b>todos</b> los servidores DNS, mide la velocidad de conexión TCP de cada servidor y devuelve la dirección IP del servidor con la velocidad de conexión más rápida.<br/>Este modo puede ralentizar significativamente las consultas DNS, si uno o más proveedores DNS no responden. Asegúrate de que tus proveedores DNS sean estables y de que el tiempo de espera tu proveedor DNS sea bajo.",
"autofix_warning_text": "Si haces clic en \"Corregir\", AdGuard Home configurará tu sistema para utilizar el servidor DNS de AdGuard Home.",
"autofix_warning_list": "Realizará estas tareas: <0>Deshabilitar el sistema DNSStubListener</0> <0>Establecer la dirección del servidor DNS en 127.0.0.1</0> <0>Reemplazar el destino del enlace simbólico de /etc/resolv.conf por /run/systemd/resolve/resolv.conf</0> <0>Detener DNSStubListener (recargar el servicio systemd-resolved)</0>",
"autofix_warning_result": "Como resultado, todas las peticiones DNS de tu sistema serán procesadas por AdGuard Home de manera predeterminada.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Razón: {{reason}}",
"check_service": "Nombre del servicio: {{service}}",
"check_hostname": "Nombre de host o nombre de dominio",
"check_client_id": "Identificador del cliente (ClientID o dirección IP)",
"check_enter_client_id": "Ingresa el identificador del cliente",
"check_dns_record": "Selecciona el tipo de registro DNS",
"service_name": "Nombre del servicio",
"check_not_found": "No se ha encontrado en tus listas de filtros",
"client_confirm_block": "¿Estás seguro de que deseas bloquear al cliente \"{{ip}}\"?",
@@ -649,13 +656,13 @@
"blocklist": "Lista de bloqueo",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamaño de la caché",
"cache_size_desc": "Tamaño de la caché DNS (en bytes). Para deshabilitar el almacenamiento en caché, déjalo vacío.",
"cache_size_desc": "Tamaño de la caché DNS (en bytes). Para desactivar el almacenamiento en caché, configúralo en 0.",
"cache_ttl_min_override": "Anular TTL mínimo",
"cache_ttl_max_override": "Anular TTL máximo",
"enter_cache_size": "Ingresa el tamaño de la caché (bytes)",
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo (en segundos)",
"enter_cache_ttl_max_override": "Ingresa el TTL máximo (en segundos)",
"cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del servidor DNS de subida al almacenar en caché las respuestas DNS.",
"cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del proveedor DNS al almacenar en caché las respuestas DNS.",
"cache_ttl_max_override_desc": "Establece un valor de tiempo de vida (segundos) máximo para las entradas en la caché DNS.",
"ttl_cache_validation": "La anulación TTL mínimo de la caché debe ser menor o igual al máximo",
"cache_optimistic": "Caché optimista",
@@ -741,7 +748,7 @@
"thursday_short": "Jue.",
"friday_short": "Vie.",
"saturday_short": "Sáb.",
"upstream_dns_cache_configuration": "Configuración de la caché DNS upstream",
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché de DNS para la configuración personalizada de este cliente",
"upstream_dns_cache_configuration": "Configuración de la caché del proveedor DNS",
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché del DNS para la configuración personalizada de este cliente",
"dns_cache_size": "Tamaño de la caché DNS, en bytes"
}

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
"parallel_requests": "Rinnakkaiset pyynnöt",
"load_balancing": "Kuormantasaus",
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
"load_balancing_desc": "Lähetä kysely kerrallaan yhdelle ylävirtapalvelimelle. AdGuard Home valitsee painotetun satunnaisalgoritmin avulla palvelimet, joilla on vähiten epäonnistuneita hakuja ja keskimääräisesti lyhin hakuaika.",
"bootstrap_dns": "Bootstrap DNS-palvelimet",
"bootstrap_dns_desc": "Ylävirroiksi määrittämiesi DoH/DoT-resolverien IP-osoitteiden selvitykseen käytettävien DNS-palvelimien IP-osoitteet. Kommentteja ei sallita.",
"fallback_dns_title": "DNS-varapalvelimet",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Käytä AdGuardin lapsilukko-palvelua",
"use_adguard_parental_hint": "AdGuard Home tarkistaa, sisältääkö verkkotunnus aikuisille tarkoitettua sisältöä. Se käyttää samaa tietosuojapainotteista rajapintaa, kuin turvallisen selauksen palvelu.",
"enforce_safe_search": "Käytä turvallista hakua",
"enforce_save_search_hint": "AdGuard Home voi pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex ja Pixabay.",
"no_servers_specified": "Palvelimia ei ole määritetty",
"general_settings": "Yleiset asetukset",
"dns_settings": "DNS-asetukset",
@@ -542,7 +542,7 @@
"stats_params": "Tilastoinnin määritys",
"config_successfully_saved": "Asetukset tallennettiin",
"interval_6_hour": "6 tuntia",
"interval_24_hour": "24 tuntia",
"interval_24_hour": "24 tunnilta",
"interval_days": "{{count}} päivä",
"interval_days_plural": "{{count}} päivää",
"domain": "Verkkotunnus",
@@ -709,9 +709,9 @@
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
"schedule_services": "Keskeytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen keskeytysajoitus tälle päätteelle.",
"schedule_services": "Pysäytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen pysäytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen pysäytysajoitus tälle päätteelle.",
"schedule_desc": "Aseta estettujen palveluiden käyttämättömyysjaksot",
"schedule_invalid_select": "Aloitusaika on oltava ennen lopetusaikaa",
"schedule_select_days": "Valitse päivät",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
"parallel_requests": "Requêtes en parallèle",
"load_balancing": "Équilibrage de charge",
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
"load_balancing_desc": "Une requête par serveur en amont à la fois.<br/>AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
"bootstrap_dns": "Serveurs DNS d'amorçage",
"bootstrap_dns_desc": "Les adresses IP des serveurs DNS utilisées pour résoudre les adresses IP des résolveurs DoH/DoT que vous spécifiez comme en amont. Les commentaires ne sont pas autorisés.",
"fallback_dns_title": "Serveurs DNS de repli",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utiliser le contrôle parental d'AdGuard",
"use_adguard_parental_hint": "AdGuard Home va vérifier s'il y a du contenu pour adultes sur le domaine. Ce sera fait par aide du même API discret que celui utilisé par le service de Sécurité de navigation.",
"enforce_safe_search": "Utiliser la Recherche Sécurisée",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Pas de serveurs spécifiés",
"general_settings": "Paramètres généraux",
"dns_settings": "Paramètres DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Réponse bloquée TTL",
"blocked_response_ttl_desc": "Spécifie pendant combien de secondes les clients doivent mettre en cache une réponse filtrée",
"form_enter_blocked_response_ttl": "Saisir le TTL de la réponse bloquée (secondes)",
"upstream_timeout": "Délai d'attente en amont",
"upstream_timeout_desc": "Spécifie le nombre de secondes à attendre pour une réponse du serveur en amont",
"form_enter_upstream_timeout": "Saisir le délai d'attente du serveur en amont en secondes",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Désactiver la résolution des adresses IPv6",
"disable_ipv6_desc": "Supprimer toutes les requêtes DNS pour les adresses IPv6 (type AAAA) et supprimer les indices IPv6 des réponses HTTPS.",
"fastest_addr": "Adresse IP la plus rapide",
"fastest_addr_desc": "Rechercher tous les serveurs DNS et renvoyer ladresse IP la plus rapide parmi toutes les réponses. Cela ralentit les requêtes DNS car AdGuard Home doit attendre les réponses de tous les serveurs DNS, mais la connectivité globale s'améliore.",
"fastest_addr_desc": "Attente les réponses de <b>tous</b> les serveurs DNS, mesure de la vitesse de connexion TCP pour chaque serveur et renvoi de l'adresse IP du serveur avec la vitesse de connexion la plus rapide.<br/>Ce mode peut considérablement ralentir les requêtes DNS, si un ou plusieurs serveurs en amont ne répondent pas. Assurez-vous que vos serveurs en amont sont stables et que votre délai dépassé en amont est faible.",
"autofix_warning_text": "Si vous cliquez sur « Réparer », AdGuard Home configurera votre système pour utiliser le serveur DNS AdGuard Home.",
"autofix_warning_list": "Ceci effectuera les tâches suivantes : <0>Désactiver le système DNSStubListener</0> <0>Définir ladresse du serveur DNS à 127.0.0.1 </0> <0>Remplacer la cible du lien symbolique de /etc/resolv.conf par /run/systemd/resolve/resolv.conf</0> <0>Arrêter DNSStubListener (recharger le service résolu par systemd)</0>",
"autofix_warning_result": "Par conséquent, toutes les demandes DNS de votre système seront traitées par AdGuardHome par défaut.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME : {{cname}}",
"check_reason": "Raison : {{reason}}",
"check_service": "Nom du service : {{service}}",
"check_hostname": "Nom d'hôte ou nom de domaine",
"check_client_id": "Identifiant du client (ClientID ou adresse IP)",
"check_enter_client_id": "Saisissez l'identifiant du client",
"check_dns_record": "Sélectionnez le type d'enregistrement DNS",
"service_name": "Nom du service",
"check_not_found": "Introuvable dans vos listes de filtres",
"client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?",
@@ -649,7 +656,7 @@
"blocklist": "Liste de blocage",
"milliseconds_abbreviation": "ms",
"cache_size": "Taille du cache",
"cache_size_desc": "Taille du cache DNS (en octets). Pour désactiver la mise en cache, laissez vide.",
"cache_size_desc": "Taille du cache DNS (en octets). Pour désactiver la mise en cache, mettez la valeur sur 0.",
"cache_ttl_min_override": "Remplacer le TTL minimum",
"cache_ttl_max_override": "Remplacer le TTL maximum",
"enter_cache_size": "Entrer la taille du cache (octets)",
@@ -676,7 +683,7 @@
"filter_allowlist": "ATTENTION : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
"last_rule_in_allowlist": "Impossible dinterdire ce client, car lexclusion de la règle « {{disallowed_rule}} » DÉSACTIVERA la liste des « clients autorisés ».",
"use_saved_key": "Utiliser la clef précédemment enregistrée",
"parental_control": "Contrôle parental",
"parental_control": "Contrôle Parental",
"safe_browsing": "Navigation sécurisée",
"served_from_cache_label": "Servi depuis le cache",
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",

View File

@@ -1,26 +1,26 @@
{
"client_settings": "Pengaturan klien",
"example_upstream_reserved": "upstream <0>untuk domain spesifik</0>;",
"example_multiple_upstreams_reserved": "beberapa server upstream <0>untuk domain spesifik</0>;",
"example_upstream_reserved": "hulu <0>untuk domain tertentu</0>;",
"example_multiple_upstreams_reserved": "beberapa hulu <0>untuk domain tertentu</0>;",
"example_upstream_comment": "komentar.",
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream secara bersamaan",
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat penyelesaian dengan mengkueri seluruh server hulu secara bersamaan.",
"parallel_requests": "Permintaan paralel",
"load_balancing": "Penyeimbang beban",
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.",
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP penyelesai DoH/DoT yang Anda tentukan sebagai hulu. Tidak diizinkan untuk berkomentar.",
"fallback_dns_title": "Server DNS cadangan",
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server hulu DNS tidak merespons. Sintaksnya sama dengan kolom hulu utama di atas.",
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
"local_ptr_title": "Server pembalik DNS pribadi",
"local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.",
"local_ptr_desc": "Server DNS yang digunakan oleh AdGuard Home untuk permintaan PTR, SOA, dan NS pribadi. Permintaan dianggap pribadi jika meminta domain ARPA yang berisi subnet dalam rentang IP pribadi (seperti \"192.168.12.34\") dan berasal dari klien dengan alamat IP pribadi. Jika tidak ditetapkan, standar pemecah DNS milik OS Anda akan digunakan, kecuali untuk alamat IP AdGuard Home.",
"local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.",
"local_ptr_placeholder": "Masukkan satu alamat IP per baris",
"resolve_clients_title": "Aktifkan resolusi hostname klien",
"resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).",
"resolve_clients_desc": "Selesaikan alamat IP klien secara terbalik ke dalam nama host mereka dengan mengirimkan kueri PTR ke penyelesai yang sesuai (server DNS pribadi untuk klien lokal, server hulu untuk klien dengan alamat IP publik).",
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
"use_private_ptr_resolvers_desc": "Lakukan pencarian DNS terbalik untuk alamat yang disajikan secara lokal menggunakan server hulu ini. Jika dinonaktifkan, Adguard Home merespons dengan NXDOMAIN untuk semua permintaan PTR tersebut kecuali untuk klien yang diketahui dari DHCP, /etc/hosts, dan seterusnya.",
"use_private_ptr_resolvers_desc": "Menyelesaikan permintaan PTR, SOA, dan NS untuk domain ARPA yang berisi alamat IP pribadi melalui server hulu pribadi, DHCP, /etc/hosts, dll. Jika dinonaktifkan, AdGuard Home akan merespons semua permintaan tersebut dengan NXDOMAIN.",
"check_dhcp_servers": "Cek untuk server DHCP",
"save_config": "Simpan pengaturan",
"enabled_dhcp": "Server DHCP diaktifkan",
@@ -49,12 +49,12 @@
"form_error_server_name": "Nama server tidak valid",
"form_error_subnet": "Subnet \"{{cidr}}\" tidak berisi alamat IP \"{{ip}}\"",
"form_error_positive": "Harus lebih dari 0",
"form_error_gateway_ip": "Sewa tidak dapat memiliki alamat IP gateway",
"form_error_gateway_ip": "Lease tidak dapat memiliki gerbang alamat IP",
"out_of_range_error": "Harus di luar rentang \"{{start}}\"-\"{{end}}\"",
"lower_range_start_error": "Harus lebih rendah dari rentang awal",
"greater_range_start_error": "Harus lebih besar dari rentang awal",
"subnet_error": "Alamat harus dalam satu subnet",
"gateway_or_subnet_invalid": "Subnet mask tidak valid",
"gateway_or_subnet_invalid": "Subnet samaran tidak valid",
"dhcp_form_gateway_input": "IP gateway",
"dhcp_form_subnet_input": "Subnet mask",
"dhcp_form_range_title": "Rentang alamat IP",
@@ -132,8 +132,8 @@
"top_clients": "Klien teratas",
"no_clients_found": "Tidak ditemukan klien",
"general_statistics": "Statistik umum",
"top_upstreams": "Top servers upstream",
"no_upstreams_data_found": "Tidak ada data server upstream yang ditemukan",
"top_upstreams": "Hulu teratas",
"no_upstreams_data_found": "Tidak ada data hulu yang ditemukan",
"number_of_dns_query_days": "Jumlah kueri DNS diproses selama {{value}} hari terakhir",
"number_of_dns_query_days_plural": "Jumlah kueri DNS yang diproses selama {{count}} hari terakhir",
"number_of_dns_query_hours": "Jumlah kueri DNS diproses selama {{{count}} jam terakhir",
@@ -147,14 +147,14 @@
"average_upstream_response_time": "Rata-rata waktu respons hulu",
"response_time": "Waktu respons",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan berkas host",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran dalam pengaturan <a>Filter</a>.",
"use_adguard_browsing_sec": "Gunakan layanan web Keamanan Penjelajahan AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
"use_adguard_parental_hint": "AdGuard Home akan mengecek jika domain mengandung materi dewasa. Akan menggunakan API yang ramah privasi yang sama sebagai layanan web keamanan penjelajahan.",
"enforce_safe_search": "Pakai pencarian aman",
"enforce_save_search_hint": "AdGuard Home dapat memaksa penelusuran aman pada mesin pencari berikut: Google, Youtube, Bing, DuckDuckGo, Yandex, dan Pixabay.",
"enforce_save_search_hint": "AdGuard Home akan memberlakukan pencarian yang aman di mesin pencari berikut ini: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Sever tidak disebutkan",
"general_settings": "Pengaturan umum",
"dns_settings": "Pengaturan DNS",
@@ -169,8 +169,8 @@
"upstream_dns_help": "Masukkan satu alamat server per baris. <a>Pelajari lebih lanjut</a> mengenai cara mengonfigurasi server DNS hulu.",
"upstream_dns_configured_in_file": "Diatur dalam {{path}}",
"test_upstream_btn": "Uji hulu",
"upstreams": "Upstream",
"upstream": "Server upstream",
"upstreams": "Hulu",
"upstream": "Hulu",
"apply_btn": "Terapkan",
"disabled_filtering_toast": "Penyaringan nonaktif",
"enabled_filtering_toast": "Penyaringan aktif",
@@ -191,7 +191,7 @@
"edit_table_action": "Ubah",
"delete_table_action": "Hapus",
"elapsed": "Berlalu",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak berkas host.",
"no_blocklist_added": "Tidak ada daftar hitam yang ditambahkan",
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
"add_blocklist": "Tambahkan daftar hitam",
@@ -211,33 +211,33 @@
"form_error_url_format": "Format URL tidak valid",
"form_error_url_or_path_format": "URL atau jalur absolut dari daftar tidak valid",
"custom_filter_rules": "Aturan penyaringan khusus",
"custom_filter_rules_hint": "Masukkan satu aturan dalam sebuah baris. Anda dapat menggunakan baik aturan adblock maupun sintaks file hosts.",
"system_host_files": "File host sistem",
"custom_filter_rules_hint": "Masukkan satu aturan pada satu baris. Anda dapat menggunakan aturan adblock atau sintaks berkas host.",
"system_host_files": "Berkas host sistem",
"examples_title": "Contoh",
"example_meaning_filter_block": "blokir akses ke example.org dan seluruh subdomainnya;",
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.orf dan seluruh subdomainnya;",
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.org dan seluruh subdomainnya;",
"example_meaning_host_block": "merespons dengan 127.0.0.1 untuk example.org (tetapi tidak untuk subdomainnya);",
"example_comment": "! Komentar di sini.",
"example_comment_meaning": "hanya sebuah komentar;",
"example_comment_hash": "# Juga sebuah komentar.",
"example_regex_meaning": "blokir akses ke domain yang cocok dengan ekspresi reguler yang ditentukan.",
"example_upstream_regular": "DNS reguler (melalui UDP);",
"example_upstream_regular_port": "DNS biasa (lebih dari UDP, dengan port);",
"example_upstream_udp": "DNS biasa (lebih dari UDP, nama host);",
"example_upstream_regular": "DNS biasa (melalui UDP);",
"example_upstream_regular_port": "DNS biasa (melalui UDP, dengan port);",
"example_upstream_udp": "DNS biasa (melalui UDP, nama host);",
"example_upstream_dot": "<0>DNS melalui TLS</0> terenkripsi;",
"example_upstream_doh": "<0>DNS melalui HTTPS</0> terenkripsi;",
"example_upstream_doh3": "DNS melalui HTTPS terenkripsi dengan <0>HTTP/3</0> secara paksa dan tidak ada cadangan ke HTTP/2 atau lebih rendah;",
"example_upstream_doq": "<0>DNS melalui QUIC</0> terenkripsi;",
"example_upstream_sdns": "<0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS-over-HTTPS</2>;",
"example_upstream_tcp": "DNS reguler (melalui TCP);",
"example_upstream_sdns": "<0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS melalui HTTPS</2>;",
"example_upstream_tcp": "DNS biasa (melalui TCP);",
"example_upstream_tcp_port": "DNS biasa (melalui TCP, dengan port);",
"example_upstream_tcp_hostname": "DNS biasa (lebih dari TCP, nama host);",
"example_upstream_tcp_hostname": "DNS biasa (melalui TCP, nama host);",
"all_lists_up_to_date_toast": "Semua daftar sudah diperbarui",
"updated_upstream_dns_toast": "Server upstream berhasil disimpan",
"updated_upstream_dns_toast": "Server hulu berhasil disimpan",
"dns_test_ok_toast": "Server DNS yang ditentukan bekerja dengan benar",
"dns_test_not_ok_toast": "Server \"{{key}}\": tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
"dns_test_parsing_error_toast": "Bagian {{section}}: baris {{line}}: tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
"dns_test_warning_toast": "Upstream \"{{key}}\" tidak menanggapi permintaan pengujian dan mungkin tidak berfungsi dengan baik",
"dns_test_warning_toast": "Hulu \"{{key}}\" tidak menanggapi permintaan pengujian dan mungkin tidak berfungsi dengan benar",
"unblock": "Buka Blokir",
"block": "Blok",
"disallow_this_client": "Cabut ijin untuk klien ini",
@@ -268,18 +268,18 @@
"rule_added_to_custom_filtering_toast": "Aturan ditambah ke aturan penyaringan khusus: {{rule}}",
"query_log_response_status": "Status: {{value}}",
"query_log_filtered": "Difilter oleh {{filter}}",
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh kueri log?",
"query_log_cleared": "Kueri log telah berhasil dihapus",
"query_log_updated": "Log permintaan telah berhasil diperbarui",
"query_log_clear": "Hapus kueri log",
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh catatan kueri?",
"query_log_cleared": "Catatan kueri berhasil dihapus",
"query_log_updated": "Catatan kueri berhasil diperbarui",
"query_log_clear": "Hapus catatan kueri",
"query_log_retention": "Rotasi kueri log",
"query_log_enable": "Aktifkan log",
"query_log_configuration": "Konfigurasi log",
"query_log_disabled": "Kueri log dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
"query_log_enable": "Aktifkan catatan",
"query_log_configuration": "Konfigurasi catatan",
"query_log_disabled": "Catatan kueri dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
"query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat",
"query_log_retention_confirm": "Apakah Anda yakin ingin mengubah rotasi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
"anonymize_client_ip": "Anonim IP klien",
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam log dan statistik",
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam catatan atau statistik",
"dns_config": "Konfigurasi server DNS",
"dns_cache_config": "Konfigurasi cache DNS",
"dns_cache_config_desc": "Disini Anda bisa mengonfigurasi cache DNS",
@@ -308,8 +308,8 @@
"form_enter_rate_limit": "Masukkan batas nilai",
"rate_limit": "Batas nilai",
"edns_enable": "Aktifkan EDNS Klien Subnet",
"edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan upstream dan catat nilai yang dikirim oleh klien di log kueri.",
"edns_use_custom_ip": "Gunakan IP khusus untuk EDNS",
"edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan hulu dan catat nilai yang dikirim oleh klien dalam catatan kueri.",
"edns_use_custom_ip": "Gunakan IP kustom untuk EDNS",
"edns_use_custom_ip_desc": "Izinkan untuk menggunakan IP kustom untuk EDNS",
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien. Atur ke 0 untuk tidak terbatas.",
"rate_limit_subnet_len_ipv4": "Panjang awalan subnet untuk alamat IPv4",
@@ -329,13 +329,13 @@
"blocking_mode_nxdomain": "NXDOMAIN: Respon pakai kode NXDOMAIN",
"blocking_mode_null_ip": "Null IP: Respon pakai alamat IP kosong (0.0.0.0 untuk A; :: untuk AAAA)",
"blocking_mode_custom_ip": "IP kustom: respon dengan alamat IP yang diset secara manual",
"theme_auto": "Auto",
"theme_auto": "Otomatis",
"theme_light": "Terang",
"theme_dark": "Gelap",
"upstream_dns_client_desc": "Jika Anda biarkan kolom ini kosong, AdGuard Home akan menggunakan server yang dikonfigurasi di <0>pengaturan DNS</0>.",
"tracker_source": "Sumber pelacak",
"source_label": "Sumber",
"found_in_known_domain_db": "Ditemukan di database domain dikenal",
"found_in_known_domain_db": "Ditemukan di basis data domain yang dikenal.",
"category_label": "Kategori",
"rule_label": "Atura(n)",
"list_label": "Daftar",
@@ -366,18 +366,18 @@
"install_devices_router": "Router",
"install_devices_router_desc": "Penyiapan ini secara otomatis mencakup semua perangkat yang terhubung ke router rumah Anda, tidak perlu mengkonfigurasi masing-masing perangkat secara manual.",
"install_devices_address": "Server DNS AdGuard Home akan menggunakan alamat berikut",
"install_devices_router_list_1": "Buka preferensi untuk router Anda. Biasanya, Anda dapat mengaksesnya dari browser Anda melalui URL, seperti http://192.168.0.1/ atau http://192.168.1.1/. Anda mungkin diminta untuk memasukkan kata sandi. Jika Anda tidak mengingatnya, Anda sering kali dapat mengatur ulang kata sandi dengan menekan tombol pada perute itu sendiri, tetapi perlu diketahui bahwa jika prosedur ini dipilih, Anda mungkin akan kehilangan seluruh konfigurasi perute. Jika router Anda memerlukan aplikasi untuk menyiapkannya, instal aplikasi tersebut di ponsel atau PC Anda dan gunakan untuk mengakses pengaturan router.",
"install_devices_router_list_1": "Buka preferensi untuk router Anda. Biasanya, Anda dapat mengaksesnya dari peramban Anda melalui URL, seperti http://192.168.0.1/ atau http://192.168.1.1/. Anda mungkin diminta untuk memasukkan kata sandi. Jika Anda tidak mengingatnya, Anda sering kali dapat mengatur ulang kata sandi dengan menekan tombol pada router itu sendiri, tetapi perlu diketahui bahwa jika prosedur ini dipilih, Anda mungkin akan kehilangan seluruh konfigurasi router. Jika router Anda memerlukan aplikasi untuk menyiapkannya, pasang aplikasi tersebut di ponsel atau PC Anda dan gunakan untuk mengakses pengaturan router.",
"install_devices_router_list_2": "Temukan pengaturan DHCP / DNS. Cari huruf DNS di sebelah kolom yang memungkinkan dua atau tiga set angka, masing-masing dipecah menjadi empat kelompok dengan satu hingga tiga digit.",
"install_devices_router_list_3": "Masukkan alamat server AdGuard Home disana",
"install_devices_router_list_4": "Anda tidak dapat menyetel server DNS kustom pada beberapa tipe router. Dalam hal ini mungkin membantu jika Anda mengatur AdGuard Home sebagai <0>server DHCP</0>. Jika tidak, Anda harus mencari petunjuk tentang cara mengkustomisasi server DNS untuk model router khusus Anda.",
"install_devices_windows_list_1": "Buka Panel Kontrol melalui menu Start atau pencarian Windows.",
"install_devices_windows_list_2": "Masuk ke kategori Jaringan dan Internet (Network and Internet) dan kemudian ke Pusat Jaringan dan Berbagi (Network and Sharing Center).",
"install_devices_windows_list_3": "Di panel kiri, klik \"Ubah pengaturan adaptor\".",
"install_devices_windows_list_4": "Klik kanan koneksi aktif Anda dan pilih Properties.",
"install_devices_windows_list_5": "Temukan \"Internet Protocol Version 4 (TCP/IPv4)\" (atau, untuk IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\") dalam daftar, pilih dan kemudian klik Properties lagi.",
"install_devices_windows_list_4": "Klik kanan koneksi aktif Anda dan pilih Properti.",
"install_devices_windows_list_5": "Temukan \"Protokol Internet Versi 4 (TCP/IPv4)\" (atau, untuk IPv6, \"Protokol Internet Versi 6 (TCP/IPv6)\") dalam daftar, pilih dan kemudian klik Properti lagi.",
"install_devices_windows_list_6": "Pilih \"Gunakan alamat server DNS berikut\" dan masukkan alamat server Beranda AdGuard Anda.",
"install_devices_macos_list_1": "Klik ikon Apple dan pergi ke System Preferences.",
"install_devices_macos_list_2": "Klik Network.",
"install_devices_macos_list_1": "Klik ikon Apple dan buka Preferensi Sistem.",
"install_devices_macos_list_2": "Klik Jaringan.",
"install_devices_macos_list_3": "Pilih koneksi pertama dalam daftar dan klik Advanced.",
"install_devices_macos_list_4": "Pilih tab DNS dan masukkan alamat server AdGuard Anda.",
"install_devices_android_list_1": "Dari layar beranda Menu Android, ketuk Pengaturan.",
@@ -394,7 +394,7 @@
"open_dashboard": "Buka Beranda",
"install_saved": "Berhasil disimpan",
"encryption_title": "Enkripsi",
"encryption_desc": "Enkripsi (HTTPS/QUIC/TLS) untuk DNS dan antarmuka admin",
"encryption_desc": "Dukungan enkripsi (HTTPS/QUIC/TLS) untuk DNS dan antarmuka web admin",
"encryption_config_saved": "Pengaturan enkripsi telah tersimpan",
"encryption_server": "Nama server",
"encryption_server_enter": "Masukkan nama domain anda",
@@ -406,7 +406,7 @@
"encryption_dot": "Port DNS-over-TLS",
"encryption_dot_desc": "Jika port ini terkonfigurasi, AdGuard Home akan menjalankan server DNS-over-TLS dalam port ini",
"encryption_doq": "Port DNS-over-QUIC ",
"encryption_doq_desc": "Jika port ini diatur secara sepesifik, AdGuard Home akan menjalankan server DNS-lewat-QUIC pada port ini.",
"encryption_doq_desc": "Jika port ini dikonfigurasi, AdGuard Home akan menjalankan server DNS melalui QUIC pada port ini.",
"encryption_certificates": "Sertifikat",
"encryption_certificates_desc": "Untuk menggunakan enkripsi, Anda perlu memberikan rantai sertifikat SSL yang valid untuk domain Anda. Anda bisa mendapatkan sertifikat gratis di <0>{{link}}</0> atau Anda dapat membelinya dari salah satu Otoritas Sertifikat tepercaya.",
"encryption_certificates_input": "Salin / rekatkan sertifikat PEM yang disandikan di sini.",
@@ -431,8 +431,8 @@
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
"form_error_port_range": "Masukkan nomor port di kisaran 80-65535",
"form_error_port_unsafe": "Ini adalah port yang tidak aman",
"form_error_equal": "Seharusnya tidak sama",
"form_error_port_unsafe": "Port tidak aman",
"form_error_equal": "Tidak boleh sama",
"form_error_password": "Kata sandi tidak cocok",
"reset_settings": "Setel ulang pengaturan",
"update_announcement": "AdGuard Home {{version}} sekarang tersedia! <0>Klik di sini</0> untuk info lebih lanjut.",
@@ -447,7 +447,7 @@
"update_failed": "Pembaruan otomatis gagal. Silakan <a>ikuti langkah-langkah berikut</a> untuk memperbarui secara manual.",
"manual_update": "Silakan <a>mengikuti langkah berikut</a> untuk memperbarui secara manual.",
"processing_update": "Silahkan tunggu, AdGuard Home sedang diperbarui",
"clients_title": "Klien yang gigih",
"clients_title": "Klien persisten",
"clients_desc": "Konfigurasikan catatan klien persisten untuk perangkat yang terhubung ke AdGuard Home",
"settings_global": "Global",
"settings_custom": "Kustom",
@@ -459,7 +459,7 @@
"client_edit": "Ubah Klien",
"client_identifier": "Identifikasi",
"ip_address": "Alamat IP",
"client_identifier_desc": "Klien dapat diidentifikasi oleh alamat IP, CIDR, alamat MAC atau ClientID (dapat digunakan untuk DoT/DoH/DoQ). <0>Di sini</0> Anda dapat mempelajari lebih lanjut tentang cara mengidentifikasi klien.",
"client_identifier_desc": "Klien dapat diidentifikasi berdasarkan alamat IP, CIDR, alamat MAC, atau ClientID (dapat digunakan untuk DoT/DoH/DoQ). Pelajari lebih lanjut tentang cara mengidentifikasi klien <0>di sini</0>.",
"form_enter_ip": "Masukkan IP",
"form_enter_subnet_ip": "Masukkan alamat IP di subnet \"{{cidr}}\"",
"form_enter_mac": "Masukkan MAC",
@@ -475,8 +475,8 @@
"clients_not_found": "Tidak ada klien ditemukan",
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
"auto_clients_title": "Klien (waktu berjalan)",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk file host, reverse DNS, dll.",
"auto_clients_title": "Klien runtime",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk berkas host, DNS terbalik, dll.",
"access_title": "Pengaturan akses",
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
"access_allowed_title": "Klien yang diizinkan",
@@ -484,9 +484,9 @@
"access_disallowed_title": "Klien yang tidak diizinkan",
"access_disallowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home akan membatalkan permintaan dari klien ini. Kolom ini diabaikan jika ada entri di daftar putih klien.",
"access_blocked_title": "Domain yang diblokir",
"access_blocked_desc": "Jangan bingung dengan filter. AdGuard Home menghapus kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di log kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, mis. \"example.org\", \"*.example.org\", atau \"||example.org^\" yang sesuai.",
"access_blocked_desc": "Jangan dikelirukan dengan filter. AdGuard Home membuang kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di catatan kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, misalnya \"example.org\", \"*.example.org\", atau \"||example.org^\" secara bersamaan.",
"access_settings_saved": "Pengaturan akses berhasil disimpan",
"updates_checked": "Versi baru AdGuard Home tersedia\n",
"updates_checked": "Versi baru AdGuard Home tersedia",
"updates_version_equal": "AdGuard Home sudah tebaru",
"check_updates_now": "Periksa pembaruan sekarang",
"version_request_error": "Pemeriksaan pembaruan gagal. Harap periksa koneksi internet anda.",
@@ -494,7 +494,7 @@
"setup_dns_privacy_1": "<0>DNS melalui TLS:</0> Gunakan <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Berikut daftar perangkat lunak yang dapat Anda gunakan.</0>",
"setup_dns_privacy_4": "Di perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh file '.mobileconfig' khusus yang menambahkan server <highlight>DNS-over-HTTPS</highlight> atau <highlight>DNS-over-TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_4": "Pada perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh berkas khusus '.mobileconfig' yang menambahkan server <highlight>DNS melalui HTTPS</highlight> atau <highlight>DNS melalui TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_android_1": "Android 9 mendukung DNS-over-TLS secara asli. Untuk mengkonfigurasinya, buka Pengaturan → Jaringan & internet → Tingkat Lanjut → DNS Pribadi dan masukkan nama domain Anda di sana.",
"setup_dns_privacy_android_2": "<0>AdGuard untuk Android</0> mendukung <1>DNS-over-HTTPS</1> dan <1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> menambahkan dukungan <1>DNS-over-HTTPS</1> untuk Android.",
@@ -517,7 +517,7 @@
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
"rewrite_applied": "Aturan Rewrite yang diterapkan",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan file hosts",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan berkas host",
"dns_rewrites": "DNS rewrite",
"form_domain": "Masukkan nama domain",
"form_answer": "Masaukan alamat IP atau nama domain",
@@ -563,7 +563,7 @@
"ignore_domains": "Domain yang diabaikan (dipisahkan oleh baris baru)",
"ignore_domains_title": "Domain yang diabaikan",
"ignore_domains_desc_stats": "Kueri yang cocok dengan aturan ini tidak ditulis ke statistik",
"ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke log kueri",
"ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke catatan kueri",
"interval_hours": "{{count}} jam",
"interval_hours_plural": "{{count}} jam",
"filters_configuration": "Konfigurasi filter",
@@ -593,8 +593,8 @@
"example_rewrite_wildcard": "tulis ulang respon untuk semua subdomain <0>contoh.org</0>.",
"rewrite_ip_address": "Alamat IP: pakai IP ini dalam respons A atau AAAA",
"rewrite_domain_name": "Nama domain: tambah ke rekaman CNAME",
"rewrite_A": "<0>A</0>: nilai khusus, biarkan <0>A</0> merekam dari upstream",
"rewrite_AAAA": "<0>AAAA</0>: nilai khusus, biarkan <0>AAAA</0> merekam dari upstream",
"rewrite_A": "<0>A</0>: nilai khusus, biarkan <0>A</0> merekam dari hulu",
"rewrite_AAAA": "<0>AAAA</0>: nilai khusus, biarkan <0>AAAA</0> merekam dari hulu",
"disable_ipv6": "Nonaktifkan penyelesaian alamat IPv6",
"disable_ipv6_desc": "Hapus semua kueri DNS untuk alamat IPv6 (ketik AAAA) dan hapus petunjuk IPv6 dari respons HTTPS.",
"fastest_addr": "Alamat IP tercepat",
@@ -655,8 +655,8 @@
"enter_cache_size": "Masukkan ukuran cache (bytes)",
"enter_cache_ttl_min_override": "Masukkan TTL minimum (detik)",
"enter_cache_ttl_max_override": "Masukkan TTL maksimum (detik)",
"cache_ttl_min_override_desc": "Perpanjang nilai waktu untuk hidup (detik) yang diterima dari server hulu saat menyimpan respons DNS.",
"cache_ttl_max_override_desc": "Tetapkan nilai waktu-online maksimum (detik) untuk entri di cache DNS.",
"cache_ttl_min_override_desc": "Perpanjang nilai time-to-live (detik) yang diterima dari server hulu saat menyimpan respons DNS.",
"cache_ttl_max_override_desc": "Tetapkan nilai maksimum time-to-live (detik) untuk entri dalam cache DNS.",
"ttl_cache_validation": "Nilai TTL cache minimum harus kurang dari atau sama dengan nilai maksimum",
"cache_optimistic": "Caching yang optimis",
"cache_optimistic_desc": "Buat AdGuard Home merespons dari cache bahkan ketika entri telah kedaluwarsa dan juga mencoba untuk menyegarkannya.",
@@ -676,9 +676,9 @@
"filter_allowlist": "PERINGATAN: Tindakan ini juga akan mengecualikan aturan \"{{disallowed_rule}}\" dari daftar klien yang diizinkan.",
"last_rule_in_allowlist": "Tidak dapat melarang klien ini karena mengecualikan aturan \"{{disallowed_rule}}\" akan MENONAKTIFKAN daftar \"Klien yang diizinkan\".",
"use_saved_key": "Gunakan kunci yang disimpan sebelumnya",
"parental_control": "Kontrol Orang Tua",
"parental_control": "Pengawasan Orang Tua",
"safe_browsing": "Penjelajahan Aman",
"served_from_cache": "{{value}} <i>(disajikan dari cache)</i>",
"served_from_cache_label": "Disajikan dari cache",
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",
@@ -688,11 +688,11 @@
"theme_auto_desc": "Otomatis (berdasarkan skema warna perangkat anda)",
"theme_dark_desc": "Tema gelap",
"theme_light_desc": "Tema terang",
"disable_for_seconds": "Untuk {{count}} detik",
"disable_for_seconds_plural": "Untuk {{count}} detik",
"disable_for_minutes": "Untuk {{count}} menit",
"disable_for_minutes_plural": "Untuk {{count}} menit",
"disable_for_hours": "Untuk {{count}} jam",
"disable_for_seconds": "Selama {{count}} detik",
"disable_for_seconds_plural": "Selama {{count}} detik",
"disable_for_minutes": "Selama {{count}} menit",
"disable_for_minutes_plural": "Selama {{count}} menit",
"disable_for_hours": "Selama {{count}} jam",
"disable_for_hours_plural": "Untuk {{count}} jam",
"disable_until_tomorrow": "Sampai besok",
"disable_notify_for_seconds": "Hentikan perlindungan selama {{count}} detik",
@@ -706,10 +706,10 @@
"custom_retention_input": "Masukkan retensi dalam hitungan jam",
"custom_rotation_input": "Masukkan rotasi dalam hitungan jam",
"protection_section_label": "Perlindungan",
"log_and_stats_section_label": "Log kueri dan statistik",
"ignore_query_log": "Abaikan klien ini di log kueri",
"log_and_stats_section_label": "Catatan kueri dan statistik",
"ignore_query_log": "Abaikan klien ini di catatan kueri",
"ignore_statistics": "Abaikan klien ini di statistik",
"schedule_services": "Menjeda pemblokiran layanan",
"schedule_services": "Jeda pemblokiran layanan",
"schedule_services_desc": "Mengonfigurasi jadwal jeda filter pemblokiran layanan",
"schedule_services_desc_client": "Mengonfigurasi jadwal jeda filter pemblokiran layanan untuk klien ini",
"schedule_desc": "Tetapkan periode tidak aktif untuk layanan yang diblokir",
@@ -741,7 +741,7 @@
"thursday_short": "Kam",
"friday_short": "Jum",
"saturday_short": "Sab",
"upstream_dns_cache_configuration": "Konfigurasi cache DNS upstream",
"upstream_dns_cache_configuration": "Konfigurasi cache DNS hulu",
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi hulu kustom pada klien ini",
"dns_cache_size": "Ukuran cache DNS, dalam byte"
}

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
"parallel_requests": "Richieste parallele",
"load_balancing": "Bilanciamento del carico",
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
"load_balancing_desc": "Esegui una query su un server upstream alla volta.<br/>AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Indirizzi IP dei server DNS utilizzati per risolvere gli indirizzi IP dei resolver DoH/DoT specificati come upstream. I commenti non sono ammessi.",
"fallback_dns_title": "Server DNS di fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utilizza il Controllo Parentale di AdGuard",
"use_adguard_parental_hint": "AdGuard Home verificherà se il dominio contiene materiale per adulti. Utilizza le stesse API privacy-friendly del servizio web 'sicurezza di navigazione'.",
"enforce_safe_search": "Utilizza Ricerca Sicura",
"enforce_save_search_hint": "AdGuard Home forzerà la ricerca sicura sui seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home applicherà la ricerca sicura nei seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nessun server specificato",
"general_settings": "Impostazioni generali",
"dns_settings": "Impostazioni DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Risposta TTL bloccata",
"blocked_response_ttl_desc": "Specifica per quanti secondi i client devono tenere nella cache una risposta filtrata",
"form_enter_blocked_response_ttl": "Inserisci tempo di vita (TTL) della risposta bloccata (secondi)",
"upstream_timeout": "Timeout upstream",
"upstream_timeout_desc": "Specifica il numero di secondi da attendere per una risposta dal server upstream",
"form_enter_upstream_timeout": "Inserisci la durata del timeout del server upstream in secondi",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS su HTTPS",
"dns_over_tls": "DNS su TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Disattiva risoluzione indirizzi IPv6",
"disable_ipv6_desc": "Eliminare tutte le query DNS per gli indirizzi IPv6 (tipo AAAA) e rimuovere i suggerimenti IPv6 dalle risposte HTTPS.",
"fastest_addr": "Indirizzo IP più veloce",
"fastest_addr_desc": "Interroga tutti i server DNS e restituisci l'indirizzo IP più veloce tra tutte le risposte. Ciò rallenterà le richieste DNS poiché AdGuard Home dovrà attendere le risposte da tutti i server DNS, ma ciò migliorerà complessivamente la connettività.",
"fastest_addr_desc": "Attendi le risposte da <b>tutti i</b> server DNS, misura la velocità di connessione TCP per ogni server e restituisci l'indirizzo IP del server con la velocità di connessione più elevata.<br/>Questa modalità può rallentare notevolmente le query DNS, se uno o più server upstream non rispondono. Assicurati che i tuoi server upstream siano stabili e che il timeout upstream sia basso.",
"autofix_warning_text": "Se fai clic su \"Correggi\", AdGuardHome configurerà il tuo sistema per utilizzare il server DNS AdGuardHome.",
"autofix_warning_list": "Eseguirà queste attività: <0> Disattiva DNSStubListener di sistema </0> <0> Imposta l'indirizzo del server DNS su 127.0.0.1 </0> <0> Sostituisci la destinazione del collegamento simbolico di /etc/resolv.conf su / run / systemd /resolve/resolv.conf </0> <0> Arresta DNSStubListener (ricarica il servizio systemd-resolved) </0>",
"autofix_warning_result": "Di conseguenza, tutte le richieste DNS dal sistema verranno elaborate da AdGuardHome per impostazione predefinita.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}",
"check_service": "Nome servizio: {{service}}",
"check_hostname": "Nome host o nome di dominio",
"check_client_id": "Identificatore client (ClientID o indirizzo IP)",
"check_enter_client_id": "Inserisci identificatore client",
"check_dns_record": "Seleziona il tipo di registrazione DNS",
"service_name": "Nome servizio",
"check_not_found": "Non trovato negli elenchi dei filtri",
"client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Lista nera",
"milliseconds_abbreviation": "ms",
"cache_size": "Dimensioni cache",
"cache_size_desc": "Dimensione della cache DNS (in byte). Per disabilitare la memorizzazione nella cache, lascia vuoto.",
"cache_size_desc": "Dimensione della cache DNS (in byte). Per disabilitare la cache, impostare su 0.",
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
"enter_cache_size": "Immetti dimensioni cache (in byte)",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
"parallel_requests": "並列リクエスト",
"load_balancing": "ロードバランシング",
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。<br/>AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
"bootstrap_dns": "ブートストラップDNSサーバ",
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。コメントは許可されていません",
"fallback_dns_title": "フォールバックDNSサーバー",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
"enforce_safe_search": "セーフサーチを使用する",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay",
"no_servers_specified": "サーバが指定されていません",
"general_settings": "一般設定",
"dns_settings": "DNS設定",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Blocked Response TTLブロック済み応答のTTL",
"blocked_response_ttl_desc": "フィルタリングされた応答をクライアントがキャッシュしておく時間(秒)を指定します。",
"form_enter_blocked_response_ttl": "ブロック済み応答のTTL秒単位を入力してください",
"upstream_timeout": "Upstream timeoutアップストリームタイムアウト",
"upstream_timeout_desc": "アップストリームサーバーからの応答を待つ秒数を指定します。",
"form_enter_upstream_timeout": "アップストリームサーバーのタイムアウト時間を秒単位で入力します。",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "IPv6アドレスの解決を無効にする",
"disable_ipv6_desc": "IPv6アドレスタイプAAAAに対するDNSクエリをすべて破棄し、HTTPS応答から IPv6 hint を削除します。",
"fastest_addr": "最速のIPアドレス",
"fastest_addr_desc": "すべてのDNSサーバーに処理要求し、全応答の中で最速のIPアドレスを返します。これにより、AdGuard HomeがすべてのDNSサーバーからの応答を待つ必要があるため、DNSクエリが遅くなりますが、全体的な接続性は向上します。",
"fastest_addr_desc": "<b>すべての</b>DNSサーバーからの応答を待ち、各サーバーのTCP接続速度を測定し、最も接続速度の速いサーバーのIPアドレスを返します。<br/>※このモードでは、1つまたは複数のアップストリームサーバーが応答しない場合、DNSクエリが大幅に遅くなることがあります。アップストリームサーバーが安定していることを確認し、アップストリームタイムアウトは小さくしておいてください。",
"autofix_warning_text": "「修正」をクリックすると、AdGuardHomeはAdGuardHome DNSサーバを使用するようにシステムを構成します。",
"autofix_warning_list": "次のタスクを実行します:<0>システムDNSStubListenerを非アクティブ化します</0> <0>DNSサーバのアドレスを127.0.0.1に設定します</0> <0>/etc/resolv.confのシンボリックリンクの対象を/run/systemd/resolve/resolv.confに置換します</0> <0>DNSStubListenerを停止しますsystemd-resolvedサービスをリロードします</0>",
"autofix_warning_result": "その結果、システムからのすべてのDNSリクエストは、デフォルトでAdGuard Homeによって処理されます。",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "理由: {{reason}}",
"check_service": "サービス名: {{service}}",
"check_hostname": "ホスト名またはドメイン名",
"check_client_id": "クライアント識別子 (ClientID または IP アドレス)",
"check_enter_client_id": "クライアント識別子を入力してください",
"check_dns_record": "DNSレコードタイプDNS record typeを選択",
"service_name": "サービス名",
"check_not_found": "フィルタ一覧には見つかりません",
"client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?",
@@ -649,7 +656,7 @@
"blocklist": "ブロックリスト",
"milliseconds_abbreviation": "ms",
"cache_size": "キャッシュサイズ",
"cache_size_desc": "DNSキャッシュサイズバイト単位※キャッシュを無効化するには、この欄を空してください。",
"cache_size_desc": "DNSキャッシュサイズバイト単位※キャッシュを無効化するには、「0」ゼロしてください。",
"cache_ttl_min_override": "最小TTLの上書き秒単位",
"cache_ttl_max_override": "最大TTLの上書き秒単位",
"enter_cache_size": "キャッシュサイズ(バイト単位)を入力してください",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
"parallel_requests": "병렬 처리 요청",
"load_balancing": "로드 밸런싱",
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다. AdGuard Home은 가중 무작위 알고리즘을 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다.<br/>AdGuard Home은 가중 무작위 알고리즘을 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
"bootstrap_dns": "부트스트랩 DNS 서버",
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
"fallback_dns_title": "폴백 DNS 서버",
@@ -108,7 +108,7 @@
"off": "OFF",
"copyright": "Copyright",
"homepage": "홈페이지",
"report_an_issue": "문제를 보고합니다",
"report_an_issue": "문제 신고",
"privacy_policy": "개인정보취급방침",
"enable_protection": "보호 활성화",
"enabled_protection": "보호 활성화됨",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
"enforce_safe_search": "세이프서치 사용",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"no_servers_specified": "지정된 서버 없음",
"general_settings": "일반 설정",
"dns_settings": "DNS 설정",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "차단된 TTL 응답",
"blocked_response_ttl_desc": "클라이언트가 필터링된 응답을 캐시해야 하는 시간(초)을 지정합니다.",
"form_enter_blocked_response_ttl": "차단된 응답 TTL(초)을 입력하세요.",
"upstream_timeout": "업스트림 제한 시간",
"upstream_timeout_desc": "업스트림 서버의 응답을 기다리는 시간(초)을 지정합니다.",
"form_enter_upstream_timeout": "업스트림 서버 응답 제한 시간을 초 단위로 입력하세요.",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "IPv6 주소 확인 비활성화",
"disable_ipv6_desc": "IPv6 주소(AAAA 유형)에 대한 모든 DNS 쿼리를 무시하고 HTTPS 유형 응답에서 IPv6 데이터를 제거합니다.",
"fastest_addr": "가장 빠른 IP 주소",
"fastest_addr_desc": "모든 DNS 서버에 쿼리를 수행한 다음 반응이 가장 빠른 IP주소를 반합니다. AdGuard Home이 모든 DNS 서버 응답을 기다려야 하기 때문에 DNS 쿼리 속도가 느려지지만 전반적인 연결이 향상됩니다.",
"fastest_addr_desc": "<b>모든</b> DNS 서버의 응답을 기다렸다가 각 서버의 TCP 연결 속도를 측정하여 연결 속도가 가장 빠른 서버의 IP 주소를 반합니다.<br/>이 모드는 하나 이상의 업스트림 서버 응답하지 않는 경우, DNS 쿼리 속도가 상당히 느려질 수 있습니다. 업스트림 서버가 안정적이고 업스트림 타임아웃이 짧은지 확인하세요.",
"autofix_warning_text": "'수정'을 클릭하면 AdGuard Home이 AdGuard Home DNS 서버를 사용하도록 시스템을 설정합니다.",
"autofix_warning_list": "다음 작업을 진행합니다: <0>DNSStubListener 시스템 비활성화</0> <0>DNS 서버 주소를 127.0.0.1로 설정</0> <0>/etc/resolv.conf의 심볼릭 링크 타겟을 /run/systemd/resolve/resolv.conf로 변경</0> <0>DNSStubListener 중지 (systemd-resolved 서비스 새로고침)</0>",
"autofix_warning_result": "결과적으로 시스템의 모든 DNS 요청은 기본적으로 AdGuard Home에 의해 처리됩니다.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "이유: {{reason}}",
"check_service": "서비스 이름: {{service}}",
"check_hostname": "호스트 이름 또는 도메인 이름",
"check_client_id": "클라이언트 식별자(클라이언트 ID 또는 IP 주소)",
"check_enter_client_id": "클라이언트 식별자 입력",
"check_dns_record": "DNS 레코드 유형 선택",
"service_name": "서비스 이름",
"check_not_found": "필터 목록에서 찾을 수 없음",
"client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?",
@@ -649,7 +656,7 @@
"blocklist": "차단 목록",
"milliseconds_abbreviation": "ms",
"cache_size": "캐시 크기",
"cache_size_desc": "DNS 캐시 크기(바이트). 캐싱을 비활성화하려면 비워 둡니다.",
"cache_size_desc": "DNS 캐시 크기(바이트). 캐싱을 사용하지 않으려면 0으로 설정합니다.",
"cache_ttl_min_override": "최소 TTL (초) 무시",
"cache_ttl_max_override": "최대 TTL (초) 무시",
"enter_cache_size": "캐시 크기를 입력하세요",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
"parallel_requests": "Parallelle verzoeken",
"load_balancing": "Volume balanceren",
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk.<br/>AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
"bootstrap_dns": "Bootstrap DNS-servers",
"bootstrap_dns_desc": "IP-adressen van DNS-servers die worden gebruikt om IP-adressen om te zetten van de DoH/DoT-resolvers die je opgeeft als upstreams. Opmerkingen zijn niet toegestaan.",
"fallback_dns_title": "Back-up DNS-servers",
@@ -110,9 +110,9 @@
"homepage": "Startpagina",
"report_an_issue": "Rapporteer een probleem",
"privacy_policy": "Privacybeleid",
"enable_protection": "Schakel bescherming in",
"enable_protection": "Bescherming inschakelen",
"enabled_protection": "Bescherming ingeschakeld",
"disable_protection": "Schakel bescherming uit",
"disable_protection": "Bescherming uitschakelen",
"disabled_protection": "Bescherming uitgeschakeld",
"refresh_statics": "Ververs statistieken",
"dns_query": "DNS-queries",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Gebruik AdGuard Ouderlijk toezicht web service",
"use_adguard_parental_hint": "AdGuard Home controleert of het domein 18+ content bevat. Dit gebeurt dmv dezelfde privacy vriendelijke API als de Browsing Security web service.",
"enforce_safe_search": "Veilig zoeken gebruiken",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home dwingt veilig zoeken af in de volgende zoekmachines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Geen servers gespecificeerd",
"general_settings": "Algemene instellingen",
"dns_settings": "DNS instellingen",
@@ -166,7 +166,7 @@
"encryption_settings": "Encryptie instellingen",
"dhcp_settings": "DHCP instellingen",
"upstream_dns": "Upstream DNS-servers",
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer weten</a> over het configureren van upstream DNS-servers.",
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer informatie</a> over het configureren van upstream DNS-servers.",
"upstream_dns_configured_in_file": "Geconfigureerd in {{path}}",
"test_upstream_btn": "Test upstream",
"upstreams": "Upstreams",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Geblokkeerde reactie TTL",
"blocked_response_ttl_desc": "Hiermee geef je op hoeveel seconden de clients een gefilterd antwoord in de cache moeten opslaan",
"form_enter_blocked_response_ttl": "Voer geblokkeerd antwoord TTL in (seconden)",
"upstream_timeout": "Upstream time-out",
"upstream_timeout_desc": "Geeft het aantal seconden aan dat moet worden gewacht op een reactie van de upstream-server",
"form_enter_upstream_timeout": "Voer de time-outduur van de upstream-server in seconden in",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-via-HTTPS",
"dns_over_tls": "DNS-via-TLS",
@@ -324,10 +327,10 @@
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in",
"blocking_ipv4_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
"blocking_ipv6_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
"blocking_mode_default": "Standaard: Reageer met een nul IP adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
"blocking_mode_default": "Standaard: Reageer met een nul IP-adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
"blocking_mode_refused": "REFUSED: Antwoorden met REFUSED code",
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP address (0.0.0.0 voor A; :: voor AAAA)",
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP-adres (0.0.0.0 voor A; :: voor AAAA)",
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
"theme_auto": "Automatisch",
"theme_light": "Licht",
@@ -598,7 +601,7 @@
"disable_ipv6": "Oplossen IPv6-adressen uitschakelen",
"disable_ipv6_desc": "Alle DNS-query's voor IPv6-adressen (type AAAA) verwijderen en IPv6-hints uit HTTPS-antwoorden verwijderen.",
"fastest_addr": "Snelste IP adres",
"fastest_addr_desc": "Alle DNS-servers bevragen en het snelste IP adres terugkoppelen. Dit zal de DNS verzoeken vertragen omdat AdGuard Home moet wachten op de antwoorden van alles DNS-servers, maar verbetert wel de connectiviteit.",
"fastest_addr_desc": "Wacht op reacties van <b>alle</b> DNS-servers, meet de TCP-verbindingssnelheid voor elke server en retourneer het IP-adres van de server met de hoogste verbindingssnelheid.<br/>Deze modus kan DNS-query's aanzienlijk vertragen als een of meer upstream-servers niet reageren. Zorg ervoor dat je upstream-servers stabiel zijn en dat je upstream-time-out laag is.",
"autofix_warning_text": "Als je op \"Repareren\" klikt, configureert AdGuard Home jouw systeem om de AdGuard Home DNS-server te gebruiken.",
"autofix_warning_list": "De volgende taken worden uitgevoerd: <0> Deactiveren van Systeem DNSStubListener</0> <0> DNS-serveradres instellen op 127.0.0.1 </0> <0> Symbolisch koppelingsdoel van /etc/resolv.conf vervangen door /run/systemd/resolve/resolv.conf </0> <0> Stop DNSStubListener (herlaad systemd-resolved service) </0>",
"autofix_warning_result": "Als gevolg hiervan worden alle DNS-aanvragen van je systeem standaard door AdGuard Home verwerkt.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Reden: {{reason}}",
"check_service": "Servicenaam: {{service}}",
"check_hostname": "Hostnaam of domeinnaam",
"check_client_id": "Client identificator (ClientID of IP-adres)",
"check_enter_client_id": "Voer Client identificator in",
"check_dns_record": "Selecteer type DNS-record",
"service_name": "Naam service",
"check_not_found": "Niet in je lijst met filters gevonden",
"client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?",
@@ -649,7 +656,7 @@
"blocklist": "Blokkeerlijst",
"milliseconds_abbreviation": "ms",
"cache_size": "Cache grootte",
"cache_size_desc": "DNS-cachegrootte (in bytes). Leeg laten om caching uit te schakelen.",
"cache_size_desc": "DNS-cachegrootte (in bytes). Om caching uit te schakelen, stel deze in op 0.",
"cache_ttl_min_override": "Minimale TTL overschrijven",
"cache_ttl_max_override": "Maximale TTL overschrijven",
"enter_cache_size": "Cache grootte invoeren (bytes)",
@@ -695,13 +702,13 @@
"disable_for_hours": "Voor {{count}} uur",
"disable_for_hours_plural": "Voor {{count}} uren",
"disable_until_tomorrow": "Tot morgen",
"disable_notify_for_seconds": "Beveiliging uitschakelen voor {{count}} seconde",
"disable_notify_for_seconds_plural": "Beveiliging uitschakelen voor {{count}} seconden",
"disable_notify_for_minutes": "Beveiliging uitschakelen voor {{count}} minuut",
"disable_notify_for_minutes_plural": "Beveiliging uitschakelen voor {{count}} minuten",
"disable_notify_for_hours": "Beveiliging uitschakelen voor {{count}} uur",
"disable_notify_for_hours_plural": "Beveiliging uitschakelen voor {{count}} uren",
"disable_notify_until_tomorrow": "Beveiliging uitschakelen tot morgen",
"disable_notify_for_seconds": "Bescherming uitschakelen voor {{count}} seconde",
"disable_notify_for_seconds_plural": "Bescherming uitschakelen voor {{count}} seconden",
"disable_notify_for_minutes": "Bescherming uitschakelen voor {{count}} minuut",
"disable_notify_for_minutes_plural": "Bescherming uitschakelen voor {{count}} minuten",
"disable_notify_for_hours": "Bescherming uitschakelen voor {{count}} uur",
"disable_notify_for_hours_plural": "Bescherming uitschakelen voor {{count}} uren",
"disable_notify_until_tomorrow": "Bescherming uitschakelen tot morgen",
"enable_protection_timer": "Bescherming wordt ingeschakeld over {{time}}",
"custom_retention_input": "Voer retentie in uren in",
"custom_rotation_input": "Voer rotatie in uren in",

View File

@@ -106,7 +106,6 @@
"stats_malware_phishing": "Blokkert skadevare/phishing",
"stats_adult": "Blokkerte voksennettsteder",
"stats_query_domain": "Mest forespurte domener",
"for_last_24_hours": "de siste 24 timene",
"for_last_days": "for den siste {{count}} dagen",
"for_last_days_plural": "de siste {{count}} dagene",
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
@@ -121,13 +120,13 @@
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
"number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul",
"number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert",
"enforced_save_search": "Påtvungede barnevennlige søk",
"number_of_dns_query_to_safe_search": "Antall DNS-forespørsler til søkemotorer der \"Safe Search\" ble fremtvunget",
"average_processing_time": "Gjennomsnittlig behandlingstid",
"response_time": "Responstid",
"average_processing_time_hint": "Gjennomsnittstid for behandling av DNS-forespørsler i millisekunder",
"block_domain_use_filters_and_hosts": "Blokker domener ved hjelp av filtre, «hosts»-filer, og rå domener",
"filters_block_toggle_hint": "Du kan sette opp blokkeringsoppføringer i <a>Filtre</a>-innstillingene.",
@@ -265,6 +264,7 @@
"custom_ip": "Tilpasset IP",
"blocking_ipv4": "IPv4-blokkering",
"blocking_ipv6": "IPv6-blokkering",
"blocked_response_ttl": "Blokkerte svars TTL",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -626,7 +626,6 @@
"use_saved_key": "Bruk den tidligere lagrede nøkkelen",
"parental_control": "Foreldrekontroll",
"safe_browsing": "Sikker surfing",
"served_from_cache": "{{value}} <i>(formidlet fra mellomlageret)</i>",
"theme_dark_desc": "Mørkt tema",
"theme_light_desc": "Lyst tema",
"disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Użyj zapytań równoległych, aby przyspieszyć rozwiązywanie przez jednoczesne wysyłanie zapytań do wszystkich serwerów nadrzędnych.",
"parallel_requests": "Równoległe żądania",
"load_balancing": "Równoważenie obciążenia",
"load_balancing_desc": "Wysyłaj zapytania do jednego serwera nadrzędnego na raz. AdGuard Home używa swojego losowego algorytmu ważonego, aby wybrać serwer, tak aby najszybszy serwer był używany częściej.",
"load_balancing_desc": "Zapytaj jeden serwer nadrzędny na raz. AdGuard Home używa ważonego, losowego algorytmu do wybierania serwerów z najmniejszą liczbą nieudanych wyszukiwań i najniższym uśrednionym czasem wyszukiwania.",
"bootstrap_dns": "Serwery DNS Bootstrap",
"bootstrap_dns_desc": "Adresy IP serwerów DNS używanych do rozpoznawania adresów IP programów rozpoznawania nazw DoH/DoT określonych jako nadrzędne. Komentarze są niedozwolone.",
"fallback_dns_title": "Rezerwowe serwery DNS",
@@ -122,7 +122,7 @@
"stats_query_domain": "Najczęściej wyszukiwane domeny",
"for_last_hours": "w ciągu ostatniej {{count}} godziny",
"for_last_hours_plural": "w ciągu ostatnich {{count}} godzin",
"for_last_days": "za ostatni dzień {{count}}",
"for_last_days": "za ostatni {{count}} dzień",
"for_last_days_plural": "z ostatnich {{count}} dni",
"stats_disabled": "Statystyki zostały wyłączone. Można je włączyć na <0>stronie ustawień</0>.",
"stats_disabled_short": "Statystyki zostały wyłączone",
@@ -130,7 +130,7 @@
"requests_count": "Licznik żądań",
"top_blocked_domains": "Najpopularniejsze zablokowane domeny",
"top_clients": "Główni klienci",
"no_clients_found": "Nie znaleziono klienta",
"no_clients_found": "Nie znaleziono klientów",
"general_statistics": "Ogólne statystyki",
"top_upstreams": "Często żądane serwery nadrzędne",
"no_upstreams_data_found": "Brak danych dotyczących serwerów nadrzędnych",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Użyj usługi Kontrola Rodzicielska AdGuard",
"use_adguard_parental_hint": "AdGuard Home sprawdzi, czy domena zawiera materiały dla dorosłych. Używa tego samego interfejsu API przyjaznego prywatności, co usługa sieciowa Bezpieczne Przeglądanie. ",
"enforce_safe_search": "Użyj bezpiecznego wyszukiwania",
"enforce_save_search_hint": "AdGuard Home wymusza bezpieczne wyszukiwanie w następujących wyszukiwarkach: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home wymusza bezpieczne wyszukiwanie w następujących wyszukiwarkach: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nie określono serwerów",
"general_settings": "Ustawienia główne",
"dns_settings": "Ustawienia DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
"load_balancing_desc": "Consulte um servidor upstream por vez.<br/>O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
"bootstrap_dns": "Servidores DNS de inicialização",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS Fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controle parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Ele usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Configurações gerais",
"dns_settings": "Configurações de DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Resposta bloqueada TTL",
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
"upstream_timeout": "Tempo limite de upstream",
"upstream_timeout_desc": "Especifica o número de segundos para esperar por uma resposta do servidor upstream",
"form_enter_upstream_timeout": "Insira a duração do tempo limite do servidor upstream em segundos",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-sobre-HTTPS",
"dns_over_tls": "DNS-sobre-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Desativar resolução de endereços IPv6",
"disable_ipv6_desc": "Descarta todas as consultas DNS para endereços IPv6 (tipo AAAA) e remove dicas de IPv6 das respostas HTTPS.",
"fastest_addr": "Endereço de IP mais rápido",
"fastest_addr_desc": "Consulta todos os servidores DNS e retorna o endereço IP mais rápido entre todas as respostas. Isso torna as consultas DNS mais lentas, pois o AdGuard Home tem que esperar pelas respostas de todos os servidores DNS, mas melhora a conectividade geral.",
"fastest_addr_desc": "Aguarde as respostas de <b>todos</b> os servidores DNS, meça a velocidade da conexão TCP para cada servidor e retorne o endereço de IP do servidor com a velocidade de conexão mais rápida.<br/>Esse modo pode retardar significativamente as consultas de DNS, se um ou mais servidores DNS primários não estiverem respondendo. Certifique-se de que seus servidores DNS primários sejam estáveis e que seu tempo de espera para DNS seja baixo.",
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
"autofix_warning_list": "Ele irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por padrão.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}",
"check_service": "Nome do serviço: {{service}}",
"check_hostname": "Nome do anfitrião ou nome de domínio",
"check_client_id": "Identificador do cliente (ClienteID ou endereço de IP)",
"check_enter_client_id": "Insira o identificador do cliente",
"check_dns_record": "Selecione o tipo de registro DNS",
"service_name": "Nome do serviço",
"check_not_found": "Não encontrado em suas listas de filtros",
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Lista de bloqueio",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamanho do cache",
"cache_size_desc": "Tamanho do cache do DNS (em bytes). Para desativar o cache, deixe em branco.",
"cache_size_desc": "Tamanho do cache do DNS (em bytes). Para desativar o cache, defina como 0.",
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
"cache_ttl_max_override": "Sobrepor o TTL máximo",
"enter_cache_size": "Digite o tamanho do cache (bytes)",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
"load_balancing_desc": "Consulta um servidor upstream de cada vez. <br/>O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas falhadas e o menor tempo médio de pesquisa.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS de fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controlo parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forçará a pesquisa segura nos seguintes motores de busca: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home aplicará pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Definições gerais",
"dns_settings": "Definições de DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Resposta bloqueada TTL",
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
"upstream_timeout": "Tempo esgotado de upstream",
"upstream_timeout_desc": "Especifica o número de segundos a aguardar por uma resposta do servidor upstream",
"form_enter_upstream_timeout": "Insira a duração do tempo esgotado do servidor upstream em segundos",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-sobre-HTTPS",
"dns_over_tls": "DNS-sobre-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Desativar resolução de endereços IPv6",
"disable_ipv6_desc": "Descarte todas as consultas DNS para endereços IPv6 (tipo AAAA) e remova as dicas IPv6 das respostas HTTPS.",
"fastest_addr": "Endereço de IP mais rápido",
"fastest_addr_desc": "Consulta todos os servidores DNS e retorna o endereço IP mais rápido entre todas as respostas. Isso torna as consultas DNS mais lentas, pois o AdGuard Home tem que esperar pelas respostas de todos os servidores DNS, mas melhora a conectividade geral.",
"fastest_addr_desc": "Aguarda por respostas de <b>todos</b> os servidores DNS, mede a velocidade da ligação TCP para cada servidor e devolva o endereço IP do servidor com a velocidade de ligação mais rápida.<br/>Este modo pode abrandar significativamente as consultas DNS, se um ou mais servidores upstream não estiverem a responder. Certifique-se de que os seus servidores upstream são estáveis e que o tempo esgotado de upstream é baixo.",
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
"autofix_warning_list": "Irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por predefinição.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Motivo: {{reason}}",
"check_service": "Nome do serviço: {{service}}",
"check_hostname": "Nome do hospedeiro ou nome de domínio",
"check_client_id": "Identificador do cliente (ClientID ou endereço IP)",
"check_enter_client_id": "Insira o identificador do cliente",
"check_dns_record": "Selecione o tipo de registro DNS",
"service_name": "Nome do serviço",
"check_not_found": "Não encontrado nas tuas listas de filtros",
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Lista de bloqueio",
"milliseconds_abbreviation": "ms",
"cache_size": "Tamanho do cache",
"cache_size_desc": "Tamanho do cache DNS (em bytes). Para desativar o cache, deixar o campo vazio.",
"cache_size_desc": "Tamanho do cache DNS (em bytes). Para desativar o cache, defina como 0.",
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
"cache_ttl_max_override": "Sobrepor o TTL máximo",
"enter_cache_size": "Digite o tamanho do cache (bytes)",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
"parallel_requests": "Параллельные запросы",
"load_balancing": "Распределение нагрузки\n",
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
"load_balancing_desc": "Запрашивать по одному upstream-серверу.<br/>AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
"fallback_dns_title": "Резервные DNS-серверы",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
"enforce_safe_search": "Включить безопасный поиск",
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Yandex и Pixabay.",
"enforce_save_search_hint": "AdGuard Home будет обеспечивать безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Нет указанных серверов",
"general_settings": "Основные настройки",
"dns_settings": "Настройки DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "TTL заблокированного ответа",
"blocked_response_ttl_desc": "Указывает, в течение скольких секунд клиенты должны кешировать отфильтрованный ответ",
"form_enter_blocked_response_ttl": "Введите TTL заблокированного ответа (в секундах)",
"upstream_timeout": "Время ожидания ответов от upstream-серверов",
"upstream_timeout_desc": "Длительность ожидания ответа от upstream-серверов в секундах",
"form_enter_upstream_timeout": "Введите время ожидания для upstream-сервера в секундах",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "Отключить обработку IPv6-адресов",
"disable_ipv6_desc": "Игнорировать все DNS-запросы адресов IPv6 (тип AAAA) и удалять IPv6-данные из ответов типа HTTPS.",
"fastest_addr": "Самый быстрый IP-адрес",
"fastest_addr_desc": "Опросить все DNS-серверы и вернуть самый быстрый IP-адрес из полученных ответов. Это замедлит DNS-запросы, так как нужно будет дождаться ответов со всех DNS-серверов, но улучшит соединение.",
"fastest_addr_desc": "Дождаться ответов от <b>всех</b> DNS-серверов, измерить скорость TCP-соединения для каждого сервера и вернуть IP-адрес сервера с самой высокой скоростью соединения.<br/>Этот режим может значительно замедлить выполнение DNS-запросов, если один или несколько серверов не отвечают. Убедитесь, что ваши серверы работают стабильно, а время ожидания серверов мало.",
"autofix_warning_text": "При нажатии «Исправить» AdGuard Home настроит вашу систему на использование DNS-сервера AdGuard Home.",
"autofix_warning_list": "Будут выполняться следующие задачи: <0>Деактивировать системный DNSStubListener</0> <0>Установить адрес сервера DNS на 127.0.0.1</0> <0>Создать символическую ссылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Остановить DNSStubListener (перезагрузить системную службу)</0>.",
"autofix_warning_result": "В результате все DNS-запросы от вашей системы будут по умолчанию обрабатываться AdGuard Home.\n",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Причина: {{reason}}",
"check_service": "Название сервиса: {{service}}",
"check_hostname": "Имя хоста или домена",
"check_client_id": "Идентификатор клиента (ClientID или IP-адрес)",
"check_enter_client_id": "Введите идентификатор клиента",
"check_dns_record": "Выберите тип DNS-записи",
"service_name": "Имя сервиса",
"check_not_found": "Не найдено в вашем списке фильтров",
"client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?",
@@ -649,7 +656,7 @@
"blocklist": "Чёрный список",
"milliseconds_abbreviation": "мс",
"cache_size": "Размер кеша",
"cache_size_desc": "Размера кеша DNS (в байтах). Чтобы отключить кэширование, оставьте поле пустым.",
"cache_size_desc": "Размер кеша DNS (в байтах). Чтобы отключить кеширование, установите значение 0.",
"cache_ttl_min_override": "Переопределить минимальный TTL",
"cache_ttl_max_override": "Переопределить максимальный TTL",
"enter_cache_size": "Введите размер кеша (в байтах)",

View File

@@ -7,7 +7,7 @@
"local_ptr_desc": "ස්ථානීය PTR විමසුම් සඳහා ඇඩ්ගාර්ඩ් හෝම් භාවිතා කරන ව.නා.ප. සේවාදායක. මෙම සේවාදායක පුද්ගලික අ.ජා.කෙ. ලිපින පරාසවල PTR විමසුම් විසඳීමට භාවිතා කරයි, උදාහරණයක් ලෙස ප්‍රතිවර්ත ව.නා.ප. භාවිතයෙන් \"192.168.12.34\". මෙය සකසා නැති නම්, ඇඩ්ගාර්ඩ් හෝම් හි ලිපින සඳහා හැරුනු විට ඔබගේ මෙහෙයුම් පද්ධතියේ පෙරනිමි ව.නා.ප. විසදුම්වල ලිපින භාවිතා කරයි.",
"local_ptr_default_resolver": "පෙරනිමි පරිදි, ඇඩ්ගාර්ඩ් හෝම් පහත ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතා කරයි: {{ip}}.",
"local_ptr_no_default_resolver": "ඇඩ්ගාර්ඩ් හෝම් හට මෙම පද්ධතිය සඳහා සුදුසු පුද්ගලික ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු නිශ්චය කරගත නොහැකි විය.",
"local_ptr_placeholder": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් යොදන්න",
"local_ptr_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
"resolve_clients_title": "අනුග්‍රාහකවල අ.ජා.කෙ. ලිපින ප්‍රතිවර්ත විසඳීම සබල කරන්න",
"use_private_ptr_resolvers_title": "පෞද්. ප්‍රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතය",
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායක පරීක්‍ෂා කරන්න",
@@ -102,7 +102,6 @@
"stats_malware_phishing": "අවහිර කළ ද්වේශාංග/තතුබෑම්",
"stats_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි",
"stats_query_domain": "ප්‍රචලිත විමසන ලද වසම්",
"for_last_24_hours": "පසුගිය පැය 24 සඳහා",
"for_last_days": "පසුගිය දවස් {{count}} සඳහා",
"for_last_days_plural": "පසුගිය දවස් {{count}} සඳහා",
"stats_disabled": "සංඛ්‍යාලේඛන අබල කර ඇත. එය <0>සැකසුම් පිටුවෙන්</0> සබල කළ හැකිය.",
@@ -115,13 +114,15 @@
"general_statistics": "පොදු සංඛ්‍යාලේඛන",
"number_of_dns_query_days": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_days_plural": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_24_hours": "පසුගිය පැය 24 සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_hours": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_hours_plural": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
"number_of_dns_query_blocked_24_hours": "දැන්වීම් වාරණ පෙරහන් සහ සත්කාරක වාරණ ලැයිස්තු මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_by_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්‍ෂණ ඒකකය මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
"number_of_dns_query_blocked_24_hours_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි ගණන",
"enforced_save_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ",
"number_of_dns_query_to_safe_search": "ආරක්‍ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්‍ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
"average_processing_time": "සාමාන්‍ය සැකසුම් කාලය",
"response_time": "ප්‍රතිචාර කාලය",
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්‍ය කාලය මිලි තත්පර වලින්",
"block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති <a>පෙරහන්</a> තුළ පිහිටුවිය හැකිය.",
@@ -130,7 +131,7 @@
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්‍ෂා කරනු ඇත. එය පිරික්සුම් ආරක්‍ෂණ වියමන සේවාව මෙන් රහස්‍යතා හිතකාමී යෙ.ක්‍ර. අ.මු. (API) භාවිතා කරයි.",
"enforce_safe_search": "ආරක්‍ෂිත සෙවුම භාවිතා කරන්න",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්‍ර තුළ ආරක්‍ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්‍ර තුළ ආරක්‍ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, එකොසියා, යාන්ඩෙක්ස් සහ පික්සාබේ.",
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
"general_settings": "පොදු සැකසුම්",
"dns_settings": "ව.නා.ප. සැකසුම්",
@@ -196,12 +197,14 @@
"example_comment_hash": "# එසේම අදහස් දැක්වීමක්.",
"example_regex_meaning": "නිශ්චිතව දක්වා ඇති නිත්‍ය වාක්‍යවිධියට ගැළපෙන වසම් වෙත ප්‍රවේශය අවහිර කරයි.",
"example_upstream_regular": "සාමාන්‍ය ව.නා.ප. (UDP හරහා);",
"example_upstream_regular_port": "සාමාන්‍ය ව.නා.ප. (UDP හරහා, තොට සමඟ);",
"example_upstream_udp": "සාමාන්‍ය ව.නා.ප. (UDP, සත්කාරක-නම හරහා);",
"example_upstream_dot": "සංකේතිත <0>TLS-මගින්-ව.නා.ප.</0>;",
"example_upstream_doh": "සංකේතිත <0>HTTPS-මගින්-ව.නා.ප.</0>;",
"example_upstream_doq": "සංකේතිත <0>QUIC-මගින්-ව.නා.ප.</0>;",
"example_upstream_sdns": "<1>DNSCrypt</1> හෝ <2>HTTPS-මගින්-ව.නා.ප.</2> පිළිවිසඳු සඳහා <0>ව.නා.ප. මුද්දර</0>;",
"example_upstream_tcp": "සාමාන්‍ය ව.නා.ප. (TCP/ස.පා.කෙ. හරහා);",
"example_upstream_tcp_port": "සාමාන්‍ය ව.නා.ප. (TCP හරහා, තොට සමඟ);",
"example_upstream_tcp_hostname": "සාමාන්‍ය ව.නා.ප. (ස.පා.කෙ., සත්කාරක-නම හරහා);",
"all_lists_up_to_date_toast": "සියළුම ලැයිස්තු දැනටමත් යාවත්කාලීනයි",
"dns_test_ok_toast": "සඳහන් කළ ව.නා.ප. සේවාදායක නිවැරදිව ක්‍රියා කරයි",
@@ -275,6 +278,7 @@
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
"rate_limit_desc": "එක් අනුග්‍රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
"rate_limit_whitelist_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
"blocking_mode_default": "පොදු: දැන්වීම් අවහිර කරන ආකාරයේ නීතියක් මගින් අවහිර කළ විට REFUSED සමඟ ප්‍රතිචාර දක්වයි; /etc/host-style ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා.කෙ. ලිපිනය සමඟ ප්‍රතිචාර දක්වයි",
@@ -505,8 +509,8 @@
"statistics_enable": "සංඛ්‍යාලේඛන සබල කරන්න",
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
"ignore_domains_title": "නොසලකන වසම්",
"ignore_domains_desc_stats": "සංඛ්‍යාලේඛනයෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"ignore_domains_desc_query": "විමසුම් සටහනෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
"ignore_domains_desc_stats": "මෙම නීති වලට ගැළපෙන විමසුම් සංඛ්‍යාලේඛනයට නොලියැවෙයි",
"ignore_domains_desc_query": "විමසුම් සටහන මෙම නීති වලට ගැළපෙන විමසුම් නොලියැවෙයි",
"interval_hours": "පැය {{count}}",
"interval_hours_plural": "පැය {{count}}",
"filters_configuration": "පෙරහන් වින්‍යාසය",
@@ -615,8 +619,8 @@
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
"parental_control": "දෙමාපිය පාලනය",
"safe_browsing": "ආරක්‍ෂිත පිරික්සුම",
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
"served_from_cache_label": "නිහිතයෙන් සැපයිණි",
"form_error_password_length": "මුරපදය අකුරු {{min}} සහ {{value}} ක් අතර විය යුතු",
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
@@ -646,6 +650,7 @@
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්‍යාලේඛන",
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්‍රාහකය යොදන්න එපා",
"ignore_statistics": "සංඛ්‍යාලේඛනයට මෙම අනුග්‍රාහකය යොදන්න එපා",
"schedule_services": "සේවා අවහිර විරාමය",
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
"schedule_select_days": "දවස් තෝරන්න",
"schedule_timezone": "වේලා කලාපයක් තෝරන්න",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
"parallel_requests": "Paralelné dopyty",
"load_balancing": "Vyrovnávanie záťaže",
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server.<br/>AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy serverov DNS používaných na rozlíšenie IP adries prekladačov DoH/DoT, ktoré zadáte ako upstream. Komentáre nie sú povolené.",
"fallback_dns_title": "Záložné servery DNS",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použiť AdGuard službu Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home skontroluje, či doména obsahuje materiály pre dospelých. Používa rovnaké API priateľské k ochrane osobných údajov ako služba Bezpečného prehliadania.",
"enforce_safe_search": "Používať bezpečné vyhľadávanie",
"enforce_save_search_hint": "AdGuard Home vynucuje bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Yandex a Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynúti bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Neboli špecifikované žiadne servery",
"general_settings": "Všeobecné nastavenia",
"dns_settings": "Nastavenia DNS",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Blokovaná odozva TTL",
"blocked_response_ttl_desc": "Určuje, na koľko sekúnd by mali klienti uložiť filtrovanú odozvu do vyrovnávacej pamäte",
"form_enter_blocked_response_ttl": "Zadajte TTL blokovanej odozve (sekundy)",
"upstream_timeout": "Časový limit pre upstream",
"upstream_timeout_desc": "Určuje počet sekúnd čakania na odpoveď z upstream servera",
"form_enter_upstream_timeout": "Zadajte trvanie časového limitu upstream servera v sekundách",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -484,7 +487,7 @@
"access_disallowed_title": "Nepovolení klienti",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší dopyty od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_blocked_title": "Nepovolené domény",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrácie URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_settings_saved": "Nastavenia prístupu úspešne uložené",
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
"updates_version_equal": "AdGuard Home je aktuálny",
@@ -598,7 +601,7 @@
"disable_ipv6": "Vypnúť rozlišovanie IPv6 adries",
"disable_ipv6_desc": "Ignorovať všetky dotazy DNS na adresy IPv6 (typ AAAA) a odstrániť IPv6 údaje z HTTPS odpovedí.",
"fastest_addr": "Najrýchlejšia IP adresa",
"fastest_addr_desc": "Dopytovať všetky servery DNS a vrátiť najrýchlejšiu IP adresu zo všetkých odpovedí. Toto spomalí DNS dopyty, pretože AdGuard Home musí čakať na odpovede zo všetkých serverov DNS, ale zlepší sa celkové pripojenie.",
"fastest_addr_desc": "Čaká na odpovede od <b>všetkých</b> DNS serverov, zmeria rýchlosť pripojenia TCP pre každý server a vráti adresu IP servera s najväčšou rýchlosťou pripojenia.<br/>Tento režim môže výrazne spomal DNS dopyty, ak jeden alebo viac upstream serverov neodpovedá. Uistite sa, že Vaše upstream servery sú stabilné a upstream upstream je nízky.",
"autofix_warning_text": "Ak kliknete na „Opraviť“, AdGuardHome nakonfiguruje Váš systém tak, aby používal DNS server AdGuardHome.",
"autofix_warning_list": "Bude vykonávať tieto úlohy: <0>Deaktivovať systém DNSStubListener</0> <0>Nastaviť adresu servera DNS na 127.0.0.1</0> <0>Nahradiť cieľový symbolický odkaz /etc/resolv.conf na /run/systemd/resolve/resolv.conf</0> <0>Zastaviť službu DNSStubListener (znova načítať službu systemd-resolved)</0>",
"autofix_warning_result": "Výsledkom bude, že všetky DNS dopyty z Vášho systému budú štandardne spracované službou AdGuard Home.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Dôvod: {{reason}}",
"check_service": "Meno služby: {{service}}",
"check_hostname": "Názov hostiteľa alebo názov domény",
"check_client_id": "Identifikátor klienta (ClientID alebo IP adresa)",
"check_enter_client_id": "Zadajte identifikátor klienta",
"check_dns_record": "Vyberte typ DNS záznamu",
"service_name": "Názov služby",
"check_not_found": "Nenašlo sa vo Vašom zozname filtrov",
"client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "Zoznam blokovaní",
"milliseconds_abbreviation": "ms",
"cache_size": "Veľkosť cache",
"cache_size_desc": "Veľkosť vyrovnávacej pamäte DNS (v bajtoch). Ak chcete zakázať ukladanie do vyrovnávacej pamäte, ponechajte pole prázdne.",
"cache_size_desc": "Veľkosť vyrovnávacej pamäte DNS (v bajtoch). Ak chcete vypnúť ukladanie do vyrovnávacej pamäte, nastavte hodnotu 0.",
"cache_ttl_min_override": "Prepísať minimálne TTL",
"cache_ttl_max_override": "Prepísať maximálne TTL",
"enter_cache_size": "Zadať veľkosť cache (v bajtoch)",

View File

@@ -461,7 +461,7 @@
"form_enter_mac": "Skriv in MAC",
"form_enter_id": "Ange identifierare",
"form_add_id": "Lägg till identifierare",
"form_client_name": "Skriv in klientnamn",
"form_client_name": "Ange klientnamn",
"name": "Namn",
"client_name": "Klient {{id}}",
"client_global_settings": "Använda globala inställningar",
@@ -674,7 +674,6 @@
"use_saved_key": "Använd den tidigare sparade nyckeln",
"parental_control": "Föräldrakontroll",
"safe_browsing": "Säker surfning",
"served_from_cache": "{{value}} <i>(levereras från cache)</i>",
"form_error_password_length": "Lösenordet måste vara {{min}} till {{max}} tecken långt",
"anonymizer_notification": "<0>Observera:</0> IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar</1>.",
"confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?",

View File

@@ -46,6 +46,7 @@
"settings": "การตั้งค่า",
"filters": "ตัวกรอง",
"query_log": "บันทึกการสืบค้น",
"nothing_found": "ไม่พบอะไร",
"faq": "คำถามที่พบบ่อย",
"version": "รุ่น",
"address": "ที่อยู่",
@@ -349,7 +350,7 @@
"statistics_configuration": "การกำหนดค่าสถิติ",
"statistics_retention": "การเก็บรักษาสถิติ",
"statistics_retention_desc": "หากคุณลดค่าช่วงเวลาข้อมูลบางอย่างจะหายไป",
"statistics_clear": " ล้างค่าสถิติ",
"statistics_clear": "ล้างสถิติ",
"statistics_clear_confirm": "คุณแน่ใจหรือไม่ว่าต้องการล้างสถิติ?",
"statistics_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บรักษาสถิติ? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
"statistics_cleared": "สถิติได้ถูกล้างเรียบร้อยแล้ว",
@@ -390,10 +391,13 @@
"check_title": "ตรวจสอบการกรอง",
"check_desc": "ตรวจสอบว่าชื่อโฮสต์ถูกกรอง",
"form_enter_host": "ป้อนชื่อโฮสต์",
"show_processed_responses": "การประมวลผล",
"show_blocked_responses": "ปิดกั้นแล้ว",
"show_whitelisted_responses": "รายการที่อนุญาต",
"show_processed_responses": "ประมวลผลแล้ว",
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
"safe_search": "ค้นหาอย่างปลอดภัย",
"blocklist": "บัญชีดำ",
"allowed": "รายการที่อนุญาต",
"filter_category_other": "อื่น ๆ",
"parental_control": "ควบคุมโดยผู้ปกครอง",
"sunday_short": "อาทิตย์",

View File

@@ -40,11 +40,11 @@
"dhcp_ipv4_settings": "DHCP IPv4 Ayarları",
"dhcp_ipv6_settings": "DHCP IPv6 Ayarları",
"form_error_required": "Gerekli alan",
"form_error_ip4_format": "Geçersiz IPv4 adresi",
"form_error_ip4_gateway_format": "Geçersiz ağ geçidi IPv4 adresi",
"form_error_ip6_format": "Geçersiz IPv6 adresi",
"form_error_ip_format": "Geçersiz IP adresi",
"form_error_mac_format": "Geçersiz MAC adresi",
"form_error_ip4_format": "IPv4 adresi geçersiz",
"form_error_ip4_gateway_format": "Ağ geçidi IPv4 adresi geçersiz",
"form_error_ip6_format": "IPv6 adresi geçersiz",
"form_error_ip_format": "IP adresi geçersiz",
"form_error_mac_format": "MAC adresi geçersiz",
"form_error_client_id_format": "İstemci Kimliği yalnızca sayılar, küçük harfler ve kısa çizgiler içermelidir",
"form_error_server_name": "Sunucu adı geçersiz",
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
@@ -68,7 +68,7 @@
"ip": "IP",
"dhcp_table_hostname": "Ana makine Adı",
"dhcp_table_expires": "Bitiş tarihi",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların İnternet bağlantısı kesilebilir!",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka bir aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi",
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
@@ -147,14 +147,14 @@
"average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
"response_time": "Yanıt süresi",
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
"block_domain_use_filters_and_hosts": "Filtre ve ana bilgisayar dosyalarını kullanarak alan adlarını engelle",
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
"use_adguard_parental": "AdGuard ebeveyn denetimi web hizmetini kullan",
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol eder. Gezinti koruması web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanır.",
"enforce_safe_search": "Güvenli Aramayı kullan",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
"enforce_safe_search": "Güvenli aramayı kullan",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Sunucu belirtilmedi",
"general_settings": "Genel ayarlar",
"dns_settings": "DNS ayarları",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "Engellenen yanıt kullanım süresi",
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
"upstream_timeout": "Üst kaynak zaman aşımı",
"upstream_timeout_desc": "Üst kaynak sunucusundan yanıt almak için kaç saniye bekleneceğini belirtir",
"form_enter_upstream_timeout": "Üst kaynak sunucusu zaman aşımı süresini saniye cinsinden girin",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -308,7 +311,7 @@
"form_enter_rate_limit": "Sıklık limitini girin",
"rate_limit": "Sıklık limiti",
"edns_enable": "EDNS istemci alt ağını etkinleştir",
"edns_cs_desc": "Kaynak yönü isteklerine EDNS İstemci Alt Ağı seçeneğini (ECS) ekleyin ve istemciler tarafından gönderilen değerleri sorgu günlüğüne kaydedin.",
"edns_cs_desc": "Üst sunucu isteklerine ECS (EDNS İstemci Alt Ağı) seçeneğini ekler ve istemciler tarafından gönderilen değerleri sorgu günlüğünde kaydeder.",
"edns_use_custom_ip": "EDNS için özel IP kullan",
"edns_use_custom_ip_desc": "EDNS için özel IP kullanımına izin ver",
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
@@ -342,17 +345,17 @@
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
"known_tracker": "Bilinen izleyici",
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
"install_welcome_desc": "AdGuard Home, ağ genelinde reklamları ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenizi sağlar, istemci tarafında herhangi bir program kullanmanıza gerek duymaz.",
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyici engelleyen bir DNS sunucusudur. Tüm ağınızı ve cihazlarınızı kontrol etmenizi sağlar ve istemci tarafında ek bir yazılım kullanmanıza gerek duymaz.",
"install_settings_title": "Yönetici Web Arayüzü",
"install_settings_listen": "Dinleme arayüzü",
"install_settings_port": "Bağlantı noktası",
"install_settings_interface_link": "AdGuard Home yönetici web arayüzünüz aşağıdaki adreslerde bulunacaktır:",
"install_settings_interface_link": "AdGuard Home yönetici web arayüzüne aşağıdaki adreslerden erişebilirsiniz:",
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
"install_settings_dns": "DNS sunucusu",
"install_settings_dns_desc": "Aşağıdaki adreslerde DNS sunucusunu kullanmak için cihazlarınızı veya yönlendiricinizi yapılandırmanız gerekir:",
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi aşağıdaki adreslerdeki DNS sunucusunu kullanacak şekilde yapılandırmanız gerekir:",
"install_settings_all_interfaces": "Tüm arayüzler",
"install_auth_title": "Kimlik Doğrulama",
"install_auth_desc": "AdGuard Home yönetim web arayüzü için şifre doğrulaması yapılandırılmalıdır. AdGuard Home'a yalnızca yerel ağınızdan erişilebilir olsa bile, onu sınırsız erişimden korumak yine de önemlidir.",
"install_auth_desc": "AdGuard Home yönetici web arayüzüne parola ile kimlik doğrulama yapılandırılmalıdır. AdGuard Home yalnızca yerel ağınızdan erişilebilir olsa bile, yine de yetkisiz erişime karşı korunması önemlidir.",
"install_auth_username": "Kullanıcı adı",
"install_auth_password": "Parola",
"install_auth_confirm": "Parolayı onayla",
@@ -366,10 +369,10 @@
"install_devices_router": "Yönlendirici",
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
"install_devices_address": "AdGuard Home DNS sunucusu aşağıdaki adresleri dinliyor",
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL aracılığıyla erişebilirsiniz. Bir parola girmeniz istenebilir. Hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemin seçilmesi durumunda yüksek ihtimalle tüm yönlendirici yapılandırmasını kaybedeceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya PC'nize yükleyin ve yönlendiricinin ayarlarına erişmek için kullanın.",
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle, tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL üzerinden erişebilirsiniz. Giriş yaparken bir parola girmeniz istenebilir. Parolanızı hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemi seçerseniz yönlendiricinin tüm yapılandırmasını kaybedebileceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya bilgisayarınıza yükleyin ve yönlendiricinin ayarlarına erişmek için bu uygulamayı kullanın.",
"install_devices_router_list_2": "DHCP/DNS ayarlarını bulun. DNS satırlarını arayın, genelde iki veya üç tanedir, üç rakam girilebilen dört ayrı grup içeren satırdır.",
"install_devices_router_list_3": "AdGuard Home sunucu adreslerinizi oraya girin.",
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu ayarlanamaz. Bu durumda, AdGuard Home'u <0>DHCP sunucusu</0> olarak ayarlamak yardımcı olabilir. Aksi takdirde, yönlendirici modeliniz için DNS sunucularını nasıl ayarlayacağınız konusunda yönlendirici kılavuzuna bakmalısınız.",
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu yapılandırılamaz. Bu durumda, AdGuard Home'u bir <0>DHCP sunucusu</0> olarak yapılandırmak yardımcı olabilir. Aksi takdirde, yönlendirici modelinizde DNS sunucularını nasıl özelleştireceğinizi öğrenmek için yönlendirici kılavuzunu kontrol etmelisiniz.",
"install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nıın.",
"install_devices_windows_list_2": "Ağ ve İnternet kategorisine girin ve ardından Ağ ve Paylaşım Merkezi'ne girin.",
"install_devices_windows_list_3": "Sol panelde \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
@@ -389,7 +392,7 @@
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_ios_list_3": "O anda aktif olan ağın adına dokunun.",
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
"get_started": "Başlayın",
"get_started": "Başla",
"next": "Sonraki",
"open_dashboard": "Panoyu Aç",
"install_saved": "Başarıyla kaydedildi",
@@ -452,14 +455,14 @@
"settings_global": "Genel",
"settings_custom": "Özel",
"table_client": "İstemci",
"table_name": "AdAdı",
"table_name": "Ad",
"save_btn": "Kaydet",
"client_add": "İstemci Ekle",
"client_new": "Yeni İstemci",
"client_edit": "İstemciyi Düzenle",
"client_identifier": "Tanımlayıcı",
"ip_address": "IP adresi",
"client_identifier_desc": "İstemciler IP adresleri, CIDR, MAC adresleri veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiyi <0>buradan</0> edinebilirsiniz.",
"client_identifier_desc": "İstemciler, IP adresi, CIDR, MAC adresi veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiye <0>buradan</0> ulaşabilirsiniz.",
"form_enter_ip": "IP girin",
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
"form_enter_mac": "MAC adresi girin",
@@ -476,7 +479,7 @@
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
"auto_clients_title": "Çalışma zamanı istemcileri",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dahil olmak üzere çeşitli kaynaklardan toplanır.",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, ana bilgisayar dosyaları, ters DNS sorguları ve çeşitli diğer kaynaklardan toplanmaktadır.",
"access_title": "Erişim ayarları",
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
"access_allowed_title": "İzin verilen istemciler",
@@ -598,12 +601,12 @@
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
"fastest_addr": "En hızlı IP adresi",
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasından en hızlı olan IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
"fastest_addr_desc": "<b>Tüm</b> DNS sunucularından yanıt bekler, her sunucu için TCP bağlantı hızını ölçer ve en hızlı bağlantı hızına sahip sunucunun IP adresini döndürür.<br/>Bu yapılandırma, bir veya daha fazla üst kaynak sunucusu yanıt vermediğinde, DNS sorgularını önemli ölçüde yavaşlatabilir. Üst kaynak sunucularınızın kararlı olduğundan ve üst kaynak zaman aşım sürenizin düşük olduğundan emin olun.",
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dahil edin. <0>Daha fazla bilgi edinin</0>.",
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Filtreleme kurallarına etiketleri dahil ederek daha hassas bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.",
"form_select_tags": "İstemci etiketlerini seçin",
"check_title": "Filtrelemeyi denetleyin",
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "Sebep: {{reason}}",
"check_service": "Hizmet adı: {{service}}",
"check_hostname": "Ana makine adı veya alan adı",
"check_client_id": "İstemci tanımlayıcısı (ClientID veya IP adresi)",
"check_enter_client_id": "İstemci tanımlayıcısı girin",
"check_dns_record": "DNS kayıt türünü seçin",
"service_name": "Hizmet adı",
"check_not_found": "Filtre listelerinizde bulunamadı",
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
@@ -624,11 +631,11 @@
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
"static_ip": "Sabit IP adresi",
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışması için sabit bir IP adresine ihtiyacı vardır. Aksi takdirde, yönlendiriciniz bir zaman sonra bu cihaza farklı bir IP adresi atayabilir.",
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışabilmesi için sabit bir IP adresine ihtiyaç duyar. Aksi takdirde, yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.",
"set_static_ip": "Sabit IP adresi ayarla",
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> sabit IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
"list_updated": "{{count}} liste güncellendi",
"list_updated_plural": "{{count}} liste güncellendi",
@@ -649,7 +656,7 @@
"blocklist": "Engel listesi",
"milliseconds_abbreviation": "ms",
"cache_size": "Önbellek boyutu",
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleğe almayı devre dışı bırakmak için boş bırakın.",
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleği devre dışı bırakmak için 0 olarak ayarlayın.",
"cache_ttl_min_override": "Minimum kullanım süresini geçersiz kıl",
"cache_ttl_max_override": "Maksimum kullanım süresini geçersiz kıl",
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
@@ -707,8 +714,8 @@
"custom_rotation_input": "Rotasyonu saat cinsinden girin",
"protection_section_label": "Koruma",
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
"ignore_query_log": "Sorgu günlüğünde bu istemciyi yoksay",
"ignore_statistics": "İstatistiklerde bu istemciyi yoksay",
"ignore_query_log": "Sorgu günlüğünde bu istemciyi gösterme",
"ignore_statistics": "İstatistiklerde bu istemciyi gösterme",
"schedule_services": "Hizmet engellemeyi duraklat",
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
@@ -742,6 +749,6 @@
"friday_short": "Cum",
"saturday_short": "Cmt",
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması",
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğe almayı etkinleştir",
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğini etkinleştir",
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
}

View File

@@ -20,7 +20,7 @@
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
"use_private_ptr_resolvers_title": "Використовувати приватні зворотні DNS-резолвери",
"use_private_ptr_resolvers_desc": "Надсилати зворотні DNS-запити до вказаних серверів для клієнтів, що обслуговуються локально. Якщо вимкнено, AdGuard Home буде відповідати NXDOMAIN на всі такі PTR-запити, окрім запитів про клієнтів, що уже відомі завдяки DHCP, /etc/hosts тощо.",
"use_private_ptr_resolvers_desc": "Розвʼязувати запити PTR, SOA та NS для доменів ARPA, що містять приватні IP-адреси, через приватні вихідні сервери, DHCP, /etc/hosts тощо. Якщо вимкнено, AdGuard Home відповідатиме на всі такі запити з NXDOMAIN.",
"check_dhcp_servers": "Перевірити DHCP-сервери",
"save_config": "Зберегти конфігурацію",
"enabled_dhcp": "DHCP-сервер увімкнено",
@@ -343,10 +343,10 @@
"known_tracker": "Відомі трекери",
"install_welcome_title": "Вітаємо в AdGuard Home!",
"install_welcome_desc": "AdGuard Home — це мережевий DNS-сервер, що блокує рекламу та відстеження. Його мета — надати вам контроль над усією мережею та всіма пристроями в ній без потреби використання програми на стороні клієнта.",
"install_settings_title": "Веб-інтерфейс адміністратора",
"install_settings_title": "Вебінтерфейс адміністратора",
"install_settings_listen": "Мережевий інтерфейс",
"install_settings_port": "Порт",
"install_settings_interface_link": "Веб-інтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
"install_settings_interface_link": "Вебінтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
"form_error_port": "Уведіть правильне значення порту",
"install_settings_dns": "DNS-сервер",
"install_settings_dns_desc": "Вам потрібно буде налаштувати свої пристрої або маршрутизатор для використання DNS-сервера за такими адресами:",

View File

@@ -6,21 +6,21 @@
"upstream_parallel": "Sử dụng truy vấn song song để tăng tốc độ giải quyết bằng cách truy vấn đồng thời tất cả các máy chủ ngược tuyến",
"parallel_requests": "Yêu cầu song song",
"load_balancing": "Cân bằng tải",
"load_balancing_desc": "Chỉ truy xuất một máy chủ trong cùng thời điểm. AdGuard Home sẽ sử dụng thuật toán trọng số ngẫu nhiên để chọn một máy chủ nhanh nhất và sử dụng máy chủ đó thường xuyên hơn.",
"load_balancing_desc": "Truy vấn một máy chủ thượng nguồn tại một thời điểm. AdGuard Home sử dụng thuật toán ngẫu nhiên có trọng số để chọn máy chủ có số lần tìm kiếm không thành công thấp nhất và thời gian tìm kiếm trung bình thấp nhất.",
"bootstrap_dns": "Máy chủ DNS Bootstrap",
"bootstrap_dns_desc": "Địa chỉ IP của máy chủ DNS được sử dụng để phân giải địa chỉ IP của trình phân giải DoH/DoT mà bạn chỉ định làm thượng nguồn. Bình luận không được phép.",
"fallback_dns_title": "Máy chủ DNS dự phòng",
"fallback_dns_desc": "Danh sách máy chủ DNS dự phòng được sử dụng khi máy chủ DNS ngược tuyến không phản hồi. Cú pháp tương tự như trong trường ngược dòng chính ở trên.",
"fallback_dns_placeholder": "Nhập một máy chủ DNS dự phòng trên mỗi dòng",
"local_ptr_title": "Máy chủ DNS riêng tư",
"local_ptr_desc": "Máy chủ DNS hoặc các máy chủ mà AdGuard Home sẽ sử dụng cho các truy vấn về tài nguyên được phân phối cục bộ. Ví dụ: máy chủ này sẽ được sử dụng để phân giải tên máy khách của máy khách cho các máy khách có địa chỉ IP riêng. Nếu không được cài đặt, AdGuard Home sẽ tự động sử dụng trình phân giải DNS mặc định của bạn.",
"local_ptr_desc": "Máy chủ DNS được AdGuard Home sử dụng cho các yêu cầu PTR, SOA và NS riêng tư. Một yêu cầu được coi là riêng tư nếu nó yêu cầu một miền ARPA chứa một mạng con trong phạm vi IP riêng tư (chẳng hạn như \"192.168.12.34\") và đến từ một máy khách có địa chỉ IP riêng. Nếu không được thiết lập, các trình phân giải DNS mặc định của hệ điều hành của bạn sẽ được sử dụng, ngoại trừ các địa chỉ IP của AdGuard Home.",
"local_ptr_default_resolver": "Theo mặc định, AdGuard Home sử dụng các hệ thống phân giải tên miền ngược sau: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home không thể xác định hệ thống phân giải tên miền ngược riêng phù hợp cho hệ thống này.",
"local_ptr_placeholder": "Nhập một địa chỉ IP trên mỗi dòng",
"resolve_clients_title": "Kích hoạt cho phép phân giải ngược về địa chỉ IP của máy khách",
"resolve_clients_desc": "Nếu được bật, AdGuard Home sẽ cố gắng phân giải ngược lại địa chỉ IP của khách hàng thành tên máy chủ của họ bằng cách gửi các truy vấn PTR tới trình phân giải tương ứng (máy chủ DNS riêng cho máy khách cục bộ, máy chủ ngược dòng cho máy khách có địa chỉ IP công cộng).",
"use_private_ptr_resolvers_title": "Sử dụng trình rDNS riêng tư",
"use_private_ptr_resolvers_desc": "Thực hiện tra cứu ngược DNS cho các địa chỉ được phân phối cục bộ bằng cách sử dụng các máy chủ nguồn. Nếu bị vô hiệu hóa, AdGuard Home sẽ phản hồi với NXDOMAIN cho tất cả các yêu cầu PTR ngoại trừ các ứng dụng khách được biết đến bởi DHCP, / etc / hosts, v. v.",
"use_private_ptr_resolvers_desc": "Giải quyết các yêu cầu PTR, SOA và NS cho các miền ARPA chứa địa chỉ IP riêng thông qua máy chủ thượng nguồn riêng, DHCP, /etc/hosts, v. v. Nếu bị vô hiệu hóa, AdGuard Home sẽ phản hồi tất cả các yêu cầu đó bằng NXDOMAIN.",
"check_dhcp_servers": "Kiểm tra máy chủ DHCP",
"save_config": "Lưu thiết lập",
"enabled_dhcp": "Máy chủ DHCP đã kích hoạt",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Sử dụng dịch vụ quản lý của phụ huynh AdGuard",
"use_adguard_parental_hint": "AdGuard Home sẽ kiểm tra nếu tên miền chứa từ khoá người lớn. Tính năng sử dụng API thân thiện với quyền riêng tư tương tự với dịch vụ bảo vệ duyệt web",
"enforce_safe_search": "Bắt buộc tìm kiếm an toàn",
"enforce_save_search_hint": "AdGuard Home thể bắt buộc tìm kiếm an toàn với các dịch vụ tìm kiếm: Google, Youtube, Bing, Yandex.",
"enforce_save_search_hint": "AdGuard Home sẽ thực thi tìm kiếm an toàn trong các công cụ tìm kiếm sau: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Không có máy chủ nào được liệt kê",
"general_settings": "Cài đặt chung",
"dns_settings": "Cài đặt DNS",
@@ -425,6 +425,9 @@
"encryption_hostnames": "Tên máy chủ",
"encryption_reset": "Bạn có chắc chắn muốn đặt lại cài đặt mã hóa?",
"encryption_warning": "Cảnh báo",
"encryption_plain_dns_enable": "Kích hoạt DNS đơn giản",
"encryption_plain_dns_desc": "DNS đơn giản được bật theo mặc định. Bạn có thể vô hiệu hóa nó để buộc tất cả các thiết bị sử dụng DNS được mã hóa. Để thực hiện việc này, bạn phải kích hoạt ít nhất một giao thức DNS được mã hóa",
"encryption_plain_dns_error": "Để tắt DNS đơn giản, hãy bật ít nhất một giao thức DNS được mã hóa",
"topline_expiring_certificate": "Chứng chỉ SSL của bạn sắp hết hạn. Cập nhật <0>Cài đặt mã hóa</0>.",
"topline_expired_certificate": "Chứng chỉ SSL của bạn đã hết hạn. Cập nhật <0>Cài đặt mã hóa</0>.",
"form_error_port_range": "Nhập giá trị cổng trong phạm vi 80-65535",
@@ -675,7 +678,7 @@
"use_saved_key": "Sử dụng khóa đã lưu trước đó",
"parental_control": "Quản lý của phụ huynh",
"safe_browsing": "Duyệt web an toàn",
"served_from_cache": "{{value}} <i>(được phục vụ từ bộ nhớ cache)</i>",
"served_from_cache_label": "Được phục vụ từ bộ nhớ đệm",
"form_error_password_length": "Mật khẩu phải dài từ {{min}} đến {{max}} ký tự",
"anonymizer_notification": "<0> Lưu ý:</0> Tính năng ẩn danh IP được bật. Bạn có thể tắt nó trong <1> Cài đặt chung</1>.",
"confirm_dns_cache_clear": "Bạn có chắc chắn muốn xóa bộ đệm ẩn DNS không?",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
"parallel_requests": "并行请求",
"load_balancing": "负载均衡",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
"load_balancing_desc": "一次查询一台上游服务器。<br/>AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
"bootstrap_dns": "Bootstrap DNS 服务器",
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
"fallback_dns_title": "后备 DNS 服务器",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
"enforce_safe_search": "使用安全搜索",
"enforce_save_search_hint": "AdGuard Home 对以下搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Yandex、Pixabay。",
"enforce_save_search_hint": "AdGuard Home 将会在下列搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex、Pixabay。",
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "屏蔽的 TTL 应答",
"blocked_response_ttl_desc": "指定客户端应缓存已过滤响应的秒数",
"form_enter_blocked_response_ttl": "输入拦截的 TTL 应答(秒)",
"upstream_timeout": "上游超时",
"upstream_timeout_desc": "指定等待上游服务器响应的秒数",
"form_enter_upstream_timeout": "输入上游服务器超时时间(以秒为单位)",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -598,7 +601,7 @@
"disable_ipv6": "禁用 IPv6 地址的解析",
"disable_ipv6_desc": "丢弃对 IPv6 地址(类型 AAAA的所有 DNS 查询,并从 HTTPS 响应中删除 IPv6 相关的信息。",
"fastest_addr": "最快的 IP 地址",
"fastest_addr_desc": "查询所有 DNS 服务器并返回所有响应中速度最快的 IP 地址。因 AdGuard Home 必须等待全部 DNS 服务器响应,这会降低 DNS 查询速度,但此举将会在总体上改善连接速度。",
"fastest_addr_desc": "等待<b>所有</b> DNS 服务器的响应,测量每个服务器的 TCP 连接速度,并返回连接速度最快的服务器的 IP 地址。<br/>如果一个或多个上游服务器没有响应,此模式会显著减慢 DNS 查询速度。确保您的上游服务器稳定且上游超时时间短。",
"autofix_warning_text": "若您单击「修复」AdGuard Home 将会配置您的系统以使用 AdGuard Home 的 DNS 服务器。",
"autofix_warning_list": "其将会进行如下工作:<0>停用系统DNSStubListener</0><0>设置DNS服务器地址为127.0.0.1</0><0>将/etc/resolv.conf的符号链接目标替换为/run/systemd/resolv/resolv.conf</0><0>停止DNSStubListener重新加载系统解析服务</0>",
"autofix_warning_result": "因此,默认情况下所有来自系统的 DNS 请求都将由 AdGuard Home 处理。",
@@ -617,6 +620,10 @@
"check_cname": "CNAME: {{cname}}",
"check_reason": "原因:{{reason}}",
"check_service": "服务名称:{{service}}",
"check_hostname": "主机名或域名",
"check_client_id": "客户端标识符ClientID 或 IP 地址)",
"check_enter_client_id": "输入客户端标识符",
"check_dns_record": "选择 DNS 记录类型",
"service_name": "服务名称",
"check_not_found": "未在您的筛选列表中找到",
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",
@@ -649,7 +656,7 @@
"blocklist": "黑名单",
"milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小",
"cache_size_desc": "DNS 缓存大小(单位:字节)。若要关闭缓存,请留空。",
"cache_size_desc": "DNS 缓存大小(单位:字节)。若要禁用缓存,请设置为 0。",
"cache_ttl_min_override": "覆盖最小 TTL 值",
"cache_ttl_max_override": "覆盖最大 TTL 值",
"enter_cache_size": "输入缓存大小(字节)",

View File

@@ -1,12 +1,12 @@
{
"client_settings": "用戶端設定",
"example_upstream_reserved": "<0>特定網域</0>上游;",
"example_upstream_reserved": "<0>特定網域</0>上游;",
"example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;",
"example_upstream_comment": "註解。",
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
"parallel_requests": "並行的請求",
"load_balancing": "負載平衡",
"load_balancing_desc": "一次查詢一台伺服器。AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
"load_balancing_desc": "一次查詢一台上游伺服器。<br/>AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
"bootstrap_dns": "自我啟動BootstrapDNS 伺服器",
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
"fallback_dns_title": "應變 DNS 伺服器",
@@ -20,17 +20,17 @@
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
"resolve_clients_desc": "透過傳送指標PTR查詢到對應的解析器私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
"use_private_ptr_resolvers_desc": "使用私人上游伺服器、DHCP/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域的 PTR、SOA NS 請求。如果禁用AdGuard Home 將對所有此類請求以 NXDOMAIN 回應。",
"use_private_ptr_resolvers_desc": "透過私有上游伺服器、DHCP/etc/hosts 等管道,解析含有私有 IP 位址的 ARPA 網域的 PTR、SOA NS 請求。若停用此功能AdGuard Home 將以 NXDOMAIN 回應所有相關請求。",
"check_dhcp_servers": "檢查動態主機設定協定DHCP伺服器",
"save_config": "儲存配置",
"enabled_dhcp": "動態主機設定協定DHCP伺服器被啟用",
"disabled_dhcp": "動態主機設定協定(DHCP伺服器被禁用",
"disabled_dhcp": "DHCP 伺服器已停用",
"unavailable_dhcp": "DHCP 為不可用的",
"unavailable_dhcp_desc": "AdGuard Home 無法於您的作業系統上執行 DHCP 伺服器",
"dhcp_title": "動態主機設定協定DHCP伺服器實驗性的",
"dhcp_description": "如果您的路由器未提供動態主機設定協定DHCP設定您可使用 AdGuard 自身內建的 DHCP 伺服器。",
"dhcp_enable": "啟用動態主機設定協定DHCP伺服器",
"dhcp_disable": "禁用動態主機設定協定(DHCP伺服器",
"dhcp_disable": "停用 DHCP 伺服器",
"dhcp_not_found": "因為 AdGuard Home 於該網路上未發現任何現行的 DHCP 伺服器啟用內建的動態主機設定協定DHCP伺服器為安全的。然而您應手動地重新檢查那個因為自動的探查目前不予 100 保證。",
"dhcp_found": "於該網路上一個現行的動態主機設定協定DHCP伺服器被發現。啟用內建的 DHCP 伺服器為不安全的。",
"dhcp_leases": "動態主機設定協定DHCP租約",
@@ -112,8 +112,8 @@
"privacy_policy": "隱私政策",
"enable_protection": "啟用防護",
"enabled_protection": "已啟用防護",
"disable_protection": "用防護",
"disabled_protection": "已用防護",
"disable_protection": "用防護",
"disabled_protection": "已用防護",
"refresh_statics": "重新整理統計資料",
"dns_query": "DNS 查詢",
"blocked_by": "<0>被過濾器封鎖</0>",
@@ -124,8 +124,8 @@
"for_last_hours_plural": "在過去的 {{count}} 小時內",
"for_last_days": "在最近的 {{count}} 日內",
"for_last_days_plural": "在最近的 {{count}} 日內",
"stats_disabled": "統計資料已被禁用。您可從<0>設定頁面</0>中打開它。",
"stats_disabled_short": "該統計資料已被禁用",
"stats_disabled": "統計功能目前停用中,請至<0>設定頁面</0>重新開啟。",
"stats_disabled_short": "該統計資料已用",
"no_domains_found": "無已發現之網域",
"requests_count": "請求總數",
"top_blocked_domains": "熱門已封鎖的網域",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面API。",
"enforce_safe_search": "使用安全搜尋",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Yandex 和 Pixabay 中強制執行安全搜尋。",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex 和 Pixabay 中強制執行安全搜尋。",
"no_servers_specified": "無已明確指定的伺服器",
"general_settings": "一般設定",
"dns_settings": "DNS 設定",
@@ -172,13 +172,13 @@
"upstreams": "上游",
"upstream": "上游伺服器",
"apply_btn": "套用",
"disabled_filtering_toast": "已用過濾",
"disabled_filtering_toast": "已用過濾",
"enabled_filtering_toast": "已啟用過濾",
"disabled_safe_browsing_toast": "已用安全瀏覽",
"disabled_safe_browsing_toast": "已用安全瀏覽",
"enabled_safe_browsing_toast": "已啟用安全瀏覽",
"disabled_parental_toast": "已用家長控制",
"disabled_parental_toast": "已用家長控制",
"enabled_parental_toast": "已啟用家長控制",
"disabled_safe_search_toast": "已用安全搜尋",
"disabled_safe_search_toast": "已用安全搜尋",
"enabled_save_search_toast": "已啟用安全搜尋",
"updated_save_search_toast": "安全搜尋設定更新成功",
"enabled_table_header": "已啟用",
@@ -275,7 +275,7 @@
"query_log_retention": "查詢記錄保留時間",
"query_log_enable": "啟用記錄",
"query_log_configuration": "記錄配置",
"query_log_disabled": "查詢記錄被禁用並可在<0>設定</0>中被配置",
"query_log_disabled": "查詢記錄功能已停用,請至「<0>設定</0>」調整",
"query_log_strict_search": "使用雙引號於嚴謹的搜尋",
"query_log_retention_confirm": "您確定要更改記錄檔保存期限嗎?如果您縮短期限部分資料可能將會遺失",
"anonymize_client_ip": "將用戶端 IP 匿名",
@@ -294,6 +294,9 @@
"blocked_response_ttl": "已封鎖的回應之存活時間TTL",
"blocked_response_ttl_desc": "對用戶端應快取受過濾的回應,指定多少秒數",
"form_enter_blocked_response_ttl": "請輸入已封鎖回應的存活時間(秒)",
"upstream_timeout": "上游超時",
"upstream_timeout_desc": "指定等待來自此上游伺服器回應的秒數",
"form_enter_upstream_timeout": "輸入上游伺服器超時時間(以秒為單位)",
"dnscrypt": "DNSCrypt",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
@@ -398,7 +401,7 @@
"encryption_config_saved": "加密配置被儲存",
"encryption_server": "伺服器名稱",
"encryption_server_enter": "輸入您的域名",
"encryption_server_desc": "如果設定AdGuard Home 檢測用戶端 IDs回覆 DDR 查詢,並執行額外的連線驗證。如果未設定,這些功能被禁用。必須與在該憑證裡的 DNS 名稱其中之一相符。",
"encryption_server_desc": "如果設定AdGuard Home 會偵測 ClientID、回應 DDR 查詢,並執行其他連線驗證。如果未設定,則會停用這些功能。必須符合憑證中的一個 DNS 名稱。",
"encryption_redirect": "自動地重新導向到 HTTPS",
"encryption_redirect_desc": "如果被勾選AdGuard Home 將自動地重新導向您從 HTTP 到 HTTPS 位址。",
"encryption_https": "HTTPS 連接埠",
@@ -426,8 +429,8 @@
"encryption_reset": "您確定您想要重置加密設定嗎?",
"encryption_warning": "警告",
"encryption_plain_dns_enable": "啟用一般的 DNS",
"encryption_plain_dns_desc": "預設情況下啟用一般 DNS。使用者可以用它強制所有裝置使用一般的 DNS。為此,必須至少啟用一個一般的 DNS 協定",
"encryption_plain_dns_error": "要禁用一般 DNS至少啟用一個一般的 DNS 協定",
"encryption_plain_dns_desc": "預設啟用一般 DNS。可以用它強制所有裝置使用加密 DNS。若要這樣做,您必須啟用至少一個加密 DNS 通訊協定",
"encryption_plain_dns_error": "若要停用一般 DNS啟用至少一個加密 DNS 通訊協定",
"topline_expiring_certificate": "您的安全通訊端層SSL憑證即將到期。更新<0>加密設定</0>。",
"topline_expired_certificate": "您的安全通訊端層SSL憑證為已到期的。更新<0>加密設定</0>。",
"form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼",
@@ -479,7 +482,7 @@
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
"access_title": "存取設定",
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
"access_allowed_title": "允許的用戶端",
"access_allowed_title": "允許的用戶端",
"access_allowed_desc": "無類別網域間路由CIDRs、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目AdGuard Home 將接受僅來自這些用戶端的請求。",
"access_disallowed_title": "未被允許的用戶端",
"access_disallowed_desc": "無類別網域間路由CIDRs、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目AdGuard Home 將排除來自這些用戶端的請求。如果在已允許的用戶端中有項目,此欄位被忽略。",
@@ -560,7 +563,7 @@
"statistics_retention_confirm": "您確定您想要更改統計資料保留嗎?如果您減少該間隔值,某些資料將被丟失",
"statistics_cleared": "統計資料被成功地清除",
"statistics_enable": "啟用統計資料",
"ignore_domains": "忽略的網域(換行分隔)",
"ignore_domains": "忽略的網域(換行分隔)",
"ignore_domains_title": "被忽略的網域",
"ignore_domains_desc_stats": "符合這些規則的查詢不會被記錄在統計資料中",
"ignore_domains_desc_query": "符合這些規則的查詢不會被寫入查詢記錄中",
@@ -569,7 +572,7 @@
"filters_configuration": "過濾器配置",
"filters_enable": "啟用過濾器",
"filters_interval": "過濾器更新間隔",
"disabled": "已用",
"disabled": "已用",
"username_label": "使用者名稱",
"username_placeholder": "輸入使用者名稱",
"password_label": "密碼",
@@ -595,10 +598,10 @@
"rewrite_domain_name": "域名新增一筆正規名稱CNAME記錄",
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
"disable_ipv6": "用 IPv6 位址解析",
"disable_ipv6": "用 IPv6 位址解析",
"disable_ipv6_desc": "停止所有對於 IPv6 位址(類型 AAAA的 DNS 查詢,並從 HTTPS 回應中移除 IPv6 的提示。",
"fastest_addr": "最快的 IP 位址",
"fastest_addr_desc": "查詢所有的 DNS 伺服器並返回在所有的回應之中最快的 IP 位址。因為 AdGuard Home 必須等待來自所有的 DNS 伺服器回應,這使 DNS 查詢變慢,但改善總體的連線。",
"fastest_addr_desc": "等待<b>所有</b> DNS 伺服器的回應,測量每個伺服器的 TCP 連線速度,並返回連線速度最快的伺服器的 IP 位址。<br/>如果一個或多個上游伺服器沒有回應,此模式會顯著減慢 DNS 查詢速度。確保您的上游伺服器穩定且上游超時時間短。",
"autofix_warning_text": "如果您點擊\"修復\"AdGuard Home 將配置您的系統使用 AdGuard Home DNS 伺服器。",
"autofix_warning_list": "它將執行這些任務:<0>撤銷系統 DNSStubListener</0> <0>設定 DNS 伺服器位址為 127.0.0.1</0> <0>用 /run/systemd/resolve/resolv.conf 取代 /etc/resolv.conf 的符號連結目標</0> <0>停止 DNSStubListener重新載入 systemd-resolved 服務)</0>",
"autofix_warning_result": "因此,預設下,來自您的系統之所有的 DNS 請求將被 AdGuard Home 處理。",
@@ -617,6 +620,10 @@
"check_cname": "正規名稱CNAME{{cname}}",
"check_reason": "原因:{{reason}}",
"check_service": "服務名稱:{{service}}",
"check_hostname": "主機名稱或域名",
"check_client_id": "用戶端識別碼ClientID 或 IP 位址)",
"check_enter_client_id": "輸入用戶識別碼",
"check_dns_record": "選擇 DNS 記錄類型",
"service_name": "服務名稱",
"check_not_found": "未在您的過濾器清單中被找到",
"client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?",
@@ -637,7 +644,7 @@
"validated_with_dnssec": "已用網域名稱系統安全性擴充功能DNSSEC驗證",
"all_queries": "所有的查詢",
"show_blocked_responses": "已封鎖的",
"show_whitelisted_responses": "允許的",
"show_whitelisted_responses": "允許的",
"show_processed_responses": "已處理的",
"blocked_safebrowsing": "被安全瀏覽封鎖",
"blocked_adult_websites": "被家長控制封鎖",
@@ -649,7 +656,7 @@
"blocklist": "封鎖清單",
"milliseconds_abbreviation": "ms",
"cache_size": "快取大小",
"cache_size_desc": "DNS 快取大小(位元組)。要禁用快取,留空。",
"cache_size_desc": "DNS 快取大小(位元組)。若要停用快取,請設為 0。",
"cache_ttl_min_override": "覆寫最小的存活時間TTL",
"cache_ttl_max_override": "覆寫最大的存活時間TTL",
"enter_cache_size": "輸入快取大小(位元組)",
@@ -673,14 +680,14 @@
"click_to_view_queries": "點擊以檢視查詢",
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。",
"adg_will_drop_dns_queries": "AdGuard Home 將持續排除來自此用戶端之所有的 DNS 查詢。",
"filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在允許的用戶端的清單之外。",
"last_rule_in_allowlist": "因為排除 \"{{disallowed_rule}}\" 規則將禁用\"已允許的用戶端\"清單,無法不允許此用戶端。",
"filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在允許的用戶端的清單之外。",
"last_rule_in_allowlist": "無法禁止此用戶端,因為排除規則 \"{{disallowed_rule}}\" 會停用「允許的用戶端」清單。",
"use_saved_key": "使用該先前已儲存的金鑰",
"parental_control": "家長控制",
"safe_browsing": "安全瀏覽",
"served_from_cache_label": "從快取中",
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
"anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
"anonymizer_notification": "<0>注意:</0>IP 匿名功能已開啟。您可在<1>一般設定</1>中關閉。",
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",
"cache_cleared": "DNS 快取被成功地清除",
"clear_cache": "清除快取",
@@ -695,14 +702,14 @@
"disable_for_hours": "{{count}} 小時",
"disable_for_hours_plural": "{{count}} 小時",
"disable_until_tomorrow": "直到明天",
"disable_notify_for_seconds": "計 {{count}} 秒用防護",
"disable_notify_for_seconds_plural": "計 {{count}} 秒用防護",
"disable_notify_for_minutes": "計 {{count}} 分鐘用防護",
"disable_notify_for_minutes_plural": "計 {{count}} 分鐘用防護",
"disable_notify_for_hours": "計 {{count}} 小時用防護",
"disable_notify_for_hours_plural": "計 {{count}} 小時用防護",
"disable_notify_until_tomorrow": "用防護直到明天",
"enable_protection_timer": "防護將於 {{time}} 啟用",
"disable_notify_for_seconds": "計 {{count}} 秒用防護",
"disable_notify_for_seconds_plural": "計 {{count}} 秒用防護",
"disable_notify_for_minutes": "計 {{count}} 分鐘用防護",
"disable_notify_for_minutes_plural": "計 {{count}} 分鐘用防護",
"disable_notify_for_hours": "計 {{count}} 小時用防護",
"disable_notify_for_hours_plural": "計 {{count}} 小時用防護",
"disable_notify_until_tomorrow": "用防護直到明天",
"enable_protection_timer": "防護將於 {{time}} 啟用",
"custom_retention_input": "輸入保留時間(小時)",
"custom_rotation_input": "輸入旋轉時間(小時)",
"protection_section_label": "防護",

View File

@@ -1,3 +1,5 @@
import { describe, expect, test, afterEach, vi, beforeEach, it } from 'vitest';
import { sortIp, countClientsStatistics, findAddressType, subnetMaskToBitMask } from '../helpers/helpers';
import { ADDRESS_TYPES } from '../helpers/constants';
@@ -259,7 +261,7 @@ describe('sortIp', () => {
const originalWarn = console.warn;
beforeEach(() => {
console.warn = jest.fn();
console.warn = vi.fn();
});
afterEach(() => {
@@ -347,15 +349,15 @@ describe('sortIp', () => {
});
describe('findAddressType', () => {
describe('ip', () => {
it('should return IP type for IP addresses', () => {
expect(findAddressType('127.0.0.1')).toStrictEqual(ADDRESS_TYPES.IP);
});
describe('cidr', () => {
it('should return CIDR type for CIDR addresses', () => {
expect(findAddressType('127.0.0.1/8')).toStrictEqual(ADDRESS_TYPES.CIDR);
});
describe('mac', () => {
it('should return UNKNOWN type for MAC addresses', () => {
expect(findAddressType('00:1B:44:11:3A:B7')).toStrictEqual(ADDRESS_TYPES.UNKNOWN);
});
});

View File

@@ -19,7 +19,6 @@ import {
CHECK_TIMEOUT,
STATUS_RESPONSE,
SETTINGS_NAMES,
FORM_NAME,
MANUAL_UPDATE_LINK,
DISABLE_PROTECTION_TIMINGS,
} from '../helpers/constants';
@@ -424,10 +423,9 @@ export const testUpstream =
}
};
export const testUpstreamWithFormValues = () => async (dispatch: any, getState: any) => {
export const testUpstreamWithFormValues = (formValues: any) => async (dispatch: any, getState: any) => {
const { upstream_dns_file } = getState().dnsConfig;
const { bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns } =
getState().form[FORM_NAME.UPSTREAM].values;
const { bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns } = formValues;
return dispatch(
testUpstream(
@@ -512,16 +510,15 @@ export const findActiveDhcpRequest = createAction('FIND_ACTIVE_DHCP_REQUEST');
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
export const findActiveDhcp = (name: any) => async (dispatch: any, getState: any) => {
export const findActiveDhcp = (selectedInterface: any) => async (dispatch: any, getState: any) => {
dispatch(findActiveDhcpRequest());
try {
const req = {
interface: name,
interface: selectedInterface,
};
const activeDhcp = await apiClient.findActiveDhcp(req);
dispatch(findActiveDhcpSuccess(activeDhcp));
const { check, interface_name, interfaces } = getState().dhcp;
const selectedInterface = getState().form[FORM_NAME.DHCP_INTERFACES].values.interface_name;
const v4 = check?.v4 ?? { static_ip: {}, other_server: {} };
const v6 = check?.v6 ?? { other_server: {} };

View File

@@ -27,7 +27,8 @@ export const setAllSettingsSuccess = createAction('SET_ALL_SETTINGS_SUCCESS');
export const setAllSettings = (values: any) => async (dispatch: any) => {
dispatch(setAllSettingsRequest());
try {
const { confirm_password, ...config } = values;
const config = { ...values };
delete config.confirm_password;
await apiClient.setAllSettings(config);
dispatch(setAllSettingsSuccess());
@@ -48,7 +49,11 @@ export const checkConfig = (values: any) => async (dispatch: any) => {
dispatch(checkConfigRequest());
try {
const check = await apiClient.checkConfig(values);
dispatch(checkConfigSuccess(check));
dispatch(checkConfigSuccess({
web: { ...values.web, ...check.web },
dns: { ...values.dns, ...check.dns },
static_ip: check.static_ip,
}));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(checkConfigFailure());

View File

@@ -3,8 +3,9 @@ import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import { normalizeLogs } from '../helpers/helpers';
import { DEFAULT_LOGS_FILTER, FORM_NAME, QUERY_LOGS_PAGE_LIMIT } from '../helpers/constants';
import { DEFAULT_LOGS_FILTER, QUERY_LOGS_PAGE_LIMIT } from '../helpers/constants';
import { addErrorToast, addSuccessToast } from './toasts';
import { SearchFormValues } from '../components/Logs';
const getLogsWithParams = async (config: any) => {
const { older_than, filter, ...values } = config;
@@ -27,12 +28,10 @@ export const getAdditionalLogsRequest = createAction('GET_ADDITIONAL_LOGS_REQUES
export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE');
export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS');
const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getState: any, total?: any) => {
const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, currentQuery?: string, total?: any) => {
const { logs, oldest } = data;
const totalData = total || { logs };
const queryForm = getState().form[FORM_NAME.LOGS_FILTER];
const currentQuery = queryForm && queryForm.values.search;
const previousQuery = filter?.search;
const isQueryTheSame =
typeof previousQuery === 'string' && typeof currentQuery === 'string' && previousQuery === currentQuery;
@@ -51,7 +50,7 @@ const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getStat
filter,
});
if (additionalLogs.oldest.length > 0) {
return await shortPollQueryLogs(additionalLogs, filter, dispatch, getState, {
return await shortPollQueryLogs(additionalLogs, filter, dispatch, currentQuery, {
logs: [...totalData.logs, ...additionalLogs.logs],
oldest: additionalLogs.oldest,
});
@@ -91,17 +90,18 @@ export const updateLogs = () => async (dispatch: any, getState: any) => {
}
};
export const getLogs = () => async (dispatch: any, getState: any) => {
export const getLogs = (currentQuery?: string) => async (dispatch: any, getState: any) => {
dispatch(getLogsRequest());
try {
const { isFiltered, filter, oldest } = getState().queryLogs;
const data = await getLogsWithParams({
older_than: oldest,
filter,
});
if (isFiltered) {
const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState);
const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery);
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
dispatch(getLogsSuccess(updatedData));
} else {
@@ -122,13 +122,13 @@ export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
* @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object
* @returns function
*/
export const setLogsFilter = (filter: any) => setLogsFilterRequest(filter);
export const setLogsFilter = (filter: SearchFormValues) => setLogsFilterRequest(filter);
export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST');
export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE');
export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS');
export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: any) => {
export const setFilteredLogs = (filter?: SearchFormValues) => async (dispatch: any) => {
dispatch(setFilteredLogsRequest());
try {
const data = await getLogsWithParams({
@@ -136,7 +136,9 @@ export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState:
filter,
});
const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState);
const currentQuery = filter?.search;
const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery);
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
dispatch(

View File

@@ -46,7 +46,7 @@ export const getStats = () => async (dispatch: any) => {
const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
const clients = await apiClient.findClients(clientsParams);
const clients = await apiClient.searchClients(clientsParams);
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
const normalizedStats = {

View File

@@ -415,7 +415,7 @@ class Api {
// Per-client settings
GET_CLIENTS = { path: 'clients', method: 'GET' };
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
SEARCH_CLIENTS = { path: 'clients/search', method: 'POST' };
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
@@ -453,11 +453,12 @@ class Api {
return this.makeRequest(path, method, parameters);
}
findClients(params: any) {
const { path, method } = this.FIND_CLIENTS;
const url = getPathWithQueryString(path, params);
return this.makeRequest(url, method);
searchClients(config: any) {
const { path, method } = this.SEARCH_CLIENTS;
const parameters = {
data: config,
};
return this.makeRequest(path, method, parameters);
}
// DNS access settings

View File

@@ -23,7 +23,7 @@ interface RowProps {
const Row = ({ label, count, response_status, tooltipTitle, translationComponents }: RowProps) => {
const content = response_status ? (
<LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
<LogsSearchLink response_status={response_status}>{count}</LogsSearchLink>
) : (
count
);
@@ -77,16 +77,16 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
? t('number_of_dns_query_hours', { count: msToHours(interval) })
: t('number_of_dns_query_days', { count: msToDays(interval) });
const rows = [
const rows: RowProps[] = [
{
label: 'dns_query',
count: numDnsQueries.toString(),
count: formatNumber(numDnsQueries),
tooltipTitle: dnsQueryTooltip,
response_status: RESPONSE_FILTER.ALL.QUERY,
},
{
label: 'blocked_by',
count: numBlockedFiltering.toString(),
count: formatNumber(numBlockedFiltering),
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
response_status: RESPONSE_FILTER.BLOCKED.QUERY,
@@ -98,19 +98,19 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
},
{
label: 'stats_malware_phishing',
count: numReplacedSafebrowsing.toString(),
count: formatNumber(numReplacedSafebrowsing),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
},
{
label: 'stats_adult',
count: numReplacedParental.toString(),
count: formatNumber(numReplacedParental),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
},
{
label: 'enforced_save_search',
count: numReplacedSafesearch.toString(),
count: formatNumber(numReplacedSafesearch),
tooltipTitle: 'number_of_dns_query_to_safe_search',
response_status: RESPONSE_FILTER.SAFE_SEARCH.QUERY,
},

View File

@@ -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 (
<div className="logs__row o-hidden">

View File

@@ -154,7 +154,7 @@ const Dashboard = ({
}}
disabled={processingProtection}>
{protectionDisabledDuration
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
? `${t('enable_protection_timer', { time: getRemaningTimeText(protectionDisabledDuration) })}`
: getProtectionBtnText(protectionEnabled)}
</button>

View File

@@ -1,62 +1,112 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { useSelector } from 'react-redux';
import { Controller, useForm } from 'react-hook-form';
import Card from '../../ui/Card';
import { renderInputField } from '../../../helpers/form';
import Info from './Info';
import { FORM_NAME } from '../../../helpers/constants';
import { RootState } from '../../../initialState';
interface CheckProps {
handleSubmit: (...args: unknown[]) => string;
pristine: boolean;
invalid: boolean;
import { RootState } from '../../../initialState';
import { validateRequiredValue } from '../../../helpers/validators';
import { Input } from '../../ui/Controls/Input';
import { DNS_RECORD_TYPES } from '../../../helpers/constants';
import { Select } from '../../ui/Controls/Select';
export type FilteringCheckFormValues = {
name: string;
client?: string;
qtype?: string;
}
const Check = (props: CheckProps) => {
const { pristine, invalid, handleSubmit } = props;
type Props = {
onSubmit?: (data: FilteringCheckFormValues) => void;
};
const Check = ({ onSubmit }: Props) => {
const { t } = useTranslation();
const processingCheck = useSelector((state: RootState) => state.filtering.processingCheck);
const hostname = useSelector((state: RootState) => state.filtering.check.hostname);
const {
control,
handleSubmit,
formState: { isValid },
} = useForm<FilteringCheckFormValues>({
mode: 'onBlur',
defaultValues: {
name: '',
client: '',
qtype: DNS_RECORD_TYPES[0],
},
});
return (
<Card title={t('check_title')} subtitle={t('check_desc')}>
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-12 col-md-6">
<div className="input-group">
<Field
id="name"
name="name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_host')}
/>
<Controller
name="name"
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
label={t('check_hostname')}
data-testid="check_domain_name"
placeholder="example.com"
error={fieldState.error?.message}
/>
)}
/>
<span className="input-group-append">
<button
className="btn btn-success btn-standard btn-large"
type="submit"
onClick={handleSubmit}
disabled={pristine || invalid || processingCheck}>
{t('check')}
</button>
</span>
</div>
<Controller
name="client"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="check_client_id"
label={t('check_client_id')}
placeholder={t('check_enter_client_id')}
error={fieldState.error?.message}
/>
)}
/>
<Controller
name="qtype"
control={control}
render={({ field }) => (
<Select
{...field}
label={t('check_dns_record')}
data-testid="check_dns_record_type"
>
{DNS_RECORD_TYPES.map((type) => (
<option key={type} value={type}>
{type}
</option>
))}
</Select>
)}
/>
<button
className="btn btn-success btn-standard btn-large"
type="submit"
data-testid="check_domain_submit"
disabled={!isValid || processingCheck}
>
{t('check')}
</button>
{hostname && (
<>
<hr />
<Info />
</>
)}
@@ -67,4 +117,4 @@ const Check = (props: CheckProps) => {
);
};
export default reduxForm({ form: FORM_NAME.DOMAIN_CHECK })(Check);
export default Check;

View File

@@ -7,7 +7,7 @@ import PageTitle from '../ui/PageTitle';
import Examples from './Examples';
import Check from './Check';
import Check, { FilteringCheckFormValues } from './Check';
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
@@ -48,8 +48,18 @@ class CustomRules extends Component<CustomRulesProps> {
this.props.setRules(this.props.filtering.userRules);
};
handleCheck = (values: any) => {
this.props.checkHost(values);
handleCheck = (values: FilteringCheckFormValues) => {
const params: FilteringCheckFormValues = { name: values.name };
if (values.client) {
params.client = values.client;
}
if (values.qtype) {
params.qtype = values.qtype;
}
this.props.checkHost(params);
};
onScroll = (e: any) => syncScroll(e, this.ref);
@@ -68,6 +78,7 @@ class CustomRules extends Component<CustomRulesProps> {
<form onSubmit={this.handleSubmit}>
<div className="text-edit-container mb-4">
<textarea
data-testid="custom_rule_textarea"
className="form-control font-monospace text-input"
value={userRules}
onChange={this.handleChange}
@@ -81,6 +92,7 @@ class CustomRules extends Component<CustomRulesProps> {
<div className="card-actions">
<button
data-testid="apply_custom_rule"
className="btn btn-success btn-standard btn-large"
type="submit"
onClick={this.handleSubmit}>

View File

@@ -0,0 +1,94 @@
import React from 'react';
import classNames from 'classnames';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { Checkbox } from '../ui/Controls/Checkbox';
const getIconsData = (homepage: string, source: string) => [
{
iconName: 'dashboard',
href: homepage,
className: 'ml-1',
},
{
iconName: 'info',
href: source,
},
];
const renderIcons = (iconsData: { iconName: string; href: string; className?: string }[]) =>
iconsData.map(({ iconName, href, className = '' }) => (
<a
key={iconName}
href={href}
target="_blank"
rel="noopener noreferrer"
className={classNames('d-flex align-items-center', className)}>
<svg className="icon icon--15 mr-1 icon--gray">
<use xlinkHref={`#${iconName}`} />
</svg>
</a>
));
type Filter = {
categoryId: string;
homepage: string;
source: string;
name: string;
};
type Category = {
name: string;
description: string;
};
type Props = {
categories: Record<string, Category>;
filters: Record<string, Filter>;
selectedSources: Record<string, boolean>;
};
export const FiltersList = ({ categories, filters, selectedSources }: Props) => {
const { t } = useTranslation();
const { control } = useFormContext();
return (
<>
{Object.entries(categories).map(([categoryId, category]) => {
const categoryFilters = Object.entries(filters)
.filter(([, filter]) => filter.categoryId === categoryId)
.map(([key, filter]) => ({ ...filter, id: key }));
return (
<div key={category.name} className="modal-body__item">
<h6 className="font-weight-bold mb-1">{t(category.name)}</h6>
<p className="mb-3">{t(category.description)}</p>
{categoryFilters.map((filter) => {
const { homepage, source, name, id } = filter;
const isSelected = selectedSources[source];
const iconsData = getIconsData(homepage, source);
return (
<div key={name} className="d-flex align-items-center pb-1">
<Controller
name={id}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid={`filters_${id}`}
title={name}
disabled={isSelected}
/>
)}
/>
{renderIcons(iconsData)}
</div>
);
})}
</div>
);
})}
</>
);
};

View File

@@ -1,208 +1,152 @@
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import classNames from 'classnames';
import { useForm, Controller, FormProvider } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { validatePath, validateRequiredValue } from '../../helpers/validators';
import { CheckboxField, renderInputField } from '../../helpers/form';
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE, FORM_NAME } from '../../helpers/constants';
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE } from '../../helpers/constants';
import filtersCatalog from '../../helpers/filters/filters';
import { FiltersList } from './FiltersList';
import { Input } from '../ui/Controls/Input';
const getIconsData = (homepage: any, source: any) => [
{
iconName: 'dashboard',
href: homepage,
className: 'ml-1',
},
{
iconName: 'info',
href: source,
},
];
type FormValues = {
enabled: boolean;
name: string;
url: string;
};
const renderIcons = (iconsData: any) =>
iconsData.map(({ iconName, href, className = '' }: any) => (
<a
key={iconName}
href={href}
target="_blank"
rel="noopener noreferrer"
className={classNames('d-flex align-items-center', className)}>
<svg className="icon icon--15 mr-1 icon--gray">
<use xlinkHref={`#${iconName}`} />
</svg>
</a>
));
const defaultValues: FormValues = {
enabled: true,
name: '',
url: '',
};
interface renderCheckboxFieldProps {
// https://redux-form.com/8.3.0/docs/api/field.md/#props
input: {
name: string;
value: string;
checked: boolean;
onChange: (...args: unknown[]) => unknown;
};
disabled: boolean;
}
const renderCheckboxField = (props: renderCheckboxFieldProps) => (
<CheckboxField
{...props}
meta={{ touched: false, error: null }}
input={{
...props.input,
checked: props.disabled || props.input.checked,
}}
/>
);
const renderFilters = ({ categories, filters }: any, selectedSources: any, t: any) =>
Object.keys(categories).map((categoryId) => {
const category = categories[categoryId];
const categoryFilters: any = [];
Object.keys(filters)
.sort()
.forEach((key) => {
const filter = filters[key];
filter.id = key;
if (filter.categoryId === categoryId) {
categoryFilters.push(filter);
}
});
return (
<div key={category.name} className="modal-body__item">
<h6 className="font-weight-bold mb-1">{t(category.name)}</h6>
<p className="mb-3">{t(category.description)}</p>
{categoryFilters.map((filter) => {
const { homepage, source, name } = filter;
const isSelected = Object.prototype.hasOwnProperty.call(selectedSources, source);
const iconsData = getIconsData(homepage, source);
return (
<div key={name} className="d-flex align-items-center pb-1">
<Field
name={filter.id}
type="checkbox"
component={renderCheckboxField}
placeholder={t(name)}
disabled={isSelected}
/>
{renderIcons(iconsData)}
</div>
);
})}
</div>
);
});
interface FormProps {
t: (...args: unknown[]) => string;
closeModal: (...args: unknown[]) => unknown;
handleSubmit: (...args: unknown[]) => string;
type Props = {
closeModal: () => void;
onSubmit: (values: FormValues) => void;
processingAddFilter: boolean;
processingConfigFilter: boolean;
whitelist?: boolean;
modalType: string;
toggleFilteringModal: (...args: unknown[]) => unknown;
selectedSources?: object;
}
toggleFilteringModal: ({ type }: { type?: keyof typeof MODAL_TYPE }) => void;
selectedSources?: Record<string, boolean>;
initialValues?: FormValues;
};
const Form = (props: FormProps) => {
const {
t,
closeModal,
handleSubmit,
processingAddFilter,
processingConfigFilter,
whitelist,
modalType,
toggleFilteringModal,
selectedSources,
} = props;
export const Form = ({
closeModal,
processingAddFilter,
processingConfigFilter,
whitelist,
modalType,
toggleFilteringModal,
selectedSources,
onSubmit,
initialValues,
}: Props) => {
const { t } = useTranslation();
const openModal = (modalType: any, timeout = MODAL_OPEN_TIMEOUT) => {
toggleFilteringModal();
const methods = useForm({
defaultValues: {
...defaultValues,
...initialValues,
},
mode: 'onBlur',
});
const { handleSubmit, control } = methods;
const openModal = (modalType: keyof typeof MODAL_TYPE, timeout = MODAL_OPEN_TIMEOUT) => {
toggleFilteringModal(undefined);
setTimeout(() => toggleFilteringModal({ type: modalType }), timeout);
};
const openFilteringListModal = () => openModal(MODAL_TYPE.CHOOSE_FILTERING_LIST);
const openFilteringListModal = () => openModal('CHOOSE_FILTERING_LIST');
const openAddFiltersModal = () => openModal(MODAL_TYPE.ADD_FILTERS);
const openAddFiltersModal = () => openModal('ADD_FILTERS');
return (
<form onSubmit={handleSubmit}>
<div className="modal-body modal-body--filters">
{modalType === MODAL_TYPE.SELECT_MODAL_TYPE && (
<div className="d-flex justify-content-around">
<button
onClick={openFilteringListModal}
className="btn btn-success btn-standard mr-2 btn-large">
{t('choose_from_list')}
</button>
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="modal-body modal-body--filters">
{modalType === MODAL_TYPE.SELECT_MODAL_TYPE && (
<div className="d-flex justify-content-around">
<button
onClick={openFilteringListModal}
className="btn btn-success btn-standard mr-2 btn-large">
{t('choose_from_list')}
</button>
<button onClick={openAddFiltersModal} className="btn btn-primary btn-standard">
{t('add_custom_list')}
</button>
</div>
)}
{modalType === MODAL_TYPE.CHOOSE_FILTERING_LIST && renderFilters(filtersCatalog, selectedSources, t)}
{modalType !== MODAL_TYPE.CHOOSE_FILTERING_LIST && modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
<>
<div className="form__group">
<Field
id="name"
name="name"
type="text"
component={renderInputField}
className="form-control"
placeholder={t('enter_name_hint')}
normalizeOnBlur={(data: any) => data.trim()}
/>
<button onClick={openAddFiltersModal} className="btn btn-primary btn-standard">
{t('add_custom_list')}
</button>
</div>
)}
{modalType === MODAL_TYPE.CHOOSE_FILTERING_LIST && (
<FiltersList
categories={filtersCatalog.categories}
filters={filtersCatalog.filters}
selectedSources={selectedSources}
/>
)}
{modalType !== MODAL_TYPE.CHOOSE_FILTERING_LIST && modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
<>
<div className="form__group">
<Controller
name="name"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="filters_name"
placeholder={t('enter_name_hint')}
error={fieldState.error?.message}
trimOnBlur
/>
)}
/>
</div>
<div className="form__group">
<Field
id="url"
name="url"
type="text"
component={renderInputField}
className="form-control"
placeholder={t('enter_url_or_path_hint')}
validate={[validateRequiredValue, validatePath]}
normalizeOnBlur={(data: any) => data.trim()}
/>
</div>
<div className="form__group">
<Controller
name="url"
control={control}
rules={{ validate: { validateRequiredValue, validatePath } }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="filters_url"
placeholder={t('enter_url_or_path_hint')}
error={fieldState.error?.message}
trimOnBlur
/>
)}
/>
</div>
<div className="form__description">
{whitelist ? t('enter_valid_allowlist') : t('enter_valid_blocklist')}
</div>
</>
)}
</div>
<div className="form__description">
{whitelist ? t('enter_valid_allowlist') : t('enter_valid_blocklist')}
</div>
</>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={closeModal}>
{t('cancel_btn')}
</button>
{modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
<button
type="submit"
className="btn btn-success"
disabled={processingAddFilter || processingConfigFilter}>
{t('save_btn')}
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={closeModal}>
{t('cancel_btn')}
</button>
)}
</div>
</form>
{modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
<button
type="submit"
data-testid="filters_save"
className="btn btn-success"
disabled={processingAddFilter || processingConfigFilter}>
{t('save_btn')}
</button>
)}
</div>
</form>
</FormProvider>
);
};
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.FILTER })])(Form);

View File

@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
import { MODAL_TYPE } from '../../helpers/constants';
import Form from './Form';
import { Form } from './Form';
import '../ui/Modal.css';
import { getMap } from '../../helpers/helpers';
@@ -75,25 +75,15 @@ class Modal extends Component<ModalProps> {
render() {
const {
isOpen,
processingAddFilter,
processingConfigFilter,
handleSubmit,
modalType,
currentFilterData,
whitelist,
toggleFilteringModal,
filters,
t,
filtersCatalog,
} = this.props;

View File

@@ -1,42 +1,69 @@
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { renderInputField } from '../../../helpers/form';
import { validateAnswer, validateDomain, validateRequiredValue } from '../../../helpers/validators';
import { FORM_NAME } from '../../../helpers/constants';
import { Input } from '../../ui/Controls/Input';
interface FormProps {
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
reset: (...args: unknown[]) => string;
toggleRewritesModal: (...args: unknown[]) => unknown;
submitting: boolean;
processingAdd: boolean;
t: (...args: unknown[]) => string;
initialValues?: object;
interface RewriteFormValues {
domain: string;
answer: string;
}
const Form = (props: FormProps) => {
const { t, handleSubmit, reset, pristine, submitting, toggleRewritesModal, processingAdd } = props;
type Props = {
processingAdd: boolean;
currentRewrite?: RewriteFormValues;
toggleRewritesModal: () => void;
onSubmit?: (data: RewriteFormValues) => Promise<void> | void;
};
const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: Props) => {
const { t } = useTranslation();
const {
handleSubmit,
reset,
control,
formState: { isDirty, isSubmitting },
} = useForm<RewriteFormValues>({
mode: 'onBlur',
defaultValues: {
domain: currentRewrite?.domain || '',
answer: currentRewrite?.answer || '',
},
});
const handleFormSubmit = async (data: RewriteFormValues) => {
if (onSubmit) {
await onSubmit(data);
}
};
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="modal-body">
<div className="form__desc form__desc--top">
<Trans>domain_desc</Trans>
</div>
<div className="form__group">
<Field
id="domain"
<Controller
name="domain"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_domain')}
validate={[validateRequiredValue, validateDomain]}
control={control}
rules={{
validate: {
validate: validateDomain,
required: validateRequiredValue,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="rewrites_domain"
placeholder={t('form_domain')}
error={fieldState.error?.message}
/>
)}
/>
</div>
<Trans>examples_title</Trans>:
@@ -44,7 +71,6 @@ const Form = (props: FormProps) => {
<li>
<code>example.org</code> <Trans>example_rewrite_domain</Trans>
</li>
<li>
<code>*.example.org</code> &nbsp;
<span>
@@ -53,14 +79,24 @@ const Form = (props: FormProps) => {
</li>
</ol>
<div className="form__group">
<Field
id="answer"
<Controller
name="answer"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_answer')}
validate={[validateRequiredValue, validateAnswer]}
control={control}
rules={{
validate: {
validate: validateAnswer,
required: validateRequiredValue,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="rewrites_answer"
placeholder={t('form_answer')}
error={fieldState.error?.message}
/>
)}
/>
</div>
</div>
@@ -77,8 +113,9 @@ const Form = (props: FormProps) => {
<div className="btn-list">
<button
type="button"
data-testid="rewrites_cancel"
className="btn btn-secondary btn-standard"
disabled={submitting || processingAdd}
disabled={isSubmitting || processingAdd}
onClick={() => {
reset();
toggleRewritesModal();
@@ -88,8 +125,9 @@ const Form = (props: FormProps) => {
<button
type="submit"
data-testid="rewrites_save"
className="btn btn-success btn-standard"
disabled={submitting || pristine || processingAdd}>
disabled={isSubmitting || !isDirty || processingAdd}>
<Trans>save_btn</Trans>
</button>
</div>
@@ -98,10 +136,4 @@ const Form = (props: FormProps) => {
);
};
export default flow([
withTranslation(),
reduxForm({
form: FORM_NAME.REWRITES,
enableReinitialize: true,
}),
])(Form);
export default Form;

View File

@@ -14,7 +14,7 @@ interface ModalProps {
processingAdd: boolean;
processingDelete: boolean;
modalType: string;
currentRewrite?: object;
currentRewrite?: { answer: string, domain: string; };
}
const Modal = (props: ModalProps) => {
@@ -23,7 +23,6 @@ const Modal = (props: ModalProps) => {
handleSubmit,
toggleRewritesModal,
processingAdd,
processingDelete,
modalType,
currentRewrite,
} = props;
@@ -50,11 +49,10 @@ const Modal = (props: ModalProps) => {
</div>
<Form
initialValues={{ ...currentRewrite }}
onSubmit={handleSubmit}
toggleRewritesModal={toggleRewritesModal}
processingAdd={processingAdd}
processingDelete={processingDelete}
currentRewrite={currentRewrite}
/>
</div>
</ReactModal>

View File

@@ -1,38 +1,55 @@
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { Trans } from 'react-i18next';
import { toggleAllServices } from '../../../helpers/helpers';
import { Controller, useForm } from 'react-hook-form';
import { renderServiceField } from '../../../helpers/form';
import { FORM_NAME } from '../../../helpers/constants';
import { ServiceField } from './ServiceField';
export type BlockedService = {
id: string;
name: string;
icon_svg: string;
};
type FormValues = {
blocked_services: Record<string, boolean>;
};
interface FormProps {
blockedServices: unknown[];
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
change: (...args: unknown[]) => unknown;
submitting: boolean;
initialValues: Record<string, boolean>;
blockedServices: BlockedService[];
onSubmit: (values: FormValues) => void;
processing: boolean;
processingSet: boolean;
t: (...args: unknown[]) => string;
}
const Form = (props: FormProps) => {
const { blockedServices, handleSubmit, change, pristine, submitting, processing, processingSet } = props;
export const Form = ({ initialValues, blockedServices, processing, processingSet, onSubmit }: FormProps) => {
const {
handleSubmit,
control,
setValue,
formState: { isSubmitting },
} = useForm<FormValues>({
mode: 'onBlur',
defaultValues: initialValues,
});
const handleToggleAllServices = async (isSelected: boolean) => {
blockedServices.forEach((service: BlockedService) => setValue(`blocked_services.${service.id}`, isSelected));
};
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form__group">
<div className="row mb-4">
<div className="col-6">
<button
type="button"
data-testid="blocked_services_block_all"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
onClick={() => toggleAllServices(blockedServices, change, true)}>
onClick={() => handleToggleAllServices(true)}>
<Trans>block_all</Trans>
</button>
</div>
@@ -40,24 +57,30 @@ const Form = (props: FormProps) => {
<div className="col-6">
<button
type="button"
data-testid="blocked_services_unblock_all"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
onClick={() => toggleAllServices(blockedServices, change, false)}>
onClick={() => handleToggleAllServices(false)}>
<Trans>unblock_all</Trans>
</button>
</div>
</div>
<div className="services">
{blockedServices.map((service: any) => (
<Field
{blockedServices.map((service: BlockedService) => (
<Controller
key={service.id}
icon={service.icon_svg}
name={`blocked_services.${service.id}`}
type="checkbox"
component={renderServiceField}
placeholder={service.name}
disabled={processing || processingSet}
control={control}
render={({ field }) => (
<ServiceField
{...field}
data-testid={`blocked_services_${service.id}`}
placeholder={service.name}
disabled={processing || processingSet}
icon={service.icon_svg}
/>
)}
/>
))}
</div>
@@ -66,19 +89,12 @@ const Form = (props: FormProps) => {
<div className="btn-list">
<button
type="submit"
data-testid="blocked_services_save"
className="btn btn-success btn-standard btn-large"
disabled={submitting || pristine || processing || processingSet}>
disabled={isSubmitting || processing || processingSet}>
<Trans>save_btn</Trans>
</button>
</div>
</form>
);
};
export default flow([
withTranslation(),
reduxForm({
form: FORM_NAME.SERVICES,
enableReinitialize: true,
}),
])(Form);

View File

@@ -0,0 +1,42 @@
import React from 'react';
import cn from 'classnames';
import { FieldValues, ControllerRenderProps } from 'react-hook-form';
type Props = ControllerRenderProps<FieldValues> & {
placeholder: string;
disabled?: boolean;
className?: string;
icon?: string;
error?: string;
};
export const ServiceField = React.forwardRef<HTMLInputElement, Props>(
({ name, value, onChange, onBlur, placeholder, disabled, className, icon, error, ...rest }, ref) => (
<>
<label className={cn('service custom-switch', className)}>
<input
name={name}
type="checkbox"
className="custom-switch-input"
checked={!!value}
onChange={onChange}
onBlur={onBlur}
ref={ref}
disabled={disabled}
{...rest}
/>
<span className="service__switch custom-switch-indicator"></span>
<span className="service__text" title={placeholder}>
{placeholder}
</span>
{icon && <div dangerouslySetInnerHTML={{ __html: window.atob(icon) }} className="service__icon" />}
</label>
{!disabled && error && <span className="form__message form__message--error">{error}</span>}
</>
),
);
ServiceField.displayName = 'ServiceField';

View File

@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import Form from './Form';
import { Form } from './Form';
import Card from '../../ui/Card';
import { getBlockedServices, getAllBlockedServices, updateBlockedServices } from '../../../actions/services';
@@ -86,7 +86,8 @@ const Services = () => {
<Card
title={t('schedule_services')}
subtitle={t('schedule_services_desc')}
bodyType="card-body box-body--settings">
bodyType="card-body box-body--settings"
>
<ScheduleForm schedule={services.list.schedule} onScheduleSubmit={handleScheduleSubmit} />
</Card>
</>

View File

@@ -59,7 +59,7 @@ const Header = () => {
<div className="header__column">
<div className="header__right">
{!processingProfile && name && (
<a href="control/logout" className="btn btn-sm btn-outline-secondary">
<a href="control/logout" className="btn btn-sm btn-outline-secondary" data-testid="sign_out">
{t('sign_out')}
</a>
)}

View File

@@ -288,7 +288,7 @@ const Row = memo(
);
return (
<div style={style} className={className} onClick={onClick} role="row">
<div style={style} className={className} onClick={onClick} role="row" data-testid="querylog_cell">
<DateCell {...rowProps} />
<DomainCell {...rowProps} />

View File

@@ -1,160 +1,81 @@
import React, { useEffect } from 'react';
import { Field, reduxForm } from 'redux-form';
import { useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import queryString from 'query-string';
import {
DEBOUNCE_FILTER_TIMEOUT,
DEFAULT_LOGS_FILTER,
FORM_NAME,
RESPONSE_FILTER,
RESPONSE_FILTER_QUERIES,
} from '../../../helpers/constants';
import { setLogsFilter } from '../../../actions/queryLogs';
import useDebounce from '../../../helpers/useDebounce';
import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers';
import { getLogsUrlParams } from '../../../helpers/helpers';
import Tooltip from '../../ui/Tooltip';
import { RootState } from '../../../initialState';
import { SearchField } from './SearchField';
import { SearchFormValues } from '..';
interface renderFilterFieldProps {
input: {
value: string;
};
id: string;
onClearInputClick: (...args: unknown[]) => unknown;
type Props = {
className?: string;
placeholder?: string;
type?: string;
disabled?: boolean;
autoComplete?: string;
tooltip?: string;
onKeyDown?: (...args: unknown[]) => unknown;
normalizeOnBlur?: (...args: unknown[]) => unknown;
meta: {
touched?: boolean;
error?: object;
};
}
const renderFilterField = ({
input,
id,
className,
placeholder,
type,
disabled,
autoComplete,
tooltip,
meta: { touched, error },
onClearInputClick,
onKeyDown,
normalizeOnBlur,
}: renderFilterFieldProps) => {
const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur);
return (
<>
<div className="input-group-search input-group-search__icon--magnifier">
<svg className="icons icon--24 icon--gray">
<use xlinkHref="#magnifier" />
</svg>
</div>
<input
{...input}
id={id}
placeholder={placeholder}
type={type}
className={className}
disabled={disabled}
autoComplete={autoComplete}
aria-label={placeholder}
onKeyDown={onKeyDown}
onBlur={onBlur}
/>
<div
className={classNames('input-group-search input-group-search__icon--cross', {
invisible: input.value.length < 1,
})}>
<svg className="icons icon--20 icon--gray" onClick={onClearInputClick}>
<use xlinkHref="#cross" />
</svg>
</div>
<span className="input-group-search input-group-search__icon--tooltip">
<Tooltip content={tooltip} className="tooltip-container">
<svg className="icons icon--20 icon--gray">
<use xlinkHref="#question" />
</svg>
</Tooltip>
</span>
{!disabled && touched && error && <span className="form__message form__message--error">{error}</span>}
</>
);
setIsLoading: (value: boolean) => void;
};
const FORM_NAMES = {
search: 'search',
response_status: 'response_status',
};
interface FiltersFormProps {
className?: string;
responseStatusClass?: string;
change: (...args: unknown[]) => unknown;
setIsLoading?: (...args: unknown[]) => unknown;
}
const Form = (props: FiltersFormProps) => {
const { className = '', responseStatusClass, setIsLoading, change } = props;
export const Form = ({ className, setIsLoading }: Props) => {
const { t } = useTranslation();
const dispatch = useDispatch();
const history = useHistory();
const { response_status, search } = useSelector(
(state: RootState) => state?.form[FORM_NAME.LOGS_FILTER].values,
shallowEqual,
);
const { register, watch, setValue } = useFormContext<SearchFormValues>();
const [debouncedSearch, setDebouncedSearch] = useDebounce(search.trim(), DEBOUNCE_FILTER_TIMEOUT);
const searchValue = watch('search');
const responseStatusValue = watch('response_status');
const [debouncedSearch, setDebouncedSearch] = useDebounce(searchValue.trim(), DEBOUNCE_FILTER_TIMEOUT);
useEffect(() => {
dispatch(
setLogsFilter({
response_status,
response_status: responseStatusValue,
search: debouncedSearch,
}),
);
history.replace(`${getLogsUrlParams(debouncedSearch, response_status)}`);
}, [response_status, debouncedSearch]);
history.replace(`${getLogsUrlParams(debouncedSearch, responseStatusValue)}`);
}, [responseStatusValue, debouncedSearch]);
if (response_status && !(response_status in RESPONSE_FILTER_QUERIES)) {
change(FORM_NAMES.response_status, DEFAULT_LOGS_FILTER[FORM_NAMES.response_status]);
}
useEffect(() => {
if (responseStatusValue && !(responseStatusValue in RESPONSE_FILTER_QUERIES)) {
setValue('response_status', DEFAULT_LOGS_FILTER.response_status);
}
}, [responseStatusValue, setValue]);
useEffect(() => {
const { search: searchUrlParam } = queryString.parse(history.location.search);
if (searchUrlParam !== searchValue) {
setValue('search', searchUrlParam ? searchUrlParam.toString() : '');
}
}, [history.location.search]);
const onInputClear = async () => {
setIsLoading(true);
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
history.push(getLogsUrlParams(DEFAULT_LOGS_FILTER.search, responseStatusValue));
setIsLoading(false);
};
const onEnterPress = (e: any) => {
const onEnterPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
setDebouncedSearch(search);
setDebouncedSearch(searchValue);
}
};
const normalizeOnBlur = (data: any) => data.trim();
return (
<form
className="d-flex flex-wrap form-control--container"
@@ -162,40 +83,29 @@ const Form = (props: FiltersFormProps) => {
e.preventDefault();
}}>
<div className="field__search">
<Field
id={FORM_NAMES.search}
name={FORM_NAMES.search}
component={renderFilterField}
type="text"
className={classNames('form-control form-control--search form-control--transparent', className)}
<SearchField
data-testid="querylog_search"
value={searchValue}
handleChange={(val) => setValue('search', val)}
onKeyDown={onEnterPress}
onClear={onInputClear}
placeholder={t('domain_or_client')}
tooltip={t('query_log_strict_search')}
onClearInputClick={onInputClear}
onKeyDown={onEnterPress}
normalizeOnBlur={normalizeOnBlur}
className={classNames('form-control form-control--search form-control--transparent', className)}
/>
</div>
<div className="field__select">
<Field
name={FORM_NAMES.response_status}
component="select"
className={classNames(
'form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent',
responseStatusClass,
)}>
<select
{...register('response_status')}
className="form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent d-sm-block">
{Object.values(RESPONSE_FILTER).map(({ QUERY, LABEL, disabled }: any) => (
<option key={LABEL} value={QUERY} disabled={disabled}>
{t(LABEL)}
</option>
))}
</Field>
</select>
</div>
</form>
);
};
export default reduxForm({
form: FORM_NAME.LOGS_FILTER,
enableReinitialize: true,
})(Form);

View File

@@ -0,0 +1,62 @@
import React, { ComponentProps } from 'react';
import Tooltip from '../../ui/Tooltip';
interface Props extends ComponentProps<'input'> {
handleChange: (newValue: string) => void;
onClear: () => void;
tooltip?: string;
}
export const SearchField = ({
handleChange,
onClear,
value,
tooltip,
className,
...rest
}: Props) => {
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
handleChange(e.target.value);
};
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
e.target.value = e.target.value.trim();
handleChange(e.target.value)
}
return (
<>
<div className="input-group-search input-group-search__icon--magnifier">
<svg className="icons icon--24 icon--gray">
<use xlinkHref="#magnifier" />
</svg>
</div>
<input
className={className}
value={value}
onChange={handleInputChange}
onBlur={handleBlur}
{...rest}
/>
{typeof value === 'string' && value.length > 0 && (
<div
className="input-group-search input-group-search__icon--cross"
onClick={onClear}
>
<svg className="icons icon--20 icon--gray">
<use xlinkHref="#cross" />
</svg>
</div>
)}
{tooltip && (
<span className="input-group-search input-group-search__icon--tooltip">
<Tooltip content={tooltip} className="tooltip-container">
<svg className="icons icon--20 icon--gray">
<use xlinkHref="#question" />
</svg>
</Tooltip>
</span>
)}
</>
);
};

View File

@@ -2,17 +2,16 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import Form from './Form';
import { Form } from './Form';
import { refreshFilteredLogs } from '../../../actions/queryLogs';
import { addSuccessToast } from '../../../actions/toasts';
interface FiltersProps {
filter: object;
processingGetLogs: boolean;
setIsLoading: (...args: unknown[]) => unknown;
}
const Filters = ({ filter, setIsLoading }: FiltersProps) => {
const Filters = ({ setIsLoading }: FiltersProps) => {
const { t } = useTranslation();
const dispatch = useDispatch();
@@ -38,11 +37,8 @@ const Filters = ({ filter, setIsLoading }: FiltersProps) => {
</svg>
</button>
</h1>
<Form
// responseStatusClass="d-sm-block"
// setIsLoading={setIsLoading}
initialValues={filter}
setIsLoading={setIsLoading}
/>
</div>
);

View File

@@ -18,6 +18,7 @@ interface InfiniteTableProps {
isLoading: boolean;
items: unknown[];
isSmallScreen: boolean;
currentQuery: string;
setDetailedDataCurrent: Dispatch<SetStateAction<any>>;
setButtonType: (...args: unknown[]) => unknown;
setModalOpened: (...args: unknown[]) => unknown;
@@ -27,6 +28,7 @@ const InfiniteTable = ({
isLoading,
items,
isSmallScreen,
currentQuery,
setDetailedDataCurrent,
setButtonType,
setModalOpened,
@@ -43,7 +45,7 @@ const InfiniteTable = ({
const listener = useCallback(() => {
if (!loadingRef.current && loader.current && isScrolledIntoView(loader.current)) {
dispatch(getLogs());
dispatch(getLogs(currentQuery));
}
}, []);

View File

@@ -7,7 +7,8 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import queryString from 'query-string';
import classNames from 'classnames';
import { BLOCK_ACTIONS, MEDIUM_SCREEN_SIZE } from '../../helpers/constants';
import { FormProvider, useForm } from 'react-hook-form';
import { BLOCK_ACTIONS, DEFAULT_LOGS_FILTER, MEDIUM_SCREEN_SIZE } from '../../helpers/constants';
import Loading from '../ui/Loading';
@@ -29,7 +30,12 @@ import { BUTTON_PREFIX } from './Cells/helpers';
import AnonymizerNotification from './AnonymizerNotification';
import { RootState } from '../../initialState';
const processContent = (data: any, buttonType: string) =>
export type SearchFormValues = {
search: string;
response_status: string;
};
const processContent = (data: any, _buttonType: string) =>
Object.entries(data).map(([key, value]) => {
if (!value) {
return null;
@@ -76,7 +82,6 @@ const Logs = () => {
const {
enabled,
processingGetConfig,
// processingAdditionalLogs,
processingGetLogs,
anonymize_client_ip: anonymizeClientIp,
} = useSelector((state: RootState) => state.queryLogs, shallowEqual);
@@ -88,6 +93,17 @@ const Logs = () => {
const search = search_url_param || filter?.search || '';
const response_status = response_status_url_param || filter?.response_status || '';
const formMethods = useForm<SearchFormValues>({
mode: 'onBlur',
defaultValues: {
search: search || DEFAULT_LOGS_FILTER.search,
response_status: response_status || DEFAULT_LOGS_FILTER.response_status,
},
});
const { watch } = formMethods;
const currentQuery = watch('search');
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth <= MEDIUM_SCREEN_SIZE);
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
@@ -174,15 +190,12 @@ const Logs = () => {
const renderPage = () => (
<>
<Filters
filter={{
response_status,
search,
}}
setIsLoading={setIsLoading}
processingGetLogs={processingGetLogs}
// processingAdditionalLogs={processingAdditionalLogs}
/>
<FormProvider {...formMethods}>
<Filters
setIsLoading={setIsLoading}
processingGetLogs={processingGetLogs}
/>
</FormProvider>
<InfiniteTable
isLoading={isLoading}
@@ -191,6 +204,7 @@ const Logs = () => {
setDetailedDataCurrent={setDetailedDataCurrent}
setButtonType={setButtonType}
setModalOpened={setModalOpened}
currentQuery={currentQuery}
/>
<Modal

View File

@@ -12,7 +12,7 @@ import whoisCell from './whoisCell';
import LogsSearchLink from '../../ui/LogsSearchLink';
import { sortIp } from '../../../helpers/helpers';
import { sortIp, formatNumber } from '../../../helpers/helpers';
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
import { TABLES_MIN_ROWS } from '../../../helpers/constants';
@@ -66,7 +66,7 @@ class AutoClients extends Component<AutoClientsProps> {
return (
<div className="logs__row">
<div className="logs__text" title={clientStats}>
<LogsSearchLink search={row.original.ip}>{clientStats}</LogsSearchLink>
<LogsSearchLink search={row.original.ip}>{formatNumber(clientStats)}</LogsSearchLink>
</div>
</div>
);

View File

@@ -12,7 +12,7 @@ import ReactTable from 'react-table';
import { getAllBlockedServices, getBlockedServices } from '../../../../actions/services';
import { initSettings } from '../../../../actions';
import { splitByNewLine, countClientsStatistics, sortIp, getService } from '../../../../helpers/helpers';
import { splitByNewLine, countClientsStatistics, sortIp, getService, formatNumber } from '../../../../helpers/helpers';
import { MODAL_TYPE, LOCAL_TIMEZONE_VALUE, TABLES_MIN_ROWS } from '../../../../helpers/constants';
import Card from '../../../ui/Card';
@@ -111,6 +111,12 @@ const ClientsTable = ({
config.tags = [];
}
if (values.ids) {
config.ids = values.ids.map((id) => id.name);
} else {
config.ids = [];
}
if (typeof values.upstreams_cache_size === 'string') {
config.upstreams_cache_size = 0;
}
@@ -300,13 +306,16 @@ const ClientsTable = ({
sortMethod: (a: any, b: any) => b - a,
minWidth: 120,
Cell: (row: any) => {
const content = CellWrap(row);
if (!row.value) {
let content = row.value;
if (typeof content === "number") {
content = formatNumber(content);
} else {
content = CellWrap(row);
}
if (!content) {
return content;
}
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>;
},
},
{

View File

@@ -1,514 +0,0 @@
import React, { useState } from 'react';
import { connect, useSelector } from 'react-redux';
import { Field, FieldArray, reduxForm, formValueSelector, FormErrors } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import Select from 'react-select';
import i18n from '../../../i18n';
import Tabs from '../../ui/Tabs';
import Examples from '../Dns/Upstream/Examples';
import { ScheduleForm } from '../../Filters/Services/ScheduleForm';
import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers';
import {
toNumber,
renderInputField,
renderGroupField,
CheckboxField,
renderServiceField,
renderTextareaField,
} from '../../../helpers/form';
import { validateClientId, validateRequiredValue } from '../../../helpers/validators';
import { CLIENT_ID_LINK, FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
import './Service.css';
import { RootState } from '../../../initialState';
const settingsCheckboxes = [
{
name: 'use_global_settings',
placeholder: 'client_global_settings',
},
{
name: 'filtering_enabled',
placeholder: 'block_domain_use_filters_and_hosts',
},
{
name: 'safebrowsing_enabled',
placeholder: 'use_adguard_browsing_sec',
},
{
name: 'parental_enabled',
placeholder: 'use_adguard_parental',
},
];
const logAndStatsCheckboxes = [
{
name: 'ignore_querylog',
placeholder: 'ignore_query_log',
},
{
name: 'ignore_statistics',
placeholder: 'ignore_statistics',
},
];
const validate = (values: any): FormErrors<any, string> => {
const errors: {
name?: string;
ids?: string[];
} = {};
const { name, ids } = values;
errors.name = validateRequiredValue(name);
if (ids && ids.length) {
const idArrayErrors: any = [];
ids.forEach((id: any, idx: any) => {
idArrayErrors[idx] = validateRequiredValue(id) || validateClientId(id);
});
if (idArrayErrors.length) {
errors.ids = idArrayErrors;
}
}
// @ts-expect-error FIXME: ts migration
return errors;
};
const renderFieldsWrapper = (placeholder: any, buttonTitle: any) =>
function cell(row: any) {
const { fields } = row;
return (
<div className="form__group">
{fields.map((ip: any, index: any) => (
<div key={index} className="mb-1">
<Field
name={ip}
component={renderGroupField}
type="text"
className="form-control"
placeholder={placeholder}
isActionAvailable={index !== 0}
removeField={() => fields.remove(index)}
normalizeOnBlur={(data: any) => data.trim()}
/>
</div>
))}
<button
type="button"
className="btn btn-link btn-block btn-sm"
onClick={() => fields.push()}
title={buttonTitle}>
<svg className="icon icon--24">
<use xlinkHref="#plus" />
</svg>
</button>
</div>
);
};
// Should create function outside of component to prevent component re-renders
const renderFields = renderFieldsWrapper(i18n.t('form_enter_id'), i18n.t('form_add_id'));
interface renderMultiselectProps {
input: {
name: string;
value: string;
checked: boolean;
onChange: (...args: unknown[]) => unknown;
onBlur: (...args: unknown[]) => unknown;
};
placeholder?: string;
options?: unknown[];
}
const renderMultiselect = (props: renderMultiselectProps) => {
const { input, placeholder, options } = props;
return (
<Select
{...input}
options={options}
className="basic-multi-select"
classNamePrefix="select"
onChange={(value: any) => input.onChange(value)}
onBlur={() => input.onBlur(input.value)}
placeholder={placeholder}
blurInputOnSelect={false}
isMulti
/>
);
};
interface FormProps {
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
reset: (...args: unknown[]) => string;
change: (...args: unknown[]) => unknown;
submitting: boolean;
handleClose: (...args: unknown[]) => unknown;
useGlobalSettings?: boolean;
useGlobalServices?: boolean;
blockedServicesSchedule?: {
time_zone: string;
};
t: (...args: unknown[]) => string;
processingAdding: boolean;
processingUpdating: boolean;
invalid: boolean;
tagsOptions: unknown[];
initialValues?: {
safe_search: any;
};
}
let Form = (props: FormProps) => {
const {
t,
handleSubmit,
reset,
change,
submitting,
useGlobalSettings,
useGlobalServices,
blockedServicesSchedule,
handleClose,
processingAdding,
processingUpdating,
invalid,
tagsOptions,
initialValues,
} = props;
const services = useSelector((store: RootState) => store?.services);
const { safe_search } = initialValues;
const safeSearchServices = { ...safe_search };
delete safeSearchServices.enabled;
const [activeTabLabel, setActiveTabLabel] = useState('settings');
const handleScheduleSubmit = (values: any) => {
change('blocked_services_schedule', { ...values });
};
const tabs = {
settings: {
title: 'settings',
component: (
<div title={props.t('main_settings')}>
<div className="form__label--bot form__label--bold">{t('protection_section_label')}</div>
{settingsCheckboxes.map((setting) => (
<div className="form__group" key={setting.name}>
<Field
name={setting.name}
type="checkbox"
component={CheckboxField}
placeholder={t(setting.placeholder)}
disabled={setting.name !== 'use_global_settings' ? useGlobalSettings : false}
/>
</div>
))}
<div className="form__group">
<Field
name="safe_search.enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('enforce_safe_search')}
disabled={useGlobalSettings}
/>
</div>
<div className="form__group--inner">
{Object.keys(safeSearchServices).map((searchKey) => (
<div key={searchKey}>
<Field
name={`safe_search.${searchKey}`}
type="checkbox"
component={CheckboxField}
placeholder={captitalizeWords(searchKey)}
disabled={useGlobalSettings}
/>
</div>
))}
</div>
<div className="form__label--bold form__label--top form__label--bot">
{t('log_and_stats_section_label')}
</div>
{logAndStatsCheckboxes.map((setting) => (
<div className="form__group" key={setting.name}>
<Field
name={setting.name}
type="checkbox"
component={CheckboxField}
placeholder={t(setting.placeholder)}
/>
</div>
))}
</div>
),
},
block_services: {
title: 'block_services',
component: (
<div title={props.t('block_services')}>
<div className="form__group">
<Field
name="use_global_blocked_services"
type="checkbox"
component={renderServiceField}
placeholder={t('blocked_services_global')}
modifier="service--global"
/>
<div className="row mb-4">
<div className="col-6">
<button
type="button"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => toggleAllServices(services.allServices, change, true)}>
<Trans>block_all</Trans>
</button>
</div>
<div className="col-6">
<button
type="button"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => toggleAllServices(services.allServices, change, false)}>
<Trans>unblock_all</Trans>
</button>
</div>
</div>
{services.allServices.length > 0 && (
<div className="services">
{services.allServices.map((service: any) => (
<Field
key={service.id}
icon={service.icon_svg}
name={`blocked_services.${service.id}`}
type="checkbox"
component={renderServiceField}
placeholder={service.name}
disabled={useGlobalServices}
/>
))}
</div>
)}
</div>
</div>
),
},
schedule_services: {
title: 'schedule_services',
component: (
<>
<div className="form__desc mb-4">
<Trans>schedule_services_desc_client</Trans>
</div>
<ScheduleForm
schedule={blockedServicesSchedule}
onScheduleSubmit={handleScheduleSubmit}
clientForm
/>
</>
),
},
upstream_dns: {
title: 'upstream_dns',
component: (
<div title={props.t('upstream_dns')}>
<div className="form__desc mb-3">
<Trans
components={[
<a href="#dns" key="0">
link
</a>,
]}>
upstream_dns_client_desc
</Trans>
</div>
<Field
id="upstreams"
name="upstreams"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea mb-5"
placeholder={t('upstream_dns')}
normalizeOnBlur={trimLinesAndRemoveEmpty}
/>
<Examples />
<div className="form__label--bold mt-5 mb-3">{t('upstream_dns_cache_configuration')}</div>
<div className="form__group mb-2">
<Field
name="upstreams_cache_enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('enable_upstream_dns_cache')}
/>
</div>
<div className="form__group form__group--settings">
<label htmlFor="upstreams_cache_size" className="form__label">
{t('dns_cache_size')}
</label>
<Field
name="upstreams_cache_size"
type="number"
component={renderInputField}
placeholder={t('enter_cache_size')}
className="form-control"
normalize={toNumber}
min={0}
max={UINT32_RANGE.MAX}
/>
</div>
</div>
),
},
};
const activeTab = tabs[activeTabLabel].component;
return (
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="form__group mb-0">
<div className="form__group">
<Field
id="name"
name="name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_client_name')}
normalizeOnBlur={(data: any) => data.trim()}
/>
</div>
<div className="form__group mb-4">
<div className="form__label">
<strong className="mr-3">
<Trans>tags_title</Trans>
</strong>
</div>
<div className="form__desc mt-0 mb-2">
<Trans
components={[
<a
target="_blank"
rel="noopener noreferrer"
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax_ctag&from=ui&app=home"
key="0">
link
</a>,
]}>
tags_desc
</Trans>
</div>
<Field
name="tags"
component={renderMultiselect}
placeholder={t('form_select_tags')}
options={tagsOptions}
/>
</div>
<div className="form__group">
<div className="form__label">
<strong className="mr-3">
<Trans>client_identifier</Trans>
</strong>
</div>
<div className="form__desc mt-0">
<Trans
components={[
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0">
text
</a>,
]}>
client_identifier_desc
</Trans>
</div>
</div>
<div className="form__group">
<FieldArray name="ids" component={renderFields} />
</div>
</div>
<Tabs
controlClass="form"
tabs={tabs}
activeTabLabel={activeTabLabel}
setActiveTabLabel={setActiveTabLabel}>
{activeTab}
</Tabs>
</div>
<div className="modal-footer">
<div className="btn-list">
<button
type="button"
className="btn btn-secondary btn-standard"
disabled={submitting}
onClick={() => {
reset();
handleClose();
}}>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
className="btn btn-success btn-standard"
disabled={submitting || invalid || processingAdding || processingUpdating}>
<Trans>save_btn</Trans>
</button>
</div>
</div>
</form>
);
};
const selector = formValueSelector(FORM_NAME.CLIENT);
Form = connect((state) => {
const useGlobalSettings = selector(state, 'use_global_settings');
const useGlobalServices = selector(state, 'use_global_blocked_services');
const blockedServicesSchedule = selector(state, 'blocked_services_schedule');
return {
useGlobalSettings,
useGlobalServices,
blockedServicesSchedule,
};
})(Form);
export default flow([
withTranslation(),
reduxForm({
form: FORM_NAME.CLIENT,
enableReinitialize: true,
validate,
}),
])(Form);

View File

@@ -0,0 +1,84 @@
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Controller, useFormContext } from 'react-hook-form';
import { ClientForm } from '../types';
import { BlockedService } from '../../../../Filters/Services/Form';
import { ServiceField } from '../../../../Filters/Services/ServiceField';
type Props = {
services: BlockedService[];
};
export const BlockedServices = ({ services }: Props) => {
const { t } = useTranslation();
const { watch, setValue, control } = useFormContext<ClientForm>();
const useGlobalServices = watch('use_global_blocked_services');
const handleToggleAllServices = (isSelected: boolean) => {
services.forEach((service: BlockedService) => setValue(`blocked_services.${service.id}`, isSelected));
};
return (
<div title={t('block_services')}>
<div className="form__group">
<Controller
name="use_global_blocked_services"
control={control}
render={({ field }) => (
<ServiceField
{...field}
data-testid="clients_use_global_blocked_services"
placeholder={t('blocked_services_global')}
className="service--global"
/>
)}
/>
<div className="row mb-4">
<div className="col-6">
<button
type="button"
data-testid="clients_block_all"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => handleToggleAllServices(true)}>
<Trans>block_all</Trans>
</button>
</div>
<div className="col-6">
<button
type="button"
data-testid="clients_unblock_all"
className="btn btn-secondary btn-block"
disabled={useGlobalServices}
onClick={() => handleToggleAllServices(false)}>
<Trans>unblock_all</Trans>
</button>
</div>
</div>
{services.length > 0 && (
<div className="services">
{services.map((service: BlockedService) => (
<Controller
key={service.id}
name={`blocked_services.${service.id}`}
control={control}
render={({ field }) => (
<ServiceField
{...field}
data-testid={`clients_service_${service.id}`}
placeholder={service.name}
disabled={useGlobalServices}
icon={service.icon_svg}
/>
)}
/>
))}
</div>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,74 @@
import React from 'react';
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { ClientForm } from '../types';
import { Input } from '../../../../ui/Controls/Input';
import { validateClientId, validateRequiredValue } from '../../../../../helpers/validators';
export const ClientIds = () => {
const { t } = useTranslation();
const { control } = useFormContext<ClientForm>();
const { fields, append, remove } = useFieldArray<ClientForm>({
control,
name: 'ids',
});
return (
<div className="form__group">
{fields.map((field, index) => (
<div key={field.id} className="mb-1">
<Controller
name={`ids.${index}.name`}
control={control}
rules={{
validate: {
required: (value) => validateRequiredValue(value),
validId: (value) => validateClientId(value),
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid={`clients_id_${index}`}
placeholder={t('form_enter_id')}
error={fieldState.error?.message}
onBlur={(event) => {
const trimmedValue = event.target.value.trim();
field.onBlur();
field.onChange(trimmedValue);
}}
rightAddon={
index !== 0 && (
<span className="input-group-append">
<button
type="button"
data-testid={`clients_id_remove_${index}`}
className="btn btn-secondary btn-icon btn-icon--green"
onClick={() => remove(index)}>
<svg className="icon icon--24">
<use xlinkHref="#cross" />
</svg>
</button>
</span>
)
}
/>
)}
/>
</div>
))}
<button
type="button"
data-testid="clients_id_add"
className="btn btn-link btn-block btn-sm"
onClick={() => append({ name: '' })}
title={t('form_add_id')}>
<svg className="icon icon--24">
<use xlinkHref="#plus" />
</svg>
</button>
</div>
);
};

View File

@@ -0,0 +1,126 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Controller, useFormContext } from 'react-hook-form';
import i18next from 'i18next';
import { captitalizeWords } from '../../../../../helpers/helpers';
import { ClientForm } from '../types';
import { Checkbox } from '../../../../ui/Controls/Checkbox';
type ProtectionSettings = 'use_global_settings' | 'filtering_enabled' | 'safebrowsing_enabled' | 'parental_enabled';
const settingsCheckboxes: {
name: ProtectionSettings;
placeholder: string;
}[] = [
{
name: 'use_global_settings',
placeholder: i18next.t('client_global_settings'),
},
{
name: 'filtering_enabled',
placeholder: i18next.t('block_domain_use_filters_and_hosts'),
},
{
name: 'safebrowsing_enabled',
placeholder: i18next.t('use_adguard_browsing_sec'),
},
{
name: 'parental_enabled',
placeholder: i18next.t('use_adguard_parental'),
},
];
type LogsStatsSettings = 'ignore_querylog' | 'ignore_statistics';
const logAndStatsCheckboxes: { name: LogsStatsSettings; placeholder: string }[] = [
{
name: 'ignore_querylog',
placeholder: i18next.t('ignore_query_log'),
},
{
name: 'ignore_statistics',
placeholder: i18next.t('ignore_statistics'),
},
];
type Props = {
safeSearchServices: Record<string, boolean>;
};
export const MainSettings = ({ safeSearchServices }: Props) => {
const { t } = useTranslation();
const { watch, control } = useFormContext<ClientForm>();
const useGlobalSettings = watch('use_global_settings');
return (
<div title={t('main_settings')}>
<div className="form__label--bot form__label--bold">{t('protection_section_label')}</div>
{settingsCheckboxes.map((setting) => (
<div className="form__group" key={setting.name}>
<Controller
name={setting.name}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid={`clients_${setting.name}`}
title={setting.placeholder}
disabled={setting.name !== 'use_global_settings' ? useGlobalSettings : false}
/>
)}
/>
</div>
))}
<div className="form__group">
<Controller
name="safe_search.enabled"
control={control}
render={({ field }) => (
<Checkbox
data-testid="clients_safe_search"
{...field}
title={t('enforce_safe_search')}
disabled={useGlobalSettings}
/>
)}
/>
</div>
<div className="form__group--inner">
{Object.keys(safeSearchServices).map((searchKey) => (
<div key={searchKey}>
<Controller
name={`safe_search.${searchKey}`}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid={`clients_safe_search_${searchKey}`}
title={captitalizeWords(searchKey)}
disabled={useGlobalSettings}
/>
)}
/>
</div>
))}
</div>
<div className="form__label--bold form__label--top form__label--bot">
{t('log_and_stats_section_label')}
</div>
{logAndStatsCheckboxes.map((setting) => (
<div className="form__group" key={setting.name}>
<Controller
name={setting.name}
control={control}
render={({ field }) => (
<Checkbox {...field} data-testid={`clients_${setting.name}`} title={setting.placeholder} />
)}
/>
</div>
))}
</div>
);
};

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { Trans } from 'react-i18next';
import { useFormContext } from 'react-hook-form';
import { ScheduleForm } from '../../../../Filters/Services/ScheduleForm';
import { ClientForm } from '../types';
export const ScheduleServices = () => {
const { watch, setValue } = useFormContext<ClientForm>();
const blockedServicesSchedule = watch('blocked_services_schedule');
const handleScheduleSubmit = (values: any) => {
setValue('blocked_services_schedule', values);
};
return (
<>
<div className="form__desc mb-4">
<Trans>schedule_services_desc_client</Trans>
</div>
<ScheduleForm schedule={blockedServicesSchedule} onScheduleSubmit={handleScheduleSubmit} clientForm />
</>
);
};

View File

@@ -0,0 +1,83 @@
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Controller, useFormContext } from 'react-hook-form';
import Examples from '../../../Dns/Upstream/Examples';
import { UINT32_RANGE } from '../../../../../helpers/constants';
import { Textarea } from '../../../../ui/Controls/Textarea';
import { ClientForm } from '../types';
import { Checkbox } from '../../../../ui/Controls/Checkbox';
import { Input } from '../../../../ui/Controls/Input';
import { toNumber } from '../../../../../helpers/form';
export const UpstreamDns = () => {
const { t } = useTranslation();
const { control } = useFormContext<ClientForm>();
return (
<div title={t('upstream_dns')}>
<div className="form__desc mb-3">
<Trans components={[<a href="#dns" key="0" />]}>upstream_dns_client_desc</Trans>
</div>
<Controller
name="upstreams"
control={control}
render={({ field }) => (
<Textarea
{...field}
data-testid="clients_upstreams"
className="form-control form-control--textarea mb-5"
placeholder={t('upstream_dns')}
trimOnBlur
/>
)}
/>
<Examples />
<div className="form__label--bold mt-5 mb-3">{t('upstream_dns_cache_configuration')}</div>
<div className="form__group mb-2">
<Controller
name="upstreams_cache_enabled"
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="clients_upstreams_cache_enabled"
title={t('enable_upstream_dns_cache')}
/>
)}
/>
</div>
<div className="form__group form__group--settings">
<label htmlFor="upstreams_cache_size" className="form__label">
{t('dns_cache_size')}
</label>
<Controller
name="upstreams_cache_size"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
data-testid="clients_upstreams_cache_size"
placeholder={t('enter_cache_size')}
error={fieldState.error?.message}
min={0}
max={UINT32_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
);
};

View File

@@ -0,0 +1,5 @@
export { BlockedServices } from './BlockedServices';
export { ClientIds } from './ClientIds';
export { ScheduleServices } from './ScheduleServices';
export { MainSettings } from './MainSettings';
export { UpstreamDns } from './UpstreamDns';

View File

@@ -0,0 +1,223 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { Trans, useTranslation } from 'react-i18next';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import Select from 'react-select';
import Tabs from '../../../ui/Tabs';
import { CLIENT_ID_LINK, LOCAL_TIMEZONE_VALUE } from '../../../../helpers/constants';
import { RootState } from '../../../../initialState';
import { Input } from '../../../ui/Controls/Input';
import { validateRequiredValue } from '../../../../helpers/validators';
import { ClientForm } from './types';
import { BlockedServices, ClientIds, MainSettings, ScheduleServices, UpstreamDns } from './components';
import '../Service.css';
const defaultFormValues: ClientForm = {
ids: [{ name: '' }],
name: '',
tags: [],
use_global_settings: false,
filtering_enabled: false,
safebrowsing_enabled: false,
parental_enabled: false,
ignore_querylog: false,
ignore_statistics: false,
blocked_services: {},
safe_search: { enabled: false },
upstreams: '',
upstreams_cache_enabled: false,
upstreams_cache_size: 0,
use_global_blocked_services: false,
blocked_services_schedule: {
time_zone: LOCAL_TIMEZONE_VALUE,
},
};
type Props = {
onSubmit: (values: ClientForm) => void;
onClose: () => void;
useGlobalSettings?: boolean;
useGlobalServices?: boolean;
blockedServicesSchedule?: {
time_zone: string;
};
processingAdding: boolean;
processingUpdating: boolean;
tagsOptions: { label: string; value: string }[];
initialValues?: ClientForm;
};
export const Form = ({
onSubmit,
onClose,
processingAdding,
processingUpdating,
tagsOptions,
initialValues,
}: Props) => {
const { t } = useTranslation();
const methods = useForm<ClientForm>({
defaultValues: {
...defaultFormValues,
...initialValues,
},
mode: 'onBlur',
});
const {
handleSubmit,
reset,
control,
formState: { isSubmitting, isValid },
} = methods;
const services = useSelector((store: RootState) => store?.services);
const { safe_search } = initialValues;
const safeSearchServices = { ...safe_search };
delete safeSearchServices.enabled;
const [activeTabLabel, setActiveTabLabel] = useState('settings');
const tabs = {
settings: {
title: 'settings',
component: <MainSettings safeSearchServices={safeSearchServices} />,
},
block_services: {
title: 'block_services',
component: <BlockedServices services={services?.allServices} />,
},
schedule_services: {
title: 'schedule_services',
component: <ScheduleServices />,
},
upstream_dns: {
title: 'upstream_dns',
component: <UpstreamDns />,
},
};
const activeTab = tabs[activeTabLabel].component;
return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="modal-body">
<div className="form__group mb-0">
<div className="form__group">
<Controller
name="name"
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="clients_name"
placeholder={t('form_client_name')}
error={fieldState.error?.message}
onBlur={(event) => {
const trimmedValue = event.target.value.trim();
field.onBlur();
field.onChange(trimmedValue);
}}
/>
)}
/>
</div>
<div className="form__group mb-4">
<div className="form__label">
<strong className="mr-3">
<Trans>tags_title</Trans>
</strong>
</div>
<div className="form__desc mt-0 mb-2">
<Trans
components={[
<a
target="_blank"
rel="noopener noreferrer"
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax_ctag&from=ui&app=home"
key="0"
/>,
]}>
tags_desc
</Trans>
</div>
<Controller
name="tags"
control={control}
render={({ field }) => (
<Select
{...field}
data-testid="clients_tags"
options={tagsOptions}
className="basic-multi-select"
classNamePrefix="select"
isMulti
/>
)}
/>
</div>
<div className="form__group">
<div className="form__label">
<strong className="mr-3">
<Trans>client_identifier</Trans>
</strong>
</div>
<div className="form__desc mt-0">
<Trans
components={[
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0" />,
]}>
client_identifier_desc
</Trans>
</div>
</div>
<div className="form__group">
<ClientIds />
</div>
</div>
<Tabs
controlClass="form"
tabs={tabs}
activeTabLabel={activeTabLabel}
setActiveTabLabel={setActiveTabLabel}>
{activeTab}
</Tabs>
</div>
<div className="modal-footer">
<div className="btn-list">
<button
type="button"
className="btn btn-secondary btn-standard"
disabled={isSubmitting}
onClick={() => {
reset();
onClose();
}}>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
className="btn btn-success btn-standard"
disabled={isSubmitting || !isValid || processingAdding || processingUpdating}>
<Trans>save_btn</Trans>
</button>
</div>
</div>
</form>
</FormProvider>
);
};

View File

@@ -0,0 +1,28 @@
export type ClientForm = {
name: string;
tags: { value: string; label: string }[];
ids: { name: string }[];
use_global_settings: boolean;
use_global_blocked_services: boolean;
blocked_services_schedule: {
time_zone: string;
};
safe_search: {
enabled: boolean;
[key: string]: boolean;
};
upstreams: string;
upstreams_cache_enabled: boolean;
upstreams_cache_size: number;
blocked_services: Record<string, boolean>;
filtering_enabled: boolean;
safebrowsing_enabled: boolean;
parental_enabled: boolean;
ignore_querylog: boolean;
ignore_statistics: boolean;
};
export type SubmitClientForm = Omit<ClientForm, 'ids' | 'tags'> & {
ids: string[];
tags: string[];
};

View File

@@ -4,8 +4,15 @@ import { Trans, withTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { MODAL_TYPE } from '../../../helpers/constants';
import { Form } from './Form';
import Form from './Form';
const normalizeIds = (initialIds?: string[]): { name: string }[] => {
if (!initialIds || initialIds.length === 0) {
return [{ name: '' }];
}
return initialIds.map((id: string) => ({ name: id }));
};
const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
if (initial && initial.blocked_services) {
@@ -19,6 +26,7 @@ const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
return {
...initial,
blocked_services: blocked,
ids: normalizeIds(initial.ids),
};
}
@@ -26,11 +34,14 @@ const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
return {
...initial,
name: clientName,
ids: [clientId],
ids: [{ name: clientId }],
};
}
return initial;
return {
...initial,
ids: normalizeIds(initial.ids),
};
};
interface ModalProps {
@@ -41,7 +52,7 @@ interface ModalProps {
handleClose: (...args: unknown[]) => unknown;
processingAdding: boolean;
processingUpdating: boolean;
tagsOptions: unknown[];
tagsOptions: { label: string; value: string }[];
t: (...args: unknown[]) => string;
clientId?: string;
}
@@ -85,7 +96,7 @@ const Modal = ({
<Form
initialValues={{ ...initialData }}
onSubmit={handleSubmit}
handleClose={handleClose}
onClose={handleClose}
processingAdding={processingAdding}
processingUpdating={processingUpdating}
tagsOptions={tagsOptions}

View File

@@ -1,28 +1,22 @@
import React, { useCallback } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import React, { useMemo } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { renderInputField, toNumber } from '../../../helpers/form';
import { FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
import { UINT32_RANGE } from '../../../helpers/constants';
import {
validateIpv4,
validateRequiredValue,
validateIpv4RangeEnd,
validateGatewaySubnetMask,
validateIpForGatewaySubnetMask,
validateIpv4,
validateIpv4RangeEnd,
validateNotInRange,
validateRequiredValue,
} from '../../../helpers/validators';
import { RootState } from '../../../initialState';
import { DhcpFormValues } from '.';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
interface FormDHCPv4Props {
handleSubmit: (...args: unknown[]) => string;
submitting: boolean;
initialValues: { v4?: any };
type FormDHCPv4Props = {
processingConfig?: boolean;
change: (field: string, value: any) => void;
reset: () => void;
ipv4placeholders?: {
gateway_ip: string;
subnet_mask: string;
@@ -30,127 +24,179 @@ interface FormDHCPv4Props {
range_end: string;
lease_duration: string;
};
}
interfaces: any;
onSubmit?: (data: DhcpFormValues) => void;
};
const FormDHCPv4 = ({ handleSubmit, submitting, processingConfig, ipv4placeholders }: FormDHCPv4Props) => {
const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }: FormDHCPv4Props) => {
const { t } = useTranslation();
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv4], shallowEqual);
const {
handleSubmit,
formState: { errors, isSubmitting },
control,
watch,
} = useFormContext<DhcpFormValues>();
const interfaces = useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES], shallowEqual);
const interface_name = interfaces?.values?.interface_name;
const interfaceName = watch('interface_name');
const isInterfaceIncludesIpv4 = interfaces?.[interfaceName]?.ipv4_addresses;
const isInterfaceIncludesIpv4 = useSelector(
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses,
);
const formValues = watch('v4');
const isEmptyConfig = !Object.values(formValues || {}).some(Boolean);
const hasV4Errors = errors.v4 && Object.keys(errors.v4).length > 0;
const isEmptyConfig = !Object.values(dhcp?.values?.v4 ?? {}).some(Boolean);
const invalid =
dhcp?.syncErrors ||
interfaces?.syncErrors ||
!isInterfaceIncludesIpv4 ||
isEmptyConfig ||
submitting ||
processingConfig;
const validateRequired = useCallback(
(value) => {
if (isEmptyConfig) {
return undefined;
}
return validateRequiredValue(value);
},
[isEmptyConfig],
);
const isDisabled = useMemo(() => {
return isSubmitting || hasV4Errors || processingConfig || !isInterfaceIncludesIpv4 || isEmptyConfig;
}, [isSubmitting, hasV4Errors, processingConfig, isInterfaceIncludesIpv4, isEmptyConfig]);
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-lg-6">
<div className="form__group form__group--settings">
<label>{t('dhcp_form_gateway_input')}</label>
<Field
<Controller
name="v4.gateway_ip"
component={renderInputField}
type="text"
className="form-control"
placeholder={t(ipv4placeholders.gateway_ip)}
validate={[validateIpv4, validateRequired, validateNotInRange]}
disabled={!isInterfaceIncludesIpv4}
control={control}
rules={{
validate: {
ipv4: validateIpv4,
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
notInRange: validateNotInRange,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_gateway_ip"
label={t('dhcp_form_gateway_input')}
placeholder={t(ipv4placeholders.gateway_ip)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/>
)}
/>
</div>
<div className="form__group form__group--settings">
<label>{t('dhcp_form_subnet_input')}</label>
<Field
<Controller
name="v4.subnet_mask"
component={renderInputField}
type="text"
className="form-control"
placeholder={t(ipv4placeholders.subnet_mask)}
validate={[validateRequired, validateGatewaySubnetMask]}
disabled={!isInterfaceIncludesIpv4}
control={control}
rules={{
validate: {
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
subnet: validateGatewaySubnetMask,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_subnet_mask"
label={t('dhcp_form_subnet_input')}
placeholder={t(ipv4placeholders.subnet_mask)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/>
)}
/>
</div>
</div>
<div className="col-lg-6">
<div className="form__group form__group--settings">
<div className="form__group mb-0">
<div className="row">
<div className="col-12">
<label>{t('dhcp_form_range_title')}</label>
</div>
<div className="col">
<Field
<Controller
name="v4.range_start"
component={renderInputField}
type="text"
className="form-control"
placeholder={t(ipv4placeholders.range_start)}
validate={[validateIpv4, validateIpForGatewaySubnetMask]}
disabled={!isInterfaceIncludesIpv4}
control={control}
rules={{
validate: {
ipv4: validateIpv4,
gateway: validateIpForGatewaySubnetMask,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_range_start"
placeholder={t(ipv4placeholders.range_start)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/>
)}
/>
</div>
<div className="col">
<Field
<Controller
name="v4.range_end"
component={renderInputField}
type="text"
className="form-control"
placeholder={t(ipv4placeholders.range_end)}
validate={[validateIpv4, validateIpv4RangeEnd, validateIpForGatewaySubnetMask]}
disabled={!isInterfaceIncludesIpv4}
control={control}
rules={{
validate: {
ipv4: validateIpv4,
rangeEnd: validateIpv4RangeEnd,
gateway: validateIpForGatewaySubnetMask,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v4_range_end"
placeholder={t(ipv4placeholders.range_end)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
/>
)}
/>
</div>
</div>
</div>
<div className="form__group form__group--settings">
<label>{t('dhcp_form_lease_title')}</label>
<Field
<Controller
name="v4.lease_duration"
component={renderInputField}
type="number"
className="form-control"
placeholder={t(ipv4placeholders.lease_duration)}
validate={validateRequired}
normalize={toNumber}
min={1}
max={UINT32_RANGE.MAX}
disabled={!isInterfaceIncludesIpv4}
control={control}
rules={{
validate: {
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
data-testid="v4_lease_duration"
label={t('dhcp_form_lease_title')}
placeholder={t(ipv4placeholders.lease_duration)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv4}
min={1}
max={UINT32_RANGE.MAX}
value={field.value ?? ''}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
</div>
<div className="btn-list">
<button type="submit" className="btn btn-success btn-standard" disabled={invalid}>
<button
data-testid="v4_save"
type="submit"
className="btn btn-success btn-standard"
disabled={isDisabled}>
{t('save_config')}
</button>
</div>
@@ -158,9 +204,4 @@ const FormDHCPv4 = ({ handleSubmit, submitting, processingConfig, ipv4placeholde
);
};
export default reduxForm<
Record<string, any>,
Omit<FormDHCPv4Props, 'submitting' | 'handleSubmit' | 'reset' | 'change'>
>({
form: FORM_NAME.DHCPv4,
})(FormDHCPv4);
export default FormDHCPv4;

View File

@@ -1,93 +1,92 @@
import React, { useCallback } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import React, { useMemo } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { renderInputField, toNumber } from '../../../helpers/form';
import { FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
import { UINT32_RANGE } from '../../../helpers/constants';
import { validateIpv6, validateRequiredValue } from '../../../helpers/validators';
import { RootState } from '../../../initialState';
import { DhcpFormValues } from '.';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
interface FormDHCPv6Props {
handleSubmit: (...args: unknown[]) => string;
submitting: boolean;
initialValues: {
v6?: any;
};
change: (field: string, value: any) => void;
reset: () => void;
type FormDHCPv6Props = {
processingConfig?: boolean;
ipv6placeholders?: {
range_start: string;
range_end: string;
lease_duration: string;
};
}
interfaces: any;
onSubmit?: (data: DhcpFormValues) => Promise<void> | void;
};
const FormDHCPv6 = ({ handleSubmit, submitting, processingConfig, ipv6placeholders }: FormDHCPv6Props) => {
const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }: FormDHCPv6Props) => {
const { t } = useTranslation();
const {
handleSubmit,
formState: { isSubmitting, isValid },
control,
watch,
} = useFormContext<DhcpFormValues>();
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv6], shallowEqual);
const interfaceName = watch('interface_name');
const isInterfaceIncludesIpv6 = interfaces?.[interfaceName]?.ipv6_addresses;
const interfaces = useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES], shallowEqual);
const interface_name = interfaces?.values?.interface_name;
const formValues = watch('v6');
const isEmptyConfig = !Object.values(formValues || {}).some(Boolean);
const isInterfaceIncludesIpv6 = useSelector(
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv6_addresses,
);
const isEmptyConfig = !Object.values(dhcp?.values?.v6 ?? {}).some(Boolean);
const invalid =
dhcp?.syncErrors ||
interfaces?.syncErrors ||
!isInterfaceIncludesIpv6 ||
isEmptyConfig ||
submitting ||
processingConfig;
const validateRequired = useCallback(
(value) => {
if (isEmptyConfig) {
return undefined;
}
return validateRequiredValue(value);
},
[isEmptyConfig],
);
const isDisabled = useMemo(() => {
return isSubmitting || !isValid || processingConfig || !isInterfaceIncludesIpv6 || isEmptyConfig;
}, [isSubmitting, isValid, processingConfig, isInterfaceIncludesIpv6, isEmptyConfig]);
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-lg-6">
<div className="form__group form__group--settings">
<div className="form__group mb-0">
<div className="row">
<div className="col-12">
<label>{t('dhcp_form_range_title')}</label>
</div>
<div className="col">
<Field
<Controller
name="v6.range_start"
component={renderInputField}
type="text"
className="form-control"
placeholder={t(ipv6placeholders.range_start)}
validate={[validateIpv6, validateRequired]}
disabled={!isInterfaceIncludesIpv6}
control={control}
rules={{
validate: isInterfaceIncludesIpv6
? {
ipv6: validateIpv6,
required: validateRequiredValue,
}
: undefined,
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v6_range_start"
placeholder={t(ipv6placeholders.range_start)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv6}
/>
)}
/>
</div>
<div className="col">
<Field
<Controller
name="v6.range_end"
component="input"
type="text"
className="form-control disabled cursor--not-allowed"
placeholder={t(ipv6placeholders.range_end)}
value={t(ipv6placeholders.range_end)}
disabled
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="v6_range_end"
placeholder={t(ipv6placeholders.range_end)}
error={fieldState.error?.message}
disabled
/>
)}
/>
</div>
</div>
@@ -97,25 +96,43 @@ const FormDHCPv6 = ({ handleSubmit, submitting, processingConfig, ipv6placeholde
<div className="row">
<div className="col-lg-6 form__group form__group--settings">
<label>{t('dhcp_form_lease_title')}</label>
<Field
<Controller
name="v6.lease_duration"
component={renderInputField}
type="number"
className="form-control"
placeholder={t(ipv6placeholders.lease_duration)}
validate={validateRequired}
normalizeOnBlur={toNumber}
min={1}
max={UINT32_RANGE.MAX}
disabled={!isInterfaceIncludesIpv6}
control={control}
rules={{
validate: isInterfaceIncludesIpv6
? {
required: validateRequiredValue,
}
: undefined,
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
data-testid="v6_lease_duration"
label={t('dhcp_form_lease_title')}
placeholder={t(ipv6placeholders.lease_duration)}
error={fieldState.error?.message}
disabled={!isInterfaceIncludesIpv6}
min={1}
max={UINT32_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
<div className="btn-list">
<button type="submit" className="btn btn-success btn-standard" disabled={invalid}>
<button
data-testid="v6_save"
type="submit"
className="btn btn-success btn-standard"
disabled={isDisabled}>
{t('save_config')}
</button>
</div>
@@ -123,9 +140,4 @@ const FormDHCPv6 = ({ handleSubmit, submitting, processingConfig, ipv6placeholde
);
};
export default reduxForm<
Record<string, any>,
Omit<FormDHCPv6Props, 'handleSubmit' | 'change' | 'submitting' | 'reset'>
>({
form: FORM_NAME.DHCPv6,
})(FormDHCPv6);
export default FormDHCPv6;

View File

@@ -1,13 +1,11 @@
import React from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { useSelector } from 'react-redux';
import { useFormContext } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { renderSelectField } from '../../../helpers/form';
import { validateRequiredValue } from '../../../helpers/validators';
import { FORM_NAME } from '../../../helpers/constants';
import { RootState } from '../../../initialState';
import { DhcpFormValues } from '.';
const renderInterfaces = (interfaces: any) =>
Object.keys(interfaces).map((item) => {
@@ -47,13 +45,13 @@ const getInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: any)
},
];
interface renderInterfaceValuesProps {
interface RenderInterfaceValuesProps {
gateway_ip: string;
hardware_address: string;
ip_addresses: string[];
}
const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: renderInterfaceValuesProps) => (
const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: RenderInterfaceValuesProps) => (
<div className="d-flex align-items-end dhcp__interfaces-info">
<ul className="list-unstyled m-0">
{getInterfaceValues({
@@ -77,11 +75,15 @@ const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: r
const Interfaces = () => {
const { t } = useTranslation();
const {
register,
watch,
formState: { errors },
} = useFormContext<DhcpFormValues>();
const { processingInterfaces, interfaces, enabled } = useSelector((store: RootState) => store.dhcp, shallowEqual);
const { processingInterfaces, interfaces, enabled } = useSelector((store: RootState) => store.dhcp);
const interface_name =
useSelector((store: RootState) => store.form[FORM_NAME.DHCP_INTERFACES]?.values?.interface_name);
const interface_name = watch('interface_name');
if (processingInterfaces || !interfaces) {
return null;
@@ -92,27 +94,34 @@ const Interfaces = () => {
return (
<div className="row dhcp__interfaces">
<div className="col col__dhcp">
<Field
name="interface_name"
component={renderSelectField}
<label htmlFor="interface_name" className="form__label">
{t('dhcp_interface_select')}
</label>
<select
id="interface_name"
data-testid="interface_name"
className="form-control custom-select pl-4 col-md"
validate={[validateRequiredValue]}
label="dhcp_interface_select">
disabled={enabled}
{...register('interface_name', {
validate: validateRequiredValue,
})}>
<option value="" disabled={enabled}>
{t('dhcp_interface_select')}
</option>
{renderInterfaces(interfaces)}
</Field>
</select>
{errors.interface_name && (
<div className="form__message form__message--error">{t(errors.interface_name.message)}</div>
)}
</div>
{interfaceValue && renderInterfaceValues({
gateway_ip: interfaceValue.gateway_ip,
hardware_address: interfaceValue.hardware_address,
ip_addresses: interfaceValue.ip_addresses
})}
{interfaceValue &&
renderInterfaceValues({
gateway_ip: interfaceValue.gateway_ip,
hardware_address: interfaceValue.hardware_address,
ip_addresses: interfaceValue.ip_addresses,
})}
</div>
);
};
export default reduxForm({
form: FORM_NAME.DHCP_INTERFACES,
})(Interfaces);
export default Interfaces;

View File

@@ -1,10 +1,9 @@
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { useForm, Controller } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { renderInputField, normalizeMac } from '../../../../helpers/form';
import { normalizeMac } from '../../../../helpers/form';
import {
validateIpv4,
validateMac,
@@ -12,12 +11,12 @@ import {
validateIpv4InCidr,
validateIpGateway,
} from '../../../../helpers/validators';
import { FORM_NAME } from '../../../../helpers/constants';
import { toggleLeaseModal } from '../../../../actions';
import { RootState } from '../../../../initialState';
import { Input } from '../../../ui/Controls/Input';
interface FormStaticLeaseProps {
type Props = {
initialValues?: {
mac?: string;
ip?: string;
@@ -25,63 +24,91 @@ interface FormStaticLeaseProps {
cidr?: string;
gatewayIp?: string;
};
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
reset: () => void;
submitting: boolean;
processingAdding?: boolean;
cidr?: string;
isEdit?: boolean;
}
onSubmit: (data: any) => void;
};
const Form = ({ handleSubmit, reset, pristine, submitting, processingAdding, cidr, isEdit }: FormStaticLeaseProps) => {
export const Form = ({ initialValues, processingAdding, cidr, isEdit, onSubmit }: Props) => {
const { t } = useTranslation();
const dispatch = useDispatch();
const dynamicLease = useSelector((store: RootState) => store.dhcp.leaseModalConfig, shallowEqual);
const {
handleSubmit,
control,
reset,
formState: { isSubmitting, isDirty },
} = useForm({
defaultValues: initialValues,
mode: 'onBlur',
});
const onClick = () => {
reset();
dispatch(toggleLeaseModal());
};
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="modal-body">
<div className="form__group">
<Field
id="mac"
<Controller
name="mac"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_mac')}
normalize={normalizeMac}
validate={[validateRequiredValue, validateMac]}
disabled={isEdit}
control={control}
rules={{ validate: { required: validateRequiredValue, mac: validateMac } }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="static_lease_mac"
placeholder={t('form_enter_mac')}
disabled={isEdit}
error={fieldState.error?.message}
onChange={(e) => field.onChange(normalizeMac(e.target.value))}
/>
)}
/>
</div>
<div className="form__group">
<Field
id="ip"
<Controller
name="ip"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_subnet_ip', { cidr })}
validate={[validateRequiredValue, validateIpv4, validateIpv4InCidr, validateIpGateway]}
control={control}
rules={{
validate: {
required: validateRequiredValue,
ipv4: validateIpv4,
inCidr: validateIpv4InCidr,
gateway: validateIpGateway,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="static_lease_ip"
error={fieldState.error?.message}
placeholder={t('form_enter_subnet_ip', { cidr })}
/>
)}
/>
</div>
<div className="form__group">
<Field
id="hostname"
<Controller
name="hostname"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_hostname')}
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
data-testid="static_lease_hostname"
error={fieldState.error?.message}
placeholder={t('form_enter_hostname')}
/>
)}
/>
</div>
</div>
@@ -90,16 +117,18 @@ const Form = ({ handleSubmit, reset, pristine, submitting, processingAdding, cid
<div className="btn-list">
<button
type="button"
data-testid="static_lease_cancel"
className="btn btn-secondary btn-standard"
disabled={submitting}
disabled={isSubmitting}
onClick={onClick}>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
data-testid="static_lease_save"
className="btn btn-success btn-standard"
disabled={submitting || processingAdding || (pristine && !dynamicLease)}>
disabled={isSubmitting || processingAdding || (!isDirty && !dynamicLease)}>
<Trans>save_btn</Trans>
</button>
</div>
@@ -107,8 +136,3 @@ const Form = ({ handleSubmit, reset, pristine, submitting, processingAdding, cid
</form>
);
};
export default reduxForm<
Record<string, any>,
Omit<FormStaticLeaseProps, 'submitting' | 'handleSubmit' | 'reset' | 'pristine'>
>({ form: FORM_NAME.LEASE })(Form);

View File

@@ -4,7 +4,7 @@ import { Trans, withTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import Form from './Form';
import { Form } from './Form';
import { toggleLeaseModal } from '../../../../actions';
import { MODAL_TYPE } from '../../../../helpers/constants';

View File

@@ -4,8 +4,8 @@ import { Trans, useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import classNames from 'classnames';
import { destroy } from 'redux-form';
import { DHCP_DESCRIPTION_PLACEHOLDERS, DHCP_FORM_NAMES, STATUS_RESPONSE, FORM_NAME } from '../../../helpers/constants';
import { FormProvider, useForm } from 'react-hook-form';
import { DHCP_DESCRIPTION_PLACEHOLDERS, STATUS_RESPONSE } from '../../../helpers/constants';
import Leases from './Leases';
@@ -40,6 +40,55 @@ import {
import './index.css';
import { RootState } from '../../../initialState';
type IPv4FormValues = {
gateway_ip?: string;
subnet_mask?: string;
range_start?: string;
range_end?: string;
lease_duration?: number;
}
type IPv6FormValues = {
range_start?: string;
range_end?: string;
lease_duration?: number;
}
const getDefaultV4Values = (v4: IPv4FormValues) => {
const emptyForm = Object.entries(v4).every(
([key, value]) => key === 'lease_duration' || value === ''
);
if (emptyForm) {
return {
...v4,
lease_duration: undefined,
}
}
return v4;
}
export type DhcpFormValues = {
v4?: IPv4FormValues;
v6?: IPv6FormValues;
interface_name?: string;
};
const DEFAULT_V4_VALUES = {
gateway_ip: '',
subnet_mask: '',
range_start: '',
range_end: '',
lease_duration: undefined,
};
const DEFAULT_V6_VALUES = {
range_start: '',
range_end: '',
lease_duration: undefined,
};
const Dhcp = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
@@ -65,12 +114,21 @@ const Dhcp = () => {
modalType,
} = useSelector((state: RootState) => state.dhcp, shallowEqual);
const interface_name =
useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES]?.values?.interface_name);
const isInterfaceIncludesIpv4 =
useSelector((state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses);
const methods = useForm<DhcpFormValues>({
mode: 'onBlur',
defaultValues: {
v4: getDefaultV4Values(v4),
v6,
interface_name: interfaceName || '',
},
});
const { watch, reset } = methods;
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv4], shallowEqual);
const interface_name = watch('interface_name');
const isInterfaceIncludesIpv4 = useSelector(
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses,
);
const ipv4Config = watch('v4');
const [ipv4placeholders, setIpv4Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv4);
const [ipv6placeholders, setIpv6Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv6);
@@ -85,6 +143,22 @@ const Dhcp = () => {
}
}, [dhcp_available]);
useEffect(() => {
if (v4 || v6 || interfaceName) {
reset({
v4: {
...DEFAULT_V4_VALUES,
...getDefaultV4Values(v4),
},
v6: {
...DEFAULT_V6_VALUES,
...v6,
},
interface_name: interfaceName || '',
});
}
}, [v4, v6, interfaceName, reset]);
useEffect(() => {
const [ipv4] = interfaces?.[interface_name]?.ipv4_addresses ?? [];
const [ipv6] = interfaces?.[interface_name]?.ipv6_addresses ?? [];
@@ -103,13 +177,17 @@ const Dhcp = () => {
const clear = () => {
// eslint-disable-next-line no-alert
if (window.confirm(t('dhcp_reset'))) {
Object.values(DHCP_FORM_NAMES).forEach((formName: any) => dispatch(destroy(formName)));
reset({
v4: DEFAULT_V4_VALUES,
v6: DEFAULT_V6_VALUES,
interface_name: '',
});
dispatch(resetDhcp());
dispatch(getDhcpStatus());
}
};
const handleSubmit = (values: any) => {
const handleSubmit = (values: DhcpFormValues) => {
dispatch(
setDhcpConfig({
interface_name,
@@ -130,12 +208,7 @@ const Dhcp = () => {
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
const getToggleDhcpButton = () => {
const filledConfig =
interface_name &&
(Object.values(v4)
.every(Boolean) ||
Object.values(v6).every(Boolean));
const filledConfig = interface_name && (Object.values(v4).every(Boolean) || Object.values(v6).every(Boolean));
const className = classNames('btn btn-sm', {
'btn-gray': enabled,
@@ -173,9 +246,6 @@ const Dhcp = () => {
const toggleModal = () => dispatch(toggleLeaseModal());
const initialV4 = enteredSomeV4Value ? v4 : {};
const initialV6 = enteredSomeV6Value ? v6 : {};
if (processing || processingInterfaces) {
return <Loading />;
}
@@ -196,19 +266,13 @@ const Dhcp = () => {
const toggleDhcpButton = getToggleDhcpButton();
const inputtedIPv4values = dhcp?.values?.v4?.gateway_ip && dhcp?.values?.v4?.subnet_mask;
const inputtedIPv4values = ipv4Config.gateway_ip && ipv4Config.subnet_mask;
const isEmptyConfig = !Object.values(dhcp?.values?.v4 ?? {}).some(Boolean);
const isEmptyConfig = !Object.values(ipv4Config).some(Boolean);
const disabledLeasesButton = Boolean(
dhcp?.syncErrors ||
!isInterfaceIncludesIpv4 ||
isEmptyConfig ||
processingConfig ||
!inputtedIPv4values,
!isInterfaceIncludesIpv4 || isEmptyConfig || processingConfig || !inputtedIPv4values,
);
const cidr = inputtedIPv4values
? `${dhcp?.values?.v4?.gateway_ip}/${subnetMaskToBitMask(dhcp?.values?.v4?.subnet_mask)}`
: '';
const cidr = inputtedIPv4values ? `${ipv4Config.gateway_ip}/${subnetMaskToBitMask(ipv4Config.subnet_mask)}` : '';
return (
<>
@@ -246,29 +310,30 @@ const Dhcp = () => {
</div>
)}
<Interfaces initialValues={{ interface_name: interfaceName }} />
<FormProvider {...methods}>
<Interfaces />
<Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings">
<div>
<FormDHCPv4
onSubmit={handleSubmit}
processingConfig={processingConfig}
ipv4placeholders={ipv4placeholders}
interfaces={interfaces}
/>
</div>
</Card>
<Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings">
<div>
<FormDHCPv6
onSubmit={handleSubmit}
processingConfig={processingConfig}
ipv6placeholders={ipv6placeholders}
interfaces={interfaces}
/>
</div>
</Card>
</FormProvider>
<Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings">
<div>
<FormDHCPv4
onSubmit={handleSubmit}
initialValues={{ v4: initialV4 }}
processingConfig={processingConfig}
ipv4placeholders={ipv4placeholders}
/>
</div>
</Card>
<Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings">
<div>
<FormDHCPv6
onSubmit={handleSubmit}
initialValues={{ v6: initialV6 }}
processingConfig={processingConfig}
ipv6placeholders={ipv6placeholders}
/>
</div>
</Card>
{enabled && (
<Card title={t('dhcp_leases')} bodyType="card-body box-body--settings">
<div className="row">
@@ -290,7 +355,7 @@ const Dhcp = () => {
processingDeleting={processingDeleting}
processingUpdating={processingUpdating}
cidr={cidr}
gatewayIp={dhcp?.values?.v4?.gateway_ip}
gatewayIp={ipv4Config.gateway_ip}
/>
<div className="btn-list mt-2">

View File

@@ -1,118 +1,140 @@
import React from 'react';
import { connect } from 'react-redux';
import React, { ReactNode } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import i18next from 'i18next';
import { CLIENT_ID_LINK } from '../../../../helpers/constants';
import { removeEmptyLines, trimMultilineString } from '../../../../helpers/helpers';
import { Textarea } from '../../../ui/Controls/Textarea';
import { renderTextareaField } from '../../../../helpers/form';
import { trimMultilineString, removeEmptyLines } from '../../../../helpers/helpers';
import { CLIENT_ID_LINK, FORM_NAME } from '../../../../helpers/constants';
type FormData = {
allowed_clients: string;
disallowed_clients: string;
blocked_hosts: string;
};
const fields = [
const fields: {
id: keyof FormData;
title: string;
subtitle: ReactNode;
normalizeOnBlur: (value: string) => string;
}[] = [
{
id: 'allowed_clients',
title: 'access_allowed_title',
subtitle: 'access_allowed_desc',
title: i18next.t('access_allowed_title'),
subtitle: (
<Trans
components={{
a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" />,
}}>
access_allowed_desc
</Trans>
),
normalizeOnBlur: removeEmptyLines,
},
{
id: 'disallowed_clients',
title: 'access_disallowed_title',
subtitle: 'access_disallowed_desc',
title: i18next.t('access_disallowed_title'),
subtitle: (
<Trans
components={{
a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" />,
}}>
access_disallowed_desc
</Trans>
),
normalizeOnBlur: trimMultilineString,
},
{
id: 'blocked_hosts',
title: 'access_blocked_title',
subtitle: 'access_blocked_desc',
title: i18next.t('access_blocked_title'),
subtitle: i18next.t('access_blocked_desc'),
normalizeOnBlur: removeEmptyLines,
},
];
interface FormProps {
handleSubmit: (...args: unknown[]) => string;
submitting: boolean;
invalid: boolean;
initialValues: object;
type FormProps = {
initialValues?: {
allowed_clients?: string;
disallowed_clients?: string;
blocked_hosts?: string;
};
onSubmit: (data: FormData) => void;
processingSet: boolean;
t: (...args: unknown[]) => string;
textarea?: boolean;
allowedClients?: string;
}
};
interface renderFieldProps {
id?: string;
title?: string;
subtitle?: string;
disabled?: boolean;
processingSet?: boolean;
normalizeOnBlur?: (...args: unknown[]) => unknown;
}
const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
const { t } = useTranslation();
let Form = (props: FormProps) => {
const { allowedClients, handleSubmit, submitting, invalid, processingSet } = props;
const {
control,
handleSubmit,
watch,
formState: { isSubmitting, isDirty },
} = useForm<FormData>({
mode: 'onBlur',
defaultValues: {
allowed_clients: initialValues?.allowed_clients || '',
disallowed_clients: initialValues?.disallowed_clients || '',
blocked_hosts: initialValues?.blocked_hosts || '',
},
});
const allowedClients = watch('allowed_clients');
const renderField = ({
id,
title,
subtitle,
disabled = false,
processingSet,
normalizeOnBlur,
}: renderFieldProps) => (
<div key={id} className="form__group mb-5">
<label className="form__label form__label--with-desc" htmlFor={id}>
<Trans>{title}</Trans>
}: {
id: keyof FormData;
title: string;
subtitle: ReactNode;
normalizeOnBlur: (value: string) => string;
}) => {
const disabled = allowedClients && id === 'disallowed_clients';
{disabled && (
<>
<span> </span>(<Trans>disabled</Trans>)
</>
)}
</label>
return (
<div key={id} className="form__group mb-5">
<label className="form__label form__label--with-desc" htmlFor={id}>
{title}
{disabled && <>&nbsp;({t('disabled')})</>}
</label>
<div className="form__desc form__desc--top">
<Trans
components={{
a: (
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer">
text
</a>
),
}}>
{subtitle}
</Trans>
<div className="form__desc form__desc--top">{subtitle}</div>
<Controller
name={id}
control={control}
render={({ field }) => (
<Textarea
{...field}
id={id}
data-testid={id}
disabled={disabled || processingSet}
onBlur={(e) => {
field.onChange(normalizeOnBlur(e.target.value));
}}
/>
)}
/>
</div>
<Field
id={id}
name={id}
component={renderTextareaField}
type="text"
className="form-control form-control--textarea font-monospace"
disabled={disabled || processingSet}
normalizeOnBlur={normalizeOnBlur}
/>
</div>
);
);
};
return (
<form onSubmit={handleSubmit}>
{fields.map((f) => {
return renderField({
...f,
disabled: allowedClients && f.id === 'disallowed_clients' || false
});
})}
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((f) => renderField(f))}
<div className="card-actions">
<div className="btn-list">
<button
type="submit"
data-testid="access_save"
className="btn btn-success btn-standard"
disabled={submitting || invalid || processingSet}>
<Trans>save_config</Trans>
disabled={isSubmitting || !isDirty || processingSet}>
{t('save_config')}
</button>
</div>
</div>
@@ -120,18 +142,4 @@ let Form = (props: FormProps) => {
);
};
const selector = formValueSelector(FORM_NAME.ACCESS);
Form = connect((state) => {
const allowedClients = selector(state, 'allowed_clients');
return {
allowedClients,
};
})(Form);
export default flow([
withTranslation(),
reduxForm({
form: FORM_NAME.ACCESS,
}),
])(Form);
export default Form;

View File

@@ -1,52 +1,72 @@
import React from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { Trans, useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { renderInputField, toNumber, CheckboxField } from '../../../../helpers/form';
import { CACHE_CONFIG_FIELDS, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
import { replaceZeroWithEmptyString } from '../../../../helpers/helpers';
import i18next from 'i18next';
import { clearDnsCache } from '../../../../actions/dnsConfig';
import { CACHE_CONFIG_FIELDS, UINT32_RANGE } from '../../../../helpers/constants';
import { replaceZeroWithEmptyString } from '../../../../helpers/helpers';
import { RootState } from '../../../../initialState';
import { Checkbox } from '../../../ui/Controls/Checkbox';
const INPUTS_FIELDS = [
{
name: CACHE_CONFIG_FIELDS.cache_size,
title: 'cache_size',
description: 'cache_size_desc',
placeholder: 'enter_cache_size',
title: i18next.t('cache_size'),
description: i18next.t('cache_size_desc'),
placeholder: i18next.t('enter_cache_size'),
},
{
name: CACHE_CONFIG_FIELDS.cache_ttl_min,
title: 'cache_ttl_min_override',
description: 'cache_ttl_min_override_desc',
placeholder: 'enter_cache_ttl_min_override',
title: i18next.t('cache_ttl_min_override'),
description: i18next.t('cache_ttl_min_override_desc'),
placeholder: i18next.t('enter_cache_ttl_min_override'),
},
{
name: CACHE_CONFIG_FIELDS.cache_ttl_max,
title: 'cache_ttl_max_override',
description: 'cache_ttl_max_override_desc',
placeholder: 'enter_cache_ttl_max_override',
title: i18next.t('cache_ttl_max_override'),
description: i18next.t('cache_ttl_max_override_desc'),
placeholder: i18next.t('enter_cache_ttl_max_override'),
},
];
interface CacheFormProps {
handleSubmit: (...args: unknown[]) => string;
submitting: boolean;
invalid: boolean;
}
type FormData = {
cache_size: number;
cache_ttl_min: number;
cache_ttl_max: number;
cache_optimistic: boolean;
};
const Form = ({ handleSubmit, submitting, invalid }: CacheFormProps) => {
type CacheFormProps = {
initialValues?: Partial<FormData>;
onSubmit: (data: FormData) => void;
};
const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
const { t } = useTranslation();
const dispatch = useDispatch();
const { processingSetConfig } = useSelector((state: RootState) => state.dnsConfig, shallowEqual);
const { cache_ttl_max, cache_ttl_min } = useSelector(
(state: RootState) => state.form[FORM_NAME.CACHE].values,
shallowEqual,
);
const { processingSetConfig } = useSelector((state: RootState) => state.dnsConfig);
const {
register,
handleSubmit,
watch,
control,
formState: { isSubmitting, isDirty },
} = useForm<FormData>({
mode: 'onBlur',
defaultValues: {
cache_size: initialValues?.cache_size || 0,
cache_ttl_min: initialValues?.cache_ttl_min || 0,
cache_ttl_max: initialValues?.cache_ttl_max || 0,
cache_optimistic: initialValues?.cache_optimistic || false,
},
});
const cache_ttl_min = watch('cache_ttl_min');
const cache_ttl_max = watch('cache_ttl_max');
const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max;
@@ -57,29 +77,30 @@ const Form = ({ handleSubmit, submitting, invalid }: CacheFormProps) => {
};
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
{INPUTS_FIELDS.map(({ name, title, description, placeholder }) => (
<div className="col-12" key={name}>
<div className="col-12 col-md-7 p-0">
<div className="form__group form__group--settings">
<label htmlFor={name} className="form__label form__label--with-desc">
{t(title)}
{title}
</label>
<div className="form__desc form__desc--top">{t(description)}</div>
<div className="form__desc form__desc--top">{description}</div>
<Field
name={name}
<input
type="number"
component={renderInputField}
placeholder={t(placeholder)}
disabled={processingSetConfig}
data-testid={`dns_${name}`}
className="form-control"
normalizeOnBlur={replaceZeroWithEmptyString}
normalize={toNumber}
placeholder={placeholder}
disabled={processingSetConfig}
min={0}
max={UINT32_RANGE.MAX}
{...register(name as keyof FormData, {
valueAsNumber: true,
setValueAs: (value) => replaceZeroWithEmptyString(value),
})}
/>
</div>
</div>
@@ -91,13 +112,18 @@ const Form = ({ handleSubmit, submitting, invalid }: CacheFormProps) => {
<div className="row">
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<Field
<Controller
name="cache_optimistic"
type="checkbox"
component={CheckboxField}
placeholder={t('cache_optimistic')}
disabled={processingSetConfig}
subtitle={t('cache_optimistic_desc')}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="dns_cache_optimistic"
title={t('cache_optimistic')}
subtitle={t('cache_optimistic_desc')}
disabled={processingSetConfig}
/>
)}
/>
</div>
</div>
@@ -105,19 +131,21 @@ const Form = ({ handleSubmit, submitting, invalid }: CacheFormProps) => {
<button
type="submit"
data-testid="dns_save"
className="btn btn-success btn-standard btn-large"
disabled={submitting || invalid || processingSetConfig || minExceedsMax}>
<Trans>save_btn</Trans>
disabled={isSubmitting || !isDirty || processingSetConfig || minExceedsMax}>
{t('save_btn')}
</button>
<button
type="button"
data-testid="dns_clear"
className="btn btn-outline-secondary btn-standard form__button"
onClick={handleClearCache}>
<Trans>clear_cache</Trans>
{t('clear_cache')}
</button>
</form>
);
};
export default reduxForm({ form: FORM_NAME.CACHE })(Form);
export default Form;

View File

@@ -1,211 +1,279 @@
import React from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { Trans, useTranslation } from 'react-i18next';
import {
renderInputField,
renderRadioField,
renderTextareaField,
CheckboxField,
toNumber,
} from '../../../../helpers/form';
import {
validateIpv4,
validateIpv6,
validateRequiredValue,
validateIp,
validateIPv4Subnet,
validateIPv6Subnet,
} from '../../../../helpers/validators';
import i18next from 'i18next';
import { validateIp, validateIpv4, validateIpv6, validateRequiredValue } from '../../../../helpers/validators';
import { removeEmptyLines } from '../../../../helpers/helpers';
import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
import { RootState } from '../../../../initialState';
import { BLOCKING_MODES, UINT32_RANGE } from '../../../../helpers/constants';
import { Checkbox } from '../../../ui/Controls/Checkbox';
import { Input } from '../../../ui/Controls/Input';
import { toNumber } from '../../../../helpers/form';
import { Textarea } from '../../../ui/Controls/Textarea';
import { Radio } from '../../../ui/Controls/Radio';
const checkboxes = [
const checkboxes: {
name: 'dnssec_enabled' | 'disable_ipv6';
placeholder: string;
subtitle: string;
}[] = [
{
name: 'dnssec_enabled',
placeholder: 'dnssec_enable',
subtitle: 'dnssec_enable_desc',
placeholder: i18next.t('dnssec_enable'),
subtitle: i18next.t('dnssec_enable_desc'),
},
{
name: 'disable_ipv6',
placeholder: 'disable_ipv6',
subtitle: 'disable_ipv6_desc',
placeholder: i18next.t('disable_ipv6'),
subtitle: i18next.t('disable_ipv6_desc'),
},
];
const customIps = [
const customIps: {
name: 'blocking_ipv4' | 'blocking_ipv6';
label: string;
description: string;
validateIp: (value: string) => string;
}[] = [
{
description: 'blocking_ipv4_desc',
name: 'blocking_ipv4',
label: i18next.t('blocking_ipv4'),
description: i18next.t('blocking_ipv4_desc'),
validateIp: validateIpv4,
},
{
description: 'blocking_ipv6_desc',
name: 'blocking_ipv6',
label: i18next.t('blocking_ipv6'),
description: i18next.t('blocking_ipv6_desc'),
validateIp: validateIpv6,
},
];
const getFields = (processing: any, t: any) =>
Object.values(BLOCKING_MODES)
const blockingModeOptions = [
{
value: BLOCKING_MODES.default,
label: i18next.t('default'),
},
{
value: BLOCKING_MODES.refused,
label: i18next.t('refused'),
},
{
value: BLOCKING_MODES.nxdomain,
label: i18next.t('nxdomain'),
},
{
value: BLOCKING_MODES.null_ip,
label: i18next.t('null_ip'),
},
{
value: BLOCKING_MODES.custom_ip,
label: i18next.t('custom_ip'),
},
];
.map((mode: any) => (
<Field
key={mode}
name="blocking_mode"
type="radio"
component={renderRadioField}
value={mode}
placeholder={t(mode)}
disabled={processing}
/>
));
const blockingModeDescriptions = [
i18next.t(`blocking_mode_default`),
i18next.t(`blocking_mode_refused`),
i18next.t(`blocking_mode_nxdomain`),
i18next.t(`blocking_mode_null_ip`),
i18next.t(`blocking_mode_custom_ip`),
];
interface ConfigFormProps {
handleSubmit: (...args: unknown[]) => string;
submitting: boolean;
invalid: boolean;
type FormData = {
ratelimit: number;
ratelimit_subnet_len_ipv4: number;
ratelimit_subnet_len_ipv6: number;
ratelimit_whitelist: string;
edns_cs_enabled: boolean;
edns_cs_use_custom: boolean;
edns_cs_custom_ip?: string;
dnssec_enabled: boolean;
disable_ipv6: boolean;
blocking_mode: string;
blocking_ipv4?: string;
blocking_ipv6?: string;
blocked_response_ttl: number;
};
type Props = {
processing?: boolean;
}
initialValues?: Partial<FormData>;
onSubmit: (data: FormData) => void;
};
const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps) => {
const Form = ({ processing, initialValues, onSubmit }: Props) => {
const { t } = useTranslation();
const { blocking_mode, edns_cs_enabled, edns_cs_use_custom } = useSelector(
(state: RootState) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {},
shallowEqual,
);
const {
handleSubmit,
watch,
control,
formState: { isSubmitting, isDirty },
} = useForm<FormData>({
mode: 'onBlur',
defaultValues: initialValues,
});
const blocking_mode = watch('blocking_mode');
const edns_cs_enabled = watch('edns_cs_enabled');
const edns_cs_use_custom = watch('edns_cs_use_custom');
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit" className="form__label form__label--with-desc">
<Trans>rate_limit</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_desc</Trans>
</div>
<Field
<Controller
name="ratelimit"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit')}
normalize={toNumber}
validate={validateRequiredValue}
min={UINT32_RANGE.MIN}
max={UINT32_RANGE.MAX}
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_ratelimit"
type="number"
label={t('rate_limit')}
desc={t('rate_limit_desc')}
error={fieldState.error?.message}
min={UINT32_RANGE.MIN}
max={UINT32_RANGE.MAX}
disabled={processing}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_subnet_len_ipv4" className="form__label form__label--with-desc">
<Trans>rate_limit_subnet_len_ipv4</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_subnet_len_ipv4_desc</Trans>
</div>
<Field
<Controller
name="ratelimit_subnet_len_ipv4"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit_subnet_len')}
normalize={toNumber}
validate={[validateRequiredValue, validateIPv4Subnet]}
min={0}
max={32}
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_subnet_ipv4"
type="number"
label={t('rate_limit_subnet_len_ipv4')}
desc={t('rate_limit_subnet_len_ipv4_desc')}
error={fieldState.error?.message}
min={0}
max={32}
disabled={processing}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_subnet_len_ipv6" className="form__label form__label--with-desc">
<Trans>rate_limit_subnet_len_ipv6</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_subnet_len_ipv6_desc</Trans>
</div>
<Field
<Controller
name="ratelimit_subnet_len_ipv6"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit_subnet_len')}
normalize={toNumber}
validate={[validateRequiredValue, validateIPv6Subnet]}
min={0}
max={128}
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_subnet_ipv6"
type="number"
label={t('rate_limit_subnet_len_ipv6')}
desc={t('rate_limit_subnet_len_ipv6_desc')}
error={fieldState.error?.message}
min={0}
max={128}
disabled={processing}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="ratelimit_whitelist" className="form__label form__label--with-desc">
<Trans>rate_limit_whitelist</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_whitelist_desc</Trans>
</div>
<Field
<Controller
name="ratelimit_whitelist"
component={renderTextareaField}
type="text"
className="form-control"
placeholder={t('rate_limit_whitelist_placeholder')}
normalizeOnBlur={removeEmptyLines}
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
data-testid="dns_config_subnet_ipv6"
label={t('rate_limit_whitelist')}
desc={t('rate_limit_whitelist_desc')}
error={fieldState.error?.message}
disabled={processing}
trimOnBlur
/>
)}
/>
</div>
</div>
<div className="col-12">
<div className="form__group form__group--settings">
<Field
<Controller
name="edns_cs_enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('edns_enable')}
disabled={processing}
subtitle={t('edns_cs_desc')}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="dns_config_edns_cs_enabled"
title={t('edns_enable')}
disabled={processing}
/>
)}
/>
</div>
</div>
<div className="col-12 form__group form__group--inner">
<div className="form__group ">
<Field
<div className="form__group">
<Controller
name="edns_cs_use_custom"
type="checkbox"
component={CheckboxField}
placeholder={t('edns_use_custom_ip')}
disabled={processing || !edns_cs_enabled}
subtitle={t('edns_use_custom_ip_desc')}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="dns_config_edns_use_custom_ip"
title={t('edns_use_custom_ip')}
disabled={processing || !edns_cs_enabled}
/>
)}
/>
</div>
{edns_cs_use_custom && (
<Field
<Controller
name="edns_cs_custom_ip"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[validateIp, validateRequiredValue]}
control={control}
rules={{
validate: {
required: validateRequiredValue,
id: validateIp,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_edns_cs_custom_ip"
error={fieldState.error?.message}
disabled={processing || !edns_cs_enabled}
/>
)}
/>
)}
</div>
@@ -213,13 +281,18 @@ const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps
{checkboxes.map(({ name, placeholder, subtitle }) => (
<div className="col-12" key={name}>
<div className="form__group form__group--settings">
<Field
<Controller
name={name}
type="checkbox"
component={CheckboxField}
placeholder={t(placeholder)}
disabled={processing}
subtitle={t(subtitle)}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid={`dns_config_${name}`}
title={placeholder}
subtitle={subtitle}
disabled={processing}
/>
)}
/>
</div>
</div>
@@ -227,42 +300,50 @@ const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps
<div className="col-12">
<div className="form__group form__group--settings mb-4">
<label className="form__label form__label--with-desc">
<Trans>blocking_mode</Trans>
</label>
<label className="form__label form__label--with-desc">{t('blocking_mode')}</label>
<div className="form__desc form__desc--top">
{Object.values(BLOCKING_MODES)
.map((mode: any) => (
<li key={mode}>
<Trans>{`blocking_mode_${mode}`}</Trans>
</li>
))}
{blockingModeDescriptions.map((desc: string) => (
<li key={desc}>{desc}</li>
))}
</div>
<div className="custom-controls-stacked">{getFields(processing, t)}</div>
<div className="custom-controls-stacked">
<Controller
name="blocking_mode"
control={control}
render={({ field }) => (
<Radio {...field} options={blockingModeOptions} disabled={processing} />
)}
/>
</div>
</div>
</div>
{blocking_mode === BLOCKING_MODES.custom_ip && (
<>
{customIps.map(({ description, name, validateIp }) => (
{customIps.map(({ label, description, name, validateIp }) => (
<div className="col-12 col-sm-6" key={name}>
<div className="form__group form__group--settings">
<label className="form__label form__label--with-desc" htmlFor={name}>
<Trans>{name}</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>{description}</Trans>
</div>
<Field
<Controller
name={name}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[validateIp, validateRequiredValue]}
control={control}
rules={{
validate: {
required: validateRequiredValue,
ip: validateIp,
},
}}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_blocked_response_ttl"
type="text"
label={label}
desc={description}
error={fieldState.error?.message}
disabled={processing}
/>
)}
/>
</div>
</div>
@@ -272,24 +353,27 @@ const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps
<div className="col-12 col-md-7">
<div className="form__group form__group--settings">
<label htmlFor="blocked_response_ttl" className="form__label form__label--with-desc">
<Trans>blocked_response_ttl</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>blocked_response_ttl_desc</Trans>
</div>
<Field
<Controller
name="blocked_response_ttl"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_blocked_response_ttl')}
normalize={toNumber}
validate={validateRequiredValue}
min={UINT32_RANGE.MIN}
max={UINT32_RANGE.MAX}
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="dns_config_blocked_response_ttl"
type="number"
label={t('blocked_response_ttl')}
desc={t('blocked_response_ttl_desc')}
error={fieldState.error?.message}
min={UINT32_RANGE.MIN}
max={UINT32_RANGE.MAX}
disabled={processing}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
@@ -297,14 +381,13 @@ const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps
<button
type="submit"
data-testid="dns_config_save"
className="btn btn-success btn-standard btn-large"
disabled={submitting || invalid || processing}>
<Trans>save_btn</Trans>
disabled={isSubmitting || !isDirty || processing}>
{t('save_btn')}
</button>
</form>
);
};
export default reduxForm<Record<string, any>, Omit<ConfigFormProps, 'invalid' | 'submitting' | 'handleSubmit'>>({
form: FORM_NAME.BLOCKING_MODE,
})(Form);
export default Form;

View File

@@ -1,173 +1,110 @@
import React, { useRef } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { Trans, useTranslation } from 'react-i18next';
import classnames from 'classnames';
import Examples from './Examples';
import { renderRadioField, renderTextareaField, CheckboxField } from '../../../../helpers/form';
import { DNS_REQUEST_OPTIONS, FORM_NAME, UPSTREAM_CONFIGURATION_WIKI_LINK } from '../../../../helpers/constants';
import i18next from 'i18next';
import clsx from 'clsx';
import { testUpstreamWithFormValues } from '../../../../actions';
import { removeEmptyLines, trimLinesAndRemoveEmpty } from '../../../../helpers/helpers';
import { DNS_REQUEST_OPTIONS, UINT32_RANGE, UPSTREAM_CONFIGURATION_WIKI_LINK } from '../../../../helpers/constants';
import { removeEmptyLines } from '../../../../helpers/helpers';
import { getTextareaCommentsHighlight, syncScroll } from '../../../../helpers/highlightTextareaComments';
import '../../../ui/texareaCommentsHighlight.css';
import { RootState } from '../../../../initialState';
import '../../../ui/texareaCommentsHighlight.css';
import Examples from './Examples';
import { Checkbox } from '../../../ui/Controls/Checkbox';
import { Textarea } from '../../../ui/Controls/Textarea';
import { Radio } from '../../../ui/Controls/Radio';
import { Input } from '../../../ui/Controls/Input';
import { validateRequiredValue } from '../../../../helpers/validators';
import { toNumber } from '../../../../helpers/form';
const UPSTREAM_DNS_NAME = 'upstream_dns';
const UPSTREAM_MODE_NAME = 'upstream_mode';
interface renderFieldProps {
name: string;
component: any;
type: string;
className?: string;
placeholder: string;
subtitle?: string;
value?: string;
normalizeOnBlur?: (...args: unknown[]) => unknown;
containerClass?: string;
onScroll?: (...args: unknown[]) => unknown;
}
const renderField = ({
name,
component,
type,
className,
placeholder,
subtitle,
value,
normalizeOnBlur,
containerClass,
onScroll,
}: renderFieldProps) => {
const { t } = useTranslation();
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
return (
<div key={placeholder} className={classnames('col-12 mb-4', containerClass)}>
<Field
id={name}
value={value}
name={name}
component={component}
type={type}
className={className}
placeholder={t(placeholder)}
subtitle={t(subtitle)}
disabled={processingSetConfig || processingTestUpstream}
normalizeOnBlur={normalizeOnBlur}
onScroll={onScroll}
/>
</div>
);
type FormData = {
upstream_dns: string;
upstream_mode: string;
fallback_dns: string;
bootstrap_dns: string;
local_ptr_upstreams: string;
use_private_ptr_resolvers: boolean;
resolve_clients: boolean;
upstream_timeout: number;
};
interface renderTextareaWithHighlightFieldProps {
className: string;
disabled?: boolean;
id: string;
input?: object;
meta?: object;
normalizeOnBlur?: (...args: unknown[]) => unknown;
onScroll?: (...args: unknown[]) => unknown;
placeholder: string;
type: string;
}
const renderTextareaWithHighlightField = (props: renderTextareaWithHighlightFieldProps) => {
const upstream_dns = useSelector((store: RootState) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
const ref = useRef(null);
const onScroll = (e: any) => syncScroll(e, ref);
return (
<>
{renderTextareaField({
...props,
disabled: !!upstream_dns_file,
onScroll,
normalizeOnBlur: trimLinesAndRemoveEmpty,
})}
{getTextareaCommentsHighlight(ref, upstream_dns)}
</>
);
type FormProps = {
initialValues?: Partial<FormData>;
onSubmit: (data: FormData) => void;
};
const INPUT_FIELDS = [
const upstreamModeOptions = [
{
name: UPSTREAM_MODE_NAME,
type: 'radio',
label: i18next.t('load_balancing'),
desc: <Trans components={{ br: <br />, b: <b /> }}>load_balancing_desc</Trans>,
value: DNS_REQUEST_OPTIONS.LOAD_BALANCING,
component: renderRadioField,
subtitle: 'load_balancing_desc',
placeholder: 'load_balancing',
},
{
name: UPSTREAM_MODE_NAME,
type: 'radio',
label: i18next.t('parallel_requests'),
desc: <Trans components={{ br: <br />, b: <b /> }}>upstream_parallel</Trans>,
value: DNS_REQUEST_OPTIONS.PARALLEL,
component: renderRadioField,
subtitle: 'upstream_parallel',
placeholder: 'parallel_requests',
},
{
name: UPSTREAM_MODE_NAME,
type: 'radio',
label: i18next.t('fastest_addr'),
desc: <Trans components={{ br: <br />, b: <b /> }}>fastest_addr_desc</Trans>,
value: DNS_REQUEST_OPTIONS.FASTEST_ADDR,
component: renderRadioField,
subtitle: 'fastest_addr_desc',
placeholder: 'fastest_addr',
},
];
interface FormProps {
handleSubmit?: (...args: unknown[]) => string;
submitting?: boolean;
invalid?: boolean;
initialValues?: object;
upstream_dns?: string;
fallback_dns?: string;
bootstrap_dns?: string;
}
const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
const dispatch = useDispatch();
const Form = ({ initialValues, onSubmit }: FormProps) => {
const { t } = useTranslation();
const dispatch = useDispatch();
const textareaRef = useRef<HTMLTextAreaElement>(null);
const upstream_dns = useSelector((store: RootState) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
const defaultLocalPtrUpstreams = useSelector((state: RootState) => state.dnsConfig.default_local_ptr_upstreams);
const handleUpstreamTest = () => dispatch(testUpstreamWithFormValues());
const testButtonClass = classnames('btn btn-primary btn-standard mr-2', {
'btn-loading': processingTestUpstream,
const {
control,
handleSubmit,
watch,
formState: { isSubmitting, isDirty },
} = useForm<FormData>({
mode: 'onBlur',
defaultValues: {
upstream_dns: initialValues?.upstream_dns || '',
upstream_mode: initialValues?.upstream_mode || DNS_REQUEST_OPTIONS.LOAD_BALANCING,
fallback_dns: initialValues?.fallback_dns || '',
bootstrap_dns: initialValues?.bootstrap_dns || '',
local_ptr_upstreams: initialValues?.local_ptr_upstreams || '',
use_private_ptr_resolvers: initialValues?.use_private_ptr_resolvers || false,
resolve_clients: initialValues?.resolve_clients || false,
upstream_timeout: initialValues?.upstream_timeout || 0,
},
});
const components = {
a: <a href={UPSTREAM_CONFIGURATION_WIKI_LINK} target="_blank" rel="noopener noreferrer" />,
const upstream_dns = watch('upstream_dns');
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
const defaultLocalPtrUpstreams = useSelector((state: RootState) => state.dnsConfig.default_local_ptr_upstreams);
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
const handleUpstreamTest = () => {
const formValues = {
bootstrap_dns: watch('bootstrap_dns'),
upstream_dns: watch('upstream_dns'),
local_ptr_upstreams: watch('local_ptr_upstreams'),
fallback_dns: watch('fallback_dns'),
};
dispatch(testUpstreamWithFormValues(formValues));
};
return (
<form onSubmit={handleSubmit} className="form--upstream">
<form onSubmit={handleSubmit(onSubmit)} className="form--upstream">
<div className="row">
<label className="col form__label" htmlFor={UPSTREAM_DNS_NAME}>
<Trans components={components}>upstream_dns_help</Trans>{' '}
<label className="col form__label" htmlFor="upstream_dns">
<Trans
components={{
a: <a href={UPSTREAM_CONFIGURATION_WIKI_LINK} target="_blank" rel="noopener noreferrer" />,
}}>
upstream_dns_help
</Trans>{' '}
<Trans
components={[
<a
@@ -184,44 +121,69 @@ const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
<div className="col-12 mb-4">
<div className="text-edit-container">
<Field
id={UPSTREAM_DNS_NAME}
name={UPSTREAM_DNS_NAME}
component={renderTextareaWithHighlightField}
type="text"
className="form-control form-control--textarea font-monospace text-input"
placeholder={t('upstream_dns')}
disabled={processingSetConfig || processingTestUpstream}
normalizeOnBlur={removeEmptyLines}
<Controller
name="upstream_dns"
control={control}
render={({ field }) => (
<>
<Textarea
{...field}
id={UPSTREAM_DNS_NAME}
data-testid="upstream_dns"
className="form-control--textarea-large text-input"
wrapperClassName="mb-0"
placeholder={t('upstream_dns')}
disabled={!!upstream_dns_file || processingSetConfig || processingTestUpstream}
onScroll={(e) => syncScroll(e, textareaRef)}
trimOnBlur
/>
{getTextareaCommentsHighlight(textareaRef, upstream_dns)}
</>
)}
/>
</div>
</div>
{INPUT_FIELDS.map(renderField)}
<div className="col-12">
<Examples />
<hr />
</div>
<div className="col-12 mb-4">
<Controller
name="upstream_mode"
control={control}
render={({ field }) => (
<Radio
{...field}
options={upstreamModeOptions}
disabled={processingSetConfig || processingTestUpstream}
/>
)}
/>
</div>
<div className="col-12">
<label className="form__label form__label--with-desc" htmlFor="fallback_dns">
<Trans>fallback_dns_title</Trans>
{t('fallback_dns_title')}
</label>
<div className="form__desc form__desc--top">
<Trans>fallback_dns_desc</Trans>
</div>
<div className="form__desc form__desc--top">{t('fallback_dns_desc')}</div>
<Field
id="fallback_dns"
<Controller
name="fallback_dns"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('fallback_dns_placeholder')}
disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
control={control}
render={({ field }) => (
<Textarea
{...field}
id="fallback_dns"
data-testid="fallback_dns"
wrapperClassName="mb-0"
placeholder={t('fallback_dns_placeholder')}
disabled={processingSetConfig}
trimOnBlur
/>
)}
/>
</div>
@@ -229,24 +191,30 @@ const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
<hr />
</div>
<div className="col-12 mb-2">
<div className="col-12">
<label className="form__label form__label--with-desc" htmlFor="bootstrap_dns">
<Trans>bootstrap_dns</Trans>
{t('bootstrap_dns')}
</label>
<div className="form__desc form__desc--top">
<Trans>bootstrap_dns_desc</Trans>
</div>
<div className="form__desc form__desc--top">{t('bootstrap_dns_desc')}</div>
<Field
id="bootstrap_dns"
<Controller
name="bootstrap_dns"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('bootstrap_dns')}
disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
control={control}
render={({ field }) => (
<Textarea
{...field}
id="bootstrap_dns"
data-testid="bootstrap_dns"
placeholder={t('bootstrap_dns')}
wrapperClassName="mb-0"
disabled={processingSetConfig}
onBlur={(e) => {
const value = removeEmptyLines(e.target.value);
field.onChange(value);
}}
/>
)}
/>
</div>
@@ -256,43 +224,47 @@ const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
<div className="col-12">
<label className="form__label form__label--with-desc" htmlFor="local_ptr">
<Trans>local_ptr_title</Trans>
{t('local_ptr_title')}
</label>
<div className="form__desc form__desc--top">
<Trans>local_ptr_desc</Trans>
</div>
<div className="form__desc form__desc--top">{t('local_ptr_desc')}</div>
<div className="form__desc form__desc--top">
{/** TODO: Add internazionalization for "" */}
{defaultLocalPtrUpstreams?.length > 0 ? (
<Trans values={{ ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', ') }}>
local_ptr_default_resolver
</Trans>
) : (
<Trans>local_ptr_no_default_resolver</Trans>
)}
{defaultLocalPtrUpstreams?.length > 0
? t('local_ptr_default_resolver', {
ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', '),
})
: t('local_ptr_no_default_resolver')}
</div>
<Field
id="local_ptr_upstreams"
<Controller
name="local_ptr_upstreams"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('local_ptr_placeholder')}
disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
control={control}
render={({ field }) => (
<Textarea
{...field}
id="local_ptr_upstreams"
data-testid="local_ptr_upstreams"
placeholder={t('local_ptr_placeholder')}
disabled={processingSetConfig}
trimOnBlur
/>
)}
/>
<div className="mt-4">
<Field
<Controller
name="use_private_ptr_resolvers"
type="checkbox"
component={CheckboxField}
placeholder={t('use_private_ptr_resolvers_title')}
subtitle={t('use_private_ptr_resolvers_desc')}
disabled={processingSetConfig}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="dns_use_private_ptr_resolvers"
title={t('use_private_ptr_resolvers_title')}
subtitle={t('use_private_ptr_resolvers_desc')}
disabled={processingSetConfig}
/>
)}
/>
</div>
</div>
@@ -302,32 +274,79 @@ const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
</div>
<div className="col-12 mb-4">
<Field
<Controller
name="resolve_clients"
type="checkbox"
component={CheckboxField}
placeholder={t('resolve_clients_title')}
subtitle={t('resolve_clients_desc')}
disabled={processingSetConfig}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="dns_resolve_clients"
title={t('resolve_clients_title')}
subtitle={t('resolve_clients_desc')}
disabled={processingSetConfig}
/>
)}
/>
</div>
<div className="col-12">
<hr />
</div>
<div className="col-12 col-md-7">
<div className="form__group">
<label htmlFor="upstream_timeout" className="form__label form__label--with-desc">
<Trans>upstream_timeout</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>upstream_timeout_desc</Trans>
</div>
<Controller
name="upstream_timeout"
control={control}
rules={{ validate: validateRequiredValue }}
render={({ field }) => (
<Input
{...field}
type="number"
id="upstream_timeout"
data-testid="upstream_timeout"
placeholder={t('form_enter_upstream_timeout')}
disabled={processingSetConfig}
min={1}
max={UINT32_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
</div>
</div>
<div className="card-actions">
<div className="btn-list">
<button
type="button"
className={testButtonClass}
data-testid="dns_upstream_test"
className={clsx('btn btn-primary btn-standard mr-2', {
'btn-loading': processingTestUpstream,
})}
onClick={handleUpstreamTest}
disabled={!upstream_dns || processingTestUpstream}>
<Trans>test_upstream_btn</Trans>
{t('test_upstream_btn')}
</button>
<button
type="submit"
data-testid="dns_upstream_save"
className="btn btn-success btn-standard"
disabled={submitting || invalid || processingSetConfig || processingTestUpstream}>
<Trans>apply_btn</Trans>
disabled={isSubmitting || !isDirty || processingSetConfig || processingTestUpstream}>
{t('apply_btn')}
</button>
</div>
</div>
@@ -335,4 +354,4 @@ const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
);
};
export default reduxForm({ form: FORM_NAME.UPSTREAM })(Form);
export default Form;

View File

@@ -19,6 +19,7 @@ const Upstream = () => {
resolve_clients,
local_ptr_upstreams,
use_private_ptr_resolvers,
upstream_timeout,
} = useSelector((state: RootState) => state.dnsConfig, shallowEqual);
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
@@ -32,6 +33,7 @@ const Upstream = () => {
resolve_clients,
local_ptr_upstreams,
use_private_ptr_resolvers,
upstream_timeout,
} = values;
const dnsConfig = {
@@ -41,6 +43,7 @@ const Upstream = () => {
resolve_clients,
local_ptr_upstreams,
use_private_ptr_resolvers,
upstream_timeout,
...(upstream_dns_file ? null : { upstream_dns }),
};
@@ -64,6 +67,7 @@ const Upstream = () => {
resolve_clients,
local_ptr_upstreams,
use_private_ptr_resolvers,
upstream_timeout,
}}
onSubmit={handleSubmit}
/>

View File

@@ -1,11 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { Trans, useTranslation } from 'react-i18next';
import { renderInputField, CheckboxField, renderRadioField, toNumber } from '../../../helpers/form';
import { Controller, useForm } from 'react-hook-form';
import i18next from 'i18next';
import {
validateServerName,
validateIsSafePort,
@@ -14,7 +12,6 @@ import {
validatePortTLS,
validatePlainDns,
} from '../../../helpers/validators';
import i18n from '../../../i18n';
import KeyStatus from './KeyStatus';
@@ -22,51 +19,39 @@ import CertificateStatus from './CertificateStatus';
import {
DNS_OVER_QUIC_PORT,
DNS_OVER_TLS_PORT,
FORM_NAME,
STANDARD_HTTPS_PORT,
ENCRYPTION_SOURCE,
} from '../../../helpers/constants';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Radio } from '../../ui/Controls/Radio';
import { Input } from '../../ui/Controls/Input';
import { Textarea } from '../../ui/Controls/Textarea';
import { EncryptionData } from '../../../initialState';
import { toNumber } from '../../../helpers/form';
const validate = (values: any) => {
const errors: { port_dns_over_tls?: string; port_https?: string } = {};
const certificateSourceOptions = [
{
label: i18next.t('encryption_certificates_source_path'),
value: ENCRYPTION_SOURCE.PATH,
},
{
label: i18next.t('encryption_certificates_source_content'),
value: ENCRYPTION_SOURCE.CONTENT,
},
];
if (values.port_dns_over_tls && values.port_https) {
if (values.port_dns_over_tls === values.port_https) {
errors.port_dns_over_tls = i18n.t('form_error_equal');
const keySourceOptions = [
{
label: i18next.t('encryption_key_source_path'),
value: ENCRYPTION_SOURCE.PATH,
},
{
label: i18next.t('encryption_key_source_content'),
value: ENCRYPTION_SOURCE.CONTENT,
},
];
errors.port_https = i18n.t('form_error_equal');
}
}
return errors;
};
const clearFields = (change: any, setTlsConfig: any, validateTlsConfig: any, t: any) => {
const fields = {
private_key: '',
certificate_chain: '',
private_key_path: '',
certificate_path: '',
port_https: STANDARD_HTTPS_PORT,
port_dns_over_tls: DNS_OVER_TLS_PORT,
port_dns_over_quic: DNS_OVER_QUIC_PORT,
server_name: '',
force_https: false,
enabled: false,
private_key_saved: false,
serve_plain_dns: true,
};
// eslint-disable-next-line no-alert
if (window.confirm(t('encryption_reset'))) {
Object.keys(fields)
.forEach((field) => change(field, fields[field]));
setTlsConfig(fields);
validateTlsConfig(fields);
}
};
const validationMessage = (warningValidation: any, isWarning: any) => {
const validationMessage = (warningValidation: string, isWarning: boolean) => {
if (!warningValidation) {
return null;
}
@@ -88,56 +73,60 @@ const validationMessage = (warningValidation: any, isWarning: any) => {
);
};
interface FormProps {
handleSubmit: (...args: unknown[]) => string;
handleChange?: (...args: unknown[]) => unknown;
isEnabled: boolean;
servePlainDns: boolean;
certificateChain: string;
privateKey: string;
certificatePath: string;
privateKeyPath: string;
change: (...args: unknown[]) => unknown;
submitting: boolean;
invalid: boolean;
initialValues: object;
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;
t: (...args: unknown[]) => string;
setTlsConfig: (...args: unknown[]) => unknown;
validateTlsConfig: (...args: unknown[]) => unknown;
certificateSource?: string;
privateKeySource?: string;
privateKeySaved?: boolean;
}
export type EncryptionFormValues = {
enabled?: boolean;
serve_plain_dns?: boolean;
server_name?: string;
force_https?: boolean;
port_https?: number;
port_dns_over_tls?: number;
port_dns_over_quic?: number;
certificate_chain?: string;
private_key?: string;
certificate_path?: string;
private_key_path?: string;
certificate_source?: string;
key_source?: string;
private_key_saved?: boolean;
};
type Props = {
initialValues: EncryptionFormValues;
encryption: EncryptionData;
onSubmit: (values: EncryptionFormValues) => void;
debouncedConfigValidation: (values: EncryptionFormValues) => void;
setTlsConfig: (values: Partial<EncryptionData>) => void;
validateTlsConfig: (values: Partial<EncryptionData>) => void;
};
const defaultValues = {
enabled: false,
serve_plain_dns: true,
server_name: '',
force_https: false,
port_https: STANDARD_HTTPS_PORT,
port_dns_over_tls: DNS_OVER_TLS_PORT,
port_dns_over_quic: DNS_OVER_QUIC_PORT,
certificate_chain: '',
private_key: '',
certificate_path: '',
private_key_path: '',
certificate_source: ENCRYPTION_SOURCE.PATH,
key_source: ENCRYPTION_SOURCE.PATH,
private_key_saved: false,
};
export const Form = ({
initialValues,
encryption,
onSubmit,
setTlsConfig,
debouncedConfigValidation,
validateTlsConfig,
}: Props) => {
const { t } = useTranslation();
let Form = (props: FormProps) => {
const {
t,
handleSubmit,
handleChange,
isEnabled,
servePlainDns,
certificateChain,
privateKey,
certificatePath,
privateKeyPath,
change,
invalid,
submitting,
processingConfig,
processingValidate,
not_after,
valid_chain,
valid_key,
@@ -148,37 +137,100 @@ let Form = (props: FormProps) => {
issuer,
subject,
warning_validation,
setTlsConfig,
validateTlsConfig,
certificateSource,
privateKeySource,
privateKeySaved,
} = props;
processingConfig,
processingValidate,
} = encryption;
const {
control,
handleSubmit,
watch,
reset,
setValue,
setError,
getValues,
formState: { isSubmitting, isValid },
} = useForm<EncryptionFormValues>({
defaultValues: {
...defaultValues,
...initialValues,
},
mode: 'onBlur',
});
const {
enabled: isEnabled,
serve_plain_dns: servePlainDns,
certificate_chain: certificateChain,
private_key: privateKey,
private_key_path: privateKeyPath,
key_source: privateKeySource,
private_key_saved: privateKeySaved,
certificate_path: certificatePath,
certificate_source: certificateSource,
} = watch();
const handleBlur = () => {
debouncedConfigValidation(getValues());
};
const isSavingDisabled = () => {
const processing = submitting || processingConfig || processingValidate;
const processing = isSubmitting || processingConfig || processingValidate;
if (servePlainDns && !isEnabled) {
return invalid || processing;
return !isValid || processing;
}
return invalid || processing || !valid_key || !valid_cert || !valid_pair;
return !isValid || processing || !valid_key || !valid_cert || !valid_pair;
};
const clearFields = () => {
if (window.confirm(t('encryption_reset'))) {
reset();
setTlsConfig(defaultValues);
validateTlsConfig(defaultValues);
}
};
const validatePorts = (values: EncryptionFormValues) => {
const errors: { port_dns_over_tls?: string; port_https?: string } = {};
if (values.port_dns_over_tls && values.port_https) {
if (values.port_dns_over_tls === values.port_https) {
errors.port_dns_over_tls = i18next.t('form_error_equal');
errors.port_https = i18next.t('form_error_equal');
}
}
return errors;
};
const onFormSubmit = (data: EncryptionFormValues) => {
const validationErrors = validatePorts(data);
if (Object.keys(validationErrors).length > 0) {
Object.entries(validationErrors).forEach(([field, message]) => {
setError(field as keyof EncryptionFormValues, { type: 'manual', message });
});
} else {
onSubmit(data);
}
};
const isDisabled = isSavingDisabled();
const isWarning = valid_key && valid_cert && valid_pair;
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onFormSubmit)}>
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings mb-3">
<Field
<Controller
name="enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('encryption_enable')}
onChange={handleChange}
control={control}
render={({ field }) => (
<Checkbox {...field} title={t('encryption_enable')} onBlur={handleBlur} />
)}
/>
</div>
@@ -187,13 +239,13 @@ let Form = (props: FormProps) => {
</div>
<div className="form__group mb-3 mt-5">
<Field
<Controller
name="serve_plain_dns"
type="checkbox"
component={CheckboxField}
placeholder={t('encryption_plain_dns_enable')}
onChange={handleChange}
validate={validatePlainDns}
control={control}
rules={{
validate: (value) => validatePlainDns(value, getValues()),
}}
render={({ field }) => <Checkbox {...field} title={t('encryption_plain_dns_enable')} />}
/>
</div>
@@ -212,16 +264,20 @@ let Form = (props: FormProps) => {
<div className="col-lg-6">
<div className="form__group form__group--settings">
<Field
id="server_name"
<Controller
name="server_name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_server_enter')}
onChange={handleChange}
disabled={!isEnabled}
validate={validateServerName}
control={control}
rules={{ validate: validateServerName }}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
placeholder={t('encryption_server_enter')}
error={fieldState.error?.message}
disabled={!isEnabled}
onBlur={handleBlur}
/>
)}
/>
<div className="form__desc">
@@ -232,13 +288,12 @@ let Form = (props: FormProps) => {
<div className="col-lg-6">
<div className="form__group form__group--settings">
<Field
<Controller
name="force_https"
type="checkbox"
component={CheckboxField}
placeholder={t('encryption_redirect')}
onChange={handleChange}
disabled={!isEnabled}
control={control}
render={({ field }) => (
<Checkbox {...field} title={t('encryption_redirect')} disabled={!isEnabled} />
)}
/>
<div className="form__desc">
@@ -255,17 +310,24 @@ let Form = (props: FormProps) => {
<Trans>encryption_https</Trans>
</label>
<Field
id="port_https"
<Controller
name="port_https"
component={renderInputField}
type="number"
className="form-control"
placeholder={t('encryption_https')}
validate={[validatePort, validateIsSafePort]}
normalize={toNumber}
onChange={handleChange}
disabled={!isEnabled}
control={control}
rules={{ validate: { validatePort, validateIsSafePort } }}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
placeholder={t('encryption_https')}
error={fieldState.error?.message}
disabled={!isEnabled}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
onBlur={handleBlur}
/>
)}
/>
<div className="form__desc">
@@ -280,17 +342,24 @@ let Form = (props: FormProps) => {
<Trans>encryption_dot</Trans>
</label>
<Field
id="port_dns_over_tls"
<Controller
name="port_dns_over_tls"
component={renderInputField}
type="number"
className="form-control"
placeholder={t('encryption_dot')}
validate={[validatePortTLS]}
normalize={toNumber}
onChange={handleChange}
disabled={!isEnabled}
control={control}
rules={{ validate: validatePortTLS }}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
placeholder={t('encryption_dot')}
error={fieldState.error?.message}
disabled={!isEnabled}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
onBlur={handleBlur}
/>
)}
/>
<div className="form__desc">
@@ -305,17 +374,24 @@ let Form = (props: FormProps) => {
<Trans>encryption_doq</Trans>
</label>
<Field
id="port_dns_over_quic"
<Controller
name="port_dns_over_quic"
component={renderInputField}
type="number"
className="form-control"
placeholder={t('encryption_doq')}
validate={[validatePortQuic]}
normalize={toNumber}
onChange={handleChange}
disabled={!isEnabled}
control={control}
rules={{ validate: validatePortQuic }}
render={({ field, fieldState }) => (
<Input
{...field}
type="number"
placeholder={t('encryption_doq')}
error={fieldState.error?.message}
disabled={!isEnabled}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
onBlur={handleBlur}
/>
)}
/>
<div className="form__desc">
@@ -352,50 +428,44 @@ let Form = (props: FormProps) => {
<div className="form__inline mb-2">
<div className="custom-controls-stacked">
<Field
<Controller
name="certificate_source"
component={renderRadioField}
type="radio"
className="form-control mr-2"
value="path"
placeholder={t('encryption_certificates_source_path')}
disabled={!isEnabled}
/>
<Field
name="certificate_source"
component={renderRadioField}
type="radio"
className="form-control mr-2"
value="content"
placeholder={t('encryption_certificates_source_content')}
disabled={!isEnabled}
control={control}
render={({ field }) => (
<Radio {...field} options={certificateSourceOptions} disabled={!isEnabled} />
)}
/>
</div>
</div>
{certificateSource === ENCRYPTION_SOURCE.CONTENT && (
<Field
id="certificate_chain"
{certificateSource === ENCRYPTION_SOURCE.CONTENT ? (
<Controller
name="certificate_chain"
component="textarea"
type="text"
className="form-control form-control--textarea"
placeholder={t('encryption_certificates_input')}
onChange={handleChange}
disabled={!isEnabled}
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
placeholder={t('encryption_certificates_input')}
disabled={!isEnabled}
error={fieldState.error?.message}
onBlur={handleBlur}
/>
)}
/>
)}
{certificateSource === ENCRYPTION_SOURCE.PATH && (
<Field
id="certificate_path"
) : (
<Controller
name="certificate_path"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_certificate_path')}
onChange={handleChange}
disabled={!isEnabled}
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
placeholder={t('encryption_certificate_path')}
error={fieldState.error?.message}
disabled={!isEnabled}
onBlur={handleBlur}
/>
)}
/>
)}
</div>
@@ -424,70 +494,67 @@ let Form = (props: FormProps) => {
<div className="form__inline mb-2">
<div className="custom-controls-stacked">
<Field
<Controller
name="key_source"
component={renderRadioField}
type="radio"
className="form-control mr-2"
value={ENCRYPTION_SOURCE.PATH}
placeholder={t('encryption_key_source_path')}
disabled={!isEnabled}
/>
<Field
name="key_source"
component={renderRadioField}
type="radio"
className="form-control mr-2"
value={ENCRYPTION_SOURCE.CONTENT}
placeholder={t('encryption_key_source_content')}
disabled={!isEnabled}
control={control}
render={({ field }) => (
<Radio {...field} options={keySourceOptions} disabled={!isEnabled} />
)}
/>
</div>
</div>
{privateKeySource === ENCRYPTION_SOURCE.PATH && (
<Field
{privateKeySource === ENCRYPTION_SOURCE.CONTENT ? (
<>
<Controller
name="private_key_saved"
control={control}
render={({ field }) => (
<Checkbox
{...field}
title={t('use_saved_key')}
disabled={!isEnabled}
onChange={(checked: boolean) => {
if (checked) {
setValue('private_key', '');
}
field.onChange(checked);
}}
onBlur={handleBlur}
/>
)}
/>
<Controller
name="private_key"
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
placeholder={t('encryption_key_input')}
disabled={!isEnabled || privateKeySaved}
error={fieldState.error?.message}
onBlur={handleBlur}
/>
)}
/>
</>
) : (
<Controller
name="private_key_path"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('encryption_private_key_path')}
onChange={handleChange}
disabled={!isEnabled}
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
type="text"
placeholder={t('encryption_private_key_path')}
error={fieldState.error?.message}
disabled={!isEnabled}
onBlur={handleBlur}
/>
)}
/>
)}
{privateKeySource === ENCRYPTION_SOURCE.CONTENT && [
<Field
key="private_key_saved"
name="private_key_saved"
type="checkbox"
className="form__group form__group--settings mb-2"
component={CheckboxField}
disabled={!isEnabled}
placeholder={t('use_saved_key')}
onChange={(event: any) => {
if (event.target.checked) {
change('private_key', '');
}
if (handleChange) {
handleChange(event);
}
}}
/>,
<Field
id="private_key"
key="private_key"
name="private_key"
component="textarea"
type="text"
className="form-control form-control--textarea"
placeholder={t('encryption_key_input')}
onChange={handleChange}
disabled={!isEnabled || privateKeySaved}
/>,
]}
</div>
<div className="form__status">
@@ -505,44 +572,11 @@ let Form = (props: FormProps) => {
<button
type="button"
className="btn btn-secondary btn-standart"
disabled={submitting || processingConfig}
onClick={() => clearFields(change, setTlsConfig, validateTlsConfig, t)}>
disabled={isSubmitting || processingConfig}
onClick={clearFields}>
<Trans>reset_settings</Trans>
</button>
</div>
</form>
);
};
const selector = formValueSelector(FORM_NAME.ENCRYPTION);
Form = connect((state) => {
const isEnabled = selector(state, 'enabled');
const servePlainDns = selector(state, 'serve_plain_dns');
const certificateChain = selector(state, 'certificate_chain');
const privateKey = selector(state, 'private_key');
const certificatePath = selector(state, 'certificate_path');
const privateKeyPath = selector(state, 'private_key_path');
const certificateSource = selector(state, 'certificate_source');
const privateKeySource = selector(state, 'key_source');
const privateKeySaved = selector(state, 'private_key_saved');
return {
isEnabled,
servePlainDns,
certificateChain,
privateKey,
certificatePath,
privateKeyPath,
certificateSource,
privateKeySource,
privateKeySaved,
};
})(Form);
export default flow([
withTranslation(),
reduxForm({
form: FORM_NAME.ENCRYPTION,
validate,
}),
])(Form);

View File

@@ -1,61 +1,60 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import debounce from 'lodash/debounce';
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { debounce } from 'lodash';
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
import Form from './Form';
import { EncryptionFormValues, Form } from './Form';
import Card from '../../ui/Card';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
import { EncryptionData } from '../../../initialState';
interface EncryptionProps {
setTlsConfig: (...args: unknown[]) => unknown;
validateTlsConfig: (...args: unknown[]) => unknown;
type Props = {
encryption: EncryptionData;
t: (...args: unknown[]) => string;
}
setTlsConfig: (values: Partial<EncryptionData>) => void;
validateTlsConfig: (values: Partial<EncryptionData>) => void;
};
class Encryption extends Component<EncryptionProps> {
componentDidMount() {
const { validateTlsConfig, encryption } = this.props;
export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => {
const { t } = useTranslation();
if (encryption.enabled) {
validateTlsConfig(encryption);
}
}
handleFormSubmit = (values: any) => {
const submitValues = this.getSubmitValues(values);
this.props.setTlsConfig(submitValues);
};
handleFormChange = debounce((values) => {
const submitValues = this.getSubmitValues(values);
if (submitValues.enabled) {
this.props.validateTlsConfig(submitValues);
}
}, DEBOUNCE_TIMEOUT);
getInitialValues = (data: any) => {
const { certificate_chain, private_key, private_key_saved } = data;
const initialValues = useMemo((): EncryptionFormValues => {
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 {
...data,
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,
};
};
}, [encryption]);
getSubmitValues = (values: any) => {
const getSubmitValues = useCallback((values: any) => {
const { certificate_source, key_source, private_key_saved, ...config } = values;
if (certificate_source === ENCRYPTION_SOURCE.PATH) {
@@ -76,63 +75,47 @@ class Encryption extends Component<EncryptionProps> {
}
return config;
};
}, []);
render() {
const { encryption, t } = this.props;
const {
enabled,
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,
serve_plain_dns,
} = encryption;
const handleFormSubmit = useCallback(
(values: any) => {
const submitValues = getSubmitValues(values);
setTlsConfig(submitValues);
},
[getSubmitValues, setTlsConfig],
);
const initialValues = this.getInitialValues({
enabled,
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,
serve_plain_dns,
});
const validateConfig = useCallback((values) => {
const submitValues = getSubmitValues(values);
return (
<div className="encryption">
<PageTitle title={t('encryption_settings')} />
if (submitValues.enabled) {
validateTlsConfig(submitValues);
}
}, []);
{encryption.processing && <Loading />}
{!encryption.processing && (
<Card
title={t('encryption_title')}
subtitle={t('encryption_desc')}
bodyType="card-body box-body--settings">
<Form
initialValues={initialValues}
onSubmit={this.handleFormSubmit}
onChange={this.handleFormChange}
setTlsConfig={this.props.setTlsConfig}
validateTlsConfig={this.props.validateTlsConfig}
{...this.props.encryption}
/>
</Card>
)}
</div>
);
}
}
const debouncedConfigValidation = useMemo(() => debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]);
export default withTranslation()(Encryption);
return (
<div className="encryption">
<PageTitle title={t('encryption_settings')} />
{encryption.processing ? (
<Loading />
) : (
<Card
title={t('encryption_title')}
subtitle={t('encryption_desc')}
bodyType="card-body box-body--settings">
<Form
initialValues={initialValues}
onSubmit={handleFormSubmit}
debouncedConfigValidation={debouncedConfigValidation}
setTlsConfig={setTlsConfig}
validateTlsConfig={validateTlsConfig}
encryption={encryption}
/>
</Card>
)}
</div>
);
};

View File

@@ -1,85 +0,0 @@
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { CheckboxField, toNumber } from '../../../helpers/form';
import { FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK, FORM_NAME } from '../../../helpers/constants';
const getTitleForInterval = (interval: any, t: any) => {
if (interval === 0) {
return t('disabled');
}
if (interval === 72 || interval === 168) {
return t('interval_days', { count: interval / 24 });
}
return t('interval_hours', { count: interval });
};
const getIntervalSelect = (processing: any, t: any, handleChange: any, toNumber: any) => (
<Field
name="interval"
className="custom-select"
component="select"
onChange={handleChange}
normalize={toNumber}
disabled={processing}>
{FILTERS_INTERVALS_HOURS.map((interval) => (
<option value={interval} key={interval}>
{getTitleForInterval(interval, t)}
</option>
))}
</Field>
);
interface FormProps {
handleSubmit: (...args: unknown[]) => string;
handleChange?: (...args: unknown[]) => unknown;
change: (...args: unknown[]) => unknown;
submitting: boolean;
invalid: boolean;
processing: boolean;
t: (...args: unknown[]) => string;
}
const Form = (props: FormProps) => {
const { handleSubmit, handleChange, processing, t } = props;
const components = {
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
};
return (
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings">
<Field
name="enabled"
type="checkbox"
modifier="checkbox--settings"
component={CheckboxField}
placeholder={t('block_domain_use_filters_and_hosts')}
subtitle={<Trans components={components}>filters_block_toggle_hint</Trans>}
onChange={handleChange}
disabled={processing}
/>
</div>
</div>
<div className="col-12 col-md-5">
<div className="form__group form__group--inner mb-5">
<label className="form__label">
<Trans>filters_interval</Trans>
</label>
{getIntervalSelect(processing, t, handleChange, toNumber)}
</div>
</div>
</div>
</form>
);
};
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.FILTER_CONFIG })])(Form);

View File

@@ -1,39 +1,115 @@
import React from 'react';
import { withTranslation } from 'react-i18next';
import debounce from 'lodash/debounce';
import React, { useEffect, useRef } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { DEBOUNCE_TIMEOUT } from '../../../helpers/constants';
import i18next from 'i18next';
import { toNumber } from '../../../helpers/form';
import { DAY, FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Select } from '../../ui/Controls/Select';
import Form from './Form';
const THREE_DAYS_INTERVAL = DAY * 3;
const SEVEN_DAYS_INTERVAL = DAY * 7;
import { getObjDiff } from '../../../helpers/helpers';
const getTitleForInterval = (interval: number) => {
if (interval === 0) {
return i18next.t('disabled');
}
interface FiltersConfigProps {
initialValues: object;
processing: boolean;
setFiltersConfig: (...args: unknown[]) => unknown;
t: (...args: unknown[]) => string;
}
if (interval === THREE_DAYS_INTERVAL || interval === SEVEN_DAYS_INTERVAL) {
return i18next.t('interval_days', { count: interval / DAY });
}
const FiltersConfig = (props: FiltersConfigProps) => {
const { initialValues, processing } = props;
const handleFormChange = debounce((values) => {
const diff = getObjDiff(initialValues, values);
if (Object.values(diff).length > 0) {
props.setFiltersConfig(values);
}
}, DEBOUNCE_TIMEOUT);
return (
<Form
initialValues={initialValues}
onSubmit={handleFormChange}
onChange={handleFormChange}
processing={processing}
/>
);
return i18next.t('interval_hours', { count: interval });
};
export default withTranslation()(FiltersConfig);
export type FormValues = {
enabled: boolean;
interval: number;
};
type Props = {
initialValues: FormValues;
setFiltersConfig: (values: FormValues) => void;
processing: boolean;
};
export const FiltersConfig = ({ initialValues, setFiltersConfig, processing }: Props) => {
const { t } = useTranslation();
const prevFormValuesRef = useRef<FormValues>(initialValues);
const { watch, control } = useForm({
mode: 'onBlur',
defaultValues: initialValues,
});
const formValues = watch();
useEffect(() => {
const prevFormValues = prevFormValuesRef.current;
if (JSON.stringify(prevFormValues) !== JSON.stringify(formValues)) {
setFiltersConfig(formValues);
prevFormValuesRef.current = formValues;
}
}, [formValues]);
const components = {
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
};
return (
<>
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings">
<Controller
name="enabled"
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="filters_enabled"
title={t('block_domain_use_filters_and_hosts')}
disabled={processing}
/>
)}
/>
<p>
<Trans components={components}>filters_block_toggle_hint</Trans>
</p>
</div>
</div>
<div className="col-12 col-md-5">
<div className="form__group form__group--inner mb-5">
<label className="form__label">
<Trans>filters_interval</Trans>
</label>
<Controller
name="interval"
control={control}
render={({ field }) => (
<Select
{...field}
data-testid="filters_interval"
disabled={processing}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}>
{FILTERS_INTERVALS_HOURS.map((interval) => (
<option value={interval} key={interval}>
{getTitleForInterval(interval)}
</option>
))}
</Select>
)}
/>
</div>
</div>
</div>
</>
);
};

View File

@@ -1,147 +1,182 @@
import React, { useEffect } from 'react';
import { change, Field, formValueSelector, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import {
CheckboxField,
toFloatNumber,
renderTextareaField,
renderInputField,
renderRadioField,
} from '../../../helpers/form';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { Controller, useForm } from 'react-hook-form';
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import {
FORM_NAME,
QUERY_LOG_INTERVALS_DAYS,
HOUR,
DAY,
RETENTION_CUSTOM,
RETENTION_CUSTOM_INPUT,
RETENTION_RANGE,
CUSTOM_INTERVAL,
} from '../../../helpers/constants';
import { QUERY_LOG_INTERVALS_DAYS, HOUR, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
import { Textarea } from '../../ui/Controls/Textarea';
const getIntervalTitle = (interval: any, t: any) => {
const getIntervalTitle = (interval: number) => {
switch (interval) {
case RETENTION_CUSTOM:
return t('settings_custom');
return i18next.t('settings_custom');
case 6 * HOUR:
return t('interval_6_hour');
return i18next.t('interval_6_hour');
case DAY:
return t('interval_24_hour');
return i18next.t('interval_24_hour');
default:
return t('interval_days', { count: interval / DAY });
return i18next.t('interval_days', { count: interval / DAY });
}
};
const getIntervalFields = (processing: any, t: any, toNumber: any) =>
QUERY_LOG_INTERVALS_DAYS.map((interval) => (
<Field
key={interval}
name="interval"
type="radio"
component={renderRadioField}
value={interval}
placeholder={getIntervalTitle(interval, t)}
normalize={toNumber}
disabled={processing}
/>
));
export type FormValues = {
enabled: boolean;
anonymize_client_ip: boolean;
interval: number;
customInterval?: number | null;
ignored: string;
};
interface FormProps {
handleSubmit: (...args: unknown[]) => string;
handleClear: (...args: unknown[]) => unknown;
submitting: boolean;
invalid: boolean;
type Props = {
initialValues: Partial<FormValues>;
processing: boolean;
processingClear: boolean;
t: (...args: unknown[]) => string;
interval?: number;
customInterval?: number;
dispatch: (...args: unknown[]) => unknown;
}
processingReset: boolean;
onSubmit: (values: FormValues) => void;
onReset: () => void;
};
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
const { t } = useTranslation();
let Form = (props: FormProps) => {
const {
handleSubmit,
submitting,
invalid,
processing,
processingClear,
handleClear,
t,
interval,
customInterval,
dispatch,
} = props;
watch,
setValue,
control,
formState: { isSubmitting },
} = useForm<FormValues>({
mode: 'onBlur',
defaultValues: {
enabled: initialValues.enabled || false,
anonymize_client_ip: initialValues.anonymize_client_ip || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
},
});
const intervalValue = watch('interval');
const customIntervalValue = watch('customInterval');
useEffect(() => {
if (QUERY_LOG_INTERVALS_DAYS.includes(interval)) {
dispatch(change(FORM_NAME.LOG_CONFIG, CUSTOM_INTERVAL, null));
if (QUERY_LOG_INTERVALS_DAYS.includes(intervalValue)) {
setValue('customInterval', null);
}
}, [interval]);
}, [intervalValue]);
const onSubmitForm = (data: FormValues) => {
onSubmit(data);
};
const handleIgnoredBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
const trimmed = trimLinesAndRemoveEmpty(e.target.value);
setValue('ignored', trimmed);
};
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<div className="form__group form__group--settings">
<Field
<Controller
name="enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('query_log_enable')}
disabled={processing}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="logs_enabled"
title={t('query_log_enable')}
disabled={processing}
/>
)}
/>
</div>
<div className="form__group form__group--settings">
<Field
<Controller
name="anonymize_client_ip"
type="checkbox"
component={CheckboxField}
placeholder={t('anonymize_client_ip')}
subtitle={t('anonymize_client_ip_desc')}
disabled={processing}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="logs_anonymize_client_ip"
title={t('anonymize_client_ip')}
subtitle={t('anonymize_client_ip_desc')}
disabled={processing}
/>
)}
/>
</div>
<label className="form__label">
<div className="form__label">
<Trans>query_log_retention</Trans>
</label>
</div>
<div className="form__group form__group--settings">
<div className="custom-controls-stacked">
<Field
key={RETENTION_CUSTOM}
name="interval"
type="radio"
component={renderRadioField}
value={QUERY_LOG_INTERVALS_DAYS.includes(interval) ? RETENTION_CUSTOM : interval}
placeholder={getIntervalTitle(RETENTION_CUSTOM, t)}
normalize={toFloatNumber}
disabled={processing}
/>
{!QUERY_LOG_INTERVALS_DAYS.includes(interval) && (
<label className="custom-control custom-radio">
<input
type="radio"
data-testid="logs_config_interval"
className="custom-control-input"
disabled={processing}
checked={!QUERY_LOG_INTERVALS_DAYS.includes(intervalValue)}
value={RETENTION_CUSTOM}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<span className="custom-control-label">{getIntervalTitle(RETENTION_CUSTOM)}</span>
</label>
{!QUERY_LOG_INTERVALS_DAYS.includes(intervalValue) && (
<div className="form__group--input">
<div className="form__desc form__desc--top">{t('custom_rotation_input')}</div>
<Field
key={RETENTION_CUSTOM_INPUT}
name={CUSTOM_INTERVAL}
type="number"
className="form-control"
component={renderInputField}
disabled={processing}
normalize={toFloatNumber}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
<Controller
name="customInterval"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="logs_config_custom_interval"
disabled={processing}
error={fieldState.error?.message}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
)}
{getIntervalFields(processing, t, toFloatNumber)}
{QUERY_LOG_INTERVALS_DAYS.map((interval) => (
<label key={interval} className="custom-control custom-radio">
<input
type="radio"
className="custom-control-input"
data-testid={`logs_config_${interval}`}
disabled={processing}
value={interval}
checked={intervalValue === interval}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<span className="custom-control-label">{getIntervalTitle(interval)}</span>
</label>
))}
</div>
</div>
@@ -154,51 +189,41 @@ let Form = (props: FormProps) => {
</div>
<div className="form__group form__group--settings">
<Field
<Controller
name="ignored"
type="textarea"
className="form-control form-control--textarea font-monospace text-input"
component={renderTextareaField}
placeholder={t('ignore_domains')}
disabled={processing}
normalizeOnBlur={trimLinesAndRemoveEmpty}
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
data-testid="logs_config_ingored"
placeholder={t('ignore_domains')}
className="text-input"
disabled={processing}
error={fieldState.error?.message}
onBlur={handleIgnoredBlur}
/>
)}
/>
</div>
<div className="mt-5">
<button
type="submit"
data-testid="logs_config_save"
className="btn btn-success btn-standard btn-large"
disabled={
submitting ||
invalid ||
processing ||
(!QUERY_LOG_INTERVALS_DAYS.includes(interval) && !customInterval)
}>
disabled={disableSubmit}>
<Trans>save_btn</Trans>
</button>
<button
type="button"
data-testid="logs_config_clear"
className="btn btn-outline-secondary btn-standard form__button"
onClick={() => handleClear()}
disabled={processingClear}>
onClick={onReset}
disabled={processingReset}>
<Trans>query_log_clear</Trans>
</button>
</div>
</form>
);
};
const selector = formValueSelector(FORM_NAME.LOG_CONFIG);
Form = connect((state) => {
const interval = selector(state, 'interval');
const customInterval = selector(state, CUSTOM_INTERVAL);
return {
interval,
customInterval,
};
})(Form);
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.LOG_CONFIG })])(Form);

View File

@@ -3,7 +3,7 @@ import { withTranslation } from 'react-i18next';
import Card from '../../ui/Card';
import Form from './Form';
import { Form, FormValues } from './Form';
import { HOUR } from '../../../helpers/constants';
interface LogsConfigProps {
@@ -20,7 +20,7 @@ interface LogsConfigProps {
}
class LogsConfig extends Component<LogsConfigProps> {
handleFormSubmit = (values: any) => {
handleFormSubmit = (values: FormValues) => {
const { t, interval: prevInterval } = this.props;
const { interval, customInterval, ...rest } = values;
@@ -53,19 +53,12 @@ class LogsConfig extends Component<LogsConfigProps> {
render() {
const {
t,
enabled,
interval,
processing,
processingClear,
anonymize_client_ip,
ignored,
customInterval,
} = this.props;
@@ -80,10 +73,10 @@ class LogsConfig extends Component<LogsConfigProps> {
anonymize_client_ip,
ignored: ignored?.join('\n'),
}}
onSubmit={this.handleFormSubmit}
processing={processing}
processingClear={processingClear}
handleClear={this.handleClear}
processingReset={processingClear}
onSubmit={this.handleFormSubmit}
onReset={this.handleClear}
/>
</div>
</Card>

View File

@@ -63,6 +63,7 @@
}
.form__message {
margin-top: 4px;
font-size: 11px;
}
@@ -97,6 +98,10 @@
margin: 0 0 8px;
}
.form__label {
margin-bottom: 8px;
}
.form__label--bold {
font-weight: 700;
}

View File

@@ -1,90 +1,101 @@
import React, { useEffect } from 'react';
import { change, Field, formValueSelector, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { connect } from 'react-redux';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import {
renderRadioField,
toNumber,
CheckboxField,
renderTextareaField,
toFloatNumber,
renderInputField,
} from '../../../helpers/form';
import {
FORM_NAME,
STATS_INTERVALS_DAYS,
DAY,
RETENTION_CUSTOM,
RETENTION_CUSTOM_INPUT,
CUSTOM_INTERVAL,
RETENTION_RANGE,
} from '../../../helpers/constants';
import { Controller, useForm } from 'react-hook-form';
import { STATS_INTERVALS_DAYS, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
import { Input } from '../../ui/Controls/Input';
import { toNumber } from '../../../helpers/form';
import { Textarea } from '../../ui/Controls/Textarea';
const getIntervalTitle = (intervalMs: any, t: any) => {
switch (intervalMs) {
const getIntervalTitle = (interval: any) => {
switch (interval) {
case RETENTION_CUSTOM:
return t('settings_custom');
return i18next.t('settings_custom');
case DAY:
return t('interval_24_hour');
return i18next.t('interval_24_hour');
default:
return t('interval_days', { count: intervalMs / DAY });
return i18next.t('interval_days', { count: interval / DAY });
}
};
interface FormProps {
handleSubmit: (...args: unknown[]) => string;
handleReset: (...args: unknown[]) => string;
change: (...args: unknown[]) => unknown;
submitting: boolean;
invalid: boolean;
export type FormValues = {
enabled: boolean;
interval: number;
customInterval?: number | null;
ignored: string;
};
const defaultFormValues = {
enabled: false,
interval: DAY,
customInterval: null,
ignored: '',
};
type Props = {
initialValues: FormValues;
processing: boolean;
processingReset: boolean;
t: (...args: unknown[]) => string;
interval?: number;
customInterval?: number;
dispatch: (...args: unknown[]) => unknown;
}
onSubmit: (values: FormValues) => void;
onReset: () => void;
};
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
const { t } = useTranslation();
let Form = (props: FormProps) => {
const {
handleSubmit,
processing,
submitting,
invalid,
handleReset,
processingReset,
t,
interval,
customInterval,
dispatch,
} = props;
watch,
setValue,
control,
formState: { isSubmitting },
} = useForm<FormValues>({
mode: 'onBlur',
defaultValues: {
...defaultFormValues,
...initialValues,
},
});
const intervalValue = watch('interval');
const customIntervalValue = watch('customInterval');
useEffect(() => {
if (STATS_INTERVALS_DAYS.includes(interval)) {
dispatch(change(FORM_NAME.STATS_CONFIG, CUSTOM_INTERVAL, null));
if (STATS_INTERVALS_DAYS.includes(intervalValue)) {
setValue('customInterval', null);
}
}, [interval]);
}, [intervalValue]);
const onSubmitForm = (data: FormValues) => {
onSubmit(data);
};
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
return (
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit(onSubmitForm)}>
<div className="form__group form__group--settings">
<Field
<Controller
name="enabled"
type="checkbox"
component={CheckboxField}
placeholder={t('statistics_enable')}
disabled={processing}
control={control}
render={({ field }) => (
<Checkbox
{...field}
data-testid="stats_config_enabled"
title={t('statistics_enable')}
disabled={processing}
/>
)}
/>
</div>
<label className="form__label form__label--with-desc">
<div className="form__label form__label--with-desc">
<Trans>statistics_retention</Trans>
</label>
</div>
<div className="form__desc form__desc--top">
<Trans>statistics_retention_desc</Trans>
@@ -92,85 +103,105 @@ let Form = (props: FormProps) => {
<div className="form__group form__group--settings mt-2">
<div className="custom-controls-stacked">
<Field
key={RETENTION_CUSTOM}
name="interval"
type="radio"
component={renderRadioField}
value={STATS_INTERVALS_DAYS.includes(interval) ? RETENTION_CUSTOM : interval}
placeholder={getIntervalTitle(RETENTION_CUSTOM, t)}
normalize={toFloatNumber}
disabled={processing}
/>
{!STATS_INTERVALS_DAYS.includes(interval) && (
<div className="form__group--input">
<div className="form__desc form__desc--top">{t('custom_retention_input')}</div>
<label className="custom-control custom-radio">
<input
type="radio"
data-testid="stats_config_interval"
className="custom-control-input"
disabled={processing}
checked={!STATS_INTERVALS_DAYS.includes(intervalValue)}
value={RETENTION_CUSTOM}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<Field
key={RETENTION_CUSTOM_INPUT}
name={CUSTOM_INTERVAL}
type="number"
className="form-control"
component={renderInputField}
disabled={processing}
normalize={toFloatNumber}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
<span className="custom-control-label">{getIntervalTitle(RETENTION_CUSTOM)}</span>
</label>
{!STATS_INTERVALS_DAYS.includes(intervalValue) && (
<div className="form__group--input">
<div className="form__desc form__desc--top">{i18next.t('custom_retention_input')}</div>
<Controller
name="customInterval"
control={control}
render={({ field, fieldState }) => (
<Input
{...field}
data-testid="stats_config_custom_interval"
disabled={processing}
error={fieldState.error?.message}
min={RETENTION_RANGE.MIN}
max={RETENTION_RANGE.MAX}
onChange={(e) => {
const { value } = e.target;
field.onChange(toNumber(value));
}}
/>
)}
/>
</div>
)}
{STATS_INTERVALS_DAYS.map((interval) => (
<Field
key={interval}
name="interval"
type="radio"
component={renderRadioField}
value={interval}
placeholder={getIntervalTitle(interval, t)}
normalize={toNumber}
disabled={processing}
/>
<label key={interval} className="custom-control custom-radio">
<input
type="radio"
className="custom-control-input"
disabled={processing}
value={interval}
checked={intervalValue === interval}
onChange={(e) => {
setValue('interval', parseInt(e.target.value, 10));
}}
/>
<span className="custom-control-label">{getIntervalTitle(interval)}</span>
</label>
))}
</div>
</div>
<label className="form__label form__label--with-desc">
<div className="form__label form__label--with-desc">
<Trans>ignore_domains_title</Trans>
</label>
</div>
<div className="form__desc form__desc--top">
<Trans>ignore_domains_desc_stats</Trans>
</div>
<div className="form__group form__group--settings">
<Field
<Controller
name="ignored"
type="textarea"
className="form-control form-control--textarea font-monospace text-input"
component={renderTextareaField}
placeholder={t('ignore_domains')}
disabled={processing}
normalizeOnBlur={trimLinesAndRemoveEmpty}
control={control}
render={({ field, fieldState }) => (
<Textarea
{...field}
data-testid="stats_config_ignored"
placeholder={t('ignore_domains')}
className="text-input"
disabled={processing}
error={fieldState.error?.message}
trimOnBlur
/>
)}
/>
</div>
<div className="mt-5">
<button
type="submit"
data-testid="stats_config_save"
className="btn btn-success btn-standard btn-large"
disabled={
submitting ||
invalid ||
processing ||
(!STATS_INTERVALS_DAYS.includes(interval) && !customInterval)
}>
disabled={disableSubmit}>
<Trans>save_btn</Trans>
</button>
<button
type="button"
data-testid="stats_config_clear"
className="btn btn-outline-secondary btn-standard form__button"
onClick={() => handleReset()}
onClick={onReset}
disabled={processingReset}>
<Trans>statistics_clear</Trans>
</button>
@@ -178,16 +209,3 @@ let Form = (props: FormProps) => {
</form>
);
};
const selector = formValueSelector(FORM_NAME.STATS_CONFIG);
Form = connect((state) => {
const interval = selector(state, 'interval');
const customInterval = selector(state, CUSTOM_INTERVAL);
return {
interval,
customInterval,
};
})(Form);
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.STATS_CONFIG })])(Form);

Some files were not shown because too many files have changed in this diff Show More