Compare commits
240 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
344c66f7ab | ||
|
|
83be002b41 | ||
|
|
9945cd3991 | ||
|
|
667263a3a8 | ||
|
|
6318fc424b | ||
|
|
e32a37a747 | ||
|
|
7805a71332 | ||
|
|
6fb2aee210 | ||
|
|
ce9bb588ed | ||
|
|
55fb914537 | ||
|
|
6f7bfd6c9c | ||
|
|
fbc0d981ba | ||
|
|
48d1c673a9 | ||
|
|
889a0eb8b3 | ||
|
|
b01c10b73e | ||
|
|
f6ad64bf69 | ||
|
|
a5e8443735 | ||
|
|
2860929a47 | ||
|
|
ecdac56616 | ||
|
|
25918e56fa | ||
|
|
df91f016f2 | ||
|
|
f7d259f653 | ||
|
|
82ab4328d4 | ||
|
|
b21e19a223 | ||
|
|
c6aed4eb57 | ||
|
|
760d466b38 | ||
|
|
258eecc55b | ||
|
|
7b93f5d7cf | ||
|
|
3be7676970 | ||
|
|
48ee2f8a42 | ||
|
|
ec83d0eb86 | ||
|
|
19347d263a | ||
|
|
b22b16d98c | ||
|
|
cadb765b7d | ||
|
|
1116da8b83 | ||
|
|
c65700923a | ||
|
|
7030c7c24c | ||
|
|
09718a2170 | ||
|
|
77cda2c2c5 | ||
|
|
d9c57cdd9a | ||
|
|
0dad53b5f7 | ||
|
|
9a7315dbea | ||
|
|
a21558f418 | ||
|
|
4f928be393 | ||
|
|
f543b47261 | ||
|
|
66b831072c | ||
|
|
80eb339896 | ||
|
|
c69639c013 | ||
|
|
5f6fbe8e08 | ||
|
|
b40bbf0260 | ||
|
|
a11c8e91ab | ||
|
|
618d0e596c | ||
|
|
fde9ea5cb1 | ||
|
|
03d9803238 | ||
|
|
bd64b8b014 | ||
|
|
67fe064fcf | ||
|
|
471668d19a | ||
|
|
42762dfe54 | ||
|
|
c9314610d4 | ||
|
|
16755c37d8 | ||
|
|
73fcbd6ea2 | ||
|
|
30244f361f | ||
|
|
083991fb21 | ||
|
|
e3200d5046 | ||
|
|
21f6ed36fe | ||
|
|
77d04d44eb | ||
|
|
b34d119255 | ||
|
|
63bd71a10c | ||
|
|
faf2b32389 | ||
|
|
d23da1b757 | ||
|
|
beb8e36eee | ||
|
|
fe70161c01 | ||
|
|
39fa4b1f8e | ||
|
|
c7a8883201 | ||
|
|
3fd467413c | ||
|
|
9728dd856f | ||
|
|
ecadf78d60 | ||
|
|
eba4612d72 | ||
|
|
9200163f85 | ||
|
|
3c17853344 | ||
|
|
993a3fc42c | ||
|
|
7bb9b2416b | ||
|
|
2de321ce24 | ||
|
|
30b2b85ff1 | ||
|
|
6ea4788f56 | ||
|
|
3c52a021b9 | ||
|
|
0ceea9af5f | ||
|
|
39b404be19 | ||
|
|
56dc3eab02 | ||
|
|
554a38eeb1 | ||
|
|
c8d3afe869 | ||
|
|
44222c604c | ||
|
|
cbf221585e | ||
|
|
48322f6d0d | ||
|
|
d5a213c639 | ||
|
|
8166c4bc33 | ||
|
|
133cd9ef6b | ||
|
|
11146f73ed | ||
|
|
1beb18db47 | ||
|
|
f7bc2273a7 | ||
|
|
d1e735a003 | ||
|
|
af4ff5c748 | ||
|
|
fc951c1226 | ||
|
|
f81fd42472 | ||
|
|
1029ea5966 | ||
|
|
c0abdb4bc7 | ||
|
|
6681178ad3 | ||
|
|
e73605c4c5 | ||
|
|
c7017d49aa | ||
|
|
191d3bde49 | ||
|
|
18876a8e5c | ||
|
|
aa4a0d9880 | ||
|
|
d03d731d65 | ||
|
|
33b58a42fe | ||
|
|
2e9e708647 | ||
|
|
8ad22841ab | ||
|
|
32cf02264c | ||
|
|
0e8445b38f | ||
|
|
cb27ecd6c0 | ||
|
|
535220b3df | ||
|
|
7b9cfa94f8 | ||
|
|
b3f2e88e9c | ||
|
|
aa7a8d45e4 | ||
|
|
49cdef3d6a | ||
|
|
fecd146552 | ||
|
|
b01efd8c98 | ||
|
|
bd4dfb261c | ||
|
|
e754e4d2f6 | ||
|
|
b220e35c99 | ||
|
|
4f5131f423 | ||
|
|
dcb043df5f | ||
|
|
86e5756262 | ||
|
|
ba0cf5739b | ||
|
|
c4a13b92d2 | ||
|
|
723279121a | ||
|
|
3ad7649f7d | ||
|
|
2898a49d86 | ||
|
|
1547f9d35e | ||
|
|
adadd55c42 | ||
|
|
33b0225aa4 | ||
|
|
97d4058d80 | ||
|
|
86207e719d | ||
|
|
113f94ff46 | ||
|
|
5673deb391 | ||
|
|
3548a393ed | ||
|
|
254515f274 | ||
|
|
bccbecc6ea | ||
|
|
66f53803af | ||
|
|
faef005ce7 | ||
|
|
941cd2a562 | ||
|
|
6a4a9a0239 | ||
|
|
b9dbe6f1b6 | ||
|
|
7fec111ef8 | ||
|
|
5e1bd99718 | ||
|
|
9d75f72ceb | ||
|
|
d98d96db1a | ||
|
|
6a0ef2df15 | ||
|
|
75c2eb4c8a | ||
|
|
d021a67d66 | ||
|
|
4ed97cab12 | ||
|
|
a38742eed7 | ||
|
|
5efa95ed26 | ||
|
|
04db7db607 | ||
|
|
d17c6c6bb3 | ||
|
|
b2052f2ef1 | ||
|
|
cddcf852c2 | ||
|
|
1def426b45 | ||
|
|
b114fd5279 | ||
|
|
d27c3284f6 | ||
|
|
ba24a26b53 | ||
|
|
3e6678b6b4 | ||
|
|
83fd6f9782 | ||
|
|
52bc1b3f10 | ||
|
|
dd2153b7ac | ||
|
|
dd96a34861 | ||
|
|
daf26ee25a | ||
|
|
7e140eaaac | ||
|
|
d07a712988 | ||
|
|
95863288bf | ||
|
|
ea12be658b | ||
|
|
faa7c9aae5 | ||
|
|
e3653e8c25 | ||
|
|
b40cb24822 | ||
|
|
74004c1aa0 | ||
|
|
3e240741f1 | ||
|
|
6cfdbef1a5 | ||
|
|
d9bde6425b | ||
|
|
e2ae9e1591 | ||
|
|
5ebcbfa9ad | ||
|
|
e276bd7a31 | ||
|
|
659b2529bf | ||
|
|
97b3ed43ab | ||
|
|
767d6d3f28 | ||
|
|
31fc9bfc52 | ||
|
|
3f06b02409 | ||
|
|
5bf958ec6b | ||
|
|
959d9ff9a0 | ||
|
|
4813b4de25 | ||
|
|
119100924c | ||
|
|
bd584de4ee | ||
|
|
ede85ab2f2 | ||
|
|
12c20288e4 | ||
|
|
5bbbf89c10 | ||
|
|
d55393ecd5 | ||
|
|
2b5927306f | ||
|
|
4f016b6ed7 | ||
|
|
3a2a6d10ec | ||
|
|
2491426b09 | ||
|
|
5ebdd1390e | ||
|
|
b7f0247575 | ||
|
|
e28186a28a | ||
|
|
de1a7ce48f | ||
|
|
48480fb33b | ||
|
|
f41332fe6b | ||
|
|
1f8b340b8f | ||
|
|
fdaf1d09d3 | ||
|
|
b9682c4f10 | ||
|
|
69dcb4effd | ||
|
|
d50fd0ba91 | ||
|
|
c2c7b4c731 | ||
|
|
952d5f3a3d | ||
|
|
3f126c9ec9 | ||
|
|
0be58ef918 | ||
|
|
8f9053e2fc | ||
|
|
68452e5330 | ||
|
|
2eacc46eaa | ||
|
|
74dcc91ea7 | ||
|
|
dd7bf61323 | ||
|
|
2819d6cace | ||
|
|
75355a6883 | ||
|
|
e9c007d56b | ||
|
|
84c9085516 | ||
|
|
9f36e57c1e | ||
|
|
7528699fc2 | ||
|
|
d280151c18 | ||
|
|
b44c755d25 | ||
|
|
e4078e87a1 | ||
|
|
be36204756 | ||
|
|
b5409d6d00 | ||
|
|
f3d6bce03e |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'build'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.23.5'
|
||||
'GO_VERSION': '1.22.3'
|
||||
'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 snap install snapcraft --classic'
|
||||
'run': 'sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft'
|
||||
- 'name': 'Set up QEMU'
|
||||
'uses': 'docker/setup-qemu-action@v1'
|
||||
- 'name': 'Set up Docker Buildx'
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'lint'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.23.5'
|
||||
'GO_VERSION': '1.22.3'
|
||||
|
||||
'on':
|
||||
'push':
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,8 +1,3 @@
|
||||
# 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.
|
||||
@@ -13,7 +8,6 @@
|
||||
# bottom to make sure they take effect.
|
||||
*.db
|
||||
*.log
|
||||
*.out
|
||||
*.snap
|
||||
*.test
|
||||
/agh-backup/
|
||||
@@ -27,7 +21,6 @@
|
||||
/launchpad_credentials
|
||||
/querylog.json*
|
||||
/snapcraft_login
|
||||
/test-reports/
|
||||
AdGuardHome
|
||||
AdGuardHome.exe
|
||||
AdGuardHome.yaml*
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"ul-indent": {
|
||||
"indent": 4
|
||||
},
|
||||
"ul-style": {
|
||||
"style": "dash"
|
||||
},
|
||||
"emphasis-style": {
|
||||
"style": "asterisk"
|
||||
},
|
||||
"no-duplicate-heading": {
|
||||
"siblings_only": true
|
||||
},
|
||||
"no-inline-html": {
|
||||
"allowed_elements": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"no-trailing-spaces": {
|
||||
"br_spaces": 0
|
||||
},
|
||||
"line-length": false,
|
||||
"no-bare-urls": false,
|
||||
"link-fragments": false
|
||||
}
|
||||
2408
CHANGELOG.md
2408
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
77
Makefile
77
Makefile
@@ -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/9799919799/utilities/make.html.
|
||||
# See https://pubs.opengroup.org/onlinepubs/9699919799/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: 9
|
||||
# AdGuard-Project-Version: 4
|
||||
|
||||
# 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
|
||||
DEPLOY_SCRIPT_PATH = not/a/real/path
|
||||
COMMIT = $$( git rev-parse --short HEAD )
|
||||
DIST_DIR = dist
|
||||
GOAMD64 = v1
|
||||
GOPROXY = https://proxy.golang.org|direct
|
||||
GOTELEMETRY = off
|
||||
GOTOOLCHAIN = go1.23.5
|
||||
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
|
||||
GOSUMDB = sum.golang.google.cn
|
||||
GOTOOLCHAIN = go1.22.3
|
||||
GPG_KEY = devteam@adguard.com
|
||||
GPG_KEY_PASSPHRASE = not-a-real-password
|
||||
NPM = npm
|
||||
@@ -35,9 +35,7 @@ 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
|
||||
|
||||
@@ -60,29 +58,21 @@ BUILD_RELEASE_DEPS_1 = go-deps
|
||||
|
||||
ENV = env\
|
||||
CHANNEL='$(CHANNEL)'\
|
||||
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
|
||||
COMMIT='$(COMMIT)'\
|
||||
DIST_DIR='$(DIST_DIR)'\
|
||||
GO="$(GO.MACRO)"\
|
||||
GOAMD64='$(GOAMD64)'\
|
||||
GOAMD64="$(GOAMD64)"\
|
||||
GOPROXY='$(GOPROXY)'\
|
||||
GOTELEMETRY='$(GOTELEMETRY)'\
|
||||
GOSUMDB='$(GOSUMDB)'\
|
||||
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)'\
|
||||
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}"\
|
||||
NEXTAPI='$(NEXTAPI)'\
|
||||
VERBOSE="$(VERBOSE.MACRO)"\
|
||||
VERSION='$(VERSION)'\
|
||||
|
||||
# Keep the line above blank.
|
||||
|
||||
@@ -90,8 +80,6 @@ ENV_MISC = 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
|
||||
@@ -105,38 +93,39 @@ build-docker: ; $(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
|
||||
build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
|
||||
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
|
||||
|
||||
clean: ; $(ENV) "$(SHELL)" ./scripts/make/clean.sh
|
||||
init: ; git config core.hooksPath ./scripts/hooks
|
||||
|
||||
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
|
||||
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
|
||||
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
|
||||
js-test: ; $(NPM) $(NPM_FLAGS) run test
|
||||
|
||||
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
|
||||
go-env: ; $(ENV) "$(GO.MACRO)" env
|
||||
go-fuzz: ; $(ENV) "$(SHELL)" ./scripts/make/go-fuzz.sh
|
||||
go-lint: ; $(ENV) "$(SHELL)" ./scripts/make/go-lint.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-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
|
||||
|
||||
# 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-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh
|
||||
go-upd-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-upd-tools.sh
|
||||
go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.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 operating systems relevant to the
|
||||
# development of the project can be typechecked and built successfully.
|
||||
# A quick check to make sure that all supported operating systems 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/...
|
||||
|
||||
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
|
||||
env GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
|
||||
env GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
|
||||
env GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
|
||||
env GOOS='linux' "$(GO.MACRO)" vet ./internal/...
|
||||
env GOOS='windows' "$(GO.MACRO)" vet ./internal/...
|
||||
|
||||
openapi-lint: ; cd ./openapi/ && $(YARN) test
|
||||
openapi-show: ; cd ./openapi/ && $(YARN) start
|
||||
|
||||
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||
|
||||
15
README.md
15
README.md
@@ -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://adguard-dns.io/kb/adguard-home/getting-started/
|
||||
[wiki-start]: https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started
|
||||
|
||||
### <a href="#guides" id="guides" name="guides">Guides</a>
|
||||
|
||||
@@ -205,9 +205,10 @@ Run `make init` to prepare the development environment.
|
||||
|
||||
You will need this to build AdGuard Home:
|
||||
|
||||
- [Go](https://golang.org/dl/) v1.23 or later;
|
||||
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
|
||||
- [Go](https://golang.org/dl/) v1.22 or later;
|
||||
- [Node.js](https://nodejs.org/en/download/) v16 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>
|
||||
|
||||
@@ -219,6 +220,14 @@ 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`.
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||
'dockerGo': 'adguard/go-builder:1.23.5--1'
|
||||
'dockerFrontend': 'adguard/home-js-builder:1.1'
|
||||
'dockerGo': 'adguard/go-builder:1.22.3--1'
|
||||
|
||||
'stages':
|
||||
- 'Build frontend':
|
||||
@@ -91,11 +91,6 @@
|
||||
'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':
|
||||
@@ -104,9 +99,6 @@
|
||||
|
||||
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; }'\
|
||||
@@ -115,8 +107,6 @@
|
||||
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\
|
||||
@@ -142,15 +132,13 @@
|
||||
# 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
|
||||
@@ -159,14 +147,14 @@
|
||||
docker info
|
||||
|
||||
# Prepare and push the build.
|
||||
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
|
||||
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
|
||||
'environment':
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
'final-tasks':
|
||||
@@ -277,8 +265,8 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||
'dockerGo': 'adguard/go-builder:1.23.5--1'
|
||||
'dockerFrontend': 'adguard/home-js-builder:1.1'
|
||||
'dockerGo': 'adguard/go-builder:1.22.3--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]+':
|
||||
@@ -293,5 +281,5 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||
'dockerGo': 'adguard/go-builder:1.23.5--1'
|
||||
'dockerFrontend': 'adguard/home-js-builder:1.1'
|
||||
'dockerGo': 'adguard/go-builder:1.22.3--1'
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||
'dockerGo': 'adguard/go-builder:1.23.5--1'
|
||||
'dockerFrontend': 'adguard/home-js-builder:1.1'
|
||||
'dockerGo': 'adguard/go-builder:1.22.3--1'
|
||||
'channel': 'development'
|
||||
|
||||
'stages':
|
||||
@@ -54,7 +54,6 @@
|
||||
'requirements':
|
||||
- 'adg-docker': 'true'
|
||||
|
||||
# TODO(e.burkov): Add the linting stage for markdown docs and shell scripts.
|
||||
'Test backend':
|
||||
'docker':
|
||||
'image': '${bamboo.dockerGo}'
|
||||
@@ -195,6 +194,6 @@
|
||||
# Set the default release channel on the release branch to beta, as we
|
||||
# may need to build a few of these.
|
||||
'variables':
|
||||
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||
'dockerGo': 'adguard/go-builder:1.23.5--1'
|
||||
'dockerFrontend': 'adguard/home-js-builder:1.1'
|
||||
'dockerGo': 'adguard/go-builder:1.22.3--1'
|
||||
'channel': 'candidate'
|
||||
|
||||
60
client/.eslintrc.json
vendored
60
client/.eslintrc.json
vendored
@@ -1,13 +1,9 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"parser": "babel-eslint",
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"prettier",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
"airbnb-base"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"jest": true,
|
||||
"node": true,
|
||||
@@ -20,21 +16,50 @@
|
||||
"version": "16.4"
|
||||
},
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".jsx", ".ts", ".tsx"]
|
||||
"webpack": {
|
||||
"config": "webpack.common.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"import/extensions": [
|
||||
"indent": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
4,
|
||||
{
|
||||
"js": "never",
|
||||
"jsx": "never",
|
||||
"ts": "never",
|
||||
"tsx": "never"
|
||||
"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
|
||||
}
|
||||
],
|
||||
"class-methods-use-this": "off",
|
||||
@@ -43,7 +68,10 @@
|
||||
"no-console": [
|
||||
"warn",
|
||||
{
|
||||
"allow": ["warn", "error"]
|
||||
"allow": [
|
||||
"warn",
|
||||
"error"
|
||||
]
|
||||
}
|
||||
],
|
||||
"import/no-extraneous-dependencies": [
|
||||
|
||||
2
client/.gitattributes
vendored
2
client/.gitattributes
vendored
@@ -1 +1 @@
|
||||
*.ts text eol=lf
|
||||
*.js text eol=lf
|
||||
|
||||
10
client/.prettierrc
vendored
10
client/.prettierrc
vendored
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": true,
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"arrowParens": "always",
|
||||
}
|
||||
46
client/.stylelintrc
vendored
Normal file
46
client/.stylelintrc
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"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
44
client/.stylelintrc.js
vendored
@@ -1,44 +0,0 @@
|
||||
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
14
client/babel.config.cjs
vendored
@@ -1,14 +0,0 @@
|
||||
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',
|
||||
],
|
||||
};
|
||||
};
|
||||
17
client/babel.config.js
vendored
Normal file
17
client/babel.config.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
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
9
client/constants.js
vendored
@@ -1,6 +1,11 @@
|
||||
export const BUILD_ENVS = {
|
||||
const BUILD_ENVS = {
|
||||
dev: 'development',
|
||||
prod: 'production',
|
||||
};
|
||||
|
||||
export const BASE_URL = 'control';
|
||||
const BASE_URL = 'control';
|
||||
|
||||
module.exports = {
|
||||
BUILD_ENVS,
|
||||
BASE_URL,
|
||||
};
|
||||
|
||||
6
client/global.d.ts
vendored
6
client/global.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
declare module '*.svg' {
|
||||
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
|
||||
export default content;
|
||||
}
|
||||
5
client/jest.config.js
vendored
Normal file
5
client/jest.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
transform: {
|
||||
'^.+\\.jsx?$': 'babel-jest',
|
||||
},
|
||||
};
|
||||
6
client/jest.config.mjs
vendored
6
client/jest.config.mjs
vendored
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
testEnvironment: 'jsdom',
|
||||
transform: {
|
||||
'^.+\\.tsx?$': 'babel-jest',
|
||||
},
|
||||
};
|
||||
38385
client/package-lock.json
generated
vendored
38385
client/package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
110
client/package.json
vendored
110
client/package.json
vendored
@@ -3,23 +3,19 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-dev": "cross-env NODE_ENV=development BUILD_ENV=dev webpack --config webpack.dev.js",
|
||||
"build-dev": "cross-env 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": "echo 'Lint temporarily disabled'",
|
||||
"lint-new": "eslint './src/**/*.(ts|tsx)'",
|
||||
"lint:fix": "eslint './src/**/*.(ts|tsx)' --fix",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:watch": "tsc --noEmit --watch"
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@nivo/line": "^0.64.0",
|
||||
"axios": "^0.19.2",
|
||||
"classnames": "^2.5.1",
|
||||
"classnames": "^2.2.6",
|
||||
"countries-and-timezones": "^3.6.0",
|
||||
"date-fns": "^1.29.0",
|
||||
"i18next": "^19.6.2",
|
||||
@@ -28,8 +24,7 @@
|
||||
"js-yaml": "^3.14.0",
|
||||
"lodash": "^4.17.19",
|
||||
"nanoid": "^3.1.9",
|
||||
"popper.js": "^1.16.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-click-outside": "^3.0.1",
|
||||
@@ -43,64 +38,53 @@
|
||||
"react-router-hash-link": "^1.2.2",
|
||||
"react-select": "^3.1.0",
|
||||
"react-table": "^6.11.4",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"redux": "^4.0.5",
|
||||
"redux-actions": "^2.6.5",
|
||||
"redux-form": "^8.3.10",
|
||||
"redux-form": "^8.3.5",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"ts-migrate": "^0.1.35",
|
||||
"url-polyfill": "^1.1.12"
|
||||
"url-polyfill": "^1.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"@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",
|
||||
"path": "^0.12.7",
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"browserslist": {
|
||||
"development": [
|
||||
|
||||
@@ -291,7 +291,7 @@
|
||||
"custom_ip": "عنوان IP مخصص",
|
||||
"blocking_ipv4": "حجب عنوان IPv4",
|
||||
"blocking_ipv6": "حجب عنوان IPv6",
|
||||
"blocked_response_ttl": "حظر استجابة TTL",
|
||||
"blocked_response_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": "السبت",
|
||||
|
||||
@@ -159,8 +159,6 @@
|
||||
"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": "Категория",
|
||||
@@ -285,12 +283,5 @@
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Сигурност",
|
||||
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това.",
|
||||
"parental_control": "Родителски контрол",
|
||||
"sunday_short": "Нд",
|
||||
"monday_short": "Пон",
|
||||
"tuesday_short": "Вт",
|
||||
"wednesday_short": "Ср",
|
||||
"thursday_short": "Чт",
|
||||
"friday_short": "Пт",
|
||||
"saturday_short": "Съб"
|
||||
"parental_control": "Родителски контрол"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
|
||||
"parallel_requests": "Paralelní požadavky",
|
||||
"load_balancing": "Optimalizace vytížení",
|
||||
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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, Ecosia, 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, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nebyly specifikovány žádné servery",
|
||||
"general_settings": "Obecná nastavení",
|
||||
"dns_settings": "Nastavení DNS",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
|
||||
"parallel_requests": "Parallelle forespørgsler",
|
||||
"load_balancing": "Belastningsfordeling",
|
||||
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
|
||||
"load_balancing_desc": "Forespørg én server ad gangen. AdGuard Home vil bruge en vægtet randomiseringsalgoritme til valg af server, så den hurtigste server oftere anvendes.",
|
||||
"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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home vil håndhæve sikker søgning i flg. søgemaskiner: Google, YouTube, Bing, DuckDuckGo, Yandex og Pixabay.",
|
||||
"no_servers_specified": "Ingen servere angivet",
|
||||
"general_settings": "Generelle indstillinger",
|
||||
"dns_settings": "DNS-indstillinger",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
|
||||
"parallel_requests": "Paralleles Abfragen",
|
||||
"load_balancing": "Lastverteilung",
|
||||
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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, Ecosia, Yandex und Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard kann Sichere Suche für folgende Suchmaschinen erzwingen: Google, YouTube, Bing, DuckDuckGo, Yandex und Pixabay.",
|
||||
"no_servers_specified": "Keine Server festgelegt",
|
||||
"general_settings": "Allgemeine Einstellungen",
|
||||
"dns_settings": "DNS-Einstellungen",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
|
||||
"parallel_requests": "Parallel requests",
|
||||
"load_balancing": "Load-balancing",
|
||||
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
|
||||
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.",
|
||||
"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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home will enforce safe search in the following search engines: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "No servers specified",
|
||||
"general_settings": "General settings",
|
||||
"dns_settings": "DNS settings",
|
||||
|
||||
@@ -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 upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
|
||||
"load_balancing_desc": "Consulta un 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.",
|
||||
"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, Ecosia, 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, Yandex y Pixabay.",
|
||||
"no_servers_specified": "No hay servidores especificados",
|
||||
"general_settings": "Configuración general",
|
||||
"dns_settings": "Configuración del DNS",
|
||||
|
||||
@@ -589,7 +589,6 @@
|
||||
"cache_optimistic_desc": "AdGuard Home را وادار می کند که از سمت حافظه پنهان پاسخ دهد حتی وقتی که موارد وارد شده منقضی شده باشد و همچنین سعی بر تازه کردن آنها می کند.",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "مسدودسازی بدافزار و فیشینگ",
|
||||
"filter_category_regional": "منطقهای",
|
||||
"filter_category_other": "ساير",
|
||||
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
|
||||
"parental_control": "نظارت والدین",
|
||||
|
||||
@@ -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ä 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.",
|
||||
"load_balancing_desc": "Lähetä pyyntö yhdelle ylävirtapalvelimelle kerrallaan. AdGuard Home pyrkii valitsemaan nopeimman palvelimen painotetun satunnaisalgoritminsa avulla.",
|
||||
"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": "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_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_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": "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.",
|
||||
"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.",
|
||||
"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 pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex ja Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home voi pakottaa turvallisen haun käyttöön seuraavissa hakukoneissa: Google, YouTube, Bing, DuckDuckGo, Yandex, 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-tiedolla",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-koodilla",
|
||||
"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 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_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_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 tunnilta",
|
||||
"interval_24_hour": "24 tuntia",
|
||||
"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": "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_services": "Keskeytä palveluesto",
|
||||
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",
|
||||
"schedule_services_desc_client": "Määritä palvelunestosuodattimen keskeytysajoitus 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",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
|
||||
"parallel_requests": "Requêtes en parallèle",
|
||||
"load_balancing": "Équilibrage de charge",
|
||||
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home appliquera la recherche sécurisée dans les moteurs de recherche suivants : Google, YouTube, Bing, DuckDuckGo, 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 d’interdire ce client, car l’exclusion 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",
|
||||
|
||||
@@ -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 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_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_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": "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.",
|
||||
"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.",
|
||||
"check_dhcp_servers": "Provjera DHCP poslužitelja",
|
||||
"save_config": "Spremi konfiguraciju",
|
||||
"enabled_dhcp": "DHCP poslužitelj je omogućen",
|
||||
@@ -425,9 +425,6 @@
|
||||
"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",
|
||||
@@ -678,7 +675,7 @@
|
||||
"use_saved_key": "Korištenje prethodno spremljenog ključa",
|
||||
"parental_control": "Roditeljska zaštita",
|
||||
"safe_browsing": "Sigurno surfanje",
|
||||
"served_from_cache_label": "Posluženo iz predmemorije",
|
||||
"served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>",
|
||||
"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?",
|
||||
|
||||
@@ -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 berkas host",
|
||||
"block_domain_use_filters_and_hosts": "Blokir domain menggunakan filter dan file hosts",
|
||||
"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 berkas host.",
|
||||
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
|
||||
"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 pada satu baris. Anda dapat menggunakan aturan adblock atau sintaks berkas host.",
|
||||
"system_host_files": "Berkas host sistem",
|
||||
"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",
|
||||
"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 berkas host, DNS terbalik, dll.",
|
||||
"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.",
|
||||
"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": "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_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_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 berkas host",
|
||||
"rewrite_hosts_applied": "Ditulis ulang oleh aturan file hosts",
|
||||
"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": "Pengawasan Orang Tua",
|
||||
"parental_control": "Kontrol 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}}",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
|
||||
"parallel_requests": "Richieste parallele",
|
||||
"load_balancing": "Bilanciamento del carico",
|
||||
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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 applicherà la ricerca sicura nei seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home forzerà la ricerca sicura sui seguenti motori di ricerca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nessun server specificato",
|
||||
"general_settings": "Impostazioni generali",
|
||||
"dns_settings": "Impostazioni DNS",
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
|
||||
"parallel_requests": "並列リクエスト",
|
||||
"load_balancing": "ロードバランシング",
|
||||
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
|
||||
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズム(weighted random algorithm)を使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
|
||||
"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、SOA、および NS リクエストに使用する DNS サーバー。プライベート IP 範囲内のサブネット (「192.168.12.34」など) を含む ARPA ドメインを要求し、プライベート IP アドレスを持つクライアントから来たリクエストが、プライベートリクエストとみなされます。本設定が特に指定されていない場合、OS のデフォルト DNS リゾルバ(AdGuard Home の IP アドレスを除く)が使用されます。",
|
||||
"local_ptr_desc": "AdGuard HomeがローカルPTRクエリに使用するDNSサーバーです。これらのサーバーは、rDNSを使ってプライベートIPアドレス(例えば\"192.168.12.34\")を持つクライアントのホスト名を解決するために使用されます。設定されていない場合、AdGuard HomeはOSのデフォルトDNSリゾルバーのアドレス(AdGuard Home自体のアドレスを除く)を自動的に使用します。",
|
||||
"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": "プライベートアップストリームサーバー、DHCP、/etc/hosts などを通じて、プライベート IP アドレスを含む ARPA ドメインの PTR、SOA、および NS リクエストを解決します。無効にした場合、AdGuard Home はこのようなリクエストのすべてに NXDOMAIN で応答します。",
|
||||
"use_private_ptr_resolvers_desc": "これらのアップストリームサーバーを使用して、ローカルで提供されるアドレスのリバースDNSルックアップを実行します。無効にすると、AdGuard Homeは、DHCP, /etc/hosts などから認識されるクライアントを除き、すべてのこのようなPTR要求に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, Ecosia, Yandex, Pixabay",
|
||||
"enforce_save_search_hint": "AdGuard Homeは、次の検索エンジンでセーフサーチを強制適用します: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay",
|
||||
"no_servers_specified": "サーバが指定されていません",
|
||||
"general_settings": "一般設定",
|
||||
"dns_settings": "DNS設定",
|
||||
|
||||
@@ -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, Ecosia, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
|
||||
"enforce_save_search_hint": "AdGuard Home은 Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay와 같은 검색 엔진에서 세이프서치를 시행합니다.",
|
||||
"no_servers_specified": "지정된 서버 없음",
|
||||
"general_settings": "일반 설정",
|
||||
"dns_settings": "DNS 설정",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
|
||||
"parallel_requests": "Parallelle verzoeken",
|
||||
"load_balancing": "Volume balanceren",
|
||||
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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 dwingt veilig zoeken af in de volgende zoekmachines: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home kan veilig zoeken forceren voor de volgende zoekmachines: Google, Youtube, Bing, en DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Geen servers gespecificeerd",
|
||||
"general_settings": "Algemene instellingen",
|
||||
"dns_settings": "DNS instellingen",
|
||||
@@ -166,7 +166,7 @@
|
||||
"encryption_settings": "Encryptie instellingen",
|
||||
"dhcp_settings": "DHCP instellingen",
|
||||
"upstream_dns": "Upstream DNS-servers",
|
||||
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer informatie</a> over het configureren van upstream DNS-servers.",
|
||||
"upstream_dns_help": "Een server-adres per regel invoeren. <a>Meer weten</a> over het configureren van upstream DNS-servers.",
|
||||
"upstream_dns_configured_in_file": "Geconfigureerd in {{path}}",
|
||||
"test_upstream_btn": "Test upstream",
|
||||
"upstreams": "Upstreams",
|
||||
@@ -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.",
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
"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.",
|
||||
|
||||
@@ -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": "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.",
|
||||
"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.",
|
||||
"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 {{count}} dzień",
|
||||
"for_last_days": "za ostatni dzień {{count}}",
|
||||
"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 klientów",
|
||||
"no_clients_found": "Nie znaleziono klienta",
|
||||
"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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home wymusza bezpieczne wyszukiwanie w następujących wyszukiwarkach: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nie określono serwerów",
|
||||
"general_settings": "Ustawienia główne",
|
||||
"dns_settings": "Ustawienia DNS",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
|
||||
"parallel_requests": "Solicitações paralelas",
|
||||
"load_balancing": "Balanceamento de carga",
|
||||
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
|
||||
"load_balancing_desc": "Consulte um servidor 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.",
|
||||
"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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "O AdGuard Home forcará a pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nenhum servidor especificado",
|
||||
"general_settings": "Configurações gerais",
|
||||
"dns_settings": "Configurações de DNS",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
|
||||
"parallel_requests": "Solicitações paralelas",
|
||||
"load_balancing": "Balanceamento de carga",
|
||||
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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 aplicará pesquisa segura nos seguintes motores de busca: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "O AdGuard Home forçará a pesquisa segura nos seguintes motores de busca: Google, Youtube, Bing, DuckDuckGo, Yandex, Pixabay.",
|
||||
"no_servers_specified": "Nenhum servidor especificado",
|
||||
"general_settings": "Definições gerais",
|
||||
"dns_settings": "Definições de DNS",
|
||||
|
||||
@@ -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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, YouTube, Bing, DuckDuckGo, Yandex и Pixabay.",
|
||||
"no_servers_specified": "Нет указанных серверов",
|
||||
"general_settings": "Основные настройки",
|
||||
"dns_settings": "Настройки DNS",
|
||||
|
||||
@@ -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,6 +102,7 @@
|
||||
"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> සබල කළ හැකිය.",
|
||||
@@ -114,15 +115,13 @@
|
||||
"general_statistics": "පොදු සංඛ්යාලේඛන",
|
||||
"number_of_dns_query_days": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||
"number_of_dns_query_days_plural": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||
"number_of_dns_query_hours": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||
"number_of_dns_query_hours_plural": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||
"number_of_dns_query_24_hours": "පසුගිය පැය 24 සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||
"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> තුළ පිහිටුවිය හැකිය.",
|
||||
@@ -131,7 +130,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": "ව.නා.ප. සැකසුම්",
|
||||
@@ -197,14 +196,12 @@
|
||||
"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": "සඳහන් කළ ව.නා.ප. සේවාදායක නිවැරදිව ක්රියා කරයි",
|
||||
@@ -278,7 +275,6 @@
|
||||
"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 ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා.කෙ. ලිපිනය සමඟ ප්රතිචාර දක්වයි",
|
||||
@@ -509,8 +505,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": "පෙරහන් වින්යාසය",
|
||||
@@ -619,8 +615,8 @@
|
||||
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
|
||||
"parental_control": "දෙමාපිය පාලනය",
|
||||
"safe_browsing": "ආරක්ෂිත පිරික්සුම",
|
||||
"served_from_cache_label": "නිහිතයෙන් සැපයිණි",
|
||||
"form_error_password_length": "මුරපදය අකුරු {{min}} සහ {{value}} ක් අතර විය යුතුය",
|
||||
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
|
||||
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
|
||||
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
|
||||
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
|
||||
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
|
||||
@@ -650,7 +646,6 @@
|
||||
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්යාලේඛන",
|
||||
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්රාහකය යොදන්න එපා",
|
||||
"ignore_statistics": "සංඛ්යාලේඛනයට මෙම අනුග්රාහකය යොදන්න එපා",
|
||||
"schedule_services": "සේවා අවහිර විරාමය",
|
||||
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
|
||||
"schedule_select_days": "දවස් තෝරන්න",
|
||||
"schedule_timezone": "වේලා කලාපයක් තෝරන්න",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
|
||||
"parallel_requests": "Paralelné dopyty",
|
||||
"load_balancing": "Vyrovnávanie záťaže",
|
||||
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
|
||||
"load_balancing_desc": "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.",
|
||||
"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 dopytu",
|
||||
"request_details": "Podrobnosti požiadavky",
|
||||
"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 vynúti bezpečné vyhľadávanie v nasledujúcich vyhľadávačoch: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
||||
"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.",
|
||||
"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 dopytov a zapíše hodnoty odoslané klientami do denníka dopytov.",
|
||||
"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_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ť dopyty 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ť požiadavky 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ší dopyty 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ší požiadavky 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á filtrácie 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á filtrovania 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",
|
||||
|
||||
@@ -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": "Genomdrev SafeSearch",
|
||||
"number_of_dns_query_to_safe_search": "Antalet DNS-förfrågningar till sökmotorer för vilka SafeSearch genomdrevs",
|
||||
"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",
|
||||
"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 SafeSearch",
|
||||
"enforce_safe_search": "Använd säker webbsökning",
|
||||
"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": "Ange klientnamn",
|
||||
"form_client_name": "Skriv in 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,6 +674,7 @@
|
||||
"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?",
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
"settings": "การตั้งค่า",
|
||||
"filters": "ตัวกรอง",
|
||||
"query_log": "บันทึกการสืบค้น",
|
||||
"nothing_found": "ไม่พบอะไร",
|
||||
"faq": "คำถามที่พบบ่อย",
|
||||
"version": "รุ่น",
|
||||
"address": "ที่อยู่",
|
||||
@@ -350,7 +349,7 @@
|
||||
"statistics_configuration": "การกำหนดค่าสถิติ",
|
||||
"statistics_retention": "การเก็บรักษาสถิติ",
|
||||
"statistics_retention_desc": "หากคุณลดค่าช่วงเวลาข้อมูลบางอย่างจะหายไป",
|
||||
"statistics_clear": "ล้างสถิติ",
|
||||
"statistics_clear": " ล้างค่าสถิติ",
|
||||
"statistics_clear_confirm": "คุณแน่ใจหรือไม่ว่าต้องการล้างสถิติ?",
|
||||
"statistics_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บรักษาสถิติ? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
|
||||
"statistics_cleared": "สถิติได้ถูกล้างเรียบร้อยแล้ว",
|
||||
@@ -391,13 +390,10 @@
|
||||
"check_title": "ตรวจสอบการกรอง",
|
||||
"check_desc": "ตรวจสอบว่าชื่อโฮสต์ถูกกรอง",
|
||||
"form_enter_host": "ป้อนชื่อโฮสต์",
|
||||
"show_blocked_responses": "ปิดกั้นแล้ว",
|
||||
"show_whitelisted_responses": "รายการที่อนุญาต",
|
||||
"show_processed_responses": "ประมวลผลแล้ว",
|
||||
"show_processed_responses": "การประมวลผล",
|
||||
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
|
||||
"safe_search": "ค้นหาอย่างปลอดภัย",
|
||||
"blocklist": "บัญชีดำ",
|
||||
"allowed": "รายการที่อนุญาต",
|
||||
"filter_category_other": "อื่น ๆ",
|
||||
"parental_control": "ควบคุมโดยผู้ปกครอง",
|
||||
"sunday_short": "อาทิตย์",
|
||||
|
||||
@@ -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": "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.",
|
||||
"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.",
|
||||
"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 internet 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 İnternet 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, Ecosia, Yandex, Pixabay.",
|
||||
"enforce_save_search_hint": "AdGuard Home, şu arama motorlarında güvenli aramayı uygular: Google, YouTube, Bing, DuckDuckGo, Yandex ve 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. dâhil 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. dahil 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 dâhil 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 dahil 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.",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
|
||||
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
|
||||
"use_private_ptr_resolvers_title": "Використовувати приватні зворотні DNS-резолвери",
|
||||
"use_private_ptr_resolvers_desc": "Розвʼязувати запити PTR, SOA та NS для доменів ARPA, що містять приватні IP-адреси, через приватні вихідні сервери, DHCP, /etc/hosts тощо. Якщо вимкнено, AdGuard Home відповідатиме на всі такі запити з NXDOMAIN.",
|
||||
"use_private_ptr_resolvers_desc": "Надсилати зворотні DNS-запити до вказаних серверів для клієнтів, що обслуговуються локально. Якщо вимкнено, AdGuard Home буде відповідати NXDOMAIN на всі такі PTR-запити, окрім запитів про клієнтів, що уже відомі завдяки DHCP, /etc/hosts тощо.",
|
||||
"check_dhcp_servers": "Перевірити DHCP-сервери",
|
||||
"save_config": "Зберегти конфігурацію",
|
||||
"enabled_dhcp": "DHCP-сервер увімкнено",
|
||||
@@ -343,10 +343,10 @@
|
||||
"known_tracker": "Відомі трекери",
|
||||
"install_welcome_title": "Вітаємо в AdGuard Home!",
|
||||
"install_welcome_desc": "AdGuard Home — це мережевий DNS-сервер, що блокує рекламу та відстеження. Його мета — надати вам контроль над усією мережею та всіма пристроями в ній без потреби використання програми на стороні клієнта.",
|
||||
"install_settings_title": "Вебінтерфейс адміністратора",
|
||||
"install_settings_title": "Веб-інтерфейс адміністратора",
|
||||
"install_settings_listen": "Мережевий інтерфейс",
|
||||
"install_settings_port": "Порт",
|
||||
"install_settings_interface_link": "Вебінтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
|
||||
"install_settings_interface_link": "Веб-інтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
|
||||
"form_error_port": "Уведіть правильне значення порту",
|
||||
"install_settings_dns": "DNS-сервер",
|
||||
"install_settings_dns_desc": "Вам потрібно буде налаштувати свої пристрої або маршрутизатор для використання DNS-сервера за такими адресами:",
|
||||
|
||||
@@ -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": "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.",
|
||||
"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.",
|
||||
"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 đượ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 tư. 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_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_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": "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.",
|
||||
"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.",
|
||||
"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 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.",
|
||||
"enforce_save_search_hint": "AdGuard Home có 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.",
|
||||
"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,9 +425,6 @@
|
||||
"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",
|
||||
@@ -678,7 +675,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_label": "Được phục vụ từ bộ nhớ đệm",
|
||||
"served_from_cache": "{{value}} <i>(được phục vụ từ bộ nhớ cache)</i>",
|
||||
"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?",
|
||||
|
||||
@@ -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、Ecosia、Yandex、Pixabay。",
|
||||
"enforce_save_search_hint": "AdGuard Home 对以下搜索引擎可强制启用安全搜索:Google、YouTube、Bing、DuckDuckGo、Yandex、Pixabay。",
|
||||
"no_servers_specified": "未找到指定的服务器",
|
||||
"general_settings": "常规设置",
|
||||
"dns_settings": "DNS 设置",
|
||||
|
||||
@@ -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 伺服器",
|
||||
@@ -20,7 +20,7 @@
|
||||
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
|
||||
"resolve_clients_desc": "透過傳送指標(PTR)查詢到對應的解析器(私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
|
||||
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
|
||||
"use_private_ptr_resolvers_desc": "通過私人上游伺服器、DHCP、/etc/hosts 等等,對包含私人 IP 位址的 ARPA 網域解析 PTR、SOA 和 NS 請求。如果被禁用,AdGuard Home 將對所有此類的請求以 NXDOMAIN 回覆。",
|
||||
"use_private_ptr_resolvers_desc": "使用私人上游伺服器、DHCP、/etc/hosts 等方式解析包含私人 IP 位址的 ARPA 網域的 PTR、SOA 和 NS 請求。如果禁用,AdGuard Home 將對所有此類請求以 NXDOMAIN 回應。",
|
||||
"check_dhcp_servers": "檢查動態主機設定協定(DHCP)伺服器",
|
||||
"save_config": "儲存配置",
|
||||
"enabled_dhcp": "動態主機設定協定(DHCP)伺服器被啟用",
|
||||
@@ -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、Ecosia、Yandex 和 Pixabay 中強制執行安全搜尋。",
|
||||
"enforce_save_search_hint": "AdGuard Home 將在下列的搜尋引擎:Google、YouTube、Bing、DuckDuckGo、Yandex 和 Pixabay 中強制執行安全搜尋。",
|
||||
"no_servers_specified": "無已明確指定的伺服器",
|
||||
"general_settings": "一般設定",
|
||||
"dns_settings": "DNS 設定",
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
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',
|
||||
@@ -47,7 +58,6 @@ 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
|
||||
@@ -73,26 +83,36 @@ 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::',
|
||||
@@ -110,11 +130,9 @@ 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 = [
|
||||
@@ -133,10 +151,8 @@ 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',
|
||||
@@ -154,10 +170,8 @@ 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',
|
||||
@@ -175,10 +189,8 @@ 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',
|
||||
@@ -198,26 +210,40 @@ 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',
|
||||
@@ -231,10 +257,8 @@ 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',
|
||||
@@ -250,11 +274,9 @@ 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;
|
||||
|
||||
@@ -269,29 +291,21 @@ 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 = [
|
||||
@@ -340,7 +354,6 @@ describe('sortIp', () => {
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
];
|
||||
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
});
|
||||
@@ -350,11 +363,9 @@ 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);
|
||||
});
|
||||
@@ -362,59 +373,42 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -457,12 +451,10 @@ 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);
|
||||
});
|
||||
});
|
||||
@@ -3,14 +3,13 @@ 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: any) => {
|
||||
export const getAccessList = () => async (dispatch) => {
|
||||
dispatch(getAccessListRequest());
|
||||
try {
|
||||
const data = await apiClient.getAccessList();
|
||||
@@ -25,7 +24,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: any) => async (dispatch: any) => {
|
||||
export const setAccessList = (config) => async (dispatch) => {
|
||||
dispatch(setAccessListRequest());
|
||||
try {
|
||||
const { allowed_clients, disallowed_clients, blocked_hosts } = config;
|
||||
@@ -49,7 +48,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: any, disallowed: any, disallowed_rule: any) => async (dispatch: any) => {
|
||||
export const toggleClientBlock = (ip, disallowed, disallowed_rule) => async (dispatch) => {
|
||||
dispatch(toggleClientBlockRequest());
|
||||
try {
|
||||
const accessList = await apiClient.getAccessList();
|
||||
@@ -61,10 +60,12 @@ export const toggleClientBlock = (ip: any, disallowed: any, disallowed_rule: any
|
||||
if (!disallowed_rule) {
|
||||
allowed_clients = allowed_clients.concat(ip);
|
||||
} else {
|
||||
disallowed_clients = disallowed_clients.filter((client: any) => client !== disallowed_rule);
|
||||
disallowed_clients = disallowed_clients
|
||||
.filter((client) => client !== disallowed_rule);
|
||||
}
|
||||
} else if (allowed_clients.length > 1) {
|
||||
allowed_clients = allowed_clients.filter((client: any) => client !== disallowed_rule);
|
||||
allowed_clients = allowed_clients
|
||||
.filter((client) => client !== disallowed_rule);
|
||||
} else {
|
||||
disallowed_clients = disallowed_clients.concat(ip);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
import i18next from 'i18next';
|
||||
import apiClient from '../api/Api';
|
||||
|
||||
import { getClients } from './index';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
|
||||
@@ -11,7 +10,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: any) => async (dispatch: any) => {
|
||||
export const addClient = (config) => async (dispatch) => {
|
||||
dispatch(addClientRequest());
|
||||
try {
|
||||
await apiClient.addClient(config);
|
||||
@@ -29,7 +28,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: any) => async (dispatch: any) => {
|
||||
export const deleteClient = (config) => async (dispatch) => {
|
||||
dispatch(deleteClientRequest());
|
||||
try {
|
||||
await apiClient.deleteClient(config);
|
||||
@@ -46,7 +45,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: any, name: any) => async (dispatch: any) => {
|
||||
export const updateClient = (config, name) => async (dispatch) => {
|
||||
dispatch(updateClientRequest());
|
||||
try {
|
||||
const data = { name, data: { ...config } };
|
||||
@@ -2,7 +2,6 @@ import { createAction } from 'redux-actions';
|
||||
import i18next from 'i18next';
|
||||
|
||||
import apiClient from '../api/Api';
|
||||
|
||||
import { splitByNewLine } from '../helpers/helpers';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
|
||||
@@ -10,7 +9,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: any) => {
|
||||
export const getDnsConfig = () => async (dispatch) => {
|
||||
dispatch(getDnsConfigRequest());
|
||||
try {
|
||||
const data = await apiClient.getDnsConfig();
|
||||
@@ -25,7 +24,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: any) => {
|
||||
export const clearDnsCache = () => async (dispatch) => {
|
||||
dispatch(clearDnsCacheRequest());
|
||||
try {
|
||||
const data = await apiClient.clearCache();
|
||||
@@ -41,7 +40,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: any) => async (dispatch: any) => {
|
||||
export const setDnsConfig = (config) => async (dispatch) => {
|
||||
dispatch(setDnsConfigRequest());
|
||||
try {
|
||||
const data = { ...config };
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
import apiClient from '../api/Api';
|
||||
|
||||
import { redirectToCurrentProtocol } from '../helpers/helpers';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
|
||||
@@ -8,7 +7,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: any) => {
|
||||
export const getTlsStatus = () => async (dispatch) => {
|
||||
dispatch(getTlsStatusRequest());
|
||||
try {
|
||||
const status = await apiClient.getTlsStatus();
|
||||
@@ -27,7 +26,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: any) => async (dispatch: any, getState: any) => {
|
||||
export const setTlsConfig = (config) => async (dispatch, getState) => {
|
||||
dispatch(setTlsConfigRequest());
|
||||
try {
|
||||
const { httpPort } = getState().dashboard;
|
||||
@@ -68,7 +67,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: any) => async (dispatch: any) => {
|
||||
export const validateTlsConfig = (config) => async (dispatch) => {
|
||||
dispatch(validateTlsConfigRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
@@ -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: any) => {
|
||||
export const getFilteringStatus = () => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const setRules = (rules) => async (dispatch) => {
|
||||
dispatch(setRulesRequest());
|
||||
try {
|
||||
const normalizedRules = {
|
||||
@@ -47,91 +47,83 @@ 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: 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());
|
||||
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());
|
||||
}
|
||||
};
|
||||
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: 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());
|
||||
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());
|
||||
}
|
||||
};
|
||||
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: 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 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 editFilterRequest = createAction('EDIT_FILTER_REQUEST');
|
||||
export const editFilterFailure = createAction('EDIT_FILTER_FAILURE');
|
||||
export const editFilterSuccess = createAction('EDIT_FILTER_SUCCESS');
|
||||
|
||||
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());
|
||||
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());
|
||||
}
|
||||
};
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const refreshFilters = (config) => async (dispatch) => {
|
||||
dispatch(refreshFiltersRequest());
|
||||
dispatch(showLoading());
|
||||
try {
|
||||
@@ -158,7 +150,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: any) => async (dispatch: any, getState: any) => {
|
||||
export const setFiltersConfig = (config) => async (dispatch, getState) => {
|
||||
dispatch(setFiltersConfigRequest());
|
||||
try {
|
||||
const { enabled } = config;
|
||||
@@ -188,18 +180,16 @@ export const checkHostSuccess = createAction('CHECK_HOST_SUCCESS');
|
||||
* @param {string} host.name
|
||||
* @returns {undefined}
|
||||
*/
|
||||
export const checkHost = (host: any) => async (dispatch: any) => {
|
||||
export const checkHost = (host) => async (dispatch) => {
|
||||
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());
|
||||
@@ -38,7 +38,7 @@ export const showSettingsFailure = createAction('SETTINGS_FAILURE_SHOW');
|
||||
* @param {*} status: boolean | SafeSearchConfig
|
||||
* @returns
|
||||
*/
|
||||
export const toggleSetting = (settingKey: any, status: any) => async (dispatch: any) => {
|
||||
export const toggleSetting = (settingKey, status) => async (dispatch) => {
|
||||
let successMessage = '';
|
||||
try {
|
||||
switch (settingKey) {
|
||||
@@ -80,58 +80,64 @@ 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: 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 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 toggleProtectionRequest = createAction('TOGGLE_PROTECTION_REQUEST');
|
||||
export const toggleProtectionFailure = createAction('TOGGLE_PROTECTION_FAILURE');
|
||||
export const toggleProtectionSuccess = createAction('TOGGLE_PROTECTION_SUCCESS');
|
||||
|
||||
const getDisabledMessage = (time: any) => {
|
||||
const getDisabledMessage = (time) => {
|
||||
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:
|
||||
@@ -139,24 +145,22 @@ const getDisabledMessage = (time: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
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 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 setDisableDurationTime = createAction('SET_DISABLED_DURATION_TIME');
|
||||
|
||||
export const setProtectionTimerTime = (updatedTime: any) => async (dispatch: any) => {
|
||||
export const setProtectionTimerTime = (updatedTime) => async (dispatch) => {
|
||||
dispatch(setDisableDurationTime({ timeToEnableProtection: updatedTime }));
|
||||
};
|
||||
|
||||
@@ -164,42 +168,40 @@ 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: any, getState: any) => {
|
||||
dispatch(getVersionRequest());
|
||||
try {
|
||||
const data = await apiClient.getGlobalVersion({ recheck_now: recheck });
|
||||
dispatch(getVersionSuccess(data));
|
||||
export const getVersion = (recheck = false) => async (dispatch, getState) => {
|
||||
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: any, handleRequestError: any, attempts = 60) => {
|
||||
const checkStatus = async (handleRequestSuccess, handleRequestError, attempts = 60) => {
|
||||
let timeout;
|
||||
|
||||
if (attempts === 0) {
|
||||
handleRequestError();
|
||||
}
|
||||
|
||||
const rmTimeout = (t: any) => t && clearTimeout(t);
|
||||
const rmTimeout = (t) => t && clearTimeout(t);
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${apiClient.baseUrl}/status`);
|
||||
@@ -218,18 +220,25 @@ const checkStatus = async (handleRequestSuccess: any, handleRequestError: any, a
|
||||
}
|
||||
} 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: any, getState: any) => {
|
||||
export const getUpdate = () => async (dispatch, getState) => {
|
||||
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" />,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -237,13 +246,12 @@ export const getUpdate = () => async (dispatch: any, getState: any) => {
|
||||
dispatch(getUpdateFailure());
|
||||
};
|
||||
|
||||
const handleRequestSuccess = (response: any) => {
|
||||
const handleRequestSuccess = (response) => {
|
||||
const responseVersion = response.data?.version;
|
||||
|
||||
if (dnsVersion !== responseVersion) {
|
||||
dispatch(getUpdateSuccess());
|
||||
|
||||
window.location.reload();
|
||||
window.location.reload(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -259,20 +267,18 @@ 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: any) => {
|
||||
export const getClients = () => async (dispatch) => {
|
||||
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());
|
||||
@@ -283,7 +289,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: any) => {
|
||||
export const getProfile = () => async (dispatch) => {
|
||||
dispatch(getProfileRequest());
|
||||
try {
|
||||
const profile = await apiClient.getProfile();
|
||||
@@ -299,17 +305,16 @@ 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: any) => {
|
||||
export const getDnsStatus = () => async (dispatch) => {
|
||||
dispatch(dnsStatusRequest());
|
||||
|
||||
const handleRequestError = () => {
|
||||
dispatch(addErrorToast({ error: 'dns_status_error' }));
|
||||
dispatch(dnsStatusFailure());
|
||||
|
||||
window.location.reload();
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
const handleRequestSuccess = (response: any) => {
|
||||
const handleRequestSuccess = (response) => {
|
||||
const dnsStatus = response.data;
|
||||
if (dnsStatus.protection_disabled_duration === 0) {
|
||||
dnsStatus.protection_disabled_duration = null;
|
||||
@@ -337,17 +342,16 @@ 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: any) => {
|
||||
export const getTimerStatus = () => async (dispatch) => {
|
||||
dispatch(timerStatusRequest());
|
||||
|
||||
const handleRequestError = () => {
|
||||
dispatch(addErrorToast({ error: 'dns_status_error' }));
|
||||
dispatch(dnsStatusFailure());
|
||||
|
||||
window.location.reload();
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
const handleRequestSuccess = (response: any) => {
|
||||
const handleRequestSuccess = (response) => {
|
||||
const dnsStatus = response.data;
|
||||
if (dnsStatus.protection_disabled_duration === 0) {
|
||||
dnsStatus.protection_disabled_duration = null;
|
||||
@@ -372,26 +376,30 @@ 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 }: any, upstream_dns_file: any) =>
|
||||
async (dispatch: any) => {
|
||||
dispatch(testUpstreamRequest());
|
||||
try {
|
||||
const removeComments = compose(filterOutComments, splitByNewLine);
|
||||
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);
|
||||
|
||||
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 }) }));
|
||||
@@ -399,54 +407,46 @@ 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'));
|
||||
}
|
||||
|
||||
dispatch(testUpstreamSuccess());
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(testUpstreamFailure());
|
||||
if (testMessages.every((message) => message === 'OK' || message.startsWith('WARNING:'))) {
|
||||
dispatch(addSuccessToast('dns_test_ok_toast'));
|
||||
}
|
||||
};
|
||||
|
||||
export const testUpstreamWithFormValues = () => async (dispatch: any, getState: any) => {
|
||||
dispatch(testUpstreamSuccess());
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(testUpstreamFailure());
|
||||
}
|
||||
};
|
||||
|
||||
export const testUpstreamWithFormValues = () => async (dispatch, getState) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const changeLanguage = (lang) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const changeTheme = (theme) => async (dispatch) => {
|
||||
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: any) => {
|
||||
export const getDhcpStatus = () => async (dispatch) => {
|
||||
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: any) => {
|
||||
export const getDhcpInterfaces = () => async (dispatch) => {
|
||||
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: any) => async (dispatch: any, getState: any) => {
|
||||
export const findActiveDhcp = (name) => async (dispatch, getState) => {
|
||||
dispatch(findActiveDhcpRequest());
|
||||
try {
|
||||
const req = {
|
||||
@@ -559,12 +559,12 @@ export const findActiveDhcp = (name: any) => async (dispatch: any, getState: any
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const setDhcpConfig = (values) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const toggleDhcp = (values) => async (dispatch) => {
|
||||
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: any) => {
|
||||
export const resetDhcp = () => async (dispatch) => {
|
||||
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: any) => {
|
||||
export const resetDhcpLeases = () => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const addStaticLease = (config) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const removeStaticLease = (config) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const updateStaticLease = (config) => async (dispatch) => {
|
||||
dispatch(updateStaticLeaseRequest());
|
||||
try {
|
||||
await apiClient.updateStaticLease(config);
|
||||
@@ -719,42 +719,42 @@ export const updateStaticLease = (config: any) => async (dispatch: any) => {
|
||||
|
||||
export const removeToast = createAction('REMOVE_TOAST');
|
||||
|
||||
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;
|
||||
export const toggleBlocking = (
|
||||
type, domain, baseRule, baseUnblocking,
|
||||
) => async (dispatch, getState) => {
|
||||
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: any, domain: any, client: any) => {
|
||||
const escapedClientName = client
|
||||
.replace(/'/g, "\\'")
|
||||
export const toggleBlockingForClient = (type, domain, client) => {
|
||||
const escapedClientName = client.replace(/'/g, '\\\'')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/,/g, '\\,')
|
||||
.replace(/\|/g, '\\|');
|
||||
@@ -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: any) => {
|
||||
export const getDefaultAddresses = () => async (dispatch) => {
|
||||
dispatch(getDefaultAddressesRequest());
|
||||
try {
|
||||
const addresses = await apiClient.getDefaultAddresses();
|
||||
@@ -24,10 +24,13 @@ 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: any) => async (dispatch: any) => {
|
||||
export const setAllSettings = (values) => async (dispatch) => {
|
||||
dispatch(setAllSettingsRequest());
|
||||
try {
|
||||
const { confirm_password, ...config } = values;
|
||||
const {
|
||||
confirm_password,
|
||||
...config
|
||||
} = values;
|
||||
|
||||
await apiClient.setAllSettings(config);
|
||||
dispatch(setAllSettingsSuccess());
|
||||
@@ -44,7 +47,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: any) => async (dispatch: any) => {
|
||||
export const checkConfig = (values) => async (dispatch) => {
|
||||
dispatch(checkConfigRequest());
|
||||
try {
|
||||
const check = await apiClient.checkConfig(values);
|
||||
@@ -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: any) => async (dispatch: any) => {
|
||||
export const processLogin = (values) => async (dispatch) => {
|
||||
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) {
|
||||
@@ -1,12 +1,13 @@
|
||||
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: any) => {
|
||||
const getLogsWithParams = async (config) => {
|
||||
const { older_than, filter, ...values } = config;
|
||||
const rawLogs = await apiClient.getQueryLog({
|
||||
...filter,
|
||||
@@ -27,20 +28,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: any, filter: any, dispatch: any, getState: any, total?: any) => {
|
||||
const shortPollQueryLogs = async (data, filter, dispatch, getState, total) => {
|
||||
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());
|
||||
@@ -74,24 +75,22 @@ 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: any, getState: any) => {
|
||||
export const updateLogs = () => async (dispatch, getState) => {
|
||||
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: any, getState: any) => {
|
||||
export const getLogs = () => async (dispatch, getState) => {
|
||||
dispatch(getLogsRequest());
|
||||
try {
|
||||
const { isFiltered, filter, oldest } = getState().queryLogs;
|
||||
@@ -122,29 +121,26 @@ export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
|
||||
* @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object
|
||||
* @returns function
|
||||
*/
|
||||
export const setLogsFilter = (filter: any) => setLogsFilterRequest(filter);
|
||||
export const setLogsFilter = (filter) => setLogsFilterRequest(filter);
|
||||
|
||||
export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST');
|
||||
export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE');
|
||||
export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS');
|
||||
|
||||
export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: any) => {
|
||||
export const setFilteredLogs = (filter) => async (dispatch, getState) => {
|
||||
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));
|
||||
@@ -153,7 +149,7 @@ export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState:
|
||||
|
||||
export const resetFilteredLogs = () => setFilteredLogs(DEFAULT_LOGS_FILTER);
|
||||
|
||||
export const refreshFilteredLogs = () => async (dispatch: any, getState: any) => {
|
||||
export const refreshFilteredLogs = () => async (dispatch, getState) => {
|
||||
const { filter } = getState().queryLogs;
|
||||
await dispatch(setFilteredLogs(filter));
|
||||
};
|
||||
@@ -162,7 +158,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: any) => {
|
||||
export const clearLogs = () => async (dispatch) => {
|
||||
dispatch(clearLogsRequest());
|
||||
try {
|
||||
await apiClient.clearQueryLog();
|
||||
@@ -178,7 +174,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: any) => {
|
||||
export const getLogsConfig = () => async (dispatch) => {
|
||||
dispatch(getLogsConfigRequest());
|
||||
try {
|
||||
const data = await apiClient.getQueryLogConfig();
|
||||
@@ -193,7 +189,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: any) => async (dispatch: any) => {
|
||||
export const setLogsConfig = (config) => async (dispatch) => {
|
||||
dispatch(setLogsConfigRequest());
|
||||
try {
|
||||
await apiClient.setQueryLogConfig(config);
|
||||
@@ -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: any) => {
|
||||
export const getRewritesList = () => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const addRewrite = (config) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const updateRewrite = (config) => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const deleteRewrite = (config) => async (dispatch) => {
|
||||
dispatch(deleteRewriteRequest());
|
||||
try {
|
||||
await apiClient.deleteRewrite(config);
|
||||
@@ -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: any) => {
|
||||
export const getBlockedServices = () => async (dispatch) => {
|
||||
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: any) => {
|
||||
export const getAllBlockedServices = () => async (dispatch) => {
|
||||
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: any) => async (dispatch: any) => {
|
||||
export const updateBlockedServices = (values) => async (dispatch) => {
|
||||
dispatch(updateBlockedServicesRequest());
|
||||
try {
|
||||
await apiClient.updateBlockedServices(values);
|
||||
@@ -1,14 +1,16 @@
|
||||
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: any) => {
|
||||
export const getStatsConfig = () => async (dispatch) => {
|
||||
dispatch(getStatsConfigRequest());
|
||||
try {
|
||||
const data = await apiClient.getStatsConfig();
|
||||
@@ -23,7 +25,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: any) => async (dispatch: any) => {
|
||||
export const setStatsConfig = (config) => async (dispatch) => {
|
||||
dispatch(setStatsConfigRequest());
|
||||
try {
|
||||
await apiClient.setStatsConfig(config);
|
||||
@@ -39,14 +41,13 @@ 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: any) => {
|
||||
export const getStats = () => async (dispatch) => {
|
||||
dispatch(getStatsRequest());
|
||||
try {
|
||||
const stats = await apiClient.getStats();
|
||||
const normalizedTopClients = normalizeTopStats(stats.top_clients);
|
||||
|
||||
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
|
||||
const clients = await apiClient.searchClients(clientsParams);
|
||||
const clients = await apiClient.findClients(clientsParams);
|
||||
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
|
||||
|
||||
const normalizedStats = {
|
||||
@@ -70,7 +71,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: any) => {
|
||||
export const resetStats = () => async (dispatch) => {
|
||||
dispatch(getStatsRequest());
|
||||
try {
|
||||
await apiClient.resetStats();
|
||||
@@ -1,16 +1,17 @@
|
||||
import axios from 'axios';
|
||||
|
||||
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 {
|
||||
QUERY_LOGS_PAGE_LIMIT, HTML_PAGES, R_PATH_LAST_PART, THEMES,
|
||||
} from '../helpers/constants';
|
||||
import { BASE_URL } from '../../constants';
|
||||
import i18n from '../i18n';
|
||||
import { LANGUAGES } from '../helpers/twosky';
|
||||
|
||||
class Api {
|
||||
baseUrl = BASE_URL;
|
||||
|
||||
async makeRequest(path: any, method = 'POST', config: any = {}) {
|
||||
async makeRequest(path, method = 'POST', config) {
|
||||
const url = `${this.baseUrl}/${path}`;
|
||||
|
||||
const axiosConfig = config || {};
|
||||
@@ -28,26 +29,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' };
|
||||
|
||||
@@ -57,11 +58,10 @@ class Api {
|
||||
|
||||
getGlobalStatus() {
|
||||
const { path, method } = this.GLOBAL_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
testUpstream(servers: any) {
|
||||
testUpstream(servers) {
|
||||
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: any) {
|
||||
getGlobalVersion(data) {
|
||||
const { path, method } = this.GLOBAL_VERSION;
|
||||
const config = {
|
||||
data,
|
||||
@@ -79,7 +79,6 @@ class Api {
|
||||
|
||||
getUpdate() {
|
||||
const { path, method } = this.GLOBAL_UPDATE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
@@ -102,11 +101,10 @@ class Api {
|
||||
|
||||
getFilteringStatus() {
|
||||
const { path, method } = this.FILTERING_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
refreshFilters(config: any) {
|
||||
refreshFilters(config) {
|
||||
const { path, method } = this.FILTERING_REFRESH;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -115,7 +113,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
addFilter(config: any) {
|
||||
addFilter(config) {
|
||||
const { path, method } = this.FILTERING_ADD_FILTER;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -124,7 +122,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
removeFilter(config: any) {
|
||||
removeFilter(config) {
|
||||
const { path, method } = this.FILTERING_REMOVE_FILTER;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -133,7 +131,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setRules(rules: any) {
|
||||
setRules(rules) {
|
||||
const { path, method } = this.FILTERING_SET_RULES;
|
||||
const parameters = {
|
||||
data: rules,
|
||||
@@ -141,7 +139,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setFiltersConfig(config: any) {
|
||||
setFiltersConfig(config) {
|
||||
const { path, method } = this.FILTERING_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -149,7 +147,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setFilterUrl(config: any) {
|
||||
setFilterUrl(config) {
|
||||
const { path, method } = this.FILTERING_SET_URL;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -157,10 +155,9 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
checkHost(params: any) {
|
||||
checkHost(params) {
|
||||
const { path, method } = this.FILTERING_CHECK_HOST;
|
||||
const url = getPathWithQueryString(path, params);
|
||||
|
||||
return this.makeRequest(url, method);
|
||||
}
|
||||
|
||||
@@ -173,19 +170,16 @@ 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);
|
||||
}
|
||||
|
||||
@@ -198,19 +192,16 @@ 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);
|
||||
}
|
||||
|
||||
@@ -221,7 +212,6 @@ class Api {
|
||||
|
||||
getSafesearchStatus() {
|
||||
const { path, method } = this.SAFESEARCH_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
@@ -238,7 +228,7 @@ class Api {
|
||||
* @param {*} data - SafeSearchConfig
|
||||
* @returns 200 ok
|
||||
*/
|
||||
updateSafesearch(data: any) {
|
||||
updateSafesearch(data) {
|
||||
const { path, method } = this.SAFESEARCH_UPDATE;
|
||||
return this.makeRequest(path, method, { data });
|
||||
}
|
||||
@@ -255,7 +245,7 @@ class Api {
|
||||
|
||||
// Language
|
||||
|
||||
async changeLanguage(config: any) {
|
||||
async changeLanguage(config) {
|
||||
const profile = await this.getProfile();
|
||||
profile.language = config.language;
|
||||
|
||||
@@ -264,7 +254,7 @@ class Api {
|
||||
|
||||
// Theme
|
||||
|
||||
async changeTheme(config: any) {
|
||||
async changeTheme(config) {
|
||||
const profile = await this.getProfile();
|
||||
profile.theme = config.theme;
|
||||
|
||||
@@ -292,17 +282,15 @@ 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: any) {
|
||||
setDhcpConfig(config) {
|
||||
const { path, method } = this.DHCP_SET_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -310,7 +298,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
findActiveDhcp(req: any) {
|
||||
findActiveDhcp(req) {
|
||||
const { path, method } = this.DHCP_FIND_ACTIVE;
|
||||
const parameters = {
|
||||
data: req,
|
||||
@@ -318,7 +306,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
addStaticLease(config: any) {
|
||||
addStaticLease(config) {
|
||||
const { path, method } = this.DHCP_ADD_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -326,7 +314,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
removeStaticLease(config: any) {
|
||||
removeStaticLease(config) {
|
||||
const { path, method } = this.DHCP_REMOVE_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -334,7 +322,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateStaticLease(config: any) {
|
||||
updateStaticLease(config) {
|
||||
const { path, method } = this.DHCP_UPDATE_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -344,13 +332,11 @@ 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);
|
||||
}
|
||||
|
||||
@@ -363,11 +349,10 @@ class Api {
|
||||
|
||||
getDefaultAddresses() {
|
||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setAllSettings(config: any) {
|
||||
setAllSettings(config) {
|
||||
const { path, method } = this.INSTALL_CONFIGURE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -375,7 +360,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
checkConfig(config: any) {
|
||||
checkConfig(config) {
|
||||
const { path, method } = this.INSTALL_CHECK_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -392,11 +377,10 @@ class Api {
|
||||
|
||||
getTlsStatus() {
|
||||
const { path, method } = this.TLS_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setTlsConfig(config: any) {
|
||||
setTlsConfig(config) {
|
||||
const { path, method } = this.TLS_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -404,7 +388,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
validateTlsConfig(config: any) {
|
||||
validateTlsConfig(config) {
|
||||
const { path, method } = this.TLS_VALIDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -415,7 +399,7 @@ class Api {
|
||||
// Per-client settings
|
||||
GET_CLIENTS = { path: 'clients', method: 'GET' };
|
||||
|
||||
SEARCH_CLIENTS = { path: 'clients/search', method: 'POST' };
|
||||
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
|
||||
|
||||
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
|
||||
|
||||
@@ -425,11 +409,10 @@ class Api {
|
||||
|
||||
getClients() {
|
||||
const { path, method } = this.GET_CLIENTS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
addClient(config: any) {
|
||||
addClient(config) {
|
||||
const { path, method } = this.ADD_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -437,7 +420,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
deleteClient(config: any) {
|
||||
deleteClient(config) {
|
||||
const { path, method } = this.DELETE_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -445,7 +428,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateClient(config: any) {
|
||||
updateClient(config) {
|
||||
const { path, method } = this.UPDATE_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -453,12 +436,10 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
searchClients(config: any) {
|
||||
const { path, method } = this.SEARCH_CLIENTS;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
findClients(params) {
|
||||
const { path, method } = this.FIND_CLIENTS;
|
||||
const url = getPathWithQueryString(path, params);
|
||||
return this.makeRequest(url, method);
|
||||
}
|
||||
|
||||
// DNS access settings
|
||||
@@ -468,11 +449,10 @@ class Api {
|
||||
|
||||
getAccessList() {
|
||||
const { path, method } = this.ACCESS_LIST;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setAccessList(config: any) {
|
||||
setAccessList(config) {
|
||||
const { path, method } = this.ACCESS_SET;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -491,11 +471,10 @@ class Api {
|
||||
|
||||
getRewritesList() {
|
||||
const { path, method } = this.REWRITES_LIST;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
addRewrite(config: any) {
|
||||
addRewrite(config) {
|
||||
const { path, method } = this.REWRITE_ADD;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -503,7 +482,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateRewrite(config: any) {
|
||||
updateRewrite(config) {
|
||||
const { path, method } = this.REWRITE_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -511,7 +490,7 @@ class Api {
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
deleteRewrite(config: any) {
|
||||
deleteRewrite(config) {
|
||||
const { path, method } = this.REWRITE_DELETE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -528,17 +507,15 @@ 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: any) {
|
||||
updateBlockedServices(config) {
|
||||
const { path, method } = this.BLOCKED_SERVICES_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
@@ -557,17 +534,15 @@ 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: any) {
|
||||
setStatsConfig(data) {
|
||||
const { path, method } = this.UPDATE_STATS_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
@@ -577,7 +552,6 @@ class Api {
|
||||
|
||||
resetStats() {
|
||||
const { path, method } = this.STATS_RESET;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
@@ -590,22 +564,20 @@ class Api {
|
||||
|
||||
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
|
||||
|
||||
getQueryLog(params: any) {
|
||||
getQueryLog(params) {
|
||||
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: any) {
|
||||
setQueryLogConfig(data) {
|
||||
const { path, method } = this.UPDATE_QUERY_LOG_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
@@ -615,14 +587,13 @@ class Api {
|
||||
|
||||
clearQueryLog() {
|
||||
const { path, method } = this.QUERY_LOG_CLEAR;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Login
|
||||
LOGIN = { path: 'login', method: 'POST' };
|
||||
|
||||
login(data: any) {
|
||||
login(data) {
|
||||
const { path, method } = this.LOGIN;
|
||||
const config = {
|
||||
data,
|
||||
@@ -637,11 +608,10 @@ class Api {
|
||||
|
||||
getProfile() {
|
||||
const { path, method } = this.GET_PROFILE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setProfile(data: any) {
|
||||
setProfile(data) {
|
||||
const theme = data.theme ? data.theme : THEMES.auto;
|
||||
const defaultLanguage = i18n.language ? i18n.language : LANGUAGES.en;
|
||||
const language = data.language ? data.language : defaultLanguage;
|
||||
@@ -659,11 +629,10 @@ class Api {
|
||||
|
||||
getDnsConfig() {
|
||||
const { path, method } = this.GET_DNS_CONFIG;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setDnsConfig(data: any) {
|
||||
setDnsConfig(data) {
|
||||
const { path, method } = this.SET_DNS_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
@@ -673,7 +642,7 @@ class Api {
|
||||
|
||||
SET_PROTECTION = { path: 'protection', method: 'POST' };
|
||||
|
||||
setProtection(data: any) {
|
||||
setProtection(data) {
|
||||
const { enabled, duration } = data;
|
||||
const { path, method } = this.SET_PROTECTION;
|
||||
|
||||
@@ -685,7 +654,6 @@ class Api {
|
||||
|
||||
clearCache() {
|
||||
const { path, method } = this.CLEAR_CACHE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
}
|
||||
@@ -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,14 +74,12 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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';
|
||||
@@ -10,6 +9,8 @@ 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';
|
||||
@@ -18,14 +19,15 @@ 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';
|
||||
@@ -33,19 +35,15 @@ 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 = [
|
||||
{
|
||||
@@ -103,17 +101,26 @@ 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<
|
||||
RootState,
|
||||
RootState['dashboard']
|
||||
>((state) => state.dashboard, shallowEqual);
|
||||
const {
|
||||
language,
|
||||
isCoreRunning,
|
||||
isUpdateAvailable,
|
||||
processing,
|
||||
theme,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
|
||||
const { processing: processingEncryption } = useSelector<RootState, RootState['encryption']>(
|
||||
(state) => state.encryption,
|
||||
shallowEqual,
|
||||
);
|
||||
const { processing: processingEncryption } = useSelector((
|
||||
state,
|
||||
) => state.encryption, shallowEqual);
|
||||
|
||||
const updateAvailable = isCoreRunning && isUpdateAvailable;
|
||||
|
||||
@@ -150,7 +157,7 @@ const App = () => {
|
||||
setLanguage();
|
||||
}, [language]);
|
||||
|
||||
const handleAutoTheme = (e: any, accountTheme: any) => {
|
||||
const handleAutoTheme = (e, accountTheme) => {
|
||||
if (accountTheme !== THEMES.auto) {
|
||||
return;
|
||||
}
|
||||
@@ -188,50 +195,35 @@ const App = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
{updateAvailable && (
|
||||
<>
|
||||
<UpdateTopline />
|
||||
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>;
|
||||
};
|
||||
|
||||
<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>
|
||||
);
|
||||
renderRoute.propTypes = {
|
||||
path: propTypes.oneOfType([propTypes.string, propTypes.arrayOf(propTypes.string)]).isRequired,
|
||||
component: propTypes.element.isRequired,
|
||||
exact: propTypes.bool,
|
||||
};
|
||||
|
||||
export default hot(App);
|
||||
@@ -1,37 +1,25 @@
|
||||
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: any) =>
|
||||
function cell(row: any) {
|
||||
const { value } = row;
|
||||
const percent = getPercent(totalBlocked, value);
|
||||
const CountCell = (totalBlocked) => function cell(row) {
|
||||
const { value } = row;
|
||||
const percent = getPercent(totalBlocked, value);
|
||||
|
||||
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;
|
||||
}
|
||||
return <Cell value={value}
|
||||
percent={percent}
|
||||
color={STATUS_COLORS.red}
|
||||
search={row.original.domain}
|
||||
/>;
|
||||
};
|
||||
|
||||
const BlockedDomains = ({
|
||||
t,
|
||||
@@ -42,13 +30,20 @@ const BlockedDomains = ({
|
||||
replacedSafebrowsing,
|
||||
replacedParental,
|
||||
replacedSafesearch,
|
||||
}: BlockedDomainsProps) => {
|
||||
const totalBlocked = blockedFiltering + replacedSafebrowsing + replacedParental + replacedSafesearch;
|
||||
}) => {
|
||||
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 }: any) => ({
|
||||
data={topBlockedDomains.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
@@ -75,4 +70,15 @@ 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);
|
||||
@@ -1,12 +1,10 @@
|
||||
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';
|
||||
|
||||
@@ -18,14 +16,11 @@ 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';
|
||||
import { RootState } from '../../initialState';
|
||||
|
||||
const getClientsPercentColor = (percent: any) => {
|
||||
const getClientsPercentColor = (percent) => {
|
||||
if (percent > 50) {
|
||||
return STATUS_COLORS.green;
|
||||
}
|
||||
@@ -35,13 +30,9 @@ const getClientsPercentColor = (percent: any) => {
|
||||
return STATUS_COLORS.red;
|
||||
};
|
||||
|
||||
const CountCell = (row: any) => {
|
||||
const {
|
||||
value,
|
||||
original: { ip },
|
||||
} = row;
|
||||
|
||||
const numDnsQueries = useSelector<RootState>((state) => state.stats.numDnsQueries, shallowEqual);
|
||||
const CountCell = (row) => {
|
||||
const { value, original: { ip } } = row;
|
||||
const numDnsQueries = useSelector((state) => state.stats.numDnsQueries, shallowEqual);
|
||||
|
||||
const percent = getPercent(numDnsQueries, value);
|
||||
const percentColor = getClientsPercentColor(percent);
|
||||
@@ -49,29 +40,22 @@ const CountCell = (row: any) => {
|
||||
return <Cell value={value} percent={percent} color={percentColor} search={ip} />;
|
||||
};
|
||||
|
||||
const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) => {
|
||||
const renderBlockingButton = (ip, disallowed, disallowed_rule) => {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
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 processingSet = useSelector((state) => state.access.processingSet);
|
||||
const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual);
|
||||
|
||||
const [isOptionsOpened, setOptionsOpened] = useState(false);
|
||||
|
||||
const toggleClientStatus = async (ip: any, disallowed: any, disallowed_rule: any) => {
|
||||
const toggleClientStatus = async (ip, disallowed, disallowed_rule) => {
|
||||
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 (allowedClients.length > 0) {
|
||||
if (allowedСlients.length > 0) {
|
||||
confirmMessage = confirmMessage.concat(`\n\n${t('filter_allowlist', { disallowed_rule })}`);
|
||||
}
|
||||
}
|
||||
@@ -89,11 +73,15 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
|
||||
|
||||
const text = disallowed ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
||||
|
||||
const lastRuleInAllowlist = !disallowed && allowedClients === disallowed_rule;
|
||||
const lastRuleInAllowlist = !disallowed && allowedСlients === 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>
|
||||
@@ -104,18 +92,16 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
|
||||
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}
|
||||
@@ -127,42 +113,35 @@ const renderBlockingButton = (ip: any, disallowed: any, disallowed_rule: any) =>
|
||||
);
|
||||
};
|
||||
|
||||
const ClientCell = (row: any) => {
|
||||
const {
|
||||
value,
|
||||
original: {
|
||||
info,
|
||||
info: { disallowed, disallowed_rule },
|
||||
},
|
||||
} = row;
|
||||
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>
|
||||
</>
|
||||
);
|
||||
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>
|
||||
</>;
|
||||
};
|
||||
|
||||
interface ClientsProps {
|
||||
refreshButton: React.ReactNode;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
const Clients = ({ refreshButton, subtitle }: ClientsProps) => {
|
||||
const Clients = ({
|
||||
refreshButton,
|
||||
subtitle,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const topClients = useSelector<RootState, RootState['stats']['topClients']>(
|
||||
(state) => state.stats.topClients,
|
||||
shallowEqual,
|
||||
);
|
||||
const topClients = useSelector((state) => state.stats.topClients, shallowEqual);
|
||||
|
||||
return (
|
||||
<Card title={t('top_clients')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
|
||||
<Card
|
||||
title={t('top_clients')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topClients.map(({ name: ip, count, info, blocked }: any) => ({
|
||||
data={topClients.map(({
|
||||
name: ip, count, info, blocked,
|
||||
}) => ({
|
||||
ip,
|
||||
count,
|
||||
info,
|
||||
@@ -188,14 +167,12 @@ const Clients = ({ refreshButton, subtitle }: ClientsProps) => {
|
||||
minRows={TABLES_MIN_ROWS}
|
||||
defaultPageSize={DASHBOARD_TABLES_DEFAULT_PAGE_SIZE}
|
||||
className="-highlight card-table-overflow--limited clients__table"
|
||||
getTrProps={(_state: any, rowInfo: any) => {
|
||||
getTrProps={(_state, rowInfo) => {
|
||||
if (!rowInfo) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const {
|
||||
info: { disallowed },
|
||||
} = rowInfo.original;
|
||||
const { info: { disallowed } } = rowInfo.original;
|
||||
|
||||
return disallowed ? { className: 'logs__row--red' } : {};
|
||||
}}
|
||||
@@ -204,4 +181,9 @@ const Clients = ({ refreshButton, subtitle }: ClientsProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
Clients.propTypes = {
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Clients;
|
||||
@@ -1,52 +1,41 @@
|
||||
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';
|
||||
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}>{count}</LogsSearchLink>
|
||||
) : (
|
||||
count
|
||||
);
|
||||
const Row = ({
|
||||
label, count, response_status, tooltipTitle, translationComponents,
|
||||
}) => {
|
||||
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>
|
||||
@@ -54,12 +43,7 @@ const Row = ({ label, count, response_status, tooltipTitle, translationComponent
|
||||
);
|
||||
};
|
||||
|
||||
interface CountersProps {
|
||||
refreshButton: React.ReactNode;
|
||||
subtitle: string;
|
||||
}
|
||||
|
||||
const Counters = ({ refreshButton, subtitle }: CountersProps) => {
|
||||
const Counters = ({ refreshButton, subtitle }) => {
|
||||
const {
|
||||
interval,
|
||||
numDnsQueries,
|
||||
@@ -69,67 +53,77 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
|
||||
numReplacedSafesearch,
|
||||
avgProcessingTime,
|
||||
timeUnits,
|
||||
} = useSelector<RootState, RootState['stats']>((state) => state.stats, shallowEqual);
|
||||
} = useSelector((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: RowProps[] = [
|
||||
const rows = [
|
||||
{
|
||||
label: 'dns_query',
|
||||
count: formatNumber(numDnsQueries),
|
||||
count: numDnsQueries,
|
||||
tooltipTitle: dnsQueryTooltip,
|
||||
response_status: RESPONSE_FILTER.ALL.QUERY,
|
||||
},
|
||||
{
|
||||
label: 'blocked_by',
|
||||
count: formatNumber(numBlockedFiltering),
|
||||
count: numBlockedFiltering,
|
||||
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: formatNumber(numReplacedSafebrowsing),
|
||||
count: numReplacedSafebrowsing,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
|
||||
},
|
||||
{
|
||||
label: 'stats_adult',
|
||||
count: formatNumber(numReplacedParental),
|
||||
count: numReplacedParental,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
|
||||
},
|
||||
{
|
||||
label: 'enforced_save_search',
|
||||
count: formatNumber(numReplacedSafesearch),
|
||||
count: numReplacedSafesearch,
|
||||
tooltipTitle: 'number_of_dns_query_to_safe_search',
|
||||
response_status: RESPONSE_FILTER.SAFE_SEARCH.QUERY,
|
||||
},
|
||||
{
|
||||
label: 'average_processing_time',
|
||||
count: avgProcessingTime ? `${round(avgProcessingTime)} ms` : '0',
|
||||
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, index) => {
|
||||
return <Row {...row} key={index} />;
|
||||
})}
|
||||
{rows.map(Row)}
|
||||
</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;
|
||||
77
client/src/components/Dashboard/DomainCell.js
Normal file
77
client/src/components/Dashboard/DomainCell.js
Normal file
@@ -0,0 +1,77 @@
|
||||
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;
|
||||
@@ -1,81 +0,0 @@
|
||||
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;
|
||||
@@ -1,7 +1,6 @@
|
||||
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';
|
||||
@@ -9,10 +8,9 @@ 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: any) => {
|
||||
const getQueriedPercentColor = (percent) => {
|
||||
if (percent > 10) {
|
||||
return STATUS_COLORS.red;
|
||||
}
|
||||
@@ -22,27 +20,26 @@ const getQueriedPercentColor = (percent: any) => {
|
||||
return STATUS_COLORS.green;
|
||||
};
|
||||
|
||||
const countCell = (dnsQueries: any) =>
|
||||
function cell(row: any) {
|
||||
const { value } = row;
|
||||
const percent = getPercent(dnsQueries, value);
|
||||
const percentColor = getQueriedPercentColor(percent);
|
||||
const countCell = (dnsQueries) => function cell(row) {
|
||||
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} />;
|
||||
};
|
||||
|
||||
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}>
|
||||
const QueriedDomains = ({
|
||||
t, refreshButton, topQueriedDomains, subtitle, dnsQueries,
|
||||
}) => (
|
||||
<Card
|
||||
title={t('stats_query_domain')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topQueriedDomains.map(({ name: domain, count }: any) => ({
|
||||
data={topQueriedDomains.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
@@ -68,4 +65,12 @@ const QueriedDomains = ({ t, refreshButton, topQueriedDomains, subtitle, dnsQuer
|
||||
</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);
|
||||
@@ -1,27 +1,15 @@
|
||||
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: 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 getNormalizedHistory = (data, interval, id) => [
|
||||
{ data: normalizeHistory(data, interval), id },
|
||||
];
|
||||
|
||||
const Statistics = ({
|
||||
interval,
|
||||
@@ -33,68 +21,61 @@ 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);
|
||||
@@ -1,34 +1,38 @@
|
||||
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';
|
||||
|
||||
interface StatsCardProps {
|
||||
total: number;
|
||||
lineData: unknown[];
|
||||
title: object;
|
||||
color: string;
|
||||
percent?: number;
|
||||
}
|
||||
|
||||
const StatsCard = ({ total, lineData, percent, title, color }: StatsCardProps) => (
|
||||
const StatsCard = ({
|
||||
total, lineData, percent, title, color,
|
||||
}) => (
|
||||
<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;
|
||||
@@ -1,48 +1,50 @@
|
||||
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';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
|
||||
interface TimeCellProps {
|
||||
value?: string | number;
|
||||
}
|
||||
|
||||
const TimeCell = ({ value }: TimeCellProps) => {
|
||||
const TimeCell = ({ value }) => {
|
||||
if (!value) {
|
||||
return '–';
|
||||
}
|
||||
|
||||
const valueInMilliseconds = formatNumber(round(Number(value) * 1000));
|
||||
const valueInMilliseconds = round(value * 1000);
|
||||
|
||||
return (
|
||||
<div className="logs__row o-hidden">
|
||||
<span className="logs__text logs__text--full" title={valueInMilliseconds.toString()}>
|
||||
<span className="logs__text logs__text--full" title={valueInMilliseconds}>
|
||||
{valueInMilliseconds} ms
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface UpstreamAvgTimeProps {
|
||||
topUpstreamsAvgTime: { name: string; count: number }[];
|
||||
refreshButton: React.ReactNode;
|
||||
subtitle: string;
|
||||
t: TFunction;
|
||||
}
|
||||
TimeCell.propTypes = {
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
};
|
||||
|
||||
const UpstreamAvgTime = ({ t, refreshButton, topUpstreamsAvgTime, subtitle }: UpstreamAvgTimeProps) => (
|
||||
<Card title={t('average_upstream_response_time')} subtitle={subtitle} bodyType="card-table" refresh={refreshButton}>
|
||||
const UpstreamAvgTime = ({
|
||||
t,
|
||||
refreshButton,
|
||||
topUpstreamsAvgTime,
|
||||
subtitle,
|
||||
}) => (
|
||||
<Card
|
||||
title={t('average_upstream_response_time')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topUpstreamsAvgTime.map(({ name: domain, count }: { name: string; count: number }) => ({
|
||||
data={topUpstreamsAvgTime.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
@@ -68,4 +70,11 @@ const UpstreamAvgTime = ({ t, refreshButton, topUpstreamsAvgTime, subtitle }: Up
|
||||
</Card>
|
||||
);
|
||||
|
||||
UpstreamAvgTime.propTypes = {
|
||||
topUpstreamsAvgTime: PropTypes.array.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(UpstreamAvgTime);
|
||||
@@ -1,47 +1,51 @@
|
||||
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: any) =>
|
||||
function cell(row: any) {
|
||||
const CountCell = (totalBlocked) => (
|
||||
function cell(row) {
|
||||
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: any) => {
|
||||
const getTotalUpstreamRequests = (stats) => {
|
||||
let total = 0;
|
||||
stats.forEach(({ count }: any) => {
|
||||
total += count;
|
||||
});
|
||||
stats.forEach(({ count }) => { total += count; });
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
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}>
|
||||
const UpstreamResponses = ({
|
||||
t,
|
||||
refreshButton,
|
||||
topUpstreamsResponses,
|
||||
subtitle,
|
||||
}) => (
|
||||
<Card
|
||||
title={t('top_upstreams')}
|
||||
subtitle={subtitle}
|
||||
bodyType="card-table"
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<ReactTable
|
||||
data={topUpstreamsResponses.map(({ name: domain, count }: { name: string; count: number }) => ({
|
||||
data={topUpstreamsResponses.map(({ name: domain, count }) => ({
|
||||
domain,
|
||||
count,
|
||||
}))}
|
||||
@@ -67,4 +71,11 @@ const UpstreamResponses = ({ t, refreshButton, topUpstreamsResponses, subtitle }
|
||||
</Card>
|
||||
);
|
||||
|
||||
UpstreamResponses.propTypes = {
|
||||
topUpstreamsResponses: PropTypes.array.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(UpstreamResponses);
|
||||
276
client/src/components/Dashboard/index.js
Normal file
276
client/src/components/Dashboard/index.js
Normal file
@@ -0,0 +1,276 @@
|
||||
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;
|
||||
@@ -1,260 +0,0 @@
|
||||
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', { time: 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;
|
||||
32
client/src/components/Filters/Actions.js
Normal file
32
client/src/components/Filters/Actions.js
Normal file
@@ -0,0 +1,32 @@
|
||||
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);
|
||||
@@ -1,27 +0,0 @@
|
||||
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);
|
||||
@@ -15,12 +15,10 @@ import {
|
||||
getRulesToFilterList,
|
||||
} from '../../../helpers/helpers';
|
||||
import { BLOCK_ACTIONS, FILTERED, FILTERED_STATUS } from '../../../helpers/constants';
|
||||
|
||||
import { toggleBlocking } from '../../../actions';
|
||||
import { RootState } from '../../../initialState';
|
||||
|
||||
const renderBlockingButton = (isFiltered: any, domain: any) => {
|
||||
const processingRules = useSelector((state: RootState) => state.filtering.processingRules);
|
||||
const renderBlockingButton = (isFiltered, domain) => {
|
||||
const processingRules = useSelector((state) => state.filtering.processingRules);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -30,32 +28,28 @@ const renderBlockingButton = (isFiltered: any, domain: any) => {
|
||||
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: RootState) => state.filtering.filters, shallowEqual);
|
||||
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 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 getReasonFiltered = (reason) => {
|
||||
const filterKey = reason.replace(FILTERED, '');
|
||||
return i18next.t('query_log_filtered', { filter: filterKey });
|
||||
};
|
||||
@@ -77,23 +71,24 @@ const getTitle = () => {
|
||||
return REASON_TO_TITLE_MAP[reason];
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{t('check_reason', { reason })}</div>
|
||||
|
||||
<div>
|
||||
{t('rule_label')}:
|
||||
{ruleAndFilterNames}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
return <>
|
||||
<div>{t('check_reason', { reason })}</div>
|
||||
<div>
|
||||
{t('rule_label')}:
|
||||
|
||||
{ruleAndFilterNames}
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
|
||||
const Info = () => {
|
||||
const { hostname, reason, service_name, cname, ip_addrs } = useSelector(
|
||||
(state: RootState) => state.filtering.check,
|
||||
shallowEqual,
|
||||
);
|
||||
const {
|
||||
hostname,
|
||||
reason,
|
||||
service_name,
|
||||
cname,
|
||||
ip_addrs,
|
||||
} = useSelector((state) => state.filtering.check, shallowEqual);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const title = getTitle();
|
||||
@@ -104,29 +99,23 @@ 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;
|
||||
66
client/src/components/Filters/Check/index.js
Normal file
66
client/src/components/Filters/Check/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
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);
|
||||
@@ -1,70 +0,0 @@
|
||||
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);
|
||||
@@ -1,46 +1,32 @@
|
||||
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';
|
||||
|
||||
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> {
|
||||
class CustomRules extends Component {
|
||||
ref = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getFilteringStatus();
|
||||
}
|
||||
|
||||
handleChange = (e: any) => {
|
||||
handleChange = (e) => {
|
||||
const { value } = e.currentTarget;
|
||||
this.handleRulesChange(value);
|
||||
};
|
||||
|
||||
handleSubmit = (e: any) => {
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
this.handleRulesSubmit();
|
||||
};
|
||||
|
||||
handleRulesChange = (value: any) => {
|
||||
handleRulesChange = (value) => {
|
||||
this.props.handleRulesChange({ userRules: value });
|
||||
};
|
||||
|
||||
@@ -48,22 +34,23 @@ class CustomRules extends Component<CustomRulesProps> {
|
||||
this.props.setRules(this.props.filtering.userRules);
|
||||
};
|
||||
|
||||
handleCheck = (values: any) => {
|
||||
handleCheck = (values) => {
|
||||
this.props.checkHost(values);
|
||||
};
|
||||
|
||||
onScroll = (e: any) => syncScroll(e, this.ref);
|
||||
onScroll = (e) => 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">
|
||||
@@ -73,31 +60,39 @@ class CustomRules extends Component<CustomRulesProps> {
|
||||
onChange={this.handleChange}
|
||||
onScroll={this.onScroll}
|
||||
/>
|
||||
{getTextareaCommentsHighlight(this.ref, userRules, [
|
||||
COMMENT_LINE_DEFAULT_TOKEN,
|
||||
'!',
|
||||
])}
|
||||
{getTextareaCommentsHighlight(
|
||||
this.ref,
|
||||
userRules,
|
||||
undefined,
|
||||
[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);
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import PageTitle from '../ui/PageTitle';
|
||||
@@ -8,41 +9,15 @@ import Actions from './Actions';
|
||||
import Table from './Table';
|
||||
|
||||
import { MODAL_TYPE } from '../../helpers/constants';
|
||||
|
||||
import { getCurrentFilter } from '../../helpers/helpers';
|
||||
|
||||
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> {
|
||||
class DnsAllowlist extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getFilteringStatus();
|
||||
}
|
||||
|
||||
handleSubmit = (values: any) => {
|
||||
handleSubmit = (values) => {
|
||||
const { name, url } = values;
|
||||
|
||||
const { filtering } = this.props;
|
||||
const whitelist = true;
|
||||
|
||||
@@ -53,17 +28,15 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
}
|
||||
};
|
||||
|
||||
handleDelete = (url: any) => {
|
||||
handleDelete = (url) => {
|
||||
if (window.confirm(this.props.t('list_confirm_delete'))) {
|
||||
const whitelist = true;
|
||||
|
||||
this.props.removeFilter(url, whitelist);
|
||||
}
|
||||
};
|
||||
|
||||
toggleFilter = (url: any, data: any) => {
|
||||
toggleFilter = (url, data) => {
|
||||
const whitelist = true;
|
||||
|
||||
this.props.toggleFilterStatus(url, data, whitelist);
|
||||
};
|
||||
|
||||
@@ -80,6 +53,7 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
t,
|
||||
toggleFilteringModal,
|
||||
addFilter,
|
||||
toggleFilterStatus,
|
||||
filtering: {
|
||||
whitelistFilters,
|
||||
isModalOpen,
|
||||
@@ -94,18 +68,19 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
},
|
||||
} = 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">
|
||||
@@ -115,11 +90,11 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
loading={loading}
|
||||
processingConfigFilter={processingConfigFilter}
|
||||
toggleFilteringModal={toggleFilteringModal}
|
||||
toggleFilterStatus={toggleFilterStatus}
|
||||
handleDelete={this.handleDelete}
|
||||
toggleFilter={this.toggleFilter}
|
||||
whitelist={whitelist}
|
||||
/>
|
||||
|
||||
<Actions
|
||||
handleAdd={this.openAddFiltersModal}
|
||||
handleRefresh={this.handleRefresh}
|
||||
@@ -130,7 +105,6 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
filters={whitelistFilters}
|
||||
isOpen={isModalOpen}
|
||||
@@ -149,4 +123,17 @@ class DnsAllowlist extends Component<DnsAllowlistProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1,38 +1,27 @@
|
||||
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';
|
||||
|
||||
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> {
|
||||
class DnsBlocklist extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getFilteringStatus();
|
||||
}
|
||||
|
||||
handleSubmit = (values: any) => {
|
||||
handleSubmit = (values) => {
|
||||
const { modalFilterUrl, modalType } = this.props.filtering;
|
||||
|
||||
switch (modalType) {
|
||||
@@ -41,25 +30,23 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
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: any, [key, value]) => {
|
||||
const changedValues = Object.entries(values)?.reduce((acc, [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:
|
||||
@@ -67,13 +54,13 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
}
|
||||
};
|
||||
|
||||
handleDelete = (url: any) => {
|
||||
handleDelete = (url) => {
|
||||
if (window.confirm(this.props.t('list_confirm_delete'))) {
|
||||
this.props.removeFilter(url);
|
||||
}
|
||||
};
|
||||
|
||||
toggleFilter = (url: any, data: any) => {
|
||||
toggleFilter = (url, data) => {
|
||||
this.props.toggleFilterStatus(url, data);
|
||||
};
|
||||
|
||||
@@ -88,11 +75,8 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
|
||||
toggleFilteringModal,
|
||||
|
||||
addFilter,
|
||||
|
||||
filtering: {
|
||||
filters,
|
||||
isModalOpen,
|
||||
@@ -107,17 +91,18 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
},
|
||||
} = 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">
|
||||
@@ -130,7 +115,6 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
handleDelete={this.handleDelete}
|
||||
toggleFilter={this.toggleFilter}
|
||||
/>
|
||||
|
||||
<Actions
|
||||
handleAdd={this.openSelectTypeModal}
|
||||
handleRefresh={this.handleRefresh}
|
||||
@@ -140,7 +124,6 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
filtersCatalog={filtersCatalog}
|
||||
filters={filters}
|
||||
@@ -159,4 +142,17 @@ class DnsBlocklist extends Component<DnsBlocklistProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -7,37 +7,31 @@ 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={[
|
||||
@@ -45,10 +39,12 @@ 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>
|
||||
191
client/src/components/Filters/Form.js
Normal file
191
client/src/components/Filters/Form.js
Normal file
@@ -0,0 +1,191 @@
|
||||
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);
|
||||
@@ -1,208 +0,0 @@
|
||||
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);
|
||||
@@ -1,13 +1,11 @@
|
||||
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');
|
||||
@@ -27,7 +25,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: any, whitelist: any) => {
|
||||
const getTitle = (modalType, whitelist) => {
|
||||
const titleType = MODAL_TYPE_TO_TITLE_TYPE_MAP[modalType];
|
||||
if (!titleType) {
|
||||
return null;
|
||||
@@ -35,39 +33,19 @@ const getTitle = (modalType: any, whitelist: any) => {
|
||||
return `${titleType}_${whitelist ? 'allowlist' : 'blocklist'}`;
|
||||
};
|
||||
|
||||
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: {},
|
||||
},
|
||||
);
|
||||
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: {},
|
||||
});
|
||||
|
||||
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> {
|
||||
class Modal extends Component {
|
||||
closeModal = () => {
|
||||
this.props.toggleFilteringModal();
|
||||
};
|
||||
@@ -75,25 +53,15 @@ class Modal extends Component<ModalProps> {
|
||||
render() {
|
||||
const {
|
||||
isOpen,
|
||||
|
||||
processingAddFilter,
|
||||
|
||||
processingConfigFilter,
|
||||
|
||||
handleSubmit,
|
||||
|
||||
modalType,
|
||||
|
||||
currentFilterData,
|
||||
|
||||
whitelist,
|
||||
|
||||
toggleFilteringModal,
|
||||
|
||||
filters,
|
||||
|
||||
t,
|
||||
|
||||
filtersCatalog,
|
||||
} = this.props;
|
||||
|
||||
@@ -122,16 +90,15 @@ class Modal extends Component<ModalProps> {
|
||||
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}
|
||||
@@ -149,4 +116,20 @@ class Modal extends Component<ModalProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1,26 +1,22 @@
|
||||
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';
|
||||
|
||||
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;
|
||||
const Form = (props) => {
|
||||
const {
|
||||
t,
|
||||
handleSubmit,
|
||||
reset,
|
||||
pristine,
|
||||
submitting,
|
||||
toggleRewritesModal,
|
||||
processingAdd,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
@@ -39,19 +35,22 @@ const Form = (props: FormProps) => {
|
||||
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> –
|
||||
<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"
|
||||
@@ -64,15 +63,14 @@ const Form = (props: FormProps) => {
|
||||
/>
|
||||
</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
|
||||
@@ -82,14 +80,15 @@ const Form = (props: FormProps) => {
|
||||
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>
|
||||
@@ -98,6 +97,17 @@ const Form = (props: FormProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
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({
|
||||
@@ -1,23 +1,12 @@
|
||||
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';
|
||||
|
||||
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 Modal = (props) => {
|
||||
const {
|
||||
isModalOpen,
|
||||
handleSubmit,
|
||||
@@ -33,7 +22,8 @@ const Modal = (props: ModalProps) => {
|
||||
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">
|
||||
@@ -43,12 +33,10 @@ const Modal = (props: ModalProps) => {
|
||||
<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}
|
||||
@@ -61,4 +49,14 @@ const Modal = (props: ModalProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
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);
|
||||
@@ -1,26 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
// @ts-expect-error FIXME: update react-table
|
||||
import PropTypes from 'prop-types';
|
||||
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';
|
||||
|
||||
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) => (
|
||||
class Table extends Component {
|
||||
cellWrap = ({ value }) => (
|
||||
<div className="logs__row o-hidden">
|
||||
<span className="logs__text" title={value}>
|
||||
{value}
|
||||
@@ -46,7 +33,7 @@ class Table extends Component<TableProps> {
|
||||
maxWidth: 100,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
Cell: (value: any) => {
|
||||
Cell: (value) => {
|
||||
const currentRewrite = {
|
||||
answer: value.row.answer,
|
||||
domain: value.row.domain,
|
||||
@@ -64,7 +51,8 @@ class Table extends Component<TableProps> {
|
||||
});
|
||||
}}
|
||||
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>
|
||||
@@ -74,7 +62,8 @@ class Table extends Component<TableProps> {
|
||||
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>
|
||||
@@ -86,7 +75,9 @@ class Table extends Component<TableProps> {
|
||||
];
|
||||
|
||||
render() {
|
||||
const { t, list, processing, processingAdd, processingDelete } = this.props;
|
||||
const {
|
||||
t, list, processing, processingAdd, processingDelete,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<ReactTable
|
||||
@@ -96,9 +87,7 @@ class Table extends Component<TableProps> {
|
||||
className="-striped -highlight card-table-overflow"
|
||||
showPagination
|
||||
defaultPageSize={LocalStorageHelper.getItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE) || 10}
|
||||
onPageSizeChange={(size: any) =>
|
||||
LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE, size)
|
||||
}
|
||||
onPageSizeChange={(size) => LocalStorageHelper.setItem(LOCAL_STORAGE_KEYS.REWRITES_PAGE_SIZE, size)}
|
||||
minRows={TABLES_MIN_ROWS}
|
||||
ofText="/"
|
||||
previousText={t('previous_btn')}
|
||||
@@ -112,4 +101,15 @@ class Table extends Component<TableProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1,39 +1,26 @@
|
||||
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';
|
||||
|
||||
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> {
|
||||
class Rewrites extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getRewritesList();
|
||||
}
|
||||
|
||||
handleDelete = (values: any) => {
|
||||
handleDelete = (values) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm(this.props.t('rewrite_confirm_delete', { key: values.domain }))) {
|
||||
this.props.deleteRewrite(values);
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = (values: any) => {
|
||||
handleSubmit = (values) => {
|
||||
const { modalType, currentRewrite } = this.props.rewrites;
|
||||
|
||||
if (modalType === MODAL_TYPE.EDIT_REWRITE && currentRewrite) {
|
||||
@@ -49,9 +36,7 @@ class Rewrites extends Component<RewritesProps> {
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
|
||||
rewrites,
|
||||
|
||||
toggleRewritesModal,
|
||||
} = this.props;
|
||||
|
||||
@@ -68,9 +53,14 @@ class Rewrites extends Component<RewritesProps> {
|
||||
|
||||
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}
|
||||
@@ -86,7 +76,8 @@ class Rewrites extends Component<RewritesProps> {
|
||||
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>
|
||||
|
||||
@@ -97,6 +88,7 @@ class Rewrites extends Component<RewritesProps> {
|
||||
handleSubmit={this.handleSubmit}
|
||||
processingAdd={processingAdd}
|
||||
processingDelete={processingDelete}
|
||||
processingUpdate={processingUpdate}
|
||||
currentRewrite={currentRewrite}
|
||||
/>
|
||||
</Fragment>
|
||||
@@ -106,4 +98,14 @@ class Rewrites extends Component<RewritesProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1,27 +1,23 @@
|
||||
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';
|
||||
|
||||
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;
|
||||
const Form = (props) => {
|
||||
const {
|
||||
blockedServices,
|
||||
handleSubmit,
|
||||
change,
|
||||
pristine,
|
||||
submitting,
|
||||
processing,
|
||||
processingSet,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
@@ -32,24 +28,24 @@ const Form = (props: FormProps) => {
|
||||
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: any) => (
|
||||
{blockedServices.map((service) => (
|
||||
<Field
|
||||
key={service.id}
|
||||
icon={service.icon_svg}
|
||||
@@ -67,7 +63,8 @@ const Form = (props: FormProps) => {
|
||||
<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>
|
||||
@@ -75,6 +72,17 @@ const Form = (props: FormProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
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({
|
||||
@@ -1,12 +1,10 @@
|
||||
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';
|
||||
@@ -16,26 +14,21 @@ export const DAYS_OF_WEEK = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
|
||||
const INITIAL_START_TIME_MS = 0;
|
||||
const INITIAL_END_TIME_MS = 86340000;
|
||||
|
||||
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) => {
|
||||
export const Modal = ({
|
||||
isOpen,
|
||||
currentDay,
|
||||
schedule,
|
||||
onClose,
|
||||
onSubmit,
|
||||
}) => {
|
||||
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<Set<string>>(new Set());
|
||||
const [days, setDays] = useState(new Set());
|
||||
|
||||
const [startTime, setStartTime] = useState(INITIAL_START_TIME_MS);
|
||||
const [endTime, setEndTime] = useState(INITIAL_END_TIME_MS);
|
||||
@@ -60,7 +53,7 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: Modal
|
||||
}
|
||||
}, [startTime, endTime]);
|
||||
|
||||
const addDays = (day: any) => {
|
||||
const addDays = (day) => {
|
||||
const newDays = new Set(days);
|
||||
|
||||
if (newDays.has(day)) {
|
||||
@@ -72,11 +65,11 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: Modal
|
||||
setDays(newDays);
|
||||
};
|
||||
|
||||
const activeDay = (day: any) => {
|
||||
const activeDay = (day) => {
|
||||
return days.has(day);
|
||||
};
|
||||
|
||||
const onFormSubmit = (e: any) => {
|
||||
const onFormSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const newSchedule = schedule;
|
||||
@@ -100,19 +93,23 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: 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) => (
|
||||
@@ -121,7 +118,8 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: Modal
|
||||
key={day}
|
||||
className="btn schedule__button-day"
|
||||
data-active={activeDay(day)}
|
||||
onClick={() => addDays(day)}>
|
||||
onClick={() => addDays(day)}
|
||||
>
|
||||
{getShortDayName(t, day)}
|
||||
</button>
|
||||
))}
|
||||
@@ -129,52 +127,69 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: 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>
|
||||
@@ -184,3 +199,11 @@ export const Modal = ({ isOpen, currentDay, schedule, onClose, onSubmit }: Modal
|
||||
</ReactModal>
|
||||
);
|
||||
};
|
||||
|
||||
Modal.propTypes = {
|
||||
schedule: PropTypes.object.isRequired,
|
||||
currentDay: PropTypes.string,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
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>
|
||||
–
|
||||
<time>{endTime.hours}:{endTime.minutes}</time>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TimePeriod.propTypes = {
|
||||
startTimeMs: PropTypes.number.isRequired,
|
||||
endTimeMs: PropTypes.number.isRequired,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user