Compare commits

...

87 Commits

Author SHA1 Message Date
Eugene Burkov
c234e5dc31 Pull request 2317: Update all
Squashed commit of the following:

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

    client: upd i18n

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

    client: upd trackers

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

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

Squashed commit of the following:

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

    Merge branch 'master' into 7400-chown-permcheck

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

    permcheck: fix nil entries

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

    permcheck: fix nil owner

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

    permcheck: imp doc

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

    permcheck: imp code

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

    permcheck: imp code

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

    all: imp code, docs

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

    permcheck: imp code, docs

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

    all: implement windows permcheck

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

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

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

Squashed commit of the following:

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

    all: upd golibs

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

    all: upd proxy

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

    all: upd proxy

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

    all: upd chlog

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

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

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

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

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

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

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

    home: imp logging

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

    home: rename config field

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

    all: enable updater for some cases

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

    updater: imp test

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

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

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

    all: imp code

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

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

Squashed commit of the following:

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

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

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

    dnsforward: imp tests

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

    dnsforward: imp tests

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

    dnsforward: imp tests

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

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

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

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

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

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

Squashed commit of the following:

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

    Merge branch 'master' into 7400-disable-perm

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

    next: add flag

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

    home: fix help

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

    all: imp code, log changes

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

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

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

    all: imp code

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

    all: imp code

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

    all: imp code

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

    all: imp code

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

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

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

    next: upd more

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

    next/websvc: upd more

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

    next: upd more

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

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

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

    all: fix pre-commit; upd dnsproxy

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

    scripts: imp install

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

    all: imp docs, scripts

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

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

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

    client: upd i18n

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

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

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

    all: fix darwin find, log changes

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

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

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

    scripts: imp fmt manually

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

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

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

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

Squashed commit of the following:

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

    updater: upd doc

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

    all: rename const

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

    all: add binary const

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

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

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

    dnsforward: imp test

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

    rulelist: imp docs, tests

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

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

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

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

Squashed commit of the following:

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

    all: fix chlog

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

    aghos: imp doc

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

    all: rm bin

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

    all: revert permcheck

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

    all: use aghos more

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

    all: fix more bugs

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

    all: fix bugs

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

    all: fix chlog, imp api

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

    scripts: fix docs

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

    scripts: imp docs

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

    all: use new functions, add tests

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

    aghos: add stat

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

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

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

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

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

    client: imp tests

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

    client: imp docs

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

    all: imp code

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

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

Squashed commit of the following:

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

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

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

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

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

    all: fix typo

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

    all: fix snap build

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

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

Squashed commit of the following:

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

    all: upd chlog

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

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

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

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

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

    home: imp code

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

    all: imp code

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

    all: imp code

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

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

Squashed commit of the following:

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

    all: upd chlog

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

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

Squashed commit of the following:

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

    all: upd chlog

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

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

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

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

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

    all: imp chlog more

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

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

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

    dnsforward: imp test more

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

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

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

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

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

    dnsforward: imp test

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

    configmigrate: imp tests

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

    all: imp types, tests

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

    all: imp chlog

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

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

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

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

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

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

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

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

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

    all: imp chlog

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

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

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

    client: imp tests

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

    client: imp code

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

    all: imp code

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

    all: imp tests

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

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

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

    all: imp code

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

    client: fix typo

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

    all: imp code

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

    home: imp code

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

    all: imp code

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

    all: imp code

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

    all: imp code

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

    client: add tests

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

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

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

    all: upd chlog

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

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

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

    all: resolve tmp todos

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

    bamboo: checkout later

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

    bamboo: list files

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

    bamboo: work with vcs properly

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

    bamboo: add repo name var

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

    bamboo: upd api key

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

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

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

    all: only sign beta

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

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

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

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

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

    all: fix docs

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

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

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

    all: imp code

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

    stats: imp code

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

    all: imp code

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

    all: imp code

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

    stats: imp code

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

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

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

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

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

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

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

    all: imp code

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

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

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

    arpdb: imp docs

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

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

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

    dnsforward: imp code

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

    dnsforward: imp code

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

    dnsforward: imp code

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

    all: imp code

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

    all: imp code

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

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

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

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

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

    all: imp code

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

    all: add tests

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

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

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

    all: imp lint

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

    all: upd all

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

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

Squashed commit of the following:

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

    docs: chlog

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

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

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

    home: riscv arch

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

    scripts: riscv arch

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

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

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

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

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

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

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

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

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

    configmigrate: revert

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

    docs

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

    locales

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

    filtering: imp code

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

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

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

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

Squashed commit of the following:

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

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

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

    changelog fix

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

    fixed changelog

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

    changelog

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

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

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

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

Squashed commit of the following:

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

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

Squashed commit of the following:

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

    dhcpsvc: imp code, docs

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

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

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

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

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

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

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

    home: todos

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

    home: imp code

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

    all: imp code

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

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

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

    dnsforward: imp code

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

    aghos: imp code

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

    all: partial revert slog logger usage

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

    all: upd dnsproxy

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

    dnsforward: slog logger

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

    all: slog logger

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

    all: upd dnsproxy

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

    all: upd dnsproxy

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

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

Squashed commit of the following:

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

    dhcpsvc: close db

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

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

Squashed commit of the following:

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

    dhcpsvc: imp docs

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

    dhcpsvc: imp code, tests

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

    dhcpsvc: imp tests

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

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

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

    all: imp chlog better

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

    all: imp chlog

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

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

Squashed commit of the following:

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

    clear query log filter fix
2024-07-04 12:43:47 +03:00
Eugene Burkov
93e4005125 Pull request 2249: Fix bamboo-specs
Squashed commit of the following:

commit 66127b1b457d092537eec3b386108200f999cc02
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 15:54:04 2024 +0300

    bamboo-specs: fix paths
2024-07-03 16:05:17 +03:00
Eugene Burkov
beeb8f0522 Pull request 2245: 4923 gopacket DHCP vol.8
Updates #4923.

Squashed commit of the following:

commit 0bfccf8bc1e63c4f5a01ce7f268e767969b368a0
Merge: 305f9fe2f 0e5e8e4dd
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 15:12:43 2024 +0300

    Merge branch 'master' into 4923-gopacket-dhcp-vol.8

commit 305f9fe2fec033f28385dfe2bbee6a27a83b9702
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 17:03:01 2024 +0300

    dhcpsvc: adjust interface

commit f05b9f42e2f50325ddaf09b5fed84b62e48c5120
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 16:59:39 2024 +0300

    dhcpsvc: use logger

commit 4779f945baf9c8722d07d589928a86290a37d3ab
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jul 2 14:59:22 2024 +0300

    dhcpsvc: add todo

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

    dhcpsvc: use slog
2024-07-03 15:29:54 +03:00
Eugene Burkov
0e5e8e4dde Pull request 2247: Upd Go
Squashed commit of the following:

commit 9dc0d489601764003adc2d7c4ae73c45c9d07bba
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:23:03 2024 +0300

    client: fix some locales

commit a130e205509cbd423c9c9793824a84b550cee0e5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:09:09 2024 +0300

    client: upd translations

commit aeccb20b6172fb019cee8820a9087a573a4fbacf
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 14:04:12 2024 +0300

    all: upd services, trackers

commit f6a7f34e17b89f22fcfaed5001c256f12653663b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:32:55 2024 +0300

    all: imp fmt

commit e8b561175c0bfd6415dcb97c98400543906fb097
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:27:47 2024 +0300

    all: add linebreak

commit 2b28fa107bf43eb3b7a1878716fb5cc0ec2204d2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jul 3 13:17:30 2024 +0300

    all: upd go
2024-07-03 15:08:17 +03:00
Ildar Kamalov
fcdebfa4d4 Pull request: ADG-8737 fixed missing version in the footer, unnecessary validation call on the encryption page
Squashed commit of the following:

commit 1c4a15f2f32cd8bfbe0878f79feee4f581f0f5a8
Merge: 399d28e67 9d1c45fd9
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Jul 2 13:08:21 2024 +0300

    Merge branch 'master' into ADG-8737

commit 399d28e67ff8f8886d5552fea96017e83a122306
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jul 1 19:37:05 2024 +0300

    fix install

commit 91d5dd23cea0dd5cde1bf5979bad181229d36f1a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jul 1 17:40:22 2024 +0300

    home: imp logs

commit 06917df08b2154c726ca2de7ed7fda1de4269655
Author: Ildar Kamalov <ik@adguard.com>
Date:   Mon Jul 1 16:38:38 2024 +0300

    ADG-8737 add missing version, remove validation call
2024-07-02 13:30:55 +03:00
Stanislav Chzhen
9d1c45fd94 Pull request 2244: AG-27492-client-storage-usage
Squashed commit of the following:

commit 46956d0f5a36fbcd2324125bcc146bdb57cb3f7e
Merge: 85ccad786 3993f4c47
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jul 1 16:11:59 2024 +0300

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

commit 85ccad7862a35cbebc935776ac057f6bb6cfc75f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Jun 28 18:05:37 2024 +0300

    all: imp docs

commit e7043efcda32635b4260344f11b5a61b5120a9d2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 27 20:10:26 2024 +0300

    all: client storage usage
2024-07-01 17:34:47 +03:00
Ainar Garipov
3993f4c476 Pull request 2243: AG-33443-upd-vetted-script
Squashed commit of the following:

commit 85ce53f0fc53c5422d49dc50d9017a7dd009f7ba
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 27 20:11:01 2024 +0300

    client: upd

commit 2588807dfdf4e7e0159ada57d6973194eaf3e286
Merge: c5fc7fbf4 a1a31cd91
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 27 20:10:34 2024 +0300

    Merge branch 'master' into AG-33443-upd-vetted-script

commit c5fc7fbf4f85cb7e123a58f42c1ee83b1b369013
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 24 13:39:46 2024 +0300

    scripts: imp log

commit af420878b4f5753187b7afa6c2c3f3db54cf7711
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 24 13:12:12 2024 +0300

    scripts: upd vetter-filters
2024-06-27 20:18:17 +03:00
Stanislav Chzhen
a1a31cd916 Pull request 2221: AG-27492-client-persistent-runtime-storage
Squashed commit of the following:

commit a2b1e829f57fa7411354d882ec67d0c8736efbac
Merge: 5fde76bb2 65b7d232a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 25 16:12:17 2024 +0300

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

commit 5fde76bb20f818f052fe89dc90c2b3ea790da4d2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Jun 21 16:58:17 2024 +0300

    all: imp code

commit eae49f91bc1b5eedae3d03b0b6c782afa11896d8
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Jun 19 20:10:55 2024 +0300

    all: use storage

commit 2c7efa46099d9b8ffe297ce247aff0aa8f45dff7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 20:14:34 2024 +0300

    client: add tests

commit d59bd7a24e273e58737c3efa832adabc57495bed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 18:31:23 2024 +0300

    client: add tests

commit 045b83882380a8e181f6892cc3245944e4c9fd52
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 15:18:08 2024 +0300

    client: add tests

commit 702467f7cadf3574c4a1b7b441ac02e26581bfcf
Merge: 4abc23bf8 1c82be295
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 17 18:40:43 2024 +0300

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

commit 4abc23bf84dd8de02a1b805afba4d5a724b39d0c
Merge: e268abf92 bed86d57f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 13 15:19:47 2024 +0300

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

commit e268abf9268aef7a5386b5e126b01b249c590f49
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 13 15:19:36 2024 +0300

    client: add tests

commit 5601cfce39599337aaf04688ffe2b14b49f856e5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 27 14:27:53 2024 +0300

    client: runtime index

commit bde3baa5da85dd5404f78bd79a6a3e85c55cf7fc
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon May 20 14:39:35 2024 +0300

    all: persistent client storage
2024-06-26 14:30:02 +03:00
Stanislav Chzhen
65b7d232ab Pull request 2242: 7079-log-enabled
Updates #7079.

Squashed commit of the following:

commit 477c5ed29c8066dc9cb175809572f613b2a0dfa0
Merge: d3c64e03a 647214092
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 19:31:54 2024 +0300

    Merge branch 'master' into 7079-log-enabled

commit d3c64e03a514e39d70e6b79209be1f8cf052a25d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 16:02:42 2024 +0300

    home: imp docs

commit 7658c9b107f109c391b6bd5407098e8e53754842
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Jun 20 14:17:08 2024 +0300

    all: log enabled
2024-06-20 19:44:51 +03:00
Eugene Burkov
6472140920 Pull request 2241: 7075 Doc load-balancing
Updates #7075.

Squashed commit of the following:

commit cd889bd828bcfaf71087727f169f05b69c508a45
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 20 17:20:13 2024 +0300

    client: imp text

commit 3d51d48037bb85df5f04b6d3f83b1857253adf88
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Jun 19 13:47:48 2024 +0300

    client: imp ui text
2024-06-20 17:34:21 +03:00
Stanislav Chzhen
08d863dd3a Pull request 2235: 7069-fix-blocked-services
Updates #7069.

Squashed commit of the following:

commit 0f87493966124af4fa4b28eb1f67281343dd8242
Merge: 87f06b864 28a6c24db
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 14:09:18 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 87f06b86432bba4d2e2583010784746452b7690f
Merge: c2440752c 66877c92d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 18 13:47:28 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit c2440752c8a223e8ab2e4a519da9259d888ca06f
Merge: 17a9c14e2 1c82be295
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 17 18:42:48 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 17a9c14e29a38ed513a827d9b5351d66f1e6cb48
Merge: 11160bc62 bed86d57f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 11 13:41:00 2024 +0300

    Merge branch 'master' into 7069-fix-blocked-services

commit 11160bc62b08fd6e984d38d5600fa62c3564668f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Jun 11 13:36:56 2024 +0300

    all: imp docs

commit 491287164d31606c93dcbeb4f1e351df0960309b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 14:03:22 2024 +0300

    home: imp code

commit 0caf8b15797c28447039736cc9641ccddee5cac0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 13:35:54 2024 +0300

    all: upd chlog

commit 46f793b2591dce54a9566ccb9cb723ab5c60cd6a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Jun 10 13:27:22 2024 +0300

    home: fix blocked services
2024-06-18 14:27:25 +03:00
Dimitry Kolyshev
28a6c24db2 Pull request: AG-33515-upd-deps
Squashed commit of the following:

commit 52698fe8d64ede24f9259810b8e31bc4e5f3682a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Jun 18 10:18:26 2024 +0300

    all: upd deps
2024-06-18 14:07:35 +03:00
Eugene Burkov
66877c92d9 Pull request 2239: 7076 Empty FSWatcher
Updates #7076.

Squashed commit of the following:

commit 6d99de9bcd1a4882f96639cf7e54fe0f33cfbfd3
Merge: c545152fa 1c82be295
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 17 18:41:49 2024 +0300

    Merge branch 'master' into 7076-empty-fswatcher

commit c545152fa157e52f9ac5ebf2e58fdc1a254faf91
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jun 14 14:57:01 2024 +0300

    all: imp code

commit e033558d7027a40a6996c08b5125e45141192071
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Jun 14 14:39:58 2024 +0300

    all: add & use empty fswatcher
2024-06-17 19:34:46 +03:00
Eugene Burkov
1c82be2950 Pull request 2237: 7053 journald Log
Updates #7053.

Squashed commit of the following:

commit f763a229660c00013fbd51cf7a3deabf00a01787
Merge: 06fc83d08 8432593be
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Jun 17 17:52:11 2024 +0300

    Merge branch 'master' into 7053-journald-log

commit 06fc83d08e45ebb2a09382f2b6961bb18a98906a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Jun 13 15:59:35 2024 +0300

    all: imp chlog

commit 1f57a7e84aee7827f9b25f232ca46152a15da371
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 20:36:55 2024 +0300

    all: imp chlog

commit 7c9a2547a82b4737e1785bb56ede2bf01bfb8533
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 18:33:26 2024 +0300

    home: imp doc

commit 63731e7ba5e4085d071092ff4c7d712a30e07f28
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Jun 11 18:27:31 2024 +0300

    home: imp systemd script
2024-06-17 18:33:16 +03:00
Dimitry Kolyshev
8432593be1 Pull request: AG-33410-aghos-err
Merge in DNS/adguard-home from AG-33410-aghos-err to master

Squashed commit of the following:

commit 6014ea1e919ea685475561e4a46284847f67ac99
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Jun 14 08:30:30 2024 +0300

    all: imp code

commit 232b207d8da42dad297f2730c42e5e84f9049ab9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Jun 13 12:18:41 2024 +0300

    all: rm aghos unsupported err
2024-06-14 12:32:19 +03:00
Ainar Garipov
bed86d57f3 Pull request 2236: fix-chlog
Squashed commit of the following:

commit e92093842b8c8627125ec16d1f5898dd2bc6d996
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Mon Jun 10 20:42:50 2024 +0300

    all: fix chlog
2024-06-10 20:49:47 +03:00
Igor Lobanov
1afe226ce8 Pull request #2231: ADG-8368 Frontend rewritten in TypeScript, added Node 18 support
Merge in DNS/adguard-home from ADG-8368-typescript-node-18 to master

Squashed commit of the following:

commit daa288ae0d76178af24595cc807055902e6f09ab
Merge: 4c89cf720 1085d59a6
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Mon Jun 10 17:22:20 2024 +0200

    merge

commit 4c89cf7209
Author: Ildar Kamalov <ik@adguard.com>
Date:   Thu Jun 6 13:27:18 2024 +0300

    remove install from initial state

commit b943f2011f
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 23:10:55 2024 +0200

    frontend production build fix

commit cd1be2d66d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 20:23:14 2024 +0200

    production build quickfix

commit 7b8ac01fc2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jun 5 19:57:31 2024 +0300

    all: upd node docker

commit 02afed66d5
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 18:23:12 2024 +0200

    changelog fixes

commit 9c0f736f0c
Merge: 62c4fbf1e e04775c4f
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 18:18:29 2024 +0200

    merge

commit 62c4fbf1e3
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:22:22 2024 +0200

    empty line in changelog

commit 76b1e44a93
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:20:37 2024 +0200

    changelog

commit f783e90040
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:19:13 2024 +0200

    filters.js -> filters.ts

commit 3d4ce6554c
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 16:18:03 2024 +0200

    generated file removed

commit e35ba58f2a
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 15:45:21 2024 +0200

    rollback unwanted changes

commit 1f30d4216d
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 15:27:36 2024 +0200

    review fix

commit 6cd4e44f07
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 11:55:39 2024 +0200

    missing generated file restoresd

commit 2ab738b303
Author: Igor Lobanov <bniwredyc@gmail.com>
Date:   Wed Jun 5 11:40:32 2024 +0200

    Frontend rewritten in TypeScript, added Node 18 support
2024-06-10 18:42:23 +03:00
Ainar Garipov
1085d59a65 Pull request 2234: all: upd chlog
Squashed commit of the following:

commit baca5552390129e3ae24e19ab45746ea861fd4c4
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Jun 6 17:55:07 2024 +0300

    all: upd chlog
2024-06-06 18:25:07 +03:00
Ainar Garipov
e04775c4fa Pull request 2232: upd-go
Squashed commit of the following:

commit 8ea462f825d27593b58b8d7f6d38c4a7fc2a3ae5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Jun 5 18:33:15 2024 +0300

    all: upd go, tools
2024-06-05 18:57:28 +03:00
Ainar Garipov
4392255d7e Pull request 2230: home: incr timeout
Updates #7041.

Squashed commit of the following:

commit 536382cf55bd158f179aaeabb1a16ed86d437257
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Jun 4 20:56:56 2024 +0300

    home: incr timeout
2024-06-04 21:06:11 +03:00
Dimitry Kolyshev
b69890b8fe Pull request: AG-32410-dnsproxy
Squashed commit of the following:

commit f115ca71eca582242b1af68b37e22b6d9697e6e3
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri May 31 14:16:16 2024 +0400

    all: upd dnsproxy
2024-06-04 15:43:42 +03:00
Eugene Burkov
9496610779 Pull request 2227: Upd all
Squashed commit of the following:

commit 8c9a4b398111b34ea4b0e4e2afa9eafb7bfb2e23
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue May 28 13:56:26 2024 +0300

    client: upd i18n, trackers
2024-05-28 15:33:49 +03:00
560 changed files with 66602 additions and 62814 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.22.3'
'GO_VERSION': '1.23.4'
'NODE_VERSION': '16'
'on':
@@ -95,7 +95,7 @@
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
'restore-keys': '${{ runner.os }}-node-'
- 'name': 'Set up Snapcraft'
'run': 'sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft'
'run': 'sudo snap install snapcraft --classic'
- 'name': 'Set up QEMU'
'uses': 'docker/setup-qemu-action@v1'
- 'name': 'Set up Docker Buildx'

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.22.3'
'GO_VERSION': '1.23.4'
'on':
'push':

7
.gitignore vendored
View File

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

View File

