Compare commits

...

108 Commits

Author SHA1 Message Date
Simon Zolin
f85de51452 MITM proxy 2020-08-18 19:23:33 +03:00
Simon Zolin
c3123473cf * DNS: resolve host names from DHCP: improve
#1956

Squashed commit of the following:

commit efeacd92b8b82a9a0a0cce8c5648f2d024b4bc9e
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Tue Aug 18 13:54:15 2020 +0300

    * DNS: resolve host names from DHCP: improve

    . Require a valid host name from DHCP lease
    . Use lower-case names
2020-08-18 17:40:36 +03:00
Simon Zolin
8d0c8ad438 + DNS: resolve host names to IP addresses leased by AGH DHCP server
Close #1956

Squashed commit of the following:

commit 21f11632c871e9c17faa77f9cd6a7aa836559779
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Aug 17 19:54:24 2020 +0300

    + DNS: resolve host names to IP addresses leased by AGH DHCP server
2020-08-18 12:36:52 +03:00
Andrey Meshkov
1e2e965ea7 Merge branch 'master' of ssh://bit.adguard.com:7999/dns/adguard-home 2020-08-14 19:30:44 +03:00
Andrey Meshkov
0b539ced92 * : different version url for edge builds
Closes: #2023
2020-08-14 19:30:40 +03:00
Simon Zolin
9e09dffbc3 - dns: limit the number of active goroutines for incoming requests processing
Close #2015

Squashed commit of the following:

commit 90ba06f1fce22a452b4d61db62bd950b976debd1
Merge: 9494b29b 473d8818
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Aug 14 19:14:26 2020 +0300

    Merge remote-tracking branch 'origin/master' into max-go

commit 9494b29b65ae8fe593a487984bed051aa78e4ff9
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Aug 14 17:03:00 2020 +0300

    + max_goroutines setting

commit 87071a5e0ed43be192a7755fb25764cd4519da5a
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Aug 14 15:29:00 2020 +0300

    - dns: limit the number of active goroutines for incoming requests processing
2020-08-14 19:27:36 +03:00
Andrey Meshkov
473d881871 Merge: - client: Fix DNS settings
Merge in DNS/adguard-home from fix/2021 to master

* commit 'a1ca7862f84b07bde441f4578b1deb025ff79660':
  Fix selector
  - client: Fix DNS settings
2020-08-13 19:09:17 +03:00
ArtemBaskal
a1ca7862f8 Fix selector 2020-08-13 18:15:14 +03:00
ArtemBaskal
8ea1e64c7b - client: Fix DNS settings 2020-08-13 18:12:27 +03:00
Artem Baskal
2f8e34e73b Pull request 734: + client: 1778 Add ip sort function, write unit tests
Close #1778

Squashed commit of the following:

commit ba63e147311799b17deaa97d7a12c2e0ec44a2ed
Merge: 143ba427 705a9d90
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Aug 13 12:00:10 2020 +0300

    Merge branch 'master' into feature/1778

commit 143ba42707da3d7eece9f3e137a8b245f7f7888f
Merge: 483d2ff9 97df1989
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Aug 13 11:16:46 2020 +0300

    Merge branch 'master' into feature/1778

commit 483d2ff9fa3de706d94a647701f1d26a3bcbb217
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 12 13:34:21 2020 +0300

    Always put ipv4 before ipv6 in sort, add invalid input unit tests

commit 26eb41b313785fe7ffaf98a7573cc5eef0dca14f
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 12 11:27:46 2020 +0300

    Rewrite tests: replace ipv4-mapped adresses

commit 4ecf287fd46945effe9ff11cfc9ae49217b9c796
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Aug 11 19:05:15 2020 +0300

    Minor fix

commit 3e5e2a6bb1f2f166619da54e5ade0904fe22a20d
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Aug 11 19:01:26 2020 +0300

    + client: 1778 Add ip sort function, write unit tests
2020-08-13 12:16:52 +03:00
Simon Zolin
705a9d909d * SB/PC: use 4-character hash in request
* use hash prefix as the cache key

Squashed commit of the following:

commit d719a84ee9b9cf43aaab4f53d07451645ea836db
Merge: d9d6d443 97df1989
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Thu Aug 13 11:41:28 2020 +0300

    Merge remote-tracking branch 'origin/master' into sbpc

commit d9d6d44376c44959f2216b08e577d8e5c5f65bff
Merge: 0a8b2483 de92c852
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Thu Aug 13 11:23:14 2020 +0300

    Merge remote-tracking branch 'origin/master' into sbpc

commit 0a8b24839683683a9d327ecf57a7d182b3996b1d
Merge: 0255a24a 9b9902f0
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Aug 12 20:21:22 2020 +0300

    Merge remote-tracking branch 'origin/master' into sbpc

commit 0255a24a191efd2e4ef23d6a00a7a9fed8831730
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Aug 12 17:16:57 2020 +0300

    - TestServerCustomClientUpstream(): fix

commit d2311902f887be9621a9d9312c73f899dd269440
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Aug 12 17:07:12 2020 +0300

    * SB/PC: hard-code Family server IP addresses to prevent from requesting them at runtime

commit ee340108f11f98d49a7af2a7e8a228c25ab1537a
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Aug 12 17:05:57 2020 +0300

    * dnsproxy v0.30.1

commit f5f53ba7116ad525204d00b80352202eee88b78c
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Aug 12 14:53:23 2020 +0300

    minor

commit fb4631e2cd570b0fd5ae26ec2b1890361275a5a8
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Aug 10 20:07:27 2020 +0300

    * SB/PC: implement new cache

commit f9f58461a6efbcfacd798f7640a4645cf1971cb2
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Aug 7 19:31:05 2020 +0300

    doc

commit ed69626a6c119ab1a3b187f5afbd4cef708c3159
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Aug 7 18:25:57 2020 +0300

    * SB/PC: use hostname prefix for cache

commit afa8040c8c0836c7e59e6fb9aaf1caccd132ea8f
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Jul 31 11:19:49 2020 +0300

    * SB/PC: use 4-character hash in request
2020-08-13 11:49:42 +03:00
Artem Baskal
97df19898f Pull request #730: + client: Add Hot Module Replacement
Merge in DNS/adguard-home from feature/hmr to master

Squashed commit of the following:

commit 952ed1955c2a7a32446d99489f137f02eb47c99e
Merge: 83484931 de92c852
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Aug 13 11:02:10 2020 +0300

    Merge branch 'master' into feature/hmr

commit 8348493105d7d63d8b0836a5c272df2b17a6b142
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 15:07:31 2020 +0300

    Remove empty prop types, remove Services empty container

commit b2fe4a30b79d91e482318ee5deea8e49c7038f7e
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 13:56:35 2020 +0300

    Move constants

commit f8be4c18c35193ad77bf5e25f311ad834c1d6870
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 13:19:02 2020 +0300

    Fix Setup bug, update webpack.dev

commit 1d9cc4ddf8af2c979eb707a7f0fc06744eec186c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 12:10:38 2020 +0300

    Review changes

commit a1edb21358def21ed1808b081ffc2f0b6755e3da
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 11:46:58 2020 +0300

    Remove lazy loading, fix updated components

commit 0aa2cf55f8d4206ac9e2f99fc1b990ed8a9c7825
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Aug 4 20:32:19 2020 +0300

    Refactor App component, add lazy loading

commit 3c2ba4772a91ff7b06641dba6c6bf3fdcd2fdf7f
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Aug 4 17:12:41 2020 +0300

    Simplify App hot loading boilerplate, setup lazy loading, update Header

commit 8df3221f315372b066f2ac0c9a1687f1677b8415
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Aug 4 15:16:06 2020 +0300

    + client: Add Hot Module Replacement
2020-08-13 11:15:45 +03:00
Artem Baskal
de92c85256 Pull request #726: - client: 1954 Make menu items position stable
- client: 1954 Make menu items position stable
Close #1954

Squashed commit of the following:

commit 24bc6faa1e45cef79e3ba83ad5d595c305e0c816
Merge: a4b07aae d3f5b407
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Aug 13 10:51:47 2020 +0300

    Merge branch 'master' into fix/1954

commit a4b07aae4b3b56d60cc95f669e6c179659d904ce
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 15:20:26 2020 +0300

    Review changes

commit 250cdc9b10f93664ed2c1f53d57295dba78e6a99
Merge: 32003f19 39f2d5c4
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Aug 5 15:18:40 2020 +0300

    Merge branch 'master' into fix/1954

commit 32003f19c6e2dda056fa6ae51f6721ea350016d1
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Aug 3 13:36:23 2020 +0300

    - client: 1954 Make menu items position stable
2020-08-13 11:01:23 +03:00
Andrey Meshkov
d3f5b40700 Merge: + Makefile: sign release binaries with gpg
Merge in DNS/adguard-home from sign-binaries-v2 to master

* commit '20a0ba5f608c18eff4022279d8ce2791aca97d1b':
  + Makefile: sign release binaries with gpg
2020-08-12 22:51:06 +03:00
Simon Zolin
20a0ba5f60 + Makefile: sign release binaries with gpg
.tar.gz and .zip archives now contain one more file - .sig

UNIX:
./AdGuardHome/:
 ./AdGuardHome/AdGuardHome
 ./AdGuardHome/AdGuardHome.sig
 ...

Windows:
./AdGuardHome/:
 ./AdGuardHome/AdGuardHome.exe
 ./AdGuardHome/AdGuardHome.exe.sig
 ...
2020-08-12 20:18:21 +03:00
Simon Zolin
9b9902f004 * Revert "Update blocked_services.go"
Squashed commit of the following:

commit 567bb4671ddb4f51c13e51094f96e870212137c8
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Tue Aug 11 19:29:31 2020 +0300

    Revert "Update blocked_services.go"

    This reverts commit 4ca4fb8a11.
2020-08-12 13:03:17 +03:00
Andrey Meshkov
020a30fb6d Merge: * locales: update
Merge in DNS/adguard-home from locales to master

* commit '8d10a269edebb007f49123e96f9f2c9f1834f4aa':
  update ru
  * locales: update
2020-08-07 18:31:31 +03:00
Simon Zolin
39f2d5c4ae * readme: update Prerequisites section
Close #1989

* commit '6ce3c52456feb6f13be976e03c42fa87aa26b099':
  * readme: update Prerequisites section
2020-08-04 15:29:09 +03:00
graysky
6ce3c52456 * readme: update Prerequisites section 2020-08-04 14:30:01 +03:00
Simon Zolin
5188da60cf * Makefile: support running with multiple parallel jobs (e.g. -j8)
* commit 'e57cbc36d944b6c4ebe99f397eb55976d786d5f8':
  * Makefile: support running with multiple parallel jobs (e.g. -j8)
2020-08-03 20:08:31 +03:00
Simon Zolin
e57cbc36d9 * Makefile: support running with multiple parallel jobs (e.g. -j8) 2020-08-03 19:13:51 +03:00
Ildar Kamalov
57e43a66c3 Merge: - client: fix log filters and guide tab styles
Closes #1951

* commit '9fecab8675b3496cb6fa5f03084e0be7c364a263':
  - client: fix log filters styles
  - client: fix guide tab styles
2020-08-03 15:45:53 +03:00
Ildar Kamalov
4f3d503916 Merge: - client: fix query log on edge legacy
Closes #1976

* commit '552280e9a36a15c7c2347af75d97fe2943f80093':
  - client: fix query log not load on edge legacy
2020-08-03 15:45:31 +03:00
Ildar Kamalov
6bb6c700d6 Merge: - client: fix empty log error
Closes #1983

* commit '3ff0c964dc05c0b393b6b7140cf59bffb8f70806':
  - client: fix empty log error
2020-08-03 15:44:55 +03:00
Ildar Kamalov
ed76a3cb8b Merge: - client: check touch events for tooltips
Closes #1922

* commit 'ce21514246dfc89f1178d9346769317f1fff4d05':
  - client: check touch events for tooltips
  - client: tooltip show delay
2020-08-03 15:41:46 +03:00
Simon Zolin
335d62b08e - "set_url": couldn't set a new path for filter local file
Close #1984

* commit '99625da1e44b4e6686f3c4b29526949ef471a100':
  - "set_url": couldn't set a new path for filter local file
2020-08-03 15:19:31 +03:00
Simon Zolin
99625da1e4 - "set_url": couldn't set a new path for filter local file 2020-08-03 14:09:47 +03:00
Ildar Kamalov
9fecab8675 - client: fix log filters styles 2020-08-01 17:13:48 +03:00
Ildar Kamalov
b9aa969a56 - client: fix guide tab styles 2020-08-01 17:12:38 +03:00
Ildar Kamalov
ce21514246 - client: check touch events for tooltips 2020-08-01 16:25:56 +03:00
Ildar Kamalov
3ff0c964dc - client: fix empty log error 2020-08-01 16:14:50 +03:00
Ildar Kamalov
552280e9a3 - client: fix query log not load on edge legacy 2020-08-01 15:35:57 +03:00
Ildar Kamalov
d154456ae5 - client: tooltip show delay 2020-08-01 13:38:26 +03:00
Andrey Meshkov
3cecd6f090 added more info about contribution 2020-07-31 11:31:47 +03:00
Simon Zolin
dc1fc82b9e * client: Corrects the provided homepage for "The Big List of Hacked Malware Web Sites"
Close #1940 Close #1948

* commit 'cecf84836494a432c59157295c1aaf8663df1a42':
  * client: Corrects the provided homepage for "The Big List of Hacked Malware Web Sites"
2020-07-31 10:17:37 +03:00
Simon Zolin
a033b68bfd * Update blocked_services.go
Close #1863

* commit '4ca4fb8a113dda47336038ced905fa87a9fa380f':
  Update blocked_services.go
2020-07-31 10:14:14 +03:00
mHatsune
4ca4fb8a11 Update blocked_services.go 2020-07-30 14:25:01 +03:00
Simon Zolin
8d10a269ed update ru 2020-07-27 13:13:14 +03:00
Simon Zolin
a536357427 * locales: update 2020-07-27 12:59:08 +03:00
Imre Kristoffer Eilertsen
cecf848364 * client: Corrects the provided homepage for "The Big List of Hacked Malware Web Sites" 2020-07-27 12:28:30 +03:00
Andrey Meshkov
ddb9a2e872 readme - update stable -> edge 2020-07-24 19:40:03 +03:00
Ildar Kamalov
cfdfd250a0 Merge: - client: show filter name on mobile for whitelisted entry
Closes #1934

Squashed commit of the following:

commit 5127467acfeec1d8a37736ecce7e92ff9f7b30c9
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 19:10:10 2020 +0300

    - client: show filter name if rule present

commit f3a3399682b2d842ec399f856e2b586aae9b3ec5
Merge: 63f4c965 3c91b8c7
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 19:09:32 2020 +0300

    Merge branch 'fix/1934' of ssh://bit.adguard.com:7999/dns/adguard-home into fix/1934

commit 3c91b8c70c0e0830a180ed1445e7c0994bc300ad
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 18:54:01 2020 +0300

    - client: show filter name for whitelisted entry

commit 63f4c9655ed5efe2bf35f1174c6c2b0af52403b5
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 18:54:01 2020 +0300

    - client: show filter name for whitelisted entry
2020-07-24 19:19:51 +03:00
Ildar Kamalov
cb01d05ef4 Merge: - client: fix host validation rule
Closes #1926

* commit 'c7e7b76cecb0ba14666ebf55dd665809a6df9612':
  - client: fix escape
  - client: fix host validation rule
  - client: fix host validation rule
  - client: fix host validation rule
2020-07-24 18:54:45 +03:00
Ildar Kamalov
c7e7b76cec - client: fix escape 2020-07-24 18:42:49 +03:00
Ildar Kamalov
f5128d27f1 - client: fix host validation rule 2020-07-24 18:36:59 +03:00
Ildar Kamalov
005f8fb279 - client: fix host validation rule 2020-07-24 18:35:03 +03:00
Ildar Kamalov
244fe093cd - client: fix host validation rule 2020-07-24 17:45:24 +03:00
Ildar Kamalov
ff9d1c234c Merge: - client: fix ui issues with table pagination and whois
Closes #1927

* commit '1c9d3acaa8864bca3e17dc3a313630dbc9ebea60':
  - client: fix whois wrap and vertical alignment
  - client: revert table paginations
2020-07-24 17:28:42 +03:00
Ildar Kamalov
1c9d3acaa8 - client: fix whois wrap and vertical alignment 2020-07-24 17:17:11 +03:00
Ildar Kamalov
0dab36a108 - client: revert table paginations 2020-07-24 16:45:59 +03:00
Ildar Kamalov
611ed94884 Merge: - client: show tooltips on tap for mobile devices
Closes #1922

* commit 'dd2c9d96e7f164e5295cf9ca7b68695dd7bc46c1':
  - client: show tooltips on tap for mobile devices
2020-07-24 16:29:14 +03:00
Ildar Kamalov
22935c5fed Merge: - client: show all available information in the response tooltip
Closes #1916

* commit '1ab650bb86f4b3103b8c650c6af862c6f1881694':
  - client: show all available information in the response tooltip
2020-07-24 16:27:58 +03:00
Simon Zolin
4a8dcbeeed Merge: - rewrites: return NOERROR without A records instead of NXDOMAIN
Close #1918

* commit 'ad4e85d8f50e1e905329f687c2748d28449d39c4':
  add test
  - rewrites: return NOERROR without A records instead of NXDOMAIN
2020-07-24 16:26:52 +03:00
Ildar Kamalov
1ab650bb86 - client: show all available information in the response tooltip 2020-07-24 16:14:21 +03:00
Ildar Kamalov
4743743b1f Merge: - client: fix container padding
Closes #1921

Squashed commit of the following:

commit b893ddf31e2251c9d0a77d14c0bc86d1d8608c0a
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 14:41:50 2020 +0300

    - client: fix container padding

commit 111f53bed2b944febc1f55de24e99cd49801120b
Author: Ildar Kamalov <ik@adguard.com>
Date:   Fri Jul 24 14:40:36 2020 +0300

    - client: npm audit fix
2020-07-24 16:13:58 +03:00
Ildar Kamalov
dd2c9d96e7 - client: show tooltips on tap for mobile devices 2020-07-24 16:11:45 +03:00
Andrey Meshkov
946bda37a3 - (ui): improved client access check performance
This is still not ideal and if the disallowed clients list is huge
enough, the slowdown is considerable. But it's at least x10 or x100
times faster than it was.

Closes: #1920
2020-07-24 13:45:46 +03:00
Simon Zolin
ad4e85d8f5 add test 2020-07-24 13:27:14 +03:00
Simon Zolin
4b9ab97271 - rewrites: return NOERROR without A records instead of NXDOMAIN
For rule "host -> ipv6" we return "ipv6" address for AAAA request
 and empty answer for A request
2020-07-24 13:18:05 +03:00
Andrey Meshkov
d2bf1e176e Update urlfilter to v0.11.2 2020-07-23 20:48:50 +03:00
Simon Zolin
ffeb88ac0c * openapi: update changelog
Squashed commit of the following:

commit a42d1762c2ffbfe65d728a5e76a3d0c5cc03a9c5
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Thu Jul 23 19:17:05 2020 +0300

    * openapi: update changelog
2020-07-23 19:25:42 +03:00
Simon Zolin
c71b8d3ad2 * use urlfilter v0.11.1
Squashed commit of the following:

commit 35d2c34355b093ec1daa40f156809a7cae089c20
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Thu Jul 23 18:00:43 2020 +0300

    * use urlfilter v0.11.1
2020-07-23 18:09:39 +03:00
Andrey Meshkov
01957bf503 * updater: cut 'v' prefix when comparing SelfUpdateMinVersion
Fix #1908

* commit '1e5419714ddcaab0913162460474dea8959529be':
  * (ui): fix the version check - strip the v prefix
  * updater: cut 'v' prefix when comparing SelfUpdateMinVersion
2020-07-23 13:21:12 +03:00
Andrey Meshkov
1e5419714d * (ui): fix the version check - strip the v prefix 2020-07-23 12:27:14 +03:00
Simon Zolin
4f4a688ee6 * updater: cut 'v' prefix when comparing SelfUpdateMinVersion 2020-07-23 12:17:20 +03:00
Andrey Meshkov
ccf5494f67 Fix edge channel version string 2020-07-22 21:29:22 +03:00
Andrey Meshkov
f2edcca54b *(global): update anti-ad filter URL
Closes: #1903
2020-07-22 20:59:42 +03:00
Andrey Meshkov
b4aa791513 Merge: Refactor auto-update
Merge in DNS/adguard-home from feature-auto-update to master

* commit '6d5d1833111f65a69b208c609eda548b15db3606':
  Fix minor issues
  * use 'update' package
  minor
  + update package: perform update tasks
  added version cache
  Auto-update interface
2020-07-22 20:38:21 +03:00
Andrey Meshkov
6d5d183311 Fix minor issues 2020-07-22 20:27:20 +03:00
Simon Zolin
e3ea2528be * use 'update' package 2020-07-22 14:20:14 +03:00
Simon Zolin
117ec4dd43 minor 2020-07-21 19:25:29 +03:00
Simon Zolin
0cc0aec5b3 + update package: perform update tasks 2020-07-21 19:10:39 +03:00
Andrey Meshkov
3c53a2162c added version cache 2020-07-21 12:17:23 +03:00
Andrey Meshkov
1bb183c2aa Auto-update interface 2020-07-20 21:14:07 +03:00
Andrey Meshkov
62ccd3fb41 rollback file unpack 2020-07-20 20:15:05 +03:00
Andrey Meshkov
a409cdc2bb fix tar.gz update 2020-07-20 20:04:54 +03:00
Andrey Meshkov
e0aa24e2b7 fix update check 2020-07-20 18:04:44 +03:00
Andrey Meshkov
0662769696 Fix version change check 2020-07-20 17:29:13 +03:00
Andrey Meshkov
dc237f10a8 Merge: Fix unpacking tar files
* commit '793194db67a9b5f8440f77863a170375d3c7612f':
  Fix unpacking tar files
2020-07-20 17:27:15 +03:00
Andrey Meshkov
4fef0c32cb Merge: Update locales
Squashed commit of the following:

commit 4d14f3a3f483a17ddd6aee3e082560d81802e5bc
Author: Andrey Meshkov <am@adguard.com>
Date:   Mon Jul 20 15:46:54 2020 +0300

    update locales

commit 5d70bef97b98f4c7d8c360db2e0d8c1ce8200b8f
Author: Andrey Meshkov <am@adguard.com>
Date:   Mon Jul 20 15:41:41 2020 +0300

    update locales

commit 35b28b1e606885853e55fa8bcf6693e7c07f6151
Merge: 051f6263 c131ac44
Author: Andrey Meshkov <am@adguard.com>
Date:   Mon Jul 20 15:35:07 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 051f62630f33ca06d814d7e646f58a3e0cdaa074
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:31:51 2020 +0300

    + client: Update locales

commit 7c88f1fbf5721098e92a3765b96b2f169afc7d43
Merge: 6d743bb1 c1e56c83
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:29:47 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 6d743bb15974a85fedb004a3c639b1f849756e2d
Merge: 1f6c5091 7d2c7a61
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:10:51 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 1f6c50915e63c18834d63d3f8535d24a4e894647
Merge: 1a861a98 4df02714
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 11:23:08 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 1a861a98326a749a347c184bb2b9aff663b3dd09
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 11:22:47 2020 +0300

    Update locales

commit 3f4850822c95b1147f39df939eca568f13b2c0dc
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 16:07:40 2020 +0300

    Update locales

commit 0f06bd8f188130e97394c8f20f27b66f263612cc
Merge: 5abae336 38366ba8
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 12:59:22 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 5abae33603e84dc410f77e7ec622314e85e03951
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 12:57:36 2020 +0300

    Update locales

commit 717455ecf42135077a7dae31281b1da0d89b27e3
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 17:45:15 2020 +0300

    Rename label

commit b399ef33eed3c0447d5c651d3b678d482dddbb5b
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 17:43:08 2020 +0300

    Replace 'All responses' with 'All queries'

commit 1f5a62a3d95906057c862b55d2ba91d67b346604
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 16:18:54 2020 +0300

    + client: Update locales
2020-07-20 15:55:15 +03:00
Ildar Kamalov
c131ac445a Merge: - client: fix names of clients in the top clients list
Closes #1893

Squashed commit of the following:

commit e5de0c4f53558e1ad89dc0069192e534f244f120
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jul 20 14:29:23 2020 +0300

    - client: fix names of clients in the top clients list
2020-07-20 15:25:28 +03:00
Simon Zolin
87789679f5 Merge: - Makefile: repack all release archives so they contain AdGuardHome directory
Squashed commit of the following:

commit f519c82f042670e0bc377ef9f8490edce26085d9
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Jul 20 14:41:46 2020 +0300

    fix

commit 890a2c2b7812c2bc69224f88646ff3ab74e1562f
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Jul 20 14:24:34 2020 +0300

    fix

commit 753ab74b34a5ef010eb8b1d364f62d5a9c904d28
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Jul 20 14:06:49 2020 +0300

    fix

commit 160d0ac83ff656d39597711ab02485c1323f7a0e
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Mon Jul 20 13:19:12 2020 +0300

    - Makefile: repack all release archives so they contain AdGuardHome directory
2020-07-20 14:55:56 +03:00
Andrey Meshkov
793194db67 Fix unpacking tar files 2020-07-20 12:32:14 +03:00
Artem Baskal
4175d82279 + client: 1889 Show a link to the FAQ article about Ubuntu when port 53 is in use
Close #1889

Squashed commit of the following:

commit 4fa57f692d0a66d20e9ba9d235ec423e5118db20
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:28:38 2020 +0300

    Revert "Update locales"

    This reverts commit dd88f8047e612022678dfabe8dda446c4b8f201b.

commit dd88f8047e612022678dfabe8dda446c4b8f201b
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:26:00 2020 +0300

    Update locales

commit 954ae2e97e988b53856766100c873e6d40b75123
Merge: 28f205a8 c1e56c83
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:23:50 2020 +0300

    Merge branch 'master' into feature/1889

commit 28f205a858281811af7d708356e10108983c7292
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 15:39:57 2020 +0300

    Rename systemdns-resolved ---> systemd-resolved

commit 3d62e26984f73285a7b788b6f42efcb3376ae562
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 15:15:10 2020 +0300

    Add commas to port 53 locale

commit 457563085186244dca36a80a959e5a9486558299
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 13:34:13 2020 +0300

    minor

commit 7aacf879951f8ab619824730cb28ea5cd2518812
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 13:27:55 2020 +0300

    + client: Show a link to the FAQ article about Ubuntu when port 53 is in use
2020-07-17 17:59:34 +03:00
Artem Baskal
c1e56c837b + client: Update locales
Squashed commit of the following:

commit 6d743bb15974a85fedb004a3c639b1f849756e2d
Merge: 1f6c5091 7d2c7a61
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 17:10:51 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 1f6c50915e63c18834d63d3f8535d24a4e894647
Merge: 1a861a98 4df02714
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 11:23:08 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 1a861a98326a749a347c184bb2b9aff663b3dd09
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 11:22:47 2020 +0300

    Update locales

commit 3f4850822c95b1147f39df939eca568f13b2c0dc
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 16:07:40 2020 +0300

    Update locales

commit 0f06bd8f188130e97394c8f20f27b66f263612cc
Merge: 5abae336 38366ba8
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 12:59:22 2020 +0300

    Merge branch 'master' into feature/update_locales

commit 5abae33603e84dc410f77e7ec622314e85e03951
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 12:57:36 2020 +0300

    Update locales

commit 717455ecf42135077a7dae31281b1da0d89b27e3
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 17:45:15 2020 +0300

    Rename label

commit b399ef33eed3c0447d5c651d3b678d482dddbb5b
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 17:43:08 2020 +0300

    Replace 'All responses' with 'All queries'

commit 1f5a62a3d95906057c862b55d2ba91d67b346604
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 16:18:54 2020 +0300

    + client: Update locales
2020-07-17 17:21:20 +03:00
Artem Baskal
7d2c7a61f1 - client: Use the same tooltip style everywhere
Close #1866

Squashed commit of the following:

commit 3347832caa33b01a0155b212987f02dc4824ab08
Merge: 7766502d d794b11e
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 15:12:45 2020 +0300

    Merge branch 'master' into fix/1866

commit 7766502d4a904ad0a4d240481f7eabf0e25cfb62
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 12:16:59 2020 +0300

    Fix icon color classes

commit 90191bf74b5eb1855c733c226f7acb4e906c7ad9
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 17 11:46:22 2020 +0300

    Use logs icons, use pointer cursor, fix review markup formatting

commit 0ba50fcd956101f5054ce38c2329df3e8abdfcd2
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 18:05:30 2020 +0300

    Use help cursor on tooltips

commit bf4e14afe69f874d29be73d8cd4cfbe240ca0304
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 17:41:47 2020 +0300

    Use tooltip in logs, rename tooltip classes

commit 00568fdc8e8484c5bae67c51ee8189a3a558e219
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 16 17:01:49 2020 +0300

    - client: Use the same tooltip style everywhere
2020-07-17 15:24:39 +03:00
Andrey Meshkov
d794b11e7a Merge: snap desktop icon
* commit '82858474a5b165efb4181975067a36e3668b89da':
  Update adguard-home-web.sh
  Update adguard-home-web.sh
  Add web launcher for desktop users
2020-07-17 10:38:19 +03:00
Andrey Meshkov
82858474a5 Added desktop icon for a snap 2020-07-16 20:25:39 +03:00
Andrey Meshkov
4df02714fd Merge branch 'master' of ssh://bit.adguard.com:7999/dns/adguard-home 2020-07-16 11:12:16 +03:00
Andrey Meshkov
1bce871fcb added instruction on how to build for a different platform 2020-07-16 11:12:12 +03:00
Andrey Meshkov
2d7be0a1e0 Merge: - auth: fix logic with --glinet argument
* commit '177404d15720bc275f356977f991a188c9ba933a':
  - auth: fix logic with --glinet argument
2020-07-16 10:52:07 +03:00
Simon Zolin
177404d157 - auth: fix logic with --glinet argument 2020-07-16 10:15:26 +03:00
Artem Baskal
e46db985e8 - client: Fix query logs UI issues
Close #1828

Squashed commit of the following:

commit bf96b9f2cc99a94a1289c47b04cde136cf0c9f37
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 20:44:22 2020 +0300

    Remove field domain from response tooltip

commit bba35fdbed6d1e2e532c8effaf2da69de3f2c078
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 20:29:24 2020 +0300

    Unify mobile modal

commit 5ee2da41594497fd64eadf0fd64c24afdad94e44
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 19:02:47 2020 +0300

    Delete unnecessary comment

commit ac3a3f13009ad508ddd7eb31aadf7e590a5c2829
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 18:59:44 2020 +0300

    minor

commit 4b1969a53ce2fcfc859c228b27816459bd8bd1d0
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 18:56:51 2020 +0300

    Fix safari mediaQuery change listener issue

commit d85de5c4e90d2460632e593cffe3ceea3137e92c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 18:10:30 2020 +0300

    Fix logs input search markup (for different locales)

commit 6d704399c5379dfda663503b3a5b1d12a92732b2
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 16:05:35 2020 +0300

    Fix whois_info markup, fix domain name overflow

commit 4c900f60a9c6b71b427d968177252eb168c424c0
Merge: a3955c98 38366ba8
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 13:42:43 2020 +0300

    Merge branch 'master' into fix/1828

commit a3955c989a939866c6772b147547344b3f8769c4
Merge: c91c41cb 2759d81a
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 15:14:47 2020 +0300

    Merge branch 'master' into fix/1828

commit c91c41cbc5f616e0af1092424e42b909d2f43f7c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 13:48:54 2020 +0300

    Fix cell overflow

commit 19e1d31a40f2e1bb1189a85b72507bcc364d4e0c
Merge: af31f48c a33164bf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:36:44 2020 +0300

    Merge branch 'master' into fix/1828

commit af31f48c4d2699ebfbd2034711c51499b42e40f5
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 10:45:57 2020 +0300

    minor

commit d9507c5f3f5758e587766ae0fa45f1b9ad703ccf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 18:34:22 2020 +0300

    - client: Fix query logs UI issues
2020-07-15 20:55:13 +03:00
Artem Baskal
38366ba801 Setup pre-commit lint hook
Squashed commit of the following:

commit 02591b74c184faf7f7156e95cf05a78fb0ea22a7
Merge: 4057c8ae a32c1f2e
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 12:36:20 2020 +0300

    Merge branch 'master' into feature/git-hooks

commit 4057c8ae117dfb5de493769dbf1577c8d59035a4
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 20:04:38 2020 +0300

    Review changes

commit 2400ab77d9e0e3f7b62b0ffd64aeccf369ff84cd
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 16:27:14 2020 +0300

    Add lint-js and lint-go to .PHONY

commit 8a4efc2cb4f2d53ebea4b88b8182e4c1eb7812eb
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 15:47:14 2020 +0300

    Run linter only if corresponding file extension is changed

commit 8e2e110e9c9c3f865503cf3c0cd3e31dd4579b71
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 19:07:42 2020 +0300

    Setup pre-commit lint hooks
2020-07-15 12:49:08 +03:00
Artem Baskal
a32c1f2ee0 - client: Fix client Access settings normalization
Close #1820

Squashed commit of the following:

commit 5aadec2e6e126588313ff006d6f95223ba19a526
Merge: a4db6b42 95f41285
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 15 11:15:31 2020 +0300

    Merge branch 'master' into fix/1820

commit a4db6b42ab9cbf43d96c783a72a99e0a2c594108
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 19:08:09 2020 +0300

    Remove textarea comma splitting

commit bb34797aac6602b405941dbd90fe6a81b663bb92
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 18:21:18 2020 +0300

    Fix client Access settings normalization

commit ac4fb536514f54c5722077d78dbbd981c4e906a8
Merge: 0c758ddc b9fca8d0
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 14 18:14:38 2020 +0300

    Merge branch 'master' into fix/1820

commit 0c758ddcd738136b92e6f947a8068ecc59f7ec25
Merge: 15650db3 f5a1f311
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 3 11:22:00 2020 +0300

    Merge branch 'master' into fix/1820

commit 15650db35323009001fd427a74a312705b54ac86
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jun 29 12:01:51 2020 +0300

    '- client: Don't normalise disallowed domains'
2020-07-15 12:35:37 +03:00
Simon Zolin
95f4128551 Merge: - fix 'Check for Updates' button visibility
Close #1878

* commit '61981a927babe1ec771c01c13e2d503e3dc957e5':
  Change getVersionSuccess reducer
  minor
  * POST /control/version.json: change response when update is disabled
2020-07-14 19:28:56 +03:00
ArtemBaskal
61981a927b Change getVersionSuccess reducer 2020-07-14 18:42:24 +03:00
Simon Zolin
54693bb158 minor 2020-07-14 17:10:34 +03:00
Simon Zolin
d38b58cd85 * POST /control/version.json: change response when update is disabled 2020-07-14 17:08:53 +03:00
Andrey Meshkov
b9fca8d0a9 -(global): typo in the makefile 2020-07-14 11:55:26 +03:00
Artem Baskal
da4a1ec23d +client: "Drill down" to activity reports
Close #1625

Squashed commit of the following:

commit a01f12c4e5831c43dbe3ae8a80f4db12077dbb2a
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 15:50:15 2020 +0300

    minor

commit b8ceb17a3b12e47de81af85fa30c2961a4a42fab
Merge: 702c55ed fecf5494
Author: Andrey Meshkov <am@adguard.com>
Date:   Mon Jul 13 15:32:44 2020 +0300

    Merge branch 'feature/1625' of ssh://bit.adguard.com:7999/dns/adguard-home into feature/1625

commit 702c55edc1ba2ab330eda8189498dfff33c92f5f
Author: Andrey Meshkov <am@adguard.com>
Date:   Mon Jul 13 15:32:41 2020 +0300

    fix makefile when there's no gopath

commit fecf5494b8c1719cb70044f336fe99c341802d25
Merge: d4c811f9 8a417604
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 15:30:21 2020 +0300

    Merge branch 'master' into feature/1625

commit d4c811f9630dee448012434e2f50f34ab8b8b899
Merge: b0a037da a33164bf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:35:16 2020 +0300

    Merge branch 'master' into feature/1625

commit b0a037daf48913fd8a4cda16d520835630072520
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:34:42 2020 +0300

    Simplify sync logs action creators

commit eeeb620ae100a554f59783fc2a14fad525ce1a82
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 11:17:08 2020 +0300

    Review changes

commit 4cbc59eec5c794df18d6cb9b33f39091ce7cfde9
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 15:23:37 2020 +0300

    Update tracker tooltip class

commit 0a705301d4726af1c8f7f7a5776b11d338ab1d54
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 13:46:10 2020 +0300

    Replace depricated addListener

commit 2ac0843239853da1725d2e038b5e4cbaef253732
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 13:39:45 2020 +0300

    Validate response_status url param

commit 2178039ebbd0cbe2c0048cb5ab7ad7c7e7571bd1
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 12:58:18 2020 +0300

    Fix setting empty search value, use strict search on drill down, extract refreshFilteredLogs action

commit 4b11c6a34049bd133077bad035d267f87cdec141
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 9 19:41:48 2020 +0300

    Normalize input search

commit 3fded3575b21bdd017723f5e487c268074599e4f
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 9 18:20:05 2020 +0300

    Optimize search

commit 9073e032e4aadcdef9d826f16a10c300ee46b30e
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 9 14:28:41 2020 +0300

    Update url string params

commit a18cffc8bfac83103fb78ffae2f786f89aea8ba1
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Thu Jul 9 12:55:50 2020 +0300

    Fix reset search

commit 33f769aed56369aacedd29ffd52b527b527d4a59
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 8 19:13:21 2020 +0300

    WIP: Add permlinks

commit 4422641cf5cff06c8485ea23d58e5d42f7cca5cd
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Wed Jul 8 14:42:28 2020 +0300

    Refactor Counters, add response_status links to query log

commit e8bb0b70ca55f31ef3fcdda13dcaad6f5d8479b5
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 7 19:33:04 2020 +0300

    Delete unnecessary file

commit b20816e9dad79866e3ec04d3093c972967b3b226
Merge: 6281084e d2c3af5c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 7 19:30:44 2020 +0300

    Resolve conflict

commit d2c3af5cf227d76f876d6d94ca016d4b242b2515
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Tue Jul 7 17:14:51 2020 +0300

    + client: Add git hooks

... and 5 more commits
2020-07-13 16:06:56 +03:00
Artem Baskal
8a417604a9 - client: Fix query logs UI issues
Close #1828

Squashed commit of the following:

commit a3955c989a939866c6772b147547344b3f8769c4
Merge: c91c41cb 2759d81a
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 15:14:47 2020 +0300

    Merge branch 'master' into fix/1828

commit c91c41cbc5f616e0af1092424e42b909d2f43f7c
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 13:48:54 2020 +0300

    Fix cell overflow

commit 19e1d31a40f2e1bb1189a85b72507bcc364d4e0c
Merge: af31f48c a33164bf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:36:44 2020 +0300

    Merge branch 'master' into fix/1828

commit af31f48c4d2699ebfbd2034711c51499b42e40f5
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 10:45:57 2020 +0300

    minor

commit d9507c5f3f5758e587766ae0fa45f1b9ad703ccf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Fri Jul 10 18:34:22 2020 +0300

    - client: Fix query logs UI issues
2020-07-13 15:23:13 +03:00
Simon Zolin
2759d81afe Merge: + service: Adding freebsd arm support
Close #1871

* commit '44aad1515aa41550f8313e7c61bcd33c24950710':
  + service: Adding freebsd arm support
2020-07-13 15:02:43 +03:00
Tejas Kokje
44aad1515a + service: Adding freebsd arm support 2020-07-13 13:34:38 +03:00
Artem Baskal
af5cb5aa5d Merge pull request #691 in DNS/adguard-home from agneevX-patch-1 to master
Squashed commit of the following:

commit 47f1bef1f584eb02a3ddcf048de7cc62dbc7ddf3
Merge: a7ff1f23 a33164bf
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 12:36:28 2020 +0300

    Merge branch 'master' into agneevX-patch-1

commit a7ff1f236ad53d7e6b30bb683443b77ebe405690
Merge: 304a51f1 b5aa42fc
Author: ArtemBaskal <a.baskal@adguard.com>
Date:   Mon Jul 13 11:42:28 2020 +0300

    Merge branch 'patch-1' of git://github.com/agneevX/AdGuardHome into agneevX-patch-1

commit b5aa42fc28
Author: Agneev Mukherjee <19761269+agneevX@users.noreply.github.com>
Date:   Fri Jun 26 17:50:22 2020 +0530

    Update index.html

commit 15f0e5e5bc
Author: Agneev Mukherjee <19761269+agneevX@users.noreply.github.com>
Date:   Fri Jun 26 17:49:37 2020 +0530

    Update install.html

commit bef3adc381
Author: Agneev Mukherjee <19761269+agneevX@users.noreply.github.com>
Date:   Thu Jun 25 18:04:07 2020 +0530

    Update login.html
2020-07-13 13:10:05 +03:00
Alan Pope
0297c12911 Update adguard-home-web.sh 2020-06-26 12:33:37 +01:00
Alan Pope
5e0fe8ba3f Update adguard-home-web.sh
Add missing space
2020-06-26 12:33:15 +01:00
Alan Pope
8863e61e8e Add web launcher for desktop users 2020-06-26 12:19:29 +01:00
176 changed files with 7652 additions and 3474 deletions

12
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -e;
git diff --cached --name-only | grep -q '.js$' && make lint-js;
found=0
git diff --cached --name-only | grep -q '.go$' && found=1
if [ $found == 1 ]; then
make lint-go || exit 1
go test ./... || exit 1
fi
exit 0;

View File

@@ -44,4 +44,4 @@ jobs:
fields: repo,message,commit,author
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -10,8 +10,7 @@ before:
- go generate ./...
builds:
-
main: ./main.go
- main: ./main.go
ldflags:
- -s -w -X main.version={{.Version}} -X main.channel={{.Env.CHANNEL}} -X main.goarm={{.Env.GOARM}}
env:
@@ -43,8 +42,7 @@ builds:
goarch: mipsle
archives:
-
# Archive name template.
- # Archive name template.
# Defaults:
# - if format is `tar.gz`, `tar.xz`, `gz` or `zip`:
# - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
@@ -62,8 +60,7 @@ archives:
- README.md
snapcrafts:
-
name: adguard-home
- name: adguard-home
base: core18
name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
summary: Network-wide ads & trackers blocking DNS server
@@ -80,11 +77,24 @@ snapcrafts:
confinement: strict
publish: false
license: GPL-3.0
extra_files:
- source: scripts/snap/local/adguard-home-web.sh
destination: adguard-home-web.sh
mode: 0755
- source: scripts/snap/gui/adguard-home-web.desktop
destination: meta/gui/adguard-home-web.desktop
mode: 0644
- source: scripts/snap/gui/adguard-home-web.png
destination: meta/gui/adguard-home-web.png
mode: 0644
apps:
adguard-home:
command: AdGuardHome -w $SNAP_DATA --no-check-update
plugs: [ network-bind ]
daemon: simple
adguard-home-web:
command: adguard-home-web.sh
plugs: [ desktop ]
checksum:
name_template: 'checksums.txt'

View File

@@ -22,7 +22,8 @@ Contents:
* Update client
* Delete client
* API: Find clients by IP
* Enable DHCP server
* DHCP server
* DHCP server in DNS
* "Show DHCP status" command
* "Check DHCP" command
* "Enable DHCP" command
@@ -51,19 +52,26 @@ Contents:
* API: Get query log
* API: Set querylog parameters
* API: Get querylog parameters
* Filtering
* DNS Filtering
* Filters update mechanism
* API: Get filtering parameters
* API: Set filtering parameters
* API: Refresh filters
* API: Add Filter
* API: Set URL parameters
* API: Delete URL
* API: Set Filter parameters
* API: Delete Filter
* API: Domain Check
* HTTP Proxy
* API: Get Proxy settings
* API: Set Proxy settings
* API: Get Proxy filtering parameters
* API: Add Proxy Filter
* API: Delete Proxy Filter
* Log-in page
* API: Log in
* API: Log out
* API: Get current user info
* Safe services
## Relations between subsystems
@@ -344,10 +352,14 @@ Response:
If `can_autoupdate` is true, then the server can automatically upgrade to a new version.
Response with empty body:
Response when auto-update is disabled by command-line argument:
200 OK
{
"disabled":true
}
It means that update check is disabled by user. UI should do nothing.
@@ -370,9 +382,9 @@ Error response:
UI shows error message "Auto-update has failed"
## Enable DHCP server
## DHCP server
Algorithm:
Enable DHCP server algorithm:
* UI shows DHCP configuration screen with "Enabled DHCP" button disabled, and "Check DHCP" button enabled
* User clicks on "Check DHCP"; UI sends request to server
@@ -384,6 +396,21 @@ Algorithm:
* UI shows the status
### DHCP server in DNS
DHCP leases are used in several ways by DNS module.
* For "A" DNS reqeust we reply with an IP address leased by our DHCP server.
< A bills-notebook.lan.
> A bills-notebook.lan. = 192.168.1.100
* For "PTR" DNS request we reply with a hostname from an active DHCP lease.
< PTR 100.1.168.192.in-addr.arpa.
> PTR 100.1.168.192.in-addr.arpa. = bills-notebook.
### "Show DHCP status" command
Request:
@@ -1456,7 +1483,7 @@ Response:
}
## Filtering
## DNS Filtering
![](doc/agh-filtering.png)
@@ -1527,7 +1554,19 @@ Response:
}
...
],
"user_rules":["...", ...]
"user_rules":["...", ...],
"proxy_filtering_enabled": true | false
"proxy_filters":[
{
"enabled":true,
"url":"https://...",
"name":"...",
"rules_count":1234,
"last_updated":"2019-09-04T18:29:30+00:00",
}
...
],
}
For both arrays `filters` and `whitelist_filters` there are unique values: id, url.
@@ -1542,6 +1581,7 @@ Request:
{
"enabled": true | false
"proxy_filtering_enabled": true | false
"interval": 0 | 1 | 12 | 1*24 || 3*24 || 7*24
}
@@ -1557,7 +1597,7 @@ Request:
POST /control/filtering/refresh
{
"whitelist": true
"type": blocklist | whitelist | proxylist
}
Response:
@@ -1578,7 +1618,7 @@ Request:
{
"name": "..."
"url": "..." // URL or an absolute file path
"whitelist": true
"type": blocklist | whitelist | proxylist
}
Response:
@@ -1586,7 +1626,7 @@ Response:
200 OK
### API: Set URL parameters
### API: Set Filter parameters
Request:
@@ -1594,11 +1634,11 @@ Request:
{
"url": "..."
"whitelist": true
"type": blocklist | whitelist | proxylist
"data": {
"name": "..."
"url": "..."
"enabled": true | false
"enabled": true
}
}
@@ -1607,7 +1647,7 @@ Response:
200 OK
### API: Delete URL
### API: Delete Filter
Request:
@@ -1615,7 +1655,7 @@ Request:
{
"url": "..."
"whitelist": true
"type": blocklist | whitelist | proxylist
}
Response:
@@ -1647,6 +1687,60 @@ Response:
}
## HTTP Proxy
Browser <-(HTTP)-> AGH Proxy <-(HTTP)-> Internet Server
HTTPS MITM:
. Browser --(CONNECT...)-> AGH Proxy --(handshake)-> Internet Server
. Browser <-(handshake,cert/AGH)-- AGH Proxy <-(cert/issuer)-- Internet Server
. Browser <-(TLS/session2)-> AGH Proxy <-(TLS/session1)-> Internet Server
### API: Get Proxy settings
Request:
GET /control/proxy_info
Response:
200 OK
{
"enabled": true|false,
"listen_address": "ip",
"listen_port": 12345,
"auth_username": "",
"auth_password": ""
}
### API: Set Proxy settings
Request:
POST /control/proxy_config
{
"enabled": true|false,
"listen_address": "ip",
"listen_port": 12345,
"auth_username": "",
"auth_password": "",
"cert_data":"...", // user-specified certificate. "": generate new
"pkey_data":"...",
}
Response:
200 OK
## Log-in page
After user completes the steps of installation wizard, he must log in into dashboard using his name and password. After user successfully logs in, he gets the Cookie which allows the server to authenticate him next time without password. After the Cookie is expired, user needs to perform log-in operation again.
@@ -1743,3 +1837,40 @@ Response:
}
If no client is configured then authentication is disabled and server sends an empty response.
### Safe services
Check if host name is blocked by SB/PC service:
* For each host name component, search for the result in cache by the first 2 bytes of SHA-256 hashes of host name components (max. is 4, i.e. sub2.sub1.host.com), excluding TLD:
hashes[] = cache_search(sha256(host.com)[0..1])
...
If hash prefix is found, search for a full hash sum in the cached data.
If found, the host is blocked.
If not found, the host is not blocked - don't request data for this prefix from the Family server again.
If hash prefix is not found, request data for this prefix from the Family server.
* Prepare query string which is generated from the first 2 bytes (converted to a 4-character string) of SHA-256 hashes of host name components (max. is 4, i.e. sub2.sub1.host.com), excluding TLD:
qs = ... + string(sha256(sub.host.com)[0..1]) + "." + string(sha256(host.com)[0..1]) + ".sb.dns.adguard.com."
For PC `.pc.dns.adguard.com` suffix is used.
* Send TXT query to Family server, receive response which contains the array of complete hash sums of the blocked hosts
* Check if one of received hash sums (`hashes[]`) matches hash sums for our host name
hashes[0] <> sha256(host.com)
hashes[0] <> sha256(sub.host.com)
hashes[1] <> sha256(host.com)
hashes[1] <> sha256(sub.host.com)
...
* Store all received hash sums in cache:
sha256(host.com)[0..1] -> hashes[0],hashes[1],...
sha256(sub.host.com)[0..1] -> hashes[2],...
...

142
Makefile
View File

@@ -14,6 +14,13 @@
# Building releases:
#
# * release -- builds AdGuard Home distros. CHANNEL must be specified (edge, release or beta).
# * release_and_sign -- builds AdGuard Home distros and signs the binary files.
# CHANNEL must be specified (edge, release or beta).
# * sign -- Repacks all release archive files and signs the binary files inside them.
# For signing to work, the public+private key pair for $(GPG_KEY) must be imported:
# gpg --import public.txt
# gpg --import private.txt
# GPG_KEY_PASSPHRASE must contain the GPG key passphrase
# * docker-multi-arch -- builds a multi-arch image. If you want it to be pushed to docker hub,
# you must specify:
# * DOCKER_IMAGE_NAME - adguard/adguard-home
@@ -23,6 +30,9 @@ GOPATH := $(shell go env GOPATH)
PWD := $(shell pwd)
TARGET=AdGuardHome
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
GPG_KEY := devteam@adguard.com
GPG_KEY_PASSPHRASE :=
GPG_CMD := gpg --detach-sig --default-key $(GPG_KEY) --pinentry-mode loopback --passphrase $(GPG_KEY_PASSPHRASE)
# See release target
DIST_DIR=dist
@@ -31,7 +41,7 @@ DIST_DIR=dist
CHANNEL ?= edge
# Validate channel
ifneq ($(CHANNEL),relese)
ifneq ($(CHANNEL),release)
ifneq ($(CHANNEL),beta)
ifneq ($(CHANNEL),edge)
$(error CHANNEL value is not valid. Valid values are release,beta or edge)
@@ -39,6 +49,12 @@ endif
endif
endif
# Version history URL (see
VERSION_HISTORY_URL="https://github.com/AdguardTeam/AdGuardHome/releases"
ifeq ($(CHANNEL),edge)
VERSION_HISTORY_URL="https://github.com/AdguardTeam/AdGuardHome/commits/master"
endif
# goreleaser command depends on the $CHANNEL
GORELEASER_COMMAND=goreleaser release --rm-dist --skip-publish --snapshot
ifneq ($(CHANNEL),edge)
@@ -55,7 +71,11 @@ SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
# Set proper version
VERSION=
ifeq ($(TAG_NAME),$(shell git describe --abbrev=4))
VERSION=$(RELEASE_VERSION)
ifeq ($(CHANNEL),edge)
VERSION=$(SNAPSHOT_VERSION)
else
VERSION=$(RELEASE_VERSION)
endif
else
VERSION=$(SNAPSHOT_VERSION)
endif
@@ -88,17 +108,25 @@ ifndef DOCKER_IMAGE_NAME
$(error DOCKER_IMAGE_NAME value is not set)
endif
.PHONY: all build client client-watch docker lint test dependencies clean release docker-multi-arch
.PHONY: all build client client-watch docker lint lint-js lint-go test dependencies clean release docker-multi-arch
all: build
build: dependencies client
go generate ./...
init:
git config core.hooksPath .githooks
build: client_with_deps
go mod download
PATH=$(GOPATH)/bin:$(PATH) go generate ./...
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
PATH=$(GOPATH)/bin:$(PATH) packr clean
client:
npm --prefix client run build-prod
client_with_deps:
npm --prefix client ci
npm --prefix client run build-prod
client-watch:
npm --prefix client run watch
@@ -116,16 +144,25 @@ docker:
@echo Now you can run the docker image:
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
lint:
@echo Running linters
golangci-lint run ./...
lint: lint-js lint-go
lint-js:
@echo Running js linter
npm --prefix client run lint
lint-go:
@echo Running go linter
golangci-lint run
test:
@echo Running unit-tests
@echo Running JS unit-tests
npm run test --prefix client
@echo Running Go unit-tests
go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
ci: dependencies client test
ci: client_with_deps
go mod download
$(MAKE) test
dependencies:
npm --prefix client ci
@@ -162,12 +199,20 @@ docker-multi-arch:
@echo If the image was pushed to the registry, you can now run it:
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
release: dependencies client
release: client_with_deps
go mod download
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
$(call write_version_file,$(VERSION))
PATH=$(GOPATH)/bin:$(PATH) packr clean
release_and_sign: client_with_deps
$(MAKE) release
$(call repack_dist)
sign:
$(call repack_dist)
define write_version_file
$(eval version := $(1))
@@ -182,8 +227,8 @@ define write_version_file
echo "{" >> $(DIST_DIR)/version.json
echo " \"version\": \"$(version)\"," >> $(DIST_DIR)/version.json
echo " \"announcement\": \"AdGuard Home $(version) is now available!\"," >> $(DIST_DIR)/version.json
echo " \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $(DIST_DIR)/version.json
echo " \"selfupdate_min_version\": \"v0.0\"," >> $(DIST_DIR)/version.json
echo " \"announcement_url\": \"$(VERSION_HISTORY_URL)\"," >> $(DIST_DIR)/version.json
echo " \"selfupdate_min_version\": \"0.0\"," >> $(DIST_DIR)/version.json
# Windows builds
echo " \"download_windows_amd64\": \"$(BASE_URL)/AdGuardHome_windows_amd64.zip\"," >> $(DIST_DIR)/version.json
@@ -223,4 +268,73 @@ define write_version_file
# Finish
echo "}" >> $(DIST_DIR)/version.json
endef
endef
define repack_dist
# Repack archive files
# A temporary solution for our auto-update code to be able to unpack these archive files
# The problem is that goreleaser doesn't add directory AdGuardHome/ to the archive file
# and we can't create it
rm -rf $(DIST_DIR)/AdGuardHome
# Windows builds
$(call zip_repack_windows,AdGuardHome_windows_amd64.zip)
$(call zip_repack_windows,AdGuardHome_windows_386.zip)
# MacOS builds
$(call zip_repack,AdGuardHome_darwin_amd64.zip)
$(call zip_repack,AdGuardHome_darwin_386.zip)
# Linux
$(call tar_repack,AdGuardHome_linux_amd64.tar.gz)
$(call tar_repack,AdGuardHome_linux_386.tar.gz)
# Linux, all kinds of ARM
$(call tar_repack,AdGuardHome_linux_armv5.tar.gz)
$(call tar_repack,AdGuardHome_linux_armv6.tar.gz)
$(call tar_repack,AdGuardHome_linux_armv7.tar.gz)
$(call tar_repack,AdGuardHome_linux_arm64.tar.gz)
# Linux, MIPS
$(call tar_repack,AdGuardHome_linux_mips_softfloat.tar.gz)
$(call tar_repack,AdGuardHome_linux_mipsle_softfloat.tar.gz)
$(call tar_repack,AdGuardHome_linux_mips64_softfloat.tar.gz)
$(call tar_repack,AdGuardHome_linux_mips64le_softfloat.tar.gz)
# FreeBSD
$(call tar_repack,AdGuardHome_freebsd_386.tar.gz)
$(call tar_repack,AdGuardHome_freebsd_amd64.tar.gz)
# FreeBSD, all kinds of ARM
$(call tar_repack,AdGuardHome_freebsd_armv5.tar.gz)
$(call tar_repack,AdGuardHome_freebsd_armv6.tar.gz)
$(call tar_repack,AdGuardHome_freebsd_armv7.tar.gz)
$(call tar_repack,AdGuardHome_freebsd_arm64.tar.gz)
endef
define zip_repack_windows
$(eval ARC := $(1))
cd $(DIST_DIR) && \
unzip $(ARC) && \
$(GPG_CMD) AdGuardHome/AdGuardHome.exe && \
zip -r $(ARC) AdGuardHome/ && \
rm -rf AdGuardHome
endef
define zip_repack
$(eval ARC := $(1))
cd $(DIST_DIR) && \
unzip $(ARC) && \
$(GPG_CMD) AdGuardHome/AdGuardHome && \
zip -r $(ARC) AdGuardHome/ && \
rm -rf AdGuardHome
endef
define tar_repack
$(eval ARC := $(1))
cd $(DIST_DIR) && \
tar xzf $(ARC) && \
$(GPG_CMD) AdGuardHome/AdGuardHome && \
tar czf $(ARC) AdGuardHome/ && \
rm -rf AdGuardHome
endef

View File

@@ -150,11 +150,17 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
### Prerequisites
Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
* [go](https://golang.org/dl/) v1.14 or later.
* [node.js](https://nodejs.org/en/download/) v10 or later.
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
* [npm](https://www.npmjs.com/) v6.14 or later.
Optionally, for Go devs:
* [golangci-lint](https://github.com/golangci/golangci-lint)
### Building
Open Terminal and execute these commands:
@@ -167,6 +173,14 @@ make
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Golang project.
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
For example:
```
GOOS=linux GOARCH=arm64 make
```
#### Preparing release
You'll need this to prepare a release build:
@@ -203,6 +217,8 @@ You may need to prepare before using these builds:
You are welcome to fork this repository, make your changes and submit a pull request — https://github.com/AdguardTeam/AdGuardHome/pulls
Please note, that we don't expect people to contribute to both UI and golang parts of the program simultaneously. Ideally, the golang part is implemented first, i.e. configuration, API, and the functionality itself. The UI part can be implemented later in a different pull request by a different person.
<a id="test-unstable-versions"></a>
### Test unstable versions
@@ -219,7 +235,7 @@ There are three options how you can install an unstable version:
There are three options how you can install an unstable version.
1. You can either install a beta version of AdGuard Home which we update periodically.
1. You can either install a beta or edge version of AdGuard Home which we update periodically. If you're already using stable version of AdGuard Home, just replace the executable with a new one.
2. You can use the Docker image from the `edge` tag, which is synced with the repo master branch.
3. You can install AdGuard Home from `beta` or `edge` channels on the Snap Store.

View File

@@ -1,9 +1,9 @@
module.exports = {
"disableEmoji": true,
"list": [
"+",
"*",
"-",
"+ ",
"* ",
"- ",
],
"maxMessageLength": 64,
"minMessageLength": 3,
@@ -12,7 +12,7 @@ module.exports = {
"scope",
"subject",
"body",
"issues"
"issues",
],
"scopes": [
"",
@@ -26,20 +26,20 @@ module.exports = {
"documentation",
],
"types": {
"+": {
"+ ": {
"description": "A new feature",
"emoji": "",
"value": "+"
"value": "+ "
},
"*": {
"* ": {
"description": "A code change that neither fixes a bug or adds a feature",
"emoji": "",
"value": "*"
"value": "* "
},
"-": {
"- ": {
"description": "A bug fix",
"emoji": "",
"value": "-"
"value": "- "
}
}
};

View File

@@ -11,6 +11,7 @@ module.exports = (api) => {
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
'react-hot-loader/babel',
],
};
};

11
client/constants.js vendored Normal file
View File

@@ -0,0 +1,11 @@
const BUILD_ENVS = {
dev: 'development',
prod: 'production',
};
const BASE_URL = '/control';
module.exports = {
BUILD_ENVS,
BASE_URL,
};

213
client/package-lock.json generated vendored
View File

@@ -1356,6 +1356,17 @@
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
},
"@hot-loader/react-dom": {
"version": "16.13.0",
"resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.13.0.tgz",
"integrity": "sha512-lJZrmkucz2MrQJTQtJobx5MICXcfQvKihszqv655p557HPi0hMOWxrNpiHv3DWD8ugNWjtWcVWqRnFvwsHq1mQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.0"
}
},
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1909,6 +1920,11 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
@@ -2252,6 +2268,12 @@
"@types/istanbul-lib-report": "*"
}
},
"@types/json-schema": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -2723,7 +2745,6 @@
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
@@ -3097,6 +3118,11 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -4818,8 +4844,7 @@
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"deep-equal": {
"version": "1.1.1",
@@ -5099,6 +5124,12 @@
}
}
},
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
"dev": true
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -5192,9 +5223,9 @@
"dev": true
},
"elliptic": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -5207,9 +5238,9 @@
},
"dependencies": {
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
"dev": true
}
}
@@ -5878,8 +5909,7 @@
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esquery": {
"version": "1.3.1",
@@ -6835,6 +6865,16 @@
}
}
},
"global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"dev": true,
"requires": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"global-modules": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
@@ -7348,18 +7388,6 @@
"requires-port": "^1.0.0"
}
},
"http-proxy-middleware": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
"dev": true,
"requires": {
"http-proxy": "^1.17.0",
"is-glob": "^4.0.0",
"lodash": "^4.17.11",
"micromatch": "^3.1.10"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -9889,10 +9917,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -10159,9 +10186,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash-es": {
"version": "4.17.15",
@@ -10619,6 +10646,15 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
"dev": true,
"requires": {
"dom-walk": "^0.1.0"
}
},
"min-indent": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
@@ -11044,6 +11080,24 @@
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
},
"dependencies": {
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"dev": true,
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
}
}
},
"npm-run-path": {
@@ -12138,13 +12192,13 @@
"dev": true
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"dev": true,
"version": "6.13.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
"integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"decode-uri-component": "^0.2.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
}
},
"querystring": {
@@ -12260,6 +12314,39 @@
"scheduler": "^0.19.1"
}
},
"react-hot-loader": {
"version": "4.12.21",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz",
"integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==",
"dev": true,
"requires": {
"fast-levenshtein": "^2.0.6",
"global": "^4.3.0",
"hoist-non-react-statics": "^3.3.0",
"loader-utils": "^1.1.0",
"prop-types": "^15.6.1",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0",
"source-map": "^0.7.3"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"requires": {
"react-is": "^16.7.0"
}
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
},
"react-i18next": {
"version": "11.4.0",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz",
@@ -13301,6 +13388,12 @@
"safe-buffer": "^5.0.1"
}
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -13455,6 +13548,18 @@
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
},
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}
},
@@ -13696,6 +13801,11 @@
"integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==",
"dev": true
},
"split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
},
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -13708,8 +13818,7 @@
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.16.1",
@@ -13833,10 +13942,9 @@
"dev": true
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
},
"string-length": {
"version": "4.0.1",
@@ -14037,12 +14145,13 @@
}
},
"schema-utils": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
"integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"requires": {
"ajv": "^6.12.0",
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
@@ -15888,6 +15997,18 @@
"ms": "^2.1.1"
}
},
"http-proxy-middleware": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
"dev": true,
"requires": {
"http-proxy": "^1.17.0",
"is-glob": "^4.0.0",
"lodash": "^4.17.11",
"micromatch": "^3.1.10"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",

9
client/package.json vendored
View File

@@ -4,14 +4,16 @@
"private": true,
"scripts": {
"build-dev": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js",
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
"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": "eslint src",
"lint:fix": "eslint src --fix",
"test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
"@hot-loader/react-dom": "^16.13.0",
"@nivo/line": "^0.49.1",
"axios": "^0.19.2",
"classnames": "^2.2.6",
@@ -19,9 +21,11 @@
"i18next": "^19.4.4",
"i18next-browser-languagedetector": "^4.2.0",
"ipaddr.js": "^1.9.1",
"lodash": "^4.17.15",
"js-yaml": "^3.14.0",
"lodash": "^4.17.19",
"nanoid": "^3.1.9",
"prop-types": "^15.7.2",
"query-string": "^6.13.1",
"react": "^16.13.1",
"react-click-outside": "^3.0.1",
"react-dom": "^16.13.1",
@@ -72,6 +76,7 @@
"path": "^0.12.7",
"postcss-flexbugs-fixes": "4.2.1",
"postcss-loader": "^3.0.0",
"react-hot-loader": "^4.12.21",
"style-loader": "^1.2.1",
"stylelint": "^13.5.0",
"stylelint-webpack-plugin": "2.0.0",

View File

@@ -6,6 +6,9 @@
<meta name="theme-color" content="#000000">
<meta name="google" content="notranslate">
<meta http-equiv="x-dns-prefetch-control" content="off">
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">

View File

@@ -5,6 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="google" content="notranslate">
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">

View File

@@ -6,6 +6,9 @@
<meta name="theme-color" content="#000000">
<meta name="google" content="notranslate">
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
<title>Login</title>

View File

@@ -236,5 +236,6 @@
"reset_settings": "Изтрий всички настройки",
"update_announcement": "Има нова AdGuard Home {{version}}! <0>Цъкни тук</0> за повече информация.",
"disable_ipv6": "Изключете IPv6 протокола",
"show_blocked_responses": "Блокирано"
"show_blocked_responses": "Блокирано",
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nový seznam povolených",
"edit_blocklist": "Upravit seznam blokovaných",
"edit_allowlist": "Upravit seznam povolených",
"choose_blocklist": "Vyberte seznamy zakázaných",
"choose_allowlist": "Vyberte seznamy povolených",
"enter_valid_blocklist": "Zadejte platnou adresu URL na seznam blokovaných.",
"enter_valid_allowlist": "Zadejte platnou adresu URL na seznam povolených.",
"form_error_url_format": "Neplatný formát URL",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonymizovat IP klienta",
"anonymize_client_ip_desc": "Neukládat úplnou IP adresu klienta do protokolů a statistik",
"dns_config": "Konfigurace DNS serveru",
"dns_cache_config": "Konfigurace mezipaměti DNS",
"dns_cache_config_desc": "Zde můžete konfigurovat mezipaměť DNS",
"blocking_mode": "Režim blokování",
"default": "Výchozí",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Doména",
"answer": "Odpověď",
"filter_added_successfully": "Seznam byl úspěšně přidán",
"filter_removed_successfully": "Seznam byl úspěšně odstraněn",
"filter_updated": "Seznam byl úspěšně aktualizován",
"statistics_configuration": "Konfigurace statistik",
"statistics_retention": "Uchovávání statistik",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Další informace</0> o vytváření vlastních seznamů hostitelů.",
"blocked_by_response": "Zakázáno dle CNAME nebo IP v odpovědi",
"blocked_by_cname_or_ip": "Zakázáno dle CNAME nebo IP",
"try_again": "Zkusit znovu",
"domain_desc": "Zadejte zástupný znak nebo název domény, kterou chcete přepsat.",
"example_rewrite_domain": "přepsat odpovědi pouze pro tento název domény.",
"example_rewrite_wildcard": "přepsat odpovědi pro všechny subdomény <0>example.org</0>.",
"rewrite_ip_address": "IP address: použít tuto IP adresu v odpovědi typu A nebo AAAA",
"rewrite_domain_name": "Název domény: Přidat záznam CNAME",
"rewrite_A": "<0>A</0>: speciální hodnota, udržet záznamy typu <0>A</0> z odchozího serveru",
"rewrite_AAAA": "<0>AAAA</0>: speciální hodnota, udržet záznamy typu <0>AAAA</0> z odchozího serveru",
"disable_ipv6": "Zakázat IPv6",
"disable_ipv6_desc": "Pokud je tato funkce povolena, budou všechny dotazy DNS pro adresy IPv6 (typ AAAA) zrušeny.",
"fastest_addr": "Nejrychlejší IP adresa",
@@ -494,6 +504,8 @@
"check": "Zkontrolovat",
"form_enter_host": "Zadejte název hostitele",
"filtered_custom_rules": "Filtrováno pomocí vlastních pravidel filtrování",
"choose_from_list": "Vybrat ze seznamu",
"add_custom_list": "Přidat vlastní seznam",
"host_whitelisted": "Hostitel je na seznamu povolených",
"check_ip": "IP adresy: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Zapnout DNSSEC",
"dnssec_enable_desc": "Nastavte příznak DNSSEC v následujících DNS dotazech a zkontrolujte výsledek (je potřebný překladač se zapnutým DNSSEC)",
"validated_with_dnssec": "Ověřeno pomocí DNSSEC",
"show_all_responses": "Všechny odpovědi",
"all_queries": "Všechny dotazy",
"show_blocked_responses": "Zablokované",
"show_whitelisted_responses": "Povolené",
"show_processed_responses": "Zpracovaný",
@@ -529,5 +541,27 @@
"rewritten": "Přepsáno",
"safe_search": "Bezpečné vyhledávání",
"blocklist": "Zakázaný",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Velikost mezipaměti",
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech)",
"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",
"enter_cache_ttl_min_override": "Zadejte minimální hodnotu TTL",
"enter_cache_ttl_max_override": "Zadejte maximální hodnotu TTL",
"cache_ttl_min_override_desc": "Přepište hodnotu TTL (minimální) obdrženou z odchozího serveru. Tato hodnota nemůže být větší než 3600 (1 hodina)",
"cache_ttl_max_override_desc": "Přepište hodnotu TTL (maximální) obdrženou z odchozího serveru",
"min_exceeds_max_value": "Minimální hodnota přesahuje maximální hodnotu",
"value_not_larger_than": "Hodnota nesmí být vyšší než {{maximum}}",
"filter_category_general": "Obecné",
"filter_category_security": "Bezpečnost",
"filter_category_regional": "Regionální",
"filter_category_other": "Ostatní",
"filter_category_general_desc": "Seznamy, které blokují slídiče a reklamu na většině zařízení",
"filter_category_security_desc": "Seznamy, které se specializují na blokování škodlivého software, zákeřných útoků nebo podvodných domén",
"filter_category_regional_desc": "Seznamy, které jsou zaměřené na regionální reklamy a sledovací servery",
"filter_category_other_desc": "Další seznamy zakázaných",
"original_response": "Původní odezva",
"click_to_view_queries": "Klikněte pro zobrazení dotazů",
"port_53_faq_link": "Port 53 je často obsazen službami \"DNSStubListener\" nebo \"systemd-resolved\". Přečtěte si <0>tento návod</0> o tom, jak to vyřešit."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Ny liste over tilladte",
"edit_blocklist": "Rediger blokeringsliste",
"edit_allowlist": "Rediger liste over tilladte",
"choose_blocklist": "Vælg blokeringslister",
"choose_allowlist": "Vælg lister over tilladte",
"enter_valid_blocklist": "Indtast en gyldig URL til blokeringslisten.",
"enter_valid_allowlist": "Indtast en gyldig URL til listen over tilladte.",
"form_error_url_format": "Ugyldigt URL-format",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonymiser klient-IP",
"anonymize_client_ip_desc": "Gem ikke klientens fulde IP-adresse i logfiler og statistikker",
"dns_config": "DNS-serverkonfiguration",
"dns_cache_config": "Konfiguration af DNS-cache",
"dns_cache_config_desc": "Her kan du konfigurere DNS-cache",
"blocking_mode": "Blokeringstilstand",
"default": "Standard",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Domæne",
"answer": "Svar",
"filter_added_successfully": "Listen er tilføjet",
"filter_removed_successfully": "Listen er blevet fjernet",
"filter_updated": "Listen er blevet opdateret",
"statistics_configuration": "Konfiguration af statistik",
"statistics_retention": "Tilbageholdelse af statistikker",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Lær mere</0> om at oprette dine egne værtslister.",
"blocked_by_response": "Blokeret af CNAME eller IP som svar",
"blocked_by_cname_or_ip": "Blokeret af CNAME eller IP",
"try_again": "Prøv igen",
"domain_desc": "Indtast det domænenavn eller wildcard, du ønsker skal omskrives.",
"example_rewrite_domain": "omskriv kun svar for dette domænenavn.",
"example_rewrite_wildcard": "omskriv svar for alle <0>example.org</0> subdomæner.",
"rewrite_ip_address": "IP-adresse: brug denne IP i et A- eller AAAA-svar",
"rewrite_domain_name": "Domænenavn: tilføj en CNAME-post",
"rewrite_A": "<0>A</0>: særlig værdi, hold <0>A</0> poster fra upstream",
"rewrite_AAAA": "<0>AAAA</0>: særlig værdi, hold <0>AAAA</0> poster fra upstream",
"disable_ipv6": "Deaktiver IPv6",
"disable_ipv6_desc": "Hvis denne funktion er aktiveret, slettes alle DNS-forespørgsler til IPv6-adresser (type AAAA).",
"fastest_addr": "Hurtigste IP-adresse",
@@ -494,6 +504,8 @@
"check": "Kontroller",
"form_enter_host": "Indtast et værtsnavn",
"filtered_custom_rules": "Filtreret af brugerdefinerede filtreringsregler",
"choose_from_list": "Vælg fra listen",
"add_custom_list": "Tilføj en tilpasset liste",
"host_whitelisted": "Værten er hvidlistet",
"check_ip": "IP-adresser: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Aktivér DNSSEC",
"dnssec_enable_desc": "Sæt DNSSEC-flag i de udgående DNS-forespørgsler, og kontroller resultatet (DNSSEC-aktiveret resolver er krævet)",
"validated_with_dnssec": "Valideret med DNSSEC",
"show_all_responses": "Alle svar",
"all_queries": "Alle forespørgsler",
"show_blocked_responses": "Blokeret",
"show_whitelisted_responses": "Hvidlistet",
"show_processed_responses": "Behandlet",
@@ -529,5 +541,27 @@
"rewritten": "Omskrevet",
"safe_search": "Sikker søgning",
"blocklist": "Blokeringsliste",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Cache-størrelse",
"cache_size_desc": "Størrelse på DNS-cache (i bytes)",
"cache_ttl_min_override": "Overskriv minimum TTL",
"cache_ttl_max_override": "Overskriv maksimal TTL",
"enter_cache_size": "Indtast cache-størrelse",
"enter_cache_ttl_min_override": "Indtast minimum TTL",
"enter_cache_ttl_max_override": "Indtast maksimum TTL",
"cache_ttl_min_override_desc": "Overskriv TTL-værdi (minimum) modtaget fra upstream-serveren. Denne værdi kan ikke være større end 3600 (1 time)",
"cache_ttl_max_override_desc": "Overskriv TTL-værdi (maksimum) modtaget fra upstream-serveren",
"min_exceeds_max_value": "Minimumsværdien overstiger maksimumværdien",
"value_not_larger_than": "Værdien kan ikke være større end {{maximum}}",
"filter_category_general": "Generelt",
"filter_category_security": "Sikkerhed",
"filter_category_regional": "Regional",
"filter_category_other": "Andre",
"filter_category_general_desc": "Lister der blokerer for sporing og reklamer på de fleste enheder",
"filter_category_security_desc": "Lister, der er specialiserede i at blokere malware, phishing eller scam-domæner",
"filter_category_regional_desc": "Lister, der fokuserer på regionale annoncer og tracking-servere",
"filter_category_other_desc": "Andre blokeringslister",
"original_response": "Oprindeligt svar",
"click_to_view_queries": "Klik for at se forespørgsler",
"port_53_faq_link": "Port 53 optages ofte af \"DNSStubListener\" eller \"systemd-resolved\" tjenester. Læs <0>denne instruktion</0> om, hvordan du løser dette."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Neue Freigabeliste",
"edit_blocklist": "Sperrliste bearbeiten",
"edit_allowlist": "Freigabeliste bearbeiten",
"choose_blocklist": "Sperrliste wählen",
"choose_allowlist": "Zulassungsliste wählen",
"enter_valid_blocklist": "Geben Sie eine gültige Adresse in die Sperrliste ein.",
"enter_valid_allowlist": "Geben Sie eine gültige Adresse in die Freigabeliste ein.",
"form_error_url_format": "Ungültiges URL-Format",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Client-IP anonymisieren",
"anonymize_client_ip_desc": "Vollständige IP-Adresse des Clients nicht in Protokollen und Statistiken speichern",
"dns_config": "DNS-Serverkonfiguration",
"dns_cache_config": "Konfiguration des DNS-Zwischenspeicher",
"dns_cache_config_desc": "Hier können Sie den DNS-Zwischenspeicher konfigurieren",
"blocking_mode": "Sperrmodus",
"default": "Standard",
"nxdomain": "NXDomain",
@@ -445,6 +449,7 @@
"domain": "Domain",
"answer": "Antwort",
"filter_added_successfully": "Der Filter wurde erfolgreich hinzugefügt",
"filter_removed_successfully": "Der Filter wurde erfolgreich entfernt",
"filter_updated": "Der Filter wurde erfolgreich aktualisiert",
"statistics_configuration": "Statistikkonfiguration",
"statistics_retention": "Statistiken speichern",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Erfahren Sie mehr</0> über die Erstellung eigener Hosts-Listen.",
"blocked_by_response": "Nach CNAME oder IP-Antwort blockiert",
"blocked_by_cname_or_ip": "Gesperrt durch CNAME oder IP",
"try_again": "Erneut versuchen",
"domain_desc": "Geben Sie den Domain-Namen oder den Platzhalter ein, der umgeschrieben werden soll.",
"example_rewrite_domain": "Antworten nur für diesen Domain-Namen umschreiben.",
"example_rewrite_wildcard": "Antworten nur für alle <0>example.org</0> Subdomains umschreiben.",
"rewrite_ip_address": "IP-Adresse: Verwenden Sie diese IP in einer A- oder AAAA-Antwort",
"rewrite_domain_name": "Domänenname: einen CNAME-Eintrag hinzufügen",
"rewrite_A": "<0>A</0>: spezieller Wert, <0>A</0>-Datensätze des \n vorgeschalteten Servers beibehalten",
"rewrite_AAAA": "<0>AAAA</0>: spezieller Wert, <0>AAAA</0>-Datensätze des vorgeschalteten Servers beibehalten",
"disable_ipv6": "IPv6 deaktivieren",
"disable_ipv6_desc": "Wenn diese Funktion aktiviert ist, werden alle DNS-Abfragen für IPv6-Adressen (Typ AAAA) verworfen.",
"fastest_addr": "Schnellste IP-Adresse",
@@ -494,6 +504,8 @@
"check": "Prüfen",
"form_enter_host": "Gerätenamen eingeben",
"filtered_custom_rules": "Nach benutzerdefinierten Filterregeln gefiltert",
"choose_from_list": "Aus Liste auswählen",
"add_custom_list": "Eigene Liste hinzufügen",
"host_whitelisted": "Der Host ist in der Positivliste enthalten",
"check_ip": "IP-Adressen: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "DNSSEC aktivieren",
"dnssec_enable_desc": "DNSSEC-Flag in den ausgehenden DNS-Abfragen mitsenden und das Ergebnis überprüfen (DNSSEC-fähiger Resolver erforderlich)",
"validated_with_dnssec": "Bestätigt mit DNSSEC",
"show_all_responses": "Alle Antworten",
"all_queries": "Alle Anfragen",
"show_blocked_responses": "Gesperrt",
"show_whitelisted_responses": "Auf der Positivliste",
"show_processed_responses": "Verarbeitet",
@@ -529,5 +541,27 @@
"rewritten": "Umgeschrieben",
"safe_search": "Sichere Suche",
"blocklist": "Sperrliste",
"milliseconds_abbreviation": "ms"
}
"milliseconds_abbreviation": "ms",
"cache_size": "Größe des Zwischenspeichers",
"cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes)",
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
"enter_cache_size": "Größe des Zwischenspeichers eingeben",
"enter_cache_ttl_min_override": "TTL-Minimalwert eingeben",
"enter_cache_ttl_max_override": "TTL-Höchstwert eingeben",
"cache_ttl_min_override_desc": "Überschreibt den TTL-Minimalwert, der vom vorgeschalteten Server empfangen wurde. Dieser Wert darf nicht größer als 3600 (Sek.) (≙ 1 Stunde) betragen.",
"cache_ttl_max_override_desc": "Überschreibt den TLL-Maximalwert, der vom vorgeschalteten Server empfangenen wurde",
"min_exceeds_max_value": "Minimalwert überschreitet Maximalwert",
"value_not_larger_than": "Wert darf höchstens {{maximum}} betragen",
"filter_category_general": "Allgemein",
"filter_category_security": "Sicherheit",
"filter_category_regional": "Regional",
"filter_category_other": "Weitere",
"filter_category_general_desc": "Listen, die Tracking und Werbung auf den meisten Geräten sperren",
"filter_category_security_desc": "Listen, die auf das Sperren von Malware, Phishing- oder Scam-Domains spezialisiert sind",
"filter_category_regional_desc": "Listen, die sich auf regionale Werbeanzeigen und Tracking-Server konzentrieren",
"filter_category_other_desc": "Weitere Sperrlisten",
"original_response": "Ursprüngliche Antwort",
"click_to_view_queries": "Anklicken, um Abfragen anzuzeigen",
"port_53_faq_link": "Port 53 wird oft von Diensten wie „DNSStubListener” oder „systemresolved” belegt. Bitte lesen Sie <0>diese Anweisung</0>, wie dies behoben werden kann."
}

View File

@@ -480,6 +480,7 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts lists.",
"blocked_by_response": "Blocked by CNAME or IP in response",
"blocked_by_cname_or_ip": "Blocked by CNAME or IP",
"try_again": "Try again",
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
"example_rewrite_domain": "rewrite responses for this domain name only.",
@@ -528,7 +529,7 @@
"dnssec_enable": "Enable DNSSEC",
"dnssec_enable_desc": "Set DNSSEC flag in the outcoming DNS queries and check the result (DNSSEC-enabled resolver is required)",
"validated_with_dnssec": "Validated with DNSSEC",
"show_all_responses": "All responses",
"all_queries": "All queries",
"show_blocked_responses": "Blocked",
"show_whitelisted_responses": "Whitelisted",
"show_processed_responses": "Processed",
@@ -541,7 +542,6 @@
"safe_search": "Safe search",
"blocklist": "Blocklist",
"milliseconds_abbreviation": "ms",
"dnssec_enable_desc": "Set DNSSEC flag in the outcoming DNS queries and check the result (DNSSEC-enabled resolver is required)",
"cache_size": "Cache size",
"cache_size_desc": "DNS cache size (in bytes)",
"cache_ttl_min_override": "Override minimum TTL",
@@ -560,5 +560,8 @@
"filter_category_general_desc": "Lists that block tracking and advertising on most of the devices",
"filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains",
"filter_category_regional_desc": "Lists that focus on regional ads and tracking servers",
"filter_category_other_desc": "Other blocklists"
}
"filter_category_other_desc": "Other blocklists",
"original_response": "Original response",
"click_to_view_queries": "Click to view queries",
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nueva lista de permitido",
"edit_blocklist": "Editar lista de bloqueo",
"edit_allowlist": "Editar lista de permitido",
"choose_blocklist": "Elegir listas de bloqueo",
"choose_allowlist": "Elegir listas de permitido",
"enter_valid_blocklist": "Ingresa una URL válida para la lista de bloqueo.",
"enter_valid_allowlist": "Ingresa una URL válida para la lista de permitido.",
"form_error_url_format": "Formato de URL no válido",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonimizar IP del cliente",
"anonymize_client_ip_desc": "No guarda la dirección IP completa del cliente en registros y estadísticas",
"dns_config": "Configuración del servidor DNS",
"dns_cache_config": "Configuración de la caché DNS",
"dns_cache_config_desc": "Aquí puedes configurar la caché DNS",
"blocking_mode": "Modo de bloqueo",
"default": "Predeterminado",
"nxdomain": "NXDOMAIN",
@@ -350,8 +354,8 @@
"fix": "Corregir",
"dns_providers": "Aquí hay una <0>lista de proveedores DNS</0> conocidos para elegir.",
"update_now": "Actualizar ahora",
"update_failed": "Error en la actualización automática. Por favor <a href='https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update'>siga los pasos</a> para actualizar manualmente.",
"processing_update": "Por favor espere, AdGuard Home se está actualizando",
"update_failed": "Error en la actualización automática. Por favor <a href='https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update'>sigue los pasos</a> para actualizar manualmente.",
"processing_update": "Por favor espera, AdGuard Home se está actualizando",
"clients_title": "Clientes",
"clients_desc": "Configurar dispositivos conectados con AdGuard Home",
"settings_global": "Global",
@@ -445,6 +449,7 @@
"domain": "Dominio",
"answer": "Respuesta",
"filter_added_successfully": "La lista ha sido añadida correctamente",
"filter_removed_successfully": "La lista ha sido eliminada correctamente",
"filter_updated": "La lista ha sido actualizada correctamente",
"statistics_configuration": "Configuración de estadísticas",
"statistics_retention": "Retención de estadísticas",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Más información</0> sobre cómo crear tus propias listas de hosts.",
"blocked_by_response": "Bloqueado por CNAME o IP en respuesta",
"blocked_by_cname_or_ip": "Bloqueado por CNAME o IP",
"try_again": "Volver a intentar",
"domain_desc": "Ingresa el nombre del dominio o comodín que deseas reescribir.",
"example_rewrite_domain": "reescribe las respuestas solo para este nombre de dominio.",
"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",
"disable_ipv6": "Deshabilitar IPv6",
"disable_ipv6_desc": "Si esta función está habilitada, se eliminarán todas las consultas DNS para direcciones IPv6 (tipo AAAA).",
"fastest_addr": "Dirección IP más rápida",
@@ -494,6 +504,8 @@
"check": "Comprobar",
"form_enter_host": "Ingresa un nombre de host",
"filtered_custom_rules": "Filtrado por reglas de filtrado personalizado",
"choose_from_list": "Elegir de la lista",
"add_custom_list": "Añadir lista personalizada",
"host_whitelisted": "El host está en la lista blanca",
"check_ip": "Direcciones IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Habilitar DNSSEC",
"dnssec_enable_desc": "Establece el indicador DNSSEC en las consultas DNS salientes y comprueba el resultado (se requiere un resolutor habilitado para DNSSEC)",
"validated_with_dnssec": "Validado con DNSSEC",
"show_all_responses": "Todas las respuestas",
"all_queries": "Todas las consultas",
"show_blocked_responses": "Bloqueado",
"show_whitelisted_responses": "En lista blanca",
"show_processed_responses": "Procesado",
@@ -529,5 +541,27 @@
"rewritten": "Reescrito",
"safe_search": "Búsqueda segura",
"blocklist": "Lista de bloqueo",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Tamaño de la caché",
"cache_size_desc": "Tamaño de la caché DNS (en bytes)",
"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é",
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo",
"enter_cache_ttl_max_override": "Ingresa el TTL máximo",
"cache_ttl_min_override_desc": "Anula el valor TTL (mínimo) recibido del servidor DNS de subida. Este valor no puede ser superior a 3600 (1 hora)",
"cache_ttl_max_override_desc": "Anula el valor TTL (máximo) recibido del servidor DNS de subida",
"min_exceeds_max_value": "El valor mínimo supera al valor máximo",
"value_not_larger_than": "El valor no puede ser mayor que {{maximum}}",
"filter_category_general": "General",
"filter_category_security": "Seguridad",
"filter_category_regional": "Regional",
"filter_category_other": "Otro",
"filter_category_general_desc": "Listas que bloquean rastreadores y anuncios en la mayoría de los dispositivos",
"filter_category_security_desc": "Listas que se especializan en bloquear dominios de malware, phishing o estafa",
"filter_category_regional_desc": "Listas que se centran en anuncios regionales y servidores de rastreo",
"filter_category_other_desc": "Otras listas de bloqueo",
"original_response": "Respuesta original",
"click_to_view_queries": "Clic para ver las consultas",
"port_53_faq_link": "El puerto 53 suele estar ocupado por los servicios \"DNSStubListener\" o \"systemd-resolved\". Por favor lee <0>esta instrucción</0> sobre cómo resolver esto."
}

View File

@@ -65,6 +65,7 @@
"filters": "فيلترها",
"filter": "فیلتر",
"query_log": "جستار وقایع",
"nothing_found": "هیچ چیز یافت نشد",
"faq": "پرسش و پاسخ",
"version": "نسخه",
"address": "آدرس",
@@ -509,7 +510,6 @@
"dnssec_enable": "فعالسازی DNSSEC",
"dnssec_enable_desc": "تنظیم نشان DNSSEC در جستارهای حاصل DNS و بررسی نتیجه (تفکیک کننده DNSSEC-فعال شده نیاز است)",
"validated_with_dnssec": "معتبر سازی با DNSSEC",
"show_all_responses": "همه پاسخ ها",
"show_blocked_responses": "مسدود شده",
"show_whitelisted_responses": "لیست سفید",
"show_processed_responses": "پردازش شده",

View File

@@ -67,6 +67,8 @@
"filters": "Filtres",
"filter": "Filtre",
"query_log": "Journal des requêtes",
"compact": "Compact",
"nothing_found": "Rien n'a été trouvé",
"faq": "FAQ",
"version": "version",
"address": "Addresse",
@@ -122,7 +124,7 @@
"dns_blocklists_desc": "AdGuard Home bloquera les domaines correspondant aux listes de blocage.",
"dns_allowlists_desc": "Les domaines provenant de listes dautorisation DNS seront autorisés même sils figurent dans lune des listes de blocage.",
"custom_filtering_rules": "Règles de filtrage personnalisées",
"encryption_settings": "Paramètres de cryptage",
"encryption_settings": "Paramètres de chiffrement",
"dhcp_settings": "Paramètres DHCP",
"upstream_dns": "Serveurs DNS upstream",
"upstream_dns_hint": "Si vous laissez ce champ vide, AdGuard Home va utiliser <a href='https://www.quad9.net/' target='_blank'>Quad9</a> comme upstream.",
@@ -160,6 +162,8 @@
"new_allowlist": "Nouvelle liste dautorisation",
"edit_blocklist": "Modifier la liste de blocage",
"edit_allowlist": "Modifier la liste dautorisation",
"choose_blocklist": "Choisir des listes de blocage",
"choose_allowlist": "Choisir des listes dautorisation",
"enter_valid_blocklist": "Saisissez une URL valide vers la liste de blocage.",
"enter_valid_allowlist": "Saisissez une URL valide vers la liste dautorisation.",
"form_error_url_format": "Format dURL incorrect",
@@ -191,6 +195,7 @@
"domain_or_client": "Domaine ou client",
"type_table_header": "Type",
"response_table_header": "Réponse",
"response_code": "Code de réponse",
"client_table_header": "Client",
"empty_response_status": "Vide",
"show_all_filter_type": "Montrer tout",
@@ -209,6 +214,7 @@
"query_log_filtered": "Filtré par {{filter}}",
"query_log_confirm_clear": "Êtes-vous sûr de vouloir effacer tout le journal des requêtes ?",
"query_log_cleared": "Le journal des requêtes a été effacé",
"query_log_updated": "Le journal des requêtes a été mis à jour",
"query_log_clear": "Effacer journal des requêtes",
"query_log_retention": "Rétention du journal des requêtes",
"query_log_enable": "Activer le journal",
@@ -219,6 +225,8 @@
"anonymize_client_ip": "Anonymiser lIP du client",
"anonymize_client_ip_desc": "Ne pas enregistrer ladresse IP complète du client dans les journaux et statistiques",
"dns_config": "Configuration du serveur DNS",
"dns_cache_config": "Configuration du cache DNS",
"dns_cache_config_desc": "Ici, vous pouvez configurer le cache DNS",
"blocking_mode": "Mode du blocage",
"default": "Par défaut",
"nxdomain": "NXDOMAIN",
@@ -241,6 +249,7 @@
"blocking_mode_null_ip": "IP nulle : Répondre avec une adresse IP nulle (0.0.0.0 pour A; :: pour AAAA)",
"blocking_mode_custom_ip": "IP personnalisée : Répondre avec une adresse IP définie manuellement",
"upstream_dns_client_desc": "Si vous laissez ce champ vide, AdGuard Home utilisera les serveurs configurés dans les <0>paramètres DNS</0>.",
"tracker_source": "Source du traceur",
"source_label": "Source",
"found_in_known_domain_db": "Trouvé dans la base de données des domaines connus",
"category_label": "Catégorie",
@@ -440,6 +449,7 @@
"domain": "Domaine",
"answer": "Réponse",
"filter_added_successfully": "Le filtre a été ajouté avec succès",
"filter_removed_successfully": "La liste a été supprimée avec succès",
"filter_updated": "Le filtre a été mis à jour avec succès",
"statistics_configuration": "Configuration des statistiques",
"statistics_retention": "Maintien des statistiques",
@@ -474,6 +484,10 @@
"domain_desc": "Saisissez le nom de domaine ou le caractère générique que vous souhaitez réécrire.",
"example_rewrite_domain": "réécrivez les réponses pour ce nom de domaine uniquement.",
"example_rewrite_wildcard": "réécrire les réponses pour tous les sous-domaines <0>exemple.org</0>.",
"rewrite_ip_address": "Adresse IP : utilisez cette IP dans une réponse A ou AAAA",
"rewrite_domain_name": "Nom de domaine : ajouter un enregistrement CNAME",
"rewrite_A": "<0>A</0> : valeur spéciale, conserver les enregistrements <0>A</0> de lamont",
"rewrite_AAAA": "<0>AAAA</0> : valeur spéciale, conserver les enregistrements <0>AAAA</0> de lamont",
"disable_ipv6": "Désactiver IPv6",
"disable_ipv6_desc": "Si cette fonctionnalité est activée, toutes les requêtes DNS visant des adresses IPv6 (type AAAA) seront annulées.",
"fastest_addr": "Adresse IP la plus rapide",
@@ -489,6 +503,8 @@
"check": "Vérifier",
"form_enter_host": "Saisissez un nom dhôte",
"filtered_custom_rules": "Filtré par des règles de filtrage personnalisées",
"choose_from_list": "Choisissez dans la liste",
"add_custom_list": "Ajouter une liste personnalisée",
"host_whitelisted": "Lhôte est sur liste blanche",
"check_ip": "Adresses IP : {{ip}}",
"check_cname": "CNAME : {{cname}}",
@@ -512,7 +528,6 @@
"dnssec_enable": "Activer DNSSEC",
"dnssec_enable_desc": "Définir lindicateur DNSSEC dans les requêtes DNS sortantes et vérifier le résultat (résolveur compatible DNSSEC requis)",
"validated_with_dnssec": "Validé avec DNSSEC",
"show_all_responses": "Toutes les réponses",
"show_blocked_responses": "Bloqué",
"show_whitelisted_responses": "Ajouté à la liste blanche",
"show_processed_responses": "Traité",
@@ -524,5 +539,26 @@
"rewritten": "Réécrit",
"safe_search": "Recherche sécurisée",
"blocklist": "Liste de blocage",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Taille du cache",
"cache_size_desc": "Taille du cache DNS (en bytes)",
"cache_ttl_min_override": "Remplacer le TTL minimum",
"cache_ttl_max_override": "Remplacer le TTL maximum",
"enter_cache_size": "Entrer la taille du cache",
"enter_cache_ttl_min_override": "Entrez le TTL minimum",
"enter_cache_ttl_max_override": "Entrez le TTL maximum",
"cache_ttl_min_override_desc": "Remplacer la valeur TTL (minimum) reçue du serveur en amont. Cette valeur ne peut pas dépasser 3600 (1 heure)",
"cache_ttl_max_override_desc": "Remplacer la valeur TTL (maximale) reçue du serveur en amont",
"min_exceeds_max_value": "La valeur minimum excède la valeur maximum",
"value_not_larger_than": "La valeur ne peut pas dépasser {{maximum}}",
"filter_category_general": "Général",
"filter_category_security": "Sécurité",
"filter_category_regional": "Régional",
"filter_category_other": "Autre",
"filter_category_general_desc": "Listes qui bloquent le pistage et la publicité sur la plupart des appareils",
"filter_category_security_desc": "Listes spécialisées dans le blocage de logiciels malveillants, dhameçonnage ou de domaines frauduleux",
"filter_category_regional_desc": "Listes axées sur les annonces régionales et les serveurs de pistage",
"filter_category_other_desc": "Autres listes noires",
"click_to_view_queries": "Cliquez pour voir les requêtes",
"port_53_faq_link": "Le port 53 est souvent occupé par les services « DNSStubListener » ou « systemd-resolved ». Veuillez lire <0>cette instruction</0> pour savoir comment résoudre ce problème."
}

View File

@@ -84,7 +84,7 @@
"disable_protection": "Onemogući zaštitu",
"disabled_protection": "Onemogućena zaštita",
"refresh_statics": "Osvježi statistiku",
"dns_query": "DNS Upiti",
"dns_query": "DNS upiti",
"blocked_by": "<0>Blokirano filtrima</0>",
"stats_malware_phishing": "Blokiran zločudni program/krađe identiteta",
"stats_adult": "Blokirane web stranice za odrasle",
@@ -111,7 +111,7 @@
"block_domain_use_filters_and_hosts": "Blokiraj domene koristeći filtre ili hosts datoteke",
"filters_block_toggle_hint": "Pravila blokiranja možete postaviti u postavkama <a href='#filters'>filtara</a>.",
"use_adguard_browsing_sec": "Koristi AdGuard uslugu zaštite pregledavanja",
"use_adguard_browsing_sec_hint": "AdGuard Home će provjeriti nalazi li se domena na popisu neželjenih domena od usluge zaštite pregledavanja. Za provjeru će se koristiti API za provjeru koji poštuje vašu privatnost. Samo mali dio SHA256 hash-a odnaziva domene se šalje poslužitelju.",
"use_adguard_browsing_sec_hint": "AdGuard Home će provjeriti nalazi li se domena na popisu neželjenih domena od usluge zaštite pregledavanja. Za provjeru će se koristiti API za provjeru koji poštuje vašu privatnost. Samo mali dio SHA256 hash-a od naziva domene se šalje poslužitelju.",
"use_adguard_parental": "Koristi web uslugu AdGuard roditeljske zaštite",
"use_adguard_parental_hint": "AdGuard Home provjeriti će sadrži li domena sadržaj za odrasle. Koristi isti API za zaštitu privatnosti kao i naša usluga zaštite pregledavanja.",
"enforce_safe_search": "Omogući sigurno pretraživanje",
@@ -162,6 +162,8 @@
"new_allowlist": "Novi popis omogućenih",
"edit_blocklist": "Uredi popis blokiranih",
"edit_allowlist": "Uredi popis omogućenih",
"choose_blocklist": "Odaberite popis blokiranih",
"choose_allowlist": "Odaberite popis dopuštenih",
"enter_valid_blocklist": "Unesite valjani URL za popis blokiranih.",
"enter_valid_allowlist": "Unesite valjani URL za popis omogućenih.",
"form_error_url_format": "Nevažeći URL format",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonimiraj IP klijenta",
"anonymize_client_ip_desc": "Ne spremajte cijelu IP adresu klijenta u zapisnike i statistike",
"dns_config": "DNS postavke poslužitelja",
"dns_cache_config": "DNS predmemorija poslužitelja",
"dns_cache_config_desc": "Ovdje možete postaviti DNS predmemoriju",
"blocking_mode": "Način blokiranja",
"default": "Zadano",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Domena",
"answer": "Odgovor",
"filter_added_successfully": "Popis je uspješno dodan",
"filter_removed_successfully": "Ovaj popis je uspješno uklonjen",
"filter_updated": "Ovaj popis je uspješno ažuriran",
"statistics_configuration": "Postavke statistike",
"statistics_retention": "Spremanje statistike",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vlastitog popisa poslužitelja.",
"blocked_by_response": "Blokirano od strane CNAME-a ili IP-a u odgovoru",
"blocked_by_cname_or_ip": "Blokirao CNAME ili IP",
"try_again": "Pokušajte ponovno",
"domain_desc": "Unesite naziv domene ili zamjenski znak koji želite prepisati.",
"example_rewrite_domain": "prepiši odgovore samo za ovaj naziv domene.",
"example_rewrite_wildcard": "prepiši odgovore za sve <0>example.org</0> poddomene.",
"rewrite_ip_address": "IP adresa: koristite ovu IP adresu u A ili AAAA odgovoru",
"rewrite_domain_name": "Naziv domene: Dodajte CNAME zapis",
"rewrite_A": "<0>A</0>: posebna vrijednost, ukloni <0>A</0> zapis od upstreama",
"rewrite_AAAA": "<0>AAAA</0>: posebna vrijednost, ukloni <0>AAAA</0> zapis od upstreama",
"disable_ipv6": "Onemogući IPv6",
"disable_ipv6_desc": "Ukoliko je ova značajka omogućena, svi DNS upiti za IPv6 adrese (AAAA tip) će biti odbačeni.",
"fastest_addr": "Najbrža IP adresa",
@@ -494,6 +504,8 @@
"check": "Provjeri",
"form_enter_host": "Unesite naziv računala",
"filtered_custom_rules": "Filtrirano prilagođenim pravilima filtriranja",
"choose_from_list": "Odaberite s popisa",
"add_custom_list": "Dodajte prilagođeni popis",
"host_whitelisted": "Računalo je na popisu dopuštenih",
"check_ip": "IP adrese: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Omogući DNSSEC",
"dnssec_enable_desc": "Omogućite DNSSEC u izlaznim DNS upitima i provjerite rezultat (potreban je resolver s omogućenim DNSSEC-om)",
"validated_with_dnssec": "Potvrđeno s DNSSEC-om",
"show_all_responses": "Svi odgovori",
"all_queries": "Svi upiti",
"show_blocked_responses": "Blokirano",
"show_whitelisted_responses": "Na popisu dopuštenih",
"show_processed_responses": "Obrađeno",
@@ -529,5 +541,27 @@
"rewritten": "Prepisano",
"safe_search": "Sigurno pretraživanje",
"blocklist": "Popis neželjenih",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Veličina predmemorije",
"cache_size_desc": "Veličina DNS predmemorije (u bajtovima)",
"cache_ttl_min_override": "Nadjačaj minimalni TTL",
"cache_ttl_max_override": "Nadjačaj maksimalni TTL",
"enter_cache_size": "Unesite veličinu predmemorije",
"enter_cache_ttl_min_override": "Unesite minimalni TTL",
"enter_cache_ttl_max_override": "Unesite maksimalni TTL",
"cache_ttl_min_override_desc": "Nadjačaj TTL vrijednost (minimum) dobivenu od upstream poslužitelja. Ova vrijednost ne može biti veća od 3600 (1 sat)",
"cache_ttl_max_override_desc": "Nadjačaj TTL vrijednost (maksimum) dobivenu od upstream poslužitelja",
"min_exceeds_max_value": "Minimalna vrijednost je veća od maksimalne",
"value_not_larger_than": "Vrijednost ne može biti veća od {{maximum}}",
"filter_category_general": "Općenito",
"filter_category_security": "Sigurnost",
"filter_category_regional": "Regionalno",
"filter_category_other": "Ostalo",
"filter_category_general_desc": "Popisi koji blokiraju pratitelje i oglase na većini uređaja",
"filter_category_security_desc": "Popisi koju su specijalizirani za blokiranje zlonamjernih programa, krađe identiteta ili domena za obmanu",
"filter_category_regional_desc": "Popisi koji se fokusiraju na regionalne oglase i poslužitelje za praćenje",
"filter_category_other_desc": "Ostali popisi blokiranih",
"original_response": "Originalni odgovor",
"click_to_view_queries": "Kliknite za pregled upita",
"port_53_faq_link": "Port 53 često zauzimaju usluge \"DNSStubListener\" ili \"systemd-resolved\". Molimo pročitajte <0>ove upute</0> o tome kako to riješiti."
}

View File

@@ -3,6 +3,8 @@
"example_upstream_reserved": "Anda dapat menetapkan DNS upstream <0>untuk domain spesifik</0>",
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream 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": "Server Bootstrap DNS dapat digunakan untuk meresolve alamat IP pada DoH/DoT resolvers yang Anda tentukan sebagai upstreams.",
"check_dhcp_servers": "Cek untuk server DHCP",
@@ -51,9 +53,11 @@
"dhcp_add_static_lease": "Tambah static lease",
"dhcp_reset": "Apakah anda yakin ingin mengatur ulang konfigurasi DHCP anda?",
"country": "Negara",
"city": "Kota",
"delete_confirm": "Apakah anda yakin ingin menghapus \"{{key}}\"?",
"form_enter_hostname": "Masukkan hostname",
"error_details": "Detail kesalahan",
"response_details": "Detail respon",
"request_details": "Detai permintaan",
"client_details": "Detail klien",
"details": "Detail",
@@ -63,6 +67,8 @@
"filters": "Penyaring",
"filter": "Filter",
"query_log": "Catatan Kueri",
"compact": "Rapat",
"nothing_found": "Tidak ditemukan",
"faq": "Tanya Jawab",
"version": "versi",
"address": "Alamat",
@@ -114,12 +120,16 @@
"general_settings": "Pengaturan umum",
"dns_settings": "Pengaturan DNS",
"dns_blocklists": "Daftar blokir DNS",
"dns_allowlists": "Daftar putih DNS",
"dns_blocklists_desc": "AdGuard Home akan memblokir domain yang cocok dengan daftar hitam.",
"dns_allowlists_desc": "Domain dari daftar putih DNS akan diizinkan bahkan jika mereka ada juga di daftar hitam.",
"custom_filtering_rules": "Aturan penyaringan khusus",
"encryption_settings": "Pengaturan enkripsi",
"dhcp_settings": "Pengaturan DHCP",
"upstream_dns": "Server DNS hulu",
"upstream_dns_hint": "Jika Anda mengosongkan kolom ini, AdGuard Home akan menggunakan <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> sebagai hulu. Gunakan tls:// untuk server DNS over TLS.",
"test_upstream_btn": "Uji hulu",
"upstreams": "Upstream",
"apply_btn": "Terapkan",
"disabled_filtering_toast": "Penyaringan nonaktif",
"enabled_filtering_toast": "Penyaringan aktif",
@@ -131,6 +141,7 @@
"enabled_save_search_toast": "Pencarian aman diaktifkan",
"enabled_table_header": "Diaktifkan",
"name_table_header": "Nama",
"list_url_table_header": "Daftar URL",
"rules_count_table_header": "Jumlah Aturan",
"last_time_updated_table_header": "Terakhir diperbaharui",
"actions_table_header": "Aksi",
@@ -139,10 +150,23 @@
"delete_table_action": "Hapus",
"elapsed": "Berlalu",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
"no_blocklist_added": "Tiada daftar hitam ditambahkan",
"no_whitelist_added": "Tiada daftar putih ditambahkan",
"add_blocklist": "Tambah daftar hitam",
"add_allowlist": "Tambah daftar putih",
"cancel_btn": "Batal",
"enter_name_hint": "Masukkan nama",
"enter_url_or_path_hint": "Masukan sebuah URL atau jalur absolut dari daftar",
"check_updates_btn": "Cek pembaruan",
"new_blocklist": "Daftar hitam baru",
"new_allowlist": "Daftar putih baru",
"edit_blocklist": "Edit daftar hitam",
"edit_allowlist": "Edit daftar putih",
"choose_blocklist": "Pilih daftar hitam",
"choose_allowlist": "Pilih daftar putih",
"enter_valid_blocklist": "Masukkan valid URL ke daftar hitam.",
"enter_valid_allowlist": "Masukkan valid URL ke daftar putih.",
"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.",
@@ -159,6 +183,7 @@
"example_upstream_doh": "terenkripsi <0>DNS-over-HTTPS</0>",
"example_upstream_sdns": "anda bisa menggunakan <0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS-over-HTTPS</2>",
"example_upstream_tcp": "DNS reguler (melalui TCP)",
"all_lists_up_to_date_toast": "Semua daftar sudah diperbarui",
"updated_upstream_dns_toast": "Server DNS hulu terbarui",
"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",
@@ -170,6 +195,7 @@
"domain_or_client": "Domain atau klien",
"type_table_header": "Tipe",
"response_table_header": "Respon",
"response_code": "Kode respon",
"client_table_header": "Klien",
"empty_response_status": "Kosong",
"show_all_filter_type": "Tampilkan semua",
@@ -188,6 +214,7 @@
"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_retention": "Retensi kueri log",
"query_log_enable": "Aktifkan log",
@@ -195,19 +222,39 @@
"query_log_disabled": "Kueri log 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 retensi 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",
"dns_config": "Konfigurasi server DNS",
"dns_cache_config": "Konfigurasi cache DNS",
"dns_cache_config_desc": "Disini Anda bisa mengonfigurasi cache DNS",
"blocking_mode": "Mode blokir",
"default": "Standar",
"nxdomain": "NXDOMAIN",
"null_ip": "Null IP",
"custom_ip": "Custom IP",
"blocking_ipv4": "Blokiran IPv4",
"blocking_ipv6": "Blokiran IPv6",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"plain_dns": "Plain DNS",
"form_enter_rate_limit": "Masukkan batas nilai",
"rate_limit": "Batas nilai",
"edns_enable": "Aktifkan EDNS Klien Subnet",
"edns_cs_desc": "Apabila dinyalakan, AdGuard Home akan mengirim subnet klien ke server-server DNS.",
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien (0: tidak terbatas)",
"blocking_ipv4_desc": "Alamat IP akan dikembalikan untuk permintaan A yang diblokir",
"blocking_ipv6_desc": "Alamat IP akan dipulihkan untuk permintaan AAAA yang diblokir",
"blocking_mode_default": "Standar: Respon pakai NXDOMAIN saat diblokir oleh aturan gaya Adblock; membalas dengan alamat IP yang ditentukan dalam aturan ketika diblokir oleh /et /aturan hosts-style",
"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",
"upstream_dns_client_desc": "Jika Anda biarkan bidang ini kosong, AdGuard Home akan memakai 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",
"category_label": "Kategori",
"rule_label": "Aturan",
"list_label": "Daftar",
"unknown_filter": "Penyaringan {{filterId}} tidak dikenal",
"known_tracker": "Pelacak yang dikenal",
"install_welcome_title": "Selamat datang di AdGuard Home!",
@@ -324,6 +371,8 @@
"client_identifier_desc": "Klien dapat diidentifikasi dengan alamat IP atau alamat MAC. Harap dicatat bahwa menggunakan MAC sebagai pengidentifikasi hanya dimungkinkan jika AdGuard Home juga merupakan <0>server DHCP</0>",
"form_enter_ip": "Masukkan IP",
"form_enter_mac": "Masukkan MAC",
"form_enter_id": "Masukkan pengenal",
"form_add_id": "Tambah pengenal",
"form_client_name": "Masukkan nama klien",
"name": "Nama",
"client_global_settings": "Gunakan pengaturan global",
@@ -332,6 +381,7 @@
"client_updated": "Klien \"{{key}}\" berhasil diperbarui",
"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": "Data pada klien yang menggunakan AdGuard Home, tetapi tidak disimpan dalam konfigurasi",
"access_title": "Pengaturan akses",
@@ -399,6 +449,8 @@
"domain": "Domain",
"answer": "Jawab",
"filter_added_successfully": "Filter telah berhasil ditambahkan",
"filter_removed_successfully": "Daftar ini telah sukses dihapus",
"filter_updated": "Daftar telah sukses diperbarui",
"statistics_configuration": "Konfigurasi statistik",
"statistics_retention": "Statistik disimpan",
"statistics_retention_desc": "Jika Anda menurunkan nilai interval, beberapa data akan hilang",
@@ -428,13 +480,33 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Pelajari lebih lanjut</0> tentang membuat daftar hitam host Anda sendiri.",
"blocked_by_response": "Diblokir oleh CNAME atau IP sebagai respon",
"blocked_by_cname_or_ip": "Diblokir oleh CNAME atau IP",
"try_again": "Coba lagi",
"domain_desc": "Masukkan nama domain atau wildcard yang ingin Anda tulis ulang.",
"example_rewrite_domain": "tulis ulang respon hanya untuk domain ini saja.",
"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",
"disable_ipv6": "Matikan IPv6",
"disable_ipv6_desc": "Apabila fitur ini dinyalakan, semua permintaan DNS untuk alamat-alamat IPv6 (tipe AAAA) akan diputus.",
"fastest_addr": "Alamat IP tercepat",
"fastest_addr_desc": "Permintaan semua server DNS dan kembalinya alamat IP tercepat di antara semua respons",
"autofix_warning_text": "Apabila anda menekan \"Perbaiki\", AdGuardHome akan mengatur sistem anda untuk menggunakan server DNS AdGuardHome.",
"autofix_warning_list": "Ini akan melakukan tugas berikut: <0>Nonaktifkan sistem DNSStubListener</0> <0> Atur alamat server DNS ke 127.0.0.1</0> <0>Ganti target tautan simbolis /etc/resolv.conf pakai /run/systemd/resolve/resolv.conf</0> <0>Hentikan DNSStubListener (muat ulang layanan sistemd-resolve service)</0>",
"autofix_warning_result": "Hasilnya, semua permintaan DNS dari sistem anda akan diproses oleh AdGuardHome secara standar.",
"tags_title": "Tag",
"tags_desc": "Anda dapat memilih tag sesuai dengan klien. Tag dapat dimasukkan dalam aturan pemfilteran dan memungkinkan Anda untuk menerapkannya lebih akurat. <0>Pelajari lebih</0>",
"form_select_tags": "Pilih tag klien",
"check_title": "Periksa penyaringan",
"check_desc": "Periksa apakah nama host telah tersaring",
"check": "Periksa",
"form_enter_host": "Masukkan nama host",
"filtered_custom_rules": "Tersaring oleh aturan penyaring Buatan",
"choose_from_list": "Pilih dari daftar",
"add_custom_list": "Tambah daftar buatan",
"host_whitelisted": "Host didaftar putihkan",
"check_ip": "Alamat IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
"check_reason": "Alasan: {{reason}}",
@@ -446,12 +518,50 @@
"client_blocked": "Klien \"{{ip}}\" sukses di blokir",
"client_unblocked": "Klien \"{{ip}}\" sukses di unblock",
"static_ip": "Alamat IP statis",
"static_ip_desc": "AdGuard Home adalah server jadi perlu alamat IP statis agar berfungsi dengan benar. Jika tidak, pada titik tertentu, router Anda dapat menetapkan alamat IP yang berbeda untuk perangkat ini.",
"set_static_ip": "Atur alamat IP statik",
"install_static_ok": "Kabar baik! Alamat IP statis sudah dikonfigurasi",
"install_static_error": "AdGuard Home tidak dapat mengonfigurasinya secara otomatis untuk antarmuka jaringan ini. Silakan cari instruksi tentang cara melakukan ini secara manual.",
"install_static_configure": "Kami mendeteksi alamat IP dinamis digunakan - <0>{{ip}}</0>. Anda ingin menggunakannya sebagai alamat statis Anda?",
"confirm_static_ip": "AdGuard Home akan mengonfigurasi {{ip}} menjadi alamat IP statis Anda. Anda ingin melanjutkan?",
"list_updated": "{{count}} daftar terbarui",
"list_updated_plural": "{{count}} daftar terbarui",
"dnssec_enable": "Aktifkan DNSSEC",
"dnssec_enable_desc": "Atur bendera DNSSEC di permintaan keluar DNS dan periksa hasilnya (resolver berkemampuan DNSSEC diperlukan)",
"validated_with_dnssec": "Tervalidasi dengan DNSSEC",
"show_all_responses": "Semua respon",
"all_queries": "Semua permintaan",
"show_blocked_responses": "Diblokir",
"show_whitelisted_responses": "Dalam Daftar Putih",
"show_processed_responses": "Terproses",
"blocked_safebrowsing": "Terblokir oleh Safebrowsing",
"blocked_adult_websites": "Situs Dewasa Terblokir",
"blocked_threats": "Blokir Ancaman",
"allowed": "Dibolehkan",
"filtered": "Tersaring",
"rewritten": "Tulis ulang",
"safe_search": "Pencarian aman",
"blocklist": "Daftar blokir",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Ukuran cache",
"cache_size_desc": "Ukuran DNS cache (dalam bit)",
"cache_ttl_min_override": "Tumpuk TTL minimum",
"cache_ttl_max_override": "Tumpuk TTL maksimum",
"enter_cache_size": "Masukkan ukuran cache",
"enter_cache_ttl_min_override": "Masukkan TTL minimum",
"enter_cache_ttl_max_override": "Masukkan TTL maksimum",
"cache_ttl_min_override_desc": "Ganti nilai TTL (minimum) yang diterima dari server upstream. Nilai ini tidak boleh lebih dari 3600 (1 jam)",
"cache_ttl_max_override_desc": "Ganti nilai TTL (maksimum) yang diterima dari server upstream",
"min_exceeds_max_value": "Nilai minimum melebihi nilai maksimum",
"value_not_larger_than": "Nilai tidak bisa lebih dari {{maximum}}",
"filter_category_general": "Umum",
"filter_category_security": "Keamanan",
"filter_category_regional": "Wilayah",
"filter_category_other": "Lainnya",
"filter_category_general_desc": "Daftar yang memblokir pelacakan dan iklan di sebagian besar perangkat",
"filter_category_security_desc": "Daftar yang khusus pada pemblokiran malware, phishing, atau domain penipuan",
"filter_category_regional_desc": "Daftar yang berfokus pada iklan regional dan server pelacakan",
"filter_category_other_desc": "Daftar hitam lain",
"original_response": "Respon asli",
"click_to_view_queries": "Klik untuk lihat permintaan",
"port_53_faq_link": "Port 53 sering ditempati oleh layanan \"DNSStubListener\" atau \"systemd-resolved\". Silakan baca <0>instruksi ini</0> tentang cara menyelesaikan ini."
}

View File

@@ -67,6 +67,8 @@
"filters": "Filtri",
"filter": "Filtro",
"query_log": "Query Log",
"compact": "Compatto",
"nothing_found": "Non è stato trovato nulla",
"faq": "FAQ",
"version": "versione",
"address": "Indirizzo",
@@ -160,6 +162,8 @@
"new_allowlist": "Nuova lista dei consentiti",
"edit_blocklist": "Modifica lista di blocco",
"edit_allowlist": "Modifica lista dei consentiti",
"choose_blocklist": "Scegli liste di blocco",
"choose_allowlist": "Scegli liste di autorizzazione",
"enter_valid_blocklist": "Inserisci un URL valido nella lista di blocco.",
"enter_valid_allowlist": "Inserisci un URL valido nella lista dei consentiti.",
"form_error_url_format": "Formato url non valido",
@@ -191,6 +195,7 @@
"domain_or_client": "Dominio o client",
"type_table_header": "Tipo",
"response_table_header": "Risposta",
"response_code": "Codice di risposta",
"client_table_header": "Client",
"empty_response_status": "Vuoto",
"show_all_filter_type": "Mostra tutti",
@@ -209,6 +214,7 @@
"query_log_filtered": "Filtrato da {{filter}}",
"query_log_confirm_clear": "Sei sicuro di voler eliminare la query log?",
"query_log_cleared": "La query log è stata cancellata correttamente",
"query_log_updated": "Il log query è stato aggiornato con successo",
"query_log_clear": "Cancella query logs",
"query_log_retention": "Ritenzione query logs",
"query_log_enable": "Abilita log",
@@ -219,6 +225,8 @@
"anonymize_client_ip": "Anonimizza client IP",
"anonymize_client_ip_desc": "Non salvare l'indirizzo IP completo del client nei log e nelle statistiche",
"dns_config": "Configurazione server DNS",
"dns_cache_config": "Configurazione cache DNS",
"dns_cache_config_desc": "Qui puoi configurare la cache DNS",
"blocking_mode": "Modalità di blocco",
"default": "Predefinito",
"nxdomain": "NXDOMAIN",
@@ -241,6 +249,7 @@
"blocking_mode_null_ip": "IP nullo: Rispondi con indirizzo IP zero (0.0.0.0 per A; :: per AAAA)",
"blocking_mode_custom_ip": "IP personalizzato: Rispondi con un indirizzo IP impostato manualmente",
"upstream_dns_client_desc": "Se lasci questo spazio vuoto, AdGuard Home utilizzerà i server configurati nelle <0>impostazioni DNS</0>.",
"tracker_source": "Origine tracker",
"source_label": "Fonte",
"found_in_known_domain_db": "Trovato nel database dei domini conosciuti.",
"category_label": "Categoria",
@@ -440,6 +449,7 @@
"domain": "Dominio",
"answer": "Risposta",
"filter_added_successfully": "Il filtro è stato aggiunto correttamente",
"filter_removed_successfully": "La lista è stata correttamente rimossa",
"filter_updated": "Il filtro è stato aggiornato correttamente",
"statistics_configuration": "Configurazione delle statistiche",
"statistics_retention": "Conservazione statistiche",
@@ -474,6 +484,10 @@
"domain_desc": "Inserire il nome di dominio o carattere jolly che si vuole riscrivere.",
"example_rewrite_domain": "riscrivi risposte per questo dominio soltanto.",
"example_rewrite_wildcard": "riscrivi risposte per tutti i sottodomini di <0>esempio.org</0>.",
"rewrite_ip_address": "Indirizzo IP: utilizza questo IP in una risposta A o AAAA",
"rewrite_domain_name": "Nome dominio: aggiungi una registrazione CNAME",
"rewrite_A": "<0>A</0>: valore speciale, mantieni registrazioni <0>A</0> dall'upstream",
"rewrite_AAAA": "<0>AAAA</0>: valore speciale, mantieni registrazioni <0>AAAA</0> dall'upstream",
"disable_ipv6": "Disabilita IPv6",
"disable_ipv6_desc": "Se questa funzione è abilitata, tutte le query DNS per gli indirizzi IPv6 (tipo AAAA) verranno eliminate.",
"fastest_addr": "Indirizzo IP più veloce",
@@ -489,6 +503,8 @@
"check": "Controlla",
"form_enter_host": "Inserisci un nome per l'host",
"filtered_custom_rules": "Filtrato dalle regole filtro personalizzate",
"choose_from_list": "Scegli dalla lista",
"add_custom_list": "Aggiungi lista personalizzata",
"host_whitelisted": "L'host è presente nella whitelist",
"check_ip": "Indirizzi IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -512,7 +528,6 @@
"dnssec_enable": "Abilita DNSSEC",
"dnssec_enable_desc": "Imposta la spunta DNSSEC nelle interrogazioni DNS in uscita e verifica il risultato (è richiesta l'attivazione del risolutore DNSSEC)",
"validated_with_dnssec": "Verificato con DNSSEC",
"show_all_responses": "Tutti i responsi",
"show_blocked_responses": "Bloccato",
"show_whitelisted_responses": "Nella whitelist",
"show_processed_responses": "Processato",
@@ -524,5 +539,25 @@
"rewritten": "Riscritto",
"safe_search": "Ricerca sicura",
"blocklist": "Lista di blocco",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Dimensioni cache",
"cache_size_desc": "Dimensioni cache DNS (in byte)",
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
"enter_cache_size": "Immetti dimensioni cache",
"enter_cache_ttl_min_override": "Immetti TTL minimo",
"enter_cache_ttl_max_override": "Immetti TTL massimo",
"cache_ttl_min_override_desc": "Sovrascrivi valore TTL (minimo) ricevuto dall'upstream del server. Questo valore non può superare 3600 (1 ora)",
"cache_ttl_max_override_desc": "Sovrascrivi valore TTL (massimo) ricevuto dall'upstream del server",
"min_exceeds_max_value": "Il valore minimo eccede quello massimo",
"value_not_larger_than": "Il valore non può essere maggiore di {{maximum}}",
"filter_category_general": "Generale",
"filter_category_security": "Sicurezza",
"filter_category_regional": "Regionale",
"filter_category_other": "Altro",
"filter_category_general_desc": "Liste che bloccano tracciamenti e pubblicità sulla maggioranza dei dispositivi",
"filter_category_security_desc": "Liste specializzate sul blocco di malware, phishing o domini scam",
"filter_category_regional_desc": "Liste focalizzare su pubblicità regionali e server traccianti",
"filter_category_other_desc": "Altre liste di blocco",
"click_to_view_queries": "Clicca per visualizzare query"
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "新しい許可リスト",
"edit_blocklist": "ブロックリストの編集",
"edit_allowlist": "許可リストの編集",
"choose_blocklist": "ブロックリストの選択",
"choose_allowlist": "許可リストの選択",
"enter_valid_blocklist": "ブロックリストへ有効なURLを入力してください。",
"enter_valid_allowlist": "許可リストへ有効なURLを入力してください。",
"form_error_url_format": "URLフォーマットが間違っています",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "クライアントIPを匿名化する",
"anonymize_client_ip_desc": "ログと統計にクライアントの完全なIPアドレスを保存しない",
"dns_config": "DNSサーバ設定",
"dns_cache_config": "DNSキャッシュ設定",
"dns_cache_config_desc": "ここでDNSキャッシュを設定できます。",
"blocking_mode": "ブロックモード",
"default": "デフォルト",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "ドメイン",
"answer": "応答",
"filter_added_successfully": "フィルタの追加に成功しました",
"filter_removed_successfully": "リストの削除に成功しました。",
"filter_updated": "フィルタの更新に成功しました",
"statistics_configuration": "統計設定",
"statistics_retention": "統計保持",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "独自ホストリストの作成についての<0>詳細はこちら</0>。",
"blocked_by_response": "応答されたCNAMEかIPアドレスによるブロック",
"blocked_by_cname_or_ip": "CNAMEもしくはIPアドレスによってブロック済み",
"try_again": "再試行する",
"domain_desc": "DNSリライトしたいドメイン名やワイルドカードを入力してください。",
"example_rewrite_domain": "このドメイン名のみへのレスポンスをリライトする",
"example_rewrite_wildcard": "<0>example.org</0>のすべてのサブドメインへのレスポンスをリライトする",
"rewrite_ip_address": "IPアドレス入力した場合AまたはAAAA応答でこのIPが使用されます。",
"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_desc": "チェックすると、IPv6アドレスタイプAAAAのすべてのDNSクエリは破棄されます。",
"fastest_addr": "最速のIPアドレス",
@@ -494,6 +504,8 @@
"check": "チェックする",
"form_enter_host": "ホスト名を入力してください",
"filtered_custom_rules": "カスタム・フィルタリングルールによる処理されました",
"choose_from_list": "リストから選択する",
"add_custom_list": "カスタムリストを追加する",
"host_whitelisted": "ホストはホワイトリストに登録されています",
"check_ip": "IPアドレス: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "DNSSECを有効にする",
"dnssec_enable_desc": "DNSクエリの応答にDNSSECフラグを設定し、結果を確認しますDNSSEC対応のリゾルバが必要です",
"validated_with_dnssec": "DNSSECにて検証済",
"show_all_responses": "すべての応答",
"all_queries": "すべてのクエリ",
"show_blocked_responses": "ブロック済",
"show_whitelisted_responses": "ホワイトリストにあり",
"show_processed_responses": "処理済",
@@ -529,5 +541,27 @@
"rewritten": "書換",
"safe_search": "セーフサーチ",
"blocklist": "ブロックリスト",
"milliseconds_abbreviation": "ms"
}
"milliseconds_abbreviation": "ms",
"cache_size": "キャッシュサイズ",
"cache_size_desc": "DNSキャッシュサイズバイト単位",
"cache_ttl_min_override": "最小TTLの上書き",
"cache_ttl_max_override": "最大TTLの上書き",
"enter_cache_size": "キャッシュサイズを入力してください",
"enter_cache_ttl_min_override": "最小TTLを入力してください",
"enter_cache_ttl_max_override": "最大TTLを入力してください",
"cache_ttl_min_override_desc": "上流サーバから受信したTTL値最小を上書き。この値は36001時間を超えることはできません。",
"cache_ttl_max_override_desc": "上流サーバから受信したTTL値最大を上書き。",
"min_exceeds_max_value": "最小値が最大値を超えています",
"value_not_larger_than": "値は{{maximum}}より大きくすることはできません",
"filter_category_general": "一般",
"filter_category_security": "セキュリティ",
"filter_category_regional": "地域別",
"filter_category_other": "その他",
"filter_category_general_desc": "ほとんどのデバイスにて追跡と広告をブロックするリストです。",
"filter_category_security_desc": "マルウェア、フィッシング、詐欺ドメインのブロック専用リストです。",
"filter_category_regional_desc": "それぞれの地域の広告と追跡サーバをターゲットするリストです。",
"filter_category_other_desc": "その他のブロックリストです。",
"original_response": "当初の応答",
"click_to_view_queries": "クエリを表示するにはクリックしてください",
"port_53_faq_link": "多くの場合、ポート53は \"DNSStubListener\" または \"systemd-resolved\" サービスによって利用されています。これを解決する方法については、<0>この手順</0>をお読みください。"
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "새 허용 목록",
"edit_blocklist": "차단 목록 수정",
"edit_allowlist": "허용 목록 수정",
"choose_blocklist": "차단 목록 선택",
"choose_allowlist": "허용 목록 선택",
"enter_valid_blocklist": "차단 목록에 유효한 URL을 입력해주세요.",
"enter_valid_allowlist": "허용 목록에 유효한 URL을 입력해주세요.",
"form_error_url_format": "잘못된 URL 형식",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "클라이언트 IP 익명화",
"anonymize_client_ip_desc": "클라이언트의 전체 IP 주소를 로그와 통계에 저장하지 않습니다.",
"dns_config": "DNS 서버 설정",
"dns_cache_config": "DNS 캐시 구성",
"dns_cache_config_desc": "여기에서 DNS 캐시를 구성 할 수 있습니다",
"blocking_mode": "차단 모드",
"default": "기본",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "도메인",
"answer": "응답",
"filter_added_successfully": "목록이 성공적으로 추가됨",
"filter_removed_successfully": "목록이 성공적으로 제거되었습니다",
"filter_updated": "필터가 성공적으로 업데이트됨",
"statistics_configuration": "통계 구성",
"statistics_retention": "통계 저장 기간",
@@ -475,10 +480,15 @@
"whois": "후이즈",
"filtering_rules_learn_more": "차단 리스트를 직접 호스트하는 법을 <0>알아보세요</0>.",
"blocked_by_response": "응답 중 차단된 CNAME 또는 IP",
"blocked_by_cname_or_ip": "CNAME 또는 IP에 의해 차단됨",
"try_again": "다시 시도해주세요",
"domain_desc": "다시 작성할 도메인 이름 또는 와일드카드를 입력합니다.",
"example_rewrite_domain": "이 도메인 이름에 대한 응답을 변경합니다.",
"example_rewrite_wildcard": "모든 서브 도메인에 대한 <0>example.org</0> 응답을 변경합니다",
"rewrite_ip_address": "IP 주소: 이 IP를 A 또는 AAAA 응답에 사용합니다",
"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_desc": "이 기능이 활성화되면 IPv6 (타입 AAAA) 의 모든 DNS 쿼리가 드랍됩니다.",
"fastest_addr": "가장 빠른 IP 주소",
@@ -494,6 +504,8 @@
"check": "확인",
"form_enter_host": "호스트 이름을 입력해주세요",
"filtered_custom_rules": "사용자 정의 필터링 규칙으로 필터링됨",
"choose_from_list": "목록에서 선택",
"add_custom_list": "사용자 정의 목록 추가",
"host_whitelisted": "예외 목록에 있는 호스트",
"check_ip": "IP 주소: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "DNSSEC 활성화",
"dnssec_enable_desc": "발신 DNS 쿼리에서 DNSSEC 플래그를 설정하고 결과를 확인합니다 (DNSSEC-enabled resolver 필수)",
"validated_with_dnssec": "DNSSEC로 검증됨",
"show_all_responses": "모든 응답",
"all_queries": "모든 쿼리",
"show_blocked_responses": "차단됨",
"show_whitelisted_responses": "예외 적용됨",
"show_processed_responses": "처리됨",
@@ -529,5 +541,26 @@
"rewritten": "재작성됨",
"safe_search": "세이프 서치",
"blocklist": "차단 목록",
"milliseconds_abbreviation": "ms"
}
"milliseconds_abbreviation": "ms",
"cache_size": "캐시 크기",
"cache_size_desc": "DNS 캐시 크기 (바이트)",
"cache_ttl_min_override": "최소 TTL 무시",
"cache_ttl_max_override": "최대 TTL 무시",
"enter_cache_size": "캐시 크기를 입력하세요",
"enter_cache_ttl_min_override": "최소 TTL을 입력하세요",
"enter_cache_ttl_max_override": "최대 TTL을 입력하세요",
"cache_ttl_min_override_desc": "업스트림 서버에서 수신한 TTL 값(최소)을 무시합니다. 이 값은 3600(1시간)보다 클 수 없습니다",
"cache_ttl_max_override_desc": "업스트림 서버에서 수신한 TTL 값(최대)을 무시합니다",
"min_exceeds_max_value": "최소값이 최대값을 초과합니다",
"value_not_larger_than": "값은 {{maximum}}보다 클 수 없습니다",
"filter_category_general": "일반 목록",
"filter_category_security": "보안 목록",
"filter_category_regional": "지역 목록",
"filter_category_other": "기타",
"filter_category_general_desc": "대부분의 기기에서 추적 및 광고를 차단하는 목록",
"filter_category_security_desc": "멀웨어, 피싱 또는 사기 도메인을 차단하는 목록",
"filter_category_regional_desc": "지역 광고 및 추적 서버에 중점을 둔 목록",
"filter_category_other_desc": "기타 차단 목록",
"original_response": "원래 응답",
"click_to_view_queries": "쿼리를 보려면 클릭합니다"
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nieuwe toestemmingslijst",
"edit_blocklist": "Blokkeerlijst beheren",
"edit_allowlist": "Toestemmingslijst beheren",
"choose_blocklist": "Blokkeringslijsten selecteren",
"choose_allowlist": "Toestemmingslijsten selecteren",
"enter_valid_blocklist": "Voer een geldige URL in voor de blokkeerlijst.",
"enter_valid_allowlist": "Voer een geldige URL in voor de toestemmingslijst.",
"form_error_url_format": "Ongeldig URL formaat",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Cliënt IP anonimiseren",
"anonymize_client_ip_desc": "Het volledige IP-adres van de cliënt niet opnemen in log- en statistiekbestanden",
"dns_config": "DNS server configuratie",
"dns_cache_config": "DNS cache configuratie",
"dns_cache_config_desc": "Hier kan de DNS cache geconfigureerd worden",
"blocking_mode": "Blocking modus",
"default": "Standaard",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Domein",
"answer": "Antwoord",
"filter_added_successfully": "De lijst is succesvol toegevoegd",
"filter_removed_successfully": "De lijst is succesvol verwijderd",
"filter_updated": "De lijst is succesvol geüpdatet",
"statistics_configuration": "Statistieken configuratie",
"statistics_retention": "Statistieken retentie",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Meer informatie</0> over het maken van je eigen host lijsten.",
"blocked_by_response": "Geblokkeerd door CNAME of IP als antwoord",
"blocked_by_cname_or_ip": "Geblokkeerd via CNAME of IP",
"try_again": "Probeer opnieuw",
"domain_desc": "Voer de domeinnaam of wildcard in die herschreven moet worden.",
"example_rewrite_domain": "herschrijf reacties uitsluitend voor deze domeinnaam.",
"example_rewrite_wildcard": "herschrijf reacties voor alle subdomeinen van <0>example.org</0>.",
"rewrite_ip_address": "IP adres: gebruik dit IP in een A of AAAA antwoord",
"rewrite_domain_name": "Domeinnaam: een CNAME record toevoegen",
"rewrite_A": "<0>A</0>: speciale waarde, <0>A</0> records uit de upstream bewaren",
"rewrite_AAAA": "<0>AAAA</0>: speciale waarde, <0>AAAA</0> records uit de upstream bewaren",
"disable_ipv6": "Zet IPv6 uit",
"disable_ipv6_desc": "Als deze functie is ingeschakeld, worden alle DNS-query's voor IPv6-adressen (type AAAA) verwijderd.",
"fastest_addr": "Snelste IP adres",
@@ -494,6 +504,8 @@
"check": "Controleren",
"form_enter_host": "Voer een hostnaam in",
"filtered_custom_rules": "Gefilterd door aangepaste filterregels",
"choose_from_list": "Uit de lijst selecteren",
"add_custom_list": "Aangepaste lijst toevoegen",
"host_whitelisted": "De host staat op de toestemmingslijst",
"check_ip": "IP-adressen: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "DNSSEC inschakelen",
"dnssec_enable_desc": "Zet de DNSSEC-vlag aan bij uitgaande DNS-query's en controleer het resultaat (DNSSEC-compatibele resolver is vereist)",
"validated_with_dnssec": "Gevalideerd met DNSSEC",
"show_all_responses": "Alle reacties",
"all_queries": "Alle vragen",
"show_blocked_responses": "Geblokkeerd",
"show_whitelisted_responses": "Op toestemmingslijst",
"show_processed_responses": "Verwerkt",
@@ -529,5 +541,27 @@
"rewritten": "Herschreven",
"safe_search": "Veilig zoeken",
"blocklist": "Blokkeerlijst",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Cache grootte",
"cache_size_desc": "DNS cache grootte (in bytes)",
"cache_ttl_min_override": "Minimale TTL overschrijven",
"cache_ttl_max_override": "Maximale TTL overschrijven",
"enter_cache_size": "Cache grootte invoeren",
"enter_cache_ttl_min_override": "Minimale TTL invoeren",
"enter_cache_ttl_max_override": "Maximale TTL invoeren",
"cache_ttl_min_override_desc": "Overschrijft TTL waarde (minimaal) ontvangen van de upstream server. Deze waarde mag niet hoger als 3600 (1 uur) zijn",
"cache_ttl_max_override_desc": "Overschrijft TTL waarde (maximaal) ontvangen van de upstream server",
"min_exceeds_max_value": "Minimale waarde overschrijdt de maximale waarde",
"value_not_larger_than": "Waarde mag niet hoger zijn dan {{maximum}}",
"filter_category_general": "Algemeen",
"filter_category_security": "Beveiliging",
"filter_category_regional": "Regionaal",
"filter_category_other": "Overig",
"filter_category_general_desc": "Lijsten die volgers en advertenties op de meeste apparaten blokkeert",
"filter_category_security_desc": "Lijsten gespecialiseerd in het blokkeren van malware, phising of scam domeinen",
"filter_category_regional_desc": "Lijsten die focussen op regionale ads en tracking servers",
"filter_category_other_desc": "Overige blokkeerlijsten",
"original_response": "Oorspronkelijke reactie",
"click_to_view_queries": "Klik om queries te bekijken",
"port_53_faq_link": "Poort 53 wordt vaak gebruikt door services als DNSStubListener- of de systeem DNS-resolver. Lees a.u.b. <0>deze instructie</0> hoe dit is op te lossen."
}

View File

@@ -2,6 +2,9 @@
"client_settings": "Klientinnstillinger",
"example_upstream_reserved": "du kan bestemme en oppstrøms-DNS <0>for et spesifikt domene(r)</0>",
"upstream_parallel": "Bruk parallele forespørsler for å få oppfarten på behandlinger, ved å forespørre til alle oppstrømstjenerne samtidig",
"parallel_requests": "Parallelle forespørsler",
"load_balancing": "Pågangstrykk-utjevning",
"load_balancing_desc": "Forespør én tjener om gangen. AdGuard Home vil bruke en 'vektlagt tilfeldig valg'-algoritme for å velge tjener, slik at den raskeste tjeneren blir brukt oftere.",
"bootstrap_dns": "Bootstrap-DNS-tjenere",
"bootstrap_dns_desc": "Bootstrap-DNS-tjenere brukes til å oppklare IP-adressene til DoH/DoT-oppklarerene som du har valgt som oppstrømstjenere.",
"check_dhcp_servers": "Se etter DHCP-tjenere",
@@ -36,6 +39,7 @@
"dhcp_interface_select": "Velg DHCP-grensesnitt",
"dhcp_hardware_address": "Maskinvareadresse",
"dhcp_ip_addresses": "IP-adresser",
"ip": "IP-adresse",
"dhcp_table_hostname": "Vertsnavn",
"dhcp_table_expires": "Utløper",
"dhcp_warning": "Hvis du vil aktivere DHCP-tjeneren likevel, så sørg for at det ikke er noen andre aktive DHCP-tjenere i nettverket ditt. Ellers kan det knekke internettilgangen til tilkoblede enheter!",
@@ -48,10 +52,14 @@
"dhcp_static_leases_not_found": "Ingen statiske DHCP-leieavtaler ble funnet",
"dhcp_add_static_lease": "Legg til statisk leieavtale",
"dhcp_reset": "Er du sikker på at du vil tilbakestille DHCP-oppsettet?",
"country": "Land",
"city": "By",
"delete_confirm": "Er du sikker på at du vil slette «{{key}}»?",
"form_enter_hostname": "Skriv inn vertsnavnet",
"error_details": "Feildetaljer",
"response_details": "Svardetaljer",
"request_details": "Detaljer over forespørsel",
"client_details": "Klientdetaljer",
"details": "Detaljer",
"back": "Tilbake",
"dashboard": "Kontrollsenter",
@@ -59,9 +67,12 @@
"filters": "Filtre",
"filter": "Filter",
"query_log": "Forespørselslogg",
"compact": "Kompakt",
"nothing_found": "Ingenting ble funnet",
"faq": "OSS",
"version": "Versjon",
"address": "Adresse",
"protocol": "Protokoll",
"on": "PÅ",
"off": "AV",
"copyright": "Opphavsrett",
@@ -134,8 +145,10 @@
"rules_count_table_header": "Antall oppføringer",
"last_time_updated_table_header": "Senest oppdatert",
"actions_table_header": "Handlinger",
"request_table_header": "Forespørsel",
"edit_table_action": "Rediger",
"delete_table_action": "Slett",
"elapsed": "Utløpt",
"filters_and_hosts_hint": "AdGuard Home forstår grunnleggende adblock-oppføringer, «hosts»-filsyntaks, og domenelister.",
"no_blocklist_added": "Ingen blokkeringslister er lagt til",
"no_whitelist_added": "Ingen hvitelister er lagt til",
@@ -143,14 +156,17 @@
"add_allowlist": "Legg til hviteliste",
"cancel_btn": "Avbryt",
"enter_name_hint": "Skriv inn navn",
"enter_url_or_path_hint": "Skriv inn listens URL eller fulle filbane",
"check_updates_btn": "Se etter oppdateringer",
"new_blocklist": "Ny blokkeringsliste",
"new_allowlist": "Ny hviteliste",
"edit_blocklist": "Rediger blokkeringsliste",
"edit_allowlist": "Rediger hviteliste",
"choose_blocklist": "Velg blokkeringslister",
"enter_valid_blocklist": "Skriv inn en gyldig nettadresse til blokkeringslisten.",
"enter_valid_allowlist": "Skriv inn en gyldig nettadresse til hvitelisten.",
"form_error_url_format": "Ugyldig URL-format",
"form_error_url_or_path_format": "Listens URL eller fulle filbane er ugyldig",
"custom_filter_rules": "Selvvalgte filtreringsregler",
"custom_filter_rules_hint": "Skriv inn én oppføring per linje. Du kan bruke adblock-oppføringer, «hosts»-filsyntaks, eller rå domener.",
"examples_title": "Eksempler",
@@ -175,8 +191,10 @@
"time_table_header": "Tidspunkt",
"date": "Dato",
"domain_name_table_header": "Domenenavn",
"domain_or_client": "Domene eller klient",
"type_table_header": "Type",
"response_table_header": "Respons",
"response_code": "Svarkode",
"client_table_header": "Klient",
"empty_response_status": "Tomt innhold",
"show_all_filter_type": "Vis alle",
@@ -195,6 +213,7 @@
"query_log_filtered": "Filtrert av {{filter}}",
"query_log_confirm_clear": "Er du sikker på at du vil slette hele forespørselsloggen?",
"query_log_cleared": "Forespørselsloggen ble vellykket slettet",
"query_log_updated": "Forespørselsloggen ble vellykket oppdatert",
"query_log_clear": "Tøm forespørselsloggene",
"query_log_retention": "Beholding av forespørselsloggføringene",
"query_log_enable": "Skru på loggføring",
@@ -202,6 +221,8 @@
"query_log_disabled": "Forespørselsloggen er skrudd av og kan bli satt opp i <0>innstillingene</0>",
"query_log_strict_search": "Bruk anførselstegn for strenge søk",
"query_log_retention_confirm": "Er du sikker på at du vil endre hvor lenge forespørselsloggføringene skal beholdes? Hvis du reduserer den interne verdien, vil noe av dataene gå tapt",
"anonymize_client_ip": "Anonymiser klient-IP-en",
"anonymize_client_ip_desc": "Ikke lagre den fulle IP-adressen til klienten i loggføringer eller statistikker",
"dns_config": "DNS-tjeneroppsett",
"blocking_mode": "Blokkeringsmodus",
"default": "Standardmodus",
@@ -210,7 +231,9 @@
"custom_ip": "Tilpasset IP",
"blocking_ipv4": "IPv4-blokkering",
"blocking_ipv6": "IPv6-blokkering",
"dns_over_https": "DNS-over-HTTPS",
"dns_over_tls": "DNS-over-TLS",
"plain_dns": "Ordinær DNS",
"form_enter_rate_limit": "Skriv inn forespørselsfrekvensgrense",
"rate_limit": "Forespørselsfrekvensgrense",
"edns_enable": "Aktiver EDNS-klientundernett",
@@ -223,12 +246,14 @@
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
"tracker_source": "Sporerkilde",
"source_label": "Kilde",
"found_in_known_domain_db": "Funnet i databasen over kjente domener.",
"category_label": "Kategori",
"rule_label": "Oppføring",
"list_label": "Liste",
"unknown_filter": "Ukjent filter {{filterId}}",
"known_tracker": "Kjent sporer",
"install_welcome_title": "Velkommen til AdGuard Home!",
"install_welcome_desc": "AdGuard Home er en nettverksdekkende reklame-og-sporings-blokkerende DNS-tjener. Formålet dens er å la deg styre hele nettverket ditt og alle dine enheter, og den krever ikke at klientene bruker spesifikke programmer.",
"install_settings_title": "Admin-nettgrensesnitt",
@@ -257,6 +282,7 @@
"install_devices_router_list_1": "Åpne innstillingene til ruteren din. Vanligvis kan du få tilgang til den på nettleseren din gjennom en URL (f.eks. http://192.168.0.1/ eller http://192.168.1.1/). Du kan bli spurt om å skrive inn passordet ditt. Hvis du ikke husker det, kan du som oftest tilbakestille passordet ditt ved å trykke på knapp på selve ruteren. Noen rutere krever et spesifikt program, som i så fall er ment å allerede ha blitt installert på din PC/mobil.",
"install_devices_router_list_2": "Finn DHCP-/DNS-innstillingene. Se etter DNS-bokstavene ved siden av et felt som tillater to eller tre sett med sifre, som hver er delt opp i fire grupper på 1-3 sifre.",
"install_devices_router_list_3": "Skriv inn din AdGuard Home-tjeners adresser her.",
"install_devices_router_list_4": "På noen rutertyper, f.eks. Altibox sine hjemmesentraler, kan man ikke velge en selvvalgt DNS-tjener. I så fall kan det hjelpe på saken om du setter opp AdGuard Home som en <0>DHCP-tjener</0>. Alternativt, burde du se i bruksanvisningen til din spesifikke rutermodell om hvordan man tilpasser DNS-tjenerne.",
"install_devices_windows_list_1": "Åpne «Kontrollpanel» gjennom Start-menyen eller et Windows-søk.",
"install_devices_windows_list_2": "Gå til «Nettverk og internett»-kategorien, og så til «Nettverks- og delingssenter».",
"install_devices_windows_list_3": "På den venstre siden av skjermen, finn «Endre innstillinger for nettverkskort» og klikk på den.",
@@ -390,6 +416,7 @@
"rewrite_confirm_delete": "Er du sikker på at du vil slette DNS-omdirigeringen for «{{key}}»?",
"rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.",
"rewrite_applied": "Benyttet omdirigeringsregelen",
"rewrite_hosts_applied": "Omskrevet av 'hosts'-oppføringen",
"dns_rewrites": "DNS-omdirigeringer",
"form_domain": "Skriv inn domene",
"form_answer": "Skriv inn IP-adresse eller domenenavn",
@@ -434,7 +461,7 @@
"filters_interval": "Filteroppdateringsvanlighet",
"disabled": "Skrudd av",
"username_label": "Brukernavn",
"username_placeholder": "Skriv inn brukernacvn",
"username_placeholder": "Skriv inn brukernavn",
"password_label": "Passord",
"password_placeholder": "Skriv inn passord",
"sign_in": "Logg på",
@@ -455,6 +482,8 @@
"example_rewrite_wildcard": "omskriv svarene til alle <0>example.org</0>-underdomener.",
"disable_ipv6": "Skru av IPv6",
"disable_ipv6_desc": "Hvis dette er skrudd på, vil alle DNS-forespørslene til IPv6-adresser (AAAA-type) bli droppet.",
"fastest_addr": "Raskeste IP-adresse",
"fastest_addr_desc": "Forespør alle DNS-tjenerne og hent den raskeste IP-adressen blant alle svarene. Dette vil gjøre DNS-forespørslene tregere, siden vi må vente på svar fra alle DNS-tjenerne, men det vil forbedre tilkoblingen generelt.",
"autofix_warning_text": "Hvis du klikker på «Fiks», vil AdGuard Home sette opp systemet ditt til å bruke 'AdGuard Home'-DNS-tjeneren.",
"autofix_warning_list": "Den vil utføre disse handlingene: <0>Skru av systemets DNSStubListener</0> <0>Sette DNS-tjeneradressen til 127.0.0.1</0> <0>Bytte ut det symbolske lenkemålet til /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (gjeninnlast 'systemd-resolved'-tjenesten)</0>",
"autofix_warning_result": "Som følge av det vil alle DNS-forespørsler fra systemet ditt bli behandlet av AdGuard Home som standard.",
@@ -466,6 +495,8 @@
"check": "Sjekk",
"form_enter_host": "Legg til et domenenavn",
"filtered_custom_rules": "Filtrert av Selvvalgte filtreringsoppføringer",
"choose_from_list": "Velg fra listen",
"add_custom_list": "Legg til en selvvalgt liste",
"host_whitelisted": "Domenet er hvitelistet",
"check_ip": "IP-adresser: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -486,10 +517,27 @@
"confirm_static_ip": "AdGuard Home vil sette opp {{ip}} til å bli din statiske IP-adresse. Vil du fortsette?",
"list_updated": "{{count}} liste oppdatert",
"list_updated_plural": "{{count}} lister oppdatert",
"dnssec_enable": "Skru på DNSSEC",
"dnssec_enable_desc": "Fest på DNSSEC-flagg til utgående DNS-forespørsler og sjekk resultatet (En DNS-oppstrømstjener med DNSSEC-støtte er påkrevd)",
"validated_with_dnssec": "Validert med DNSSEC",
"show_blocked_responses": "Blokkért",
"show_whitelisted_responses": "Hvitelistet",
"show_processed_responses": "Bearbeidet",
"blocked_safebrowsing": "Blokkert av barnevennlig nettlesing",
"blocked_adult_websites": "Blokkerte voksennettsteder",
"blocked_threats": "Blokkerte trusler",
"allowed": "Unntak",
"blocklist": "Blokkeringsliste"
}
"filtered": "Filtrert",
"rewritten": "Omskrevet",
"safe_search": "Trygge søk",
"blocklist": "Blokkeringsliste",
"milliseconds_abbreviation": "ms",
"cache_size": "Mellomlagerstørrelse",
"enter_cache_size": "Skriv inn mellomlagerstørrelse",
"filter_category_general": "Generelt",
"filter_category_security": "Sikkerhet",
"filter_category_regional": "Regional",
"filter_category_other": "Andre",
"filter_category_other_desc": "Andre blokkeringslister",
"click_to_view_queries": "Klikk for å vise forespørsler"
}

View File

@@ -67,6 +67,7 @@
"filters": "Filtry",
"filter": "Filtr",
"query_log": "Dziennik zapytań",
"compact": "Kompaktowy",
"nothing_found": "Nic nie znaleziono",
"faq": "FAQ",
"version": "wersja",
@@ -100,14 +101,14 @@
"number_of_dns_query_days": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
"number_of_dns_query_days_plural": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
"number_of_dns_query_24_hours": "Liczba zapytań DNS przetworzonych w ciągu ostatnich 24 godzin",
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy bloków hosta",
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy zablokowanych hostów",
"number_of_dns_query_blocked_24_hours_by_sec": "Liczba żądań DNS zablokowanych przez moduł AdGuard Bezpieczne Przeglądanie",
"number_of_dns_query_blocked_24_hours_adult": "Liczba zablokowanych witryn dla dorosłych",
"enforced_save_search": "Wymuszone bezpieczne wyszukiwanie",
"number_of_dns_query_to_safe_search": "Liczba żądań DNS do wyszukiwarek, dla których zastosowano wymuszenie bezpiecznego wyszukiwania",
"average_processing_time": "Średni czas przetwarzania",
"average_processing_time_hint": "Średni czas przetwarzania żądania DNS liczony w milisekundach",
"block_domain_use_filters_and_hosts": "Blokuj domeny za pomocą filtrów i plików host",
"block_domain_use_filters_and_hosts": "Zablokuj domeny za pomocą filtrów i plików host",
"filters_block_toggle_hint": "Możesz skonfigurować reguły blokowania w ustawieniach <a href='#filters'>Filtry</a> ",
"use_adguard_browsing_sec": "Użyj usługi sieciowej Bezpieczne Przeglądanie AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home sprawdzi, czy domena jest na czarnej liście przez serwis internetowy Bezpieczne Przeglądanie. Będzie korzystać z interfejsu API przyjaznego dla prywatności w celu przeprowadzenia kontroli: na serwer wysyłany jest tylko krótki prefiks nazwy domeny SHA256.",
@@ -118,10 +119,10 @@
"no_servers_specified": "Nie określono serwerów",
"general_settings": "Ustawienia główne",
"dns_settings": "Ustawienia DNS",
"dns_blocklists": "Lista zablokowanych DNS",
"dns_allowlists": "Lista dozwolonych DNS",
"dns_blocklists": "Listy zablokowanych DNS",
"dns_allowlists": "Listy dozwolonych DNS",
"dns_blocklists_desc": "AdGuard Home zablokuje domeny pasujące do listy zablokowanych.",
"dns_allowlists_desc": "Domeny z białej listy DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
"dns_allowlists_desc": "Domeny z listy dozwolonych DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
"custom_filtering_rules": "Niestandardowe reguły filtrowania",
"encryption_settings": "Ustawienia szyfrowania",
"dhcp_settings": "Ustawienia DHCP",
@@ -149,8 +150,8 @@
"delete_table_action": "Usuń",
"elapsed": "Upłynęło",
"filters_and_hosts_hint": "AdGuard Home rozumie podstawowe reguły adblocka i składnię plików hostów.",
"no_blocklist_added": "Nie dodano listy zablokowanych",
"no_whitelist_added": "Nie dodano listy dozwolonych",
"no_blocklist_added": "Nie dodano list zablokowanych",
"no_whitelist_added": "Nie dodano list dozwolonych",
"add_blocklist": "Dodaj listę zablokowanych",
"add_allowlist": "Dodaj listę dozwolonych",
"cancel_btn": "Anuluj",
@@ -161,6 +162,8 @@
"new_allowlist": "Nowa lista dozwolonych",
"edit_blocklist": "Edytuj listę zablokowanych",
"edit_allowlist": "Edytuj listę dozwolonych",
"choose_blocklist": "Wybierz listy zablokowanych",
"choose_allowlist": "Wybierz listy dozwolonych",
"enter_valid_blocklist": "Wpisz prawidłowy adres URL do listy zablokowanych.",
"enter_valid_allowlist": "Wpisz prawidłowy adres URL do listy dozwolonych.",
"form_error_url_format": "Format adresu URL jest nieprawidłowy",
@@ -174,7 +177,7 @@
"example_comment": "! Tutaj jest komentarz",
"example_comment_meaning": "komentarz",
"example_comment_hash": "# Również komentarz",
"example_regex_meaning": "blokuj dostęp do domen pasujących do określonego wyrażenia regularnego",
"example_regex_meaning": "zablokuj dostęp do domen pasujących do określonego wyrażenia regularnego",
"example_upstream_regular": "normalny DNS (przez UDP)",
"example_upstream_dot": "zaszyfrowany <0>DNS-over-TLS</0>",
"example_upstream_doh": "zaszyfrowany <0>DNS-over-HTTPS</0>",
@@ -211,6 +214,7 @@
"query_log_filtered": "Filtrowane przez {{filter}}",
"query_log_confirm_clear": "Czy na pewno chcesz wyczyścić cały dziennik zapytań?",
"query_log_cleared": "Dziennik zapytań został pomyślnie wyczyszczony",
"query_log_updated": "Dziennik zapytań został zaktualizowany",
"query_log_clear": "Wyczyść dzienniki zapytań",
"query_log_retention": "Przechowywanie dzienników zapytań",
"query_log_enable": "Włącz dziennik",
@@ -221,6 +225,8 @@
"anonymize_client_ip": "Anonimizuj adres IP klienta",
"anonymize_client_ip_desc": "Nie zapisuj pełnego adresu IP w dziennikach i statystykach",
"dns_config": "Konfiguracja serwera DNS",
"dns_cache_config": "Konfiguracja pamięci podręcznej DNS",
"dns_cache_config_desc": "Tutaj możesz skonfigurować pamięć podręczną DNS",
"blocking_mode": "Tryb blokowania",
"default": "Domyślny",
"nxdomain": "NXDOMAIN",
@@ -243,6 +249,7 @@
"blocking_mode_null_ip": "Null IP: Odpowiedz z zerowym adresem IP (0.0.0.0 dla A; :: dla AAAA)",
"blocking_mode_custom_ip": "Niestandardowy adres IP: Odpowiedz ręcznie ustawionym adresem IP",
"upstream_dns_client_desc": "Jeśli to pole pozostanie puste, AdGuard Home użyje serwerów skonfigurowanych w <0>Ustawieniach DNS</0>.",
"tracker_source": "Źródło skryptu śledzącego",
"source_label": "Źródło",
"found_in_known_domain_db": "Znaleziono w bazie danych znanych domen.",
"category_label": "Kategoria",
@@ -420,7 +427,7 @@
"form_error_answer_format": "Nieprawidłowy format odpowiedzi",
"configure": "Skonfiguruj",
"main_settings": "Ustawienia główne",
"block_services": "Blokuj określone usługi",
"block_services": "Zablokuj określone usługi",
"blocked_services": "Zablokowane usługi",
"blocked_services_desc": "Pozwala szybko zablokować popularne witryny i usługi.",
"blocked_services_saved": "Zablokowane usługi zostały pomyślnie zapisane",
@@ -437,11 +444,12 @@
"stats_params": "Konfiguracja statystyk",
"config_successfully_saved": "Konfiguracja została pomyślnie zapisana",
"interval_24_hour": "24 godziny",
"interval_days": "{{count}} dzień",
"interval_days": "{{count}} dni",
"interval_days_plural": "{{count}} dni",
"domain": "Domena",
"answer": "Odpowiedź",
"filter_added_successfully": "Lista została pomyślnie dodana",
"filter_removed_successfully": "Lista została usunięta",
"filter_updated": "Filtr został pomyślnie zaktualizowany",
"statistics_configuration": "Konfiguracja statystyk",
"statistics_retention": "Przechowywanie statystyk",
@@ -472,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Dowiedz się więcej</0> o tworzeniu własnych list blokowania hostów.",
"blocked_by_response": "W odpowiedzi zablokowany przez CNAME lub IP",
"blocked_by_cname_or_ip": "Zablokowany przez rekord CNAME lub adres IP",
"try_again": "Spróbuj ponownie",
"domain_desc": "Wpisz nazwę domeny lub symbol wieloznaczny, który chcesz przepisać.",
"example_rewrite_domain": "przepisz odpowiedzi tylko dla tej nazwy domeny.",
"example_rewrite_wildcard": "przepisz odpowiedzi dla wszystkich subdomen <0>example.org</0>.",
"rewrite_ip_address": "Adres IP: użyj tego adresu IP w odpowiedzi A lub AAAA",
"rewrite_domain_name": "Nazwa domeny: dodaj rekord CNAME",
"rewrite_A": "<0>A</0>: wartość specjalna, zachowaj rekord <0>A</0> z głównego serwera DNS",
"rewrite_AAAA": "<0>AAAA</0>: wartość specjalna, zachowaj rekord <0>AAAA</0> z głównego serwera DNS",
"disable_ipv6": "Wyłącz IPv6",
"disable_ipv6_desc": "Jeśli ta funkcja jest włączona, wszystkie zapytania DNS dotyczące adresów IPv6 (typ AAAA) zostaną usunięte.",
"fastest_addr": "Najszybszy adres IP",
@@ -491,6 +504,8 @@
"check": "Sprawdź",
"form_enter_host": "Wpisz nazwę hosta",
"filtered_custom_rules": "Filtrowane według niestandardowych reguł filtrowania",
"choose_from_list": "Wybierz z listy",
"add_custom_list": "Dodaj listę niestandardową",
"host_whitelisted": "Host znajduje się na białej liście",
"check_ip": "Adresy IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -514,7 +529,7 @@
"dnssec_enable": "Włącz DNSSEC",
"dnssec_enable_desc": "Ustaw flagę DNSSEC w wychodzących zapytaniach DNS i sprawdź wynik (wymagany jest usługodawca z obsługą zabezpieczania DNSSEC)",
"validated_with_dnssec": "Zweryfikowany przez DNSSEC",
"show_all_responses": "Wszystkie odpowiedzi",
"all_queries": "Wszystkie zapytania",
"show_blocked_responses": "Zablokowane",
"show_whitelisted_responses": "Biała lista",
"show_processed_responses": "Przetworzono",
@@ -526,5 +541,27 @@
"rewritten": "Przepisane",
"safe_search": "Bezpieczne wyszukiwanie",
"blocklist": "Lista zablokowanych",
"milliseconds_abbreviation": "ms"
}
"milliseconds_abbreviation": "ms",
"cache_size": "Rozmiar pamięci podręcznej",
"cache_size_desc": "Rozmiar pamięci podręcznej DNS (w bajtach)",
"cache_ttl_min_override": "Nadpisz minimalną wartość TTL",
"cache_ttl_max_override": "Nadpisz maksymalną wartość TTL",
"enter_cache_size": "Wpisz rozmiar pamięci podręcznej",
"enter_cache_ttl_min_override": "Wpisz minimalną wartość TTL",
"enter_cache_ttl_max_override": "Wpisz maksymalną wartość TTL",
"cache_ttl_min_override_desc": "Nadpisz wartość TTL (minimalną) otrzymaną od serwera nadrzędnego. Wartość nie może być większa niż 3600 (1 godzina)",
"cache_ttl_max_override_desc": "Nadpisz wartość TTL (maksymalną) otrzymaną od serwera nadrzędnego",
"min_exceeds_max_value": "Minimalna wartość przekracza maksymalną wartość",
"value_not_larger_than": "Wartość nie może być większa niż {{maximum}}",
"filter_category_general": "Ogólne",
"filter_category_security": "Bezpieczeństwo",
"filter_category_regional": "Regionalne",
"filter_category_other": "Inne",
"filter_category_general_desc": "Listy, które blokują skrypty śledzące i reklamy na większości urządzeń",
"filter_category_security_desc": "Listy, które specjalizują się w blokowaniu domen ze złośliwym oprogramowaniem, phishingiem lub oszustwami",
"filter_category_regional_desc": "Listy, które koncentrują się na reklamach regionalnych i serwerach ze skryptami śledzącymi",
"filter_category_other_desc": "Inne listy zablokowanych",
"original_response": "Oryginalna odpowiedź",
"click_to_view_queries": "Kliknij, aby wyświetlić zapytania",
"port_53_faq_link": "Port 53 jest często zajęty przez usługi \"DNSStubListener\" lub \"systemd-resolved\". Przeczytaj <0>tę instrukcję</0> jak to rozwiązać."
}

View File

@@ -67,6 +67,8 @@
"filters": "Filtros",
"filter": "Filtro",
"query_log": "Registro de consultas",
"compact": "Compactar",
"nothing_found": "Nada encontrado",
"faq": "FAQ",
"version": "Versão",
"address": "Endereço",
@@ -160,6 +162,8 @@
"new_allowlist": "Nova lista branca",
"edit_blocklist": "Editar lista negra",
"edit_allowlist": "Editar lista branca",
"choose_blocklist": "Escolha as listas negras",
"choose_allowlist": "Escolha as listas brancas",
"enter_valid_blocklist": "Digite uma URL válida para a lista negra.",
"enter_valid_allowlist": "Digite uma URL válida para a lista branca.",
"form_error_url_format": "Formato da URL inválida",
@@ -191,6 +195,7 @@
"domain_or_client": "Domínio ou cliente",
"type_table_header": "Tipo",
"response_table_header": "Resposta",
"response_code": "Código de resposta",
"client_table_header": "Cliente",
"empty_response_status": "Vazio",
"show_all_filter_type": "Mostrar todos",
@@ -209,6 +214,7 @@
"query_log_filtered": "Filtrado por {{filter}}",
"query_log_confirm_clear": "Você tem certeza que deseja limpar o registro de consulta?",
"query_log_cleared": "O registro de consulta foi limpo com sucesso",
"query_log_updated": "O registro da consulta foi atualizado com sucesso",
"query_log_clear": "Limpar registros de consulta",
"query_log_retention": "Arquivamento de registros de consultas",
"query_log_enable": "Ativar registro",
@@ -219,6 +225,8 @@
"anonymize_client_ip": "Tornar anônimo o IP do cliente",
"anonymize_client_ip_desc": "Não salva o endereço de IP completo do cliente em registros e estatísticas",
"dns_config": "Configuração do servidor DNS",
"dns_cache_config": "Configuração de cache DNS",
"dns_cache_config_desc": "Aqui você pode configurar o cache do DNS",
"blocking_mode": "Modo de bloqueio",
"default": "Padrão",
"nxdomain": "NXDOMAIN",
@@ -241,6 +249,7 @@
"blocking_mode_null_ip": "IP nulo: Responder com endereço IP zero (0.0.0.0 para A; :: para AAAA)",
"blocking_mode_custom_ip": "IP personalizado: Responder com um endereço IP definido manualmente",
"upstream_dns_client_desc": "Se você mantiver este campo vazio, o AdGuard Home usará os servidores configurados nas configurações <0>DNS</0>.",
"tracker_source": "Fonte do rastreador",
"source_label": "Fonte",
"found_in_known_domain_db": "Encontrado no banco de dados de domínios conhecidos.",
"category_label": "Categoria",
@@ -440,6 +449,7 @@
"domain": "Domínio",
"answer": "Resposta",
"filter_added_successfully": "O filtro foi adicionado com sucesso",
"filter_removed_successfully": "A lista foi removida com sucesso",
"filter_updated": "O filtro atualizado com sucesso",
"statistics_configuration": "Configurações de estatísticas",
"statistics_retention": "Permanência das estatísticas",
@@ -474,6 +484,10 @@
"domain_desc": "Digite o nome do domínio ou wildcard que pretende reescrever.",
"example_rewrite_domain": "reescrever respostas apenas para este nome de domínio.",
"example_rewrite_wildcard": "reescrever respostas para todos subdomínios <0>exemplo.org</0>.",
"rewrite_ip_address": "Endereço IP: use esse IP em uma resposta A ou AAAA",
"rewrite_domain_name": "Nome de domínio: adicione um registro CNAME",
"rewrite_A": "<0>A</0>: valor especial, mantenha <0>A</0> nos registros do upstream",
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantenha <0>AAAA</0> nos registros do upstream",
"disable_ipv6": "Desativar IPv6",
"disable_ipv6_desc": "Se este recurso estiver ativado, todas as consultas de DNS para endereços IPv6 (tipo AAAA) serão ignoradas.",
"fastest_addr": "Endereço de IP mais rápido",
@@ -489,6 +503,8 @@
"check": "Verificar",
"form_enter_host": "Digite o nome do host",
"filtered_custom_rules": "Filtrado pelas regras de filtragem personalizadas",
"choose_from_list": "Escolha na lista",
"add_custom_list": "Adicionar uma lista personalizada",
"host_whitelisted": "O host está na lista branca",
"check_ip": "Endereços de IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -512,7 +528,6 @@
"dnssec_enable": "Ativar DNSSEC",
"dnssec_enable_desc": "Definir a flag DNSSEC nas consultas de DNS em andamento e verificar o resultado (é necessário um resolvedor DNSSEC ativado)",
"validated_with_dnssec": "Validado com DNSSEC",
"show_all_responses": "Todas as respostas",
"show_blocked_responses": "Bloqueado",
"show_whitelisted_responses": "Na lista branca",
"show_processed_responses": "Processado",
@@ -524,5 +539,25 @@
"rewritten": "Reescrito",
"safe_search": "Pesquisa segura",
"blocklist": "Lista negra",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Tamanho do cache",
"cache_size_desc": "Tamanho do cache do DNS (em bytes)",
"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",
"enter_cache_ttl_min_override": "Digite o TTL mínimo",
"enter_cache_ttl_max_override": "Digite o TTL máximo",
"cache_ttl_min_override_desc": "Substituição do valor TTL (mínimo) recebido do servidor servidor DNS primário. Esse valor não pode exceder 3600 (1 hora)",
"cache_ttl_max_override_desc": "Substituição do valor TTL (máximo) recebido do servidor de DNS primário",
"min_exceeds_max_value": "O valor mínimo excede o valor máximo",
"value_not_larger_than": "O valor não pode ser maior que {{maximum}}",
"filter_category_general": "Geral",
"filter_category_security": "Segurança",
"filter_category_regional": "Regional",
"filter_category_other": "Outro",
"filter_category_general_desc": "Listas que bloqueiam o rastreamento e a publicidade na maioria dos dispositivos",
"filter_category_security_desc": "Listas especializadas em bloquear domínios de malware, phishing ou fraude",
"filter_category_regional_desc": "Listas focadas em anúncios regionais e servidores de rastreamento",
"filter_category_other_desc": "Outras listas negras",
"click_to_view_queries": "Clique para ver as consultas"
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nouă autorizare",
"edit_blocklist": "Editare blocare",
"edit_allowlist": "Editare autorizare",
"choose_blocklist": "Alegeți liste de blocări",
"choose_allowlist": "Alegeți liste de permisiuni",
"enter_valid_blocklist": "Introduceți un URL valid pentru blocare.",
"enter_valid_allowlist": "Introduceți un URL valid pentru autorizare.",
"form_error_url_format": "Format URL invalid",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonimizare client IP",
"anonymize_client_ip_desc": "Nu salvați adresa IP completă a clientului în jurnale și statistici",
"dns_config": "Configurația serverului DNS",
"dns_cache_config": "Configurare cache DNS",
"dns_cache_config_desc": "Aici puteți configura cache-ul DNS",
"blocking_mode": "Modul de blocare",
"default": "Implicit",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Domeniu",
"answer": "Răspuns",
"filter_added_successfully": "Filtrul a fost adăugat cu succes",
"filter_removed_successfully": "Lista a fost eliminată cu succes",
"filter_updated": "Filtrul a fost actualizat cu succes",
"statistics_configuration": "Configurația statisticilor",
"statistics_retention": "Păstrarea statisticilor",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Aflați mai multe</0> despre crearea propriilor liste hosts.",
"blocked_by_response": "Blocat de CNAME sau IP ca răspuns",
"blocked_by_cname_or_ip": "Blocat de CNAME sau IP",
"try_again": "Încercați din nou",
"domain_desc": "Introduceți un nume de domeniu sau wildcard care doriți să fie rescris.",
"example_rewrite_domain": "rescrie răspunsuri numai pentru acest nume de domeniu.",
"example_rewrite_wildcard": "rescrie răspunsuri pentru toate subdomeniile <0>exemplu.org</0>.",
"rewrite_ip_address": "Adresa IP: utilizați acest IP într-un răspuns A sau AAAA",
"rewrite_domain_name": "Nume de domeniu: adăugați o înregistrare CNAME",
"rewrite_A": "<0>A</0>: valoare specială, păstrați <0>A</0> înregistrări din amonte",
"rewrite_AAAA": "<0>AAAA</0>: valoare specială, păstrați <0>AAAA</0> înregistrări din amonte",
"disable_ipv6": "Dezactivați IPv6",
"disable_ipv6_desc": "Dacă această opțiune este activată, toate interogările DNS pentru adrese IPv6 (tip AAAA) vor fi anulate.",
"fastest_addr": "Cea mai rapidă adresă IP",
@@ -494,6 +504,8 @@
"check": "Verificați",
"form_enter_host": "Introduceți un nume de host",
"filtered_custom_rules": "Filtrat prin reguli de filtrare personalizate",
"choose_from_list": "Alege din listă",
"add_custom_list": "Adăugați propria listă",
"host_whitelisted": "Numele de host este în lista albă",
"check_ip": "Adrese IP: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Activați DNSSEC",
"dnssec_enable_desc": "Setați steagul DNSSEC pe interogările DNS de ieșire și verificați rezultatul (este necesar un resolver DNSSEC activat)",
"validated_with_dnssec": "Validat cu DNSSEC",
"show_all_responses": "Toate răspunsurile",
"all_queries": "Toate interogările",
"show_blocked_responses": "Blocat",
"show_whitelisted_responses": "Pe lista albă",
"show_processed_responses": "Tratat",
@@ -529,5 +541,27 @@
"rewritten": "Rescrise",
"safe_search": "Căutare sigură",
"blocklist": "Lista neagră",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Mărime cache",
"cache_size_desc": "Mărime cache DNS (în octeți)",
"cache_ttl_min_override": "Suprascrieți TTL minim",
"cache_ttl_max_override": "Suprascrieți TTL maxim",
"enter_cache_size": "Introduceți mărime cache",
"enter_cache_ttl_min_override": "Introduceți TTL minim",
"enter_cache_ttl_max_override": "Introduceți TTL maxim",
"cache_ttl_min_override_desc": "Suprascrie valoarea TTL (minimă) primită de la serverul din amonte. valoare nu poate fi mai mare de 3600 (1 oră)",
"cache_ttl_max_override_desc": "Suprascrie valoarea TTL (maximă) primită de la serverul din amonte",
"min_exceeds_max_value": "Valoarea minimă depășește valoarea maximă",
"value_not_larger_than": "Valoarea nu poate fi mai mare decât {{maximum}}",
"filter_category_general": "General",
"filter_category_security": "Securitate",
"filter_category_regional": "Regional",
"filter_category_other": "Altele",
"filter_category_general_desc": "Liste care blochează urmărirea și publicitatea pe majoritatea aparatelor",
"filter_category_security_desc": "Liste specializate în blocarea domeniilor malware, phishing sau înșelătorie",
"filter_category_regional_desc": "Liste focalizate pe reclame regionale și servere de urmărire",
"filter_category_other_desc": "Alte liste de blocări",
"original_response": "Răspuns original",
"click_to_view_queries": "Clicați pentru a vizualiza interogări",
"port_53_faq_link": "Portul 53 este adesea ocupat de serviciile \"DNSStubListener\" sau \"systemd-resolved\". Vă rugăm să citiți <0>această instrucțiune</0> despre cum să rezolvați aceasta."
}

View File

@@ -110,11 +110,11 @@
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов хостов",
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a href='#filters'> \"Фильтрах\"</a>.",
"use_adguard_browsing_sec": "Использовать Безопасную навигацию AdGuard",
"use_adguard_browsing_sec": "Включить Безопасную навигацию AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home проверит, включен ли домен в веб-службу безопасности браузера. Он будет использовать API, чтобы выполнить проверку: на сервер отправляется только короткий префикс имени домена SHA256.",
"use_adguard_parental": "Используйте модуль Родительского контроля AdGuard ",
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
"enforce_safe_search": "Усилить безопасный поиск",
"enforce_safe_search": "Включить безопасный поиск",
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, Youtube, Bing, DuckDuckGo, Yandex и Pixabay.",
"no_servers_specified": "Нет указанных серверов",
"general_settings": "Основные настройки",
@@ -162,6 +162,8 @@
"new_allowlist": "Новый белый список",
"edit_blocklist": "Редактировать черный список",
"edit_allowlist": "Редактировать белый список",
"choose_blocklist": "Выберите списки блокировки",
"choose_allowlist": "Выберите списки разрешённых",
"enter_valid_blocklist": "Добавьте действующий URL-адрес в черный список.",
"enter_valid_allowlist": "Добавьте действующий URL-адрес в белый список.",
"form_error_url_format": "Неверный формат URL",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Анонимизировать IP-адрес клиента",
"anonymize_client_ip_desc": "Не сохранять полный IP-адрес клиента в журналах и статистике",
"dns_config": "Настройки DNS-сервера",
"dns_cache_config": "Настройка кэша DNS",
"dns_cache_config_desc": "Здесь можно настроить кэш DNS",
"blocking_mode": "Режим блокировки",
"default": "Стандартный",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Домен",
"answer": "Ответ",
"filter_added_successfully": "Список успешно добавлен",
"filter_removed_successfully": "Список успешно удален",
"filter_updated": "Список успешно обновлен",
"statistics_configuration": "Конфигурация статистики",
"statistics_retention": "Сохранение статистики",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Узнайте больше</0> о создании собственных списков блокировки хостов.",
"blocked_by_response": "Заблокировано по CNAME или IP в ответе",
"blocked_by_cname_or_ip": "Заблокировано с помощью CNAME или IP",
"try_again": "Попробовать еще раз",
"domain_desc": "Введите имя или маску домена, который вы хотите перенаправить.",
"example_rewrite_domain": "перенаправляет ответы только для этого домена.",
"example_rewrite_wildcard": "перенаправляет ответы для всех поддоменов <0>example.org</0>.",
"rewrite_ip_address": "IP-адрес: используйте этот IP для А или АААА ответов",
"rewrite_domain_name": "Доменное имя: добавить запись CNAME",
"rewrite_A": "<0>A</0>: специальное значение, хранить записи <0>A</0> с upstream-сервера",
"rewrite_AAAA": "<0>AAAA</0>: специальное значение, хранить записи <0>AAAA</0> с upstream-сервера",
"disable_ipv6": "Отключить IPv6",
"disable_ipv6_desc": "Если эта опция включена, все DNS-запросы адресов IPv6 (тип AAAA) будут игнорироваться.",
"fastest_addr": "Самый быстрый IP-адрес",
@@ -494,6 +504,8 @@
"check": "Проверить",
"form_enter_host": "Введите имя хоста",
"filtered_custom_rules": "Отфильтрованы с помощью пользовательских правил фильтрации",
"choose_from_list": "Выбрать из списка",
"add_custom_list": "Добавить свой список",
"host_whitelisted": "Хост занесен в белый список",
"check_ip": "IP-адреса: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,8 +529,8 @@
"dnssec_enable": "Включить DNSSEC",
"dnssec_enable_desc": "Установите флаг DNSSEC в исходящих DNS-запросах и проверьте результат (требуется резолвер с поддержкой DNSSEC)",
"validated_with_dnssec": "Подтверждено с помощью DNSSEC",
"show_all_responses": "Все ответы",
"show_blocked_responses": "Blocked",
"all_queries": "Все запросы",
"show_blocked_responses": "Заблокировано",
"show_whitelisted_responses": "В белом списке",
"show_processed_responses": "Обработан",
"blocked_safebrowsing": "Заблокировано согласно базе данных Safebrowsing",
@@ -529,5 +541,27 @@
"rewritten": "Переписанные",
"safe_search": "Безопасный поиск",
"blocklist": "Черный список",
"milliseconds_abbreviation": "мс"
}
"milliseconds_abbreviation": "мс",
"cache_size": "Размер кеша",
"cache_size_desc": "Размера кеша DNS (в байтах)",
"cache_ttl_min_override": "Переопределить минимальный TTL",
"cache_ttl_max_override": "Переопределить максимальный TTL",
"enter_cache_size": "Введите размер кеша",
"enter_cache_ttl_min_override": "Введите минимальный TTL",
"enter_cache_ttl_max_override": "Введите максимальный TTL",
"cache_ttl_min_override_desc": "Переопределить TTL-значение (минимальное), полученное с upstream-сервера. Это значение не может быть больше 3600 (1 часа)",
"cache_ttl_max_override_desc": "Переопределить TTL-значение (максимальное), полученное с upstream-сервера",
"min_exceeds_max_value": "Минимальное значение превышает максимальное значение",
"value_not_larger_than": "Значение не может быть больше {{maximum}}",
"filter_category_general": "Общие",
"filter_category_security": "Безопасность",
"filter_category_regional": "Региональные",
"filter_category_other": "Другие",
"filter_category_general_desc": "Списки, которые блокируют отслеживание и рекламу на большинстве устройств",
"filter_category_security_desc": "Списки, которые специализируются на блокировке вредоносных программ, фишинговых или мошеннических доменов",
"filter_category_regional_desc": "Списки, которые фокусируются на региональной рекламе и серверах отслеживания",
"filter_category_other_desc": "Другие списки блокировки",
"original_response": "Первоначальный ответ",
"click_to_view_queries": "Нажмите, чтобы просмотреть запросы",
"port_53_faq_link": "Порт 53 часто занят службами \"DNSStubListener\" или \"systemd-resolved\". Ознакомьтесь с <0>инструкцией</0> о том, как это разрешить."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nový zoznam povolených DNS",
"edit_blocklist": "Upraviť zoznam blokovaných DNS",
"edit_allowlist": "Upraviť zoznam povolených DNS",
"choose_blocklist": "Vybrať blokovací zoznam",
"choose_allowlist": "Vybrať povolený zoznam",
"enter_valid_blocklist": "Zadajte platnú URL adresu do zoznamu blokovaných DNS.",
"enter_valid_allowlist": "Zadajte platnú URL adresu do zoznamu povolených DNS.",
"form_error_url_format": "Neplatný URL formát",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonymizujte IP klienta",
"anonymize_client_ip_desc": "Neukladať úplnú IP adresu klienta do protokolov a štatistík",
"dns_config": "Konfigurácia DNS servera",
"dns_cache_config": "Konfigurácia DNS cache",
"dns_cache_config_desc": "Tu môžete nakonfigurovať DNS cache",
"blocking_mode": "Spôsob blokovania",
"default": "Predvolené",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Doména",
"answer": "Odpoveď",
"filter_added_successfully": "Filter bol úspešne pridaný",
"filter_removed_successfully": "Zoznam bol úspešne odstránený",
"filter_updated": "Filter bol úspešne aktualizovaný",
"statistics_configuration": "Konfigurácia štatistiky",
"statistics_retention": "Štatistika za obdobie",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
"blocked_by_cname_or_ip": "Zablokované na základe CNAME alebo IP",
"try_again": "Skúste znova",
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
"example_rewrite_domain": "prepísať odpovede iba pre toto meno domény.",
"example_rewrite_wildcard": "prepísať odpovede pre všetky subdomény <0>example.org</0>.",
"rewrite_ip_address": "IP adresa: použite túto IP v odpovedi A alebo AAAA",
"rewrite_domain_name": "Meno domény: pridajte záznam CNAME",
"rewrite_A": "<0>A</0>: špeciálna hodnota, uchovávajte záznamy <0>A</0> z upstream",
"rewrite_AAAA": "<0>AAAA</0>: špeciálna hodnota, uchovávajte záznamy <0>AAAA</0> z upstream",
"disable_ipv6": "Vypnúť IPv6",
"disable_ipv6_desc": "Ak je táto funkcia zapnutá, všetky dotazy DNS na adresy IPv6 (typ AAAA) budú zrušené.",
"fastest_addr": "Najrýchlejšia IP adresa",
@@ -494,6 +504,8 @@
"check": "Kontrola",
"form_enter_host": "Zadajte meno hostiteľa",
"filtered_custom_rules": "Filtrované podľa vlastných filtračných pravidiel",
"choose_from_list": "Vybrať zo zoznamu",
"add_custom_list": "Pridať vlastný zoznam",
"host_whitelisted": "Hostiteľ je na bielej listine",
"check_ip": "IP adresy: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Zapnúť DNSSEC",
"dnssec_enable_desc": "Nastavte príznak DNSSEC v nasledujúcich DNS dopytoch a skontrolujte výsledok (je potrebný prekladač so zapnutým DNSSEC)",
"validated_with_dnssec": "Overené pomocou DNSSEC",
"show_all_responses": "Všetky odpovede",
"all_queries": "Všetky dopyty",
"show_blocked_responses": "Zablokované",
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
"show_processed_responses": "Spracované",
@@ -529,5 +541,27 @@
"rewritten": "Prepísané",
"safe_search": "Bezpečné vyhľadávanie",
"blocklist": "Zoznam blokovaní",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Veľkosť cache",
"cache_size_desc": "Veľkosť DNS cache (v bajtoch)",
"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",
"enter_cache_ttl_min_override": "Zadať minimálne TTL",
"enter_cache_ttl_max_override": "Zadať maximálne TTL",
"cache_ttl_min_override_desc": "Prepíše hodnotu TTL (minimálnu) prijatú z upstream servera. Táto hodnota nemôže byť väčšia ako 3600 (1 hodina)",
"cache_ttl_max_override_desc": "Prepíše hodnotu TTL (maximálnu) prijatú z upstream servera",
"min_exceeds_max_value": "Minimálna hodnota je väčšia ako maximálna",
"value_not_larger_than": "Hodnota nemôže byť väčšia ako {{maximum}}",
"filter_category_general": "Všeobecné",
"filter_category_security": "Bezpečnosť",
"filter_category_regional": "Regionálne",
"filter_category_other": "Iné",
"filter_category_general_desc": "Zoznamy, ktoré blokujú sledovanie a reklamu na väčšine zariadení",
"filter_category_security_desc": "Zoznamy, ktoré sa špecializujú na blokovanie domén škodlivého softvéru alebo podvodov",
"filter_category_regional_desc": "Zoznamy zamerané na regionálne reklamy a sledovacie servery",
"filter_category_other_desc": "Iné blokovacie zoznamy",
"original_response": "Pôvodná odozva",
"click_to_view_queries": "Kliknite pre zobrazenie dopytov",
"port_53_faq_link": "Port 53 je často obsadený službami \"DNSStubListener\" alebo \"systemd-resolved\". Prečítajte si <0>tento návod</0> o tom, ako to vyriešiť."
}

View File

@@ -67,6 +67,8 @@
"filters": "Filtri",
"filter": "Filtriraj",
"query_log": "Dnevnik poizvedb",
"compact": "Stisni",
"nothing_found": "Nič ni bilo najdeno",
"faq": "Pogosta vprašanja in odgovori (FAQ)",
"version": "različica",
"address": "Naslov",
@@ -160,6 +162,8 @@
"new_allowlist": "Nov seznam dovoljenih",
"edit_blocklist": "Uredi seznam nedovoljenih",
"edit_allowlist": "Uredi seznam dovoljenih",
"choose_blocklist": "Izberite sezname za zaviranje",
"choose_allowlist": "Izberite sezname dovoljenih",
"enter_valid_blocklist": "Vnesite veljaven URL naslov seznama nedovoljenih.",
"enter_valid_allowlist": "Vnesite veljaven URL naslov seznama dovoljenih.",
"form_error_url_format": "Neveljaven format URL naslova",
@@ -191,6 +195,7 @@
"domain_or_client": "Domena ali odjemalec",
"type_table_header": "Vrsta",
"response_table_header": "Odgovor",
"response_code": "Koda odziva",
"client_table_header": "Odjemalec",
"empty_response_status": "Prazno",
"show_all_filter_type": "Prikaži vse",
@@ -209,6 +214,7 @@
"query_log_filtered": "Filtriran z {{filter}}",
"query_log_confirm_clear": "Ali ste prepričani, da želite počistiti celoten dnevnik poizvedb?",
"query_log_cleared": "Dnevnik poizvedb je uspešno izbrisan",
"query_log_updated": "Dnevnik poizvedb je bil uspešno posodobljen",
"query_log_clear": "Počisti dnevnike poizvedb",
"query_log_retention": "Zadrževanje dnevnikov poizvedb",
"query_log_enable": "Omogoči dnevni",
@@ -219,6 +225,8 @@
"anonymize_client_ip": "Anonimiziraj odjemalca IP",
"anonymize_client_ip_desc": "Ne shrani celotnega naslova IP odjemalca v dnevnikih in statistiki",
"dns_config": "Konfiguracija strežnika DNS",
"dns_cache_config": "Konfiguracija strežnika DNS",
"dns_cache_config_desc": "Tu lahko konfigurirate predpomnilnik DNS",
"blocking_mode": "Način zaviranja",
"default": "Privzeto",
"nxdomain": "NXDOMAIN",
@@ -241,6 +249,7 @@
"blocking_mode_null_ip": "Prazen IP: Odziv z ničelnim naslovom IP (0.0.0.0 za A; :: za AAAA)",
"blocking_mode_custom_ip": "IP po meri: Odziv z ročno nastavljenim naslovom IP",
"upstream_dns_client_desc": "Če pustite to polje prazno, bo AdGuard Home uporabil strežnike, konfigurirane v <0>nastavitvah DNS</0>.",
"tracker_source": "Vir sledilca",
"source_label": "Vir",
"found_in_known_domain_db": "Najdeno v zbirki podatkov znanih domen.",
"category_label": "Kategorija",
@@ -440,6 +449,7 @@
"domain": "Domena",
"answer": "Odgovor",
"filter_added_successfully": "Seznam je bil uspešno dodan",
"filter_removed_successfully": "Seznam je bil uspešno odstranjen",
"filter_updated": "Filter je bil uspešno posodobljen",
"statistics_configuration": "Nastavitve statistike",
"statistics_retention": "Statistika zadrževanja",
@@ -469,11 +479,16 @@
"descr": "Opis",
"whois": "Whois",
"filtering_rules_learn_more": "<0>Več o</0> ustvarjanju lastnih seznamov gostiteljev.",
"blocked_by_response": "Onemogočeno z CNAME ali IP v odgovoru",
"blocked_by_response": "Onemogočeno s CNAME ali IP v odgovoru",
"blocked_by_cname_or_ip": "Onemogočeno s CNAME ali IP naslovom",
"try_again": "Poskusi ponovno",
"domain_desc": "Vnesite ime domene ali nadomestni znak, ki ga želite prepisati.",
"example_rewrite_domain": "prepiše odgovore samo za to ime domene.",
"example_rewrite_wildcard": "prepiše odgovore za vse poddomene <0>example.org</0>.",
"rewrite_ip_address": "IP naslov: uporabi ta IP v odzivu A ali AAAA",
"rewrite_domain_name": "Ime domene: dodaj CNAME zapis",
"rewrite_A": ">A</0>: posebna vrednost, obdrži <0>A</0> zapise iz gorvodnega toka",
"rewrite_AAAA": ">A</0>: posebna vrednost, obdrži <0>AAAA</0> zapise iz gorvodnega toka",
"disable_ipv6": "Onemogoči IPv6",
"disable_ipv6_desc": "Če je ta funkcija omogočena, bodo vse poizvedbe DNS za naslove IPv6 (vrste AAAA) izpadle.",
"fastest_addr": "Najhitrejši IP naslov",
@@ -489,6 +504,8 @@
"check": "Preveri",
"form_enter_host": "Vnesite ime gostitelja",
"filtered_custom_rules": "Filtrirano s pravili filtriranja po meri",
"choose_from_list": "Izberi s seznama",
"add_custom_list": "Dodaj seznam po meri",
"host_whitelisted": "Gostitelj je na seznamu dovoljenih",
"check_ip": "IP naslovi: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -512,10 +529,10 @@
"dnssec_enable": "Omogoči DNSSEC",
"dnssec_enable_desc": "V odhodnih poizvedbah DNS nastavite zastavico DNSSEC in preverite rezultat (zahtevan je omogočen reševalnik DNSSEC)",
"validated_with_dnssec": "Potrjen z DNSSEC",
"show_all_responses": "Vsi odgovori",
"all_queries": "Vse poizvedbe",
"show_blocked_responses": "Onemogočen",
"show_whitelisted_responses": "Na seznamu dovoljenih",
"show_processed_responses": "Obdelan",
"show_processed_responses": "Obdelana",
"blocked_safebrowsing": "Onemogočeno z 'Varnim brskanjem'",
"blocked_adult_websites": "Onemogočeno spletnih strani za odrasle",
"blocked_threats": "Onemogočeno groženj",
@@ -524,5 +541,27 @@
"rewritten": "Znova napisano",
"safe_search": "Varno iskanje",
"blocklist": "Seznam nedovoljenih",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Velikost predpomnilnika",
"cache_size_desc": "Velikost predpomnilnika DNS (v bajtih)",
"cache_ttl_min_override": "Preglasi najmanjši TTL",
"cache_ttl_max_override": "Preglasi največji TTL",
"enter_cache_size": "Vnesite velikost predpomnilnika",
"enter_cache_ttl_min_override": "Vnesite najmanjši TTL",
"enter_cache_ttl_max_override": "Vnesite največji TTL",
"cache_ttl_min_override_desc": "Vrednost preglasovanja TTL (najmanjša), prejeta od zgornjega strežnika. Ta vrednost ne sme presegati 3600 (1 ura)",
"cache_ttl_max_override_desc": "Vrednost preglasovanja TTL (največja), prejeta od zgornjega strežnika",
"min_exceeds_max_value": "Najmanjša vrednost presega največjo vrednost",
"value_not_larger_than": "Vrednost ne sme biti večja od {{maximum}}",
"filter_category_general": "Splošno",
"filter_category_security": "Varnost",
"filter_category_regional": "Področno",
"filter_category_other": "Drugo",
"filter_category_general_desc": "Seznami, ki zavirajo sledenje in oglaševanje na večini naprav",
"filter_category_security_desc": "Seznami, ki so specializirani za onemogočanje domen zlonamernih programov, lažnega predstavljanja ali prevar",
"filter_category_regional_desc": "Seznami, ki so osredotočeni na področne oglase in strežnike za sledenje",
"filter_category_other_desc": "Drugi seznami za zaviranje",
"original_response": "Izviren odgovor",
"click_to_view_queries": "Kliknite za prikaz poizvedb",
"port_53_faq_link": "Vrata 53 pogosto zasedajo storitve 'DNSStubListener' ali 'Sistemsko razrešene storitve'. Preberite <0>to navodilo</0> o tem, kako to rešiti."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "Nova lista dozvoljenih",
"edit_blocklist": "Uredi blok listu",
"edit_allowlist": "Uredi listu dozvoljenih",
"choose_blocklist": "Izaberite liste blokiranja",
"choose_allowlist": "Izaberite liste dozvoljenih",
"enter_valid_blocklist": "Unesite važeći URL do blok liste.",
"enter_valid_allowlist": "Unesite važeći URL do liste dozvoljenih.",
"form_error_url_format": "Nevažeći format Url-a",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "Anonimizuj IP klijenta",
"anonymize_client_ip_desc": "Ne čuvaj punu IP adresu klijenta u dnevnicima i statistikama",
"dns_config": "Konfiguracija DNS servera",
"dns_cache_config": "Konfigurisanje DNS predmemorije",
"dns_cache_config_desc": "Ovde možete konfigurisati DNS predmemoriju",
"blocking_mode": "Način blokiranja",
"default": "Podrazumevano",
"nxdomain": "NXDOMAIN",
@@ -445,6 +449,7 @@
"domain": "Domen",
"answer": "Odgovor",
"filter_added_successfully": "Filter je uspešno dodat",
"filter_removed_successfully": "Lista je uspešno uklonjena",
"filter_updated": "Filter je uspešno ažuriran",
"statistics_configuration": "Konfiguracija statistike",
"statistics_retention": "Zadržavanje statistike",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vaše lične blokliste hostova.",
"blocked_by_response": "Blokirano od CNAME ili IP u odgovoru",
"blocked_by_cname_or_ip": "Blokirano od CNAME ili IP",
"try_again": "Pokušaj ponovo",
"domain_desc": "Unesite domen ili džoker koji želite da prepišete.",
"example_rewrite_domain": "prepiši odgovore samo za ovaj domen.",
"example_rewrite_wildcard": "prepiši odgovore za sve poddomene na <0>example.org</0>.",
"rewrite_ip_address": "IP adresa: kkoristite ovaj IP u A ili AAAA odgovoru",
"rewrite_domain_name": "Ime domena: dodajte CNAME zapis",
"rewrite_A": "<0>A</0>: posebna vrednost, zadrži <0>A</0> records iz apstrima",
"rewrite_AAAA": "<0>AAAA</0>: posebna vrednost, zadržite <0>AAAA</0> records iz apstrima",
"disable_ipv6": "Isključi IPv6",
"disable_ipv6_desc": "Ako je ovo uključeno, svi DNS unosi za IPv6 adrese (type AAAA) će biti odbačeni.",
"fastest_addr": "Najbrža IP adresa",
@@ -494,6 +504,8 @@
"check": "Proveri",
"form_enter_host": "Unesite host",
"filtered_custom_rules": "Filtrirano od strane prilagođenog pravila",
"choose_from_list": "Izaberite sa liste",
"add_custom_list": "Dodaj prilagođenu listu",
"host_whitelisted": "Host je na beloj listi",
"check_ip": "IP adrese: {{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "Uključi DNSSEC",
"dnssec_enable_desc": "Postavlja DNSSEC zastavicu u odlaznim DNS zahtevima i proverava rezultat (DNSSEC rešavač je potreban)",
"validated_with_dnssec": "Potvrđeno sa DNSSEC",
"show_all_responses": "Svi odgovori",
"all_queries": "Svi zahtevi",
"show_blocked_responses": "Blokirano",
"show_whitelisted_responses": "Na beloj listi",
"show_processed_responses": "Obrađeno",
@@ -529,5 +541,27 @@
"rewritten": "Prepisano",
"safe_search": "Sigurna pretraga",
"blocklist": "Lista blokiranih",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "Veličina predmemorije",
"cache_size_desc": "Veličina DNS predmemorije (u bitovima)",
"cache_ttl_min_override": "Prepiši najmanji TTL",
"cache_ttl_max_override": "Prepiši najveći TTL",
"enter_cache_size": "Unesite veličinu predmemorije",
"enter_cache_ttl_min_override": "Unesite najmanji TTL",
"enter_cache_ttl_max_override": "Unesite najveći TTL",
"cache_ttl_min_override_desc": "Prepiši TTL vrednost (minimum) dobijen od apstrim servera. Ova vrednost ne može biti veća od 3600 (1 sat)",
"cache_ttl_max_override_desc": "Prepiši TTL vrednost (maksimum) dobijen od apstrim servera",
"min_exceeds_max_value": "Najmanja vrednost je dosegla najveću vrednost",
"value_not_larger_than": "Vrednost ne može biti veća od {{maximum}}",
"filter_category_general": "Opšte",
"filter_category_security": "Bezbednost",
"filter_category_regional": "Region",
"filter_category_other": "Ostalo",
"filter_category_general_desc": "Lista koja blokira praćenja i reklame na većini uređaja",
"filter_category_security_desc": "Lista specijalizovana za blokiranje štetnog softvera, štetnih i fišing domena",
"filter_category_regional_desc": "Lista koja se usredsređuje na regionalne reklame i servere praćenja",
"filter_category_other_desc": "Ostale liste blokiranja",
"original_response": "Izvorni odgovor",
"click_to_view_queries": "Kliknite da pogledate zahteve",
"port_53_faq_link": "Port 53 je najčešće zauzet od \"DNSStubListener\" ili \"systemd-resolved\" usluga. Pročitajte <0>ovo uputstvo</0> kako da to rešite."
}

View File

@@ -239,7 +239,7 @@
"rule_label": "Kural",
"list_label": "Liste",
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
"install_welcome_title": "AdGuard Home'a hoşgeldiniz!",
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenize yarayan bir araçtır, istemci tarafında bir program kullanmanıza gerek duymaz.",
"install_settings_title": "Yönetici Web Arayüzü",
"install_settings_listen": "Dinleme arayüzü",
@@ -505,5 +505,6 @@
"blocked_adult_websites": "Yetişkin içerikli site engellendi",
"blocked_threats": "Engellenen Tehditler",
"allowed": "İzin verildi",
"blocklist": "Engellenen listesi"
"blocklist": "Engellenen listesi",
"port_53_faq_link": "Port 53 genellikle \"DNSStubListener\" veya \"sistemd-resolved\" hizmetler tarafından kullanılır. Lütfen problemin nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun."
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "新的允许清单",
"edit_blocklist": "编辑阻止列表",
"edit_allowlist": "编辑允许列表",
"choose_blocklist": "选择拦截列表",
"choose_allowlist": "选择允许列表",
"enter_valid_blocklist": "输入有效的阻止列表URL",
"enter_valid_allowlist": "输入有效的允许列表URL",
"form_error_url_format": "无效的URL格式",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "匿名化客户端IP",
"anonymize_client_ip_desc": "不要在日志和统计信息中保存客户端的完整IP地址",
"dns_config": "DNS服务设定",
"dns_cache_config": "DNS缓存配置",
"dns_cache_config_desc": "你可以在此处配置 DNS缓存",
"blocking_mode": "拦截模式",
"default": "默认",
"nxdomain": "无效域名",
@@ -445,6 +449,7 @@
"domain": "域名",
"answer": "应答",
"filter_added_successfully": "已成功添加过滤器",
"filter_removed_successfully": "已成功删除该列表",
"filter_updated": "成功更新过滤器",
"statistics_configuration": "统计配置",
"statistics_retention": "统计保留",
@@ -475,10 +480,15 @@
"whois": "Whois",
"filtering_rules_learn_more": "<0>了解更多</0>关于创建自己的hosts清单。",
"blocked_by_response": "因响应的CNAME或IP被屏蔽",
"blocked_by_cname_or_ip": "按CNAME或IP拦截",
"try_again": "重试",
"domain_desc": "输入您要重写的域名或通配符。",
"example_rewrite_domain": "仅重写此域名的响应。",
"example_rewrite_wildcard": "重写所有<0>example.org</0> 子域的响应。",
"rewrite_ip_address": "IP地址在 A或AAAA响应中使用这个IP",
"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_desc": "启用后所有IPv6地址 (type AAAA) 的DNS查询都会被丢弃。",
"fastest_addr": "最快的 IP 地址",
@@ -494,6 +504,8 @@
"check": "检查",
"form_enter_host": "输入主机名称",
"filtered_custom_rules": "被自定义过滤规则过滤",
"choose_from_list": "从列表中选择",
"add_custom_list": "添加一个自定义列表",
"host_whitelisted": "主机在白名单内",
"check_ip": "IP地址{{ip}}",
"check_cname": "CNAME: {{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "启用DNSSEC",
"dnssec_enable_desc": "在发出DNS查询中设置DNSSEC标志并检查结果(需要启用DNSSEC的解析器)",
"validated_with_dnssec": "通过DNSSEC验证",
"show_all_responses": "所有响应",
"all_queries": "所有查询记录",
"show_blocked_responses": "已拦截",
"show_whitelisted_responses": "已列入白名单",
"show_processed_responses": "已处理",
@@ -529,5 +541,27 @@
"rewritten": "重写项",
"safe_search": "安全搜索",
"blocklist": "拦截列表",
"milliseconds_abbreviation": "毫秒"
}
"milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小",
"cache_size_desc": "DNS缓存大小 (单位:字节)",
"cache_ttl_min_override": "覆盖最小TTL值",
"cache_ttl_max_override": "覆盖最大TTL值",
"enter_cache_size": "输入缓存大小",
"enter_cache_ttl_min_override": "输入最小TTL值",
"enter_cache_ttl_max_override": "输入最大TTL值",
"cache_ttl_min_override_desc": "覆盖从上游服务器接收到的TTL值 (最小)。这个值不能超过3600秒(1小时)",
"cache_ttl_max_override_desc": "覆盖从上游服务器接收到的TTL值(最大)",
"min_exceeds_max_value": "最小值超过最大值",
"value_not_larger_than": "值不能大于{{maximum}}",
"filter_category_general": "常规",
"filter_category_security": "安全",
"filter_category_regional": "区域",
"filter_category_other": "其它",
"filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表",
"filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表",
"filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表",
"filter_category_other_desc": "其他阻止列表",
"original_response": "原始响应",
"click_to_view_queries": "点击查看查询",
"port_53_faq_link": "53端口常被DNSStubListener或systemdn解析的服务占用。请阅读<0>这份关于如何解决这一问题的说明</0>"
}

View File

@@ -162,6 +162,8 @@
"new_allowlist": "新的允許清單",
"edit_blocklist": "編輯封鎖清單",
"edit_allowlist": "編輯允許清單",
"choose_blocklist": "選擇封鎖清單",
"choose_allowlist": "選擇允許清單",
"enter_valid_blocklist": "輸入一個到該封鎖清單之有效的網址。",
"enter_valid_allowlist": "輸入一個到該允許清單之有效的網址。",
"form_error_url_format": "無效的網址格式",
@@ -223,6 +225,8 @@
"anonymize_client_ip": "將用戶端 IP 匿名",
"anonymize_client_ip_desc": "不要在記錄和統計資料中儲存用戶端之完整的 IP 位址",
"dns_config": "DNS 伺服器配置",
"dns_cache_config": "DNS 快取配置",
"dns_cache_config_desc": "於此您可配置 DNS 快取",
"blocking_mode": "封鎖模式",
"default": "預設",
"nxdomain": "不存在的網域NXDOMAIN",
@@ -276,7 +280,7 @@
"install_submit_title": "恭喜!",
"install_submit_desc": "該設置程序被完成,且您準備好開始使用 AdGuard Home。",
"install_devices_router": "路由器",
"install_devices_router_desc": "設置將自動地涵蓋被連線至您的家庭路由器之所有的裝置,且您將無需手動地配置它們每個。",
"install_devices_router_desc": "設置將自動地涵蓋所有被連線至您的家庭路由器之裝置,且您將無需手動地配置它們每個。",
"install_devices_address": "AdGuard Home DNS 伺服器正在監聽下列的位址",
"install_devices_router_list_1": "開啟關於您的路由器之偏好設定。通常地,您可透過網址(如 http://192.168.0.1/ 或 http://192.168.1.1/)從您的瀏覽器中存取它。您可能被要求輸入該密碼。如果您不記得它,您經常可透過按壓於該路由器本身上的按鈕來重置密碼。某些路由器需要特定的應用程式,既然如此其應已被安裝於您的電腦/手機上。",
"install_devices_router_list_2": "找到 DHCP/DNS 設定。尋找緊鄰著允許兩組或三組數字集的欄位之 DNS 字母,每組被拆成四個含有一至三個數字的群集。",
@@ -445,6 +449,7 @@
"domain": "網域",
"answer": "回應",
"filter_added_successfully": "該清單已被成功地加入",
"filter_removed_successfully": "該清單已被成功地移除",
"filter_updated": "該清單已被成功地更新",
"statistics_configuration": "統計資料配置",
"statistics_retention": "統計資料保留",
@@ -474,11 +479,16 @@
"descr": "說明",
"whois": "Whois",
"filtering_rules_learn_more": "<0>了解更多</0>有關創建您自己的主機hosts清單。",
"blocked_by_response": "被正規名稱CNAME或 IP 封鎖作為回應",
"blocked_by_response": "在回應過程中被正規名稱CNAME或 IP 封鎖",
"blocked_by_cname_or_ip": "被正規名稱CNAME或 IP 封鎖",
"try_again": "再次嘗試",
"domain_desc": "輸入您想要被改寫的域名或萬用字元wildcard。",
"example_rewrite_domain": "僅對於此域名改寫回應。",
"example_rewrite_wildcard": "對於所有的 <0>example.org</0> 子網域改寫回應。",
"rewrite_ip_address": "IP 位址:在一個 A 或 AAAA 回應中使用此 IP",
"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_desc": "如果此功能被啟用,所有對於 IPv6 位址(類型 AAAA的 DNS 查詢將被丟棄。",
"fastest_addr": "最快的 IP 位址",
@@ -494,6 +504,8 @@
"check": "檢查",
"form_enter_host": "輸入主機名稱",
"filtered_custom_rules": "被自訂的過濾規則過濾",
"choose_from_list": "從該清單中選擇",
"add_custom_list": "增加一個自訂的清單",
"host_whitelisted": "該主機被列入白名單",
"check_ip": "IP 位址:{{ip}}",
"check_cname": "正規名稱CNAME{{cname}}",
@@ -517,7 +529,7 @@
"dnssec_enable": "啟用網域名稱系統安全性擴充功能DNSSEC",
"dnssec_enable_desc": "在發出的 DNS 查詢中設定 DNSSEC 標記並檢查該結果(已啟用 DNSSEC 的解析器是必須的)",
"validated_with_dnssec": "已用網域名稱系統安全性擴充功能DNSSEC驗證",
"show_all_responses": "所有的回應",
"all_queries": "所有的查詢",
"show_blocked_responses": "已封鎖的",
"show_whitelisted_responses": "已列入白名單的",
"show_processed_responses": "已處理的",
@@ -529,5 +541,27 @@
"rewritten": "已改寫的",
"safe_search": "安全搜尋",
"blocklist": "封鎖清單",
"milliseconds_abbreviation": "ms"
"milliseconds_abbreviation": "ms",
"cache_size": "快取大小",
"cache_size_desc": "DNS 快取大小(以位元組)",
"cache_ttl_min_override": "覆寫最小的存活時間TTL以秒數",
"cache_ttl_max_override": "覆寫最大的存活時間TTL以秒數",
"enter_cache_size": "輸入快取大小",
"enter_cache_ttl_min_override": "輸入最小的存活時間TTL",
"enter_cache_ttl_max_override": "輸入最大的存活時間TTL",
"cache_ttl_min_override_desc": "覆寫從上游的伺服器收到的存活時間TTL數值最小值。此數值不能大於 36001 小時)",
"cache_ttl_max_override_desc": "覆寫從上游的伺服器收到的存活時間TTL數值最大值",
"min_exceeds_max_value": "最小值超過最大值",
"value_not_larger_than": "數值不能大於 {{maximum}}",
"filter_category_general": "一般的",
"filter_category_security": "安全性",
"filter_category_regional": "區域性的",
"filter_category_other": "其它的",
"filter_category_general_desc": "封鎖大多數朝向裝置的追蹤和廣告之清單",
"filter_category_security_desc": "專精於封鎖惡意軟體、網路釣魚或詐騙網域之清單",
"filter_category_regional_desc": "專注於區域性的廣告和追蹤伺服器之清單",
"filter_category_other_desc": "其它的封鎖清單",
"original_response": "原始的回應",
"click_to_view_queries": "點擊以檢視查詢",
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。"
}

View File

@@ -1,4 +1,4 @@
import { getIpMatchListStatus } from '../helpers/helpers';
import { getIpMatchListStatus, sortIp } from '../helpers/helpers';
import { IP_MATCH_LIST_STATUS } from '../helpers/constants';
describe('getIpMatchListStatus', () => {
@@ -129,3 +129,356 @@ describe('getIpMatchListStatus', () => {
});
});
});
describe('sortIp', () => {
describe('ipv4', () => {
test('one octet differ', () => {
const arr = [
'127.0.2.0',
'127.0.3.0',
'127.0.1.0',
];
const sortedArr = [
'127.0.1.0',
'127.0.2.0',
'127.0.3.0',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('few octets differ', () => {
const arr = [
'192.168.11.10',
'192.168.10.0',
'192.168.11.11',
'192.168.10.10',
'192.168.1.10',
'192.168.0.1',
'192.168.1.0',
'192.168.1.1',
'192.168.11.0',
'192.168.0.10',
'192.168.10.11',
'192.168.0.11',
'192.168.1.11',
'192.168.0.0',
'192.168.10.1',
'192.168.11.1',
];
const sortedArr = [
'192.168.0.0',
'192.168.0.1',
'192.168.0.10',
'192.168.0.11',
'192.168.1.0',
'192.168.1.1',
'192.168.1.10',
'192.168.1.11',
'192.168.10.0',
'192.168.10.1',
'192.168.10.10',
'192.168.10.11',
'192.168.11.0',
'192.168.11.1',
'192.168.11.10',
'192.168.11.11',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
// Example from issue https://github.com/AdguardTeam/AdGuardHome/issues/1778#issuecomment-640937599
const arr2 = [
'192.168.2.11',
'192.168.3.1',
'192.168.2.100',
'192.168.2.2',
'192.168.2.1',
'192.168.2.10',
'192.168.2.99',
'192.168.2.200',
'192.168.2.199',
];
const sortedArr2 = [
'192.168.2.1',
'192.168.2.2',
'192.168.2.10',
'192.168.2.11',
'192.168.2.99',
'192.168.2.100',
'192.168.2.199',
'192.168.2.200',
'192.168.3.1',
];
expect(arr2.sort(sortIp)).toStrictEqual(sortedArr2);
});
});
describe('ipv6', () => {
test('only long form', () => {
const arr = [
'2001:db8:11a3:9d7:0:0:0:2',
'2001:db8:11a3:9d7:0:0:0:3',
'2001:db8:11a3:9d7:0:0:0:1',
];
const sortedArr = [
'2001:db8:11a3:9d7:0:0:0:1',
'2001:db8:11a3:9d7:0:0:0:2',
'2001:db8:11a3:9d7:0:0:0:3',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('only short form', () => {
const arr = [
'2001:db8::',
'2001:db7::',
'2001:db9::',
];
const sortedArr = [
'2001:db7::',
'2001:db8::',
'2001:db9::',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('long and short forms', () => {
const arr = [
'2001:db8::',
'2001:db7:11a3:9d7:0:0:0:2',
'2001:db6:11a3:9d7:0:0:0:1',
'2001:db6::',
'2001:db7:11a3:9d7:0:0:0:1',
'2001:db7::',
];
const sortedArr = [
'2001:db6::',
'2001:db6:11a3:9d7:0:0:0:1',
'2001:db7::',
'2001:db7:11a3:9d7:0:0:0:1',
'2001:db7:11a3:9d7:0:0:0:2',
'2001:db8::',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
describe('ipv4 and ipv6', () => {
test('ipv6 long form', () => {
const arr = [
'127.0.0.3',
'2001:db8:11a3:9d7:0:0:0:1',
'2001:db8:11a3:9d7:0:0:0:3',
'127.0.0.1',
'2001:db8:11a3:9d7:0:0:0:2',
'127.0.0.2',
];
const sortedArr = [
'127.0.0.1',
'127.0.0.2',
'127.0.0.3',
'2001:db8:11a3:9d7:0:0:0:1',
'2001:db8:11a3:9d7:0:0:0:2',
'2001:db8:11a3:9d7:0:0:0:3',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('ipv6 short form', () => {
const arr = [
'2001:db8:11a3:9d7::1',
'127.0.0.3',
'2001:db8:11a3:9d7::3',
'127.0.0.1',
'2001:db8:11a3:9d7::2',
'127.0.0.2',
];
const sortedArr = [
'127.0.0.1',
'127.0.0.2',
'127.0.0.3',
'2001:db8:11a3:9d7::1',
'2001:db8:11a3:9d7::2',
'2001:db8:11a3:9d7::3',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('ipv6 long and short forms', () => {
const arr = [
'2001:db8:11a3:9d7::1',
'127.0.0.3',
'2001:db8:11a3:9d7:0:0:0:2',
'127.0.0.1',
'2001:db8:11a3:9d7::3',
'127.0.0.2',
];
const sortedArr = [
'127.0.0.1',
'127.0.0.2',
'127.0.0.3',
'2001:db8:11a3:9d7::1',
'2001:db8:11a3:9d7:0:0:0:2',
'2001:db8:11a3:9d7::3',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('always put ipv4 before ipv6', () => {
const arr = [
'::1',
'0.0.0.2',
'127.0.0.1',
'::2',
'2001:db8:11a3:9d7:0:0:0:2',
'0.0.0.1',
'2001:db8:11a3:9d7::1',
];
const sortedArr = [
'0.0.0.1',
'0.0.0.2',
'127.0.0.1',
'::1',
'::2',
'2001:db8:11a3:9d7::1',
'2001:db8:11a3:9d7:0:0:0:2',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
describe('cidr', () => {
test('only ipv4 cidr', () => {
const arr = [
'192.168.0.1/9',
'192.168.0.1/7',
'192.168.0.1/8',
];
const sortedArr = [
'192.168.0.1/7',
'192.168.0.1/8',
'192.168.0.1/9',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('ipv4 and cidr ipv4', () => {
const arr = [
'192.168.0.1/9',
'192.168.0.1',
'192.168.0.1/32',
'192.168.0.1/7',
'192.168.0.1/8',
];
const sortedArr = [
'192.168.0.1/7',
'192.168.0.1/8',
'192.168.0.1/9',
'192.168.0.1/32',
'192.168.0.1',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('only ipv6 cidr', () => {
const arr = [
'2001:db8:11a3:9d7::1/32',
'2001:db8:11a3:9d7::1/64',
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7::1/24',
];
const sortedArr = [
'2001:db8:11a3:9d7::1/24',
'2001:db8:11a3:9d7::1/32',
'2001:db8:11a3:9d7::1/64',
'2001:db8:11a3:9d7::1/128',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
test('ipv6 and cidr ipv6', () => {
const arr = [
'2001:db8:11a3:9d7::1/32',
'2001:db8:11a3:9d7::1',
'2001:db8:11a3:9d7::1/64',
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7::1/24',
];
const sortedArr = [
'2001:db8:11a3:9d7::1/24',
'2001:db8:11a3:9d7::1/32',
'2001:db8:11a3:9d7::1/64',
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7::1',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
describe('invalid input', () => {
const originalError = console.error;
beforeEach(() => {
console.error = jest.fn();
});
afterEach(() => {
expect(console.error).toHaveBeenCalled();
console.error = originalError;
});
test('invalid strings', () => {
const arr = ['invalid ip', 'invalid cidr'];
expect(arr.sort(sortIp)).toStrictEqual(arr);
});
test('invalid ip', () => {
const arr = ['127.0.0.2.', '.127.0.0.1.', '.2001:db8:11a3:9d7:0:0:0:0'];
expect(arr.sort(sortIp)).toStrictEqual(arr);
});
test('invalid cidr', () => {
const arr = ['127.0.0.2/33', '2001:db8:11a3:9d7:0:0:0:0/129'];
expect(arr.sort(sortIp)).toStrictEqual(arr);
});
test('valid and invalid ip', () => {
const arr = ['127.0.0.4.', '127.0.0.1', '.127.0.0.3', '127.0.0.2'];
expect(arr.sort(sortIp)).toStrictEqual(arr);
});
});
describe('mixed', () => {
test('ipv4, ipv6 in short and long forms and cidr', () => {
const arr = [
'2001:db8:11a3:9d7:0:0:0:1/32',
'192.168.1.2',
'127.0.0.2',
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7:0:0:0:1',
'127.0.0.1/12',
'192.168.1.1',
'2001:db8::/32',
'2001:db8:11a3:9d7::1/24',
'192.168.1.2/12',
'2001:db7::/32',
'127.0.0.1',
'2001:db8:11a3:9d7:0:0:0:2',
'192.168.1.1/24',
'2001:db7::/64',
'2001:db7::',
'2001:db8::',
'2001:db8:11a3:9d7:0:0:0:1/128',
'192.168.1.1/12',
'127.0.0.1/32',
'::1',
];
const sortedArr = [
'127.0.0.1/12',
'127.0.0.1/32',
'127.0.0.1',
'127.0.0.2',
'192.168.1.1/12',
'192.168.1.1/24',
'192.168.1.1',
'192.168.1.2/12',
'192.168.1.2',
'::1',
'2001:db7::/32',
'2001:db7::/64',
'2001:db7::',
'2001:db8::/32',
'2001:db8::',
'2001:db8:11a3:9d7::1/24',
'2001:db8:11a3:9d7:0:0:0:1/32',
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7:0:0:0:1/128',
'2001:db8:11a3:9d7:0:0:0:1',
'2001:db8:11a3:9d7:0:0:0:2',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
});

View File

@@ -2,9 +2,9 @@ import { createAction } from 'redux-actions';
import i18next from 'i18next';
import apiClient from '../api/Api';
import { normalizeTextarea } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts';
import { BLOCK_ACTIONS } from '../helpers/constants';
import { splitByNewLine } from '../helpers/helpers';
export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST');
export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE');
@@ -31,9 +31,9 @@ export const setAccessList = (config) => async (dispatch) => {
const { allowed_clients, disallowed_clients, blocked_hosts } = config;
const values = {
allowed_clients: normalizeTextarea(allowed_clients),
disallowed_clients: normalizeTextarea(disallowed_clients),
blocked_hosts: normalizeTextarea(blocked_hosts),
allowed_clients: splitByNewLine(allowed_clients),
disallowed_clients: splitByNewLine(disallowed_clients),
blocked_hosts: splitByNewLine(blocked_hosts),
};
await apiClient.setAccessList(values);

View File

@@ -1,7 +1,7 @@
import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import { normalizeTextarea } from '../helpers/helpers';
import { splitByNewLine } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts';
export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST');
@@ -30,11 +30,11 @@ export const setDnsConfig = (config) => async (dispatch) => {
let hasDnsSettings = false;
if (Object.prototype.hasOwnProperty.call(data, 'bootstrap_dns')) {
data.bootstrap_dns = normalizeTextarea(config.bootstrap_dns);
data.bootstrap_dns = splitByNewLine(config.bootstrap_dns);
hasDnsSettings = true;
}
if (Object.prototype.hasOwnProperty.call(data, 'upstream_dns')) {
data.upstream_dns = normalizeTextarea(config.upstream_dns);
data.upstream_dns = splitByNewLine(config.upstream_dns);
hasDnsSettings = true;
}

View File

@@ -2,8 +2,9 @@ import { createAction } from 'redux-actions';
import i18next from 'i18next';
import axios from 'axios';
import { isVersionGreater, normalizeTextarea, sortClients } from '../helpers/helpers';
import { splitByNewLine, sortClients } from '../helpers/helpers';
import { CHECK_TIMEOUT, SETTINGS_NAMES } from '../helpers/constants';
import { areEqualVersions } from '../helpers/version';
import { getTlsStatus } from './encryption';
import apiClient from '../api/Api';
import { addErrorToast, addNoticeToast, addSuccessToast } from './toasts';
@@ -121,7 +122,7 @@ export const getVersion = (recheck = false) => async (dispatch, getState) => {
const { dnsVersion } = getState().dashboard;
const currentVersion = dnsVersion === 'undefined' ? 0 : dnsVersion;
if (data && isVersionGreater(currentVersion, data.new_version)) {
if (data && !areEqualVersions(currentVersion, data.new_version)) {
dispatch(addSuccessToast('updates_checked'));
} else {
dispatch(addSuccessToast('updates_version_equal'));
@@ -147,7 +148,7 @@ const checkStatus = async (handleRequestSuccess, handleRequestError, attempts =
const rmTimeout = (t) => t && clearTimeout(t);
try {
const response = await axios.get('control/status');
const response = await axios.get(`${apiClient.baseUrl}/status`);
rmTimeout(timeout);
if (response?.status === 200) {
handleRequestSuccess(response);
@@ -279,8 +280,8 @@ export const testUpstream = (config) => async (dispatch) => {
dispatch(testUpstreamRequest());
try {
const values = { ...config };
values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns);
values.upstream_dns = normalizeTextarea(values.upstream_dns);
values.bootstrap_dns = splitByNewLine(values.bootstrap_dns);
values.upstream_dns = splitByNewLine(values.upstream_dns);
const upstreamResponse = await apiClient.testUpstream(values);
const testMessages = Object.keys(upstreamResponse)

View File

@@ -2,12 +2,19 @@ import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import { normalizeLogs, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers';
import { TABLE_DEFAULT_PAGE_SIZE, TABLE_FIRST_PAGE } from '../helpers/constants';
import {
DEFAULT_LOGS_FILTER,
TABLE_DEFAULT_PAGE_SIZE,
TABLE_FIRST_PAGE,
} from '../helpers/constants';
import { addErrorToast, addSuccessToast } from './toasts';
const getLogsWithParams = async (config) => {
const { older_than, filter, ...values } = config;
const rawLogs = await apiClient.getQueryLog({ ...filter, older_than });
const rawLogs = await apiClient.getQueryLog({
...filter,
older_than,
});
const { data, oldest } = rawLogs;
let logs = normalizeLogs(data);
const clientsParams = getParamsForClientsSearch(logs, 'client');
@@ -18,7 +25,11 @@ const getLogsWithParams = async (config) => {
}
return {
logs, oldest, older_than, filter, ...values,
logs,
oldest,
older_than,
filter,
...values,
};
};
@@ -38,7 +49,10 @@ const checkFilteredLogs = async (data, filter, dispatch, total) => {
dispatch(getAdditionalLogsRequest());
try {
const additionalLogs = await getLogsWithParams({ older_than: oldest, filter });
const additionalLogs = await getLogsWithParams({
older_than: oldest,
filter,
});
if (additionalLogs.oldest.length > 0) {
return await checkFilteredLogs(additionalLogs, filter, dispatch, {
logs: [...totalData.logs, ...additionalLogs.logs],
@@ -69,13 +83,19 @@ export const getLogs = (config) => async (dispatch, getState) => {
dispatch(getLogsRequest());
try {
const { isFiltered, filter, page } = getState().queryLogs;
const data = await getLogsWithParams({ ...config, filter });
const data = await getLogsWithParams({
...config,
filter,
});
if (isFiltered) {
const additionalData = await checkFilteredLogs(data, filter, dispatch);
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
dispatch(getLogsSuccess(updatedData));
dispatch(setLogsPagination({ page, pageSize: TABLE_DEFAULT_PAGE_SIZE }));
dispatch(setLogsPagination({
page,
pageSize: TABLE_DEFAULT_PAGE_SIZE,
}));
} else {
dispatch(getLogsSuccess(data));
}
@@ -86,24 +106,48 @@ export const getLogs = (config) => async (dispatch, getState) => {
};
export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
export const setLogsFilterFailure = createAction('SET_LOGS_FILTER_FAILURE');
export const setLogsFilterSuccess = createAction('SET_LOGS_FILTER_SUCCESS');
export const setLogsFilter = (filter) => async (dispatch) => {
dispatch(setLogsFilterRequest());
/**
*
* @param filter
* @param {string} filter.search
* @param {string} filter.response_status query field of RESPONSE_FILTER object
* @returns function
*/
export const setLogsFilter = (filter) => 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) => async (dispatch) => {
dispatch(setFilteredLogsRequest());
try {
const data = await getLogsWithParams({ older_than: '', filter });
const data = await getLogsWithParams({
older_than: '',
filter,
});
const additionalData = await checkFilteredLogs(data, filter, dispatch);
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
dispatch(setLogsFilterSuccess({ ...updatedData, filter }));
dispatch(setFilteredLogsSuccess({
...updatedData,
filter,
}));
dispatch(setLogsPage(TABLE_FIRST_PAGE));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(setLogsFilterFailure(error));
dispatch(setFilteredLogsFailure(error));
}
};
export const resetFilteredLogs = () => setFilteredLogs(DEFAULT_LOGS_FILTER);
export const refreshFilteredLogs = () => async (dispatch, getState) => {
const { filter } = getState().queryLogs;
await dispatch(setFilteredLogs(filter));
};
export const clearLogsRequest = createAction('CLEAR_LOGS_REQUEST');
export const clearLogsFailure = createAction('CLEAR_LOGS_FAILURE');
export const clearLogsSuccess = createAction('CLEAR_LOGS_SUCCESS');

View File

@@ -2,9 +2,10 @@ import axios from 'axios';
import { getPathWithQueryString } from '../helpers/helpers';
import { R_PATH_LAST_PART } from '../helpers/constants';
import { BASE_URL } from '../../constants';
class Api {
baseUrl = 'control';
baseUrl = BASE_URL;
async makeRequest(path, method = 'POST', config) {
try {
@@ -26,18 +27,30 @@ class Api {
throw new Error(`${errorPath} | ${error.response.data} | ${error.response.status}`);
}
throw new Error(`${errorPath} | ${error.message ? error.message : error}`);
throw new Error(`${errorPath} | ${error.message || error}`);
}
}
// Global methods
GLOBAL_STATUS = { path: 'status', method: 'GET' };
GLOBAL_STATUS = {
path: 'status',
method: 'GET',
};
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
GLOBAL_TEST_UPSTREAM_DNS = {
path: 'test_upstream_dns',
method: 'POST',
};
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
GLOBAL_VERSION = {
path: 'version.json',
method: 'POST',
};
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
GLOBAL_UPDATE = {
path: 'update',
method: 'POST',
};
getGlobalStatus() {
const { path, method } = this.GLOBAL_STATUS;
@@ -68,21 +81,45 @@ class Api {
}
// Filtering
FILTERING_STATUS = { path: 'filtering/status', method: 'GET' };
FILTERING_STATUS = {
path: 'filtering/status',
method: 'GET',
};
FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' };
FILTERING_ADD_FILTER = {
path: 'filtering/add_url',
method: 'POST',
};
FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' };
FILTERING_REMOVE_FILTER = {
path: 'filtering/remove_url',
method: 'POST',
};
FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' };
FILTERING_SET_RULES = {
path: 'filtering/set_rules',
method: 'POST',
};
FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' };
FILTERING_REFRESH = {
path: 'filtering/refresh',
method: 'POST',
};
FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' };
FILTERING_SET_URL = {
path: 'filtering/set_url',
method: 'POST',
};
FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' };
FILTERING_CONFIG = {
path: 'filtering/config',
method: 'POST',
};
FILTERING_CHECK_HOST = { path: 'filtering/check_host', method: 'GET' };
FILTERING_CHECK_HOST = {
path: 'filtering/check_host',
method: 'GET',
};
getFilteringStatus() {
const { path, method } = this.FILTERING_STATUS;
@@ -153,11 +190,20 @@ class Api {
}
// Parental
PARENTAL_STATUS = { path: 'parental/status', method: 'GET' };
PARENTAL_STATUS = {
path: 'parental/status',
method: 'GET',
};
PARENTAL_ENABLE = { path: 'parental/enable', method: 'POST' };
PARENTAL_ENABLE = {
path: 'parental/enable',
method: 'POST',
};
PARENTAL_DISABLE = { path: 'parental/disable', method: 'POST' };
PARENTAL_DISABLE = {
path: 'parental/disable',
method: 'POST',
};
getParentalStatus() {
const { path, method } = this.PARENTAL_STATUS;
@@ -180,11 +226,20 @@ class Api {
}
// Safebrowsing
SAFEBROWSING_STATUS = { path: 'safebrowsing/status', method: 'GET' };
SAFEBROWSING_STATUS = {
path: 'safebrowsing/status',
method: 'GET',
};
SAFEBROWSING_ENABLE = { path: 'safebrowsing/enable', method: 'POST' };
SAFEBROWSING_ENABLE = {
path: 'safebrowsing/enable',
method: 'POST',
};
SAFEBROWSING_DISABLE = { path: 'safebrowsing/disable', method: 'POST' };
SAFEBROWSING_DISABLE = {
path: 'safebrowsing/disable',
method: 'POST',
};
getSafebrowsingStatus() {
const { path, method } = this.SAFEBROWSING_STATUS;
@@ -202,11 +257,20 @@ class Api {
}
// Safesearch
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
SAFESEARCH_STATUS = {
path: 'safesearch/status',
method: 'GET',
};
SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' };
SAFESEARCH_ENABLE = {
path: 'safesearch/enable',
method: 'POST',
};
SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' };
SAFESEARCH_DISABLE = {
path: 'safesearch/disable',
method: 'POST',
};
getSafesearchStatus() {
const { path, method } = this.SAFESEARCH_STATUS;
@@ -224,9 +288,15 @@ class Api {
}
// Language
CURRENT_LANGUAGE = { path: 'i18n/current_language', method: 'GET' };
CURRENT_LANGUAGE = {
path: 'i18n/current_language',
method: 'GET',
};
CHANGE_LANGUAGE = { path: 'i18n/change_language', method: 'POST' };
CHANGE_LANGUAGE = {
path: 'i18n/change_language',
method: 'POST',
};
getCurrentLanguage() {
const { path, method } = this.CURRENT_LANGUAGE;
@@ -243,19 +313,40 @@ class Api {
}
// DHCP
DHCP_STATUS = { path: 'dhcp/status', method: 'GET' };
DHCP_STATUS = {
path: 'dhcp/status',
method: 'GET',
};
DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' };
DHCP_SET_CONFIG = {
path: 'dhcp/set_config',
method: 'POST',
};
DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'POST' };
DHCP_FIND_ACTIVE = {
path: 'dhcp/find_active_dhcp',
method: 'POST',
};
DHCP_INTERFACES = { path: 'dhcp/interfaces', method: 'GET' };
DHCP_INTERFACES = {
path: 'dhcp/interfaces',
method: 'GET',
};
DHCP_ADD_STATIC_LEASE = { path: 'dhcp/add_static_lease', method: 'POST' };
DHCP_ADD_STATIC_LEASE = {
path: 'dhcp/add_static_lease',
method: 'POST',
};
DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' };
DHCP_REMOVE_STATIC_LEASE = {
path: 'dhcp/remove_static_lease',
method: 'POST',
};
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
DHCP_RESET = {
path: 'dhcp/reset',
method: 'POST',
};
getDhcpStatus() {
const { path, method } = this.DHCP_STATUS;
@@ -309,11 +400,20 @@ class Api {
}
// Installation
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
INSTALL_GET_ADDRESSES = {
path: 'install/get_addresses',
method: 'GET',
};
INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' };
INSTALL_CONFIGURE = {
path: 'install/configure',
method: 'POST',
};
INSTALL_CHECK_CONFIG = { path: 'install/check_config', method: 'POST' };
INSTALL_CHECK_CONFIG = {
path: 'install/check_config',
method: 'POST',
};
getDefaultAddresses() {
const { path, method } = this.INSTALL_GET_ADDRESSES;
@@ -339,11 +439,20 @@ class Api {
}
// DNS-over-HTTPS and DNS-over-TLS
TLS_STATUS = { path: 'tls/status', method: 'GET' };
TLS_STATUS = {
path: 'tls/status',
method: 'GET',
};
TLS_CONFIG = { path: 'tls/configure', method: 'POST' };
TLS_CONFIG = {
path: 'tls/configure',
method: 'POST',
};
TLS_VALIDATE = { path: 'tls/validate', method: 'POST' };
TLS_VALIDATE = {
path: 'tls/validate',
method: 'POST',
};
getTlsStatus() {
const { path, method } = this.TLS_STATUS;
@@ -369,15 +478,30 @@ class Api {
}
// Per-client settings
GET_CLIENTS = { path: 'clients', method: 'GET' };
GET_CLIENTS = {
path: 'clients',
method: 'GET',
};
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
FIND_CLIENTS = {
path: 'clients/find',
method: 'GET',
};
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
ADD_CLIENT = {
path: 'clients/add',
method: 'POST',
};
DELETE_CLIENT = { path: 'clients/delete', method: 'POST' };
DELETE_CLIENT = {
path: 'clients/delete',
method: 'POST',
};
UPDATE_CLIENT = { path: 'clients/update', method: 'POST' };
UPDATE_CLIENT = {
path: 'clients/update',
method: 'POST',
};
getClients() {
const { path, method } = this.GET_CLIENTS;
@@ -418,9 +542,15 @@ class Api {
}
// DNS access settings
ACCESS_LIST = { path: 'access/list', method: 'GET' };
ACCESS_LIST = {
path: 'access/list',
method: 'GET',
};
ACCESS_SET = { path: 'access/set', method: 'POST' };
ACCESS_SET = {
path: 'access/set',
method: 'POST',
};
getAccessList() {
const { path, method } = this.ACCESS_LIST;
@@ -437,11 +567,20 @@ class Api {
}
// DNS rewrites
REWRITES_LIST = { path: 'rewrite/list', method: 'GET' };
REWRITES_LIST = {
path: 'rewrite/list',
method: 'GET',
};
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
REWRITE_ADD = {
path: 'rewrite/add',
method: 'POST',
};
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
REWRITE_DELETE = {
path: 'rewrite/delete',
method: 'POST',
};
getRewritesList() {
const { path, method } = this.REWRITES_LIST;
@@ -467,9 +606,15 @@ class Api {
}
// Blocked services
BLOCKED_SERVICES_LIST = { path: 'blocked_services/list', method: 'GET' };
BLOCKED_SERVICES_LIST = {
path: 'blocked_services/list',
method: 'GET',
};
BLOCKED_SERVICES_SET = { path: 'blocked_services/set', method: 'POST' };
BLOCKED_SERVICES_SET = {
path: 'blocked_services/set',
method: 'POST',
};
getBlockedServices() {
const { path, method } = this.BLOCKED_SERVICES_LIST;
@@ -486,13 +631,25 @@ class Api {
}
// Settings for statistics
GET_STATS = { path: 'stats', method: 'GET' };
GET_STATS = {
path: 'stats',
method: 'GET',
};
STATS_INFO = { path: 'stats_info', method: 'GET' };
STATS_INFO = {
path: 'stats_info',
method: 'GET',
};
STATS_CONFIG = { path: 'stats_config', method: 'POST' };
STATS_CONFIG = {
path: 'stats_config',
method: 'POST',
};
STATS_RESET = { path: 'stats_reset', method: 'POST' };
STATS_RESET = {
path: 'stats_reset',
method: 'POST',
};
getStats() {
const { path, method } = this.GET_STATS;
@@ -519,13 +676,25 @@ class Api {
}
// Query log
GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
GET_QUERY_LOG = {
path: 'querylog',
method: 'GET',
};
QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' };
QUERY_LOG_CONFIG = {
path: 'querylog_config',
method: 'POST',
};
QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' };
QUERY_LOG_INFO = {
path: 'querylog_info',
method: 'GET',
};
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
QUERY_LOG_CLEAR = {
path: 'querylog_clear',
method: 'POST',
};
getQueryLog(params) {
const { path, method } = this.GET_QUERY_LOG;
@@ -553,7 +722,10 @@ class Api {
}
// Login
LOGIN = { path: 'login', method: 'POST' };
LOGIN = {
path: 'login',
method: 'POST',
};
login(data) {
const { path, method } = this.LOGIN;
@@ -565,7 +737,10 @@ class Api {
}
// Profile
GET_PROFILE = { path: 'profile', method: 'GET' };
GET_PROFILE = {
path: 'profile',
method: 'GET',
};
getProfile() {
const { path, method } = this.GET_PROFILE;
@@ -573,9 +748,15 @@ class Api {
}
// DNS config
GET_DNS_CONFIG = { path: 'dns_info', method: 'GET' };
GET_DNS_CONFIG = {
path: 'dns_info',
method: 'GET',
};
SET_DNS_CONFIG = { path: 'dns_config', method: 'POST' };
SET_DNS_CONFIG = {
path: 'dns_config',
method: 'POST',
};
getDnsConfig() {
const { path, method } = this.GET_DNS_CONFIG;

View File

@@ -28,10 +28,6 @@ body {
}
@media screen and (max-width: 992px) {
.container {
padding: 0 !important;
}
.container--wrap {
min-height: calc(100vh);
}

View File

@@ -1,30 +1,16 @@
import React, { Component, Fragment } from 'react';
import React, { useEffect } from 'react';
import { HashRouter, Route } from 'react-router-dom';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import LoadingBar from 'react-redux-loading-bar';
import { hot } from 'react-hot-loader/root';
import 'react-table/react-table.css';
import '../ui/Tabler.css';
import '../ui/ReactTable.css';
import './index.css';
import Header from '../../containers/Header';
import Dashboard from '../../containers/Dashboard';
import Settings from '../../containers/Settings';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import CustomRules from '../../containers/CustomRules';
import DnsBlocklist from '../../containers/DnsBlocklist';
import DnsAllowlist from '../../containers/DnsAllowlist';
import DnsRewrites from '../../containers/DnsRewrites';
import Dns from '../../containers/Dns';
import Encryption from '../../containers/Encryption';
import Dhcp from '../../containers/Dhcp';
import Clients from '../../containers/Clients';
import Logs from '../../containers/Logs';
import SetupGuide from '../../containers/SetupGuide';
import propTypes from 'prop-types';
import Toasts from '../Toasts';
import Footer from '../ui/Footer';
import Status from '../ui/Status';
@@ -35,31 +21,107 @@ import Icons from '../ui/Icons';
import i18n from '../../i18n';
import Loading from '../ui/Loading';
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
import Header from '../Header';
import { changeLanguage, getDnsStatus } from '../../actions';
import Dashboard from '../../containers/Dashboard';
import Logs from '../../containers/Logs';
import SetupGuide from '../../containers/SetupGuide';
import Settings from '../../containers/Settings';
import Dns from '../../containers/Dns';
import Encryption from '../../containers/Encryption';
import Dhcp from '../../containers/Dhcp';
import Clients from '../../containers/Clients';
import DnsBlocklist from '../../containers/DnsBlocklist';
import DnsAllowlist from '../../containers/DnsAllowlist';
import DnsRewrites from '../../containers/DnsRewrites';
import CustomRules from '../../containers/CustomRules';
import Services from '../Filters/Services';
import { setHtmlLangAttr } from '../../helpers/helpers';
class App extends Component {
componentDidMount() {
this.props.getDnsStatus();
}
const ROUTES = [
{
path: MENU_URLS.root,
component: Dashboard,
exact: true,
},
{
path: [`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs],
component: Logs,
},
{
path: MENU_URLS.guide,
component: SetupGuide,
},
{
path: SETTINGS_URLS.settings,
component: Settings,
},
{
path: SETTINGS_URLS.dns,
component: Dns,
},
{
path: SETTINGS_URLS.encryption,
component: Encryption,
},
{
path: SETTINGS_URLS.dhcp,
component: Dhcp,
},
{
path: SETTINGS_URLS.clients,
component: Clients,
},
{
path: FILTERS_URLS.dns_blocklists,
component: DnsBlocklist,
},
{
path: FILTERS_URLS.dns_allowlists,
component: DnsAllowlist,
},
{
path: FILTERS_URLS.dns_rewrites,
component: DnsRewrites,
},
{
path: FILTERS_URLS.custom_rules,
component: CustomRules,
},
{
path: FILTERS_URLS.blocked_services,
component: Services,
},
];
componentDidUpdate(prevProps) {
if (this.props.dashboard.language !== prevProps.dashboard.language) {
this.setLanguage();
}
}
const renderRoute = ({ path, component, exact }, idx) => <Route
key={idx}
exact={exact}
path={path}
component={component}
/>;
reloadPage = () => {
window.location.reload();
};
const App = () => {
const dispatch = useDispatch();
const {
language,
isCoreRunning,
isUpdateAvailable,
processing,
} = useSelector((state) => state.dashboard, shallowEqual);
handleUpdate = () => {
this.props.getUpdate();
};
const { processing: processingEncryption } = useSelector((
state,
) => state.encryption, shallowEqual);
setLanguage = () => {
const { processing, language } = this.props.dashboard;
const updateAvailable = isCoreRunning && isUpdateAvailable;
useEffect(() => {
dispatch(getDnsStatus());
}, []);
const setLanguage = () => {
if (!processing) {
if (language) {
i18n.changeLanguage(language);
@@ -68,91 +130,52 @@ class App extends Component {
}
i18n.on('languageChanged', (lang) => {
this.props.changeLanguage(lang);
dispatch(changeLanguage(lang));
});
};
render() {
const { dashboard, encryption, getVersion } = this.props;
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
useEffect(() => {
setLanguage();
}, [language]);
return (
<HashRouter hashType="noslash">
<Fragment>
{updateAvailable && (
<Fragment>
<UpdateTopline
url={dashboard.announcementUrl}
version={dashboard.newVersion}
canAutoUpdate={dashboard.canAutoUpdate}
getUpdate={this.handleUpdate}
processingUpdate={dashboard.processingUpdate}
/>
<UpdateOverlay processingUpdate={dashboard.processingUpdate} />
</Fragment>
)}
{!encryption.processing && (
<EncryptionTopline notAfter={encryption.not_after} />
)}
<LoadingBar className="loading-bar" updateTime={1000} />
<Route component={Header} />
<div className="container container--wrap pb-5">
{dashboard.processing && <Loading />}
{!dashboard.isCoreRunning && (
<div className="row row-cards">
<div className="col-lg-12">
<Status reloadPage={this.reloadPage}
message="dns_start"
/>
<Loading />
</div>
const reloadPage = () => {
window.location.reload();
};
return (
<HashRouter hashType="noslash">
<>
{updateAvailable && <>
<UpdateTopline />
<UpdateOverlay />
</>}
{!processingEncryption && <EncryptionTopline />}
<LoadingBar className="loading-bar" updateTime={1000} />
<Header />
<div className="container container--wrap pb-5">
{processing && <Loading />}
{!isCoreRunning && (
<div className="row row-cards">
<div className="col-lg-12">
<Status reloadPage={reloadPage} message="dns_start" />
<Loading />
</div>
)}
{!dashboard.processing && dashboard.isCoreRunning && (
<>
<Route path={MENU_URLS.root} exact component={Dashboard} />
<Route path={MENU_URLS.logs} component={Logs} />
<Route path={MENU_URLS.guide} component={SetupGuide} />
<Route path={SETTINGS_URLS.settings} component={Settings} />
<Route path={SETTINGS_URLS.dns} component={Dns} />
<Route path={SETTINGS_URLS.encryption} component={Encryption} />
<Route path={SETTINGS_URLS.dhcp} component={Dhcp} />
<Route path={SETTINGS_URLS.clients} component={Clients} />
<Route path={FILTERS_URLS.dns_blocklists}
component={DnsBlocklist} />
<Route path={FILTERS_URLS.dns_allowlists}
component={DnsAllowlist} />
<Route path={FILTERS_URLS.dns_rewrites} component={DnsRewrites} />
<Route path={FILTERS_URLS.custom_rules} component={CustomRules} />
<Route path={FILTERS_URLS.blocked_services} component={Services} />
</>
)}
</div>
<Footer
dnsVersion={dashboard.dnsVersion}
dnsPort={dashboard.dnsPort}
processingVersion={dashboard.processingVersion}
getVersion={getVersion}
checkUpdateFlag={dashboard.checkUpdateFlag}
/>
<Toasts />
<Icons />
</Fragment>
</HashRouter>
);
}
}
App.propTypes = {
getDnsStatus: PropTypes.func,
getUpdate: PropTypes.func,
enableDns: PropTypes.func,
dashboard: PropTypes.object,
isCoreRunning: PropTypes.bool,
error: PropTypes.string,
changeLanguage: PropTypes.func,
encryption: PropTypes.object,
getVersion: PropTypes.func,
</div>
)}
{!processing && isCoreRunning && ROUTES.map(renderRoute)}
</div>
<Footer />
<Toasts />
<Icons />
</>
</HashRouter>
);
};
export default withTranslation()(App);
renderRoute.propTypes = {
path: propTypes.oneOfType([propTypes.string, propTypes.arrayOf(propTypes.string)]).isRequired,
component: propTypes.element.isRequired,
exact: propTypes.bool,
};
export default hot(App);

View File

@@ -14,7 +14,11 @@ const CountCell = (totalBlocked) => function cell(row) {
const { value } = row;
const percent = getPercent(totalBlocked, value);
return <Cell value={value} percent={percent} color={STATUS_COLORS.red} />;
return <Cell value={value}
percent={percent}
color={STATUS_COLORS.red}
search={row.original.domain}
/>;
};
const BlockedDomains = ({

View File

@@ -6,7 +6,7 @@ import { Trans, withTranslation } from 'react-i18next';
import Card from '../ui/Card';
import Cell from '../ui/Cell';
import { getPercent, getIpMatchListStatus } from '../../helpers/helpers';
import { getPercent, getIpMatchListStatus, sortIp } from '../../helpers/helpers';
import { IP_MATCH_LIST_STATUS, STATUS_COLORS } from '../../helpers/constants';
import { formatClientCell } from '../../helpers/formatClientCell';
@@ -25,7 +25,7 @@ const countCell = (dnsQueries) => function cell(row) {
const percent = getPercent(dnsQueries, value);
const percentColor = getClientsPercentColor(percent);
return <Cell value={value} percent={percent} color={percentColor} />;
return <Cell value={value} percent={percent} color={percentColor} search={row.original.ip} />;
};
const renderBlockingButton = (ipMatchListStatus, ip, handleClick, processing) => {
@@ -62,7 +62,7 @@ const clientCell = (t, toggleClientStatus, processing, disallowedClients) => fun
return (
<>
<div className="logs__row logs__row--overflow logs__row--column">
{formatClientCell(row, t)}
{formatClientCell(row, true, false)}
</div>
{ipMatchListStatus !== IP_MATCH_LIST_STATUS.CIDR
&& renderBlockingButton(ipMatchListStatus, value, toggleClientStatus, processing)}
@@ -99,7 +99,7 @@ const Clients = ({
{
Header: 'IP',
accessor: 'ip',
sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
sortMethod: sortIp,
Cell: clientCell(t, toggleClientStatus, processingAccessSet, disallowedClients),
},
{

View File

@@ -1,31 +1,85 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import propTypes from 'prop-types';
import { Trans, useTranslation } from 'react-i18next';
import round from 'lodash/round';
import { shallowEqual, useSelector } from 'react-redux';
import Card from '../ui/Card';
import Tooltip from '../ui/Tooltip';
import { formatNumber } from '../../helpers/helpers';
import LogsSearchLink from '../ui/LogsSearchLink';
import { RESPONSE_FILTER } from '../../helpers/constants';
import Tooltip from '../ui/Tooltip';
const tooltipType = 'tooltip-custom--narrow';
const Row = ({
label, count, response_status, tooltipTitle, translationComponents,
}) => {
const content = response_status
? <LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
: count;
const Counters = (props) => {
return <tr key={label}>
<td>
<Trans components={translationComponents}>{label}</Trans>
<Tooltip content={tooltipTitle} placement="top"
className="tooltip-container tooltip-custom--narrow text-center">
<svg className="icons icon--20 icon--lightgray ml-2">
<use xlinkHref="#question" />
</svg>
</Tooltip>
</td>
<td className="text-right"><strong>{content}</strong></td>
</tr>;
};
const Counters = ({ refreshButton, subtitle }) => {
const {
t,
interval,
refreshButton,
subtitle,
dnsQueries,
blockedFiltering,
replacedSafebrowsing,
replacedParental,
replacedSafesearch,
numDnsQueries,
numBlockedFiltering,
numReplacedSafebrowsing,
numReplacedParental,
numReplacedSafesearch,
avgProcessingTime,
} = props;
} = useSelector((state) => state.stats, shallowEqual);
const { t } = useTranslation();
const tooltipTitle = interval === 1
? t('number_of_dns_query_24_hours')
: t('number_of_dns_query_days', { count: interval });
const rows = [
{
label: 'dns_query',
count: numDnsQueries,
tooltipTitle: interval === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: interval }),
response_status: RESPONSE_FILTER.ALL.query,
},
{
label: 'blocked_by',
count: numBlockedFiltering,
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
response_status: RESPONSE_FILTER.BLOCKED.query,
translationComponents: [<a href="#filters" key="0">link</a>],
},
{
label: 'stats_malware_phishing',
count: numReplacedSafebrowsing,
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
response_status: RESPONSE_FILTER.BLOCKED_THREATS.query,
},
{
label: 'stats_adult',
count: numReplacedParental,
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.query,
},
{
label: 'enforced_save_search',
count: numReplacedSafesearch,
tooltipTitle: 'number_of_dns_query_to_safe_search',
response_status: RESPONSE_FILTER.SAFE_SEARCH.query,
},
{
label: 'average_processing_time',
count: avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0,
tooltipTitle: 'average_processing_time_hint',
},
];
return (
<Card
@@ -35,104 +89,23 @@ const Counters = (props) => {
refresh={refreshButton}
>
<table className="table card-table">
<tbody>
<tr>
<td>
<Trans>dns_query</Trans>
<Tooltip text={tooltipTitle} type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
{formatNumber(dnsQueries)}
</span>
</td>
</tr>
<tr>
<td>
<Trans components={[<a href="#filters" key="0">link</a>]}>
blocked_by
</Trans>
<Tooltip
text={t('number_of_dns_query_blocked_24_hours')}
type={tooltipType}
/>
</td>
<td className="text-right">
<span className="text-muted">
{formatNumber(blockedFiltering)}
</span>
</td>
</tr>
<tr>
<td>
<Trans>stats_malware_phishing</Trans>
<Tooltip
text={t('number_of_dns_query_blocked_24_hours_by_sec')}
type={tooltipType}
/>
</td>
<td className="text-right">
<span className="text-muted">
{formatNumber(replacedSafebrowsing)}
</span>
</td>
</tr>
<tr>
<td>
<Trans>stats_adult</Trans>
<Tooltip
text={t('number_of_dns_query_blocked_24_hours_adult')}
type={tooltipType}
/>
</td>
<td className="text-right">
<span className="text-muted">
{formatNumber(replacedParental)}
</span>
</td>
</tr>
<tr>
<td>
<Trans>enforced_save_search</Trans>
<Tooltip
text={t('number_of_dns_query_to_safe_search')}
type={tooltipType}
/>
</td>
<td className="text-right">
<span className="text-muted">
{formatNumber(replacedSafesearch)}
</span>
</td>
</tr>
<tr>
<td>
<Trans>average_processing_time</Trans>
<Tooltip text={t('average_processing_time_hint')} type={tooltipType} />
</td>
<td className="text-right">
<span className="text-muted">
{avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0}
</span>
</td>
</tr>
</tbody>
<tbody>{rows.map(Row)}</tbody>
</table>
</Card>
);
};
Counters.propTypes = {
dnsQueries: PropTypes.number.isRequired,
blockedFiltering: PropTypes.number.isRequired,
replacedSafebrowsing: PropTypes.number.isRequired,
replacedParental: PropTypes.number.isRequired,
replacedSafesearch: PropTypes.number.isRequired,
avgProcessingTime: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
interval: PropTypes.number.isRequired,
t: PropTypes.func.isRequired,
Row.propTypes = {
label: propTypes.string.isRequired,
count: propTypes.string.isRequired,
response_status: propTypes.string,
tooltipTitle: propTypes.string.isRequired,
translationComponents: propTypes.arrayOf(propTypes.element),
};
export default withTranslation()(Counters);
Counters.propTypes = {
refreshButton: propTypes.node.isRequired,
subtitle: propTypes.string.isRequired,
};
export default Counters;

View File

@@ -1,18 +1,67 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getTrackerData } from '../../helpers/trackers/trackers';
import Popover from '../ui/Popover';
import { Trans } from 'react-i18next';
import { getSourceData, getTrackerData } from '../../helpers/trackers/trackers';
import Tooltip from '../ui/Tooltip';
import { captitalizeWords } from '../../helpers/helpers';
const renderLabel = (value) => <strong><Trans>{value}</Trans></strong>;
const renderLink = ({ url, name }) => <a
className="tooltip-custom__content-link"
target="_blank"
rel="noopener noreferrer"
href={url}
>
<strong>{name}</strong>
</a>;
const getTrackerInfo = (trackerData) => [{
key: 'name_table_header',
value: trackerData,
render: renderLink,
},
{
key: 'category_label',
value: captitalizeWords(trackerData.category),
render: renderLabel,
},
{
key: 'source_label',
value: getSourceData(trackerData),
render: renderLink,
}];
const DomainCell = ({ value }) => {
const trackerData = getTrackerData(value);
const content = trackerData && <div className="popover__list">
<div className="tooltip-custom__content-title mb-1">
<Trans>found_in_known_domain_db</Trans>
</div>
{getTrackerInfo(trackerData)
.map(({ key, value, render }) => <div
key={key}
className="tooltip-custom__content-item"
>
<Trans>{key}</Trans>: {render(value)}
</div>)}
</div>;
return (
<div className="logs__row">
<div className="logs__text logs__text--domain" title={value}>
<div className="logs__text" title={value}>
{value}
</div>
{trackerData && <Popover data={trackerData} />}
{trackerData
&& <Tooltip content={content} placement="top"
className="tooltip-container tooltip-custom--wide">
<svg className="icons icon--24 icon--green ml-1">
<use xlinkHref="#privacy" />
</svg>
</Tooltip>}
</div>
);
};
@@ -21,4 +70,9 @@ DomainCell.propTypes = {
value: PropTypes.string.isRequired,
};
renderLink.propTypes = {
url: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
};
export default DomainCell;

View File

@@ -13,7 +13,8 @@ import { getPercent } from '../../helpers/helpers';
const getQueriedPercentColor = (percent) => {
if (percent > 10) {
return STATUS_COLORS.red;
} if (percent > 5) {
}
if (percent > 5) {
return STATUS_COLORS.yellow;
}
return STATUS_COLORS.green;
@@ -24,7 +25,8 @@ const countCell = (dnsQueries) => function cell(row) {
const percent = getPercent(dnsQueries, value);
const percentColor = getQueriedPercentColor(percent);
return <Cell value={value} percent={percent} color={percentColor} />;
return <Cell value={value} percent={percent} color={percentColor}
search={row.original.domain} />;
};
const QueriedDomains = ({

View File

@@ -111,13 +111,6 @@ class Dashboard extends Component {
<div className="col-lg-6">
<Counters
subtitle={subtitle}
interval={stats.interval}
dnsQueries={stats.numDnsQueries}
blockedFiltering={stats.numBlockedFiltering}
replacedSafebrowsing={stats.numReplacedSafebrowsing}
replacedParental={stats.numReplacedParental}
replacedSafesearch={stats.numReplacedSafesearch}
avgProcessingTime={stats.avgProcessingTime}
refreshButton={refreshButton}
/>
</div>

View File

@@ -62,23 +62,13 @@ class Table extends Component {
showPagination
defaultPageSize={10}
minRows={5}
previousText={
<svg className="icons icon--small icon--gray">
<use xlinkHref="#arrow-left" />
</svg>}
nextText={
<svg className="icons icon--small icon--gray">
<use xlinkHref="#arrow-right" />
</svg>}
loadingText={t('loading_table_status')}
pageText=''
ofText=''
ofText="/"
previousText={t('previous_btn')}
nextText={t('next_btn')}
pageText={t('page_table_footer_text')}
rowsText={t('rows_table_footer_text')}
loadingText={t('loading_table_status')}
noDataText={t('rewrite_not_found')}
showPageSizeOptions={false}
showPageJump={false}
renderTotalPagesCount={() => false}
getPaginationProps={() => ({ className: 'custom-pagination' })}
/>
);
}

View File

@@ -59,6 +59,4 @@ const Services = () => {
);
};
Services.propTypes = {};
export default Services;

View File

@@ -128,24 +128,15 @@ class Table extends Component {
columns={this.columns}
showPagination
defaultPageSize={10}
showPageSizeOptions={false}
showPageJump={false}
renderTotalPagesCount={() => false}
loading={loading}
minRows={6}
pageText=''
ofText=''
ofText="/"
previousText={t('previous_btn')}
nextText={t('next_btn')}
pageText={t('page_table_footer_text')}
rowsText={t('rows_table_footer_text')}
loadingText={t('loading_table_status')}
noDataText={whitelist ? t('no_whitelist_added') : t('no_blocklist_added')}
getPaginationProps={() => ({ className: 'custom-pagination' })}
previousText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-left" />
</svg>}
nextText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-right" />
</svg>}
/>
);
}

View File

@@ -1,6 +1,6 @@
.nav-tabs .nav-link.active {
border-color: #66b574;
color: #66b574;
border-color: var(--green-74);
color: var(--green-74);
background: transparent;
}
@@ -18,7 +18,7 @@
.nav-tabs .nav-link.active .nav-icon,
.nav-tabs .nav-item.show .nav-icon {
stroke: #66b574;
stroke: var(--green-74);
}
.nav-tabs .nav-link.active:hover .nav-icon,
@@ -74,9 +74,9 @@
}
.nav-tabs .nav-item.show .nav-link {
color: #66b574;
color: var(--green-74);
background-color: #fff;
border-bottom-color: #66b574;
border-bottom-color: var(--green-74);
}
.header__right {

View File

@@ -1,16 +1,19 @@
import React, { Component, Fragment } from 'react';
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import enhanceWithClickOutside from 'react-click-outside';
import classnames from 'classnames';
import { Trans, withTranslation } from 'react-i18next';
import { SETTINGS_URLS, FILTERS_URLS, MENU_URLS } from '../../helpers/constants';
import Dropdown from '../ui/Dropdown';
const MENU_ITEMS = [
{
route: MENU_URLS.root, exact: true, icon: 'dashboard', text: 'dashboard', order: 0,
route: MENU_URLS.root,
exact: true,
icon: 'dashboard',
text: 'dashboard',
order: 0,
},
// Settings dropdown should have visual order 1
@@ -18,27 +21,63 @@ const MENU_ITEMS = [
// Filters dropdown should have visual order 2
{
route: MENU_URLS.logs, icon: 'log', text: 'query_log', order: 3,
route: MENU_URLS.logs,
icon: 'log',
text: 'query_log',
order: 3,
},
{
route: MENU_URLS.guide, icon: 'setup', text: 'setup_guide', order: 4,
route: MENU_URLS.guide,
icon: 'setup',
text: 'setup_guide',
order: 4,
},
];
const SETTINGS_ITEMS = [
{ route: SETTINGS_URLS.settings, text: 'general_settings' },
{ route: SETTINGS_URLS.dns, text: 'dns_settings' },
{ route: SETTINGS_URLS.encryption, text: 'encryption_settings' },
{ route: SETTINGS_URLS.clients, text: 'client_settings' },
{ route: SETTINGS_URLS.dhcp, text: 'dhcp_settings' },
{
route: SETTINGS_URLS.settings,
text: 'general_settings',
},
{
route: SETTINGS_URLS.dns,
text: 'dns_settings',
},
{
route: SETTINGS_URLS.encryption,
text: 'encryption_settings',
},
{
route: SETTINGS_URLS.clients,
text: 'client_settings',
},
{
route: SETTINGS_URLS.dhcp,
text: 'dhcp_settings',
},
];
const FILTERS_ITEMS = [
{ route: FILTERS_URLS.dns_blocklists, text: 'dns_blocklists' },
{ route: FILTERS_URLS.dns_allowlists, text: 'dns_allowlists' },
{ route: FILTERS_URLS.dns_rewrites, text: 'dns_rewrites' },
{ route: FILTERS_URLS.blocked_services, text: 'blocked_services' },
{ route: FILTERS_URLS.custom_rules, text: 'custom_filtering_rules' },
{
route: FILTERS_URLS.dns_blocklists,
text: 'dns_blocklists',
},
{
route: FILTERS_URLS.dns_allowlists,
text: 'dns_allowlists',
},
{
route: FILTERS_URLS.dns_rewrites,
text: 'dns_rewrites',
},
{
route: FILTERS_URLS.blocked_services,
text: 'blocked_services',
},
{
route: FILTERS_URLS.custom_rules,
text: 'custom_filtering_rules',
},
];
class Menu extends Component {
@@ -51,8 +90,8 @@ class Menu extends Component {
};
getActiveClassForDropdown = (URLS) => {
const { pathname } = this.props.location;
const isActivePage = Object.values(URLS).some((item) => item === pathname);
const isActivePage = Object.values(URLS)
.some((item) => item === this.props.pathname);
return isActivePage ? 'active' : '';
};
@@ -79,18 +118,18 @@ class Menu extends Component {
getDropdown = ({
label, order, URLS, icon, ITEMS,
}) => (
<Dropdown
label={this.props.t(label)}
baseClassName={`dropdown nav-item order-${order}`}
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
icon={icon}>
{ITEMS.map((item) => (
this.getNavLink({
...item,
order,
className: 'dropdown-item',
})))}
</Dropdown>
<Dropdown
label={this.props.t(label)}
baseClassName='dropdown'
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
icon={icon}>
{ITEMS.map((item) => (
this.getNavLink({
...item,
order,
className: 'dropdown-item',
})))}
</Dropdown>
);
render() {
@@ -99,7 +138,7 @@ class Menu extends Component {
'mobile-menu--active': this.props.isMenuOpen,
});
return (
<Fragment>
<>
<div className={menuClass}>
<ul className="nav nav-tabs border-0 flex-column flex-lg-row flex-nowrap">
{MENU_ITEMS.map((item) => (
@@ -108,34 +147,41 @@ class Menu extends Component {
key={item.text}
onClick={this.closeMenu}
>
{this.getNavLink({ ...item, className: 'nav-link' })}
{this.getNavLink({
...item,
className: 'nav-link',
})}
</li>
))}
{this.getDropdown({
order: 1,
label: 'settings',
icon: 'settings',
URLS: SETTINGS_URLS,
ITEMS: SETTINGS_ITEMS,
})}
{this.getDropdown({
order: 2,
label: 'filters',
icon: 'filters',
URLS: FILTERS_URLS,
ITEMS: FILTERS_ITEMS,
})}
<li className="nav-item order-1">
{this.getDropdown({
order: 1,
label: 'settings',
icon: 'settings',
URLS: SETTINGS_URLS,
ITEMS: SETTINGS_ITEMS,
})}
</li>
<li className="nav-item order-2">
{this.getDropdown({
order: 2,
label: 'filters',
icon: 'filters',
URLS: FILTERS_URLS,
ITEMS: FILTERS_ITEMS,
})}
</li>
</ul>
</div>
</Fragment>
</>
);
}
}
Menu.propTypes = {
isMenuOpen: PropTypes.bool,
closeMenu: PropTypes.func,
location: PropTypes.object,
isMenuOpen: PropTypes.bool.isRequired,
closeMenu: PropTypes.func.isRequired,
pathname: PropTypes.string.isRequired,
t: PropTypes.func,
};

View File

@@ -1,83 +1,77 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { shallowEqual, useSelector } from 'react-redux';
import { Trans } from 'react-i18next';
import classnames from 'classnames';
import { Trans, withTranslation } from 'react-i18next';
import Menu from './Menu';
import logo from '../ui/svg/logo.svg';
import './Header.css';
class Header extends Component {
state = {
isMenuOpen: false,
const Header = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const {
protectionEnabled,
processing,
isCoreRunning,
processingProfile,
name,
} = useSelector((state) => state.dashboard, shallowEqual);
const { pathname } = useLocation();
const toggleMenuOpen = () => {
setIsMenuOpen((isMenuOpen) => !isMenuOpen);
};
toggleMenuOpen = () => {
this.setState((prevState) => ({ isMenuOpen: !prevState.isMenuOpen }));
const closeMenu = () => {
setIsMenuOpen(false);
};
closeMenu = () => {
this.setState({ isMenuOpen: false });
};
const badgeClass = classnames('badge dns-status', {
'badge-success': protectionEnabled,
'badge-danger': !protectionEnabled,
});
render() {
const { dashboard, location } = this.props;
const { isMenuOpen } = this.state;
const badgeClass = classnames({
'badge dns-status': true,
'badge-success': dashboard.protectionEnabled,
'badge-danger': !dashboard.protectionEnabled,
});
return (
<div className="header">
<div className="header__container">
<div className="header__row">
<div
className="header-toggler d-lg-none ml-lg-0 collapsed"
onClick={this.toggleMenuOpen}
>
<span className="header-toggler-icon" />
</div>
<div className="header__column">
<div className="d-flex align-items-center">
<Link to="/" className="nav-link pl-0 pr-1">
<img src={logo} alt="" className="header-brand-img" />
</Link>
{!dashboard.processing && dashboard.isCoreRunning && (
<span className={badgeClass}>
<Trans>{dashboard.protectionEnabled ? 'on' : 'off'}</Trans>
return (
<div className="header">
<div className="header__container">
<div className="header__row">
<div
className="header-toggler d-lg-none ml-lg-0 collapsed"
onClick={toggleMenuOpen}
>
<span className="header-toggler-icon" />
</div>
<div className="header__column">
<div className="d-flex align-items-center">
<Link to="/" className="nav-link pl-0 pr-1">
<img src={logo} alt="" className="header-brand-img" />
</Link>
{!processing && isCoreRunning && (
<span className={badgeClass}>
<Trans>{protectionEnabled ? 'on' : 'off'}</Trans>
</span>
)}
</div>
)}
</div>
<Menu
location={location}
isMenuOpen={isMenuOpen}
closeMenu={this.closeMenu}
/>
<div className="header__column">
<div className="header__right">
{!dashboard.processingProfile && dashboard.name
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
<Trans>sign_out</Trans>
</a>
}
</div>
</div>
<Menu
pathname={pathname}
isMenuOpen={isMenuOpen}
closeMenu={closeMenu}
/>
<div className="header__column">
<div className="header__right">
{!processingProfile && name
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
<Trans>sign_out</Trans>
</a>}
</div>
</div>
</div>
</div>
);
}
}
Header.propTypes = {
dashboard: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
getVersion: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
</div>
);
};
export default withTranslation()(Header);
export default Header;

View File

@@ -1,4 +1,4 @@
.tooltip__container {
.tooltip-custom__container {
padding: 1rem 1.5rem 1.25rem 1.5rem;
font-size: 16px !important;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);

View File

@@ -3,7 +3,7 @@ import { nanoid } from 'nanoid';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { formatClientCell } from '../../../helpers/formatClientCell';
import getHintElement from './getHintElement';
import getIconTooltip from './getIconTooltip';
import { checkFiltered } from '../../../helpers/helpers';
import { BLOCK_ACTIONS } from '../../../helpers/constants';
@@ -16,6 +16,7 @@ const getClientCell = ({
const autoClient = autoClients.find((autoClient) => autoClient.name === client);
const source = autoClient?.source;
const whoisAvailable = whois_info && Object.keys(whois_info).length > 0;
const id = nanoid();
@@ -33,11 +34,11 @@ const getClientCell = ({
const isFiltered = checkFiltered(reason);
const nameClass = classNames('w-90 o-hidden d-flex flex-column', {
'mt-2': isDetailed && !name,
'mt-2': isDetailed && !name && !whoisAvailable,
'white-space--nowrap': isDetailed,
});
const hintClass = classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', {
const hintClass = classNames('icons mr-4 icon--24 icon--lightgray', {
'my-3': isDetailed,
});
@@ -68,7 +69,7 @@ const getClientCell = ({
return (
<div className="logs__row o-hidden h-100">
{processedData && getHintElement({
{getIconTooltip({
className: hintClass,
columnClass: 'grid grid--limited',
tooltipClass: 'px-5 pb-5 pt-4 mw-75',
@@ -78,12 +79,19 @@ const getClientCell = ({
content: processedData,
placement: 'bottom',
})}
<div
className={nameClass}>
<div data-tip={true} data-for={id}>{formatClientCell(row, t, isDetailed)}</div>
{isDetailed && name
&& <div className="detailed-info d-none d-sm-block logs__text"
title={name}>{name}</div>}
<div className={nameClass}>
<div data-tip={true} data-for={id}>
{formatClientCell(row, isDetailed)}
</div>
{isDetailed && name && !whoisAvailable && (
<div
className="detailed-info d-none d-sm-block logs__text"
title={name}
>
{name}
</div>
)}
</div>
{renderBlockingButton(isFiltered, domain)}
</div>

View File

@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import getHintElement from './getHintElement';
import getIconTooltip from './getIconTooltip';
import {
DEFAULT_SHORT_DATE_FORMAT_OPTIONS,
LONG_TIME_FORMAT,
@@ -21,28 +21,18 @@ const getDomainCell = (props) => {
const hasTracker = !!tracker;
const lockIconClass = classNames('icons', 'icon--small', 'd-none', 'd-sm-block', 'cursor--pointer', {
'icon--active': answer_dnssec,
const lockIconClass = classNames('icons icon--24 d-none d-sm-block', {
'icon--green': answer_dnssec,
'icon--disabled': !answer_dnssec,
'my-3': isDetailed,
});
const privacyIconClass = classNames('icons', 'mx-2', 'icon--small', 'd-none', 'd-sm-block', 'cursor--pointer', {
'icon--active': hasTracker,
const privacyIconClass = classNames('icons mx-2 icon--24 d-none d-sm-block', {
'icon--green': hasTracker,
'icon--disabled': !hasTracker,
'my-3': isDetailed,
});
const dnssecHint = getHintElement({
className: lockIconClass,
tooltipClass: 'py-4 px-5 pb-45',
canShowTooltip: answer_dnssec,
xlinkHref: 'lock',
columnClass: 'w-100',
content: 'validated_with_dnssec',
placement: 'bottom',
});
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
const ip = type ? `${t('type_table_header')}: ${type}` : '';
@@ -66,7 +56,7 @@ const getDomainCell = (props) => {
const renderGrid = (content, idx) => {
const preparedContent = typeof content === 'string' ? t(content) : content;
const className = classNames('text-truncate key-colon o-hidden', {
const className = classNames('text-truncate o-hidden', {
'overflow-break': preparedContent.length > 100,
});
return <div key={idx} className={className}>{preparedContent}</div>;
@@ -82,7 +72,7 @@ const getDomainCell = (props) => {
const renderContent = hasTracker ? requestDetails.concat(getGrid(knownTrackerDataObj, 'known_tracker', 'pt-4')) : requestDetails;
const trackerHint = getHintElement({
const trackerHint = getIconTooltip({
className: privacyIconClass,
tooltipClass: 'pt-4 pb-5 px-5 mw-75',
xlinkHref: 'privacy',
@@ -100,7 +90,15 @@ const getDomainCell = (props) => {
return (
<div className="logs__row o-hidden">
{dnssec_enabled && dnssecHint}
{dnssec_enabled && getIconTooltip({
className: lockIconClass,
tooltipClass: 'py-4 px-5 pb-45',
canShowTooltip: answer_dnssec,
xlinkHref: 'lock',
columnClass: 'w-100',
content: 'validated_with_dnssec',
placement: 'bottom',
})}
{trackerHint}
<div className={valueClass}>
<div className="text-truncate" title={domain}>{domain}</div>

View File

@@ -1,65 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import TooltipTrigger from 'react-popper-tooltip';
import { Trans } from 'react-i18next';
import classNames from 'classnames';
import './Tooltip.css';
import 'react-popper-tooltip/dist/styles.css';
import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants';
const getHintElement = ({
className,
contentItemClass,
columnClass,
canShowTooltip = true,
xlinkHref,
title,
placement,
tooltipClass,
content,
renderContent = React.Children.map(
content,
(item, idx) => <div key={idx} className={contentItemClass}>
<Trans>{item || '—'}</Trans>
</div>,
),
}) => <TooltipTrigger placement={placement} trigger="hover" delayHide={HIDE_TOOLTIP_DELAY} tooltip={
({
tooltipRef,
getTooltipProps,
}) => <div {...getTooltipProps({
ref: tooltipRef,
className: classNames('tooltip__container', tooltipClass, { 'd-none': !canShowTooltip }),
})}
>
{title && <div className="pb-4 h-25 grid-content font-weight-bold">
<Trans>{title}</Trans>
</div>}
<div className={classNames(columnClass)}>{renderContent}</div>
</div>
}>{({
getTriggerProps, triggerRef,
}) => <span {...getTriggerProps({ ref: triggerRef })}>
{xlinkHref && <svg className={className}>
<use xlinkHref={`#${xlinkHref}`} />
</svg>}
</span>}
</TooltipTrigger>;
getHintElement.propTypes = {
className: PropTypes.string,
contentItemClass: PropTypes.string,
columnClass: PropTypes.string,
tooltipClass: PropTypes.string,
title: PropTypes.string,
placement: PropTypes.string,
canShowTooltip: PropTypes.string,
xlinkHref: PropTypes.string,
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
]),
renderContent: PropTypes.arrayOf(PropTypes.element),
};
export default getHintElement;

View File

@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
import classNames from 'classnames';
import { processContent } from '../../../helpers/helpers';
import Tooltip from '../../ui/Tooltip';
import 'react-popper-tooltip/dist/styles.css';
import './IconTooltip.css';
const getIconTooltip = ({
className,
contentItemClass,
columnClass,
canShowTooltip = true,
xlinkHref,
title,
placement,
tooltipClass,
content,
renderContent = content ? React.Children.map(
processContent(content),
(item, idx) => <div key={idx} className={contentItemClass}>
<Trans>{item || '—'}</Trans>
</div>,
) : null,
}) => {
const tooltipContent = <>
{title
&& <div className="pb-4 h-25 grid-content font-weight-bold"><Trans>{title}</Trans></div>}
<div className={classNames(columnClass)}>{renderContent}</div>
</>;
const tooltipClassName = classNames('tooltip-custom__container', tooltipClass, { 'd-none': !canShowTooltip });
return <Tooltip
className={tooltipClassName}
content={tooltipContent}
placement={placement}
>
{xlinkHref && <svg className={className}>
<use xlinkHref={`#${xlinkHref}`} />
</svg>}
</Tooltip>;
};
getIconTooltip.propTypes = {
className: PropTypes.string,
contentItemClass: PropTypes.string,
columnClass: PropTypes.string,
tooltipClass: PropTypes.string,
title: PropTypes.string,
placement: PropTypes.string,
canShowTooltip: PropTypes.string,
xlinkHref: PropTypes.string,
content: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
]),
renderContent: PropTypes.arrayOf(PropTypes.element),
};
export default getIconTooltip;

View File

@@ -5,22 +5,27 @@ import {
FILTERED_STATUS,
FILTERED_STATUS_TO_META_MAP,
} from '../../../helpers/constants';
import getHintElement from './getHintElement';
import getIconTooltip from './getIconTooltip';
const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
const {
reason, filterId, rule, status, upstream, elapsedMs, domain, response,
reason, filterId, rule, status, upstream, elapsedMs, response, originalResponse,
} = row.original;
const { filters, whitelistFilters } = filtering;
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
const statusLabel = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
const filter = getFilterName(filters, whitelistFilters, filterId, t);
const renderResponses = (responseArr) => {
if (responseArr.length === 0) {
if (responseArr?.length === 0) {
return '';
}
@@ -33,81 +38,39 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
})}</div>;
};
const FILTERED_STATUS_TO_FIELDS_MAP = {
[FILTERED_STATUS.NOT_FILTERED_NOT_FOUND]: {
domain,
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
response_table_header: renderResponses(response),
},
[FILTERED_STATUS.FILTERED_BLOCKED_SERVICE]: {
domain,
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
filter,
rule_label: rule,
response_code: status,
},
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
domain,
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
filter,
rule_label: rule,
response_code: status,
},
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
domain,
encryption_status: boldStatusLabel,
filter,
rule_label: rule,
response_code: status,
},
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
domain,
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
},
[FILTERED_STATUS.FILTERED_BLACK_LIST]: {
domain,
encryption_status: boldStatusLabel,
filter,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
},
const COMMON_CONTENT = {
encryption_status: boldStatusLabel,
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
response_code: status,
filter,
rule_label: rule,
response_table_header: renderResponses(response),
original_response: renderResponses(originalResponse),
};
const fields = FILTERED_STATUS_TO_FIELDS_MAP[reason]
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE
|| reason === FILTERED_STATUS.FILTERED_BLACK_LIST
? filter : formattedElapsedMs;
const content = rule
? Object.entries(COMMON_CONTENT)
: Object.entries({ ...COMMON_CONTENT, filter: '' });
const detailedInfo = isBlocked ? filter : formattedElapsedMs;
return (
<div className="logs__row">
{fields && getHintElement({
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
{getIconTooltip({
className: classNames('icons mr-4 icon--24 icon--lightgray', { 'my-3': isDetailed }),
columnClass: 'grid grid--limited',
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
contentItemClass: 'text-truncate key-colon o-hidden',
xlinkHref: 'question',
title: 'response_details',
content: fields,
content,
placement: 'bottom',
})}
<div className="text-truncate">
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
{isDetailed && <div
className="detailed-info d-none d-sm-block pt-1 text-truncate" title={detailedInfo}>{detailedInfo}</div>}
className="detailed-info d-none d-sm-block pt-1 text-truncate"
title={detailedInfo}>{detailedInfo}</div>}
</div>
</div>
);

View File

@@ -2,17 +2,20 @@ import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { useTranslation } from 'react-i18next';
import debounce from 'lodash/debounce';
import { useDispatch } from 'react-redux';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import classNames from 'classnames';
import {
DEBOUNCE_FILTER_TIMEOUT,
DEFAULT_LOGS_FILTER,
FORM_NAME,
RESPONSE_FILTER,
RESPONSE_FILTER_QUERIES,
} from '../../../helpers/constants';
import Tooltip from '../../ui/Tooltip';
import { setLogsFilter } from '../../../actions/queryLogs';
import useDebounce from '../../../helpers/useDebounce';
import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers';
import Tooltip from '../../ui/Tooltip';
const renderFilterField = ({
input,
@@ -25,34 +28,47 @@ const renderFilterField = ({
tooltip,
meta: { touched, error },
onClearInputClick,
}) => <>
<div className="input-group-search input-group-search__icon--magnifier">
<svg className="icons icon--small icon--gray">
<use xlinkHref="#magnifier" />
</svg>
</div>
<input
{...input}
id={id}
placeholder={placeholder}
type={type}
className={className}
disabled={disabled}
autoComplete={autoComplete}
aria-label={placeholder} />
<div
className={classNames('input-group-search input-group-search__icon--cross', { invisible: input.value.length < 1 })}>
<svg className="icons icon--smallest icon--gray" onClick={onClearInputClick}>
<use xlinkHref="#cross" />
</svg>
</div>
<span className="input-group-search input-group-search__icon--tooltip">
<Tooltip text={tooltip} type='tooltip-custom--logs' />
onKeyDown,
normalizeOnBlur,
}) => {
const onBlur = (event) => 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>)}
</>;
{!disabled
&& touched
&& (error && <span className="form__message form__message--error">{error}</span>)}
</>;
};
renderFilterField.propTypes = {
input: PropTypes.object.isRequired,
@@ -64,71 +80,108 @@ renderFilterField.propTypes = {
disabled: PropTypes.string,
autoComplete: PropTypes.string,
tooltip: PropTypes.string,
onKeyDown: PropTypes.func,
normalizeOnBlur: PropTypes.func,
meta: PropTypes.shape({
touched: PropTypes.bool,
error: PropTypes.object,
}).isRequired,
};
const FORM_NAMES = {
search: 'search',
response_status: 'response_status',
};
const Form = (props) => {
const {
className = '',
responseStatusClass,
submit,
reset,
setIsLoading,
change,
} = props;
const { t } = useTranslation();
const dispatch = useDispatch();
const history = useHistory();
const debouncedSubmit = debounce(submit, DEBOUNCE_FILTER_TIMEOUT);
const zeroDelaySubmit = () => setTimeout(submit, 0);
const {
response_status, search,
} = useSelector((state) => state.form[FORM_NAME.LOGS_FILTER].values, shallowEqual);
const clearInput = async () => {
await dispatch(setLogsFilter(DEFAULT_LOGS_FILTER));
await reset();
};
const [
debouncedSearch,
setDebouncedSearch,
] = useDebounce(search.trim(), DEBOUNCE_FILTER_TIMEOUT);
useEffect(() => {
dispatch(setLogsFilter({
response_status,
search: debouncedSearch,
}));
history.replace(`${getLogsUrlParams(debouncedSearch, response_status)}`);
}, [response_status, debouncedSearch]);
if (response_status && !(response_status in RESPONSE_FILTER_QUERIES)) {
change(FORM_NAMES.response_status, DEFAULT_LOGS_FILTER[FORM_NAMES.response_status]);
}
const onInputClear = async () => {
setIsLoading(true);
await clearInput();
setDebouncedSearch(DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
setIsLoading(false);
};
useEffect(() => clearInput, []);
const onEnterPress = (e) => {
if (e.key === 'Enter') {
setDebouncedSearch(search);
}
};
const normalizeOnBlur = (data) => data.trim();
return (
<form className="d-flex flex-wrap form-control--container"
onSubmit={(e) => {
e.preventDefault();
zeroDelaySubmit();
debouncedSubmit.cancel();
}}
<form
className="d-flex flex-wrap form-control--container"
onSubmit={(e) => {
e.preventDefault();
}}
>
<Field
id="search"
name="search"
component={renderFilterField}
type="text"
className={classNames('form-control--search form-control--transparent', className)}
placeholder={t('domain_or_client')}
tooltip={t('query_log_strict_search')}
onChange={debouncedSubmit}
onClearInputClick={onInputClear}
/>
<div className="field__search">
<Field
id={FORM_NAMES.search}
name={FORM_NAMES.search}
component={renderFilterField}
type="text"
className={classNames('form-control--search form-control--transparent', className)}
placeholder={t('domain_or_client')}
tooltip={t('query_log_strict_search')}
onClearInputClick={onInputClear}
onKeyDown={onEnterPress}
normalizeOnBlur={normalizeOnBlur}
/>
</div>
<div className="field__select">
<Field
name="response_status"
name={FORM_NAMES.response_status}
component="select"
className={classNames('form-control custom-select custom-select--logs custom-select__arrow--left ml-small form-control--transparent', responseStatusClass)}
onChange={zeroDelaySubmit}
className={classNames('form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent', responseStatusClass)}
>
{Object.values(RESPONSE_FILTER)
.map(({
query, label, disabled,
}) => <option key={label} value={query}
disabled={disabled}>{t(label)}</option>)}
}) => (
<option
key={label}
value={query}
disabled={disabled}
>
{t(label)}
</option>
))
}
</Field>
</div>
</form>
@@ -136,14 +189,13 @@ const Form = (props) => {
};
Form.propTypes = {
handleChange: PropTypes.func,
className: PropTypes.string,
responseStatusClass: PropTypes.string,
submit: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
change: PropTypes.func.isRequired,
setIsLoading: PropTypes.func.isRequired,
};
export default reduxForm({
form: FORM_NAME.LOGS_FILTER,
enableReinitialize: true,
})(Form);

View File

@@ -1,43 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
import { useDispatch } from 'react-redux';
import Form from './Form';
import { setLogsFilter } from '../../../actions/queryLogs';
const Filters = ({ filter, refreshLogs, setIsLoading }) => {
const dispatch = useDispatch();
const onSubmit = async (values) => {
setIsLoading(true);
await dispatch(setLogsFilter(values));
setIsLoading(false);
};
return (
const Filters = ({ filter, refreshLogs, setIsLoading }) => (
<div className="page-header page-header--logs">
<h1 className="page-title page-title--large">
<Trans>query_log</Trans>
<button
type="button"
className="btn btn-icon--green ml-3 bg-transparent"
className="btn btn-icon--green logs__refresh"
onClick={refreshLogs}
>
<svg className="icons icon--small">
<svg className="icons icon--24">
<use xlinkHref="#update" />
</svg>
</button>
</h1>
<Form
responseStatusClass="d-sm-block"
initialValues={filter}
onSubmit={onSubmit}
setIsLoading={setIsLoading}
/>
/>
</div>
);
};
);
Filters.propTypes = {
filter: PropTypes.object.isRequired,

View File

@@ -13,7 +13,8 @@
}
.card-table .logs__row {
overflow: visible;
overflow: hidden;
text-overflow: ellipsis;
}
.logs__row--center {
@@ -57,12 +58,7 @@
width: 100%;
}
.logs__text--domain {
max-width: 285px;
}
.logs__text--wrap,
.logs__text--whois {
.logs__text--wrap {
line-height: 1.4;
white-space: normal;
}
@@ -74,6 +70,7 @@
.logs__text--whois {
line-height: 1.2;
color: #9aa0ac;
}
.logs__row .tooltip-custom {
@@ -202,6 +199,7 @@
.logs__whois {
display: inline;
font-size: 12px;
white-space: nowrap;
}
.logs__whois::after {
@@ -364,7 +362,7 @@
display: flex !important;
}
.-pageInfo {
.logs__table .-pageInfo {
--side-size: 2rem;
font-variant-numeric: tabular-nums !important;
background-color: transparent !important;
@@ -378,18 +376,18 @@
align-items: center;
}
.pagination-bottom {
.logs__table .pagination-bottom {
justify-content: center !important;
display: flex !important;
}
.-center:before {
.logs__table .-center:before {
content: '...';
transform: translateY(-0.25rem);
margin: auto;
}
.-center:after {
.logs__table .-center:after {
content: '...';
transform: translateY(-0.25rem);
margin: auto;
@@ -401,10 +399,6 @@
top: 0.5rem;
}
.icon--light-gray {
color: var(--gray-8);
}
.link--green {
color: var(--green79);
}
@@ -443,27 +437,24 @@
}
.custom-select__arrow--left {
background: #fff url('./chevron-down.svg') no-repeat left 0.2rem center;
background-size: 1.5rem;
background: var(--white) url('../ui/svg/chevron-down.svg') no-repeat;
background-position: 5px 9px;
background-size: 22px;
}
.custom-select--logs {
padding: 0.5rem 0.75rem 0.5rem 1.75rem !important;
padding: 0.5rem 0.75rem 0.5rem 2rem !important;
}
.bg--danger {
color: var(--danger);
}
.ml-small {
margin-left: 3.3125rem;
}
.form-control--search {
width: 39.125rem;
box-shadow: 0 1px 0 #ddd;
padding: 0 2.5rem;
height: 2.25rem;
flex-grow: 1;
}
.form-control--transparent {
@@ -493,31 +484,12 @@
}
.form-control--container {
max-width: 100%;
flex: auto;
}
@media (max-width: 1279.98px) {
.form-control--search {
max-width: 30.125rem;
}
.form-control--container {
max-width: 70%;
}
.form-control--search {
max-width: 50%;
}
}
@media (max-width: 991.98px) {
.form-control--search {
max-width: 40%;
}
.form-control--container {
max-width: 100%;
}
.field__search {
display: flex;
flex-grow: 1;
}
@media (max-width: 767.98px) {
@@ -528,6 +500,21 @@
.ml-small {
margin-left: 1.5rem;
}
.form-control--container {
width: 100%;
flex-direction: column;
}
.form-control--search {
width: 100%;
}
.field__select {
margin-top: 1.5rem;
padding-left: 24px;
padding-right: 24px;
}
}
@media (max-width: 575px) {
@@ -544,16 +531,6 @@
}
}
@media (max-width: 500px) {
.form-control--search {
max-width: 85%;
}
.field__select {
margin-top: 1.5rem;
}
}
.loading__container > .-loading-inner {
top: 10rem !important;
bottom: initial !important;
@@ -562,3 +539,16 @@
.loading__text {
transform: translateY(3rem);
}
.logs__refresh {
position: relative;
top: 3px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
padding: 0;
margin-left: 15px;
background-color: transparent;
}

View File

@@ -12,7 +12,7 @@ import {
FILTERED_STATUS_TO_META_MAP,
TABLE_DEFAULT_PAGE_SIZE,
SCHEME_TO_PROTOCOL_MAP,
CUSTOM_FILTERING_RULES_ID,
CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS,
} from '../../helpers/constants';
import getDateCell from './Cells/getDateCell';
import getDomainCell from './Cells/getDomainCell';
@@ -25,7 +25,7 @@ import {
formatDateTime,
formatElapsedMs,
formatTime,
processContent,
} from '../../helpers/helpers';
import Loading from '../ui/Loading';
import { getSourceData } from '../../helpers/trackers/trackers';
@@ -49,7 +49,7 @@ const Table = (props) => {
isLoading,
} = props;
const [t] = useTranslation();
const { t } = useTranslation();
const toggleBlocking = (type, domain) => {
const {
@@ -154,27 +154,23 @@ const Table = (props) => {
headerClassName: 'logs__text',
},
{
Header: () => {
const plainSelected = classNames('cursor--pointer', {
'icon--selected': !isDetailed,
});
const detailedSelected = classNames('cursor--pointer', {
'icon--selected': isDetailed,
});
Header: function Header() {
return <div className="d-flex justify-content-between">
{t('client_table_header')}
{<span>
<svg
className={`icons icon--small icon--active mr-2 cursor--pointer ${plainSelected}`}
className={classNames('icons icon--24 icon--green mr-2 cursor--pointer', {
'icon--selected': !isDetailed,
})}
onClick={() => toggleDetailedLogs(false)}
>
<title><Trans>compact</Trans></title>
<use xlinkHref='#list' />
</svg>
<svg
className={`icons icon--small icon--active cursor--pointer ${detailedSelected}`}
className={classNames('icons icon--24 icon--green cursor--pointer', {
'icon--selected': isDetailed,
})}
onClick={() => toggleDetailedLogs(true)}
>
<title><Trans>default</Trans></title>
@@ -239,7 +235,7 @@ const Table = (props) => {
sortable={false}
resizable={false}
data={logs || []}
loading={isLoading}
loading={isLoading || processingGetLogs}
showPageJump={false}
showPageSizeOptions={false}
onPageChange={changePage}
@@ -261,12 +257,12 @@ const Table = (props) => {
getPaginationProps={() => ({ className: 'custom-pagination custom-pagination--padding' })}
getTbodyProps={() => ({ className: 'd-block' })}
previousText={
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
<title><Trans>previous_btn</Trans></title>
<use xlinkHref="#arrow-left" />
</svg>}
nextText={
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
<title><Trans>next_btn</Trans></title>
<use xlinkHref="#arrow-right" />
</svg>}
@@ -300,6 +296,9 @@ const Table = (props) => {
type,
client_proto,
filterId,
rule,
originalResponse,
status,
} = rowInfo.original;
const hasTracker = !!tracker;
@@ -317,22 +316,29 @@ const Table = (props) => {
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
const isFiltered = checkFiltered(reason);
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
const onToggleBlock = () => {
toggleBlocking(buttonType, domain);
};
const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const statusBlocked = <div className="bg--danger">{status}</div>;
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
const requestStatus = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
const sourceData = getSourceData(tracker);
const { filters, whitelistFilters } = filtering;
const filter = getFilterName(filters, whitelistFilters, filterId, t);
const detailedData = {
time_table_header: formatTime(time, LONG_TIME_FORMAT),
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
encryption_status: status,
encryption_status: isBlocked
? <div className="bg--danger">{requestStatus}</div> : requestStatus,
domain,
type_table_header: type,
protocol,
@@ -340,12 +346,19 @@ const Table = (props) => {
table_name: tracker?.name,
category_label: hasTracker && captitalizeWords(tracker.category),
tracker_source: hasTracker && sourceData
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
className="link--green">{sourceData.name}</a>,
&& <a
href={sourceData.url}
target="_blank"
rel="noopener noreferrer"
className="link--green">{sourceData.name}
</a>,
response_details: 'title',
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
filter: rule ? filter : null,
rule_label: rule,
response_table_header: response?.join('\n'),
response_code: status,
client_details: 'title',
ip_address: client,
name: info?.name,
@@ -354,39 +367,14 @@ const Table = (props) => {
network,
source_label: source,
validated_with_dnssec: dnssec_enabled ? Boolean(answer_dnssec) : false,
original_response: originalResponse?.join('\n'),
[buttonType]: <div onClick={onToggleBlock}
className="title--border bg--danger text-center">{t(buttonType)}</div>,
className={classNames('title--border text-center', {
'bg--danger': isBlocked,
})}>{t(buttonType)}</div>,
};
const { filters, whitelistFilters } = filtering;
const filter = getFilterName(filters, whitelistFilters, filterId, t);
const detailedDataBlocked = {
time_table_header: formatTime(time, LONG_TIME_FORMAT),
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
encryption_status: statusBlocked,
domain,
type_table_header: type,
protocol,
known_tracker: 'title',
table_name: tracker?.name,
category_label: hasTracker && captitalizeWords(tracker.category),
source_label: hasTracker && sourceData
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
className="link--green">{sourceData.name}</a>,
response_details: 'title',
install_settings_dns: upstream,
elapsed: formattedElapsedMs,
filter,
response_table_header: response?.join('\n'),
[buttonType]: <div onClick={onToggleBlock}
className="title--border text-center">{t(buttonType)}</div>,
};
const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData;
setDetailedDataCurrent(detailedDataCurrent);
setDetailedDataCurrent(processContent(detailedData));
setButtonType(buttonType);
setModalOpened(true);
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>

Before

Width:  |  Height:  |  Size: 264 B

View File

@@ -2,11 +2,15 @@ import React, { Fragment, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Trans } from 'react-i18next';
import Modal from 'react-modal';
import { useDispatch } from 'react-redux';
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, smallScreenSize,
BLOCK_ACTIONS,
TABLE_DEFAULT_PAGE_SIZE,
TABLE_FIRST_PAGE,
SMALL_SCREEN_SIZE,
} from '../../helpers/constants';
import Loading from '../ui/Loading';
import Filters from './Filters';
@@ -15,15 +19,21 @@ import Disabled from './Disabled';
import { getFilteringStatus } from '../../actions/filtering';
import { getClients } from '../../actions';
import { getDnsConfig } from '../../actions/dnsConfig';
import { getLogsConfig } from '../../actions/queryLogs';
import {
getLogsConfig,
refreshFilteredLogs,
resetFilteredLogs,
setFilteredLogs,
} from '../../actions/queryLogs';
import { addSuccessToast } from '../../actions/toasts';
import './Logs.css';
const INITIAL_REQUEST = true;
const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST];
export const processContent = (data, buttonType) => Object.entries(data)
const processContent = (data, buttonType) => Object.entries(data)
.map(([key, value]) => {
if (!value) {
return null;
}
const isTitle = value === 'title';
const isButton = key === buttonType;
const isBoolean = typeof value === 'boolean';
@@ -40,7 +50,9 @@ export const processContent = (data, buttonType) => Object.entries(data)
return isHidden ? null : <Fragment key={key}>
<div
className={`key__${key} ${keyClass} ${(isBoolean && value === true) ? 'font-weight-bold' : ''}`}>
className={classNames(`key__${key}`, keyClass, {
'font-weight-bold': isBoolean && value === true,
})}>
<Trans>{isButton ? value : key}</Trans>
</div>
<div className={`value__${key} text-pre text-truncate`}>
@@ -52,22 +64,44 @@ export const processContent = (data, buttonType) => Object.entries(data)
const Logs = (props) => {
const dispatch = useDispatch();
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < smallScreenSize);
const history = useHistory();
const {
response_status: response_status_url_param = '',
search: search_url_param = '',
} = queryString.parse(history.location.search);
const { filter } = useSelector((state) => state.queryLogs, shallowEqual);
const search = filter?.search || search_url_param;
const response_status = filter?.response_status || response_status_url_param;
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < SMALL_SCREEN_SIZE);
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
const [isModalOpened, setModalOpened] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
(async () => {
setIsLoading(true);
await dispatch(setFilteredLogs({
search,
response_status,
}));
setIsLoading(false);
})();
}, [response_status, search]);
const {
filtering,
setLogsPage,
setLogsPagination,
setLogsFilter,
toggleDetailedLogs,
dashboard,
dnsConfig,
queryLogs: {
filter,
enabled,
processingGetConfig,
processingAdditionalLogs,
@@ -80,7 +114,7 @@ const Logs = (props) => {
},
} = props;
const mediaQuery = window.matchMedia(`(max-width: ${smallScreenSize}px)`);
const mediaQuery = window.matchMedia(`(max-width: ${SMALL_SCREEN_SIZE}px)`);
const mediaQueryHandler = (e) => {
setIsSmallScreen(e.matches);
if (e.matches) {
@@ -88,16 +122,10 @@ const Logs = (props) => {
}
};
useEffect(() => {
mediaQuery.addListener(mediaQueryHandler);
return () => mediaQuery.removeListener(mediaQueryHandler);
}, []);
const closeModal = () => setModalOpened(false);
const getLogs = (older_than, page, initial) => {
if (props.queryLogs.enabled) {
if (enabled) {
props.getLogs({
older_than,
page,
@@ -108,6 +136,17 @@ const Logs = (props) => {
};
useEffect(() => {
try {
mediaQuery.addEventListener('change', mediaQueryHandler);
} catch (e1) {
try {
// Safari 13.1 do not support mediaQuery.addEventListener('change', handler)
mediaQuery.addListener(mediaQueryHandler);
} catch (e2) {
console.error(e2);
}
}
(async () => {
setIsLoading(true);
dispatch(setLogsPage(TABLE_FIRST_PAGE));
@@ -115,7 +154,6 @@ const Logs = (props) => {
dispatch(getClients());
try {
await Promise.all([
getLogs(...INITIAL_REQUEST_DATA),
dispatch(getLogsConfig()),
dispatch(getDnsConfig()),
]);
@@ -125,13 +163,27 @@ const Logs = (props) => {
setIsLoading(false);
}
})();
return () => {
try {
mediaQuery.removeEventListener('change', mediaQueryHandler);
} catch (e1) {
try {
mediaQuery.removeListener(mediaQueryHandler);
} catch (e2) {
console.error(e2);
}
}
dispatch(resetFilteredLogs());
};
}, []);
const refreshLogs = async () => {
setIsLoading(true);
await Promise.all([
dispatch(setLogsPage(TABLE_FIRST_PAGE)),
getLogs(...INITIAL_REQUEST_DATA),
dispatch(refreshFilteredLogs()),
]);
dispatch(addSuccessToast('query_log_updated'));
setIsLoading(false);
@@ -141,13 +193,15 @@ const Logs = (props) => {
<>
{enabled && processingGetConfig && <Loading />}
{enabled && !processingGetConfig && (
<Fragment>
<>
<Filters
filter={filter}
filter={{
response_status,
search,
}}
setIsLoading={setIsLoading}
processingGetLogs={processingGetLogs}
processingAdditionalLogs={processingAdditionalLogs}
setLogsFilter={setLogsFilter}
refreshLogs={refreshLogs}
/>
<Table
@@ -191,13 +245,13 @@ const Logs = (props) => {
}}
>
<svg
className="icon icon--small icon-cross d-block d-md-none cursor--pointer"
className="icon icon--24 icon-cross d-block d-md-none cursor--pointer"
onClick={closeModal}>
<use xlinkHref="#cross" />
</svg>
{processContent(detailedDataCurrent, buttonType)}
</Modal>
</Fragment>
</>
)}
{!enabled && !processingGetConfig && (
<Disabled />
@@ -215,7 +269,6 @@ Logs.propTypes = {
setRules: PropTypes.func.isRequired,
addSuccessToast: PropTypes.func.isRequired,
setLogsPagination: PropTypes.func.isRequired,
setLogsFilter: PropTypes.func.isRequired,
setLogsPage: PropTypes.func.isRequired,
toggleDetailedLogs: PropTypes.func.isRequired,
dnsConfig: PropTypes.object.isRequired,

View File

@@ -7,6 +7,8 @@ import Card from '../../ui/Card';
import CellWrap from '../../ui/CellWrap';
import whoisCell from './whoisCell';
import LogsSearchLink from '../../ui/LogsSearchLink';
import { sortIp } from '../../../helpers/helpers';
const COLUMN_MIN_WIDTH = 200;
@@ -17,6 +19,7 @@ class AutoClients extends Component {
accessor: 'ip',
minWidth: COLUMN_MIN_WIDTH,
Cell: CellWrap,
sortMethod: sortIp,
},
{
Header: this.props.t('table_name'),
@@ -49,7 +52,9 @@ class AutoClients extends Component {
return (
<div className="logs__row">
<div className="logs__text" title={clientStats}>
{clientStats}
<LogsSearchLink search={row.original.ip}>
{clientStats}
</LogsSearchLink>
</div>
</div>
);
@@ -82,23 +87,13 @@ class AutoClients extends Component {
showPagination
defaultPageSize={10}
minRows={5}
showPageSizeOptions={false}
showPageJump={false}
renderTotalPagesCount={() => false}
previousText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-left" />
</svg>}
nextText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-right" />
</svg>}
loadingText={t('loading_table_status')}
pageText=''
ofText=''
ofText="/"
previousText={t('previous_btn')}
nextText={t('next_btn')}
pageText={t('page_table_footer_text')}
rowsText={t('rows_table_footer_text')}
loadingText={t('loading_table_status')}
noDataText={t('clients_not_found')}
getPaginationProps={() => ({ className: 'custom-pagination' })}
/>
</Card>
);

View File

@@ -4,10 +4,11 @@ import { Trans, withTranslation } from 'react-i18next';
import ReactTable from 'react-table';
import { MODAL_TYPE } from '../../../helpers/constants';
import { normalizeTextarea } from '../../../helpers/helpers';
import { splitByNewLine } from '../../../helpers/helpers';
import Card from '../../ui/Card';
import Modal from './Modal';
import CellWrap from '../../ui/CellWrap';
import LogsSearchLink from '../../ui/LogsSearchLink';
class ClientsTable extends Component {
handleFormAdd = (values) => {
@@ -29,7 +30,7 @@ class ClientsTable extends Component {
}
if (values.upstreams && typeof values.upstreams === 'string') {
config.upstreams = normalizeTextarea(values.upstreams);
config.upstreams = splitByNewLine(values.upstreams);
} else {
config.upstreams = [];
}
@@ -49,7 +50,10 @@ class ClientsTable extends Component {
};
getOptionsWithLabels = (options) => (
options.map((option) => ({ value: option, label: option }))
options.map((option) => ({
value: option,
label: option,
}))
);
getClient = (name, clients) => {
@@ -203,7 +207,15 @@ class ClientsTable extends Component {
accessor: (row) => this.props.normalizedTopClients.configured[row.name] || 0,
sortMethod: (a, b) => b - a,
minWidth: 120,
Cell: CellWrap,
Cell: (row) => {
const content = CellWrap(row);
if (!row.value) {
return content;
}
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
},
},
{
Header: this.props.t('actions_table_header'),
@@ -285,23 +297,13 @@ class ClientsTable extends Component {
showPagination
defaultPageSize={10}
minRows={5}
showPageSizeOptions={false}
showPageJump={false}
renderTotalPagesCount={() => false}
previousText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-left" />
</svg>}
nextText={
<svg className="icons icon--small icon--gray w-100 h-100">
<use xlinkHref="#arrow-right" />
</svg>}
loadingText={t('loading_table_status')}
pageText=''
ofText=''
ofText="/"
previousText={t('previous_btn')}
nextText={t('next_btn')}
pageText={t('page_table_footer_text')}
rowsText={t('rows_table_footer_text')}
loadingText={t('loading_table_status')}
noDataText={t('clients_not_found')}
getPaginationProps={() => ({ className: 'custom-pagination' })}
/>
<button
type="button"
@@ -311,7 +313,6 @@ class ClientsTable extends Component {
>
<Trans>client_add</Trans>
</button>
<Modal
isModalOpen={isModalOpen}
modalType={modalType}

View File

@@ -88,7 +88,7 @@ const renderFieldsWrapper = (placeholder, buttonTitle) => function cell(row) {
onClick={() => fields.push()}
title={buttonTitle}
>
<svg className="icon icon--small">
<svg className="icon icon--24">
<use xlinkHref="#plus" />
</svg>
</button>

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import { Trans, withTranslation } from 'react-i18next';
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../helpers/constants';
import { sortIp } from '../../../helpers/helpers';
class Leases extends Component {
cellWrap = ({ value }) => (
@@ -27,6 +28,7 @@ class Leases extends Component {
Header: 'IP',
accessor: 'ip',
Cell: this.cellWrap,
sortMethod: sortIp,
}, {
Header: <Trans>dhcp_table_hostname</Trans>,
accessor: 'hostname',

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import ReactTable from 'react-table';
import { Trans, withTranslation } from 'react-i18next';
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../../helpers/constants';
import { sortIp } from '../../../../helpers/helpers';
import Modal from './Modal';
class StaticLeases extends Component {
@@ -49,6 +49,7 @@ class StaticLeases extends Component {
{
Header: 'IP',
accessor: 'ip',
sortMethod: sortIp,
Cell: this.cellWrap,
},
{

View File

@@ -1,4 +1,4 @@
import React, { Component, Fragment } from 'react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Trans, withTranslation } from 'react-i18next';
@@ -34,13 +34,14 @@ class Dhcp extends Component {
} = this.props.dhcp;
const otherDhcpFound = check?.otherServer
&& check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
const filledConfig = Object.keys(config).every((key) => {
if (key === 'enabled' || key === 'icmp_timeout_msec') {
return true;
}
const filledConfig = Object.keys(config)
.every((key) => {
if (key === 'enabled' || key === 'icmp_timeout_msec') {
return true;
}
return config[key];
});
return config[key];
});
if (config.enabled) {
return (
@@ -114,40 +115,35 @@ class Dhcp extends Component {
getStaticIpWarning = (t, check, interfaceName) => {
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
return (
<Fragment>
<div className="text-danger mb-2">
<Trans>dhcp_static_ip_error</Trans>
<div className="mt-2 mb-2">
<Accordion label={t('error_details')}>
<span>{check.staticIP.error}</span>
</Accordion>
</div>
return <>
<div className="text-danger mb-2">
<Trans>dhcp_static_ip_error</Trans>
<div className="mt-2 mb-2">
<Accordion label={t('error_details')}>
<span>{check.staticIP.error}</span>
</Accordion>
</div>
<hr className="mt-4 mb-4" />
</Fragment>
);
} if (
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
</div>
<hr className="mt-4 mb-4" />
</>;
}
if (check.staticIP.static === DHCP_STATUS_RESPONSE.NO
&& check.staticIP.ip
&& interfaceName
) {
return (
<Fragment>
<div className="text-secondary mb-2">
<Trans
components={[<strong key="0">example</strong>]}
values={{
interfaceName,
ipAddress: check.staticIP.ip,
}}
>
dhcp_dynamic_ip_found
</Trans>
</div>
<hr className="mt-4 mb-4" />
</Fragment>
);
&& interfaceName) {
return <>
<div className="text-secondary mb-2">
<Trans
components={[<strong key="0">example</strong>]}
values={{
interfaceName,
ipAddress: check.staticIP.ip,
}}
>
dhcp_dynamic_ip_found
</Trans>
</div>
<hr className="mt-4 mb-4" />
</>;
}
return '';
@@ -163,104 +159,101 @@ class Dhcp extends Component {
removeStaticLease,
toggleLeaseModal,
} = this.props;
const statusButtonClass = classnames({
'btn btn-primary btn-standard': true,
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
});
const { enabled, interface_name, ...values } = dhcp.config;
return (
<Fragment>
<PageTitle title={t('dhcp_settings')} />
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
{!dhcp.processing && !dhcp.processingInterfaces && (
<Fragment>
<Card
title={t('dhcp_title')}
subtitle={t('dhcp_description')}
bodyType="card-body box-body--settings"
>
<div className="dhcp">
<Fragment>
<Form
onSubmit={this.handleFormSubmit}
initialValues={{
interface_name,
...values,
}}
interfaces={dhcp.interfaces}
processingConfig={dhcp.processingConfig}
processingInterfaces={dhcp.processingInterfaces}
enabled={enabled}
resetDhcp={resetDhcp}
/>
<hr />
<div className="card-actions mb-3">
{this.getToggleDhcpButton()}
<button
type="button"
className={statusButtonClass}
onClick={() => findActiveDhcp(interface_name)}
disabled={
enabled || !interface_name || dhcp.processingConfig
}
>
<Trans>check_dhcp_servers</Trans>
</button>
</div>
{!enabled && dhcp.check && (
<Fragment>
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
{this.getActiveDhcpMessage(t, dhcp.check)}
{this.getDhcpWarning(dhcp.check)}
</Fragment>
)}
</Fragment>
return <>
<PageTitle title={t('dhcp_settings')} />
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
{!dhcp.processing && !dhcp.processingInterfaces && <>
<Card
title={t('dhcp_title')}
subtitle={t('dhcp_description')}
bodyType="card-body box-body--settings"
>
<div className="dhcp">
<>
<Form
onSubmit={this.handleFormSubmit}
initialValues={{
interface_name,
...values,
}}
interfaces={dhcp.interfaces}
processingConfig={dhcp.processingConfig}
processingInterfaces={dhcp.processingInterfaces}
enabled={enabled}
resetDhcp={resetDhcp}
/>
<hr />
<div className="card-actions mb-3">
{this.getToggleDhcpButton()}
<button
type="button"
className={statusButtonClass}
onClick={() => findActiveDhcp(interface_name)}
disabled={
enabled || !interface_name || dhcp.processingConfig
}
>
<Trans>check_dhcp_servers</Trans>
</button>
</div>
</Card>
{dhcp.config.enabled && (
<Card
title={t('dhcp_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
</div>
</Card>
)}
<Card
title={t('dhcp_static_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={addStaticLease}
removeStaticLease={removeStaticLease}
toggleLeaseModal={toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
{!enabled && dhcp.check && (
<>
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
{this.getActiveDhcpMessage(t, dhcp.check)}
{this.getDhcpWarning(dhcp.check)}
</>
)}
</>
</div>
</Card>
{dhcp.config.enabled && (
<Card
title={t('dhcp_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Leases leases={dhcp.leases} />
</div>
</Card>
</Fragment>
</div>
</Card>
)}
</Fragment>
);
<Card
title={t('dhcp_static_leases')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col-12">
<StaticLeases
staticLeases={dhcp.staticLeases}
isModalOpen={dhcp.isModalOpen}
addStaticLease={addStaticLease}
removeStaticLease={removeStaticLease}
toggleLeaseModal={toggleLeaseModal}
processingAdding={dhcp.processingAdding}
processingDeleting={dhcp.processingDeleting}
/>
</div>
<div className="col-12">
<button
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleLeaseModal()}
>
<Trans>dhcp_add_static_lease</Trans>
</button>
</div>
</div>
</Card>
</>}
</>;
}
}

View File

@@ -4,7 +4,10 @@ import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { renderTextareaField } from '../../../../helpers/form';
import { normalizeMultiline } from '../../../../helpers/helpers';
import {
trimMultilineString,
removeEmptyLines,
} from '../../../../helpers/helpers';
import { FORM_NAME } from '../../../../helpers/constants';
const fields = [
@@ -12,16 +15,19 @@ const fields = [
id: 'allowed_clients',
title: 'access_allowed_title',
subtitle: 'access_allowed_desc',
normalizeOnBlur: removeEmptyLines,
},
{
id: 'disallowed_clients',
title: 'access_disallowed_title',
subtitle: 'access_disallowed_desc',
normalizeOnBlur: trimMultilineString,
},
{
id: 'blocked_hosts',
title: 'access_blocked_title',
subtitle: 'access_blocked_desc',
normalizeOnBlur: removeEmptyLines,
},
];
@@ -31,7 +37,7 @@ const Form = (props) => {
} = props;
const renderField = ({
id, title, subtitle, disabled = processingSet,
id, title, subtitle, disabled = processingSet, normalizeOnBlur,
}) => <div key={id} className="form__group mb-5">
<label className="form__label form__label--with-desc" htmlFor={id}>
<Trans>{title}</Trans>
@@ -46,7 +52,7 @@ const Form = (props) => {
type="text"
className="form-control form-control--textarea font-monospace"
disabled={disabled}
normalizeOnBlur={id === 'disallowed_clients' ? normalizeMultiline : undefined}
normalizeOnBlur={normalizeOnBlur}
/>
</div>;
@@ -55,6 +61,7 @@ const Form = (props) => {
title: PropTypes.string,
subtitle: PropTypes.string,
disabled: PropTypes.bool,
normalizeOnBlur: PropTypes.func,
};
return (

View File

@@ -1,40 +1,36 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import Form from './Form';
import Card from '../../../ui/Card';
import { setAccessList } from '../../../../actions/access';
class Access extends Component {
handleFormSubmit = (values) => {
this.props.setAccessList(values);
const Access = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const {
processing,
processingSet,
...values
} = useSelector((state) => state.access, shallowEqual);
const handleFormSubmit = (values) => {
dispatch(setAccessList(values));
};
render() {
const { t, access } = this.props;
const { processing, processingSet, ...values } = access;
return (
<Card
title={t('access_title')}
subtitle={t('access_desc')}
bodyType="card-body box-body--settings"
>
<Form
initialValues={values}
onSubmit={this.handleFormSubmit}
processingSet={processingSet}
/>
</Card>
);
}
}
Access.propTypes = {
access: PropTypes.object.isRequired,
setAccessList: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
return (
<Card
title={t('access_title')}
subtitle={t('access_desc')}
bodyType="card-body box-body--settings"
>
<Form
initialValues={values}
onSubmit={handleFormSubmit}
processingSet={processingSet}
/>
</Card>
);
};
export default withTranslation()(Access);
export default Access;

View File

@@ -53,7 +53,7 @@ const Form = ({
{INPUTS_FIELDS.map(({
name, title, description, placeholder, validate, max,
}) => <div className="col-12" key={name}>
<div className="col-7 p-0">
<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)}</label>

View File

@@ -1,9 +1,8 @@
import React, { Fragment } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { shallowEqual, useSelector } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { Trans, useTranslation } from 'react-i18next';
import {
renderInputField,
renderRadioField,
@@ -18,32 +17,36 @@ import {
} from '../../../../helpers/validators';
import { BLOCKING_MODES, FORM_NAME } from '../../../../helpers/constants';
const checkboxes = [{
name: 'edns_cs_enabled',
placeholder: 'edns_enable',
subtitle: 'edns_cs_desc',
},
{
name: 'dnssec_enabled',
placeholder: 'dnssec_enable',
subtitle: 'dnssec_enable_desc',
},
{
name: 'disable_ipv6',
placeholder: 'disable_ipv6',
subtitle: 'disable_ipv6_desc',
}];
const checkboxes = [
{
name: 'edns_cs_enabled',
placeholder: 'edns_enable',
subtitle: 'edns_cs_desc',
},
{
name: 'dnssec_enabled',
placeholder: 'dnssec_enable',
subtitle: 'dnssec_enable_desc',
},
{
name: 'disable_ipv6',
placeholder: 'disable_ipv6',
subtitle: 'disable_ipv6_desc',
},
];
const customIps = [{
description: 'blocking_ipv4_desc',
name: 'blocking_ipv4',
validateIp: validateIpv4,
},
{
description: 'blocking_ipv6_desc',
name: 'blocking_ipv6',
validateIp: validateIpv6,
}];
const customIps = [
{
description: 'blocking_ipv4_desc',
name: 'blocking_ipv4',
validateIp: validateIpv4,
},
{
description: 'blocking_ipv6_desc',
name: 'blocking_ipv6',
validateIp: validateIpv6,
},
];
const getFields = (processing, t) => Object.values(BLOCKING_MODES)
.map((mode) => (
@@ -58,114 +61,107 @@ const getFields = (processing, t) => Object.values(BLOCKING_MODES)
/>
));
let Form = ({
handleSubmit, submitting, invalid, processing, blockingMode, t,
}) => <form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12 col-sm-6">
<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
name="ratelimit"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit')}
normalize={toNumber}
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
/>
</div>
</div>
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
<div className="form__group form__group--settings">
<Field
name={name}
type="checkbox"
component={renderSelectField}
placeholder={t(placeholder)}
disabled={processing}
subtitle={t(subtitle)}
/>
</div>
</div>)}
<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>
<div className="form__desc form__desc--top">
{Object.values(BLOCKING_MODES)
.map((mode) => (
<li key={mode}>
<Trans>{`blocking_mode_${mode}`}</Trans>
</li>
))}
</div>
<div className="custom-controls-stacked">
{getFields(processing, t)}
</div>
</div>
</div>
{blockingMode === BLOCKING_MODES.custom_ip && (
<Fragment>
{customIps.map(({
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
name={name}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[validateIp, validateRequiredValue]}
/>
const Form = ({
handleSubmit, submitting, invalid, processing,
}) => {
const { t } = useTranslation();
const {
blocking_mode,
} = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual);
return <form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12 col-sm-6">
<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>
</div>)}
</Fragment>
)}
</div>
<button
type="submit"
className="btn btn-success btn-standard btn-large"
disabled={submitting || invalid || processing}
>
<Trans>save_btn</Trans>
</button>
</form>;
<Field
name="ratelimit"
type="number"
component={renderInputField}
className="form-control"
placeholder={t('form_enter_rate_limit')}
normalize={toNumber}
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
/>
</div>
</div>
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
<div className="form__group form__group--settings">
<Field
name={name}
type="checkbox"
component={renderSelectField}
placeholder={t(placeholder)}
disabled={processing}
subtitle={t(subtitle)}
/>
</div>
</div>)}
<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>
<div className="form__desc form__desc--top">
{Object.values(BLOCKING_MODES)
.map((mode) => (
<li key={mode}>
<Trans>{`blocking_mode_${mode}`}</Trans>
</li>
))}
</div>
<div className="custom-controls-stacked">
{getFields(processing, t)}
</div>
</div>
</div>
{blocking_mode === BLOCKING_MODES.custom_ip && (
<>
{customIps.map(({
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
name={name}
component={renderInputField}
className="form-control"
placeholder={t('form_enter_ip')}
validate={[validateIp, validateRequiredValue]}
/>
</div>
</div>)}
</>
)}
</div>
<button
type="submit"
className="btn btn-success btn-standard btn-large"
disabled={submitting || invalid || processing}
>
<Trans>save_btn</Trans>
</button>
</form>;
};
Form.propTypes = {
blockingMode: PropTypes.string.isRequired,
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
processing: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
};
const selector = formValueSelector(FORM_NAME.BLOCKING_MODE);
Form = connect((state) => {
const blockingMode = selector(state, 'blocking_mode');
return {
blockingMode,
};
})(Form);
export default flow([
withTranslation(),
reduxForm({ form: FORM_NAME.BLOCKING_MODE }),
])(Form);
export default reduxForm({ form: FORM_NAME.BLOCKING_MODE })(Form);

View File

@@ -1,15 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import { useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import Card from '../../../ui/Card';
import Form from './Form';
import { setDnsConfig } from '../../../../actions/dnsConfig';
const Config = ({ t, dnsConfig, setDnsConfig }) => {
const handleFormSubmit = (values) => {
setDnsConfig(values);
};
const Config = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const {
blocking_mode,
ratelimit,
@@ -19,7 +17,11 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
dnssec_enabled,
disable_ipv6,
processingSetConfig,
} = dnsConfig;
} = useSelector((state) => state.dnsConfig, shallowEqual);
const handleFormSubmit = (values) => {
dispatch(setDnsConfig(values));
};
return (
<Card
@@ -46,10 +48,4 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
);
};
Config.propTypes = {
dnsConfig: PropTypes.object.isRequired,
setDnsConfig: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(Config);
export default Config;

View File

@@ -6,9 +6,10 @@ import { Trans, useTranslation } from 'react-i18next';
import classnames from 'classnames';
import Examples from './Examples';
import { renderRadioField } from '../../../../helpers/form';
import { renderRadioField, renderTextareaField } from '../../../../helpers/form';
import { DNS_REQUEST_OPTIONS, FORM_NAME } from '../../../../helpers/constants';
import { testUpstream } from '../../../../actions';
import { removeEmptyLines } from '../../../../helpers/helpers';
const getInputFields = () => [{
// eslint-disable-next-line react/display-name
@@ -17,9 +18,10 @@ const getInputFields = () => [{
</label>,
name: 'upstream_dns',
type: 'text',
component: 'textarea',
component: renderTextareaField,
className: 'form-control form-control--textarea font-monospace',
placeholder: 'upstream_dns',
normalizeOnBlur: removeEmptyLines,
},
{
name: 'upstream_mode',
@@ -50,10 +52,11 @@ const Form = ({
submitting, invalid, processingSetConfig, processingTestUpstream, handleSubmit,
}) => {
const dispatch = useDispatch();
const [t] = useTranslation();
const { t } = useTranslation();
const upstream_dns = useSelector((store) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
const bootstrap_dns = useSelector((store) => store.form[FORM_NAME.UPSTREAM]
.values.bootstrap_dns);
const bootstrap_dns = useSelector(
(store) => store.form[FORM_NAME.UPSTREAM].values.bootstrap_dns,
);
const handleUpstreamTest = () => dispatch(testUpstream({
upstream_dns,
@@ -69,7 +72,8 @@ const Form = ({
return <form onSubmit={handleSubmit}>
<div className="row">
{INPUT_FIELDS.map(({
name, component, type, className, placeholder, getTitle, subtitle, disabled, value,
name, component, type, className, placeholder,
getTitle, subtitle, disabled, value, normalizeOnBlur,
}) => <div className="col-12 mb-4" key={placeholder}>
{typeof getTitle === 'function' && getTitle()}
<Field
@@ -82,6 +86,7 @@ const Form = ({
placeholder={t(placeholder)}
subtitle={t(subtitle)}
disabled={processingSetConfig || processingTestUpstream || disabled}
normalizeOnBlur={normalizeOnBlur}
/>
</div>)}
<div className="col-12">
@@ -101,11 +106,12 @@ const Form = ({
<Field
id="bootstrap_dns"
name="bootstrap_dns"
component="textarea"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('bootstrap_dns')}
disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
/>
</div>
</div>

View File

@@ -1,56 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import Form from './Form';
import Card from '../../../ui/Card';
import { setDnsConfig } from '../../../../actions/dnsConfig';
const Upstream = (props) => {
const [t] = useTranslation();
const Upstream = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const {
upstream_dns,
bootstrap_dns,
upstream_mode,
processingSetConfig,
} = useSelector((state) => state.dnsConfig, shallowEqual);
const { processingTestUpstream } = useSelector((state) => state.settings, shallowEqual);
const handleSubmit = (values) => {
dispatch(setDnsConfig(values));
};
const {
processingTestUpstream,
dnsConfig: {
upstream_dns,
bootstrap_dns,
processingSetConfig,
upstream_mode,
},
} = props;
return (
<Card
title={t('upstream_dns')}
subtitle={t('upstream_dns_hint')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Form
initialValues={{
upstream_dns,
bootstrap_dns,
upstream_mode,
}}
onSubmit={handleSubmit}
processingTestUpstream={processingTestUpstream}
processingSetConfig={processingSetConfig}
/>
</div>
return <Card
title={t('upstream_dns')}
subtitle={t('upstream_dns_hint')}
bodyType="card-body box-body--settings"
>
<div className="row">
<div className="col">
<Form
initialValues={{
upstream_dns,
bootstrap_dns,
upstream_mode,
}}
onSubmit={handleSubmit}
processingTestUpstream={processingTestUpstream}
processingSetConfig={processingSetConfig}
/>
</div>
</Card>
);
};
Upstream.propTypes = {
processingTestUpstream: PropTypes.bool.isRequired,
dnsConfig: PropTypes.object.isRequired,
</div>
</Card>;
};
export default Upstream;

View File

@@ -1,64 +1,40 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import Upstream from './Upstream';
import Access from './Access';
import Config from './Config';
import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading';
import CacheConfig from './Cache';
import { getDnsConfig } from '../../../actions/dnsConfig';
import { getAccessList } from '../../../actions/access';
const Dns = (props) => {
const Dns = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const processing = useSelector((state) => state.access.processing);
const processingGetConfig = useSelector((state) => state.dnsConfig.processingGetConfig);
const isDataLoading = processing || processingGetConfig;
useEffect(() => {
props.getAccessList();
props.getDnsConfig();
dispatch(getAccessList());
dispatch(getDnsConfig());
}, []);
const {
settings,
access,
setAccessList,
dnsConfig,
setDnsConfig,
} = props;
const isDataLoading = access.processing || dnsConfig.processingGetConfig;
return (
<>
<PageTitle title={t('dns_settings')} />
{isDataLoading
? <Loading />
: <>
<Upstream
processingTestUpstream={settings.processingTestUpstream}
dnsConfig={dnsConfig}
/>
<Config
dnsConfig={dnsConfig}
setDnsConfig={setDnsConfig}
/>
<CacheConfig
dnsConfig={dnsConfig}
setDnsConfig={setDnsConfig}
/>
<Access access={access} setAccessList={setAccessList} />
</>}
</>
);
};
Dns.propTypes = {
settings: PropTypes.object.isRequired,
getAccessList: PropTypes.func.isRequired,
setAccessList: PropTypes.func.isRequired,
access: PropTypes.object.isRequired,
dnsConfig: PropTypes.object.isRequired,
setDnsConfig: PropTypes.func.isRequired,
getDnsConfig: PropTypes.func.isRequired,
return <>
<PageTitle title={t('dns_settings')} />
{isDataLoading
? <Loading />
: <>
<Upstream />
<Config />
<CacheConfig />
<Access />
</>}
</>;
};
export default Dns;

View File

@@ -10,53 +10,50 @@ import Checkbox from '../ui/Checkbox';
import Loading from '../ui/Loading';
import PageTitle from '../ui/PageTitle';
import Card from '../ui/Card';
import { getObjectKeysSorted } from '../../helpers/helpers';
import './Settings.css';
class Settings extends Component {
settings = {
safebrowsing: {
enabled: false,
title: 'use_adguard_browsing_sec',
subtitle: 'use_adguard_browsing_sec_hint',
},
parental: {
enabled: false,
title: 'use_adguard_parental',
subtitle: 'use_adguard_parental_hint',
},
safesearch: {
enabled: false,
title: 'enforce_safe_search',
subtitle: 'enforce_save_search_hint',
},
};
const ORDER_KEY = 'order';
const SETTINGS = {
safebrowsing: {
enabled: false,
title: 'use_adguard_browsing_sec',
subtitle: 'use_adguard_browsing_sec_hint',
[ORDER_KEY]: 0,
},
parental: {
enabled: false,
title: 'use_adguard_parental',
subtitle: 'use_adguard_parental_hint',
[ORDER_KEY]: 1,
},
safesearch: {
enabled: false,
title: 'enforce_safe_search',
subtitle: 'enforce_save_search_hint',
[ORDER_KEY]: 2,
},
};
class Settings extends Component {
componentDidMount() {
this.props.initSettings(this.settings);
this.props.initSettings(SETTINGS);
this.props.getStatsConfig();
this.props.getLogsConfig();
this.props.getFilteringStatus();
}
renderSettings = (settings) => {
const settingsKeys = Object.keys(settings);
if (settingsKeys.length > 0) {
return settingsKeys.map((key) => {
const setting = settings[key];
const { enabled } = setting;
return (
<Checkbox
{...settings[key]}
key={key}
handleChange={() => this.props.toggleSetting(key, enabled)}
/>
);
});
}
return '';
};
renderSettings = (settings) => getObjectKeysSorted(settings, ORDER_KEY)
.map((key) => {
const setting = settings[key];
const { enabled } = setting;
return <Checkbox
{...setting}
key={key}
handleChange={() => this.props.toggleSetting(key, enabled)}
/>;
});
render() {
const {

View File

@@ -13,3 +13,17 @@
margin-bottom: 20px;
font-size: 15px;
}
.guide__address {
display: block;
margin-bottom: 7px;
font-size: 13px;
font-weight: 700;
}
@media screen and (min-width: 768px) {
.guide__address {
display: list-item;
font-size: 15px;
}
}

View File

@@ -24,8 +24,8 @@ const SetupGuide = ({
<div className="mt-1">
<Trans>install_devices_address</Trans>:
</div>
<div className="mt-2 font-weight-bold">
{dnsAddresses.map((ip) => <li key={ip}>{ip}</li>)}
<div className="mt-3">
{dnsAddresses.map((ip) => <li key={ip} className="guide__address">{ip}</li>)}
</div>
</div>
<Guide dnsAddresses={dnsAddresses} />

View File

@@ -1,30 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import LogsSearchLink from './LogsSearchLink';
import { formatNumber } from '../../helpers/helpers';
const Cell = ({ value, percent, color }) => (
<div className="stats__row">
<div className="stats__row-value mb-1">
<strong>{formatNumber(value)}</strong>
<small className="ml-3 text-muted">{percent}%</small>
</div>
<div className="progress progress-xs">
<div
className="progress-bar"
style={{
width: `${percent}%`,
backgroundColor: color,
}}
/>
</div>
const Cell = ({
value, percent, color, search,
}) => <div className="stats__row">
<div className="stats__row-value mb-1">
<strong><LogsSearchLink search={search}>{formatNumber(value)}</LogsSearchLink></strong>
<small className="ml-3 text-muted">{percent}%</small>
</div>
);
<div className="progress progress-xs">
<div
className="progress-bar"
style={{
width: `${percent}%`,
backgroundColor: color,
}}
/>
</div>
</div>;
Cell.propTypes = {
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired,
color: PropTypes.string.isRequired,
search: PropTypes.string,
onSearchRedirect: PropTypes.func,
};
export default Cell;

View File

@@ -1,6 +1,6 @@
.dropdown-item.active,
.dropdown-item:active {
background-color: #66b574;
background-color: var(--green-74);
}
.dropdown-menu {

View File

@@ -1,19 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
import isAfter from 'date-fns/is_after';
import addDays from 'date-fns/add_days';
import { useSelector } from 'react-redux';
import Topline from './Topline';
import { EMPTY_DATE } from '../../helpers/constants';
const EncryptionTopline = (props) => {
if (props.notAfter === EMPTY_DATE) {
return false;
const EncryptionTopline = () => {
const not_after = useSelector((state) => state.encryption.not_after);
if (not_after === EMPTY_DATE) {
return null;
}
const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter);
const isExpired = isAfter(Date.now(), props.notAfter);
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
const isExpired = isAfter(Date.now(), not_after);
if (isExpired) {
return (
@@ -23,7 +24,9 @@ const EncryptionTopline = (props) => {
</Trans>
</Topline>
);
} if (isAboutExpire) {
}
if (isAboutExpire) {
return (
<Topline type="warning">
<Trans components={[<a href="#encryption" key="0">link</a>]}>
@@ -36,8 +39,4 @@ const EncryptionTopline = (props) => {
return false;
};
EncryptionTopline.propTypes = {
notAfter: PropTypes.string.isRequired,
};
export default withTranslation()(EncryptionTopline);
export default EncryptionTopline;

View File

@@ -1,5 +1,4 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
@@ -28,7 +27,7 @@ const linksData = [
},
];
const Footer = (props) => {
const Footer = () => {
const { t } = useTranslation();
const getYear = () => {
@@ -59,11 +58,6 @@ const Footer = (props) => {
{t(name)}
</a>);
const {
dnsVersion, processingVersion, getVersion, checkUpdateFlag,
} = props;
return (
<>
<footer className="footer">
@@ -94,12 +88,7 @@ const Footer = (props) => {
<div className="footer__row">
{renderCopyright()}
<div className="footer__column footer__column--language">
<Version
dnsVersion={dnsVersion}
processingVersion={processingVersion}
getVersion={getVersion}
checkUpdateFlag={checkUpdateFlag}
/>
<Version />
</div>
</div>
</div>
@@ -108,11 +97,4 @@ const Footer = (props) => {
);
};
Footer.propTypes = {
dnsVersion: PropTypes.string,
processingVersion: PropTypes.bool,
getVersion: PropTypes.func,
checkUpdateFlag: PropTypes.bool,
};
export default Footer;

View File

@@ -4,24 +4,36 @@
height: 100%;
}
.icon--small {
width: 1.5rem;
height: 1.5rem;
.icon--24 {
--size: 1.5rem;
width: var(--size);
height: var(--size);
}
.icon--smallest {
width: 1.2rem;
height: 1.2rem;
.icon--20 {
--size: 1.25rem;
width: var(--size);
height: var(--size);
}
.icon--18 {
--size: 1.125rem;
width: var(--size);
height: var(--size);
}
.icon--gray {
color: var(--gray-a5);
}
.icon--green {
color: var(--green-74);
}
.icon--disabled {
color: var(--gray-d8);
}
.icon--active {
color: #66b574;
.icon--lightgray {
color: var(--gray-8);
}

View File

@@ -1,9 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import './Loading.css';
const Loading = () => (
<div className="loading" />
const Loading = ({ className }) => (
<div className={classNames('loading', className)} />
);
Loading.propTypes = {
className: PropTypes.string,
};
export default Loading;

View File

@@ -0,0 +1,7 @@
.stats__link {
color: inherit;
}
.stats__link:hover {
cursor: pointer;
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import './LogsSearchLink.css';
import { getLogsUrlParams } from '../../helpers/helpers';
import { MENU_URLS } from '../../helpers/constants';
const LogsSearchLink = ({
search = '', response_status = '', children, link = MENU_URLS.logs,
}) => {
const { t } = useTranslation();
const to = link === MENU_URLS.logs ? `${MENU_URLS.logs}${getLogsUrlParams(search && `"${search}"`, response_status)}` : link;
return <Link to={to}
className={'stats__link'}
tabIndex={0}
title={t('click_to_view_queries')}
aria-label={t('click_to_view_queries')}>{children}</Link>;
};
LogsSearchLink.propTypes = {
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element]).isRequired,
search: PropTypes.string,
response_status: PropTypes.string,
link: PropTypes.string,
};
export default LogsSearchLink;

View File

@@ -6,18 +6,24 @@
.page-header--logs {
flex-direction: row;
align-items: flex-end;
margin: 2rem 0 3rem;
margin: 2rem 0 2.8rem;
}
.page-header--logs .page-title {
display: inline-flex;
align-items: center;
}
@media (max-width: 991px) {
.page-header--logs {
flex-direction: column;
align-items: center;
margin-bottom: 1.5rem;
margin: 1.1rem 0;
}
.page-header--logs .page-title {
padding-bottom: 2.5rem;;
margin-bottom: 1.1rem;
font-size: 1.8rem;
}
}

View File

@@ -1,151 +0,0 @@
.popover-wrap {
position: relative;
top: 1px;
display: inline-block;
vertical-align: middle;
align-self: flex-start;
}
.popover__trigger {
position: relative;
top: 3px;
margin: 0 8px;
cursor: pointer;
}
.popover__trigger:after {
content: "";
position: absolute;
top: -6px;
left: -3px;
width: 26px;
height: 24px;
}
.popover__trigger--address {
top: 0;
margin: 0;
line-height: 1.2;
}
.popover__trigger--address:after {
display: none;
}
.popover__body {
content: "";
display: flex;
position: absolute;
bottom: calc(100% + 3px);
left: 50%;
z-index: 1;
min-width: 275px;
padding: 10px 15px;
font-size: 0.8rem;
white-space: normal;
color: #fff;
background-color: #585965;
border-radius: 3px;
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
transform: translateX(-50%);
visibility: hidden;
opacity: 0;
}
.popover__body--filter {
min-width: 100%;
}
.popover__body:after {
content: "";
position: absolute;
bottom: -5px;
left: calc(50% - 6px);
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #585965;
}
.popover__body--address {
top: calc(100% + 10px);
right: 0;
left: initial;
bottom: initial;
z-index: 1;
min-width: 100px;
padding: 12px 18px;
font-weight: 700;
text-align: left;
white-space: nowrap;
transform: none;
cursor: default;
}
.popover__body--address:after {
top: -11px;
left: initial;
right: 40px;
border-top: 6px solid transparent;
border-bottom: 6px solid #585965;
}
.popover__body--address:before {
content: "";
position: absolute;
top: -7px;
left: 0;
width: 100%;
height: 10px;
}
.popover__trigger:hover + .popover__body,
.popover__body:hover {
visibility: visible;
opacity: 1;
}
.popover__icon {
width: 20px;
height: 20px;
stroke: #9aa0ac;
color: #9aa0ac;
}
.popover__icon--green {
color: #66b574;
stroke: #66b574;
}
.popover__list--bold {
font-weight: 700;
}
.popover__list-title {
margin-bottom: 3px;
}
.popover__list-item {
margin-bottom: 2px;
}
.popover__list-item--nowrap {
max-width: 300px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.popover__list-item:last-child {
margin-bottom: 0;
}
.popover__link {
color: #66b586;
}
.popover__link:hover,
.popover__link:focus {
color: #66b586;
}

View File

@@ -1,69 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import { getSourceData } from '../../helpers/trackers/trackers';
import { captitalizeWords } from '../../helpers/helpers';
import './Popover.css';
class Popover extends Component {
render() {
const { data } = this.props;
const sourceData = getSourceData(data);
const source = (
<div className="popover__list-item">
<Trans>source_label</Trans>: <a className="popover__link" target="_blank"
rel="noopener noreferrer"
href={sourceData.url}>
<strong>{sourceData.name}</strong>
</a>
</div>
);
const tracker = (
<div className="popover__list-item">
<Trans>name_table_header</Trans>: <a className="popover__link" target="_blank"
rel="noopener noreferrer"
href={data.url}>
<strong>{data.name}</strong>
</a>
</div>
);
const categoryName = captitalizeWords(data.category);
return (
<div className="popover-wrap">
<div className="popover__trigger">
<svg className="popover__icon" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" fill="none" strokeWidth="2" strokeLinecap="round"
strokeLinejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</div>
<div className="popover__body">
<div className="popover__list">
<div className="popover__list-title">
<Trans>found_in_known_domain_db</Trans>
</div>
{tracker}
<div className="popover__list-item">
<Trans>category_label</Trans>: <strong>
<Trans>{categoryName}</Trans></strong>
</div>
{source}
</div>
</div>
</div>
);
}
}
Popover.propTypes = {
data: PropTypes.object.isRequired,
};
export default withTranslation()(Popover);

View File

@@ -1,52 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import './Popover.css';
class PopoverFilter extends Component {
render() {
const { rule, filter, service } = this.props;
if (!rule && !service) {
return '';
}
return (
<div className="popover-wrap">
<div className="popover__trigger popover__trigger--filter">
<svg className="popover__icon popover__icon--green">
<use xlinkHref="#question" />
</svg>
</div>
<div className="popover__body popover__body--filter">
<div className="popover__list">
{rule && (
<div className="popover__list-item popover__list-item--nowrap">
<Trans>rule_label</Trans>: <strong>{rule}</strong>
</div>
)}
{filter && (
<div className="popover__list-item popover__list-item--nowrap">
<Trans>list_label</Trans>: <strong>{filter}</strong>
</div>
)}
{service && (
<div className="popover__list-item popover__list-item--nowrap">
<Trans>blocked_service</Trans>: <strong>{service}</strong>
</div>
)}
</div>
</div>
</div>
);
}
}
PopoverFilter.propTypes = {
rule: PropTypes.string,
filter: PropTypes.string,
service: PropTypes.string,
};
export default withTranslation()(PopoverFilter);

Some files were not shown because too many files have changed in this diff Show More