Compare commits
1 Commits
beta-v0.10
...
infra-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de86f57902 |
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@@ -1,8 +1,8 @@
|
|||||||
'name': 'build'
|
'name': 'build'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.24.2'
|
'GO_VERSION': '1.23.1'
|
||||||
'NODE_VERSION': '20'
|
'NODE_VERSION': '16'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
'with':
|
'with':
|
||||||
'node-version': '${{ env.NODE_VERSION }}'
|
'node-version': '${{ env.NODE_VERSION }}'
|
||||||
- 'name': 'Set up Go modules cache'
|
- 'name': 'Set up Go modules cache'
|
||||||
'uses': 'actions/cache@v4'
|
'uses': 'actions/cache@v2'
|
||||||
'with':
|
'with':
|
||||||
'path': '~/go/pkg/mod'
|
'path': '~/go/pkg/mod'
|
||||||
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
|
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
'id': 'npm-cache'
|
'id': 'npm-cache'
|
||||||
'run': 'echo "::set-output name=dir::$( npm config get cache )"'
|
'run': 'echo "::set-output name=dir::$( npm config get cache )"'
|
||||||
- 'name': 'Set up npm cache'
|
- 'name': 'Set up npm cache'
|
||||||
'uses': 'actions/cache@v4'
|
'uses': 'actions/cache@v2'
|
||||||
'with':
|
'with':
|
||||||
'path': '${{ steps.npm-cache.outputs.dir }}'
|
'path': '${{ steps.npm-cache.outputs.dir }}'
|
||||||
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
'with':
|
'with':
|
||||||
'node-version': '${{ env.NODE_VERSION }}'
|
'node-version': '${{ env.NODE_VERSION }}'
|
||||||
- 'name': 'Set up Go modules cache'
|
- 'name': 'Set up Go modules cache'
|
||||||
'uses': 'actions/cache@v4'
|
'uses': 'actions/cache@v2'
|
||||||
'with':
|
'with':
|
||||||
'path': '~/go/pkg/mod'
|
'path': '~/go/pkg/mod'
|
||||||
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
|
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
|
||||||
@@ -89,13 +89,13 @@
|
|||||||
'id': 'npm-cache'
|
'id': 'npm-cache'
|
||||||
'run': 'echo "::set-output name=dir::$(npm config get cache)"'
|
'run': 'echo "::set-output name=dir::$(npm config get cache)"'
|
||||||
- 'name': 'Set up npm cache'
|
- 'name': 'Set up npm cache'
|
||||||
'uses': 'actions/cache@v4'
|
'uses': 'actions/cache@v2'
|
||||||
'with':
|
'with':
|
||||||
'path': '${{ steps.npm-cache.outputs.dir }}'
|
'path': '${{ steps.npm-cache.outputs.dir }}'
|
||||||
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
||||||
'restore-keys': '${{ runner.os }}-node-'
|
'restore-keys': '${{ runner.os }}-node-'
|
||||||
- 'name': 'Set up Snapcraft'
|
- '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'
|
- 'name': 'Set up QEMU'
|
||||||
'uses': 'docker/setup-qemu-action@v1'
|
'uses': 'docker/setup-qemu-action@v1'
|
||||||
- 'name': 'Set up Docker Buildx'
|
- 'name': 'Set up Docker Buildx'
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
'name': 'lint'
|
'name': 'lint'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.24.2'
|
'GO_VERSION': '1.23.1'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
|
|||||||
11
.gitignore
vendored
11
.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
|
# 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
|
# added, the harder it gets to maintain and manage projects' gitignores. Put
|
||||||
# them into your global gitignore file instead.
|
# them into your global gitignore file instead.
|
||||||
@@ -13,16 +8,11 @@
|
|||||||
# bottom to make sure they take effect.
|
# bottom to make sure they take effect.
|
||||||
*.db
|
*.db
|
||||||
*.log
|
*.log
|
||||||
*.out
|
|
||||||
*.snap
|
*.snap
|
||||||
*.test
|
*.test
|
||||||
/agh-backup/
|
/agh-backup/
|
||||||
/bin/
|
/bin/
|
||||||
/build/*
|
/build/*
|
||||||
/client/blob-report/
|
|
||||||
/client/playwright-report/
|
|
||||||
/client/playwright/.cache/
|
|
||||||
/client/test-results/
|
|
||||||
/data/
|
/data/
|
||||||
/dist/
|
/dist/
|
||||||
/filtering/tests/filtering.TestLotsOfRules*.pprof
|
/filtering/tests/filtering.TestLotsOfRules*.pprof
|
||||||
@@ -31,7 +21,6 @@
|
|||||||
/launchpad_credentials
|
/launchpad_credentials
|
||||||
/querylog.json*
|
/querylog.json*
|
||||||
/snapcraft_login
|
/snapcraft_login
|
||||||
/test-reports/
|
|
||||||
AdGuardHome
|
AdGuardHome
|
||||||
AdGuardHome.exe
|
AdGuardHome.exe
|
||||||
AdGuardHome.yaml*
|
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
|
|
||||||
}
|
|
||||||
2534
CHANGELOG.md
2534
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
50
Makefile
50
Makefile
@@ -1,14 +1,14 @@
|
|||||||
# Keep the Makefile POSIX-compliant. We currently allow hyphens in
|
# Keep the Makefile POSIX-compliant. We currently allow hyphens in
|
||||||
# target names, but that may change in the future.
|
# 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:
|
.POSIX:
|
||||||
|
|
||||||
# This comment is used to simplify checking local copies of the
|
# This comment is used to simplify checking local copies of the
|
||||||
# Makefile. Bump this number every time a significant change is made to
|
# Makefile. Bump this number every time a significant change is made to
|
||||||
# this Makefile.
|
# this Makefile.
|
||||||
#
|
#
|
||||||
# AdGuard-Project-Version: 9
|
# AdGuard-Project-Version: 6
|
||||||
|
|
||||||
# Don't name these macros "GO" etc., because GNU Make apparently makes
|
# Don't name these macros "GO" etc., because GNU Make apparently makes
|
||||||
# them exported environment variables with the literal value of
|
# them exported environment variables with the literal value of
|
||||||
@@ -22,22 +22,25 @@ VERBOSE.MACRO = $${VERBOSE:-0}
|
|||||||
|
|
||||||
CHANNEL = development
|
CHANNEL = development
|
||||||
CLIENT_DIR = client
|
CLIENT_DIR = client
|
||||||
|
COMMIT = $$( git rev-parse --short HEAD )
|
||||||
DEPLOY_SCRIPT_PATH = not/a/real/path
|
DEPLOY_SCRIPT_PATH = not/a/real/path
|
||||||
DIST_DIR = dist
|
DIST_DIR = dist
|
||||||
GOAMD64 = v1
|
GOAMD64 = v1
|
||||||
GOPROXY = https://proxy.golang.org|direct
|
GOPROXY = https://proxy.golang.org|direct
|
||||||
|
GOSUMDB = sum.golang.google.cn
|
||||||
|
GOTOOLCHAIN = go1.23.1
|
||||||
GOTELEMETRY = off
|
GOTELEMETRY = off
|
||||||
GOTOOLCHAIN = go1.24.2
|
|
||||||
GPG_KEY = devteam@adguard.com
|
GPG_KEY = devteam@adguard.com
|
||||||
GPG_KEY_PASSPHRASE = not-a-real-password
|
GPG_KEY_PASSPHRASE = not-a-real-password
|
||||||
NPM = npm
|
NPM = npm
|
||||||
NPM_FLAGS = --prefix $(CLIENT_DIR)
|
NPM_FLAGS = --prefix $(CLIENT_DIR)
|
||||||
NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress
|
NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress --ignore-engines\
|
||||||
|
--ignore-optional --ignore-platform --ignore-scripts
|
||||||
RACE = 0
|
RACE = 0
|
||||||
REVISION = $${REVISION:-$$(git rev-parse --short HEAD)}
|
|
||||||
SIGN = 1
|
SIGN = 1
|
||||||
SIGNER_API_KEY = not-a-real-key
|
SIGNER_API_KEY = not-a-real-key
|
||||||
VERSION = v0.0.0
|
VERSION = v0.0.0
|
||||||
|
YARN = yarn
|
||||||
|
|
||||||
NEXTAPI = 0
|
NEXTAPI = 0
|
||||||
|
|
||||||
@@ -58,28 +61,28 @@ BUILD_RELEASE_DEPS_1 = go-deps
|
|||||||
|
|
||||||
ENV = env\
|
ENV = env\
|
||||||
CHANNEL='$(CHANNEL)'\
|
CHANNEL='$(CHANNEL)'\
|
||||||
|
COMMIT='$(COMMIT)'\
|
||||||
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
|
DEPLOY_SCRIPT_PATH='$(DEPLOY_SCRIPT_PATH)' \
|
||||||
DIST_DIR='$(DIST_DIR)'\
|
DIST_DIR='$(DIST_DIR)'\
|
||||||
GO="$(GO.MACRO)"\
|
GO="$(GO.MACRO)"\
|
||||||
GOAMD64='$(GOAMD64)'\
|
GOAMD64='$(GOAMD64)'\
|
||||||
GOPROXY='$(GOPROXY)'\
|
GOPROXY='$(GOPROXY)'\
|
||||||
|
GOSUMDB='$(GOSUMDB)'\
|
||||||
GOTELEMETRY='$(GOTELEMETRY)'\
|
GOTELEMETRY='$(GOTELEMETRY)'\
|
||||||
GOTOOLCHAIN='$(GOTOOLCHAIN)'\
|
GOTOOLCHAIN='$(GOTOOLCHAIN)'\
|
||||||
GPG_KEY='$(GPG_KEY)'\
|
GPG_KEY='$(GPG_KEY)'\
|
||||||
GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\
|
GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\
|
||||||
NEXTAPI='$(NEXTAPI)'\
|
|
||||||
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
|
||||||
RACE='$(RACE)'\
|
RACE='$(RACE)'\
|
||||||
REVISION='$(REVISION)'\
|
|
||||||
SIGN='$(SIGN)'\
|
SIGN='$(SIGN)'\
|
||||||
SIGNER_API_KEY='$(SIGNER_API_KEY)' \
|
SIGNER_API_KEY='$(SIGNER_API_KEY)' \
|
||||||
|
NEXTAPI='$(NEXTAPI)'\
|
||||||
VERBOSE="$(VERBOSE.MACRO)"\
|
VERBOSE="$(VERBOSE.MACRO)"\
|
||||||
VERSION="$(VERSION)"\
|
VERSION="$(VERSION)"\
|
||||||
|
|
||||||
# Keep the line above blank.
|
# Keep the line above blank.
|
||||||
|
|
||||||
ENV_MISC = env\
|
ENV_MISC = env\
|
||||||
PATH="$${PWD}/bin:$$("$(GO.MACRO)" env GOPATH)/bin:$${PATH}"\
|
|
||||||
VERBOSE="$(VERBOSE.MACRO)"\
|
VERBOSE="$(VERBOSE.MACRO)"\
|
||||||
|
|
||||||
# Keep the line above blank.
|
# Keep the line above blank.
|
||||||
@@ -88,8 +91,6 @@ ENV_MISC = env\
|
|||||||
# full build.
|
# full build.
|
||||||
build: deps quick-build
|
build: deps quick-build
|
||||||
|
|
||||||
init: ; git config core.hooksPath ./scripts/hooks
|
|
||||||
|
|
||||||
quick-build: js-build go-build
|
quick-build: js-build go-build
|
||||||
|
|
||||||
deps: js-deps go-deps
|
deps: js-deps go-deps
|
||||||
@@ -103,12 +104,13 @@ build-docker: ; $(ENV) "$(SHELL)" ./scripts/make/build-docker.sh
|
|||||||
build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
|
build-release: $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT))
|
||||||
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
|
$(ENV) "$(SHELL)" ./scripts/make/build-release.sh
|
||||||
|
|
||||||
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
|
clean: ; $(ENV) "$(SHELL)" ./scripts/make/clean.sh
|
||||||
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
|
init: ; git config core.hooksPath ./scripts/hooks
|
||||||
js-typecheck: ; $(NPM) $(NPM_FLAGS) run typecheck
|
|
||||||
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
|
js-build: ; $(NPM) $(NPM_FLAGS) run build-prod
|
||||||
js-test: ; $(NPM) $(NPM_FLAGS) run test
|
js-deps: ; $(NPM) $(NPM_INSTALL_FLAGS) ci
|
||||||
js-test-e2e: ; $(NPM) $(NPM_FLAGS) run test:e2e
|
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
|
||||||
|
js-test: ; $(NPM) $(NPM_FLAGS) run test
|
||||||
|
|
||||||
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
go-bench: ; $(ENV) "$(SHELL)" ./scripts/make/go-bench.sh
|
||||||
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
|
||||||
@@ -127,15 +129,17 @@ go-check: go-tools go-lint go-test
|
|||||||
# A quick check to make sure that all operating systems relevant to the
|
# A quick check to make sure that all operating systems relevant to the
|
||||||
# development of the project can be typechecked and built successfully.
|
# development of the project can be typechecked and built successfully.
|
||||||
go-os-check:
|
go-os-check:
|
||||||
$(ENV) GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
|
env GOOS='darwin' "$(GO.MACRO)" vet ./internal/...
|
||||||
$(ENV) GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
|
env GOOS='freebsd' "$(GO.MACRO)" vet ./internal/...
|
||||||
$(ENV) GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
|
env GOOS='openbsd' "$(GO.MACRO)" vet ./internal/...
|
||||||
$(ENV) GOOS='linux' "$(GO.MACRO)" vet ./internal/...
|
env GOOS='linux' "$(GO.MACRO)" vet ./internal/...
|
||||||
$(ENV) GOOS='windows' "$(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
|
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
|
||||||
|
|
||||||
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
|
md-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/md-lint.sh
|
||||||
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
|
sh-lint: ; $(ENV_MISC) "$(SHELL)" ./scripts/make/sh-lint.sh
|
||||||
|
|
||||||
# TODO(a.garipov): Re-add openapi-lint.
|
|
||||||
|
|||||||
24
README.md
24
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
|
[Docker Hub]: https://hub.docker.com/r/adguard/adguardhome
|
||||||
[Snap Store]: https://snapcraft.io/adguard-home
|
[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>
|
### <a href="#guides" id="guides" name="guides">Guides</a>
|
||||||
|
|
||||||
@@ -205,9 +205,9 @@ Run `make init` to prepare the development environment.
|
|||||||
|
|
||||||
You will need this to build AdGuard Home:
|
You will need this to build AdGuard Home:
|
||||||
|
|
||||||
- [Go](https://golang.org/dl/) v1.24 or later;
|
- [Go](https://golang.org/dl/) v1.23 or later;
|
||||||
- [Node.js](https://nodejs.org/en/download/) v20.19 or later;
|
- [Node.js](https://nodejs.org/en/download/) v18.18 or later;
|
||||||
- [npm](https://www.npmjs.com/) v10.8 or later;
|
- [npm](https://www.npmjs.com/) v8 or later;
|
||||||
|
|
||||||
### <a href="#building" id="building" name="building">Building</a>
|
### <a href="#building" id="building" name="building">Building</a>
|
||||||
|
|
||||||
@@ -290,22 +290,6 @@ When you need to debug the frontend without recompiling the production version e
|
|||||||
[targ-docker]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-dockersh-build-a-multi-architecture-docker-image
|
[targ-docker]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-dockersh-build-a-multi-architecture-docker-image
|
||||||
[targ-release]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-releasesh-build-a-release-for-all-platforms
|
[targ-release]: https://github.com/AdguardTeam/AdGuardHome/tree/master/scripts#build-releasesh-build-a-release-for-all-platforms
|
||||||
|
|
||||||
#### <a href="#e2e-frontend-tests" id="e2e-frontend-tests" name="e2e-frontend-tests">End-to-End (E2E) Frontend Tests</a>
|
|
||||||
|
|
||||||
AdGuard Home uses [Playwright](https://playwright.dev) for E2E testing. Tests are located in `tests/e2e`.
|
|
||||||
|
|
||||||
**Running Tests:**
|
|
||||||
- `npm run test:e2e` – run all tests (headless).
|
|
||||||
- `npm run test:e2e:interactive` – run tests interactively.
|
|
||||||
- `npm run test:e2e:debug` – run tests in debug mode.
|
|
||||||
- `npm run test:e2e:codegen` – generate new test code.
|
|
||||||
|
|
||||||
**Setup:**
|
|
||||||
1. Run `npm install` to install dependencies.
|
|
||||||
2. Run `npx playwright install` to set up required browsers.
|
|
||||||
|
|
||||||
> **Warning:** Playwright will download and install its own browser binaries for testing, which may differ from the browsers installed on your system.
|
|
||||||
|
|
||||||
## <a href="#contributing" id="contributing" name="contributing">Contributing</a>
|
## <a href="#contributing" id="contributing" name="contributing">Contributing</a>
|
||||||
|
|
||||||
You are welcome to fork this repository, make your changes and [submit a pull request][pr]. Please make sure you follow our [code guidelines][guide] though.
|
You are welcome to fork this repository, make your changes and [submit a pull request][pr]. Please make sure you follow our [code guidelines][guide] though.
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
# Make sure to sync any changes with the branch overrides below.
|
# Make sure to sync any changes with the branch overrides below.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'edge'
|
'channel': 'edge'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:3.1'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.24.2--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
- 'Build frontend':
|
- 'Build frontend':
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
'docker':
|
'docker':
|
||||||
'image': '${bamboo.dockerFrontend}'
|
'image': '${bamboo.dockerFrontend}'
|
||||||
'volumes':
|
'volumes':
|
||||||
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
|
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
|
||||||
'key': 'BF'
|
'key': 'BF'
|
||||||
'other':
|
'other':
|
||||||
'clean-working-dir': true
|
'clean-working-dir': true
|
||||||
@@ -142,32 +142,29 @@
|
|||||||
# Install Qemu, create builder.
|
# Install Qemu, create builder.
|
||||||
docker version -f '{{ .Server.Experimental }}'
|
docker version -f '{{ .Server.Experimental }}'
|
||||||
docker buildx rm buildx-builder || :
|
docker buildx rm buildx-builder || :
|
||||||
docker buildx create \
|
docker buildx create --name buildx-builder --driver docker-container\
|
||||||
--name buildx-builder \
|
--use
|
||||||
--driver docker-container \
|
|
||||||
--use
|
|
||||||
docker buildx inspect --bootstrap
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
# Login to DockerHub.
|
# Login to DockerHub.
|
||||||
docker login -u="${bamboo.dockerHubUsername}" \
|
docker login -u="${bamboo.dockerHubUsername}"\
|
||||||
-p="${bamboo.dockerHubPassword}"
|
-p="${bamboo.dockerHubPassword}"
|
||||||
|
|
||||||
# Boot the builder.
|
# Boot the builder.
|
||||||
docker buildx inspect --bootstrap
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
# Print Docker info.
|
# Print Docker info.
|
||||||
docker info
|
docker info
|
||||||
docker buildx version
|
|
||||||
|
|
||||||
# Prepare and push the build.
|
# Prepare and push the build.
|
||||||
env \
|
env\
|
||||||
CHANNEL="${bamboo.channel}" \
|
CHANNEL="${bamboo.channel}"\
|
||||||
REVISION="${bamboo.repository.revision.number}" \
|
COMMIT="${bamboo.repository.revision.number}"\
|
||||||
DIST_DIR='dist' \
|
DIST_DIR='dist'\
|
||||||
DOCKER_IMAGE_NAME='adguard/adguardhome' \
|
DOCKER_IMAGE_NAME='adguard/adguardhome'\
|
||||||
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true" \
|
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true"\
|
||||||
VERBOSE='1' \
|
VERBOSE='1'\
|
||||||
sh ./scripts/make/build-docker.sh
|
sh ./scripts/make/build-docker.sh
|
||||||
'environment':
|
'environment':
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled
|
DOCKER_CLI_EXPERIMENTAL=enabled
|
||||||
'final-tasks':
|
'final-tasks':
|
||||||
@@ -278,8 +275,8 @@
|
|||||||
# need to build a few of these.
|
# need to build a few of these.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'beta'
|
'channel': 'beta'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:3.1'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.24.2--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
# release-vX.Y.Z branches are the branches from which the actual final
|
# release-vX.Y.Z branches are the branches from which the actual final
|
||||||
# release is built.
|
# release is built.
|
||||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||||
@@ -294,5 +291,5 @@
|
|||||||
# are the ones that actually get released.
|
# are the ones that actually get released.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'release'
|
'channel': 'release'
|
||||||
'dockerFrontend': 'adguard/home-js-builder:3.1'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.24.2--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
'key': 'AHBRTSPECS'
|
'key': 'AHBRTSPECS'
|
||||||
'name': 'AdGuard Home - Build and run tests'
|
'name': 'AdGuard Home - Build and run tests'
|
||||||
'variables':
|
'variables':
|
||||||
'dockerFrontend': 'adguard/home-js-builder:3.1'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.24.2--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
'channel': 'development'
|
'channel': 'development'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
@@ -29,17 +29,11 @@
|
|||||||
jobs:
|
jobs:
|
||||||
- 'Artifact'
|
- 'Artifact'
|
||||||
|
|
||||||
- 'E2E':
|
|
||||||
manual: false
|
|
||||||
final: false
|
|
||||||
jobs:
|
|
||||||
- 'Test e2e'
|
|
||||||
|
|
||||||
'Test frontend':
|
'Test frontend':
|
||||||
'docker':
|
'docker':
|
||||||
'image': '${bamboo.dockerFrontend}'
|
'image': '${bamboo.dockerFrontend}'
|
||||||
'volumes':
|
'volumes':
|
||||||
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
|
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
|
||||||
'key': 'JSTEST'
|
'key': 'JSTEST'
|
||||||
'other':
|
'other':
|
||||||
'clean-working-dir': true
|
'clean-working-dir': true
|
||||||
@@ -54,7 +48,7 @@
|
|||||||
|
|
||||||
set -e -f -u -x
|
set -e -f -u -x
|
||||||
|
|
||||||
make VERBOSE=1 js-deps js-typecheck js-lint js-test
|
make VERBOSE=1 js-deps js-lint js-test
|
||||||
'final-tasks':
|
'final-tasks':
|
||||||
- 'clean'
|
- 'clean'
|
||||||
'requirements':
|
'requirements':
|
||||||
@@ -103,7 +97,7 @@
|
|||||||
'docker':
|
'docker':
|
||||||
'image': '${bamboo.dockerFrontend}'
|
'image': '${bamboo.dockerFrontend}'
|
||||||
'volumes':
|
'volumes':
|
||||||
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
|
'${system.YARN_DIR}': '${bamboo.cacheYarn}'
|
||||||
'key': 'BF'
|
'key': 'BF'
|
||||||
'other':
|
'other':
|
||||||
'clean-working-dir': true
|
'clean-working-dir': true
|
||||||
@@ -171,38 +165,6 @@
|
|||||||
'requirements':
|
'requirements':
|
||||||
- 'adg-docker': 'true'
|
- 'adg-docker': 'true'
|
||||||
|
|
||||||
'Test e2e':
|
|
||||||
'artifact-subscriptions':
|
|
||||||
- 'artifact': 'AdGuardHome_linux_amd64'
|
|
||||||
- 'artifact': 'AdGuardHome frontend'
|
|
||||||
'docker':
|
|
||||||
'image': '${bamboo.dockerFrontend}'
|
|
||||||
'volumes':
|
|
||||||
'${system.NPM_DIR}': '${bamboo.cacheNpm}'
|
|
||||||
'key': 'E2ETEST'
|
|
||||||
'other':
|
|
||||||
'clean-working-dir': true
|
|
||||||
'tasks':
|
|
||||||
- 'checkout':
|
|
||||||
'force-clean-build': true
|
|
||||||
- 'script':
|
|
||||||
'interpreter': 'SHELL'
|
|
||||||
'scripts':
|
|
||||||
- |
|
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e -f -u -x
|
|
||||||
|
|
||||||
export CI=true
|
|
||||||
|
|
||||||
tar -xzf dist/AdGuardHome_linux_amd64.tar.gz -C /tmp
|
|
||||||
|
|
||||||
mv /tmp/AdGuardHome/AdGuardHome ./AdGuardHome
|
|
||||||
|
|
||||||
make VERBOSE=1 js-deps js-test-e2e
|
|
||||||
'requirements':
|
|
||||||
- 'adg-docker': 'true'
|
|
||||||
|
|
||||||
'branches':
|
'branches':
|
||||||
'create': 'for-pull-request'
|
'create': 'for-pull-request'
|
||||||
'delete':
|
'delete':
|
||||||
@@ -233,6 +195,6 @@
|
|||||||
# Set the default release channel on the release branch to beta, as we
|
# Set the default release channel on the release branch to beta, as we
|
||||||
# may need to build a few of these.
|
# may need to build a few of these.
|
||||||
'variables':
|
'variables':
|
||||||
'dockerFrontend': 'adguard/home-js-builder:3.1'
|
'dockerFrontend': 'adguard/home-js-builder:2.0'
|
||||||
'dockerGo': 'adguard/go-builder:1.24.2--1'
|
'dockerGo': 'adguard/go-builder:1.23.1--1'
|
||||||
'channel': 'candidate'
|
'channel': 'candidate'
|
||||||
|
|||||||
22
client/.eslintrc.json
vendored
22
client/.eslintrc.json
vendored
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": ["prettier"],
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"extends": [
|
"extends": [
|
||||||
"airbnb-base",
|
"airbnb-base",
|
||||||
"prettier",
|
"prettier",
|
||||||
@@ -23,23 +21,12 @@
|
|||||||
},
|
},
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"node": {
|
"node": {
|
||||||
"extensions": [
|
"extensions": [".js", ".jsx", ".ts", ".tsx"]
|
||||||
".js",
|
|
||||||
".jsx",
|
|
||||||
".ts",
|
|
||||||
".tsx"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"argsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"import/extensions": [
|
"import/extensions": [
|
||||||
"error",
|
"error",
|
||||||
"ignorePackages",
|
"ignorePackages",
|
||||||
@@ -56,10 +43,7 @@
|
|||||||
"no-console": [
|
"no-console": [
|
||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
"allow": [
|
"allow": ["warn", "error"]
|
||||||
"warn",
|
|
||||||
"error"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"import/no-extraneous-dependencies": [
|
"import/no-extraneous-dependencies": [
|
||||||
|
|||||||
6
client/jest.config.mjs
vendored
Normal file
6
client/jest.config.mjs
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'babel-jest',
|
||||||
|
},
|
||||||
|
};
|
||||||
8829
client/package-lock.json
generated
vendored
8829
client/package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
23
client/package.json
vendored
23
client/package.json
vendored
@@ -7,14 +7,11 @@
|
|||||||
"build-prod": "cross-env BUILD_ENV=prod webpack --config webpack.prod.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": "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",
|
"watch:hot": "cross-env BUILD_ENV=dev webpack-dev-server --config webpack.dev.js",
|
||||||
"lint": "eslint --ext .ts,.tsx src",
|
"lint": "echo 'Lint temporarily disabled'",
|
||||||
"lint:fix": "eslint --ext .ts,.tsx src --fix",
|
"lint-new": "eslint './src/**/*.(ts|tsx)'",
|
||||||
"test": "vitest --run",
|
"lint:fix": "eslint './src/**/*.(ts|tsx)' --fix",
|
||||||
"test:watch": "vitest --watch",
|
"test": "jest",
|
||||||
"test:e2e": "npx playwright test tests/e2e",
|
"test:watch": "jest --watch",
|
||||||
"test:e2e:interactive": "npx playwright test --ui",
|
|
||||||
"test:e2e:debug": "npx playwright test --debug",
|
|
||||||
"test:e2e:codegen": "npx playwright codegen",
|
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"typecheck:watch": "tsc --noEmit --watch"
|
"typecheck:watch": "tsc --noEmit --watch"
|
||||||
},
|
},
|
||||||
@@ -23,7 +20,6 @@
|
|||||||
"@nivo/line": "^0.64.0",
|
"@nivo/line": "^0.64.0",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"countries-and-timezones": "^3.6.0",
|
"countries-and-timezones": "^3.6.0",
|
||||||
"date-fns": "^1.29.0",
|
"date-fns": "^1.29.0",
|
||||||
"i18next": "^19.6.2",
|
"i18next": "^19.6.2",
|
||||||
@@ -38,7 +34,6 @@
|
|||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-click-outside": "^3.0.1",
|
"react-click-outside": "^3.0.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-hook-form": "^7.54.0",
|
|
||||||
"react-i18next": "^11.7.2",
|
"react-i18next": "^11.7.2",
|
||||||
"react-modal": "^3.11.2",
|
"react-modal": "^3.11.2",
|
||||||
"react-popper-tooltip": "^2.11.1",
|
"react-popper-tooltip": "^2.11.1",
|
||||||
@@ -51,6 +46,7 @@
|
|||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-actions": "^2.6.5",
|
"redux-actions": "^2.6.5",
|
||||||
|
"redux-form": "^8.3.10",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"ts-migrate": "^0.1.35",
|
"ts-migrate": "^0.1.35",
|
||||||
"url-polyfill": "^1.1.12"
|
"url-polyfill": "^1.1.12"
|
||||||
@@ -64,15 +60,15 @@
|
|||||||
"@babel/plugin-transform-runtime": "^7.24.3",
|
"@babel/plugin-transform-runtime": "^7.24.3",
|
||||||
"@babel/preset-env": "^7.24.5",
|
"@babel/preset-env": "^7.24.5",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.1",
|
||||||
"@playwright/test": "1.50.1",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/lodash": "^4.17.4",
|
"@types/lodash": "^4.17.4",
|
||||||
"@types/node": "^22.13.10",
|
|
||||||
"@types/react": "^17.0.80",
|
"@types/react": "^17.0.80",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/react-redux": "^7.1.33",
|
"@types/react-redux": "^7.1.33",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/react-table": "^7.7.20",
|
"@types/react-table": "^7.7.20",
|
||||||
"@types/redux-actions": "^2.6.5",
|
"@types/redux-actions": "^2.6.5",
|
||||||
|
"@types/redux-form": "^8.3.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||||
"@typescript-eslint/parser": "^7.10.0",
|
"@typescript-eslint/parser": "^7.10.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
@@ -89,6 +85,8 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"html-webpack-plugin": "^5.6.0",
|
"html-webpack-plugin": "^5.6.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"jscodeshift": "^0.15.2",
|
"jscodeshift": "^0.15.2",
|
||||||
"mini-css-extract-plugin": "^2.9.0",
|
"mini-css-extract-plugin": "^2.9.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
@@ -99,7 +97,6 @@
|
|||||||
"stylelint": "^16.5.0",
|
"stylelint": "^16.5.0",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vitest": "^3.1.1",
|
|
||||||
"webpack": "^5.91.0",
|
"webpack": "^5.91.0",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-dev-server": "^5.0.4",
|
"webpack-dev-server": "^5.0.4",
|
||||||
|
|||||||
52
client/playwright.config.ts
vendored
52
client/playwright.config.ts
vendored
@@ -1,52 +0,0 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
import { CONFIG_FILE_PATH } from './tests/constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
|
||||||
*/
|
|
||||||
export default defineConfig({
|
|
||||||
testDir: './tests/e2e',
|
|
||||||
globalSetup: path.resolve('./tests/e2e/globalSetup.ts'),
|
|
||||||
globalTeardown: path.resolve('./tests/e2e/globalTeardown.ts'),
|
|
||||||
timeout: 5000,
|
|
||||||
/* Run tests in files in parallel */
|
|
||||||
fullyParallel: true,
|
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
||||||
forbidOnly: !!process.env.CI,
|
|
||||||
/* Retry on CI only */
|
|
||||||
retries: process.env.CI ? 2 : 0,
|
|
||||||
/* Opt out of parallel tests on CI. */
|
|
||||||
workers: process.env.CI ? 1 : undefined,
|
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
||||||
reporter: 'html',
|
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
||||||
use: {
|
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
||||||
baseURL: 'http://127.0.0.1:3000',
|
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
||||||
trace: 'on-first-retry',
|
|
||||||
launchOptions: {
|
|
||||||
headless: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'chromium',
|
|
||||||
use: { ...devices['Desktop Chrome'] },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
webServer: {
|
|
||||||
stdout: process.env.CI ? 'pipe' : 'ignore',
|
|
||||||
command: `${!process.env.CI ? 'sudo ' : ''}./AdGuardHome --local-frontend -v -c ${CONFIG_FILE_PATH}`,
|
|
||||||
url: 'http://127.0.0.1:3000',
|
|
||||||
cwd: '..',
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
timeout: 10000,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -291,7 +291,7 @@
|
|||||||
"custom_ip": "عنوان IP مخصص",
|
"custom_ip": "عنوان IP مخصص",
|
||||||
"blocking_ipv4": "حجب عنوان IPv4",
|
"blocking_ipv4": "حجب عنوان IPv4",
|
||||||
"blocking_ipv6": "حجب عنوان IPv6",
|
"blocking_ipv6": "حجب عنوان IPv6",
|
||||||
"blocked_response_ttl": "حظر استجابة TTL",
|
"blocked_response_ttl": "زمن حظر الاستجابة",
|
||||||
"blocked_response_ttl_desc": "تحديد عدد الثواني التي يجب على العملاء تخزين الاستجابة التي تمت تصفيتها مؤقتًا",
|
"blocked_response_ttl_desc": "تحديد عدد الثواني التي يجب على العملاء تخزين الاستجابة التي تمت تصفيتها مؤقتًا",
|
||||||
"form_enter_blocked_response_ttl": "أدخل وقت الاستجابة المحظورة TTL (بالثواني)",
|
"form_enter_blocked_response_ttl": "أدخل وقت الاستجابة المحظورة TTL (بالثواني)",
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
@@ -734,10 +734,10 @@
|
|||||||
"thursday": "الخميس",
|
"thursday": "الخميس",
|
||||||
"friday": "الجمعة",
|
"friday": "الجمعة",
|
||||||
"saturday": "السبت",
|
"saturday": "السبت",
|
||||||
"sunday_short": "الأحد",
|
"sunday_short": "الاحد",
|
||||||
"monday_short": "الإثنين",
|
"monday_short": "الإثنين",
|
||||||
"tuesday_short": "الثلاثاء",
|
"tuesday_short": "الثلاثاء",
|
||||||
"wednesday_short": "الأربعاء",
|
"wednesday_short": "الاربعاء",
|
||||||
"thursday_short": "الخميس",
|
"thursday_short": "الخميس",
|
||||||
"friday_short": "الجمعة",
|
"friday_short": "الجمعة",
|
||||||
"saturday_short": "السبت",
|
"saturday_short": "السبت",
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"client_settings": "Налады кліентаў",
|
"client_settings": "Налады кліентаў",
|
||||||
"example_upstream_reserved": "upstream <0>для канкрэтных даменаў</0>;",
|
"example_upstream_reserved": "upstream <0>для канкрэтных даменаў</0>;",
|
||||||
"example_multiple_upstreams_reserved": "некалькі сервер DNSаў <0>для канкрэтных даменаў</0>;",
|
"example_multiple_upstreams_reserved": "некалькі DNS-сервераў <0>для канкрэтных даменаў</0>;",
|
||||||
"example_upstream_comment": "каментар.",
|
"example_upstream_comment": "каментар.",
|
||||||
"upstream_parallel": "Ужыць адначасныя запыты да ўсіх сервераў для паскарэння апрацоўкі запыту",
|
"upstream_parallel": "Ужыць адначасныя запыты да ўсіх сервераў для паскарэння апрацоўкі запыту",
|
||||||
"parallel_requests": "Паралельныя запыты",
|
"parallel_requests": "Паралельныя запыты",
|
||||||
"load_balancing": "Размеркаванне нагрузкі",
|
"load_balancing": "Размеркаванне нагрузкі",
|
||||||
"load_balancing_desc": "Запытвайце па адным серверы за раз. AdGuard Home будзе выкарыстоўваць выпадковы алгарытм для выбару сервера, так што самы хуткі сервер будзе выкарыстоўвацца часцей.",
|
"load_balancing_desc": "Запытвайце па адным серверы за раз. AdGuard Home будзе выкарыстоўваць выпадковы алгарытм для выбару сервера, так што самы хуткі сервер будзе выкарыстоўвацца часцей.",
|
||||||
"bootstrap_dns": "Bootstrap сервер DNSы",
|
"bootstrap_dns": "Bootstrap DNS-серверы",
|
||||||
"bootstrap_dns_desc": "IP-адрасы сервер DNSаў, якія выкарыстоўваюцца для вырашэння IP-адрасоў распознавальнікаў DoH/DoT, якія вы ўказваеце ў якасці перадачы. Каментары не дапускаюцца.",
|
"bootstrap_dns_desc": "IP-адрасы DNS-сервераў, якія выкарыстоўваюцца для вырашэння IP-адрасоў распознавальнікаў DoH/DoT, якія вы ўказваеце ў якасці перадачы. Каментары не дапускаюцца.",
|
||||||
"fallback_dns_title": "Рэзервовыя сервер DNSы",
|
"fallback_dns_title": "Рэзервовыя DNS-серверы",
|
||||||
"fallback_dns_desc": "Спіс рэзервовых сервер DNSаў, якія выкарыстоўваюцца, калі вышэйшыя сервер DNSы не адказваюць. Сінтаксіс такі ж, як і ў галоўным полі ўверх.",
|
"fallback_dns_desc": "Спіс рэзервовых DNS-сервераў, якія выкарыстоўваюцца, калі вышэйшыя DNS-серверы не адказваюць. Сінтаксіс такі ж, як і ў галоўным полі ўверх.",
|
||||||
"fallback_dns_placeholder": "Увядзіце па адным рэзервовым серверы DNS у радку",
|
"fallback_dns_placeholder": "Увядзіце па адным рэзервовым серверы DNS у радку",
|
||||||
"local_ptr_title": "Прыватныя сервер DNSы",
|
"local_ptr_title": "Прыватныя DNS-серверы",
|
||||||
"local_ptr_desc": "DNS-серверы, якія AdGuard Home выкарыстоўвае для лакальных PTR-запытаў. Гэтыя серверы выкарыстоўваюцца, каб атрымаць даменавыя імёны кліентаў з прыватнымі IP-адрасамі, напрыклад «192.168.12.34», з дапамогай rDNS. Калі спіс пусты, AdGuard Home выкарыстоўвае прадвызначаныя DNS-серверы вашай АС.",
|
"local_ptr_desc": "DNS-серверы, якія AdGuard Home выкарыстоўвае для лакальных PTR-запытаў. Гэтыя серверы выкарыстоўваюцца, каб атрымаць даменавыя імёны кліентаў з прыватнымі IP-адрасамі, напрыклад «192.168.12.34», з дапамогай rDNS. Калі спіс пусты, AdGuard Home выкарыстоўвае прадвызначаныя DNS-серверы вашай АС.",
|
||||||
"local_ptr_default_resolver": "Па змаўчанні AdGuard Home выкарыстоўвае наступныя зваротныя DNS-рэзолверы: {{ip}}.",
|
"local_ptr_default_resolver": "Па змаўчанні AdGuard Home выкарыстоўвае наступныя зваротныя DNS-рэзолверы: {{ip}}.",
|
||||||
"local_ptr_no_default_resolver": "AdGuard Home не змог вызначыць прыдатныя прыватныя адваротныя DNS-рэзолверы для гэтай сістэмы.",
|
"local_ptr_no_default_resolver": "AdGuard Home не змог вызначыць прыдатныя прыватныя адваротныя DNS-рэзолверы для гэтай сістэмы.",
|
||||||
"local_ptr_placeholder": "Увядзіце па адным адрасе на радок",
|
"local_ptr_placeholder": "Увядзіце па адным адрасе на радок",
|
||||||
"resolve_clients_title": "Уключыць запытванне даменавых імёнаў для кліентаў",
|
"resolve_clients_title": "Уключыць запытванне даменавых імёнаў для кліентаў",
|
||||||
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны сервер DNS для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
|
"resolve_clients_desc": "AdGuard Home будзе спрабаваць аўтаматычна вызначыць даменавыя імёны кліентаў праз PTR-запыты да адпаведных сервераў (прыватны DNS-сервер для лакальных кліентаў, upstream-серверы для кліентаў з публічным IP-адрасам).",
|
||||||
"use_private_ptr_resolvers_title": "Ужываць прыватныя адваротныя DNS-рэзолверы",
|
"use_private_ptr_resolvers_title": "Ужываць прыватныя адваротныя DNS-рэзолверы",
|
||||||
"use_private_ptr_resolvers_desc": "Пасылаць адваротныя DNS-запыты для лакальна абслугоўных адрасоў на паказаныя серверы. Калі адключана, AdGuard Home будзе адказваць NXDOMAIN на ўсе падобныя PTR-запыты, апроч запытаў пра кліентаў, ужо вядомых па DHCP, /etc/hosts і гэтак далей.",
|
"use_private_ptr_resolvers_desc": "Пасылаць адваротныя DNS-запыты для лакальна абслугоўных адрасоў на паказаныя серверы. Калі адключана, AdGuard Home будзе адказваць NXDOMAIN на ўсе падобныя PTR-запыты, апроч запытаў пра кліентаў, ужо вядомых па DHCP, /etc/hosts і гэтак далей.",
|
||||||
"check_dhcp_servers": "Праверыць DHCP-серверы",
|
"check_dhcp_servers": "Праверыць DHCP-серверы",
|
||||||
@@ -101,13 +101,13 @@
|
|||||||
"compact": "Компактный",
|
"compact": "Компактный",
|
||||||
"nothing_found": "Нічога не знойдзена",
|
"nothing_found": "Нічога не знойдзена",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"version": "Версія",
|
"version": "версія",
|
||||||
"address": "Адрас",
|
"address": "Адрас",
|
||||||
"protocol": "Пратакол",
|
"protocol": "Пратакол",
|
||||||
"on": "УКЛ",
|
"on": "УКЛ",
|
||||||
"off": "Выкл",
|
"off": "Выкл",
|
||||||
"copyright": "Усе правы захаваныя",
|
"copyright": "Усе правы захаваныя",
|
||||||
"homepage": "Хатняя старонка",
|
"homepage": "Галоўная",
|
||||||
"report_an_issue": "Паведаміць пра праблему",
|
"report_an_issue": "Паведаміць пра праблему",
|
||||||
"privacy_policy": "Палітыка прыватнасці",
|
"privacy_policy": "Палітыка прыватнасці",
|
||||||
"enable_protection": "Уключыць абарону",
|
"enable_protection": "Уключыць абарону",
|
||||||
@@ -165,8 +165,8 @@
|
|||||||
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі",
|
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі",
|
||||||
"encryption_settings": "Налады шыфравання",
|
"encryption_settings": "Налады шыфравання",
|
||||||
"dhcp_settings": "Налады DHCP",
|
"dhcp_settings": "Налады DHCP",
|
||||||
"upstream_dns": "Upstream сервер DNSы",
|
"upstream_dns": "Upstream DNS-серверы",
|
||||||
"upstream_dns_help": "Увядзіце адрасы сервераў па адным у радку. <a>Даведацца больш </a> пра наладжванне сервер DNSаў.",
|
"upstream_dns_help": "Увядзіце адрасы сервераў па адным у радку. <a>Даведацца больш </a> пра наладжванне DNS-сервераў.",
|
||||||
"upstream_dns_configured_in_file": "Наладжаны ў {{path}}",
|
"upstream_dns_configured_in_file": "Наладжаны ў {{path}}",
|
||||||
"test_upstream_btn": "Тэст upstream сервераў",
|
"test_upstream_btn": "Тэст upstream сервераў",
|
||||||
"upstreams": "Upstreams",
|
"upstreams": "Upstreams",
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
"enabled_save_search_toast": "Уключаны бяспечны пошук",
|
"enabled_save_search_toast": "Уключаны бяспечны пошук",
|
||||||
"updated_save_search_toast": "Налады бяспечнага пошуку абноўлены",
|
"updated_save_search_toast": "Налады бяспечнага пошуку абноўлены",
|
||||||
"enabled_table_header": "УКЛ.",
|
"enabled_table_header": "УКЛ.",
|
||||||
"name_table_header": "Назва",
|
"name_table_header": "Імя",
|
||||||
"list_url_table_header": "URL-адрас спіса",
|
"list_url_table_header": "URL-адрас спіса",
|
||||||
"rules_count_table_header": "Колькасць правілаў:",
|
"rules_count_table_header": "Колькасць правілаў:",
|
||||||
"last_time_updated_table_header": "Апошняе абнаўленне",
|
"last_time_updated_table_header": "Апошняе абнаўленне",
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
"no_whitelist_added": "Белыя спісы не дададзены",
|
"no_whitelist_added": "Белыя спісы не дададзены",
|
||||||
"add_blocklist": "Дадаць чорны спіс",
|
"add_blocklist": "Дадаць чорны спіс",
|
||||||
"add_allowlist": "Дадаць белы спіс",
|
"add_allowlist": "Дадаць белы спіс",
|
||||||
"cancel_btn": "Скасаваць",
|
"cancel_btn": "Адмена",
|
||||||
"enter_name_hint": "Увядзіце імя",
|
"enter_name_hint": "Увядзіце імя",
|
||||||
"enter_url_or_path_hint": "Увядзіце URL-адрас ці абсалютны шлях да спіса",
|
"enter_url_or_path_hint": "Увядзіце URL-адрас ці абсалютны шлях да спіса",
|
||||||
"check_updates_btn": "Праверыць абнаўленні",
|
"check_updates_btn": "Праверыць абнаўленні",
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
"example_meaning_host_block": "адказаць 127.0.0.1 для example.org (але не для яго паддаменаў);",
|
"example_meaning_host_block": "адказаць 127.0.0.1 для example.org (але не для яго паддаменаў);",
|
||||||
"example_comment": "! Так можна дадаваць апісанне.",
|
"example_comment": "! Так можна дадаваць апісанне.",
|
||||||
"example_comment_meaning": "каментар;",
|
"example_comment_meaning": "каментар;",
|
||||||
"example_comment_hash": "# Таксама каментарый.",
|
"example_comment_hash": "# І вось так таксама.",
|
||||||
"example_regex_meaning": "блакаваць доступ да даменаў, якія адпавядаюць зададзенаму рэгулярнаму выразу.",
|
"example_regex_meaning": "блакаваць доступ да даменаў, якія адпавядаюць зададзенаму рэгулярнаму выразу.",
|
||||||
"example_upstream_regular": "звычайны DNS (наўзверх UDP);",
|
"example_upstream_regular": "звычайны DNS (наўзверх UDP);",
|
||||||
"example_upstream_regular_port": "звычайны DNS (праз UDP, імя хаста);",
|
"example_upstream_regular_port": "звычайны DNS (праз UDP, імя хаста);",
|
||||||
@@ -233,13 +233,13 @@
|
|||||||
"example_upstream_tcp_port": "звычайны DNS (праз TCP, імя хаста);",
|
"example_upstream_tcp_port": "звычайны DNS (праз TCP, імя хаста);",
|
||||||
"example_upstream_tcp_hostname": "звычайны DNS (праз TCP, імя хаста);",
|
"example_upstream_tcp_hostname": "звычайны DNS (праз TCP, імя хаста);",
|
||||||
"all_lists_up_to_date_toast": "Усе спісы ўжо абноўлены",
|
"all_lists_up_to_date_toast": "Усе спісы ўжо абноўлены",
|
||||||
"updated_upstream_dns_toast": "Upstream сервер DNSы абноўлены",
|
"updated_upstream_dns_toast": "Upstream DNS-серверы абноўлены",
|
||||||
"dns_test_ok_toast": "Паказаныя серверы DNS працуюць карэктна",
|
"dns_test_ok_toast": "Паказаныя серверы DNS працуюць карэктна",
|
||||||
"dns_test_not_ok_toast": "Сервер «{{key}}»: немагчыма выкарыстоўваць, праверце слушнасць напісання",
|
"dns_test_not_ok_toast": "Сервер «{{key}}»: немагчыма выкарыстоўваць, праверце слушнасць напісання",
|
||||||
"dns_test_parsing_error_toast": "Раздзел {{section}}: радок {{line}}: немагчыма выкарыстоўваць, праверце слушнасць напісання",
|
"dns_test_parsing_error_toast": "Раздзел {{section}}: радок {{line}}: немагчыма выкарыстоўваць, праверце слушнасць напісання",
|
||||||
"dns_test_warning_toast": "Upstream «{{key}}» не адказвае на тэставыя запыты і можа не працаваць належным чынам",
|
"dns_test_warning_toast": "Upstream «{{key}}» не адказвае на тэставыя запыты і можа не працаваць належным чынам",
|
||||||
"unblock": "Адблакаваць",
|
"unblock": "Адблакаваць",
|
||||||
"block": "Заблакіраваць",
|
"block": "Заблакаваць",
|
||||||
"disallow_this_client": "Забараніць доступ гэтаму кліенту",
|
"disallow_this_client": "Забараніць доступ гэтаму кліенту",
|
||||||
"allow_this_client": "Дазволіць доступ гэтаму кліенту",
|
"allow_this_client": "Дазволіць доступ гэтаму кліенту",
|
||||||
"block_for_this_client_only": "Заблакаваць толькі для гэтага кліента",
|
"block_for_this_client_only": "Заблакаваць толькі для гэтага кліента",
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
"no_logs_found": "Логі не знойдзены",
|
"no_logs_found": "Логі не знойдзены",
|
||||||
"refresh_btn": "Абнавіць",
|
"refresh_btn": "Абнавіць",
|
||||||
"previous_btn": "Назад",
|
"previous_btn": "Назад",
|
||||||
"next_btn": "Далей",
|
"next_btn": "Наперад",
|
||||||
"loading_table_status": "Загрузка...",
|
"loading_table_status": "Загрузка...",
|
||||||
"page_table_footer_text": "Старонка",
|
"page_table_footer_text": "Старонка",
|
||||||
"rows_table_footer_text": "радкоў",
|
"rows_table_footer_text": "радкоў",
|
||||||
@@ -280,7 +280,7 @@
|
|||||||
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
|
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
|
||||||
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
|
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
|
||||||
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
|
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
|
||||||
"dns_config": "Налады сервер DNSа",
|
"dns_config": "Налады DNS-сервера",
|
||||||
"dns_cache_config": "Налада кэша DNS",
|
"dns_cache_config": "Налада кэша DNS",
|
||||||
"dns_cache_config_desc": "Тут можна наладзіць кэш DNS",
|
"dns_cache_config_desc": "Тут можна наладзіць кэш DNS",
|
||||||
"blocking_mode": "Рэжым блакавання",
|
"blocking_mode": "Рэжым блакавання",
|
||||||
@@ -342,14 +342,14 @@
|
|||||||
"unknown_filter": "Невядомы фільтр {{filterId}}",
|
"unknown_filter": "Невядомы фільтр {{filterId}}",
|
||||||
"known_tracker": "Вядомы трэкер",
|
"known_tracker": "Вядомы трэкер",
|
||||||
"install_welcome_title": "Сардэчна запрашаем у AdGuard Home!",
|
"install_welcome_title": "Сардэчна запрашаем у AdGuard Home!",
|
||||||
"install_welcome_desc": "AdGuard Home – гэта сервер DNS, што блакуе рэкламу і трэкінг. Яго мэта – даць вам магчымасць кантраляваць усю ваша сеціва і ўсе падлучаныя прылады. Ён не патрабуе ўсталёўкі кліенцкіх праграм.",
|
"install_welcome_desc": "AdGuard Home – гэта DNS-сервер, што блакуе рэкламу і трэкінг. Яго мэта – даць вам магчымасць кантраляваць усю ваша сеціва і ўсе падлучаныя прылады. Ён не патрабуе ўсталёўкі кліенцкіх праграм.",
|
||||||
"install_settings_title": "Ўэб-інтэрфейс адміністравання",
|
"install_settings_title": "Ўэб-інтэрфейс адміністравання",
|
||||||
"install_settings_listen": "Інтэрфейс сеціва",
|
"install_settings_listen": "Інтэрфейс сеціва",
|
||||||
"install_settings_port": "Порт",
|
"install_settings_port": "Порт",
|
||||||
"install_settings_interface_link": "Ваш ўэб-інтэрфейс адміністравання AdGuard Home будзе даступны па наступных адрасах:",
|
"install_settings_interface_link": "Ваш ўэб-інтэрфейс адміністравання AdGuard Home будзе даступны па наступных адрасах:",
|
||||||
"form_error_port": "Увядзіце карэктны нумар порта",
|
"form_error_port": "Увядзіце карэктны нумар порта",
|
||||||
"install_settings_dns": "DNS-сервер",
|
"install_settings_dns": "DNS-сервер",
|
||||||
"install_settings_dns_desc": "Вам будзе трэба наладзіць свае прылады ці роўтар на выкарыстанне сервер DNSа на адным з наступных адрасоў:",
|
"install_settings_dns_desc": "Вам будзе трэба наладзіць свае прылады ці роўтар на выкарыстанне DNS-сервера на адным з наступных адрасоў:",
|
||||||
"install_settings_all_interfaces": "Усе інтэрфейсы",
|
"install_settings_all_interfaces": "Усе інтэрфейсы",
|
||||||
"install_auth_title": "Аўтарызацыя",
|
"install_auth_title": "Аўтарызацыя",
|
||||||
"install_auth_desc": "Настойліва рэкамендуецца наладзіць аўтэнтыфікацыю паролем для ўэб-інтэрфейсу AdGuard Home. Нават калі ён даступны толькі ў вашай лакальнай сетцы, важна абараніць яго ад неабмежаванага доступу.",
|
"install_auth_desc": "Настойліва рэкамендуецца наладзіць аўтэнтыфікацыю паролем для ўэб-інтэрфейсу AdGuard Home. Нават калі ён даступны толькі ў вашай лакальнай сетцы, важна абараніць яго ад неабмежаванага доступу.",
|
||||||
@@ -365,17 +365,17 @@
|
|||||||
"install_submit_desc": "Працэдура налады завершана і вы гатовы пачаць выкарыстанне AdGuard Home.",
|
"install_submit_desc": "Працэдура налады завершана і вы гатовы пачаць выкарыстанне AdGuard Home.",
|
||||||
"install_devices_router": "Роўтар",
|
"install_devices_router": "Роўтар",
|
||||||
"install_devices_router_desc": "Такая наладка аўтаматычна пакрые ўсе прылады, што выкарыстоўваюць ваш хатні роўтар, і вам не трэба будзе наладжваць кожнае з іх у асобнасці.",
|
"install_devices_router_desc": "Такая наладка аўтаматычна пакрые ўсе прылады, што выкарыстоўваюць ваш хатні роўтар, і вам не трэба будзе наладжваць кожнае з іх у асобнасці.",
|
||||||
"install_devices_address": "сервер DNS AdGuard Home даступны па наступных адрасах",
|
"install_devices_address": "DNS-сервер AdGuard Home даступны па наступных адрасах",
|
||||||
"install_devices_router_list_1": "Адкрыйце налады вашага роўтара. Звычайна вы можаце адкрыць іх у вашым браўзары, напрыклад, http://192.168.0.1/ ці http://192.168.1.1/. Вас могуць папрасіць увесці пароль. Калі вы не помніце яго, пароль часта можна скінуць, націснуўшы на кнопку на самым роўтары. Некаторыя роўтары патрабуюць адмысловага дадатку, які ў гэтым выпадку павінен быць ужо ўсталявана на ваш кампутар ці тэлефон.",
|
"install_devices_router_list_1": "Адкрыйце налады вашага роўтара. Звычайна вы можаце адкрыць іх у вашым браўзары, напрыклад, http://192.168.0.1/ ці http://192.168.1.1/. Вас могуць папрасіць увесці пароль. Калі вы не помніце яго, пароль часта можна скінуць, націснуўшы на кнопку на самым роўтары. Некаторыя роўтары патрабуюць адмысловага дадатку, які ў гэтым выпадку павінен быць ужо ўсталявана на ваш кампутар ці тэлефон.",
|
||||||
"install_devices_router_list_2": "Знайдзіце налады DHCP ці DNS. Знайдзіце літары «DNS» поруч з тэкставым полем, у якое можна ўвесці два ці тры шэрагі лічбаў, падзеленых на 4 групы ад адной до трох лічбаў.",
|
"install_devices_router_list_2": "Знайдзіце налады DHCP ці DNS. Знайдзіце літары «DNS» поруч з тэкставым полем, у якое можна ўвесці два ці тры шэрагі лічбаў, падзеленых на 4 групы ад адной до трох лічбаў.",
|
||||||
"install_devices_router_list_3": "Увядзіце туды адрас вашага AdGuard Home.",
|
"install_devices_router_list_3": "Увядзіце туды адрас вашага AdGuard Home.",
|
||||||
"install_devices_router_list_4": "Вы не можаце ўсталяваць уласны сервер DNS на некаторых тыпах маршрутызатараў. У гэтым выпадку можа дапамагчы налада AdGuard Home у якасці <a href='#dhcp'>DHCP-сервера</a>. У адваротным выпадку вам трэба звярнуцца да кіраўніцтва па наладзе сервер DNSаў для вашай пэўнай мадэлі маршрутызатара.",
|
"install_devices_router_list_4": "Вы не можаце ўсталяваць уласны DNS-сервер на некаторых тыпах маршрутызатараў. У гэтым выпадку можа дапамагчы налада AdGuard Home у якасці <a href='#dhcp'>DHCP-сервера</a>. У адваротным выпадку вам трэба звярнуцца да кіраўніцтва па наладзе DNS-сервераў для вашай пэўнай мадэлі маршрутызатара.",
|
||||||
"install_devices_windows_list_1": "Адкрыйце Панэль кіравання праз меню «Пуск» ці праз пошук Windows.",
|
"install_devices_windows_list_1": "Адкрыйце Панэль кіравання праз меню «Пуск» ці праз пошук Windows.",
|
||||||
"install_devices_windows_list_2": "Перайдзіце ў «Сеціва і інтэрнэт», а потым у «Цэнтр кіравання сеціва і агульным доступам».",
|
"install_devices_windows_list_2": "Перайдзіце ў «Сеціва і інтэрнэт», а потым у «Цэнтр кіравання сеціва і агульным доступам».",
|
||||||
"install_devices_windows_list_3": "У левым боку экрана клікніце «Змена параметраў адаптара».",
|
"install_devices_windows_list_3": "У левым боку экрана клікніце «Змена параметраў адаптара».",
|
||||||
"install_devices_windows_list_4": "Пстрыкніце правай кнопкай мышы ваша актыўнае злучэнне і абярыце Уласцівасці.",
|
"install_devices_windows_list_4": "Пстрыкніце правай кнопкай мышы ваша актыўнае злучэнне і абярыце Уласцівасці.",
|
||||||
"install_devices_windows_list_5": "Знайдзіце ў спісе пункт «IP версіі 4 (TCP/IPv4)», вылучыце яго і потым ізноў націсніце «Уласцівасці».",
|
"install_devices_windows_list_5": "Знайдзіце ў спісе пункт «IP версіі 4 (TCP/IPv4)», вылучыце яго і потым ізноў націсніце «Уласцівасці».",
|
||||||
"install_devices_windows_list_6": "Абярыце «Выкарыстаць наступныя адрасы сервер DNSаў» і ўвядзіце адрас AdGuard Home.",
|
"install_devices_windows_list_6": "Абярыце «Выкарыстаць наступныя адрасы DNS-сервераў» і ўвядзіце адрас AdGuard Home.",
|
||||||
"install_devices_macos_list_1": "Клікніце па абразку Apple і перайдзіце ў Сістэмныя налады.",
|
"install_devices_macos_list_1": "Клікніце па абразку Apple і перайдзіце ў Сістэмныя налады.",
|
||||||
"install_devices_macos_list_2": "Клікніце па іконцы Сеціва.",
|
"install_devices_macos_list_2": "Клікніце па іконцы Сеціва.",
|
||||||
"install_devices_macos_list_3": "Абярыце першае падлучэнне ў спісе і націсніце кнопку «Дадаткова».",
|
"install_devices_macos_list_3": "Абярыце першае падлучэнне ў спісе і націсніце кнопку «Дадаткова».",
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
"encryption_key": "Прыватны ключ",
|
"encryption_key": "Прыватны ключ",
|
||||||
"encryption_key_input": "Скапіюйце сюды прыватны ключ у PEM-кадоўцы.",
|
"encryption_key_input": "Скапіюйце сюды прыватны ключ у PEM-кадоўцы.",
|
||||||
"encryption_enable": "Уключыць шыфраванне (HTTPS, DNS-over-HTTPS і DNS-over-TLS)",
|
"encryption_enable": "Уключыць шыфраванне (HTTPS, DNS-over-HTTPS і DNS-over-TLS)",
|
||||||
"encryption_enable_desc": "Калі шыфраванне ўлучана, ўэб-інтэрфейс AdGuard Home будзе працаваць па HTTPS, а сервер DNS будзе таксама працаваць па DNS-over-HTTPS і DNS-over-TLS.",
|
"encryption_enable_desc": "Калі шыфраванне ўлучана, ўэб-інтэрфейс AdGuard Home будзе працаваць па HTTPS, а DNS-сервер будзе таксама працаваць па DNS-over-HTTPS і DNS-over-TLS.",
|
||||||
"encryption_chain_valid": "Ланцужок сертыфікатаў валідны",
|
"encryption_chain_valid": "Ланцужок сертыфікатаў валідны",
|
||||||
"encryption_chain_invalid": "Ланцужок сертыфікатаў не валідны",
|
"encryption_chain_invalid": "Ланцужок сертыфікатаў не валідны",
|
||||||
"encryption_key_valid": "Валідны {{type}} прыватны ключ",
|
"encryption_key_valid": "Валідны {{type}} прыватны ключ",
|
||||||
@@ -435,8 +435,8 @@
|
|||||||
"update_announcement": "AdGuard Home {{version}} ужо даступная! <0>Націсніце сюды</0>, каб даведацца больш.",
|
"update_announcement": "AdGuard Home {{version}} ужо даступная! <0>Націсніце сюды</0>, каб даведацца больш.",
|
||||||
"setup_guide": "Інструкцыя па наладзе",
|
"setup_guide": "Інструкцыя па наладзе",
|
||||||
"dns_addresses": "Адрасы DNS",
|
"dns_addresses": "Адрасы DNS",
|
||||||
"dns_start": "сервер DNS запускаецца",
|
"dns_start": "DNS-сервер запускаецца",
|
||||||
"dns_status_error": "Памылка праверкі стану сервер DNSа",
|
"dns_status_error": "Памылка праверкі стану DNS-сервера",
|
||||||
"down": "Уніз",
|
"down": "Уніз",
|
||||||
"fix": "Выправіць",
|
"fix": "Выправіць",
|
||||||
"dns_providers": "<0>Спіс вядомых DNS-правайдараў</0> на выбар.",
|
"dns_providers": "<0>Спіс вядомых DNS-правайдараў</0> на выбар.",
|
||||||
@@ -449,7 +449,7 @@
|
|||||||
"settings_global": "Глабальныя",
|
"settings_global": "Глабальныя",
|
||||||
"settings_custom": "Свае",
|
"settings_custom": "Свае",
|
||||||
"table_client": "Кліент",
|
"table_client": "Кліент",
|
||||||
"table_name": "Назва",
|
"table_name": "Імя",
|
||||||
"save_btn": "Захаваць",
|
"save_btn": "Захаваць",
|
||||||
"client_add": "Дадаць кліента",
|
"client_add": "Дадаць кліента",
|
||||||
"client_new": "Новы кліент",
|
"client_new": "Новы кліент",
|
||||||
@@ -475,7 +475,7 @@
|
|||||||
"auto_clients_title": "Кліенты (runtime)",
|
"auto_clients_title": "Кліенты (runtime)",
|
||||||
"auto_clients_desc": "Інфармацыя аб IP-адрасах прылад, якія выкарыстоўваюць або могуць выкарыстоўваць AdGuard Home. Гэтая інфармацыя збіраецца з некалькіх крыніц, уключаючы файлы хостаў, зваротны DNS і г.д.",
|
"auto_clients_desc": "Інфармацыя аб IP-адрасах прылад, якія выкарыстоўваюць або могуць выкарыстоўваць AdGuard Home. Гэтая інфармацыя збіраецца з некалькіх крыніц, уключаючы файлы хостаў, зваротны DNS і г.д.",
|
||||||
"access_title": "Налады доступу",
|
"access_title": "Налады доступу",
|
||||||
"access_desc": "Тут вы можаце наладзіць правілы доступу да сервер DNSу AdGuard Home",
|
"access_desc": "Тут вы можаце наладзіць правілы доступу да DNS-серверу AdGuard Home",
|
||||||
"access_allowed_title": "Дазволеныя кліенты",
|
"access_allowed_title": "Дазволеныя кліенты",
|
||||||
"access_allowed_desc": "Спіс CIDR, IP-адрасоў або <a>ClientID</a>. Калі ў гэтым спісе ёсць запісы, AdGuard Home будзе прымаць запыты толькі ад гэтых кліентаў.",
|
"access_allowed_desc": "Спіс CIDR, IP-адрасоў або <a>ClientID</a>. Калі ў гэтым спісе ёсць запісы, AdGuard Home будзе прымаць запыты толькі ад гэтых кліентаў.",
|
||||||
"access_disallowed_title": "Забароненыя кліенты",
|
"access_disallowed_title": "Забароненыя кліенты",
|
||||||
@@ -596,7 +596,7 @@
|
|||||||
"disable_ipv6_desc": "Ігнараваць усе запыты DNS для адрасоў IPv6 (тып AAAA) і выдаленне дадзеных IPv6 з адказаў тыпу HTTPS.",
|
"disable_ipv6_desc": "Ігнараваць усе запыты DNS для адрасоў IPv6 (тып AAAA) і выдаленне дадзеных IPv6 з адказаў тыпу HTTPS.",
|
||||||
"fastest_addr": "Найхуткі IP-адрас",
|
"fastest_addr": "Найхуткі IP-адрас",
|
||||||
"fastest_addr_desc": "Апытайце ўсе DNS-серверы і вярніце самы хуткі IP-адрас сярод усіх адказаў. Гэта замарудзіць выкананне DNS-запытаў, бо нам давядзецца чакаць адказаў ад усіх DNS-сервераў, але палепшыць агульную ўзаемасувязь.",
|
"fastest_addr_desc": "Апытайце ўсе DNS-серверы і вярніце самы хуткі IP-адрас сярод усіх адказаў. Гэта замарудзіць выкананне DNS-запытаў, бо нам давядзецца чакаць адказаў ад усіх DNS-сервераў, але палепшыць агульную ўзаемасувязь.",
|
||||||
"autofix_warning_text": "Пры націску «Выправіць» AdGuard Home наладзіць вашу сістэму на выкарыстанне сервер DNSа AdGuard Home.",
|
"autofix_warning_text": "Пры націску «Выправіць» AdGuard Home наладзіць вашу сістэму на выкарыстанне DNS-сервера AdGuard Home.",
|
||||||
"autofix_warning_list": "Будуць выконвацца наступныя заданні: <0>Дэактываваць сістэмны DNSStubListener</0> <0>Усталяваць адрас сервера DNS на 127.0.0.1</0> <0>Стварыць сімвалічную спасылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Спыніць DNSStubListener (перазагрузіць сістэмную службу)</0>.",
|
"autofix_warning_list": "Будуць выконвацца наступныя заданні: <0>Дэактываваць сістэмны DNSStubListener</0> <0>Усталяваць адрас сервера DNS на 127.0.0.1</0> <0>Стварыць сімвалічную спасылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Спыніць DNSStubListener (перазагрузіць сістэмную службу)</0>.",
|
||||||
"autofix_warning_result": "У выніку ўсе DNS-запыты ад вашай сістэмы будуць па змаўчанні апрацоўвацца AdGuard Home.\n",
|
"autofix_warning_result": "У выніку ўсе DNS-запыты ад вашай сістэмы будуць па змаўчанні апрацоўвацца AdGuard Home.\n",
|
||||||
"tags_title": "Тэгі",
|
"tags_title": "Тэгі",
|
||||||
@@ -634,12 +634,12 @@
|
|||||||
"validated_with_dnssec": "Проверено с помощью DNSSEC",
|
"validated_with_dnssec": "Проверено с помощью DNSSEC",
|
||||||
"all_queries": "Усе запыты",
|
"all_queries": "Усе запыты",
|
||||||
"show_blocked_responses": "Заблакавана",
|
"show_blocked_responses": "Заблакавана",
|
||||||
"show_whitelisted_responses": "У белым спісе",
|
"show_whitelisted_responses": "Белы спіс",
|
||||||
"show_processed_responses": "Апрацавана",
|
"show_processed_responses": "Апрацавана",
|
||||||
"blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
|
"blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
|
||||||
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
|
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
|
||||||
"blocked_threats": "Заблакавана пагроз",
|
"blocked_threats": "Заблакавана пагроз",
|
||||||
"allowed": "У белым спісе",
|
"allowed": "Дазволены",
|
||||||
"filtered": "Адфільтраваныя",
|
"filtered": "Адфільтраваныя",
|
||||||
"rewritten": "Перапісаныя",
|
"rewritten": "Перапісаныя",
|
||||||
"safe_search": "Бяспечны пошук",
|
"safe_search": "Бяспечны пошук",
|
||||||
@@ -738,7 +738,7 @@
|
|||||||
"thursday_short": "Чц.",
|
"thursday_short": "Чц.",
|
||||||
"friday_short": "Пт.",
|
"friday_short": "Пт.",
|
||||||
"saturday_short": "Сб.",
|
"saturday_short": "Сб.",
|
||||||
"upstream_dns_cache_configuration": "Канфігурацыя кэша upstream сервер DNSаў",
|
"upstream_dns_cache_configuration": "Канфігурацыя кэша upstream DNS-сервераў",
|
||||||
"enable_upstream_dns_cache": "Ўключыць кэшаванне для карыстацкай канфігурацыі upstream-сервераў гэтага кліента",
|
"enable_upstream_dns_cache": "Ўключыць кэшаванне для карыстацкай канфігурацыі upstream-сервераў гэтага кліента",
|
||||||
"dns_cache_size": "Памер кэша DNS, у байтах"
|
"dns_cache_size": "Памер кэша DNS, у байтах"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
"filter": "Филтър",
|
"filter": "Филтър",
|
||||||
"query_log": "История на заявките",
|
"query_log": "История на заявките",
|
||||||
"compact": "Compact",
|
"compact": "Compact",
|
||||||
"nothing_found": "Нищо не е намерено",
|
|
||||||
"faq": "ЧЗВ",
|
"faq": "ЧЗВ",
|
||||||
"version": "версия",
|
"version": "версия",
|
||||||
"address": "Адрес",
|
"address": "Адрес",
|
||||||
@@ -66,12 +65,14 @@
|
|||||||
"stats_malware_phishing": "вируси/атаки",
|
"stats_malware_phishing": "вируси/атаки",
|
||||||
"stats_adult": "сайтове за възрастни",
|
"stats_adult": "сайтове за възрастни",
|
||||||
"stats_query_domain": "Най-отваряни страници",
|
"stats_query_domain": "Най-отваряни страници",
|
||||||
|
"for_last_24_hours": "за последните 24 часа",
|
||||||
"no_domains_found": "Няма намерени резултати",
|
"no_domains_found": "Няма намерени резултати",
|
||||||
"requests_count": "Сума на заявките",
|
"requests_count": "Сума на заявките",
|
||||||
"top_blocked_domains": "Най-блокирани страници",
|
"top_blocked_domains": "Най-блокирани страници",
|
||||||
"top_clients": "Най-активни IP адреси",
|
"top_clients": "Най-активни IP адреси",
|
||||||
"no_clients_found": "Нямa намерени адреси",
|
"no_clients_found": "Нямa намерени адреси",
|
||||||
"general_statistics": "Обща статисика",
|
"general_statistics": "Обща статисика",
|
||||||
|
"number_of_dns_query_24_hours": "Сума на DNS заявки за последните 24 часа",
|
||||||
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
|
"number_of_dns_query_blocked_24_hours": "Сума на блокирани DNS заявки от филтрите за реклама и местни",
|
||||||
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
|
"number_of_dns_query_blocked_24_hours_by_sec": "Сума на блокирани DNS заявки от AdGuard свързани със сигурността",
|
||||||
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
|
"number_of_dns_query_blocked_24_hours_adult": "Сума на блокирани сайтове за възрастни",
|
||||||
@@ -155,12 +156,9 @@
|
|||||||
"rule_added_to_custom_filtering_toast": "Добавено до местни правила за филтриране: {{rule}}",
|
"rule_added_to_custom_filtering_toast": "Добавено до местни правила за филтриране: {{rule}}",
|
||||||
"default": "По подразбиране",
|
"default": "По подразбиране",
|
||||||
"custom_ip": "Персонализиран IP",
|
"custom_ip": "Персонализиран IP",
|
||||||
"dnscrypt": "DNSCrypt",
|
|
||||||
"dns_over_https": "DNS-пред-HTTPS",
|
"dns_over_https": "DNS-пред-HTTPS",
|
||||||
"dns_over_quic": "DNS-over-QUIC",
|
"dns_over_quic": "DNS-over-QUIC",
|
||||||
"plain_dns": "Обикновен DNS",
|
"plain_dns": "Обикновен DNS",
|
||||||
"theme_light": "Светла тема",
|
|
||||||
"theme_dark": "Тъмна тема",
|
|
||||||
"source_label": "Източник",
|
"source_label": "Източник",
|
||||||
"found_in_known_domain_db": "Намерен в списъците с домейни.",
|
"found_in_known_domain_db": "Намерен в списъците с домейни.",
|
||||||
"category_label": "Категория",
|
"category_label": "Категория",
|
||||||
@@ -284,14 +282,6 @@
|
|||||||
"blocklist": "Черен списък",
|
"blocklist": "Черен списък",
|
||||||
"filter_category_general": "General",
|
"filter_category_general": "General",
|
||||||
"filter_category_security": "Сигурност",
|
"filter_category_security": "Сигурност",
|
||||||
"filter_category_other": "Друго",
|
|
||||||
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това.",
|
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това.",
|
||||||
"parental_control": "Родителски контрол",
|
"parental_control": "Родителски контрол"
|
||||||
"sunday_short": "Нд",
|
|
||||||
"monday_short": "Пон",
|
|
||||||
"tuesday_short": "Вт",
|
|
||||||
"wednesday_short": "Ср",
|
|
||||||
"thursday_short": "Чт",
|
|
||||||
"friday_short": "Пт",
|
|
||||||
"saturday_short": "Съб"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"parallel_requests": "Paralelní požadavky",
|
||||||
"load_balancing": "Optimalizace vytížení",
|
"load_balancing": "Optimalizace vytížení",
|
||||||
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas.<br/>AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
|
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
|
||||||
"bootstrap_dns": "Bootstrap DNS servery",
|
"bootstrap_dns": "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.",
|
"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",
|
"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": "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í.",
|
"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_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",
|
"no_servers_specified": "Nebyly specifikovány žádné servery",
|
||||||
"general_settings": "Obecná nastavení",
|
"general_settings": "Obecná nastavení",
|
||||||
"dns_settings": "Nastavení DNS",
|
"dns_settings": "Nastavení DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "TTL blokované odezvy",
|
"blocked_response_ttl": "TTL blokované odezvy",
|
||||||
"blocked_response_ttl_desc": "Určuje, na kolik sekund by měli klienti ukládat filtrovanou odezvu do mezipaměti",
|
"blocked_response_ttl_desc": "Určuje, na kolik sekund by měli klienti ukládat filtrovanou odezvu do mezipaměti",
|
||||||
"form_enter_blocked_response_ttl": "Zadejte TTL blokované odezvy (v sekundách)",
|
"form_enter_blocked_response_ttl": "Zadejte TTL blokované odezvy (v sekundách)",
|
||||||
"upstream_timeout": "Časový limit odchozího serveru",
|
|
||||||
"upstream_timeout_desc": "Určuje počet sekund čekání na odpověď od odchozího serveru",
|
|
||||||
"form_enter_upstream_timeout": "Zadejte dobu časového limitu odchozího serveru v sekundách",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS skrze HTTPS",
|
"dns_over_https": "DNS skrze HTTPS",
|
||||||
"dns_over_tls": "DNS skrze TLS",
|
"dns_over_tls": "DNS skrze TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Zakázat řešení IPv6 adres",
|
"disable_ipv6": "Zakázat řešení IPv6 adres",
|
||||||
"disable_ipv6_desc": "Odstranění všech dotazů DNS na adresy IPv6 (typ AAAA) a odstranění náznaků IPv6 z odpovědí HTTPS.",
|
"disable_ipv6_desc": "Odstranění všech dotazů DNS na adresy IPv6 (typ AAAA) a odstranění náznaků IPv6 z odpovědí HTTPS.",
|
||||||
"fastest_addr": "Nejrychlejší IP adresa",
|
"fastest_addr": "Nejrychlejší IP adresa",
|
||||||
"fastest_addr_desc": "Počká na odpovědi <b>všech</b> serverů DNS, změří rychlost připojení TCP pro každý server a vrátí IP adresu serveru s nejvyšší rychlostí připojení.<br/>Tento režim může výrazně zpomalit dotazy DNS, pokud jeden nebo více odchozích serverů neodpovídá. Ujistěte se, že vaše odchozí servery jsou stabilní a že časový limit odchozích serverů je nízký.",
|
"fastest_addr_desc": "Dotazovat všechny DNS servery a vrátit nejrychlejší IP adresu ze všech odpovědí. To zpomalí dotazy DNS, protože AdGuard Home musí čekat na odpovědi ze všech serverů DNS, ale celková konektivita se zlepší.",
|
||||||
"autofix_warning_text": "Pokud kliknete na „Opravit“, AdGuard Home nakonfiguruje váš systém tak, aby používal DNS server AdGuard Home.",
|
"autofix_warning_text": "Pokud kliknete na „Opravit“, AdGuard Home nakonfiguruje váš systém tak, aby používal DNS server AdGuard Home.",
|
||||||
"autofix_warning_list": "Jsou prováděny následující úlohy: <0>Deaktivace systému DNSStubListener</0> <0>Nastavení adresy serveru DNS na 127.0.0.1</0> <0>Nahrazení cíle symbolického odkazu z /etc/resolv.conf do /run/systemd/resolve/resolv.conf</0> <0>Zastavení služby DNSStubListener (znovu načtení služby systemd-resolved)</0>",
|
"autofix_warning_list": "Jsou prováděny následující úlohy: <0>Deaktivace systému DNSStubListener</0> <0>Nastavení adresy serveru DNS na 127.0.0.1</0> <0>Nahrazení cíle symbolického odkazu z /etc/resolv.conf do /run/systemd/resolve/resolv.conf</0> <0>Zastavení služby DNSStubListener (znovu načtení služby systemd-resolved)</0>",
|
||||||
"autofix_warning_result": "Výsledkem je, že všechny požadavky DNS z vašeho systému jsou ve výchozím nastavení zpracovány službou AdGuard Home.",
|
"autofix_warning_result": "Výsledkem je, že všechny požadavky DNS z vašeho systému jsou ve výchozím nastavení zpracovány službou AdGuard Home.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Důvod: {{reason}}",
|
"check_reason": "Důvod: {{reason}}",
|
||||||
"check_service": "Název služby: {{service}}",
|
"check_service": "Název služby: {{service}}",
|
||||||
"check_hostname": "Název hostitele nebo domény",
|
|
||||||
"check_client_id": "Identifikátor klienta (ClientID nebo IP adresa)",
|
|
||||||
"check_enter_client_id": "Zadejte identifikátor klienta",
|
|
||||||
"check_dns_record": "Vyberte typ DNS záznamu",
|
|
||||||
"service_name": "Název služby",
|
"service_name": "Název služby",
|
||||||
"check_not_found": "Nenalezeno ve Vašich seznamech filtrů",
|
"check_not_found": "Nenalezeno ve Vašich seznamech filtrů",
|
||||||
"client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?",
|
"client_confirm_block": "Opravdu chcete zablokovat klienta „{{ip}}“?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Zakázaný",
|
"blocklist": "Zakázaný",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Velikost mezipaměti",
|
"cache_size": "Velikost mezipaměti",
|
||||||
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech). Chcete-li ukládání do mezipaměti zakázat, nastavte 0.",
|
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech). Chcete-li ukládání do mezipaměti zakázat, ponechte prázdné.",
|
||||||
"cache_ttl_min_override": "Přepsat minimální hodnotu TTL",
|
"cache_ttl_min_override": "Přepsat minimální hodnotu TTL",
|
||||||
"cache_ttl_max_override": "Přepsat maximální hodnotu TTL",
|
"cache_ttl_max_override": "Přepsat maximální hodnotu TTL",
|
||||||
"enter_cache_size": "Zadejte velikost mezipaměti (v bajtech)",
|
"enter_cache_size": "Zadejte velikost mezipaměti (v bajtech)",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
|
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
|
||||||
"parallel_requests": "Parallelle forespørgsler",
|
"parallel_requests": "Parallelle forespørgsler",
|
||||||
"load_balancing": "Belastningsfordeling",
|
"load_balancing": "Belastningsfordeling",
|
||||||
"load_balancing_desc": "Forespørg én upstream-server ad gangen.<br/>AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
|
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
|
||||||
"bootstrap_dns": "Bootstrap DNS-servere",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Reserve DNS-servere",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Brug AdGuards forældrekontrolwebtjeneste",
|
"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.",
|
"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_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",
|
"no_servers_specified": "Ingen servere angivet",
|
||||||
"general_settings": "Generelle indstillinger",
|
"general_settings": "Generelle indstillinger",
|
||||||
"dns_settings": "DNS-indstillinger",
|
"dns_settings": "DNS-indstillinger",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Blokeret svar TTL",
|
"blocked_response_ttl": "Blokeret svar TTL",
|
||||||
"blocked_response_ttl_desc": "Angiver, i hvor mange sekunder klienterne skal cache-lagre et filtreret svar",
|
"blocked_response_ttl_desc": "Angiver, i hvor mange sekunder klienterne skal cache-lagre et filtreret svar",
|
||||||
"form_enter_blocked_response_ttl": "Angiv blokeringssvar TTL (sekunder)",
|
"form_enter_blocked_response_ttl": "Angiv blokeringssvar TTL (sekunder)",
|
||||||
"upstream_timeout": "Upstream-timeout",
|
|
||||||
"upstream_timeout_desc": "Angiver antallet af sekunder, der skal ventes på et svar fra upstream-serveren",
|
|
||||||
"form_enter_upstream_timeout": "Angiv varigheden af upstream-server timeout i sekunder",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Deaktivér IPv6-adresseopløsning",
|
"disable_ipv6": "Deaktivér IPv6-adresseopløsning",
|
||||||
"disable_ipv6_desc": "Drop alle DNS-forespørgsler for IPv6-adresser (type AAAA), og fjern IPv6-tips fra HTTPS-svar.",
|
"disable_ipv6_desc": "Drop alle DNS-forespørgsler for IPv6-adresser (type AAAA), og fjern IPv6-tips fra HTTPS-svar.",
|
||||||
"fastest_addr": "Hurtigste IP-adresse",
|
"fastest_addr": "Hurtigste IP-adresse",
|
||||||
"fastest_addr_desc": "Vent på svar fra <b>alle</b> DNS-servere, mål TCP-forbindelseshastigheden for hver server, og returner IP-adressen på serveren med den hurtigste forbindelseshastighed.<br/>Denne tilstand kan sinke DNS-forespørgsler, betydeligt hvis en eller flere upstream-servere ikke svarer. Sørg for, at upstream-serverene er stabile, og at upstream-timeouten er lav.",
|
"fastest_addr_desc": "Forespørger alle DNS-servere og returner den hurtigste IP-adresse blandt alle svar. Dette vil gøre DNS-forespørgslerne langsommere grundet afventning af svar fra alle DNS-servere, men forbedrer samlet set forbindelsen.",
|
||||||
"autofix_warning_text": "Klikker du på \"Reparér\", opsætter AdGuard Home dit system til brug med AdGuard Home DNS-server.",
|
"autofix_warning_text": "Klikker du på \"Reparér\", opsætter AdGuard Home dit system til brug med AdGuard Home DNS-server.",
|
||||||
"autofix_warning_list": "Den vil udføre disse opgaver: <0>Deaktivere system DNSStubListener</0> <0>Opsætte DNS-serveradressen til 127.0.0.1</0> <0>Erstatte symbolsk linkmål for /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (genindlæs systemd-opløst tjeneste)</0>",
|
"autofix_warning_list": "Den vil udføre disse opgaver: <0>Deaktivere system DNSStubListener</0> <0>Opsætte DNS-serveradressen til 127.0.0.1</0> <0>Erstatte symbolsk linkmål for /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (genindlæs systemd-opløst tjeneste)</0>",
|
||||||
"autofix_warning_result": "Det betyder, at alle DNS-forespørgsler fra dit system som standard behandles af AdGuard Home.",
|
"autofix_warning_result": "Det betyder, at alle DNS-forespørgsler fra dit system som standard behandles af AdGuard Home.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Årsag: {{reason}}",
|
"check_reason": "Årsag: {{reason}}",
|
||||||
"check_service": "Tjenestenavn: {{service}}",
|
"check_service": "Tjenestenavn: {{service}}",
|
||||||
"check_hostname": "Værts- eller domænenavn",
|
|
||||||
"check_client_id": "Klientidentifikator (ClientID eller IP-adresse)",
|
|
||||||
"check_enter_client_id": "Angiv klientidentifikator",
|
|
||||||
"check_dns_record": "Vælg DNS-posttype",
|
|
||||||
"service_name": "Tjenestenavn",
|
"service_name": "Tjenestenavn",
|
||||||
"check_not_found": "Ikke fundet i dine filterlister",
|
"check_not_found": "Ikke fundet i dine filterlister",
|
||||||
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",
|
"client_confirm_block": "Sikker på, at du vil blokere klienten \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Sortliste",
|
"blocklist": "Sortliste",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Cache-størrelse",
|
"cache_size": "Cache-størrelse",
|
||||||
"cache_size_desc": "DNS cache-størrelse (i bytes). Sæt til 0 for at deaktivere cache.",
|
"cache_size_desc": "DNS cache-størrelse (i bytes). Lad stå tomt for at deaktivere cache.",
|
||||||
"cache_ttl_min_override": "Tilsidesæt minimum TTL",
|
"cache_ttl_min_override": "Tilsidesæt minimum TTL",
|
||||||
"cache_ttl_max_override": "Tilsidesæt maksimal TTL",
|
"cache_ttl_max_override": "Tilsidesæt maksimal TTL",
|
||||||
"enter_cache_size": "Angiv cache-størrelse (bytes)",
|
"enter_cache_size": "Angiv cache-størrelse (bytes)",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
|
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
|
||||||
"parallel_requests": "Paralleles Abfragen",
|
"parallel_requests": "Paralleles Abfragen",
|
||||||
"load_balancing": "Lastverteilung",
|
"load_balancing": "Lastverteilung",
|
||||||
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt.<br/> AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
|
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
|
||||||
"bootstrap_dns": "Bootstrap-DNS-Server",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Fallback-DNS-Server",
|
||||||
@@ -97,9 +97,9 @@
|
|||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
"filters": "Filter",
|
"filters": "Filter",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"query_log": "Abfrageprotokoll",
|
"query_log": "Anfragenprotokoll",
|
||||||
"compact": "Kompakt",
|
"compact": "Kompakt",
|
||||||
"nothing_found": "Nichts gefunden",
|
"nothing_found": "Nichts gefunden\n",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"address": "Adresse",
|
"address": "Adresse",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "AdGuard Webservice für Kindersicherung verwenden",
|
"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.",
|
"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_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",
|
"no_servers_specified": "Keine Server festgelegt",
|
||||||
"general_settings": "Allgemeine Einstellungen",
|
"general_settings": "Allgemeine Einstellungen",
|
||||||
"dns_settings": "DNS-Einstellungen",
|
"dns_settings": "DNS-Einstellungen",
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
"cancel_btn": "Abbrechen",
|
"cancel_btn": "Abbrechen",
|
||||||
"enter_name_hint": "Name eingeben",
|
"enter_name_hint": "Name eingeben",
|
||||||
"enter_url_or_path_hint": "URL oder absoluten Pfad der Liste eingeben",
|
"enter_url_or_path_hint": "URL oder absoluten Pfad der Liste eingeben",
|
||||||
"check_updates_btn": "Nach Updates suchen",
|
"check_updates_btn": "Nach Aktualisierungen suchen",
|
||||||
"new_blocklist": "Neue Sperrliste",
|
"new_blocklist": "Neue Sperrliste",
|
||||||
"new_allowlist": "Neue Positivliste",
|
"new_allowlist": "Neue Positivliste",
|
||||||
"edit_blocklist": "Sperrliste bearbeiten",
|
"edit_blocklist": "Sperrliste bearbeiten",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Gültigkeitsdauer der blockierten Antwort",
|
"blocked_response_ttl": "Gültigkeitsdauer der blockierten Antwort",
|
||||||
"blocked_response_ttl_desc": "Gibt an, wie viele Sekunden lang die Clients eine gefilterte Antwort zwischenspeichern sollen",
|
"blocked_response_ttl_desc": "Gibt an, wie viele Sekunden lang die Clients eine gefilterte Antwort zwischenspeichern sollen",
|
||||||
"form_enter_blocked_response_ttl": "Geben Sie die Gültigkeitsdauer für blockierte Antworten ein (in Sekunden)",
|
"form_enter_blocked_response_ttl": "Geben Sie die Gültigkeitsdauer für blockierte Antworten ein (in Sekunden)",
|
||||||
"upstream_timeout": "Upstream-Timeout",
|
|
||||||
"upstream_timeout_desc": "Gibt die Anzahl der Sekunden an, die auf eine Antwort des Upstream-Servers gewartet werden soll",
|
|
||||||
"form_enter_upstream_timeout": "Geben Sie die Timeout-Dauer des Upstream-Servers in Sekunden ein",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -566,7 +563,7 @@
|
|||||||
"ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)",
|
"ignore_domains": "Ignorierte Domains (durch Zeilenumbruch getrennt)",
|
||||||
"ignore_domains_title": "Ignorierte Domains",
|
"ignore_domains_title": "Ignorierte Domains",
|
||||||
"ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen",
|
"ignore_domains_desc_stats": "Abfragen, die diesen Regeln entsprechen, werden nicht in die Statistik aufgenommen",
|
||||||
"ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Abfrageprotokoll aufgenommen",
|
"ignore_domains_desc_query": "Abfragen, die diesen Regeln entsprechen, werden nicht in das Anfragenprotokoll aufgenommen",
|
||||||
"interval_hours": "{{count}} Stunde",
|
"interval_hours": "{{count}} Stunde",
|
||||||
"interval_hours_plural": "{{count}} Stunden",
|
"interval_hours_plural": "{{count}} Stunden",
|
||||||
"filters_configuration": "Filterkonfiguration",
|
"filters_configuration": "Filterkonfiguration",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "IPv6 deaktivieren",
|
"disable_ipv6": "IPv6 deaktivieren",
|
||||||
"disable_ipv6_desc": "Alle DNS-Anfragen für IPv6-Adressen (Typ AAAA) verwerfen und IPv6-Hinweise aus HTTPS-Antworten entfernen.",
|
"disable_ipv6_desc": "Alle DNS-Anfragen für IPv6-Adressen (Typ AAAA) verwerfen und IPv6-Hinweise aus HTTPS-Antworten entfernen.",
|
||||||
"fastest_addr": "Schnellste IP-Adresse",
|
"fastest_addr": "Schnellste IP-Adresse",
|
||||||
"fastest_addr_desc": "Auf Antworten von <b>allen</b> DNS-Servern warten, die TCP-Verbindungsgeschwindigkeit für jeden Server messen und die IP-Adresse des Servers mit der schnellsten Verbindungsgeschwindigkeit zurückgeben.<br/>Dieser Modus kann DNS-Anfragen erheblich verlangsamen, wenn ein oder mehrere Server nicht antworten. Stellen Sie sicher, dass Ihre Server stabil laufen und das Upstream-Timeout niedrig ist.",
|
"fastest_addr_desc": "Fragen Sie alle DNS-Server ab und geben Sie die schnellste IP-Adresse unter allen Antworten zurück. Dies verlangsamt DNS-Abfragen, da AdGuard Home auf Antworten von allen DNS-Servern warten muss, verbessert jedoch die Gesamtkonnektivität.",
|
||||||
"autofix_warning_text": "Wenn Sie auf „Beheben“ klicken, konfiguriert AdGuardHome Ihr System für die Verwendung des AdGuardHome-DNS-Servers.",
|
"autofix_warning_text": "Wenn Sie auf „Beheben“ klicken, konfiguriert AdGuardHome Ihr System für die Verwendung des AdGuardHome-DNS-Servers.",
|
||||||
"autofix_warning_list": "Es werden folgende Aufgaben ausgeführt: <0>Deaktivieren des DNSStubListener-Systems</0> <0>Festlegen der DNS-Server-Adresse auf 127.0.0.1</0> <0>Ersetzen des symbolischen Linkziels von /etc/resolv.conf auf /run/systemd/resolve/resolv.conf</0> <0>Anhalten des DNSStubListener (systemseitig aufgelöster Dienst wird nachladen)</0>",
|
"autofix_warning_list": "Es werden folgende Aufgaben ausgeführt: <0>Deaktivieren des DNSStubListener-Systems</0> <0>Festlegen der DNS-Server-Adresse auf 127.0.0.1</0> <0>Ersetzen des symbolischen Linkziels von /etc/resolv.conf auf /run/systemd/resolve/resolv.conf</0> <0>Anhalten des DNSStubListener (systemseitig aufgelöster Dienst wird nachladen)</0>",
|
||||||
"autofix_warning_result": "Als Folge daraus werden alle DNS-Anforderungen von Ihrem System standardmäßig von AdGuardHome verarbeitet.",
|
"autofix_warning_result": "Als Folge daraus werden alle DNS-Anforderungen von Ihrem System standardmäßig von AdGuardHome verarbeitet.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Grund: {{reason}}",
|
"check_reason": "Grund: {{reason}}",
|
||||||
"check_service": "Dienstname: {{service}}",
|
"check_service": "Dienstname: {{service}}",
|
||||||
"check_hostname": "Hostname oder Domainname",
|
|
||||||
"check_client_id": "Client-Kennung (ClientID oder IP-Adresse)",
|
|
||||||
"check_enter_client_id": "Client-Kennung eingeben",
|
|
||||||
"check_dns_record": "DNS-Datensatztyp auswählen",
|
|
||||||
"service_name": "Name des Dienstes",
|
"service_name": "Name des Dienstes",
|
||||||
"check_not_found": "Nicht in Ihren Filterlisten enthalten",
|
"check_not_found": "Nicht in Ihren Filterlisten enthalten",
|
||||||
"client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?",
|
"client_confirm_block": "Möchten Sie den Client „{{ip}}“ wirklich sperren?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Sperrliste",
|
"blocklist": "Sperrliste",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Größe des Cache",
|
"cache_size": "Größe des Cache",
|
||||||
"cache_size_desc": "Größe des DNS-Cache (in Bytes). Um das Caching zu deaktivieren, setzen Sie den Wert auf 0.",
|
"cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes)",
|
||||||
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
|
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
|
||||||
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
|
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
|
||||||
"enter_cache_size": "Größe des Cache (Bytes) eingeben",
|
"enter_cache_size": "Größe des Cache (Bytes) eingeben",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
|
"upstream_parallel": "Use parallel queries to speed up resolving by querying all upstream servers simultaneously.",
|
||||||
"parallel_requests": "Parallel requests",
|
"parallel_requests": "Parallel requests",
|
||||||
"load_balancing": "Load-balancing",
|
"load_balancing": "Load-balancing",
|
||||||
"load_balancing_desc": "Query one upstream server at a time.<br/>AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
|
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses a weighted random algorithm to select servers with the lowest number of failed lookups and the lowest average lookup time.",
|
||||||
"bootstrap_dns": "Bootstrap DNS servers",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Fallback DNS servers",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Blocked response TTL",
|
"blocked_response_ttl": "Blocked response TTL",
|
||||||
"blocked_response_ttl_desc": "Specifies for how many seconds the clients should cache a filtered response",
|
"blocked_response_ttl_desc": "Specifies for how many seconds the clients should cache a filtered response",
|
||||||
"form_enter_blocked_response_ttl": "Enter blocked response TTL (seconds)",
|
"form_enter_blocked_response_ttl": "Enter blocked response TTL (seconds)",
|
||||||
"upstream_timeout": "Upstream timeout",
|
|
||||||
"upstream_timeout_desc": "Specifies the number of seconds to wait for a response from the upstream server",
|
|
||||||
"form_enter_upstream_timeout": "Enter the upstream server timeout duration in seconds",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Disable resolving of IPv6 addresses",
|
"disable_ipv6": "Disable resolving of IPv6 addresses",
|
||||||
"disable_ipv6_desc": "Drop all DNS queries for IPv6 addresses (type AAAA) and remove IPv6 hints from HTTPS responses.",
|
"disable_ipv6_desc": "Drop all DNS queries for IPv6 addresses (type AAAA) and remove IPv6 hints from HTTPS responses.",
|
||||||
"fastest_addr": "Fastest IP address",
|
"fastest_addr": "Fastest IP address",
|
||||||
"fastest_addr_desc": "Wait for responses from <b>all</b> DNS servers, measure the TCP connection speed for each server, and return the IP address of the server with the fastest connection speed.<br/>This mode can significantly slow down DNS queries, if one or more upstream servers are not responding. Make sure that your upstream servers are stable and your upstream timeout is low.",
|
"fastest_addr_desc": "Query all DNS servers and return the fastest IP address among all responses. This slows down DNS queries as AdGuard Home has to wait for responses from all DNS servers, but improves the overall connectivity.",
|
||||||
"autofix_warning_text": "If you click \"Fix\", AdGuard Home will configure your system to use AdGuard Home DNS server.",
|
"autofix_warning_text": "If you click \"Fix\", AdGuard Home will configure your system to use AdGuard Home DNS server.",
|
||||||
"autofix_warning_list": "It will perform these tasks: <0>Deactivate system DNSStubListener</0> <0>Set DNS server address to 127.0.0.1</0> <0>Replace symbolic link target of /etc/resolv.conf with /run/systemd/resolve/resolv.conf</0> <0>Stop DNSStubListener (reload systemd-resolved service)</0>",
|
"autofix_warning_list": "It will perform these tasks: <0>Deactivate system DNSStubListener</0> <0>Set DNS server address to 127.0.0.1</0> <0>Replace symbolic link target of /etc/resolv.conf with /run/systemd/resolve/resolv.conf</0> <0>Stop DNSStubListener (reload systemd-resolved service)</0>",
|
||||||
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuard Home by default.",
|
"autofix_warning_result": "As a result all DNS requests from your system will be processed by AdGuard Home by default.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Reason: {{reason}}",
|
"check_reason": "Reason: {{reason}}",
|
||||||
"check_service": "Service name: {{service}}",
|
"check_service": "Service name: {{service}}",
|
||||||
"check_hostname": "Hostname or domain name",
|
|
||||||
"check_client_id": "Client identifier (ClientID or IP address)",
|
|
||||||
"check_enter_client_id": "Enter client identifier",
|
|
||||||
"check_dns_record": "Select DNS record type",
|
|
||||||
"service_name": "Service name",
|
"service_name": "Service name",
|
||||||
"check_not_found": "Not found in your filter lists",
|
"check_not_found": "Not found in your filter lists",
|
||||||
"client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?",
|
"client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Blocklist",
|
"blocklist": "Blocklist",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Cache size",
|
"cache_size": "Cache size",
|
||||||
"cache_size_desc": "DNS cache size (in bytes). To disable caching, set to 0.",
|
"cache_size_desc": "DNS cache size (in bytes). To disable caching, leave empty.",
|
||||||
"cache_ttl_min_override": "Override minimum TTL",
|
"cache_ttl_min_override": "Override minimum TTL",
|
||||||
"cache_ttl_max_override": "Override maximum TTL",
|
"cache_ttl_max_override": "Override maximum TTL",
|
||||||
"enter_cache_size": "Enter cache size (bytes)",
|
"enter_cache_size": "Enter cache size (bytes)",
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"client_settings": "Configuración de clientes",
|
"client_settings": "Configuración de clientes",
|
||||||
"example_upstream_reserved": "un proveedor DNS <0>para un dominio específico</0>.",
|
"example_upstream_reserved": "un DNS de subida <0>para un dominio específico</0>.",
|
||||||
"example_multiple_upstreams_reserved": "múltiples proveedores DNS <0>para dominios específicos</0>.",
|
"example_multiple_upstreams_reserved": "múltiples upstreams <0>para dominios específicos</0>;",
|
||||||
"example_upstream_comment": "un comentario.",
|
"example_upstream_comment": "un comentario.",
|
||||||
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los proveedores DNS.",
|
"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",
|
"parallel_requests": "Consultas paralelas",
|
||||||
"load_balancing": "Balanceo de carga",
|
"load_balancing": "Balanceo de carga",
|
||||||
"load_balancing_desc": "Consulta un proveedor DNS a la vez.<br/>AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo promedio de búsqueda.",
|
"load_balancing_desc": "Consulta un servidor upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
|
||||||
"bootstrap_dns": "Servidores DNS de arranque",
|
"bootstrap_dns": "Servidores DNS de arranque",
|
||||||
"bootstrap_dns_desc": "Direcciones IP de los servidores DNS utilizados para resolver las direcciones IP de los resolutores DoH/DoT que especifiques como proveedores DNS. No se permiten comentarios.",
|
"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",
|
"fallback_dns_title": "Servidores DNS alternativos",
|
||||||
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los proveedores DNS no responden. La sintaxis es la misma que en el campo de los principales proveedores DNS anterior.",
|
"fallback_dns_desc": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
|
||||||
"fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea",
|
"fallback_dns_placeholder": "Ingresa un servidor DNS alternativo por línea",
|
||||||
"local_ptr_title": "Servidores DNS inversos y privados",
|
"local_ptr_title": "Servidores DNS inversos y privados",
|
||||||
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
|
"local_ptr_desc": "Los servidores DNS que AdGuard Home utiliza para consultas PTR, SOA y NS privadas. La petición se considera privada si solicita un dominio ARPA que contiene una subred dentro de rangos IP privados, por ejemplo \"192.168.12.34\", y procede de un cliente con dirección privada. Si no se configura, AdGuard Home utiliza las direcciones de los resolvedores DNS predeterminados de tu sistema operativo, excepto las direcciones del propio AdGuard Home.",
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
"local_ptr_no_default_resolver": "AdGuard Home no pudo determinar los resolutores DNS inversos y privados adecuados para este sistema.",
|
"local_ptr_no_default_resolver": "AdGuard Home no pudo determinar los resolutores DNS inversos y privados adecuados para este sistema.",
|
||||||
"local_ptr_placeholder": "Ingresa una dirección IP por línea",
|
"local_ptr_placeholder": "Ingresa una dirección IP por línea",
|
||||||
"resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes",
|
"resolve_clients_title": "Habilitar la resolución inversa de las direcciones IP de clientes",
|
||||||
"resolve_clients_desc": "Resuelve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, proveedores DNS para clientes con direcciones IP públicas).",
|
"resolve_clients_desc": "Resolve de manera inversa las direcciones IP de los clientes a sus nombres de hosts enviando consultas PTR a los resolutores correspondientes (servidores DNS privados para clientes locales, servidores DNS de subida para clientes con direcciones IP públicas).",
|
||||||
"use_private_ptr_resolvers_title": "Usar resolutores DNS inversos y privados",
|
"use_private_ptr_resolvers_title": "Usar resolutores DNS inversos y privados",
|
||||||
"use_private_ptr_resolvers_desc": "Resuelve peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones IP privadas a través de proveedores DNS privados, DHCP, /etc/hosts, etc. Si se deshabilita, AdGuard Home responderá a todas estas peticiones con NXDOMAIN.",
|
"use_private_ptr_resolvers_desc": "Resolver las peticiones PTR, SOA y NS para dominios ARPA que contienen direcciones privadas utilizando servidores upstream privados, DHCP, /etc/hosts, etc. Si se desactiva, AdGuard Home responde a todas estas consultas con NXDOMAIN.",
|
||||||
"check_dhcp_servers": "Comprobar si hay servidores DHCP",
|
"check_dhcp_servers": "Comprobar si hay servidores DHCP",
|
||||||
"save_config": "Guardar configuración",
|
"save_config": "Guardar configuración",
|
||||||
"enabled_dhcp": "Servidor DHCP habilitado",
|
"enabled_dhcp": "Servidor DHCP habilitado",
|
||||||
@@ -132,8 +132,8 @@
|
|||||||
"top_clients": "Clientes más frecuentes",
|
"top_clients": "Clientes más frecuentes",
|
||||||
"no_clients_found": "No se han encontrado clientes",
|
"no_clients_found": "No se han encontrado clientes",
|
||||||
"general_statistics": "Estadísticas generales",
|
"general_statistics": "Estadísticas generales",
|
||||||
"top_upstreams": "Proveedores DNS más frecuentes",
|
"top_upstreams": "DNS de subida más frecuentes",
|
||||||
"no_upstreams_data_found": "No se han encontrado datos de proveedores DNS",
|
"no_upstreams_data_found": "No se han encontrado datos de DNS de subida",
|
||||||
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
|
"number_of_dns_query_days": "Número de consultas DNS procesadas durante el último {{count}} día",
|
||||||
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
|
"number_of_dns_query_days_plural": "Número de consultas DNS procesadas durante los últimos {{count}} días",
|
||||||
"number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora",
|
"number_of_dns_query_hours": "Número de consultas DNS procesadas durante la última {{count}} hora",
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
"enforced_save_search": "Búsquedas seguras forzadas",
|
"enforced_save_search": "Búsquedas seguras forzadas",
|
||||||
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
|
"number_of_dns_query_to_safe_search": "Número de peticiones DNS a los motores de búsqueda para los que se aplicó la búsqueda segura forzada",
|
||||||
"average_processing_time": "Tiempo promedio de procesamiento",
|
"average_processing_time": "Tiempo promedio de procesamiento",
|
||||||
"average_upstream_response_time": "Tiempo promedio de respuesta del proveedor DNS",
|
"average_upstream_response_time": "Tiempo promedio de respuesta upstream",
|
||||||
"response_time": "Tiempo de respuesta",
|
"response_time": "Tiempo de respuesta",
|
||||||
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
|
"average_processing_time_hint": "Tiempo promedio en milisegundos al procesar una petición DNS",
|
||||||
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
|
"block_domain_use_filters_and_hosts": "Bloquear dominios usando filtros y archivos hosts",
|
||||||
@@ -154,10 +154,10 @@
|
|||||||
"use_adguard_parental": "Usar el control parental de AdGuard",
|
"use_adguard_parental": "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.",
|
"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_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",
|
"no_servers_specified": "No hay servidores especificados",
|
||||||
"general_settings": "Configuración general",
|
"general_settings": "Configuración general",
|
||||||
"dns_settings": "Configuración DNS",
|
"dns_settings": "Configuración del DNS",
|
||||||
"dns_blocklists": "Listas de bloqueo DNS",
|
"dns_blocklists": "Listas de bloqueo DNS",
|
||||||
"dns_allowlists": "Listas de permitido DNS",
|
"dns_allowlists": "Listas de permitido DNS",
|
||||||
"dns_blocklists_desc": "AdGuard Home bloqueará los dominios que coincidan con las listas de bloqueo.",
|
"dns_blocklists_desc": "AdGuard Home bloqueará los dominios que coincidan con las listas de bloqueo.",
|
||||||
@@ -165,12 +165,12 @@
|
|||||||
"custom_filtering_rules": "Reglas de filtrado personalizado",
|
"custom_filtering_rules": "Reglas de filtrado personalizado",
|
||||||
"encryption_settings": "Configuración de cifrado",
|
"encryption_settings": "Configuración de cifrado",
|
||||||
"dhcp_settings": "Configuración DHCP",
|
"dhcp_settings": "Configuración DHCP",
|
||||||
"upstream_dns": "Proveedores DNS",
|
"upstream_dns": "Servidores DNS de subida",
|
||||||
"upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los proveedores DNS.",
|
"upstream_dns_help": "Ingresa una dirección de servidor por línea. <a>Más información</a> sobre la configuración de los servidores DNS de subida.",
|
||||||
"upstream_dns_configured_in_file": "Configurado en {{path}}",
|
"upstream_dns_configured_in_file": "Configurado en {{path}}",
|
||||||
"test_upstream_btn": "Probar proveedores DNS",
|
"test_upstream_btn": "Probar DNS de subida",
|
||||||
"upstreams": "Proveedores DNS",
|
"upstreams": "DNS de subida",
|
||||||
"upstream": "Proveedor DNS",
|
"upstream": "DNS de subida",
|
||||||
"apply_btn": "Aplicar",
|
"apply_btn": "Aplicar",
|
||||||
"disabled_filtering_toast": "Filtrado deshabilitado",
|
"disabled_filtering_toast": "Filtrado deshabilitado",
|
||||||
"enabled_filtering_toast": "Filtrado habilitado",
|
"enabled_filtering_toast": "Filtrado habilitado",
|
||||||
@@ -233,11 +233,11 @@
|
|||||||
"example_upstream_tcp_port": "DNS regular (mediante TCP, con puerto).",
|
"example_upstream_tcp_port": "DNS regular (mediante TCP, con puerto).",
|
||||||
"example_upstream_tcp_hostname": "DNS regular (mediante TCP, nombre del host).",
|
"example_upstream_tcp_hostname": "DNS regular (mediante TCP, nombre del host).",
|
||||||
"all_lists_up_to_date_toast": "Todas las listas ya están actualizadas",
|
"all_lists_up_to_date_toast": "Todas las listas ya están actualizadas",
|
||||||
"updated_upstream_dns_toast": "Proveedores DNS guardados correctamente",
|
"updated_upstream_dns_toast": "Servidores DNS de subida guardados correctamente",
|
||||||
"dns_test_ok_toast": "Los servidores DNS especificados funcionan correctamente",
|
"dns_test_ok_toast": "Los servidores DNS especificados funcionan correctamente",
|
||||||
"dns_test_not_ok_toast": "Servidor \"{{key}}\": no se puede utilizar, por favor revisa si lo has escrito correctamente",
|
"dns_test_not_ok_toast": "Servidor \"{{key}}\": no se puede utilizar, por favor revisa si lo has escrito correctamente",
|
||||||
"dns_test_parsing_error_toast": "No se pudo utilizar la sección {{section}}: línea {{line}}:, verifica si la escribiste correctamente",
|
"dns_test_parsing_error_toast": "No se pudo utilizar la sección {{section}}: línea {{line}}:, verifica si la escribiste correctamente",
|
||||||
"dns_test_warning_toast": "Proveedor DNS \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente",
|
"dns_test_warning_toast": "DNS de subida \"{{key}}\" no responde a las peticiones de prueba y es posible que no funcione correctamente",
|
||||||
"unblock": "Desbloquear",
|
"unblock": "Desbloquear",
|
||||||
"block": "Bloquear",
|
"block": "Bloquear",
|
||||||
"disallow_this_client": "No permitir a este cliente",
|
"disallow_this_client": "No permitir a este cliente",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Respuesta TTL bloqueada",
|
"blocked_response_ttl": "Respuesta TTL bloqueada",
|
||||||
"blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
|
"blocked_response_ttl_desc": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
|
||||||
"form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)",
|
"form_enter_blocked_response_ttl": "Ingresa el TTL de respuesta bloqueada (segundos)",
|
||||||
"upstream_timeout": "Tiempo de espera del proveedor DNS",
|
|
||||||
"upstream_timeout_desc": "Especifica el número de segundos que se debe esperar para recibir una respuesta del proveedor DNS",
|
|
||||||
"form_enter_upstream_timeout": "Ingresa la duración de tiempo de espera del proveedor DNS en segundos",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS mediante HTTPS",
|
"dns_over_https": "DNS mediante HTTPS",
|
||||||
"dns_over_tls": "DNS mediante TLS",
|
"dns_over_tls": "DNS mediante TLS",
|
||||||
@@ -311,7 +308,7 @@
|
|||||||
"form_enter_rate_limit": "Ingresa el límite de cantidad",
|
"form_enter_rate_limit": "Ingresa el límite de cantidad",
|
||||||
"rate_limit": "Límite de cantidad",
|
"rate_limit": "Límite de cantidad",
|
||||||
"edns_enable": "Habilitar subred de cliente EDNS",
|
"edns_enable": "Habilitar subred de cliente EDNS",
|
||||||
"edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del proveedor DNS y registra los valores enviados por los clientes en el registro de consultas.",
|
"edns_cs_desc": "Añade la opción subred de cliente EDNS (ECS) a las peticiones del DNS de subida y registra los valores enviados por los clientes en el registro de consultas.",
|
||||||
"edns_use_custom_ip": "Usar IP personalizada para EDNS",
|
"edns_use_custom_ip": "Usar IP personalizada para EDNS",
|
||||||
"edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS",
|
"edns_use_custom_ip_desc": "Permitir el uso de IP personalizadas para EDNS",
|
||||||
"rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.",
|
"rate_limit_desc": "Número de peticiones por segundo permitidas por cliente. Establecerlo en 0 significa que no hay límite.",
|
||||||
@@ -335,7 +332,7 @@
|
|||||||
"theme_auto": "Auto",
|
"theme_auto": "Auto",
|
||||||
"theme_light": "Claro",
|
"theme_light": "Claro",
|
||||||
"theme_dark": "Oscuro",
|
"theme_dark": "Oscuro",
|
||||||
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración DNS</0>.",
|
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración del DNS</0>.",
|
||||||
"tracker_source": "Fuente del rastreador",
|
"tracker_source": "Fuente del rastreador",
|
||||||
"source_label": "Fuente",
|
"source_label": "Fuente",
|
||||||
"found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.",
|
"found_in_known_domain_db": "Encontrado en la base de datos de dominios conocidos.",
|
||||||
@@ -596,12 +593,12 @@
|
|||||||
"example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.",
|
"example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.",
|
||||||
"rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA",
|
"rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA",
|
||||||
"rewrite_domain_name": "Nombre de dominio: añade un registro CNAME",
|
"rewrite_domain_name": "Nombre de dominio: añade un registro CNAME",
|
||||||
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del proveedor DNS",
|
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del DNS de subida",
|
||||||
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del proveedor DNS",
|
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del DNS de subida",
|
||||||
"disable_ipv6": "Deshabilitar resolución de direcciones IPv6",
|
"disable_ipv6": "Deshabilitar resolución de direcciones IPv6",
|
||||||
"disable_ipv6_desc": "Descarta todas las consultas de DNS para direcciones IPv6 (tipo AAAA) y elimina las sugerencias de IPv6 de las respuestas HTTPS.",
|
"disable_ipv6_desc": "Descarta todas las consultas de DNS para direcciones IPv6 (tipo AAAA) y elimina las sugerencias de IPv6 de las respuestas HTTPS.",
|
||||||
"fastest_addr": "Dirección IP más rápida",
|
"fastest_addr": "Dirección IP más rápida",
|
||||||
"fastest_addr_desc": "Espera respuestas de <b>todos</b> los servidores DNS, mide la velocidad de conexión TCP de cada servidor y devuelve la dirección IP del servidor con la velocidad de conexión más rápida.<br/>Este modo puede ralentizar significativamente las consultas DNS, si uno o más proveedores DNS no responden. Asegúrate de que tus proveedores DNS sean estables y de que el tiempo de espera tu proveedor DNS sea bajo.",
|
"fastest_addr_desc": "Consulta todos los servidores DNS y devuelve la dirección IP más rápida de todas las respuestas. Esto ralentiza las consultas DNS ya que AdGuard Home tiene que esperar las respuestas de todos los servidores DNS, pero mejora la conectividad general.",
|
||||||
"autofix_warning_text": "Si haces clic en \"Corregir\", AdGuard Home configurará tu sistema para utilizar el servidor DNS de AdGuard Home.",
|
"autofix_warning_text": "Si haces clic en \"Corregir\", AdGuard Home configurará tu sistema para utilizar el servidor DNS de AdGuard Home.",
|
||||||
"autofix_warning_list": "Realizará estas tareas: <0>Deshabilitar el sistema DNSStubListener</0> <0>Establecer la dirección del servidor DNS en 127.0.0.1</0> <0>Reemplazar el destino del enlace simbólico de /etc/resolv.conf por /run/systemd/resolve/resolv.conf</0> <0>Detener DNSStubListener (recargar el servicio systemd-resolved)</0>",
|
"autofix_warning_list": "Realizará estas tareas: <0>Deshabilitar el sistema DNSStubListener</0> <0>Establecer la dirección del servidor DNS en 127.0.0.1</0> <0>Reemplazar el destino del enlace simbólico de /etc/resolv.conf por /run/systemd/resolve/resolv.conf</0> <0>Detener DNSStubListener (recargar el servicio systemd-resolved)</0>",
|
||||||
"autofix_warning_result": "Como resultado, todas las peticiones DNS de tu sistema serán procesadas por AdGuard Home de manera predeterminada.",
|
"autofix_warning_result": "Como resultado, todas las peticiones DNS de tu sistema serán procesadas por AdGuard Home de manera predeterminada.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Razón: {{reason}}",
|
"check_reason": "Razón: {{reason}}",
|
||||||
"check_service": "Nombre del servicio: {{service}}",
|
"check_service": "Nombre del servicio: {{service}}",
|
||||||
"check_hostname": "Nombre de host o nombre de dominio",
|
|
||||||
"check_client_id": "Identificador del cliente (ClientID o dirección IP)",
|
|
||||||
"check_enter_client_id": "Ingresa el identificador del cliente",
|
|
||||||
"check_dns_record": "Selecciona el tipo de registro DNS",
|
|
||||||
"service_name": "Nombre del servicio",
|
"service_name": "Nombre del servicio",
|
||||||
"check_not_found": "No se ha encontrado en tus listas de filtros",
|
"check_not_found": "No se ha encontrado en tus listas de filtros",
|
||||||
"client_confirm_block": "¿Estás seguro de que deseas bloquear al cliente \"{{ip}}\"?",
|
"client_confirm_block": "¿Estás seguro de que deseas bloquear al cliente \"{{ip}}\"?",
|
||||||
@@ -656,13 +649,13 @@
|
|||||||
"blocklist": "Lista de bloqueo",
|
"blocklist": "Lista de bloqueo",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Tamaño de la caché",
|
"cache_size": "Tamaño de la caché",
|
||||||
"cache_size_desc": "Tamaño de la caché DNS (en bytes). Para desactivar el almacenamiento en caché, configúralo en 0.",
|
"cache_size_desc": "Tamaño de la caché DNS (en bytes). Para deshabilitar el almacenamiento en caché, déjalo vacío.",
|
||||||
"cache_ttl_min_override": "Anular TTL mínimo",
|
"cache_ttl_min_override": "Anular TTL mínimo",
|
||||||
"cache_ttl_max_override": "Anular TTL máximo",
|
"cache_ttl_max_override": "Anular TTL máximo",
|
||||||
"enter_cache_size": "Ingresa el tamaño de la caché (bytes)",
|
"enter_cache_size": "Ingresa el tamaño de la caché (bytes)",
|
||||||
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo (en segundos)",
|
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo (en segundos)",
|
||||||
"enter_cache_ttl_max_override": "Ingresa el TTL máximo (en segundos)",
|
"enter_cache_ttl_max_override": "Ingresa el TTL máximo (en segundos)",
|
||||||
"cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del proveedor DNS al almacenar en caché las respuestas DNS.",
|
"cache_ttl_min_override_desc": "Amplía el corto tiempo de vida (segundos) de los valores recibidos del servidor DNS de subida al almacenar en caché las respuestas DNS.",
|
||||||
"cache_ttl_max_override_desc": "Establece un valor de tiempo de vida (segundos) máximo para las entradas en la caché DNS.",
|
"cache_ttl_max_override_desc": "Establece un valor de tiempo de vida (segundos) máximo para las entradas en la caché DNS.",
|
||||||
"ttl_cache_validation": "La anulación TTL mínimo de la caché debe ser menor o igual al máximo",
|
"ttl_cache_validation": "La anulación TTL mínimo de la caché debe ser menor o igual al máximo",
|
||||||
"cache_optimistic": "Caché optimista",
|
"cache_optimistic": "Caché optimista",
|
||||||
@@ -748,7 +741,7 @@
|
|||||||
"thursday_short": "Jue.",
|
"thursday_short": "Jue.",
|
||||||
"friday_short": "Vie.",
|
"friday_short": "Vie.",
|
||||||
"saturday_short": "Sáb.",
|
"saturday_short": "Sáb.",
|
||||||
"upstream_dns_cache_configuration": "Configuración de la caché del proveedor DNS",
|
"upstream_dns_cache_configuration": "Configuración de la caché DNS upstream",
|
||||||
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché del DNS para la configuración personalizada de este cliente",
|
"enable_upstream_dns_cache": "Habilitar el almacenamiento en caché de DNS para la configuración personalizada de este cliente",
|
||||||
"dns_cache_size": "Tamaño de la caché DNS, en bytes"
|
"dns_cache_size": "Tamaño de la caché DNS, en bytes"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
|
"upstream_parallel": "Käytä rinnakkaisia pyyntöjä ja nopeuta selvitystä käyttämällä kaikkia ylävirtapalvelimia samanaikaisesti.",
|
||||||
"parallel_requests": "Rinnakkaiset pyynnöt",
|
"parallel_requests": "Rinnakkaiset pyynnöt",
|
||||||
"load_balancing": "Kuormantasaus",
|
"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": "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.",
|
"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_title": "DNS-varapalvelimet",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Käytä AdGuardin lapsilukko-palvelua",
|
"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.",
|
"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_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",
|
"no_servers_specified": "Palvelimia ei ole määritetty",
|
||||||
"general_settings": "Yleiset asetukset",
|
"general_settings": "Yleiset asetukset",
|
||||||
"dns_settings": "DNS-asetukset",
|
"dns_settings": "DNS-asetukset",
|
||||||
@@ -542,7 +542,7 @@
|
|||||||
"stats_params": "Tilastoinnin määritys",
|
"stats_params": "Tilastoinnin määritys",
|
||||||
"config_successfully_saved": "Asetukset tallennettiin",
|
"config_successfully_saved": "Asetukset tallennettiin",
|
||||||
"interval_6_hour": "6 tuntia",
|
"interval_6_hour": "6 tuntia",
|
||||||
"interval_24_hour": "24 tunnilta",
|
"interval_24_hour": "24 tuntia",
|
||||||
"interval_days": "{{count}} päivä",
|
"interval_days": "{{count}} päivä",
|
||||||
"interval_days_plural": "{{count}} päivää",
|
"interval_days_plural": "{{count}} päivää",
|
||||||
"domain": "Verkkotunnus",
|
"domain": "Verkkotunnus",
|
||||||
@@ -709,9 +709,9 @@
|
|||||||
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
|
"log_and_stats_section_label": "Pyyntöhistoria ja tilastot",
|
||||||
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
|
"ignore_query_log": "Älä huomioi tätä päätelaitetta pyyntöhistoriassa",
|
||||||
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
|
"ignore_statistics": "Älä huomioi tätä päätettä tilastoissa",
|
||||||
"schedule_services": "Pysäytä palveluesto",
|
"schedule_services": "Keskeytä palveluesto",
|
||||||
"schedule_services_desc": "Määritä palvelunestosuodattimen pysäytysajoitus.",
|
"schedule_services_desc": "Määritä palvelunestosuodattimen keskeytysajoitus.",
|
||||||
"schedule_services_desc_client": "Määritä palvelunestosuodattimen pysäytysajoitus tälle päätteelle.",
|
"schedule_services_desc_client": "Määritä palvelunestosuodattimen keskeytysajoitus tälle päätteelle.",
|
||||||
"schedule_desc": "Aseta estettujen palveluiden käyttämättömyysjaksot",
|
"schedule_desc": "Aseta estettujen palveluiden käyttämättömyysjaksot",
|
||||||
"schedule_invalid_select": "Aloitusaika on oltava ennen lopetusaikaa",
|
"schedule_invalid_select": "Aloitusaika on oltava ennen lopetusaikaa",
|
||||||
"schedule_select_days": "Valitse päivät",
|
"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.",
|
"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",
|
"parallel_requests": "Requêtes en parallèle",
|
||||||
"load_balancing": "Équilibrage de charge",
|
"load_balancing": "Équilibrage de charge",
|
||||||
"load_balancing_desc": "Une requête par serveur en amont à la fois.<br/>AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
|
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
|
||||||
"bootstrap_dns": "Serveurs DNS d'amorçage",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Serveurs DNS de repli",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Utiliser le contrôle parental d'AdGuard",
|
"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.",
|
"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_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",
|
"no_servers_specified": "Pas de serveurs spécifiés",
|
||||||
"general_settings": "Paramètres généraux",
|
"general_settings": "Paramètres généraux",
|
||||||
"dns_settings": "Paramètres DNS",
|
"dns_settings": "Paramètres DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Réponse bloquée TTL",
|
"blocked_response_ttl": "Réponse bloquée TTL",
|
||||||
"blocked_response_ttl_desc": "Spécifie pendant combien de secondes les clients doivent mettre en cache une réponse filtrée",
|
"blocked_response_ttl_desc": "Spécifie pendant combien de secondes les clients doivent mettre en cache une réponse filtrée",
|
||||||
"form_enter_blocked_response_ttl": "Saisir le TTL de la réponse bloquée (secondes)",
|
"form_enter_blocked_response_ttl": "Saisir le TTL de la réponse bloquée (secondes)",
|
||||||
"upstream_timeout": "Délai d'attente en amont",
|
|
||||||
"upstream_timeout_desc": "Spécifie le nombre de secondes à attendre pour une réponse du serveur en amont",
|
|
||||||
"form_enter_upstream_timeout": "Saisir le délai d'attente du serveur en amont en secondes",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Désactiver la résolution des adresses IPv6",
|
"disable_ipv6": "Désactiver la résolution des adresses IPv6",
|
||||||
"disable_ipv6_desc": "Supprimer toutes les requêtes DNS pour les adresses IPv6 (type AAAA) et supprimer les indices IPv6 des réponses HTTPS.",
|
"disable_ipv6_desc": "Supprimer toutes les requêtes DNS pour les adresses IPv6 (type AAAA) et supprimer les indices IPv6 des réponses HTTPS.",
|
||||||
"fastest_addr": "Adresse IP la plus rapide",
|
"fastest_addr": "Adresse IP la plus rapide",
|
||||||
"fastest_addr_desc": "Attente les réponses de <b>tous</b> les serveurs DNS, mesure de la vitesse de connexion TCP pour chaque serveur et renvoi de l'adresse IP du serveur avec la vitesse de connexion la plus rapide.<br/>Ce mode peut considérablement ralentir les requêtes DNS, si un ou plusieurs serveurs en amont ne répondent pas. Assurez-vous que vos serveurs en amont sont stables et que votre délai dépassé en amont est faible.",
|
"fastest_addr_desc": "Rechercher tous les serveurs DNS et renvoyer l’adresse IP la plus rapide parmi toutes les réponses. Cela ralentit les requêtes DNS car AdGuard Home doit attendre les réponses de tous les serveurs DNS, mais la connectivité globale s'améliore.",
|
||||||
"autofix_warning_text": "Si vous cliquez sur « Réparer », AdGuard Home configurera votre système pour utiliser le serveur DNS AdGuard Home.",
|
"autofix_warning_text": "Si vous cliquez sur « Réparer », AdGuard Home configurera votre système pour utiliser le serveur DNS AdGuard Home.",
|
||||||
"autofix_warning_list": "Ceci effectuera les tâches suivantes : <0>Désactiver le système DNSStubListener</0> <0>Définir l’adresse du serveur DNS à 127.0.0.1 </0> <0>Remplacer la cible du lien symbolique de /etc/resolv.conf par /run/systemd/resolve/resolv.conf</0> <0>Arrêter DNSStubListener (recharger le service résolu par systemd)</0>",
|
"autofix_warning_list": "Ceci effectuera les tâches suivantes : <0>Désactiver le système DNSStubListener</0> <0>Définir l’adresse du serveur DNS à 127.0.0.1 </0> <0>Remplacer la cible du lien symbolique de /etc/resolv.conf par /run/systemd/resolve/resolv.conf</0> <0>Arrêter DNSStubListener (recharger le service résolu par systemd)</0>",
|
||||||
"autofix_warning_result": "Par conséquent, toutes les demandes DNS de votre système seront traitées par AdGuardHome par défaut.",
|
"autofix_warning_result": "Par conséquent, toutes les demandes DNS de votre système seront traitées par AdGuardHome par défaut.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME : {{cname}}",
|
"check_cname": "CNAME : {{cname}}",
|
||||||
"check_reason": "Raison : {{reason}}",
|
"check_reason": "Raison : {{reason}}",
|
||||||
"check_service": "Nom du service : {{service}}",
|
"check_service": "Nom du service : {{service}}",
|
||||||
"check_hostname": "Nom d'hôte ou nom de domaine",
|
|
||||||
"check_client_id": "Identifiant du client (ClientID ou adresse IP)",
|
|
||||||
"check_enter_client_id": "Saisissez l'identifiant du client",
|
|
||||||
"check_dns_record": "Sélectionnez le type d'enregistrement DNS",
|
|
||||||
"service_name": "Nom du service",
|
"service_name": "Nom du service",
|
||||||
"check_not_found": "Introuvable dans vos listes de filtres",
|
"check_not_found": "Introuvable dans vos listes de filtres",
|
||||||
"client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?",
|
"client_confirm_block": "Voulez-vous vraiment bloquer le client « {{ip}} » ?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Liste de blocage",
|
"blocklist": "Liste de blocage",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Taille du cache",
|
"cache_size": "Taille du cache",
|
||||||
"cache_size_desc": "Taille du cache DNS (en octets). Pour désactiver la mise en cache, mettez la valeur sur 0.",
|
"cache_size_desc": "Taille du cache DNS (en octets). Pour désactiver la mise en cache, laissez vide.",
|
||||||
"cache_ttl_min_override": "Remplacer le TTL minimum",
|
"cache_ttl_min_override": "Remplacer le TTL minimum",
|
||||||
"cache_ttl_max_override": "Remplacer le TTL maximum",
|
"cache_ttl_max_override": "Remplacer le TTL maximum",
|
||||||
"enter_cache_size": "Entrer la taille du cache (octets)",
|
"enter_cache_size": "Entrer la taille du cache (octets)",
|
||||||
@@ -683,7 +676,7 @@
|
|||||||
"filter_allowlist": "ATTENTION : Cette action exclura également la règle « {{disallowed_rule}} » de la liste des clients autorisés.",
|
"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 ».",
|
"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",
|
"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",
|
"safe_browsing": "Navigation sécurisée",
|
||||||
"served_from_cache_label": "Servi depuis le cache",
|
"served_from_cache_label": "Servi depuis le cache",
|
||||||
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}} caractères",
|
"form_error_password_length": "Le mot de passe doit comporter entre {{min}} et {{max}} caractères",
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
{
|
{
|
||||||
"client_settings": "Pengaturan klien",
|
"client_settings": "Pengaturan klien",
|
||||||
"example_upstream_reserved": "hulu <0>untuk domain tertentu</0>;",
|
"example_upstream_reserved": "upstream <0>untuk domain spesifik</0>;",
|
||||||
"example_multiple_upstreams_reserved": "beberapa hulu <0>untuk domain tertentu</0>;",
|
"example_multiple_upstreams_reserved": "beberapa server upstream <0>untuk domain spesifik</0>;",
|
||||||
"example_upstream_comment": "komentar.",
|
"example_upstream_comment": "komentar.",
|
||||||
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat penyelesaian dengan mengkueri seluruh server hulu secara bersamaan.",
|
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream secara bersamaan",
|
||||||
"parallel_requests": "Permintaan paralel",
|
"parallel_requests": "Permintaan paralel",
|
||||||
"load_balancing": "Penyeimbang beban",
|
"load_balancing": "Penyeimbang beban",
|
||||||
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
|
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
|
||||||
"bootstrap_dns": "Server DNS bootstrap",
|
"bootstrap_dns": "Server DNS bootstrap",
|
||||||
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP penyelesai DoH/DoT yang Anda tentukan sebagai hulu. Tidak diizinkan untuk berkomentar.",
|
"bootstrap_dns_desc": "Alamat IP server DNS yang digunakan untuk menyelesaikan alamat IP resolver DoH/DoT yang Anda tentukan sebagai upstream. Komentar tidak diizinkan.",
|
||||||
"fallback_dns_title": "Server DNS cadangan",
|
"fallback_dns_title": "Server DNS cadangan",
|
||||||
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server hulu DNS tidak merespons. Sintaksnya sama dengan kolom hulu utama di atas.",
|
"fallback_dns_desc": "Daftar server DNS cadangan yang digunakan ketika server hulu DNS tidak merespons. Sintaksnya sama dengan kolom hulu utama di atas.",
|
||||||
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
|
"fallback_dns_placeholder": "Masukkan satu server DNS cadangan per baris",
|
||||||
"local_ptr_title": "Server pembalik DNS pribadi",
|
"local_ptr_title": "Server pembalik DNS pribadi",
|
||||||
"local_ptr_desc": "Server DNS yang digunakan oleh AdGuard Home untuk permintaan PTR, SOA, dan NS pribadi. Permintaan dianggap pribadi jika meminta domain ARPA yang berisi subnet dalam rentang IP pribadi (seperti \"192.168.12.34\") dan berasal dari klien dengan alamat IP pribadi. Jika tidak ditetapkan, standar pemecah DNS milik OS Anda akan digunakan, kecuali untuk alamat IP AdGuard Home.",
|
"local_ptr_desc": "Server DNS yang digunakan AdGuard Home untuk kueri PTR lokal. Server ini digunakan untuk menyelesaikan nama host klien dengan alamat IP pribadi, misalnya \"192.168.12.34\", menggunakan DNS terbalik. Jika tidak disetel, AdGuard Home menggunakan alamat resolver DNS default OS Anda kecuali untuk alamat AdGuard Home itu sendiri.",
|
||||||
"local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.",
|
"local_ptr_default_resolver": "Secara bawaan, AdGuard Home menggunakan pemecah DNS terbalik: {{ip}}.",
|
||||||
"local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.",
|
"local_ptr_no_default_resolver": "AdGuard Home tidak dapat menentukan pemecah DNS terbalik yang sesuai untuk sistem ini.",
|
||||||
"local_ptr_placeholder": "Masukkan satu alamat IP per baris",
|
"local_ptr_placeholder": "Masukkan satu alamat IP per baris",
|
||||||
"resolve_clients_title": "Aktifkan resolusi hostname klien",
|
"resolve_clients_title": "Aktifkan resolusi hostname klien",
|
||||||
"resolve_clients_desc": "Selesaikan alamat IP klien secara terbalik ke dalam nama host mereka dengan mengirimkan kueri PTR ke penyelesai yang sesuai (server DNS pribadi untuk klien lokal, server hulu untuk klien dengan alamat IP publik).",
|
"resolve_clients_desc": "Menyelesaikan alamat IP klien secara terbalik ke nama host mereka dengan mengirimkan kueri PTR ke resolver yang sesuai (server DNS pribadi untuk klien lokal, server upstream untuk klien dengan alamat IP publik).",
|
||||||
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
|
"use_private_ptr_resolvers_title": "Gunakan server pembalik DNS pribadi",
|
||||||
"use_private_ptr_resolvers_desc": "Menyelesaikan permintaan PTR, SOA, dan NS untuk domain ARPA yang berisi alamat IP pribadi melalui server hulu pribadi, DHCP, /etc/hosts, dll. Jika dinonaktifkan, AdGuard Home akan merespons semua permintaan tersebut dengan NXDOMAIN.",
|
"use_private_ptr_resolvers_desc": "Lakukan pencarian DNS terbalik untuk alamat yang disajikan secara lokal menggunakan server hulu ini. Jika dinonaktifkan, Adguard Home merespons dengan NXDOMAIN untuk semua permintaan PTR tersebut kecuali untuk klien yang diketahui dari DHCP, /etc/hosts, dan seterusnya.",
|
||||||
"check_dhcp_servers": "Cek untuk server DHCP",
|
"check_dhcp_servers": "Cek untuk server DHCP",
|
||||||
"save_config": "Simpan pengaturan",
|
"save_config": "Simpan pengaturan",
|
||||||
"enabled_dhcp": "Server DHCP diaktifkan",
|
"enabled_dhcp": "Server DHCP diaktifkan",
|
||||||
@@ -49,12 +49,12 @@
|
|||||||
"form_error_server_name": "Nama server tidak valid",
|
"form_error_server_name": "Nama server tidak valid",
|
||||||
"form_error_subnet": "Subnet \"{{cidr}}\" tidak berisi alamat IP \"{{ip}}\"",
|
"form_error_subnet": "Subnet \"{{cidr}}\" tidak berisi alamat IP \"{{ip}}\"",
|
||||||
"form_error_positive": "Harus lebih dari 0",
|
"form_error_positive": "Harus lebih dari 0",
|
||||||
"form_error_gateway_ip": "Lease tidak dapat memiliki gerbang alamat IP",
|
"form_error_gateway_ip": "Sewa tidak dapat memiliki alamat IP gateway",
|
||||||
"out_of_range_error": "Harus di luar rentang \"{{start}}\"-\"{{end}}\"",
|
"out_of_range_error": "Harus di luar rentang \"{{start}}\"-\"{{end}}\"",
|
||||||
"lower_range_start_error": "Harus lebih rendah dari rentang awal",
|
"lower_range_start_error": "Harus lebih rendah dari rentang awal",
|
||||||
"greater_range_start_error": "Harus lebih besar dari rentang awal",
|
"greater_range_start_error": "Harus lebih besar dari rentang awal",
|
||||||
"subnet_error": "Alamat harus dalam satu subnet",
|
"subnet_error": "Alamat harus dalam satu subnet",
|
||||||
"gateway_or_subnet_invalid": "Subnet samaran tidak valid",
|
"gateway_or_subnet_invalid": "Subnet mask tidak valid",
|
||||||
"dhcp_form_gateway_input": "IP gateway",
|
"dhcp_form_gateway_input": "IP gateway",
|
||||||
"dhcp_form_subnet_input": "Subnet mask",
|
"dhcp_form_subnet_input": "Subnet mask",
|
||||||
"dhcp_form_range_title": "Rentang alamat IP",
|
"dhcp_form_range_title": "Rentang alamat IP",
|
||||||
@@ -132,8 +132,8 @@
|
|||||||
"top_clients": "Klien teratas",
|
"top_clients": "Klien teratas",
|
||||||
"no_clients_found": "Tidak ditemukan klien",
|
"no_clients_found": "Tidak ditemukan klien",
|
||||||
"general_statistics": "Statistik umum",
|
"general_statistics": "Statistik umum",
|
||||||
"top_upstreams": "Hulu teratas",
|
"top_upstreams": "Top servers upstream",
|
||||||
"no_upstreams_data_found": "Tidak ada data hulu yang ditemukan",
|
"no_upstreams_data_found": "Tidak ada data server upstream yang ditemukan",
|
||||||
"number_of_dns_query_days": "Jumlah kueri DNS diproses selama {{value}} hari terakhir",
|
"number_of_dns_query_days": "Jumlah kueri DNS diproses selama {{value}} hari terakhir",
|
||||||
"number_of_dns_query_days_plural": "Jumlah kueri DNS yang diproses selama {{count}} hari terakhir",
|
"number_of_dns_query_days_plural": "Jumlah kueri DNS yang diproses selama {{count}} hari terakhir",
|
||||||
"number_of_dns_query_hours": "Jumlah kueri DNS diproses selama {{{count}} jam terakhir",
|
"number_of_dns_query_hours": "Jumlah kueri DNS diproses selama {{{count}} jam terakhir",
|
||||||
@@ -147,14 +147,14 @@
|
|||||||
"average_upstream_response_time": "Rata-rata waktu respons hulu",
|
"average_upstream_response_time": "Rata-rata waktu respons hulu",
|
||||||
"response_time": "Waktu respons",
|
"response_time": "Waktu respons",
|
||||||
"average_processing_time_hint": "Rata-rata waktu dalam milidetik untuk pemrosesan sebuah permintaan DNS",
|
"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>.",
|
"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": "Gunakan layanan web Keamanan Penjelajahan AdGuard",
|
||||||
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
|
"use_adguard_browsing_sec_hint": "AdGuard Home akan memeriksa apakah domain diblokir oleh layanan web keamanan penjelajahan. Ini akan menggunakan API pencarian yang ramah privasi untuk melakukan pemeriksaan: hanya awalan singkat dari hash nama domain SHA256 yang dikirim ke server.",
|
||||||
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
|
"use_adguard_parental": "Gunakan layanan web kontrol orang tua AdGuard",
|
||||||
"use_adguard_parental_hint": "AdGuard Home akan mengecek jika domain mengandung materi dewasa. Akan menggunakan API yang ramah privasi yang sama sebagai layanan web keamanan penjelajahan.",
|
"use_adguard_parental_hint": "AdGuard Home akan mengecek jika domain mengandung materi dewasa. Akan menggunakan API yang ramah privasi yang sama sebagai layanan web keamanan penjelajahan.",
|
||||||
"enforce_safe_search": "Pakai pencarian aman",
|
"enforce_safe_search": "Pakai pencarian aman",
|
||||||
"enforce_save_search_hint": "AdGuard Home akan memberlakukan pencarian yang aman di mesin pencari berikut ini: Google, YouTube, Bing, DuckDuckGo, Ecosia, Yandex, Pixabay.",
|
"enforce_save_search_hint": "AdGuard Home dapat memaksa penelusuran aman pada mesin pencari berikut: Google, Youtube, Bing, DuckDuckGo, Yandex, dan Pixabay.",
|
||||||
"no_servers_specified": "Sever tidak disebutkan",
|
"no_servers_specified": "Sever tidak disebutkan",
|
||||||
"general_settings": "Pengaturan umum",
|
"general_settings": "Pengaturan umum",
|
||||||
"dns_settings": "Pengaturan DNS",
|
"dns_settings": "Pengaturan DNS",
|
||||||
@@ -169,8 +169,8 @@
|
|||||||
"upstream_dns_help": "Masukkan satu alamat server per baris. <a>Pelajari lebih lanjut</a> mengenai cara mengonfigurasi server DNS hulu.",
|
"upstream_dns_help": "Masukkan satu alamat server per baris. <a>Pelajari lebih lanjut</a> mengenai cara mengonfigurasi server DNS hulu.",
|
||||||
"upstream_dns_configured_in_file": "Diatur dalam {{path}}",
|
"upstream_dns_configured_in_file": "Diatur dalam {{path}}",
|
||||||
"test_upstream_btn": "Uji hulu",
|
"test_upstream_btn": "Uji hulu",
|
||||||
"upstreams": "Hulu",
|
"upstreams": "Upstream",
|
||||||
"upstream": "Hulu",
|
"upstream": "Server upstream",
|
||||||
"apply_btn": "Terapkan",
|
"apply_btn": "Terapkan",
|
||||||
"disabled_filtering_toast": "Penyaringan nonaktif",
|
"disabled_filtering_toast": "Penyaringan nonaktif",
|
||||||
"enabled_filtering_toast": "Penyaringan aktif",
|
"enabled_filtering_toast": "Penyaringan aktif",
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
"edit_table_action": "Ubah",
|
"edit_table_action": "Ubah",
|
||||||
"delete_table_action": "Hapus",
|
"delete_table_action": "Hapus",
|
||||||
"elapsed": "Berlalu",
|
"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_blocklist_added": "Tidak ada daftar hitam yang ditambahkan",
|
||||||
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
|
"no_whitelist_added": "Tidak ada daftar putih yang ditambahkan",
|
||||||
"add_blocklist": "Tambahkan daftar hitam",
|
"add_blocklist": "Tambahkan daftar hitam",
|
||||||
@@ -211,33 +211,33 @@
|
|||||||
"form_error_url_format": "Format URL tidak valid",
|
"form_error_url_format": "Format URL tidak valid",
|
||||||
"form_error_url_or_path_format": "URL atau jalur absolut dari daftar 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": "Aturan penyaringan khusus",
|
||||||
"custom_filter_rules_hint": "Masukkan satu aturan pada satu baris. Anda dapat menggunakan aturan adblock atau sintaks berkas host.",
|
"custom_filter_rules_hint": "Masukkan satu aturan dalam sebuah baris. Anda dapat menggunakan baik aturan adblock maupun sintaks file hosts.",
|
||||||
"system_host_files": "Berkas host sistem",
|
"system_host_files": "File host sistem",
|
||||||
"examples_title": "Contoh",
|
"examples_title": "Contoh",
|
||||||
"example_meaning_filter_block": "blokir akses ke example.org dan seluruh subdomainnya;",
|
"example_meaning_filter_block": "blokir akses ke example.org dan seluruh subdomainnya;",
|
||||||
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.org dan seluruh subdomainnya;",
|
"example_meaning_filter_whitelist": "buka blokir akses ke domain example.orf dan seluruh subdomainnya;",
|
||||||
"example_meaning_host_block": "merespons dengan 127.0.0.1 untuk example.org (tetapi tidak untuk subdomainnya);",
|
"example_meaning_host_block": "merespons dengan 127.0.0.1 untuk example.org (tetapi tidak untuk subdomainnya);",
|
||||||
"example_comment": "! Komentar di sini.",
|
"example_comment": "! Komentar di sini.",
|
||||||
"example_comment_meaning": "hanya sebuah komentar;",
|
"example_comment_meaning": "hanya sebuah komentar;",
|
||||||
"example_comment_hash": "# Juga sebuah komentar.",
|
"example_comment_hash": "# Juga sebuah komentar.",
|
||||||
"example_regex_meaning": "blokir akses ke domain yang cocok dengan ekspresi reguler yang ditentukan.",
|
"example_regex_meaning": "blokir akses ke domain yang cocok dengan ekspresi reguler yang ditentukan.",
|
||||||
"example_upstream_regular": "DNS biasa (melalui UDP);",
|
"example_upstream_regular": "DNS reguler (melalui UDP);",
|
||||||
"example_upstream_regular_port": "DNS biasa (melalui UDP, dengan port);",
|
"example_upstream_regular_port": "DNS biasa (lebih dari UDP, dengan port);",
|
||||||
"example_upstream_udp": "DNS biasa (melalui UDP, nama host);",
|
"example_upstream_udp": "DNS biasa (lebih dari UDP, nama host);",
|
||||||
"example_upstream_dot": "<0>DNS melalui TLS</0> terenkripsi;",
|
"example_upstream_dot": "<0>DNS melalui TLS</0> terenkripsi;",
|
||||||
"example_upstream_doh": "<0>DNS melalui HTTPS</0> terenkripsi;",
|
"example_upstream_doh": "<0>DNS melalui HTTPS</0> terenkripsi;",
|
||||||
"example_upstream_doh3": "DNS melalui HTTPS terenkripsi dengan <0>HTTP/3</0> secara paksa dan tidak ada cadangan ke HTTP/2 atau lebih rendah;",
|
"example_upstream_doh3": "DNS melalui HTTPS terenkripsi dengan <0>HTTP/3</0> secara paksa dan tidak ada cadangan ke HTTP/2 atau lebih rendah;",
|
||||||
"example_upstream_doq": "<0>DNS melalui QUIC</0> terenkripsi;",
|
"example_upstream_doq": "<0>DNS melalui QUIC</0> terenkripsi;",
|
||||||
"example_upstream_sdns": "<0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS melalui HTTPS</2>;",
|
"example_upstream_sdns": "<0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS-over-HTTPS</2>;",
|
||||||
"example_upstream_tcp": "DNS biasa (melalui TCP);",
|
"example_upstream_tcp": "DNS reguler (melalui TCP);",
|
||||||
"example_upstream_tcp_port": "DNS biasa (melalui TCP, dengan port);",
|
"example_upstream_tcp_port": "DNS biasa (melalui TCP, dengan port);",
|
||||||
"example_upstream_tcp_hostname": "DNS biasa (melalui TCP, nama host);",
|
"example_upstream_tcp_hostname": "DNS biasa (lebih dari TCP, nama host);",
|
||||||
"all_lists_up_to_date_toast": "Semua daftar sudah diperbarui",
|
"all_lists_up_to_date_toast": "Semua daftar sudah diperbarui",
|
||||||
"updated_upstream_dns_toast": "Server hulu berhasil disimpan",
|
"updated_upstream_dns_toast": "Server upstream berhasil disimpan",
|
||||||
"dns_test_ok_toast": "Server DNS yang ditentukan bekerja dengan benar",
|
"dns_test_ok_toast": "Server DNS yang ditentukan bekerja dengan benar",
|
||||||
"dns_test_not_ok_toast": "Server \"{{key}}\": tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
|
"dns_test_not_ok_toast": "Server \"{{key}}\": tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
|
||||||
"dns_test_parsing_error_toast": "Bagian {{section}}: baris {{line}}: tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
|
"dns_test_parsing_error_toast": "Bagian {{section}}: baris {{line}}: tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
|
||||||
"dns_test_warning_toast": "Hulu \"{{key}}\" tidak menanggapi permintaan pengujian dan mungkin tidak berfungsi dengan benar",
|
"dns_test_warning_toast": "Upstream \"{{key}}\" tidak menanggapi permintaan pengujian dan mungkin tidak berfungsi dengan baik",
|
||||||
"unblock": "Buka Blokir",
|
"unblock": "Buka Blokir",
|
||||||
"block": "Blok",
|
"block": "Blok",
|
||||||
"disallow_this_client": "Cabut ijin untuk klien ini",
|
"disallow_this_client": "Cabut ijin untuk klien ini",
|
||||||
@@ -268,18 +268,18 @@
|
|||||||
"rule_added_to_custom_filtering_toast": "Aturan ditambah ke aturan penyaringan khusus: {{rule}}",
|
"rule_added_to_custom_filtering_toast": "Aturan ditambah ke aturan penyaringan khusus: {{rule}}",
|
||||||
"query_log_response_status": "Status: {{value}}",
|
"query_log_response_status": "Status: {{value}}",
|
||||||
"query_log_filtered": "Difilter oleh {{filter}}",
|
"query_log_filtered": "Difilter oleh {{filter}}",
|
||||||
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh catatan kueri?",
|
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh kueri log?",
|
||||||
"query_log_cleared": "Catatan kueri berhasil dihapus",
|
"query_log_cleared": "Kueri log telah berhasil dihapus",
|
||||||
"query_log_updated": "Catatan kueri berhasil diperbarui",
|
"query_log_updated": "Log permintaan telah berhasil diperbarui",
|
||||||
"query_log_clear": "Hapus catatan kueri",
|
"query_log_clear": "Hapus kueri log",
|
||||||
"query_log_retention": "Rotasi kueri log",
|
"query_log_retention": "Rotasi kueri log",
|
||||||
"query_log_enable": "Aktifkan catatan",
|
"query_log_enable": "Aktifkan log",
|
||||||
"query_log_configuration": "Konfigurasi catatan",
|
"query_log_configuration": "Konfigurasi log",
|
||||||
"query_log_disabled": "Catatan kueri dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
|
"query_log_disabled": "Kueri log dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
|
||||||
"query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat",
|
"query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat",
|
||||||
"query_log_retention_confirm": "Apakah Anda yakin ingin mengubah rotasi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
|
"query_log_retention_confirm": "Apakah Anda yakin ingin mengubah rotasi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
|
||||||
"anonymize_client_ip": "Anonim IP klien",
|
"anonymize_client_ip": "Anonim IP klien",
|
||||||
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam catatan atau statistik",
|
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam log dan statistik",
|
||||||
"dns_config": "Konfigurasi server DNS",
|
"dns_config": "Konfigurasi server DNS",
|
||||||
"dns_cache_config": "Konfigurasi cache DNS",
|
"dns_cache_config": "Konfigurasi cache DNS",
|
||||||
"dns_cache_config_desc": "Disini Anda bisa mengonfigurasi cache DNS",
|
"dns_cache_config_desc": "Disini Anda bisa mengonfigurasi cache DNS",
|
||||||
@@ -308,8 +308,8 @@
|
|||||||
"form_enter_rate_limit": "Masukkan batas nilai",
|
"form_enter_rate_limit": "Masukkan batas nilai",
|
||||||
"rate_limit": "Batas nilai",
|
"rate_limit": "Batas nilai",
|
||||||
"edns_enable": "Aktifkan EDNS Klien Subnet",
|
"edns_enable": "Aktifkan EDNS Klien Subnet",
|
||||||
"edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan hulu dan catat nilai yang dikirim oleh klien dalam catatan kueri.",
|
"edns_cs_desc": "Tambahkan opsi EDNS Client Subnet (ECS) ke permintaan upstream dan catat nilai yang dikirim oleh klien di log kueri.",
|
||||||
"edns_use_custom_ip": "Gunakan IP kustom untuk EDNS",
|
"edns_use_custom_ip": "Gunakan IP khusus untuk EDNS",
|
||||||
"edns_use_custom_ip_desc": "Izinkan untuk menggunakan IP kustom untuk EDNS",
|
"edns_use_custom_ip_desc": "Izinkan untuk menggunakan IP kustom untuk EDNS",
|
||||||
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien. Atur ke 0 untuk tidak terbatas.",
|
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien. Atur ke 0 untuk tidak terbatas.",
|
||||||
"rate_limit_subnet_len_ipv4": "Panjang awalan subnet untuk alamat IPv4",
|
"rate_limit_subnet_len_ipv4": "Panjang awalan subnet untuk alamat IPv4",
|
||||||
@@ -329,13 +329,13 @@
|
|||||||
"blocking_mode_nxdomain": "NXDOMAIN: Respon pakai kode NXDOMAIN",
|
"blocking_mode_nxdomain": "NXDOMAIN: Respon pakai kode NXDOMAIN",
|
||||||
"blocking_mode_null_ip": "Null IP: Respon pakai alamat IP kosong (0.0.0.0 untuk A; :: untuk AAAA)",
|
"blocking_mode_null_ip": "Null IP: Respon pakai alamat IP kosong (0.0.0.0 untuk A; :: untuk AAAA)",
|
||||||
"blocking_mode_custom_ip": "IP kustom: respon dengan alamat IP yang diset secara manual",
|
"blocking_mode_custom_ip": "IP kustom: respon dengan alamat IP yang diset secara manual",
|
||||||
"theme_auto": "Otomatis",
|
"theme_auto": "Auto",
|
||||||
"theme_light": "Terang",
|
"theme_light": "Terang",
|
||||||
"theme_dark": "Gelap",
|
"theme_dark": "Gelap",
|
||||||
"upstream_dns_client_desc": "Jika Anda biarkan kolom ini kosong, AdGuard Home akan menggunakan server yang dikonfigurasi di <0>pengaturan DNS</0>.",
|
"upstream_dns_client_desc": "Jika Anda biarkan kolom ini kosong, AdGuard Home akan menggunakan server yang dikonfigurasi di <0>pengaturan DNS</0>.",
|
||||||
"tracker_source": "Sumber pelacak",
|
"tracker_source": "Sumber pelacak",
|
||||||
"source_label": "Sumber",
|
"source_label": "Sumber",
|
||||||
"found_in_known_domain_db": "Ditemukan di basis data domain yang dikenal.",
|
"found_in_known_domain_db": "Ditemukan di database domain dikenal",
|
||||||
"category_label": "Kategori",
|
"category_label": "Kategori",
|
||||||
"rule_label": "Atura(n)",
|
"rule_label": "Atura(n)",
|
||||||
"list_label": "Daftar",
|
"list_label": "Daftar",
|
||||||
@@ -366,18 +366,18 @@
|
|||||||
"install_devices_router": "Router",
|
"install_devices_router": "Router",
|
||||||
"install_devices_router_desc": "Penyiapan ini secara otomatis mencakup semua perangkat yang terhubung ke router rumah Anda, tidak perlu mengkonfigurasi masing-masing perangkat secara manual.",
|
"install_devices_router_desc": "Penyiapan ini secara otomatis mencakup semua perangkat yang terhubung ke router rumah Anda, tidak perlu mengkonfigurasi masing-masing perangkat secara manual.",
|
||||||
"install_devices_address": "Server DNS AdGuard Home akan menggunakan alamat berikut",
|
"install_devices_address": "Server DNS AdGuard Home akan menggunakan alamat berikut",
|
||||||
"install_devices_router_list_1": "Buka preferensi untuk router Anda. Biasanya, Anda dapat mengaksesnya dari peramban Anda melalui URL, seperti http://192.168.0.1/ atau http://192.168.1.1/. Anda mungkin diminta untuk memasukkan kata sandi. Jika Anda tidak mengingatnya, Anda sering kali dapat mengatur ulang kata sandi dengan menekan tombol pada router itu sendiri, tetapi perlu diketahui bahwa jika prosedur ini dipilih, Anda mungkin akan kehilangan seluruh konfigurasi router. Jika router Anda memerlukan aplikasi untuk menyiapkannya, pasang aplikasi tersebut di ponsel atau PC Anda dan gunakan untuk mengakses pengaturan router.",
|
"install_devices_router_list_1": "Buka preferensi untuk router Anda. Biasanya, Anda dapat mengaksesnya dari browser Anda melalui URL, seperti http://192.168.0.1/ atau http://192.168.1.1/. Anda mungkin diminta untuk memasukkan kata sandi. Jika Anda tidak mengingatnya, Anda sering kali dapat mengatur ulang kata sandi dengan menekan tombol pada perute itu sendiri, tetapi perlu diketahui bahwa jika prosedur ini dipilih, Anda mungkin akan kehilangan seluruh konfigurasi perute. Jika router Anda memerlukan aplikasi untuk menyiapkannya, instal aplikasi tersebut di ponsel atau PC Anda dan gunakan untuk mengakses pengaturan router.",
|
||||||
"install_devices_router_list_2": "Temukan pengaturan DHCP / DNS. Cari huruf DNS di sebelah kolom yang memungkinkan dua atau tiga set angka, masing-masing dipecah menjadi empat kelompok dengan satu hingga tiga digit.",
|
"install_devices_router_list_2": "Temukan pengaturan DHCP / DNS. Cari huruf DNS di sebelah kolom yang memungkinkan dua atau tiga set angka, masing-masing dipecah menjadi empat kelompok dengan satu hingga tiga digit.",
|
||||||
"install_devices_router_list_3": "Masukkan alamat server AdGuard Home disana",
|
"install_devices_router_list_3": "Masukkan alamat server AdGuard Home disana",
|
||||||
"install_devices_router_list_4": "Anda tidak dapat menyetel server DNS kustom pada beberapa tipe router. Dalam hal ini mungkin membantu jika Anda mengatur AdGuard Home sebagai <0>server DHCP</0>. Jika tidak, Anda harus mencari petunjuk tentang cara mengkustomisasi server DNS untuk model router khusus Anda.",
|
"install_devices_router_list_4": "Anda tidak dapat menyetel server DNS kustom pada beberapa tipe router. Dalam hal ini mungkin membantu jika Anda mengatur AdGuard Home sebagai <0>server DHCP</0>. Jika tidak, Anda harus mencari petunjuk tentang cara mengkustomisasi server DNS untuk model router khusus Anda.",
|
||||||
"install_devices_windows_list_1": "Buka Panel Kontrol melalui menu Start atau pencarian Windows.",
|
"install_devices_windows_list_1": "Buka Panel Kontrol melalui menu Start atau pencarian Windows.",
|
||||||
"install_devices_windows_list_2": "Masuk ke kategori Jaringan dan Internet (Network and Internet) dan kemudian ke Pusat Jaringan dan Berbagi (Network and Sharing Center).",
|
"install_devices_windows_list_2": "Masuk ke kategori Jaringan dan Internet (Network and Internet) dan kemudian ke Pusat Jaringan dan Berbagi (Network and Sharing Center).",
|
||||||
"install_devices_windows_list_3": "Di panel kiri, klik \"Ubah pengaturan adaptor\".",
|
"install_devices_windows_list_3": "Di panel kiri, klik \"Ubah pengaturan adaptor\".",
|
||||||
"install_devices_windows_list_4": "Klik kanan koneksi aktif Anda dan pilih Properti.",
|
"install_devices_windows_list_4": "Klik kanan koneksi aktif Anda dan pilih Properties.",
|
||||||
"install_devices_windows_list_5": "Temukan \"Protokol Internet Versi 4 (TCP/IPv4)\" (atau, untuk IPv6, \"Protokol Internet Versi 6 (TCP/IPv6)\") dalam daftar, pilih dan kemudian klik Properti lagi.",
|
"install_devices_windows_list_5": "Temukan \"Internet Protocol Version 4 (TCP/IPv4)\" (atau, untuk IPv6, \"Internet Protocol Version 6 (TCP/IPv6)\") dalam daftar, pilih dan kemudian klik Properties lagi.",
|
||||||
"install_devices_windows_list_6": "Pilih \"Gunakan alamat server DNS berikut\" dan masukkan alamat server Beranda AdGuard Anda.",
|
"install_devices_windows_list_6": "Pilih \"Gunakan alamat server DNS berikut\" dan masukkan alamat server Beranda AdGuard Anda.",
|
||||||
"install_devices_macos_list_1": "Klik ikon Apple dan buka Preferensi Sistem.",
|
"install_devices_macos_list_1": "Klik ikon Apple dan pergi ke System Preferences.",
|
||||||
"install_devices_macos_list_2": "Klik Jaringan.",
|
"install_devices_macos_list_2": "Klik Network.",
|
||||||
"install_devices_macos_list_3": "Pilih koneksi pertama dalam daftar dan klik Advanced.",
|
"install_devices_macos_list_3": "Pilih koneksi pertama dalam daftar dan klik Advanced.",
|
||||||
"install_devices_macos_list_4": "Pilih tab DNS dan masukkan alamat server AdGuard Anda.",
|
"install_devices_macos_list_4": "Pilih tab DNS dan masukkan alamat server AdGuard Anda.",
|
||||||
"install_devices_android_list_1": "Dari layar beranda Menu Android, ketuk Pengaturan.",
|
"install_devices_android_list_1": "Dari layar beranda Menu Android, ketuk Pengaturan.",
|
||||||
@@ -394,7 +394,7 @@
|
|||||||
"open_dashboard": "Buka Beranda",
|
"open_dashboard": "Buka Beranda",
|
||||||
"install_saved": "Berhasil disimpan",
|
"install_saved": "Berhasil disimpan",
|
||||||
"encryption_title": "Enkripsi",
|
"encryption_title": "Enkripsi",
|
||||||
"encryption_desc": "Dukungan enkripsi (HTTPS/QUIC/TLS) untuk DNS dan antarmuka web admin",
|
"encryption_desc": "Enkripsi (HTTPS/QUIC/TLS) untuk DNS dan antarmuka admin",
|
||||||
"encryption_config_saved": "Pengaturan enkripsi telah tersimpan",
|
"encryption_config_saved": "Pengaturan enkripsi telah tersimpan",
|
||||||
"encryption_server": "Nama server",
|
"encryption_server": "Nama server",
|
||||||
"encryption_server_enter": "Masukkan nama domain anda",
|
"encryption_server_enter": "Masukkan nama domain anda",
|
||||||
@@ -406,7 +406,7 @@
|
|||||||
"encryption_dot": "Port DNS-over-TLS",
|
"encryption_dot": "Port DNS-over-TLS",
|
||||||
"encryption_dot_desc": "Jika port ini terkonfigurasi, AdGuard Home akan menjalankan server DNS-over-TLS dalam port ini",
|
"encryption_dot_desc": "Jika port ini terkonfigurasi, AdGuard Home akan menjalankan server DNS-over-TLS dalam port ini",
|
||||||
"encryption_doq": "Port DNS-over-QUIC ",
|
"encryption_doq": "Port DNS-over-QUIC ",
|
||||||
"encryption_doq_desc": "Jika port ini dikonfigurasi, AdGuard Home akan menjalankan server DNS melalui QUIC pada port ini.",
|
"encryption_doq_desc": "Jika port ini diatur secara sepesifik, AdGuard Home akan menjalankan server DNS-lewat-QUIC pada port ini.",
|
||||||
"encryption_certificates": "Sertifikat",
|
"encryption_certificates": "Sertifikat",
|
||||||
"encryption_certificates_desc": "Untuk menggunakan enkripsi, Anda perlu memberikan rantai sertifikat SSL yang valid untuk domain Anda. Anda bisa mendapatkan sertifikat gratis di <0>{{link}}</0> atau Anda dapat membelinya dari salah satu Otoritas Sertifikat tepercaya.",
|
"encryption_certificates_desc": "Untuk menggunakan enkripsi, Anda perlu memberikan rantai sertifikat SSL yang valid untuk domain Anda. Anda bisa mendapatkan sertifikat gratis di <0>{{link}}</0> atau Anda dapat membelinya dari salah satu Otoritas Sertifikat tepercaya.",
|
||||||
"encryption_certificates_input": "Salin / rekatkan sertifikat PEM yang disandikan di sini.",
|
"encryption_certificates_input": "Salin / rekatkan sertifikat PEM yang disandikan di sini.",
|
||||||
@@ -431,8 +431,8 @@
|
|||||||
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
|
"topline_expiring_certificate": "Sertifikat SSL Anda hampir kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
|
||||||
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
|
"topline_expired_certificate": "Sertifikat SSL Anda kedaluwarsa. Perbarui <0>Pengaturan enkripsi</0>.",
|
||||||
"form_error_port_range": "Masukkan nomor port di kisaran 80-65535",
|
"form_error_port_range": "Masukkan nomor port di kisaran 80-65535",
|
||||||
"form_error_port_unsafe": "Port tidak aman",
|
"form_error_port_unsafe": "Ini adalah port yang tidak aman",
|
||||||
"form_error_equal": "Tidak boleh sama",
|
"form_error_equal": "Seharusnya tidak sama",
|
||||||
"form_error_password": "Kata sandi tidak cocok",
|
"form_error_password": "Kata sandi tidak cocok",
|
||||||
"reset_settings": "Setel ulang pengaturan",
|
"reset_settings": "Setel ulang pengaturan",
|
||||||
"update_announcement": "AdGuard Home {{version}} sekarang tersedia! <0>Klik di sini</0> untuk info lebih lanjut.",
|
"update_announcement": "AdGuard Home {{version}} sekarang tersedia! <0>Klik di sini</0> untuk info lebih lanjut.",
|
||||||
@@ -447,7 +447,7 @@
|
|||||||
"update_failed": "Pembaruan otomatis gagal. Silakan <a>ikuti langkah-langkah berikut</a> untuk memperbarui secara manual.",
|
"update_failed": "Pembaruan otomatis gagal. Silakan <a>ikuti langkah-langkah berikut</a> untuk memperbarui secara manual.",
|
||||||
"manual_update": "Silakan <a>mengikuti langkah berikut</a> untuk memperbarui secara manual.",
|
"manual_update": "Silakan <a>mengikuti langkah berikut</a> untuk memperbarui secara manual.",
|
||||||
"processing_update": "Silahkan tunggu, AdGuard Home sedang diperbarui",
|
"processing_update": "Silahkan tunggu, AdGuard Home sedang diperbarui",
|
||||||
"clients_title": "Klien persisten",
|
"clients_title": "Klien yang gigih",
|
||||||
"clients_desc": "Konfigurasikan catatan klien persisten untuk perangkat yang terhubung ke AdGuard Home",
|
"clients_desc": "Konfigurasikan catatan klien persisten untuk perangkat yang terhubung ke AdGuard Home",
|
||||||
"settings_global": "Global",
|
"settings_global": "Global",
|
||||||
"settings_custom": "Kustom",
|
"settings_custom": "Kustom",
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
"client_edit": "Ubah Klien",
|
"client_edit": "Ubah Klien",
|
||||||
"client_identifier": "Identifikasi",
|
"client_identifier": "Identifikasi",
|
||||||
"ip_address": "Alamat IP",
|
"ip_address": "Alamat IP",
|
||||||
"client_identifier_desc": "Klien dapat diidentifikasi berdasarkan alamat IP, CIDR, alamat MAC, atau ClientID (dapat digunakan untuk DoT/DoH/DoQ). Pelajari lebih lanjut tentang cara mengidentifikasi klien <0>di sini</0>.",
|
"client_identifier_desc": "Klien dapat diidentifikasi oleh alamat IP, CIDR, alamat MAC atau ClientID (dapat digunakan untuk DoT/DoH/DoQ). <0>Di sini</0> Anda dapat mempelajari lebih lanjut tentang cara mengidentifikasi klien.",
|
||||||
"form_enter_ip": "Masukkan IP",
|
"form_enter_ip": "Masukkan IP",
|
||||||
"form_enter_subnet_ip": "Masukkan alamat IP di subnet \"{{cidr}}\"",
|
"form_enter_subnet_ip": "Masukkan alamat IP di subnet \"{{cidr}}\"",
|
||||||
"form_enter_mac": "Masukkan MAC",
|
"form_enter_mac": "Masukkan MAC",
|
||||||
@@ -475,8 +475,8 @@
|
|||||||
"clients_not_found": "Tidak ada klien ditemukan",
|
"clients_not_found": "Tidak ada klien ditemukan",
|
||||||
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
|
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
|
||||||
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
|
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
|
||||||
"auto_clients_title": "Klien runtime",
|
"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_title": "Pengaturan akses",
|
||||||
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
|
"access_desc": "Disini anda dapat mengatur aturan akses untuk server AdGuard Home DNS",
|
||||||
"access_allowed_title": "Klien yang diizinkan",
|
"access_allowed_title": "Klien yang diizinkan",
|
||||||
@@ -484,9 +484,9 @@
|
|||||||
"access_disallowed_title": "Klien yang tidak diizinkan",
|
"access_disallowed_title": "Klien yang tidak diizinkan",
|
||||||
"access_disallowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home akan membatalkan permintaan dari klien ini. Kolom ini diabaikan jika ada entri di daftar putih klien.",
|
"access_disallowed_desc": "Daftar CIDR, alamat IP, atau <a>ClientID</a>. Jika daftar ini memiliki entri, AdGuard Home akan membatalkan permintaan dari klien ini. Kolom ini diabaikan jika ada entri di daftar putih klien.",
|
||||||
"access_blocked_title": "Domain yang diblokir",
|
"access_blocked_title": "Domain yang diblokir",
|
||||||
"access_blocked_desc": "Jangan dikelirukan dengan filter. AdGuard Home membuang kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di catatan kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, misalnya \"example.org\", \"*.example.org\", atau \"||example.org^\" secara bersamaan.",
|
"access_blocked_desc": "Jangan bingung dengan filter. AdGuard Home menghapus kueri DNS yang cocok dengan domain ini, dan kueri ini bahkan tidak muncul di log kueri. Anda dapat menentukan nama domain, karakter pengganti, atau aturan filter URL yang tepat, mis. \"example.org\", \"*.example.org\", atau \"||example.org^\" yang sesuai.",
|
||||||
"access_settings_saved": "Pengaturan akses berhasil disimpan",
|
"access_settings_saved": "Pengaturan akses berhasil disimpan",
|
||||||
"updates_checked": "Versi baru AdGuard Home tersedia",
|
"updates_checked": "Versi baru AdGuard Home tersedia\n",
|
||||||
"updates_version_equal": "AdGuard Home sudah tebaru",
|
"updates_version_equal": "AdGuard Home sudah tebaru",
|
||||||
"check_updates_now": "Periksa pembaruan sekarang",
|
"check_updates_now": "Periksa pembaruan sekarang",
|
||||||
"version_request_error": "Pemeriksaan pembaruan gagal. Harap periksa koneksi internet anda.",
|
"version_request_error": "Pemeriksaan pembaruan gagal. Harap periksa koneksi internet anda.",
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
"setup_dns_privacy_1": "<0>DNS melalui TLS:</0> Gunakan <1>{{address}}</1> string.",
|
"setup_dns_privacy_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_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_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_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_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.",
|
"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_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_desc": "Memungkinkan untuk dengan mudah mengkonfigurasi respons DNS kustom untuk nama domain tertentu.",
|
||||||
"rewrite_applied": "Aturan Rewrite yang diterapkan",
|
"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",
|
"dns_rewrites": "DNS rewrite",
|
||||||
"form_domain": "Masukkan nama domain",
|
"form_domain": "Masukkan nama domain",
|
||||||
"form_answer": "Masaukan alamat IP atau nama domain",
|
"form_answer": "Masaukan alamat IP atau nama domain",
|
||||||
@@ -563,7 +563,7 @@
|
|||||||
"ignore_domains": "Domain yang diabaikan (dipisahkan oleh baris baru)",
|
"ignore_domains": "Domain yang diabaikan (dipisahkan oleh baris baru)",
|
||||||
"ignore_domains_title": "Domain yang diabaikan",
|
"ignore_domains_title": "Domain yang diabaikan",
|
||||||
"ignore_domains_desc_stats": "Kueri yang cocok dengan aturan ini tidak ditulis ke statistik",
|
"ignore_domains_desc_stats": "Kueri yang cocok dengan aturan ini tidak ditulis ke statistik",
|
||||||
"ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke catatan kueri",
|
"ignore_domains_desc_query": "Kueri yang cocok dengan aturan ini tidak ditulis ke log kueri",
|
||||||
"interval_hours": "{{count}} jam",
|
"interval_hours": "{{count}} jam",
|
||||||
"interval_hours_plural": "{{count}} jam",
|
"interval_hours_plural": "{{count}} jam",
|
||||||
"filters_configuration": "Konfigurasi filter",
|
"filters_configuration": "Konfigurasi filter",
|
||||||
@@ -593,8 +593,8 @@
|
|||||||
"example_rewrite_wildcard": "tulis ulang respon untuk semua subdomain <0>contoh.org</0>.",
|
"example_rewrite_wildcard": "tulis ulang respon untuk semua subdomain <0>contoh.org</0>.",
|
||||||
"rewrite_ip_address": "Alamat IP: pakai IP ini dalam respons A atau AAAA",
|
"rewrite_ip_address": "Alamat IP: pakai IP ini dalam respons A atau AAAA",
|
||||||
"rewrite_domain_name": "Nama domain: tambah ke rekaman CNAME",
|
"rewrite_domain_name": "Nama domain: tambah ke rekaman CNAME",
|
||||||
"rewrite_A": "<0>A</0>: nilai khusus, biarkan <0>A</0> merekam dari hulu",
|
"rewrite_A": "<0>A</0>: nilai khusus, biarkan <0>A</0> merekam dari upstream",
|
||||||
"rewrite_AAAA": "<0>AAAA</0>: nilai khusus, biarkan <0>AAAA</0> merekam dari hulu",
|
"rewrite_AAAA": "<0>AAAA</0>: nilai khusus, biarkan <0>AAAA</0> merekam dari upstream",
|
||||||
"disable_ipv6": "Nonaktifkan penyelesaian alamat IPv6",
|
"disable_ipv6": "Nonaktifkan penyelesaian alamat IPv6",
|
||||||
"disable_ipv6_desc": "Hapus semua kueri DNS untuk alamat IPv6 (ketik AAAA) dan hapus petunjuk IPv6 dari respons HTTPS.",
|
"disable_ipv6_desc": "Hapus semua kueri DNS untuk alamat IPv6 (ketik AAAA) dan hapus petunjuk IPv6 dari respons HTTPS.",
|
||||||
"fastest_addr": "Alamat IP tercepat",
|
"fastest_addr": "Alamat IP tercepat",
|
||||||
@@ -655,8 +655,8 @@
|
|||||||
"enter_cache_size": "Masukkan ukuran cache (bytes)",
|
"enter_cache_size": "Masukkan ukuran cache (bytes)",
|
||||||
"enter_cache_ttl_min_override": "Masukkan TTL minimum (detik)",
|
"enter_cache_ttl_min_override": "Masukkan TTL minimum (detik)",
|
||||||
"enter_cache_ttl_max_override": "Masukkan TTL maksimum (detik)",
|
"enter_cache_ttl_max_override": "Masukkan TTL maksimum (detik)",
|
||||||
"cache_ttl_min_override_desc": "Perpanjang nilai time-to-live (detik) yang diterima dari server hulu saat menyimpan respons DNS.",
|
"cache_ttl_min_override_desc": "Perpanjang nilai waktu untuk hidup (detik) yang diterima dari server hulu saat menyimpan respons DNS.",
|
||||||
"cache_ttl_max_override_desc": "Tetapkan nilai maksimum time-to-live (detik) untuk entri dalam cache DNS.",
|
"cache_ttl_max_override_desc": "Tetapkan nilai waktu-online maksimum (detik) untuk entri di cache DNS.",
|
||||||
"ttl_cache_validation": "Nilai TTL cache minimum harus kurang dari atau sama dengan nilai maksimum",
|
"ttl_cache_validation": "Nilai TTL cache minimum harus kurang dari atau sama dengan nilai maksimum",
|
||||||
"cache_optimistic": "Caching yang optimis",
|
"cache_optimistic": "Caching yang optimis",
|
||||||
"cache_optimistic_desc": "Buat AdGuard Home merespons dari cache bahkan ketika entri telah kedaluwarsa dan juga mencoba untuk menyegarkannya.",
|
"cache_optimistic_desc": "Buat AdGuard Home merespons dari cache bahkan ketika entri telah kedaluwarsa dan juga mencoba untuk menyegarkannya.",
|
||||||
@@ -676,9 +676,9 @@
|
|||||||
"filter_allowlist": "PERINGATAN: Tindakan ini juga akan mengecualikan aturan \"{{disallowed_rule}}\" dari daftar klien yang diizinkan.",
|
"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\".",
|
"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",
|
"use_saved_key": "Gunakan kunci yang disimpan sebelumnya",
|
||||||
"parental_control": "Pengawasan Orang Tua",
|
"parental_control": "Kontrol Orang Tua",
|
||||||
"safe_browsing": "Penjelajahan Aman",
|
"safe_browsing": "Penjelajahan Aman",
|
||||||
"served_from_cache_label": "Disajikan dari cache",
|
"served_from_cache": "{{value}} <i>(disajikan dari cache)</i>",
|
||||||
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
|
"form_error_password_length": "Kata sandi harus terdiri dari {{min}} hingga {{max}}",
|
||||||
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
|
"anonymizer_notification": "<0>Catatan:</0> Anonimisasi IP diaktifkan. Anda dapat menonaktifkannya di <1>Pengaturan umum</1> .",
|
||||||
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",
|
"confirm_dns_cache_clear": "Apakah Anda yakin ingin menghapus cache DNS?",
|
||||||
@@ -688,11 +688,11 @@
|
|||||||
"theme_auto_desc": "Otomatis (berdasarkan skema warna perangkat anda)",
|
"theme_auto_desc": "Otomatis (berdasarkan skema warna perangkat anda)",
|
||||||
"theme_dark_desc": "Tema gelap",
|
"theme_dark_desc": "Tema gelap",
|
||||||
"theme_light_desc": "Tema terang",
|
"theme_light_desc": "Tema terang",
|
||||||
"disable_for_seconds": "Selama {{count}} detik",
|
"disable_for_seconds": "Untuk {{count}} detik",
|
||||||
"disable_for_seconds_plural": "Selama {{count}} detik",
|
"disable_for_seconds_plural": "Untuk {{count}} detik",
|
||||||
"disable_for_minutes": "Selama {{count}} menit",
|
"disable_for_minutes": "Untuk {{count}} menit",
|
||||||
"disable_for_minutes_plural": "Selama {{count}} menit",
|
"disable_for_minutes_plural": "Untuk {{count}} menit",
|
||||||
"disable_for_hours": "Selama {{count}} jam",
|
"disable_for_hours": "Untuk {{count}} jam",
|
||||||
"disable_for_hours_plural": "Untuk {{count}} jam",
|
"disable_for_hours_plural": "Untuk {{count}} jam",
|
||||||
"disable_until_tomorrow": "Sampai besok",
|
"disable_until_tomorrow": "Sampai besok",
|
||||||
"disable_notify_for_seconds": "Hentikan perlindungan selama {{count}} detik",
|
"disable_notify_for_seconds": "Hentikan perlindungan selama {{count}} detik",
|
||||||
@@ -706,10 +706,10 @@
|
|||||||
"custom_retention_input": "Masukkan retensi dalam hitungan jam",
|
"custom_retention_input": "Masukkan retensi dalam hitungan jam",
|
||||||
"custom_rotation_input": "Masukkan rotasi dalam hitungan jam",
|
"custom_rotation_input": "Masukkan rotasi dalam hitungan jam",
|
||||||
"protection_section_label": "Perlindungan",
|
"protection_section_label": "Perlindungan",
|
||||||
"log_and_stats_section_label": "Catatan kueri dan statistik",
|
"log_and_stats_section_label": "Log kueri dan statistik",
|
||||||
"ignore_query_log": "Abaikan klien ini di catatan kueri",
|
"ignore_query_log": "Abaikan klien ini di log kueri",
|
||||||
"ignore_statistics": "Abaikan klien ini di statistik",
|
"ignore_statistics": "Abaikan klien ini di statistik",
|
||||||
"schedule_services": "Jeda pemblokiran layanan",
|
"schedule_services": "Menjeda pemblokiran layanan",
|
||||||
"schedule_services_desc": "Mengonfigurasi jadwal jeda filter pemblokiran layanan",
|
"schedule_services_desc": "Mengonfigurasi jadwal jeda filter pemblokiran layanan",
|
||||||
"schedule_services_desc_client": "Mengonfigurasi jadwal jeda filter pemblokiran layanan untuk klien ini",
|
"schedule_services_desc_client": "Mengonfigurasi jadwal jeda filter pemblokiran layanan untuk klien ini",
|
||||||
"schedule_desc": "Tetapkan periode tidak aktif untuk layanan yang diblokir",
|
"schedule_desc": "Tetapkan periode tidak aktif untuk layanan yang diblokir",
|
||||||
@@ -741,7 +741,7 @@
|
|||||||
"thursday_short": "Kam",
|
"thursday_short": "Kam",
|
||||||
"friday_short": "Jum",
|
"friday_short": "Jum",
|
||||||
"saturday_short": "Sab",
|
"saturday_short": "Sab",
|
||||||
"upstream_dns_cache_configuration": "Konfigurasi cache DNS hulu",
|
"upstream_dns_cache_configuration": "Konfigurasi cache DNS upstream",
|
||||||
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi hulu kustom pada klien ini",
|
"enable_upstream_dns_cache": "Aktifkan cache DNS untuk konfigurasi hulu kustom pada klien ini",
|
||||||
"dns_cache_size": "Ukuran cache DNS, dalam byte"
|
"dns_cache_size": "Ukuran cache DNS, dalam byte"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
|
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
|
||||||
"parallel_requests": "Richieste parallele",
|
"parallel_requests": "Richieste parallele",
|
||||||
"load_balancing": "Bilanciamento del carico",
|
"load_balancing": "Bilanciamento del carico",
|
||||||
"load_balancing_desc": "Esegui una query su un server upstream alla volta.<br/>AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
|
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
|
||||||
"bootstrap_dns": "Server DNS bootstrap",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Server DNS di fallback",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Utilizza il Controllo Parentale di AdGuard",
|
"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'.",
|
"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_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",
|
"no_servers_specified": "Nessun server specificato",
|
||||||
"general_settings": "Impostazioni generali",
|
"general_settings": "Impostazioni generali",
|
||||||
"dns_settings": "Impostazioni DNS",
|
"dns_settings": "Impostazioni DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Risposta TTL bloccata",
|
"blocked_response_ttl": "Risposta TTL bloccata",
|
||||||
"blocked_response_ttl_desc": "Specifica per quanti secondi i client devono tenere nella cache una risposta filtrata",
|
"blocked_response_ttl_desc": "Specifica per quanti secondi i client devono tenere nella cache una risposta filtrata",
|
||||||
"form_enter_blocked_response_ttl": "Inserisci tempo di vita (TTL) della risposta bloccata (secondi)",
|
"form_enter_blocked_response_ttl": "Inserisci tempo di vita (TTL) della risposta bloccata (secondi)",
|
||||||
"upstream_timeout": "Timeout upstream",
|
|
||||||
"upstream_timeout_desc": "Specifica il numero di secondi da attendere per una risposta dal server upstream",
|
|
||||||
"form_enter_upstream_timeout": "Inserisci la durata del timeout del server upstream in secondi",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS su HTTPS",
|
"dns_over_https": "DNS su HTTPS",
|
||||||
"dns_over_tls": "DNS su TLS",
|
"dns_over_tls": "DNS su TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Disattiva risoluzione indirizzi IPv6",
|
"disable_ipv6": "Disattiva risoluzione indirizzi IPv6",
|
||||||
"disable_ipv6_desc": "Eliminare tutte le query DNS per gli indirizzi IPv6 (tipo AAAA) e rimuovere i suggerimenti IPv6 dalle risposte HTTPS.",
|
"disable_ipv6_desc": "Eliminare tutte le query DNS per gli indirizzi IPv6 (tipo AAAA) e rimuovere i suggerimenti IPv6 dalle risposte HTTPS.",
|
||||||
"fastest_addr": "Indirizzo IP più veloce",
|
"fastest_addr": "Indirizzo IP più veloce",
|
||||||
"fastest_addr_desc": "Attendi le risposte da <b>tutti i</b> server DNS, misura la velocità di connessione TCP per ogni server e restituisci l'indirizzo IP del server con la velocità di connessione più elevata.<br/>Questa modalità può rallentare notevolmente le query DNS, se uno o più server upstream non rispondono. Assicurati che i tuoi server upstream siano stabili e che il timeout upstream sia basso.",
|
"fastest_addr_desc": "Interroga tutti i server DNS e restituisci l'indirizzo IP più veloce tra tutte le risposte. Ciò rallenterà le richieste DNS poiché AdGuard Home dovrà attendere le risposte da tutti i server DNS, ma ciò migliorerà complessivamente la connettività.",
|
||||||
"autofix_warning_text": "Se fai clic su \"Correggi\", AdGuardHome configurerà il tuo sistema per utilizzare il server DNS AdGuardHome.",
|
"autofix_warning_text": "Se fai clic su \"Correggi\", AdGuardHome configurerà il tuo sistema per utilizzare il server DNS AdGuardHome.",
|
||||||
"autofix_warning_list": "Eseguirà queste attività: <0> Disattiva DNSStubListener di sistema </0> <0> Imposta l'indirizzo del server DNS su 127.0.0.1 </0> <0> Sostituisci la destinazione del collegamento simbolico di /etc/resolv.conf su / run / systemd /resolve/resolv.conf </0> <0> Arresta DNSStubListener (ricarica il servizio systemd-resolved) </0>",
|
"autofix_warning_list": "Eseguirà queste attività: <0> Disattiva DNSStubListener di sistema </0> <0> Imposta l'indirizzo del server DNS su 127.0.0.1 </0> <0> Sostituisci la destinazione del collegamento simbolico di /etc/resolv.conf su / run / systemd /resolve/resolv.conf </0> <0> Arresta DNSStubListener (ricarica il servizio systemd-resolved) </0>",
|
||||||
"autofix_warning_result": "Di conseguenza, tutte le richieste DNS dal sistema verranno elaborate da AdGuardHome per impostazione predefinita.",
|
"autofix_warning_result": "Di conseguenza, tutte le richieste DNS dal sistema verranno elaborate da AdGuardHome per impostazione predefinita.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Motivo: {{reason}}",
|
"check_reason": "Motivo: {{reason}}",
|
||||||
"check_service": "Nome servizio: {{service}}",
|
"check_service": "Nome servizio: {{service}}",
|
||||||
"check_hostname": "Nome host o nome di dominio",
|
|
||||||
"check_client_id": "Identificatore client (ClientID o indirizzo IP)",
|
|
||||||
"check_enter_client_id": "Inserisci identificatore client",
|
|
||||||
"check_dns_record": "Seleziona il tipo di registrazione DNS",
|
|
||||||
"service_name": "Nome servizio",
|
"service_name": "Nome servizio",
|
||||||
"check_not_found": "Non trovato negli elenchi dei filtri",
|
"check_not_found": "Non trovato negli elenchi dei filtri",
|
||||||
"client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?",
|
"client_confirm_block": "Sei sicuro di voler bloccare il client \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Lista nera",
|
"blocklist": "Lista nera",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Dimensioni cache",
|
"cache_size": "Dimensioni cache",
|
||||||
"cache_size_desc": "Dimensione della cache DNS (in byte). Per disabilitare la cache, impostare su 0.",
|
"cache_size_desc": "Dimensione della cache DNS (in byte). Per disabilitare la memorizzazione nella cache, lascia vuoto.",
|
||||||
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
|
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
|
||||||
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
|
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
|
||||||
"enter_cache_size": "Immetti dimensioni cache (in byte)",
|
"enter_cache_size": "Immetti dimensioni cache (in byte)",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
|
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
|
||||||
"parallel_requests": "並列リクエスト",
|
"parallel_requests": "並列リクエスト",
|
||||||
"load_balancing": "ロードバランシング",
|
"load_balancing": "ロードバランシング",
|
||||||
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。<br/>AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
|
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
|
||||||
"bootstrap_dns": "ブートストラップDNSサーバ",
|
"bootstrap_dns": "ブートストラップDNSサーバ",
|
||||||
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。(コメントは許可されていません)",
|
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。(コメントは許可されていません)",
|
||||||
"fallback_dns_title": "フォールバックDNSサーバー",
|
"fallback_dns_title": "フォールバックDNSサーバー",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
|
"use_adguard_parental": "AdGuardペアレンタルコントロール・ウェブサービスを使用する",
|
||||||
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
|
"use_adguard_parental_hint": "AdGuard Homeは、ドメインにアダルトコンテンツが含まれているかどうかを確認します。 ブラウジングセキュリティ・ウェブサービスと同じプライバシーに優しいAPIを使用します。",
|
||||||
"enforce_safe_search": "セーフサーチを使用する",
|
"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": "サーバが指定されていません",
|
"no_servers_specified": "サーバが指定されていません",
|
||||||
"general_settings": "一般設定",
|
"general_settings": "一般設定",
|
||||||
"dns_settings": "DNS設定",
|
"dns_settings": "DNS設定",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Blocked Response TTL(ブロック済み応答のTTL)",
|
"blocked_response_ttl": "Blocked Response TTL(ブロック済み応答のTTL)",
|
||||||
"blocked_response_ttl_desc": "フィルタリングされた応答をクライアントがキャッシュしておく時間(秒)を指定します。",
|
"blocked_response_ttl_desc": "フィルタリングされた応答をクライアントがキャッシュしておく時間(秒)を指定します。",
|
||||||
"form_enter_blocked_response_ttl": "ブロック済み応答のTTL(秒単位)を入力してください",
|
"form_enter_blocked_response_ttl": "ブロック済み応答のTTL(秒単位)を入力してください",
|
||||||
"upstream_timeout": "Upstream timeout(アップストリームタイムアウト)",
|
|
||||||
"upstream_timeout_desc": "アップストリームサーバーからの応答を待つ秒数を指定します。",
|
|
||||||
"form_enter_upstream_timeout": "アップストリームサーバーのタイムアウト時間を秒単位で入力します。",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "IPv6アドレスの解決を無効にする",
|
"disable_ipv6": "IPv6アドレスの解決を無効にする",
|
||||||
"disable_ipv6_desc": "IPv6アドレス(タイプAAAA)に対するDNSクエリをすべて破棄し、HTTPS応答から IPv6 hint を削除します。",
|
"disable_ipv6_desc": "IPv6アドレス(タイプAAAA)に対するDNSクエリをすべて破棄し、HTTPS応答から IPv6 hint を削除します。",
|
||||||
"fastest_addr": "最速のIPアドレス",
|
"fastest_addr": "最速のIPアドレス",
|
||||||
"fastest_addr_desc": "<b>すべての</b>DNSサーバーからの応答を待ち、各サーバーのTCP接続速度を測定し、最も接続速度の速いサーバーのIPアドレスを返します。<br/>※このモードでは、1つまたは複数のアップストリームサーバーが応答しない場合、DNSクエリが大幅に遅くなることがあります。アップストリームサーバーが安定していることを確認し、アップストリームタイムアウトは小さくしておいてください。",
|
"fastest_addr_desc": "すべてのDNSサーバーに処理要求し、全応答の中で最速のIPアドレスを返します。これにより、AdGuard HomeがすべてのDNSサーバーからの応答を待つ必要があるため、DNSクエリが遅くなりますが、全体的な接続性は向上します。",
|
||||||
"autofix_warning_text": "「修正」をクリックすると、AdGuardHomeはAdGuardHome DNSサーバを使用するようにシステムを構成します。",
|
"autofix_warning_text": "「修正」をクリックすると、AdGuardHomeはAdGuardHome DNSサーバを使用するようにシステムを構成します。",
|
||||||
"autofix_warning_list": "次のタスクを実行します:<0>システムDNSStubListenerを非アクティブ化します</0> <0>DNSサーバのアドレスを127.0.0.1に設定します</0> <0>/etc/resolv.confのシンボリックリンクの対象を/run/systemd/resolve/resolv.confに置換します</0> <0>DNSStubListenerを停止します(systemd-resolvedサービスをリロードします)</0>",
|
"autofix_warning_list": "次のタスクを実行します:<0>システムDNSStubListenerを非アクティブ化します</0> <0>DNSサーバのアドレスを127.0.0.1に設定します</0> <0>/etc/resolv.confのシンボリックリンクの対象を/run/systemd/resolve/resolv.confに置換します</0> <0>DNSStubListenerを停止します(systemd-resolvedサービスをリロードします)</0>",
|
||||||
"autofix_warning_result": "その結果、システムからのすべてのDNSリクエストは、デフォルトでAdGuard Homeによって処理されます。",
|
"autofix_warning_result": "その結果、システムからのすべてのDNSリクエストは、デフォルトでAdGuard Homeによって処理されます。",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "理由: {{reason}}",
|
"check_reason": "理由: {{reason}}",
|
||||||
"check_service": "サービス名: {{service}}",
|
"check_service": "サービス名: {{service}}",
|
||||||
"check_hostname": "ホスト名またはドメイン名",
|
|
||||||
"check_client_id": "クライアント識別子 (ClientID または IP アドレス)",
|
|
||||||
"check_enter_client_id": "クライアント識別子を入力してください",
|
|
||||||
"check_dns_record": "DNSレコードタイプ(DNS record type)を選択",
|
|
||||||
"service_name": "サービス名",
|
"service_name": "サービス名",
|
||||||
"check_not_found": "フィルタ一覧には見つかりません",
|
"check_not_found": "フィルタ一覧には見つかりません",
|
||||||
"client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?",
|
"client_confirm_block": "クライアント\"{{ip}}\"をブロックしてもよろしいですか?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "ブロックリスト",
|
"blocklist": "ブロックリスト",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "キャッシュサイズ",
|
"cache_size": "キャッシュサイズ",
|
||||||
"cache_size_desc": "DNSキャッシュサイズ(バイト単位)※キャッシュを無効化するには、「0」(ゼロ)にしてください。",
|
"cache_size_desc": "DNSキャッシュサイズ(バイト単位)。※キャッシュを無効化するには、この欄を空してください。",
|
||||||
"cache_ttl_min_override": "最小TTLの上書き(秒単位)",
|
"cache_ttl_min_override": "最小TTLの上書き(秒単位)",
|
||||||
"cache_ttl_max_override": "最大TTLの上書き(秒単位)",
|
"cache_ttl_max_override": "最大TTLの上書き(秒単位)",
|
||||||
"enter_cache_size": "キャッシュサイズ(バイト単位)を入力してください",
|
"enter_cache_size": "キャッシュサイズ(バイト単位)を入力してください",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
|
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
|
||||||
"parallel_requests": "병렬 처리 요청",
|
"parallel_requests": "병렬 처리 요청",
|
||||||
"load_balancing": "로드 밸런싱",
|
"load_balancing": "로드 밸런싱",
|
||||||
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다.<br/>AdGuard Home은 가중 무작위 알고리즘을 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
|
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다. AdGuard Home은 가중 무작위 알고리즘을 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
|
||||||
"bootstrap_dns": "부트스트랩 DNS 서버",
|
"bootstrap_dns": "부트스트랩 DNS 서버",
|
||||||
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
|
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
|
||||||
"fallback_dns_title": "폴백 DNS 서버",
|
"fallback_dns_title": "폴백 DNS 서버",
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
"off": "OFF",
|
"off": "OFF",
|
||||||
"copyright": "Copyright",
|
"copyright": "Copyright",
|
||||||
"homepage": "홈페이지",
|
"homepage": "홈페이지",
|
||||||
"report_an_issue": "문제 신고",
|
"report_an_issue": "문제를 보고합니다",
|
||||||
"privacy_policy": "개인정보취급방침",
|
"privacy_policy": "개인정보취급방침",
|
||||||
"enable_protection": "보호 활성화",
|
"enable_protection": "보호 활성화",
|
||||||
"enabled_protection": "보호 활성화됨",
|
"enabled_protection": "보호 활성화됨",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
|
"use_adguard_parental": "AdGuard 자녀 보호 웹 서비스 사용",
|
||||||
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
|
"use_adguard_parental_hint": "AdGuard Home은 도메인에 성인 자료가 포함되어 있는지 확인합니다. 브라우징 보안 웹 서비스와 동일한 개인정보 보호 API를 사용함.",
|
||||||
"enforce_safe_search": "세이프서치 사용",
|
"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": "지정된 서버 없음",
|
"no_servers_specified": "지정된 서버 없음",
|
||||||
"general_settings": "일반 설정",
|
"general_settings": "일반 설정",
|
||||||
"dns_settings": "DNS 설정",
|
"dns_settings": "DNS 설정",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "차단된 TTL 응답",
|
"blocked_response_ttl": "차단된 TTL 응답",
|
||||||
"blocked_response_ttl_desc": "클라이언트가 필터링된 응답을 캐시해야 하는 시간(초)을 지정합니다.",
|
"blocked_response_ttl_desc": "클라이언트가 필터링된 응답을 캐시해야 하는 시간(초)을 지정합니다.",
|
||||||
"form_enter_blocked_response_ttl": "차단된 응답 TTL(초)을 입력하세요.",
|
"form_enter_blocked_response_ttl": "차단된 응답 TTL(초)을 입력하세요.",
|
||||||
"upstream_timeout": "업스트림 제한 시간",
|
|
||||||
"upstream_timeout_desc": "업스트림 서버의 응답을 기다리는 시간(초)을 지정합니다.",
|
|
||||||
"form_enter_upstream_timeout": "업스트림 서버 응답 제한 시간을 초 단위로 입력하세요.",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "IPv6 주소 확인 비활성화",
|
"disable_ipv6": "IPv6 주소 확인 비활성화",
|
||||||
"disable_ipv6_desc": "IPv6 주소(AAAA 유형)에 대한 모든 DNS 쿼리를 무시하고 HTTPS 유형 응답에서 IPv6 데이터를 제거합니다.",
|
"disable_ipv6_desc": "IPv6 주소(AAAA 유형)에 대한 모든 DNS 쿼리를 무시하고 HTTPS 유형 응답에서 IPv6 데이터를 제거합니다.",
|
||||||
"fastest_addr": "가장 빠른 IP 주소",
|
"fastest_addr": "가장 빠른 IP 주소",
|
||||||
"fastest_addr_desc": "<b>모든</b> DNS 서버의 응답을 기다렸다가 각 서버의 TCP 연결 속도를 측정하여 연결 속도가 가장 빠른 서버의 IP 주소를 반환합니다.<br/>이 모드는 하나 이상의 업스트림 서버가 응답하지 않는 경우, DNS 쿼리 속도가 상당히 느려질 수 있습니다. 업스트림 서버가 안정적이고 업스트림 타임아웃이 짧은지 확인하세요.",
|
"fastest_addr_desc": "모든 DNS 서버에 쿼리를 수행한 다음 반응이 가장 빠른 IP주소를 반송합니다. AdGuard Home이 모든 DNS 서버의 응답을 기다려야 하기 때문에 DNS 쿼리 속도가 느려지지만 전반적인 연결이 향상됩니다.",
|
||||||
"autofix_warning_text": "'수정'을 클릭하면 AdGuard Home이 AdGuard Home DNS 서버를 사용하도록 시스템을 설정합니다.",
|
"autofix_warning_text": "'수정'을 클릭하면 AdGuard Home이 AdGuard Home DNS 서버를 사용하도록 시스템을 설정합니다.",
|
||||||
"autofix_warning_list": "다음 작업을 진행합니다: <0>DNSStubListener 시스템 비활성화</0> <0>DNS 서버 주소를 127.0.0.1로 설정</0> <0>/etc/resolv.conf의 심볼릭 링크 타겟을 /run/systemd/resolve/resolv.conf로 변경</0> <0>DNSStubListener 중지 (systemd-resolved 서비스 새로고침)</0>",
|
"autofix_warning_list": "다음 작업을 진행합니다: <0>DNSStubListener 시스템 비활성화</0> <0>DNS 서버 주소를 127.0.0.1로 설정</0> <0>/etc/resolv.conf의 심볼릭 링크 타겟을 /run/systemd/resolve/resolv.conf로 변경</0> <0>DNSStubListener 중지 (systemd-resolved 서비스 새로고침)</0>",
|
||||||
"autofix_warning_result": "결과적으로 시스템의 모든 DNS 요청은 기본적으로 AdGuard Home에 의해 처리됩니다.",
|
"autofix_warning_result": "결과적으로 시스템의 모든 DNS 요청은 기본적으로 AdGuard Home에 의해 처리됩니다.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "이유: {{reason}}",
|
"check_reason": "이유: {{reason}}",
|
||||||
"check_service": "서비스 이름: {{service}}",
|
"check_service": "서비스 이름: {{service}}",
|
||||||
"check_hostname": "호스트 이름 또는 도메인 이름",
|
|
||||||
"check_client_id": "클라이언트 식별자(클라이언트 ID 또는 IP 주소)",
|
|
||||||
"check_enter_client_id": "클라이언트 식별자 입력",
|
|
||||||
"check_dns_record": "DNS 레코드 유형 선택",
|
|
||||||
"service_name": "서비스 이름",
|
"service_name": "서비스 이름",
|
||||||
"check_not_found": "필터 목록에서 찾을 수 없음",
|
"check_not_found": "필터 목록에서 찾을 수 없음",
|
||||||
"client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?",
|
"client_confirm_block": "정말로 클라이언트 '{{ip}}'을(를) 차단하시겠습니까?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "차단 목록",
|
"blocklist": "차단 목록",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "캐시 크기",
|
"cache_size": "캐시 크기",
|
||||||
"cache_size_desc": "DNS 캐시 크기(바이트). 캐싱을 사용하지 않으려면 0으로 설정합니다.",
|
"cache_size_desc": "DNS 캐시 크기(바이트). 캐싱을 비활성화하려면 비워 둡니다.",
|
||||||
"cache_ttl_min_override": "최소 TTL (초) 무시",
|
"cache_ttl_min_override": "최소 TTL (초) 무시",
|
||||||
"cache_ttl_max_override": "최대 TTL (초) 무시",
|
"cache_ttl_max_override": "최대 TTL (초) 무시",
|
||||||
"enter_cache_size": "캐시 크기를 입력하세요",
|
"enter_cache_size": "캐시 크기를 입력하세요",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
|
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
|
||||||
"parallel_requests": "Parallelle verzoeken",
|
"parallel_requests": "Parallelle verzoeken",
|
||||||
"load_balancing": "Volume balanceren",
|
"load_balancing": "Volume balanceren",
|
||||||
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk.<br/>AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
|
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
|
||||||
"bootstrap_dns": "Bootstrap DNS-servers",
|
"bootstrap_dns": "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.",
|
"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",
|
"fallback_dns_title": "Back-up DNS-servers",
|
||||||
@@ -110,9 +110,9 @@
|
|||||||
"homepage": "Startpagina",
|
"homepage": "Startpagina",
|
||||||
"report_an_issue": "Rapporteer een probleem",
|
"report_an_issue": "Rapporteer een probleem",
|
||||||
"privacy_policy": "Privacybeleid",
|
"privacy_policy": "Privacybeleid",
|
||||||
"enable_protection": "Bescherming inschakelen",
|
"enable_protection": "Schakel bescherming in",
|
||||||
"enabled_protection": "Bescherming ingeschakeld",
|
"enabled_protection": "Bescherming ingeschakeld",
|
||||||
"disable_protection": "Bescherming uitschakelen",
|
"disable_protection": "Schakel bescherming uit",
|
||||||
"disabled_protection": "Bescherming uitgeschakeld",
|
"disabled_protection": "Bescherming uitgeschakeld",
|
||||||
"refresh_statics": "Ververs statistieken",
|
"refresh_statics": "Ververs statistieken",
|
||||||
"dns_query": "DNS-queries",
|
"dns_query": "DNS-queries",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Gebruik AdGuard Ouderlijk toezicht web service",
|
"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.",
|
"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_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",
|
"no_servers_specified": "Geen servers gespecificeerd",
|
||||||
"general_settings": "Algemene instellingen",
|
"general_settings": "Algemene instellingen",
|
||||||
"dns_settings": "DNS instellingen",
|
"dns_settings": "DNS instellingen",
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
"encryption_settings": "Encryptie instellingen",
|
"encryption_settings": "Encryptie instellingen",
|
||||||
"dhcp_settings": "DHCP instellingen",
|
"dhcp_settings": "DHCP instellingen",
|
||||||
"upstream_dns": "Upstream DNS-servers",
|
"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}}",
|
"upstream_dns_configured_in_file": "Geconfigureerd in {{path}}",
|
||||||
"test_upstream_btn": "Test upstream",
|
"test_upstream_btn": "Test upstream",
|
||||||
"upstreams": "Upstreams",
|
"upstreams": "Upstreams",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Geblokkeerde reactie TTL",
|
"blocked_response_ttl": "Geblokkeerde reactie TTL",
|
||||||
"blocked_response_ttl_desc": "Hiermee geef je op hoeveel seconden de clients een gefilterd antwoord in de cache moeten opslaan",
|
"blocked_response_ttl_desc": "Hiermee geef je op hoeveel seconden de clients een gefilterd antwoord in de cache moeten opslaan",
|
||||||
"form_enter_blocked_response_ttl": "Voer geblokkeerd antwoord TTL in (seconden)",
|
"form_enter_blocked_response_ttl": "Voer geblokkeerd antwoord TTL in (seconden)",
|
||||||
"upstream_timeout": "Upstream time-out",
|
|
||||||
"upstream_timeout_desc": "Geeft het aantal seconden aan dat moet worden gewacht op een reactie van de upstream-server",
|
|
||||||
"form_enter_upstream_timeout": "Voer de time-outduur van de upstream-server in seconden in",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-via-HTTPS",
|
"dns_over_https": "DNS-via-HTTPS",
|
||||||
"dns_over_tls": "DNS-via-TLS",
|
"dns_over_tls": "DNS-via-TLS",
|
||||||
@@ -327,10 +324,10 @@
|
|||||||
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in",
|
"rate_limit_whitelist_placeholder": "Voer één IP-adres per regel in",
|
||||||
"blocking_ipv4_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
"blocking_ipv4_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
||||||
"blocking_ipv6_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
"blocking_ipv6_desc": "IP-adres dat moet worden teruggegeven voor een geblokkeerd A-verzoek",
|
||||||
"blocking_mode_default": "Standaard: Reageer met een nul IP-adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
|
"blocking_mode_default": "Standaard: Reageer met een nul IP adres (0.0.0.0 for A; :: voor AAAA) wanneer geblokkeerd door een Adblock-type regel; reageer met het IP-adres dat is opgegeven in de regel wanneer geblokkeerd door een /etc/hosts type regel",
|
||||||
"blocking_mode_refused": "REFUSED: Antwoorden met REFUSED code",
|
"blocking_mode_refused": "REFUSED: Antwoorden met REFUSED code",
|
||||||
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
|
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
|
||||||
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP-adres (0.0.0.0 voor A; :: voor AAAA)",
|
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP address (0.0.0.0 voor A; :: voor AAAA)",
|
||||||
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
|
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
|
||||||
"theme_auto": "Automatisch",
|
"theme_auto": "Automatisch",
|
||||||
"theme_light": "Licht",
|
"theme_light": "Licht",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Oplossen IPv6-adressen uitschakelen",
|
"disable_ipv6": "Oplossen IPv6-adressen uitschakelen",
|
||||||
"disable_ipv6_desc": "Alle DNS-query's voor IPv6-adressen (type AAAA) verwijderen en IPv6-hints uit HTTPS-antwoorden verwijderen.",
|
"disable_ipv6_desc": "Alle DNS-query's voor IPv6-adressen (type AAAA) verwijderen en IPv6-hints uit HTTPS-antwoorden verwijderen.",
|
||||||
"fastest_addr": "Snelste IP adres",
|
"fastest_addr": "Snelste IP adres",
|
||||||
"fastest_addr_desc": "Wacht op reacties van <b>alle</b> DNS-servers, meet de TCP-verbindingssnelheid voor elke server en retourneer het IP-adres van de server met de hoogste verbindingssnelheid.<br/>Deze modus kan DNS-query's aanzienlijk vertragen als een of meer upstream-servers niet reageren. Zorg ervoor dat je upstream-servers stabiel zijn en dat je upstream-time-out laag is.",
|
"fastest_addr_desc": "Alle DNS-servers bevragen en het snelste IP adres terugkoppelen. Dit zal de DNS verzoeken vertragen omdat AdGuard Home moet wachten op de antwoorden van alles DNS-servers, maar verbetert wel de connectiviteit.",
|
||||||
"autofix_warning_text": "Als je op \"Repareren\" klikt, configureert AdGuard Home jouw systeem om de AdGuard Home DNS-server te gebruiken.",
|
"autofix_warning_text": "Als je op \"Repareren\" klikt, configureert AdGuard Home jouw systeem om de AdGuard Home DNS-server te gebruiken.",
|
||||||
"autofix_warning_list": "De volgende taken worden uitgevoerd: <0> Deactiveren van Systeem DNSStubListener</0> <0> DNS-serveradres instellen op 127.0.0.1 </0> <0> Symbolisch koppelingsdoel van /etc/resolv.conf vervangen door /run/systemd/resolve/resolv.conf </0> <0> Stop DNSStubListener (herlaad systemd-resolved service) </0>",
|
"autofix_warning_list": "De volgende taken worden uitgevoerd: <0> Deactiveren van Systeem DNSStubListener</0> <0> DNS-serveradres instellen op 127.0.0.1 </0> <0> Symbolisch koppelingsdoel van /etc/resolv.conf vervangen door /run/systemd/resolve/resolv.conf </0> <0> Stop DNSStubListener (herlaad systemd-resolved service) </0>",
|
||||||
"autofix_warning_result": "Als gevolg hiervan worden alle DNS-aanvragen van je systeem standaard door AdGuard Home verwerkt.",
|
"autofix_warning_result": "Als gevolg hiervan worden alle DNS-aanvragen van je systeem standaard door AdGuard Home verwerkt.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Reden: {{reason}}",
|
"check_reason": "Reden: {{reason}}",
|
||||||
"check_service": "Servicenaam: {{service}}",
|
"check_service": "Servicenaam: {{service}}",
|
||||||
"check_hostname": "Hostnaam of domeinnaam",
|
|
||||||
"check_client_id": "Client identificator (ClientID of IP-adres)",
|
|
||||||
"check_enter_client_id": "Voer Client identificator in",
|
|
||||||
"check_dns_record": "Selecteer type DNS-record",
|
|
||||||
"service_name": "Naam service",
|
"service_name": "Naam service",
|
||||||
"check_not_found": "Niet in je lijst met filters gevonden",
|
"check_not_found": "Niet in je lijst met filters gevonden",
|
||||||
"client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?",
|
"client_confirm_block": "Weet je zeker dat je client \"{{ip}}\" wil blokkeren?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Blokkeerlijst",
|
"blocklist": "Blokkeerlijst",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Cache grootte",
|
"cache_size": "Cache grootte",
|
||||||
"cache_size_desc": "DNS-cachegrootte (in bytes). Om caching uit te schakelen, stel deze in op 0.",
|
"cache_size_desc": "DNS-cachegrootte (in bytes). Leeg laten om caching uit te schakelen.",
|
||||||
"cache_ttl_min_override": "Minimale TTL overschrijven",
|
"cache_ttl_min_override": "Minimale TTL overschrijven",
|
||||||
"cache_ttl_max_override": "Maximale TTL overschrijven",
|
"cache_ttl_max_override": "Maximale TTL overschrijven",
|
||||||
"enter_cache_size": "Cache grootte invoeren (bytes)",
|
"enter_cache_size": "Cache grootte invoeren (bytes)",
|
||||||
@@ -702,13 +695,13 @@
|
|||||||
"disable_for_hours": "Voor {{count}} uur",
|
"disable_for_hours": "Voor {{count}} uur",
|
||||||
"disable_for_hours_plural": "Voor {{count}} uren",
|
"disable_for_hours_plural": "Voor {{count}} uren",
|
||||||
"disable_until_tomorrow": "Tot morgen",
|
"disable_until_tomorrow": "Tot morgen",
|
||||||
"disable_notify_for_seconds": "Bescherming uitschakelen voor {{count}} seconde",
|
"disable_notify_for_seconds": "Beveiliging uitschakelen voor {{count}} seconde",
|
||||||
"disable_notify_for_seconds_plural": "Bescherming uitschakelen voor {{count}} seconden",
|
"disable_notify_for_seconds_plural": "Beveiliging uitschakelen voor {{count}} seconden",
|
||||||
"disable_notify_for_minutes": "Bescherming uitschakelen voor {{count}} minuut",
|
"disable_notify_for_minutes": "Beveiliging uitschakelen voor {{count}} minuut",
|
||||||
"disable_notify_for_minutes_plural": "Bescherming uitschakelen voor {{count}} minuten",
|
"disable_notify_for_minutes_plural": "Beveiliging uitschakelen voor {{count}} minuten",
|
||||||
"disable_notify_for_hours": "Bescherming uitschakelen voor {{count}} uur",
|
"disable_notify_for_hours": "Beveiliging uitschakelen voor {{count}} uur",
|
||||||
"disable_notify_for_hours_plural": "Bescherming uitschakelen voor {{count}} uren",
|
"disable_notify_for_hours_plural": "Beveiliging uitschakelen voor {{count}} uren",
|
||||||
"disable_notify_until_tomorrow": "Bescherming uitschakelen tot morgen",
|
"disable_notify_until_tomorrow": "Beveiliging uitschakelen tot morgen",
|
||||||
"enable_protection_timer": "Bescherming wordt ingeschakeld over {{time}}",
|
"enable_protection_timer": "Bescherming wordt ingeschakeld over {{time}}",
|
||||||
"custom_retention_input": "Voer retentie in uren in",
|
"custom_retention_input": "Voer retentie in uren in",
|
||||||
"custom_rotation_input": "Voer rotatie in uren in",
|
"custom_rotation_input": "Voer rotatie in uren in",
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
"stats_malware_phishing": "Blokkert skadevare/phishing",
|
"stats_malware_phishing": "Blokkert skadevare/phishing",
|
||||||
"stats_adult": "Blokkerte voksennettsteder",
|
"stats_adult": "Blokkerte voksennettsteder",
|
||||||
"stats_query_domain": "Mest forespurte domener",
|
"stats_query_domain": "Mest forespurte domener",
|
||||||
|
"for_last_24_hours": "de siste 24 timene",
|
||||||
"for_last_days": "for den siste {{count}} dagen",
|
"for_last_days": "for den siste {{count}} dagen",
|
||||||
"for_last_days_plural": "de siste {{count}} dagene",
|
"for_last_days_plural": "de siste {{count}} dagene",
|
||||||
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
|
"stats_disabled": "Statistikkene har blitt skrudd av. Du kan skru den på fra <0>innstillingssiden</0>.",
|
||||||
@@ -120,13 +121,13 @@
|
|||||||
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
|
"no_upstreams_data_found": "Ingen oppstrøms servere data funnet",
|
||||||
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
|
"number_of_dns_query_days": "Antall DNS-spørringer behandlet for de siste {{count}} dagene",
|
||||||
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
|
"number_of_dns_query_days_plural": "Antall DNS-forespørsler som ble behandlet de siste {{count}} dagene",
|
||||||
|
"number_of_dns_query_24_hours": "Antall DNS-forespørsler som ble behandlet de siste 24 timene",
|
||||||
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
|
"number_of_dns_query_blocked_24_hours": "Antall DNS-forespørsler som ble blokkert av adblock-filtre, hosts-lister, og domene-lister",
|
||||||
"number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul",
|
"number_of_dns_query_blocked_24_hours_by_sec": "Antall DNS-forespørsler som ble blokkert av AdGuard sin nettlesersikkerhetsmodul",
|
||||||
"number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert",
|
"number_of_dns_query_blocked_24_hours_adult": "Antall voksennettsteder som ble blokkert",
|
||||||
"enforced_save_search": "Påtvungede barnevennlige søk",
|
"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",
|
"number_of_dns_query_to_safe_search": "Antall DNS-forespørsler til søkemotorer der \"Safe Search\" ble fremtvunget",
|
||||||
"average_processing_time": "Gjennomsnittlig behandlingstid",
|
"average_processing_time": "Gjennomsnittlig behandlingstid",
|
||||||
"response_time": "Responstid",
|
|
||||||
"average_processing_time_hint": "Gjennomsnittstid for behandling av DNS-forespørsler i millisekunder",
|
"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",
|
"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.",
|
"filters_block_toggle_hint": "Du kan sette opp blokkeringsoppføringer i <a>Filtre</a>-innstillingene.",
|
||||||
@@ -264,7 +265,6 @@
|
|||||||
"custom_ip": "Tilpasset IP",
|
"custom_ip": "Tilpasset IP",
|
||||||
"blocking_ipv4": "IPv4-blokkering",
|
"blocking_ipv4": "IPv4-blokkering",
|
||||||
"blocking_ipv6": "IPv6-blokkering",
|
"blocking_ipv6": "IPv6-blokkering",
|
||||||
"blocked_response_ttl": "Blokkerte svars TTL",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -626,6 +626,7 @@
|
|||||||
"use_saved_key": "Bruk den tidligere lagrede nøkkelen",
|
"use_saved_key": "Bruk den tidligere lagrede nøkkelen",
|
||||||
"parental_control": "Foreldrekontroll",
|
"parental_control": "Foreldrekontroll",
|
||||||
"safe_browsing": "Sikker surfing",
|
"safe_browsing": "Sikker surfing",
|
||||||
|
"served_from_cache": "{{value}} <i>(formidlet fra mellomlageret)</i>",
|
||||||
"theme_dark_desc": "Mørkt tema",
|
"theme_dark_desc": "Mørkt tema",
|
||||||
"theme_light_desc": "Lyst tema",
|
"theme_light_desc": "Lyst tema",
|
||||||
"disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen",
|
"disable_notify_until_tomorrow": "Deaktiver beskyttelsen til i morgen",
|
||||||
|
|||||||
@@ -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.",
|
"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",
|
"parallel_requests": "Równoległe żądania",
|
||||||
"load_balancing": "Równoważenie obciążenia",
|
"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": "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.",
|
"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",
|
"fallback_dns_title": "Rezerwowe serwery DNS",
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
"stats_query_domain": "Najczęściej wyszukiwane domeny",
|
"stats_query_domain": "Najczęściej wyszukiwane domeny",
|
||||||
"for_last_hours": "w ciągu ostatniej {{count}} godziny",
|
"for_last_hours": "w ciągu ostatniej {{count}} godziny",
|
||||||
"for_last_hours_plural": "w ciągu ostatnich {{count}} godzin",
|
"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",
|
"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": "Statystyki zostały wyłączone. Można je włączyć na <0>stronie ustawień</0>.",
|
||||||
"stats_disabled_short": "Statystyki zostały wyłączone",
|
"stats_disabled_short": "Statystyki zostały wyłączone",
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
"requests_count": "Licznik żądań",
|
"requests_count": "Licznik żądań",
|
||||||
"top_blocked_domains": "Najpopularniejsze zablokowane domeny",
|
"top_blocked_domains": "Najpopularniejsze zablokowane domeny",
|
||||||
"top_clients": "Główni klienci",
|
"top_clients": "Główni klienci",
|
||||||
"no_clients_found": "Nie znaleziono klientów",
|
"no_clients_found": "Nie znaleziono klienta",
|
||||||
"general_statistics": "Ogólne statystyki",
|
"general_statistics": "Ogólne statystyki",
|
||||||
"top_upstreams": "Często żądane serwery nadrzędne",
|
"top_upstreams": "Często żądane serwery nadrzędne",
|
||||||
"no_upstreams_data_found": "Brak danych dotyczących serwerów nadrzędnych",
|
"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": "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. ",
|
"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_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",
|
"no_servers_specified": "Nie określono serwerów",
|
||||||
"general_settings": "Ustawienia główne",
|
"general_settings": "Ustawienia główne",
|
||||||
"dns_settings": "Ustawienia DNS",
|
"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",
|
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
|
||||||
"parallel_requests": "Solicitações paralelas",
|
"parallel_requests": "Solicitações paralelas",
|
||||||
"load_balancing": "Balanceamento de carga",
|
"load_balancing": "Balanceamento de carga",
|
||||||
"load_balancing_desc": "Consulte um servidor upstream por vez.<br/>O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
|
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
|
||||||
"bootstrap_dns": "Servidores DNS de inicialização",
|
"bootstrap_dns": "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.",
|
"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",
|
"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": "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.",
|
"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_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",
|
"no_servers_specified": "Nenhum servidor especificado",
|
||||||
"general_settings": "Configurações gerais",
|
"general_settings": "Configurações gerais",
|
||||||
"dns_settings": "Configurações de DNS",
|
"dns_settings": "Configurações de DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Resposta bloqueada TTL",
|
"blocked_response_ttl": "Resposta bloqueada TTL",
|
||||||
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
|
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
|
||||||
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
|
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
|
||||||
"upstream_timeout": "Tempo limite de upstream",
|
|
||||||
"upstream_timeout_desc": "Especifica o número de segundos para esperar por uma resposta do servidor upstream",
|
|
||||||
"form_enter_upstream_timeout": "Insira a duração do tempo limite do servidor upstream em segundos",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-sobre-HTTPS",
|
"dns_over_https": "DNS-sobre-HTTPS",
|
||||||
"dns_over_tls": "DNS-sobre-TLS",
|
"dns_over_tls": "DNS-sobre-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Desativar resolução de endereços IPv6",
|
"disable_ipv6": "Desativar resolução de endereços IPv6",
|
||||||
"disable_ipv6_desc": "Descarta todas as consultas DNS para endereços IPv6 (tipo AAAA) e remove dicas de IPv6 das respostas HTTPS.",
|
"disable_ipv6_desc": "Descarta todas as consultas DNS para endereços IPv6 (tipo AAAA) e remove dicas de IPv6 das respostas HTTPS.",
|
||||||
"fastest_addr": "Endereço de IP mais rápido",
|
"fastest_addr": "Endereço de IP mais rápido",
|
||||||
"fastest_addr_desc": "Aguarde as respostas de <b>todos</b> os servidores DNS, meça a velocidade da conexão TCP para cada servidor e retorne o endereço de IP do servidor com a velocidade de conexão mais rápida.<br/>Esse modo pode retardar significativamente as consultas de DNS, se um ou mais servidores DNS primários não estiverem respondendo. Certifique-se de que seus servidores DNS primários sejam estáveis e que seu tempo de espera para DNS seja baixo.",
|
"fastest_addr_desc": "Consulta todos os servidores DNS e retorna o endereço IP mais rápido entre todas as respostas. Isso torna as consultas DNS mais lentas, pois o AdGuard Home tem que esperar pelas respostas de todos os servidores DNS, mas melhora a conectividade geral.",
|
||||||
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
|
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
|
||||||
"autofix_warning_list": "Ele irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
|
"autofix_warning_list": "Ele irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
|
||||||
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por padrão.",
|
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por padrão.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Motivo: {{reason}}",
|
"check_reason": "Motivo: {{reason}}",
|
||||||
"check_service": "Nome do serviço: {{service}}",
|
"check_service": "Nome do serviço: {{service}}",
|
||||||
"check_hostname": "Nome do anfitrião ou nome de domínio",
|
|
||||||
"check_client_id": "Identificador do cliente (ClienteID ou endereço de IP)",
|
|
||||||
"check_enter_client_id": "Insira o identificador do cliente",
|
|
||||||
"check_dns_record": "Selecione o tipo de registro DNS",
|
|
||||||
"service_name": "Nome do serviço",
|
"service_name": "Nome do serviço",
|
||||||
"check_not_found": "Não encontrado em suas listas de filtros",
|
"check_not_found": "Não encontrado em suas listas de filtros",
|
||||||
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Lista de bloqueio",
|
"blocklist": "Lista de bloqueio",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Tamanho do cache",
|
"cache_size": "Tamanho do cache",
|
||||||
"cache_size_desc": "Tamanho do cache do DNS (em bytes). Para desativar o cache, defina como 0.",
|
"cache_size_desc": "Tamanho do cache do DNS (em bytes). Para desativar o cache, deixe em branco.",
|
||||||
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
|
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
|
||||||
"cache_ttl_max_override": "Sobrepor o TTL máximo",
|
"cache_ttl_max_override": "Sobrepor o TTL máximo",
|
||||||
"enter_cache_size": "Digite o tamanho do cache (bytes)",
|
"enter_cache_size": "Digite o tamanho do cache (bytes)",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
|
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
|
||||||
"parallel_requests": "Solicitações paralelas",
|
"parallel_requests": "Solicitações paralelas",
|
||||||
"load_balancing": "Balanceamento de carga",
|
"load_balancing": "Balanceamento de carga",
|
||||||
"load_balancing_desc": "Consulta um servidor upstream de cada vez. <br/>O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas falhadas e o menor tempo médio de pesquisa.",
|
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
|
||||||
"bootstrap_dns": "Servidores DNS de arranque",
|
"bootstrap_dns": "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.",
|
"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",
|
"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": "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.",
|
"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_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",
|
"no_servers_specified": "Nenhum servidor especificado",
|
||||||
"general_settings": "Definições gerais",
|
"general_settings": "Definições gerais",
|
||||||
"dns_settings": "Definições de DNS",
|
"dns_settings": "Definições de DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Resposta bloqueada TTL",
|
"blocked_response_ttl": "Resposta bloqueada TTL",
|
||||||
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
|
"blocked_response_ttl_desc": "Especifica por quantos segundos os clientes devem armazenar em cache uma resposta filtrada",
|
||||||
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
|
"form_enter_blocked_response_ttl": "Insira o TTL da resposta bloqueada (segundos)",
|
||||||
"upstream_timeout": "Tempo esgotado de upstream",
|
|
||||||
"upstream_timeout_desc": "Especifica o número de segundos a aguardar por uma resposta do servidor upstream",
|
|
||||||
"form_enter_upstream_timeout": "Insira a duração do tempo esgotado do servidor upstream em segundos",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-sobre-HTTPS",
|
"dns_over_https": "DNS-sobre-HTTPS",
|
||||||
"dns_over_tls": "DNS-sobre-TLS",
|
"dns_over_tls": "DNS-sobre-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Desativar resolução de endereços IPv6",
|
"disable_ipv6": "Desativar resolução de endereços IPv6",
|
||||||
"disable_ipv6_desc": "Descarte todas as consultas DNS para endereços IPv6 (tipo AAAA) e remova as dicas IPv6 das respostas HTTPS.",
|
"disable_ipv6_desc": "Descarte todas as consultas DNS para endereços IPv6 (tipo AAAA) e remova as dicas IPv6 das respostas HTTPS.",
|
||||||
"fastest_addr": "Endereço de IP mais rápido",
|
"fastest_addr": "Endereço de IP mais rápido",
|
||||||
"fastest_addr_desc": "Aguarda por respostas de <b>todos</b> os servidores DNS, mede a velocidade da ligação TCP para cada servidor e devolva o endereço IP do servidor com a velocidade de ligação mais rápida.<br/>Este modo pode abrandar significativamente as consultas DNS, se um ou mais servidores upstream não estiverem a responder. Certifique-se de que os seus servidores upstream são estáveis e que o tempo esgotado de upstream é baixo.",
|
"fastest_addr_desc": "Consulta todos os servidores DNS e retorna o endereço IP mais rápido entre todas as respostas. Isso torna as consultas DNS mais lentas, pois o AdGuard Home tem que esperar pelas respostas de todos os servidores DNS, mas melhora a conectividade geral.",
|
||||||
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
|
"autofix_warning_text": "Se clicar em \"Corrigir\", o AdGuardHome irá configurar o seu sistema para utilizar o servidor DNS do AdGuardHome.",
|
||||||
"autofix_warning_list": "Irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
|
"autofix_warning_list": "Irá realizar estas tarefas: <0>Desativar sistema DNSStubListener</0> <0>Definir endereço do servidor DNS para 127.0.0.1</0> <0>Substituir o alvo simbólico do link /etc/resolv.conf para /run/systemd/resolv.conf</0> <0>Parar DNSStubListener (recarregar serviço resolvido pelo sistema)</0>",
|
||||||
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por predefinição.",
|
"autofix_warning_result": "Como resultado, todos as solicitações DNS do seu sistema serão processadas pelo AdGuard Home por predefinição.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Motivo: {{reason}}",
|
"check_reason": "Motivo: {{reason}}",
|
||||||
"check_service": "Nome do serviço: {{service}}",
|
"check_service": "Nome do serviço: {{service}}",
|
||||||
"check_hostname": "Nome do hospedeiro ou nome de domínio",
|
|
||||||
"check_client_id": "Identificador do cliente (ClientID ou endereço IP)",
|
|
||||||
"check_enter_client_id": "Insira o identificador do cliente",
|
|
||||||
"check_dns_record": "Selecione o tipo de registro DNS",
|
|
||||||
"service_name": "Nome do serviço",
|
"service_name": "Nome do serviço",
|
||||||
"check_not_found": "Não encontrado nas tuas listas de filtros",
|
"check_not_found": "Não encontrado nas tuas listas de filtros",
|
||||||
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
"client_confirm_block": "Você tem certeza de que deseja bloquear o cliente \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Lista de bloqueio",
|
"blocklist": "Lista de bloqueio",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Tamanho do cache",
|
"cache_size": "Tamanho do cache",
|
||||||
"cache_size_desc": "Tamanho do cache DNS (em bytes). Para desativar o cache, defina como 0.",
|
"cache_size_desc": "Tamanho do cache DNS (em bytes). Para desativar o cache, deixar o campo vazio.",
|
||||||
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
|
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
|
||||||
"cache_ttl_max_override": "Sobrepor o TTL máximo",
|
"cache_ttl_max_override": "Sobrepor o TTL máximo",
|
||||||
"enter_cache_size": "Digite o tamanho do cache (bytes)",
|
"enter_cache_size": "Digite o tamanho do cache (bytes)",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
|
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
|
||||||
"parallel_requests": "Параллельные запросы",
|
"parallel_requests": "Параллельные запросы",
|
||||||
"load_balancing": "Распределение нагрузки\n",
|
"load_balancing": "Распределение нагрузки\n",
|
||||||
"load_balancing_desc": "Запрашивать по одному upstream-серверу.<br/>AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
|
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
|
||||||
"bootstrap_dns": "Bootstrap DNS-серверы",
|
"bootstrap_dns": "Bootstrap DNS-серверы",
|
||||||
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
|
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
|
||||||
"fallback_dns_title": "Резервные DNS-серверы",
|
"fallback_dns_title": "Резервные DNS-серверы",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
|
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
|
||||||
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
|
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
|
||||||
"enforce_safe_search": "Включить безопасный поиск",
|
"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": "Нет указанных серверов",
|
"no_servers_specified": "Нет указанных серверов",
|
||||||
"general_settings": "Основные настройки",
|
"general_settings": "Основные настройки",
|
||||||
"dns_settings": "Настройки DNS",
|
"dns_settings": "Настройки DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "TTL заблокированного ответа",
|
"blocked_response_ttl": "TTL заблокированного ответа",
|
||||||
"blocked_response_ttl_desc": "Указывает, в течение скольких секунд клиенты должны кешировать отфильтрованный ответ",
|
"blocked_response_ttl_desc": "Указывает, в течение скольких секунд клиенты должны кешировать отфильтрованный ответ",
|
||||||
"form_enter_blocked_response_ttl": "Введите TTL заблокированного ответа (в секундах)",
|
"form_enter_blocked_response_ttl": "Введите TTL заблокированного ответа (в секундах)",
|
||||||
"upstream_timeout": "Время ожидания ответов от upstream-серверов",
|
|
||||||
"upstream_timeout_desc": "Длительность ожидания ответа от upstream-серверов в секундах",
|
|
||||||
"form_enter_upstream_timeout": "Введите время ожидания для upstream-сервера в секундах",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Отключить обработку IPv6-адресов",
|
"disable_ipv6": "Отключить обработку IPv6-адресов",
|
||||||
"disable_ipv6_desc": "Игнорировать все DNS-запросы адресов IPv6 (тип AAAA) и удалять IPv6-данные из ответов типа HTTPS.",
|
"disable_ipv6_desc": "Игнорировать все DNS-запросы адресов IPv6 (тип AAAA) и удалять IPv6-данные из ответов типа HTTPS.",
|
||||||
"fastest_addr": "Самый быстрый IP-адрес",
|
"fastest_addr": "Самый быстрый IP-адрес",
|
||||||
"fastest_addr_desc": "Дождаться ответов от <b>всех</b> DNS-серверов, измерить скорость TCP-соединения для каждого сервера и вернуть IP-адрес сервера с самой высокой скоростью соединения.<br/>Этот режим может значительно замедлить выполнение DNS-запросов, если один или несколько серверов не отвечают. Убедитесь, что ваши серверы работают стабильно, а время ожидания серверов мало.",
|
"fastest_addr_desc": "Опросить все DNS-серверы и вернуть самый быстрый IP-адрес из полученных ответов. Это замедлит DNS-запросы, так как нужно будет дождаться ответов со всех DNS-серверов, но улучшит соединение.",
|
||||||
"autofix_warning_text": "При нажатии «Исправить» AdGuard Home настроит вашу систему на использование DNS-сервера AdGuard Home.",
|
"autofix_warning_text": "При нажатии «Исправить» AdGuard Home настроит вашу систему на использование DNS-сервера AdGuard Home.",
|
||||||
"autofix_warning_list": "Будут выполняться следующие задачи: <0>Деактивировать системный DNSStubListener</0> <0>Установить адрес сервера DNS на 127.0.0.1</0> <0>Создать символическую ссылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Остановить DNSStubListener (перезагрузить системную службу)</0>.",
|
"autofix_warning_list": "Будут выполняться следующие задачи: <0>Деактивировать системный DNSStubListener</0> <0>Установить адрес сервера DNS на 127.0.0.1</0> <0>Создать символическую ссылку /etc/resolv.conf на /run/systemd/resolve/resolv.conf</0> <0>Остановить DNSStubListener (перезагрузить системную службу)</0>.",
|
||||||
"autofix_warning_result": "В результате все DNS-запросы от вашей системы будут по умолчанию обрабатываться AdGuard Home.\n",
|
"autofix_warning_result": "В результате все DNS-запросы от вашей системы будут по умолчанию обрабатываться AdGuard Home.\n",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Причина: {{reason}}",
|
"check_reason": "Причина: {{reason}}",
|
||||||
"check_service": "Название сервиса: {{service}}",
|
"check_service": "Название сервиса: {{service}}",
|
||||||
"check_hostname": "Имя хоста или домена",
|
|
||||||
"check_client_id": "Идентификатор клиента (ClientID или IP-адрес)",
|
|
||||||
"check_enter_client_id": "Введите идентификатор клиента",
|
|
||||||
"check_dns_record": "Выберите тип DNS-записи",
|
|
||||||
"service_name": "Имя сервиса",
|
"service_name": "Имя сервиса",
|
||||||
"check_not_found": "Не найдено в вашем списке фильтров",
|
"check_not_found": "Не найдено в вашем списке фильтров",
|
||||||
"client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?",
|
"client_confirm_block": "Вы уверены, что хотите заблокировать клиента «{{ip}}»?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Чёрный список",
|
"blocklist": "Чёрный список",
|
||||||
"milliseconds_abbreviation": "мс",
|
"milliseconds_abbreviation": "мс",
|
||||||
"cache_size": "Размер кеша",
|
"cache_size": "Размер кеша",
|
||||||
"cache_size_desc": "Размер кеша DNS (в байтах). Чтобы отключить кеширование, установите значение 0.",
|
"cache_size_desc": "Размера кеша DNS (в байтах). Чтобы отключить кэширование, оставьте поле пустым.",
|
||||||
"cache_ttl_min_override": "Переопределить минимальный TTL",
|
"cache_ttl_min_override": "Переопределить минимальный TTL",
|
||||||
"cache_ttl_max_override": "Переопределить максимальный TTL",
|
"cache_ttl_max_override": "Переопределить максимальный TTL",
|
||||||
"enter_cache_size": "Введите размер кеша (в байтах)",
|
"enter_cache_size": "Введите размер кеша (в байтах)",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"local_ptr_desc": "ස්ථානීය PTR විමසුම් සඳහා ඇඩ්ගාර්ඩ් හෝම් භාවිතා කරන ව.නා.ප. සේවාදායක. මෙම සේවාදායක පුද්ගලික අ.ජා.කෙ. ලිපින පරාසවල PTR විමසුම් විසඳීමට භාවිතා කරයි, උදාහරණයක් ලෙස ප්රතිවර්ත ව.නා.ප. භාවිතයෙන් \"192.168.12.34\". මෙය සකසා නැති නම්, ඇඩ්ගාර්ඩ් හෝම් හි ලිපින සඳහා හැරුනු විට ඔබගේ මෙහෙයුම් පද්ධතියේ පෙරනිමි ව.නා.ප. විසදුම්වල ලිපින භාවිතා කරයි.",
|
"local_ptr_desc": "ස්ථානීය PTR විමසුම් සඳහා ඇඩ්ගාර්ඩ් හෝම් භාවිතා කරන ව.නා.ප. සේවාදායක. මෙම සේවාදායක පුද්ගලික අ.ජා.කෙ. ලිපින පරාසවල PTR විමසුම් විසඳීමට භාවිතා කරයි, උදාහරණයක් ලෙස ප්රතිවර්ත ව.නා.ප. භාවිතයෙන් \"192.168.12.34\". මෙය සකසා නැති නම්, ඇඩ්ගාර්ඩ් හෝම් හි ලිපින සඳහා හැරුනු විට ඔබගේ මෙහෙයුම් පද්ධතියේ පෙරනිමි ව.නා.ප. විසදුම්වල ලිපින භාවිතා කරයි.",
|
||||||
"local_ptr_default_resolver": "පෙරනිමි පරිදි, ඇඩ්ගාර්ඩ් හෝම් පහත ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතා කරයි: {{ip}}.",
|
"local_ptr_default_resolver": "පෙරනිමි පරිදි, ඇඩ්ගාර්ඩ් හෝම් පහත ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතා කරයි: {{ip}}.",
|
||||||
"local_ptr_no_default_resolver": "ඇඩ්ගාර්ඩ් හෝම් හට මෙම පද්ධතිය සඳහා සුදුසු පුද්ගලික ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු නිශ්චය කරගත නොහැකි විය.",
|
"local_ptr_no_default_resolver": "ඇඩ්ගාර්ඩ් හෝම් හට මෙම පද්ධතිය සඳහා සුදුසු පුද්ගලික ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු නිශ්චය කරගත නොහැකි විය.",
|
||||||
"local_ptr_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
|
"local_ptr_placeholder": "පේළියකට එක් සේවාදායක ලිපිනය බැගින් යොදන්න",
|
||||||
"resolve_clients_title": "අනුග්රාහකවල අ.ජා.කෙ. ලිපින ප්රතිවර්ත විසඳීම සබල කරන්න",
|
"resolve_clients_title": "අනුග්රාහකවල අ.ජා.කෙ. ලිපින ප්රතිවර්ත විසඳීම සබල කරන්න",
|
||||||
"use_private_ptr_resolvers_title": "පෞද්. ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතය",
|
"use_private_ptr_resolvers_title": "පෞද්. ප්රතිවර්ත ව.නා.ප. පිළිවිසඳු භාවිතය",
|
||||||
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායක පරීක්ෂා කරන්න",
|
"check_dhcp_servers": "ග.ධා.වි.කෙ. සේවාදායක පරීක්ෂා කරන්න",
|
||||||
@@ -102,6 +102,7 @@
|
|||||||
"stats_malware_phishing": "අවහිර කළ ද්වේශාංග/තතුබෑම්",
|
"stats_malware_phishing": "අවහිර කළ ද්වේශාංග/තතුබෑම්",
|
||||||
"stats_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි",
|
"stats_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි",
|
||||||
"stats_query_domain": "ප්රචලිත විමසන ලද වසම්",
|
"stats_query_domain": "ප්රචලිත විමසන ලද වසම්",
|
||||||
|
"for_last_24_hours": "පසුගිය පැය 24 සඳහා",
|
||||||
"for_last_days": "පසුගිය දවස් {{count}} සඳහා",
|
"for_last_days": "පසුගිය දවස් {{count}} සඳහා",
|
||||||
"for_last_days_plural": "පසුගිය දවස් {{count}} සඳහා",
|
"for_last_days_plural": "පසුගිය දවස් {{count}} සඳහා",
|
||||||
"stats_disabled": "සංඛ්යාලේඛන අබල කර ඇත. එය <0>සැකසුම් පිටුවෙන්</0> සබල කළ හැකිය.",
|
"stats_disabled": "සංඛ්යාලේඛන අබල කර ඇත. එය <0>සැකසුම් පිටුවෙන්</0> සබල කළ හැකිය.",
|
||||||
@@ -114,15 +115,13 @@
|
|||||||
"general_statistics": "පොදු සංඛ්යාලේඛන",
|
"general_statistics": "පොදු සංඛ්යාලේඛන",
|
||||||
"number_of_dns_query_days": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
"number_of_dns_query_days": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||||
"number_of_dns_query_days_plural": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
"number_of_dns_query_days_plural": "පසුගිය දවස් {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||||
"number_of_dns_query_hours": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
"number_of_dns_query_24_hours": "පසුගිය පැය 24 සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
||||||
"number_of_dns_query_hours_plural": "පසුගිය පැය {{count}} සඳහා සැකසූ ව.නා.ප. විමසුම් ගණන",
|
|
||||||
"number_of_dns_query_blocked_24_hours": "දැන්වීම් වාරණ පෙරහන් සහ සත්කාරක වාරණ ලැයිස්තු මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
|
"number_of_dns_query_blocked_24_hours": "දැන්වීම් වාරණ පෙරහන් සහ සත්කාරක වාරණ ලැයිස්තු මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
|
||||||
"number_of_dns_query_blocked_24_hours_by_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්ෂණ ඒකකය මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
|
"number_of_dns_query_blocked_24_hours_by_sec": "ඇඩ්ගාර්ඩ් පිරික්සුම් ආරක්ෂණ ඒකකය මගින් අවහිර කළ ව.නා.ප. ඉල්ලීම් ගණන",
|
||||||
"number_of_dns_query_blocked_24_hours_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි ගණන",
|
"number_of_dns_query_blocked_24_hours_adult": "අවහිර කළ වැඩිහිටි වියමන අඩවි ගණන",
|
||||||
"enforced_save_search": "ආරක්ෂිත සෙවීම බලාත්මක කළ",
|
"enforced_save_search": "ආරක්ෂිත සෙවීම බලාත්මක කළ",
|
||||||
"number_of_dns_query_to_safe_search": "ආරක්ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
|
"number_of_dns_query_to_safe_search": "ආරක්ෂිත සෙවීම බලාත්මක කළ සෙවුම් යන්ත්ර සඳහා ව.නා.ප. ඉල්ලීම් ගණන",
|
||||||
"average_processing_time": "සාමාන්ය සැකසුම් කාලය",
|
"average_processing_time": "සාමාන්ය සැකසුම් කාලය",
|
||||||
"response_time": "ප්රතිචාර කාලය",
|
|
||||||
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්ය කාලය මිලි තත්පර වලින්",
|
"average_processing_time_hint": "ව.නා.ප. ඉල්ලීමක් සැකසීමේ සාමාන්ය කාලය මිලි තත්පර වලින්",
|
||||||
"block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
|
"block_domain_use_filters_and_hosts": "පෙරහන් හා සත්කාරක ගොනු භාවිතයෙන් වසම් අවහිර කරන්න",
|
||||||
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති <a>පෙරහන්</a> තුළ පිහිටුවිය හැකිය.",
|
"filters_block_toggle_hint": "ඔබට අවහිර කිරීමේ නීති <a>පෙරහන්</a> තුළ පිහිටුවිය හැකිය.",
|
||||||
@@ -131,7 +130,7 @@
|
|||||||
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
|
"use_adguard_parental": "ඇඩ්ගාර්ඩ් දෙමාපිය පාලන වියමන සේවාව භාවිතා කරන්න",
|
||||||
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්ෂා කරනු ඇත. එය පිරික්සුම් ආරක්ෂණ වියමන සේවාව මෙන් රහස්යතා හිතකාමී යෙ.ක්ර. අ.මු. (API) භාවිතා කරයි.",
|
"use_adguard_parental_hint": "වසමේ වැඩිහිටියන්ට අදාල කරුණු අඩංගු දැයි ඇඩ්ගාර්ඩ් හෝම් විසින් පරීක්ෂා කරනු ඇත. එය පිරික්සුම් ආරක්ෂණ වියමන සේවාව මෙන් රහස්යතා හිතකාමී යෙ.ක්ර. අ.මු. (API) භාවිතා කරයි.",
|
||||||
"enforce_safe_search": "ආරක්ෂිත සෙවුම භාවිතා කරන්න",
|
"enforce_safe_search": "ආරක්ෂිත සෙවුම භාවිතා කරන්න",
|
||||||
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්ර තුළ ආරක්ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, එකොසියා, යාන්ඩෙක්ස් සහ පික්සාබේ.",
|
"enforce_save_search_hint": "ඇඩ්ගාර්ඩ් හෝම් පහත සෙවුම් යන්ත්ර තුළ ආරක්ෂිත සෙවුම බලාත්මක කරනු ඇත: ගූගල්, යූටියුබ්, බින්ග්, ඩක්ඩක්ගෝ, යාන්ඩෙක්ස් සහ පික්සාබේ.",
|
||||||
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
|
"no_servers_specified": "සේවාදායක කිසිවක් නිශ්චිතව දක්වා නැත",
|
||||||
"general_settings": "පොදු සැකසුම්",
|
"general_settings": "පොදු සැකසුම්",
|
||||||
"dns_settings": "ව.නා.ප. සැකසුම්",
|
"dns_settings": "ව.නා.ප. සැකසුම්",
|
||||||
@@ -197,14 +196,12 @@
|
|||||||
"example_comment_hash": "# එසේම අදහස් දැක්වීමක්.",
|
"example_comment_hash": "# එසේම අදහස් දැක්වීමක්.",
|
||||||
"example_regex_meaning": "නිශ්චිතව දක්වා ඇති නිත්ය වාක්යවිධියට ගැළපෙන වසම් වෙත ප්රවේශය අවහිර කරයි.",
|
"example_regex_meaning": "නිශ්චිතව දක්වා ඇති නිත්ය වාක්යවිධියට ගැළපෙන වසම් වෙත ප්රවේශය අවහිර කරයි.",
|
||||||
"example_upstream_regular": "සාමාන්ය ව.නා.ප. (UDP හරහා);",
|
"example_upstream_regular": "සාමාන්ය ව.නා.ප. (UDP හරහා);",
|
||||||
"example_upstream_regular_port": "සාමාන්ය ව.නා.ප. (UDP හරහා, තොට සමඟ);",
|
|
||||||
"example_upstream_udp": "සාමාන්ය ව.නා.ප. (UDP, සත්කාරක-නම හරහා);",
|
"example_upstream_udp": "සාමාන්ය ව.නා.ප. (UDP, සත්කාරක-නම හරහා);",
|
||||||
"example_upstream_dot": "සංකේතිත <0>TLS-මගින්-ව.නා.ප.</0>;",
|
"example_upstream_dot": "සංකේතිත <0>TLS-මගින්-ව.නා.ප.</0>;",
|
||||||
"example_upstream_doh": "සංකේතිත <0>HTTPS-මගින්-ව.නා.ප.</0>;",
|
"example_upstream_doh": "සංකේතිත <0>HTTPS-මගින්-ව.නා.ප.</0>;",
|
||||||
"example_upstream_doq": "සංකේතිත <0>QUIC-මගින්-ව.නා.ප.</0>;",
|
"example_upstream_doq": "සංකේතිත <0>QUIC-මගින්-ව.නා.ප.</0>;",
|
||||||
"example_upstream_sdns": "<1>DNSCrypt</1> හෝ <2>HTTPS-මගින්-ව.නා.ප.</2> පිළිවිසඳු සඳහා <0>ව.නා.ප. මුද්දර</0>;",
|
"example_upstream_sdns": "<1>DNSCrypt</1> හෝ <2>HTTPS-මගින්-ව.නා.ප.</2> පිළිවිසඳු සඳහා <0>ව.නා.ප. මුද්දර</0>;",
|
||||||
"example_upstream_tcp": "සාමාන්ය ව.නා.ප. (TCP/ස.පා.කෙ. හරහා);",
|
"example_upstream_tcp": "සාමාන්ය ව.නා.ප. (TCP/ස.පා.කෙ. හරහා);",
|
||||||
"example_upstream_tcp_port": "සාමාන්ය ව.නා.ප. (TCP හරහා, තොට සමඟ);",
|
|
||||||
"example_upstream_tcp_hostname": "සාමාන්ය ව.නා.ප. (ස.පා.කෙ., සත්කාරක-නම හරහා);",
|
"example_upstream_tcp_hostname": "සාමාන්ය ව.නා.ප. (ස.පා.කෙ., සත්කාරක-නම හරහා);",
|
||||||
"all_lists_up_to_date_toast": "සියළුම ලැයිස්තු දැනටමත් යාවත්කාලීනයි",
|
"all_lists_up_to_date_toast": "සියළුම ලැයිස්තු දැනටමත් යාවත්කාලීනයි",
|
||||||
"dns_test_ok_toast": "සඳහන් කළ ව.නා.ප. සේවාදායක නිවැරදිව ක්රියා කරයි",
|
"dns_test_ok_toast": "සඳහන් කළ ව.නා.ප. සේවාදායක නිවැරදිව ක්රියා කරයි",
|
||||||
@@ -278,7 +275,6 @@
|
|||||||
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
|
"edns_use_custom_ip": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. යොදාගන්න",
|
||||||
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
|
"edns_use_custom_ip_desc": "EDNS සඳහා අභිරුචි අ.ජා.කෙ. භාවිතයට ඉඩදෙන්න",
|
||||||
"rate_limit_desc": "එක් අනුග්රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
|
"rate_limit_desc": "එක් අනුග්රාහකයකට ඉඩ දී ඇති තත්පරයට ඉල්ලීම් ගණන. එය 0 ලෙස සැකසීම යනුවෙන් අදහස් කරන්නේ සීමාවක් නැති බවයි.",
|
||||||
"rate_limit_whitelist_placeholder": "පේළියකට අ.ජා.කෙ. ලිපිනය බැගින් ලියන්න",
|
|
||||||
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
"blocking_ipv4_desc": "අවහිර කළ A ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||||
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
"blocking_ipv6_desc": "අවහිර කළ AAAA ඉල්ලීමක් සඳහා ආපසු එවිය යුතු අ.ජා.කෙ. (IP) ලිපිනය",
|
||||||
"blocking_mode_default": "පොදු: දැන්වීම් අවහිර කරන ආකාරයේ නීතියක් මගින් අවහිර කළ විට REFUSED සමඟ ප්රතිචාර දක්වයි; /etc/host-style ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා.කෙ. ලිපිනය සමඟ ප්රතිචාර දක්වයි",
|
"blocking_mode_default": "පොදු: දැන්වීම් අවහිර කරන ආකාරයේ නීතියක් මගින් අවහිර කළ විට REFUSED සමඟ ප්රතිචාර දක්වයි; /etc/host-style ආකාරයේ නීතියක් මගින් අවහිර කළ විට නීතියේ දක්වා ඇති අ.ජා.කෙ. ලිපිනය සමඟ ප්රතිචාර දක්වයි",
|
||||||
@@ -509,8 +505,8 @@
|
|||||||
"statistics_enable": "සංඛ්යාලේඛන සබල කරන්න",
|
"statistics_enable": "සංඛ්යාලේඛන සබල කරන්න",
|
||||||
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
|
"ignore_domains": "නොසලකන වසම් (පේළියකට එක බැගින්)",
|
||||||
"ignore_domains_title": "නොසලකන වසම්",
|
"ignore_domains_title": "නොසලකන වසම්",
|
||||||
"ignore_domains_desc_stats": "මෙම නීති වලට ගැළපෙන විමසුම් සංඛ්යාලේඛනයට නොලියැවෙයි",
|
"ignore_domains_desc_stats": "සංඛ්යාලේඛනයෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
|
||||||
"ignore_domains_desc_query": "විමසුම් සටහනට මෙම නීති වලට ගැළපෙන විමසුම් නොලියැවෙයි",
|
"ignore_domains_desc_query": "විමසුම් සටහනෙහි මෙම වසම් සඳහා විමසුම් නොලියැවෙයි",
|
||||||
"interval_hours": "පැය {{count}}",
|
"interval_hours": "පැය {{count}}",
|
||||||
"interval_hours_plural": "පැය {{count}}",
|
"interval_hours_plural": "පැය {{count}}",
|
||||||
"filters_configuration": "පෙරහන් වින්යාසය",
|
"filters_configuration": "පෙරහන් වින්යාසය",
|
||||||
@@ -619,8 +615,8 @@
|
|||||||
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
|
"use_saved_key": "පෙර සුරැකි යතුර භාවිතා කරන්න",
|
||||||
"parental_control": "දෙමාපිය පාලනය",
|
"parental_control": "දෙමාපිය පාලනය",
|
||||||
"safe_browsing": "ආරක්ෂිත පිරික්සුම",
|
"safe_browsing": "ආරක්ෂිත පිරික්සුම",
|
||||||
"served_from_cache_label": "නිහිතයෙන් සැපයිණි",
|
"served_from_cache": "{{value}} <i>(නිහිතයෙන් ගැනිණි)</i>",
|
||||||
"form_error_password_length": "මුරපදය අකුරු {{min}} සහ {{value}} ක් අතර විය යුතුය",
|
"form_error_password_length": "මුරපදය අවම වශයෙන් අකුරු {{value}} ක් දිගු විය යුතුමයි",
|
||||||
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
|
"anonymizer_notification": "<0>සටහන:</0> අ.ජා.කෙ. නිර්නාමිකකරණය සබලයි. ඔබට එය <1>පොදු සැකසුම්</1> හරහා අබල කිරීමට හැකිය .",
|
||||||
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
|
"confirm_dns_cache_clear": "ඔබට ව.නා.ප. නිහිතය හිස් කිරීමට වුවමනාද?",
|
||||||
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
|
"cache_cleared": "ව.නා.ප. නිහිතය හිස් කෙරිණි",
|
||||||
@@ -650,7 +646,6 @@
|
|||||||
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්යාලේඛන",
|
"log_and_stats_section_label": "විමසුම් සටහන හා සංඛ්යාලේඛන",
|
||||||
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්රාහකය යොදන්න එපා",
|
"ignore_query_log": "විමසුම් සටහනට මෙම අනුග්රාහකය යොදන්න එපා",
|
||||||
"ignore_statistics": "සංඛ්යාලේඛනයට මෙම අනුග්රාහකය යොදන්න එපා",
|
"ignore_statistics": "සංඛ්යාලේඛනයට මෙම අනුග්රාහකය යොදන්න එපා",
|
||||||
"schedule_services": "සේවා අවහිර විරාමය",
|
|
||||||
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
|
"schedule_invalid_select": "ආරම්භක වේලාව අවසන් වේලාවට කලින් විය යුතුය",
|
||||||
"schedule_select_days": "දවස් තෝරන්න",
|
"schedule_select_days": "දවස් තෝරන්න",
|
||||||
"schedule_timezone": "වේලා කලාපයක් තෝරන්න",
|
"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.",
|
"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",
|
"parallel_requests": "Paralelné dopyty",
|
||||||
"load_balancing": "Vyrovnávanie záťaže",
|
"load_balancing": "Vyrovnávanie záťaže",
|
||||||
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server.<br/>AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
|
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
|
||||||
"bootstrap_dns": "Bootstrap DNS servery",
|
"bootstrap_dns": "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é.",
|
"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",
|
"fallback_dns_title": "Záložné servery DNS",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "Použiť AdGuard službu Rodičovská kontrola",
|
"use_adguard_parental": "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.",
|
"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_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",
|
"no_servers_specified": "Neboli špecifikované žiadne servery",
|
||||||
"general_settings": "Všeobecné nastavenia",
|
"general_settings": "Všeobecné nastavenia",
|
||||||
"dns_settings": "Nastavenia DNS",
|
"dns_settings": "Nastavenia DNS",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Blokovaná odozva TTL",
|
"blocked_response_ttl": "Blokovaná odozva TTL",
|
||||||
"blocked_response_ttl_desc": "Určuje, na koľko sekúnd by mali klienti uložiť filtrovanú odozvu do vyrovnávacej pamäte",
|
"blocked_response_ttl_desc": "Určuje, na koľko sekúnd by mali klienti uložiť filtrovanú odozvu do vyrovnávacej pamäte",
|
||||||
"form_enter_blocked_response_ttl": "Zadajte TTL blokovanej odozve (sekundy)",
|
"form_enter_blocked_response_ttl": "Zadajte TTL blokovanej odozve (sekundy)",
|
||||||
"upstream_timeout": "Časový limit pre upstream",
|
|
||||||
"upstream_timeout_desc": "Určuje počet sekúnd čakania na odpoveď z upstream servera",
|
|
||||||
"form_enter_upstream_timeout": "Zadajte trvanie časového limitu upstream servera v sekundách",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -487,7 +484,7 @@
|
|||||||
"access_disallowed_title": "Nepovolení klienti",
|
"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ší dopyty od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
|
||||||
"access_blocked_title": "Nepovolené domény",
|
"access_blocked_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é",
|
"access_settings_saved": "Nastavenia prístupu úspešne uložené",
|
||||||
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
|
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
|
||||||
"updates_version_equal": "AdGuard Home je aktuálny",
|
"updates_version_equal": "AdGuard Home je aktuálny",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "Vypnúť rozlišovanie IPv6 adries",
|
"disable_ipv6": "Vypnúť rozlišovanie IPv6 adries",
|
||||||
"disable_ipv6_desc": "Ignorovať všetky dotazy DNS na adresy IPv6 (typ AAAA) a odstrániť IPv6 údaje z HTTPS odpovedí.",
|
"disable_ipv6_desc": "Ignorovať všetky dotazy DNS na adresy IPv6 (typ AAAA) a odstrániť IPv6 údaje z HTTPS odpovedí.",
|
||||||
"fastest_addr": "Najrýchlejšia IP adresa",
|
"fastest_addr": "Najrýchlejšia IP adresa",
|
||||||
"fastest_addr_desc": "Čaká na odpovede od <b>všetkých</b> DNS serverov, zmeria rýchlosť pripojenia TCP pre každý server a vráti adresu IP servera s najväčšou rýchlosťou pripojenia.<br/>Tento režim môže výrazne spomaliť DNS dopyty, ak jeden alebo viac upstream serverov neodpovedá. Uistite sa, že Vaše upstream servery sú stabilné a upstream upstream je nízky.",
|
"fastest_addr_desc": "Dopytovať všetky servery DNS a vrátiť najrýchlejšiu IP adresu zo všetkých odpovedí. Toto spomalí DNS dopyty, pretože AdGuard Home musí čakať na odpovede zo všetkých serverov DNS, ale zlepší sa celkové pripojenie.",
|
||||||
"autofix_warning_text": "Ak kliknete na „Opraviť“, AdGuardHome nakonfiguruje Váš systém tak, aby používal DNS server AdGuardHome.",
|
"autofix_warning_text": "Ak kliknete na „Opraviť“, AdGuardHome nakonfiguruje Váš systém tak, aby používal DNS server AdGuardHome.",
|
||||||
"autofix_warning_list": "Bude vykonávať tieto úlohy: <0>Deaktivovať systém DNSStubListener</0> <0>Nastaviť adresu servera DNS na 127.0.0.1</0> <0>Nahradiť cieľový symbolický odkaz /etc/resolv.conf na /run/systemd/resolve/resolv.conf</0> <0>Zastaviť službu DNSStubListener (znova načítať službu systemd-resolved)</0>",
|
"autofix_warning_list": "Bude vykonávať tieto úlohy: <0>Deaktivovať systém DNSStubListener</0> <0>Nastaviť adresu servera DNS na 127.0.0.1</0> <0>Nahradiť cieľový symbolický odkaz /etc/resolv.conf na /run/systemd/resolve/resolv.conf</0> <0>Zastaviť službu DNSStubListener (znova načítať službu systemd-resolved)</0>",
|
||||||
"autofix_warning_result": "Výsledkom bude, že všetky DNS dopyty z Vášho systému budú štandardne spracované službou AdGuard Home.",
|
"autofix_warning_result": "Výsledkom bude, že všetky DNS dopyty z Vášho systému budú štandardne spracované službou AdGuard Home.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Dôvod: {{reason}}",
|
"check_reason": "Dôvod: {{reason}}",
|
||||||
"check_service": "Meno služby: {{service}}",
|
"check_service": "Meno služby: {{service}}",
|
||||||
"check_hostname": "Názov hostiteľa alebo názov domény",
|
|
||||||
"check_client_id": "Identifikátor klienta (ClientID alebo IP adresa)",
|
|
||||||
"check_enter_client_id": "Zadajte identifikátor klienta",
|
|
||||||
"check_dns_record": "Vyberte typ DNS záznamu",
|
|
||||||
"service_name": "Názov služby",
|
"service_name": "Názov služby",
|
||||||
"check_not_found": "Nenašlo sa vo Vašom zozname filtrov",
|
"check_not_found": "Nenašlo sa vo Vašom zozname filtrov",
|
||||||
"client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?",
|
"client_confirm_block": "Naozaj chcete zablokovať klienta \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Zoznam blokovaní",
|
"blocklist": "Zoznam blokovaní",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Veľkosť cache",
|
"cache_size": "Veľkosť cache",
|
||||||
"cache_size_desc": "Veľkosť vyrovnávacej pamäte DNS (v bajtoch). Ak chcete vypnúť ukladanie do vyrovnávacej pamäte, nastavte hodnotu 0.",
|
"cache_size_desc": "Veľkosť vyrovnávacej pamäte DNS (v bajtoch). Ak chcete zakázať ukladanie do vyrovnávacej pamäte, ponechajte pole prázdne.",
|
||||||
"cache_ttl_min_override": "Prepísať minimálne TTL",
|
"cache_ttl_min_override": "Prepísať minimálne TTL",
|
||||||
"cache_ttl_max_override": "Prepísať maximálne TTL",
|
"cache_ttl_max_override": "Prepísať maximálne TTL",
|
||||||
"enter_cache_size": "Zadať veľkosť cache (v bajtoch)",
|
"enter_cache_size": "Zadať veľkosť cache (v bajtoch)",
|
||||||
|
|||||||
@@ -461,7 +461,7 @@
|
|||||||
"form_enter_mac": "Skriv in MAC",
|
"form_enter_mac": "Skriv in MAC",
|
||||||
"form_enter_id": "Ange identifierare",
|
"form_enter_id": "Ange identifierare",
|
||||||
"form_add_id": "Lägg till identifierare",
|
"form_add_id": "Lägg till identifierare",
|
||||||
"form_client_name": "Ange klientnamn",
|
"form_client_name": "Skriv in klientnamn",
|
||||||
"name": "Namn",
|
"name": "Namn",
|
||||||
"client_name": "Klient {{id}}",
|
"client_name": "Klient {{id}}",
|
||||||
"client_global_settings": "Använda globala inställningar",
|
"client_global_settings": "Använda globala inställningar",
|
||||||
@@ -674,6 +674,7 @@
|
|||||||
"use_saved_key": "Använd den tidigare sparade nyckeln",
|
"use_saved_key": "Använd den tidigare sparade nyckeln",
|
||||||
"parental_control": "Föräldrakontroll",
|
"parental_control": "Föräldrakontroll",
|
||||||
"safe_browsing": "Säker surfning",
|
"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",
|
"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>.",
|
"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?",
|
"confirm_dns_cache_clear": "Är du säker på att du vill rensa DNS-cache?",
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
"settings": "การตั้งค่า",
|
"settings": "การตั้งค่า",
|
||||||
"filters": "ตัวกรอง",
|
"filters": "ตัวกรอง",
|
||||||
"query_log": "บันทึกการสืบค้น",
|
"query_log": "บันทึกการสืบค้น",
|
||||||
"nothing_found": "ไม่พบอะไร",
|
|
||||||
"faq": "คำถามที่พบบ่อย",
|
"faq": "คำถามที่พบบ่อย",
|
||||||
"version": "รุ่น",
|
"version": "รุ่น",
|
||||||
"address": "ที่อยู่",
|
"address": "ที่อยู่",
|
||||||
@@ -350,7 +349,7 @@
|
|||||||
"statistics_configuration": "การกำหนดค่าสถิติ",
|
"statistics_configuration": "การกำหนดค่าสถิติ",
|
||||||
"statistics_retention": "การเก็บรักษาสถิติ",
|
"statistics_retention": "การเก็บรักษาสถิติ",
|
||||||
"statistics_retention_desc": "หากคุณลดค่าช่วงเวลาข้อมูลบางอย่างจะหายไป",
|
"statistics_retention_desc": "หากคุณลดค่าช่วงเวลาข้อมูลบางอย่างจะหายไป",
|
||||||
"statistics_clear": "ล้างสถิติ",
|
"statistics_clear": " ล้างค่าสถิติ",
|
||||||
"statistics_clear_confirm": "คุณแน่ใจหรือไม่ว่าต้องการล้างสถิติ?",
|
"statistics_clear_confirm": "คุณแน่ใจหรือไม่ว่าต้องการล้างสถิติ?",
|
||||||
"statistics_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บรักษาสถิติ? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
|
"statistics_retention_confirm": "คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนการเก็บรักษาสถิติ? หากคุณลดค่าช่วงเวลา ข้อมูลบางอย่างจะหายไป",
|
||||||
"statistics_cleared": "สถิติได้ถูกล้างเรียบร้อยแล้ว",
|
"statistics_cleared": "สถิติได้ถูกล้างเรียบร้อยแล้ว",
|
||||||
@@ -391,13 +390,10 @@
|
|||||||
"check_title": "ตรวจสอบการกรอง",
|
"check_title": "ตรวจสอบการกรอง",
|
||||||
"check_desc": "ตรวจสอบว่าชื่อโฮสต์ถูกกรอง",
|
"check_desc": "ตรวจสอบว่าชื่อโฮสต์ถูกกรอง",
|
||||||
"form_enter_host": "ป้อนชื่อโฮสต์",
|
"form_enter_host": "ป้อนชื่อโฮสต์",
|
||||||
"show_blocked_responses": "ปิดกั้นแล้ว",
|
"show_processed_responses": "การประมวลผล",
|
||||||
"show_whitelisted_responses": "รายการที่อนุญาต",
|
|
||||||
"show_processed_responses": "ประมวลผลแล้ว",
|
|
||||||
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
|
"blocked_adult_websites": "ถูกปิดกั้นโดยการควบคุมของผู้ปกครอง",
|
||||||
"safe_search": "ค้นหาอย่างปลอดภัย",
|
"safe_search": "ค้นหาอย่างปลอดภัย",
|
||||||
"blocklist": "บัญชีดำ",
|
"blocklist": "บัญชีดำ",
|
||||||
"allowed": "รายการที่อนุญาต",
|
|
||||||
"filter_category_other": "อื่น ๆ",
|
"filter_category_other": "อื่น ๆ",
|
||||||
"parental_control": "ควบคุมโดยผู้ปกครอง",
|
"parental_control": "ควบคุมโดยผู้ปกครอง",
|
||||||
"sunday_short": "อาทิตย์",
|
"sunday_short": "อาทิตย์",
|
||||||
|
|||||||
@@ -40,11 +40,11 @@
|
|||||||
"dhcp_ipv4_settings": "DHCP IPv4 Ayarları",
|
"dhcp_ipv4_settings": "DHCP IPv4 Ayarları",
|
||||||
"dhcp_ipv6_settings": "DHCP IPv6 Ayarları",
|
"dhcp_ipv6_settings": "DHCP IPv6 Ayarları",
|
||||||
"form_error_required": "Gerekli alan",
|
"form_error_required": "Gerekli alan",
|
||||||
"form_error_ip4_format": "IPv4 adresi geçersiz",
|
"form_error_ip4_format": "Geçersiz IPv4 adresi",
|
||||||
"form_error_ip4_gateway_format": "Ağ geçidi IPv4 adresi geçersiz",
|
"form_error_ip4_gateway_format": "Geçersiz ağ geçidi IPv4 adresi",
|
||||||
"form_error_ip6_format": "IPv6 adresi geçersiz",
|
"form_error_ip6_format": "Geçersiz IPv6 adresi",
|
||||||
"form_error_ip_format": "IP adresi geçersiz",
|
"form_error_ip_format": "Geçersiz IP adresi",
|
||||||
"form_error_mac_format": "MAC adresi geçersiz",
|
"form_error_mac_format": "Geçersiz MAC adresi",
|
||||||
"form_error_client_id_format": "İstemci Kimliği yalnızca sayılar, küçük harfler ve kısa çizgiler içermelidir",
|
"form_error_client_id_format": "İstemci Kimliği yalnızca sayılar, küçük harfler ve kısa çizgiler içermelidir",
|
||||||
"form_error_server_name": "Sunucu adı geçersiz",
|
"form_error_server_name": "Sunucu adı geçersiz",
|
||||||
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
|
"form_error_subnet": "\"{{cidr}}\" alt ağı, \"{{ip}}\" IP adresini içermiyor",
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
"ip": "IP",
|
"ip": "IP",
|
||||||
"dhcp_table_hostname": "Ana makine Adı",
|
"dhcp_table_hostname": "Ana makine Adı",
|
||||||
"dhcp_table_expires": "Bitiş tarihi",
|
"dhcp_table_expires": "Bitiş tarihi",
|
||||||
"dhcp_warning": "DHCP sunucusunu yine de etkinleştirmek istiyorsanız, ağınızda başka bir aktif DHCP sunucusu olmadığından emin olun, aksi takdirde ağa bağlı cihazların internet bağlantısı kesilebilir!",
|
"dhcp_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_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_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.",
|
"dhcp_dynamic_ip_found": "Sisteminiz, <0>{{interfaceName}}</0> arayüzü için dinamik IP adresi yapılandırması kullanıyor. DHCP sunucusunu kullanmak için sabit bir IP adresi ayarlanmalıdır. Geçerli olan IP adresiniz <0>{{ipAddress}}</0>. \"DHCP sunucusunu etkinleştir\" düğmesine basarsanız, AdGuard Home bu IP adresini otomatik bir şekilde sabit olarak ayarlayacaktır.",
|
||||||
@@ -147,14 +147,14 @@
|
|||||||
"average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
|
"average_upstream_response_time": "Ortalama üst kaynak yanıt süresi",
|
||||||
"response_time": "Yanıt süresi",
|
"response_time": "Yanıt süresi",
|
||||||
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
|
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
|
||||||
"block_domain_use_filters_and_hosts": "Filtre ve ana bilgisayar dosyalarını kullanarak alan adlarını engelle",
|
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
|
||||||
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
|
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
|
||||||
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
|
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
|
||||||
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
|
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
|
||||||
"use_adguard_parental": "AdGuard ebeveyn denetimi web hizmetini kullan",
|
"use_adguard_parental": "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.",
|
"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_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",
|
"no_servers_specified": "Sunucu belirtilmedi",
|
||||||
"general_settings": "Genel ayarlar",
|
"general_settings": "Genel ayarlar",
|
||||||
"dns_settings": "DNS ayarları",
|
"dns_settings": "DNS ayarları",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "Engellenen yanıt kullanım süresi",
|
"blocked_response_ttl": "Engellenen yanıt kullanım süresi",
|
||||||
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
|
"blocked_response_ttl_desc": "İstemcilerin filtrelenmiş bir yanıtı kaç saniye süreyle önbelleğe alması gerektiğini belirtir",
|
||||||
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
|
"form_enter_blocked_response_ttl": "Engellenen yanıt kullanım süresini girin (saniye)",
|
||||||
"upstream_timeout": "Üst kaynak zaman aşımı",
|
|
||||||
"upstream_timeout_desc": "Üst kaynak sunucusundan yanıt almak için kaç saniye bekleneceğini belirtir",
|
|
||||||
"form_enter_upstream_timeout": "Üst kaynak sunucusu zaman aşımı süresini saniye cinsinden girin",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -311,7 +308,7 @@
|
|||||||
"form_enter_rate_limit": "Sıklık limitini girin",
|
"form_enter_rate_limit": "Sıklık limitini girin",
|
||||||
"rate_limit": "Sıklık limiti",
|
"rate_limit": "Sıklık limiti",
|
||||||
"edns_enable": "EDNS istemci alt ağını etkinleştir",
|
"edns_enable": "EDNS istemci alt ağını etkinleştir",
|
||||||
"edns_cs_desc": "Üst sunucu isteklerine ECS (EDNS İstemci Alt Ağı) seçeneğini ekler ve istemciler tarafından gönderilen değerleri sorgu günlüğünde kaydeder.",
|
"edns_cs_desc": "Kaynak yönü isteklerine EDNS İstemci Alt Ağı seçeneğini (ECS) ekleyin ve istemciler tarafından gönderilen değerleri sorgu günlüğüne kaydedin.",
|
||||||
"edns_use_custom_ip": "EDNS için özel IP kullan",
|
"edns_use_custom_ip": "EDNS için özel IP kullan",
|
||||||
"edns_use_custom_ip_desc": "EDNS için özel IP kullanımına izin ver",
|
"edns_use_custom_ip_desc": "EDNS için özel IP kullanımına izin ver",
|
||||||
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
|
"rate_limit_desc": "İstemci başına izin verilen saniyedeki istek sayısı. 0 olarak ayarlamak, sınır olmadığı anlamına gelir.",
|
||||||
@@ -345,17 +342,17 @@
|
|||||||
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
|
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
|
||||||
"known_tracker": "Bilinen izleyici",
|
"known_tracker": "Bilinen izleyici",
|
||||||
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
|
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
|
||||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyici engelleyen bir DNS sunucusudur. Tüm ağınızı ve cihazlarınızı kontrol etmenizi sağlar ve istemci tarafında ek bir yazılım kullanmanıza gerek duymaz.",
|
"install_welcome_desc": "AdGuard Home, ağ genelinde reklamları ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenizi sağlar, istemci tarafında herhangi bir program kullanmanıza gerek duymaz.",
|
||||||
"install_settings_title": "Yönetici Web Arayüzü",
|
"install_settings_title": "Yönetici Web Arayüzü",
|
||||||
"install_settings_listen": "Dinleme arayüzü",
|
"install_settings_listen": "Dinleme arayüzü",
|
||||||
"install_settings_port": "Bağlantı noktası",
|
"install_settings_port": "Bağlantı noktası",
|
||||||
"install_settings_interface_link": "AdGuard Home yönetici web arayüzüne aşağıdaki adreslerden erişebilirsiniz:",
|
"install_settings_interface_link": "AdGuard Home yönetici web arayüzünüz aşağıdaki adreslerde bulunacaktır:",
|
||||||
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
|
"form_error_port": "Geçerli bir bağlantı noktası değeri girin",
|
||||||
"install_settings_dns": "DNS sunucusu",
|
"install_settings_dns": "DNS sunucusu",
|
||||||
"install_settings_dns_desc": "Cihazlarınızı veya yönlendiricinizi aşağıdaki adreslerdeki DNS sunucusunu kullanacak şekilde yapılandırmanız gerekir:",
|
"install_settings_dns_desc": "Aşağıdaki adreslerde DNS sunucusunu kullanmak için cihazlarınızı veya yönlendiricinizi yapılandırmanız gerekir:",
|
||||||
"install_settings_all_interfaces": "Tüm arayüzler",
|
"install_settings_all_interfaces": "Tüm arayüzler",
|
||||||
"install_auth_title": "Kimlik Doğrulama",
|
"install_auth_title": "Kimlik Doğrulama",
|
||||||
"install_auth_desc": "AdGuard Home yönetici web arayüzüne parola ile kimlik doğrulama yapılandırılmalıdır. AdGuard Home yalnızca yerel ağınızdan erişilebilir olsa bile, yine de yetkisiz erişime karşı korunması önemlidir.",
|
"install_auth_desc": "AdGuard Home yönetim web arayüzü için şifre doğrulaması yapılandırılmalıdır. AdGuard Home'a yalnızca yerel ağınızdan erişilebilir olsa bile, onu sınırsız erişimden korumak yine de önemlidir.",
|
||||||
"install_auth_username": "Kullanıcı adı",
|
"install_auth_username": "Kullanıcı adı",
|
||||||
"install_auth_password": "Parola",
|
"install_auth_password": "Parola",
|
||||||
"install_auth_confirm": "Parolayı onayla",
|
"install_auth_confirm": "Parolayı onayla",
|
||||||
@@ -369,10 +366,10 @@
|
|||||||
"install_devices_router": "Yönlendirici",
|
"install_devices_router": "Yönlendirici",
|
||||||
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
|
"install_devices_router_desc": "Bu kurulum, ev yönlendiricinize bağlı tüm cihazları otomatik olarak kapsar ve her birini elle yapılandırmanıza gerek yoktur.",
|
||||||
"install_devices_address": "AdGuard Home DNS sunucusu aşağıdaki adresleri dinliyor",
|
"install_devices_address": "AdGuard Home DNS sunucusu aşağıdaki adresleri dinliyor",
|
||||||
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle, tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL üzerinden erişebilirsiniz. Giriş yaparken bir parola girmeniz istenebilir. Parolanızı hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemi seçerseniz yönlendiricinin tüm yapılandırmasını kaybedebileceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya bilgisayarınıza yükleyin ve yönlendiricinin ayarlarına erişmek için bu uygulamayı kullanın.",
|
"install_devices_router_list_1": "Yönlendiricinizin ayarlarına gidin. Genellikle tarayıcınızdan http://192.168.0.1/ veya http://192.168.1.1/ gibi bir URL aracılığıyla erişebilirsiniz. Bir parola girmeniz istenebilir. Hatırlamıyorsanız, genellikle yönlendiricinin üzerindeki bir düğmeye basarak parolayı sıfırlayabilirsiniz, ancak bu işlemin seçilmesi durumunda yüksek ihtimalle tüm yönlendirici yapılandırmasını kaybedeceğinizi unutmayın. Yönlendiricinizin kurulumu için bir uygulama gerekiyorsa, lütfen uygulamayı telefonunuza veya PC'nize yükleyin ve yönlendiricinin ayarlarına erişmek için kullanın.",
|
||||||
"install_devices_router_list_2": "DHCP/DNS ayarlarını bulun. DNS satırlarını arayın, genelde iki veya üç tanedir, üç rakam girilebilen dört ayrı grup içeren satırdır.",
|
"install_devices_router_list_2": "DHCP/DNS ayarlarını bulun. DNS satırlarını arayın, genelde iki veya üç tanedir, üç rakam girilebilen dört ayrı grup içeren satırdır.",
|
||||||
"install_devices_router_list_3": "AdGuard Home sunucu adreslerinizi oraya girin.",
|
"install_devices_router_list_3": "AdGuard Home sunucu adreslerinizi oraya girin.",
|
||||||
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu yapılandırılamaz. Bu durumda, AdGuard Home'u bir <0>DHCP sunucusu</0> olarak yapılandırmak yardımcı olabilir. Aksi takdirde, yönlendirici modelinizde DNS sunucularını nasıl özelleştireceğinizi öğrenmek için yönlendirici kılavuzunu kontrol etmelisiniz.",
|
"install_devices_router_list_4": "Bazı yönlendirici türlerinde özel bir DNS sunucusu ayarlanamaz. Bu durumda, AdGuard Home'u <0>DHCP sunucusu</0> olarak ayarlamak yardımcı olabilir. Aksi takdirde, yönlendirici modeliniz için DNS sunucularını nasıl ayarlayacağınız konusunda yönlendirici kılavuzuna bakmalısınız.",
|
||||||
"install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nı açın.",
|
"install_devices_windows_list_1": "Başlat menüsünden veya Windows araması aracılığıyla Denetim Masası'nı açın.",
|
||||||
"install_devices_windows_list_2": "Ağ ve İnternet kategorisine girin ve ardından Ağ ve Paylaşım Merkezi'ne girin.",
|
"install_devices_windows_list_2": "Ağ ve İnternet kategorisine girin ve ardından Ağ ve Paylaşım Merkezi'ne girin.",
|
||||||
"install_devices_windows_list_3": "Sol panelde \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
|
"install_devices_windows_list_3": "Sol panelde \"Bağdaştırıcı ayarlarını değiştirin\" öğesine tıklayın.",
|
||||||
@@ -392,7 +389,7 @@
|
|||||||
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
|
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
|
||||||
"install_devices_ios_list_3": "O anda aktif olan ağın adına dokunun.",
|
"install_devices_ios_list_3": "O anda aktif olan ağın adına dokunun.",
|
||||||
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
|
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
|
||||||
"get_started": "Başla",
|
"get_started": "Başlayın",
|
||||||
"next": "Sonraki",
|
"next": "Sonraki",
|
||||||
"open_dashboard": "Panoyu Aç",
|
"open_dashboard": "Panoyu Aç",
|
||||||
"install_saved": "Başarıyla kaydedildi",
|
"install_saved": "Başarıyla kaydedildi",
|
||||||
@@ -455,14 +452,14 @@
|
|||||||
"settings_global": "Genel",
|
"settings_global": "Genel",
|
||||||
"settings_custom": "Özel",
|
"settings_custom": "Özel",
|
||||||
"table_client": "İstemci",
|
"table_client": "İstemci",
|
||||||
"table_name": "Ad",
|
"table_name": "AdAdı",
|
||||||
"save_btn": "Kaydet",
|
"save_btn": "Kaydet",
|
||||||
"client_add": "İstemci Ekle",
|
"client_add": "İstemci Ekle",
|
||||||
"client_new": "Yeni İstemci",
|
"client_new": "Yeni İstemci",
|
||||||
"client_edit": "İstemciyi Düzenle",
|
"client_edit": "İstemciyi Düzenle",
|
||||||
"client_identifier": "Tanımlayıcı",
|
"client_identifier": "Tanımlayıcı",
|
||||||
"ip_address": "IP adresi",
|
"ip_address": "IP adresi",
|
||||||
"client_identifier_desc": "İstemciler, IP adresi, CIDR, MAC adresi veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiye <0>buradan</0> ulaşabilirsiniz.",
|
"client_identifier_desc": "İstemciler IP adresleri, CIDR, MAC adresleri veya ClientID (DoT/DoH/DoQ için kullanılabilir) ile tanımlanabilir. İstemcileri nasıl tanımlayacağınız hakkında daha fazla bilgiyi <0>buradan</0> edinebilirsiniz.",
|
||||||
"form_enter_ip": "IP girin",
|
"form_enter_ip": "IP girin",
|
||||||
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
|
"form_enter_subnet_ip": "\"{{cidr}}\" alt ağına bir IP adresi girin",
|
||||||
"form_enter_mac": "MAC adresi girin",
|
"form_enter_mac": "MAC adresi girin",
|
||||||
@@ -479,7 +476,7 @@
|
|||||||
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
"client_confirm_delete": "\"{{key}}\" istemcisini silmek istediğinizden emin misiniz?",
|
||||||
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
"list_confirm_delete": "Bu listeyi silmek istediğinizden emin misiniz?",
|
||||||
"auto_clients_title": "Çalışma zamanı istemcileri",
|
"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, ana bilgisayar dosyaları, ters DNS sorguları ve çeşitli diğer kaynaklardan toplanmaktadı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_title": "Erişim ayarları",
|
||||||
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
"access_desc": "AdGuard Home DNS sunucusu için erişim kurallarını buradan yapılandırabilirsiniz",
|
||||||
"access_allowed_title": "İzin verilen istemciler",
|
"access_allowed_title": "İzin verilen istemciler",
|
||||||
@@ -601,12 +598,12 @@
|
|||||||
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
|
"disable_ipv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
|
||||||
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
|
"disable_ipv6_desc": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
|
||||||
"fastest_addr": "En hızlı IP adresi",
|
"fastest_addr": "En hızlı IP adresi",
|
||||||
"fastest_addr_desc": "<b>Tüm</b> DNS sunucularından yanıt bekler, her sunucu için TCP bağlantı hızını ölçer ve en hızlı bağlantı hızına sahip sunucunun IP adresini döndürür.<br/>Bu yapılandırma, bir veya daha fazla üst kaynak sunucusu yanıt vermediğinde, DNS sorgularını önemli ölçüde yavaşlatabilir. Üst kaynak sunucularınızın kararlı olduğundan ve üst kaynak zaman aşım sürenizin düşük olduğundan emin olun.",
|
"fastest_addr_desc": "Tüm DNS sunucularını sorgulayın ve tüm yanıtlar arasından en hızlı olan IP adresini döndürün. AdGuard Home'un tüm DNS sunucularından yanıt beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
|
||||||
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
|
"autofix_warning_text": "\"Düzelt\" seçeneğine tıklarsanız, AdGuard Home, sisteminizi AdGuard Home DNS sunucusunu kullanacak şekilde yapılandırır.",
|
||||||
"autofix_warning_list": "Bu görevleri gerçekleştirir: <0>Sistem DNSStubListener'ı devre dışı bırakın</0> <0>DNS sunucusu adresini 127.0.0.1 olarak ayarlayın</0> <0>/etc/resolv.conf'un sembolik bağlantı hedefini /run/systemd/resolve/resolv.conf ile değiştirin<0> <0>DNSStubListener'ı durdurun (systemd çözümlenmiş hizmeti yeniden yükleyin)</0>",
|
"autofix_warning_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.",
|
"autofix_warning_result": "Sonuç olarak, sisteminizden gelen tüm DNS istekleri varsayılan olarak AdGuard Home tarafından işlenecektir.",
|
||||||
"tags_title": "Etiketler",
|
"tags_title": "Etiketler",
|
||||||
"tags_desc": "İstemciyi tanımlayan etiketleri seçebilirsiniz. Filtreleme kurallarına etiketleri dahil ederek daha hassas bir şekilde uygulayabilirsiniz. <0>Daha fazla bilgi edinin</0>.",
|
"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",
|
"form_select_tags": "İstemci etiketlerini seçin",
|
||||||
"check_title": "Filtrelemeyi denetleyin",
|
"check_title": "Filtrelemeyi denetleyin",
|
||||||
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
|
"check_desc": "Ana makine adının filtreleme durumunu kontrol edin.",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "Sebep: {{reason}}",
|
"check_reason": "Sebep: {{reason}}",
|
||||||
"check_service": "Hizmet adı: {{service}}",
|
"check_service": "Hizmet adı: {{service}}",
|
||||||
"check_hostname": "Ana makine adı veya alan adı",
|
|
||||||
"check_client_id": "İstemci tanımlayıcısı (ClientID veya IP adresi)",
|
|
||||||
"check_enter_client_id": "İstemci tanımlayıcısı girin",
|
|
||||||
"check_dns_record": "DNS kayıt türünü seçin",
|
|
||||||
"service_name": "Hizmet adı",
|
"service_name": "Hizmet adı",
|
||||||
"check_not_found": "Filtre listelerinizde bulunamadı",
|
"check_not_found": "Filtre listelerinizde bulunamadı",
|
||||||
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
|
"client_confirm_block": "\"{{ip}}\" istemcisini engellemek istediğinizden emin misiniz?",
|
||||||
@@ -631,11 +624,11 @@
|
|||||||
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
|
"client_blocked": "\"{{ip}}\" istemcisi başarıyla engellendi",
|
||||||
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
|
"client_unblocked": "\"{{ip}}\" istemcinin engellemesi başarıyla kaldırıldı",
|
||||||
"static_ip": "Sabit IP adresi",
|
"static_ip": "Sabit IP adresi",
|
||||||
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışabilmesi için sabit bir IP adresine ihtiyaç duyar. Aksi takdirde, yönlendiriciniz bu cihaza farklı bir IP adresi atayabilir.",
|
"static_ip_desc": "AdGuard Home bir sunucudur, bu nedenle düzgün çalışması için sabit bir IP adresine ihtiyacı vardır. Aksi takdirde, yönlendiriciniz bir zaman sonra bu cihaza farklı bir IP adresi atayabilir.",
|
||||||
"set_static_ip": "Sabit IP adresi ayarla",
|
"set_static_ip": "Sabit IP adresi ayarla",
|
||||||
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
|
"install_static_ok": "Güzel haber! Sabit IP adresi zaten yapılandırılmış",
|
||||||
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
|
"install_static_error": "AdGuard Home, bu ağ arayüzü için otomatik olarak yapılandıramıyor. Lütfen bunu elle nasıl yapacağınızla ilgili talimatlara bakın.",
|
||||||
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> sabit IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
|
"install_static_configure": "AdGuard Home, <0>{{ip}}</0> dinamik IP adresinin kullanıldığını tespit etti. Sabit adresiniz olarak ayarlanmasını ister misiniz?",
|
||||||
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
|
"confirm_static_ip": "AdGuard Home, {{ip}} adresini sabit IP adresiniz olacak şekilde yapılandırır. Devam etmek istiyor musunuz?",
|
||||||
"list_updated": "{{count}} liste güncellendi",
|
"list_updated": "{{count}} liste güncellendi",
|
||||||
"list_updated_plural": "{{count}} liste güncellendi",
|
"list_updated_plural": "{{count}} liste güncellendi",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "Engel listesi",
|
"blocklist": "Engel listesi",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "Önbellek boyutu",
|
"cache_size": "Önbellek boyutu",
|
||||||
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleği devre dışı bırakmak için 0 olarak ayarlayın.",
|
"cache_size_desc": "DNS önbellek boyutu (bayt cinsinden). Önbelleğe almayı devre dışı bırakmak için boş bırakın.",
|
||||||
"cache_ttl_min_override": "Minimum kullanım süresini geçersiz kıl",
|
"cache_ttl_min_override": "Minimum kullanım süresini geçersiz kıl",
|
||||||
"cache_ttl_max_override": "Maksimum kullanım süresini geçersiz kıl",
|
"cache_ttl_max_override": "Maksimum kullanım süresini geçersiz kıl",
|
||||||
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
|
"enter_cache_size": "Önbellek boyutunu girin (bayt)",
|
||||||
@@ -714,8 +707,8 @@
|
|||||||
"custom_rotation_input": "Rotasyonu saat cinsinden girin",
|
"custom_rotation_input": "Rotasyonu saat cinsinden girin",
|
||||||
"protection_section_label": "Koruma",
|
"protection_section_label": "Koruma",
|
||||||
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
|
"log_and_stats_section_label": "Sorgu günlüğü ve istatistikler",
|
||||||
"ignore_query_log": "Sorgu günlüğünde bu istemciyi gösterme",
|
"ignore_query_log": "Sorgu günlüğünde bu istemciyi yoksay",
|
||||||
"ignore_statistics": "İstatistiklerde bu istemciyi gösterme",
|
"ignore_statistics": "İstatistiklerde bu istemciyi yoksay",
|
||||||
"schedule_services": "Hizmet engellemeyi duraklat",
|
"schedule_services": "Hizmet engellemeyi duraklat",
|
||||||
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
"schedule_services_desc": "Hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
||||||
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
"schedule_services_desc_client": "Bu istemci için hizmet engelleme filtresinin duraklatma planını yapılandırın",
|
||||||
@@ -749,6 +742,6 @@
|
|||||||
"friday_short": "Cum",
|
"friday_short": "Cum",
|
||||||
"saturday_short": "Cmt",
|
"saturday_short": "Cmt",
|
||||||
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması",
|
"upstream_dns_cache_configuration": "Üst kaynak DNS önbellek yapılandırması",
|
||||||
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğini etkinleştir",
|
"enable_upstream_dns_cache": "Bu istemcinin özel üst kaynak yapılandırması için DNS önbelleğe almayı etkinleştir",
|
||||||
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
|
"dns_cache_size": "DNS önbellek boyutu, bayt cinsinden"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
|
"resolve_clients_title": "Увімкнути зворотне вирішення IP-адрес клієнтів",
|
||||||
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
|
"resolve_clients_desc": "Визначати доменні імена клієнтів за допомогою PTR-запитів до відповідних серверів — приватних DNS-серверів для локальних клієнтів та upstream-серверів для клієнтів з публічними IP-адресами.",
|
||||||
"use_private_ptr_resolvers_title": "Використовувати приватні зворотні DNS-резолвери",
|
"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-сервери",
|
"check_dhcp_servers": "Перевірити DHCP-сервери",
|
||||||
"save_config": "Зберегти конфігурацію",
|
"save_config": "Зберегти конфігурацію",
|
||||||
"enabled_dhcp": "DHCP-сервер увімкнено",
|
"enabled_dhcp": "DHCP-сервер увімкнено",
|
||||||
@@ -343,10 +343,10 @@
|
|||||||
"known_tracker": "Відомі трекери",
|
"known_tracker": "Відомі трекери",
|
||||||
"install_welcome_title": "Вітаємо в AdGuard Home!",
|
"install_welcome_title": "Вітаємо в AdGuard Home!",
|
||||||
"install_welcome_desc": "AdGuard Home — це мережевий DNS-сервер, що блокує рекламу та відстеження. Його мета — надати вам контроль над усією мережею та всіма пристроями в ній без потреби використання програми на стороні клієнта.",
|
"install_welcome_desc": "AdGuard Home — це мережевий DNS-сервер, що блокує рекламу та відстеження. Його мета — надати вам контроль над усією мережею та всіма пристроями в ній без потреби використання програми на стороні клієнта.",
|
||||||
"install_settings_title": "Вебінтерфейс адміністратора",
|
"install_settings_title": "Веб-інтерфейс адміністратора",
|
||||||
"install_settings_listen": "Мережевий інтерфейс",
|
"install_settings_listen": "Мережевий інтерфейс",
|
||||||
"install_settings_port": "Порт",
|
"install_settings_port": "Порт",
|
||||||
"install_settings_interface_link": "Вебінтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
|
"install_settings_interface_link": "Веб-інтерфейс адміністратора AdGuard Home буде доступний за такими адресами:",
|
||||||
"form_error_port": "Уведіть правильне значення порту",
|
"form_error_port": "Уведіть правильне значення порту",
|
||||||
"install_settings_dns": "DNS-сервер",
|
"install_settings_dns": "DNS-сервер",
|
||||||
"install_settings_dns_desc": "Вам потрібно буде налаштувати свої пристрої або маршрутизатор для використання 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",
|
"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",
|
"parallel_requests": "Yêu cầu song song",
|
||||||
"load_balancing": "Cân bằng tải",
|
"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": "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.",
|
"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_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_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",
|
"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_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_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_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",
|
"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_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).",
|
"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_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",
|
"check_dhcp_servers": "Kiểm tra máy chủ DHCP",
|
||||||
"save_config": "Lưu thiết lập",
|
"save_config": "Lưu thiết lập",
|
||||||
"enabled_dhcp": "Máy chủ DHCP đã kích hoạt",
|
"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": "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",
|
"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_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ê",
|
"no_servers_specified": "Không có máy chủ nào được liệt kê",
|
||||||
"general_settings": "Cài đặt chung",
|
"general_settings": "Cài đặt chung",
|
||||||
"dns_settings": "Cài đặt DNS",
|
"dns_settings": "Cài đặt DNS",
|
||||||
@@ -425,9 +425,6 @@
|
|||||||
"encryption_hostnames": "Tên máy chủ",
|
"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_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_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_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>.",
|
"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",
|
"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 đó",
|
"use_saved_key": "Sử dụng khóa đã lưu trước đó",
|
||||||
"parental_control": "Quản lý của phụ huynh",
|
"parental_control": "Quản lý của phụ huynh",
|
||||||
"safe_browsing": "Duyệt web an toàn",
|
"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ự",
|
"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>.",
|
"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?",
|
"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": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
|
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
|
||||||
"parallel_requests": "并行请求",
|
"parallel_requests": "并行请求",
|
||||||
"load_balancing": "负载均衡",
|
"load_balancing": "负载均衡",
|
||||||
"load_balancing_desc": "一次查询一台上游服务器。<br/>AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
|
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
|
||||||
"bootstrap_dns": "Bootstrap DNS 服务器",
|
"bootstrap_dns": "Bootstrap DNS 服务器",
|
||||||
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
|
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
|
||||||
"fallback_dns_title": "后备 DNS 服务器",
|
"fallback_dns_title": "后备 DNS 服务器",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
|
"use_adguard_parental": "使用 AdGuard 【家长控制】服务",
|
||||||
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
|
"use_adguard_parental_hint": "AdGuard Home 将使用与浏览安全服务相同的隐私性强的 API 来检查域名指向的网站是否包含成人内容。",
|
||||||
"enforce_safe_search": "使用安全搜索",
|
"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": "未找到指定的服务器",
|
"no_servers_specified": "未找到指定的服务器",
|
||||||
"general_settings": "常规设置",
|
"general_settings": "常规设置",
|
||||||
"dns_settings": "DNS 设置",
|
"dns_settings": "DNS 设置",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "屏蔽的 TTL 应答",
|
"blocked_response_ttl": "屏蔽的 TTL 应答",
|
||||||
"blocked_response_ttl_desc": "指定客户端应缓存已过滤响应的秒数",
|
"blocked_response_ttl_desc": "指定客户端应缓存已过滤响应的秒数",
|
||||||
"form_enter_blocked_response_ttl": "输入拦截的 TTL 应答(秒)",
|
"form_enter_blocked_response_ttl": "输入拦截的 TTL 应答(秒)",
|
||||||
"upstream_timeout": "上游超时",
|
|
||||||
"upstream_timeout_desc": "指定等待上游服务器响应的秒数",
|
|
||||||
"form_enter_upstream_timeout": "输入上游服务器超时时间(以秒为单位)",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -601,7 +598,7 @@
|
|||||||
"disable_ipv6": "禁用 IPv6 地址的解析",
|
"disable_ipv6": "禁用 IPv6 地址的解析",
|
||||||
"disable_ipv6_desc": "丢弃对 IPv6 地址(类型 AAAA)的所有 DNS 查询,并从 HTTPS 响应中删除 IPv6 相关的信息。",
|
"disable_ipv6_desc": "丢弃对 IPv6 地址(类型 AAAA)的所有 DNS 查询,并从 HTTPS 响应中删除 IPv6 相关的信息。",
|
||||||
"fastest_addr": "最快的 IP 地址",
|
"fastest_addr": "最快的 IP 地址",
|
||||||
"fastest_addr_desc": "等待<b>所有</b> DNS 服务器的响应,测量每个服务器的 TCP 连接速度,并返回连接速度最快的服务器的 IP 地址。<br/>如果一个或多个上游服务器没有响应,此模式会显著减慢 DNS 查询速度。确保您的上游服务器稳定且上游超时时间短。",
|
"fastest_addr_desc": "查询所有 DNS 服务器并返回所有响应中速度最快的 IP 地址。因 AdGuard Home 必须等待全部 DNS 服务器响应,这会降低 DNS 查询的速度,但此举将会在总体上改善连接速度。",
|
||||||
"autofix_warning_text": "若您单击「修复」,AdGuard Home 将会配置您的系统以使用 AdGuard Home 的 DNS 服务器。",
|
"autofix_warning_text": "若您单击「修复」,AdGuard Home 将会配置您的系统以使用 AdGuard Home 的 DNS 服务器。",
|
||||||
"autofix_warning_list": "其将会进行如下工作:<0>停用系统DNSStubListener</0><0>设置DNS服务器地址为127.0.0.1</0><0>将/etc/resolv.conf的符号链接目标替换为/run/systemd/resolv/resolv.conf</0><0>停止DNSStubListener(重新加载系统解析服务)</0>",
|
"autofix_warning_list": "其将会进行如下工作:<0>停用系统DNSStubListener</0><0>设置DNS服务器地址为127.0.0.1</0><0>将/etc/resolv.conf的符号链接目标替换为/run/systemd/resolv/resolv.conf</0><0>停止DNSStubListener(重新加载系统解析服务)</0>",
|
||||||
"autofix_warning_result": "因此,默认情况下所有来自系统的 DNS 请求都将由 AdGuard Home 处理。",
|
"autofix_warning_result": "因此,默认情况下所有来自系统的 DNS 请求都将由 AdGuard Home 处理。",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "CNAME: {{cname}}",
|
"check_cname": "CNAME: {{cname}}",
|
||||||
"check_reason": "原因:{{reason}}",
|
"check_reason": "原因:{{reason}}",
|
||||||
"check_service": "服务名称:{{service}}",
|
"check_service": "服务名称:{{service}}",
|
||||||
"check_hostname": "主机名或域名",
|
|
||||||
"check_client_id": "客户端标识符(ClientID 或 IP 地址)",
|
|
||||||
"check_enter_client_id": "输入客户端标识符",
|
|
||||||
"check_dns_record": "选择 DNS 记录类型",
|
|
||||||
"service_name": "服务名称",
|
"service_name": "服务名称",
|
||||||
"check_not_found": "未在您的筛选列表中找到",
|
"check_not_found": "未在您的筛选列表中找到",
|
||||||
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",
|
"client_confirm_block": "确定要阻止客户端 \"{{ip}}\"?",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "黑名单",
|
"blocklist": "黑名单",
|
||||||
"milliseconds_abbreviation": "毫秒",
|
"milliseconds_abbreviation": "毫秒",
|
||||||
"cache_size": "缓存大小",
|
"cache_size": "缓存大小",
|
||||||
"cache_size_desc": "DNS 缓存大小(单位:字节)。若要禁用缓存,请设置为 0。",
|
"cache_size_desc": "DNS 缓存大小(单位:字节)。若要关闭缓存,请留空。",
|
||||||
"cache_ttl_min_override": "覆盖最小 TTL 值",
|
"cache_ttl_min_override": "覆盖最小 TTL 值",
|
||||||
"cache_ttl_max_override": "覆盖最大 TTL 值",
|
"cache_ttl_max_override": "覆盖最大 TTL 值",
|
||||||
"enter_cache_size": "输入缓存大小(字节)",
|
"enter_cache_size": "输入缓存大小(字节)",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"client_settings": "用戶端設定",
|
"client_settings": "用戶端設定",
|
||||||
"example_upstream_reserved": "<0>特定網域</0>的上游;",
|
"example_upstream_reserved": "<0>供特定的網域</0>之上游;",
|
||||||
"example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;",
|
"example_multiple_upstreams_reserved": "<0>特定網域</0>的多個上游伺服器;",
|
||||||
"example_upstream_comment": "註解。",
|
"example_upstream_comment": "註解。",
|
||||||
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
|
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
|
||||||
"parallel_requests": "並行的請求",
|
"parallel_requests": "並行的請求",
|
||||||
"load_balancing": "負載平衡",
|
"load_balancing": "負載平衡",
|
||||||
"load_balancing_desc": "一次查詢一台上游伺服器。<br/>AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
|
"load_balancing_desc": "一次查詢一台伺服器。AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
|
||||||
"bootstrap_dns": "自我啟動(Bootstrap)DNS 伺服器",
|
"bootstrap_dns": "自我啟動(Bootstrap)DNS 伺服器",
|
||||||
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
|
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
|
||||||
"fallback_dns_title": "應變 DNS 伺服器",
|
"fallback_dns_title": "應變 DNS 伺服器",
|
||||||
@@ -20,17 +20,17 @@
|
|||||||
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
|
"resolve_clients_title": "啟用用戶端的 IP 位址之反向的解析",
|
||||||
"resolve_clients_desc": "透過傳送指標(PTR)查詢到對應的解析器(私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
|
"resolve_clients_desc": "透過傳送指標(PTR)查詢到對應的解析器(私人 DNS 伺服器供區域的用戶端,上游的伺服器供有公共 IP 位址的用戶端),反向地解析用戶端的 IP 位址變為它們的主機名稱。",
|
||||||
"use_private_ptr_resolvers_title": "使用私人反向的 DNS 解析器",
|
"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)伺服器",
|
"check_dhcp_servers": "檢查動態主機設定協定(DHCP)伺服器",
|
||||||
"save_config": "儲存配置",
|
"save_config": "儲存配置",
|
||||||
"enabled_dhcp": "動態主機設定協定(DHCP)伺服器被啟用",
|
"enabled_dhcp": "動態主機設定協定(DHCP)伺服器被啟用",
|
||||||
"disabled_dhcp": "DHCP 伺服器已停用",
|
"disabled_dhcp": "動態主機設定協定(DHCP)伺服器被禁用",
|
||||||
"unavailable_dhcp": "DHCP 為不可用的",
|
"unavailable_dhcp": "DHCP 為不可用的",
|
||||||
"unavailable_dhcp_desc": "AdGuard Home 無法於您的作業系統上執行 DHCP 伺服器",
|
"unavailable_dhcp_desc": "AdGuard Home 無法於您的作業系統上執行 DHCP 伺服器",
|
||||||
"dhcp_title": "動態主機設定協定(DHCP)伺服器(實驗性的!)",
|
"dhcp_title": "動態主機設定協定(DHCP)伺服器(實驗性的!)",
|
||||||
"dhcp_description": "如果您的路由器未提供動態主機設定協定(DHCP)設定,您可使用 AdGuard 自身內建的 DHCP 伺服器。",
|
"dhcp_description": "如果您的路由器未提供動態主機設定協定(DHCP)設定,您可使用 AdGuard 自身內建的 DHCP 伺服器。",
|
||||||
"dhcp_enable": "啟用動態主機設定協定(DHCP)伺服器",
|
"dhcp_enable": "啟用動態主機設定協定(DHCP)伺服器",
|
||||||
"dhcp_disable": "停用 DHCP 伺服器",
|
"dhcp_disable": "禁用動態主機設定協定(DHCP)伺服器",
|
||||||
"dhcp_not_found": "因為 AdGuard Home 於該網路上未發現任何現行的 DHCP 伺服器,啟用內建的動態主機設定協定(DHCP)伺服器為安全的。然而,您應手動地重新檢查那個,因為自動的探查目前不予 100% 保證。",
|
"dhcp_not_found": "因為 AdGuard Home 於該網路上未發現任何現行的 DHCP 伺服器,啟用內建的動態主機設定協定(DHCP)伺服器為安全的。然而,您應手動地重新檢查那個,因為自動的探查目前不予 100% 保證。",
|
||||||
"dhcp_found": "於該網路上,一個現行的動態主機設定協定(DHCP)伺服器被發現。啟用內建的 DHCP 伺服器為不安全的。",
|
"dhcp_found": "於該網路上,一個現行的動態主機設定協定(DHCP)伺服器被發現。啟用內建的 DHCP 伺服器為不安全的。",
|
||||||
"dhcp_leases": "動態主機設定協定(DHCP)租約",
|
"dhcp_leases": "動態主機設定協定(DHCP)租約",
|
||||||
@@ -112,8 +112,8 @@
|
|||||||
"privacy_policy": "隱私政策",
|
"privacy_policy": "隱私政策",
|
||||||
"enable_protection": "啟用防護",
|
"enable_protection": "啟用防護",
|
||||||
"enabled_protection": "已啟用防護",
|
"enabled_protection": "已啟用防護",
|
||||||
"disable_protection": "停用防護",
|
"disable_protection": "禁用防護",
|
||||||
"disabled_protection": "已停用防護",
|
"disabled_protection": "已禁用防護",
|
||||||
"refresh_statics": "重新整理統計資料",
|
"refresh_statics": "重新整理統計資料",
|
||||||
"dns_query": "DNS 查詢",
|
"dns_query": "DNS 查詢",
|
||||||
"blocked_by": "<0>被過濾器封鎖</0>",
|
"blocked_by": "<0>被過濾器封鎖</0>",
|
||||||
@@ -124,8 +124,8 @@
|
|||||||
"for_last_hours_plural": "在過去的 {{count}} 小時內",
|
"for_last_hours_plural": "在過去的 {{count}} 小時內",
|
||||||
"for_last_days": "在最近的 {{count}} 日內",
|
"for_last_days": "在最近的 {{count}} 日內",
|
||||||
"for_last_days_plural": "在最近的 {{count}} 日內",
|
"for_last_days_plural": "在最近的 {{count}} 日內",
|
||||||
"stats_disabled": "統計功能目前停用中,請至<0>設定頁面</0>重新開啟。",
|
"stats_disabled": "該統計資料已被禁用。您可從<0>設定頁面</0>中打開它。",
|
||||||
"stats_disabled_short": "該統計資料已停用",
|
"stats_disabled_short": "該統計資料已被禁用",
|
||||||
"no_domains_found": "無已發現之網域",
|
"no_domains_found": "無已發現之網域",
|
||||||
"requests_count": "請求總數",
|
"requests_count": "請求總數",
|
||||||
"top_blocked_domains": "熱門已封鎖的網域",
|
"top_blocked_domains": "熱門已封鎖的網域",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
|
"use_adguard_parental": "使用 AdGuard 家長控制之網路服務",
|
||||||
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面(API)。",
|
"use_adguard_parental_hint": "AdGuard Home 將檢查網域是否包含成人資料。它使用如同瀏覽安全網路服務一樣之對隱私友好的應用程式介面(API)。",
|
||||||
"enforce_safe_search": "使用安全搜尋",
|
"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": "無已明確指定的伺服器",
|
"no_servers_specified": "無已明確指定的伺服器",
|
||||||
"general_settings": "一般設定",
|
"general_settings": "一般設定",
|
||||||
"dns_settings": "DNS 設定",
|
"dns_settings": "DNS 設定",
|
||||||
@@ -172,13 +172,13 @@
|
|||||||
"upstreams": "上游",
|
"upstreams": "上游",
|
||||||
"upstream": "上游伺服器",
|
"upstream": "上游伺服器",
|
||||||
"apply_btn": "套用",
|
"apply_btn": "套用",
|
||||||
"disabled_filtering_toast": "已停用過濾",
|
"disabled_filtering_toast": "已禁用過濾",
|
||||||
"enabled_filtering_toast": "已啟用過濾",
|
"enabled_filtering_toast": "已啟用過濾",
|
||||||
"disabled_safe_browsing_toast": "已停用安全瀏覽",
|
"disabled_safe_browsing_toast": "已禁用安全瀏覽",
|
||||||
"enabled_safe_browsing_toast": "已啟用安全瀏覽",
|
"enabled_safe_browsing_toast": "已啟用安全瀏覽",
|
||||||
"disabled_parental_toast": "已停用家長控制",
|
"disabled_parental_toast": "已禁用家長控制",
|
||||||
"enabled_parental_toast": "已啟用家長控制",
|
"enabled_parental_toast": "已啟用家長控制",
|
||||||
"disabled_safe_search_toast": "已停用安全搜尋",
|
"disabled_safe_search_toast": "已禁用安全搜尋",
|
||||||
"enabled_save_search_toast": "已啟用安全搜尋",
|
"enabled_save_search_toast": "已啟用安全搜尋",
|
||||||
"updated_save_search_toast": "安全搜尋設定更新成功",
|
"updated_save_search_toast": "安全搜尋設定更新成功",
|
||||||
"enabled_table_header": "已啟用",
|
"enabled_table_header": "已啟用",
|
||||||
@@ -275,7 +275,7 @@
|
|||||||
"query_log_retention": "查詢記錄保留時間",
|
"query_log_retention": "查詢記錄保留時間",
|
||||||
"query_log_enable": "啟用記錄",
|
"query_log_enable": "啟用記錄",
|
||||||
"query_log_configuration": "記錄配置",
|
"query_log_configuration": "記錄配置",
|
||||||
"query_log_disabled": "查詢記錄功能已停用,請至「<0>設定</0>」調整",
|
"query_log_disabled": "查詢記錄被禁用並可在<0>設定</0>中被配置",
|
||||||
"query_log_strict_search": "使用雙引號於嚴謹的搜尋",
|
"query_log_strict_search": "使用雙引號於嚴謹的搜尋",
|
||||||
"query_log_retention_confirm": "您確定要更改記錄檔保存期限嗎?如果您縮短期限部分資料可能將會遺失",
|
"query_log_retention_confirm": "您確定要更改記錄檔保存期限嗎?如果您縮短期限部分資料可能將會遺失",
|
||||||
"anonymize_client_ip": "將用戶端 IP 匿名",
|
"anonymize_client_ip": "將用戶端 IP 匿名",
|
||||||
@@ -294,9 +294,6 @@
|
|||||||
"blocked_response_ttl": "已封鎖的回應之存活時間(TTL)",
|
"blocked_response_ttl": "已封鎖的回應之存活時間(TTL)",
|
||||||
"blocked_response_ttl_desc": "對用戶端應快取受過濾的回應,指定多少秒數",
|
"blocked_response_ttl_desc": "對用戶端應快取受過濾的回應,指定多少秒數",
|
||||||
"form_enter_blocked_response_ttl": "請輸入已封鎖回應的存活時間(秒)",
|
"form_enter_blocked_response_ttl": "請輸入已封鎖回應的存活時間(秒)",
|
||||||
"upstream_timeout": "上游超時",
|
|
||||||
"upstream_timeout_desc": "指定等待來自此上游伺服器回應的秒數",
|
|
||||||
"form_enter_upstream_timeout": "輸入上游伺服器超時時間(以秒為單位)",
|
|
||||||
"dnscrypt": "DNSCrypt",
|
"dnscrypt": "DNSCrypt",
|
||||||
"dns_over_https": "DNS-over-HTTPS",
|
"dns_over_https": "DNS-over-HTTPS",
|
||||||
"dns_over_tls": "DNS-over-TLS",
|
"dns_over_tls": "DNS-over-TLS",
|
||||||
@@ -401,7 +398,7 @@
|
|||||||
"encryption_config_saved": "加密配置被儲存",
|
"encryption_config_saved": "加密配置被儲存",
|
||||||
"encryption_server": "伺服器名稱",
|
"encryption_server": "伺服器名稱",
|
||||||
"encryption_server_enter": "輸入您的域名",
|
"encryption_server_enter": "輸入您的域名",
|
||||||
"encryption_server_desc": "如果設定,AdGuard Home 會偵測 ClientID、回應 DDR 查詢,並執行其他連線驗證。如果未設定,則會停用這些功能。必須符合憑證中的一個 DNS 名稱。",
|
"encryption_server_desc": "如果被設定,AdGuard Home 檢測用戶端 IDs,回覆 DDR 查詢,並執行額外的連線驗證。如果未被設定,這些功能被禁用。必須與在該憑證裡的 DNS 名稱其中之一相符。",
|
||||||
"encryption_redirect": "自動地重新導向到 HTTPS",
|
"encryption_redirect": "自動地重新導向到 HTTPS",
|
||||||
"encryption_redirect_desc": "如果被勾選,AdGuard Home 將自動地重新導向您從 HTTP 到 HTTPS 位址。",
|
"encryption_redirect_desc": "如果被勾選,AdGuard Home 將自動地重新導向您從 HTTP 到 HTTPS 位址。",
|
||||||
"encryption_https": "HTTPS 連接埠",
|
"encryption_https": "HTTPS 連接埠",
|
||||||
@@ -429,8 +426,8 @@
|
|||||||
"encryption_reset": "您確定您想要重置加密設定嗎?",
|
"encryption_reset": "您確定您想要重置加密設定嗎?",
|
||||||
"encryption_warning": "警告",
|
"encryption_warning": "警告",
|
||||||
"encryption_plain_dns_enable": "啟用一般的 DNS",
|
"encryption_plain_dns_enable": "啟用一般的 DNS",
|
||||||
"encryption_plain_dns_desc": "預設啟用一般 DNS。您可以停用它以強制所有裝置使用加密 DNS。若要這樣做,您必須啟用至少一個加密 DNS 通訊協定",
|
"encryption_plain_dns_desc": "預設情況下啟用一般的 DNS。使用者可以禁用它,強制所有裝置使用一般的 DNS。為此,必須至少啟用一個一般的 DNS 協定。",
|
||||||
"encryption_plain_dns_error": "若要停用一般 DNS,請啟用至少一個加密 DNS 通訊協定",
|
"encryption_plain_dns_error": "要禁用一般的 DNS,請至少啟用一個一般的 DNS 協定",
|
||||||
"topline_expiring_certificate": "您的安全通訊端層(SSL)憑證即將到期。更新<0>加密設定</0>。",
|
"topline_expiring_certificate": "您的安全通訊端層(SSL)憑證即將到期。更新<0>加密設定</0>。",
|
||||||
"topline_expired_certificate": "您的安全通訊端層(SSL)憑證為已到期的。更新<0>加密設定</0>。",
|
"topline_expired_certificate": "您的安全通訊端層(SSL)憑證為已到期的。更新<0>加密設定</0>。",
|
||||||
"form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼",
|
"form_error_port_range": "輸入在 80-65535 之範圍內的連接埠號碼",
|
||||||
@@ -482,7 +479,7 @@
|
|||||||
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
|
"auto_clients_desc": "AdGuard Home 使用或可能使用的裝置的 IP 地址資訊。這些資訊來自多個來源,包括主機檔案、反向 DNS 等。",
|
||||||
"access_title": "存取設定",
|
"access_title": "存取設定",
|
||||||
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
"access_desc": "於此您可配置用於 AdGuard Home DNS 伺服器之存取規則",
|
||||||
"access_allowed_title": "被允許的用戶端",
|
"access_allowed_title": "已允許的用戶端",
|
||||||
"access_allowed_desc": "無類別網域間路由(CIDRs)、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目,AdGuard Home 將接受僅來自這些用戶端的請求。",
|
"access_allowed_desc": "無類別網域間路由(CIDRs)、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目,AdGuard Home 將接受僅來自這些用戶端的請求。",
|
||||||
"access_disallowed_title": "未被允許的用戶端",
|
"access_disallowed_title": "未被允許的用戶端",
|
||||||
"access_disallowed_desc": "無類別網域間路由(CIDRs)、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目,AdGuard Home 將排除來自這些用戶端的請求。如果在已允許的用戶端中有項目,此欄位被忽略。",
|
"access_disallowed_desc": "無類別網域間路由(CIDRs)、IP 位址或<a>用戶端 IDs</a> 之清單。如果此清單有項目,AdGuard Home 將排除來自這些用戶端的請求。如果在已允許的用戶端中有項目,此欄位被忽略。",
|
||||||
@@ -563,7 +560,7 @@
|
|||||||
"statistics_retention_confirm": "您確定您想要更改統計資料保留嗎?如果您減少該間隔值,某些資料將被丟失",
|
"statistics_retention_confirm": "您確定您想要更改統計資料保留嗎?如果您減少該間隔值,某些資料將被丟失",
|
||||||
"statistics_cleared": "統計資料被成功地清除",
|
"statistics_cleared": "統計資料被成功地清除",
|
||||||
"statistics_enable": "啟用統計資料",
|
"statistics_enable": "啟用統計資料",
|
||||||
"ignore_domains": "被忽略的網域(被換行分隔)",
|
"ignore_domains": "忽略的網域(以換行符分隔)",
|
||||||
"ignore_domains_title": "被忽略的網域",
|
"ignore_domains_title": "被忽略的網域",
|
||||||
"ignore_domains_desc_stats": "符合這些規則的查詢不會被記錄在統計資料中",
|
"ignore_domains_desc_stats": "符合這些規則的查詢不會被記錄在統計資料中",
|
||||||
"ignore_domains_desc_query": "符合這些規則的查詢不會被寫入查詢記錄中",
|
"ignore_domains_desc_query": "符合這些規則的查詢不會被寫入查詢記錄中",
|
||||||
@@ -572,7 +569,7 @@
|
|||||||
"filters_configuration": "過濾器配置",
|
"filters_configuration": "過濾器配置",
|
||||||
"filters_enable": "啟用過濾器",
|
"filters_enable": "啟用過濾器",
|
||||||
"filters_interval": "過濾器更新間隔",
|
"filters_interval": "過濾器更新間隔",
|
||||||
"disabled": "已停用",
|
"disabled": "已禁用",
|
||||||
"username_label": "使用者名稱",
|
"username_label": "使用者名稱",
|
||||||
"username_placeholder": "輸入使用者名稱",
|
"username_placeholder": "輸入使用者名稱",
|
||||||
"password_label": "密碼",
|
"password_label": "密碼",
|
||||||
@@ -598,10 +595,10 @@
|
|||||||
"rewrite_domain_name": "域名:新增一筆正規名稱(CNAME)記錄",
|
"rewrite_domain_name": "域名:新增一筆正規名稱(CNAME)記錄",
|
||||||
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
|
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
|
||||||
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
|
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
|
||||||
"disable_ipv6": "停用 IPv6 位址解析",
|
"disable_ipv6": "禁用 IPv6 位址之解析",
|
||||||
"disable_ipv6_desc": "停止所有對於 IPv6 位址(類型 AAAA)的 DNS 查詢,並從 HTTPS 回應中移除 IPv6 的提示。",
|
"disable_ipv6_desc": "停止所有對於 IPv6 位址(類型 AAAA)的 DNS 查詢,並從 HTTPS 回應中移除 IPv6 的提示。",
|
||||||
"fastest_addr": "最快的 IP 位址",
|
"fastest_addr": "最快的 IP 位址",
|
||||||
"fastest_addr_desc": "等待<b>所有</b> DNS 伺服器的回應,測量每個伺服器的 TCP 連線速度,並返回連線速度最快的伺服器的 IP 位址。<br/>如果一個或多個上游伺服器沒有回應,此模式會顯著減慢 DNS 查詢速度。確保您的上游伺服器穩定且上游超時時間短。",
|
"fastest_addr_desc": "查詢所有的 DNS 伺服器並返回在所有的回應之中最快的 IP 位址。因為 AdGuard Home 必須等待來自所有的 DNS 伺服器之回應,這使 DNS 查詢變慢,但改善總體的連線。",
|
||||||
"autofix_warning_text": "如果您點擊\"修復\",AdGuard Home 將配置您的系統使用 AdGuard Home DNS 伺服器。",
|
"autofix_warning_text": "如果您點擊\"修復\",AdGuard Home 將配置您的系統使用 AdGuard Home DNS 伺服器。",
|
||||||
"autofix_warning_list": "它將執行這些任務:<0>撤銷系統 DNSStubListener</0> <0>設定 DNS 伺服器位址為 127.0.0.1</0> <0>用 /run/systemd/resolve/resolv.conf 取代 /etc/resolv.conf 的符號連結目標</0> <0>停止 DNSStubListener(重新載入 systemd-resolved 服務)</0>",
|
"autofix_warning_list": "它將執行這些任務:<0>撤銷系統 DNSStubListener</0> <0>設定 DNS 伺服器位址為 127.0.0.1</0> <0>用 /run/systemd/resolve/resolv.conf 取代 /etc/resolv.conf 的符號連結目標</0> <0>停止 DNSStubListener(重新載入 systemd-resolved 服務)</0>",
|
||||||
"autofix_warning_result": "因此,預設下,來自您的系統之所有的 DNS 請求將被 AdGuard Home 處理。",
|
"autofix_warning_result": "因此,預設下,來自您的系統之所有的 DNS 請求將被 AdGuard Home 處理。",
|
||||||
@@ -620,10 +617,6 @@
|
|||||||
"check_cname": "正規名稱(CNAME):{{cname}}",
|
"check_cname": "正規名稱(CNAME):{{cname}}",
|
||||||
"check_reason": "原因:{{reason}}",
|
"check_reason": "原因:{{reason}}",
|
||||||
"check_service": "服務名稱:{{service}}",
|
"check_service": "服務名稱:{{service}}",
|
||||||
"check_hostname": "主機名稱或域名",
|
|
||||||
"check_client_id": "用戶端識別碼(ClientID 或 IP 位址)",
|
|
||||||
"check_enter_client_id": "輸入用戶識別碼",
|
|
||||||
"check_dns_record": "選擇 DNS 記錄類型",
|
|
||||||
"service_name": "服務名稱",
|
"service_name": "服務名稱",
|
||||||
"check_not_found": "未在您的過濾器清單中被找到",
|
"check_not_found": "未在您的過濾器清單中被找到",
|
||||||
"client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?",
|
"client_confirm_block": "您確定您想要封鎖該用戶端 \"{{ip}}\" 嗎?",
|
||||||
@@ -644,7 +637,7 @@
|
|||||||
"validated_with_dnssec": "已用網域名稱系統安全性擴充功能(DNSSEC)驗證",
|
"validated_with_dnssec": "已用網域名稱系統安全性擴充功能(DNSSEC)驗證",
|
||||||
"all_queries": "所有的查詢",
|
"all_queries": "所有的查詢",
|
||||||
"show_blocked_responses": "已封鎖的",
|
"show_blocked_responses": "已封鎖的",
|
||||||
"show_whitelisted_responses": "被允許的",
|
"show_whitelisted_responses": "已允許的",
|
||||||
"show_processed_responses": "已處理的",
|
"show_processed_responses": "已處理的",
|
||||||
"blocked_safebrowsing": "被安全瀏覽封鎖",
|
"blocked_safebrowsing": "被安全瀏覽封鎖",
|
||||||
"blocked_adult_websites": "被家長控制封鎖",
|
"blocked_adult_websites": "被家長控制封鎖",
|
||||||
@@ -656,7 +649,7 @@
|
|||||||
"blocklist": "封鎖清單",
|
"blocklist": "封鎖清單",
|
||||||
"milliseconds_abbreviation": "ms",
|
"milliseconds_abbreviation": "ms",
|
||||||
"cache_size": "快取大小",
|
"cache_size": "快取大小",
|
||||||
"cache_size_desc": "DNS 快取大小(位元組)。若要停用快取,請設為 0。",
|
"cache_size_desc": "DNS 快取大小(以位元組)。要禁用快取,留空。",
|
||||||
"cache_ttl_min_override": "覆寫最小的存活時間(TTL)",
|
"cache_ttl_min_override": "覆寫最小的存活時間(TTL)",
|
||||||
"cache_ttl_max_override": "覆寫最大的存活時間(TTL)",
|
"cache_ttl_max_override": "覆寫最大的存活時間(TTL)",
|
||||||
"enter_cache_size": "輸入快取大小(位元組)",
|
"enter_cache_size": "輸入快取大小(位元組)",
|
||||||
@@ -680,14 +673,14 @@
|
|||||||
"click_to_view_queries": "點擊以檢視查詢",
|
"click_to_view_queries": "點擊以檢視查詢",
|
||||||
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。",
|
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。",
|
||||||
"adg_will_drop_dns_queries": "AdGuard Home 將持續排除來自此用戶端之所有的 DNS 查詢。",
|
"adg_will_drop_dns_queries": "AdGuard Home 將持續排除來自此用戶端之所有的 DNS 查詢。",
|
||||||
"filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在被允許的用戶端的清單之外。",
|
"filter_allowlist": "警告:此動作也將把 \"{{disallowed_rule}}\" 規則排除在已允許的用戶端的清單之外。",
|
||||||
"last_rule_in_allowlist": "無法禁止此用戶端,因為排除規則 \"{{disallowed_rule}}\" 會停用「允許的用戶端」清單。",
|
"last_rule_in_allowlist": "因為排除 \"{{disallowed_rule}}\" 規則將禁用\"已允許的用戶端\"清單,無法不允許此用戶端。",
|
||||||
"use_saved_key": "使用該先前已儲存的金鑰",
|
"use_saved_key": "使用該先前已儲存的金鑰",
|
||||||
"parental_control": "家長控制",
|
"parental_control": "家長控制",
|
||||||
"safe_browsing": "安全瀏覽",
|
"safe_browsing": "安全瀏覽",
|
||||||
"served_from_cache_label": "從快取中",
|
"served_from_cache_label": "從快取中",
|
||||||
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
|
"form_error_password_length": "密碼長度必須為 {{min}} 到 {{max}} 個字符",
|
||||||
"anonymizer_notification": "<0>注意:</0>IP 匿名功能已開啟。您可在<1>一般設定</1>中關閉。",
|
"anonymizer_notification": "<0>注意:</0>IP 匿名化被啟用。您可在<1>一般設定</1>中禁用它。",
|
||||||
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",
|
"confirm_dns_cache_clear": "您確定您想要清除 DNS 快取嗎?",
|
||||||
"cache_cleared": "DNS 快取被成功地清除",
|
"cache_cleared": "DNS 快取被成功地清除",
|
||||||
"clear_cache": "清除快取",
|
"clear_cache": "清除快取",
|
||||||
@@ -702,14 +695,14 @@
|
|||||||
"disable_for_hours": "{{count}} 小時",
|
"disable_for_hours": "{{count}} 小時",
|
||||||
"disable_for_hours_plural": "{{count}} 小時",
|
"disable_for_hours_plural": "{{count}} 小時",
|
||||||
"disable_until_tomorrow": "直到明天",
|
"disable_until_tomorrow": "直到明天",
|
||||||
"disable_notify_for_seconds": "計 {{count}} 秒停用防護",
|
"disable_notify_for_seconds": "計 {{count}} 秒禁用防護",
|
||||||
"disable_notify_for_seconds_plural": "計 {{count}} 秒停用防護",
|
"disable_notify_for_seconds_plural": "計 {{count}} 秒禁用防護",
|
||||||
"disable_notify_for_minutes": "計 {{count}} 分鐘停用防護",
|
"disable_notify_for_minutes": "計 {{count}} 分鐘禁用防護",
|
||||||
"disable_notify_for_minutes_plural": "計 {{count}} 分鐘停用防護",
|
"disable_notify_for_minutes_plural": "計 {{count}} 分鐘禁用防護",
|
||||||
"disable_notify_for_hours": "計 {{count}} 小時停用防護",
|
"disable_notify_for_hours": "計 {{count}} 小時禁用防護",
|
||||||
"disable_notify_for_hours_plural": "計 {{count}} 小時停用防護",
|
"disable_notify_for_hours_plural": "計 {{count}} 小時禁用防護",
|
||||||
"disable_notify_until_tomorrow": "停用防護直到明天",
|
"disable_notify_until_tomorrow": "禁用防護直到明天",
|
||||||
"enable_protection_timer": "防護將於 {{time}} 啟用",
|
"enable_protection_timer": "防護將於 {{time}} 被啟用",
|
||||||
"custom_retention_input": "輸入保留時間(小時)",
|
"custom_retention_input": "輸入保留時間(小時)",
|
||||||
"custom_rotation_input": "輸入旋轉時間(小時)",
|
"custom_rotation_input": "輸入旋轉時間(小時)",
|
||||||
"protection_section_label": "防護",
|
"protection_section_label": "防護",
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { describe, expect, test, afterEach, vi, beforeEach, it } from 'vitest';
|
|
||||||
|
|
||||||
import { sortIp, countClientsStatistics, findAddressType, subnetMaskToBitMask } from '../helpers/helpers';
|
import { sortIp, countClientsStatistics, findAddressType, subnetMaskToBitMask } from '../helpers/helpers';
|
||||||
import { ADDRESS_TYPES } from '../helpers/constants';
|
import { ADDRESS_TYPES } from '../helpers/constants';
|
||||||
|
|
||||||
@@ -261,7 +259,7 @@ describe('sortIp', () => {
|
|||||||
const originalWarn = console.warn;
|
const originalWarn = console.warn;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
console.warn = vi.fn();
|
console.warn = jest.fn();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -349,15 +347,15 @@ describe('sortIp', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('findAddressType', () => {
|
describe('findAddressType', () => {
|
||||||
it('should return IP type for IP addresses', () => {
|
describe('ip', () => {
|
||||||
expect(findAddressType('127.0.0.1')).toStrictEqual(ADDRESS_TYPES.IP);
|
expect(findAddressType('127.0.0.1')).toStrictEqual(ADDRESS_TYPES.IP);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return CIDR type for CIDR addresses', () => {
|
describe('cidr', () => {
|
||||||
expect(findAddressType('127.0.0.1/8')).toStrictEqual(ADDRESS_TYPES.CIDR);
|
expect(findAddressType('127.0.0.1/8')).toStrictEqual(ADDRESS_TYPES.CIDR);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return UNKNOWN type for MAC addresses', () => {
|
describe('mac', () => {
|
||||||
expect(findAddressType('00:1B:44:11:3A:B7')).toStrictEqual(ADDRESS_TYPES.UNKNOWN);
|
expect(findAddressType('00:1B:44:11:3A:B7')).toStrictEqual(ADDRESS_TYPES.UNKNOWN);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
CHECK_TIMEOUT,
|
CHECK_TIMEOUT,
|
||||||
STATUS_RESPONSE,
|
STATUS_RESPONSE,
|
||||||
SETTINGS_NAMES,
|
SETTINGS_NAMES,
|
||||||
|
FORM_NAME,
|
||||||
MANUAL_UPDATE_LINK,
|
MANUAL_UPDATE_LINK,
|
||||||
DISABLE_PROTECTION_TIMINGS,
|
DISABLE_PROTECTION_TIMINGS,
|
||||||
} from '../helpers/constants';
|
} from '../helpers/constants';
|
||||||
@@ -423,9 +424,10 @@ export const testUpstream =
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testUpstreamWithFormValues = (formValues: any) => async (dispatch: any, getState: any) => {
|
export const testUpstreamWithFormValues = () => async (dispatch: any, getState: any) => {
|
||||||
const { upstream_dns_file } = getState().dnsConfig;
|
const { upstream_dns_file } = getState().dnsConfig;
|
||||||
const { bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns } = formValues;
|
const { bootstrap_dns, upstream_dns, local_ptr_upstreams, fallback_dns } =
|
||||||
|
getState().form[FORM_NAME.UPSTREAM].values;
|
||||||
|
|
||||||
return dispatch(
|
return dispatch(
|
||||||
testUpstream(
|
testUpstream(
|
||||||
@@ -510,15 +512,16 @@ export const findActiveDhcpRequest = createAction('FIND_ACTIVE_DHCP_REQUEST');
|
|||||||
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
|
export const findActiveDhcpSuccess = createAction('FIND_ACTIVE_DHCP_SUCCESS');
|
||||||
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
|
export const findActiveDhcpFailure = createAction('FIND_ACTIVE_DHCP_FAILURE');
|
||||||
|
|
||||||
export const findActiveDhcp = (selectedInterface: any) => async (dispatch: any, getState: any) => {
|
export const findActiveDhcp = (name: any) => async (dispatch: any, getState: any) => {
|
||||||
dispatch(findActiveDhcpRequest());
|
dispatch(findActiveDhcpRequest());
|
||||||
try {
|
try {
|
||||||
const req = {
|
const req = {
|
||||||
interface: selectedInterface,
|
interface: name,
|
||||||
};
|
};
|
||||||
const activeDhcp = await apiClient.findActiveDhcp(req);
|
const activeDhcp = await apiClient.findActiveDhcp(req);
|
||||||
dispatch(findActiveDhcpSuccess(activeDhcp));
|
dispatch(findActiveDhcpSuccess(activeDhcp));
|
||||||
const { check, interface_name, interfaces } = getState().dhcp;
|
const { check, interface_name, interfaces } = getState().dhcp;
|
||||||
|
const selectedInterface = getState().form[FORM_NAME.DHCP_INTERFACES].values.interface_name;
|
||||||
const v4 = check?.v4 ?? { static_ip: {}, other_server: {} };
|
const v4 = check?.v4 ?? { static_ip: {}, other_server: {} };
|
||||||
const v6 = check?.v6 ?? { other_server: {} };
|
const v6 = check?.v6 ?? { other_server: {} };
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ export const setAllSettingsSuccess = createAction('SET_ALL_SETTINGS_SUCCESS');
|
|||||||
export const setAllSettings = (values: any) => async (dispatch: any) => {
|
export const setAllSettings = (values: any) => async (dispatch: any) => {
|
||||||
dispatch(setAllSettingsRequest());
|
dispatch(setAllSettingsRequest());
|
||||||
try {
|
try {
|
||||||
const config = { ...values };
|
const { confirm_password, ...config } = values;
|
||||||
delete config.confirm_password;
|
|
||||||
|
|
||||||
await apiClient.setAllSettings(config);
|
await apiClient.setAllSettings(config);
|
||||||
dispatch(setAllSettingsSuccess());
|
dispatch(setAllSettingsSuccess());
|
||||||
@@ -49,11 +48,7 @@ export const checkConfig = (values: any) => async (dispatch: any) => {
|
|||||||
dispatch(checkConfigRequest());
|
dispatch(checkConfigRequest());
|
||||||
try {
|
try {
|
||||||
const check = await apiClient.checkConfig(values);
|
const check = await apiClient.checkConfig(values);
|
||||||
dispatch(checkConfigSuccess({
|
dispatch(checkConfigSuccess(check));
|
||||||
web: { ...values.web, ...check.web },
|
|
||||||
dns: { ...values.dns, ...check.dns },
|
|
||||||
static_ip: check.static_ip,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(addErrorToast({ error }));
|
dispatch(addErrorToast({ error }));
|
||||||
dispatch(checkConfigFailure());
|
dispatch(checkConfigFailure());
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import { createAction } from 'redux-actions';
|
|||||||
import apiClient from '../api/Api';
|
import apiClient from '../api/Api';
|
||||||
|
|
||||||
import { normalizeLogs } from '../helpers/helpers';
|
import { normalizeLogs } from '../helpers/helpers';
|
||||||
import { DEFAULT_LOGS_FILTER, 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';
|
import { addErrorToast, addSuccessToast } from './toasts';
|
||||||
import { SearchFormValues } from '../components/Logs';
|
|
||||||
|
|
||||||
const getLogsWithParams = async (config: any) => {
|
const getLogsWithParams = async (config: any) => {
|
||||||
const { older_than, filter, ...values } = config;
|
const { older_than, filter, ...values } = config;
|
||||||
@@ -28,10 +27,12 @@ export const getAdditionalLogsRequest = createAction('GET_ADDITIONAL_LOGS_REQUES
|
|||||||
export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE');
|
export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE');
|
||||||
export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS');
|
export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS');
|
||||||
|
|
||||||
const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, currentQuery?: string, total?: any) => {
|
const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getState: any, total?: any) => {
|
||||||
const { logs, oldest } = data;
|
const { logs, oldest } = data;
|
||||||
const totalData = total || { logs };
|
const totalData = total || { logs };
|
||||||
|
|
||||||
|
const queryForm = getState().form[FORM_NAME.LOGS_FILTER];
|
||||||
|
const currentQuery = queryForm && queryForm.values.search;
|
||||||
const previousQuery = filter?.search;
|
const previousQuery = filter?.search;
|
||||||
const isQueryTheSame =
|
const isQueryTheSame =
|
||||||
typeof previousQuery === 'string' && typeof currentQuery === 'string' && previousQuery === currentQuery;
|
typeof previousQuery === 'string' && typeof currentQuery === 'string' && previousQuery === currentQuery;
|
||||||
@@ -50,7 +51,7 @@ const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, current
|
|||||||
filter,
|
filter,
|
||||||
});
|
});
|
||||||
if (additionalLogs.oldest.length > 0) {
|
if (additionalLogs.oldest.length > 0) {
|
||||||
return await shortPollQueryLogs(additionalLogs, filter, dispatch, currentQuery, {
|
return await shortPollQueryLogs(additionalLogs, filter, dispatch, getState, {
|
||||||
logs: [...totalData.logs, ...additionalLogs.logs],
|
logs: [...totalData.logs, ...additionalLogs.logs],
|
||||||
oldest: additionalLogs.oldest,
|
oldest: additionalLogs.oldest,
|
||||||
});
|
});
|
||||||
@@ -90,18 +91,17 @@ export const updateLogs = () => async (dispatch: any, getState: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLogs = (currentQuery?: string) => async (dispatch: any, getState: any) => {
|
export const getLogs = () => async (dispatch: any, getState: any) => {
|
||||||
dispatch(getLogsRequest());
|
dispatch(getLogsRequest());
|
||||||
try {
|
try {
|
||||||
const { isFiltered, filter, oldest } = getState().queryLogs;
|
const { isFiltered, filter, oldest } = getState().queryLogs;
|
||||||
|
|
||||||
const data = await getLogsWithParams({
|
const data = await getLogsWithParams({
|
||||||
older_than: oldest,
|
older_than: oldest,
|
||||||
filter,
|
filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isFiltered) {
|
if (isFiltered) {
|
||||||
const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery);
|
const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState);
|
||||||
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
||||||
dispatch(getLogsSuccess(updatedData));
|
dispatch(getLogsSuccess(updatedData));
|
||||||
} else {
|
} else {
|
||||||
@@ -122,13 +122,13 @@ export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
|
|||||||
* @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object
|
* @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object
|
||||||
* @returns function
|
* @returns function
|
||||||
*/
|
*/
|
||||||
export const setLogsFilter = (filter: SearchFormValues) => setLogsFilterRequest(filter);
|
export const setLogsFilter = (filter: any) => setLogsFilterRequest(filter);
|
||||||
|
|
||||||
export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST');
|
export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST');
|
||||||
export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE');
|
export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE');
|
||||||
export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS');
|
export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS');
|
||||||
|
|
||||||
export const setFilteredLogs = (filter?: SearchFormValues) => async (dispatch: any) => {
|
export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: any) => {
|
||||||
dispatch(setFilteredLogsRequest());
|
dispatch(setFilteredLogsRequest());
|
||||||
try {
|
try {
|
||||||
const data = await getLogsWithParams({
|
const data = await getLogsWithParams({
|
||||||
@@ -136,9 +136,7 @@ export const setFilteredLogs = (filter?: SearchFormValues) => async (dispatch: a
|
|||||||
filter,
|
filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentQuery = filter?.search;
|
const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState);
|
||||||
|
|
||||||
const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery);
|
|
||||||
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const getStats = () => async (dispatch: any) => {
|
|||||||
const normalizedTopClients = normalizeTopStats(stats.top_clients);
|
const normalizedTopClients = normalizeTopStats(stats.top_clients);
|
||||||
|
|
||||||
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
|
const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name');
|
||||||
const clients = await apiClient.searchClients(clientsParams);
|
const clients = await apiClient.findClients(clientsParams);
|
||||||
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
|
const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name');
|
||||||
|
|
||||||
const normalizedStats = {
|
const normalizedStats = {
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ class Api {
|
|||||||
// Per-client settings
|
// Per-client settings
|
||||||
GET_CLIENTS = { path: 'clients', method: 'GET' };
|
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' };
|
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
|
||||||
|
|
||||||
@@ -453,12 +453,11 @@ class Api {
|
|||||||
return this.makeRequest(path, method, parameters);
|
return this.makeRequest(path, method, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchClients(config: any) {
|
findClients(params: any) {
|
||||||
const { path, method } = this.SEARCH_CLIENTS;
|
const { path, method } = this.FIND_CLIENTS;
|
||||||
const parameters = {
|
const url = getPathWithQueryString(path, params);
|
||||||
data: config,
|
|
||||||
};
|
return this.makeRequest(url, method);
|
||||||
return this.makeRequest(path, method, parameters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNS access settings
|
// DNS access settings
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface RowProps {
|
|||||||
|
|
||||||
const Row = ({ label, count, response_status, tooltipTitle, translationComponents }: RowProps) => {
|
const Row = ({ label, count, response_status, tooltipTitle, translationComponents }: RowProps) => {
|
||||||
const content = response_status ? (
|
const content = response_status ? (
|
||||||
<LogsSearchLink response_status={response_status}>{count}</LogsSearchLink>
|
<LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
|
||||||
) : (
|
) : (
|
||||||
count
|
count
|
||||||
);
|
);
|
||||||
@@ -77,16 +77,16 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
|
|||||||
? t('number_of_dns_query_hours', { count: msToHours(interval) })
|
? t('number_of_dns_query_hours', { count: msToHours(interval) })
|
||||||
: t('number_of_dns_query_days', { count: msToDays(interval) });
|
: t('number_of_dns_query_days', { count: msToDays(interval) });
|
||||||
|
|
||||||
const rows: RowProps[] = [
|
const rows = [
|
||||||
{
|
{
|
||||||
label: 'dns_query',
|
label: 'dns_query',
|
||||||
count: formatNumber(numDnsQueries),
|
count: numDnsQueries.toString(),
|
||||||
tooltipTitle: dnsQueryTooltip,
|
tooltipTitle: dnsQueryTooltip,
|
||||||
response_status: RESPONSE_FILTER.ALL.QUERY,
|
response_status: RESPONSE_FILTER.ALL.QUERY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'blocked_by',
|
label: 'blocked_by',
|
||||||
count: formatNumber(numBlockedFiltering),
|
count: numBlockedFiltering.toString(),
|
||||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
|
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
|
||||||
response_status: RESPONSE_FILTER.BLOCKED.QUERY,
|
response_status: RESPONSE_FILTER.BLOCKED.QUERY,
|
||||||
|
|
||||||
@@ -98,19 +98,19 @@ const Counters = ({ refreshButton, subtitle }: CountersProps) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'stats_malware_phishing',
|
label: 'stats_malware_phishing',
|
||||||
count: formatNumber(numReplacedSafebrowsing),
|
count: numReplacedSafebrowsing.toString(),
|
||||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
|
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
|
||||||
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
|
response_status: RESPONSE_FILTER.BLOCKED_THREATS.QUERY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'stats_adult',
|
label: 'stats_adult',
|
||||||
count: formatNumber(numReplacedParental),
|
count: numReplacedParental.toString(),
|
||||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
|
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
|
||||||
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
|
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.QUERY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'enforced_save_search',
|
label: 'enforced_save_search',
|
||||||
count: formatNumber(numReplacedSafesearch),
|
count: numReplacedSafesearch.toString(),
|
||||||
tooltipTitle: 'number_of_dns_query_to_safe_search',
|
tooltipTitle: 'number_of_dns_query_to_safe_search',
|
||||||
response_status: RESPONSE_FILTER.SAFE_SEARCH.QUERY,
|
response_status: RESPONSE_FILTER.SAFE_SEARCH.QUERY,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import Card from '../ui/Card';
|
|||||||
|
|
||||||
import DomainCell from './DomainCell';
|
import DomainCell from './DomainCell';
|
||||||
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants';
|
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants';
|
||||||
import { formatNumber } from '../../helpers/helpers';
|
|
||||||
|
|
||||||
interface TimeCellProps {
|
interface TimeCellProps {
|
||||||
value?: string | number;
|
value?: string | number;
|
||||||
@@ -21,7 +20,7 @@ const TimeCell = ({ value }: TimeCellProps) => {
|
|||||||
return '–';
|
return '–';
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueInMilliseconds = formatNumber(round(Number(value) * 1000));
|
const valueInMilliseconds = round(Number(value) * 1000);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs__row o-hidden">
|
<div className="logs__row o-hidden">
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ const Dashboard = ({
|
|||||||
}}
|
}}
|
||||||
disabled={processingProtection}>
|
disabled={processingProtection}>
|
||||||
{protectionDisabledDuration
|
{protectionDisabledDuration
|
||||||
? `${t('enable_protection_timer', { time: getRemaningTimeText(protectionDisabledDuration) })}`
|
? `${t('enable_protection_timer')} ${getRemaningTimeText(protectionDisabledDuration)}`
|
||||||
: getProtectionBtnText(protectionEnabled)}
|
: getProtectionBtnText(protectionEnabled)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +1,62 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
|
|
||||||
import Card from '../../ui/Card';
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
|
import { renderInputField } from '../../../helpers/form';
|
||||||
|
|
||||||
import Info from './Info';
|
import Info from './Info';
|
||||||
|
import { FORM_NAME } from '../../../helpers/constants';
|
||||||
import { RootState } from '../../../initialState';
|
import { RootState } from '../../../initialState';
|
||||||
import { validateRequiredValue } from '../../../helpers/validators';
|
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { DNS_RECORD_TYPES } from '../../../helpers/constants';
|
|
||||||
import { Select } from '../../ui/Controls/Select';
|
|
||||||
|
|
||||||
export type FilteringCheckFormValues = {
|
interface CheckProps {
|
||||||
name: string;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
client?: string;
|
pristine: boolean;
|
||||||
qtype?: string;
|
invalid: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
const Check = (props: CheckProps) => {
|
||||||
onSubmit?: (data: FilteringCheckFormValues) => void;
|
const { pristine, invalid, handleSubmit } = props;
|
||||||
};
|
|
||||||
|
|
||||||
const Check = ({ onSubmit }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const processingCheck = useSelector((state: RootState) => state.filtering.processingCheck);
|
const processingCheck = useSelector((state: RootState) => state.filtering.processingCheck);
|
||||||
const hostname = useSelector((state: RootState) => state.filtering.check.hostname);
|
|
||||||
|
|
||||||
const {
|
const hostname = useSelector((state: RootState) => state.filtering.check.hostname);
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
formState: { isValid },
|
|
||||||
} = useForm<FilteringCheckFormValues>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
name: '',
|
|
||||||
client: '',
|
|
||||||
qtype: DNS_RECORD_TYPES[0],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t('check_title')} subtitle={t('check_desc')}>
|
<Card title={t('check_title')} subtitle={t('check_desc')}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<Controller
|
<div className="input-group">
|
||||||
name="name"
|
<Field
|
||||||
control={control}
|
id="name"
|
||||||
rules={{ validate: validateRequiredValue }}
|
name="name"
|
||||||
render={({ field, fieldState }) => (
|
component={renderInputField}
|
||||||
<Input
|
type="text"
|
||||||
{...field}
|
className="form-control"
|
||||||
type="text"
|
placeholder={t('form_enter_host')}
|
||||||
label={t('check_hostname')}
|
/>
|
||||||
data-testid="check_domain_name"
|
|
||||||
placeholder="example.com"
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
<span className="input-group-append">
|
||||||
name="client"
|
<button
|
||||||
control={control}
|
className="btn btn-success btn-standard btn-large"
|
||||||
render={({ field, fieldState }) => (
|
type="submit"
|
||||||
<Input
|
onClick={handleSubmit}
|
||||||
{...field}
|
disabled={pristine || invalid || processingCheck}>
|
||||||
type="text"
|
{t('check')}
|
||||||
data-testid="check_client_id"
|
</button>
|
||||||
label={t('check_client_id')}
|
</span>
|
||||||
placeholder={t('check_enter_client_id')}
|
</div>
|
||||||
error={fieldState.error?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="qtype"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
label={t('check_dns_record')}
|
|
||||||
data-testid="check_dns_record_type"
|
|
||||||
>
|
|
||||||
{DNS_RECORD_TYPES.map((type) => (
|
|
||||||
<option key={type} value={type}>
|
|
||||||
{type}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
|
||||||
type="submit"
|
|
||||||
data-testid="check_domain_submit"
|
|
||||||
disabled={!isValid || processingCheck}
|
|
||||||
>
|
|
||||||
{t('check')}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{hostname && (
|
{hostname && (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<Info />
|
<Info />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -117,4 +67,4 @@ const Check = ({ onSubmit }: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Check;
|
export default reduxForm({ form: FORM_NAME.DOMAIN_CHECK })(Check);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import PageTitle from '../ui/PageTitle';
|
|||||||
|
|
||||||
import Examples from './Examples';
|
import Examples from './Examples';
|
||||||
|
|
||||||
import Check, { FilteringCheckFormValues } from './Check';
|
import Check from './Check';
|
||||||
|
|
||||||
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
|
import { getTextareaCommentsHighlight, syncScroll } from '../../helpers/highlightTextareaComments';
|
||||||
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
|
import { COMMENT_LINE_DEFAULT_TOKEN } from '../../helpers/constants';
|
||||||
@@ -48,18 +48,8 @@ class CustomRules extends Component<CustomRulesProps> {
|
|||||||
this.props.setRules(this.props.filtering.userRules);
|
this.props.setRules(this.props.filtering.userRules);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCheck = (values: FilteringCheckFormValues) => {
|
handleCheck = (values: any) => {
|
||||||
const params: FilteringCheckFormValues = { name: values.name };
|
this.props.checkHost(values);
|
||||||
|
|
||||||
if (values.client) {
|
|
||||||
params.client = values.client;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.qtype) {
|
|
||||||
params.qtype = values.qtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.checkHost(params);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onScroll = (e: any) => syncScroll(e, this.ref);
|
onScroll = (e: any) => syncScroll(e, this.ref);
|
||||||
@@ -78,7 +68,6 @@ class CustomRules extends Component<CustomRulesProps> {
|
|||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<div className="text-edit-container mb-4">
|
<div className="text-edit-container mb-4">
|
||||||
<textarea
|
<textarea
|
||||||
data-testid="custom_rule_textarea"
|
|
||||||
className="form-control font-monospace text-input"
|
className="form-control font-monospace text-input"
|
||||||
value={userRules}
|
value={userRules}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
@@ -92,7 +81,6 @@ class CustomRules extends Component<CustomRulesProps> {
|
|||||||
|
|
||||||
<div className="card-actions">
|
<div className="card-actions">
|
||||||
<button
|
<button
|
||||||
data-testid="apply_custom_rule"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={this.handleSubmit}>
|
onClick={this.handleSubmit}>
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Checkbox } from '../ui/Controls/Checkbox';
|
|
||||||
|
|
||||||
const getIconsData = (homepage: string, source: string) => [
|
|
||||||
{
|
|
||||||
iconName: 'dashboard',
|
|
||||||
href: homepage,
|
|
||||||
className: 'ml-1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconName: 'info',
|
|
||||||
href: source,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const renderIcons = (iconsData: { iconName: string; href: string; className?: string }[]) =>
|
|
||||||
iconsData.map(({ iconName, href, className = '' }) => (
|
|
||||||
<a
|
|
||||||
key={iconName}
|
|
||||||
href={href}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={classNames('d-flex align-items-center', className)}>
|
|
||||||
<svg className="icon icon--15 mr-1 icon--gray">
|
|
||||||
<use xlinkHref={`#${iconName}`} />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
));
|
|
||||||
|
|
||||||
type Filter = {
|
|
||||||
categoryId: string;
|
|
||||||
homepage: string;
|
|
||||||
source: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Category = {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
categories: Record<string, Category>;
|
|
||||||
filters: Record<string, Filter>;
|
|
||||||
selectedSources: Record<string, boolean>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FiltersList = ({ categories, filters, selectedSources }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { control } = useFormContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{Object.entries(categories).map(([categoryId, category]) => {
|
|
||||||
const categoryFilters = Object.entries(filters)
|
|
||||||
.filter(([, filter]) => filter.categoryId === categoryId)
|
|
||||||
.map(([key, filter]) => ({ ...filter, id: key }));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={category.name} className="modal-body__item">
|
|
||||||
<h6 className="font-weight-bold mb-1">{t(category.name)}</h6>
|
|
||||||
<p className="mb-3">{t(category.description)}</p>
|
|
||||||
{categoryFilters.map((filter) => {
|
|
||||||
const { homepage, source, name, id } = filter;
|
|
||||||
const isSelected = selectedSources[source];
|
|
||||||
const iconsData = getIconsData(homepage, source);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={name} className="d-flex align-items-center pb-1">
|
|
||||||
<Controller
|
|
||||||
name={id}
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
data-testid={`filters_${id}`}
|
|
||||||
title={name}
|
|
||||||
disabled={isSelected}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{renderIcons(iconsData)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,152 +1,208 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useForm, Controller, FormProvider } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
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 { validatePath, validateRequiredValue } from '../../helpers/validators';
|
||||||
|
|
||||||
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE } from '../../helpers/constants';
|
import { CheckboxField, renderInputField } from '../../helpers/form';
|
||||||
|
import { MODAL_OPEN_TIMEOUT, MODAL_TYPE, FORM_NAME } from '../../helpers/constants';
|
||||||
import filtersCatalog from '../../helpers/filters/filters';
|
import filtersCatalog from '../../helpers/filters/filters';
|
||||||
import { FiltersList } from './FiltersList';
|
|
||||||
import { Input } from '../ui/Controls/Input';
|
|
||||||
|
|
||||||
type FormValues = {
|
const getIconsData = (homepage: any, source: any) => [
|
||||||
enabled: boolean;
|
{
|
||||||
name: string;
|
iconName: 'dashboard',
|
||||||
url: string;
|
href: homepage,
|
||||||
};
|
className: 'ml-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconName: 'info',
|
||||||
|
href: source,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const defaultValues: FormValues = {
|
const renderIcons = (iconsData: any) =>
|
||||||
enabled: true,
|
iconsData.map(({ iconName, href, className = '' }: any) => (
|
||||||
name: '',
|
<a
|
||||||
url: '',
|
key={iconName}
|
||||||
};
|
href={href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={classNames('d-flex align-items-center', className)}>
|
||||||
|
<svg className="icon icon--15 mr-1 icon--gray">
|
||||||
|
<use xlinkHref={`#${iconName}`} />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
));
|
||||||
|
|
||||||
type Props = {
|
interface renderCheckboxFieldProps {
|
||||||
closeModal: () => void;
|
// https://redux-form.com/8.3.0/docs/api/field.md/#props
|
||||||
onSubmit: (values: FormValues) => void;
|
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;
|
processingAddFilter: boolean;
|
||||||
processingConfigFilter: boolean;
|
processingConfigFilter: boolean;
|
||||||
whitelist?: boolean;
|
whitelist?: boolean;
|
||||||
modalType: string;
|
modalType: string;
|
||||||
toggleFilteringModal: ({ type }: { type?: keyof typeof MODAL_TYPE }) => void;
|
toggleFilteringModal: (...args: unknown[]) => unknown;
|
||||||
selectedSources?: Record<string, boolean>;
|
selectedSources?: object;
|
||||||
initialValues?: FormValues;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export const Form = ({
|
const Form = (props: FormProps) => {
|
||||||
closeModal,
|
const {
|
||||||
processingAddFilter,
|
t,
|
||||||
processingConfigFilter,
|
closeModal,
|
||||||
whitelist,
|
handleSubmit,
|
||||||
modalType,
|
processingAddFilter,
|
||||||
toggleFilteringModal,
|
processingConfigFilter,
|
||||||
selectedSources,
|
whitelist,
|
||||||
onSubmit,
|
modalType,
|
||||||
initialValues,
|
toggleFilteringModal,
|
||||||
}: Props) => {
|
selectedSources,
|
||||||
const { t } = useTranslation();
|
} = props;
|
||||||
|
|
||||||
const methods = useForm({
|
const openModal = (modalType: any, timeout = MODAL_OPEN_TIMEOUT) => {
|
||||||
defaultValues: {
|
toggleFilteringModal();
|
||||||
...defaultValues,
|
|
||||||
...initialValues,
|
|
||||||
},
|
|
||||||
mode: 'onBlur',
|
|
||||||
});
|
|
||||||
const { handleSubmit, control } = methods;
|
|
||||||
|
|
||||||
const openModal = (modalType: keyof typeof MODAL_TYPE, timeout = MODAL_OPEN_TIMEOUT) => {
|
|
||||||
toggleFilteringModal(undefined);
|
|
||||||
setTimeout(() => toggleFilteringModal({ type: modalType }), timeout);
|
setTimeout(() => toggleFilteringModal({ type: modalType }), timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openFilteringListModal = () => openModal('CHOOSE_FILTERING_LIST');
|
const openFilteringListModal = () => openModal(MODAL_TYPE.CHOOSE_FILTERING_LIST);
|
||||||
|
|
||||||
const openAddFiltersModal = () => openModal('ADD_FILTERS');
|
const openAddFiltersModal = () => openModal(MODAL_TYPE.ADD_FILTERS);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<form onSubmit={handleSubmit}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<div className="modal-body modal-body--filters">
|
||||||
<div className="modal-body modal-body--filters">
|
{modalType === MODAL_TYPE.SELECT_MODAL_TYPE && (
|
||||||
{modalType === MODAL_TYPE.SELECT_MODAL_TYPE && (
|
<div className="d-flex justify-content-around">
|
||||||
<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 && (
|
|
||||||
<FiltersList
|
|
||||||
categories={filtersCatalog.categories}
|
|
||||||
filters={filtersCatalog.filters}
|
|
||||||
selectedSources={selectedSources}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{modalType !== MODAL_TYPE.CHOOSE_FILTERING_LIST && modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
|
|
||||||
<>
|
|
||||||
<div className="form__group">
|
|
||||||
<Controller
|
|
||||||
name="name"
|
|
||||||
control={control}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="filters_name"
|
|
||||||
placeholder={t('enter_name_hint')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group">
|
|
||||||
<Controller
|
|
||||||
name="url"
|
|
||||||
control={control}
|
|
||||||
rules={{ validate: { validateRequiredValue, validatePath } }}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="filters_url"
|
|
||||||
placeholder={t('enter_url_or_path_hint')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__description">
|
|
||||||
{whitelist ? t('enter_valid_allowlist') : t('enter_valid_blocklist')}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="modal-footer">
|
|
||||||
<button type="button" className="btn btn-secondary" onClick={closeModal}>
|
|
||||||
{t('cancel_btn')}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && (
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
onClick={openFilteringListModal}
|
||||||
data-testid="filters_save"
|
className="btn btn-success btn-standard mr-2 btn-large">
|
||||||
className="btn btn-success"
|
{t('choose_from_list')}
|
||||||
disabled={processingAddFilter || processingConfigFilter}>
|
|
||||||
{t('save_btn')}
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
</div>
|
<button onClick={openAddFiltersModal} className="btn btn-primary btn-standard">
|
||||||
</form>
|
{t('add_custom_list')}
|
||||||
</FormProvider>
|
</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);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { MODAL_TYPE } from '../../helpers/constants';
|
import { MODAL_TYPE } from '../../helpers/constants';
|
||||||
|
|
||||||
import { Form } from './Form';
|
import Form from './Form';
|
||||||
import '../ui/Modal.css';
|
import '../ui/Modal.css';
|
||||||
|
|
||||||
import { getMap } from '../../helpers/helpers';
|
import { getMap } from '../../helpers/helpers';
|
||||||
@@ -75,15 +75,25 @@ class Modal extends Component<ModalProps> {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
||||||
processingAddFilter,
|
processingAddFilter,
|
||||||
|
|
||||||
processingConfigFilter,
|
processingConfigFilter,
|
||||||
|
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
|
||||||
modalType,
|
modalType,
|
||||||
|
|
||||||
currentFilterData,
|
currentFilterData,
|
||||||
|
|
||||||
whitelist,
|
whitelist,
|
||||||
|
|
||||||
toggleFilteringModal,
|
toggleFilteringModal,
|
||||||
|
|
||||||
filters,
|
filters,
|
||||||
|
|
||||||
t,
|
t,
|
||||||
|
|
||||||
filtersCatalog,
|
filtersCatalog,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +1,42 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
import { renderInputField } from '../../../helpers/form';
|
||||||
import { validateAnswer, validateDomain, validateRequiredValue } from '../../../helpers/validators';
|
import { validateAnswer, validateDomain, validateRequiredValue } from '../../../helpers/validators';
|
||||||
import { Input } from '../../ui/Controls/Input';
|
import { FORM_NAME } from '../../../helpers/constants';
|
||||||
|
|
||||||
interface RewriteFormValues {
|
interface FormProps {
|
||||||
domain: string;
|
pristine: boolean;
|
||||||
answer: string;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
reset: (...args: unknown[]) => string;
|
||||||
|
toggleRewritesModal: (...args: unknown[]) => unknown;
|
||||||
|
submitting: boolean;
|
||||||
|
processingAdd: boolean;
|
||||||
|
t: (...args: unknown[]) => string;
|
||||||
|
initialValues?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
const Form = (props: FormProps) => {
|
||||||
processingAdd: boolean;
|
const { t, handleSubmit, reset, pristine, submitting, toggleRewritesModal, processingAdd } = props;
|
||||||
currentRewrite?: RewriteFormValues;
|
|
||||||
toggleRewritesModal: () => void;
|
|
||||||
onSubmit?: (data: RewriteFormValues) => Promise<void> | void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
reset,
|
|
||||||
control,
|
|
||||||
formState: { isDirty, isSubmitting },
|
|
||||||
} = useForm<RewriteFormValues>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
domain: currentRewrite?.domain || '',
|
|
||||||
answer: currentRewrite?.answer || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleFormSubmit = async (data: RewriteFormValues) => {
|
|
||||||
if (onSubmit) {
|
|
||||||
await onSubmit(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
<Trans>domain_desc</Trans>
|
<Trans>domain_desc</Trans>
|
||||||
</div>
|
</div>
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Controller
|
<Field
|
||||||
|
id="domain"
|
||||||
name="domain"
|
name="domain"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
validate: validateDomain,
|
placeholder={t('form_domain')}
|
||||||
required: validateRequiredValue,
|
validate={[validateRequiredValue, validateDomain]}
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="rewrites_domain"
|
|
||||||
placeholder={t('form_domain')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Trans>examples_title</Trans>:
|
<Trans>examples_title</Trans>:
|
||||||
@@ -71,6 +44,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }:
|
|||||||
<li>
|
<li>
|
||||||
<code>example.org</code> – <Trans>example_rewrite_domain</Trans>
|
<code>example.org</code> – <Trans>example_rewrite_domain</Trans>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<code>*.example.org</code> –
|
<code>*.example.org</code> –
|
||||||
<span>
|
<span>
|
||||||
@@ -79,24 +53,14 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }:
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Controller
|
<Field
|
||||||
|
id="answer"
|
||||||
name="answer"
|
name="answer"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
validate: validateAnswer,
|
placeholder={t('form_answer')}
|
||||||
required: validateRequiredValue,
|
validate={[validateRequiredValue, validateAnswer]}
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="rewrites_answer"
|
|
||||||
placeholder={t('form_answer')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,9 +77,8 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }:
|
|||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="rewrites_cancel"
|
|
||||||
className="btn btn-secondary btn-standard"
|
className="btn btn-secondary btn-standard"
|
||||||
disabled={isSubmitting || processingAdd}
|
disabled={submitting || processingAdd}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset();
|
reset();
|
||||||
toggleRewritesModal();
|
toggleRewritesModal();
|
||||||
@@ -125,9 +88,8 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }:
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="rewrites_save"
|
|
||||||
className="btn btn-success btn-standard"
|
className="btn btn-success btn-standard"
|
||||||
disabled={isSubmitting || !isDirty || processingAdd}>
|
disabled={submitting || pristine || processingAdd}>
|
||||||
<Trans>save_btn</Trans>
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,4 +98,10 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
export default flow([
|
||||||
|
withTranslation(),
|
||||||
|
reduxForm({
|
||||||
|
form: FORM_NAME.REWRITES,
|
||||||
|
enableReinitialize: true,
|
||||||
|
}),
|
||||||
|
])(Form);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface ModalProps {
|
|||||||
processingAdd: boolean;
|
processingAdd: boolean;
|
||||||
processingDelete: boolean;
|
processingDelete: boolean;
|
||||||
modalType: string;
|
modalType: string;
|
||||||
currentRewrite?: { answer: string, domain: string; };
|
currentRewrite?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Modal = (props: ModalProps) => {
|
const Modal = (props: ModalProps) => {
|
||||||
@@ -23,6 +23,7 @@ const Modal = (props: ModalProps) => {
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
toggleRewritesModal,
|
toggleRewritesModal,
|
||||||
processingAdd,
|
processingAdd,
|
||||||
|
processingDelete,
|
||||||
modalType,
|
modalType,
|
||||||
currentRewrite,
|
currentRewrite,
|
||||||
} = props;
|
} = props;
|
||||||
@@ -49,10 +50,11 @@ const Modal = (props: ModalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
|
initialValues={{ ...currentRewrite }}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
toggleRewritesModal={toggleRewritesModal}
|
toggleRewritesModal={toggleRewritesModal}
|
||||||
processingAdd={processingAdd}
|
processingAdd={processingAdd}
|
||||||
currentRewrite={currentRewrite}
|
processingDelete={processingDelete}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ReactModal>
|
</ReactModal>
|
||||||
|
|||||||
@@ -1,55 +1,38 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Trans } from 'react-i18next';
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { toggleAllServices } from '../../../helpers/helpers';
|
||||||
|
|
||||||
import { ServiceField } from './ServiceField';
|
import { renderServiceField } from '../../../helpers/form';
|
||||||
|
import { FORM_NAME } from '../../../helpers/constants';
|
||||||
export type BlockedService = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
icon_svg: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type FormValues = {
|
|
||||||
blocked_services: Record<string, boolean>;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface FormProps {
|
interface FormProps {
|
||||||
initialValues: Record<string, boolean>;
|
blockedServices: unknown[];
|
||||||
blockedServices: BlockedService[];
|
pristine: boolean;
|
||||||
onSubmit: (values: FormValues) => void;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
change: (...args: unknown[]) => unknown;
|
||||||
|
submitting: boolean;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
processingSet: boolean;
|
processingSet: boolean;
|
||||||
|
t: (...args: unknown[]) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Form = ({ initialValues, blockedServices, processing, processingSet, onSubmit }: FormProps) => {
|
const Form = (props: FormProps) => {
|
||||||
const {
|
const { blockedServices, handleSubmit, change, pristine, submitting, processing, processingSet } = props;
|
||||||
handleSubmit,
|
|
||||||
control,
|
|
||||||
setValue,
|
|
||||||
formState: { isSubmitting },
|
|
||||||
} = useForm<FormValues>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: initialValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleToggleAllServices = async (isSelected: boolean) => {
|
|
||||||
blockedServices.forEach((service: BlockedService) => setValue(`blocked_services.${service.id}`, isSelected));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<div className="row mb-4">
|
<div className="row mb-4">
|
||||||
<div className="col-6">
|
<div className="col-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="blocked_services_block_all"
|
|
||||||
className="btn btn-secondary btn-block"
|
className="btn btn-secondary btn-block"
|
||||||
disabled={processing || processingSet}
|
disabled={processing || processingSet}
|
||||||
onClick={() => handleToggleAllServices(true)}>
|
onClick={() => toggleAllServices(blockedServices, change, true)}>
|
||||||
<Trans>block_all</Trans>
|
<Trans>block_all</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,30 +40,24 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet
|
|||||||
<div className="col-6">
|
<div className="col-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="blocked_services_unblock_all"
|
|
||||||
className="btn btn-secondary btn-block"
|
className="btn btn-secondary btn-block"
|
||||||
disabled={processing || processingSet}
|
disabled={processing || processingSet}
|
||||||
onClick={() => handleToggleAllServices(false)}>
|
onClick={() => toggleAllServices(blockedServices, change, false)}>
|
||||||
<Trans>unblock_all</Trans>
|
<Trans>unblock_all</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="services">
|
<div className="services">
|
||||||
{blockedServices.map((service: BlockedService) => (
|
{blockedServices.map((service: any) => (
|
||||||
<Controller
|
<Field
|
||||||
key={service.id}
|
key={service.id}
|
||||||
|
icon={service.icon_svg}
|
||||||
name={`blocked_services.${service.id}`}
|
name={`blocked_services.${service.id}`}
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={renderServiceField}
|
||||||
<ServiceField
|
placeholder={service.name}
|
||||||
{...field}
|
disabled={processing || processingSet}
|
||||||
data-testid={`blocked_services_${service.id}`}
|
|
||||||
placeholder={service.name}
|
|
||||||
disabled={processing || processingSet}
|
|
||||||
icon={service.icon_svg}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -89,12 +66,19 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet
|
|||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="blocked_services_save"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
disabled={isSubmitting || processing || processingSet}>
|
disabled={submitting || pristine || processing || processingSet}>
|
||||||
<Trans>save_btn</Trans>
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withTranslation(),
|
||||||
|
reduxForm({
|
||||||
|
form: FORM_NAME.SERVICES,
|
||||||
|
enableReinitialize: true,
|
||||||
|
}),
|
||||||
|
])(Form);
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import cn from 'classnames';
|
|
||||||
import { FieldValues, ControllerRenderProps } from 'react-hook-form';
|
|
||||||
|
|
||||||
type Props = ControllerRenderProps<FieldValues> & {
|
|
||||||
placeholder: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
className?: string;
|
|
||||||
icon?: string;
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ServiceField = React.forwardRef<HTMLInputElement, Props>(
|
|
||||||
({ name, value, onChange, onBlur, placeholder, disabled, className, icon, error, ...rest }, ref) => (
|
|
||||||
<>
|
|
||||||
<label className={cn('service custom-switch', className)}>
|
|
||||||
<input
|
|
||||||
name={name}
|
|
||||||
type="checkbox"
|
|
||||||
className="custom-switch-input"
|
|
||||||
checked={!!value}
|
|
||||||
onChange={onChange}
|
|
||||||
onBlur={onBlur}
|
|
||||||
ref={ref}
|
|
||||||
disabled={disabled}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="service__switch custom-switch-indicator"></span>
|
|
||||||
|
|
||||||
<span className="service__text" title={placeholder}>
|
|
||||||
{placeholder}
|
|
||||||
</span>
|
|
||||||
{icon && <div dangerouslySetInnerHTML={{ __html: window.atob(icon) }} className="service__icon" />}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{!disabled && error && <span className="form__message form__message--error">{error}</span>}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
ServiceField.displayName = 'ServiceField';
|
|
||||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { Form } from './Form';
|
import Form from './Form';
|
||||||
|
|
||||||
import Card from '../../ui/Card';
|
import Card from '../../ui/Card';
|
||||||
import { getBlockedServices, getAllBlockedServices, updateBlockedServices } from '../../../actions/services';
|
import { getBlockedServices, getAllBlockedServices, updateBlockedServices } from '../../../actions/services';
|
||||||
@@ -86,8 +86,7 @@ const Services = () => {
|
|||||||
<Card
|
<Card
|
||||||
title={t('schedule_services')}
|
title={t('schedule_services')}
|
||||||
subtitle={t('schedule_services_desc')}
|
subtitle={t('schedule_services_desc')}
|
||||||
bodyType="card-body box-body--settings"
|
bodyType="card-body box-body--settings">
|
||||||
>
|
|
||||||
<ScheduleForm schedule={services.list.schedule} onScheduleSubmit={handleScheduleSubmit} />
|
<ScheduleForm schedule={services.list.schedule} onScheduleSubmit={handleScheduleSubmit} />
|
||||||
</Card>
|
</Card>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const Header = () => {
|
|||||||
<div className="header__column">
|
<div className="header__column">
|
||||||
<div className="header__right">
|
<div className="header__right">
|
||||||
{!processingProfile && name && (
|
{!processingProfile && name && (
|
||||||
<a href="control/logout" className="btn btn-sm btn-outline-secondary" data-testid="sign_out">
|
<a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||||
{t('sign_out')}
|
{t('sign_out')}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ const Row = memo(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style} className={className} onClick={onClick} role="row" data-testid="querylog_cell">
|
<div style={style} className={className} onClick={onClick} role="row">
|
||||||
<DateCell {...rowProps} />
|
<DateCell {...rowProps} />
|
||||||
|
|
||||||
<DomainCell {...rowProps} />
|
<DomainCell {...rowProps} />
|
||||||
|
|||||||
@@ -1,81 +1,158 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Field, type InjectedFormProps, reduxForm } from 'redux-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useFormContext } from 'react-hook-form';
|
|
||||||
import queryString from 'query-string';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEBOUNCE_FILTER_TIMEOUT,
|
DEBOUNCE_FILTER_TIMEOUT,
|
||||||
DEFAULT_LOGS_FILTER,
|
DEFAULT_LOGS_FILTER,
|
||||||
|
FORM_NAME,
|
||||||
RESPONSE_FILTER,
|
RESPONSE_FILTER,
|
||||||
RESPONSE_FILTER_QUERIES,
|
RESPONSE_FILTER_QUERIES,
|
||||||
} from '../../../helpers/constants';
|
} from '../../../helpers/constants';
|
||||||
import { setLogsFilter } from '../../../actions/queryLogs';
|
import { setLogsFilter } from '../../../actions/queryLogs';
|
||||||
import useDebounce from '../../../helpers/useDebounce';
|
import useDebounce from '../../../helpers/useDebounce';
|
||||||
|
|
||||||
import { getLogsUrlParams } from '../../../helpers/helpers';
|
import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers';
|
||||||
|
|
||||||
import { SearchField } from './SearchField';
|
import Tooltip from '../../ui/Tooltip';
|
||||||
import { SearchFormValues } from '..';
|
import { RootState } from '../../../initialState';
|
||||||
|
|
||||||
type Props = {
|
interface renderFilterFieldProps {
|
||||||
|
input: {
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
id: string;
|
||||||
|
onClearInputClick: (...args: unknown[]) => unknown;
|
||||||
className?: string;
|
className?: string;
|
||||||
setIsLoading: (value: boolean) => void;
|
placeholder?: string;
|
||||||
|
type?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
autoComplete?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
onKeyDown?: (...args: unknown[]) => unknown;
|
||||||
|
normalizeOnBlur?: (...args: unknown[]) => unknown;
|
||||||
|
meta: {
|
||||||
|
touched?: boolean;
|
||||||
|
error?: object;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderFilterField = ({
|
||||||
|
input,
|
||||||
|
id,
|
||||||
|
className,
|
||||||
|
placeholder,
|
||||||
|
type,
|
||||||
|
disabled,
|
||||||
|
autoComplete,
|
||||||
|
tooltip,
|
||||||
|
meta: { touched, error },
|
||||||
|
onClearInputClick,
|
||||||
|
onKeyDown,
|
||||||
|
normalizeOnBlur,
|
||||||
|
}: renderFilterFieldProps) => {
|
||||||
|
const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="input-group-search input-group-search__icon--magnifier">
|
||||||
|
<svg className="icons icon--24 icon--gray">
|
||||||
|
<use xlinkHref="#magnifier" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
{...input}
|
||||||
|
id={id}
|
||||||
|
placeholder={placeholder}
|
||||||
|
type={type}
|
||||||
|
className={className}
|
||||||
|
disabled={disabled}
|
||||||
|
autoComplete={autoComplete}
|
||||||
|
aria-label={placeholder}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onBlur={onBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classNames('input-group-search input-group-search__icon--cross', {
|
||||||
|
invisible: input.value.length < 1,
|
||||||
|
})}>
|
||||||
|
<svg className="icons icon--20 icon--gray" onClick={onClearInputClick}>
|
||||||
|
<use xlinkHref="#cross" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="input-group-search input-group-search__icon--tooltip">
|
||||||
|
<Tooltip content={tooltip} className="tooltip-container">
|
||||||
|
<svg className="icons icon--20 icon--gray">
|
||||||
|
<use xlinkHref="#question" />
|
||||||
|
</svg>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
{!disabled && touched && error && <span className="form__message form__message--error">{error}</span>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Form = ({ className, setIsLoading }: Props) => {
|
const FORM_NAMES = {
|
||||||
|
search: 'search',
|
||||||
|
response_status: 'response_status',
|
||||||
|
};
|
||||||
|
|
||||||
|
type FiltersFormProps = {
|
||||||
|
className?: string;
|
||||||
|
responseStatusClass?: string;
|
||||||
|
setIsLoading: (...args: unknown[]) => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Form = (props: FiltersFormProps & InjectedFormProps) => {
|
||||||
|
const { className = '', responseStatusClass, setIsLoading, change } = props;
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { register, watch, setValue } = useFormContext<SearchFormValues>();
|
const { response_status, search } = useSelector(
|
||||||
|
(state: RootState) => state?.form[FORM_NAME.LOGS_FILTER].values,
|
||||||
|
shallowEqual,
|
||||||
|
);
|
||||||
|
|
||||||
const searchValue = watch('search');
|
const [debouncedSearch, setDebouncedSearch] = useDebounce(search.trim(), DEBOUNCE_FILTER_TIMEOUT);
|
||||||
const responseStatusValue = watch('response_status');
|
|
||||||
|
|
||||||
const [debouncedSearch, setDebouncedSearch] = useDebounce(searchValue.trim(), DEBOUNCE_FILTER_TIMEOUT);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
setLogsFilter({
|
setLogsFilter({
|
||||||
response_status: responseStatusValue,
|
response_status,
|
||||||
search: debouncedSearch,
|
search: debouncedSearch,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
history.replace(`${getLogsUrlParams(debouncedSearch, responseStatusValue)}`);
|
history.replace(`${getLogsUrlParams(debouncedSearch, response_status)}`);
|
||||||
}, [responseStatusValue, debouncedSearch]);
|
}, [response_status, debouncedSearch]);
|
||||||
|
|
||||||
useEffect(() => {
|
if (response_status && !(response_status in RESPONSE_FILTER_QUERIES)) {
|
||||||
if (responseStatusValue && !(responseStatusValue in RESPONSE_FILTER_QUERIES)) {
|
change(FORM_NAMES.response_status, DEFAULT_LOGS_FILTER[FORM_NAMES.response_status]);
|
||||||
setValue('response_status', DEFAULT_LOGS_FILTER.response_status);
|
}
|
||||||
}
|
|
||||||
}, [responseStatusValue, setValue]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const { search: searchUrlParam } = queryString.parse(history.location.search);
|
|
||||||
|
|
||||||
if (searchUrlParam !== searchValue) {
|
|
||||||
setValue('search', searchUrlParam ? searchUrlParam.toString() : '');
|
|
||||||
}
|
|
||||||
}, [history.location.search]);
|
|
||||||
|
|
||||||
const onInputClear = async () => {
|
const onInputClear = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
history.push(getLogsUrlParams(DEFAULT_LOGS_FILTER.search, responseStatusValue));
|
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEnterPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const onEnterPress = (e: any) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
setDebouncedSearch(searchValue);
|
setDebouncedSearch(search);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeOnBlur = (data: any) => data.trim();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="d-flex flex-wrap form-control--container"
|
className="d-flex flex-wrap form-control--container"
|
||||||
@@ -83,29 +160,40 @@ export const Form = ({ className, setIsLoading }: Props) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}>
|
}}>
|
||||||
<div className="field__search">
|
<div className="field__search">
|
||||||
<SearchField
|
<Field
|
||||||
data-testid="querylog_search"
|
id={FORM_NAMES.search}
|
||||||
value={searchValue}
|
name={FORM_NAMES.search}
|
||||||
handleChange={(val) => setValue('search', val)}
|
component={renderFilterField}
|
||||||
onKeyDown={onEnterPress}
|
type="text"
|
||||||
onClear={onInputClear}
|
className={classNames('form-control form-control--search form-control--transparent', className)}
|
||||||
placeholder={t('domain_or_client')}
|
placeholder={t('domain_or_client')}
|
||||||
tooltip={t('query_log_strict_search')}
|
tooltip={t('query_log_strict_search')}
|
||||||
className={classNames('form-control form-control--search form-control--transparent', className)}
|
onClearInputClick={onInputClear}
|
||||||
|
onKeyDown={onEnterPress}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="field__select">
|
<div className="field__select">
|
||||||
<select
|
<Field
|
||||||
{...register('response_status')}
|
name={FORM_NAMES.response_status}
|
||||||
className="form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent d-sm-block">
|
component="select"
|
||||||
|
className={classNames(
|
||||||
|
'form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent',
|
||||||
|
responseStatusClass,
|
||||||
|
)}>
|
||||||
{Object.values(RESPONSE_FILTER).map(({ QUERY, LABEL, disabled }: any) => (
|
{Object.values(RESPONSE_FILTER).map(({ QUERY, LABEL, disabled }: any) => (
|
||||||
<option key={LABEL} value={QUERY} disabled={disabled}>
|
<option key={LABEL} value={QUERY} disabled={disabled}>
|
||||||
{t(LABEL)}
|
{t(LABEL)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FiltersForm = reduxForm<Record<string, any>, FiltersFormProps>({
|
||||||
|
form: FORM_NAME.LOGS_FILTER,
|
||||||
|
enableReinitialize: true,
|
||||||
|
})(Form);
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import React, { ComponentProps } from 'react';
|
|
||||||
import Tooltip from '../../ui/Tooltip';
|
|
||||||
|
|
||||||
interface Props extends ComponentProps<'input'> {
|
|
||||||
handleChange: (newValue: string) => void;
|
|
||||||
onClear: () => void;
|
|
||||||
tooltip?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SearchField = ({
|
|
||||||
handleChange,
|
|
||||||
onClear,
|
|
||||||
value,
|
|
||||||
tooltip,
|
|
||||||
className,
|
|
||||||
...rest
|
|
||||||
}: Props) => {
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
handleChange(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
||||||
e.target.value = e.target.value.trim();
|
|
||||||
handleChange(e.target.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="input-group-search input-group-search__icon--magnifier">
|
|
||||||
<svg className="icons icon--24 icon--gray">
|
|
||||||
<use xlinkHref="#magnifier" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
className={className}
|
|
||||||
value={value}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
{typeof value === 'string' && value.length > 0 && (
|
|
||||||
<div
|
|
||||||
className="input-group-search input-group-search__icon--cross"
|
|
||||||
onClick={onClear}
|
|
||||||
>
|
|
||||||
<svg className="icons icon--20 icon--gray">
|
|
||||||
<use xlinkHref="#cross" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{tooltip && (
|
|
||||||
<span className="input-group-search input-group-search__icon--tooltip">
|
|
||||||
<Tooltip content={tooltip} className="tooltip-container">
|
|
||||||
<svg className="icons icon--20 icon--gray">
|
|
||||||
<use xlinkHref="#question" />
|
|
||||||
</svg>
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -2,16 +2,17 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { Form } from './Form';
|
import { FiltersForm } from './Form';
|
||||||
import { refreshFilteredLogs } from '../../../actions/queryLogs';
|
import { refreshFilteredLogs } from '../../../actions/queryLogs';
|
||||||
import { addSuccessToast } from '../../../actions/toasts';
|
import { addSuccessToast } from '../../../actions/toasts';
|
||||||
|
|
||||||
interface FiltersProps {
|
interface FiltersProps {
|
||||||
|
filter: object;
|
||||||
processingGetLogs: boolean;
|
processingGetLogs: boolean;
|
||||||
setIsLoading: (...args: unknown[]) => unknown;
|
setIsLoading: (...args: unknown[]) => unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Filters = ({ setIsLoading }: FiltersProps) => {
|
const Filters = ({ filter, setIsLoading }: FiltersProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
@@ -37,9 +38,7 @@ const Filters = ({ setIsLoading }: FiltersProps) => {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</h1>
|
</h1>
|
||||||
<Form
|
<FiltersForm responseStatusClass="d-sm-block" setIsLoading={setIsLoading} initialValues={filter} />
|
||||||
setIsLoading={setIsLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ interface InfiniteTableProps {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
items: unknown[];
|
items: unknown[];
|
||||||
isSmallScreen: boolean;
|
isSmallScreen: boolean;
|
||||||
currentQuery: string;
|
|
||||||
setDetailedDataCurrent: Dispatch<SetStateAction<any>>;
|
setDetailedDataCurrent: Dispatch<SetStateAction<any>>;
|
||||||
setButtonType: (...args: unknown[]) => unknown;
|
setButtonType: (...args: unknown[]) => unknown;
|
||||||
setModalOpened: (...args: unknown[]) => unknown;
|
setModalOpened: (...args: unknown[]) => unknown;
|
||||||
@@ -28,7 +27,6 @@ const InfiniteTable = ({
|
|||||||
isLoading,
|
isLoading,
|
||||||
items,
|
items,
|
||||||
isSmallScreen,
|
isSmallScreen,
|
||||||
currentQuery,
|
|
||||||
setDetailedDataCurrent,
|
setDetailedDataCurrent,
|
||||||
setButtonType,
|
setButtonType,
|
||||||
setModalOpened,
|
setModalOpened,
|
||||||
@@ -45,7 +43,7 @@ const InfiniteTable = ({
|
|||||||
|
|
||||||
const listener = useCallback(() => {
|
const listener = useCallback(() => {
|
||||||
if (!loadingRef.current && loader.current && isScrolledIntoView(loader.current)) {
|
if (!loadingRef.current && loader.current && isScrolledIntoView(loader.current)) {
|
||||||
dispatch(getLogs(currentQuery));
|
dispatch(getLogs());
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { BLOCK_ACTIONS, MEDIUM_SCREEN_SIZE } from '../../helpers/constants';
|
||||||
import { BLOCK_ACTIONS, DEFAULT_LOGS_FILTER, MEDIUM_SCREEN_SIZE } from '../../helpers/constants';
|
|
||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
|
|
||||||
@@ -30,12 +29,7 @@ import { BUTTON_PREFIX } from './Cells/helpers';
|
|||||||
import AnonymizerNotification from './AnonymizerNotification';
|
import AnonymizerNotification from './AnonymizerNotification';
|
||||||
import { RootState } from '../../initialState';
|
import { RootState } from '../../initialState';
|
||||||
|
|
||||||
export type SearchFormValues = {
|
const processContent = (data: any, buttonType: string) =>
|
||||||
search: string;
|
|
||||||
response_status: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const processContent = (data: any, _buttonType: string) =>
|
|
||||||
Object.entries(data).map(([key, value]) => {
|
Object.entries(data).map(([key, value]) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null;
|
return null;
|
||||||
@@ -82,6 +76,7 @@ const Logs = () => {
|
|||||||
const {
|
const {
|
||||||
enabled,
|
enabled,
|
||||||
processingGetConfig,
|
processingGetConfig,
|
||||||
|
// processingAdditionalLogs,
|
||||||
processingGetLogs,
|
processingGetLogs,
|
||||||
anonymize_client_ip: anonymizeClientIp,
|
anonymize_client_ip: anonymizeClientIp,
|
||||||
} = useSelector((state: RootState) => state.queryLogs, shallowEqual);
|
} = useSelector((state: RootState) => state.queryLogs, shallowEqual);
|
||||||
@@ -93,17 +88,6 @@ const Logs = () => {
|
|||||||
const search = search_url_param || filter?.search || '';
|
const search = search_url_param || filter?.search || '';
|
||||||
const response_status = response_status_url_param || filter?.response_status || '';
|
const response_status = response_status_url_param || filter?.response_status || '';
|
||||||
|
|
||||||
const formMethods = useForm<SearchFormValues>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
search: search || DEFAULT_LOGS_FILTER.search,
|
|
||||||
response_status: response_status || DEFAULT_LOGS_FILTER.response_status,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { watch } = formMethods;
|
|
||||||
const currentQuery = watch('search');
|
|
||||||
|
|
||||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth <= MEDIUM_SCREEN_SIZE);
|
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth <= MEDIUM_SCREEN_SIZE);
|
||||||
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
|
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
|
||||||
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
|
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
|
||||||
@@ -190,12 +174,15 @@ const Logs = () => {
|
|||||||
|
|
||||||
const renderPage = () => (
|
const renderPage = () => (
|
||||||
<>
|
<>
|
||||||
<FormProvider {...formMethods}>
|
<Filters
|
||||||
<Filters
|
filter={{
|
||||||
setIsLoading={setIsLoading}
|
response_status,
|
||||||
processingGetLogs={processingGetLogs}
|
search,
|
||||||
/>
|
}}
|
||||||
</FormProvider>
|
setIsLoading={setIsLoading}
|
||||||
|
processingGetLogs={processingGetLogs}
|
||||||
|
// processingAdditionalLogs={processingAdditionalLogs}
|
||||||
|
/>
|
||||||
|
|
||||||
<InfiniteTable
|
<InfiniteTable
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -204,7 +191,6 @@ const Logs = () => {
|
|||||||
setDetailedDataCurrent={setDetailedDataCurrent}
|
setDetailedDataCurrent={setDetailedDataCurrent}
|
||||||
setButtonType={setButtonType}
|
setButtonType={setButtonType}
|
||||||
setModalOpened={setModalOpened}
|
setModalOpened={setModalOpened}
|
||||||
currentQuery={currentQuery}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import whoisCell from './whoisCell';
|
|||||||
|
|
||||||
import LogsSearchLink from '../../ui/LogsSearchLink';
|
import LogsSearchLink from '../../ui/LogsSearchLink';
|
||||||
|
|
||||||
import { sortIp, formatNumber } from '../../../helpers/helpers';
|
import { sortIp } from '../../../helpers/helpers';
|
||||||
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
|
import { LocalStorageHelper, LOCAL_STORAGE_KEYS } from '../../../helpers/localStorageHelper';
|
||||||
import { TABLES_MIN_ROWS } from '../../../helpers/constants';
|
import { TABLES_MIN_ROWS } from '../../../helpers/constants';
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class AutoClients extends Component<AutoClientsProps> {
|
|||||||
return (
|
return (
|
||||||
<div className="logs__row">
|
<div className="logs__row">
|
||||||
<div className="logs__text" title={clientStats}>
|
<div className="logs__text" title={clientStats}>
|
||||||
<LogsSearchLink search={row.original.ip}>{formatNumber(clientStats)}</LogsSearchLink>
|
<LogsSearchLink search={row.original.ip}>{clientStats}</LogsSearchLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import ReactTable from 'react-table';
|
|||||||
import { getAllBlockedServices, getBlockedServices } from '../../../../actions/services';
|
import { getAllBlockedServices, getBlockedServices } from '../../../../actions/services';
|
||||||
|
|
||||||
import { initSettings } from '../../../../actions';
|
import { initSettings } from '../../../../actions';
|
||||||
import { splitByNewLine, countClientsStatistics, sortIp, getService, formatNumber } from '../../../../helpers/helpers';
|
import { splitByNewLine, countClientsStatistics, sortIp, getService } from '../../../../helpers/helpers';
|
||||||
import { MODAL_TYPE, LOCAL_TIMEZONE_VALUE, TABLES_MIN_ROWS } from '../../../../helpers/constants';
|
import { MODAL_TYPE, LOCAL_TIMEZONE_VALUE, TABLES_MIN_ROWS } from '../../../../helpers/constants';
|
||||||
|
|
||||||
import Card from '../../../ui/Card';
|
import Card from '../../../ui/Card';
|
||||||
@@ -111,12 +111,6 @@ const ClientsTable = ({
|
|||||||
config.tags = [];
|
config.tags = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.ids) {
|
|
||||||
config.ids = values.ids.map((id) => id.name);
|
|
||||||
} else {
|
|
||||||
config.ids = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof values.upstreams_cache_size === 'string') {
|
if (typeof values.upstreams_cache_size === 'string') {
|
||||||
config.upstreams_cache_size = 0;
|
config.upstreams_cache_size = 0;
|
||||||
}
|
}
|
||||||
@@ -306,16 +300,13 @@ const ClientsTable = ({
|
|||||||
sortMethod: (a: any, b: any) => b - a,
|
sortMethod: (a: any, b: any) => b - a,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
Cell: (row: any) => {
|
Cell: (row: any) => {
|
||||||
let content = row.value;
|
const content = CellWrap(row);
|
||||||
if (typeof content === "number") {
|
|
||||||
content = formatNumber(content);
|
if (!row.value) {
|
||||||
} else {
|
|
||||||
content = CellWrap(row);
|
|
||||||
}
|
|
||||||
if (!content) {
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
return <LogsSearchLink search={row.original.name}>{content}</LogsSearchLink>;
|
|
||||||
|
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
514
client/src/components/Settings/Clients/Form.tsx
Normal file
514
client/src/components/Settings/Clients/Form.tsx
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { connect, useSelector } from 'react-redux';
|
||||||
|
import { Field, FieldArray, reduxForm, formValueSelector, FormErrors } from 'redux-form';
|
||||||
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
import Select from 'react-select';
|
||||||
|
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
|
import Tabs from '../../ui/Tabs';
|
||||||
|
|
||||||
|
import Examples from '../Dns/Upstream/Examples';
|
||||||
|
|
||||||
|
import { ScheduleForm } from '../../Filters/Services/ScheduleForm';
|
||||||
|
import { toggleAllServices, trimLinesAndRemoveEmpty, captitalizeWords } from '../../../helpers/helpers';
|
||||||
|
import {
|
||||||
|
toNumber,
|
||||||
|
renderInputField,
|
||||||
|
renderGroupField,
|
||||||
|
CheckboxField,
|
||||||
|
renderServiceField,
|
||||||
|
renderTextareaField,
|
||||||
|
} from '../../../helpers/form';
|
||||||
|
import { validateClientId, validateRequiredValue } from '../../../helpers/validators';
|
||||||
|
import { CLIENT_ID_LINK, FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
|
||||||
|
import './Service.css';
|
||||||
|
import { RootState } from '../../../initialState';
|
||||||
|
|
||||||
|
const settingsCheckboxes = [
|
||||||
|
{
|
||||||
|
name: 'use_global_settings',
|
||||||
|
placeholder: 'client_global_settings',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'filtering_enabled',
|
||||||
|
placeholder: 'block_domain_use_filters_and_hosts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'safebrowsing_enabled',
|
||||||
|
placeholder: 'use_adguard_browsing_sec',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parental_enabled',
|
||||||
|
placeholder: 'use_adguard_parental',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const logAndStatsCheckboxes = [
|
||||||
|
{
|
||||||
|
name: 'ignore_querylog',
|
||||||
|
placeholder: 'ignore_query_log',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ignore_statistics',
|
||||||
|
placeholder: 'ignore_statistics',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const validate = (values: any): FormErrors<any, string> => {
|
||||||
|
const errors: {
|
||||||
|
name?: string;
|
||||||
|
ids?: string[];
|
||||||
|
} = {};
|
||||||
|
const { name, ids } = values;
|
||||||
|
|
||||||
|
errors.name = validateRequiredValue(name);
|
||||||
|
|
||||||
|
if (ids && ids.length) {
|
||||||
|
const idArrayErrors: any = [];
|
||||||
|
ids.forEach((id: any, idx: any) => {
|
||||||
|
idArrayErrors[idx] = validateRequiredValue(id) || validateClientId(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (idArrayErrors.length) {
|
||||||
|
errors.ids = idArrayErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ts-expect-error FIXME: ts migration
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFieldsWrapper = (placeholder: any, buttonTitle: any) =>
|
||||||
|
function cell(row: any) {
|
||||||
|
const { fields } = row;
|
||||||
|
return (
|
||||||
|
<div className="form__group">
|
||||||
|
{fields.map((ip: any, index: any) => (
|
||||||
|
<div key={index} className="mb-1">
|
||||||
|
<Field
|
||||||
|
name={ip}
|
||||||
|
component={renderGroupField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder={placeholder}
|
||||||
|
isActionAvailable={index !== 0}
|
||||||
|
removeField={() => fields.remove(index)}
|
||||||
|
normalizeOnBlur={(data: any) => data.trim()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-link btn-block btn-sm"
|
||||||
|
onClick={() => fields.push()}
|
||||||
|
title={buttonTitle}>
|
||||||
|
<svg className="icon icon--24">
|
||||||
|
<use xlinkHref="#plus" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Should create function outside of component to prevent component re-renders
|
||||||
|
const renderFields = renderFieldsWrapper(i18n.t('form_enter_id'), i18n.t('form_add_id'));
|
||||||
|
|
||||||
|
interface renderMultiselectProps {
|
||||||
|
input: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
checked: boolean;
|
||||||
|
onChange: (...args: unknown[]) => unknown;
|
||||||
|
onBlur: (...args: unknown[]) => unknown;
|
||||||
|
};
|
||||||
|
placeholder?: string;
|
||||||
|
options?: unknown[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderMultiselect = (props: renderMultiselectProps) => {
|
||||||
|
const { input, placeholder, options } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...input}
|
||||||
|
options={options}
|
||||||
|
className="basic-multi-select"
|
||||||
|
classNamePrefix="select"
|
||||||
|
onChange={(value: any) => input.onChange(value)}
|
||||||
|
onBlur={() => input.onBlur(input.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
blurInputOnSelect={false}
|
||||||
|
isMulti
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FormProps {
|
||||||
|
pristine: boolean;
|
||||||
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
reset: (...args: unknown[]) => string;
|
||||||
|
change: (...args: unknown[]) => unknown;
|
||||||
|
submitting: boolean;
|
||||||
|
handleClose: (...args: unknown[]) => unknown;
|
||||||
|
useGlobalSettings?: boolean;
|
||||||
|
useGlobalServices?: boolean;
|
||||||
|
blockedServicesSchedule?: {
|
||||||
|
time_zone: string;
|
||||||
|
};
|
||||||
|
t: (...args: unknown[]) => string;
|
||||||
|
processingAdding: boolean;
|
||||||
|
processingUpdating: boolean;
|
||||||
|
invalid: boolean;
|
||||||
|
tagsOptions: unknown[];
|
||||||
|
initialValues?: {
|
||||||
|
safe_search: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let Form = (props: FormProps) => {
|
||||||
|
const {
|
||||||
|
t,
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
change,
|
||||||
|
submitting,
|
||||||
|
useGlobalSettings,
|
||||||
|
useGlobalServices,
|
||||||
|
blockedServicesSchedule,
|
||||||
|
handleClose,
|
||||||
|
processingAdding,
|
||||||
|
processingUpdating,
|
||||||
|
invalid,
|
||||||
|
tagsOptions,
|
||||||
|
initialValues,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const services = useSelector((store: RootState) => store?.services);
|
||||||
|
const { safe_search } = initialValues;
|
||||||
|
const safeSearchServices = { ...safe_search };
|
||||||
|
delete safeSearchServices.enabled;
|
||||||
|
|
||||||
|
const [activeTabLabel, setActiveTabLabel] = useState('settings');
|
||||||
|
|
||||||
|
const handleScheduleSubmit = (values: any) => {
|
||||||
|
change('blocked_services_schedule', { ...values });
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabs = {
|
||||||
|
settings: {
|
||||||
|
title: 'settings',
|
||||||
|
|
||||||
|
component: (
|
||||||
|
<div title={props.t('main_settings')}>
|
||||||
|
<div className="form__label--bot form__label--bold">{t('protection_section_label')}</div>
|
||||||
|
{settingsCheckboxes.map((setting) => (
|
||||||
|
<div className="form__group" key={setting.name}>
|
||||||
|
<Field
|
||||||
|
name={setting.name}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t(setting.placeholder)}
|
||||||
|
disabled={setting.name !== 'use_global_settings' ? useGlobalSettings : false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="form__group">
|
||||||
|
<Field
|
||||||
|
name="safe_search.enabled"
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('enforce_safe_search')}
|
||||||
|
disabled={useGlobalSettings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__group--inner">
|
||||||
|
{Object.keys(safeSearchServices).map((searchKey) => (
|
||||||
|
<div key={searchKey}>
|
||||||
|
<Field
|
||||||
|
name={`safe_search.${searchKey}`}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={captitalizeWords(searchKey)}
|
||||||
|
disabled={useGlobalSettings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__label--bold form__label--top form__label--bot">
|
||||||
|
{t('log_and_stats_section_label')}
|
||||||
|
</div>
|
||||||
|
{logAndStatsCheckboxes.map((setting) => (
|
||||||
|
<div className="form__group" key={setting.name}>
|
||||||
|
<Field
|
||||||
|
name={setting.name}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t(setting.placeholder)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
block_services: {
|
||||||
|
title: 'block_services',
|
||||||
|
|
||||||
|
component: (
|
||||||
|
<div title={props.t('block_services')}>
|
||||||
|
<div className="form__group">
|
||||||
|
<Field
|
||||||
|
name="use_global_blocked_services"
|
||||||
|
type="checkbox"
|
||||||
|
component={renderServiceField}
|
||||||
|
placeholder={t('blocked_services_global')}
|
||||||
|
modifier="service--global"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="row mb-4">
|
||||||
|
<div className="col-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary btn-block"
|
||||||
|
disabled={useGlobalServices}
|
||||||
|
onClick={() => toggleAllServices(services.allServices, change, true)}>
|
||||||
|
<Trans>block_all</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary btn-block"
|
||||||
|
disabled={useGlobalServices}
|
||||||
|
onClick={() => toggleAllServices(services.allServices, change, false)}>
|
||||||
|
<Trans>unblock_all</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{services.allServices.length > 0 && (
|
||||||
|
<div className="services">
|
||||||
|
{services.allServices.map((service: any) => (
|
||||||
|
<Field
|
||||||
|
key={service.id}
|
||||||
|
icon={service.icon_svg}
|
||||||
|
name={`blocked_services.${service.id}`}
|
||||||
|
type="checkbox"
|
||||||
|
component={renderServiceField}
|
||||||
|
placeholder={service.name}
|
||||||
|
disabled={useGlobalServices}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
schedule_services: {
|
||||||
|
title: 'schedule_services',
|
||||||
|
component: (
|
||||||
|
<>
|
||||||
|
<div className="form__desc mb-4">
|
||||||
|
<Trans>schedule_services_desc_client</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ScheduleForm
|
||||||
|
schedule={blockedServicesSchedule}
|
||||||
|
onScheduleSubmit={handleScheduleSubmit}
|
||||||
|
clientForm
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
upstream_dns: {
|
||||||
|
title: 'upstream_dns',
|
||||||
|
|
||||||
|
component: (
|
||||||
|
<div title={props.t('upstream_dns')}>
|
||||||
|
<div className="form__desc mb-3">
|
||||||
|
<Trans
|
||||||
|
components={[
|
||||||
|
<a href="#dns" key="0">
|
||||||
|
link
|
||||||
|
</a>,
|
||||||
|
]}>
|
||||||
|
upstream_dns_client_desc
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
id="upstreams"
|
||||||
|
name="upstreams"
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea mb-5"
|
||||||
|
placeholder={t('upstream_dns')}
|
||||||
|
normalizeOnBlur={trimLinesAndRemoveEmpty}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Examples />
|
||||||
|
|
||||||
|
<div className="form__label--bold mt-5 mb-3">{t('upstream_dns_cache_configuration')}</div>
|
||||||
|
|
||||||
|
<div className="form__group mb-2">
|
||||||
|
<Field
|
||||||
|
name="upstreams_cache_enabled"
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('enable_upstream_dns_cache')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label htmlFor="upstreams_cache_size" className="form__label">
|
||||||
|
{t('dns_cache_size')}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="upstreams_cache_size"
|
||||||
|
type="number"
|
||||||
|
component={renderInputField}
|
||||||
|
placeholder={t('enter_cache_size')}
|
||||||
|
className="form-control"
|
||||||
|
normalize={toNumber}
|
||||||
|
min={0}
|
||||||
|
max={UINT32_RANGE.MAX}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeTab = tabs[activeTabLabel].component;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="modal-body">
|
||||||
|
<div className="form__group mb-0">
|
||||||
|
<div className="form__group">
|
||||||
|
<Field
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
component={renderInputField}
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('form_client_name')}
|
||||||
|
normalizeOnBlur={(data: any) => data.trim()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__group mb-4">
|
||||||
|
<div className="form__label">
|
||||||
|
<strong className="mr-3">
|
||||||
|
<Trans>tags_title</Trans>
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__desc mt-0 mb-2">
|
||||||
|
<Trans
|
||||||
|
components={[
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax_ctag&from=ui&app=home"
|
||||||
|
key="0">
|
||||||
|
link
|
||||||
|
</a>,
|
||||||
|
]}>
|
||||||
|
tags_desc
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="tags"
|
||||||
|
component={renderMultiselect}
|
||||||
|
placeholder={t('form_select_tags')}
|
||||||
|
options={tagsOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__group">
|
||||||
|
<div className="form__label">
|
||||||
|
<strong className="mr-3">
|
||||||
|
<Trans>client_identifier</Trans>
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__desc mt-0">
|
||||||
|
<Trans
|
||||||
|
components={[
|
||||||
|
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0">
|
||||||
|
text
|
||||||
|
</a>,
|
||||||
|
]}>
|
||||||
|
client_identifier_desc
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form__group">
|
||||||
|
<FieldArray name="ids" component={renderFields} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tabs
|
||||||
|
controlClass="form"
|
||||||
|
tabs={tabs}
|
||||||
|
activeTabLabel={activeTabLabel}
|
||||||
|
setActiveTabLabel={setActiveTabLabel}>
|
||||||
|
{activeTab}
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-footer">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary btn-standard"
|
||||||
|
disabled={submitting}
|
||||||
|
onClick={() => {
|
||||||
|
reset();
|
||||||
|
handleClose();
|
||||||
|
}}>
|
||||||
|
<Trans>cancel_btn</Trans>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={submitting || invalid || processingAdding || processingUpdating}>
|
||||||
|
<Trans>save_btn</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector(FORM_NAME.CLIENT);
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const useGlobalSettings = selector(state, 'use_global_settings');
|
||||||
|
const useGlobalServices = selector(state, 'use_global_blocked_services');
|
||||||
|
const blockedServicesSchedule = selector(state, 'blocked_services_schedule');
|
||||||
|
return {
|
||||||
|
useGlobalSettings,
|
||||||
|
useGlobalServices,
|
||||||
|
blockedServicesSchedule,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withTranslation(),
|
||||||
|
reduxForm({
|
||||||
|
form: FORM_NAME.CLIENT,
|
||||||
|
enableReinitialize: true,
|
||||||
|
validate,
|
||||||
|
}),
|
||||||
|
])(Form);
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
|
||||||
import { ClientForm } from '../types';
|
|
||||||
import { BlockedService } from '../../../../Filters/Services/Form';
|
|
||||||
import { ServiceField } from '../../../../Filters/Services/ServiceField';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
services: BlockedService[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BlockedServices = ({ services }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { watch, setValue, control } = useFormContext<ClientForm>();
|
|
||||||
|
|
||||||
const useGlobalServices = watch('use_global_blocked_services');
|
|
||||||
|
|
||||||
const handleToggleAllServices = (isSelected: boolean) => {
|
|
||||||
services.forEach((service: BlockedService) => setValue(`blocked_services.${service.id}`, isSelected));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div title={t('block_services')}>
|
|
||||||
<div className="form__group">
|
|
||||||
<Controller
|
|
||||||
name="use_global_blocked_services"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<ServiceField
|
|
||||||
{...field}
|
|
||||||
data-testid="clients_use_global_blocked_services"
|
|
||||||
placeholder={t('blocked_services_global')}
|
|
||||||
className="service--global"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="row mb-4">
|
|
||||||
<div className="col-6">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-testid="clients_block_all"
|
|
||||||
className="btn btn-secondary btn-block"
|
|
||||||
disabled={useGlobalServices}
|
|
||||||
onClick={() => handleToggleAllServices(true)}>
|
|
||||||
<Trans>block_all</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-6">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-testid="clients_unblock_all"
|
|
||||||
className="btn btn-secondary btn-block"
|
|
||||||
disabled={useGlobalServices}
|
|
||||||
onClick={() => handleToggleAllServices(false)}>
|
|
||||||
<Trans>unblock_all</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{services.length > 0 && (
|
|
||||||
<div className="services">
|
|
||||||
{services.map((service: BlockedService) => (
|
|
||||||
<Controller
|
|
||||||
key={service.id}
|
|
||||||
name={`blocked_services.${service.id}`}
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<ServiceField
|
|
||||||
{...field}
|
|
||||||
data-testid={`clients_service_${service.id}`}
|
|
||||||
placeholder={service.name}
|
|
||||||
disabled={useGlobalServices}
|
|
||||||
icon={service.icon_svg}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Controller, useFieldArray, useFormContext } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { ClientForm } from '../types';
|
|
||||||
import { Input } from '../../../../ui/Controls/Input';
|
|
||||||
import { validateClientId, validateRequiredValue } from '../../../../../helpers/validators';
|
|
||||||
|
|
||||||
export const ClientIds = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { control } = useFormContext<ClientForm>();
|
|
||||||
|
|
||||||
const { fields, append, remove } = useFieldArray<ClientForm>({
|
|
||||||
control,
|
|
||||||
name: 'ids',
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="form__group">
|
|
||||||
{fields.map((field, index) => (
|
|
||||||
<div key={field.id} className="mb-1">
|
|
||||||
<Controller
|
|
||||||
name={`ids.${index}.name`}
|
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
validate: {
|
|
||||||
required: (value) => validateRequiredValue(value),
|
|
||||||
validId: (value) => validateClientId(value),
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid={`clients_id_${index}`}
|
|
||||||
placeholder={t('form_enter_id')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
onBlur={(event) => {
|
|
||||||
const trimmedValue = event.target.value.trim();
|
|
||||||
field.onBlur();
|
|
||||||
field.onChange(trimmedValue);
|
|
||||||
}}
|
|
||||||
rightAddon={
|
|
||||||
index !== 0 && (
|
|
||||||
<span className="input-group-append">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-testid={`clients_id_remove_${index}`}
|
|
||||||
className="btn btn-secondary btn-icon btn-icon--green"
|
|
||||||
onClick={() => remove(index)}>
|
|
||||||
<svg className="icon icon--24">
|
|
||||||
<use xlinkHref="#cross" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-testid="clients_id_add"
|
|
||||||
className="btn btn-link btn-block btn-sm"
|
|
||||||
onClick={() => append({ name: '' })}
|
|
||||||
title={t('form_add_id')}>
|
|
||||||
<svg className="icon icon--24">
|
|
||||||
<use xlinkHref="#plus" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
|
||||||
import i18next from 'i18next';
|
|
||||||
import { captitalizeWords } from '../../../../../helpers/helpers';
|
|
||||||
import { ClientForm } from '../types';
|
|
||||||
import { Checkbox } from '../../../../ui/Controls/Checkbox';
|
|
||||||
|
|
||||||
type ProtectionSettings = 'use_global_settings' | 'filtering_enabled' | 'safebrowsing_enabled' | 'parental_enabled';
|
|
||||||
|
|
||||||
const settingsCheckboxes: {
|
|
||||||
name: ProtectionSettings;
|
|
||||||
placeholder: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
name: 'use_global_settings',
|
|
||||||
placeholder: i18next.t('client_global_settings'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'filtering_enabled',
|
|
||||||
placeholder: i18next.t('block_domain_use_filters_and_hosts'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'safebrowsing_enabled',
|
|
||||||
placeholder: i18next.t('use_adguard_browsing_sec'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parental_enabled',
|
|
||||||
placeholder: i18next.t('use_adguard_parental'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
type LogsStatsSettings = 'ignore_querylog' | 'ignore_statistics';
|
|
||||||
|
|
||||||
const logAndStatsCheckboxes: { name: LogsStatsSettings; placeholder: string }[] = [
|
|
||||||
{
|
|
||||||
name: 'ignore_querylog',
|
|
||||||
placeholder: i18next.t('ignore_query_log'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ignore_statistics',
|
|
||||||
placeholder: i18next.t('ignore_statistics'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
safeSearchServices: Record<string, boolean>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MainSettings = ({ safeSearchServices }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { watch, control } = useFormContext<ClientForm>();
|
|
||||||
|
|
||||||
const useGlobalSettings = watch('use_global_settings');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div title={t('main_settings')}>
|
|
||||||
<div className="form__label--bot form__label--bold">{t('protection_section_label')}</div>
|
|
||||||
{settingsCheckboxes.map((setting) => (
|
|
||||||
<div className="form__group" key={setting.name}>
|
|
||||||
<Controller
|
|
||||||
name={setting.name}
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
data-testid={`clients_${setting.name}`}
|
|
||||||
title={setting.placeholder}
|
|
||||||
disabled={setting.name !== 'use_global_settings' ? useGlobalSettings : false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<div className="form__group">
|
|
||||||
<Controller
|
|
||||||
name="safe_search.enabled"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
data-testid="clients_safe_search"
|
|
||||||
{...field}
|
|
||||||
title={t('enforce_safe_search')}
|
|
||||||
disabled={useGlobalSettings}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group--inner">
|
|
||||||
{Object.keys(safeSearchServices).map((searchKey) => (
|
|
||||||
<div key={searchKey}>
|
|
||||||
<Controller
|
|
||||||
name={`safe_search.${searchKey}`}
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
data-testid={`clients_safe_search_${searchKey}`}
|
|
||||||
title={captitalizeWords(searchKey)}
|
|
||||||
disabled={useGlobalSettings}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__label--bold form__label--top form__label--bot">
|
|
||||||
{t('log_and_stats_section_label')}
|
|
||||||
</div>
|
|
||||||
{logAndStatsCheckboxes.map((setting) => (
|
|
||||||
<div className="form__group" key={setting.name}>
|
|
||||||
<Controller
|
|
||||||
name={setting.name}
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox {...field} data-testid={`clients_${setting.name}`} title={setting.placeholder} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Trans } from 'react-i18next';
|
|
||||||
import { useFormContext } from 'react-hook-form';
|
|
||||||
import { ScheduleForm } from '../../../../Filters/Services/ScheduleForm';
|
|
||||||
import { ClientForm } from '../types';
|
|
||||||
|
|
||||||
export const ScheduleServices = () => {
|
|
||||||
const { watch, setValue } = useFormContext<ClientForm>();
|
|
||||||
|
|
||||||
const blockedServicesSchedule = watch('blocked_services_schedule');
|
|
||||||
|
|
||||||
const handleScheduleSubmit = (values: any) => {
|
|
||||||
setValue('blocked_services_schedule', values);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="form__desc mb-4">
|
|
||||||
<Trans>schedule_services_desc_client</Trans>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ScheduleForm schedule={blockedServicesSchedule} onScheduleSubmit={handleScheduleSubmit} clientForm />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
|
||||||
import Examples from '../../../Dns/Upstream/Examples';
|
|
||||||
import { UINT32_RANGE } from '../../../../../helpers/constants';
|
|
||||||
import { Textarea } from '../../../../ui/Controls/Textarea';
|
|
||||||
import { ClientForm } from '../types';
|
|
||||||
import { Checkbox } from '../../../../ui/Controls/Checkbox';
|
|
||||||
import { Input } from '../../../../ui/Controls/Input';
|
|
||||||
import { toNumber } from '../../../../../helpers/form';
|
|
||||||
|
|
||||||
export const UpstreamDns = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const { control } = useFormContext<ClientForm>();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div title={t('upstream_dns')}>
|
|
||||||
<div className="form__desc mb-3">
|
|
||||||
<Trans components={[<a href="#dns" key="0" />]}>upstream_dns_client_desc</Trans>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="upstreams"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Textarea
|
|
||||||
{...field}
|
|
||||||
data-testid="clients_upstreams"
|
|
||||||
className="form-control form-control--textarea mb-5"
|
|
||||||
placeholder={t('upstream_dns')}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Examples />
|
|
||||||
|
|
||||||
<div className="form__label--bold mt-5 mb-3">{t('upstream_dns_cache_configuration')}</div>
|
|
||||||
|
|
||||||
<div className="form__group mb-2">
|
|
||||||
<Controller
|
|
||||||
name="upstreams_cache_enabled"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
data-testid="clients_upstreams_cache_enabled"
|
|
||||||
title={t('enable_upstream_dns_cache')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
|
||||||
<label htmlFor="upstreams_cache_size" className="form__label">
|
|
||||||
{t('dns_cache_size')}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="upstreams_cache_size"
|
|
||||||
control={control}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="number"
|
|
||||||
data-testid="clients_upstreams_cache_size"
|
|
||||||
placeholder={t('enter_cache_size')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
min={0}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export { BlockedServices } from './BlockedServices';
|
|
||||||
export { ClientIds } from './ClientIds';
|
|
||||||
export { ScheduleServices } from './ScheduleServices';
|
|
||||||
export { MainSettings } from './MainSettings';
|
|
||||||
export { UpstreamDns } from './UpstreamDns';
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
|
||||||
import Select from 'react-select';
|
|
||||||
|
|
||||||
import Tabs from '../../../ui/Tabs';
|
|
||||||
import { CLIENT_ID_LINK, LOCAL_TIMEZONE_VALUE } from '../../../../helpers/constants';
|
|
||||||
import { RootState } from '../../../../initialState';
|
|
||||||
import { Input } from '../../../ui/Controls/Input';
|
|
||||||
import { validateRequiredValue } from '../../../../helpers/validators';
|
|
||||||
import { ClientForm } from './types';
|
|
||||||
import { BlockedServices, ClientIds, MainSettings, ScheduleServices, UpstreamDns } from './components';
|
|
||||||
|
|
||||||
import '../Service.css';
|
|
||||||
|
|
||||||
const defaultFormValues: ClientForm = {
|
|
||||||
ids: [{ name: '' }],
|
|
||||||
name: '',
|
|
||||||
tags: [],
|
|
||||||
use_global_settings: false,
|
|
||||||
filtering_enabled: false,
|
|
||||||
safebrowsing_enabled: false,
|
|
||||||
parental_enabled: false,
|
|
||||||
ignore_querylog: false,
|
|
||||||
ignore_statistics: false,
|
|
||||||
blocked_services: {},
|
|
||||||
safe_search: { enabled: false },
|
|
||||||
upstreams: '',
|
|
||||||
upstreams_cache_enabled: false,
|
|
||||||
upstreams_cache_size: 0,
|
|
||||||
use_global_blocked_services: false,
|
|
||||||
blocked_services_schedule: {
|
|
||||||
time_zone: LOCAL_TIMEZONE_VALUE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
onSubmit: (values: ClientForm) => void;
|
|
||||||
onClose: () => void;
|
|
||||||
useGlobalSettings?: boolean;
|
|
||||||
useGlobalServices?: boolean;
|
|
||||||
blockedServicesSchedule?: {
|
|
||||||
time_zone: string;
|
|
||||||
};
|
|
||||||
processingAdding: boolean;
|
|
||||||
processingUpdating: boolean;
|
|
||||||
tagsOptions: { label: string; value: string }[];
|
|
||||||
initialValues?: ClientForm;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Form = ({
|
|
||||||
onSubmit,
|
|
||||||
onClose,
|
|
||||||
processingAdding,
|
|
||||||
processingUpdating,
|
|
||||||
tagsOptions,
|
|
||||||
initialValues,
|
|
||||||
}: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const methods = useForm<ClientForm>({
|
|
||||||
defaultValues: {
|
|
||||||
...defaultFormValues,
|
|
||||||
...initialValues,
|
|
||||||
},
|
|
||||||
mode: 'onBlur',
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
reset,
|
|
||||||
control,
|
|
||||||
formState: { isSubmitting, isValid },
|
|
||||||
} = methods;
|
|
||||||
|
|
||||||
const services = useSelector((store: RootState) => store?.services);
|
|
||||||
const { safe_search } = initialValues;
|
|
||||||
const safeSearchServices = { ...safe_search };
|
|
||||||
delete safeSearchServices.enabled;
|
|
||||||
|
|
||||||
const [activeTabLabel, setActiveTabLabel] = useState('settings');
|
|
||||||
|
|
||||||
const tabs = {
|
|
||||||
settings: {
|
|
||||||
title: 'settings',
|
|
||||||
component: <MainSettings safeSearchServices={safeSearchServices} />,
|
|
||||||
},
|
|
||||||
block_services: {
|
|
||||||
title: 'block_services',
|
|
||||||
component: <BlockedServices services={services?.allServices} />,
|
|
||||||
},
|
|
||||||
schedule_services: {
|
|
||||||
title: 'schedule_services',
|
|
||||||
component: <ScheduleServices />,
|
|
||||||
},
|
|
||||||
upstream_dns: {
|
|
||||||
title: 'upstream_dns',
|
|
||||||
component: <UpstreamDns />,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const activeTab = tabs[activeTabLabel].component;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormProvider {...methods}>
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<div className="modal-body">
|
|
||||||
<div className="form__group mb-0">
|
|
||||||
<div className="form__group">
|
|
||||||
<Controller
|
|
||||||
name="name"
|
|
||||||
control={control}
|
|
||||||
rules={{ validate: validateRequiredValue }}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="clients_name"
|
|
||||||
placeholder={t('form_client_name')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
onBlur={(event) => {
|
|
||||||
const trimmedValue = event.target.value.trim();
|
|
||||||
field.onBlur();
|
|
||||||
field.onChange(trimmedValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group mb-4">
|
|
||||||
<div className="form__label">
|
|
||||||
<strong className="mr-3">
|
|
||||||
<Trans>tags_title</Trans>
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__desc mt-0 mb-2">
|
|
||||||
<Trans
|
|
||||||
components={[
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href="https://link.adtidy.org/forward.html?action=dns_kb_filtering_syntax_ctag&from=ui&app=home"
|
|
||||||
key="0"
|
|
||||||
/>,
|
|
||||||
]}>
|
|
||||||
tags_desc
|
|
||||||
</Trans>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="tags"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
data-testid="clients_tags"
|
|
||||||
options={tagsOptions}
|
|
||||||
className="basic-multi-select"
|
|
||||||
classNamePrefix="select"
|
|
||||||
isMulti
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group">
|
|
||||||
<div className="form__label">
|
|
||||||
<strong className="mr-3">
|
|
||||||
<Trans>client_identifier</Trans>
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__desc mt-0">
|
|
||||||
<Trans
|
|
||||||
components={[
|
|
||||||
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" key="0" />,
|
|
||||||
]}>
|
|
||||||
client_identifier_desc
|
|
||||||
</Trans>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form__group">
|
|
||||||
<ClientIds />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Tabs
|
|
||||||
controlClass="form"
|
|
||||||
tabs={tabs}
|
|
||||||
activeTabLabel={activeTabLabel}
|
|
||||||
setActiveTabLabel={setActiveTabLabel}>
|
|
||||||
{activeTab}
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="modal-footer">
|
|
||||||
<div className="btn-list">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary btn-standard"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
onClick={() => {
|
|
||||||
reset();
|
|
||||||
onClose();
|
|
||||||
}}>
|
|
||||||
<Trans>cancel_btn</Trans>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-success btn-standard"
|
|
||||||
disabled={isSubmitting || !isValid || processingAdding || processingUpdating}>
|
|
||||||
<Trans>save_btn</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</FormProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
export type ClientForm = {
|
|
||||||
name: string;
|
|
||||||
tags: { value: string; label: string }[];
|
|
||||||
ids: { name: string }[];
|
|
||||||
use_global_settings: boolean;
|
|
||||||
use_global_blocked_services: boolean;
|
|
||||||
blocked_services_schedule: {
|
|
||||||
time_zone: string;
|
|
||||||
};
|
|
||||||
safe_search: {
|
|
||||||
enabled: boolean;
|
|
||||||
[key: string]: boolean;
|
|
||||||
};
|
|
||||||
upstreams: string;
|
|
||||||
upstreams_cache_enabled: boolean;
|
|
||||||
upstreams_cache_size: number;
|
|
||||||
blocked_services: Record<string, boolean>;
|
|
||||||
filtering_enabled: boolean;
|
|
||||||
safebrowsing_enabled: boolean;
|
|
||||||
parental_enabled: boolean;
|
|
||||||
ignore_querylog: boolean;
|
|
||||||
ignore_statistics: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SubmitClientForm = Omit<ClientForm, 'ids' | 'tags'> & {
|
|
||||||
ids: string[];
|
|
||||||
tags: string[];
|
|
||||||
};
|
|
||||||
@@ -4,15 +4,8 @@ import { Trans, withTranslation } from 'react-i18next';
|
|||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
|
|
||||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||||
import { Form } from './Form';
|
|
||||||
|
|
||||||
const normalizeIds = (initialIds?: string[]): { name: string }[] => {
|
import Form from './Form';
|
||||||
if (!initialIds || initialIds.length === 0) {
|
|
||||||
return [{ name: '' }];
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialIds.map((id: string) => ({ name: id }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
|
const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
|
||||||
if (initial && initial.blocked_services) {
|
if (initial && initial.blocked_services) {
|
||||||
@@ -26,7 +19,6 @@ const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
|
|||||||
return {
|
return {
|
||||||
...initial,
|
...initial,
|
||||||
blocked_services: blocked,
|
blocked_services: blocked,
|
||||||
ids: normalizeIds(initial.ids),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,14 +26,11 @@ const getInitialData = ({ initial, modalType, clientId, clientName }: any) => {
|
|||||||
return {
|
return {
|
||||||
...initial,
|
...initial,
|
||||||
name: clientName,
|
name: clientName,
|
||||||
ids: [{ name: clientId }],
|
ids: [clientId],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return initial;
|
||||||
...initial,
|
|
||||||
ids: normalizeIds(initial.ids),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
@@ -52,7 +41,7 @@ interface ModalProps {
|
|||||||
handleClose: (...args: unknown[]) => unknown;
|
handleClose: (...args: unknown[]) => unknown;
|
||||||
processingAdding: boolean;
|
processingAdding: boolean;
|
||||||
processingUpdating: boolean;
|
processingUpdating: boolean;
|
||||||
tagsOptions: { label: string; value: string }[];
|
tagsOptions: unknown[];
|
||||||
t: (...args: unknown[]) => string;
|
t: (...args: unknown[]) => string;
|
||||||
clientId?: string;
|
clientId?: string;
|
||||||
}
|
}
|
||||||
@@ -96,7 +85,7 @@ const Modal = ({
|
|||||||
<Form
|
<Form
|
||||||
initialValues={{ ...initialData }}
|
initialValues={{ ...initialData }}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onClose={handleClose}
|
handleClose={handleClose}
|
||||||
processingAdding={processingAdding}
|
processingAdding={processingAdding}
|
||||||
processingUpdating={processingUpdating}
|
processingUpdating={processingUpdating}
|
||||||
tagsOptions={tagsOptions}
|
tagsOptions={tagsOptions}
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { UINT32_RANGE } from '../../../helpers/constants';
|
import { renderInputField, toNumber } from '../../../helpers/form';
|
||||||
|
import { FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
|
||||||
import {
|
import {
|
||||||
|
validateIpv4,
|
||||||
|
validateRequiredValue,
|
||||||
|
validateIpv4RangeEnd,
|
||||||
validateGatewaySubnetMask,
|
validateGatewaySubnetMask,
|
||||||
validateIpForGatewaySubnetMask,
|
validateIpForGatewaySubnetMask,
|
||||||
validateIpv4,
|
|
||||||
validateIpv4RangeEnd,
|
|
||||||
validateNotInRange,
|
validateNotInRange,
|
||||||
validateRequiredValue,
|
|
||||||
} from '../../../helpers/validators';
|
} from '../../../helpers/validators';
|
||||||
import { DhcpFormValues } from '.';
|
import { RootState } from '../../../initialState';
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
|
|
||||||
type FormDHCPv4Props = {
|
interface FormDHCPv4Props {
|
||||||
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
submitting: boolean;
|
||||||
|
initialValues: { v4?: any };
|
||||||
processingConfig?: boolean;
|
processingConfig?: boolean;
|
||||||
|
change: (field: string, value: any) => void;
|
||||||
|
reset: () => void;
|
||||||
ipv4placeholders?: {
|
ipv4placeholders?: {
|
||||||
gateway_ip: string;
|
gateway_ip: string;
|
||||||
subnet_mask: string;
|
subnet_mask: string;
|
||||||
@@ -24,179 +30,127 @@ type FormDHCPv4Props = {
|
|||||||
range_end: string;
|
range_end: string;
|
||||||
lease_duration: string;
|
lease_duration: string;
|
||||||
};
|
};
|
||||||
interfaces: any;
|
}
|
||||||
onSubmit?: (data: DhcpFormValues) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }: FormDHCPv4Props) => {
|
const FormDHCPv4 = ({ handleSubmit, submitting, processingConfig, ipv4placeholders }: FormDHCPv4Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const {
|
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv4], shallowEqual);
|
||||||
handleSubmit,
|
|
||||||
formState: { errors, isSubmitting },
|
|
||||||
control,
|
|
||||||
watch,
|
|
||||||
} = useFormContext<DhcpFormValues>();
|
|
||||||
|
|
||||||
const interfaceName = watch('interface_name');
|
const interfaces = useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES], shallowEqual);
|
||||||
const isInterfaceIncludesIpv4 = interfaces?.[interfaceName]?.ipv4_addresses;
|
const interface_name = interfaces?.values?.interface_name;
|
||||||
|
|
||||||
const formValues = watch('v4');
|
const isInterfaceIncludesIpv4 = useSelector(
|
||||||
const isEmptyConfig = !Object.values(formValues || {}).some(Boolean);
|
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses,
|
||||||
const hasV4Errors = errors.v4 && Object.keys(errors.v4).length > 0;
|
);
|
||||||
|
|
||||||
const isDisabled = useMemo(() => {
|
const isEmptyConfig = !Object.values(dhcp?.values?.v4 ?? {}).some(Boolean);
|
||||||
return isSubmitting || hasV4Errors || processingConfig || !isInterfaceIncludesIpv4 || isEmptyConfig;
|
|
||||||
}, [isSubmitting, hasV4Errors, processingConfig, isInterfaceIncludesIpv4, isEmptyConfig]);
|
const invalid =
|
||||||
|
dhcp?.syncErrors ||
|
||||||
|
interfaces?.syncErrors ||
|
||||||
|
!isInterfaceIncludesIpv4 ||
|
||||||
|
isEmptyConfig ||
|
||||||
|
submitting ||
|
||||||
|
processingConfig;
|
||||||
|
|
||||||
|
const validateRequired = useCallback(
|
||||||
|
(value) => {
|
||||||
|
if (isEmptyConfig) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return validateRequiredValue(value);
|
||||||
|
},
|
||||||
|
[isEmptyConfig],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label>{t('dhcp_form_gateway_input')}</label>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="v4.gateway_ip"
|
name="v4.gateway_ip"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
ipv4: validateIpv4,
|
placeholder={t(ipv4placeholders.gateway_ip)}
|
||||||
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
|
validate={[validateIpv4, validateRequired, validateNotInRange]}
|
||||||
notInRange: validateNotInRange,
|
disabled={!isInterfaceIncludesIpv4}
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="v4_gateway_ip"
|
|
||||||
label={t('dhcp_form_gateway_input')}
|
|
||||||
placeholder={t(ipv4placeholders.gateway_ip)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv4}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label>{t('dhcp_form_subnet_input')}</label>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="v4.subnet_mask"
|
name="v4.subnet_mask"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
|
placeholder={t(ipv4placeholders.subnet_mask)}
|
||||||
subnet: validateGatewaySubnetMask,
|
validate={[validateRequired, validateGatewaySubnetMask]}
|
||||||
},
|
disabled={!isInterfaceIncludesIpv4}
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="v4_subnet_mask"
|
|
||||||
label={t('dhcp_form_subnet_input')}
|
|
||||||
placeholder={t(ipv4placeholders.subnet_mask)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv4}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="form__group mb-0">
|
<div className="form__group form__group--settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label>{t('dhcp_form_range_title')}</label>
|
<label>{t('dhcp_form_range_title')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col">
|
<div className="col">
|
||||||
<Controller
|
<Field
|
||||||
name="v4.range_start"
|
name="v4.range_start"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
ipv4: validateIpv4,
|
placeholder={t(ipv4placeholders.range_start)}
|
||||||
gateway: validateIpForGatewaySubnetMask,
|
validate={[validateIpv4, validateIpForGatewaySubnetMask]}
|
||||||
},
|
disabled={!isInterfaceIncludesIpv4}
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="v4_range_start"
|
|
||||||
placeholder={t(ipv4placeholders.range_start)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv4}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col">
|
<div className="col">
|
||||||
<Controller
|
<Field
|
||||||
name="v4.range_end"
|
name="v4.range_end"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
ipv4: validateIpv4,
|
placeholder={t(ipv4placeholders.range_end)}
|
||||||
rangeEnd: validateIpv4RangeEnd,
|
validate={[validateIpv4, validateIpv4RangeEnd, validateIpForGatewaySubnetMask]}
|
||||||
gateway: validateIpForGatewaySubnetMask,
|
disabled={!isInterfaceIncludesIpv4}
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="v4_range_end"
|
|
||||||
placeholder={t(ipv4placeholders.range_end)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv4}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label>{t('dhcp_form_lease_title')}</label>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="v4.lease_duration"
|
name="v4.lease_duration"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="number"
|
||||||
validate: {
|
className="form-control"
|
||||||
required: (value) => (isEmptyConfig ? undefined : validateRequiredValue(value)),
|
placeholder={t(ipv4placeholders.lease_duration)}
|
||||||
},
|
validate={validateRequired}
|
||||||
}}
|
normalize={toNumber}
|
||||||
render={({ field, fieldState }) => (
|
min={1}
|
||||||
<Input
|
max={UINT32_RANGE.MAX}
|
||||||
{...field}
|
disabled={!isInterfaceIncludesIpv4}
|
||||||
type="number"
|
|
||||||
data-testid="v4_lease_duration"
|
|
||||||
label={t('dhcp_form_lease_title')}
|
|
||||||
placeholder={t(ipv4placeholders.lease_duration)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv4}
|
|
||||||
min={1}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
value={field.value ?? ''}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button type="submit" className="btn btn-success btn-standard" disabled={invalid}>
|
||||||
data-testid="v4_save"
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-success btn-standard"
|
|
||||||
disabled={isDisabled}>
|
|
||||||
{t('save_config')}
|
{t('save_config')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -204,4 +158,9 @@ const FormDHCPv4 = ({ processingConfig, ipv4placeholders, interfaces, onSubmit }
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FormDHCPv4;
|
export default reduxForm<
|
||||||
|
Record<string, any>,
|
||||||
|
Omit<FormDHCPv4Props, 'submitting' | 'handleSubmit' | 'reset' | 'change'>
|
||||||
|
>({
|
||||||
|
form: FORM_NAME.DHCPv4,
|
||||||
|
})(FormDHCPv4);
|
||||||
|
|||||||
@@ -1,92 +1,93 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Controller, useFormContext } from 'react-hook-form';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { UINT32_RANGE } from '../../../helpers/constants';
|
import { renderInputField, toNumber } from '../../../helpers/form';
|
||||||
|
import { FORM_NAME, UINT32_RANGE } from '../../../helpers/constants';
|
||||||
import { validateIpv6, validateRequiredValue } from '../../../helpers/validators';
|
import { validateIpv6, validateRequiredValue } from '../../../helpers/validators';
|
||||||
import { DhcpFormValues } from '.';
|
import { RootState } from '../../../initialState';
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
|
|
||||||
type FormDHCPv6Props = {
|
interface FormDHCPv6Props {
|
||||||
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
submitting: boolean;
|
||||||
|
initialValues: {
|
||||||
|
v6?: any;
|
||||||
|
};
|
||||||
|
change: (field: string, value: any) => void;
|
||||||
|
reset: () => void;
|
||||||
processingConfig?: boolean;
|
processingConfig?: boolean;
|
||||||
ipv6placeholders?: {
|
ipv6placeholders?: {
|
||||||
range_start: string;
|
range_start: string;
|
||||||
range_end: string;
|
range_end: string;
|
||||||
lease_duration: string;
|
lease_duration: string;
|
||||||
};
|
};
|
||||||
interfaces: any;
|
}
|
||||||
onSubmit?: (data: DhcpFormValues) => Promise<void> | void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }: FormDHCPv6Props) => {
|
const FormDHCPv6 = ({ handleSubmit, submitting, processingConfig, ipv6placeholders }: FormDHCPv6Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
formState: { isSubmitting, isValid },
|
|
||||||
control,
|
|
||||||
watch,
|
|
||||||
} = useFormContext<DhcpFormValues>();
|
|
||||||
|
|
||||||
const interfaceName = watch('interface_name');
|
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv6], shallowEqual);
|
||||||
const isInterfaceIncludesIpv6 = interfaces?.[interfaceName]?.ipv6_addresses;
|
|
||||||
|
|
||||||
const formValues = watch('v6');
|
const interfaces = useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES], shallowEqual);
|
||||||
const isEmptyConfig = !Object.values(formValues || {}).some(Boolean);
|
const interface_name = interfaces?.values?.interface_name;
|
||||||
|
|
||||||
const isDisabled = useMemo(() => {
|
const isInterfaceIncludesIpv6 = useSelector(
|
||||||
return isSubmitting || !isValid || processingConfig || !isInterfaceIncludesIpv6 || isEmptyConfig;
|
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv6_addresses,
|
||||||
}, [isSubmitting, isValid, processingConfig, isInterfaceIncludesIpv6, isEmptyConfig]);
|
);
|
||||||
|
|
||||||
|
const isEmptyConfig = !Object.values(dhcp?.values?.v6 ?? {}).some(Boolean);
|
||||||
|
|
||||||
|
const invalid =
|
||||||
|
dhcp?.syncErrors ||
|
||||||
|
interfaces?.syncErrors ||
|
||||||
|
!isInterfaceIncludesIpv6 ||
|
||||||
|
isEmptyConfig ||
|
||||||
|
submitting ||
|
||||||
|
processingConfig;
|
||||||
|
|
||||||
|
const validateRequired = useCallback(
|
||||||
|
(value) => {
|
||||||
|
if (isEmptyConfig) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return validateRequiredValue(value);
|
||||||
|
},
|
||||||
|
[isEmptyConfig],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="form__group mb-0">
|
<div className="form__group form__group--settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label>{t('dhcp_form_range_title')}</label>
|
<label>{t('dhcp_form_range_title')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col">
|
<div className="col">
|
||||||
<Controller
|
<Field
|
||||||
name="v6.range_start"
|
name="v6.range_start"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: isInterfaceIncludesIpv6
|
className="form-control"
|
||||||
? {
|
placeholder={t(ipv6placeholders.range_start)}
|
||||||
ipv6: validateIpv6,
|
validate={[validateIpv6, validateRequired]}
|
||||||
required: validateRequiredValue,
|
disabled={!isInterfaceIncludesIpv6}
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="v6_range_start"
|
|
||||||
placeholder={t(ipv6placeholders.range_start)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv6}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col">
|
<div className="col">
|
||||||
<Controller
|
<Field
|
||||||
name="v6.range_end"
|
name="v6.range_end"
|
||||||
control={control}
|
component="input"
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Input
|
className="form-control disabled cursor--not-allowed"
|
||||||
{...field}
|
placeholder={t(ipv6placeholders.range_end)}
|
||||||
type="text"
|
value={t(ipv6placeholders.range_end)}
|
||||||
data-testid="v6_range_end"
|
disabled
|
||||||
placeholder={t(ipv6placeholders.range_end)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,43 +97,25 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
|
|||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-lg-6 form__group form__group--settings">
|
<div className="col-lg-6 form__group form__group--settings">
|
||||||
<Controller
|
<label>{t('dhcp_form_lease_title')}</label>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="v6.lease_duration"
|
name="v6.lease_duration"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="number"
|
||||||
validate: isInterfaceIncludesIpv6
|
className="form-control"
|
||||||
? {
|
placeholder={t(ipv6placeholders.lease_duration)}
|
||||||
required: validateRequiredValue,
|
validate={validateRequired}
|
||||||
}
|
normalizeOnBlur={toNumber}
|
||||||
: undefined,
|
min={1}
|
||||||
}}
|
max={UINT32_RANGE.MAX}
|
||||||
render={({ field, fieldState }) => (
|
disabled={!isInterfaceIncludesIpv6}
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="number"
|
|
||||||
data-testid="v6_lease_duration"
|
|
||||||
label={t('dhcp_form_lease_title')}
|
|
||||||
placeholder={t(ipv6placeholders.lease_duration)}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isInterfaceIncludesIpv6}
|
|
||||||
min={1}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button type="submit" className="btn btn-success btn-standard" disabled={invalid}>
|
||||||
data-testid="v6_save"
|
|
||||||
type="submit"
|
|
||||||
className="btn btn-success btn-standard"
|
|
||||||
disabled={isDisabled}>
|
|
||||||
{t('save_config')}
|
{t('save_config')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,4 +123,9 @@ const FormDHCPv6 = ({ processingConfig, ipv6placeholders, interfaces, onSubmit }
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FormDHCPv6;
|
export default reduxForm<
|
||||||
|
Record<string, any>,
|
||||||
|
Omit<FormDHCPv6Props, 'handleSubmit' | 'change' | 'submitting' | 'reset'>
|
||||||
|
>({
|
||||||
|
form: FORM_NAME.DHCPv6,
|
||||||
|
})(FormDHCPv6);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { useFormContext } from 'react-hook-form';
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { renderSelectField } from '../../../helpers/form';
|
||||||
import { validateRequiredValue } from '../../../helpers/validators';
|
import { validateRequiredValue } from '../../../helpers/validators';
|
||||||
|
import { FORM_NAME } from '../../../helpers/constants';
|
||||||
import { RootState } from '../../../initialState';
|
import { RootState } from '../../../initialState';
|
||||||
import { DhcpFormValues } from '.';
|
|
||||||
|
|
||||||
const renderInterfaces = (interfaces: any) =>
|
const renderInterfaces = (interfaces: any) =>
|
||||||
Object.keys(interfaces).map((item) => {
|
Object.keys(interfaces).map((item) => {
|
||||||
@@ -45,13 +47,13 @@ const getInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: any)
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface RenderInterfaceValuesProps {
|
interface renderInterfaceValuesProps {
|
||||||
gateway_ip: string;
|
gateway_ip: string;
|
||||||
hardware_address: string;
|
hardware_address: string;
|
||||||
ip_addresses: string[];
|
ip_addresses: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: RenderInterfaceValuesProps) => (
|
const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: renderInterfaceValuesProps) => (
|
||||||
<div className="d-flex align-items-end dhcp__interfaces-info">
|
<div className="d-flex align-items-end dhcp__interfaces-info">
|
||||||
<ul className="list-unstyled m-0">
|
<ul className="list-unstyled m-0">
|
||||||
{getInterfaceValues({
|
{getInterfaceValues({
|
||||||
@@ -75,15 +77,11 @@ const renderInterfaceValues = ({ gateway_ip, hardware_address, ip_addresses }: R
|
|||||||
|
|
||||||
const Interfaces = () => {
|
const Interfaces = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
|
||||||
register,
|
|
||||||
watch,
|
|
||||||
formState: { errors },
|
|
||||||
} = useFormContext<DhcpFormValues>();
|
|
||||||
|
|
||||||
const { processingInterfaces, interfaces, enabled } = useSelector((store: RootState) => store.dhcp);
|
const { processingInterfaces, interfaces, enabled } = useSelector((store: RootState) => store.dhcp, shallowEqual);
|
||||||
|
|
||||||
const interface_name = watch('interface_name');
|
const interface_name =
|
||||||
|
useSelector((store: RootState) => store.form[FORM_NAME.DHCP_INTERFACES]?.values?.interface_name);
|
||||||
|
|
||||||
if (processingInterfaces || !interfaces) {
|
if (processingInterfaces || !interfaces) {
|
||||||
return null;
|
return null;
|
||||||
@@ -94,34 +92,27 @@ const Interfaces = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="row dhcp__interfaces">
|
<div className="row dhcp__interfaces">
|
||||||
<div className="col col__dhcp">
|
<div className="col col__dhcp">
|
||||||
<label htmlFor="interface_name" className="form__label">
|
<Field
|
||||||
{t('dhcp_interface_select')}
|
name="interface_name"
|
||||||
</label>
|
component={renderSelectField}
|
||||||
<select
|
|
||||||
id="interface_name"
|
|
||||||
data-testid="interface_name"
|
|
||||||
className="form-control custom-select pl-4 col-md"
|
className="form-control custom-select pl-4 col-md"
|
||||||
disabled={enabled}
|
validate={[validateRequiredValue]}
|
||||||
{...register('interface_name', {
|
label="dhcp_interface_select">
|
||||||
validate: validateRequiredValue,
|
|
||||||
})}>
|
|
||||||
<option value="" disabled={enabled}>
|
<option value="" disabled={enabled}>
|
||||||
{t('dhcp_interface_select')}
|
{t('dhcp_interface_select')}
|
||||||
</option>
|
</option>
|
||||||
{renderInterfaces(interfaces)}
|
{renderInterfaces(interfaces)}
|
||||||
</select>
|
</Field>
|
||||||
{errors.interface_name && (
|
|
||||||
<div className="form__message form__message--error">{t(errors.interface_name.message)}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{interfaceValue &&
|
{interfaceValue && renderInterfaceValues({
|
||||||
renderInterfaceValues({
|
gateway_ip: interfaceValue.gateway_ip,
|
||||||
gateway_ip: interfaceValue.gateway_ip,
|
hardware_address: interfaceValue.hardware_address,
|
||||||
hardware_address: interfaceValue.hardware_address,
|
ip_addresses: interfaceValue.ip_addresses
|
||||||
ip_addresses: interfaceValue.ip_addresses,
|
})}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Interfaces;
|
export default reduxForm({
|
||||||
|
form: FORM_NAME.DHCP_INTERFACES,
|
||||||
|
})(Interfaces);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
||||||
|
|
||||||
import { normalizeMac } from '../../../../helpers/form';
|
import { renderInputField, normalizeMac } from '../../../../helpers/form';
|
||||||
import {
|
import {
|
||||||
validateIpv4,
|
validateIpv4,
|
||||||
validateMac,
|
validateMac,
|
||||||
@@ -11,12 +12,12 @@ import {
|
|||||||
validateIpv4InCidr,
|
validateIpv4InCidr,
|
||||||
validateIpGateway,
|
validateIpGateway,
|
||||||
} from '../../../../helpers/validators';
|
} from '../../../../helpers/validators';
|
||||||
|
import { FORM_NAME } from '../../../../helpers/constants';
|
||||||
|
|
||||||
import { toggleLeaseModal } from '../../../../actions';
|
import { toggleLeaseModal } from '../../../../actions';
|
||||||
import { RootState } from '../../../../initialState';
|
import { RootState } from '../../../../initialState';
|
||||||
import { Input } from '../../../ui/Controls/Input';
|
|
||||||
|
|
||||||
type Props = {
|
interface FormStaticLeaseProps {
|
||||||
initialValues?: {
|
initialValues?: {
|
||||||
mac?: string;
|
mac?: string;
|
||||||
ip?: string;
|
ip?: string;
|
||||||
@@ -24,26 +25,20 @@ type Props = {
|
|||||||
cidr?: string;
|
cidr?: string;
|
||||||
gatewayIp?: string;
|
gatewayIp?: string;
|
||||||
};
|
};
|
||||||
|
pristine: boolean;
|
||||||
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
reset: () => void;
|
||||||
|
submitting: boolean;
|
||||||
processingAdding?: boolean;
|
processingAdding?: boolean;
|
||||||
cidr?: string;
|
cidr?: string;
|
||||||
isEdit?: boolean;
|
isEdit?: boolean;
|
||||||
onSubmit: (data: any) => void;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export const Form = ({ initialValues, processingAdding, cidr, isEdit, onSubmit }: Props) => {
|
const Form = ({ handleSubmit, reset, pristine, submitting, processingAdding, cidr, isEdit }: FormStaticLeaseProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const dynamicLease = useSelector((store: RootState) => store.dhcp.leaseModalConfig, shallowEqual);
|
|
||||||
|
|
||||||
const {
|
const dynamicLease = useSelector((store: RootState) => store.dhcp.leaseModalConfig, shallowEqual);
|
||||||
handleSubmit,
|
|
||||||
control,
|
|
||||||
reset,
|
|
||||||
formState: { isSubmitting, isDirty },
|
|
||||||
} = useForm({
|
|
||||||
defaultValues: initialValues,
|
|
||||||
mode: 'onBlur',
|
|
||||||
});
|
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
reset();
|
reset();
|
||||||
@@ -51,64 +46,42 @@ export const Form = ({ initialValues, processingAdding, cidr, isEdit, onSubmit }
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Controller
|
<Field
|
||||||
|
id="mac"
|
||||||
name="mac"
|
name="mac"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{ validate: { required: validateRequiredValue, mac: validateMac } }}
|
type="text"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('form_enter_mac')}
|
||||||
{...field}
|
normalize={normalizeMac}
|
||||||
type="text"
|
validate={[validateRequiredValue, validateMac]}
|
||||||
data-testid="static_lease_mac"
|
disabled={isEdit}
|
||||||
placeholder={t('form_enter_mac')}
|
|
||||||
disabled={isEdit}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
onChange={(e) => field.onChange(normalizeMac(e.target.value))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Controller
|
<Field
|
||||||
|
id="ip"
|
||||||
name="ip"
|
name="ip"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
type="text"
|
||||||
validate: {
|
className="form-control"
|
||||||
required: validateRequiredValue,
|
placeholder={t('form_enter_subnet_ip', { cidr })}
|
||||||
ipv4: validateIpv4,
|
validate={[validateRequiredValue, validateIpv4, validateIpv4InCidr, validateIpGateway]}
|
||||||
inCidr: validateIpv4InCidr,
|
|
||||||
gateway: validateIpGateway,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
data-testid="static_lease_ip"
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
placeholder={t('form_enter_subnet_ip', { cidr })}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Controller
|
<Field
|
||||||
|
id="hostname"
|
||||||
name="hostname"
|
name="hostname"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Input
|
className="form-control"
|
||||||
{...field}
|
placeholder={t('form_enter_hostname')}
|
||||||
type="text"
|
|
||||||
data-testid="static_lease_hostname"
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
placeholder={t('form_enter_hostname')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,18 +90,16 @@ export const Form = ({ initialValues, processingAdding, cidr, isEdit, onSubmit }
|
|||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="static_lease_cancel"
|
|
||||||
className="btn btn-secondary btn-standard"
|
className="btn btn-secondary btn-standard"
|
||||||
disabled={isSubmitting}
|
disabled={submitting}
|
||||||
onClick={onClick}>
|
onClick={onClick}>
|
||||||
<Trans>cancel_btn</Trans>
|
<Trans>cancel_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="static_lease_save"
|
|
||||||
className="btn btn-success btn-standard"
|
className="btn btn-success btn-standard"
|
||||||
disabled={isSubmitting || processingAdding || (!isDirty && !dynamicLease)}>
|
disabled={submitting || processingAdding || (pristine && !dynamicLease)}>
|
||||||
<Trans>save_btn</Trans>
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,3 +107,8 @@ export const Form = ({ initialValues, processingAdding, cidr, isEdit, onSubmit }
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default reduxForm<
|
||||||
|
Record<string, any>,
|
||||||
|
Omit<FormStaticLeaseProps, 'submitting' | 'handleSubmit' | 'reset' | 'pristine'>
|
||||||
|
>({ form: FORM_NAME.LEASE })(Form);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Trans, withTranslation } from 'react-i18next';
|
|||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { Form } from './Form';
|
import Form from './Form';
|
||||||
|
|
||||||
import { toggleLeaseModal } from '../../../../actions';
|
import { toggleLeaseModal } from '../../../../actions';
|
||||||
import { MODAL_TYPE } from '../../../../helpers/constants';
|
import { MODAL_TYPE } from '../../../../helpers/constants';
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { destroy } from 'redux-form';
|
||||||
import { DHCP_DESCRIPTION_PLACEHOLDERS, STATUS_RESPONSE } from '../../../helpers/constants';
|
import { DHCP_DESCRIPTION_PLACEHOLDERS, DHCP_FORM_NAMES, STATUS_RESPONSE, FORM_NAME } from '../../../helpers/constants';
|
||||||
|
|
||||||
import Leases from './Leases';
|
import Leases from './Leases';
|
||||||
|
|
||||||
@@ -40,55 +40,6 @@ import {
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import { RootState } from '../../../initialState';
|
import { RootState } from '../../../initialState';
|
||||||
|
|
||||||
type IPv4FormValues = {
|
|
||||||
gateway_ip?: string;
|
|
||||||
subnet_mask?: string;
|
|
||||||
range_start?: string;
|
|
||||||
range_end?: string;
|
|
||||||
lease_duration?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type IPv6FormValues = {
|
|
||||||
range_start?: string;
|
|
||||||
range_end?: string;
|
|
||||||
lease_duration?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDefaultV4Values = (v4: IPv4FormValues) => {
|
|
||||||
const emptyForm = Object.entries(v4).every(
|
|
||||||
([key, value]) => key === 'lease_duration' || value === ''
|
|
||||||
);
|
|
||||||
|
|
||||||
if (emptyForm) {
|
|
||||||
return {
|
|
||||||
...v4,
|
|
||||||
lease_duration: undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DhcpFormValues = {
|
|
||||||
v4?: IPv4FormValues;
|
|
||||||
v6?: IPv6FormValues;
|
|
||||||
interface_name?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_V4_VALUES = {
|
|
||||||
gateway_ip: '',
|
|
||||||
subnet_mask: '',
|
|
||||||
range_start: '',
|
|
||||||
range_end: '',
|
|
||||||
lease_duration: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_V6_VALUES = {
|
|
||||||
range_start: '',
|
|
||||||
range_end: '',
|
|
||||||
lease_duration: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Dhcp = () => {
|
const Dhcp = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -114,21 +65,12 @@ const Dhcp = () => {
|
|||||||
modalType,
|
modalType,
|
||||||
} = useSelector((state: RootState) => state.dhcp, shallowEqual);
|
} = useSelector((state: RootState) => state.dhcp, shallowEqual);
|
||||||
|
|
||||||
const methods = useForm<DhcpFormValues>({
|
const interface_name =
|
||||||
mode: 'onBlur',
|
useSelector((state: RootState) => state.form[FORM_NAME.DHCP_INTERFACES]?.values?.interface_name);
|
||||||
defaultValues: {
|
const isInterfaceIncludesIpv4 =
|
||||||
v4: getDefaultV4Values(v4),
|
useSelector((state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses);
|
||||||
v6,
|
|
||||||
interface_name: interfaceName || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { watch, reset } = methods;
|
|
||||||
|
|
||||||
const interface_name = watch('interface_name');
|
const dhcp = useSelector((state: RootState) => state.form[FORM_NAME.DHCPv4], shallowEqual);
|
||||||
const isInterfaceIncludesIpv4 = useSelector(
|
|
||||||
(state: RootState) => !!state.dhcp?.interfaces?.[interface_name]?.ipv4_addresses,
|
|
||||||
);
|
|
||||||
const ipv4Config = watch('v4');
|
|
||||||
|
|
||||||
const [ipv4placeholders, setIpv4Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv4);
|
const [ipv4placeholders, setIpv4Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv4);
|
||||||
const [ipv6placeholders, setIpv6Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv6);
|
const [ipv6placeholders, setIpv6Placeholders] = useState(DHCP_DESCRIPTION_PLACEHOLDERS.ipv6);
|
||||||
@@ -143,22 +85,6 @@ const Dhcp = () => {
|
|||||||
}
|
}
|
||||||
}, [dhcp_available]);
|
}, [dhcp_available]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (v4 || v6 || interfaceName) {
|
|
||||||
reset({
|
|
||||||
v4: {
|
|
||||||
...DEFAULT_V4_VALUES,
|
|
||||||
...getDefaultV4Values(v4),
|
|
||||||
},
|
|
||||||
v6: {
|
|
||||||
...DEFAULT_V6_VALUES,
|
|
||||||
...v6,
|
|
||||||
},
|
|
||||||
interface_name: interfaceName || '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [v4, v6, interfaceName, reset]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const [ipv4] = interfaces?.[interface_name]?.ipv4_addresses ?? [];
|
const [ipv4] = interfaces?.[interface_name]?.ipv4_addresses ?? [];
|
||||||
const [ipv6] = interfaces?.[interface_name]?.ipv6_addresses ?? [];
|
const [ipv6] = interfaces?.[interface_name]?.ipv6_addresses ?? [];
|
||||||
@@ -177,17 +103,13 @@ const Dhcp = () => {
|
|||||||
const clear = () => {
|
const clear = () => {
|
||||||
// eslint-disable-next-line no-alert
|
// eslint-disable-next-line no-alert
|
||||||
if (window.confirm(t('dhcp_reset'))) {
|
if (window.confirm(t('dhcp_reset'))) {
|
||||||
reset({
|
Object.values(DHCP_FORM_NAMES).forEach((formName: any) => dispatch(destroy(formName)));
|
||||||
v4: DEFAULT_V4_VALUES,
|
|
||||||
v6: DEFAULT_V6_VALUES,
|
|
||||||
interface_name: '',
|
|
||||||
});
|
|
||||||
dispatch(resetDhcp());
|
dispatch(resetDhcp());
|
||||||
dispatch(getDhcpStatus());
|
dispatch(getDhcpStatus());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (values: DhcpFormValues) => {
|
const handleSubmit = (values: any) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
setDhcpConfig({
|
setDhcpConfig({
|
||||||
interface_name,
|
interface_name,
|
||||||
@@ -208,7 +130,12 @@ const Dhcp = () => {
|
|||||||
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
const enteredSomeValue = enteredSomeV4Value || enteredSomeV6Value || interfaceName;
|
||||||
|
|
||||||
const getToggleDhcpButton = () => {
|
const getToggleDhcpButton = () => {
|
||||||
const filledConfig = interface_name && (Object.values(v4).every(Boolean) || Object.values(v6).every(Boolean));
|
const filledConfig =
|
||||||
|
interface_name &&
|
||||||
|
(Object.values(v4)
|
||||||
|
|
||||||
|
.every(Boolean) ||
|
||||||
|
Object.values(v6).every(Boolean));
|
||||||
|
|
||||||
const className = classNames('btn btn-sm', {
|
const className = classNames('btn btn-sm', {
|
||||||
'btn-gray': enabled,
|
'btn-gray': enabled,
|
||||||
@@ -246,6 +173,9 @@ const Dhcp = () => {
|
|||||||
|
|
||||||
const toggleModal = () => dispatch(toggleLeaseModal());
|
const toggleModal = () => dispatch(toggleLeaseModal());
|
||||||
|
|
||||||
|
const initialV4 = enteredSomeV4Value ? v4 : {};
|
||||||
|
const initialV6 = enteredSomeV6Value ? v6 : {};
|
||||||
|
|
||||||
if (processing || processingInterfaces) {
|
if (processing || processingInterfaces) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
@@ -266,13 +196,19 @@ const Dhcp = () => {
|
|||||||
|
|
||||||
const toggleDhcpButton = getToggleDhcpButton();
|
const toggleDhcpButton = getToggleDhcpButton();
|
||||||
|
|
||||||
const inputtedIPv4values = ipv4Config.gateway_ip && ipv4Config.subnet_mask;
|
const inputtedIPv4values = dhcp?.values?.v4?.gateway_ip && dhcp?.values?.v4?.subnet_mask;
|
||||||
|
|
||||||
const isEmptyConfig = !Object.values(ipv4Config).some(Boolean);
|
const isEmptyConfig = !Object.values(dhcp?.values?.v4 ?? {}).some(Boolean);
|
||||||
const disabledLeasesButton = Boolean(
|
const disabledLeasesButton = Boolean(
|
||||||
!isInterfaceIncludesIpv4 || isEmptyConfig || processingConfig || !inputtedIPv4values,
|
dhcp?.syncErrors ||
|
||||||
|
!isInterfaceIncludesIpv4 ||
|
||||||
|
isEmptyConfig ||
|
||||||
|
processingConfig ||
|
||||||
|
!inputtedIPv4values,
|
||||||
);
|
);
|
||||||
const cidr = inputtedIPv4values ? `${ipv4Config.gateway_ip}/${subnetMaskToBitMask(ipv4Config.subnet_mask)}` : '';
|
const cidr = inputtedIPv4values
|
||||||
|
? `${dhcp?.values?.v4?.gateway_ip}/${subnetMaskToBitMask(dhcp?.values?.v4?.subnet_mask)}`
|
||||||
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -310,30 +246,29 @@ const Dhcp = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FormProvider {...methods}>
|
<Interfaces initialValues={{ interface_name: interfaceName }} />
|
||||||
<Interfaces />
|
|
||||||
<Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings">
|
|
||||||
<div>
|
|
||||||
<FormDHCPv4
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
processingConfig={processingConfig}
|
|
||||||
ipv4placeholders={ipv4placeholders}
|
|
||||||
interfaces={interfaces}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
<Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings">
|
|
||||||
<div>
|
|
||||||
<FormDHCPv6
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
processingConfig={processingConfig}
|
|
||||||
ipv6placeholders={ipv6placeholders}
|
|
||||||
interfaces={interfaces}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</FormProvider>
|
|
||||||
|
|
||||||
|
<Card title={t('dhcp_ipv4_settings')} bodyType="card-body box-body--settings">
|
||||||
|
<div>
|
||||||
|
<FormDHCPv4
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
initialValues={{ v4: initialV4 }}
|
||||||
|
processingConfig={processingConfig}
|
||||||
|
ipv4placeholders={ipv4placeholders}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title={t('dhcp_ipv6_settings')} bodyType="card-body box-body--settings">
|
||||||
|
<div>
|
||||||
|
<FormDHCPv6
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
initialValues={{ v6: initialV6 }}
|
||||||
|
processingConfig={processingConfig}
|
||||||
|
ipv6placeholders={ipv6placeholders}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
{enabled && (
|
{enabled && (
|
||||||
<Card title={t('dhcp_leases')} bodyType="card-body box-body--settings">
|
<Card title={t('dhcp_leases')} bodyType="card-body box-body--settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@@ -355,7 +290,7 @@ const Dhcp = () => {
|
|||||||
processingDeleting={processingDeleting}
|
processingDeleting={processingDeleting}
|
||||||
processingUpdating={processingUpdating}
|
processingUpdating={processingUpdating}
|
||||||
cidr={cidr}
|
cidr={cidr}
|
||||||
gatewayIp={ipv4Config.gateway_ip}
|
gatewayIp={dhcp?.values?.v4?.gateway_ip}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="btn-list mt-2">
|
<div className="btn-list mt-2">
|
||||||
|
|||||||
@@ -1,140 +1,118 @@
|
|||||||
import React, { ReactNode } from 'react';
|
import React from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { connect } from 'react-redux';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||||
import { CLIENT_ID_LINK } from '../../../../helpers/constants';
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
import { removeEmptyLines, trimMultilineString } from '../../../../helpers/helpers';
|
import flow from 'lodash/flow';
|
||||||
import { Textarea } from '../../../ui/Controls/Textarea';
|
|
||||||
|
|
||||||
type FormData = {
|
import { renderTextareaField } from '../../../../helpers/form';
|
||||||
allowed_clients: string;
|
import { trimMultilineString, removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
disallowed_clients: string;
|
import { CLIENT_ID_LINK, FORM_NAME } from '../../../../helpers/constants';
|
||||||
blocked_hosts: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fields: {
|
const fields = [
|
||||||
id: keyof FormData;
|
|
||||||
title: string;
|
|
||||||
subtitle: ReactNode;
|
|
||||||
normalizeOnBlur: (value: string) => string;
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
id: 'allowed_clients',
|
id: 'allowed_clients',
|
||||||
title: i18next.t('access_allowed_title'),
|
title: 'access_allowed_title',
|
||||||
subtitle: (
|
subtitle: 'access_allowed_desc',
|
||||||
<Trans
|
|
||||||
components={{
|
|
||||||
a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" />,
|
|
||||||
}}>
|
|
||||||
access_allowed_desc
|
|
||||||
</Trans>
|
|
||||||
),
|
|
||||||
normalizeOnBlur: removeEmptyLines,
|
normalizeOnBlur: removeEmptyLines,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'disallowed_clients',
|
id: 'disallowed_clients',
|
||||||
title: i18next.t('access_disallowed_title'),
|
title: 'access_disallowed_title',
|
||||||
subtitle: (
|
subtitle: 'access_disallowed_desc',
|
||||||
<Trans
|
|
||||||
components={{
|
|
||||||
a: <a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer" />,
|
|
||||||
}}>
|
|
||||||
access_disallowed_desc
|
|
||||||
</Trans>
|
|
||||||
),
|
|
||||||
normalizeOnBlur: trimMultilineString,
|
normalizeOnBlur: trimMultilineString,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'blocked_hosts',
|
id: 'blocked_hosts',
|
||||||
title: i18next.t('access_blocked_title'),
|
title: 'access_blocked_title',
|
||||||
subtitle: i18next.t('access_blocked_desc'),
|
subtitle: 'access_blocked_desc',
|
||||||
normalizeOnBlur: removeEmptyLines,
|
normalizeOnBlur: removeEmptyLines,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
type FormProps = {
|
interface FormProps {
|
||||||
initialValues?: {
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
allowed_clients?: string;
|
submitting: boolean;
|
||||||
disallowed_clients?: string;
|
invalid: boolean;
|
||||||
blocked_hosts?: string;
|
initialValues: object;
|
||||||
};
|
|
||||||
onSubmit: (data: FormData) => void;
|
|
||||||
processingSet: boolean;
|
processingSet: boolean;
|
||||||
};
|
t: (...args: unknown[]) => string;
|
||||||
|
textarea?: boolean;
|
||||||
|
allowedClients?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
|
interface renderFieldProps {
|
||||||
const { t } = useTranslation();
|
id?: string;
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
processingSet?: boolean;
|
||||||
|
normalizeOnBlur?: (...args: unknown[]) => unknown;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
let Form = (props: FormProps) => {
|
||||||
control,
|
const { allowedClients, handleSubmit, submitting, invalid, processingSet } = props;
|
||||||
handleSubmit,
|
|
||||||
watch,
|
|
||||||
formState: { isSubmitting, isDirty },
|
|
||||||
} = useForm<FormData>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
allowed_clients: initialValues?.allowed_clients || '',
|
|
||||||
disallowed_clients: initialValues?.disallowed_clients || '',
|
|
||||||
blocked_hosts: initialValues?.blocked_hosts || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const allowedClients = watch('allowed_clients');
|
|
||||||
|
|
||||||
const renderField = ({
|
const renderField = ({
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
|
disabled = false,
|
||||||
|
processingSet,
|
||||||
normalizeOnBlur,
|
normalizeOnBlur,
|
||||||
}: {
|
}: renderFieldProps) => (
|
||||||
id: keyof FormData;
|
<div key={id} className="form__group mb-5">
|
||||||
title: string;
|
<label className="form__label form__label--with-desc" htmlFor={id}>
|
||||||
subtitle: ReactNode;
|
<Trans>{title}</Trans>
|
||||||
normalizeOnBlur: (value: string) => string;
|
|
||||||
}) => {
|
|
||||||
const disabled = allowedClients && id === 'disallowed_clients';
|
|
||||||
|
|
||||||
return (
|
{disabled && (
|
||||||
<div key={id} className="form__group mb-5">
|
<>
|
||||||
<label className="form__label form__label--with-desc" htmlFor={id}>
|
<span> </span>(<Trans>disabled</Trans>)
|
||||||
{title}
|
</>
|
||||||
{disabled && <> ({t('disabled')})</>}
|
)}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">{subtitle}</div>
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans
|
||||||
<Controller
|
components={{
|
||||||
name={id}
|
a: (
|
||||||
control={control}
|
<a href={CLIENT_ID_LINK} target="_blank" rel="noopener noreferrer">
|
||||||
render={({ field }) => (
|
text
|
||||||
<Textarea
|
</a>
|
||||||
{...field}
|
),
|
||||||
id={id}
|
}}>
|
||||||
data-testid={id}
|
{subtitle}
|
||||||
disabled={disabled || processingSet}
|
</Trans>
|
||||||
onBlur={(e) => {
|
|
||||||
field.onChange(normalizeOnBlur(e.target.value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
};
|
<Field
|
||||||
|
id={id}
|
||||||
|
name={id}
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea font-monospace"
|
||||||
|
disabled={disabled || processingSet}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
{fields.map((f) => renderField(f))}
|
{fields.map((f) => {
|
||||||
|
return renderField({
|
||||||
|
...f,
|
||||||
|
disabled: allowedClients && f.id === 'disallowed_clients' || false
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
|
||||||
<div className="card-actions">
|
<div className="card-actions">
|
||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="access_save"
|
|
||||||
className="btn btn-success btn-standard"
|
className="btn btn-success btn-standard"
|
||||||
disabled={isSubmitting || !isDirty || processingSet}>
|
disabled={submitting || invalid || processingSet}>
|
||||||
{t('save_config')}
|
<Trans>save_config</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,4 +120,18 @@ const Form = ({ initialValues, onSubmit, processingSet }: FormProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
const selector = formValueSelector(FORM_NAME.ACCESS);
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const allowedClients = selector(state, 'allowed_clients');
|
||||||
|
return {
|
||||||
|
allowedClients,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withTranslation(),
|
||||||
|
reduxForm({
|
||||||
|
form: FORM_NAME.ACCESS,
|
||||||
|
}),
|
||||||
|
])(Form);
|
||||||
|
|||||||
@@ -1,72 +1,52 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { clearDnsCache } from '../../../../actions/dnsConfig';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { CACHE_CONFIG_FIELDS, UINT32_RANGE } from '../../../../helpers/constants';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { renderInputField, toNumber, CheckboxField } from '../../../../helpers/form';
|
||||||
|
import { CACHE_CONFIG_FIELDS, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
|
||||||
|
|
||||||
import { replaceZeroWithEmptyString } from '../../../../helpers/helpers';
|
import { replaceZeroWithEmptyString } from '../../../../helpers/helpers';
|
||||||
|
import { clearDnsCache } from '../../../../actions/dnsConfig';
|
||||||
import { RootState } from '../../../../initialState';
|
import { RootState } from '../../../../initialState';
|
||||||
import { Checkbox } from '../../../ui/Controls/Checkbox';
|
|
||||||
|
|
||||||
const INPUTS_FIELDS = [
|
const INPUTS_FIELDS = [
|
||||||
{
|
{
|
||||||
name: CACHE_CONFIG_FIELDS.cache_size,
|
name: CACHE_CONFIG_FIELDS.cache_size,
|
||||||
title: i18next.t('cache_size'),
|
title: 'cache_size',
|
||||||
description: i18next.t('cache_size_desc'),
|
description: 'cache_size_desc',
|
||||||
placeholder: i18next.t('enter_cache_size'),
|
placeholder: 'enter_cache_size',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: CACHE_CONFIG_FIELDS.cache_ttl_min,
|
name: CACHE_CONFIG_FIELDS.cache_ttl_min,
|
||||||
title: i18next.t('cache_ttl_min_override'),
|
title: 'cache_ttl_min_override',
|
||||||
description: i18next.t('cache_ttl_min_override_desc'),
|
description: 'cache_ttl_min_override_desc',
|
||||||
placeholder: i18next.t('enter_cache_ttl_min_override'),
|
placeholder: 'enter_cache_ttl_min_override',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: CACHE_CONFIG_FIELDS.cache_ttl_max,
|
name: CACHE_CONFIG_FIELDS.cache_ttl_max,
|
||||||
title: i18next.t('cache_ttl_max_override'),
|
title: 'cache_ttl_max_override',
|
||||||
description: i18next.t('cache_ttl_max_override_desc'),
|
description: 'cache_ttl_max_override_desc',
|
||||||
placeholder: i18next.t('enter_cache_ttl_max_override'),
|
placeholder: 'enter_cache_ttl_max_override',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
type FormData = {
|
interface CacheFormProps {
|
||||||
cache_size: number;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
cache_ttl_min: number;
|
submitting: boolean;
|
||||||
cache_ttl_max: number;
|
invalid: boolean;
|
||||||
cache_optimistic: boolean;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
type CacheFormProps = {
|
const Form = ({ handleSubmit, submitting, invalid }: CacheFormProps) => {
|
||||||
initialValues?: Partial<FormData>;
|
|
||||||
onSubmit: (data: FormData) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { processingSetConfig } = useSelector((state: RootState) => state.dnsConfig);
|
const { processingSetConfig } = useSelector((state: RootState) => state.dnsConfig, shallowEqual);
|
||||||
|
const { cache_ttl_max, cache_ttl_min } = useSelector(
|
||||||
const {
|
(state: RootState) => state.form[FORM_NAME.CACHE].values,
|
||||||
register,
|
shallowEqual,
|
||||||
handleSubmit,
|
);
|
||||||
watch,
|
|
||||||
control,
|
|
||||||
formState: { isSubmitting, isDirty },
|
|
||||||
} = useForm<FormData>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
cache_size: initialValues?.cache_size || 0,
|
|
||||||
cache_ttl_min: initialValues?.cache_ttl_min || 0,
|
|
||||||
cache_ttl_max: initialValues?.cache_ttl_max || 0,
|
|
||||||
cache_optimistic: initialValues?.cache_optimistic || false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const cache_ttl_min = watch('cache_ttl_min');
|
|
||||||
const cache_ttl_max = watch('cache_ttl_max');
|
|
||||||
|
|
||||||
const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max;
|
const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max;
|
||||||
|
|
||||||
@@ -77,30 +57,29 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
{INPUTS_FIELDS.map(({ name, title, description, placeholder }) => (
|
{INPUTS_FIELDS.map(({ name, title, description, placeholder }) => (
|
||||||
<div className="col-12" key={name}>
|
<div className="col-12" key={name}>
|
||||||
<div className="col-12 col-md-7 p-0">
|
<div className="col-12 col-md-7 p-0">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<label htmlFor={name} className="form__label form__label--with-desc">
|
<label htmlFor={name} className="form__label form__label--with-desc">
|
||||||
{title}
|
{t(title)}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">{description}</div>
|
<div className="form__desc form__desc--top">{t(description)}</div>
|
||||||
|
|
||||||
<input
|
<Field
|
||||||
|
name={name}
|
||||||
type="number"
|
type="number"
|
||||||
data-testid={`dns_${name}`}
|
component={renderInputField}
|
||||||
className="form-control"
|
placeholder={t(placeholder)}
|
||||||
placeholder={placeholder}
|
|
||||||
disabled={processingSetConfig}
|
disabled={processingSetConfig}
|
||||||
|
className="form-control"
|
||||||
|
normalizeOnBlur={replaceZeroWithEmptyString}
|
||||||
|
normalize={toNumber}
|
||||||
min={0}
|
min={0}
|
||||||
max={UINT32_RANGE.MAX}
|
max={UINT32_RANGE.MAX}
|
||||||
{...register(name as keyof FormData, {
|
|
||||||
valueAsNumber: true,
|
|
||||||
setValueAs: (value) => replaceZeroWithEmptyString(value),
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,18 +91,13 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="cache_optimistic"
|
name="cache_optimistic"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('cache_optimistic')}
|
||||||
{...field}
|
disabled={processingSetConfig}
|
||||||
data-testid="dns_cache_optimistic"
|
subtitle={t('cache_optimistic_desc')}
|
||||||
title={t('cache_optimistic')}
|
|
||||||
subtitle={t('cache_optimistic_desc')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,21 +105,19 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="dns_save"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
disabled={isSubmitting || !isDirty || processingSetConfig || minExceedsMax}>
|
disabled={submitting || invalid || processingSetConfig || minExceedsMax}>
|
||||||
{t('save_btn')}
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="dns_clear"
|
|
||||||
className="btn btn-outline-secondary btn-standard form__button"
|
className="btn btn-outline-secondary btn-standard form__button"
|
||||||
onClick={handleClearCache}>
|
onClick={handleClearCache}>
|
||||||
{t('clear_cache')}
|
<Trans>clear_cache</Trans>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
export default reduxForm({ form: FORM_NAME.CACHE })(Form);
|
||||||
|
|||||||
@@ -1,279 +1,211 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import { validateIp, validateIpv4, validateIpv6, validateRequiredValue } from '../../../../helpers/validators';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
renderInputField,
|
||||||
|
renderRadioField,
|
||||||
|
renderTextareaField,
|
||||||
|
CheckboxField,
|
||||||
|
toNumber,
|
||||||
|
} from '../../../../helpers/form';
|
||||||
|
import {
|
||||||
|
validateIpv4,
|
||||||
|
validateIpv6,
|
||||||
|
validateRequiredValue,
|
||||||
|
validateIp,
|
||||||
|
validateIPv4Subnet,
|
||||||
|
validateIPv6Subnet,
|
||||||
|
} from '../../../../helpers/validators';
|
||||||
|
|
||||||
import { BLOCKING_MODES, UINT32_RANGE } from '../../../../helpers/constants';
|
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
import { Checkbox } from '../../../ui/Controls/Checkbox';
|
import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants';
|
||||||
import { Input } from '../../../ui/Controls/Input';
|
import { RootState } from '../../../../initialState';
|
||||||
import { toNumber } from '../../../../helpers/form';
|
|
||||||
import { Textarea } from '../../../ui/Controls/Textarea';
|
|
||||||
import { Radio } from '../../../ui/Controls/Radio';
|
|
||||||
|
|
||||||
const checkboxes: {
|
const checkboxes = [
|
||||||
name: 'dnssec_enabled' | 'disable_ipv6';
|
|
||||||
placeholder: string;
|
|
||||||
subtitle: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
name: 'dnssec_enabled',
|
name: 'dnssec_enabled',
|
||||||
placeholder: i18next.t('dnssec_enable'),
|
placeholder: 'dnssec_enable',
|
||||||
subtitle: i18next.t('dnssec_enable_desc'),
|
subtitle: 'dnssec_enable_desc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'disable_ipv6',
|
name: 'disable_ipv6',
|
||||||
placeholder: i18next.t('disable_ipv6'),
|
placeholder: 'disable_ipv6',
|
||||||
subtitle: i18next.t('disable_ipv6_desc'),
|
subtitle: 'disable_ipv6_desc',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const customIps: {
|
const customIps = [
|
||||||
name: 'blocking_ipv4' | 'blocking_ipv6';
|
|
||||||
label: string;
|
|
||||||
description: string;
|
|
||||||
validateIp: (value: string) => string;
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
|
description: 'blocking_ipv4_desc',
|
||||||
name: 'blocking_ipv4',
|
name: 'blocking_ipv4',
|
||||||
label: i18next.t('blocking_ipv4'),
|
|
||||||
description: i18next.t('blocking_ipv4_desc'),
|
|
||||||
validateIp: validateIpv4,
|
validateIp: validateIpv4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
description: 'blocking_ipv6_desc',
|
||||||
name: 'blocking_ipv6',
|
name: 'blocking_ipv6',
|
||||||
label: i18next.t('blocking_ipv6'),
|
|
||||||
description: i18next.t('blocking_ipv6_desc'),
|
|
||||||
validateIp: validateIpv6,
|
validateIp: validateIpv6,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const blockingModeOptions = [
|
const getFields = (processing: any, t: any) =>
|
||||||
{
|
Object.values(BLOCKING_MODES)
|
||||||
value: BLOCKING_MODES.default,
|
|
||||||
label: i18next.t('default'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: BLOCKING_MODES.refused,
|
|
||||||
label: i18next.t('refused'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: BLOCKING_MODES.nxdomain,
|
|
||||||
label: i18next.t('nxdomain'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: BLOCKING_MODES.null_ip,
|
|
||||||
label: i18next.t('null_ip'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: BLOCKING_MODES.custom_ip,
|
|
||||||
label: i18next.t('custom_ip'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const blockingModeDescriptions = [
|
.map((mode: any) => (
|
||||||
i18next.t(`blocking_mode_default`),
|
<Field
|
||||||
i18next.t(`blocking_mode_refused`),
|
key={mode}
|
||||||
i18next.t(`blocking_mode_nxdomain`),
|
name="blocking_mode"
|
||||||
i18next.t(`blocking_mode_null_ip`),
|
type="radio"
|
||||||
i18next.t(`blocking_mode_custom_ip`),
|
component={renderRadioField}
|
||||||
];
|
value={mode}
|
||||||
|
placeholder={t(mode)}
|
||||||
|
disabled={processing}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
type FormData = {
|
interface ConfigFormProps {
|
||||||
ratelimit: number;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
ratelimit_subnet_len_ipv4: number;
|
submitting: boolean;
|
||||||
ratelimit_subnet_len_ipv6: number;
|
invalid: boolean;
|
||||||
ratelimit_whitelist: string;
|
|
||||||
edns_cs_enabled: boolean;
|
|
||||||
edns_cs_use_custom: boolean;
|
|
||||||
edns_cs_custom_ip?: string;
|
|
||||||
dnssec_enabled: boolean;
|
|
||||||
disable_ipv6: boolean;
|
|
||||||
blocking_mode: string;
|
|
||||||
blocking_ipv4?: string;
|
|
||||||
blocking_ipv6?: string;
|
|
||||||
blocked_response_ttl: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
processing?: boolean;
|
processing?: boolean;
|
||||||
initialValues?: Partial<FormData>;
|
}
|
||||||
onSubmit: (data: FormData) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Form = ({ processing, initialValues, onSubmit }: Props) => {
|
const Form = ({ handleSubmit, submitting, invalid, processing }: ConfigFormProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { blocking_mode, edns_cs_enabled, edns_cs_use_custom } = useSelector(
|
||||||
const {
|
(state: RootState) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {},
|
||||||
handleSubmit,
|
shallowEqual,
|
||||||
watch,
|
);
|
||||||
control,
|
|
||||||
formState: { isSubmitting, isDirty },
|
|
||||||
} = useForm<FormData>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: initialValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
const blocking_mode = watch('blocking_mode');
|
|
||||||
const edns_cs_enabled = watch('edns_cs_enabled');
|
|
||||||
const edns_cs_use_custom = watch('edns_cs_use_custom');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label htmlFor="ratelimit" className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="ratelimit"
|
name="ratelimit"
|
||||||
control={control}
|
type="number"
|
||||||
rules={{ validate: validateRequiredValue }}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('form_enter_rate_limit')}
|
||||||
{...field}
|
normalize={toNumber}
|
||||||
data-testid="dns_config_ratelimit"
|
validate={validateRequiredValue}
|
||||||
type="number"
|
min={UINT32_RANGE.MIN}
|
||||||
label={t('rate_limit')}
|
max={UINT32_RANGE.MAX}
|
||||||
desc={t('rate_limit_desc')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
min={UINT32_RANGE.MIN}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
disabled={processing}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label htmlFor="ratelimit_subnet_len_ipv4" className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv4</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv4_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="ratelimit_subnet_len_ipv4"
|
name="ratelimit_subnet_len_ipv4"
|
||||||
control={control}
|
type="number"
|
||||||
rules={{ validate: validateRequiredValue }}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('form_enter_rate_limit_subnet_len')}
|
||||||
{...field}
|
normalize={toNumber}
|
||||||
data-testid="dns_config_subnet_ipv4"
|
validate={[validateRequiredValue, validateIPv4Subnet]}
|
||||||
type="number"
|
min={0}
|
||||||
label={t('rate_limit_subnet_len_ipv4')}
|
max={32}
|
||||||
desc={t('rate_limit_subnet_len_ipv4_desc')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
min={0}
|
|
||||||
max={32}
|
|
||||||
disabled={processing}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label htmlFor="ratelimit_subnet_len_ipv6" className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv6</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_subnet_len_ipv6_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="ratelimit_subnet_len_ipv6"
|
name="ratelimit_subnet_len_ipv6"
|
||||||
control={control}
|
type="number"
|
||||||
rules={{ validate: validateRequiredValue }}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('form_enter_rate_limit_subnet_len')}
|
||||||
{...field}
|
normalize={toNumber}
|
||||||
data-testid="dns_config_subnet_ipv6"
|
validate={[validateRequiredValue, validateIPv6Subnet]}
|
||||||
type="number"
|
min={0}
|
||||||
label={t('rate_limit_subnet_len_ipv6')}
|
max={128}
|
||||||
desc={t('rate_limit_subnet_len_ipv6_desc')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
min={0}
|
|
||||||
max={128}
|
|
||||||
disabled={processing}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label htmlFor="ratelimit_whitelist" className="form__label form__label--with-desc">
|
||||||
|
<Trans>rate_limit_whitelist</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>rate_limit_whitelist_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="ratelimit_whitelist"
|
name="ratelimit_whitelist"
|
||||||
control={control}
|
component={renderTextareaField}
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Textarea
|
className="form-control"
|
||||||
{...field}
|
placeholder={t('rate_limit_whitelist_placeholder')}
|
||||||
data-testid="dns_config_subnet_ipv6"
|
normalizeOnBlur={removeEmptyLines}
|
||||||
label={t('rate_limit_whitelist')}
|
|
||||||
desc={t('rate_limit_whitelist_desc')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={processing}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="edns_cs_enabled"
|
name="edns_cs_enabled"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('edns_enable')}
|
||||||
{...field}
|
disabled={processing}
|
||||||
data-testid="dns_config_edns_cs_enabled"
|
subtitle={t('edns_cs_desc')}
|
||||||
title={t('edns_enable')}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 form__group form__group--inner">
|
<div className="col-12 form__group form__group--inner">
|
||||||
<div className="form__group">
|
<div className="form__group ">
|
||||||
<Controller
|
<Field
|
||||||
name="edns_cs_use_custom"
|
name="edns_cs_use_custom"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('edns_use_custom_ip')}
|
||||||
{...field}
|
disabled={processing || !edns_cs_enabled}
|
||||||
data-testid="dns_config_edns_use_custom_ip"
|
subtitle={t('edns_use_custom_ip_desc')}
|
||||||
title={t('edns_use_custom_ip')}
|
|
||||||
disabled={processing || !edns_cs_enabled}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{edns_cs_use_custom && (
|
{edns_cs_use_custom && (
|
||||||
<Controller
|
<Field
|
||||||
name="edns_cs_custom_ip"
|
name="edns_cs_custom_ip"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
className="form-control"
|
||||||
validate: {
|
placeholder={t('form_enter_ip')}
|
||||||
required: validateRequiredValue,
|
validate={[validateIp, validateRequiredValue]}
|
||||||
id: validateIp,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
data-testid="dns_config_edns_cs_custom_ip"
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={processing || !edns_cs_enabled}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -281,18 +213,13 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
|
|||||||
{checkboxes.map(({ name, placeholder, subtitle }) => (
|
{checkboxes.map(({ name, placeholder, subtitle }) => (
|
||||||
<div className="col-12" key={name}>
|
<div className="col-12" key={name}>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name={name}
|
name={name}
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t(placeholder)}
|
||||||
{...field}
|
disabled={processing}
|
||||||
data-testid={`dns_config_${name}`}
|
subtitle={t(subtitle)}
|
||||||
title={placeholder}
|
|
||||||
subtitle={subtitle}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -300,50 +227,42 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
|
|||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="form__group form__group--settings mb-4">
|
<div className="form__group form__group--settings mb-4">
|
||||||
<label className="form__label form__label--with-desc">{t('blocking_mode')}</label>
|
<label className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocking_mode</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
{blockingModeDescriptions.map((desc: string) => (
|
{Object.values(BLOCKING_MODES)
|
||||||
<li key={desc}>{desc}</li>
|
|
||||||
))}
|
.map((mode: any) => (
|
||||||
|
<li key={mode}>
|
||||||
|
<Trans>{`blocking_mode_${mode}`}</Trans>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="custom-controls-stacked">
|
<div className="custom-controls-stacked">{getFields(processing, t)}</div>
|
||||||
<Controller
|
|
||||||
name="blocking_mode"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Radio {...field} options={blockingModeOptions} disabled={processing} />
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{blocking_mode === BLOCKING_MODES.custom_ip && (
|
{blocking_mode === BLOCKING_MODES.custom_ip && (
|
||||||
<>
|
<>
|
||||||
{customIps.map(({ label, description, name, validateIp }) => (
|
{customIps.map(({ description, name, validateIp }) => (
|
||||||
<div className="col-12 col-sm-6" key={name}>
|
<div className="col-12 col-sm-6" key={name}>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label className="form__label form__label--with-desc" htmlFor={name}>
|
||||||
|
<Trans>{name}</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>{description}</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name={name}
|
name={name}
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{
|
className="form-control"
|
||||||
validate: {
|
placeholder={t('form_enter_ip')}
|
||||||
required: validateRequiredValue,
|
validate={[validateIp, validateRequiredValue]}
|
||||||
ip: validateIp,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
data-testid="dns_config_blocked_response_ttl"
|
|
||||||
type="text"
|
|
||||||
label={label}
|
|
||||||
desc={description}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -353,27 +272,24 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
|
|||||||
|
|
||||||
<div className="col-12 col-md-7">
|
<div className="col-12 col-md-7">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<label htmlFor="blocked_response_ttl" className="form__label form__label--with-desc">
|
||||||
|
<Trans>blocked_response_ttl</Trans>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>blocked_response_ttl_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
name="blocked_response_ttl"
|
name="blocked_response_ttl"
|
||||||
control={control}
|
type="number"
|
||||||
rules={{ validate: validateRequiredValue }}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('form_enter_blocked_response_ttl')}
|
||||||
{...field}
|
normalize={toNumber}
|
||||||
data-testid="dns_config_blocked_response_ttl"
|
validate={validateRequiredValue}
|
||||||
type="number"
|
min={UINT32_RANGE.MIN}
|
||||||
label={t('blocked_response_ttl')}
|
max={UINT32_RANGE.MAX}
|
||||||
desc={t('blocked_response_ttl_desc')}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
min={UINT32_RANGE.MIN}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
disabled={processing}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -381,13 +297,14 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="dns_config_save"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
disabled={isSubmitting || !isDirty || processing}>
|
disabled={submitting || invalid || processing}>
|
||||||
{t('save_btn')}
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
export default reduxForm<Record<string, any>, Omit<ConfigFormProps, 'invalid' | 'submitting' | 'handleSubmit'>>({
|
||||||
|
form: FORM_NAME.BLOCKING_MODE,
|
||||||
|
})(Form);
|
||||||
|
|||||||
@@ -1,110 +1,173 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import { Field, reduxForm } from 'redux-form';
|
||||||
import clsx from 'clsx';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { testUpstreamWithFormValues } from '../../../../actions';
|
import classnames from 'classnames';
|
||||||
import { DNS_REQUEST_OPTIONS, UINT32_RANGE, UPSTREAM_CONFIGURATION_WIKI_LINK } from '../../../../helpers/constants';
|
|
||||||
import { removeEmptyLines } from '../../../../helpers/helpers';
|
|
||||||
import { getTextareaCommentsHighlight, syncScroll } from '../../../../helpers/highlightTextareaComments';
|
|
||||||
import { RootState } from '../../../../initialState';
|
|
||||||
import '../../../ui/texareaCommentsHighlight.css';
|
|
||||||
import Examples from './Examples';
|
import Examples from './Examples';
|
||||||
import { Checkbox } from '../../../ui/Controls/Checkbox';
|
|
||||||
import { Textarea } from '../../../ui/Controls/Textarea';
|
import { renderRadioField, renderTextareaField, CheckboxField } from '../../../../helpers/form';
|
||||||
import { Radio } from '../../../ui/Controls/Radio';
|
import { DNS_REQUEST_OPTIONS, FORM_NAME, UPSTREAM_CONFIGURATION_WIKI_LINK } from '../../../../helpers/constants';
|
||||||
import { Input } from '../../../ui/Controls/Input';
|
|
||||||
import { validateRequiredValue } from '../../../../helpers/validators';
|
import { testUpstreamWithFormValues } from '../../../../actions';
|
||||||
import { toNumber } from '../../../../helpers/form';
|
|
||||||
|
import { removeEmptyLines, trimLinesAndRemoveEmpty } from '../../../../helpers/helpers';
|
||||||
|
|
||||||
|
import { getTextareaCommentsHighlight, syncScroll } from '../../../../helpers/highlightTextareaComments';
|
||||||
|
import '../../../ui/texareaCommentsHighlight.css';
|
||||||
|
import { RootState } from '../../../../initialState';
|
||||||
|
|
||||||
const UPSTREAM_DNS_NAME = 'upstream_dns';
|
const UPSTREAM_DNS_NAME = 'upstream_dns';
|
||||||
|
const UPSTREAM_MODE_NAME = 'upstream_mode';
|
||||||
|
|
||||||
type FormData = {
|
interface renderFieldProps {
|
||||||
upstream_dns: string;
|
name: string;
|
||||||
upstream_mode: string;
|
component: any;
|
||||||
fallback_dns: string;
|
type: string;
|
||||||
bootstrap_dns: string;
|
className?: string;
|
||||||
local_ptr_upstreams: string;
|
placeholder: string;
|
||||||
use_private_ptr_resolvers: boolean;
|
subtitle?: string;
|
||||||
resolve_clients: boolean;
|
value?: string;
|
||||||
upstream_timeout: number;
|
normalizeOnBlur?: (...args: unknown[]) => unknown;
|
||||||
|
containerClass?: string;
|
||||||
|
onScroll?: (...args: unknown[]) => unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderField = ({
|
||||||
|
name,
|
||||||
|
component,
|
||||||
|
type,
|
||||||
|
className,
|
||||||
|
placeholder,
|
||||||
|
subtitle,
|
||||||
|
value,
|
||||||
|
normalizeOnBlur,
|
||||||
|
containerClass,
|
||||||
|
onScroll,
|
||||||
|
}: renderFieldProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
|
||||||
|
|
||||||
|
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={placeholder} className={classnames('col-12 mb-4', containerClass)}>
|
||||||
|
<Field
|
||||||
|
id={name}
|
||||||
|
value={value}
|
||||||
|
name={name}
|
||||||
|
component={component}
|
||||||
|
type={type}
|
||||||
|
className={className}
|
||||||
|
placeholder={t(placeholder)}
|
||||||
|
subtitle={t(subtitle)}
|
||||||
|
disabled={processingSetConfig || processingTestUpstream}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
|
onScroll={onScroll}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type FormProps = {
|
interface renderTextareaWithHighlightFieldProps {
|
||||||
initialValues?: Partial<FormData>;
|
className: string;
|
||||||
onSubmit: (data: FormData) => void;
|
disabled?: boolean;
|
||||||
|
id: string;
|
||||||
|
input?: object;
|
||||||
|
meta?: object;
|
||||||
|
normalizeOnBlur?: (...args: unknown[]) => unknown;
|
||||||
|
onScroll?: (...args: unknown[]) => unknown;
|
||||||
|
placeholder: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderTextareaWithHighlightField = (props: renderTextareaWithHighlightFieldProps) => {
|
||||||
|
const upstream_dns = useSelector((store: RootState) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
|
||||||
|
|
||||||
|
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
const onScroll = (e: any) => syncScroll(e, ref);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderTextareaField({
|
||||||
|
...props,
|
||||||
|
disabled: !!upstream_dns_file,
|
||||||
|
onScroll,
|
||||||
|
normalizeOnBlur: trimLinesAndRemoveEmpty,
|
||||||
|
})}
|
||||||
|
|
||||||
|
{getTextareaCommentsHighlight(ref, upstream_dns)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const upstreamModeOptions = [
|
const INPUT_FIELDS = [
|
||||||
{
|
{
|
||||||
label: i18next.t('load_balancing'),
|
name: UPSTREAM_MODE_NAME,
|
||||||
desc: <Trans components={{ br: <br />, b: <b /> }}>load_balancing_desc</Trans>,
|
type: 'radio',
|
||||||
value: DNS_REQUEST_OPTIONS.LOAD_BALANCING,
|
value: DNS_REQUEST_OPTIONS.LOAD_BALANCING,
|
||||||
|
component: renderRadioField,
|
||||||
|
subtitle: 'load_balancing_desc',
|
||||||
|
placeholder: 'load_balancing',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18next.t('parallel_requests'),
|
name: UPSTREAM_MODE_NAME,
|
||||||
desc: <Trans components={{ br: <br />, b: <b /> }}>upstream_parallel</Trans>,
|
type: 'radio',
|
||||||
value: DNS_REQUEST_OPTIONS.PARALLEL,
|
value: DNS_REQUEST_OPTIONS.PARALLEL,
|
||||||
|
component: renderRadioField,
|
||||||
|
subtitle: 'upstream_parallel',
|
||||||
|
placeholder: 'parallel_requests',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: i18next.t('fastest_addr'),
|
name: UPSTREAM_MODE_NAME,
|
||||||
desc: <Trans components={{ br: <br />, b: <b /> }}>fastest_addr_desc</Trans>,
|
type: 'radio',
|
||||||
value: DNS_REQUEST_OPTIONS.FASTEST_ADDR,
|
value: DNS_REQUEST_OPTIONS.FASTEST_ADDR,
|
||||||
|
component: renderRadioField,
|
||||||
|
subtitle: 'fastest_addr_desc',
|
||||||
|
placeholder: 'fastest_addr',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Form = ({ initialValues, onSubmit }: FormProps) => {
|
interface FormProps {
|
||||||
const { t } = useTranslation();
|
handleSubmit?: (...args: unknown[]) => string;
|
||||||
|
submitting?: boolean;
|
||||||
|
invalid?: boolean;
|
||||||
|
initialValues?: object;
|
||||||
|
upstream_dns?: string;
|
||||||
|
fallback_dns?: string;
|
||||||
|
bootstrap_dns?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Form = ({ submitting, invalid, handleSubmit }: FormProps) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const {
|
const upstream_dns = useSelector((store: RootState) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
watch,
|
|
||||||
formState: { isSubmitting, isDirty },
|
|
||||||
} = useForm<FormData>({
|
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
|
||||||
upstream_dns: initialValues?.upstream_dns || '',
|
|
||||||
upstream_mode: initialValues?.upstream_mode || DNS_REQUEST_OPTIONS.LOAD_BALANCING,
|
|
||||||
fallback_dns: initialValues?.fallback_dns || '',
|
|
||||||
bootstrap_dns: initialValues?.bootstrap_dns || '',
|
|
||||||
local_ptr_upstreams: initialValues?.local_ptr_upstreams || '',
|
|
||||||
use_private_ptr_resolvers: initialValues?.use_private_ptr_resolvers || false,
|
|
||||||
resolve_clients: initialValues?.resolve_clients || false,
|
|
||||||
upstream_timeout: initialValues?.upstream_timeout || 0,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const upstream_dns = watch('upstream_dns');
|
|
||||||
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
|
const processingTestUpstream = useSelector((state: RootState) => state.settings.processingTestUpstream);
|
||||||
|
|
||||||
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
|
const processingSetConfig = useSelector((state: RootState) => state.dnsConfig.processingSetConfig);
|
||||||
const defaultLocalPtrUpstreams = useSelector((state: RootState) => state.dnsConfig.default_local_ptr_upstreams);
|
const defaultLocalPtrUpstreams = useSelector((state: RootState) => state.dnsConfig.default_local_ptr_upstreams);
|
||||||
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
|
|
||||||
|
|
||||||
const handleUpstreamTest = () => {
|
const handleUpstreamTest = () => dispatch(testUpstreamWithFormValues());
|
||||||
const formValues = {
|
|
||||||
bootstrap_dns: watch('bootstrap_dns'),
|
const testButtonClass = classnames('btn btn-primary btn-standard mr-2', {
|
||||||
upstream_dns: watch('upstream_dns'),
|
'btn-loading': processingTestUpstream,
|
||||||
local_ptr_upstreams: watch('local_ptr_upstreams'),
|
});
|
||||||
fallback_dns: watch('fallback_dns'),
|
|
||||||
};
|
const components = {
|
||||||
dispatch(testUpstreamWithFormValues(formValues));
|
a: <a href={UPSTREAM_CONFIGURATION_WIKI_LINK} target="_blank" rel="noopener noreferrer" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="form--upstream">
|
<form onSubmit={handleSubmit} className="form--upstream">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<label className="col form__label" htmlFor="upstream_dns">
|
<label className="col form__label" htmlFor={UPSTREAM_DNS_NAME}>
|
||||||
<Trans
|
<Trans components={components}>upstream_dns_help</Trans>{' '}
|
||||||
components={{
|
|
||||||
a: <a href={UPSTREAM_CONFIGURATION_WIKI_LINK} target="_blank" rel="noopener noreferrer" />,
|
|
||||||
}}>
|
|
||||||
upstream_dns_help
|
|
||||||
</Trans>{' '}
|
|
||||||
<Trans
|
<Trans
|
||||||
components={[
|
components={[
|
||||||
<a
|
<a
|
||||||
@@ -121,69 +184,44 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
|
|||||||
|
|
||||||
<div className="col-12 mb-4">
|
<div className="col-12 mb-4">
|
||||||
<div className="text-edit-container">
|
<div className="text-edit-container">
|
||||||
<Controller
|
<Field
|
||||||
name="upstream_dns"
|
id={UPSTREAM_DNS_NAME}
|
||||||
control={control}
|
name={UPSTREAM_DNS_NAME}
|
||||||
render={({ field }) => (
|
component={renderTextareaWithHighlightField}
|
||||||
<>
|
type="text"
|
||||||
<Textarea
|
className="form-control form-control--textarea font-monospace text-input"
|
||||||
{...field}
|
placeholder={t('upstream_dns')}
|
||||||
id={UPSTREAM_DNS_NAME}
|
disabled={processingSetConfig || processingTestUpstream}
|
||||||
data-testid="upstream_dns"
|
normalizeOnBlur={removeEmptyLines}
|
||||||
className="form-control--textarea-large text-input"
|
|
||||||
wrapperClassName="mb-0"
|
|
||||||
placeholder={t('upstream_dns')}
|
|
||||||
disabled={!!upstream_dns_file || processingSetConfig || processingTestUpstream}
|
|
||||||
onScroll={(e) => syncScroll(e, textareaRef)}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
{getTextareaCommentsHighlight(textareaRef, upstream_dns)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{INPUT_FIELDS.map(renderField)}
|
||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<Examples />
|
<Examples />
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12 mb-4">
|
<hr />
|
||||||
<Controller
|
|
||||||
name="upstream_mode"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Radio
|
|
||||||
{...field}
|
|
||||||
options={upstreamModeOptions}
|
|
||||||
disabled={processingSetConfig || processingTestUpstream}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form__label form__label--with-desc" htmlFor="fallback_dns">
|
<label className="form__label form__label--with-desc" htmlFor="fallback_dns">
|
||||||
{t('fallback_dns_title')}
|
<Trans>fallback_dns_title</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">{t('fallback_dns_desc')}</div>
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>fallback_dns_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
|
id="fallback_dns"
|
||||||
name="fallback_dns"
|
name="fallback_dns"
|
||||||
control={control}
|
component={renderTextareaField}
|
||||||
render={({ field }) => (
|
type="text"
|
||||||
<Textarea
|
className="form-control form-control--textarea form-control--textarea-small font-monospace"
|
||||||
{...field}
|
placeholder={t('fallback_dns_placeholder')}
|
||||||
id="fallback_dns"
|
disabled={processingSetConfig}
|
||||||
data-testid="fallback_dns"
|
normalizeOnBlur={removeEmptyLines}
|
||||||
wrapperClassName="mb-0"
|
|
||||||
placeholder={t('fallback_dns_placeholder')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -191,30 +229,24 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
|
|||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12 mb-2">
|
||||||
<label className="form__label form__label--with-desc" htmlFor="bootstrap_dns">
|
<label className="form__label form__label--with-desc" htmlFor="bootstrap_dns">
|
||||||
{t('bootstrap_dns')}
|
<Trans>bootstrap_dns</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">{t('bootstrap_dns_desc')}</div>
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>bootstrap_dns_desc</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
|
id="bootstrap_dns"
|
||||||
name="bootstrap_dns"
|
name="bootstrap_dns"
|
||||||
control={control}
|
component={renderTextareaField}
|
||||||
render={({ field }) => (
|
type="text"
|
||||||
<Textarea
|
className="form-control form-control--textarea form-control--textarea-small font-monospace"
|
||||||
{...field}
|
placeholder={t('bootstrap_dns')}
|
||||||
id="bootstrap_dns"
|
disabled={processingSetConfig}
|
||||||
data-testid="bootstrap_dns"
|
normalizeOnBlur={removeEmptyLines}
|
||||||
placeholder={t('bootstrap_dns')}
|
|
||||||
wrapperClassName="mb-0"
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
onBlur={(e) => {
|
|
||||||
const value = removeEmptyLines(e.target.value);
|
|
||||||
field.onChange(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -224,47 +256,43 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
|
|||||||
|
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<label className="form__label form__label--with-desc" htmlFor="local_ptr">
|
<label className="form__label form__label--with-desc" htmlFor="local_ptr">
|
||||||
{t('local_ptr_title')}
|
<Trans>local_ptr_title</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">{t('local_ptr_desc')}</div>
|
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
{defaultLocalPtrUpstreams?.length > 0
|
<Trans>local_ptr_desc</Trans>
|
||||||
? t('local_ptr_default_resolver', {
|
|
||||||
ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', '),
|
|
||||||
})
|
|
||||||
: t('local_ptr_no_default_resolver')}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Controller
|
<div className="form__desc form__desc--top">
|
||||||
name="local_ptr_upstreams"
|
{/** TODO: Add internazionalization for "" */}
|
||||||
control={control}
|
{defaultLocalPtrUpstreams?.length > 0 ? (
|
||||||
render={({ field }) => (
|
<Trans values={{ ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', ') }}>
|
||||||
<Textarea
|
local_ptr_default_resolver
|
||||||
{...field}
|
</Trans>
|
||||||
id="local_ptr_upstreams"
|
) : (
|
||||||
data-testid="local_ptr_upstreams"
|
<Trans>local_ptr_no_default_resolver</Trans>
|
||||||
placeholder={t('local_ptr_placeholder')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
id="local_ptr_upstreams"
|
||||||
|
name="local_ptr_upstreams"
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea form-control--textarea-small font-monospace"
|
||||||
|
placeholder={t('local_ptr_placeholder')}
|
||||||
|
disabled={processingSetConfig}
|
||||||
|
normalizeOnBlur={removeEmptyLines}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Controller
|
<Field
|
||||||
name="use_private_ptr_resolvers"
|
name="use_private_ptr_resolvers"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('use_private_ptr_resolvers_title')}
|
||||||
{...field}
|
subtitle={t('use_private_ptr_resolvers_desc')}
|
||||||
data-testid="dns_use_private_ptr_resolvers"
|
disabled={processingSetConfig}
|
||||||
title={t('use_private_ptr_resolvers_title')}
|
|
||||||
subtitle={t('use_private_ptr_resolvers_desc')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,79 +302,32 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12 mb-4">
|
<div className="col-12 mb-4">
|
||||||
<Controller
|
<Field
|
||||||
name="resolve_clients"
|
name="resolve_clients"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('resolve_clients_title')}
|
||||||
{...field}
|
subtitle={t('resolve_clients_desc')}
|
||||||
data-testid="dns_resolve_clients"
|
disabled={processingSetConfig}
|
||||||
title={t('resolve_clients_title')}
|
|
||||||
subtitle={t('resolve_clients_desc')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-12">
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12 col-md-7">
|
|
||||||
<div className="form__group">
|
|
||||||
<label htmlFor="upstream_timeout" className="form__label form__label--with-desc">
|
|
||||||
<Trans>upstream_timeout</Trans>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">
|
|
||||||
<Trans>upstream_timeout_desc</Trans>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="upstream_timeout"
|
|
||||||
control={control}
|
|
||||||
rules={{ validate: validateRequiredValue }}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="number"
|
|
||||||
id="upstream_timeout"
|
|
||||||
data-testid="upstream_timeout"
|
|
||||||
placeholder={t('form_enter_upstream_timeout')}
|
|
||||||
disabled={processingSetConfig}
|
|
||||||
min={1}
|
|
||||||
max={UINT32_RANGE.MAX}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card-actions">
|
<div className="card-actions">
|
||||||
<div className="btn-list">
|
<div className="btn-list">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="dns_upstream_test"
|
className={testButtonClass}
|
||||||
className={clsx('btn btn-primary btn-standard mr-2', {
|
|
||||||
'btn-loading': processingTestUpstream,
|
|
||||||
})}
|
|
||||||
onClick={handleUpstreamTest}
|
onClick={handleUpstreamTest}
|
||||||
disabled={!upstream_dns || processingTestUpstream}>
|
disabled={!upstream_dns || processingTestUpstream}>
|
||||||
{t('test_upstream_btn')}
|
<Trans>test_upstream_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="dns_upstream_save"
|
|
||||||
className="btn btn-success btn-standard"
|
className="btn btn-success btn-standard"
|
||||||
disabled={isSubmitting || !isDirty || processingSetConfig || processingTestUpstream}>
|
disabled={submitting || invalid || processingSetConfig || processingTestUpstream}>
|
||||||
{t('apply_btn')}
|
<Trans>apply_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -354,4 +335,4 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Form;
|
export default reduxForm({ form: FORM_NAME.UPSTREAM })(Form);
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ const Upstream = () => {
|
|||||||
resolve_clients,
|
resolve_clients,
|
||||||
local_ptr_upstreams,
|
local_ptr_upstreams,
|
||||||
use_private_ptr_resolvers,
|
use_private_ptr_resolvers,
|
||||||
upstream_timeout,
|
|
||||||
} = useSelector((state: RootState) => state.dnsConfig, shallowEqual);
|
} = useSelector((state: RootState) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
|
const upstream_dns_file = useSelector((state: RootState) => state.dnsConfig.upstream_dns_file);
|
||||||
@@ -33,7 +32,6 @@ const Upstream = () => {
|
|||||||
resolve_clients,
|
resolve_clients,
|
||||||
local_ptr_upstreams,
|
local_ptr_upstreams,
|
||||||
use_private_ptr_resolvers,
|
use_private_ptr_resolvers,
|
||||||
upstream_timeout,
|
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
const dnsConfig = {
|
const dnsConfig = {
|
||||||
@@ -43,7 +41,6 @@ const Upstream = () => {
|
|||||||
resolve_clients,
|
resolve_clients,
|
||||||
local_ptr_upstreams,
|
local_ptr_upstreams,
|
||||||
use_private_ptr_resolvers,
|
use_private_ptr_resolvers,
|
||||||
upstream_timeout,
|
|
||||||
...(upstream_dns_file ? null : { upstream_dns }),
|
...(upstream_dns_file ? null : { upstream_dns }),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +64,6 @@ const Upstream = () => {
|
|||||||
resolve_clients,
|
resolve_clients,
|
||||||
local_ptr_upstreams,
|
local_ptr_upstreams,
|
||||||
use_private_ptr_resolvers,
|
use_private_ptr_resolvers,
|
||||||
upstream_timeout,
|
|
||||||
}}
|
}}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||||
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { renderInputField, CheckboxField, renderRadioField, toNumber } from '../../../helpers/form';
|
||||||
import i18next from 'i18next';
|
|
||||||
import {
|
import {
|
||||||
validateServerName,
|
validateServerName,
|
||||||
validateIsSafePort,
|
validateIsSafePort,
|
||||||
@@ -12,6 +14,7 @@ import {
|
|||||||
validatePortTLS,
|
validatePortTLS,
|
||||||
validatePlainDns,
|
validatePlainDns,
|
||||||
} from '../../../helpers/validators';
|
} from '../../../helpers/validators';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
import KeyStatus from './KeyStatus';
|
import KeyStatus from './KeyStatus';
|
||||||
|
|
||||||
@@ -19,39 +22,51 @@ import CertificateStatus from './CertificateStatus';
|
|||||||
import {
|
import {
|
||||||
DNS_OVER_QUIC_PORT,
|
DNS_OVER_QUIC_PORT,
|
||||||
DNS_OVER_TLS_PORT,
|
DNS_OVER_TLS_PORT,
|
||||||
|
FORM_NAME,
|
||||||
STANDARD_HTTPS_PORT,
|
STANDARD_HTTPS_PORT,
|
||||||
ENCRYPTION_SOURCE,
|
ENCRYPTION_SOURCE,
|
||||||
} from '../../../helpers/constants';
|
} from '../../../helpers/constants';
|
||||||
import { Checkbox } from '../../ui/Controls/Checkbox';
|
|
||||||
import { Radio } from '../../ui/Controls/Radio';
|
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { Textarea } from '../../ui/Controls/Textarea';
|
|
||||||
import { EncryptionData } from '../../../initialState';
|
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
|
|
||||||
const certificateSourceOptions = [
|
const validate = (values: any) => {
|
||||||
{
|
const errors: { port_dns_over_tls?: string; port_https?: string } = {};
|
||||||
label: i18next.t('encryption_certificates_source_path'),
|
|
||||||
value: ENCRYPTION_SOURCE.PATH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18next.t('encryption_certificates_source_content'),
|
|
||||||
value: ENCRYPTION_SOURCE.CONTENT,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const keySourceOptions = [
|
if (values.port_dns_over_tls && values.port_https) {
|
||||||
{
|
if (values.port_dns_over_tls === values.port_https) {
|
||||||
label: i18next.t('encryption_key_source_path'),
|
errors.port_dns_over_tls = i18n.t('form_error_equal');
|
||||||
value: ENCRYPTION_SOURCE.PATH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18next.t('encryption_key_source_content'),
|
|
||||||
value: ENCRYPTION_SOURCE.CONTENT,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const validationMessage = (warningValidation: string, isWarning: boolean) => {
|
errors.port_https = i18n.t('form_error_equal');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearFields = (change: any, setTlsConfig: any, validateTlsConfig: any, t: any) => {
|
||||||
|
const fields = {
|
||||||
|
private_key: '',
|
||||||
|
certificate_chain: '',
|
||||||
|
private_key_path: '',
|
||||||
|
certificate_path: '',
|
||||||
|
port_https: STANDARD_HTTPS_PORT,
|
||||||
|
port_dns_over_tls: DNS_OVER_TLS_PORT,
|
||||||
|
port_dns_over_quic: DNS_OVER_QUIC_PORT,
|
||||||
|
server_name: '',
|
||||||
|
force_https: false,
|
||||||
|
enabled: false,
|
||||||
|
private_key_saved: false,
|
||||||
|
serve_plain_dns: true,
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
if (window.confirm(t('encryption_reset'))) {
|
||||||
|
Object.keys(fields)
|
||||||
|
|
||||||
|
.forEach((field) => change(field, fields[field]));
|
||||||
|
setTlsConfig(fields);
|
||||||
|
validateTlsConfig(fields);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validationMessage = (warningValidation: any, isWarning: any) => {
|
||||||
if (!warningValidation) {
|
if (!warningValidation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -73,60 +88,56 @@ const validationMessage = (warningValidation: string, isWarning: boolean) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EncryptionFormValues = {
|
interface FormProps {
|
||||||
enabled?: boolean;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
serve_plain_dns?: boolean;
|
handleChange?: (...args: unknown[]) => unknown;
|
||||||
server_name?: string;
|
isEnabled: boolean;
|
||||||
force_https?: boolean;
|
servePlainDns: boolean;
|
||||||
port_https?: number;
|
certificateChain: string;
|
||||||
port_dns_over_tls?: number;
|
privateKey: string;
|
||||||
port_dns_over_quic?: number;
|
certificatePath: string;
|
||||||
certificate_chain?: string;
|
privateKeyPath: string;
|
||||||
private_key?: string;
|
change: (...args: unknown[]) => unknown;
|
||||||
certificate_path?: string;
|
submitting: boolean;
|
||||||
private_key_path?: string;
|
invalid: boolean;
|
||||||
certificate_source?: string;
|
initialValues: object;
|
||||||
key_source?: string;
|
processingConfig: boolean;
|
||||||
private_key_saved?: boolean;
|
processingValidate: boolean;
|
||||||
};
|
status_key?: string;
|
||||||
|
not_after?: string;
|
||||||
type Props = {
|
warning_validation?: string;
|
||||||
initialValues: EncryptionFormValues;
|
valid_chain?: boolean;
|
||||||
encryption: EncryptionData;
|
valid_key?: boolean;
|
||||||
onSubmit: (values: EncryptionFormValues) => void;
|
valid_cert?: boolean;
|
||||||
debouncedConfigValidation: (values: EncryptionFormValues) => void;
|
valid_pair?: boolean;
|
||||||
setTlsConfig: (values: Partial<EncryptionData>) => void;
|
dns_names?: string[];
|
||||||
validateTlsConfig: (values: Partial<EncryptionData>) => void;
|
key_type?: string;
|
||||||
};
|
issuer?: string;
|
||||||
|
subject?: string;
|
||||||
const defaultValues = {
|
t: (...args: unknown[]) => string;
|
||||||
enabled: false,
|
setTlsConfig: (...args: unknown[]) => unknown;
|
||||||
serve_plain_dns: true,
|
validateTlsConfig: (...args: unknown[]) => unknown;
|
||||||
server_name: '',
|
certificateSource?: string;
|
||||||
force_https: false,
|
privateKeySource?: string;
|
||||||
port_https: STANDARD_HTTPS_PORT,
|
privateKeySaved?: boolean;
|
||||||
port_dns_over_tls: DNS_OVER_TLS_PORT,
|
}
|
||||||
port_dns_over_quic: DNS_OVER_QUIC_PORT,
|
|
||||||
certificate_chain: '',
|
|
||||||
private_key: '',
|
|
||||||
certificate_path: '',
|
|
||||||
private_key_path: '',
|
|
||||||
certificate_source: ENCRYPTION_SOURCE.PATH,
|
|
||||||
key_source: ENCRYPTION_SOURCE.PATH,
|
|
||||||
private_key_saved: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Form = ({
|
|
||||||
initialValues,
|
|
||||||
encryption,
|
|
||||||
onSubmit,
|
|
||||||
setTlsConfig,
|
|
||||||
debouncedConfigValidation,
|
|
||||||
validateTlsConfig,
|
|
||||||
}: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
|
let Form = (props: FormProps) => {
|
||||||
const {
|
const {
|
||||||
|
t,
|
||||||
|
handleSubmit,
|
||||||
|
handleChange,
|
||||||
|
isEnabled,
|
||||||
|
servePlainDns,
|
||||||
|
certificateChain,
|
||||||
|
privateKey,
|
||||||
|
certificatePath,
|
||||||
|
privateKeyPath,
|
||||||
|
change,
|
||||||
|
invalid,
|
||||||
|
submitting,
|
||||||
|
processingConfig,
|
||||||
|
processingValidate,
|
||||||
not_after,
|
not_after,
|
||||||
valid_chain,
|
valid_chain,
|
||||||
valid_key,
|
valid_key,
|
||||||
@@ -137,100 +148,37 @@ export const Form = ({
|
|||||||
issuer,
|
issuer,
|
||||||
subject,
|
subject,
|
||||||
warning_validation,
|
warning_validation,
|
||||||
processingConfig,
|
setTlsConfig,
|
||||||
processingValidate,
|
validateTlsConfig,
|
||||||
} = encryption;
|
certificateSource,
|
||||||
|
privateKeySource,
|
||||||
const {
|
privateKeySaved,
|
||||||
control,
|
} = props;
|
||||||
handleSubmit,
|
|
||||||
watch,
|
|
||||||
reset,
|
|
||||||
setValue,
|
|
||||||
setError,
|
|
||||||
getValues,
|
|
||||||
formState: { isSubmitting, isValid },
|
|
||||||
} = useForm<EncryptionFormValues>({
|
|
||||||
defaultValues: {
|
|
||||||
...defaultValues,
|
|
||||||
...initialValues,
|
|
||||||
},
|
|
||||||
mode: 'onBlur',
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
enabled: isEnabled,
|
|
||||||
serve_plain_dns: servePlainDns,
|
|
||||||
certificate_chain: certificateChain,
|
|
||||||
private_key: privateKey,
|
|
||||||
private_key_path: privateKeyPath,
|
|
||||||
key_source: privateKeySource,
|
|
||||||
private_key_saved: privateKeySaved,
|
|
||||||
certificate_path: certificatePath,
|
|
||||||
certificate_source: certificateSource,
|
|
||||||
} = watch();
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
|
||||||
debouncedConfigValidation(getValues());
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSavingDisabled = () => {
|
const isSavingDisabled = () => {
|
||||||
const processing = isSubmitting || processingConfig || processingValidate;
|
const processing = submitting || processingConfig || processingValidate;
|
||||||
|
|
||||||
if (servePlainDns && !isEnabled) {
|
if (servePlainDns && !isEnabled) {
|
||||||
return !isValid || processing;
|
return invalid || processing;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isValid || processing || !valid_key || !valid_cert || !valid_pair;
|
return invalid || processing || !valid_key || !valid_cert || !valid_pair;
|
||||||
};
|
|
||||||
|
|
||||||
const clearFields = () => {
|
|
||||||
if (window.confirm(t('encryption_reset'))) {
|
|
||||||
reset();
|
|
||||||
setTlsConfig(defaultValues);
|
|
||||||
validateTlsConfig(defaultValues);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const validatePorts = (values: EncryptionFormValues) => {
|
|
||||||
const errors: { port_dns_over_tls?: string; port_https?: string } = {};
|
|
||||||
|
|
||||||
if (values.port_dns_over_tls && values.port_https) {
|
|
||||||
if (values.port_dns_over_tls === values.port_https) {
|
|
||||||
errors.port_dns_over_tls = i18next.t('form_error_equal');
|
|
||||||
errors.port_https = i18next.t('form_error_equal');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFormSubmit = (data: EncryptionFormValues) => {
|
|
||||||
const validationErrors = validatePorts(data);
|
|
||||||
|
|
||||||
if (Object.keys(validationErrors).length > 0) {
|
|
||||||
Object.entries(validationErrors).forEach(([field, message]) => {
|
|
||||||
setError(field as keyof EncryptionFormValues, { type: 'manual', message });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
onSubmit(data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDisabled = isSavingDisabled();
|
const isDisabled = isSavingDisabled();
|
||||||
const isWarning = valid_key && valid_cert && valid_pair;
|
const isWarning = valid_key && valid_cert && valid_pair;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="form__group form__group--settings mb-3">
|
<div className="form__group form__group--settings mb-3">
|
||||||
<Controller
|
<Field
|
||||||
name="enabled"
|
name="enabled"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox {...field} title={t('encryption_enable')} onBlur={handleBlur} />
|
placeholder={t('encryption_enable')}
|
||||||
)}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -239,13 +187,13 @@ export const Form = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group mb-3 mt-5">
|
<div className="form__group mb-3 mt-5">
|
||||||
<Controller
|
<Field
|
||||||
name="serve_plain_dns"
|
name="serve_plain_dns"
|
||||||
control={control}
|
type="checkbox"
|
||||||
rules={{
|
component={CheckboxField}
|
||||||
validate: (value) => validatePlainDns(value, getValues()),
|
placeholder={t('encryption_plain_dns_enable')}
|
||||||
}}
|
onChange={handleChange}
|
||||||
render={({ field }) => <Checkbox {...field} title={t('encryption_plain_dns_enable')} />}
|
validate={validatePlainDns}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -264,20 +212,16 @@ export const Form = ({
|
|||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
|
id="server_name"
|
||||||
name="server_name"
|
name="server_name"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{ validate: validateServerName }}
|
type="text"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('encryption_server_enter')}
|
||||||
{...field}
|
onChange={handleChange}
|
||||||
type="text"
|
disabled={!isEnabled}
|
||||||
placeholder={t('encryption_server_enter')}
|
validate={validateServerName}
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isEnabled}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
@@ -288,12 +232,13 @@ export const Form = ({
|
|||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="force_https"
|
name="force_https"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox {...field} title={t('encryption_redirect')} disabled={!isEnabled} />
|
placeholder={t('encryption_redirect')}
|
||||||
)}
|
onChange={handleChange}
|
||||||
|
disabled={!isEnabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
@@ -310,24 +255,17 @@ export const Form = ({
|
|||||||
<Trans>encryption_https</Trans>
|
<Trans>encryption_https</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
|
id="port_https"
|
||||||
name="port_https"
|
name="port_https"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{ validate: { validatePort, validateIsSafePort } }}
|
type="number"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('encryption_https')}
|
||||||
{...field}
|
validate={[validatePort, validateIsSafePort]}
|
||||||
type="number"
|
normalize={toNumber}
|
||||||
placeholder={t('encryption_https')}
|
onChange={handleChange}
|
||||||
error={fieldState.error?.message}
|
disabled={!isEnabled}
|
||||||
disabled={!isEnabled}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
@@ -342,24 +280,17 @@ export const Form = ({
|
|||||||
<Trans>encryption_dot</Trans>
|
<Trans>encryption_dot</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
|
id="port_dns_over_tls"
|
||||||
name="port_dns_over_tls"
|
name="port_dns_over_tls"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{ validate: validatePortTLS }}
|
type="number"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('encryption_dot')}
|
||||||
{...field}
|
validate={[validatePortTLS]}
|
||||||
type="number"
|
normalize={toNumber}
|
||||||
placeholder={t('encryption_dot')}
|
onChange={handleChange}
|
||||||
error={fieldState.error?.message}
|
disabled={!isEnabled}
|
||||||
disabled={!isEnabled}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
@@ -374,24 +305,17 @@ export const Form = ({
|
|||||||
<Trans>encryption_doq</Trans>
|
<Trans>encryption_doq</Trans>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
|
id="port_dns_over_quic"
|
||||||
name="port_dns_over_quic"
|
name="port_dns_over_quic"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
rules={{ validate: validatePortQuic }}
|
type="number"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control"
|
||||||
<Input
|
placeholder={t('encryption_doq')}
|
||||||
{...field}
|
validate={[validatePortQuic]}
|
||||||
type="number"
|
normalize={toNumber}
|
||||||
placeholder={t('encryption_doq')}
|
onChange={handleChange}
|
||||||
error={fieldState.error?.message}
|
disabled={!isEnabled}
|
||||||
disabled={!isEnabled}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="form__desc">
|
<div className="form__desc">
|
||||||
@@ -428,44 +352,50 @@ export const Form = ({
|
|||||||
|
|
||||||
<div className="form__inline mb-2">
|
<div className="form__inline mb-2">
|
||||||
<div className="custom-controls-stacked">
|
<div className="custom-controls-stacked">
|
||||||
<Controller
|
<Field
|
||||||
name="certificate_source"
|
name="certificate_source"
|
||||||
control={control}
|
component={renderRadioField}
|
||||||
render={({ field }) => (
|
type="radio"
|
||||||
<Radio {...field} options={certificateSourceOptions} disabled={!isEnabled} />
|
className="form-control mr-2"
|
||||||
)}
|
value="path"
|
||||||
|
placeholder={t('encryption_certificates_source_path')}
|
||||||
|
disabled={!isEnabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="certificate_source"
|
||||||
|
component={renderRadioField}
|
||||||
|
type="radio"
|
||||||
|
className="form-control mr-2"
|
||||||
|
value="content"
|
||||||
|
placeholder={t('encryption_certificates_source_content')}
|
||||||
|
disabled={!isEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{certificateSource === ENCRYPTION_SOURCE.CONTENT ? (
|
{certificateSource === ENCRYPTION_SOURCE.CONTENT && (
|
||||||
<Controller
|
<Field
|
||||||
|
id="certificate_chain"
|
||||||
name="certificate_chain"
|
name="certificate_chain"
|
||||||
control={control}
|
component="textarea"
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Textarea
|
className="form-control form-control--textarea"
|
||||||
{...field}
|
placeholder={t('encryption_certificates_input')}
|
||||||
placeholder={t('encryption_certificates_input')}
|
onChange={handleChange}
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
error={fieldState.error?.message}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
)}
|
||||||
<Controller
|
{certificateSource === ENCRYPTION_SOURCE.PATH && (
|
||||||
|
<Field
|
||||||
|
id="certificate_path"
|
||||||
name="certificate_path"
|
name="certificate_path"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Input
|
className="form-control"
|
||||||
{...field}
|
placeholder={t('encryption_certificate_path')}
|
||||||
type="text"
|
onChange={handleChange}
|
||||||
placeholder={t('encryption_certificate_path')}
|
disabled={!isEnabled}
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isEnabled}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -494,67 +424,70 @@ export const Form = ({
|
|||||||
|
|
||||||
<div className="form__inline mb-2">
|
<div className="form__inline mb-2">
|
||||||
<div className="custom-controls-stacked">
|
<div className="custom-controls-stacked">
|
||||||
<Controller
|
<Field
|
||||||
name="key_source"
|
name="key_source"
|
||||||
control={control}
|
component={renderRadioField}
|
||||||
render={({ field }) => (
|
type="radio"
|
||||||
<Radio {...field} options={keySourceOptions} disabled={!isEnabled} />
|
className="form-control mr-2"
|
||||||
)}
|
value={ENCRYPTION_SOURCE.PATH}
|
||||||
|
placeholder={t('encryption_key_source_path')}
|
||||||
|
disabled={!isEnabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
name="key_source"
|
||||||
|
component={renderRadioField}
|
||||||
|
type="radio"
|
||||||
|
className="form-control mr-2"
|
||||||
|
value={ENCRYPTION_SOURCE.CONTENT}
|
||||||
|
placeholder={t('encryption_key_source_content')}
|
||||||
|
disabled={!isEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{privateKeySource === ENCRYPTION_SOURCE.CONTENT ? (
|
{privateKeySource === ENCRYPTION_SOURCE.PATH && (
|
||||||
<>
|
<Field
|
||||||
<Controller
|
|
||||||
name="private_key_saved"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
title={t('use_saved_key')}
|
|
||||||
disabled={!isEnabled}
|
|
||||||
onChange={(checked: boolean) => {
|
|
||||||
if (checked) {
|
|
||||||
setValue('private_key', '');
|
|
||||||
}
|
|
||||||
field.onChange(checked);
|
|
||||||
}}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="private_key"
|
|
||||||
control={control}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<Textarea
|
|
||||||
{...field}
|
|
||||||
placeholder={t('encryption_key_input')}
|
|
||||||
disabled={!isEnabled || privateKeySaved}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Controller
|
|
||||||
name="private_key_path"
|
name="private_key_path"
|
||||||
control={control}
|
component={renderInputField}
|
||||||
render={({ field, fieldState }) => (
|
type="text"
|
||||||
<Input
|
className="form-control"
|
||||||
{...field}
|
placeholder={t('encryption_private_key_path')}
|
||||||
type="text"
|
onChange={handleChange}
|
||||||
placeholder={t('encryption_private_key_path')}
|
disabled={!isEnabled}
|
||||||
error={fieldState.error?.message}
|
|
||||||
disabled={!isEnabled}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{privateKeySource === ENCRYPTION_SOURCE.CONTENT && [
|
||||||
|
<Field
|
||||||
|
key="private_key_saved"
|
||||||
|
name="private_key_saved"
|
||||||
|
type="checkbox"
|
||||||
|
className="form__group form__group--settings mb-2"
|
||||||
|
component={CheckboxField}
|
||||||
|
disabled={!isEnabled}
|
||||||
|
placeholder={t('use_saved_key')}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
if (event.target.checked) {
|
||||||
|
change('private_key', '');
|
||||||
|
}
|
||||||
|
if (handleChange) {
|
||||||
|
handleChange(event);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<Field
|
||||||
|
id="private_key"
|
||||||
|
key="private_key"
|
||||||
|
name="private_key"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea"
|
||||||
|
placeholder={t('encryption_key_input')}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={!isEnabled || privateKeySaved}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__status">
|
<div className="form__status">
|
||||||
@@ -572,11 +505,44 @@ export const Form = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary btn-standart"
|
className="btn btn-secondary btn-standart"
|
||||||
disabled={isSubmitting || processingConfig}
|
disabled={submitting || processingConfig}
|
||||||
onClick={clearFields}>
|
onClick={() => clearFields(change, setTlsConfig, validateTlsConfig, t)}>
|
||||||
<Trans>reset_settings</Trans>
|
<Trans>reset_settings</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector(FORM_NAME.ENCRYPTION);
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const isEnabled = selector(state, 'enabled');
|
||||||
|
const servePlainDns = selector(state, 'serve_plain_dns');
|
||||||
|
const certificateChain = selector(state, 'certificate_chain');
|
||||||
|
const privateKey = selector(state, 'private_key');
|
||||||
|
const certificatePath = selector(state, 'certificate_path');
|
||||||
|
const privateKeyPath = selector(state, 'private_key_path');
|
||||||
|
const certificateSource = selector(state, 'certificate_source');
|
||||||
|
const privateKeySource = selector(state, 'key_source');
|
||||||
|
const privateKeySaved = selector(state, 'private_key_saved');
|
||||||
|
return {
|
||||||
|
isEnabled,
|
||||||
|
servePlainDns,
|
||||||
|
certificateChain,
|
||||||
|
privateKey,
|
||||||
|
certificatePath,
|
||||||
|
privateKeyPath,
|
||||||
|
certificateSource,
|
||||||
|
privateKeySource,
|
||||||
|
privateKeySaved,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withTranslation(),
|
||||||
|
reduxForm({
|
||||||
|
form: FORM_NAME.ENCRYPTION,
|
||||||
|
validate,
|
||||||
|
}),
|
||||||
|
])(Form);
|
||||||
|
|||||||
@@ -1,60 +1,61 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { debounce } from 'lodash';
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
|
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
|
||||||
|
|
||||||
import { EncryptionFormValues, Form } from './Form';
|
import Form from './Form';
|
||||||
|
|
||||||
import Card from '../../ui/Card';
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
|
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import { EncryptionData } from '../../../initialState';
|
import { EncryptionData } from '../../../initialState';
|
||||||
|
|
||||||
type Props = {
|
interface EncryptionProps {
|
||||||
|
setTlsConfig: (...args: unknown[]) => unknown;
|
||||||
|
validateTlsConfig: (...args: unknown[]) => unknown;
|
||||||
encryption: EncryptionData;
|
encryption: EncryptionData;
|
||||||
setTlsConfig: (values: Partial<EncryptionData>) => void;
|
t: (...args: unknown[]) => string;
|
||||||
validateTlsConfig: (values: Partial<EncryptionData>) => void;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => {
|
class Encryption extends Component<EncryptionProps> {
|
||||||
const { t } = useTranslation();
|
componentDidMount() {
|
||||||
|
const { validateTlsConfig, encryption } = this.props;
|
||||||
|
|
||||||
const initialValues = useMemo((): EncryptionFormValues => {
|
if (encryption.enabled) {
|
||||||
const {
|
validateTlsConfig(encryption);
|
||||||
enabled,
|
}
|
||||||
serve_plain_dns,
|
}
|
||||||
server_name,
|
|
||||||
force_https,
|
handleFormSubmit = (values: any) => {
|
||||||
port_https,
|
const submitValues = this.getSubmitValues(values);
|
||||||
port_dns_over_tls,
|
|
||||||
port_dns_over_quic,
|
this.props.setTlsConfig(submitValues);
|
||||||
certificate_chain,
|
};
|
||||||
private_key,
|
|
||||||
certificate_path,
|
handleFormChange = debounce((values) => {
|
||||||
private_key_path,
|
const submitValues = this.getSubmitValues(values);
|
||||||
private_key_saved,
|
|
||||||
} = encryption;
|
if (submitValues.enabled) {
|
||||||
|
this.props.validateTlsConfig(submitValues);
|
||||||
|
}
|
||||||
|
}, DEBOUNCE_TIMEOUT);
|
||||||
|
|
||||||
|
getInitialValues = (data: any) => {
|
||||||
|
const { certificate_chain, private_key, private_key_saved } = data;
|
||||||
const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
||||||
const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enabled,
|
...data,
|
||||||
serve_plain_dns,
|
|
||||||
server_name,
|
|
||||||
force_https,
|
|
||||||
port_https,
|
|
||||||
port_dns_over_tls,
|
|
||||||
port_dns_over_quic,
|
|
||||||
certificate_chain,
|
|
||||||
private_key,
|
|
||||||
certificate_path,
|
|
||||||
private_key_path,
|
|
||||||
private_key_saved,
|
|
||||||
certificate_source,
|
certificate_source,
|
||||||
key_source,
|
key_source,
|
||||||
};
|
};
|
||||||
}, [encryption]);
|
};
|
||||||
|
|
||||||
const getSubmitValues = useCallback((values: any) => {
|
getSubmitValues = (values: any) => {
|
||||||
const { certificate_source, key_source, private_key_saved, ...config } = values;
|
const { certificate_source, key_source, private_key_saved, ...config } = values;
|
||||||
|
|
||||||
if (certificate_source === ENCRYPTION_SOURCE.PATH) {
|
if (certificate_source === ENCRYPTION_SOURCE.PATH) {
|
||||||
@@ -75,47 +76,63 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
|
|||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const handleFormSubmit = useCallback(
|
render() {
|
||||||
(values: any) => {
|
const { encryption, t } = this.props;
|
||||||
const submitValues = getSubmitValues(values);
|
const {
|
||||||
setTlsConfig(submitValues);
|
enabled,
|
||||||
},
|
server_name,
|
||||||
[getSubmitValues, setTlsConfig],
|
force_https,
|
||||||
);
|
port_https,
|
||||||
|
port_dns_over_tls,
|
||||||
|
port_dns_over_quic,
|
||||||
|
certificate_chain,
|
||||||
|
private_key,
|
||||||
|
certificate_path,
|
||||||
|
private_key_path,
|
||||||
|
private_key_saved,
|
||||||
|
serve_plain_dns,
|
||||||
|
} = encryption;
|
||||||
|
|
||||||
const validateConfig = useCallback((values) => {
|
const initialValues = this.getInitialValues({
|
||||||
const submitValues = getSubmitValues(values);
|
enabled,
|
||||||
|
server_name,
|
||||||
|
force_https,
|
||||||
|
port_https,
|
||||||
|
port_dns_over_tls,
|
||||||
|
port_dns_over_quic,
|
||||||
|
certificate_chain,
|
||||||
|
private_key,
|
||||||
|
certificate_path,
|
||||||
|
private_key_path,
|
||||||
|
private_key_saved,
|
||||||
|
serve_plain_dns,
|
||||||
|
});
|
||||||
|
|
||||||
if (submitValues.enabled) {
|
return (
|
||||||
validateTlsConfig(submitValues);
|
<div className="encryption">
|
||||||
}
|
<PageTitle title={t('encryption_settings')} />
|
||||||
}, []);
|
|
||||||
|
|
||||||
const debouncedConfigValidation = useMemo(() => debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]);
|
{encryption.processing && <Loading />}
|
||||||
|
{!encryption.processing && (
|
||||||
|
<Card
|
||||||
|
title={t('encryption_title')}
|
||||||
|
subtitle={t('encryption_desc')}
|
||||||
|
bodyType="card-body box-body--settings">
|
||||||
|
<Form
|
||||||
|
initialValues={initialValues}
|
||||||
|
onSubmit={this.handleFormSubmit}
|
||||||
|
onChange={this.handleFormChange}
|
||||||
|
setTlsConfig={this.props.setTlsConfig}
|
||||||
|
validateTlsConfig={this.props.validateTlsConfig}
|
||||||
|
{...this.props.encryption}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
export default withTranslation()(Encryption);
|
||||||
<div className="encryption">
|
|
||||||
<PageTitle title={t('encryption_settings')} />
|
|
||||||
|
|
||||||
{encryption.processing ? (
|
|
||||||
<Loading />
|
|
||||||
) : (
|
|
||||||
<Card
|
|
||||||
title={t('encryption_title')}
|
|
||||||
subtitle={t('encryption_desc')}
|
|
||||||
bodyType="card-body box-body--settings">
|
|
||||||
<Form
|
|
||||||
initialValues={initialValues}
|
|
||||||
onSubmit={handleFormSubmit}
|
|
||||||
debouncedConfigValidation={debouncedConfigValidation}
|
|
||||||
setTlsConfig={setTlsConfig}
|
|
||||||
validateTlsConfig={validateTlsConfig}
|
|
||||||
encryption={encryption}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
85
client/src/components/Settings/FiltersConfig/Form.tsx
Normal file
85
client/src/components/Settings/FiltersConfig/Form.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
import { CheckboxField, toNumber } from '../../../helpers/form';
|
||||||
|
import { FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK, FORM_NAME } from '../../../helpers/constants';
|
||||||
|
|
||||||
|
const getTitleForInterval = (interval: any, t: any) => {
|
||||||
|
if (interval === 0) {
|
||||||
|
return t('disabled');
|
||||||
|
}
|
||||||
|
if (interval === 72 || interval === 168) {
|
||||||
|
return t('interval_days', { count: interval / 24 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('interval_hours', { count: interval });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIntervalSelect = (processing: any, t: any, handleChange: any, toNumber: any) => (
|
||||||
|
<Field
|
||||||
|
name="interval"
|
||||||
|
className="custom-select"
|
||||||
|
component="select"
|
||||||
|
onChange={handleChange}
|
||||||
|
normalize={toNumber}
|
||||||
|
disabled={processing}>
|
||||||
|
{FILTERS_INTERVALS_HOURS.map((interval) => (
|
||||||
|
<option value={interval} key={interval}>
|
||||||
|
{getTitleForInterval(interval, t)}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface FormProps {
|
||||||
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
handleChange?: (...args: unknown[]) => unknown;
|
||||||
|
change: (...args: unknown[]) => unknown;
|
||||||
|
submitting: boolean;
|
||||||
|
invalid: boolean;
|
||||||
|
processing: boolean;
|
||||||
|
t: (...args: unknown[]) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Form = (props: FormProps) => {
|
||||||
|
const { handleSubmit, handleChange, processing, t } = props;
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name="enabled"
|
||||||
|
type="checkbox"
|
||||||
|
modifier="checkbox--settings"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('block_domain_use_filters_and_hosts')}
|
||||||
|
subtitle={<Trans components={components}>filters_block_toggle_hint</Trans>}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={processing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 col-md-5">
|
||||||
|
<div className="form__group form__group--inner mb-5">
|
||||||
|
<label className="form__label">
|
||||||
|
<Trans>filters_interval</Trans>
|
||||||
|
</label>
|
||||||
|
{getIntervalSelect(processing, t, handleChange, toNumber)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.FILTER_CONFIG })])(Form);
|
||||||
@@ -1,115 +1,39 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import i18next from 'i18next';
|
import { DEBOUNCE_TIMEOUT } from '../../../helpers/constants';
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
import { DAY, FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
|
|
||||||
import { Checkbox } from '../../ui/Controls/Checkbox';
|
|
||||||
import { Select } from '../../ui/Controls/Select';
|
|
||||||
|
|
||||||
const THREE_DAYS_INTERVAL = DAY * 3;
|
import Form from './Form';
|
||||||
const SEVEN_DAYS_INTERVAL = DAY * 7;
|
|
||||||
|
|
||||||
const getTitleForInterval = (interval: number) => {
|
import { getObjDiff } from '../../../helpers/helpers';
|
||||||
if (interval === 0) {
|
|
||||||
return i18next.t('disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interval === THREE_DAYS_INTERVAL || interval === SEVEN_DAYS_INTERVAL) {
|
interface FiltersConfigProps {
|
||||||
return i18next.t('interval_days', { count: interval / DAY });
|
initialValues: object;
|
||||||
}
|
|
||||||
|
|
||||||
return i18next.t('interval_hours', { count: interval });
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FormValues = {
|
|
||||||
enabled: boolean;
|
|
||||||
interval: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
initialValues: FormValues;
|
|
||||||
setFiltersConfig: (values: FormValues) => void;
|
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
};
|
setFiltersConfig: (...args: unknown[]) => unknown;
|
||||||
|
t: (...args: unknown[]) => string;
|
||||||
|
}
|
||||||
|
|
||||||
export const FiltersConfig = ({ initialValues, setFiltersConfig, processing }: Props) => {
|
const FiltersConfig = (props: FiltersConfigProps) => {
|
||||||
const { t } = useTranslation();
|
const { initialValues, processing } = props;
|
||||||
const prevFormValuesRef = useRef<FormValues>(initialValues);
|
|
||||||
|
|
||||||
const { watch, control } = useForm({
|
const handleFormChange = debounce((values) => {
|
||||||
mode: 'onBlur',
|
const diff = getObjDiff(initialValues, values);
|
||||||
defaultValues: initialValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
const formValues = watch();
|
if (Object.values(diff).length > 0) {
|
||||||
|
props.setFiltersConfig(values);
|
||||||
useEffect(() => {
|
|
||||||
const prevFormValues = prevFormValuesRef.current;
|
|
||||||
|
|
||||||
if (JSON.stringify(prevFormValues) !== JSON.stringify(formValues)) {
|
|
||||||
setFiltersConfig(formValues);
|
|
||||||
prevFormValuesRef.current = formValues;
|
|
||||||
}
|
}
|
||||||
}, [formValues]);
|
}, DEBOUNCE_TIMEOUT);
|
||||||
|
|
||||||
const components = {
|
|
||||||
a: <a href={FILTERS_RELATIVE_LINK} rel="noopener noreferrer" />,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Form
|
||||||
<div className="row">
|
initialValues={initialValues}
|
||||||
<div className="col-12">
|
onSubmit={handleFormChange}
|
||||||
<div className="form__group form__group--settings">
|
onChange={handleFormChange}
|
||||||
<Controller
|
processing={processing}
|
||||||
name="enabled"
|
/>
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Checkbox
|
|
||||||
{...field}
|
|
||||||
data-testid="filters_enabled"
|
|
||||||
title={t('block_domain_use_filters_and_hosts')}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<Trans components={components}>filters_block_toggle_hint</Trans>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-12 col-md-5">
|
|
||||||
<div className="form__group form__group--inner mb-5">
|
|
||||||
<label className="form__label">
|
|
||||||
<Trans>filters_interval</Trans>
|
|
||||||
</label>
|
|
||||||
<Controller
|
|
||||||
name="interval"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
data-testid="filters_interval"
|
|
||||||
disabled={processing}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}>
|
|
||||||
{FILTERS_INTERVALS_HOURS.map((interval) => (
|
|
||||||
<option value={interval} key={interval}>
|
|
||||||
{getTitleForInterval(interval)}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default withTranslation()(FiltersConfig);
|
||||||
|
|||||||
@@ -1,182 +1,147 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { change, Field, formValueSelector, reduxForm } from 'redux-form';
|
||||||
import i18next from 'i18next';
|
import { connect } from 'react-redux';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CheckboxField,
|
||||||
|
toFloatNumber,
|
||||||
|
renderTextareaField,
|
||||||
|
renderInputField,
|
||||||
|
renderRadioField,
|
||||||
|
} from '../../../helpers/form';
|
||||||
|
|
||||||
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
|
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
|
||||||
import { QUERY_LOG_INTERVALS_DAYS, HOUR, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
|
import {
|
||||||
|
FORM_NAME,
|
||||||
|
QUERY_LOG_INTERVALS_DAYS,
|
||||||
|
HOUR,
|
||||||
|
DAY,
|
||||||
|
RETENTION_CUSTOM,
|
||||||
|
RETENTION_CUSTOM_INPUT,
|
||||||
|
RETENTION_RANGE,
|
||||||
|
CUSTOM_INTERVAL,
|
||||||
|
} from '../../../helpers/constants';
|
||||||
import '../FormButton.css';
|
import '../FormButton.css';
|
||||||
import { Checkbox } from '../../ui/Controls/Checkbox';
|
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
import { Textarea } from '../../ui/Controls/Textarea';
|
|
||||||
|
|
||||||
const getIntervalTitle = (interval: number) => {
|
const getIntervalTitle = (interval: any, t: any) => {
|
||||||
switch (interval) {
|
switch (interval) {
|
||||||
case RETENTION_CUSTOM:
|
case RETENTION_CUSTOM:
|
||||||
return i18next.t('settings_custom');
|
return t('settings_custom');
|
||||||
case 6 * HOUR:
|
case 6 * HOUR:
|
||||||
return i18next.t('interval_6_hour');
|
return t('interval_6_hour');
|
||||||
case DAY:
|
case DAY:
|
||||||
return i18next.t('interval_24_hour');
|
return t('interval_24_hour');
|
||||||
default:
|
default:
|
||||||
return i18next.t('interval_days', { count: interval / DAY });
|
return t('interval_days', { count: interval / DAY });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormValues = {
|
const getIntervalFields = (processing: any, t: any, toNumber: any) =>
|
||||||
enabled: boolean;
|
QUERY_LOG_INTERVALS_DAYS.map((interval) => (
|
||||||
anonymize_client_ip: boolean;
|
<Field
|
||||||
interval: number;
|
key={interval}
|
||||||
customInterval?: number | null;
|
name="interval"
|
||||||
ignored: string;
|
type="radio"
|
||||||
};
|
component={renderRadioField}
|
||||||
|
value={interval}
|
||||||
|
placeholder={getIntervalTitle(interval, t)}
|
||||||
|
normalize={toNumber}
|
||||||
|
disabled={processing}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
type Props = {
|
interface FormProps {
|
||||||
initialValues: Partial<FormValues>;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
|
handleClear: (...args: unknown[]) => unknown;
|
||||||
|
submitting: boolean;
|
||||||
|
invalid: boolean;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
processingReset: boolean;
|
processingClear: boolean;
|
||||||
onSubmit: (values: FormValues) => void;
|
t: (...args: unknown[]) => string;
|
||||||
onReset: () => void;
|
interval?: number;
|
||||||
};
|
customInterval?: number;
|
||||||
|
dispatch: (...args: unknown[]) => unknown;
|
||||||
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
|
}
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
|
let Form = (props: FormProps) => {
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
watch,
|
submitting,
|
||||||
setValue,
|
invalid,
|
||||||
control,
|
processing,
|
||||||
formState: { isSubmitting },
|
processingClear,
|
||||||
} = useForm<FormValues>({
|
handleClear,
|
||||||
mode: 'onBlur',
|
t,
|
||||||
defaultValues: {
|
interval,
|
||||||
enabled: initialValues.enabled || false,
|
customInterval,
|
||||||
anonymize_client_ip: initialValues.anonymize_client_ip || false,
|
dispatch,
|
||||||
interval: initialValues.interval || DAY,
|
} = props;
|
||||||
customInterval: initialValues.customInterval || null,
|
|
||||||
ignored: initialValues.ignored || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const intervalValue = watch('interval');
|
|
||||||
const customIntervalValue = watch('customInterval');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (QUERY_LOG_INTERVALS_DAYS.includes(intervalValue)) {
|
if (QUERY_LOG_INTERVALS_DAYS.includes(interval)) {
|
||||||
setValue('customInterval', null);
|
dispatch(change(FORM_NAME.LOG_CONFIG, CUSTOM_INTERVAL, null));
|
||||||
}
|
}
|
||||||
}, [intervalValue]);
|
}, [interval]);
|
||||||
|
|
||||||
const onSubmitForm = (data: FormValues) => {
|
|
||||||
onSubmit(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIgnoredBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
|
||||||
const trimmed = trimLinesAndRemoveEmpty(e.target.value);
|
|
||||||
setValue('ignored', trimmed);
|
|
||||||
};
|
|
||||||
|
|
||||||
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmitForm)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="enabled"
|
name="enabled"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('query_log_enable')}
|
||||||
{...field}
|
disabled={processing}
|
||||||
data-testid="logs_enabled"
|
|
||||||
title={t('query_log_enable')}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="anonymize_client_ip"
|
name="anonymize_client_ip"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('anonymize_client_ip')}
|
||||||
{...field}
|
subtitle={t('anonymize_client_ip_desc')}
|
||||||
data-testid="logs_anonymize_client_ip"
|
disabled={processing}
|
||||||
title={t('anonymize_client_ip')}
|
|
||||||
subtitle={t('anonymize_client_ip_desc')}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__label">
|
<label className="form__label">
|
||||||
<Trans>query_log_retention</Trans>
|
<Trans>query_log_retention</Trans>
|
||||||
</div>
|
</label>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<div className="custom-controls-stacked">
|
<div className="custom-controls-stacked">
|
||||||
<label className="custom-control custom-radio">
|
<Field
|
||||||
<input
|
key={RETENTION_CUSTOM}
|
||||||
type="radio"
|
name="interval"
|
||||||
data-testid="logs_config_interval"
|
type="radio"
|
||||||
className="custom-control-input"
|
component={renderRadioField}
|
||||||
disabled={processing}
|
value={QUERY_LOG_INTERVALS_DAYS.includes(interval) ? RETENTION_CUSTOM : interval}
|
||||||
checked={!QUERY_LOG_INTERVALS_DAYS.includes(intervalValue)}
|
placeholder={getIntervalTitle(RETENTION_CUSTOM, t)}
|
||||||
value={RETENTION_CUSTOM}
|
normalize={toFloatNumber}
|
||||||
onChange={(e) => {
|
disabled={processing}
|
||||||
setValue('interval', parseInt(e.target.value, 10));
|
/>
|
||||||
}}
|
{!QUERY_LOG_INTERVALS_DAYS.includes(interval) && (
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="custom-control-label">{getIntervalTitle(RETENTION_CUSTOM)}</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{!QUERY_LOG_INTERVALS_DAYS.includes(intervalValue) && (
|
|
||||||
<div className="form__group--input">
|
<div className="form__group--input">
|
||||||
<div className="form__desc form__desc--top">{t('custom_rotation_input')}</div>
|
<div className="form__desc form__desc--top">{t('custom_rotation_input')}</div>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
name="customInterval"
|
key={RETENTION_CUSTOM_INPUT}
|
||||||
control={control}
|
name={CUSTOM_INTERVAL}
|
||||||
render={({ field, fieldState }) => (
|
type="number"
|
||||||
<Input
|
className="form-control"
|
||||||
{...field}
|
component={renderInputField}
|
||||||
data-testid="logs_config_custom_interval"
|
disabled={processing}
|
||||||
disabled={processing}
|
normalize={toFloatNumber}
|
||||||
error={fieldState.error?.message}
|
min={RETENTION_RANGE.MIN}
|
||||||
min={RETENTION_RANGE.MIN}
|
max={RETENTION_RANGE.MAX}
|
||||||
max={RETENTION_RANGE.MAX}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{getIntervalFields(processing, t, toFloatNumber)}
|
||||||
{QUERY_LOG_INTERVALS_DAYS.map((interval) => (
|
|
||||||
<label key={interval} className="custom-control custom-radio">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
className="custom-control-input"
|
|
||||||
data-testid={`logs_config_${interval}`}
|
|
||||||
disabled={processing}
|
|
||||||
value={interval}
|
|
||||||
checked={intervalValue === interval}
|
|
||||||
onChange={(e) => {
|
|
||||||
setValue('interval', parseInt(e.target.value, 10));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="custom-control-label">{getIntervalTitle(interval)}</span>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -189,41 +154,51 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="ignored"
|
name="ignored"
|
||||||
control={control}
|
type="textarea"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control form-control--textarea font-monospace text-input"
|
||||||
<Textarea
|
component={renderTextareaField}
|
||||||
{...field}
|
placeholder={t('ignore_domains')}
|
||||||
data-testid="logs_config_ingored"
|
disabled={processing}
|
||||||
placeholder={t('ignore_domains')}
|
normalizeOnBlur={trimLinesAndRemoveEmpty}
|
||||||
className="text-input"
|
|
||||||
disabled={processing}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
onBlur={handleIgnoredBlur}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="logs_config_save"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
disabled={disableSubmit}>
|
disabled={
|
||||||
|
submitting ||
|
||||||
|
invalid ||
|
||||||
|
processing ||
|
||||||
|
(!QUERY_LOG_INTERVALS_DAYS.includes(interval) && !customInterval)
|
||||||
|
}>
|
||||||
<Trans>save_btn</Trans>
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="logs_config_clear"
|
|
||||||
className="btn btn-outline-secondary btn-standard form__button"
|
className="btn btn-outline-secondary btn-standard form__button"
|
||||||
onClick={onReset}
|
onClick={() => handleClear()}
|
||||||
disabled={processingReset}>
|
disabled={processingClear}>
|
||||||
<Trans>query_log_clear</Trans>
|
<Trans>query_log_clear</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector(FORM_NAME.LOG_CONFIG);
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const interval = selector(state, 'interval');
|
||||||
|
const customInterval = selector(state, CUSTOM_INTERVAL);
|
||||||
|
return {
|
||||||
|
interval,
|
||||||
|
customInterval,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.LOG_CONFIG })])(Form);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { withTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import Card from '../../ui/Card';
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
import { Form, FormValues } from './Form';
|
import Form from './Form';
|
||||||
import { HOUR } from '../../../helpers/constants';
|
import { HOUR } from '../../../helpers/constants';
|
||||||
|
|
||||||
interface LogsConfigProps {
|
interface LogsConfigProps {
|
||||||
@@ -20,7 +20,7 @@ interface LogsConfigProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LogsConfig extends Component<LogsConfigProps> {
|
class LogsConfig extends Component<LogsConfigProps> {
|
||||||
handleFormSubmit = (values: FormValues) => {
|
handleFormSubmit = (values: any) => {
|
||||||
const { t, interval: prevInterval } = this.props;
|
const { t, interval: prevInterval } = this.props;
|
||||||
const { interval, customInterval, ...rest } = values;
|
const { interval, customInterval, ...rest } = values;
|
||||||
|
|
||||||
@@ -53,12 +53,19 @@ class LogsConfig extends Component<LogsConfigProps> {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
t,
|
t,
|
||||||
|
|
||||||
enabled,
|
enabled,
|
||||||
|
|
||||||
interval,
|
interval,
|
||||||
|
|
||||||
processing,
|
processing,
|
||||||
|
|
||||||
processingClear,
|
processingClear,
|
||||||
|
|
||||||
anonymize_client_ip,
|
anonymize_client_ip,
|
||||||
|
|
||||||
ignored,
|
ignored,
|
||||||
|
|
||||||
customInterval,
|
customInterval,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@@ -73,10 +80,10 @@ class LogsConfig extends Component<LogsConfigProps> {
|
|||||||
anonymize_client_ip,
|
anonymize_client_ip,
|
||||||
ignored: ignored?.join('\n'),
|
ignored: ignored?.join('\n'),
|
||||||
}}
|
}}
|
||||||
processing={processing}
|
|
||||||
processingReset={processingClear}
|
|
||||||
onSubmit={this.handleFormSubmit}
|
onSubmit={this.handleFormSubmit}
|
||||||
onReset={this.handleClear}
|
processing={processing}
|
||||||
|
processingClear={processingClear}
|
||||||
|
handleClear={this.handleClear}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -63,7 +63,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form__message {
|
.form__message {
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,10 +97,6 @@
|
|||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form__label {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__label--bold {
|
.form__label--bold {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,90 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { change, Field, formValueSelector, reduxForm } from 'redux-form';
|
||||||
import i18next from 'i18next';
|
import { Trans, withTranslation } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import {
|
||||||
import { STATS_INTERVALS_DAYS, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants';
|
renderRadioField,
|
||||||
|
toNumber,
|
||||||
|
CheckboxField,
|
||||||
|
renderTextareaField,
|
||||||
|
toFloatNumber,
|
||||||
|
renderInputField,
|
||||||
|
} from '../../../helpers/form';
|
||||||
|
import {
|
||||||
|
FORM_NAME,
|
||||||
|
STATS_INTERVALS_DAYS,
|
||||||
|
DAY,
|
||||||
|
RETENTION_CUSTOM,
|
||||||
|
RETENTION_CUSTOM_INPUT,
|
||||||
|
CUSTOM_INTERVAL,
|
||||||
|
RETENTION_RANGE,
|
||||||
|
} from '../../../helpers/constants';
|
||||||
|
|
||||||
|
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
|
||||||
import '../FormButton.css';
|
import '../FormButton.css';
|
||||||
import { Checkbox } from '../../ui/Controls/Checkbox';
|
|
||||||
import { Input } from '../../ui/Controls/Input';
|
|
||||||
import { toNumber } from '../../../helpers/form';
|
|
||||||
import { Textarea } from '../../ui/Controls/Textarea';
|
|
||||||
|
|
||||||
const getIntervalTitle = (interval: any) => {
|
const getIntervalTitle = (intervalMs: any, t: any) => {
|
||||||
switch (interval) {
|
switch (intervalMs) {
|
||||||
case RETENTION_CUSTOM:
|
case RETENTION_CUSTOM:
|
||||||
return i18next.t('settings_custom');
|
return t('settings_custom');
|
||||||
case DAY:
|
case DAY:
|
||||||
return i18next.t('interval_24_hour');
|
return t('interval_24_hour');
|
||||||
default:
|
default:
|
||||||
return i18next.t('interval_days', { count: interval / DAY });
|
return t('interval_days', { count: intervalMs / DAY });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FormValues = {
|
interface FormProps {
|
||||||
enabled: boolean;
|
handleSubmit: (...args: unknown[]) => string;
|
||||||
interval: number;
|
handleReset: (...args: unknown[]) => string;
|
||||||
customInterval?: number | null;
|
change: (...args: unknown[]) => unknown;
|
||||||
ignored: string;
|
submitting: boolean;
|
||||||
};
|
invalid: boolean;
|
||||||
|
|
||||||
const defaultFormValues = {
|
|
||||||
enabled: false,
|
|
||||||
interval: DAY,
|
|
||||||
customInterval: null,
|
|
||||||
ignored: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
initialValues: FormValues;
|
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
processingReset: boolean;
|
processingReset: boolean;
|
||||||
onSubmit: (values: FormValues) => void;
|
t: (...args: unknown[]) => string;
|
||||||
onReset: () => void;
|
interval?: number;
|
||||||
};
|
customInterval?: number;
|
||||||
|
dispatch: (...args: unknown[]) => unknown;
|
||||||
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
|
}
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
|
let Form = (props: FormProps) => {
|
||||||
const {
|
const {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
watch,
|
processing,
|
||||||
setValue,
|
submitting,
|
||||||
control,
|
invalid,
|
||||||
formState: { isSubmitting },
|
handleReset,
|
||||||
} = useForm<FormValues>({
|
processingReset,
|
||||||
mode: 'onBlur',
|
t,
|
||||||
defaultValues: {
|
interval,
|
||||||
...defaultFormValues,
|
customInterval,
|
||||||
...initialValues,
|
dispatch,
|
||||||
},
|
} = props;
|
||||||
});
|
|
||||||
|
|
||||||
const intervalValue = watch('interval');
|
|
||||||
const customIntervalValue = watch('customInterval');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (STATS_INTERVALS_DAYS.includes(intervalValue)) {
|
if (STATS_INTERVALS_DAYS.includes(interval)) {
|
||||||
setValue('customInterval', null);
|
dispatch(change(FORM_NAME.STATS_CONFIG, CUSTOM_INTERVAL, null));
|
||||||
}
|
}
|
||||||
}, [intervalValue]);
|
}, [interval]);
|
||||||
|
|
||||||
const onSubmitForm = (data: FormValues) => {
|
|
||||||
onSubmit(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmitForm)}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="enabled"
|
name="enabled"
|
||||||
control={control}
|
type="checkbox"
|
||||||
render={({ field }) => (
|
component={CheckboxField}
|
||||||
<Checkbox
|
placeholder={t('statistics_enable')}
|
||||||
{...field}
|
disabled={processing}
|
||||||
data-testid="stats_config_enabled"
|
|
||||||
title={t('statistics_enable')}
|
|
||||||
disabled={processing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__label form__label--with-desc">
|
<label className="form__label form__label--with-desc">
|
||||||
<Trans>statistics_retention</Trans>
|
<Trans>statistics_retention</Trans>
|
||||||
</div>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
<Trans>statistics_retention_desc</Trans>
|
<Trans>statistics_retention_desc</Trans>
|
||||||
@@ -103,105 +92,85 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
|
|||||||
|
|
||||||
<div className="form__group form__group--settings mt-2">
|
<div className="form__group form__group--settings mt-2">
|
||||||
<div className="custom-controls-stacked">
|
<div className="custom-controls-stacked">
|
||||||
<label className="custom-control custom-radio">
|
<Field
|
||||||
<input
|
key={RETENTION_CUSTOM}
|
||||||
type="radio"
|
name="interval"
|
||||||
data-testid="stats_config_interval"
|
type="radio"
|
||||||
className="custom-control-input"
|
component={renderRadioField}
|
||||||
disabled={processing}
|
value={STATS_INTERVALS_DAYS.includes(interval) ? RETENTION_CUSTOM : interval}
|
||||||
checked={!STATS_INTERVALS_DAYS.includes(intervalValue)}
|
placeholder={getIntervalTitle(RETENTION_CUSTOM, t)}
|
||||||
value={RETENTION_CUSTOM}
|
normalize={toFloatNumber}
|
||||||
onChange={(e) => {
|
disabled={processing}
|
||||||
setValue('interval', parseInt(e.target.value, 10));
|
/>
|
||||||
}}
|
{!STATS_INTERVALS_DAYS.includes(interval) && (
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="custom-control-label">{getIntervalTitle(RETENTION_CUSTOM)}</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{!STATS_INTERVALS_DAYS.includes(intervalValue) && (
|
|
||||||
<div className="form__group--input">
|
<div className="form__group--input">
|
||||||
<div className="form__desc form__desc--top">{i18next.t('custom_retention_input')}</div>
|
<div className="form__desc form__desc--top">{t('custom_retention_input')}</div>
|
||||||
|
|
||||||
<Controller
|
<Field
|
||||||
name="customInterval"
|
key={RETENTION_CUSTOM_INPUT}
|
||||||
control={control}
|
name={CUSTOM_INTERVAL}
|
||||||
render={({ field, fieldState }) => (
|
type="number"
|
||||||
<Input
|
className="form-control"
|
||||||
{...field}
|
component={renderInputField}
|
||||||
data-testid="stats_config_custom_interval"
|
disabled={processing}
|
||||||
disabled={processing}
|
normalize={toFloatNumber}
|
||||||
error={fieldState.error?.message}
|
min={RETENTION_RANGE.MIN}
|
||||||
min={RETENTION_RANGE.MIN}
|
max={RETENTION_RANGE.MAX}
|
||||||
max={RETENTION_RANGE.MAX}
|
|
||||||
onChange={(e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
field.onChange(toNumber(value));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{STATS_INTERVALS_DAYS.map((interval) => (
|
{STATS_INTERVALS_DAYS.map((interval) => (
|
||||||
<label key={interval} className="custom-control custom-radio">
|
<Field
|
||||||
<input
|
key={interval}
|
||||||
type="radio"
|
name="interval"
|
||||||
className="custom-control-input"
|
type="radio"
|
||||||
disabled={processing}
|
component={renderRadioField}
|
||||||
value={interval}
|
value={interval}
|
||||||
checked={intervalValue === interval}
|
placeholder={getIntervalTitle(interval, t)}
|
||||||
onChange={(e) => {
|
normalize={toNumber}
|
||||||
setValue('interval', parseInt(e.target.value, 10));
|
disabled={processing}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="custom-control-label">{getIntervalTitle(interval)}</span>
|
|
||||||
</label>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__label form__label--with-desc">
|
<label className="form__label form__label--with-desc">
|
||||||
<Trans>ignore_domains_title</Trans>
|
<Trans>ignore_domains_title</Trans>
|
||||||
</div>
|
</label>
|
||||||
|
|
||||||
<div className="form__desc form__desc--top">
|
<div className="form__desc form__desc--top">
|
||||||
<Trans>ignore_domains_desc_stats</Trans>
|
<Trans>ignore_domains_desc_stats</Trans>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<Controller
|
<Field
|
||||||
name="ignored"
|
name="ignored"
|
||||||
control={control}
|
type="textarea"
|
||||||
render={({ field, fieldState }) => (
|
className="form-control form-control--textarea font-monospace text-input"
|
||||||
<Textarea
|
component={renderTextareaField}
|
||||||
{...field}
|
placeholder={t('ignore_domains')}
|
||||||
data-testid="stats_config_ignored"
|
disabled={processing}
|
||||||
placeholder={t('ignore_domains')}
|
normalizeOnBlur={trimLinesAndRemoveEmpty}
|
||||||
className="text-input"
|
|
||||||
disabled={processing}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
trimOnBlur
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="stats_config_save"
|
|
||||||
className="btn btn-success btn-standard btn-large"
|
className="btn btn-success btn-standard btn-large"
|
||||||
disabled={disableSubmit}>
|
disabled={
|
||||||
|
submitting ||
|
||||||
|
invalid ||
|
||||||
|
processing ||
|
||||||
|
(!STATS_INTERVALS_DAYS.includes(interval) && !customInterval)
|
||||||
|
}>
|
||||||
<Trans>save_btn</Trans>
|
<Trans>save_btn</Trans>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
data-testid="stats_config_clear"
|
|
||||||
className="btn btn-outline-secondary btn-standard form__button"
|
className="btn btn-outline-secondary btn-standard form__button"
|
||||||
onClick={onReset}
|
onClick={() => handleReset()}
|
||||||
disabled={processingReset}>
|
disabled={processingReset}>
|
||||||
<Trans>statistics_clear</Trans>
|
<Trans>statistics_clear</Trans>
|
||||||
</button>
|
</button>
|
||||||
@@ -209,3 +178,16 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR
|
|||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector(FORM_NAME.STATS_CONFIG);
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const interval = selector(state, 'interval');
|
||||||
|
const customInterval = selector(state, CUSTOM_INTERVAL);
|
||||||
|
return {
|
||||||
|
interval,
|
||||||
|
customInterval,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([withTranslation(), reduxForm({ form: FORM_NAME.STATS_CONFIG })])(Form);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user