@@ -7,28 +7,239 @@ The format is based on
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<!--
TODO(a.garipov): Use the common markdown formatting tools.
-->
## [Unreleased]
<!--
## [v0.108.0] - TBA
## [v0.107.55] - 2024-11-09 (APPROX.)
## [v0.107.51] - 2024-06-22 (APPROX.)
See also the [v0.107.55 GitHub milestone][ms-v0.107.55].
See also the [v0.107.51 GitHub milestone][ms-v0.107.51].
[ms-v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/milestone/86?closed=1
[ms-v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/milestone/90?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Security
- The permission check and migration on Windows has been fixed to use the
Windows security model more accurately ([#7400]).
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [1.23.4][go-1.23.4].
- The release executables are now signed.
### Added
- The `--no-permcheck` command-line option to disable checking and migration of
permissions for the security-sensitive files and directories, which caused
issues on Windows ([#7400]).
### Fixed
- Goroutine leak during the upstream DNS server test ([#7357]).
- Goroutine leak during configuration update resulting in increased response
time ([#6818]).
[#6818]: https://github.com/AdguardTeam/AdGuardHome/issues/6818
[#7357]: https://github.com/AdguardTeam/AdGuardHome/issues/7357
[#7400]: https://github.com/AdguardTeam/AdGuardHome/issues/7400
[go-1.23.4]: https://groups.google.com/g/golang-announce/c/3DyiMkYx4Fo
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.54] - 2024-11-06
See also the [v0.107.54 GitHub milestone][ms-v0.107.54].
### Security
- Incorrect handling of sensitive files permissions on Windows ([#7314]).
### Changed
- Improved filtering performance ([#6818]).
### Fixed
- Repetitive statistics log messages ([#7338]).
- Custom client cache ([#7250]).
- Missing runtime clients with information from the system hosts file on first
AdGuard Home start ([#7315]).
[#6818]: https://github.com/AdguardTeam/AdGuardHome/issues/6818
[#7250]: https://github.com/AdguardTeam/AdGuardHome/issues/7250
[#7314]: https://github.com/AdguardTeam/AdGuardHome/issues/7314
[#7315]: https://github.com/AdguardTeam/AdGuardHome/issues/7315
[#7338]: https://github.com/AdguardTeam/AdGuardHome/issues/7338
[ms-v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/milestone/89?closed=1
## [v0.107.53] - 2024-10-03
See also the [v0.107.53 GitHub milestone][ms-v0.107.53].
### Security
- Previous versions of AdGuard Home allowed users to add any system file it had
access to as filters, exposing them to be world-readable. To prevent this,
AdGuard Home now allows adding filtering-rule list files only from files
matching the patterns enumerated in the `filtering.safe_fs_patterns` property
in the configuration file.
We thank @itz-d0dgy for reporting this vulnerability, designated
CVE-2024-36814, to us.
- Additionally, AdGuard Home will now try to change the permissions of its files
and directories to more restrictive ones to prevent similar vulnerabilities
as well as limit the access to the configuration.
We thank @go-compile for reporting this vulnerability, designated
CVE-2024-36586, to us.
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [1.23.2][go-1.23.2].
### Added
- Support for 64-bit RISC-V architecture ([#5704]).
- Ecosia search engine is now supported in safe search ([#5009]).
### Changed
- Upstream server URL domain names requirements has been relaxed and now follow
the same rules as their domain specifications.
#### Configuration changes
In this release, the schema version has changed from 28 to 29.
- The new array `filtering.safe_fs_patterns` contains glob patterns for paths of
files that can be added as local filtering-rule lists. The migration should
add list files that have already been added, as well as the default value,
`$DATA_DIR/userfilters/*`.
### Fixed
- Property `clients.runtime_sources.dhcp` in the configuration file not taking
effect.
- Stale Google safe search domains list ([#7155]).
- Bing safe search from Edge sidebar ([#7154]).
- Text overflow on the query log page ([#7119]).
### Known issues
- Due to the complexity of the Windows permissions architecture and poor support
from the standard Go library, we have to postpone the proper automated Windows
fix until the next release.
**Temporary workaround:** Set the permissions of the `AdGuardHome` directory
to more restrictive ones manually. To do that:
1. Locate the `AdGuardHome` directory.
2. Right-click on it and navigate to *Properties → Security → Advanced.*
3. (You might need to disable permission inheritance to make them more
restricted.)
4. Adjust to give the `Full control` access to only the user which runs
AdGuard Home. Typically, `Administrator`.
[#5009]: https://github.com/AdguardTeam/AdGuardHome/issues/5009
[#5704]: https://github.com/AdguardTeam/AdGuardHome/issues/5704
[#7119]: https://github.com/AdguardTeam/AdGuardHome/issues/7119
[#7154]: https://github.com/AdguardTeam/AdGuardHome/pull/7154
[#7155]: https://github.com/AdguardTeam/AdGuardHome/pull/7155
[go-1.23.2]: https://groups.google.com/g/golang-announce/c/NKEc8VT7Fz0
[ms-v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/milestone/88?closed=1
## [v0.107.52] - 2024-07-04
See also the [v0.107.52 GitHub milestone][ms-v0.107.52].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.22.5][go-1.22.5].
### Added
- The ability to disable logging using the new `log.enabled` configuration
property ([#7079]).
### Changed
- Frontend rewritten in TypeScript.
- The `systemd`-based service now uses `journal` for logging by default. It
also doesn't create the `/var/log/` directory anymore ([#7053]).
**NOTE:** With an installed service for changes to take effect, you need to
reinstall the service using `-r` flag of the [install script][install-script]
or via the CLI (with root privileges):
```sh
./AdGuardHome -s uninstall
./AdGuardHome -s install
```
Don't forget to backup your configuration file and other important data before
reinstalling the service.
### Deprecated
- Node 18 support, Node 20 will be required in future releases.
### Fixed
- Panic caused by missing user-specific blocked services object in configuration
file ([#7069]).
- Tracking `/etc/hosts` file changes causing panics within particular
filesystems on start ([#7076]).
[#7053]: https://github.com/AdguardTeam/AdGuardHome/issues/7053
[#7069]: https://github.com/AdguardTeam/AdGuardHome/issues/7069
[#7076]: https://github.com/AdguardTeam/AdGuardHome/issues/7076
[#7079]: https://github.com/AdguardTeam/AdGuardHome/issues/7079
[go-1.22.5]: https://groups.google.com/g/golang-announce/c/gyb7aM1C9H4
[install-script]: https://github.com/AdguardTeam/AdGuardHome/?tab=readme-ov-file#automated-install-linux-and-mac
[ms-v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/milestone/87?closed=1
## [v0.107.51] - 2024-06-06
See also the [v0.107.51 GitHub milestone][ms-v0.107.51].
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.22.4][go-1.22.4].
### Changed
- The HTTP server's write timeout has been increased from 1 minute to 5 minutes
to match the one used by AdGuard Home's HTTP client to fetch filtering-list
data ([#7041]).
[#7041]: https://github.com/AdguardTeam/AdGuardHome/issues/7041
[go-1.22.4]: https://groups.google.com/g/golang-announce/c/XbxouI9gY7k/
[ms-v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/milestone/86?closed=1
## [v0.107.50] - 2024-05-23
See also the [v0.107.50 GitHub milestone][ms-v0.107.50].
@@ -2986,11 +3197,15 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...HEAD
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.55...HEAD
[v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...v0.107.55
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...HEAD
[v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.53...v0.107.54
[v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.52...v0.107.53
[v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...v0.107.52
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
[v0.107.50]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.49...v0.107.50
[v0.107.49]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.48...v0.107.49
[v0.107.48]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.47...v0.107.48

View File

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

View File

@@ -114,7 +114,7 @@ If you're running **Linux,** there's a secure and easy way to install AdGuard Ho
[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
[Snap Store]: https://snapcraft.io/adguard-home
[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
[wiki-start]: https://adguard-dns.io/kb/adguard-home/getting-started/
### <a href="#guides" id="guides" name="guides">Guides</a>
@@ -205,10 +205,9 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
- [Go](https://golang.org/dl/) v1.22 or later;
- [Node.js](https://nodejs.org/en/download/) v16 or later;
- [Go](https://golang.org/dl/) v1.23 or later;
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
- [npm](https://www.npmjs.com/) v8 or later;
- [yarn](https://yarnpkg.com/) v1.22.5 or later.
### <a href="#building" id="building" name="building">Building</a>
@@ -220,14 +219,6 @@ cd AdGuardHome
make
```
#### <a href="#building-node" id="building-node" name="building-node">Building with Node.js 17 and later</a>
In order to build AdGuard Home with Node.js 17 and later, specify `--openssl-legacy-provider` option.
```sh
export NODE_OPTIONS=--openssl-legacy-provider
```
> [!WARNING]
> The non-standard `-j` flag is currently not supported, so building with `make -j 4` or setting your `MAKEFLAGS` to include, for example, `-j 4` is likely to break the build. If you do have your `MAKEFLAGS` set to that, and you don't want to change it, you can override it by running `make -j 1`.

View File

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

View File

@@ -5,8 +5,8 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.4--1'
'channel': 'development'
'stages':
@@ -54,6 +54,7 @@
'requirements':
- 'adg-docker': 'true'
# TODO(e.burkov): Add the linting stage for markdown docs and shell scripts.
'Test backend':
'docker':
'image': '${bamboo.dockerGo}'
@@ -194,6 +195,6 @@
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
'dockerFrontend': 'adguard/home-js-builder:1.1'
'dockerGo': 'adguard/go-builder:1.22.3--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.23.4--1'
'channel': 'candidate'

60
client/.eslintrc.json vendored
View File

@@ -1,9 +1,13 @@
{
"parser": "babel-eslint",
"plugins": ["prettier"],
"extends": [
"airbnb-base",
"prettier",
"eslint:recommended",
"plugin:react/recommended",
"airbnb-base"
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"env": {
"jest": true,
"node": true,
@@ -16,50 +20,21 @@
"version": "16.4"
},
"import/resolver": {
"webpack": {
"config": "webpack.common.js"
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"rules": {
"indent": [
"@typescript-eslint/no-explicit-any": "off",
"import/extensions": [
"error",
4,
"ignorePackages",
{
"SwitchCase": 1,
"VariableDeclarator": 1,
"outerIIFEBody": 1,
"FunctionDeclaration": {
"parameters": 1,
"body": 1
},
"FunctionExpression": {
"parameters": 1,
"body": 1
},
"CallExpression": {
"arguments": 1
},
"ArrayExpression": 1,
"ObjectExpression": 1,
"ImportDeclaration": 1,
"flatTernaryExpressions": false,
"ignoredNodes": [
"JSXElement",
"JSXElement > *",
"JSXAttribute",
"JSXIdentifier",
"JSXNamespacedName",
"JSXMemberExpression",
"JSXSpreadAttribute",
"JSXExpressionContainer",
"JSXOpeningElement",
"JSXClosingElement",
"JSXText",
"JSXEmptyExpression",
"JSXSpreadChild"
],
"ignoreComments": false
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
],
"class-methods-use-this": "off",
@@ -68,10 +43,7 @@
"no-console": [
"warn",
{
"allow": [
"warn",
"error"
]
"allow": ["warn", "error"]
}
],
"import/no-extraneous-dependencies": [

View File

@@ -1 +1 @@
*.js text eol=lf
*.ts text eol=lf

10
client/.prettierrc vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": true,
"tabWidth": 4,
"semi": true,
"arrowParens": "always",
}

46
client/.stylelintrc vendored
View File

@@ -1,46 +0,0 @@
{
"defaultSeverity": "warning",
"rules": {
"block-closing-brace-empty-line-before": "never",
"block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-named": "never",
"color-no-invalid-hex": true,
"length-zero-no-unit": true,
"declaration-block-trailing-semicolon": "always",
"custom-property-empty-line-before": ["always", {
"except": [
"after-custom-property",
"first-nested"
]
}],
"declaration-block-no-duplicate-properties": true,
"declaration-colon-space-after": "always",
"declaration-empty-line-before": ["always", {
"except": [
"after-declaration",
"first-nested",
"after-comment"
]
}],
"font-weight-notation": "numeric",
"indentation": [4, {
"except": ["value"]
}],
"max-empty-lines": 2,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"property-no-unknown": [true, {
"ignoreProperties": "/lost-.+/"
}],
"rule-empty-line-before": [ "always-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment"]
}],
"string-quotes": "double",
"value-list-comma-space-after": "always",
"unit-case": "lower"
}
}

44
client/.stylelintrc.js vendored Normal file
View File

@@ -0,0 +1,44 @@
module.exports = {
rules: {
"selector-type-no-unknown": true,
"block-closing-brace-empty-line-before": "never",
"block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-named": "never",
"color-no-invalid-hex": true,
"length-zero-no-unit": true,
"declaration-block-trailing-semicolon": "always",
"custom-property-empty-line-before": ["always", {
"except": [
"after-custom-property",
"first-nested"
]
}],
"declaration-block-no-duplicate-properties": true,
"declaration-colon-space-after": "always",
"declaration-empty-line-before": ["always", {
"except": [
"after-declaration",
"first-nested",
"after-comment"
]
}],
"font-weight-notation": "numeric",
"indentation": [4, {
"except": ["value"]
}],
"max-empty-lines": 2,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"property-no-unknown": true,
"rule-empty-line-before": ["always-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment"]
}],
"string-quotes": "double",
"value-list-comma-space-after": "always",
"unit-case": "lower"
}
}

14
client/babel.config.cjs vendored Normal file
View File

@@ -0,0 +1,14 @@
module.exports = (api) => {
api.cache(false);
return {
presets: ['@babel/preset-env', '@babel/preset-typescript', '@babel/preset-react'],
plugins: [
'@babel/plugin-transform-runtime',
'@babel/plugin-transform-class-properties',
'@babel/plugin-transform-object-rest-spread',
'@babel/plugin-transform-nullish-coalescing-operator',
'@babel/plugin-transform-optional-chaining',
'react-hot-loader/babel',
],
};
};

View File

@@ -1,17 +0,0 @@
module.exports = (api) => {
api.cache(false);
return {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-runtime',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
'react-hot-loader/babel',
],
};
};

9
client/constants.js vendored
View File

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

6
client/global.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
import React from 'react';
declare module '*.svg' {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}

View File

@@ -1,5 +0,0 @@
module.exports = {
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
};

6
client/jest.config.mjs vendored Normal file
View File

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

39895
client/package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

110
client/package.json vendored
View File

@@ -3,19 +3,23 @@
"version": "0.1.0",
"private": true,
"scripts": {
"build-dev": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js",
"build-dev": "cross-env NODE_ENV=development BUILD_ENV=dev webpack --config webpack.dev.js",
"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",
"lint": "echo 'Lint temporarily disabled'",
"lint-new": "eslint './src/**/*.(ts|tsx)'",
"lint:fix": "eslint './src/**/*.(ts|tsx)' --fix",
"test": "jest",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"typecheck": "tsc --noEmit",
"typecheck:watch": "tsc --noEmit --watch"
},
"type": "module",
"dependencies": {
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"classnames": "^2.5.1",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",
@@ -24,7 +28,8 @@
"js-yaml": "^3.14.0",
"lodash": "^4.17.19",
"nanoid": "^3.1.9",
"prop-types": "^15.7.2",
"popper.js": "^1.16.1",
"prop-types": "^15.8.1",
"query-string": "^6.13.1",
"react": "^16.13.1",
"react-click-outside": "^3.0.1",
@@ -38,53 +43,64 @@
"react-router-hash-link": "^1.2.2",
"react-select": "^3.1.0",
"react-table": "^6.11.4",
"react-transition-group": "^4.4.1",
"react-transition-group": "^4.4.5",
"redux": "^4.0.5",
"redux-actions": "^2.6.5",
"redux-form": "^8.3.5",
"redux-form": "^8.3.10",
"redux-thunk": "^2.3.0",
"url-polyfill": "^1.1.9"
"ts-migrate": "^0.1.35",
"url-polyfill": "^1.1.12"
},
"devDependencies": {
"@babel/core": "^7.9.6",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4",
"@babel/plugin-proposal-object-rest-spread": "^7.9.6",
"@babel/plugin-proposal-optional-chaining": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.9.4",
"autoprefixer": "^9.8.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.0.1",
"cross-env": "^7.0.2",
"css-loader": "^3.5.3",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.1.0",
"eslint-import-resolver-webpack": "^0.12.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^2.5.0",
"file-loader": "6.0.0",
"html-webpack-plugin": "^4.3.0",
"jest": "^26.0.1",
"mini-css-extract-plugin": "^0.9.0",
"@babel/core": "^7.24.5",
"@babel/plugin-transform-class-properties": "^7.24.1",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1",
"@babel/plugin-transform-object-rest-spread": "^7.24.5",
"@babel/plugin-transform-optional-chaining": "^7.24.5",
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.4",
"@types/react": "^17.0.80",
"@types/react-dom": "^18.3.0",
"@types/react-redux": "^7.1.33",
"@types/react-router-dom": "^5.3.3",
"@types/react-table": "^7.7.20",
"@types/redux-actions": "^2.6.5",
"@types/redux-form": "^8.3.10",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.10.0",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^12.0.2",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.6.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jscodeshift": "^0.15.2",
"mini-css-extract-plugin": "^2.9.0",
"path": "^0.12.7",
"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",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2"
"postcss-loader": "^8.1.1",
"prettier": "^3.2.5",
"react-hot-loader": "^4.13.1",
"style-loader": "^4.0.0",
"stylelint": "^16.5.0",
"ts-loader": "^9.5.1",
"url-loader": "^4.1.1",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-merge": "^5.10.0"
},
"browserslist": {
"development": [

View File

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

View File

@@ -159,6 +159,8 @@
"dns_over_https": "DNS-пред-HTTPS",
"dns_over_quic": "DNS-over-QUIC",
"plain_dns": "Обикновен DNS",
"theme_light": "Светла тема",
"theme_dark": "Тъмна тема",
"source_label": "Източник",
"found_in_known_domain_db": "Намерен в списъците с домейни.",
"category_label": "Категория",
@@ -283,5 +285,12 @@
"filter_category_general": "General",
"filter_category_security": "Сигурност",
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това.",
"parental_control": "Родителски контрол"
"parental_control": "Родителски контрол",
"sunday_short": "Нд",
"monday_short": "Пон",
"tuesday_short": "Вт",
"wednesday_short": "Ср",
"thursday_short": "Чт",
"friday_short": "Пт",
"saturday_short": "Съб"
}

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
"parallel_requests": "Paralelní požadavky",
"load_balancing": "Optimalizace vytížení",
"load_balancing_desc": "Optimalizovaný dotaz na odchozí server. AdGuard Home použije vážený náhodný algoritmus k výběru serveru, takže nejrychlejší server je používán častěji.",
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy DNS serverů používaných k překladu IP adres řešitelů DoH/DoT, které zadáte jako odchozí servery. Komentáře nejsou povoleny.",
"fallback_dns_title": "Záložní DNS servery",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použít službu AdGuard Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home zkontroluje, zda doména obsahuje materiály pro dospělé. Používá stejné API přátelské k ochraně osobních údajů jako služba Bezpečnost prohlížení.",
"enforce_safe_search": "Použít bezpečné vyhledávání",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynutí bezpečné vyhledávání v následujících vyhledávačích: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nebyly specifikovány žádné servery",
"general_settings": "Obecná nastavení",
"dns_settings": "Nastavení DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
"parallel_requests": "Parallelle forespørgsler",
"load_balancing": "Belastningsfordeling",
"load_balancing_desc": "Forespørg én server ad gangen. AdGuard Home vil bruge en vægtet randomiseringsalgoritme til valg af server, så den hurtigste server oftere anvendes.",
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
"bootstrap_dns": "Bootstrap DNS-servere",
"bootstrap_dns_desc": "IP-adresser på DNS-servere, som bruges til at opløse IP-adresser på de DoH/DoT-opløsere, som angives som upstreams. Kommentarer er ikke tilladt.",
"fallback_dns_title": "Reserve DNS-servere",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Brug AdGuards forældrekontrolwebtjeneste",
"use_adguard_parental_hint": "AdGuard Home vil tjekke, om domænet indeholder voksenindhold vha. den samme fortrolighedsvenlige API som browsingsikkerhedswebtjenesten.",
"enforce_safe_search": "Brug sikker søgning",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Yandex og Pixabay.",
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Ingen servere angivet",
"general_settings": "Generelle indstillinger",
"dns_settings": "DNS-indstillinger",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
"parallel_requests": "Paralleles Abfragen",
"load_balancing": "Lastverteilung",
"load_balancing_desc": "Einen Server nach dem anderen abfragen. AdGuard Home verwendet den gewichteten Zufallsalgorithmus, um den Server so auszuwählen, dass der schnellste Server häufiger verwendet wird.",
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
"bootstrap_dns": "Bootstrap-DNS-Server",
"bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.",
"fallback_dns_title": "Fallback-DNS-Server",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard Webservice für Kindersicherung verwenden",
"use_adguard_parental_hint": "AdGuard Home wird prüfen, ob die Domain jugendgefährdende Inhalte enthält. Zum Schutz Ihrer Privatsphäre wird die selbe API wie für den Webservice für Internetsicherheit verwendet.",
"enforce_safe_search": "Sichere Suche verwenden",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Yandex und Pixabay.",
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex und Pixabay.",
"no_servers_specified": "Keine Server festgelegt",
"general_settings": "Allgemeine Einstellungen",
"dns_settings": "DNS-Einstellungen",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
"parallel_requests": "Parallel requests",
"load_balancing": "Load-balancing",
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.",
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
"bootstrap_dns": "Bootstrap DNS servers",
"bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.",
"fallback_dns_title": "Fallback DNS servers",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Use AdGuard parental control web service",
"use_adguard_parental_hint": "AdGuard Home will check if domain contains adult materials. It uses the same privacy-friendly API as the browsing security web service.",
"enforce_safe_search": "Use Safe Search",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "No servers specified",
"general_settings": "General settings",
"dns_settings": "DNS settings",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"parallel_requests": "Consultas paralelas",
"load_balancing": "Balanceo de carga",
"load_balancing_desc": "Consulta un servidor DNS de subida a la vez. AdGuard Home utiliza su algoritmo aleatorio ponderado para elegir el servidor más rápido y sea utilizado con más frecuencia.",
"load_balancing_desc": "Consulta un servidor upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar el control parental de AdGuard",
"use_adguard_parental_hint": "AdGuard Home comprobará si el dominio contiene materiales para adultos. Utiliza la misma API amigable con la privacidad del servicio web de seguridad de navegación.",
"enforce_safe_search": "Usar búsqueda segura",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Yandex y Pixabay.",
"enforce_save_search_hint": "AdGuard Home reforzará la búsqueda segura en los siguientes motores de búsqueda: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex y Pixabay.",
"no_servers_specified": "No hay servidores especificados",
"general_settings": "Configuración general",
"dns_settings": "Configuración del DNS",

View File

@@ -589,6 +589,7 @@
"cache_optimistic_desc": "AdGuard Home را وادار می کند که از سمت حافظه پنهان پاسخ دهد حتی وقتی که موارد وارد شده منقضی شده باشد و همچنین سعی بر تازه کردن آنها می کند.",
"filter_category_general": "General",
"filter_category_security": "مسدودسازی بدافزار و فیشینگ",
"filter_category_regional": "منطقه‌ای",
"filter_category_other": "ساير",
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
"parental_control": "نظارت والدین",

View File

@@ -6,21 +6,21 @@
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
"parallel_requests": "Rinnakkaiset pyynnöt",
"load_balancing": "Kuormantasaus",
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
"load_balancing_desc": "Lähetä kysely kerrallaan yhdelle ylävirtapalvelimelle. AdGuard Home valitsee painotetun satunnaisalgoritmin avulla palvelimet, joilla on vähiten epäonnistuneita hakuja ja keskimääräisesti lyhin hakuaika.",
"bootstrap_dns": "Bootstrap DNS-palvelimet",
"bootstrap_dns_desc": "Ylävirroiksi määrittämiesi DoH/DoT-resolverien IP-osoitteiden selvitykseen käytettävien DNS-palvelimien IP-osoitteet. Kommentteja ei sallita.",
"fallback_dns_title": "DNS-varapalvelimet",
"fallback_dns_desc": "Listaus DNS-varapalvelimista, joita käytetään kun lähtevät DNS-palvelimet eivät vastaa. Syntaksi on sama kuin yllä olevassa pääylävirrat-kentässä.",
"fallback_dns_placeholder": "Syötä yksi DNS-varapalvelin per rivi",
"local_ptr_title": "Yksityiset käänteis-DNS-palvelimet",
"local_ptr_desc": "DNS-palvelimet, joita AdGuard Home käyttää paikallisille PTR-pyynnöille. Näitä palvelimia käytetään yksityistä IP-osoitetta käyttävien PTR-pyyntöjen osoitteiden, kuten \"192.168.12.34\", selvitykseen käänteis-DNS:n avulla. Jos ei käytössä, AdGuard Home käyttää käyttöjärjestelmän oletusarvoisia DNS-resolvereita, poislukien AdGuard Homen omat osoitteet.",
"local_ptr_desc": "AdGuard Homen yksityisille PTR-, SOA- ja NS-pyynnöille käyttämät DNS-palvelimet. Pyyntöä luokitellaan yksityiseksi, jos se pyytää yksityistä IP-aluetta (kuten \"192.168.12.34\") käyttävän aliverkon sisältävää ARPA-verkkotunnusta ja on lähtöisin päätteeltä, jolla on yksityinen IP-osoite. Jos tätä ei ole määritetty, käytetään käyttöjärjestelmän oletusarvoisia DNS-resolvereita (AdGuard Homen IP-osoitteet pois lukien).",
"local_ptr_default_resolver": "Oletusarvoisesti AdGuard Home käyttää seuraavia käänteis-DNS-resolvereita: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home ei voinut määrittää tälle järjestelmälle sopivaa yksityistä käänteis-DNS-resolveria.",
"local_ptr_placeholder": "Syötä yksi IP-osoite per rivi",
"resolve_clients_title": "Käytä päätelaitteiden IP-osoitteille käänteistä selvitystä",
"resolve_clients_desc": "Selvitä päätelaitteiden IP-osoitteiden isäntänimet käänteisesti lähettämällä PTR-pyynnöt sopiville resolvereille (yksityiset DNS-palvelimet paikallisille päätelaitteille, ylävirtapalvelimet päätelaitteille, joilla on julkiset IP-osoitteet).",
"use_private_ptr_resolvers_title": "Käytä yksityisiä käänteis-DNS-resolvereita",
"use_private_ptr_resolvers_desc": "Suorita käänteis-DNS-selvitykset paikallisesti tarjotuille osoitteille käyttäen näitä ylävirtapalvelimia. Jos ei käytössä, vastaa AdGuard Home kaikkiin sen tyyppisiin PTR-pyyntöihin NXDOMAIN-arvolla, pois lukien DHCP, /etc/hosts, yms. -tiedoista tunnistettut päätelaitteet.",
"use_private_ptr_resolvers_desc": "Selvitä yksityisiä IP-osoitteita sisältävien ARPA-verkkotunnusten PTR-, SOA- ja NS-pyynnöt käyttäen yksityisiä ylävirtapalvelimia, DHCP:tä, /etc/hosts-määrityksiä, yms. Jos tämä ei ole käytössä, AdGuard Home vastaa tällaisiin pyyntöihin NXDOMAIN-tiedolla.",
"check_dhcp_servers": "Etsi DHCP-palvelimia",
"save_config": "Tallenna asetukset",
"enabled_dhcp": "DHCP-palvelin otettiin käyttöön",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Käytä AdGuardin lapsilukko-palvelua",
"use_adguard_parental_hint": "AdGuard Home tarkistaa, sisältääkö verkkotunnus aikuisille tarkoitettua sisältöä. Se käyttää samaa tietosuojapainotteista rajapintaa, kuin turvallisen selauksen palvelu.",
"enforce_safe_search": "Käytä turvallista hakua",
"enforce_save_search_hint": "AdGuard Home voi pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex ja Pixabay.",
"no_servers_specified": "Palvelimia ei ole määritetty",
"general_settings": "Yleiset asetukset",
"dns_settings": "DNS-asetukset",
@@ -326,7 +326,7 @@
"blocking_ipv6_desc": "Estettyyn AAAA-pyyntöön palautettava IP-osoite",
"blocking_mode_default": "Oletus: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA) kun estetään mainoseston säännöllä; vastaa säännön määrittämällä IP-osoitteella kun estetään /etc/hosts-tyyppisellä säännöllä",
"blocking_mode_refused": "REFUSED: Vastaa REFUSED-koodilla",
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-koodilla",
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-tiedolla",
"blocking_mode_null_ip": "Tyhjä IP: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA)",
"blocking_mode_custom_ip": "Mukautettu IP: Vastaa manuaalisesti määritetyllä IP-osoitteella",
"theme_auto": "Automaattinen",
@@ -501,8 +501,8 @@
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> tukee <1>DNS-over-HTTPS</1>, mutta oman palvelimen käyttö' varten sille on luotava <2>DNS Stamp</2> -merkintä.",
"setup_dns_privacy_ios_2": "<0>AdGuard iOS:lle</0> tukee <1>DNS-over-HTTPS</1> ja <1>DNS-over-TLS</1> -toteutuksia.",
"setup_dns_privacy_other_title": "Muita toteutuksia",
"setup_dns_privacy_other_1": "AdGuard Home voi itse olla turvallinen DNS-päätelaite millä tahansa alustalla.",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> tukee kaikkia tunnettuja turvallisia DNS-protokollia.",
"setup_dns_privacy_other_1": "AdGuard Home voi itse olla suojattu DNS -pääte millä tahansa alustalla.",
"setup_dns_privacy_other_2": "<0>dnsproxy</0> tukee kaikkia tunnettuja suojattuja DNS-protokollia.",
"setup_dns_privacy_other_3": "<0>dnscrypt-proxy</0> tukee <1>DNS-over-HTTPS</1> -protokollaa.",
"setup_dns_privacy_other_4": "<0>Mozilla Firefox</0> tukee <1>DNS-over-HTTPS</1>-toteutusta.",
"setup_dns_privacy_other_5": "Löydät lisää toteutuksia <0>täältä</0> ja <1>täältä</1>.",
@@ -542,7 +542,7 @@
"stats_params": "Tilastoinnin määritys",
"config_successfully_saved": "Asetukset tallennettiin",
"interval_6_hour": "6 tuntia",
"interval_24_hour": "24 tuntia",
"interval_24_hour": "24 tunnilta",
"interval_days": "{{count}} päivä",
"interval_days_plural": "{{count}} päivää",
"domain": "Verkkotunnus",
@@ -709,9 +709,9 @@
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
"schedule_services": "Keskeytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen keskeytysajoitus tälle päätteelle.",
"schedule_services": "Pysäytä palveluesto",
"schedule_services_desc": "Määritä palvelunestosuodattimen pysäytysajoitus.",
"schedule_services_desc_client": "Määritä palvelunestosuodattimen pysäytysajoitus tälle päätteelle.",
"schedule_desc": "Aseta estettujen palveluiden käyttämättömyysjaksot",
"schedule_invalid_select": "Aloitusaika on oltava ennen lopetusaikaa",
"schedule_select_days": "Valitse päivät",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
"parallel_requests": "Requêtes en parallèle",
"load_balancing": "Équilibrage de charge",
"load_balancing_desc": "Interroger un serveur en amont à la fois. AdGuard Home utilise son algorithme aléatoire pondéré pour choisir le serveur de sorte que le serveur le plus rapide soit utilisé plus souvent.",
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
"bootstrap_dns": "Serveurs DNS d'amorçage",
"bootstrap_dns_desc": "Les adresses IP des serveurs DNS utilisées pour résoudre les adresses IP des résolveurs DoH/DoT que vous spécifiez comme en amont. Les commentaires ne sont pas autorisés.",
"fallback_dns_title": "Serveurs DNS de repli",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utiliser le contrôle parental d'AdGuard",
"use_adguard_parental_hint": "AdGuard Home va vérifier s'il y a du contenu pour adultes sur le domaine. Ce sera fait par aide du même API discret que celui utilisé par le service de Sécurité de navigation.",
"enforce_safe_search": "Utiliser la Recherche Sécurisée",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Pas de serveurs spécifiés",
"general_settings": "Paramètres généraux",
"dns_settings": "Paramètres DNS",
@@ -676,7 +676,7 @@
"filter_allowlist": "ATTENTION : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
"last_rule_in_allowlist": "Impossible dinterdire ce client, car lexclusion de la règle « {{disallowed_rule}} » DÉSACTIVERA la liste des « clients autorisés ».",
"use_saved_key": "Utiliser la clef précédemment enregistrée",
"parental_control": "Contrôle parental",
"parental_control": "Contrôle Parental",
"safe_browsing": "Navigation sécurisée",
"served_from_cache_label": "Servi depuis le cache",
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}}  caractères",

View File

@@ -13,14 +13,14 @@
"fallback_dns_desc": "Popis rezervnih DNS poslužitelja koji se koriste kada uzvodni DNS poslužitelji ne odgovaraju. Sintaksa je ista kao u gornjem polju glavnog uzvodnog toka.",
"fallback_dns_placeholder": "Unesite jedan rezervni DNS poslužitelj po retku",
"local_ptr_title": "Privatni obrnuti DNS poslužitelji",
"local_ptr_desc": "DNS poslužitelji koje AdGuard Home koristi za lokalne PTR upite. Ti se poslužitelji koriste za razrješavanje naziva glavnog računala klijenata s privatnim IP adresama, na primjer \"192.168.12.34\", koristeći obrnuti DNS. Ako nije postavljeno, AdGuard Home koristi adrese zadanih DNS razrješivača vašeg OS-a, osim za adrese samog AdGuard Homea.",
"local_ptr_desc": "DNS poslužitelji koje koristi AdGuard Home za privatne PTR, SOA i NS zahtjeve. Zahtjev se smatra privatnim ako traži ARPA domenu koja sadrži podmrežu unutar privatnih IP raspona (kao što je \"192.168.12.34\") i dolazi od klijenta s privatnom IP adresom. Ako nije postavljeno, koristit će se zadani DNS rezolveri vašeg OS-a, osim za AdGuard Home IP adrese.",
"local_ptr_default_resolver": "Prema zadanim postavkama AdGuard Home koristi sljedeće obrnute DNS razrješivače: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nije mogao odrediti prikladne privatne obrnute DNS razrješivače za ovaj sustav.",
"local_ptr_placeholder": "Unesite jednu adresu poslužitelja po retku",
"resolve_clients_title": "Omogući obrnuto rješavanje IP adresa klijenata",
"resolve_clients_desc": "Obrnuto razriješite IP adrese klijenata u nazive glavnih računala slanjem PTR upita odgovarajućim razrješivačima (privatni DNS poslužitelji za lokalne klijente, uzvodni poslužitelji za klijente s javnim IP adresama).",
"use_private_ptr_resolvers_title": "Koristi privatne reverzne DNS razrješivače",
"use_private_ptr_resolvers_desc": "Izvršite obrnuta DNS traženja za lokalno poslužene adrese pomoću ovih uzlaznih poslužitelja. Ako je onemogućen, AdGuard Home odgovara S NXDOMAIN-om na sve takve PTR zahtjeve osim za klijente poznate iz DHCP-a, /etc/hosts i tako dalje.",
"use_private_ptr_resolvers_desc": "Razriješi PTR, SOA i NS zahtjeve za ARPA domene koje sadrže privatne IP adrese putem privatnih uzvodnih poslužitelja, DHCP-a, /etc/hostova itd. Ako je onemogućeno, AdGuard Home će na sve takve zahtjeve odgovoriti s NXDOMAIN.",
"check_dhcp_servers": "Provjera DHCP poslužitelja",
"save_config": "Spremi konfiguraciju",
"enabled_dhcp": "DHCP poslužitelj je omogućen",
@@ -425,6 +425,9 @@
"encryption_hostnames": "Nazivi računala",
"encryption_reset": "Jeste li sigurni da želite poništiti postavke šifriranja?",
"encryption_warning": "Upozorenje",
"encryption_plain_dns_enable": "Omogući obični DNS",
"encryption_plain_dns_desc": "Obični DNS je omogućen prema zadanim postavkama. Možete ga onemogućiti kako biste prisilili sve uređaje da koriste šifrirani DNS. Da biste to učinili, morate omogućiti barem jedan kriptirani DNS protokol",
"encryption_plain_dns_error": "Da biste onemogućili obični DNS, omogućite barem jedan kriptirani DNS protokol",
"topline_expiring_certificate": "Vaš SSL certifikat uskoro ističe. Ažurirajte <0>Postavke šifriranja</0>.",
"topline_expired_certificate": "Vaš SSL certifikat je istekao. Ažurirajte <0>Postavke šifriranja</0>.",
"form_error_port_range": "Unesite broj porta od 80 do 65536",
@@ -675,7 +678,7 @@
"use_saved_key": "Korištenje prethodno spremljenog ključa",
"parental_control": "Roditeljska zaštita",
"safe_browsing": "Sigurno surfanje",
"served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>",
"served_from_cache_label": "Posluženo iz predmemorije",
"form_error_password_length": "Lozinka mora sadržavati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Napomena:</0>IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama</1>.",
"confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?",

View File

@@ -147,7 +147,7 @@
"average_upstream_response_time": "Rata-rata waktu respons hulu",
"response_time": "Waktu respons",
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan berkas host",
"filters_block_toggle_hint": "Anda dapat menyiapkan aturan pemblokiran dalam pengaturan <a>Filter</a>.",
"use_adguard_browsing_sec": "Gunakan layanan web Keamanan Penjelajahan AdGuard",
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
@@ -191,7 +191,7 @@
"edit_table_action": "Ubah",
"delete_table_action": "Hapus",
"elapsed": "Berlalu",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak berkas host.",
"no_blocklist_added": "Tidak ada daftar hitam yang ditambahkan",
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
"add_blocklist": "Tambahkan daftar hitam",
@@ -211,8 +211,8 @@
"form_error_url_format": "Format URL tidak valid",
"form_error_url_or_path_format": "URL atau jalur absolut dari daftar tidak valid",
"custom_filter_rules": "Aturan penyaringan khusus",
"custom_filter_rules_hint": "Masukkan satu aturan dalam sebuah baris. Anda dapat menggunakan baik aturan adblock maupun sintaks file hosts.",
"system_host_files": "File host sistem",
"custom_filter_rules_hint": "Masukkan satu aturan pada satu baris. Anda dapat menggunakan aturan adblock atau sintaks berkas host.",
"system_host_files": "Berkas host sistem",
"examples_title": "Contoh",
"example_meaning_filter_block": "blokir akses ke example.org dan seluruh subdomainnya;",
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.orf dan seluruh subdomainnya;",
@@ -476,7 +476,7 @@
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
"auto_clients_title": "Klien (waktu berjalan)",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk file host, reverse DNS, dll.",
"auto_clients_desc": "Informasi tentang alamat IP perangkat yang menggunakan atau mungkin menggunakan AdGuard Home. Informasi ini dikumpulkan dari beberapa sumber, termasuk berkas host, DNS terbalik, dll.",
"access_title": "Pengaturan akses",
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
"access_allowed_title": "Klien yang diizinkan",
@@ -494,7 +494,7 @@
"setup_dns_privacy_1": "<0>DNS melalui TLS:</0> Gunakan <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-TLS:</0> Memakai <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Berikut daftar perangkat lunak yang dapat Anda gunakan.</0>",
"setup_dns_privacy_4": "Di perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh file '.mobileconfig' khusus yang menambahkan server <highlight>DNS-over-HTTPS</highlight> atau <highlight>DNS-over-TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_4": "Pada perangkat iOS 14 atau macOS Big Sur, Anda dapat mengunduh berkas khusus '.mobileconfig' yang menambahkan server <highlight>DNS melalui HTTPS</highlight> atau <highlight>DNS melalui TLS</highlight> ke pengaturan DNS.",
"setup_dns_privacy_android_1": "Android 9 mendukung DNS-over-TLS secara asli. Untuk mengkonfigurasinya, buka Pengaturan → Jaringan & internet → Tingkat Lanjut → DNS Pribadi dan masukkan nama domain Anda di sana.",
"setup_dns_privacy_android_2": "<0>AdGuard untuk Android</0> mendukung <1>DNS-over-HTTPS</1> dan <1>DNS-over-TLS</1>.",
"setup_dns_privacy_android_3": "<0>Intra</0> menambahkan dukungan <1>DNS-over-HTTPS</1> untuk Android.",
@@ -517,7 +517,7 @@
"rewrite_confirm_delete": "Apakah anda yakin ingin menghapus DNS rewrite untuk \"{{key}}\"?",
"rewrite_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
"rewrite_applied": "Aturan Rewrite yang diterapkan",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan file hosts",
"rewrite_hosts_applied": "Ditulis ulang oleh aturan berkas host",
"dns_rewrites": "DNS rewrite",
"form_domain": "Masukkan nama domain",
"form_answer": "Masaukan alamat IP atau nama domain",
@@ -676,7 +676,7 @@
"filter_allowlist": "PERINGATAN: Tindakan ini juga akan mengecualikan aturan \"{{disallowed_rule}}\" dari daftar klien yang diizinkan.",
"last_rule_in_allowlist": "Tidak dapat melarang klien ini karena mengecualikan aturan \"{{disallowed_rule}}\" akan MENONAKTIFKAN daftar \"Klien yang diizinkan\".",
"use_saved_key": "Gunakan kunci yang disimpan sebelumnya",
"parental_control": "Kontrol Orang Tua",
"parental_control": "Pengawasan Orang Tua",
"safe_browsing": "Penjelajahan Aman",
"served_from_cache": "{{value}} <i>(disajikan dari cache)</i>",
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
"parallel_requests": "Richieste parallele",
"load_balancing": "Bilanciamento del carico",
"load_balancing_desc": "Interroga un server upstream per volta. AdGuard Home utilizzerà un algoritmo casuale ponderato per la selezione del server, in maniera tale da scegliere spesso il più veloce.",
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Indirizzi IP dei server DNS utilizzati per risolvere gli indirizzi IP dei resolver DoH/DoT specificati come upstream. I commenti non sono ammessi.",
"fallback_dns_title": "Server DNS di fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Utilizza il Controllo Parentale di AdGuard",
"use_adguard_parental_hint": "AdGuard Home verificherà se il dominio contiene materiale per adulti. Utilizza le stesse API privacy-friendly del servizio web 'sicurezza di navigazione'.",
"enforce_safe_search": "Utilizza Ricerca Sicura",
"enforce_save_search_hint": "AdGuard Home forzerà la ricerca sicura sui seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home applicherà la ricerca sicura nei seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nessun server specificato",
"general_settings": "Impostazioni generali",
"dns_settings": "Impostazioni DNS",

View File

@@ -6,21 +6,21 @@
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
"parallel_requests": "並列リクエスト",
"load_balancing": "ロードバランシング",
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズムweighted random algorithmを使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
"bootstrap_dns": "ブートストラップDNSサーバ",
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。コメントは許可されていません",
"fallback_dns_title": "フォールバックDNSサーバー",
"fallback_dns_desc": "アップストリームDNSサーバーが応答しない場合に使用されるフォールバックDNSサーバーのリストです。構文は上記のmain upstreamsフィールドと同じです。",
"fallback_dns_placeholder": "フォールバックDNSサーバーを1行に1つずつ入力してください。",
"local_ptr_title": "プライベートリバースDNSサーバー",
"local_ptr_desc": "AdGuard HomeがローカルPTRクエリに使用するDNSサーバーです。これらのサーバーは、rDNSを使ってプライベートIPアドレス(例えば\"192.168.12.34\")を持つクライアントのホスト名を解決するために使用されます。設定されていない場合、AdGuard HomeはOSのデフォルトDNSリゾルバーのアドレスAdGuard Home自体のアドレスを除く)を自動的に使用します。",
"local_ptr_desc": "AdGuard Home がプライベート PTR、SOA、および NS リクエストに使用する DNS サーバープライベート IP 範囲内のサブネット (「192.168.12.34」など) を含む ARPA ドメインを要求し、プライベート IP アドレスを持つクライアントから来たリクエストが、プライベートリクエストとみなされます。設定が特に指定されていない場合、OS のデフォルト DNS リゾルバAdGuard Home の IP アドレスを除く)が使用されます。",
"local_ptr_default_resolver": "デフォルトでは、AdGuard Homeは次のリバースDNSリゾルバを使用します: {{ip}}",
"local_ptr_no_default_resolver": "AdGuard Homeは、このシステムに適したプライベートリバースDNSリゾルバを特定できませんでした。",
"local_ptr_placeholder": "IPアドレスを1行に1つずづ入力してください。",
"resolve_clients_title": "クライアントのIPアドレスの逆解決を有効にする",
"resolve_clients_desc": "対応するリゾルバーローカルクライアントの場合はプライベートDNSサーバ、パブリックIPを持つクライアントの場合はアップストリームサーバーにPTRクエリを送信することにより、クライアントのIPアドレスをホストネームに逆解決します。",
"use_private_ptr_resolvers_title": "プライベートリバースDNSリゾルバを使用",
"use_private_ptr_resolvers_desc": "これらのアップストリームサーバーを使用して、ローカルで提供されるアドレスのリバースDNSルックアップを実行します。無効にすると、AdGuard Homeは、DHCP, /etc/hosts などから認識されるクライアントを除き、すべてのこのようなPTR要求にNXDOMAINで応答します。",
"use_private_ptr_resolvers_desc": "プライベートアップストリームサーバー、DHCP、/etc/hosts などを通じて、プライベート IP アドレスを含む ARPA ドメインの PTR、SOA、および NS リクエストを解決します。無効にした場合、AdGuard Home はこのようなリクエストのすべてに NXDOMAIN で応答します。",
"check_dhcp_servers": "DHCPサーバをチェックする",
"save_config": "構成を保存する",
"enabled_dhcp": "DHCPサーバを有効にしました",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
"enforce_safe_search": "セーフサーチを使用する",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay",
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay",
"no_servers_specified": "サーバが指定されていません",
"general_settings": "一般設定",
"dns_settings": "DNS設定",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
"parallel_requests": "병렬 처리 요청",
"load_balancing": "로드 밸런싱",
"load_balancing_desc": "한 번에 하나의 서버씩 질의합니다. AdGuard Home은 가중 랜덤 알고리즘 사용해서 가장 빠른 서버가 자주 사용되도록 서버를 선택합니다.",
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다. AdGuard Home은 가중 무작위 알고리즘 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
"bootstrap_dns": "부트스트랩 DNS 서버",
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
"fallback_dns_title": "폴백 DNS 서버",
@@ -108,7 +108,7 @@
"off": "OFF",
"copyright": "Copyright",
"homepage": "홈페이지",
"report_an_issue": "문제를 보고합니다",
"report_an_issue": "문제 신고",
"privacy_policy": "개인정보취급방침",
"enable_protection": "보호 활성화",
"enabled_protection": "보호 활성화됨",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
"enforce_safe_search": "세이프서치 사용",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
"no_servers_specified": "지정된 서버 없음",
"general_settings": "일반 설정",
"dns_settings": "DNS 설정",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
"parallel_requests": "Parallelle verzoeken",
"load_balancing": "Volume balanceren",
"load_balancing_desc": "Eén server per keer bevragen. AdGuard Home gebruikt hiervoor een gewogen willekeurig algoritme om de server te kiezen zodat de snelste server meer zal gebruikt worden.",
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
"bootstrap_dns": "Bootstrap DNS-servers",
"bootstrap_dns_desc": "IP-adressen van DNS-servers die worden gebruikt om IP-adressen om te zetten van de DoH/DoT-resolvers die je opgeeft als upstreams. Opmerkingen zijn niet toegestaan.",
"fallback_dns_title": "Back-up DNS-servers",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Gebruik AdGuard Ouderlijk toezicht web service",
"use_adguard_parental_hint": "AdGuard Home controleert of het domein 18+ content bevat. Dit gebeurt dmv dezelfde privacy vriendelijke API als de Browsing Security web service.",
"enforce_safe_search": "Veilig zoeken gebruiken",
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "AdGuard Home dwingt veilig zoeken af in de volgende zoekmachines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Geen servers gespecificeerd",
"general_settings": "Algemene instellingen",
"dns_settings": "DNS instellingen",
@@ -495,7 +495,7 @@
"setup_dns_privacy_2": "<0>DNS-via-HTTPS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Hou er rekening mee dat het beveiligde DNS protocol alleen beschikbaar is voor Android 9. U moet dus extra software installeren voor andere besturingssystemen.</0><0>Hier is een lijst van te gebruiken software.</0>",
"setup_dns_privacy_4": "Op een iOS 14 of macOS Big Sur apparaat kan je een speciaal '.mobileconfig'-bestand downloaden dat <highlight>DNS-via-HTTPS</highlight> of <highlight>DNS-via-TLS</highlight> servers aan de DNS-instellingen toevoegt.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé-DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_2": "<0>AdGuard voor Android</0>ondersteunt<1>DNS-via-HTTPS </1>en<1>DNS-via-TLS</1>.",
"setup_dns_privacy_android_3": "<0> Intra </0> voegt <1> DNS-via-HTTPS</1> ondersteuning toe aan Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> ondersteunt <1> DNS-via-HTTPS </1>, maar om het te configureren op jouw eigen server moet er een <2> DNS-stempel </2> gegenereerd worden.",

View File

@@ -128,6 +128,7 @@
"enforced_save_search": "Påtvungede barnevennlige søk",
"number_of_dns_query_to_safe_search": "Antall DNS-forespørsler til søkemotorer der \"Safe Search\" ble fremtvunget",
"average_processing_time": "Gjennomsnittlig behandlingstid",
"response_time": "Responstid",
"average_processing_time_hint": "Gjennomsnittstid for behandling av DNS-forespørsler i millisekunder",
"block_domain_use_filters_and_hosts": "Blokker domener ved hjelp av filtre, «hosts»-filer, og rå domener",
"filters_block_toggle_hint": "Du kan sette opp blokkeringsoppføringer i <a>Filtre</a>-innstillingene.",

View File

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

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
"bootstrap_dns": "Servidores DNS de inicialização",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS Fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controle parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Ele usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Configurações gerais",
"dns_settings": "Configurações de DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS de fallback",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Usar o serviço de controlo parental do AdGuard",
"use_adguard_parental_hint": "O AdGuard Home irá verificar se o domínio contém conteúdo adulto. Usa a mesma API amigável de privacidade que o serviço de segurança da navegação.",
"enforce_safe_search": "Usar pesquisa segura",
"enforce_save_search_hint": "O AdGuard Home forçará a pesquisa segura nos seguintes motores de busca: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"enforce_save_search_hint": "O AdGuard Home aplicará pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Nenhum servidor especificado",
"general_settings": "Definições gerais",
"dns_settings": "Definições de DNS",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
"parallel_requests": "Параллельные запросы",
"load_balancing": "Распределение нагрузки\n",
"load_balancing_desc": "Запрашивать по одному серверу за раз. AdGuard Home использует алгоритм взвешенного случайного выбора сервера, так что самый быстрый сервер используется чаще.",
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
"fallback_dns_title": "Резервные DNS-серверы",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
"enforce_safe_search": "Включить безопасный поиск",
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Yandex и Pixabay.",
"enforce_save_search_hint": "AdGuard Home будет обеспечивать безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Нет указанных серверов",
"general_settings": "Основные настройки",
"dns_settings": "Настройки DNS",

View File

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

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
"parallel_requests": "Paralelné dopyty",
"load_balancing": "Vyrovnávanie záťaže",
"load_balancing_desc": "Dopytovať len jeden server v danom čase. AdGuard Home použije na výber servera vážený náhodný algoritmus, aby sa najrýchlejší server používal častejšie.",
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy serverov DNS používaných na rozlíšenie IP adries prekladačov DoH/DoT, ktoré zadáte ako upstream. Komentáre nie sú povolené.",
"fallback_dns_title": "Záložné servery DNS",
@@ -89,7 +89,7 @@
"form_enter_hostname": "Zadajte meno hostiteľa",
"error_details": "Podrobnosti chyby",
"response_details": "Podrobnosti odpovede",
"request_details": "Podrobnosti požiadavky",
"request_details": "Podrobnosti dopytu",
"client_details": "Podrobnosti klienta",
"details": "Podrobnosti",
"back": "Naspäť",
@@ -154,7 +154,7 @@
"use_adguard_parental": "Použiť AdGuard službu Rodičovská kontrola",
"use_adguard_parental_hint": "AdGuard Home skontroluje, či doména obsahuje materiály pre dospelých. Používa rovnaké API priateľské k ochrane osobných údajov ako služba Bezpečného prehliadania.",
"enforce_safe_search": "Používať bezpečné vyhľadávanie",
"enforce_save_search_hint": "AdGuard Home vynucuje bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Yandex a Pixabay.",
"enforce_save_search_hint": "AdGuard Home vynúti bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Neboli špecifikované žiadne servery",
"general_settings": "Všeobecné nastavenia",
"dns_settings": "Nastavenia DNS",
@@ -308,7 +308,7 @@
"form_enter_rate_limit": "Zadajte rýchlostný limit",
"rate_limit": "Rýchlostný limit",
"edns_enable": "Povoliť klientsku podsiete EDNS",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream požiadaviek a zapíše hodnoty odoslané klientmi do denníka dopytov.",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream dopytov a zapíše hodnoty odoslané klientami do denníka dopytov.",
"edns_use_custom_ip": "Použiť vlastnú IP adresu pre EDNS",
"edns_use_custom_ip_desc": "Povoliť používanie vlastnej IP adresy pre EDNS",
"rate_limit_desc": "Počet požiadaviek za sekundu, ktoré môže jeden klient vykonať. Nastavenie na hodnotu 0 znamená neobmedzene.",
@@ -480,11 +480,11 @@
"access_title": "Nastavenia prístupu",
"access_desc": "Tu môžete konfigurovať pravidlá prístupu pre server DNS AdGuard Home.",
"access_allowed_title": "Povolení klienti",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať požiadavky iba od týchto klientov.",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať dopyty iba od týchto klientov.",
"access_disallowed_title": "Nepovolení klienti",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší požiadavky od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší dopyty od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_blocked_title": "Nepovolené domény",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrácie URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_settings_saved": "Nastavenia prístupu úspešne uložené",
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
"updates_version_equal": "AdGuard Home je aktuálny",

View File

@@ -9,7 +9,7 @@
"load_balancing_desc": "Fråga en uppströmsserver åt gången. AdGuard Home använder sin viktade slumpmässiga algoritm för att välja server så att den snabbaste servern används oftare.",
"bootstrap_dns": "Bootstrap-DNS-servrar",
"bootstrap_dns_desc": "IP-adresser för DNS-servrar som används för att lösa IP-adresser för de DoH/DoT-resolvers som du anger som uppströms. Kommentarer är inte tillåtna.",
"fallback_dns_title": "Reserv-DNS-servrar",
"fallback_dns_title": "Reserv DNS-servrar",
"fallback_dns_desc": "Lista över reserv-DNS-servrar som används när uppströms DNS-servrar inte svarar. Syntaxen är densamma som i huvuduppströmsfältet ovan.",
"fallback_dns_placeholder": "Ange en reserv-DNS-server per rad",
"local_ptr_title": "Privata omvända DNS-servrar",
@@ -141,8 +141,8 @@
"number_of_dns_query_blocked_24_hours": "Antalet DNS-förfrågningar som blockerades av annonsfilter och värdens blockeringsklistor",
"number_of_dns_query_blocked_24_hours_by_sec": "Antalet DNS-förfrågningar som blockerades av AdGuards modul för surfsäkerhet",
"number_of_dns_query_blocked_24_hours_adult": "Antalet vuxensajter som blockerats",
"enforced_save_search": "Aktivering av Säker surf",
"number_of_dns_query_to_safe_search": "Antalet DNS-förfrågningar mot sökmotorer där Säker surf tvingats",
"enforced_save_search": "Genomdrev SafeSearch",
"number_of_dns_query_to_safe_search": "Antalet DNS-förfrågningar till sökmotorer för vilka SafeSearch genomdrevs",
"average_processing_time": "Genomsnittlig processtid",
"average_upstream_response_time": "Genomsnittlig svarstid uppströmsserver",
"response_time": "Svarstid",
@@ -153,7 +153,7 @@
"use_adguard_browsing_sec_hint": "AdGuard Home kommer att kontrollera om en domän är blockerad av webbservicen surfsäkerhet. Med en integritetsvänlig metod görs en API-lookup för att kontrollera: endast ett kort prefix i domännamnet SHA256 hash skickas till servern.",
"use_adguard_parental": "Använda AdGuards webbservice för föräldrakontroll",
"use_adguard_parental_hint": "AdGuard Home kommer att kontrollera domäner för innehåll av vuxenmaterial . Samma integritetsvänliga metod för API-lookup som tillämpas i webbservicens surfsäkerhet används.",
"enforce_safe_search": "Använd säker webbsökning",
"enforce_safe_search": "Använd SafeSearch",
"enforce_save_search_hint": "AdGuard Home kommer tvinga säker surf på följande sökmotorer: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
"no_servers_specified": "Inga servrar angivna",
"general_settings": "Allmänna inställningar",
@@ -461,7 +461,7 @@
"form_enter_mac": "Skriv in MAC",
"form_enter_id": "Ange identifierare",
"form_add_id": "Lägg till identifierare",
"form_client_name": "Skriv in klientnamn",
"form_client_name": "Ange klientnamn",
"name": "Namn",
"client_name": "Klient {{id}}",
"client_global_settings": "Använda globala inställningar",
@@ -657,7 +657,7 @@
"cache_optimistic": "Optimistisk cachning",
"cache_optimistic_desc": "Få AdGuard Home att svara från cachen även när posterna har gått ut och försök även uppdatera dem.",
"filter_category_general": "Allmänt",
"filter_category_security": "säkerhet",
"filter_category_security": "Säkerhet",
"filter_category_regional": "Regional",
"filter_category_other": "Övrigt",
"filter_category_general_desc": "Listor som blockerar spårning och reklam på de flesta enheterna",
@@ -674,7 +674,6 @@
"use_saved_key": "Använd den tidigare sparade nyckeln",
"parental_control": "Föräldrakontroll",
"safe_browsing": "Säker surfning",
"served_from_cache": "{{value}} <i>(levereras från cache)</i>",
"form_error_password_length": "Lösenordet måste vara {{min}} till {{max}} tecken långt",
"anonymizer_notification": "<0>Observera:</0> IP-anonymisering är aktiverad. Du kan inaktivera den i <1>Allmänna inställningar</1>.",
"confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?",

View File

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

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"parallel_requests": "Paralel istekler",
"load_balancing": "Yük dengeleme",
"load_balancing_desc": "Her seferde bir üst sunucuyu sorgulayın. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
"load_balancing_desc": "Aynı anda bir üst kaynak sunucusunu sorgulayın. AdGuard Home, en düşük başarısız arama sayısına ve en düşük ortalama arama süresine sahip sunucuları seçmek için ağırlıklı rastgele bir algoritma kullanır.",
"bootstrap_dns": "DNS Önyükleme sunucuları",
"bootstrap_dns_desc": "Üst kaynak olarak belirttiğiniz DoH/DoT çözümleyicilerin IP adreslerini çözümlemek için kullanılan DNS sunucularının IP adresleri. Yorumlara izin verilmez.",
"fallback_dns_title": "Yedek DNS sunucuları",
@@ -68,7 +68,7 @@
"ip": "IP",
"dhcp_table_hostname": "Ana makine Adı",
"dhcp_table_expires": "Bitiş tarihi",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların İnternet bağlantısı kesilebilir!",
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
"dhcp_error": "AdGuard Home, ağda başka bir etkin DHCP sunucusu olup olmadığını belirleyemedi",
"dhcp_static_ip_error": "DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. AdGuard Home, bu ağ arayüzünün sabit bir IP adresi kullanılarak yapılandırılıp yapılandırılmadığını belirleyemedi. Lütfen sabit IP adresini elle ayarlayın.",
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
@@ -154,7 +154,7 @@
"use_adguard_parental": "AdGuard ebeveyn denetimi web hizmetini kullan",
"use_adguard_parental_hint": "AdGuard Home, alan adının yetişkin içerik bulundurup bulundurmadığını kontrol eder. Gezinti koruması web hizmeti ile kullandığımız aynı gizlilik dostu API'yi kullanır.",
"enforce_safe_search": "Güvenli Aramayı kullan",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Yandex ve Pixabay.",
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
"no_servers_specified": "Sunucu belirtilmedi",
"general_settings": "Genel ayarlar",
"dns_settings": "DNS ayarları",
@@ -476,7 +476,7 @@
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
"auto_clients_title": "Çalışma zamanı istemcileri",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dahil olmak üzere çeşitli kaynaklardan toplanır.",
"auto_clients_desc": "AdGuard Home'u kullanan veya kullanabilecek cihazların IP adresleri hakkında bilgiler. Bu bilgiler, hosts dosyaları, ters DNS, vb. dâhil olmak üzere çeşitli kaynaklardan toplanır.",
"access_title": "Erişim ayarları",
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
"access_allowed_title": "İzin verilen istemciler",
@@ -603,7 +603,7 @@
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
"tags_title": "Etiketler",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dahil edin. <0>Daha fazla bilgi edinin</0>.",
"tags_desc": "İstemciye karşılık gelen etiketleri seçebilirsiniz. Etiketleri daha kesin olarak uygulamak için filtreleme kurallarına dâhil edin. <0>Daha fazla bilgi edinin</0>.",
"form_select_tags": "İstemci etiketlerini seçin",
"check_title": "Filtrelemeyi denetleyin",
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",

View File

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

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
"parallel_requests": "并行请求",
"load_balancing": "负载均衡",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择服务器,以便更常使用最快的服务器。",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
"bootstrap_dns": "Bootstrap DNS 服务器",
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
"fallback_dns_title": "后备 DNS 服务器",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
"enforce_safe_search": "使用安全搜索",
"enforce_save_search_hint": "AdGuard Home 对以下搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Yandex、Pixabay。",
"enforce_save_search_hint": "AdGuard Home 将会在下列搜索引擎强制启用安全搜索Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex、Pixabay。",
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",

View File

@@ -6,7 +6,7 @@
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
"parallel_requests": "並行的請求",
"load_balancing": "負載平衡",
"load_balancing_desc": "次查詢一個上游伺服器。AdGuard Home 使用它的加權隨機演算法來選擇伺服器,以便最快的伺服器被更常使用。",
"load_balancing_desc": "次查詢一伺服器。AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
"bootstrap_dns": "自我啟動BootstrapDNS 伺服器",
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
"fallback_dns_title": "應變 DNS 伺服器",
@@ -154,7 +154,7 @@
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面API。",
"enforce_safe_search": "使用安全搜尋",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Yandex 和 Pixabay 中強制執行安全搜尋。",
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎Google、YouTube、Bing、DuckDuckGo、Ecosia、Yandex 和 Pixabay 中強制執行安全搜尋。",
"no_servers_specified": "無已明確指定的伺服器",
"general_settings": "一般設定",
"dns_settings": "DNS 設定",

View File

@@ -1,26 +1,15 @@
import {
sortIp,
countClientsStatistics,
findAddressType,
subnetMaskToBitMask,
} from '../helpers/helpers';
import { sortIp, countClientsStatistics, findAddressType, subnetMaskToBitMask } from '../helpers/helpers';
import { ADDRESS_TYPES } from '../helpers/constants';
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',
];
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',
@@ -58,6 +47,7 @@ describe('sortIp', () => {
'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
@@ -83,36 +73,26 @@ describe('sortIp', () => {
'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',
];
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::',
];
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::',
@@ -130,9 +110,11 @@ describe('sortIp', () => {
'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 = [
@@ -151,8 +133,10 @@ describe('sortIp', () => {
'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',
@@ -170,8 +154,10 @@ describe('sortIp', () => {
'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',
@@ -189,8 +175,10 @@ describe('sortIp', () => {
'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',
@@ -210,40 +198,26 @@ describe('sortIp', () => {
'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',
];
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',
];
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',
@@ -257,8 +231,10 @@ describe('sortIp', () => {
'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',
@@ -274,9 +250,11 @@ describe('sortIp', () => {
'2001:db8:11a3:9d7::1/128',
'2001:db8:11a3:9d7::1',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
describe('invalid input', () => {
const originalWarn = console.warn;
@@ -291,21 +269,29 @@ describe('sortIp', () => {
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 = [
@@ -354,6 +340,7 @@ describe('sortIp', () => {
'2001:db8:11a3:9d7:0:0:0:1',
'2001:db8:11a3:9d7:0:0:0:2',
];
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
});
});
@@ -363,9 +350,11 @@ describe('findAddressType', () => {
describe('ip', () => {
expect(findAddressType('127.0.0.1')).toStrictEqual(ADDRESS_TYPES.IP);
});
describe('cidr', () => {
expect(findAddressType('127.0.0.1/8')).toStrictEqual(ADDRESS_TYPES.CIDR);
});
describe('mac', () => {
expect(findAddressType('00:1B:44:11:3A:B7')).toStrictEqual(ADDRESS_TYPES.UNKNOWN);
});
@@ -373,42 +362,59 @@ describe('findAddressType', () => {
describe('countClientsStatistics', () => {
test('single ip', () => {
expect(countClientsStatistics(['127.0.0.1'], {
'127.0.0.1': 1,
})).toStrictEqual(1);
expect(
countClientsStatistics(['127.0.0.1'], {
'127.0.0.1': 1,
}),
).toStrictEqual(1);
});
test('multiple ip', () => {
expect(countClientsStatistics(['127.0.0.1', '127.0.0.2'], {
'127.0.0.1': 1,
'127.0.0.2': 2,
})).toStrictEqual(1 + 2);
expect(
countClientsStatistics(['127.0.0.1', '127.0.0.2'], {
'127.0.0.1': 1,
'127.0.0.2': 2,
}),
).toStrictEqual(1 + 2);
});
test('cidr', () => {
expect(countClientsStatistics(['127.0.0.0/8'], {
'127.0.0.1': 1,
'127.0.0.2': 2,
})).toStrictEqual(1 + 2);
expect(
countClientsStatistics(['127.0.0.0/8'], {
'127.0.0.1': 1,
'127.0.0.2': 2,
}),
).toStrictEqual(1 + 2);
});
test('cidr and multiple ip', () => {
expect(countClientsStatistics(['1.1.1.1', '2.2.2.2', '3.3.3.0/24'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
})).toStrictEqual(1 + 2 + 3);
expect(
countClientsStatistics(['1.1.1.1', '2.2.2.2', '3.3.3.0/24'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
}),
).toStrictEqual(1 + 2 + 3);
});
test('mac', () => {
expect(countClientsStatistics(['00:1B:44:11:3A:B7', '2.2.2.2', '3.3.3.0/24'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
})).toStrictEqual(2 + 3);
expect(
countClientsStatistics(['00:1B:44:11:3A:B7', '2.2.2.2', '3.3.3.0/24'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
}),
).toStrictEqual(2 + 3);
});
test('not found', () => {
expect(countClientsStatistics(['4.4.4.4', '5.5.5.5', '6.6.6.6'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
})).toStrictEqual(0);
expect(
countClientsStatistics(['4.4.4.4', '5.5.5.5', '6.6.6.6'], {
'1.1.1.1': 1,
'2.2.2.2': 2,
'3.3.3.3': 3,
}),
).toStrictEqual(0);
});
});
@@ -451,10 +457,12 @@ describe('subnetMaskToBitMask', () => {
test('correct for all subnetMasks', () => {
expect(
subnetMasks.map((subnetMask) => {
const bitmask = subnetMaskToBitMask(subnetMask);
return subnetMasks[bitmask] === subnetMask;
}).every((res) => res === true),
subnetMasks
.map((subnetMask) => {
const bitmask = subnetMaskToBitMask(subnetMask);
return subnetMasks[bitmask] === subnetMask;
})
.every((res) => res === true),
).toEqual(true);
});
});

View File

@@ -3,13 +3,14 @@ import i18next from 'i18next';
import apiClient from '../api/Api';
import { addErrorToast, addSuccessToast } from './toasts';
import { splitByNewLine } from '../helpers/helpers';
export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST');
export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE');
export const getAccessListSuccess = createAction('GET_ACCESS_LIST_SUCCESS');
export const getAccessList = () => async (dispatch) => {
export const getAccessList = () => async (dispatch: any) => {
dispatch(getAccessListRequest());
try {
const data = await apiClient.getAccessList();
@@ -24,7 +25,7 @@ export const setAccessListRequest = createAction('SET_ACCESS_LIST_REQUEST');
export const setAccessListFailure = createAction('SET_ACCESS_LIST_FAILURE');
export const setAccessListSuccess = createAction('SET_ACCESS_LIST_SUCCESS');
export const setAccessList = (config) => async (dispatch) => {
export const setAccessList = (config: any) => async (dispatch: any) => {
dispatch(setAccessListRequest());
try {
const { allowed_clients, disallowed_clients, blocked_hosts } = config;
@@ -48,7 +49,7 @@ export const toggleClientBlockRequest = createAction('TOGGLE_CLIENT_BLOCK_REQUES
export const toggleClientBlockFailure = createAction('TOGGLE_CLIENT_BLOCK_FAILURE');
export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCESS');
export const toggleClientBlock = (ip, disallowed, disallowed_rule) => async (dispatch) => {
export const toggleClientBlock = (ip: any, disallowed: any, disallowed_rule: any) => async (dispatch: any) => {
dispatch(toggleClientBlockRequest());
try {
const accessList = await apiClient.getAccessList();
@@ -60,12 +61,10 @@ export const toggleClientBlock = (ip, disallowed, disallowed_rule) => async (dis
if (!disallowed_rule) {
allowed_clients = allowed_clients.concat(ip);
} else {
disallowed_clients = disallowed_clients
.filter((client) => client !== disallowed_rule);
disallowed_clients = disallowed_clients.filter((client: any) => client !== disallowed_rule);
}
} else if (allowed_clients.length > 1) {
allowed_clients = allowed_clients
.filter((client) => client !== disallowed_rule);
allowed_clients = allowed_clients.filter((client: any) => client !== disallowed_rule);
} else {
disallowed_clients = disallowed_clients.concat(ip);
}

View File

@@ -1,6 +1,7 @@
import { createAction } from 'redux-actions';
import i18next from 'i18next';
import apiClient from '../api/Api';
import { getClients } from './index';
import { addErrorToast, addSuccessToast } from './toasts';
@@ -10,7 +11,7 @@ export const addClientRequest = createAction('ADD_CLIENT_REQUEST');
export const addClientFailure = createAction('ADD_CLIENT_FAILURE');
export const addClientSuccess = createAction('ADD_CLIENT_SUCCESS');
export const addClient = (config) => async (dispatch) => {
export const addClient = (config: any) => async (dispatch: any) => {
dispatch(addClientRequest());
try {
await apiClient.addClient(config);
@@ -28,7 +29,7 @@ export const deleteClientRequest = createAction('DELETE_CLIENT_REQUEST');
export const deleteClientFailure = createAction('DELETE_CLIENT_FAILURE');
export const deleteClientSuccess = createAction('DELETE_CLIENT_SUCCESS');
export const deleteClient = (config) => async (dispatch) => {
export const deleteClient = (config: any) => async (dispatch: any) => {
dispatch(deleteClientRequest());
try {
await apiClient.deleteClient(config);
@@ -45,7 +46,7 @@ export const updateClientRequest = createAction('UPDATE_CLIENT_REQUEST');
export const updateClientFailure = createAction('UPDATE_CLIENT_FAILURE');
export const updateClientSuccess = createAction('UPDATE_CLIENT_SUCCESS');
export const updateClient = (config, name) => async (dispatch) => {
export const updateClient = (config: any, name: any) => async (dispatch: any) => {
dispatch(updateClientRequest());
try {
const data = { name, data: { ...config } };

View File

@@ -2,6 +2,7 @@ import { createAction } from 'redux-actions';
import i18next from 'i18next';
import apiClient from '../api/Api';
import { splitByNewLine } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts';
@@ -9,7 +10,7 @@ export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST');
export const getDnsConfigFailure = createAction('GET_DNS_CONFIG_FAILURE');
export const getDnsConfigSuccess = createAction('GET_DNS_CONFIG_SUCCESS');
export const getDnsConfig = () => async (dispatch) => {
export const getDnsConfig = () => async (dispatch: any) => {
dispatch(getDnsConfigRequest());
try {
const data = await apiClient.getDnsConfig();
@@ -24,7 +25,7 @@ export const clearDnsCacheRequest = createAction('CLEAR_DNS_CACHE_REQUEST');
export const clearDnsCacheFailure = createAction('CLEAR_DNS_CACHE_FAILURE');
export const clearDnsCacheSuccess = createAction('CLEAR_DNS_CACHE_SUCCESS');
export const clearDnsCache = () => async (dispatch) => {
export const clearDnsCache = () => async (dispatch: any) => {
dispatch(clearDnsCacheRequest());
try {
const data = await apiClient.clearCache();
@@ -40,7 +41,7 @@ export const setDnsConfigRequest = createAction('SET_DNS_CONFIG_REQUEST');
export const setDnsConfigFailure = createAction('SET_DNS_CONFIG_FAILURE');
export const setDnsConfigSuccess = createAction('SET_DNS_CONFIG_SUCCESS');
export const setDnsConfig = (config) => async (dispatch) => {
export const setDnsConfig = (config: any) => async (dispatch: any) => {
dispatch(setDnsConfigRequest());
try {
const data = { ...config };

View File

@@ -1,5 +1,6 @@
import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import { redirectToCurrentProtocol } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts';
@@ -7,7 +8,7 @@ export const getTlsStatusRequest = createAction('GET_TLS_STATUS_REQUEST');
export const getTlsStatusFailure = createAction('GET_TLS_STATUS_FAILURE');
export const getTlsStatusSuccess = createAction('GET_TLS_STATUS_SUCCESS');
export const getTlsStatus = () => async (dispatch) => {
export const getTlsStatus = () => async (dispatch: any) => {
dispatch(getTlsStatusRequest());
try {
const status = await apiClient.getTlsStatus();
@@ -26,7 +27,7 @@ export const setTlsConfigFailure = createAction('SET_TLS_CONFIG_FAILURE');
export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS');
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
export const setTlsConfig = (config) => async (dispatch, getState) => {
export const setTlsConfig = (config: any) => async (dispatch: any, getState: any) => {
dispatch(setTlsConfigRequest());
try {
const { httpPort } = getState().dashboard;
@@ -67,7 +68,7 @@ export const validateTlsConfigRequest = createAction('VALIDATE_TLS_CONFIG_REQUES
export const validateTlsConfigFailure = createAction('VALIDATE_TLS_CONFIG_FAILURE');
export const validateTlsConfigSuccess = createAction('VALIDATE_TLS_CONFIG_SUCCESS');
export const validateTlsConfig = (config) => async (dispatch) => {
export const validateTlsConfig = (config: any) => async (dispatch: any) => {
dispatch(validateTlsConfigRequest());
try {
const values = { ...config };

View File

@@ -13,7 +13,7 @@ export const getFilteringStatusRequest = createAction('GET_FILTERING_STATUS_REQU
export const getFilteringStatusFailure = createAction('GET_FILTERING_STATUS_FAILURE');
export const getFilteringStatusSuccess = createAction('GET_FILTERING_STATUS_SUCCESS');
export const getFilteringStatus = () => async (dispatch) => {
export const getFilteringStatus = () => async (dispatch: any) => {
dispatch(getFilteringStatusRequest());
try {
const status = await apiClient.getFilteringStatus();
@@ -28,7 +28,7 @@ export const setRulesRequest = createAction('SET_RULES_REQUEST');
export const setRulesFailure = createAction('SET_RULES_FAILURE');
export const setRulesSuccess = createAction('SET_RULES_SUCCESS');
export const setRules = (rules) => async (dispatch) => {
export const setRules = (rules: any) => async (dispatch: any) => {
dispatch(setRulesRequest());
try {
const normalizedRules = {
@@ -47,83 +47,91 @@ export const addFilterRequest = createAction('ADD_FILTER_REQUEST');
export const addFilterFailure = createAction('ADD_FILTER_FAILURE');
export const addFilterSuccess = createAction('ADD_FILTER_SUCCESS');
export const addFilter = (url, name, whitelist = false) => async (dispatch, getState) => {
dispatch(addFilterRequest());
try {
await apiClient.addFilter({ url, name, whitelist });
dispatch(addFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
export const addFilter =
(url: any, name: any, whitelist = false) =>
async (dispatch: any, getState: any) => {
dispatch(addFilterRequest());
try {
await apiClient.addFilter({ url, name, whitelist });
dispatch(addFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
}
dispatch(addSuccessToast('filter_added_successfully'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(addFilterFailure());
}
dispatch(addSuccessToast('filter_added_successfully'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(addFilterFailure());
}
};
};
export const removeFilterRequest = createAction('REMOVE_FILTER_REQUEST');
export const removeFilterFailure = createAction('REMOVE_FILTER_FAILURE');
export const removeFilterSuccess = createAction('REMOVE_FILTER_SUCCESS');
export const removeFilter = (url, whitelist = false) => async (dispatch, getState) => {
dispatch(removeFilterRequest());
try {
await apiClient.removeFilter({ url, whitelist });
dispatch(removeFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
export const removeFilter =
(url: any, whitelist = false) =>
async (dispatch: any, getState: any) => {
dispatch(removeFilterRequest());
try {
await apiClient.removeFilter({ url, whitelist });
dispatch(removeFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
}
dispatch(addSuccessToast('filter_removed_successfully'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(removeFilterFailure());
}
dispatch(addSuccessToast('filter_removed_successfully'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(removeFilterFailure());
}
};
};
export const toggleFilterRequest = createAction('FILTER_TOGGLE_REQUEST');
export const toggleFilterFailure = createAction('FILTER_TOGGLE_FAILURE');
export const toggleFilterSuccess = createAction('FILTER_TOGGLE_SUCCESS');
export const toggleFilterStatus = (url, data, whitelist = false) => async (dispatch) => {
dispatch(toggleFilterRequest());
try {
await apiClient.setFilterUrl({ url, data, whitelist });
dispatch(toggleFilterSuccess(url));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleFilterFailure());
}
};
export const toggleFilterStatus =
(url: any, data: any, whitelist = false) =>
async (dispatch: any) => {
dispatch(toggleFilterRequest());
try {
await apiClient.setFilterUrl({ url, data, whitelist });
dispatch(toggleFilterSuccess(url));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleFilterFailure());
}
};
export const editFilterRequest = createAction('EDIT_FILTER_REQUEST');
export const editFilterFailure = createAction('EDIT_FILTER_FAILURE');
export const editFilterSuccess = createAction('EDIT_FILTER_SUCCESS');
export const editFilter = (url, data, whitelist = false) => async (dispatch, getState) => {
dispatch(editFilterRequest());
try {
await apiClient.setFilterUrl({ url, data, whitelist });
dispatch(editFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
export const editFilter =
(url: any, data: any, whitelist = false) =>
async (dispatch: any, getState: any) => {
dispatch(editFilterRequest());
try {
await apiClient.setFilterUrl({ url, data, whitelist });
dispatch(editFilterSuccess(url));
if (getState().filtering.isModalOpen) {
dispatch(toggleFilteringModal());
}
dispatch(addSuccessToast('filter_updated'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(editFilterFailure());
}
dispatch(addSuccessToast('filter_updated'));
dispatch(getFilteringStatus());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(editFilterFailure());
}
};
};
export const refreshFiltersRequest = createAction('FILTERING_REFRESH_REQUEST');
export const refreshFiltersFailure = createAction('FILTERING_REFRESH_FAILURE');
export const refreshFiltersSuccess = createAction('FILTERING_REFRESH_SUCCESS');
export const refreshFilters = (config) => async (dispatch) => {
export const refreshFilters = (config: any) => async (dispatch: any) => {
dispatch(refreshFiltersRequest());
dispatch(showLoading());
try {
@@ -150,7 +158,7 @@ export const setFiltersConfigRequest = createAction('SET_FILTERS_CONFIG_REQUEST'
export const setFiltersConfigFailure = createAction('SET_FILTERS_CONFIG_FAILURE');
export const setFiltersConfigSuccess = createAction('SET_FILTERS_CONFIG_SUCCESS');
export const setFiltersConfig = (config) => async (dispatch, getState) => {
export const setFiltersConfig = (config: any) => async (dispatch: any, getState: any) => {
dispatch(setFiltersConfigRequest());
try {
const { enabled } = config;
@@ -180,16 +188,18 @@ export const checkHostSuccess = createAction('CHECK_HOST_SUCCESS');
* @param {string} host.name
* @returns {undefined}
*/
export const checkHost = (host) => async (dispatch) => {
export const checkHost = (host: any) => async (dispatch: any) => {
dispatch(checkHostRequest());
try {
const data = await apiClient.checkHost(host);
const { name: hostname } = host;
dispatch(checkHostSuccess({
hostname,
...data,
}));
dispatch(
checkHostSuccess({
hostname,
...data,
}),
);
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(checkHostFailure());

View File

@@ -38,7 +38,7 @@ export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW');
* @param {*} status: boolean | SafeSearchConfig
* @returns
*/
export const toggleSetting = (settingKey, status) => async (dispatch) => {
export const toggleSetting = (settingKey: any, status: any) => async (dispatch: any) => {
let successMessage = '';
try {
switch (settingKey) {
@@ -80,64 +80,58 @@ export const initSettingsRequest = createAction('SETTINGS_INIT_REQUEST');
export const initSettingsFailure = createAction('SETTINGS_INIT_FAILURE');
export const initSettingsSuccess = createAction('SETTINGS_INIT_SUCCESS');
export const initSettings = (settingsList = {
safebrowsing: {}, parental: {},
}) => async (dispatch) => {
dispatch(initSettingsRequest());
try {
const safebrowsingStatus = await apiClient.getSafebrowsingStatus();
const parentalStatus = await apiClient.getParentalStatus();
const safesearchStatus = await apiClient.getSafesearchStatus();
const {
safebrowsing,
parental,
} = settingsList;
const newSettingsList = {
safebrowsing: {
...safebrowsing,
enabled: safebrowsingStatus.enabled,
},
parental: {
...parental,
enabled: parentalStatus.enabled,
},
safesearch: {
...safesearchStatus,
},
};
dispatch(initSettingsSuccess({ settingsList: newSettingsList }));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(initSettingsFailure());
}
};
export const initSettings =
(
settingsList = {
safebrowsing: {},
parental: {},
},
) =>
async (dispatch: any) => {
dispatch(initSettingsRequest());
try {
const safebrowsingStatus = await apiClient.getSafebrowsingStatus();
const parentalStatus = await apiClient.getParentalStatus();
const safesearchStatus = await apiClient.getSafesearchStatus();
const { safebrowsing, parental } = settingsList;
const newSettingsList = {
safebrowsing: {
...safebrowsing,
enabled: safebrowsingStatus.enabled,
},
parental: {
...parental,
enabled: parentalStatus.enabled,
},
safesearch: {
...safesearchStatus,
},
};
dispatch(initSettingsSuccess({ settingsList: newSettingsList }));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(initSettingsFailure());
}
};
export const toggleProtectionRequest = createAction('TOGGLE_PROTECTION_REQUEST');
export const toggleProtectionFailure = createAction('TOGGLE_PROTECTION_FAILURE');
export const toggleProtectionSuccess = createAction('TOGGLE_PROTECTION_SUCCESS');
const getDisabledMessage = (time) => {
const getDisabledMessage = (time: any) => {
switch (time) {
case DISABLE_PROTECTION_TIMINGS.HALF_MINUTE:
return i18next.t(
'disable_notify_for_seconds',
{ count: msToSeconds(DISABLE_PROTECTION_TIMINGS.HALF_MINUTE) },
);
return i18next.t('disable_notify_for_seconds', {
count: msToSeconds(DISABLE_PROTECTION_TIMINGS.HALF_MINUTE),
});
case DISABLE_PROTECTION_TIMINGS.MINUTE:
return i18next.t(
'disable_notify_for_minutes',
{ count: msToMinutes(DISABLE_PROTECTION_TIMINGS.MINUTE) },
);
return i18next.t('disable_notify_for_minutes', { count: msToMinutes(DISABLE_PROTECTION_TIMINGS.MINUTE) });
case DISABLE_PROTECTION_TIMINGS.TEN_MINUTES:
return i18next.t(
'disable_notify_for_minutes',
{ count: msToMinutes(DISABLE_PROTECTION_TIMINGS.TEN_MINUTES) },
);
return i18next.t('disable_notify_for_minutes', {
count: msToMinutes(DISABLE_PROTECTION_TIMINGS.TEN_MINUTES),
});
case DISABLE_PROTECTION_TIMINGS.HOUR:
return i18next.t(
'disable_notify_for_hours',
{ count: msToHours(DISABLE_PROTECTION_TIMINGS.HOUR) },
);
return i18next.t('disable_notify_for_hours', { count: msToHours(DISABLE_PROTECTION_TIMINGS.HOUR) });
case DISABLE_PROTECTION_TIMINGS.TOMORROW:
return i18next.t('disable_notify_until_tomorrow');
default:
@@ -145,22 +139,24 @@ const getDisabledMessage = (time) => {
}
};
export const toggleProtection = (status, time = null) => async (dispatch) => {
dispatch(toggleProtectionRequest());
try {
const successMessage = status ? getDisabledMessage(time) : 'enabled_protection';
await apiClient.setProtection({ enabled: !status, duration: time });
dispatch(addSuccessToast(successMessage));
dispatch(toggleProtectionSuccess({ disabledDuration: time }));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleProtectionFailure());
}
};
export const toggleProtection =
(status: any, time = null) =>
async (dispatch: any) => {
dispatch(toggleProtectionRequest());
try {
const successMessage = status ? getDisabledMessage(time) : 'enabled_protection';
await apiClient.setProtection({ enabled: !status, duration: time });
dispatch(addSuccessToast(successMessage));
dispatch(toggleProtectionSuccess({ disabledDuration: time }));
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(toggleProtectionFailure());
}
};
export const setDisableDurationTime = createAction('SET_DISABLED_DURATION_TIME');
export const setProtectionTimerTime = (updatedTime) => async (dispatch) => {
export const setProtectionTimerTime = (updatedTime: any) => async (dispatch: any) => {
dispatch(setDisableDurationTime({ timeToEnableProtection: updatedTime }));
};
@@ -168,40 +164,42 @@ export const getVersionRequest = createAction('GET_VERSION_REQUEST');
export const getVersionFailure = createAction('GET_VERSION_FAILURE');
export const getVersionSuccess = createAction('GET_VERSION_SUCCESS');
export const getVersion = (recheck = false) => async (dispatch, getState) => {
dispatch(getVersionRequest());
try {
const data = await apiClient.getGlobalVersion({ recheck_now: recheck });
dispatch(getVersionSuccess(data));
export const getVersion =
(recheck = false) =>
async (dispatch: any, getState: any) => {
dispatch(getVersionRequest());
try {
const data = await apiClient.getGlobalVersion({ recheck_now: recheck });
dispatch(getVersionSuccess(data));
if (recheck) {
const { dnsVersion } = getState().dashboard;
const currentVersion = dnsVersion === 'undefined' ? 0 : dnsVersion;
if (recheck) {
const { dnsVersion } = getState().dashboard;
const currentVersion = dnsVersion === 'undefined' ? 0 : dnsVersion;
if (data && !areEqualVersions(currentVersion, data.new_version)) {
dispatch(addSuccessToast('updates_checked'));
} else {
dispatch(addSuccessToast('updates_version_equal'));
if (data && !areEqualVersions(currentVersion, data.new_version)) {
dispatch(addSuccessToast('updates_checked'));
} else {
dispatch(addSuccessToast('updates_version_equal'));
}
}
} catch (error) {
dispatch(addErrorToast({ error: 'version_request_error' }));
dispatch(getVersionFailure());
}
} catch (error) {
dispatch(addErrorToast({ error: 'version_request_error' }));
dispatch(getVersionFailure());
}
};
};
export const getUpdateRequest = createAction('GET_UPDATE_REQUEST');
export const getUpdateFailure = createAction('GET_UPDATE_FAILURE');
export const getUpdateSuccess = createAction('GET_UPDATE_SUCCESS');
const checkStatus = async (handleRequestSuccess, handleRequestError, attempts = 60) => {
const checkStatus = async (handleRequestSuccess: any, handleRequestError: any, attempts = 60) => {
let timeout;
if (attempts === 0) {
handleRequestError();
}
const rmTimeout = (t) => t && clearTimeout(t);
const rmTimeout = (t: any) => t && clearTimeout(t);
try {
const response = await axios.get(`${apiClient.baseUrl}/status`);
@@ -220,25 +218,18 @@ const checkStatus = async (handleRequestSuccess, handleRequestError, attempts =
}
} catch (error) {
rmTimeout(timeout);
timeout = setTimeout(
checkStatus,
CHECK_TIMEOUT,
handleRequestSuccess,
handleRequestError,
attempts - 1,
);
timeout = setTimeout(checkStatus, CHECK_TIMEOUT, handleRequestSuccess, handleRequestError, attempts - 1);
}
};
export const getUpdate = () => async (dispatch, getState) => {
export const getUpdate = () => async (dispatch: any, getState: any) => {
const { dnsVersion } = getState().dashboard;
dispatch(getUpdateRequest());
const handleRequestError = () => {
const options = {
components: {
a: <a href={MANUAL_UPDATE_LINK} target="_blank"
rel="noopener noreferrer" />,
a: <a href={MANUAL_UPDATE_LINK} target="_blank" rel="noopener noreferrer" />,
},
};
@@ -246,12 +237,13 @@ export const getUpdate = () => async (dispatch, getState) => {
dispatch(getUpdateFailure());
};
const handleRequestSuccess = (response) => {
const handleRequestSuccess = (response: any) => {
const responseVersion = response.data?.version;
if (dnsVersion !== responseVersion) {
dispatch(getUpdateSuccess());
window.location.reload(true);
window.location.reload();
}
};
@@ -267,18 +259,20 @@ export const getClientsRequest = createAction('GET_CLIENTS_REQUEST');
export const getClientsFailure = createAction('GET_CLIENTS_FAILURE');
export const getClientsSuccess = createAction('GET_CLIENTS_SUCCESS');
export const getClients = () => async (dispatch) => {
export const getClients = () => async (dispatch: any) => {
dispatch(getClientsRequest());
try {
const data = await apiClient.getClients();
const sortedClients = data.clients && sortClients(data.clients);
const sortedAutoClients = data.auto_clients && sortClients(data.auto_clients);
dispatch(getClientsSuccess({
clients: sortedClients || [],
autoClients: sortedAutoClients || [],
supportedTags: data.supported_tags || [],
}));
dispatch(
getClientsSuccess({
clients: sortedClients || [],
autoClients: sortedAutoClients || [],
supportedTags: data.supported_tags || [],
}),
);
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(getClientsFailure());
@@ -289,7 +283,7 @@ export const getProfileRequest = createAction('GET_PROFILE_REQUEST');
export const getProfileFailure = createAction('GET_PROFILE_FAILURE');
export const getProfileSuccess = createAction('GET_PROFILE_SUCCESS');
export const getProfile = () => async (dispatch) => {
export const getProfile = () => async (dispatch: any) => {
dispatch(getProfileRequest());
try {
const profile = await apiClient.getProfile();
@@ -305,16 +299,17 @@ export const dnsStatusFailure = createAction('DNS_STATUS_FAILURE');
export const dnsStatusSuccess = createAction('DNS_STATUS_SUCCESS');
export const setDnsRunningStatus = createAction('SET_DNS_RUNNING_STATUS');
export const getDnsStatus = () => async (dispatch) => {
export const getDnsStatus = () => async (dispatch: any) => {
dispatch(dnsStatusRequest());
const handleRequestError = () => {
dispatch(addErrorToast({ error: 'dns_status_error' }));
dispatch(dnsStatusFailure());
window.location.reload(true);
window.location.reload();
};
const handleRequestSuccess = (response) => {
const handleRequestSuccess = (response: any) => {
const dnsStatus = response.data;
if (dnsStatus.protection_disabled_duration === 0) {
dnsStatus.protection_disabled_duration = null;
@@ -342,16 +337,17 @@ export const timerStatusRequest = createAction('TIMER_STATUS_REQUEST');
export const timerStatusFailure = createAction('TIMER_STATUS_FAILURE');
export const timerStatusSuccess = createAction('TIMER_STATUS_SUCCESS');
export const getTimerStatus = () => async (dispatch) => {
export const getTimerStatus = () => async (dispatch: any) => {
dispatch(timerStatusRequest());
const handleRequestError = () => {
dispatch(addErrorToast({ error: 'dns_status_error' }));
dispatch(dnsStatusFailure());
window.location.reload(true);
window.location.reload();
};
const handleRequestSuccess = (response) => {
const handleRequestSuccess = (response: any) => {
const dnsStatus = response.data;
if (dnsStatus.protection_disabled_duration === 0) {
dnsStatus.protection_disabled_duration = null;
@@ -376,30 +372,26 @@ export const testUpstreamRequest = createAction('TEST_UPSTREAM_REQUEST');
export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE');
export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS');
export const testUpstream = (
{
bootstrap_dns,
upstream_dns,
local_ptr_upstreams,
fallback_dns,
}, upstream_dns_file,
) => async (dispatch) => {
dispatch(testUpstreamRequest());
try {
const removeComments = compose(filterOutComments, splitByNewLine);
export const testUpstream =
({ bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns }: any, upstream_dns_file: any) =>
async (dispatch: any) => {
dispatch(testUpstreamRequest());
try {
const removeComments = compose(filterOutComments, splitByNewLine);
const config = {
bootstrap_dns: splitByNewLine(bootstrap_dns),
private_upstream: splitByNewLine(local_ptr_upstreams),
fallback_dns: splitByNewLine(fallback_dns),
...(upstream_dns_file ? null : {
upstream_dns: removeComments(upstream_dns),
}),
};
const config = {
bootstrap_dns: splitByNewLine(bootstrap_dns),
private_upstream: splitByNewLine(local_ptr_upstreams),
fallback_dns: splitByNewLine(fallback_dns),
...(upstream_dns_file
? null
: {
upstream_dns: removeComments(upstream_dns),
}),
};
const upstreamResponse = await apiClient.testUpstream(config);
const testMessages = Object.keys(upstreamResponse)
.map((key) => {
const upstreamResponse = await apiClient.testUpstream(config);
const testMessages = Object.keys(upstreamResponse).map((key) => {
const message = upstreamResponse[key];
if (message.startsWith('WARNING:')) {
dispatch(addErrorToast({ error: i18next.t('dns_test_warning_toast', { key }) }));
@@ -407,46 +399,54 @@ export const testUpstream = (
const info = message.substring(0, message.indexOf(':'));
const [sectionKey, line] = info.split(' ');
const section = i18next.t(sectionKey);
dispatch(addErrorToast({ error: i18next.t('dns_test_parsing_error_toast', { section, line }) }));
dispatch(
addErrorToast({
error: i18next.t('dns_test_parsing_error_toast', {
section,
line,
}),
}),
);
} else if (message !== 'OK') {
dispatch(addErrorToast({ error: i18next.t('dns_test_not_ok_toast', { key }) }));
}
return message;
});
if (testMessages.every((message) => message === 'OK' || message.startsWith('WARNING:'))) {
dispatch(addSuccessToast('dns_test_ok_toast'));
if (testMessages.every((message) => message === 'OK' || message.startsWith('WARNING:'))) {
dispatch(addSuccessToast('dns_test_ok_toast'));
}
dispatch(testUpstreamSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(testUpstreamFailure());
}
};
dispatch(testUpstreamSuccess());
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(testUpstreamFailure());
}
};
export const testUpstreamWithFormValues = () => async (dispatch, getState) => {
export const testUpstreamWithFormValues = () => async (dispatch: any, getState: any) => {
const { upstream_dns_file } = getState().dnsConfig;
const {
bootstrap_dns,
upstream_dns,
local_ptr_upstreams,
fallback_dns,
} = getState().form[FORM_NAME.UPSTREAM].values;
const { bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns } =
getState().form[FORM_NAME.UPSTREAM].values;
return dispatch(testUpstream({
bootstrap_dns,
upstream_dns,
local_ptr_upstreams,
fallback_dns,
}, upstream_dns_file));
return dispatch(
testUpstream(
{
bootstrap_dns,
upstream_dns,
local_ptr_upstreams,
fallback_dns,
},
upstream_dns_file,
),
);
};
export const changeLanguageRequest = createAction('CHANGE_LANGUAGE_REQUEST');
export const changeLanguageFailure = createAction('CHANGE_LANGUAGE_FAILURE');
export const changeLanguageSuccess = createAction('CHANGE_LANGUAGE_SUCCESS');
export const changeLanguage = (lang) => async (dispatch) => {
export const changeLanguage = (lang: any) => async (dispatch: any) => {
dispatch(changeLanguageRequest());
try {
await apiClient.changeLanguage({ language: lang });
@@ -461,7 +461,7 @@ export const changeThemeRequest = createAction('CHANGE_THEME_REQUEST');
export const changeThemeFailure = createAction('CHANGE_THEME_FAILURE');
export const changeThemeSuccess = createAction('CHANGE_THEME_SUCCESS');
export const changeTheme = (theme) => async (dispatch) => {
export const changeTheme = (theme: any) => async (dispatch: any) => {
dispatch(changeThemeRequest());
try {
await apiClient.changeTheme({ theme });
@@ -476,7 +476,7 @@ export const getDhcpStatusRequest = createAction('GET_DHCP_STATUS_REQUEST');
export const getDhcpStatusSuccess = createAction('GET_DHCP_STATUS_SUCCESS');
export const getDhcpStatusFailure = createAction('GET_DHCP_STATUS_FAILURE');
export const getDhcpStatus = () => async (dispatch) => {
export const getDhcpStatus = () => async (dispatch: any) => {
dispatch(getDhcpStatusRequest());
try {
const globalStatus = await apiClient.getGlobalStatus();
@@ -497,7 +497,7 @@ export const getDhcpInterfacesRequest = createAction('GET_DHCP_INTERFACES_REQUES
export const getDhcpInterfacesSuccess = createAction('GET_DHCP_INTERFACES_SUCCESS');
export const getDhcpInterfacesFailure = createAction('GET_DHCP_INTERFACES_FAILURE');
export const getDhcpInterfaces = () => async (dispatch) => {
export const getDhcpInterfaces = () => async (dispatch: any) => {
dispatch(getDhcpInterfacesRequest());
try {
const interfaces = await apiClient.getDhcpInterfaces();
@@ -512,7 +512,7 @@ export const findActiveDhcpRequest = createAction('FIND_ACTIVE_DHCP_REQUEST');
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
export const findActiveDhcp = (name) => async (dispatch, getState) => {
export const findActiveDhcp = (name: any) => async (dispatch: any, getState: any) => {
dispatch(findActiveDhcpRequest());
try {
const req = {
@@ -559,12 +559,12 @@ export const findActiveDhcp = (name) => async (dispatch, getState) => {
return;
}
if ((hasV4Interface && v4.other_server.found === STATUS_RESPONSE.YES)
|| (hasV6Interface && v6.other_server.found === STATUS_RESPONSE.YES)) {
if (
(hasV4Interface && v4.other_server.found === STATUS_RESPONSE.YES) ||
(hasV6Interface && v6.other_server.found === STATUS_RESPONSE.YES)
) {
dispatch(addErrorToast({ error: 'dhcp_found' }));
} else if (hasV4Interface && v4.static_ip.static === STATUS_RESPONSE.NO
&& v4.static_ip.ip
&& interface_name) {
} else if (hasV4Interface && v4.static_ip.static === STATUS_RESPONSE.NO && v4.static_ip.ip && interface_name) {
const warning = i18next.t('dhcp_dynamic_ip_found', {
interfaceName: interface_name,
ipAddress: v4.static_ip.ip,
@@ -587,7 +587,7 @@ export const setDhcpConfigRequest = createAction('SET_DHCP_CONFIG_REQUEST');
export const setDhcpConfigSuccess = createAction('SET_DHCP_CONFIG_SUCCESS');
export const setDhcpConfigFailure = createAction('SET_DHCP_CONFIG_FAILURE');
export const setDhcpConfig = (values) => async (dispatch) => {
export const setDhcpConfig = (values: any) => async (dispatch: any) => {
dispatch(setDhcpConfigRequest());
try {
await apiClient.setDhcpConfig(values);
@@ -603,7 +603,7 @@ export const toggleDhcpRequest = createAction('TOGGLE_DHCP_REQUEST');
export const toggleDhcpFailure = createAction('TOGGLE_DHCP_FAILURE');
export const toggleDhcpSuccess = createAction('TOGGLE_DHCP_SUCCESS');
export const toggleDhcp = (values) => async (dispatch) => {
export const toggleDhcp = (values: any) => async (dispatch: any) => {
dispatch(toggleDhcpRequest());
let config = {
...values,
@@ -633,7 +633,7 @@ export const resetDhcpRequest = createAction('RESET_DHCP_REQUEST');
export const resetDhcpSuccess = createAction('RESET_DHCP_SUCCESS');
export const resetDhcpFailure = createAction('RESET_DHCP_FAILURE');
export const resetDhcp = () => async (dispatch) => {
export const resetDhcp = () => async (dispatch: any) => {
dispatch(resetDhcpRequest());
try {
const status = await apiClient.resetDhcp();
@@ -649,7 +649,7 @@ export const resetDhcpLeasesRequest = createAction('RESET_DHCP_LEASES_REQUEST');
export const resetDhcpLeasesSuccess = createAction('RESET_DHCP_LEASES_SUCCESS');
export const resetDhcpLeasesFailure = createAction('RESET_DHCP_LEASES_FAILURE');
export const resetDhcpLeases = () => async (dispatch) => {
export const resetDhcpLeases = () => async (dispatch: any) => {
dispatch(resetDhcpLeasesRequest());
try {
const status = await apiClient.resetDhcpLeases();
@@ -667,7 +667,7 @@ export const addStaticLeaseRequest = createAction('ADD_STATIC_LEASE_REQUEST');
export const addStaticLeaseFailure = createAction('ADD_STATIC_LEASE_FAILURE');
export const addStaticLeaseSuccess = createAction('ADD_STATIC_LEASE_SUCCESS');
export const addStaticLease = (config) => async (dispatch) => {
export const addStaticLease = (config: any) => async (dispatch: any) => {
dispatch(addStaticLeaseRequest());
try {
const name = config.hostname || config.ip;
@@ -686,7 +686,7 @@ export const removeStaticLeaseRequest = createAction('REMOVE_STATIC_LEASE_REQUES
export const removeStaticLeaseFailure = createAction('REMOVE_STATIC_LEASE_FAILURE');
export const removeStaticLeaseSuccess = createAction('REMOVE_STATIC_LEASE_SUCCESS');
export const removeStaticLease = (config) => async (dispatch) => {
export const removeStaticLease = (config: any) => async (dispatch: any) => {
dispatch(removeStaticLeaseRequest());
try {
const name = config.hostname || config.ip;
@@ -703,7 +703,7 @@ export const updateStaticLeaseRequest = createAction('UPDATE_STATIC_LEASE_REQUES
export const updateStaticLeaseFailure = createAction('UPDATE_STATIC_LEASE_FAILURE');
export const updateStaticLeaseSuccess = createAction('UPDATE_STATIC_LEASE_SUCCESS');
export const updateStaticLease = (config) => async (dispatch) => {
export const updateStaticLease = (config: any) => async (dispatch: any) => {
dispatch(updateStaticLeaseRequest());
try {
await apiClient.updateStaticLease(config);
@@ -719,42 +719,42 @@ export const updateStaticLease = (config) => async (dispatch) => {
export const removeToast = createAction('REMOVE_TOAST');
export const toggleBlocking = (
type, domain, baseRule, baseUnblocking,
) => async (dispatch, getState) => {
const baseBlockingRule = baseRule || `||${domain}^$important`;
const baseUnblockingRule = baseUnblocking || `@@${baseBlockingRule}`;
const { userRules } = getState().filtering;
export const toggleBlocking =
(type: any, domain: any, baseRule?: string, baseUnblocking?: string) => async (dispatch: any, getState: any) => {
const baseBlockingRule = baseRule || `||${domain}^$important`;
const baseUnblockingRule = baseUnblocking || `@@${baseBlockingRule}`;
const { userRules } = getState().filtering;
const lineEnding = !endsWith(userRules, '\n') ? '\n' : '';
const lineEnding = !endsWith(userRules, '\n') ? '\n' : '';
const blockingRule = type === BLOCK_ACTIONS.BLOCK ? baseUnblockingRule : baseBlockingRule;
const unblockingRule = type === BLOCK_ACTIONS.BLOCK ? baseBlockingRule : baseUnblockingRule;
const preparedBlockingRule = new RegExp(`(^|\n)${escapeRegExp(blockingRule)}($|\n)`);
const preparedUnblockingRule = new RegExp(`(^|\n)${escapeRegExp(unblockingRule)}($|\n)`);
const blockingRule = type === BLOCK_ACTIONS.BLOCK ? baseUnblockingRule : baseBlockingRule;
const unblockingRule = type === BLOCK_ACTIONS.BLOCK ? baseBlockingRule : baseUnblockingRule;
const preparedBlockingRule = new RegExp(`(^|\n)${escapeRegExp(blockingRule)}($|\n)`);
const preparedUnblockingRule = new RegExp(`(^|\n)${escapeRegExp(unblockingRule)}($|\n)`);
const matchPreparedBlockingRule = userRules.match(preparedBlockingRule);
const matchPreparedUnblockingRule = userRules.match(preparedUnblockingRule);
const matchPreparedBlockingRule = userRules.match(preparedBlockingRule);
const matchPreparedUnblockingRule = userRules.match(preparedUnblockingRule);
if (matchPreparedBlockingRule) {
await dispatch(setRules(userRules.replace(`${blockingRule}`, '')));
dispatch(addSuccessToast(i18next.t('rule_removed_from_custom_filtering_toast', { rule: blockingRule })));
} else if (!matchPreparedUnblockingRule) {
await dispatch(setRules(`${userRules}${lineEnding}${unblockingRule}\n`));
dispatch(addSuccessToast(i18next.t('rule_added_to_custom_filtering_toast', { rule: unblockingRule })));
} else if (matchPreparedUnblockingRule) {
dispatch(addSuccessToast(i18next.t('rule_added_to_custom_filtering_toast', { rule: unblockingRule })));
return;
} else if (!matchPreparedBlockingRule) {
dispatch(addSuccessToast(i18next.t('rule_removed_from_custom_filtering_toast', { rule: blockingRule })));
return;
}
if (matchPreparedBlockingRule) {
await dispatch(setRules(userRules.replace(`${blockingRule}`, '')));
dispatch(addSuccessToast(i18next.t('rule_removed_from_custom_filtering_toast', { rule: blockingRule })));
} else if (!matchPreparedUnblockingRule) {
await dispatch(setRules(`${userRules}${lineEnding}${unblockingRule}\n`));
dispatch(addSuccessToast(i18next.t('rule_added_to_custom_filtering_toast', { rule: unblockingRule })));
} else if (matchPreparedUnblockingRule) {
dispatch(addSuccessToast(i18next.t('rule_added_to_custom_filtering_toast', { rule: unblockingRule })));
return;
} else if (!matchPreparedBlockingRule) {
dispatch(addSuccessToast(i18next.t('rule_removed_from_custom_filtering_toast', { rule: blockingRule })));
return;
}
dispatch(getFilteringStatus());
};
dispatch(getFilteringStatus());
};
export const toggleBlockingForClient = (type, domain, client) => {
const escapedClientName = client.replace(/'/g, '\\\'')
export const toggleBlockingForClient = (type: any, domain: any, client: any) => {
const escapedClientName = client
.replace(/'/g, "\\'")
.replace(/"/g, '\\"')
.replace(/,/g, '\\,')
.replace(/\|/g, '\\|');

View File

@@ -9,7 +9,7 @@ export const getDefaultAddressesRequest = createAction('GET_DEFAULT_ADDRESSES_RE
export const getDefaultAddressesFailure = createAction('GET_DEFAULT_ADDRESSES_FAILURE');
export const getDefaultAddressesSuccess = createAction('GET_DEFAULT_ADDRESSES_SUCCESS');
export const getDefaultAddresses = () => async (dispatch) => {
export const getDefaultAddresses = () => async (dispatch: any) => {
dispatch(getDefaultAddressesRequest());
try {
const addresses = await apiClient.getDefaultAddresses();
@@ -24,13 +24,10 @@ export const setAllSettingsRequest = createAction('SET_ALL_SETTINGS_REQUEST');
export const setAllSettingsFailure = createAction('SET_ALL_SETTINGS_FAILURE');
export const setAllSettingsSuccess = createAction('SET_ALL_SETTINGS_SUCCESS');
export const setAllSettings = (values) => async (dispatch) => {
export const setAllSettings = (values: any) => async (dispatch: any) => {
dispatch(setAllSettingsRequest());
try {
const {
confirm_password,
...config
} = values;
const { confirm_password, ...config } = values;
await apiClient.setAllSettings(config);
dispatch(setAllSettingsSuccess());
@@ -47,7 +44,7 @@ export const checkConfigRequest = createAction('CHECK_CONFIG_REQUEST');
export const checkConfigFailure = createAction('CHECK_CONFIG_FAILURE');
export const checkConfigSuccess = createAction('CHECK_CONFIG_SUCCESS');
export const checkConfig = (values) => async (dispatch) => {
export const checkConfig = (values: any) => async (dispatch: any) => {
dispatch(checkConfigRequest());
try {
const check = await apiClient.checkConfig(values);

View File

@@ -8,12 +8,12 @@ export const processLoginRequest = createAction('PROCESS_LOGIN_REQUEST');
export const processLoginFailure = createAction('PROCESS_LOGIN_FAILURE');
export const processLoginSuccess = createAction('PROCESS_LOGIN_SUCCESS');
export const processLogin = (values) => async (dispatch) => {
export const processLogin = (values: any) => async (dispatch: any) => {
dispatch(processLoginRequest());
try {
await apiClient.login(values);
const dashboardUrl = window.location.origin
+ window.location.pathname.replace(HTML_PAGES.LOGIN, HTML_PAGES.MAIN);
const dashboardUrl =
window.location.origin + window.location.pathname.replace(HTML_PAGES.LOGIN, HTML_PAGES.MAIN);
window.location.replace(dashboardUrl);
dispatch(processLoginSuccess());
} catch (error) {

View File

@@ -1,13 +1,12 @@
import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import { normalizeLogs } from '../helpers/helpers';
import {
DEFAULT_LOGS_FILTER, FORM_NAME, QUERY_LOGS_PAGE_LIMIT,
} from '../helpers/constants';
import { DEFAULT_LOGS_FILTER, FORM_NAME, QUERY_LOGS_PAGE_LIMIT } from '../helpers/constants';
import { addErrorToast, addSuccessToast } from './toasts';
const getLogsWithParams = async (config) => {
const getLogsWithParams = async (config: any) => {
const { older_than, filter, ...values } = config;
const rawLogs = await apiClient.getQueryLog({
...filter,
@@ -28,20 +27,20 @@ export const getAdditionalLogsRequest = createAction('GET_ADDITIONAL_LOGS_REQUES
export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE');
export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS');
const shortPollQueryLogs = async (data, filter, dispatch, getState, total) => {
const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getState: any, total?: any) => {
const { logs, oldest } = data;
const totalData = total || { logs };
const queryForm = getState().form[FORM_NAME.LOGS_FILTER];
const currentQuery = queryForm && queryForm.values.search;
const previousQuery = filter?.search;
const isQueryTheSame = typeof previousQuery === 'string'
&& typeof currentQuery === 'string'
&& previousQuery === currentQuery;
const isQueryTheSame =
typeof previousQuery === 'string' && typeof currentQuery === 'string' && previousQuery === currentQuery;
const isShortPollingNeeded = (logs.length < QUERY_LOGS_PAGE_LIMIT
|| totalData.logs.length < QUERY_LOGS_PAGE_LIMIT)
&& oldest !== '' && isQueryTheSame;
const isShortPollingNeeded =
(logs.length < QUERY_LOGS_PAGE_LIMIT || totalData.logs.length < QUERY_LOGS_PAGE_LIMIT) &&
oldest !== '' &&
isQueryTheSame;
if (isShortPollingNeeded) {
dispatch(getAdditionalLogsRequest());
@@ -75,22 +74,24 @@ export const getLogsRequest = createAction('GET_LOGS_REQUEST');
export const getLogsFailure = createAction('GET_LOGS_FAILURE');
export const getLogsSuccess = createAction('GET_LOGS_SUCCESS');
export const updateLogs = () => async (dispatch, getState) => {
export const updateLogs = () => async (dispatch: any, getState: any) => {
try {
const { logs, oldest, older_than } = getState().queryLogs;
dispatch(getLogsSuccess({
logs,
oldest,
older_than,
}));
dispatch(
getLogsSuccess({
logs,
oldest,
older_than,
}),
);
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(getLogsFailure(error));
}
};
export const getLogs = () => async (dispatch, getState) => {
export const getLogs = () => async (dispatch: any, getState: any) => {
dispatch(getLogsRequest());
try {
const { isFiltered, filter, oldest } = getState().queryLogs;
@@ -121,26 +122,29 @@ export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
* @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object
* @returns function
*/
export const setLogsFilter = (filter) => setLogsFilterRequest(filter);
export const setLogsFilter = (filter: any) => 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, getState) => {
export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: any) => {
dispatch(setFilteredLogsRequest());
try {
const data = await getLogsWithParams({
older_than: '',
filter,
});
const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState);
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
dispatch(setFilteredLogsSuccess({
...updatedData,
filter,
}));
dispatch(
setFilteredLogsSuccess({
...updatedData,
filter,
}),
);
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(setFilteredLogsFailure(error));
@@ -149,7 +153,7 @@ export const setFilteredLogs = (filter) => async (dispatch, getState) => {
export const resetFilteredLogs = () => setFilteredLogs(DEFAULT_LOGS_FILTER);
export const refreshFilteredLogs = () => async (dispatch, getState) => {
export const refreshFilteredLogs = () => async (dispatch: any, getState: any) => {
const { filter } = getState().queryLogs;
await dispatch(setFilteredLogs(filter));
};
@@ -158,7 +162,7 @@ export const clearLogsRequest = createAction('CLEAR_LOGS_REQUEST');
export const clearLogsFailure = createAction('CLEAR_LOGS_FAILURE');
export const clearLogsSuccess = createAction('CLEAR_LOGS_SUCCESS');
export const clearLogs = () => async (dispatch) => {
export const clearLogs = () => async (dispatch: any) => {
dispatch(clearLogsRequest());
try {
await apiClient.clearQueryLog();
@@ -174,7 +178,7 @@ export const getLogsConfigRequest = createAction('GET_LOGS_CONFIG_REQUEST');
export const getLogsConfigFailure = createAction('GET_LOGS_CONFIG_FAILURE');
export const getLogsConfigSuccess = createAction('GET_LOGS_CONFIG_SUCCESS');
export const getLogsConfig = () => async (dispatch) => {
export const getLogsConfig = () => async (dispatch: any) => {
dispatch(getLogsConfigRequest());
try {
const data = await apiClient.getQueryLogConfig();
@@ -189,7 +193,7 @@ export const setLogsConfigRequest = createAction('SET_LOGS_CONFIG_REQUEST');
export const setLogsConfigFailure = createAction('SET_LOGS_CONFIG_FAILURE');
export const setLogsConfigSuccess = createAction('SET_LOGS_CONFIG_SUCCESS');
export const setLogsConfig = (config) => async (dispatch) => {
export const setLogsConfig = (config: any) => async (dispatch: any) => {
dispatch(setLogsConfigRequest());
try {
await apiClient.setQueryLogConfig(config);

View File

@@ -9,7 +9,7 @@ export const getRewritesListRequest = createAction('GET_REWRITES_LIST_REQUEST');
export const getRewritesListFailure = createAction('GET_REWRITES_LIST_FAILURE');
export const getRewritesListSuccess = createAction('GET_REWRITES_LIST_SUCCESS');
export const getRewritesList = () => async (dispatch) => {
export const getRewritesList = () => async (dispatch: any) => {
dispatch(getRewritesListRequest());
try {
const data = await apiClient.getRewritesList();
@@ -24,7 +24,7 @@ export const addRewriteRequest = createAction('ADD_REWRITE_REQUEST');
export const addRewriteFailure = createAction('ADD_REWRITE_FAILURE');
export const addRewriteSuccess = createAction('ADD_REWRITE_SUCCESS');
export const addRewrite = (config) => async (dispatch) => {
export const addRewrite = (config: any) => async (dispatch: any) => {
dispatch(addRewriteRequest());
try {
await apiClient.addRewrite(config);
@@ -47,7 +47,7 @@ export const updateRewriteSuccess = createAction('UPDATE_REWRITE_SUCCESS');
* @param {string} config.target - current DNS rewrite value
* @param {string} config.update - updated DNS rewrite value
*/
export const updateRewrite = (config) => async (dispatch) => {
export const updateRewrite = (config: any) => async (dispatch: any) => {
dispatch(updateRewriteRequest());
try {
await apiClient.updateRewrite(config);
@@ -65,7 +65,7 @@ export const deleteRewriteRequest = createAction('DELETE_REWRITE_REQUEST');
export const deleteRewriteFailure = createAction('DELETE_REWRITE_FAILURE');
export const deleteRewriteSuccess = createAction('DELETE_REWRITE_SUCCESS');
export const deleteRewrite = (config) => async (dispatch) => {
export const deleteRewrite = (config: any) => async (dispatch: any) => {
dispatch(deleteRewriteRequest());
try {
await apiClient.deleteRewrite(config);

View File

@@ -6,7 +6,7 @@ export const getBlockedServicesRequest = createAction('GET_BLOCKED_SERVICES_REQU
export const getBlockedServicesFailure = createAction('GET_BLOCKED_SERVICES_FAILURE');
export const getBlockedServicesSuccess = createAction('GET_BLOCKED_SERVICES_SUCCESS');
export const getBlockedServices = () => async (dispatch) => {
export const getBlockedServices = () => async (dispatch: any) => {
dispatch(getBlockedServicesRequest());
try {
const data = await apiClient.getBlockedServices();
@@ -21,7 +21,7 @@ export const getAllBlockedServicesRequest = createAction('GET_ALL_BLOCKED_SERVIC
export const getAllBlockedServicesFailure = createAction('GET_ALL_BLOCKED_SERVICES_FAILURE');
export const getAllBlockedServicesSuccess = createAction('GET_ALL_BLOCKED_SERVICES_SUCCESS');
export const getAllBlockedServices = () => async (dispatch) => {
export const getAllBlockedServices = () => async (dispatch: any) => {
dispatch(getAllBlockedServicesRequest());
try {
const data = await apiClient.getAllBlockedServices();
@@ -36,7 +36,7 @@ export const updateBlockedServicesRequest = createAction('UPDATE_BLOCKED_SERVICE
export const updateBlockedServicesFailure = createAction('UPDATE_BLOCKED_SERVICES_FAILURE');
export const updateBlockedServicesSuccess = createAction('UPDATE_BLOCKED_SERVICES_SUCCESS');
export const updateBlockedServices = (values) => async (dispatch) => {
export const updateBlockedServices = (values: any) => async (dispatch: any) => {
dispatch(updateBlockedServicesRequest());
try {
await apiClient.updateBlockedServices(values);

View File

@@ -1,16 +1,14 @@
import { createAction } from 'redux-actions';
import apiClient from '../api/Api';
import {
normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo,
} from '../helpers/helpers';
import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers';
import { addErrorToast, addSuccessToast } from './toasts';
export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST');
export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE');
export const getStatsConfigSuccess = createAction('GET_STATS_CONFIG_SUCCESS');
export const getStatsConfig = () => async (dispatch) => {
export const getStatsConfig = () => async (dispatch: any) => {
dispatch(getStatsConfigRequest());
try {
const data = await apiClient.getStatsConfig();
@@ -25,7 +23,7 @@ export const setStatsConfigRequest = createAction('SET_STATS_CONFIG_REQUEST');
export const setStatsConfigFailure = createAction('SET_STATS_CONFIG_FAILURE');
export const setStatsConfigSuccess = createAction('SET_STATS_CONFIG_SUCCESS');
export const setStatsConfig = (config) => async (dispatch) => {
export const setStatsConfig = (config: any) => async (dispatch: any) => {
dispatch(setStatsConfigRequest());
try {
await apiClient.setStatsConfig(config);
@@ -41,11 +39,12 @@ export const getStatsRequest = createAction('GET_STATS_REQUEST');
export const getStatsFailure = createAction('GET_STATS_FAILURE');
export const getStatsSuccess = createAction('GET_STATS_SUCCESS');
export const getStats = () => async (dispatch) => {
export const getStats = () => async (dispatch: any) => {
dispatch(getStatsRequest());
try {
const stats = await apiClient.getStats();
const normalizedTopClients = normalizeTopStats(stats.top_clients);
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
const clients = await apiClient.findClients(clientsParams);
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
@@ -71,7 +70,7 @@ export const resetStatsRequest = createAction('RESET_STATS_REQUEST');
export const resetStatsFailure = createAction('RESET_STATS_FAILURE');
export const resetStatsSuccess = createAction('RESET_STATS_SUCCESS');
export const resetStats = () => async (dispatch) => {
export const resetStats = () => async (dispatch: any) => {
dispatch(getStatsRequest());
try {
await apiClient.resetStats();

View File

@@ -1,17 +1,16 @@
import axios from 'axios';
import { getPathWithQueryString } from '../helpers/helpers';
import {
QUERY_LOGS_PAGE_LIMIT, HTML_PAGES, R_PATH_LAST_PART, THEMES,
} from '../helpers/constants';
import { BASE_URL } from '../../constants';
import { getPathWithQueryString } from '../helpers/helpers';
import { QUERY_LOGS_PAGE_LIMIT, HTML_PAGES, R_PATH_LAST_PART, THEMES } from '../helpers/constants';
import i18n from '../i18n';
import { LANGUAGES } from '../helpers/twosky';
class Api {
baseUrl = BASE_URL;
async makeRequest(path, method = 'POST', config) {
async makeRequest(path: any, method = 'POST', config: any = {}) {
const url = `${this.baseUrl}/${path}`;
const axiosConfig = config || {};
@@ -29,26 +28,26 @@ class Api {
return response.data;
} catch (error) {
const errorPath = url;
if (error.response) {
const { pathname } = document.location;
const shouldRedirect = pathname !== HTML_PAGES.LOGIN
&& pathname !== HTML_PAGES.INSTALL;
const shouldRedirect = pathname !== HTML_PAGES.LOGIN && pathname !== HTML_PAGES.INSTALL;
if (error.response.status === 403 && shouldRedirect) {
const loginPageUrl = window.location.href
.replace(R_PATH_LAST_PART, HTML_PAGES.LOGIN);
const loginPageUrl = window.location.href.replace(R_PATH_LAST_PART, HTML_PAGES.LOGIN);
window.location.replace(loginPageUrl);
return false;
}
throw new Error(`${errorPath} | ${error.response.data} | ${error.response.status}`);
}
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' };
@@ -58,10 +57,11 @@ class Api {
getGlobalStatus() {
const { path, method } = this.GLOBAL_STATUS;
return this.makeRequest(path, method);
}
testUpstream(servers) {
testUpstream(servers: any) {
const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS;
const config = {
data: servers,
@@ -69,7 +69,7 @@ class Api {
return this.makeRequest(path, method, config);
}
getGlobalVersion(data) {
getGlobalVersion(data: any) {
const { path, method } = this.GLOBAL_VERSION;
const config = {
data,
@@ -79,6 +79,7 @@ class Api {
getUpdate() {
const { path, method } = this.GLOBAL_UPDATE;
return this.makeRequest(path, method);
}
@@ -101,10 +102,11 @@ class Api {
getFilteringStatus() {
const { path, method } = this.FILTERING_STATUS;
return this.makeRequest(path, method);
}
refreshFilters(config) {
refreshFilters(config: any) {
const { path, method } = this.FILTERING_REFRESH;
const parameters = {
data: config,
@@ -113,7 +115,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
addFilter(config) {
addFilter(config: any) {
const { path, method } = this.FILTERING_ADD_FILTER;
const parameters = {
data: config,
@@ -122,7 +124,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
removeFilter(config) {
removeFilter(config: any) {
const { path, method } = this.FILTERING_REMOVE_FILTER;
const parameters = {
data: config,
@@ -131,7 +133,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
setRules(rules) {
setRules(rules: any) {
const { path, method } = this.FILTERING_SET_RULES;
const parameters = {
data: rules,
@@ -139,7 +141,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
setFiltersConfig(config) {
setFiltersConfig(config: any) {
const { path, method } = this.FILTERING_CONFIG;
const parameters = {
data: config,
@@ -147,7 +149,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
setFilterUrl(config) {
setFilterUrl(config: any) {
const { path, method } = this.FILTERING_SET_URL;
const parameters = {
data: config,
@@ -155,9 +157,10 @@ class Api {
return this.makeRequest(path, method, parameters);
}
checkHost(params) {
checkHost(params: any) {
const { path, method } = this.FILTERING_CHECK_HOST;
const url = getPathWithQueryString(path, params);
return this.makeRequest(url, method);
}
@@ -170,16 +173,19 @@ class Api {
getParentalStatus() {
const { path, method } = this.PARENTAL_STATUS;
return this.makeRequest(path, method);
}
enableParentalControl() {
const { path, method } = this.PARENTAL_ENABLE;
return this.makeRequest(path, method);
}
disableParentalControl() {
const { path, method } = this.PARENTAL_DISABLE;
return this.makeRequest(path, method);
}
@@ -192,16 +198,19 @@ class Api {
getSafebrowsingStatus() {
const { path, method } = this.SAFEBROWSING_STATUS;
return this.makeRequest(path, method);
}
enableSafebrowsing() {
const { path, method } = this.SAFEBROWSING_ENABLE;
return this.makeRequest(path, method);
}
disableSafebrowsing() {
const { path, method } = this.SAFEBROWSING_DISABLE;
return this.makeRequest(path, method);
}
@@ -212,6 +221,7 @@ class Api {
getSafesearchStatus() {
const { path, method } = this.SAFESEARCH_STATUS;
return this.makeRequest(path, method);
}
@@ -228,7 +238,7 @@ class Api {
* @param {*} data - SafeSearchConfig
* @returns 200 ok
*/
updateSafesearch(data) {
updateSafesearch(data: any) {
const { path, method } = this.SAFESEARCH_UPDATE;
return this.makeRequest(path, method, { data });
}
@@ -245,7 +255,7 @@ class Api {
// Language
async changeLanguage(config) {
async changeLanguage(config: any) {
const profile = await this.getProfile();
profile.language = config.language;
@@ -254,7 +264,7 @@ class Api {
// Theme
async changeTheme(config) {
async changeTheme(config: any) {
const profile = await this.getProfile();
profile.theme = config.theme;
@@ -282,15 +292,17 @@ class Api {
getDhcpStatus() {
const { path, method } = this.DHCP_STATUS;
return this.makeRequest(path, method);
}
getDhcpInterfaces() {
const { path, method } = this.DHCP_INTERFACES;
return this.makeRequest(path, method);
}
setDhcpConfig(config) {
setDhcpConfig(config: any) {
const { path, method } = this.DHCP_SET_CONFIG;
const parameters = {
data: config,
@@ -298,7 +310,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
findActiveDhcp(req) {
findActiveDhcp(req: any) {
const { path, method } = this.DHCP_FIND_ACTIVE;
const parameters = {
data: req,
@@ -306,7 +318,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
addStaticLease(config) {
addStaticLease(config: any) {
const { path, method } = this.DHCP_ADD_STATIC_LEASE;
const parameters = {
data: config,
@@ -314,7 +326,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
removeStaticLease(config) {
removeStaticLease(config: any) {
const { path, method } = this.DHCP_REMOVE_STATIC_LEASE;
const parameters = {
data: config,
@@ -322,7 +334,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
updateStaticLease(config) {
updateStaticLease(config: any) {
const { path, method } = this.DHCP_UPDATE_STATIC_LEASE;
const parameters = {
data: config,
@@ -332,11 +344,13 @@ class Api {
resetDhcp() {
const { path, method } = this.DHCP_RESET;
return this.makeRequest(path, method);
}
resetDhcpLeases() {
const { path, method } = this.DHCP_LEASES_RESET;
return this.makeRequest(path, method);
}
@@ -349,10 +363,11 @@ class Api {
getDefaultAddresses() {
const { path, method } = this.INSTALL_GET_ADDRESSES;
return this.makeRequest(path, method);
}
setAllSettings(config) {
setAllSettings(config: any) {
const { path, method } = this.INSTALL_CONFIGURE;
const parameters = {
data: config,
@@ -360,7 +375,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
checkConfig(config) {
checkConfig(config: any) {
const { path, method } = this.INSTALL_CHECK_CONFIG;
const parameters = {
data: config,
@@ -377,10 +392,11 @@ class Api {
getTlsStatus() {
const { path, method } = this.TLS_STATUS;
return this.makeRequest(path, method);
}
setTlsConfig(config) {
setTlsConfig(config: any) {
const { path, method } = this.TLS_CONFIG;
const parameters = {
data: config,
@@ -388,7 +404,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
validateTlsConfig(config) {
validateTlsConfig(config: any) {
const { path, method } = this.TLS_VALIDATE;
const parameters = {
data: config,
@@ -409,10 +425,11 @@ class Api {
getClients() {
const { path, method } = this.GET_CLIENTS;
return this.makeRequest(path, method);
}
addClient(config) {
addClient(config: any) {
const { path, method } = this.ADD_CLIENT;
const parameters = {
data: config,
@@ -420,7 +437,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
deleteClient(config) {
deleteClient(config: any) {
const { path, method } = this.DELETE_CLIENT;
const parameters = {
data: config,
@@ -428,7 +445,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
updateClient(config) {
updateClient(config: any) {
const { path, method } = this.UPDATE_CLIENT;
const parameters = {
data: config,
@@ -436,9 +453,10 @@ class Api {
return this.makeRequest(path, method, parameters);
}
findClients(params) {
findClients(params: any) {
const { path, method } = this.FIND_CLIENTS;
const url = getPathWithQueryString(path, params);
return this.makeRequest(url, method);
}
@@ -449,10 +467,11 @@ class Api {
getAccessList() {
const { path, method } = this.ACCESS_LIST;
return this.makeRequest(path, method);
}
setAccessList(config) {
setAccessList(config: any) {
const { path, method } = this.ACCESS_SET;
const parameters = {
data: config,
@@ -471,10 +490,11 @@ class Api {
getRewritesList() {
const { path, method } = this.REWRITES_LIST;
return this.makeRequest(path, method);
}
addRewrite(config) {
addRewrite(config: any) {
const { path, method } = this.REWRITE_ADD;
const parameters = {
data: config,
@@ -482,7 +502,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
updateRewrite(config) {
updateRewrite(config: any) {
const { path, method } = this.REWRITE_UPDATE;
const parameters = {
data: config,
@@ -490,7 +510,7 @@ class Api {
return this.makeRequest(path, method, parameters);
}
deleteRewrite(config) {
deleteRewrite(config: any) {
const { path, method } = this.REWRITE_DELETE;
const parameters = {
data: config,
@@ -507,15 +527,17 @@ class Api {
getAllBlockedServices() {
const { path, method } = this.BLOCKED_SERVICES_ALL;
return this.makeRequest(path, method);
}
getBlockedServices() {
const { path, method } = this.BLOCKED_SERVICES_GET;
return this.makeRequest(path, method);
}
updateBlockedServices(config) {
updateBlockedServices(config: any) {
const { path, method } = this.BLOCKED_SERVICES_UPDATE;
const parameters = {
data: config,
@@ -534,15 +556,17 @@ class Api {
getStats() {
const { path, method } = this.GET_STATS;
return this.makeRequest(path, method);
}
getStatsConfig() {
const { path, method } = this.GET_STATS_CONFIG;
return this.makeRequest(path, method);
}
setStatsConfig(data) {
setStatsConfig(data: any) {
const { path, method } = this.UPDATE_STATS_CONFIG;
const config = {
data,
@@ -552,6 +576,7 @@ class Api {
resetStats() {
const { path, method } = this.STATS_RESET;
return this.makeRequest(path, method);
}
@@ -564,20 +589,22 @@ class Api {
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
getQueryLog(params) {
getQueryLog(params: any) {
const { path, method } = this.GET_QUERY_LOG;
// eslint-disable-next-line no-param-reassign
params.limit = QUERY_LOGS_PAGE_LIMIT;
const url = getPathWithQueryString(path, params);
return this.makeRequest(url, method);
}
getQueryLogConfig() {
const { path, method } = this.GET_QUERY_LOG_CONFIG;
return this.makeRequest(path, method);
}
setQueryLogConfig(data) {
setQueryLogConfig(data: any) {
const { path, method } = this.UPDATE_QUERY_LOG_CONFIG;
const config = {
data,
@@ -587,13 +614,14 @@ class Api {
clearQueryLog() {
const { path, method } = this.QUERY_LOG_CLEAR;
return this.makeRequest(path, method);
}
// Login
LOGIN = { path: 'login', method: 'POST' };
login(data) {
login(data: any) {
const { path, method } = this.LOGIN;
const config = {
data,
@@ -608,10 +636,11 @@ class Api {
getProfile() {
const { path, method } = this.GET_PROFILE;
return this.makeRequest(path, method);
}
setProfile(data) {
setProfile(data: any) {
const theme = data.theme ? data.theme : THEMES.auto;
const defaultLanguage = i18n.language ? i18n.language : LANGUAGES.en;
const language = data.language ? data.language : defaultLanguage;
@@ -629,10 +658,11 @@ class Api {
getDnsConfig() {
const { path, method } = this.GET_DNS_CONFIG;
return this.makeRequest(path, method);
}
setDnsConfig(data) {
setDnsConfig(data: any) {
const { path, method } = this.SET_DNS_CONFIG;
const config = {
data,
@@ -642,7 +672,7 @@ class Api {
SET_PROTECTION = { path: 'protection', method: 'POST' };
setProtection(data) {
setProtection(data: any) {
const { enabled, duration } = data;
const { path, method } = this.SET_PROTECTION;
@@ -654,6 +684,7 @@ class Api {
clearCache() {
const { path, method } = this.CLEAR_CACHE;
return this.makeRequest(path, method);
}
}

View File

@@ -15,8 +15,8 @@
--btn-success-bgcolor: #5eba00;
--form-disabled-bgcolor: #f8f9fa;
--form-disabled-color: #495057;
--rt-nodata-bgcolor: rgba(255,255,255,0.8);
--rt-nodata-color: rgba(0,0,0,0.5);
--rt-nodata-bgcolor: rgba(255, 255, 255, 0.8);
--rt-nodata-color: rgba(0, 0, 0, 0.5);
--modal-overlay-bgcolor: rgba(255, 255, 255, 0.75);
--logs__table-bgcolor: #fff;
--logs__row--blue-bgcolor: #e5effd;
@@ -28,7 +28,7 @@
--gray-d8: #d8d8d8;
--gray-f3: #f3f3f3;
--loading-bg: rgba(255, 255, 255, 0.48);
--font-family-monospace: Monaco, Menlo, "Ubuntu Mono", Consolas, source-code-pro, monospace;
--font-family-monospace: Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace;
--font-size-disable-autozoom: 1rem;
--alert-message-color: #24426c;
--alert-message-border: #cbdbf2;
@@ -37,7 +37,7 @@
--radio-bg: #ffffff;
}
[data-theme="dark"] {
[data-theme='dark'] {
--black: #ffffff;
--bgcolor: #131313;
--mcolor: #e6e6e6;
@@ -74,12 +74,14 @@
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
}
/* Disable Auto Zoom in Input - Safari on iPhone https://stackoverflow.com/a/6394497 */
@media screen and (max-width: 767px) {
input, select, textarea {
input,
select,
textarea {
font-size: var(--font-size-disable-autozoom);
}
}

View File

@@ -1,4 +1,5 @@
import React, { useEffect } from 'react';
import { HashRouter, Route } from 'react-router-dom';
import LoadingBar from 'react-redux-loading-bar';
import { hot } from 'react-hot-loader/root';
@@ -9,8 +10,6 @@ import '../ui/ReactTable.css';
import './index.css';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import propTypes from 'prop-types';
import Toasts from '../Toasts';
import Footer from '../ui/Footer';
import Status from '../ui/Status';
@@ -19,15 +18,14 @@ import UpdateOverlay from '../ui/UpdateOverlay';
import EncryptionTopline from '../ui/EncryptionTopline';
import Icons from '../ui/Icons';
import i18n from '../../i18n';
import Loading from '../ui/Loading';
import {
FILTERS_URLS,
MENU_URLS,
SETTINGS_URLS,
THEMES,
} from '../../helpers/constants';
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS, THEMES } from '../../helpers/constants';
import { getLogsUrlParams, setHtmlLangAttr, setUITheme } from '../../helpers/helpers';
import Header from '../Header';
import { changeLanguage, getDnsStatus, getTimerStatus } from '../../actions';
import Dashboard from '../../containers/Dashboard';
@@ -35,15 +33,19 @@ import SetupGuide from '../../containers/SetupGuide';
import Settings from '../../containers/Settings';
import Dns from '../../containers/Dns';
import Encryption from '../../containers/Encryption';
import Dhcp from '../Settings/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 Logs from '../Logs';
import ProtectionTimer from '../ProtectionTimer';
import { RootState } from '../../initialState';
const ROUTES = [
{
@@ -101,26 +103,17 @@ const ROUTES = [
},
];
const renderRoute = ({ path, component, exact }, idx) => <Route
key={idx}
exact={exact}
path={path}
component={component}
/>;
const App = () => {
const dispatch = useDispatch();
const {
language,
isCoreRunning,
isUpdateAvailable,
processing,
theme,
} = useSelector((state) => state.dashboard, shallowEqual);
const { language, isCoreRunning, isUpdateAvailable, processing, theme } = useSelector<
RootState,
RootState['dashboard']
>((state) => state.dashboard, shallowEqual);
const { processing: processingEncryption } = useSelector((
state,
) => state.encryption, shallowEqual);
const { processing: processingEncryption } = useSelector<RootState, RootState['encryption']>(
(state) => state.encryption,
shallowEqual,
);
const updateAvailable = isCoreRunning && isUpdateAvailable;
@@ -157,7 +150,7 @@ const App = () => {
setLanguage();
}, [language]);
const handleAutoTheme = (e, accountTheme) => {
const handleAutoTheme = (e: any, accountTheme: any) => {
if (accountTheme !== THEMES.auto) {
return;
}
@@ -195,35 +188,50 @@ const App = () => {
window.location.reload();
};
return <HashRouter hashType="noslash">
{updateAvailable && <>
<UpdateTopline />
<UpdateOverlay />
</>}
{!processingEncryption && <EncryptionTopline />}
<LoadingBar className="loading-bar" updateTime={1000} />
<Header />
<ProtectionTimer />
<div className="container container--wrap pb-5 pt-5">
{processing && <Loading />}
{!isCoreRunning && <div className="row row-cards">
<div className="col-lg-12">
<Status reloadPage={reloadPage} message="dns_start" />
<Loading />
</div>
</div>}
{!processing && isCoreRunning && ROUTES.map(renderRoute)}
</div>
<Footer />
<Toasts />
<Icons />
</HashRouter>;
};
return (
<HashRouter hashType="noslash">
{updateAvailable && (
<>
<UpdateTopline />
renderRoute.propTypes = {
path: propTypes.oneOfType([propTypes.string, propTypes.arrayOf(propTypes.string)]).isRequired,
component: propTypes.element.isRequired,
exact: propTypes.bool,
<UpdateOverlay />
</>
)}
{!processingEncryption && <EncryptionTopline />}
<LoadingBar className="loading-bar" updateTime={1000} />
<Header />
<ProtectionTimer />
<div className="container container--wrap pb-5 pt-5">
{processing && <Loading />}
{!isCoreRunning && (
<div className="row row-cards">
<div className="col-lg-12">
<Status reloadPage={reloadPage} message="dns_start" />
<Loading />
</div>
</div>
)}
{!processing &&
isCoreRunning &&
ROUTES.map((route, index) => (
<Route key={index} exact={route.exact} path={route.path} component={route.component} />
))}
</div>
<Footer />
<Toasts />
<Icons />
</HashRouter>
);
};
export default hot(App);

View File

@@ -1,25 +1,37 @@
import React from 'react';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import PropTypes from 'prop-types';
import { withTranslation, Trans } from 'react-i18next';
import { TFunction } from 'i18next';
import Card from '../ui/Card';
import Cell from '../ui/Cell';
import DomainCell from './DomainCell';
import { getPercent } from '../../helpers/helpers';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, STATUS_COLORS, TABLES_MIN_ROWS } from '../../helpers/constants';
const CountCell = (totalBlocked) => function cell(row) {
const { value } = row;
const percent = getPercent(totalBlocked, value);
const CountCell = (totalBlocked: any) =>
function cell(row: any) {
const { value } = row;
const percent = getPercent(totalBlocked, value);
return <Cell value={value}
percent={percent}
color={STATUS_COLORS.red}
search={row.original.domain}
/>;
};
return <Cell value={value} percent={percent} color={STATUS_COLORS.red} search={row.original.domain} />;
};
interface BlockedDomainsProps {
topBlockedDomains: unknown[];
blockedFiltering: number;
replacedSafebrowsing: number;
replacedSafesearch: number;
replacedParental: number;
refreshButton: React.ReactNode;
subtitle: string;
t: TFunction;
}
const BlockedDomains = ({
t,
@@ -30,20 +42,13 @@ const BlockedDomains = ({
replacedSafebrowsing,
replacedParental,
replacedSafesearch,
}) => {
const totalBlocked = (
blockedFiltering + replacedSafebrowsing + replacedParental + replacedSafesearch
);
}: BlockedDomainsProps) => {
const totalBlocked = blockedFiltering + replacedSafebrowsing + replacedParental + replacedSafesearch;
return (
<Card
title={t('top_blocked_domains')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
<Card title={t('top_blocked_domains')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<ReactTable
data={topBlockedDomains.map(({ name: domain, count }) => ({
data={topBlockedDomains.map(({ name: domain, count }: any) => ({
domain,
count,
}))}
@@ -70,15 +75,4 @@ const BlockedDomains = ({
);
};
BlockedDomains.propTypes = {
topBlockedDomains: PropTypes.array.isRequired,
blockedFiltering: PropTypes.number.isRequired,
replacedSafebrowsing: PropTypes.number.isRequired,
replacedSafesearch: PropTypes.number.isRequired,
replacedParental: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(BlockedDomains);

View File

@@ -1,10 +1,12 @@
import React, { useState } from 'react';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import PropTypes from 'prop-types';
import { Trans, useTranslation } from 'react-i18next';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import classNames from 'classnames';
import Card from '../ui/Card';
import Cell from '../ui/Cell';
@@ -16,11 +18,14 @@ import {
TABLES_MIN_ROWS,
} from '../../helpers/constants';
import { toggleClientBlock } from '../../actions/access';
import { renderFormattedClientCell } from '../../helpers/renderFormattedClientCell';
import { getStats } from '../../actions/stats';
import IconTooltip from '../Logs/Cells/IconTooltip';
const getClientsPercentColor = (percent) => {
import IconTooltip from '../Logs/Cells/IconTooltip';
import { RootState } from '../../initialState';
const getClientsPercentColor = (percent: any) => {
if (percent > 50) {
return STATUS_COLORS.green;
}
@@ -30,9 +35,13 @@ const getClientsPercentColor = (percent) => {
return STATUS_COLORS.red;
};
const CountCell = (row) => {
const { value, original: { ip } } = row;
const numDnsQueries = useSelector((state) => state.stats.numDnsQueries, shallowEqual);
const CountCell = (row: any) => {
const {
value,
original: { ip },
} = row;
const numDnsQueries = useSelector<RootState>((state) => state.stats.numDnsQueries, shallowEqual);
const percent = getPercent(numDnsQueries, value);
const percentColor = getClientsPercentColor(percent);
@@ -40,22 +49,29 @@ const CountCell = (row) => {
return <Cell value={value} percent={percent} color={percentColor} search={ip} />;
};
const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) => {
const dispatch = useDispatch();
const { t } = useTranslation();
const processingSet = useSelector((state) => state.access.processingSet);
const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual);
const processingSet = useSelector<RootState, RootState['access']['processingSet']>(
(state) => state.access.processingSet,
);
const allowedClients = useSelector<RootState, RootState['access']['allowed_clients']>(
(state) => state.access.allowed_clients,
shallowEqual,
);
const [isOptionsOpened, setOptionsOpened] = useState(false);
const toggleClientStatus = async (ip, disallowed, disallowed_rule) => {
const toggleClientStatus = async (ip: any, disallowed: any, disallowed_rule: any) => {
let confirmMessage;
if (disallowed) {
confirmMessage = t('client_confirm_unblock', { ip: disallowed_rule || ip });
} else {
confirmMessage = `${t('adg_will_drop_dns_queries')} ${t('client_confirm_block', { ip })}`;
if (allowedСlients.length > 0) {
if (allowedClients.length > 0) {
confirmMessage = confirmMessage.concat(`\n\n${t('filter_allowlist', { disallowed_rule })}`);
}
}
@@ -73,15 +89,11 @@ const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
const text = disallowed ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
const lastRuleInAllowlist = !disallowed && allowedСlients === disallowed_rule;
const lastRuleInAllowlist = !disallowed && allowedClients === disallowed_rule;
const disabled = processingSet || lastRuleInAllowlist;
return (
<div className="table__action">
<button
type="button"
className="btn btn-icon btn-sm px-0"
onClick={() => setOptionsOpened(true)}
>
<button type="button" className="btn btn-icon btn-sm px-0" onClick={() => setOptionsOpened(true)}>
<svg className="icon24 icon--lightgray button-action__icon">
<use xlinkHref="#bullets" />
</svg>
@@ -92,16 +104,18 @@ const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
tooltipClass="button-action--arrow-option-container"
xlinkHref="bullets"
triggerClass="btn btn-icon btn-sm px-0 button-action__hidden-trigger"
content={(
content={
<button
className={classNames('button-action--arrow-option px-4 py-1', disallowed ? 'bg--green' : 'bg--danger')}
className={classNames(
'button-action--arrow-option px-4 py-1',
disallowed ? 'bg--green' : 'bg--danger',
)}
onClick={onClick}
disabled={disabled}
title={lastRuleInAllowlist ? t('last_rule_in_allowlist', { disallowed_rule }) : ''}
>
title={lastRuleInAllowlist ? t('last_rule_in_allowlist', { disallowed_rule }) : ''}>
<Trans>{text}</Trans>
</button>
)}
}
placement="bottom-end"
trigger="click"
onVisibilityChange={setOptionsOpened}
@@ -113,35 +127,42 @@ const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
);
};
const ClientCell = (row) => {
const { value, original: { info, info: { disallowed, disallowed_rule } } } = row;
return <>
<div className="logs__row logs__row--overflow logs__row--column d-flex align-items-center">
{renderFormattedClientCell(value, info, true)}
{renderBlockingButton(value, disallowed, disallowed_rule)}
</div>
</>;
};
const Clients = ({
refreshButton,
subtitle,
}) => {
const { t } = useTranslation();
const topClients = useSelector((state) => state.stats.topClients, shallowEqual);
const ClientCell = (row: any) => {
const {
value,
original: {
info,
info: { disallowed, disallowed_rule },
},
} = row;
return (
<Card
title={t('top_clients')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
<>
<div className="logs__row logs__row--overflow logs__row--column d-flex align-items-center">
{renderFormattedClientCell(value, info, true)}
{renderBlockingButton(value, disallowed, disallowed_rule)}
</div>
</>
);
};
interface ClientsProps {
refreshButton: React.ReactNode;
subtitle: string;
}
const Clients = ({ refreshButton, subtitle }: ClientsProps) => {
const { t } = useTranslation();
const topClients = useSelector<RootState, RootState['stats']['topClients']>(
(state) => state.stats.topClients,
shallowEqual,
);
return (
<Card title={t('top_clients')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<ReactTable
data={topClients.map(({
name: ip, count, info, blocked,
}) => ({
data={topClients.map(({ name: ip, count, info, blocked }: any) => ({
ip,
count,
info,
@@ -167,12 +188,14 @@ const Clients = ({
minRows={TABLES_MIN_ROWS}
defaultPageSize={DASHBOARD_TABLES_DEFAULT_PAGE_SIZE}
className="-highlight card-table-overflow--limited clients__table"
getTrProps={(_state, rowInfo) => {
getTrProps={(_state: any, rowInfo: any) => {
if (!rowInfo) {
return {};
}
const { info: { disallowed } } = rowInfo.original;
const {
info: { disallowed },
} = rowInfo.original;
return disallowed ? { className: 'logs__row--red' } : {};
}}
@@ -181,9 +204,4 @@ const Clients = ({
);
};
Clients.propTypes = {
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
};
export default Clients;

View File

@@ -1,41 +1,52 @@
import React from 'react';
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 { formatNumber, msToDays, msToHours } from '../../helpers/helpers';
import LogsSearchLink from '../ui/LogsSearchLink';
import { RESPONSE_FILTER, TIME_UNITS } from '../../helpers/constants';
import Tooltip from '../ui/Tooltip';
const Row = ({
label, count, response_status, tooltipTitle, translationComponents,
}) => {
const content = response_status
? <LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
: count;
import Tooltip from '../ui/Tooltip';
import { RootState } from '../../initialState';
interface RowProps {
label: string;
count: string;
response_status?: string;
tooltipTitle: string;
translationComponents?: React.ReactElement[];
}
const Row = ({ label, count, response_status, tooltipTitle, translationComponents }: RowProps) => {
const content = response_status ? (
<LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
) : (
count
);
return (
<div className="counters__row" key={label}>
<div className="counters__column">
<span className="counters__title">
<Trans components={translationComponents}>
{label}
</Trans>
<Trans components={translationComponents}>{label}</Trans>
</span>
<span className="counters__tooltip">
<Tooltip
content={tooltipTitle}
placement="top"
className="tooltip-container tooltip-custom--narrow text-center"
>
className="tooltip-container tooltip-custom--narrow text-center">
<svg className="icons icon--20 icon--lightgray ml-2">
<use xlinkHref="#question" />
</svg>
</Tooltip>
</span>
</div>
<div className="counters__column counters__column--value">
<strong>{content}</strong>
</div>
@@ -43,7 +54,12 @@ const Row = ({
);
};
const Counters = ({ refreshButton, subtitle }) => {
interface CountersProps {
refreshButton: React.ReactNode;
subtitle: string;
}
const Counters = ({ refreshButton, subtitle }: CountersProps) => {
const {
interval,
numDnsQueries,
@@ -53,77 +69,67 @@ const Counters = ({ refreshButton, subtitle }) => {
numReplacedSafesearch,
avgProcessingTime,
timeUnits,
} = useSelector((state) => state.stats, shallowEqual);
} = useSelector<RootState, RootState['stats']>((state) => state.stats, shallowEqual);
const { t } = useTranslation();
const dnsQueryTooltip = timeUnits === TIME_UNITS.HOURS
? t('number_of_dns_query_hours', { count: msToHours(interval) })
: t('number_of_dns_query_days', { count: msToDays(interval) });
const dnsQueryTooltip =
timeUnits === TIME_UNITS.HOURS
? t('number_of_dns_query_hours', { count: msToHours(interval) })
: t('number_of_dns_query_days', { count: msToDays(interval) });
const rows = [
{
label: 'dns_query',
count: numDnsQueries,
count: numDnsQueries.toString(),
tooltipTitle: dnsQueryTooltip,
response_status: RESPONSE_FILTER.ALL.QUERY,
},
{
label: 'blocked_by',
count: numBlockedFiltering,
count: numBlockedFiltering.toString(),
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
response_status: RESPONSE_FILTER.BLOCKED.QUERY,
translationComponents: [<a href="#filters" key="0">link</a>],
translationComponents: [
<a href="#filters" key="0">
link
</a>,
],
},
{
label: 'stats_malware_phishing',
count: numReplacedSafebrowsing,
count: numReplacedSafebrowsing.toString(),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
},
{
label: 'stats_adult',
count: numReplacedParental,
count: numReplacedParental.toString(),
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
},
{
label: 'enforced_save_search',
count: numReplacedSafesearch,
count: numReplacedSafesearch.toString(),
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,
count: avgProcessingTime ? `${round(avgProcessingTime)} ms` : '0',
tooltipTitle: 'average_processing_time_hint',
},
];
return (
<Card
title={t('general_statistics')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
<Card title={t('general_statistics')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<div className="counters">
{rows.map(Row)}
{rows.map((row, index) => {
return <Row {...row} key={index} />;
})}
</div>
</Card>
);
};
Row.propTypes = {
label: propTypes.string.isRequired,
count: propTypes.string.isRequired,
response_status: propTypes.string,
tooltipTitle: propTypes.string.isRequired,
translationComponents: propTypes.arrayOf(propTypes.element),
};
Counters.propTypes = {
refreshButton: propTypes.node.isRequired,
subtitle: propTypes.string.isRequired,
};
export default Counters;

View File

@@ -1,77 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
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" title={value}>
{value}
</div>
{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>
);
};
DomainCell.propTypes = {
value: PropTypes.string.isRequired,
};
renderLink.propTypes = {
url: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
};
export default DomainCell;

View File

@@ -0,0 +1,81 @@
import React from 'react';
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: any) => (
<strong>
<Trans>{value}</Trans>
</strong>
);
interface renderLinkProps {
url: string;
name: string;
}
const renderLink = ({ url, name }: renderLinkProps) => (
<a className="tooltip-custom__content-link" target="_blank" rel="noopener noreferrer" href={url}>
<strong>{name}</strong>
</a>
);
const getTrackerInfo = (trackerData: any) => [
{
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,
},
];
interface DomainCellProps {
value: string;
}
const DomainCell = ({ value }: DomainCellProps) => {
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" title={value}>
{value}
</div>
{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>
);
};
export default DomainCell;

View File

@@ -1,6 +1,7 @@
import React from 'react';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import PropTypes from 'prop-types';
import { withTranslation, Trans } from 'react-i18next';
import Card from '../ui/Card';
@@ -8,9 +9,10 @@ import Cell from '../ui/Cell';
import DomainCell from './DomainCell';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, STATUS_COLORS, TABLES_MIN_ROWS } from '../../helpers/constants';
import { getPercent } from '../../helpers/helpers';
const getQueriedPercentColor = (percent) => {
const getQueriedPercentColor = (percent: any) => {
if (percent > 10) {
return STATUS_COLORS.red;
}
@@ -20,26 +22,27 @@ const getQueriedPercentColor = (percent) => {
return STATUS_COLORS.green;
};
const countCell = (dnsQueries) => function cell(row) {
const { value } = row;
const percent = getPercent(dnsQueries, value);
const percentColor = getQueriedPercentColor(percent);
const countCell = (dnsQueries: any) =>
function cell(row: any) {
const { value } = row;
const percent = getPercent(dnsQueries, value);
const percentColor = getQueriedPercentColor(percent);
return <Cell value={value} percent={percent} color={percentColor}
search={row.original.domain} />;
};
return <Cell value={value} percent={percent} color={percentColor} search={row.original.domain} />;
};
const QueriedDomains = ({
t, refreshButton, topQueriedDomains, subtitle, dnsQueries,
}) => (
<Card
title={t('stats_query_domain')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
interface QueriedDomainsProps {
topQueriedDomains: unknown[];
dnsQueries: number;
refreshButton: React.ReactNode;
subtitle: string;
t: (...args: unknown[]) => string;
}
const QueriedDomains = ({ t, refreshButton, topQueriedDomains, subtitle, dnsQueries }: QueriedDomainsProps) => (
<Card title={t('stats_query_domain')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<ReactTable
data={topQueriedDomains.map(({ name: domain, count }) => ({
data={topQueriedDomains.map(({ name: domain, count }: any) => ({
domain,
count,
}))}
@@ -65,12 +68,4 @@ const QueriedDomains = ({
</Card>
);
QueriedDomains.propTypes = {
topQueriedDomains: PropTypes.array.isRequired,
dnsQueries: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(QueriedDomains);

View File

@@ -1,15 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { withTranslation, Trans } from 'react-i18next';
import StatsCard from './StatsCard';
import { getPercent, normalizeHistory } from '../../helpers/helpers';
import { RESPONSE_FILTER } from '../../helpers/constants';
const getNormalizedHistory = (data, interval, id) => [
{ data: normalizeHistory(data, interval), id },
];
const getNormalizedHistory = (data: any, interval: any, id: any) => [{ data: normalizeHistory(data), id }];
interface StatisticsProps {
interval: number;
dnsQueries: number[];
blockedFiltering: unknown[];
replacedSafebrowsing: unknown[];
replacedParental: unknown[];
numDnsQueries: number;
numBlockedFiltering: number;
numReplacedSafebrowsing: number;
numReplacedParental: number;
refreshButton: React.ReactNode;
}
const Statistics = ({
interval,
@@ -21,61 +33,68 @@ const Statistics = ({
numBlockedFiltering,
numReplacedSafebrowsing,
numReplacedParental,
}) => (
}: StatisticsProps) => (
<div className="row">
<div className="col-sm-6 col-lg-3">
<StatsCard
total={numDnsQueries}
lineData={getNormalizedHistory(dnsQueries, interval, 'dnsQuery')}
title={<Link to="logs"><Trans>dns_query</Trans></Link>}
title={
<Link to="logs">
<Trans>dns_query</Trans>
</Link>
}
color="blue"
/>
</div>
<div className="col-sm-6 col-lg-3">
<StatsCard
total={numBlockedFiltering}
lineData={getNormalizedHistory(blockedFiltering, interval, 'blockedFiltering')}
percent={getPercent(numDnsQueries, numBlockedFiltering)}
title={<Trans components={[<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED.QUERY}`} key="0">link</Link>]}>blocked_by</Trans>}
title={
<Trans
components={[
<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED.QUERY}`} key="0">
link
</Link>,
]}>
blocked_by
</Trans>
}
color="red"
/>
</div>
<div className="col-sm-6 col-lg-3">
<StatsCard
total={numReplacedSafebrowsing}
lineData={getNormalizedHistory(
replacedSafebrowsing,
interval,
'replacedSafebrowsing',
)}
lineData={getNormalizedHistory(replacedSafebrowsing, interval, 'replacedSafebrowsing')}
percent={getPercent(numDnsQueries, numReplacedSafebrowsing)}
title={<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_THREATS.QUERY}`}><Trans>stats_malware_phishing</Trans></Link>}
title={
<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_THREATS.QUERY}`}>
<Trans>stats_malware_phishing</Trans>
</Link>
}
color="green"
/>
</div>
<div className="col-sm-6 col-lg-3">
<StatsCard
total={numReplacedParental}
lineData={getNormalizedHistory(replacedParental, interval, 'replacedParental')}
percent={getPercent(numDnsQueries, numReplacedParental)}
title={<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY}`}><Trans>stats_adult</Trans></Link>}
title={
<Link to={`logs?response_status=${RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY}`}>
<Trans>stats_adult</Trans>
</Link>
}
color="yellow"
/>
</div>
</div>
);
Statistics.propTypes = {
interval: PropTypes.number.isRequired,
dnsQueries: PropTypes.array.isRequired,
blockedFiltering: PropTypes.array.isRequired,
replacedSafebrowsing: PropTypes.array.isRequired,
replacedParental: PropTypes.array.isRequired,
numDnsQueries: PropTypes.number.isRequired,
numBlockedFiltering: PropTypes.number.isRequired,
numReplacedSafebrowsing: PropTypes.number.isRequired,
numReplacedParental: PropTypes.number.isRequired,
refreshButton: PropTypes.node.isRequired,
};
export default withTranslation()(Statistics);

View File

@@ -1,38 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
import { STATUS_COLORS } from '../../helpers/constants';
import { formatNumber } from '../../helpers/helpers';
import Card from '../ui/Card';
import Line from '../ui/Line';
const StatsCard = ({
total, lineData, percent, title, color,
}) => (
interface StatsCardProps {
total: number;
lineData: unknown[];
title: object;
color: string;
percent?: number;
}
const StatsCard = ({ total, lineData, percent, title, color }: StatsCardProps) => (
<Card type="card--full" bodyType="card-wrap">
<div className="card-body-stats">
<div className={`card-value card-value-stats text-${color}`}>
{formatNumber(total)}
</div>
<div className={`card-value card-value-stats text-${color}`}>{formatNumber(total)}</div>
<div className="card-title-stats">{title}</div>
</div>
{percent >= 0 && (
<div className={`card-value card-value-percent text-${color}`}>
{percent}
</div>
)}
{percent >= 0 && <div className={`card-value card-value-percent text-${color}`}>{percent}</div>}
<div className="card-chart-bg">
<Line data={lineData} color={STATUS_COLORS[color]} />
</div>
</Card>
);
StatsCard.propTypes = {
total: PropTypes.number.isRequired,
lineData: PropTypes.array.isRequired,
title: PropTypes.object.isRequired,
color: PropTypes.string.isRequired,
percent: PropTypes.number,
};
export default StatsCard;

View File

@@ -1,50 +1,47 @@
import React from 'react';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import PropTypes from 'prop-types';
import round from 'lodash/round';
import { withTranslation, Trans } from 'react-i18next';
import { TFunction } from 'i18next';
import Card from '../ui/Card';
import DomainCell from './DomainCell';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants';
const TimeCell = ({ value }) => {
interface TimeCellProps {
value?: string | number;
}
const TimeCell = ({ value }: TimeCellProps) => {
if (!value) {
return '';
}
const valueInMilliseconds = round(value * 1000);
const valueInMilliseconds = round(Number(value) * 1000);
return (
<div className="logs__row o-hidden">
<span className="logs__text logs__text--full" title={valueInMilliseconds}>
<span className="logs__text logs__text--full" title={valueInMilliseconds.toString()}>
{valueInMilliseconds}&nbsp;ms
</span>
</div>
);
};
TimeCell.propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
};
interface UpstreamAvgTimeProps {
topUpstreamsAvgTime: { name: string; count: number }[];
refreshButton: React.ReactNode;
subtitle: string;
t: TFunction;
}
const UpstreamAvgTime = ({
t,
refreshButton,
topUpstreamsAvgTime,
subtitle,
}) => (
<Card
title={t('average_upstream_response_time')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
const UpstreamAvgTime = ({ t, refreshButton, topUpstreamsAvgTime, subtitle }: UpstreamAvgTimeProps) => (
<Card title={t('average_upstream_response_time')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<ReactTable
data={topUpstreamsAvgTime.map(({ name: domain, count }) => ({
data={topUpstreamsAvgTime.map(({ name: domain, count }: { name: string; count: number }) => ({
domain,
count,
}))}
@@ -70,11 +67,4 @@ const UpstreamAvgTime = ({
</Card>
);
UpstreamAvgTime.propTypes = {
topUpstreamsAvgTime: PropTypes.array.isRequired,
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(UpstreamAvgTime);

View File

@@ -1,51 +1,47 @@
import React from 'react';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import PropTypes from 'prop-types';
import { withTranslation, Trans } from 'react-i18next';
import { TFunction } from 'i18next';
import Card from '../ui/Card';
import Cell from '../ui/Cell';
import DomainCell from './DomainCell';
import { getPercent } from '../../helpers/helpers';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, STATUS_COLORS, TABLES_MIN_ROWS } from '../../helpers/constants';
const CountCell = (totalBlocked) => (
function cell(row) {
const CountCell = (totalBlocked: any) =>
function cell(row: any) {
const { value } = row;
const percent = getPercent(totalBlocked, value);
return (
<Cell
value={value}
percent={percent}
color={STATUS_COLORS.green}
/>
);
}
);
return <Cell value={value} percent={percent} color={STATUS_COLORS.green} />;
};
const getTotalUpstreamRequests = (stats) => {
const getTotalUpstreamRequests = (stats: any) => {
let total = 0;
stats.forEach(({ count }) => { total += count; });
stats.forEach(({ count }: any) => {
total += count;
});
return total;
};
const UpstreamResponses = ({
t,
refreshButton,
topUpstreamsResponses,
subtitle,
}) => (
<Card
title={t('top_upstreams')}
subtitle={subtitle}
bodyType="card-table"
refresh={refreshButton}
>
interface UpstreamResponsesProps {
topUpstreamsResponses: { name: string; count: number }[];
refreshButton: React.ReactNode;
subtitle: string;
t: TFunction;
}
const UpstreamResponses = ({ t, refreshButton, topUpstreamsResponses, subtitle }: UpstreamResponsesProps) => (
<Card title={t('top_upstreams')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
<ReactTable
data={topUpstreamsResponses.map(({ name: domain, count }) => ({
data={topUpstreamsResponses.map(({ name: domain, count }: { name: string; count: number }) => ({
domain,
count,
}))}
@@ -71,11 +67,4 @@ const UpstreamResponses = ({
</Card>
);
UpstreamResponses.propTypes = {
topUpstreamsResponses: PropTypes.array.isRequired,
refreshButton: PropTypes.node.isRequired,
subtitle: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(UpstreamResponses);

View File

@@ -1,276 +0,0 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { HashLink as Link } from 'react-router-hash-link';
import { Trans, useTranslation } from 'react-i18next';
import classNames from 'classnames';
import Statistics from './Statistics';
import Counters from './Counters';
import Clients from './Clients';
import QueriedDomains from './QueriedDomains';
import BlockedDomains from './BlockedDomains';
import {
DISABLE_PROTECTION_TIMINGS,
ONE_SECOND_IN_MS,
SETTINGS_URLS,
TIME_UNITS,
} from '../../helpers/constants';
import {
msToSeconds,
msToMinutes,
msToHours,
msToDays,
} from '../../helpers/helpers';
import PageTitle from '../ui/PageTitle';
import Loading from '../ui/Loading';
import './Dashboard.css';
import Dropdown from '../ui/Dropdown';
import UpstreamResponses from './UpstreamResponses';
import UpstreamAvgTime from './UpstreamAvgTime';
const Dashboard = ({
getAccessList,
getStats,
getStatsConfig,
dashboard,
dashboard: { protectionEnabled, processingProtection, protectionDisabledDuration },
toggleProtection,
stats,
access,
}) => {
const { t } = useTranslation();
const getAllStats = () => {
getAccessList();
getStats();
getStatsConfig();
};
useEffect(() => {
getAllStats();
}, []);
const getSubtitle = () => {
if (!stats.enabled) {
return t('stats_disabled_short');
}
const msIn7Days = 604800000;
if (stats.timeUnits === TIME_UNITS.HOURS && stats.interval === msIn7Days) {
return t('for_last_days', { count: msToDays(stats.interval) });
}
return stats.timeUnits === TIME_UNITS.HOURS
? t('for_last_hours', { count: msToHours(stats.interval) })
: t('for_last_days', { count: msToDays(stats.interval) });
};
const buttonClass = classNames('btn btn-sm dashboard-protection-button', {
'btn-gray': protectionEnabled,
'btn-success': !protectionEnabled,
});
const refreshButton = <button
type="button"
className="btn btn-icon btn-outline-primary btn-sm"
title={t('refresh_btn')}
onClick={() => getAllStats()}
>
<svg className="icons icon12">
<use xlinkHref="#refresh" />
</svg>
</button>;
const statsProcessing = stats.processingStats
|| stats.processingGetConfig
|| access.processing;
const subtitle = getSubtitle();
const DISABLE_PROTECTION_ITEMS = [
{
text: t('disable_for_seconds', { count: msToSeconds(DISABLE_PROTECTION_TIMINGS.HALF_MINUTE) }),
disableTime: DISABLE_PROTECTION_TIMINGS.HALF_MINUTE,
},
{
text: t('disable_for_minutes', { count: msToMinutes(DISABLE_PROTECTION_TIMINGS.MINUTE) }),
disableTime: DISABLE_PROTECTION_TIMINGS.MINUTE,
},
{
text: t('disable_for_minutes', { count: msToMinutes(DISABLE_PROTECTION_TIMINGS.TEN_MINUTES) }),
disableTime: DISABLE_PROTECTION_TIMINGS.TEN_MINUTES,
},
{
text: t('disable_for_hours', { count: msToHours(DISABLE_PROTECTION_TIMINGS.HOUR) }),
disableTime: DISABLE_PROTECTION_TIMINGS.HOUR,
},
{
text: t('disable_until_tomorrow'),
disableTime: DISABLE_PROTECTION_TIMINGS.TOMORROW,
},
];
const getDisableProtectionItems = () => (
Object.values(DISABLE_PROTECTION_ITEMS)
.map((item, index) => (
<div
key={`disable_timings_${index}`}
className="dropdown-item"
onClick={() => {
toggleProtection(protectionEnabled, item.disableTime - ONE_SECOND_IN_MS);
}}
>
{item.text}
</div>
))
);
const getRemaningTimeText = (milliseconds) => {
if (!milliseconds) {
return '';
}
const date = new Date(milliseconds);
const hh = date.getUTCHours();
const mm = `0${date.getUTCMinutes()}`.slice(-2);
const ss = `0${date.getUTCSeconds()}`.slice(-2);
const formattedHH = `0${hh}`.slice(-2);
return hh ? `${formattedHH}:${mm}:${ss}` : `${mm}:${ss}`;
};
const getProtectionBtnText = (status) => (status ? t('disable_protection') : t('enable_protection'));
return <>
<PageTitle title={t('dashboard')} containerClass="page-title--dashboard">
<div className="page-title__protection">
<button
type="button"
className={buttonClass}
onClick={() => {
toggleProtection(protectionEnabled);
}}
disabled={processingProtection}
>
{protectionDisabledDuration
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
: getProtectionBtnText(protectionEnabled)
}
</button>
{protectionEnabled && <Dropdown
label=""
baseClassName="dropdown-protection"
icon="arrow-down"
controlClassName="dropdown-protection__toggle"
menuClassName="dropdown-menu dropdown-menu-arrow dropdown-menu--protection"
>
{getDisableProtectionItems()}
</Dropdown>}
</div>
<button
type="button"
className="btn btn-outline-primary btn-sm"
onClick={getAllStats}
>
<Trans>refresh_statics</Trans>
</button>
</PageTitle>
{statsProcessing && <Loading />}
{!statsProcessing && <div className="row row-cards dashboard">
<div className="col-lg-12">
{stats.interval === 0 && (
<div className="alert alert-warning" role="alert">
<Trans components={[
<Link
to={`${SETTINGS_URLS.settings}#stats-config`}
key="0"
>
link
</Link>,
]}>
stats_disabled
</Trans>
</div>
)}
<Statistics
interval={msToDays(stats.interval)}
dnsQueries={stats.dnsQueries}
blockedFiltering={stats.blockedFiltering}
replacedSafebrowsing={stats.replacedSafebrowsing}
replacedParental={stats.replacedParental}
numDnsQueries={stats.numDnsQueries}
numBlockedFiltering={stats.numBlockedFiltering}
numReplacedSafebrowsing={stats.numReplacedSafebrowsing}
numReplacedParental={stats.numReplacedParental}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<Counters
subtitle={subtitle}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<Clients
subtitle={subtitle}
dnsQueries={stats.numDnsQueries}
topClients={stats.topClients}
clients={dashboard.clients}
autoClients={dashboard.autoClients}
refreshButton={refreshButton}
processingAccessSet={access.processingSet}
disallowedClients={access.disallowed_clients}
/>
</div>
<div className="col-lg-6">
<QueriedDomains
subtitle={subtitle}
dnsQueries={stats.numDnsQueries}
topQueriedDomains={stats.topQueriedDomains}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<BlockedDomains
subtitle={subtitle}
topBlockedDomains={stats.topBlockedDomains}
blockedFiltering={stats.numBlockedFiltering}
replacedSafebrowsing={stats.numReplacedSafebrowsing}
replacedSafesearch={stats.numReplacedSafesearch}
replacedParental={stats.numReplacedParental}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<UpstreamResponses
subtitle={subtitle}
topUpstreamsResponses={stats.topUpstreamsResponses}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<UpstreamAvgTime
subtitle={subtitle}
topUpstreamsAvgTime={stats.topUpstreamsAvgTime}
refreshButton={refreshButton}
/>
</div>
</div>}
</>;
};
Dashboard.propTypes = {
dashboard: PropTypes.object.isRequired,
stats: PropTypes.object.isRequired,
access: PropTypes.object.isRequired,
getStats: PropTypes.func.isRequired,
getStatsConfig: PropTypes.func.isRequired,
toggleProtection: PropTypes.func.isRequired,
getClients: PropTypes.func.isRequired,
getAccessList: PropTypes.func.isRequired,
};
export default Dashboard;

View File

@@ -0,0 +1,260 @@
import React, { useEffect } from 'react';
import { HashLink as Link } from 'react-router-hash-link';
import { Trans, useTranslation } from 'react-i18next';
import classNames from 'classnames';
import Statistics from './Statistics';
import Counters from './Counters';
import Clients from './Clients';
import QueriedDomains from './QueriedDomains';
import BlockedDomains from './BlockedDomains';
import { DISABLE_PROTECTION_TIMINGS, ONE_SECOND_IN_MS, SETTINGS_URLS, TIME_UNITS } from '../../helpers/constants';
import { msToSeconds, msToMinutes, msToHours, msToDays } from '../../helpers/helpers';
import PageTitle from '../ui/PageTitle';
import Loading from '../ui/Loading';
import './Dashboard.css';
import Dropdown from '../ui/Dropdown';
import UpstreamResponses from './UpstreamResponses';
import UpstreamAvgTime from './UpstreamAvgTime';
import { AccessData, DashboardData, StatsData } from '../../initialState';
interface DashboardProps {
dashboard: DashboardData;
stats: StatsData;
access: AccessData;
getStats: (...args: unknown[]) => unknown;
getStatsConfig: (...args: unknown[]) => unknown;
toggleProtection: (...args: unknown[]) => unknown;
getClients: (...args: unknown[]) => unknown;
getAccessList: () => (dispatch: any) => void;
}
const Dashboard = ({
getAccessList,
getStats,
getStatsConfig,
dashboard: { protectionEnabled, processingProtection, protectionDisabledDuration },
toggleProtection,
stats,
access,
}: DashboardProps) => {
const { t } = useTranslation();
const getAllStats = () => {
getAccessList();
getStats();
getStatsConfig();
};
useEffect(() => {
getAllStats();
}, []);
const getSubtitle = () => {
if (!stats.enabled) {
return t('stats_disabled_short');
}
const msIn7Days = 604800000;
if (stats.timeUnits === TIME_UNITS.HOURS && stats.interval === msIn7Days) {
return t('for_last_days', { count: msToDays(stats.interval) });
}
return stats.timeUnits === TIME_UNITS.HOURS
? t('for_last_hours', { count: msToHours(stats.interval) })
: t('for_last_days', { count: msToDays(stats.interval) });
};
const buttonClass = classNames('btn btn-sm dashboard-protection-button', {
'btn-gray': protectionEnabled,
'btn-success': !protectionEnabled,
});
const refreshButton = (
<button
type="button"
className="btn btn-icon btn-outline-primary btn-sm"
title={t('refresh_btn')}
onClick={() => getAllStats()}>
<svg className="icons icon12">
<use xlinkHref="#refresh" />
</svg>
</button>
);
const statsProcessing = stats.processingStats || stats.processingGetConfig || access.processing;
const subtitle = getSubtitle();
const DISABLE_PROTECTION_ITEMS = [
{
text: t('disable_for_seconds', { count: msToSeconds(DISABLE_PROTECTION_TIMINGS.HALF_MINUTE) }),
disableTime: DISABLE_PROTECTION_TIMINGS.HALF_MINUTE,
},
{
text: t('disable_for_minutes', { count: msToMinutes(DISABLE_PROTECTION_TIMINGS.MINUTE) }),
disableTime: DISABLE_PROTECTION_TIMINGS.MINUTE,
},
{
text: t('disable_for_minutes', { count: msToMinutes(DISABLE_PROTECTION_TIMINGS.TEN_MINUTES) }),
disableTime: DISABLE_PROTECTION_TIMINGS.TEN_MINUTES,
},
{
text: t('disable_for_hours', { count: msToHours(DISABLE_PROTECTION_TIMINGS.HOUR) }),
disableTime: DISABLE_PROTECTION_TIMINGS.HOUR,
},
{
text: t('disable_until_tomorrow'),
disableTime: DISABLE_PROTECTION_TIMINGS.TOMORROW,
},
];
const getDisableProtectionItems = () =>
Object.values(DISABLE_PROTECTION_ITEMS).map((item: any, index: any) => (
<div
key={`disable_timings_${index}`}
className="dropdown-item"
onClick={() => {
toggleProtection(protectionEnabled, item.disableTime - ONE_SECOND_IN_MS);
}}>
{item.text}
</div>
));
const getRemaningTimeText = (milliseconds: any) => {
if (!milliseconds) {
return '';
}
const date = new Date(milliseconds);
const hh = date.getUTCHours();
const mm = `0${date.getUTCMinutes()}`.slice(-2);
const ss = `0${date.getUTCSeconds()}`.slice(-2);
const formattedHH = `0${hh}`.slice(-2);
return hh ? `${formattedHH}:${mm}:${ss}` : `${mm}:${ss}`;
};
const getProtectionBtnText = (status: any) => (status ? t('disable_protection') : t('enable_protection'));
return (
<>
<PageTitle title={t('dashboard')} containerClass="page-title--dashboard">
<div className="page-title__protection">
<button
type="button"
className={buttonClass}
onClick={() => {
toggleProtection(protectionEnabled);
}}
disabled={processingProtection}>
{protectionDisabledDuration
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
: getProtectionBtnText(protectionEnabled)}
</button>
{protectionEnabled && (
<Dropdown
label=""
baseClassName="dropdown-protection"
icon="arrow-down"
controlClassName="dropdown-protection__toggle"
menuClassName="dropdown-menu dropdown-menu-arrow dropdown-menu--protection">
{getDisableProtectionItems()}
</Dropdown>
)}
</div>
<button type="button" className="btn btn-outline-primary btn-sm" onClick={getAllStats}>
<Trans>refresh_statics</Trans>
</button>
</PageTitle>
{statsProcessing && <Loading />}
{!statsProcessing && (
<div className="row row-cards dashboard">
<div className="col-lg-12">
{stats.interval === 0 && (
<div className="alert alert-warning" role="alert">
<Trans
components={[
<Link to={`${SETTINGS_URLS.settings}#stats-config`} key="0">
link
</Link>,
]}>
stats_disabled
</Trans>
</div>
)}
<Statistics
interval={msToDays(stats.interval)}
dnsQueries={stats.dnsQueries}
blockedFiltering={stats.blockedFiltering}
replacedSafebrowsing={stats.replacedSafebrowsing}
replacedParental={stats.replacedParental}
numDnsQueries={stats.numDnsQueries}
numBlockedFiltering={stats.numBlockedFiltering}
numReplacedSafebrowsing={stats.numReplacedSafebrowsing}
numReplacedParental={stats.numReplacedParental}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<Counters subtitle={subtitle} refreshButton={refreshButton} />
</div>
<div className="col-lg-6">
<Clients subtitle={subtitle} refreshButton={refreshButton} />
</div>
<div className="col-lg-6">
<QueriedDomains
subtitle={subtitle}
dnsQueries={stats.numDnsQueries}
topQueriedDomains={stats.topQueriedDomains}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<BlockedDomains
subtitle={subtitle}
topBlockedDomains={stats.topBlockedDomains}
blockedFiltering={stats.numBlockedFiltering}
replacedSafebrowsing={stats.numReplacedSafebrowsing}
replacedSafesearch={stats.numReplacedSafesearch}
replacedParental={stats.numReplacedParental}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<UpstreamResponses
subtitle={subtitle}
topUpstreamsResponses={stats.topUpstreamsResponses}
refreshButton={refreshButton}
/>
</div>
<div className="col-lg-6">
<UpstreamAvgTime
subtitle={subtitle}
topUpstreamsAvgTime={stats.topUpstreamsAvgTime}
refreshButton={refreshButton}
/>
</div>
</div>
)}
</>
);
};
export default Dashboard;

View File

@@ -1,32 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withTranslation, Trans } from 'react-i18next';
const Actions = ({
handleAdd, handleRefresh, processingRefreshFilters, whitelist,
}) => <div className="card-actions">
<button
className="btn btn-success btn-standard mr-2 btn-large mb-2"
type="submit"
onClick={handleAdd}
>
{whitelist ? <Trans>add_allowlist</Trans> : <Trans>add_blocklist</Trans>}
</button>
<button
className="btn btn-primary btn-standard mb-2"
type="submit"
onClick={handleRefresh}
disabled={processingRefreshFilters}
>
<Trans>check_updates_btn</Trans>
</button>
</div>;
Actions.propTypes = {
handleAdd: PropTypes.func.isRequired,
handleRefresh: PropTypes.func.isRequired,
processingRefreshFilters: PropTypes.bool.isRequired,
whitelist: PropTypes.bool,
};
export default withTranslation()(Actions);

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { withTranslation, Trans } from 'react-i18next';
interface ActionsProps {
handleAdd: (...args: unknown[]) => unknown;
handleRefresh: (...args: unknown[]) => unknown;
processingRefreshFilters: boolean;
whitelist?: boolean;
}
const Actions = ({ handleAdd, handleRefresh, processingRefreshFilters, whitelist }: ActionsProps) => (
<div className="card-actions">
<button className="btn btn-success btn-standard mr-2 btn-large mb-2" type="submit" onClick={handleAdd}>
{whitelist ? <Trans>add_allowlist</Trans> : <Trans>add_blocklist</Trans>}
</button>
<button
className="btn btn-primary btn-standard mb-2"
type="submit"
onClick={handleRefresh}
disabled={processingRefreshFilters}>
<Trans>check_updates_btn</Trans>
</button>
</div>
);
export default withTranslation()(Actions);

View File

@@ -15,10 +15,12 @@ import {
getRulesToFilterList,
} from '../../../helpers/helpers';
import { BLOCK_ACTIONS, FILTERED, FILTERED_STATUS } from '../../../helpers/constants';
import { toggleBlocking } from '../../../actions';
const renderBlockingButton = (isFiltered, domain) => {
const processingRules = useSelector((state) => state.filtering.processingRules);
import { toggleBlocking } from '../../../actions';
import { RootState } from '../../../initialState';
const renderBlockingButton = (isFiltered: any, domain: any) => {
const processingRules = useSelector((state: RootState) => state.filtering.processingRules);
const dispatch = useDispatch();
const { t } = useTranslation();
@@ -28,28 +30,32 @@ const renderBlockingButton = (isFiltered, domain) => {
await dispatch(toggleBlocking(buttonType, domain));
};
const buttonClass = classNames('mt-3 button-action button-action--main button-action--active button-action--small', {
'button-action--unblock': isFiltered,
});
const buttonClass = classNames(
'mt-3 button-action button-action--main button-action--active button-action--small',
{
'button-action--unblock': isFiltered,
},
);
return <button type="button"
className={buttonClass}
onClick={onClick}
disabled={processingRules}
>
return (
<button type="button" className={buttonClass} onClick={onClick} disabled={processingRules}>
{t(buttonType)}
</button>;
</button>
);
};
const getTitle = () => {
const { t } = useTranslation();
const filters = useSelector((state) => state.filtering.filters, shallowEqual);
const whitelistFilters = useSelector((state) => state.filtering.whitelistFilters, shallowEqual);
const rules = useSelector((state) => state.filtering.check.rules, shallowEqual);
const reason = useSelector((state) => state.filtering.check.reason);
const filters = useSelector((state: RootState) => state.filtering.filters, shallowEqual);
const getReasonFiltered = (reason) => {
const whitelistFilters = useSelector((state: RootState) => state.filtering.whitelistFilters, shallowEqual);
const rules = useSelector((state: RootState) => state.filtering.check.rules, shallowEqual);
const reason = useSelector((state: RootState) => state.filtering.check.reason);
const getReasonFiltered = (reason: any) => {
const filterKey = reason.replace(FILTERED, '');
return i18next.t('query_log_filtered', { filter: filterKey });
};
@@ -71,24 +77,23 @@ const getTitle = () => {
return REASON_TO_TITLE_MAP[reason];
}
return <>
<div>{t('check_reason', { reason })}</div>
<div>
{t('rule_label')}:
&nbsp;
{ruleAndFilterNames}
</div>
</>;
return (
<>
<div>{t('check_reason', { reason })}</div>
<div>
{t('rule_label')}: &nbsp;
{ruleAndFilterNames}
</div>
</>
);
};
const Info = () => {
const {
hostname,
reason,
service_name,
cname,
ip_addrs,
} = useSelector((state) => state.filtering.check, shallowEqual);
const { hostname, reason, service_name, cname, ip_addrs } = useSelector(
(state: RootState) => state.filtering.check,
shallowEqual,
);
const { t } = useTranslation();
const title = getTitle();
@@ -99,23 +104,29 @@ const Info = () => {
'logs__row--green': checkWhiteList(reason),
});
const onlyFiltered = checkSafeSearch(reason)
|| checkSafeBrowsing(reason)
|| checkParental(reason);
const onlyFiltered = checkSafeSearch(reason) || checkSafeBrowsing(reason) || checkParental(reason);
const isFiltered = checkFiltered(reason);
return <div className={className}>
<div><strong>{hostname}</strong></div>
<div>{title}</div>
{!onlyFiltered
&& <>
{service_name && <div>{t('check_service', { service: service_name })}</div>}
{cname && <div>{t('check_cname', { cname })}</div>}
{ip_addrs && <div>{t('check_ip', { ip: ip_addrs.join(', ') })}</div>}
{renderBlockingButton(isFiltered, hostname)}
</>}
</div>;
return (
<div className={className}>
<div>
<strong>{hostname}</strong>
</div>
<div>{title}</div>
{!onlyFiltered && (
<>
{service_name && <div>{t('check_service', { service: service_name })}</div>}
{cname && <div>{t('check_cname', { cname })}</div>}
{ip_addrs && <div>{t('check_ip', { ip: ip_addrs.join(', ') })}</div>}
{renderBlockingButton(isFiltered, hostname)}
</>
)}
</div>
);
};
export default Info;

View File

@@ -1,66 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { useSelector } from 'react-redux';
import Card from '../../ui/Card';
import { renderInputField } from '../../../helpers/form';
import Info from './Info';
import { FORM_NAME } from '../../../helpers/constants';
const Check = (props) => {
const {
pristine,
invalid,
handleSubmit,
} = props;
const { t } = useTranslation();
const processingCheck = useSelector((state) => state.filtering.processingCheck);
const hostname = useSelector((state) => state.filtering.check.hostname);
return <Card
title={t('check_title')}
subtitle={t('check_desc')}
>
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12 col-md-6">
<div className="input-group">
<Field
id="name"
name="name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_host')}
/>
<span className="input-group-append">
<button
className="btn btn-success btn-standard btn-large"
type="submit"
onClick={handleSubmit}
disabled={pristine || invalid || processingCheck}
>
{t('check')}
</button>
</span>
</div>
{hostname && <>
<hr />
<Info />
</>}
</div>
</div>
</form>
</Card>;
};
Check.propTypes = {
handleSubmit: PropTypes.func.isRequired,
pristine: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
};
export default reduxForm({ form: FORM_NAME.DOMAIN_CHECK })(Check);

View File

@@ -0,0 +1,70 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Field, reduxForm } from 'redux-form';
import { useSelector } from 'react-redux';
import Card from '../../ui/Card';
import { renderInputField } from '../../../helpers/form';
import Info from './Info';
import { FORM_NAME } from '../../../helpers/constants';
import { RootState } from '../../../initialState';
interface CheckProps {
handleSubmit: (...args: unknown[]) => string;
pristine: boolean;
invalid: boolean;
}
const Check = (props: CheckProps) => {
const { pristine, invalid, handleSubmit } = props;
const { t } = useTranslation();
const processingCheck = useSelector((state: RootState) => state.filtering.processingCheck);
const hostname = useSelector((state: RootState) => state.filtering.check.hostname);
return (
<Card title={t('check_title')} subtitle={t('check_desc')}>
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col-12 col-md-6">
<div className="input-group">
<Field
id="name"
name="name"
component={renderInputField}
type="text"
className="form-control"
placeholder={t('form_enter_host')}
/>
<span className="input-group-append">
<button
className="btn btn-success btn-standard btn-large"
type="submit"
onClick={handleSubmit}
disabled={pristine || invalid || processingCheck}>
{t('check')}
</button>
</span>
</div>
{hostname && (
<>
<hr />
<Info />
</>
)}
</div>
</div>
</form>
</Card>
);
};
export default reduxForm({ form: FORM_NAME.DOMAIN_CHECK })(Check);

View File

@@ -1,32 +1,46 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import Card from '../ui/Card';
import PageTitle from '../ui/PageTitle';
import Examples from './Examples';
import Check from './Check';
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
import '../ui/texareaCommentsHighlight.css';
import { FilteringData } from '../../initialState';
class CustomRules extends Component {
interface CustomRulesProps {
filtering: FilteringData;
setRules: (...args: unknown[]) => unknown;
checkHost: (...args: unknown[]) => string;
getFilteringStatus: (...args: unknown[]) => unknown;
handleRulesChange: (...args: unknown[]) => unknown;
t: (...args: unknown[]) => string;
}
class CustomRules extends Component<CustomRulesProps> {
ref = React.createRef();
componentDidMount() {
this.props.getFilteringStatus();
}
handleChange = (e) => {
handleChange = (e: any) => {
const { value } = e.currentTarget;
this.handleRulesChange(value);
};
handleSubmit = (e) => {
handleSubmit = (e: any) => {
e.preventDefault();
this.handleRulesSubmit();
};
handleRulesChange = (value) => {
handleRulesChange = (value: any) => {
this.props.handleRulesChange({ userRules: value });
};
@@ -34,23 +48,22 @@ class CustomRules extends Component {
this.props.setRules(this.props.filtering.userRules);
};
handleCheck = (values) => {
handleCheck = (values: any) => {
this.props.checkHost(values);
};
onScroll = (e) => syncScroll(e, this.ref)
onScroll = (e: any) => syncScroll(e, this.ref);
render() {
const {
t,
filtering: {
userRules,
},
filtering: { userRules },
} = this.props;
return (
<>
<PageTitle title={t('custom_filtering_rules')} />
<Card subtitle={t('custom_filter_rules_hint')}>
<form onSubmit={this.handleSubmit}>
<div className="text-edit-container mb-4">
@@ -60,39 +73,31 @@ class CustomRules extends Component {
onChange={this.handleChange}
onScroll={this.onScroll}
/>
{getTextareaCommentsHighlight(
this.ref,
userRules,
undefined,
[COMMENT_LINE_DEFAULT_TOKEN, '!'],
)}
{getTextareaCommentsHighlight(this.ref, userRules, [
COMMENT_LINE_DEFAULT_TOKEN,
'!',
])}
</div>
<div className="card-actions">
<button
className="btn btn-success btn-standard btn-large"
type="submit"
onClick={this.handleSubmit}
>
onClick={this.handleSubmit}>
<Trans>apply_btn</Trans>
</button>
</div>
</form>
<hr />
<Examples />
</Card>
<Check onSubmit={this.handleCheck} />
</>
);
}
}
CustomRules.propTypes = {
filtering: PropTypes.object.isRequired,
setRules: PropTypes.func.isRequired,
checkHost: PropTypes.func.isRequired,
getFilteringStatus: PropTypes.func.isRequired,
handleRulesChange: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(CustomRules);

View File

@@ -1,5 +1,4 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import PageTitle from '../ui/PageTitle';
@@ -9,15 +8,41 @@ import Actions from './Actions';
import Table from './Table';
import { MODAL_TYPE } from '../../helpers/constants';
import { getCurrentFilter } from '../../helpers/helpers';
class DnsAllowlist extends Component {
interface DnsAllowlistProps {
getFilteringStatus: (...args: unknown[]) => unknown;
filtering: {
modalType: string;
modalFilterUrl: string;
isModalOpen: boolean;
isFilterAdded: boolean;
processingRefreshFilters: boolean;
processingRemoveFilter: boolean;
processingAddFilter: boolean;
processingConfigFilter: boolean;
processingFilters: boolean;
whitelistFilters: any[];
};
removeFilter: (...args: unknown[]) => unknown;
toggleFilterStatus: (...args: unknown[]) => unknown;
addFilter: (...args: unknown[]) => unknown;
toggleFilteringModal: (...args: unknown[]) => unknown;
handleRulesChange: (...args: unknown[]) => unknown;
refreshFilters: (...args: unknown[]) => unknown;
editFilter: (...args: unknown[]) => unknown;
t: (...args: unknown[]) => string;
}
class DnsAllowlist extends Component<DnsAllowlistProps> {
componentDidMount() {
this.props.getFilteringStatus();
}
handleSubmit = (values) => {
handleSubmit = (values: any) => {
const { name, url } = values;
const { filtering } = this.props;
const whitelist = true;
@@ -28,15 +53,17 @@ class DnsAllowlist extends Component {
}
};
handleDelete = (url) => {
handleDelete = (url: any) => {
if (window.confirm(this.props.t('list_confirm_delete'))) {
const whitelist = true;
this.props.removeFilter(url, whitelist);
}
};
toggleFilter = (url, data) => {
toggleFilter = (url: any, data: any) => {
const whitelist = true;
this.props.toggleFilterStatus(url, data, whitelist);
};
@@ -53,7 +80,6 @@ class DnsAllowlist extends Component {
t,
toggleFilteringModal,
addFilter,
toggleFilterStatus,
filtering: {
whitelistFilters,
isModalOpen,
@@ -68,19 +94,18 @@ class DnsAllowlist extends Component {
},
} = this.props;
const currentFilterData = getCurrentFilter(modalFilterUrl, whitelistFilters);
const loading = processingConfigFilter
|| processingFilters
|| processingAddFilter
|| processingRemoveFilter
|| processingRefreshFilters;
const loading =
processingConfigFilter ||
processingFilters ||
processingAddFilter ||
processingRemoveFilter ||
processingRefreshFilters;
const whitelist = true;
return (
<>
<PageTitle
title={t('dns_allowlists')}
subtitle={t('dns_allowlists_desc')}
/>
<PageTitle title={t('dns_allowlists')} subtitle={t('dns_allowlists_desc')} />
<div className="content">
<div className="row">
<div className="col-md-12">
@@ -90,11 +115,11 @@ class DnsAllowlist extends Component {
loading={loading}
processingConfigFilter={processingConfigFilter}
toggleFilteringModal={toggleFilteringModal}
toggleFilterStatus={toggleFilterStatus}
handleDelete={this.handleDelete}
toggleFilter={this.toggleFilter}
whitelist={whitelist}
/>
<Actions
handleAdd={this.openAddFiltersModal}
handleRefresh={this.handleRefresh}
@@ -105,6 +130,7 @@ class DnsAllowlist extends Component {
</div>
</div>
</div>
<Modal
filters={whitelistFilters}
isOpen={isModalOpen}
@@ -123,17 +149,4 @@ class DnsAllowlist extends Component {
}
}
DnsAllowlist.propTypes = {
getFilteringStatus: PropTypes.func.isRequired,
filtering: PropTypes.object.isRequired,
removeFilter: PropTypes.func.isRequired,
toggleFilterStatus: PropTypes.func.isRequired,
addFilter: PropTypes.func.isRequired,
toggleFilteringModal: PropTypes.func.isRequired,
handleRulesChange: PropTypes.func.isRequired,
refreshFilters: PropTypes.func.isRequired,
editFilter: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(DnsAllowlist);

View File

@@ -1,27 +1,38 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import PageTitle from '../ui/PageTitle';
import Card from '../ui/Card';
import Modal from './Modal';
import Actions from './Actions';
import Table from './Table';
import { MODAL_TYPE } from '../../helpers/constants';
import {
getCurrentFilter,
} from '../../helpers/helpers';
import { getCurrentFilter } from '../../helpers/helpers';
import filtersCatalog from '../../helpers/filters/filters';
import { FilteringData } from '../../initialState';
class DnsBlocklist extends Component {
interface DnsBlocklistProps {
getFilteringStatus: (...args: unknown[]) => unknown;
filtering: FilteringData;
removeFilter: (...args: unknown[]) => unknown;
toggleFilterStatus: (...args: unknown[]) => unknown;
addFilter: (...args: unknown[]) => unknown;
toggleFilteringModal: (...args: unknown[]) => unknown;
handleRulesChange: (...args: unknown[]) => unknown;
refreshFilters: (...args: unknown[]) => unknown;
editFilter: (...args: unknown[]) => unknown;
t: (...args: unknown[]) => string;
}
class DnsBlocklist extends Component<DnsBlocklistProps> {
componentDidMount() {
this.props.getFilteringStatus();
}
handleSubmit = (values) => {
handleSubmit = (values: any) => {
const { modalFilterUrl, modalType } = this.props.filtering;
switch (modalType) {
@@ -30,23 +41,25 @@ class DnsBlocklist extends Component {
break;
case MODAL_TYPE.ADD_FILTERS: {
const { name, url } = values;
this.props.addFilter(url, name);
break;
}
case MODAL_TYPE.CHOOSE_FILTERING_LIST: {
const changedValues = Object.entries(values)?.reduce((acc, [key, value]) => {
const changedValues = Object.entries(values)?.reduce((acc: any, [key, value]) => {
if (value && key in filtersCatalog.filters) {
acc[key] = value;
}
return acc;
}, {});
Object.keys(changedValues)
.forEach((fieldName) => {
// filterId is actually in the field name
const { source, name } = filtersCatalog.filters[fieldName];
this.props.addFilter(source, name);
});
Object.keys(changedValues).forEach((fieldName) => {
// filterId is actually in the field name
const { source, name } = filtersCatalog.filters[fieldName];
this.props.addFilter(source, name);
});
break;
}
default:
@@ -54,13 +67,13 @@ class DnsBlocklist extends Component {
}
};
handleDelete = (url) => {
handleDelete = (url: any) => {
if (window.confirm(this.props.t('list_confirm_delete'))) {
this.props.removeFilter(url);
}
};
toggleFilter = (url, data) => {
toggleFilter = (url: any, data: any) => {
this.props.toggleFilterStatus(url, data);
};
@@ -75,8 +88,11 @@ class DnsBlocklist extends Component {
render() {
const {
t,
toggleFilteringModal,
addFilter,
filtering: {
filters,
isModalOpen,
@@ -91,18 +107,17 @@ class DnsBlocklist extends Component {
},
} = this.props;
const currentFilterData = getCurrentFilter(modalFilterUrl, filters);
const loading = processingConfigFilter
|| processingFilters
|| processingAddFilter
|| processingRemoveFilter
|| processingRefreshFilters;
const loading =
processingConfigFilter ||
processingFilters ||
processingAddFilter ||
processingRemoveFilter ||
processingRefreshFilters;
return (
<>
<PageTitle
title={t('dns_blocklists')}
subtitle={t('dns_blocklists_desc')}
/>
<PageTitle title={t('dns_blocklists')} subtitle={t('dns_blocklists_desc')} />
<div className="content">
<div className="row">
<div className="col-md-12">
@@ -115,6 +130,7 @@ class DnsBlocklist extends Component {
handleDelete={this.handleDelete}
toggleFilter={this.toggleFilter}
/>
<Actions
handleAdd={this.openSelectTypeModal}
handleRefresh={this.handleRefresh}
@@ -124,6 +140,7 @@ class DnsBlocklist extends Component {
</div>
</div>
</div>
<Modal
filtersCatalog={filtersCatalog}
filters={filters}
@@ -142,17 +159,4 @@ class DnsBlocklist extends Component {
}
}
DnsBlocklist.propTypes = {
getFilteringStatus: PropTypes.func.isRequired,
filtering: PropTypes.object.isRequired,
removeFilter: PropTypes.func.isRequired,
toggleFilterStatus: PropTypes.func.isRequired,
addFilter: PropTypes.func.isRequired,
toggleFilteringModal: PropTypes.func.isRequired,
handleRulesChange: PropTypes.func.isRequired,
refreshFilters: PropTypes.func.isRequired,
editFilter: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};
export default withTranslation()(DnsBlocklist);

View File

@@ -7,31 +7,37 @@ const Examples = () => (
<Trans>examples_title</Trans>:
<ol className="leading-loose">
<li>
<code>||example.org^</code>:
<Trans>example_meaning_filter_block</Trans>
<code>||example.org^</code>:<Trans>example_meaning_filter_block</Trans>
</li>
<li>
<code> @@||example.org^</code>:
<Trans>example_meaning_filter_whitelist</Trans>
<code> @@||example.org^</code>:<Trans>example_meaning_filter_whitelist</Trans>
</li>
<li>
<code>127.0.0.1 example.org</code>:
<Trans>example_meaning_host_block</Trans>
<code>127.0.0.1 example.org</code>:<Trans>example_meaning_host_block</Trans>
</li>
<li>
<code><Trans>example_comment</Trans></code>:
<Trans>example_comment_meaning</Trans>
<code>
<Trans>example_comment</Trans>
</code>
:<Trans>example_comment_meaning</Trans>
</li>
<li>
<code><Trans>example_comment_hash</Trans></code>:
<Trans>example_comment_meaning</Trans>
<code>
<Trans>example_comment_hash</Trans>
</code>
:<Trans>example_comment_meaning</Trans>
</li>
<li>
<code>/REGEX/</code>:
<Trans>example_regex_meaning</Trans>
<code>/REGEX/</code>:<Trans>example_regex_meaning</Trans>
</li>
</ol>
</div>
<p className="mt-1">
<Trans
components={[
@@ -39,12 +45,10 @@ const Examples = () => (
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax&from=ui&app=home"
target="_blank"
rel="noopener noreferrer"
key="0"
>
key="0">
link
</a>,
]}
>
]}>
filtering_rules_learn_more
</Trans>
</p>

View File

@@ -1,191 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import classNames from 'classnames';
import { validatePath, validateRequiredValue } from '../../helpers/validators';
import { CheckboxField, renderInputField } from '../../helpers/form';
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE, FORM_NAME } from '../../helpers/constants';
import filtersCatalog from '../../helpers/filters/filters';
const getIconsData = (homepage, source) => ([
{
iconName: 'dashboard',
href: homepage,
className: 'ml-1',
},
{
iconName: 'info',
href: source,
},
]);
const renderIcons = (iconsData) => iconsData.map(({
iconName,
href,
className = '',
}) => <a key={iconName} href={href} target="_blank" rel="noopener noreferrer"
className={classNames('d-flex align-items-center', className)}
>
<svg className="icon icon--15 mr-1 icon--gray">
<use xlinkHref={`#${iconName}`} />
</svg>
</a>);
const renderCheckboxField = (
props,
) => <CheckboxField
{...props}
input={{
...props.input,
checked: props.disabled || props.input.checked,
}}
/>;
renderCheckboxField.propTypes = {
// https://redux-form.com/8.3.0/docs/api/field.md/#props
input: PropTypes.object.isRequired,
disabled: PropTypes.bool.isRequired,
};
const renderFilters = ({ categories, filters }, selectedSources, t) => Object.keys(categories)
.map((categoryId) => {
const category = categories[categoryId];
const categoryFilters = [];
Object.keys(filters)
.sort()
.forEach((key) => {
const filter = filters[key];
filter.id = key;
if (filter.categoryId === categoryId) {
categoryFilters.push(filter);
}
});
return <div key={category.name} className="modal-body__item">
<h6 className="font-weight-bold mb-1">{t(category.name)}</h6>
<p className="mb-3">{t(category.description)}</p>
{categoryFilters.map((filter) => {
const { homepage, source, name } = filter;
const isSelected = Object.prototype.hasOwnProperty.call(selectedSources, source);
const iconsData = getIconsData(homepage, source);
return <div key={name} className="d-flex align-items-center pb-1">
<Field
name={filter.id}
type="checkbox"
component={renderCheckboxField}
placeholder={t(name)}
disabled={isSelected}
/>
{renderIcons(iconsData)}
</div>;
})}
</div>;
});
const Form = (props) => {
const {
t,
closeModal,
handleSubmit,
processingAddFilter,
processingConfigFilter,
whitelist,
modalType,
toggleFilteringModal,
selectedSources,
} = props;
const openModal = (modalType, timeout = MODAL_OPEN_TIMEOUT) => {
toggleFilteringModal();
setTimeout(() => toggleFilteringModal({ type: modalType }), timeout);
};
const openFilteringListModal = () => openModal(MODAL_TYPE.CHOOSE_FILTERING_LIST);
const openAddFiltersModal = () => openModal(MODAL_TYPE.ADD_FILTERS);
return <form onSubmit={handleSubmit}>
<div className="modal-body modal-body--filters">
{modalType === MODAL_TYPE.SELECT_MODAL_TYPE
&& <div className="d-flex justify-content-around">
<button onClick={openFilteringListModal}
className="btn btn-success btn-standard mr-2 btn-large">
{t('choose_from_list')}
</button>
<button onClick={openAddFiltersModal} className="btn btn-primary btn-standard">
{t('add_custom_list')}
</button>
</div>}
{modalType === MODAL_TYPE.CHOOSE_FILTERING_LIST
&& renderFilters(filtersCatalog, selectedSources, t)}
{modalType !== MODAL_TYPE.CHOOSE_FILTERING_LIST
&& modalType !== MODAL_TYPE.SELECT_MODAL_TYPE
&& <>
<div className="form__group">
<Field
id="name"
name="name"
type="text"
component={renderInputField}
className="form-control"
placeholder={t('enter_name_hint')}
normalizeOnBlur={(data) => data.trim()}
/>
</div>
<div className="form__group">
<Field
id="url"
name="url"
type="text"
component={renderInputField}
className="form-control"
placeholder={t('enter_url_or_path_hint')}
validate={[validateRequiredValue, validatePath]}
normalizeOnBlur={(data) => data.trim()}
/>
</div>
<div className="form__description">
{whitelist ? t('enter_valid_allowlist') : t('enter_valid_blocklist')}
</div>
</>}
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-secondary"
onClick={closeModal}
>
{t('cancel_btn')}
</button>
{modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && <button
type="submit"
className="btn btn-success"
disabled={processingAddFilter || processingConfigFilter}
>
{t('save_btn')}
</button>}
</div>
</form>;
};
Form.propTypes = {
t: PropTypes.func.isRequired,
closeModal: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
processingAddFilter: PropTypes.bool.isRequired,
processingConfigFilter: PropTypes.bool.isRequired,
whitelist: PropTypes.bool,
modalType: PropTypes.string.isRequired,
toggleFilteringModal: PropTypes.func.isRequired,
selectedSources: PropTypes.object,
};
export default flow([
withTranslation(),
reduxForm({ form: FORM_NAME.FILTER }),
])(Form);

View File

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

View File

@@ -1,11 +1,13 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactModal from 'react-modal';
import { withTranslation } from 'react-i18next';
import { MODAL_TYPE } from '../../helpers/constants';
import Form from './Form';
import '../ui/Modal.css';
import { getMap } from '../../helpers/helpers';
ReactModal.setAppElement('#root');
@@ -25,7 +27,7 @@ const MODAL_TYPE_TO_TITLE_TYPE_MAP = {
* @returns {'new_allowlist' | 'edit_allowlist' | 'choose_allowlist' |
* 'new_blocklist' | 'edit_blocklist' | 'choose_blocklist' | null}
*/
const getTitle = (modalType, whitelist) => {
const getTitle = (modalType: any, whitelist: any) => {
const titleType = MODAL_TYPE_TO_TITLE_TYPE_MAP[modalType];
if (!titleType) {
return null;
@@ -33,19 +35,39 @@ const getTitle = (modalType, whitelist) => {
return `${titleType}_${whitelist ? 'allowlist' : 'blocklist'}`;
};
const getSelectedValues = (filters, catalogSourcesToIdMap) => filters.reduce((acc, { url }) => {
if (Object.prototype.hasOwnProperty.call(catalogSourcesToIdMap, url)) {
const fieldId = `filter${catalogSourcesToIdMap[url]}`;
acc.selectedFilterIds[fieldId] = true;
acc.selectedSources[url] = true;
}
return acc;
}, {
selectedFilterIds: {},
selectedSources: {},
});
const getSelectedValues = (filters: any, catalogSourcesToIdMap: any) =>
filters.reduce(
(acc: any, { url }: any) => {
if (Object.prototype.hasOwnProperty.call(catalogSourcesToIdMap, url)) {
const fieldId = `filter${catalogSourcesToIdMap[url]}`;
acc.selectedFilterIds[fieldId] = true;
acc.selectedSources[url] = true;
}
return acc;
},
{
selectedFilterIds: {},
selectedSources: {},
},
);
class Modal extends Component {
interface ModalProps {
toggleFilteringModal: (...args: unknown[]) => unknown;
isOpen: boolean;
addFilter: (...args: unknown[]) => unknown;
isFilterAdded: boolean;
processingAddFilter: boolean;
processingConfigFilter: boolean;
handleSubmit: (values: any) => void;
modalType: string;
currentFilterData: object;
t: (...args: unknown[]) => string;
whitelist?: boolean;
filters: unknown[];
filtersCatalog?: any;
}
class Modal extends Component<ModalProps> {
closeModal = () => {
this.props.toggleFilteringModal();
};
@@ -53,15 +75,25 @@ class Modal extends Component {
render() {
const {
isOpen,
processingAddFilter,
processingConfigFilter,
handleSubmit,
modalType,
currentFilterData,
whitelist,
toggleFilteringModal,
filters,
t,
filtersCatalog,
} = this.props;
@@ -90,15 +122,16 @@ class Modal extends Component {
className="Modal__Bootstrap modal-dialog modal-dialog-centered"
closeTimeoutMS={0}
isOpen={isOpen}
onRequestClose={this.closeModal}
>
onRequestClose={this.closeModal}>
<div className="modal-content">
<div className="modal-header">
{title && <h4 className="modal-title">{title}</h4>}
<button type="button" className="close" onClick={this.closeModal}>
<span className="sr-only">Close</span>
</button>
</div>
<Form
selectedSources={selectedSources}
initialValues={initialValues}
@@ -116,20 +149,4 @@ class Modal extends Component {
}
}
Modal.propTypes = {
toggleFilteringModal: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
addFilter: PropTypes.func.isRequired,
isFilterAdded: PropTypes.bool.isRequired,
processingAddFilter: PropTypes.bool.isRequired,
processingConfigFilter: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
modalType: PropTypes.string.isRequired,
currentFilterData: PropTypes.object.isRequired,
t: PropTypes.func.isRequired,
whitelist: PropTypes.bool,
filters: PropTypes.array.isRequired,
filtersCatalog: PropTypes.object,
};
export default withTranslation()(Modal);

View File

@@ -1,22 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { renderInputField } from '../../../helpers/form';
import { validateAnswer, validateDomain, validateRequiredValue } from '../../../helpers/validators';
import { FORM_NAME } from '../../../helpers/constants';
const Form = (props) => {
const {
t,
handleSubmit,
reset,
pristine,
submitting,
toggleRewritesModal,
processingAdd,
} = props;
interface FormProps {
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
reset: (...args: unknown[]) => string;
toggleRewritesModal: (...args: unknown[]) => unknown;
submitting: boolean;
processingAdd: boolean;
t: (...args: unknown[]) => string;
initialValues?: object;
}
const Form = (props: FormProps) => {
const { t, handleSubmit, reset, pristine, submitting, toggleRewritesModal, processingAdd } = props;
return (
<form onSubmit={handleSubmit}>
@@ -35,22 +39,19 @@ const Form = (props) => {
validate={[validateRequiredValue, validateDomain]}
/>
</div>
<Trans>examples_title</Trans>:
<ol className="leading-loose">
<li>
<code>example.org</code> <Trans>example_rewrite_domain</Trans>
</li>
<li>
<code>*.example.org</code> &nbsp;
<span>
<Trans components={[<code key="0">text</code>]}>
example_rewrite_wildcard
</Trans>
<Trans components={[<code key="0">text</code>]}>example_rewrite_wildcard</Trans>
</span>
</li>
</ol>
<div className="form__group">
<Field
id="answer"
@@ -63,14 +64,15 @@ const Form = (props) => {
/>
</div>
</div>
<ul>{['rewrite_ip_address',
'rewrite_domain_name',
'rewrite_A',
'rewrite_AAAA']
.map((str) => <li key={str}>
<Trans components={[<code key="0">text</code>]}>{str}</Trans>
</li>)
}</ul>
<ul>
{['rewrite_ip_address', 'rewrite_domain_name', 'rewrite_A', 'rewrite_AAAA'].map((str) => (
<li key={str}>
<Trans components={[<code key="0">text</code>]}>{str}</Trans>
</li>
))}
</ul>
<div className="modal-footer">
<div className="btn-list">
<button
@@ -80,15 +82,14 @@ const Form = (props) => {
onClick={() => {
reset();
toggleRewritesModal();
}}
>
}}>
<Trans>cancel_btn</Trans>
</button>
<button
type="submit"
className="btn btn-success btn-standard"
disabled={submitting || pristine || processingAdd}
>
disabled={submitting || pristine || processingAdd}>
<Trans>save_btn</Trans>
</button>
</div>
@@ -97,17 +98,6 @@ const Form = (props) => {
);
};
Form.propTypes = {
pristine: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
initialValues: PropTypes.object,
};
export default flow([
withTranslation(),
reduxForm({

View File

@@ -1,12 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { MODAL_TYPE } from '../../../helpers/constants';
import Form from './Form';
const Modal = (props) => {
interface ModalProps {
isModalOpen: boolean;
handleSubmit: (values: any) => void;
toggleRewritesModal: (...args: unknown[]) => unknown;
processingAdd: boolean;
processingDelete: boolean;
modalType: string;
currentRewrite?: object;
}
const Modal = (props: ModalProps) => {
const {
isModalOpen,
handleSubmit,
@@ -22,8 +33,7 @@ const Modal = (props) => {
className="Modal__Bootstrap modal-dialog modal-dialog-centered"
closeTimeoutMS={0}
isOpen={isModalOpen}
onRequestClose={() => toggleRewritesModal()}
>
onRequestClose={() => toggleRewritesModal()}>
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">
@@ -33,10 +43,12 @@ const Modal = (props) => {
<Trans>rewrite_add</Trans>
)}
</h4>
<button type="button" className="close" onClick={() => toggleRewritesModal()}>
<span className="sr-only">Close</span>
</button>
</div>
<Form
initialValues={{ ...currentRewrite }}
onSubmit={handleSubmit}
@@ -49,14 +61,4 @@ const Modal = (props) => {
);
};
Modal.propTypes = {
isModalOpen: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired,
modalType: PropTypes.string.isRequired,
currentRewrite: PropTypes.object,
};
export default withTranslation()(Modal);

View File

@@ -1,13 +1,26 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// @ts-expect-error FIXME: update react-table
import ReactTable from 'react-table';
import { withTranslation } from 'react-i18next';
import { sortIp } from '../../../helpers/helpers';
import { MODAL_TYPE, TABLES_MIN_ROWS } from '../../../helpers/constants';
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
class Table extends Component {
cellWrap = ({ value }) => (
interface TableProps {
t: (...args: unknown[]) => string;
list: unknown[];
processing: boolean;
processingAdd: boolean;
processingDelete: boolean;
processingUpdate: boolean;
handleDelete: (...args: unknown[]) => unknown;
toggleRewritesModal: (...args: unknown[]) => unknown;
}
class Table extends Component<TableProps> {
cellWrap = ({ value }: any) => (
<div className="logs__row o-hidden">
<span className="logs__text" title={value}>
{value}
@@ -33,7 +46,7 @@ class Table extends Component {
maxWidth: 100,
sortable: false,
resizable: false,
Cell: (value) => {
Cell: (value: any) => {
const currentRewrite = {
answer: value.row.answer,
domain: value.row.domain,
@@ -51,8 +64,7 @@ class Table extends Component {
});
}}
disabled={this.props.processingUpdate}
title={this.props.t('edit_table_action')}
>
title={this.props.t('edit_table_action')}>
<svg className="icons icon12">
<use xlinkHref="#edit" />
</svg>
@@ -62,8 +74,7 @@ class Table extends Component {
type="button"
className="btn btn-icon btn-outline-secondary btn-sm"
onClick={() => this.props.handleDelete(currentRewrite)}
title={this.props.t('delete_table_action')}
>
title={this.props.t('delete_table_action')}>
<svg className="icons">
<use xlinkHref="#delete" />
</svg>
@@ -75,9 +86,7 @@ class Table extends Component {
];
render() {
const {
t, list, processing, processingAdd, processingDelete,
} = this.props;
const { t, list, processing, processingAdd, processingDelete } = this.props;
return (
<ReactTable
@@ -87,7 +96,9 @@ class Table extends Component {
className="-striped -highlight card-table-overflow"
showPagination
defaultPageSize={LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE) || 10}
onPageSizeChange={(size) => LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE, size)}
onPageSizeChange={(size: any) =>
LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE, size)
}
minRows={TABLES_MIN_ROWS}
ofText="/"
previousText={t('previous_btn')}
@@ -101,15 +112,4 @@ class Table extends Component {
}
}
Table.propTypes = {
t: PropTypes.func.isRequired,
list: PropTypes.array.isRequired,
processing: PropTypes.bool.isRequired,
processingAdd: PropTypes.bool.isRequired,
processingDelete: PropTypes.bool.isRequired,
processingUpdate: PropTypes.bool.isRequired,
handleDelete: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
};
export default withTranslation()(Table);

View File

@@ -1,26 +1,39 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Trans, withTranslation } from 'react-i18next';
import Table from './Table';
import Modal from './Modal';
import Card from '../../ui/Card';
import PageTitle from '../../ui/PageTitle';
import { MODAL_TYPE } from '../../../helpers/constants';
import { RewritesData } from '../../../initialState';
class Rewrites extends Component {
interface RewritesProps {
t: (...args: unknown[]) => string;
getRewritesList: () => (dispatch: any) => void;
toggleRewritesModal: (...args: unknown[]) => unknown;
addRewrite: (...args: unknown[]) => unknown;
deleteRewrite: (...args: unknown[]) => unknown;
updateRewrite: (...args: unknown[]) => unknown;
rewrites: RewritesData;
}
class Rewrites extends Component<RewritesProps> {
componentDidMount() {
this.props.getRewritesList();
}
handleDelete = (values) => {
handleDelete = (values: any) => {
// eslint-disable-next-line no-alert
if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) {
this.props.deleteRewrite(values);
}
};
handleSubmit = (values) => {
handleSubmit = (values: any) => {
const { modalType, currentRewrite } = this.props.rewrites;
if (modalType === MODAL_TYPE.EDIT_REWRITE && currentRewrite) {
@@ -36,7 +49,9 @@ class Rewrites extends Component {
render() {
const {
t,
rewrites,
toggleRewritesModal,
} = this.props;
@@ -53,14 +68,9 @@ class Rewrites extends Component {
return (
<Fragment>
<PageTitle
title={t('dns_rewrites')}
subtitle={t('rewrite_desc')}
/>
<Card
id="rewrites"
bodyType="card-body box-body--settings"
>
<PageTitle title={t('dns_rewrites')} subtitle={t('rewrite_desc')} />
<Card id="rewrites" bodyType="card-body box-body--settings">
<Fragment>
<Table
list={list}
@@ -76,8 +86,7 @@ class Rewrites extends Component {
type="button"
className="btn btn-success btn-standard mt-3"
onClick={() => toggleRewritesModal({ type: MODAL_TYPE.ADD_REWRITE })}
disabled={processingAdd}
>
disabled={processingAdd}>
<Trans>rewrite_add</Trans>
</button>
@@ -88,7 +97,6 @@ class Rewrites extends Component {
handleSubmit={this.handleSubmit}
processingAdd={processingAdd}
processingDelete={processingDelete}
processingUpdate={processingUpdate}
currentRewrite={currentRewrite}
/>
</Fragment>
@@ -98,14 +106,4 @@ class Rewrites extends Component {
}
}
Rewrites.propTypes = {
t: PropTypes.func.isRequired,
getRewritesList: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired,
addRewrite: PropTypes.func.isRequired,
deleteRewrite: PropTypes.func.isRequired,
updateRewrite: PropTypes.func.isRequired,
rewrites: PropTypes.object.isRequired,
};
export default withTranslation()(Rewrites);

View File

@@ -1,23 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Field, reduxForm } from 'redux-form';
import { Trans, withTranslation } from 'react-i18next';
import flow from 'lodash/flow';
import { toggleAllServices } from '../../../helpers/helpers';
import { renderServiceField } from '../../../helpers/form';
import { FORM_NAME } from '../../../helpers/constants';
const Form = (props) => {
const {
blockedServices,
handleSubmit,
change,
pristine,
submitting,
processing,
processingSet,
} = props;
interface FormProps {
blockedServices: unknown[];
pristine: boolean;
handleSubmit: (...args: unknown[]) => string;
change: (...args: unknown[]) => unknown;
submitting: boolean;
processing: boolean;
processingSet: boolean;
t: (...args: unknown[]) => string;
}
const Form = (props: FormProps) => {
const { blockedServices, handleSubmit, change, pristine, submitting, processing, processingSet } = props;
return (
<form onSubmit={handleSubmit}>
@@ -28,24 +32,24 @@ const Form = (props) => {
type="button"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
onClick={() => toggleAllServices(blockedServices, change, true)}
>
onClick={() => toggleAllServices(blockedServices, change, true)}>
<Trans>block_all</Trans>
</button>
</div>
<div className="col-6">
<button
type="button"
className="btn btn-secondary btn-block"
disabled={processing || processingSet}
onClick={() => toggleAllServices(blockedServices, change, false)}
>
onClick={() => toggleAllServices(blockedServices, change, false)}>
<Trans>unblock_all</Trans>
</button>
</div>
</div>
<div className="services">
{blockedServices.map((service) => (
{blockedServices.map((service: any) => (
<Field
key={service.id}
icon={service.icon_svg}
@@ -63,8 +67,7 @@ const Form = (props) => {
<button
type="submit"
className="btn btn-success btn-standard btn-large"
disabled={submitting || pristine || processing || processingSet}
>
disabled={submitting || pristine || processing || processingSet}>
<Trans>save_btn</Trans>
</button>
</div>
@@ -72,17 +75,6 @@ const Form = (props) => {
);
};
Form.propTypes = {
blockedServices: PropTypes.array.isRequired,
pristine: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
change: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
processing: PropTypes.bool.isRequired,
processingSet: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
};
export default flow([
withTranslation(),
reduxForm({

View File

@@ -1,10 +1,12 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { Timezone } from './Timezone';
import { TimeSelect } from './TimeSelect';
import { TimePeriod } from './TimePeriod';
import { getFullDayName, getShortDayName } from './helpers';
import { LOCAL_TIMEZONE_VALUE } from '../../../../helpers/constants';
@@ -14,21 +16,26 @@ export const DAYS_OF_WEEK = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const INITIAL_START_TIME_MS = 0;
const INITIAL_END_TIME_MS = 86340000;
export const Modal = ({
isOpen,
currentDay,
schedule,
onClose,
onSubmit,
}) => {
interface ModalProps {
schedule: {
time_zone: string;
};
currentDay?: string;
isOpen: boolean;
onClose: (...args: unknown[]) => unknown;
onSubmit: (values: any) => void;
}
export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: ModalProps) => {
const [t] = useTranslation();
const intialTimezone = schedule.time_zone === LOCAL_TIMEZONE_VALUE
? Intl.DateTimeFormat().resolvedOptions().timeZone
: schedule.time_zone;
const intialTimezone =
schedule.time_zone === LOCAL_TIMEZONE_VALUE
? Intl.DateTimeFormat().resolvedOptions().timeZone
: schedule.time_zone;
const [timezone, setTimezone] = useState(intialTimezone);
const [days, setDays] = useState(new Set());
const [days, setDays] = useState<Set<string>>(new Set());
const [startTime, setStartTime] = useState(INITIAL_START_TIME_MS);
const [endTime, setEndTime] = useState(INITIAL_END_TIME_MS);
@@ -53,7 +60,7 @@ export const Modal = ({
}
}, [startTime, endTime]);
const addDays = (day) => {
const addDays = (day: any) => {
const newDays = new Set(days);
if (newDays.has(day)) {
@@ -65,11 +72,11 @@ export const Modal = ({
setDays(newDays);
};
const activeDay = (day) => {
const activeDay = (day: any) => {
return days.has(day);
};
const onFormSubmit = (e) => {
const onFormSubmit = (e: any) => {
e.preventDefault();
const newSchedule = schedule;
@@ -93,23 +100,19 @@ export const Modal = ({
className="Modal__Bootstrap modal-dialog modal-dialog-centered modal-dialog--schedule"
closeTimeoutMS={0}
isOpen={isOpen}
onRequestClose={onClose}
>
onRequestClose={onClose}>
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">
{currentDay ? t('schedule_edit') : t('schedule_new')}
</h4>
<h4 className="modal-title">{currentDay ? t('schedule_edit') : t('schedule_new')}</h4>
<button type="button" className="close" onClick={onClose}>
<span className="sr-only">Close</span>
</button>
</div>
<form onSubmit={onFormSubmit}>
<div className="modal-body">
<Timezone
timezone={timezone}
setTimezone={setTimezone}
/>
<Timezone timezone={timezone} setTimezone={setTimezone} />
<div className="schedule__days">
{DAYS_OF_WEEK.map((day) => (
@@ -118,8 +121,7 @@ export const Modal = ({
key={day}
className="btn schedule__button-day"
data-active={activeDay(day)}
onClick={() => addDays(day)}
>
onClick={() => addDays(day)}>
{getShortDayName(t, day)}
</button>
))}
@@ -127,69 +129,52 @@ export const Modal = ({
<div className="schedule__time-wrap">
<div className="schedule__time-row">
<TimeSelect
value={startTime}
onChange={(v) => setStartTime(v)}
/>
<TimeSelect value={startTime} onChange={(v) => setStartTime(v)} />
<TimeSelect
value={endTime}
onChange={(v) => setEndTime(v)}
/>
<TimeSelect value={endTime} onChange={(v) => setEndTime(v)} />
</div>
{wrongPeriod && (
<div className="schedule__error">
{t('schedule_invalid_select')}
</div>
)}
{wrongPeriod && <div className="schedule__error">{t('schedule_invalid_select')}</div>}
</div>
<div className="schedule__info">
<div className="schedule__info-title">
{t('schedule_modal_time_off')}
</div>
<div className="schedule__info-title">{t('schedule_modal_time_off')}</div>
<div className="schedule__info-row">
<svg className="icons schedule__info-icon">
<use xlinkHref="#calendar" />
</svg>
{days.size ? (
Array.from(days).map((day) => getFullDayName(t, day)).join(', ')
Array.from(days)
.map((day) => getFullDayName(t, day))
.join(', ')
) : (
<span>
</span>
<span></span>
)}
</div>
<div className="schedule__info-row">
<svg className="icons schedule__info-icon">
<use xlinkHref="#watch" />
</svg>
{wrongPeriod ? (
<span>
</span>
<span></span>
) : (
<TimePeriod
startTimeMs={startTime}
endTimeMs={endTime}
/>
<TimePeriod startTimeMs={startTime} endTimeMs={endTime} />
)}
</div>
</div>
<div className="schedule__notice">
{t('schedule_modal_description')}
</div>
<div className="schedule__notice">{t('schedule_modal_description')}</div>
</div>
<div className="modal-footer">
<div className="btn-list">
<button
type="button"
className="btn btn-success btn-standard"
disabled={days.size === 0 || wrongPeriod}
onClick={onFormSubmit}
>
onClick={onFormSubmit}>
{currentDay ? t('schedule_save') : t('schedule_add')}
</button>
</div>
@@ -199,11 +184,3 @@ export const Modal = ({
</ReactModal>
);
};
Modal.propTypes = {
schedule: PropTypes.object.isRequired,
currentDay: PropTypes.string,
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};

View File

@@ -1,25 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getTimeFromMs } from './helpers';
export const TimePeriod = ({
startTimeMs,
endTimeMs,
}) => {
const startTime = getTimeFromMs(startTimeMs);
const endTime = getTimeFromMs(endTimeMs);
return (
<div className="schedule__time">
<time>{startTime.hours}:{startTime.minutes}</time>
&nbsp;&nbsp;
<time>{endTime.hours}:{endTime.minutes}</time>
</div>
);
};
TimePeriod.propTypes = {
startTimeMs: PropTypes.number.isRequired,
endTimeMs: PropTypes.number.isRequired,
};

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { getTimeFromMs } from './helpers';
interface TimePeriodProps {
startTimeMs: number;
endTimeMs: number;
}
export const TimePeriod = ({ startTimeMs, endTimeMs }: TimePeriodProps) => {
const startTime = getTimeFromMs(startTimeMs);
const endTime = getTimeFromMs(endTimeMs);
return (
<div className="schedule__time">
<time>
{startTime.hours}:{startTime.minutes}
</time>
&nbsp;&nbsp;
<time>
{endTime.hours}:{endTime.minutes}
</time>
</div>
);
};

View File

@@ -1,37 +1,35 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { getTimeFromMs, convertTimeToMs } from './helpers';
export const TimeSelect = ({
value,
onChange,
}) => {
interface TimeSelectProps {
value: number;
onChange: (time: number) => void;
}
export const TimeSelect = ({ value, onChange }: TimeSelectProps) => {
const { hours: initialHours, minutes: initialMinutes } = getTimeFromMs(value);
const [hours, setHours] = useState(initialHours);
const [minutes, setMinutes] = useState(initialMinutes);
const hourOptions = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
const minuteOptions = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
const onHourChange = (event) => {
const onHourChange = (event: any) => {
setHours(event.target.value);
onChange(convertTimeToMs(event.target.value, minutes));
};
const onMinuteChange = (event) => {
const onMinuteChange = (event: any) => {
setMinutes(event.target.value);
onChange(convertTimeToMs(hours, event.target.value));
};
return (
<div className="schedule__time-select">
<select
value={hours}
onChange={onHourChange}
className="form-control custom-select"
>
<select value={hours} onChange={onHourChange} className="form-control custom-select">
{hourOptions.map((hour) => (
<option key={hour} value={hour}>
{hour}
@@ -39,11 +37,7 @@ export const TimeSelect = ({
))}
</select>
&nbsp;:&nbsp;
<select
value={minutes}
onChange={onMinuteChange}
className="form-control custom-select"
>
<select value={minutes} onChange={onMinuteChange} className="form-control custom-select">
{minuteOptions.map((minute) => (
<option key={minute} value={minute}>
{minute}
@@ -53,8 +47,3 @@ export const TimeSelect = ({
</div>
);
};
TimeSelect.propTypes = {
value: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired,
};

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