Compare commits
49 Commits
v0.103.0-b
...
v0.103.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2bf1e176e | ||
|
|
ffeb88ac0c | ||
|
|
c71b8d3ad2 | ||
|
|
01957bf503 | ||
|
|
1e5419714d | ||
|
|
4f4a688ee6 | ||
|
|
ccf5494f67 | ||
|
|
f2edcca54b | ||
|
|
b4aa791513 | ||
|
|
6d5d183311 | ||
|
|
e3ea2528be | ||
|
|
117ec4dd43 | ||
|
|
0cc0aec5b3 | ||
|
|
3c53a2162c | ||
|
|
1bb183c2aa | ||
|
|
62ccd3fb41 | ||
|
|
a409cdc2bb | ||
|
|
e0aa24e2b7 | ||
|
|
0662769696 | ||
|
|
dc237f10a8 | ||
|
|
4fef0c32cb | ||
|
|
c131ac445a | ||
|
|
87789679f5 | ||
|
|
793194db67 | ||
|
|
4175d82279 | ||
|
|
c1e56c837b | ||
|
|
7d2c7a61f1 | ||
|
|
d794b11e7a | ||
|
|
82858474a5 | ||
|
|
4df02714fd | ||
|
|
1bce871fcb | ||
|
|
2d7be0a1e0 | ||
|
|
177404d157 | ||
|
|
e46db985e8 | ||
|
|
38366ba801 | ||
|
|
a32c1f2ee0 | ||
|
|
95f4128551 | ||
|
|
61981a927b | ||
|
|
54693bb158 | ||
|
|
d38b58cd85 | ||
|
|
b9fca8d0a9 | ||
|
|
da4a1ec23d | ||
|
|
8a417604a9 | ||
|
|
2759d81afe | ||
|
|
44aad1515a | ||
|
|
af5cb5aa5d | ||
|
|
0297c12911 | ||
|
|
5e0fe8ba3f | ||
|
|
8863e61e8e |
12
.githooks/pre-commit
Executable file
12
.githooks/pre-commit
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e;
|
||||
git diff --cached --name-only | grep -q '.js$' && make lint-js;
|
||||
|
||||
found=0
|
||||
git diff --cached --name-only | grep -q '.go$' && found=1
|
||||
if [ $found == 1 ]; then
|
||||
make lint-go || exit 1
|
||||
go test ./... || exit 1
|
||||
fi
|
||||
|
||||
exit 0;
|
||||
@@ -10,8 +10,7 @@ before:
|
||||
- go generate ./...
|
||||
|
||||
builds:
|
||||
-
|
||||
main: ./main.go
|
||||
- main: ./main.go
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Version}} -X main.channel={{.Env.CHANNEL}} -X main.goarm={{.Env.GOARM}}
|
||||
env:
|
||||
@@ -43,8 +42,7 @@ builds:
|
||||
goarch: mipsle
|
||||
|
||||
archives:
|
||||
-
|
||||
# Archive name template.
|
||||
- # Archive name template.
|
||||
# Defaults:
|
||||
# - if format is `tar.gz`, `tar.xz`, `gz` or `zip`:
|
||||
# - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
|
||||
@@ -62,8 +60,7 @@ archives:
|
||||
- README.md
|
||||
|
||||
snapcrafts:
|
||||
-
|
||||
name: adguard-home
|
||||
- name: adguard-home
|
||||
base: core18
|
||||
name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
summary: Network-wide ads & trackers blocking DNS server
|
||||
@@ -80,11 +77,24 @@ snapcrafts:
|
||||
confinement: strict
|
||||
publish: false
|
||||
license: GPL-3.0
|
||||
extra_files:
|
||||
- source: scripts/snap/local/adguard-home-web.sh
|
||||
destination: adguard-home-web.sh
|
||||
mode: 0755
|
||||
- source: scripts/snap/gui/adguard-home-web.desktop
|
||||
destination: meta/gui/adguard-home-web.desktop
|
||||
mode: 0644
|
||||
- source: scripts/snap/gui/adguard-home-web.png
|
||||
destination: meta/gui/adguard-home-web.png
|
||||
mode: 0644
|
||||
apps:
|
||||
adguard-home:
|
||||
command: AdGuardHome -w $SNAP_DATA --no-check-update
|
||||
plugs: [ network-bind ]
|
||||
daemon: simple
|
||||
adguard-home-web:
|
||||
command: adguard-home-web.sh
|
||||
plugs: [ desktop ]
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
@@ -344,10 +344,14 @@ Response:
|
||||
|
||||
If `can_autoupdate` is true, then the server can automatically upgrade to a new version.
|
||||
|
||||
Response with empty body:
|
||||
Response when auto-update is disabled by command-line argument:
|
||||
|
||||
200 OK
|
||||
|
||||
{
|
||||
"disabled":true
|
||||
}
|
||||
|
||||
It means that update check is disabled by user. UI should do nothing.
|
||||
|
||||
|
||||
|
||||
65
Makefile
65
Makefile
@@ -31,7 +31,7 @@ DIST_DIR=dist
|
||||
CHANNEL ?= edge
|
||||
|
||||
# Validate channel
|
||||
ifneq ($(CHANNEL),relese)
|
||||
ifneq ($(CHANNEL),release)
|
||||
ifneq ($(CHANNEL),beta)
|
||||
ifneq ($(CHANNEL),edge)
|
||||
$(error CHANNEL value is not valid. Valid values are release,beta or edge)
|
||||
@@ -55,7 +55,11 @@ SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
|
||||
# Set proper version
|
||||
VERSION=
|
||||
ifeq ($(TAG_NAME),$(shell git describe --abbrev=4))
|
||||
VERSION=$(RELEASE_VERSION)
|
||||
ifeq ($(CHANNEL),edge)
|
||||
VERSION=$(SNAPSHOT_VERSION)
|
||||
else
|
||||
VERSION=$(RELEASE_VERSION)
|
||||
endif
|
||||
else
|
||||
VERSION=$(SNAPSHOT_VERSION)
|
||||
endif
|
||||
@@ -88,11 +92,14 @@ ifndef DOCKER_IMAGE_NAME
|
||||
$(error DOCKER_IMAGE_NAME value is not set)
|
||||
endif
|
||||
|
||||
.PHONY: all build client client-watch docker lint test dependencies clean release docker-multi-arch
|
||||
.PHONY: all build client client-watch docker lint lint-js lint-go test dependencies clean release docker-multi-arch
|
||||
all: build
|
||||
|
||||
init:
|
||||
git config core.hooksPath .githooks
|
||||
|
||||
build: dependencies client
|
||||
go generate ./...
|
||||
PATH=$(GOPATH)/bin:$(PATH) go generate ./...
|
||||
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||
|
||||
@@ -116,11 +123,16 @@ docker:
|
||||
@echo Now you can run the docker image:
|
||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||
|
||||
lint:
|
||||
@echo Running linters
|
||||
golangci-lint run ./...
|
||||
lint: lint-js lint-go
|
||||
|
||||
lint-js:
|
||||
@echo Running js linter
|
||||
npm --prefix client run lint
|
||||
|
||||
lint-go:
|
||||
@echo Running go linter
|
||||
golangci-lint run
|
||||
|
||||
test:
|
||||
@echo Running unit-tests
|
||||
go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
@@ -165,6 +177,7 @@ docker-multi-arch:
|
||||
release: dependencies client
|
||||
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
||||
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
||||
$(call repack_dist)
|
||||
$(call write_version_file,$(VERSION))
|
||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||
|
||||
@@ -183,7 +196,7 @@ define write_version_file
|
||||
echo " \"version\": \"$(version)\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"announcement\": \"AdGuard Home $(version) is now available!\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"selfupdate_min_version\": \"v0.0\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"selfupdate_min_version\": \"0.0\"," >> $(DIST_DIR)/version.json
|
||||
|
||||
# Windows builds
|
||||
echo " \"download_windows_amd64\": \"$(BASE_URL)/AdGuardHome_windows_amd64.zip\"," >> $(DIST_DIR)/version.json
|
||||
@@ -223,4 +236,38 @@ define write_version_file
|
||||
|
||||
# Finish
|
||||
echo "}" >> $(DIST_DIR)/version.json
|
||||
endef
|
||||
endef
|
||||
|
||||
define repack_dist
|
||||
# Repack archive files
|
||||
# A temporary solution for our auto-update code to be able to unpack these archive files
|
||||
# The problem is that goreleaser doesn't add directory AdGuardHome/ to the archive file
|
||||
# and we can't create it
|
||||
rm -rf $(DIST_DIR)/AdGuardHome
|
||||
|
||||
# Linux
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_amd64.tar.gz && tar czf AdGuardHome_linux_amd64.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_386.tar.gz && tar czf AdGuardHome_linux_386.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
|
||||
# Linux, all kinds of ARM
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_armv5.tar.gz && tar czf AdGuardHome_linux_armv5.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_armv6.tar.gz && tar czf AdGuardHome_linux_armv6.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_armv7.tar.gz && tar czf AdGuardHome_linux_armv7.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_arm64.tar.gz && tar czf AdGuardHome_linux_arm64.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
|
||||
# Linux, MIPS
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_mips_softfloat.tar.gz && tar czf AdGuardHome_linux_mips_softfloat.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_mipsle_softfloat.tar.gz && tar czf AdGuardHome_linux_mipsle_softfloat.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_mips64_softfloat.tar.gz && tar czf AdGuardHome_linux_mips64_softfloat.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_linux_mips64le_softfloat.tar.gz && tar czf AdGuardHome_linux_mips64le_softfloat.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
|
||||
# FreeBSD
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_386.tar.gz && tar czf AdGuardHome_freebsd_386.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_amd64.tar.gz && tar czf AdGuardHome_freebsd_amd64.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
|
||||
# FreeBSD, all kinds of ARM
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_armv5.tar.gz && tar czf AdGuardHome_freebsd_armv5.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_armv6.tar.gz && tar czf AdGuardHome_freebsd_armv6.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_armv7.tar.gz && tar czf AdGuardHome_freebsd_armv7.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
cd $(DIST_DIR) && tar xzf AdGuardHome_freebsd_arm64.tar.gz && tar czf AdGuardHome_freebsd_arm64.tar.gz AdGuardHome/ && rm -rf AdGuardHome
|
||||
endef
|
||||
|
||||
13
README.md
13
README.md
@@ -150,11 +150,14 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Run `make init` to prepare the development environment.
|
||||
|
||||
You will need this to build AdGuard Home:
|
||||
|
||||
* [go](https://golang.org/dl/) v1.14 or later.
|
||||
* [node.js](https://nodejs.org/en/download/) v10 or later.
|
||||
|
||||
* [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
|
||||
### Building
|
||||
|
||||
Open Terminal and execute these commands:
|
||||
@@ -167,6 +170,14 @@ make
|
||||
|
||||
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
||||
|
||||
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Golang project.
|
||||
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
||||
|
||||
For example:
|
||||
```
|
||||
GOOS=linux GOARCH=arm64 make
|
||||
```
|
||||
|
||||
#### Preparing release
|
||||
|
||||
You'll need this to prepare a release build:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
"disableEmoji": true,
|
||||
"list": [
|
||||
"+",
|
||||
"*",
|
||||
"-",
|
||||
"+ ",
|
||||
"* ",
|
||||
"- ",
|
||||
],
|
||||
"maxMessageLength": 64,
|
||||
"minMessageLength": 3,
|
||||
@@ -12,7 +12,7 @@ module.exports = {
|
||||
"scope",
|
||||
"subject",
|
||||
"body",
|
||||
"issues"
|
||||
"issues",
|
||||
],
|
||||
"scopes": [
|
||||
"",
|
||||
@@ -26,20 +26,20 @@ module.exports = {
|
||||
"documentation",
|
||||
],
|
||||
"types": {
|
||||
"+": {
|
||||
"+ ": {
|
||||
"description": "A new feature",
|
||||
"emoji": "",
|
||||
"value": "+"
|
||||
"value": "+ "
|
||||
},
|
||||
"*": {
|
||||
"* ": {
|
||||
"description": "A code change that neither fixes a bug or adds a feature",
|
||||
"emoji": "",
|
||||
"value": "*"
|
||||
"value": "* "
|
||||
},
|
||||
"-": {
|
||||
"- ": {
|
||||
"description": "A bug fix",
|
||||
"emoji": "",
|
||||
"value": "-"
|
||||
"value": "- "
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
67
client/package-lock.json
generated
vendored
67
client/package-lock.json
generated
vendored
@@ -1909,6 +1909,11 @@
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||
@@ -3097,6 +3102,11 @@
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||
@@ -4818,8 +4828,7 @@
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.1.1",
|
||||
@@ -11044,6 +11053,24 @@
|
||||
"prepend-http": "^1.0.0",
|
||||
"query-string": "^4.1.0",
|
||||
"sort-keys": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"query-string": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"npm-run-path": {
|
||||
@@ -12138,13 +12165,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"query-string": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
||||
"dev": true,
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
|
||||
"integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
|
||||
"requires": {
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"split-on-first": "^1.0.0",
|
||||
"strict-uri-encode": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"querystring": {
|
||||
@@ -13455,6 +13482,18 @@
|
||||
"is-data-descriptor": "^1.0.0",
|
||||
"kind-of": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13696,6 +13735,11 @@
|
||||
"integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==",
|
||||
"dev": true
|
||||
},
|
||||
"split-on-first": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
|
||||
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
|
||||
},
|
||||
"split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
@@ -13833,10 +13877,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "4.0.1",
|
||||
|
||||
1
client/package.json
vendored
1
client/package.json
vendored
@@ -22,6 +22,7 @@
|
||||
"lodash": "^4.17.15",
|
||||
"nanoid": "^3.1.9",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-click-outside": "^3.0.1",
|
||||
"react-dom": "^16.13.1",
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="google" content="notranslate">
|
||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="google" content="notranslate">
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="google" content="notranslate">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||
<title>Login</title>
|
||||
|
||||
@@ -236,5 +236,6 @@
|
||||
"reset_settings": "Изтрий всички настройки",
|
||||
"update_announcement": "Има нова AdGuard Home {{version}}! <0>Цъкни тук</0> за повече информация.",
|
||||
"disable_ipv6": "Изключете IPv6 протокола",
|
||||
"show_blocked_responses": "Блокирано"
|
||||
"show_blocked_responses": "Блокирано",
|
||||
"port_53_faq_link": "Порт 53 често е зает от \"DNSStubListener\" или \"systemd-resolved\" услуги. Моля, прочетете <0>тази инструкция</0> как да решите това."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nový seznam povolených",
|
||||
"edit_blocklist": "Upravit seznam blokovaných",
|
||||
"edit_allowlist": "Upravit seznam povolených",
|
||||
"choose_blocklist": "Vyberte seznamy zakázaných",
|
||||
"choose_allowlist": "Vyberte seznamy povolených",
|
||||
"enter_valid_blocklist": "Zadejte platnou adresu URL na seznam blokovaných.",
|
||||
"enter_valid_allowlist": "Zadejte platnou adresu URL na seznam povolených.",
|
||||
"form_error_url_format": "Neplatný formát URL",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonymizovat IP klienta",
|
||||
"anonymize_client_ip_desc": "Neukládat úplnou IP adresu klienta do protokolů a statistik",
|
||||
"dns_config": "Konfigurace DNS serveru",
|
||||
"dns_cache_config": "Konfigurace mezipaměti DNS",
|
||||
"dns_cache_config_desc": "Zde můžete konfigurovat mezipaměť DNS",
|
||||
"blocking_mode": "Režim blokování",
|
||||
"default": "Výchozí",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Doména",
|
||||
"answer": "Odpověď",
|
||||
"filter_added_successfully": "Seznam byl úspěšně přidán",
|
||||
"filter_removed_successfully": "Seznam byl úspěšně odstraněn",
|
||||
"filter_updated": "Seznam byl úspěšně aktualizován",
|
||||
"statistics_configuration": "Konfigurace statistik",
|
||||
"statistics_retention": "Uchovávání statistik",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Další informace</0> o vytváření vlastních seznamů hostitelů.",
|
||||
"blocked_by_response": "Zakázáno dle CNAME nebo IP v odpovědi",
|
||||
"blocked_by_cname_or_ip": "Zakázáno dle CNAME nebo IP",
|
||||
"try_again": "Zkusit znovu",
|
||||
"domain_desc": "Zadejte zástupný znak nebo název domény, kterou chcete přepsat.",
|
||||
"example_rewrite_domain": "přepsat odpovědi pouze pro tento název domény.",
|
||||
"example_rewrite_wildcard": "přepsat odpovědi pro všechny subdomény <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP address: použít tuto IP adresu v odpovědi typu A nebo AAAA",
|
||||
"rewrite_domain_name": "Název domény: Přidat záznam CNAME",
|
||||
"rewrite_A": "<0>A</0>: speciální hodnota, udržet záznamy typu <0>A</0> z odchozího serveru",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: speciální hodnota, udržet záznamy typu <0>AAAA</0> z odchozího serveru",
|
||||
"disable_ipv6": "Zakázat IPv6",
|
||||
"disable_ipv6_desc": "Pokud je tato funkce povolena, budou všechny dotazy DNS pro adresy IPv6 (typ AAAA) zrušeny.",
|
||||
"fastest_addr": "Nejrychlejší IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Zkontrolovat",
|
||||
"form_enter_host": "Zadejte název hostitele",
|
||||
"filtered_custom_rules": "Filtrováno pomocí vlastních pravidel filtrování",
|
||||
"choose_from_list": "Vybrat ze seznamu",
|
||||
"add_custom_list": "Přidat vlastní seznam",
|
||||
"host_whitelisted": "Hostitel je na seznamu povolených",
|
||||
"check_ip": "IP adresy: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Zapnout DNSSEC",
|
||||
"dnssec_enable_desc": "Nastavte příznak DNSSEC v následujících DNS dotazech a zkontrolujte výsledek (je potřebný překladač se zapnutým DNSSEC)",
|
||||
"validated_with_dnssec": "Ověřeno pomocí DNSSEC",
|
||||
"show_all_responses": "Všechny odpovědi",
|
||||
"all_queries": "Všechny dotazy",
|
||||
"show_blocked_responses": "Zablokované",
|
||||
"show_whitelisted_responses": "Povolené",
|
||||
"show_processed_responses": "Zpracovaný",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Přepsáno",
|
||||
"safe_search": "Bezpečné vyhledávání",
|
||||
"blocklist": "Zakázaný",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Velikost mezipaměti",
|
||||
"cache_size_desc": "Velikost mezipaměti DNS (v bajtech)",
|
||||
"cache_ttl_min_override": "Přepsat minimální hodnotu TTL",
|
||||
"cache_ttl_max_override": "Přepsat maximální hodnotu TTL",
|
||||
"enter_cache_size": "Zadejte velikost mezipaměti",
|
||||
"enter_cache_ttl_min_override": "Zadejte minimální hodnotu TTL",
|
||||
"enter_cache_ttl_max_override": "Zadejte maximální hodnotu TTL",
|
||||
"cache_ttl_min_override_desc": "Přepište hodnotu TTL (minimální) obdrženou z odchozího serveru. Tato hodnota nemůže být větší než 3600 (1 hodina)",
|
||||
"cache_ttl_max_override_desc": "Přepište hodnotu TTL (maximální) obdrženou z odchozího serveru",
|
||||
"min_exceeds_max_value": "Minimální hodnota přesahuje maximální hodnotu",
|
||||
"value_not_larger_than": "Hodnota nesmí být vyšší než {{maximum}}",
|
||||
"filter_category_general": "Obecné",
|
||||
"filter_category_security": "Bezpečnost",
|
||||
"filter_category_regional": "Regionální",
|
||||
"filter_category_other": "Ostatní",
|
||||
"filter_category_general_desc": "Seznamy, které blokují slídiče a reklamu na většině zařízení",
|
||||
"filter_category_security_desc": "Seznamy, které se specializují na blokování škodlivého software, zákeřných útoků nebo podvodných domén",
|
||||
"filter_category_regional_desc": "Seznamy, které jsou zaměřené na regionální reklamy a sledovací servery",
|
||||
"filter_category_other_desc": "Další seznamy zakázaných",
|
||||
"original_response": "Původní odezva",
|
||||
"click_to_view_queries": "Klikněte pro zobrazení dotazů",
|
||||
"port_53_faq_link": "Port 53 je často obsazen službami \"DNSStubListener\" nebo \"systemd-resolved\". Přečtěte si <0>tento návod</0> o tom, jak to vyřešit."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Ny liste over tilladte",
|
||||
"edit_blocklist": "Rediger blokeringsliste",
|
||||
"edit_allowlist": "Rediger liste over tilladte",
|
||||
"choose_blocklist": "Vælg blokeringslister",
|
||||
"choose_allowlist": "Vælg lister over tilladte",
|
||||
"enter_valid_blocklist": "Indtast en gyldig URL til blokeringslisten.",
|
||||
"enter_valid_allowlist": "Indtast en gyldig URL til listen over tilladte.",
|
||||
"form_error_url_format": "Ugyldigt URL-format",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonymiser klient-IP",
|
||||
"anonymize_client_ip_desc": "Gem ikke klientens fulde IP-adresse i logfiler og statistikker",
|
||||
"dns_config": "DNS-serverkonfiguration",
|
||||
"dns_cache_config": "Konfiguration af DNS-cache",
|
||||
"dns_cache_config_desc": "Her kan du konfigurere DNS-cache",
|
||||
"blocking_mode": "Blokeringstilstand",
|
||||
"default": "Standard",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domæne",
|
||||
"answer": "Svar",
|
||||
"filter_added_successfully": "Listen er tilføjet",
|
||||
"filter_removed_successfully": "Listen er blevet fjernet",
|
||||
"filter_updated": "Listen er blevet opdateret",
|
||||
"statistics_configuration": "Konfiguration af statistik",
|
||||
"statistics_retention": "Tilbageholdelse af statistikker",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Lær mere</0> om at oprette dine egne værtslister.",
|
||||
"blocked_by_response": "Blokeret af CNAME eller IP som svar",
|
||||
"blocked_by_cname_or_ip": "Blokeret af CNAME eller IP",
|
||||
"try_again": "Prøv igen",
|
||||
"domain_desc": "Indtast det domænenavn eller wildcard, du ønsker skal omskrives.",
|
||||
"example_rewrite_domain": "omskriv kun svar for dette domænenavn.",
|
||||
"example_rewrite_wildcard": "omskriv svar for alle <0>example.org</0> subdomæner.",
|
||||
"rewrite_ip_address": "IP-adresse: brug denne IP i et A- eller AAAA-svar",
|
||||
"rewrite_domain_name": "Domænenavn: tilføj en CNAME-post",
|
||||
"rewrite_A": "<0>A</0>: særlig værdi, hold <0>A</0> poster fra upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: særlig værdi, hold <0>AAAA</0> poster fra upstream",
|
||||
"disable_ipv6": "Deaktiver IPv6",
|
||||
"disable_ipv6_desc": "Hvis denne funktion er aktiveret, slettes alle DNS-forespørgsler til IPv6-adresser (type AAAA).",
|
||||
"fastest_addr": "Hurtigste IP-adresse",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Kontroller",
|
||||
"form_enter_host": "Indtast et værtsnavn",
|
||||
"filtered_custom_rules": "Filtreret af brugerdefinerede filtreringsregler",
|
||||
"choose_from_list": "Vælg fra listen",
|
||||
"add_custom_list": "Tilføj en tilpasset liste",
|
||||
"host_whitelisted": "Værten er hvidlistet",
|
||||
"check_ip": "IP-adresser: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Aktivér DNSSEC",
|
||||
"dnssec_enable_desc": "Sæt DNSSEC-flag i de udgående DNS-forespørgsler, og kontroller resultatet (DNSSEC-aktiveret resolver er krævet)",
|
||||
"validated_with_dnssec": "Valideret med DNSSEC",
|
||||
"show_all_responses": "Alle svar",
|
||||
"all_queries": "Alle forespørgsler",
|
||||
"show_blocked_responses": "Blokeret",
|
||||
"show_whitelisted_responses": "Hvidlistet",
|
||||
"show_processed_responses": "Behandlet",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Omskrevet",
|
||||
"safe_search": "Sikker søgning",
|
||||
"blocklist": "Blokeringsliste",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Cache-størrelse",
|
||||
"cache_size_desc": "Størrelse på DNS-cache (i bytes)",
|
||||
"cache_ttl_min_override": "Overskriv minimum TTL",
|
||||
"cache_ttl_max_override": "Overskriv maksimal TTL",
|
||||
"enter_cache_size": "Indtast cache-størrelse",
|
||||
"enter_cache_ttl_min_override": "Indtast minimum TTL",
|
||||
"enter_cache_ttl_max_override": "Indtast maksimum TTL",
|
||||
"cache_ttl_min_override_desc": "Overskriv TTL-værdi (minimum) modtaget fra upstream-serveren. Denne værdi kan ikke være større end 3600 (1 time)",
|
||||
"cache_ttl_max_override_desc": "Overskriv TTL-værdi (maksimum) modtaget fra upstream-serveren",
|
||||
"min_exceeds_max_value": "Minimumsværdien overstiger maksimumværdien",
|
||||
"value_not_larger_than": "Værdien kan ikke være større end {{maximum}}",
|
||||
"filter_category_general": "Generelt",
|
||||
"filter_category_security": "Sikkerhed",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Andre",
|
||||
"filter_category_general_desc": "Lister der blokerer for sporing og reklamer på de fleste enheder",
|
||||
"filter_category_security_desc": "Lister, der er specialiserede i at blokere malware, phishing eller scam-domæner",
|
||||
"filter_category_regional_desc": "Lister, der fokuserer på regionale annoncer og tracking-servere",
|
||||
"filter_category_other_desc": "Andre blokeringslister",
|
||||
"original_response": "Oprindeligt svar",
|
||||
"click_to_view_queries": "Klik for at se forespørgsler",
|
||||
"port_53_faq_link": "Port 53 optages ofte af \"DNSStubListener\" eller \"systemd-resolved\" tjenester. Læs <0>denne instruktion</0> om, hvordan du løser dette."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Neue Freigabeliste",
|
||||
"edit_blocklist": "Sperrliste bearbeiten",
|
||||
"edit_allowlist": "Freigabeliste bearbeiten",
|
||||
"choose_blocklist": "Sperrliste wählen",
|
||||
"choose_allowlist": "Zulassungsliste wählen",
|
||||
"enter_valid_blocklist": "Geben Sie eine gültige Adresse in die Sperrliste ein.",
|
||||
"enter_valid_allowlist": "Geben Sie eine gültige Adresse in die Freigabeliste ein.",
|
||||
"form_error_url_format": "Ungültiges URL-Format",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Client-IP anonymisieren",
|
||||
"anonymize_client_ip_desc": "Vollständige IP-Adresse des Clients nicht in Protokollen und Statistiken speichern",
|
||||
"dns_config": "DNS-Serverkonfiguration",
|
||||
"dns_cache_config": "Konfiguration des DNS-Zwischenspeicher",
|
||||
"dns_cache_config_desc": "Hier können Sie den DNS-Zwischenspeicher konfigurieren",
|
||||
"blocking_mode": "Sperrmodus",
|
||||
"default": "Standard",
|
||||
"nxdomain": "NXDomain",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domain",
|
||||
"answer": "Antwort",
|
||||
"filter_added_successfully": "Der Filter wurde erfolgreich hinzugefügt",
|
||||
"filter_removed_successfully": "Der Filter wurde erfolgreich entfernt",
|
||||
"filter_updated": "Der Filter wurde erfolgreich aktualisiert",
|
||||
"statistics_configuration": "Statistikkonfiguration",
|
||||
"statistics_retention": "Statistiken speichern",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Erfahren Sie mehr</0> über die Erstellung eigener Hosts-Listen.",
|
||||
"blocked_by_response": "Nach CNAME oder IP-Antwort blockiert",
|
||||
"blocked_by_cname_or_ip": "Gesperrt durch CNAME oder IP",
|
||||
"try_again": "Erneut versuchen",
|
||||
"domain_desc": "Geben Sie den Domain-Namen oder den Platzhalter ein, der umgeschrieben werden soll.",
|
||||
"example_rewrite_domain": "Antworten nur für diesen Domain-Namen umschreiben.",
|
||||
"example_rewrite_wildcard": "Antworten nur für alle <0>example.org</0> Subdomains umschreiben.",
|
||||
"rewrite_ip_address": "IP-Adresse: Verwenden Sie diese IP in einer A- oder AAAA-Antwort",
|
||||
"rewrite_domain_name": "Domänenname: einen CNAME-Eintrag hinzufügen",
|
||||
"rewrite_A": "<0>A</0>: spezieller Wert, <0>A</0>-Datensätze des \n vorgeschalteten Servers beibehalten",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: spezieller Wert, <0>AAAA</0>-Datensätze des vorgeschalteten Servers beibehalten",
|
||||
"disable_ipv6": "IPv6 deaktivieren",
|
||||
"disable_ipv6_desc": "Wenn diese Funktion aktiviert ist, werden alle DNS-Abfragen für IPv6-Adressen (Typ AAAA) verworfen.",
|
||||
"fastest_addr": "Schnellste IP-Adresse",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Prüfen",
|
||||
"form_enter_host": "Gerätenamen eingeben",
|
||||
"filtered_custom_rules": "Nach benutzerdefinierten Filterregeln gefiltert",
|
||||
"choose_from_list": "Aus Liste auswählen",
|
||||
"add_custom_list": "Eigene Liste hinzufügen",
|
||||
"host_whitelisted": "Der Host ist in der Positivliste enthalten",
|
||||
"check_ip": "IP-Adressen: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "DNSSEC aktivieren",
|
||||
"dnssec_enable_desc": "DNSSEC-Flag in den ausgehenden DNS-Abfragen mitsenden und das Ergebnis überprüfen (DNSSEC-fähiger Resolver erforderlich)",
|
||||
"validated_with_dnssec": "Bestätigt mit DNSSEC",
|
||||
"show_all_responses": "Alle Antworten",
|
||||
"all_queries": "Alle Anfragen",
|
||||
"show_blocked_responses": "Gesperrt",
|
||||
"show_whitelisted_responses": "Auf der Positivliste",
|
||||
"show_processed_responses": "Verarbeitet",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Umgeschrieben",
|
||||
"safe_search": "Sichere Suche",
|
||||
"blocklist": "Sperrliste",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
}
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Größe des Zwischenspeichers",
|
||||
"cache_size_desc": "Größe des DNS-Zwischenspeichers (in Bytes)",
|
||||
"cache_ttl_min_override": "TTL-Minimalwert überschreiben",
|
||||
"cache_ttl_max_override": "TTL-Höchstwert überschreiben",
|
||||
"enter_cache_size": "Größe des Zwischenspeichers eingeben",
|
||||
"enter_cache_ttl_min_override": "TTL-Minimalwert eingeben",
|
||||
"enter_cache_ttl_max_override": "TTL-Höchstwert eingeben",
|
||||
"cache_ttl_min_override_desc": "Überschreibt den TTL-Minimalwert, der vom vorgeschalteten Server empfangen wurde. Dieser Wert darf nicht größer als 3600 (Sek.) (≙ 1 Stunde) betragen.",
|
||||
"cache_ttl_max_override_desc": "Überschreibt den TLL-Maximalwert, der vom vorgeschalteten Server empfangenen wurde",
|
||||
"min_exceeds_max_value": "Minimalwert überschreitet Maximalwert",
|
||||
"value_not_larger_than": "Wert darf höchstens {{maximum}} betragen",
|
||||
"filter_category_general": "Allgemein",
|
||||
"filter_category_security": "Sicherheit",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Weitere",
|
||||
"filter_category_general_desc": "Listen, die Tracking und Werbung auf den meisten Geräten sperren",
|
||||
"filter_category_security_desc": "Listen, die auf das Sperren von Malware, Phishing- oder Scam-Domains spezialisiert sind",
|
||||
"filter_category_regional_desc": "Listen, die sich auf regionale Werbeanzeigen und Tracking-Server konzentrieren",
|
||||
"filter_category_other_desc": "Weitere Sperrlisten",
|
||||
"original_response": "Ursprüngliche Antwort",
|
||||
"click_to_view_queries": "Anklicken, um Abfragen anzuzeigen",
|
||||
"port_53_faq_link": "Port 53 wird oft von Diensten wie „DNSStubListener” oder „systemresolved” belegt. Bitte lesen Sie <0>diese Anweisung</0>, wie dies behoben werden kann."
|
||||
}
|
||||
@@ -480,6 +480,7 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts lists.",
|
||||
"blocked_by_response": "Blocked by CNAME or IP in response",
|
||||
"blocked_by_cname_or_ip": "Blocked by CNAME or IP",
|
||||
"try_again": "Try again",
|
||||
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
|
||||
"example_rewrite_domain": "rewrite responses for this domain name only.",
|
||||
@@ -528,7 +529,7 @@
|
||||
"dnssec_enable": "Enable DNSSEC",
|
||||
"dnssec_enable_desc": "Set DNSSEC flag in the outcoming DNS queries and check the result (DNSSEC-enabled resolver is required)",
|
||||
"validated_with_dnssec": "Validated with DNSSEC",
|
||||
"show_all_responses": "All responses",
|
||||
"all_queries": "All queries",
|
||||
"show_blocked_responses": "Blocked",
|
||||
"show_whitelisted_responses": "Whitelisted",
|
||||
"show_processed_responses": "Processed",
|
||||
@@ -541,7 +542,6 @@
|
||||
"safe_search": "Safe search",
|
||||
"blocklist": "Blocklist",
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"dnssec_enable_desc": "Set DNSSEC flag in the outcoming DNS queries and check the result (DNSSEC-enabled resolver is required)",
|
||||
"cache_size": "Cache size",
|
||||
"cache_size_desc": "DNS cache size (in bytes)",
|
||||
"cache_ttl_min_override": "Override minimum TTL",
|
||||
@@ -560,5 +560,8 @@
|
||||
"filter_category_general_desc": "Lists that block tracking and advertising on most of the devices",
|
||||
"filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains",
|
||||
"filter_category_regional_desc": "Lists that focus on regional ads and tracking servers",
|
||||
"filter_category_other_desc": "Other blocklists"
|
||||
}
|
||||
"filter_category_other_desc": "Other blocklists",
|
||||
"original_response": "Original response",
|
||||
"click_to_view_queries": "Click to view queries",
|
||||
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nueva lista de permitido",
|
||||
"edit_blocklist": "Editar lista de bloqueo",
|
||||
"edit_allowlist": "Editar lista de permitido",
|
||||
"choose_blocklist": "Elegir listas de bloqueo",
|
||||
"choose_allowlist": "Elegir listas de permitido",
|
||||
"enter_valid_blocklist": "Ingresa una URL válida para la lista de bloqueo.",
|
||||
"enter_valid_allowlist": "Ingresa una URL válida para la lista de permitido.",
|
||||
"form_error_url_format": "Formato de URL no válido",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizar IP del cliente",
|
||||
"anonymize_client_ip_desc": "No guarda la dirección IP completa del cliente en registros y estadísticas",
|
||||
"dns_config": "Configuración del servidor DNS",
|
||||
"dns_cache_config": "Configuración de la caché DNS",
|
||||
"dns_cache_config_desc": "Aquí puedes configurar la caché DNS",
|
||||
"blocking_mode": "Modo de bloqueo",
|
||||
"default": "Predeterminado",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -350,8 +354,8 @@
|
||||
"fix": "Corregir",
|
||||
"dns_providers": "Aquí hay una <0>lista de proveedores DNS</0> conocidos para elegir.",
|
||||
"update_now": "Actualizar ahora",
|
||||
"update_failed": "Error en la actualización automática. Por favor <a href='https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update'>siga los pasos</a> para actualizar manualmente.",
|
||||
"processing_update": "Por favor espere, AdGuard Home se está actualizando",
|
||||
"update_failed": "Error en la actualización automática. Por favor <a href='https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#update'>sigue los pasos</a> para actualizar manualmente.",
|
||||
"processing_update": "Por favor espera, AdGuard Home se está actualizando",
|
||||
"clients_title": "Clientes",
|
||||
"clients_desc": "Configurar dispositivos conectados con AdGuard Home",
|
||||
"settings_global": "Global",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Dominio",
|
||||
"answer": "Respuesta",
|
||||
"filter_added_successfully": "La lista ha sido añadida correctamente",
|
||||
"filter_removed_successfully": "La lista ha sido eliminada correctamente",
|
||||
"filter_updated": "La lista ha sido actualizada correctamente",
|
||||
"statistics_configuration": "Configuración de estadísticas",
|
||||
"statistics_retention": "Retención de estadísticas",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Más información</0> sobre cómo crear tus propias listas de hosts.",
|
||||
"blocked_by_response": "Bloqueado por CNAME o IP en respuesta",
|
||||
"blocked_by_cname_or_ip": "Bloqueado por CNAME o IP",
|
||||
"try_again": "Volver a intentar",
|
||||
"domain_desc": "Ingresa el nombre del dominio o comodín que deseas reescribir.",
|
||||
"example_rewrite_domain": "reescribe las respuestas solo para este nombre de dominio.",
|
||||
"example_rewrite_wildcard": "reescribe las respuestas para todos los subdominios de <0>ejemplo.org</0>.",
|
||||
"rewrite_ip_address": "Dirección IP: utiliza esta IP en una respuesta A o AAAA",
|
||||
"rewrite_domain_name": "Nombre de dominio: añade un registro CNAME",
|
||||
"rewrite_A": "<0>A</0>: valor especial, mantiene registros <0>A</0> del DNS de subida",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantiene registros <0>AAAA</0> del DNS de subida",
|
||||
"disable_ipv6": "Deshabilitar IPv6",
|
||||
"disable_ipv6_desc": "Si esta función está habilitada, se eliminarán todas las consultas DNS para direcciones IPv6 (tipo AAAA).",
|
||||
"fastest_addr": "Dirección IP más rápida",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Comprobar",
|
||||
"form_enter_host": "Ingresa un nombre de host",
|
||||
"filtered_custom_rules": "Filtrado por reglas de filtrado personalizado",
|
||||
"choose_from_list": "Elegir de la lista",
|
||||
"add_custom_list": "Añadir lista personalizada",
|
||||
"host_whitelisted": "El host está en la lista blanca",
|
||||
"check_ip": "Direcciones IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Habilitar DNSSEC",
|
||||
"dnssec_enable_desc": "Establece el indicador DNSSEC en las consultas DNS salientes y comprueba el resultado (se requiere un resolutor habilitado para DNSSEC)",
|
||||
"validated_with_dnssec": "Validado con DNSSEC",
|
||||
"show_all_responses": "Todas las respuestas",
|
||||
"all_queries": "Todas las consultas",
|
||||
"show_blocked_responses": "Bloqueado",
|
||||
"show_whitelisted_responses": "En lista blanca",
|
||||
"show_processed_responses": "Procesado",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Reescrito",
|
||||
"safe_search": "Búsqueda segura",
|
||||
"blocklist": "Lista de bloqueo",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Tamaño de la caché",
|
||||
"cache_size_desc": "Tamaño de la caché DNS (en bytes)",
|
||||
"cache_ttl_min_override": "Anular TTL mínimo",
|
||||
"cache_ttl_max_override": "Anular TTL máximo",
|
||||
"enter_cache_size": "Ingresa el tamaño de la caché",
|
||||
"enter_cache_ttl_min_override": "Ingresa el TTL mínimo",
|
||||
"enter_cache_ttl_max_override": "Ingresa el TTL máximo",
|
||||
"cache_ttl_min_override_desc": "Anula el valor TTL (mínimo) recibido del servidor DNS de subida. Este valor no puede ser superior a 3600 (1 hora)",
|
||||
"cache_ttl_max_override_desc": "Anula el valor TTL (máximo) recibido del servidor DNS de subida",
|
||||
"min_exceeds_max_value": "El valor mínimo supera al valor máximo",
|
||||
"value_not_larger_than": "El valor no puede ser mayor que {{maximum}}",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Seguridad",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Otro",
|
||||
"filter_category_general_desc": "Listas que bloquean rastreadores y anuncios en la mayoría de los dispositivos",
|
||||
"filter_category_security_desc": "Listas que se especializan en bloquear dominios de malware, phishing o estafa",
|
||||
"filter_category_regional_desc": "Listas que se centran en anuncios regionales y servidores de rastreo",
|
||||
"filter_category_other_desc": "Otras listas de bloqueo",
|
||||
"original_response": "Respuesta original",
|
||||
"click_to_view_queries": "Clic para ver las consultas",
|
||||
"port_53_faq_link": "El puerto 53 suele estar ocupado por los servicios \"DNSStubListener\" o \"systemd-resolved\". Por favor lee <0>esta instrucción</0> sobre cómo resolver esto."
|
||||
}
|
||||
@@ -65,6 +65,7 @@
|
||||
"filters": "فيلترها",
|
||||
"filter": "فیلتر",
|
||||
"query_log": "جستار وقایع",
|
||||
"nothing_found": "هیچ چیز یافت نشد",
|
||||
"faq": "پرسش و پاسخ",
|
||||
"version": "نسخه",
|
||||
"address": "آدرس",
|
||||
@@ -509,7 +510,6 @@
|
||||
"dnssec_enable": "فعالسازی DNSSEC",
|
||||
"dnssec_enable_desc": "تنظیم نشان DNSSEC در جستارهای حاصل DNS و بررسی نتیجه (تفکیک کننده DNSSEC-فعال شده نیاز است)",
|
||||
"validated_with_dnssec": "معتبر سازی با DNSSEC",
|
||||
"show_all_responses": "همه پاسخ ها",
|
||||
"show_blocked_responses": "مسدود شده",
|
||||
"show_whitelisted_responses": "لیست سفید",
|
||||
"show_processed_responses": "پردازش شده",
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
"filters": "Filtres",
|
||||
"filter": "Filtre",
|
||||
"query_log": "Journal des requêtes",
|
||||
"compact": "Compact",
|
||||
"nothing_found": "Rien n'a été trouvé",
|
||||
"faq": "FAQ",
|
||||
"version": "version",
|
||||
"address": "Addresse",
|
||||
@@ -122,7 +124,7 @@
|
||||
"dns_blocklists_desc": "AdGuard Home bloquera les domaines correspondant aux listes de blocage.",
|
||||
"dns_allowlists_desc": "Les domaines provenant de listes d’autorisation DNS seront autorisés même s’ils figurent dans l’une des listes de blocage.",
|
||||
"custom_filtering_rules": "Règles de filtrage personnalisées",
|
||||
"encryption_settings": "Paramètres de cryptage",
|
||||
"encryption_settings": "Paramètres de chiffrement",
|
||||
"dhcp_settings": "Paramètres DHCP",
|
||||
"upstream_dns": "Serveurs DNS upstream",
|
||||
"upstream_dns_hint": "Si vous laissez ce champ vide, AdGuard Home va utiliser <a href='https://www.quad9.net/' target='_blank'>Quad9</a> comme upstream.",
|
||||
@@ -160,6 +162,8 @@
|
||||
"new_allowlist": "Nouvelle liste d’autorisation",
|
||||
"edit_blocklist": "Modifier la liste de blocage",
|
||||
"edit_allowlist": "Modifier la liste d’autorisation",
|
||||
"choose_blocklist": "Choisir des listes de blocage",
|
||||
"choose_allowlist": "Choisir des listes d’autorisation",
|
||||
"enter_valid_blocklist": "Saisissez une URL valide vers la liste de blocage.",
|
||||
"enter_valid_allowlist": "Saisissez une URL valide vers la liste d’autorisation.",
|
||||
"form_error_url_format": "Format d’URL incorrect",
|
||||
@@ -191,6 +195,7 @@
|
||||
"domain_or_client": "Domaine ou client",
|
||||
"type_table_header": "Type",
|
||||
"response_table_header": "Réponse",
|
||||
"response_code": "Code de réponse",
|
||||
"client_table_header": "Client",
|
||||
"empty_response_status": "Vide",
|
||||
"show_all_filter_type": "Montrer tout",
|
||||
@@ -209,6 +214,7 @@
|
||||
"query_log_filtered": "Filtré par {{filter}}",
|
||||
"query_log_confirm_clear": "Êtes-vous sûr de vouloir effacer tout le journal des requêtes ?",
|
||||
"query_log_cleared": "Le journal des requêtes a été effacé",
|
||||
"query_log_updated": "Le journal des requêtes a été mis à jour",
|
||||
"query_log_clear": "Effacer journal des requêtes",
|
||||
"query_log_retention": "Rétention du journal des requêtes",
|
||||
"query_log_enable": "Activer le journal",
|
||||
@@ -219,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonymiser l’IP du client",
|
||||
"anonymize_client_ip_desc": "Ne pas enregistrer l’adresse IP complète du client dans les journaux et statistiques",
|
||||
"dns_config": "Configuration du serveur DNS",
|
||||
"dns_cache_config": "Configuration du cache DNS",
|
||||
"dns_cache_config_desc": "Ici, vous pouvez configurer le cache DNS",
|
||||
"blocking_mode": "Mode du blocage",
|
||||
"default": "Par défaut",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -241,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "IP nulle : Répondre avec une adresse IP nulle (0.0.0.0 pour A; :: pour AAAA)",
|
||||
"blocking_mode_custom_ip": "IP personnalisée : Répondre avec une adresse IP définie manuellement",
|
||||
"upstream_dns_client_desc": "Si vous laissez ce champ vide, AdGuard Home utilisera les serveurs configurés dans les <0>paramètres DNS</0>.",
|
||||
"tracker_source": "Source du traceur",
|
||||
"source_label": "Source",
|
||||
"found_in_known_domain_db": "Trouvé dans la base de données des domaines connus",
|
||||
"category_label": "Catégorie",
|
||||
@@ -440,6 +449,7 @@
|
||||
"domain": "Domaine",
|
||||
"answer": "Réponse",
|
||||
"filter_added_successfully": "Le filtre a été ajouté avec succès",
|
||||
"filter_removed_successfully": "La liste a été supprimée avec succès",
|
||||
"filter_updated": "Le filtre a été mis à jour avec succès",
|
||||
"statistics_configuration": "Configuration des statistiques",
|
||||
"statistics_retention": "Maintien des statistiques",
|
||||
@@ -474,6 +484,10 @@
|
||||
"domain_desc": "Saisissez le nom de domaine ou le caractère générique que vous souhaitez réécrire.",
|
||||
"example_rewrite_domain": "réécrivez les réponses pour ce nom de domaine uniquement.",
|
||||
"example_rewrite_wildcard": "réécrire les réponses pour tous les sous-domaines <0>exemple.org</0>.",
|
||||
"rewrite_ip_address": "Adresse IP : utilisez cette IP dans une réponse A ou AAAA",
|
||||
"rewrite_domain_name": "Nom de domaine : ajouter un enregistrement CNAME",
|
||||
"rewrite_A": "<0>A</0> : valeur spéciale, conserver les enregistrements <0>A</0> de l’amont",
|
||||
"rewrite_AAAA": "<0>AAAA</0> : valeur spéciale, conserver les enregistrements <0>AAAA</0> de l’amont",
|
||||
"disable_ipv6": "Désactiver IPv6",
|
||||
"disable_ipv6_desc": "Si cette fonctionnalité est activée, toutes les requêtes DNS visant des adresses IPv6 (type AAAA) seront annulées.",
|
||||
"fastest_addr": "Adresse IP la plus rapide",
|
||||
@@ -489,6 +503,8 @@
|
||||
"check": "Vérifier",
|
||||
"form_enter_host": "Saisissez un nom d’hôte",
|
||||
"filtered_custom_rules": "Filtré par des règles de filtrage personnalisées",
|
||||
"choose_from_list": "Choisissez dans la liste",
|
||||
"add_custom_list": "Ajouter une liste personnalisée",
|
||||
"host_whitelisted": "L’hôte est sur liste blanche",
|
||||
"check_ip": "Adresses IP : {{ip}}",
|
||||
"check_cname": "CNAME : {{cname}}",
|
||||
@@ -512,7 +528,6 @@
|
||||
"dnssec_enable": "Activer DNSSEC",
|
||||
"dnssec_enable_desc": "Définir l’indicateur DNSSEC dans les requêtes DNS sortantes et vérifier le résultat (résolveur compatible DNSSEC requis)",
|
||||
"validated_with_dnssec": "Validé avec DNSSEC",
|
||||
"show_all_responses": "Toutes les réponses",
|
||||
"show_blocked_responses": "Bloqué",
|
||||
"show_whitelisted_responses": "Ajouté à la liste blanche",
|
||||
"show_processed_responses": "Traité",
|
||||
@@ -524,5 +539,26 @@
|
||||
"rewritten": "Réécrit",
|
||||
"safe_search": "Recherche sécurisée",
|
||||
"blocklist": "Liste de blocage",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Taille du cache",
|
||||
"cache_size_desc": "Taille du cache DNS (en bytes)",
|
||||
"cache_ttl_min_override": "Remplacer le TTL minimum",
|
||||
"cache_ttl_max_override": "Remplacer le TTL maximum",
|
||||
"enter_cache_size": "Entrer la taille du cache",
|
||||
"enter_cache_ttl_min_override": "Entrez le TTL minimum",
|
||||
"enter_cache_ttl_max_override": "Entrez le TTL maximum",
|
||||
"cache_ttl_min_override_desc": "Remplacer la valeur TTL (minimum) reçue du serveur en amont. Cette valeur ne peut pas dépasser 3600 (1 heure)",
|
||||
"cache_ttl_max_override_desc": "Remplacer la valeur TTL (maximale) reçue du serveur en amont",
|
||||
"min_exceeds_max_value": "La valeur minimum excède la valeur maximum",
|
||||
"value_not_larger_than": "La valeur ne peut pas dépasser {{maximum}}",
|
||||
"filter_category_general": "Général",
|
||||
"filter_category_security": "Sécurité",
|
||||
"filter_category_regional": "Régional",
|
||||
"filter_category_other": "Autre",
|
||||
"filter_category_general_desc": "Listes qui bloquent le pistage et la publicité sur la plupart des appareils",
|
||||
"filter_category_security_desc": "Listes spécialisées dans le blocage de logiciels malveillants, d’hameçonnage ou de domaines frauduleux",
|
||||
"filter_category_regional_desc": "Listes axées sur les annonces régionales et les serveurs de pistage",
|
||||
"filter_category_other_desc": "Autres listes noires",
|
||||
"click_to_view_queries": "Cliquez pour voir les requêtes",
|
||||
"port_53_faq_link": "Le port 53 est souvent occupé par les services « DNSStubListener » ou « systemd-resolved ». Veuillez lire <0>cette instruction</0> pour savoir comment résoudre ce problème."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Novi popis omogućenih",
|
||||
"edit_blocklist": "Uredi popis blokiranih",
|
||||
"edit_allowlist": "Uredi popis omogućenih",
|
||||
"choose_blocklist": "Odaberite popis blokiranih",
|
||||
"choose_allowlist": "Odaberite popis dopuštenih",
|
||||
"enter_valid_blocklist": "Unesite valjani URL za popis blokiranih.",
|
||||
"enter_valid_allowlist": "Unesite valjani URL za popis omogućenih.",
|
||||
"form_error_url_format": "Nevažeći URL format",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimiraj IP klijenta",
|
||||
"anonymize_client_ip_desc": "Ne spremajte cijelu IP adresu klijenta u zapisnike i statistike",
|
||||
"dns_config": "DNS postavke poslužitelja",
|
||||
"dns_cache_config": "DNS predmemorija poslužitelja",
|
||||
"dns_cache_config_desc": "Ovdje možete postaviti DNS predmemoriju",
|
||||
"blocking_mode": "Način blokiranja",
|
||||
"default": "Zadano",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domena",
|
||||
"answer": "Odgovor",
|
||||
"filter_added_successfully": "Popis je uspješno dodan",
|
||||
"filter_removed_successfully": "Ovaj popis je uspješno uklonjen",
|
||||
"filter_updated": "Ovaj popis je uspješno ažuriran",
|
||||
"statistics_configuration": "Postavke statistike",
|
||||
"statistics_retention": "Spremanje statistike",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vlastitog popisa poslužitelja.",
|
||||
"blocked_by_response": "Blokirano od strane CNAME-a ili IP-a u odgovoru",
|
||||
"blocked_by_cname_or_ip": "Blokirao CNAME ili IP",
|
||||
"try_again": "Pokušajte ponovno",
|
||||
"domain_desc": "Unesite naziv domene ili zamjenski znak koji želite prepisati.",
|
||||
"example_rewrite_domain": "prepiši odgovore samo za ovaj naziv domene.",
|
||||
"example_rewrite_wildcard": "prepiši odgovore za sve <0>example.org</0> poddomene.",
|
||||
"rewrite_ip_address": "IP adresa: koristite ovu IP adresu u A ili AAAA odgovoru",
|
||||
"rewrite_domain_name": "Naziv domene: Dodajte CNAME zapis",
|
||||
"rewrite_A": "<0>A</0>: posebna vrijednost, ukloni <0>A</0> zapis od upstreama",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: posebna vrijednost, ukloni <0>AAAA</0> zapis od upstreama",
|
||||
"disable_ipv6": "Onemogući IPv6",
|
||||
"disable_ipv6_desc": "Ukoliko je ova značajka omogućena, svi DNS upiti za IPv6 adrese (AAAA tip) će biti odbačeni.",
|
||||
"fastest_addr": "Najbrža IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Provjeri",
|
||||
"form_enter_host": "Unesite naziv računala",
|
||||
"filtered_custom_rules": "Filtrirano prilagođenim pravilima filtriranja",
|
||||
"choose_from_list": "Odaberite s popisa",
|
||||
"add_custom_list": "Dodajte prilagođeni popis",
|
||||
"host_whitelisted": "Računalo je na popisu dopuštenih",
|
||||
"check_ip": "IP adrese: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Omogući DNSSEC",
|
||||
"dnssec_enable_desc": "Omogućite DNSSEC u izlaznim DNS upitima i provjerite rezultat (potreban je resolver s omogućenim DNSSEC-om)",
|
||||
"validated_with_dnssec": "Potvrđeno s DNSSEC-om",
|
||||
"show_all_responses": "Svi odgovori",
|
||||
"all_queries": "Svi upiti",
|
||||
"show_blocked_responses": "Blokirano",
|
||||
"show_whitelisted_responses": "Na popisu dopuštenih",
|
||||
"show_processed_responses": "Obrađeno",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Prepisano",
|
||||
"safe_search": "Sigurno pretraživanje",
|
||||
"blocklist": "Popis neželjenih",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veličina predmemorije",
|
||||
"cache_size_desc": "Veličina DNS predmemorije (u bajtovima)",
|
||||
"cache_ttl_min_override": "Nadjačaj minimalni TTL",
|
||||
"cache_ttl_max_override": "Nadjačaj maksimalni TTL",
|
||||
"enter_cache_size": "Unesite veličinu predmemorije",
|
||||
"enter_cache_ttl_min_override": "Unesite minimalni TTL",
|
||||
"enter_cache_ttl_max_override": "Unesite maksimalni TTL",
|
||||
"cache_ttl_min_override_desc": "Nadjačaj TTL vrijednost (minimum) dobivenu od upstream poslužitelja. Ova vrijednost ne može biti veća od 3600 (1 sat)",
|
||||
"cache_ttl_max_override_desc": "Nadjačaj TTL vrijednost (maksimum) dobivenu od upstream poslužitelja",
|
||||
"min_exceeds_max_value": "Minimalna vrijednost je veća od maksimalne",
|
||||
"value_not_larger_than": "Vrijednost ne može biti veća od {{maximum}}",
|
||||
"filter_category_general": "Općenito",
|
||||
"filter_category_security": "Sigurnost",
|
||||
"filter_category_regional": "Regionalno",
|
||||
"filter_category_other": "Ostalo",
|
||||
"filter_category_general_desc": "Popisi koji blokiraju pratitelje i oglase na većini uređaja",
|
||||
"filter_category_security_desc": "Popisi koju su specijalizirani za blokiranje zlonamjernih programa, krađe identiteta ili domena za obmanu",
|
||||
"filter_category_regional_desc": "Popisi koji se fokusiraju na regionalne oglase i poslužitelje za praćenje",
|
||||
"filter_category_other_desc": "Ostali popisi blokiranih",
|
||||
"original_response": "Originalni odgovor",
|
||||
"click_to_view_queries": "Kliknite za pregled upita",
|
||||
"port_53_faq_link": "Port 53 često zauzimaju usluge \"DNSStubListener\" ili \"systemd-resolved\". Molimo pročitajte <0>ove upute</0> o tome kako to riješiti."
|
||||
}
|
||||
@@ -447,7 +447,6 @@
|
||||
"client_unblocked": "Klien \"{{ip}}\" sukses di unblock",
|
||||
"static_ip": "Alamat IP statis",
|
||||
"validated_with_dnssec": "Tervalidasi dengan DNSSEC",
|
||||
"show_all_responses": "Semua respon",
|
||||
"show_blocked_responses": "Diblokir",
|
||||
"show_whitelisted_responses": "Dalam Daftar Putih",
|
||||
"show_processed_responses": "Terproses",
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
"filters": "Filtri",
|
||||
"filter": "Filtro",
|
||||
"query_log": "Query Log",
|
||||
"compact": "Compatto",
|
||||
"nothing_found": "Non è stato trovato nulla",
|
||||
"faq": "FAQ",
|
||||
"version": "versione",
|
||||
"address": "Indirizzo",
|
||||
@@ -160,6 +162,8 @@
|
||||
"new_allowlist": "Nuova lista dei consentiti",
|
||||
"edit_blocklist": "Modifica lista di blocco",
|
||||
"edit_allowlist": "Modifica lista dei consentiti",
|
||||
"choose_blocklist": "Scegli liste di blocco",
|
||||
"choose_allowlist": "Scegli liste di autorizzazione",
|
||||
"enter_valid_blocklist": "Inserisci un URL valido nella lista di blocco.",
|
||||
"enter_valid_allowlist": "Inserisci un URL valido nella lista dei consentiti.",
|
||||
"form_error_url_format": "Formato url non valido",
|
||||
@@ -191,6 +195,7 @@
|
||||
"domain_or_client": "Dominio o client",
|
||||
"type_table_header": "Tipo",
|
||||
"response_table_header": "Risposta",
|
||||
"response_code": "Codice di risposta",
|
||||
"client_table_header": "Client",
|
||||
"empty_response_status": "Vuoto",
|
||||
"show_all_filter_type": "Mostra tutti",
|
||||
@@ -209,6 +214,7 @@
|
||||
"query_log_filtered": "Filtrato da {{filter}}",
|
||||
"query_log_confirm_clear": "Sei sicuro di voler eliminare la query log?",
|
||||
"query_log_cleared": "La query log è stata cancellata correttamente",
|
||||
"query_log_updated": "Il log query è stato aggiornato con successo",
|
||||
"query_log_clear": "Cancella query logs",
|
||||
"query_log_retention": "Ritenzione query logs",
|
||||
"query_log_enable": "Abilita log",
|
||||
@@ -219,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizza client IP",
|
||||
"anonymize_client_ip_desc": "Non salvare l'indirizzo IP completo del client nei log e nelle statistiche",
|
||||
"dns_config": "Configurazione server DNS",
|
||||
"dns_cache_config": "Configurazione cache DNS",
|
||||
"dns_cache_config_desc": "Qui puoi configurare la cache DNS",
|
||||
"blocking_mode": "Modalità di blocco",
|
||||
"default": "Predefinito",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -241,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "IP nullo: Rispondi con indirizzo IP zero (0.0.0.0 per A; :: per AAAA)",
|
||||
"blocking_mode_custom_ip": "IP personalizzato: Rispondi con un indirizzo IP impostato manualmente",
|
||||
"upstream_dns_client_desc": "Se lasci questo spazio vuoto, AdGuard Home utilizzerà i server configurati nelle <0>impostazioni DNS</0>.",
|
||||
"tracker_source": "Origine tracker",
|
||||
"source_label": "Fonte",
|
||||
"found_in_known_domain_db": "Trovato nel database dei domini conosciuti.",
|
||||
"category_label": "Categoria",
|
||||
@@ -440,6 +449,7 @@
|
||||
"domain": "Dominio",
|
||||
"answer": "Risposta",
|
||||
"filter_added_successfully": "Il filtro è stato aggiunto correttamente",
|
||||
"filter_removed_successfully": "La lista è stata correttamente rimossa",
|
||||
"filter_updated": "Il filtro è stato aggiornato correttamente",
|
||||
"statistics_configuration": "Configurazione delle statistiche",
|
||||
"statistics_retention": "Conservazione statistiche",
|
||||
@@ -474,6 +484,10 @@
|
||||
"domain_desc": "Inserire il nome di dominio o carattere jolly che si vuole riscrivere.",
|
||||
"example_rewrite_domain": "riscrivi risposte per questo dominio soltanto.",
|
||||
"example_rewrite_wildcard": "riscrivi risposte per tutti i sottodomini di <0>esempio.org</0>.",
|
||||
"rewrite_ip_address": "Indirizzo IP: utilizza questo IP in una risposta A o AAAA",
|
||||
"rewrite_domain_name": "Nome dominio: aggiungi una registrazione CNAME",
|
||||
"rewrite_A": "<0>A</0>: valore speciale, mantieni registrazioni <0>A</0> dall'upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: valore speciale, mantieni registrazioni <0>AAAA</0> dall'upstream",
|
||||
"disable_ipv6": "Disabilita IPv6",
|
||||
"disable_ipv6_desc": "Se questa funzione è abilitata, tutte le query DNS per gli indirizzi IPv6 (tipo AAAA) verranno eliminate.",
|
||||
"fastest_addr": "Indirizzo IP più veloce",
|
||||
@@ -489,6 +503,8 @@
|
||||
"check": "Controlla",
|
||||
"form_enter_host": "Inserisci un nome per l'host",
|
||||
"filtered_custom_rules": "Filtrato dalle regole filtro personalizzate",
|
||||
"choose_from_list": "Scegli dalla lista",
|
||||
"add_custom_list": "Aggiungi lista personalizzata",
|
||||
"host_whitelisted": "L'host è presente nella whitelist",
|
||||
"check_ip": "Indirizzi IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -512,7 +528,6 @@
|
||||
"dnssec_enable": "Abilita DNSSEC",
|
||||
"dnssec_enable_desc": "Imposta la spunta DNSSEC nelle interrogazioni DNS in uscita e verifica il risultato (è richiesta l'attivazione del risolutore DNSSEC)",
|
||||
"validated_with_dnssec": "Verificato con DNSSEC",
|
||||
"show_all_responses": "Tutti i responsi",
|
||||
"show_blocked_responses": "Bloccato",
|
||||
"show_whitelisted_responses": "Nella whitelist",
|
||||
"show_processed_responses": "Processato",
|
||||
@@ -524,5 +539,25 @@
|
||||
"rewritten": "Riscritto",
|
||||
"safe_search": "Ricerca sicura",
|
||||
"blocklist": "Lista di blocco",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Dimensioni cache",
|
||||
"cache_size_desc": "Dimensioni cache DNS (in byte)",
|
||||
"cache_ttl_min_override": "Sovrascrivi TTL minimo",
|
||||
"cache_ttl_max_override": "Sovrascrivi TTL massimo",
|
||||
"enter_cache_size": "Immetti dimensioni cache",
|
||||
"enter_cache_ttl_min_override": "Immetti TTL minimo",
|
||||
"enter_cache_ttl_max_override": "Immetti TTL massimo",
|
||||
"cache_ttl_min_override_desc": "Sovrascrivi valore TTL (minimo) ricevuto dall'upstream del server. Questo valore non può superare 3600 (1 ora)",
|
||||
"cache_ttl_max_override_desc": "Sovrascrivi valore TTL (massimo) ricevuto dall'upstream del server",
|
||||
"min_exceeds_max_value": "Il valore minimo eccede quello massimo",
|
||||
"value_not_larger_than": "Il valore non può essere maggiore di {{maximum}}",
|
||||
"filter_category_general": "Generale",
|
||||
"filter_category_security": "Sicurezza",
|
||||
"filter_category_regional": "Regionale",
|
||||
"filter_category_other": "Altro",
|
||||
"filter_category_general_desc": "Liste che bloccano tracciamenti e pubblicità sulla maggioranza dei dispositivi",
|
||||
"filter_category_security_desc": "Liste specializzate sul blocco di malware, phishing o domini scam",
|
||||
"filter_category_regional_desc": "Liste focalizzare su pubblicità regionali e server traccianti",
|
||||
"filter_category_other_desc": "Altre liste di blocco",
|
||||
"click_to_view_queries": "Clicca per visualizzare query"
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "新しい許可リスト",
|
||||
"edit_blocklist": "ブロックリストの編集",
|
||||
"edit_allowlist": "許可リストの編集",
|
||||
"choose_blocklist": "ブロックリストの選択",
|
||||
"choose_allowlist": "許可リストの選択",
|
||||
"enter_valid_blocklist": "ブロックリストへ有効なURLを入力してください。",
|
||||
"enter_valid_allowlist": "許可リストへ有効なURLを入力してください。",
|
||||
"form_error_url_format": "URLフォーマットが間違っています",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "クライアントIPを匿名化する",
|
||||
"anonymize_client_ip_desc": "ログと統計にクライアントの完全なIPアドレスを保存しない",
|
||||
"dns_config": "DNSサーバ設定",
|
||||
"dns_cache_config": "DNSキャッシュ設定",
|
||||
"dns_cache_config_desc": "ここでDNSキャッシュを設定できます。",
|
||||
"blocking_mode": "ブロックモード",
|
||||
"default": "デフォルト",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "ドメイン",
|
||||
"answer": "応答",
|
||||
"filter_added_successfully": "フィルタの追加に成功しました",
|
||||
"filter_removed_successfully": "リストの削除に成功しました。",
|
||||
"filter_updated": "フィルタの更新に成功しました",
|
||||
"statistics_configuration": "統計設定",
|
||||
"statistics_retention": "統計保持",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "独自ホストリストの作成についての<0>詳細はこちら</0>。",
|
||||
"blocked_by_response": "応答されたCNAMEかIPアドレスによるブロック",
|
||||
"blocked_by_cname_or_ip": "CNAMEもしくはIPアドレスによってブロック済み",
|
||||
"try_again": "再試行する",
|
||||
"domain_desc": "DNSリライトしたいドメイン名やワイルドカードを入力してください。",
|
||||
"example_rewrite_domain": "このドメイン名のみへのレスポンスをリライトする",
|
||||
"example_rewrite_wildcard": "<0>example.org</0>のすべてのサブドメインへのレスポンスをリライトする",
|
||||
"rewrite_ip_address": "IPアドレス入力した場合:AまたはAAAA応答でこのIPが使用されます。",
|
||||
"rewrite_domain_name": "ドメイン名入力した場合:CNAME記録が追加されます。",
|
||||
"rewrite_A": "<0>A</0>:特別な値、アップストリームからの<0>A</0>記録を保持します。",
|
||||
"rewrite_AAAA": "<0>AAAA</0>:特別な値、アップストリームからの<0>AAAA</0>記録を保持します。",
|
||||
"disable_ipv6": "IPv6を無効にする",
|
||||
"disable_ipv6_desc": "チェックすると、IPv6アドレス(タイプAAAA)のすべてのDNSクエリは破棄されます。",
|
||||
"fastest_addr": "最速のIPアドレス",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "チェックする",
|
||||
"form_enter_host": "ホスト名を入力してください",
|
||||
"filtered_custom_rules": "カスタム・フィルタリングルールによる処理されました",
|
||||
"choose_from_list": "リストから選択する",
|
||||
"add_custom_list": "カスタムリストを追加する",
|
||||
"host_whitelisted": "ホストはホワイトリストに登録されています",
|
||||
"check_ip": "IPアドレス: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "DNSSECを有効にする",
|
||||
"dnssec_enable_desc": "DNSクエリの応答にDNSSECフラグを設定し、結果を確認します(DNSSEC対応のリゾルバが必要です)",
|
||||
"validated_with_dnssec": "DNSSECにて検証済",
|
||||
"show_all_responses": "すべての応答",
|
||||
"all_queries": "すべてのクエリ",
|
||||
"show_blocked_responses": "ブロック済",
|
||||
"show_whitelisted_responses": "ホワイトリストにあり",
|
||||
"show_processed_responses": "処理済",
|
||||
@@ -529,5 +541,26 @@
|
||||
"rewritten": "書換",
|
||||
"safe_search": "セーフサーチ",
|
||||
"blocklist": "ブロックリスト",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
}
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "キャッシュサイズ",
|
||||
"cache_size_desc": "DNSキャッシュサイズ(バイト単位)",
|
||||
"cache_ttl_min_override": "最小TTLの上書き",
|
||||
"cache_ttl_max_override": "最大TTLの上書き",
|
||||
"enter_cache_size": "キャッシュサイズを入力してください",
|
||||
"enter_cache_ttl_min_override": "最小TTLを入力してください",
|
||||
"enter_cache_ttl_max_override": "最大TTLを入力してください",
|
||||
"cache_ttl_min_override_desc": "上流サーバから受信したTTL値(最小)を上書き。この値は3600(1時間)を超えることはできません。",
|
||||
"cache_ttl_max_override_desc": "上流サーバから受信したTTL値(最大)を上書き。",
|
||||
"min_exceeds_max_value": "最小値が最大値を超えています",
|
||||
"value_not_larger_than": "値は{{maximum}}より大きくすることはできません",
|
||||
"filter_category_general": "一般",
|
||||
"filter_category_security": "セキュリティ",
|
||||
"filter_category_regional": "地域別",
|
||||
"filter_category_other": "その他",
|
||||
"filter_category_general_desc": "ほとんどのデバイスにて追跡と広告をブロックするリストです。",
|
||||
"filter_category_security_desc": "マルウェア、フィッシング、詐欺ドメインのブロック専用リストです。",
|
||||
"filter_category_regional_desc": "それぞれの地域の広告と追跡サーバをターゲットするリストです。",
|
||||
"filter_category_other_desc": "その他のブロックリストです。",
|
||||
"original_response": "当初の応答",
|
||||
"click_to_view_queries": "クエリを表示するにはクリックしてください"
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "새 허용 목록",
|
||||
"edit_blocklist": "차단 목록 수정",
|
||||
"edit_allowlist": "허용 목록 수정",
|
||||
"choose_blocklist": "차단 목록 선택",
|
||||
"choose_allowlist": "허용 목록 선택",
|
||||
"enter_valid_blocklist": "차단 목록에 유효한 URL을 입력해주세요.",
|
||||
"enter_valid_allowlist": "허용 목록에 유효한 URL을 입력해주세요.",
|
||||
"form_error_url_format": "잘못된 URL 형식",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "클라이언트 IP 익명화",
|
||||
"anonymize_client_ip_desc": "클라이언트의 전체 IP 주소를 로그와 통계에 저장하지 않습니다.",
|
||||
"dns_config": "DNS 서버 설정",
|
||||
"dns_cache_config": "DNS 캐시 구성",
|
||||
"dns_cache_config_desc": "여기에서 DNS 캐시를 구성 할 수 있습니다",
|
||||
"blocking_mode": "차단 모드",
|
||||
"default": "기본",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "도메인",
|
||||
"answer": "응답",
|
||||
"filter_added_successfully": "목록이 성공적으로 추가됨",
|
||||
"filter_removed_successfully": "목록이 성공적으로 제거되었습니다",
|
||||
"filter_updated": "필터가 성공적으로 업데이트됨",
|
||||
"statistics_configuration": "통계 구성",
|
||||
"statistics_retention": "통계 저장 기간",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "후이즈",
|
||||
"filtering_rules_learn_more": "차단 리스트를 직접 호스트하는 법을 <0>알아보세요</0>.",
|
||||
"blocked_by_response": "응답 중 차단된 CNAME 또는 IP",
|
||||
"blocked_by_cname_or_ip": "CNAME 또는 IP에 의해 차단됨",
|
||||
"try_again": "다시 시도해주세요",
|
||||
"domain_desc": "다시 작성할 도메인 이름 또는 와일드카드를 입력합니다.",
|
||||
"example_rewrite_domain": "이 도메인 이름에 대한 응답을 변경합니다.",
|
||||
"example_rewrite_wildcard": "모든 서브 도메인에 대한 <0>example.org</0> 응답을 변경합니다",
|
||||
"rewrite_ip_address": "IP 주소: 이 IP를 A 또는 AAAA 응답에 사용합니다",
|
||||
"rewrite_domain_name": "도메인 이름: CNAME 레코드 추가",
|
||||
"rewrite_A": "<0> A</0>: 특수 값, 업스트림에서 <0> A</0> 기록 유지",
|
||||
"rewrite_AAAA": "<0> AAAA</0>: 특수 값, 업스트림에서 <0> AAAA</0> 기록 유지",
|
||||
"disable_ipv6": "IPv6 비활성화",
|
||||
"disable_ipv6_desc": "이 기능이 활성화되면 IPv6 (타입 AAAA) 의 모든 DNS 쿼리가 드랍됩니다.",
|
||||
"fastest_addr": "가장 빠른 IP 주소",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "확인",
|
||||
"form_enter_host": "호스트 이름을 입력해주세요",
|
||||
"filtered_custom_rules": "사용자 정의 필터링 규칙으로 필터링됨",
|
||||
"choose_from_list": "목록에서 선택",
|
||||
"add_custom_list": "사용자 정의 목록 추가",
|
||||
"host_whitelisted": "예외 목록에 있는 호스트",
|
||||
"check_ip": "IP 주소: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "DNSSEC 활성화",
|
||||
"dnssec_enable_desc": "발신 DNS 쿼리에서 DNSSEC 플래그를 설정하고 결과를 확인합니다 (DNSSEC-enabled resolver 필수)",
|
||||
"validated_with_dnssec": "DNSSEC로 검증됨",
|
||||
"show_all_responses": "모든 응답",
|
||||
"all_queries": "모든 쿼리",
|
||||
"show_blocked_responses": "차단됨",
|
||||
"show_whitelisted_responses": "예외 적용됨",
|
||||
"show_processed_responses": "처리됨",
|
||||
@@ -529,5 +541,26 @@
|
||||
"rewritten": "재작성됨",
|
||||
"safe_search": "세이프 서치",
|
||||
"blocklist": "차단 목록",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
}
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "캐시 크기",
|
||||
"cache_size_desc": "DNS 캐시 크기 (바이트)",
|
||||
"cache_ttl_min_override": "최소 TTL 무시",
|
||||
"cache_ttl_max_override": "최대 TTL 무시",
|
||||
"enter_cache_size": "캐시 크기를 입력하세요",
|
||||
"enter_cache_ttl_min_override": "최소 TTL을 입력하세요",
|
||||
"enter_cache_ttl_max_override": "최대 TTL을 입력하세요",
|
||||
"cache_ttl_min_override_desc": "업스트림 서버에서 수신한 TTL 값(최소)을 무시합니다. 이 값은 3600(1시간)보다 클 수 없습니다",
|
||||
"cache_ttl_max_override_desc": "업스트림 서버에서 수신한 TTL 값(최대)을 무시합니다",
|
||||
"min_exceeds_max_value": "최소값이 최대값을 초과합니다",
|
||||
"value_not_larger_than": "값은 {{maximum}}보다 클 수 없습니다",
|
||||
"filter_category_general": "일반 목록",
|
||||
"filter_category_security": "보안 목록",
|
||||
"filter_category_regional": "지역 목록",
|
||||
"filter_category_other": "기타",
|
||||
"filter_category_general_desc": "대부분의 기기에서 추적 및 광고를 차단하는 목록",
|
||||
"filter_category_security_desc": "멀웨어, 피싱 또는 사기 도메인을 차단하는 목록",
|
||||
"filter_category_regional_desc": "지역 광고 및 추적 서버에 중점을 둔 목록",
|
||||
"filter_category_other_desc": "기타 차단 목록",
|
||||
"original_response": "원래 응답",
|
||||
"click_to_view_queries": "쿼리를 보려면 클릭합니다"
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nieuwe toestemmingslijst",
|
||||
"edit_blocklist": "Blokkeerlijst beheren",
|
||||
"edit_allowlist": "Toestemmingslijst beheren",
|
||||
"choose_blocklist": "Blokkeringslijsten selecteren",
|
||||
"choose_allowlist": "Toestemmingslijsten selecteren",
|
||||
"enter_valid_blocklist": "Voer een geldige URL in voor de blokkeerlijst.",
|
||||
"enter_valid_allowlist": "Voer een geldige URL in voor de toestemmingslijst.",
|
||||
"form_error_url_format": "Ongeldig URL formaat",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Cliënt IP anonimiseren",
|
||||
"anonymize_client_ip_desc": "Het volledige IP-adres van de cliënt niet opnemen in log- en statistiekbestanden",
|
||||
"dns_config": "DNS server configuratie",
|
||||
"dns_cache_config": "DNS cache configuratie",
|
||||
"dns_cache_config_desc": "Hier kan de DNS cache geconfigureerd worden",
|
||||
"blocking_mode": "Blocking modus",
|
||||
"default": "Standaard",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domein",
|
||||
"answer": "Antwoord",
|
||||
"filter_added_successfully": "De lijst is succesvol toegevoegd",
|
||||
"filter_removed_successfully": "De lijst is succesvol verwijderd",
|
||||
"filter_updated": "De lijst is succesvol geüpdatet",
|
||||
"statistics_configuration": "Statistieken configuratie",
|
||||
"statistics_retention": "Statistieken retentie",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Meer informatie</0> over het maken van je eigen host lijsten.",
|
||||
"blocked_by_response": "Geblokkeerd door CNAME of IP als antwoord",
|
||||
"blocked_by_cname_or_ip": "Geblokkeerd via CNAME of IP",
|
||||
"try_again": "Probeer opnieuw",
|
||||
"domain_desc": "Voer de domeinnaam of wildcard in die herschreven moet worden.",
|
||||
"example_rewrite_domain": "herschrijf reacties uitsluitend voor deze domeinnaam.",
|
||||
"example_rewrite_wildcard": "herschrijf reacties voor alle subdomeinen van <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP adres: gebruik dit IP in een A of AAAA antwoord",
|
||||
"rewrite_domain_name": "Domeinnaam: een CNAME record toevoegen",
|
||||
"rewrite_A": "<0>A</0>: speciale waarde, <0>A</0> records uit de upstream bewaren",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: speciale waarde, <0>AAAA</0> records uit de upstream bewaren",
|
||||
"disable_ipv6": "Zet IPv6 uit",
|
||||
"disable_ipv6_desc": "Als deze functie is ingeschakeld, worden alle DNS-query's voor IPv6-adressen (type AAAA) verwijderd.",
|
||||
"fastest_addr": "Snelste IP adres",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Controleren",
|
||||
"form_enter_host": "Voer een hostnaam in",
|
||||
"filtered_custom_rules": "Gefilterd door aangepaste filterregels",
|
||||
"choose_from_list": "Uit de lijst selecteren",
|
||||
"add_custom_list": "Aangepaste lijst toevoegen",
|
||||
"host_whitelisted": "De host staat op de toestemmingslijst",
|
||||
"check_ip": "IP-adressen: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "DNSSEC inschakelen",
|
||||
"dnssec_enable_desc": "Zet de DNSSEC-vlag aan bij uitgaande DNS-query's en controleer het resultaat (DNSSEC-compatibele resolver is vereist)",
|
||||
"validated_with_dnssec": "Gevalideerd met DNSSEC",
|
||||
"show_all_responses": "Alle reacties",
|
||||
"all_queries": "Alle vragen",
|
||||
"show_blocked_responses": "Geblokkeerd",
|
||||
"show_whitelisted_responses": "Op toestemmingslijst",
|
||||
"show_processed_responses": "Verwerkt",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Herschreven",
|
||||
"safe_search": "Veilig zoeken",
|
||||
"blocklist": "Blokkeerlijst",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Cache grootte",
|
||||
"cache_size_desc": "DNS cache grootte (in bytes)",
|
||||
"cache_ttl_min_override": "Minimale TTL overschrijven",
|
||||
"cache_ttl_max_override": "Maximale TTL overschrijven",
|
||||
"enter_cache_size": "Cache grootte invoeren",
|
||||
"enter_cache_ttl_min_override": "Minimale TTL invoeren",
|
||||
"enter_cache_ttl_max_override": "Maximale TTL invoeren",
|
||||
"cache_ttl_min_override_desc": "Overschrijft TTL waarde (minimaal) ontvangen van de upstream server. Deze waarde mag niet hoger als 3600 (1 uur) zijn",
|
||||
"cache_ttl_max_override_desc": "Overschrijft TTL waarde (maximaal) ontvangen van de upstream server",
|
||||
"min_exceeds_max_value": "Minimale waarde overschrijdt de maximale waarde",
|
||||
"value_not_larger_than": "Waarde mag niet hoger zijn dan {{maximum}}",
|
||||
"filter_category_general": "Algemeen",
|
||||
"filter_category_security": "Beveiliging",
|
||||
"filter_category_regional": "Regionaal",
|
||||
"filter_category_other": "Overig",
|
||||
"filter_category_general_desc": "Lijsten die volgers en advertenties op de meeste apparaten blokkeert",
|
||||
"filter_category_security_desc": "Lijsten gespecialiseerd in het blokkeren van malware, phising of scam domeinen",
|
||||
"filter_category_regional_desc": "Lijsten die focussen op regionale ads en tracking servers",
|
||||
"filter_category_other_desc": "Overige blokkeerlijsten",
|
||||
"original_response": "Oorspronkelijke reactie",
|
||||
"click_to_view_queries": "Klik om queries te bekijken",
|
||||
"port_53_faq_link": "Poort 53 wordt vaak gebruikt door services als DNSStubListener- of de systeem DNS-resolver. Lees a.u.b. <0>deze instructie</0> hoe dit is op te lossen."
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
"client_settings": "Klientinnstillinger",
|
||||
"example_upstream_reserved": "du kan bestemme en oppstrøms-DNS <0>for et spesifikt domene(r)</0>",
|
||||
"upstream_parallel": "Bruk parallele forespørsler for å få oppfarten på behandlinger, ved å forespørre til alle oppstrømstjenerne samtidig",
|
||||
"parallel_requests": "Parallelle forespørsler",
|
||||
"load_balancing": "Pågangstrykk-utjevning",
|
||||
"load_balancing_desc": "Forespør én tjener om gangen. AdGuard Home vil bruke en 'vektlagt tilfeldig valg'-algoritme for å velge tjener, slik at den raskeste tjeneren blir brukt oftere.",
|
||||
"bootstrap_dns": "Bootstrap-DNS-tjenere",
|
||||
"bootstrap_dns_desc": "Bootstrap-DNS-tjenere brukes til å oppklare IP-adressene til DoH/DoT-oppklarerene som du har valgt som oppstrømstjenere.",
|
||||
"check_dhcp_servers": "Se etter DHCP-tjenere",
|
||||
@@ -36,6 +39,7 @@
|
||||
"dhcp_interface_select": "Velg DHCP-grensesnitt",
|
||||
"dhcp_hardware_address": "Maskinvareadresse",
|
||||
"dhcp_ip_addresses": "IP-adresser",
|
||||
"ip": "IP-adresse",
|
||||
"dhcp_table_hostname": "Vertsnavn",
|
||||
"dhcp_table_expires": "Utløper",
|
||||
"dhcp_warning": "Hvis du vil aktivere DHCP-tjeneren likevel, så sørg for at det ikke er noen andre aktive DHCP-tjenere i nettverket ditt. Ellers kan det knekke internettilgangen til tilkoblede enheter!",
|
||||
@@ -48,10 +52,14 @@
|
||||
"dhcp_static_leases_not_found": "Ingen statiske DHCP-leieavtaler ble funnet",
|
||||
"dhcp_add_static_lease": "Legg til statisk leieavtale",
|
||||
"dhcp_reset": "Er du sikker på at du vil tilbakestille DHCP-oppsettet?",
|
||||
"country": "Land",
|
||||
"city": "By",
|
||||
"delete_confirm": "Er du sikker på at du vil slette «{{key}}»?",
|
||||
"form_enter_hostname": "Skriv inn vertsnavnet",
|
||||
"error_details": "Feildetaljer",
|
||||
"response_details": "Svardetaljer",
|
||||
"request_details": "Detaljer over forespørsel",
|
||||
"client_details": "Klientdetaljer",
|
||||
"details": "Detaljer",
|
||||
"back": "Tilbake",
|
||||
"dashboard": "Kontrollsenter",
|
||||
@@ -59,9 +67,12 @@
|
||||
"filters": "Filtre",
|
||||
"filter": "Filter",
|
||||
"query_log": "Forespørselslogg",
|
||||
"compact": "Kompakt",
|
||||
"nothing_found": "Ingenting ble funnet",
|
||||
"faq": "OSS",
|
||||
"version": "Versjon",
|
||||
"address": "Adresse",
|
||||
"protocol": "Protokoll",
|
||||
"on": "PÅ",
|
||||
"off": "AV",
|
||||
"copyright": "Opphavsrett",
|
||||
@@ -134,8 +145,10 @@
|
||||
"rules_count_table_header": "Antall oppføringer",
|
||||
"last_time_updated_table_header": "Senest oppdatert",
|
||||
"actions_table_header": "Handlinger",
|
||||
"request_table_header": "Forespørsel",
|
||||
"edit_table_action": "Rediger",
|
||||
"delete_table_action": "Slett",
|
||||
"elapsed": "Utløpt",
|
||||
"filters_and_hosts_hint": "AdGuard Home forstår grunnleggende adblock-oppføringer, «hosts»-filsyntaks, og domenelister.",
|
||||
"no_blocklist_added": "Ingen blokkeringslister er lagt til",
|
||||
"no_whitelist_added": "Ingen hvitelister er lagt til",
|
||||
@@ -143,14 +156,17 @@
|
||||
"add_allowlist": "Legg til hviteliste",
|
||||
"cancel_btn": "Avbryt",
|
||||
"enter_name_hint": "Skriv inn navn",
|
||||
"enter_url_or_path_hint": "Skriv inn listens URL eller fulle filbane",
|
||||
"check_updates_btn": "Se etter oppdateringer",
|
||||
"new_blocklist": "Ny blokkeringsliste",
|
||||
"new_allowlist": "Ny hviteliste",
|
||||
"edit_blocklist": "Rediger blokkeringsliste",
|
||||
"edit_allowlist": "Rediger hviteliste",
|
||||
"choose_blocklist": "Velg blokkeringslister",
|
||||
"enter_valid_blocklist": "Skriv inn en gyldig nettadresse til blokkeringslisten.",
|
||||
"enter_valid_allowlist": "Skriv inn en gyldig nettadresse til hvitelisten.",
|
||||
"form_error_url_format": "Ugyldig URL-format",
|
||||
"form_error_url_or_path_format": "Listens URL eller fulle filbane er ugyldig",
|
||||
"custom_filter_rules": "Selvvalgte filtreringsregler",
|
||||
"custom_filter_rules_hint": "Skriv inn én oppføring per linje. Du kan bruke adblock-oppføringer, «hosts»-filsyntaks, eller rå domener.",
|
||||
"examples_title": "Eksempler",
|
||||
@@ -175,8 +191,10 @@
|
||||
"time_table_header": "Tidspunkt",
|
||||
"date": "Dato",
|
||||
"domain_name_table_header": "Domenenavn",
|
||||
"domain_or_client": "Domene eller klient",
|
||||
"type_table_header": "Type",
|
||||
"response_table_header": "Respons",
|
||||
"response_code": "Svarkode",
|
||||
"client_table_header": "Klient",
|
||||
"empty_response_status": "Tomt innhold",
|
||||
"show_all_filter_type": "Vis alle",
|
||||
@@ -195,6 +213,7 @@
|
||||
"query_log_filtered": "Filtrert av {{filter}}",
|
||||
"query_log_confirm_clear": "Er du sikker på at du vil slette hele forespørselsloggen?",
|
||||
"query_log_cleared": "Forespørselsloggen ble vellykket slettet",
|
||||
"query_log_updated": "Forespørselsloggen ble vellykket oppdatert",
|
||||
"query_log_clear": "Tøm forespørselsloggene",
|
||||
"query_log_retention": "Beholding av forespørselsloggføringene",
|
||||
"query_log_enable": "Skru på loggføring",
|
||||
@@ -202,6 +221,8 @@
|
||||
"query_log_disabled": "Forespørselsloggen er skrudd av og kan bli satt opp i <0>innstillingene</0>",
|
||||
"query_log_strict_search": "Bruk anførselstegn for strenge søk",
|
||||
"query_log_retention_confirm": "Er du sikker på at du vil endre hvor lenge forespørselsloggføringene skal beholdes? Hvis du reduserer den interne verdien, vil noe av dataene gå tapt",
|
||||
"anonymize_client_ip": "Anonymiser klient-IP-en",
|
||||
"anonymize_client_ip_desc": "Ikke lagre den fulle IP-adressen til klienten i loggføringer eller statistikker",
|
||||
"dns_config": "DNS-tjeneroppsett",
|
||||
"blocking_mode": "Blokkeringsmodus",
|
||||
"default": "Standardmodus",
|
||||
@@ -210,7 +231,9 @@
|
||||
"custom_ip": "Tilpasset IP",
|
||||
"blocking_ipv4": "IPv4-blokkering",
|
||||
"blocking_ipv6": "IPv6-blokkering",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"plain_dns": "Ordinær DNS",
|
||||
"form_enter_rate_limit": "Skriv inn forespørselsfrekvensgrense",
|
||||
"rate_limit": "Forespørselsfrekvensgrense",
|
||||
"edns_enable": "Aktiver EDNS-klientundernett",
|
||||
@@ -223,12 +246,14 @@
|
||||
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
|
||||
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
|
||||
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
|
||||
"tracker_source": "Sporerkilde",
|
||||
"source_label": "Kilde",
|
||||
"found_in_known_domain_db": "Funnet i databasen over kjente domener.",
|
||||
"category_label": "Kategori",
|
||||
"rule_label": "Oppføring",
|
||||
"list_label": "Liste",
|
||||
"unknown_filter": "Ukjent filter {{filterId}}",
|
||||
"known_tracker": "Kjent sporer",
|
||||
"install_welcome_title": "Velkommen til AdGuard Home!",
|
||||
"install_welcome_desc": "AdGuard Home er en nettverksdekkende reklame-og-sporings-blokkerende DNS-tjener. Formålet dens er å la deg styre hele nettverket ditt og alle dine enheter, og den krever ikke at klientene bruker spesifikke programmer.",
|
||||
"install_settings_title": "Admin-nettgrensesnitt",
|
||||
@@ -257,6 +282,7 @@
|
||||
"install_devices_router_list_1": "Åpne innstillingene til ruteren din. Vanligvis kan du få tilgang til den på nettleseren din gjennom en URL (f.eks. http://192.168.0.1/ eller http://192.168.1.1/). Du kan bli spurt om å skrive inn passordet ditt. Hvis du ikke husker det, kan du som oftest tilbakestille passordet ditt ved å trykke på knapp på selve ruteren. Noen rutere krever et spesifikt program, som i så fall er ment å allerede ha blitt installert på din PC/mobil.",
|
||||
"install_devices_router_list_2": "Finn DHCP-/DNS-innstillingene. Se etter DNS-bokstavene ved siden av et felt som tillater to eller tre sett med sifre, som hver er delt opp i fire grupper på 1-3 sifre.",
|
||||
"install_devices_router_list_3": "Skriv inn din AdGuard Home-tjeners adresser her.",
|
||||
"install_devices_router_list_4": "På noen rutertyper, f.eks. Altibox sine hjemmesentraler, kan man ikke velge en selvvalgt DNS-tjener. I så fall kan det hjelpe på saken om du setter opp AdGuard Home som en <0>DHCP-tjener</0>. Alternativt, burde du se i bruksanvisningen til din spesifikke rutermodell om hvordan man tilpasser DNS-tjenerne.",
|
||||
"install_devices_windows_list_1": "Åpne «Kontrollpanel» gjennom Start-menyen eller et Windows-søk.",
|
||||
"install_devices_windows_list_2": "Gå til «Nettverk og internett»-kategorien, og så til «Nettverks- og delingssenter».",
|
||||
"install_devices_windows_list_3": "På den venstre siden av skjermen, finn «Endre innstillinger for nettverkskort» og klikk på den.",
|
||||
@@ -390,6 +416,7 @@
|
||||
"rewrite_confirm_delete": "Er du sikker på at du vil slette DNS-omdirigeringen for «{{key}}»?",
|
||||
"rewrite_desc": "Lar deg enkelt konfigurere selvvalgte DNS-tilbakemeldinger for et spesifikt domenenavn.",
|
||||
"rewrite_applied": "Benyttet omdirigeringsregelen",
|
||||
"rewrite_hosts_applied": "Omskrevet av 'hosts'-oppføringen",
|
||||
"dns_rewrites": "DNS-omdirigeringer",
|
||||
"form_domain": "Skriv inn domene",
|
||||
"form_answer": "Skriv inn IP-adresse eller domenenavn",
|
||||
@@ -434,7 +461,7 @@
|
||||
"filters_interval": "Filteroppdateringsvanlighet",
|
||||
"disabled": "Skrudd av",
|
||||
"username_label": "Brukernavn",
|
||||
"username_placeholder": "Skriv inn brukernacvn",
|
||||
"username_placeholder": "Skriv inn brukernavn",
|
||||
"password_label": "Passord",
|
||||
"password_placeholder": "Skriv inn passord",
|
||||
"sign_in": "Logg på",
|
||||
@@ -455,6 +482,8 @@
|
||||
"example_rewrite_wildcard": "omskriv svarene til alle <0>example.org</0>-underdomener.",
|
||||
"disable_ipv6": "Skru av IPv6",
|
||||
"disable_ipv6_desc": "Hvis dette er skrudd på, vil alle DNS-forespørslene til IPv6-adresser (AAAA-type) bli droppet.",
|
||||
"fastest_addr": "Raskeste IP-adresse",
|
||||
"fastest_addr_desc": "Forespør alle DNS-tjenerne og hent den raskeste IP-adressen blant alle svarene. Dette vil gjøre DNS-forespørslene tregere, siden vi må vente på svar fra alle DNS-tjenerne, men det vil forbedre tilkoblingen generelt.",
|
||||
"autofix_warning_text": "Hvis du klikker på «Fiks», vil AdGuard Home sette opp systemet ditt til å bruke 'AdGuard Home'-DNS-tjeneren.",
|
||||
"autofix_warning_list": "Den vil utføre disse handlingene: <0>Skru av systemets DNSStubListener</0> <0>Sette DNS-tjeneradressen til 127.0.0.1</0> <0>Bytte ut det symbolske lenkemålet til /etc/resolv.conf med /run/systemd/resolve/resolv.conf</0> <0>Stoppe DNSStubListener (gjeninnlast 'systemd-resolved'-tjenesten)</0>",
|
||||
"autofix_warning_result": "Som følge av det vil alle DNS-forespørsler fra systemet ditt bli behandlet av AdGuard Home som standard.",
|
||||
@@ -466,6 +495,8 @@
|
||||
"check": "Sjekk",
|
||||
"form_enter_host": "Legg til et domenenavn",
|
||||
"filtered_custom_rules": "Filtrert av Selvvalgte filtreringsoppføringer",
|
||||
"choose_from_list": "Velg fra listen",
|
||||
"add_custom_list": "Legg til en selvvalgt liste",
|
||||
"host_whitelisted": "Domenet er hvitelistet",
|
||||
"check_ip": "IP-adresser: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -486,10 +517,27 @@
|
||||
"confirm_static_ip": "AdGuard Home vil sette opp {{ip}} til å bli din statiske IP-adresse. Vil du fortsette?",
|
||||
"list_updated": "{{count}} liste oppdatert",
|
||||
"list_updated_plural": "{{count}} lister oppdatert",
|
||||
"dnssec_enable": "Skru på DNSSEC",
|
||||
"dnssec_enable_desc": "Fest på DNSSEC-flagg til utgående DNS-forespørsler og sjekk resultatet (En DNS-oppstrømstjener med DNSSEC-støtte er påkrevd)",
|
||||
"validated_with_dnssec": "Validert med DNSSEC",
|
||||
"show_blocked_responses": "Blokkért",
|
||||
"show_whitelisted_responses": "Hvitelistet",
|
||||
"show_processed_responses": "Bearbeidet",
|
||||
"blocked_safebrowsing": "Blokkert av barnevennlig nettlesing",
|
||||
"blocked_adult_websites": "Blokkerte voksennettsteder",
|
||||
"blocked_threats": "Blokkerte trusler",
|
||||
"allowed": "Unntak",
|
||||
"blocklist": "Blokkeringsliste"
|
||||
}
|
||||
"filtered": "Filtrert",
|
||||
"rewritten": "Omskrevet",
|
||||
"safe_search": "Trygge søk",
|
||||
"blocklist": "Blokkeringsliste",
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Mellomlagerstørrelse",
|
||||
"enter_cache_size": "Skriv inn mellomlagerstørrelse",
|
||||
"filter_category_general": "Generelt",
|
||||
"filter_category_security": "Sikkerhet",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Andre",
|
||||
"filter_category_other_desc": "Andre blokkeringslister",
|
||||
"click_to_view_queries": "Klikk for å vise forespørsler"
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
"filters": "Filtry",
|
||||
"filter": "Filtr",
|
||||
"query_log": "Dziennik zapytań",
|
||||
"compact": "Kompaktowy",
|
||||
"nothing_found": "Nic nie znaleziono",
|
||||
"faq": "FAQ",
|
||||
"version": "wersja",
|
||||
@@ -100,7 +101,7 @@
|
||||
"number_of_dns_query_days": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_days_plural": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_24_hours": "Liczba zapytań DNS przetworzonych w ciągu ostatnich 24 godzin",
|
||||
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy bloków hosta",
|
||||
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy zablokowanych hostów",
|
||||
"number_of_dns_query_blocked_24_hours_by_sec": "Liczba żądań DNS zablokowanych przez moduł AdGuard Bezpieczne Przeglądanie",
|
||||
"number_of_dns_query_blocked_24_hours_adult": "Liczba zablokowanych witryn dla dorosłych",
|
||||
"enforced_save_search": "Wymuszone bezpieczne wyszukiwanie",
|
||||
@@ -118,10 +119,10 @@
|
||||
"no_servers_specified": "Nie określono serwerów",
|
||||
"general_settings": "Ustawienia główne",
|
||||
"dns_settings": "Ustawienia DNS",
|
||||
"dns_blocklists": "Lista zablokowanych DNS",
|
||||
"dns_allowlists": "Lista dozwolonych DNS",
|
||||
"dns_blocklists": "Listy zablokowanych DNS",
|
||||
"dns_allowlists": "Listy dozwolonych DNS",
|
||||
"dns_blocklists_desc": "AdGuard Home zablokuje domeny pasujące do listy zablokowanych.",
|
||||
"dns_allowlists_desc": "Domeny z białej listy DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
|
||||
"dns_allowlists_desc": "Domeny z listy dozwolonych DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
|
||||
"custom_filtering_rules": "Niestandardowe reguły filtrowania",
|
||||
"encryption_settings": "Ustawienia szyfrowania",
|
||||
"dhcp_settings": "Ustawienia DHCP",
|
||||
@@ -149,8 +150,8 @@
|
||||
"delete_table_action": "Usuń",
|
||||
"elapsed": "Upłynęło",
|
||||
"filters_and_hosts_hint": "AdGuard Home rozumie podstawowe reguły adblocka i składnię plików hostów.",
|
||||
"no_blocklist_added": "Nie dodano listy zablokowanych",
|
||||
"no_whitelist_added": "Nie dodano listy dozwolonych",
|
||||
"no_blocklist_added": "Nie dodano list zablokowanych",
|
||||
"no_whitelist_added": "Nie dodano list dozwolonych",
|
||||
"add_blocklist": "Dodaj listę zablokowanych",
|
||||
"add_allowlist": "Dodaj listę dozwolonych",
|
||||
"cancel_btn": "Anuluj",
|
||||
@@ -161,6 +162,8 @@
|
||||
"new_allowlist": "Nowa lista dozwolonych",
|
||||
"edit_blocklist": "Edytuj listę zablokowanych",
|
||||
"edit_allowlist": "Edytuj listę dozwolonych",
|
||||
"choose_blocklist": "Wybierz listy zablokowanych",
|
||||
"choose_allowlist": "Wybierz listy dozwolonych",
|
||||
"enter_valid_blocklist": "Wpisz prawidłowy adres URL do listy zablokowanych.",
|
||||
"enter_valid_allowlist": "Wpisz prawidłowy adres URL do listy dozwolonych.",
|
||||
"form_error_url_format": "Format adresu URL jest nieprawidłowy",
|
||||
@@ -211,6 +214,7 @@
|
||||
"query_log_filtered": "Filtrowane przez {{filter}}",
|
||||
"query_log_confirm_clear": "Czy na pewno chcesz wyczyścić cały dziennik zapytań?",
|
||||
"query_log_cleared": "Dziennik zapytań został pomyślnie wyczyszczony",
|
||||
"query_log_updated": "Dziennik zapytań został zaktualizowany",
|
||||
"query_log_clear": "Wyczyść dzienniki zapytań",
|
||||
"query_log_retention": "Przechowywanie dzienników zapytań",
|
||||
"query_log_enable": "Włącz dziennik",
|
||||
@@ -221,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizuj adres IP klienta",
|
||||
"anonymize_client_ip_desc": "Nie zapisuj pełnego adresu IP w dziennikach i statystykach",
|
||||
"dns_config": "Konfiguracja serwera DNS",
|
||||
"dns_cache_config": "Konfiguracja pamięci podręcznej DNS",
|
||||
"dns_cache_config_desc": "Tutaj możesz skonfigurować pamięć podręczną DNS",
|
||||
"blocking_mode": "Tryb blokowania",
|
||||
"default": "Domyślny",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -243,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "Null IP: Odpowiedz z zerowym adresem IP (0.0.0.0 dla A; :: dla AAAA)",
|
||||
"blocking_mode_custom_ip": "Niestandardowy adres IP: Odpowiedz ręcznie ustawionym adresem IP",
|
||||
"upstream_dns_client_desc": "Jeśli to pole pozostanie puste, AdGuard Home użyje serwerów skonfigurowanych w <0>Ustawieniach DNS</0>.",
|
||||
"tracker_source": "Źródło skryptu śledzącego",
|
||||
"source_label": "Źródło",
|
||||
"found_in_known_domain_db": "Znaleziono w bazie danych znanych domen.",
|
||||
"category_label": "Kategoria",
|
||||
@@ -442,6 +449,7 @@
|
||||
"domain": "Domena",
|
||||
"answer": "Odpowiedź",
|
||||
"filter_added_successfully": "Lista została pomyślnie dodana",
|
||||
"filter_removed_successfully": "Lista została usunięta",
|
||||
"filter_updated": "Filtr został pomyślnie zaktualizowany",
|
||||
"statistics_configuration": "Konfiguracja statystyk",
|
||||
"statistics_retention": "Przechowywanie statystyk",
|
||||
@@ -472,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Dowiedz się więcej</0> o tworzeniu własnych list blokowania hostów.",
|
||||
"blocked_by_response": "W odpowiedzi zablokowany przez CNAME lub IP",
|
||||
"blocked_by_cname_or_ip": "Zablokowany przez rekord CNAME lub adres IP",
|
||||
"try_again": "Spróbuj ponownie",
|
||||
"domain_desc": "Wpisz nazwę domeny lub symbol wieloznaczny, który chcesz przepisać.",
|
||||
"example_rewrite_domain": "przepisz odpowiedzi tylko dla tej nazwy domeny.",
|
||||
"example_rewrite_wildcard": "przepisz odpowiedzi dla wszystkich subdomen <0>example.org</0>.",
|
||||
"rewrite_ip_address": "Adres IP: użyj tego adresu IP w odpowiedzi A lub AAAA",
|
||||
"rewrite_domain_name": "Nazwa domeny: dodaj rekord CNAME",
|
||||
"rewrite_A": "<0>A</0>: wartość specjalna, zachowaj rekord <0>A</0> z głównego serwera DNS",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: wartość specjalna, zachowaj rekord <0>AAAA</0> z głównego serwera DNS",
|
||||
"disable_ipv6": "Wyłącz IPv6",
|
||||
"disable_ipv6_desc": "Jeśli ta funkcja jest włączona, wszystkie zapytania DNS dotyczące adresów IPv6 (typ AAAA) zostaną usunięte.",
|
||||
"fastest_addr": "Najszybszy adres IP",
|
||||
@@ -491,6 +504,8 @@
|
||||
"check": "Sprawdź",
|
||||
"form_enter_host": "Wpisz nazwę hosta",
|
||||
"filtered_custom_rules": "Filtrowane według niestandardowych reguł filtrowania",
|
||||
"choose_from_list": "Wybierz z listy",
|
||||
"add_custom_list": "Dodaj listę niestandardową",
|
||||
"host_whitelisted": "Host znajduje się na białej liście",
|
||||
"check_ip": "Adresy IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -514,7 +529,7 @@
|
||||
"dnssec_enable": "Włącz DNSSEC",
|
||||
"dnssec_enable_desc": "Ustaw flagę DNSSEC w wychodzących zapytaniach DNS i sprawdź wynik (wymagany jest usługodawca z obsługą zabezpieczania DNSSEC)",
|
||||
"validated_with_dnssec": "Zweryfikowany przez DNSSEC",
|
||||
"show_all_responses": "Wszystkie odpowiedzi",
|
||||
"all_queries": "Wszystkie zapytania",
|
||||
"show_blocked_responses": "Zablokowane",
|
||||
"show_whitelisted_responses": "Biała lista",
|
||||
"show_processed_responses": "Przetworzono",
|
||||
@@ -526,5 +541,27 @@
|
||||
"rewritten": "Przepisane",
|
||||
"safe_search": "Bezpieczne wyszukiwanie",
|
||||
"blocklist": "Lista zablokowanych",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
}
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Rozmiar pamięci podręcznej",
|
||||
"cache_size_desc": "Rozmiar pamięci podręcznej DNS (w bajtach)",
|
||||
"cache_ttl_min_override": "Nadpisz minimalną wartość TTL",
|
||||
"cache_ttl_max_override": "Nadpisz maksymalną wartość TTL",
|
||||
"enter_cache_size": "Wpisz rozmiar pamięci podręcznej",
|
||||
"enter_cache_ttl_min_override": "Wpisz minimalną wartość TTL",
|
||||
"enter_cache_ttl_max_override": "Wpisz maksymalną wartość TTL",
|
||||
"cache_ttl_min_override_desc": "Nadpisz wartość TTL (minimalną) otrzymaną od serwera nadrzędnego. Wartość nie może być większa niż 3600 (1 godzina)",
|
||||
"cache_ttl_max_override_desc": "Nadpisz wartość TTL (maksymalną) otrzymaną od serwera nadrzędnego",
|
||||
"min_exceeds_max_value": "Minimalna wartość przekracza maksymalną wartość",
|
||||
"value_not_larger_than": "Wartość nie może być większa niż {{maximum}}",
|
||||
"filter_category_general": "Ogólne",
|
||||
"filter_category_security": "Bezpieczeństwo",
|
||||
"filter_category_regional": "Regionalne",
|
||||
"filter_category_other": "Inne",
|
||||
"filter_category_general_desc": "Listy, które blokują skrypty śledzące i reklamy na większości urządzeń",
|
||||
"filter_category_security_desc": "Listy, które specjalizują się w blokowaniu domen ze złośliwym oprogramowaniem, phishingiem lub oszustwami",
|
||||
"filter_category_regional_desc": "Listy, które koncentrują się na reklamach regionalnych i serwerach ze skryptami śledzącymi",
|
||||
"filter_category_other_desc": "Inne listy zablokowanych",
|
||||
"original_response": "Oryginalna odpowiedź",
|
||||
"click_to_view_queries": "Kliknij, aby wyświetlić zapytania",
|
||||
"port_53_faq_link": "Port 53 jest często zajęty przez usługi \"DNSStubListener\" lub \"systemd-resolved\". Przeczytaj <0>tę instrukcję</0> jak to rozwiązać."
|
||||
}
|
||||
@@ -67,6 +67,8 @@
|
||||
"filters": "Filtros",
|
||||
"filter": "Filtro",
|
||||
"query_log": "Registro de consultas",
|
||||
"compact": "Compactar",
|
||||
"nothing_found": "Nada encontrado",
|
||||
"faq": "FAQ",
|
||||
"version": "Versão",
|
||||
"address": "Endereço",
|
||||
@@ -160,6 +162,8 @@
|
||||
"new_allowlist": "Nova lista branca",
|
||||
"edit_blocklist": "Editar lista negra",
|
||||
"edit_allowlist": "Editar lista branca",
|
||||
"choose_blocklist": "Escolha as listas negras",
|
||||
"choose_allowlist": "Escolha as listas brancas",
|
||||
"enter_valid_blocklist": "Digite uma URL válida para a lista negra.",
|
||||
"enter_valid_allowlist": "Digite uma URL válida para a lista branca.",
|
||||
"form_error_url_format": "Formato da URL inválida",
|
||||
@@ -191,6 +195,7 @@
|
||||
"domain_or_client": "Domínio ou cliente",
|
||||
"type_table_header": "Tipo",
|
||||
"response_table_header": "Resposta",
|
||||
"response_code": "Código de resposta",
|
||||
"client_table_header": "Cliente",
|
||||
"empty_response_status": "Vazio",
|
||||
"show_all_filter_type": "Mostrar todos",
|
||||
@@ -209,6 +214,7 @@
|
||||
"query_log_filtered": "Filtrado por {{filter}}",
|
||||
"query_log_confirm_clear": "Você tem certeza que deseja limpar o registro de consulta?",
|
||||
"query_log_cleared": "O registro de consulta foi limpo com sucesso",
|
||||
"query_log_updated": "O registro da consulta foi atualizado com sucesso",
|
||||
"query_log_clear": "Limpar registros de consulta",
|
||||
"query_log_retention": "Arquivamento de registros de consultas",
|
||||
"query_log_enable": "Ativar registro",
|
||||
@@ -219,6 +225,8 @@
|
||||
"anonymize_client_ip": "Tornar anônimo o IP do cliente",
|
||||
"anonymize_client_ip_desc": "Não salva o endereço de IP completo do cliente em registros e estatísticas",
|
||||
"dns_config": "Configuração do servidor DNS",
|
||||
"dns_cache_config": "Configuração de cache DNS",
|
||||
"dns_cache_config_desc": "Aqui você pode configurar o cache do DNS",
|
||||
"blocking_mode": "Modo de bloqueio",
|
||||
"default": "Padrão",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -241,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "IP nulo: Responder com endereço IP zero (0.0.0.0 para A; :: para AAAA)",
|
||||
"blocking_mode_custom_ip": "IP personalizado: Responder com um endereço IP definido manualmente",
|
||||
"upstream_dns_client_desc": "Se você mantiver este campo vazio, o AdGuard Home usará os servidores configurados nas configurações <0>DNS</0>.",
|
||||
"tracker_source": "Fonte do rastreador",
|
||||
"source_label": "Fonte",
|
||||
"found_in_known_domain_db": "Encontrado no banco de dados de domínios conhecidos.",
|
||||
"category_label": "Categoria",
|
||||
@@ -440,6 +449,7 @@
|
||||
"domain": "Domínio",
|
||||
"answer": "Resposta",
|
||||
"filter_added_successfully": "O filtro foi adicionado com sucesso",
|
||||
"filter_removed_successfully": "A lista foi removida com sucesso",
|
||||
"filter_updated": "O filtro atualizado com sucesso",
|
||||
"statistics_configuration": "Configurações de estatísticas",
|
||||
"statistics_retention": "Permanência das estatísticas",
|
||||
@@ -474,6 +484,10 @@
|
||||
"domain_desc": "Digite o nome do domínio ou wildcard que pretende reescrever.",
|
||||
"example_rewrite_domain": "reescrever respostas apenas para este nome de domínio.",
|
||||
"example_rewrite_wildcard": "reescrever respostas para todos subdomínios <0>exemplo.org</0>.",
|
||||
"rewrite_ip_address": "Endereço IP: use esse IP em uma resposta A ou AAAA",
|
||||
"rewrite_domain_name": "Nome de domínio: adicione um registro CNAME",
|
||||
"rewrite_A": "<0>A</0>: valor especial, mantenha <0>A</0> nos registros do upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: valor especial, mantenha <0>AAAA</0> nos registros do upstream",
|
||||
"disable_ipv6": "Desativar IPv6",
|
||||
"disable_ipv6_desc": "Se este recurso estiver ativado, todas as consultas de DNS para endereços IPv6 (tipo AAAA) serão ignoradas.",
|
||||
"fastest_addr": "Endereço de IP mais rápido",
|
||||
@@ -489,6 +503,8 @@
|
||||
"check": "Verificar",
|
||||
"form_enter_host": "Digite o nome do host",
|
||||
"filtered_custom_rules": "Filtrado pelas regras de filtragem personalizadas",
|
||||
"choose_from_list": "Escolha na lista",
|
||||
"add_custom_list": "Adicionar uma lista personalizada",
|
||||
"host_whitelisted": "O host está na lista branca",
|
||||
"check_ip": "Endereços de IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -512,7 +528,6 @@
|
||||
"dnssec_enable": "Ativar DNSSEC",
|
||||
"dnssec_enable_desc": "Definir a flag DNSSEC nas consultas de DNS em andamento e verificar o resultado (é necessário um resolvedor DNSSEC ativado)",
|
||||
"validated_with_dnssec": "Validado com DNSSEC",
|
||||
"show_all_responses": "Todas as respostas",
|
||||
"show_blocked_responses": "Bloqueado",
|
||||
"show_whitelisted_responses": "Na lista branca",
|
||||
"show_processed_responses": "Processado",
|
||||
@@ -524,5 +539,25 @@
|
||||
"rewritten": "Reescrito",
|
||||
"safe_search": "Pesquisa segura",
|
||||
"blocklist": "Lista negra",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Tamanho do cache",
|
||||
"cache_size_desc": "Tamanho do cache do DNS (em bytes)",
|
||||
"cache_ttl_min_override": "Sobrepor o TTL mínimo",
|
||||
"cache_ttl_max_override": "Sobrepor o TTL máximo",
|
||||
"enter_cache_size": "Digite o tamanho do cache",
|
||||
"enter_cache_ttl_min_override": "Digite o TTL mínimo",
|
||||
"enter_cache_ttl_max_override": "Digite o TTL máximo",
|
||||
"cache_ttl_min_override_desc": "Substituição do valor TTL (mínimo) recebido do servidor servidor DNS primário. Esse valor não pode exceder 3600 (1 hora)",
|
||||
"cache_ttl_max_override_desc": "Substituição do valor TTL (máximo) recebido do servidor de DNS primário",
|
||||
"min_exceeds_max_value": "O valor mínimo excede o valor máximo",
|
||||
"value_not_larger_than": "O valor não pode ser maior que {{maximum}}",
|
||||
"filter_category_general": "Geral",
|
||||
"filter_category_security": "Segurança",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Outro",
|
||||
"filter_category_general_desc": "Listas que bloqueiam o rastreamento e a publicidade na maioria dos dispositivos",
|
||||
"filter_category_security_desc": "Listas especializadas em bloquear domínios de malware, phishing ou fraude",
|
||||
"filter_category_regional_desc": "Listas focadas em anúncios regionais e servidores de rastreamento",
|
||||
"filter_category_other_desc": "Outras listas negras",
|
||||
"click_to_view_queries": "Clique para ver as consultas"
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nouă autorizare",
|
||||
"edit_blocklist": "Editare blocare",
|
||||
"edit_allowlist": "Editare autorizare",
|
||||
"choose_blocklist": "Alegeți liste de blocări",
|
||||
"choose_allowlist": "Alegeți liste de permisiuni",
|
||||
"enter_valid_blocklist": "Introduceți un URL valid pentru blocare.",
|
||||
"enter_valid_allowlist": "Introduceți un URL valid pentru autorizare.",
|
||||
"form_error_url_format": "Format URL invalid",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizare client IP",
|
||||
"anonymize_client_ip_desc": "Nu salvați adresa IP completă a clientului în jurnale și statistici",
|
||||
"dns_config": "Configurația serverului DNS",
|
||||
"dns_cache_config": "Configurare cache DNS",
|
||||
"dns_cache_config_desc": "Aici puteți configura cache-ul DNS",
|
||||
"blocking_mode": "Modul de blocare",
|
||||
"default": "Implicit",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domeniu",
|
||||
"answer": "Răspuns",
|
||||
"filter_added_successfully": "Filtrul a fost adăugat cu succes",
|
||||
"filter_removed_successfully": "Lista a fost eliminată cu succes",
|
||||
"filter_updated": "Filtrul a fost actualizat cu succes",
|
||||
"statistics_configuration": "Configurația statisticilor",
|
||||
"statistics_retention": "Păstrarea statisticilor",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Aflați mai multe</0> despre crearea propriilor liste hosts.",
|
||||
"blocked_by_response": "Blocat de CNAME sau IP ca răspuns",
|
||||
"blocked_by_cname_or_ip": "Blocat de CNAME sau IP",
|
||||
"try_again": "Încercați din nou",
|
||||
"domain_desc": "Introduceți un nume de domeniu sau wildcard care doriți să fie rescris.",
|
||||
"example_rewrite_domain": "rescrie răspunsuri numai pentru acest nume de domeniu.",
|
||||
"example_rewrite_wildcard": "rescrie răspunsuri pentru toate subdomeniile <0>exemplu.org</0>.",
|
||||
"rewrite_ip_address": "Adresa IP: utilizați acest IP într-un răspuns A sau AAAA",
|
||||
"rewrite_domain_name": "Nume de domeniu: adăugați o înregistrare CNAME",
|
||||
"rewrite_A": "<0>A</0>: valoare specială, păstrați <0>A</0> înregistrări din amonte",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: valoare specială, păstrați <0>AAAA</0> înregistrări din amonte",
|
||||
"disable_ipv6": "Dezactivați IPv6",
|
||||
"disable_ipv6_desc": "Dacă această opțiune este activată, toate interogările DNS pentru adrese IPv6 (tip AAAA) vor fi anulate.",
|
||||
"fastest_addr": "Cea mai rapidă adresă IP",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Verificați",
|
||||
"form_enter_host": "Introduceți un nume de host",
|
||||
"filtered_custom_rules": "Filtrat prin reguli de filtrare personalizate",
|
||||
"choose_from_list": "Alege din listă",
|
||||
"add_custom_list": "Adăugați propria listă",
|
||||
"host_whitelisted": "Numele de host este în lista albă",
|
||||
"check_ip": "Adrese IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Activați DNSSEC",
|
||||
"dnssec_enable_desc": "Setați steagul DNSSEC pe interogările DNS de ieșire și verificați rezultatul (este necesar un resolver DNSSEC activat)",
|
||||
"validated_with_dnssec": "Validat cu DNSSEC",
|
||||
"show_all_responses": "Toate răspunsurile",
|
||||
"all_queries": "Toate interogările",
|
||||
"show_blocked_responses": "Blocat",
|
||||
"show_whitelisted_responses": "Pe lista albă",
|
||||
"show_processed_responses": "Tratat",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Rescrise",
|
||||
"safe_search": "Căutare sigură",
|
||||
"blocklist": "Lista neagră",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Mărime cache",
|
||||
"cache_size_desc": "Mărime cache DNS (în octeți)",
|
||||
"cache_ttl_min_override": "Suprascrieți TTL minim",
|
||||
"cache_ttl_max_override": "Suprascrieți TTL maxim",
|
||||
"enter_cache_size": "Introduceți mărime cache",
|
||||
"enter_cache_ttl_min_override": "Introduceți TTL minim",
|
||||
"enter_cache_ttl_max_override": "Introduceți TTL maxim",
|
||||
"cache_ttl_min_override_desc": "Suprascrie valoarea TTL (minimă) primită de la serverul din amonte. valoare nu poate fi mai mare de 3600 (1 oră)",
|
||||
"cache_ttl_max_override_desc": "Suprascrie valoarea TTL (maximă) primită de la serverul din amonte",
|
||||
"min_exceeds_max_value": "Valoarea minimă depășește valoarea maximă",
|
||||
"value_not_larger_than": "Valoarea nu poate fi mai mare decât {{maximum}}",
|
||||
"filter_category_general": "General",
|
||||
"filter_category_security": "Securitate",
|
||||
"filter_category_regional": "Regional",
|
||||
"filter_category_other": "Altele",
|
||||
"filter_category_general_desc": "Liste care blochează urmărirea și publicitatea pe majoritatea aparatelor",
|
||||
"filter_category_security_desc": "Liste specializate în blocarea domeniilor malware, phishing sau înșelătorie",
|
||||
"filter_category_regional_desc": "Liste focalizate pe reclame regionale și servere de urmărire",
|
||||
"filter_category_other_desc": "Alte liste de blocări",
|
||||
"original_response": "Răspuns original",
|
||||
"click_to_view_queries": "Clicați pentru a vizualiza interogări",
|
||||
"port_53_faq_link": "Portul 53 este adesea ocupat de serviciile \"DNSStubListener\" sau \"systemd-resolved\". Vă rugăm să citiți <0>această instrucțiune</0> despre cum să rezolvați aceasta."
|
||||
}
|
||||
@@ -110,11 +110,11 @@
|
||||
"average_processing_time_hint": "Среднее время для обработки запроса DNS в миллисекундах",
|
||||
"block_domain_use_filters_and_hosts": "Блокировать домены с использованием фильтров и файлов хостов",
|
||||
"filters_block_toggle_hint": "Вы можете настроить правила блокировки в <a href='#filters'> \"Фильтрах\"</a>.",
|
||||
"use_adguard_browsing_sec": "Использовать Безопасную навигацию AdGuard",
|
||||
"use_adguard_browsing_sec": "Включить Безопасную навигацию AdGuard",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home проверит, включен ли домен в веб-службу безопасности браузера. Он будет использовать API, чтобы выполнить проверку: на сервер отправляется только короткий префикс имени домена SHA256.",
|
||||
"use_adguard_parental": "Используйте модуль Родительского контроля AdGuard ",
|
||||
"use_adguard_parental": "Включить модуль Родительского контроля AdGuard ",
|
||||
"use_adguard_parental_hint": "AdGuard Home проверит, содержит ли домен материалы 18+. Он использует тот же API для обеспечения конфиденциальности, что и веб-служба безопасности браузера.",
|
||||
"enforce_safe_search": "Усилить безопасный поиск",
|
||||
"enforce_safe_search": "Включить безопасный поиск",
|
||||
"enforce_save_search_hint": "AdGuard Home может обеспечить безопасный поиск в следующих поисковых системах: Google, Youtube, Bing, DuckDuckGo, Yandex и Pixabay.",
|
||||
"no_servers_specified": "Нет указанных серверов",
|
||||
"general_settings": "Основные настройки",
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Новый белый список",
|
||||
"edit_blocklist": "Редактировать черный список",
|
||||
"edit_allowlist": "Редактировать белый список",
|
||||
"choose_blocklist": "Выберите списки блокировки",
|
||||
"choose_allowlist": "Выберите списки разрешённых",
|
||||
"enter_valid_blocklist": "Добавьте действующий URL-адрес в черный список.",
|
||||
"enter_valid_allowlist": "Добавьте действующий URL-адрес в белый список.",
|
||||
"form_error_url_format": "Неверный формат URL",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Анонимизировать IP-адрес клиента",
|
||||
"anonymize_client_ip_desc": "Не сохранять полный IP-адрес клиента в журналах и статистике",
|
||||
"dns_config": "Настройки DNS-сервера",
|
||||
"dns_cache_config": "Настройка кэша DNS",
|
||||
"dns_cache_config_desc": "Здесь можно настроить кэш DNS",
|
||||
"blocking_mode": "Режим блокировки",
|
||||
"default": "Стандартный",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Домен",
|
||||
"answer": "Ответ",
|
||||
"filter_added_successfully": "Список успешно добавлен",
|
||||
"filter_removed_successfully": "Список успешно удален",
|
||||
"filter_updated": "Список успешно обновлен",
|
||||
"statistics_configuration": "Конфигурация статистики",
|
||||
"statistics_retention": "Сохранение статистики",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Узнайте больше</0> о создании собственных списков блокировки хостов.",
|
||||
"blocked_by_response": "Заблокировано по CNAME или IP в ответе",
|
||||
"blocked_by_cname_or_ip": "Заблокировано с помощью CNAME или IP",
|
||||
"try_again": "Попробовать еще раз",
|
||||
"domain_desc": "Введите имя или маску домена, который вы хотите перенаправить.",
|
||||
"example_rewrite_domain": "перенаправляет ответы только для этого домена.",
|
||||
"example_rewrite_wildcard": "перенаправляет ответы для всех поддоменов <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP-адрес: используйте этот IP для А или АААА ответов",
|
||||
"rewrite_domain_name": "Доменное имя: добавить запись CNAME",
|
||||
"rewrite_A": "<0>A</0>: специальное значение, хранить записи <0>A</0> с upstream-сервера",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: специальное значение, хранить записи <0>AAAA</0> с upstream-сервера",
|
||||
"disable_ipv6": "Отключить IPv6",
|
||||
"disable_ipv6_desc": "Если эта опция включена, все DNS-запросы адресов IPv6 (тип AAAA) будут игнорироваться.",
|
||||
"fastest_addr": "Самый быстрый IP-адрес",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Проверить",
|
||||
"form_enter_host": "Введите имя хоста",
|
||||
"filtered_custom_rules": "Отфильтрованы с помощью пользовательских правил фильтрации",
|
||||
"choose_from_list": "Выбрать из списка",
|
||||
"add_custom_list": "Добавить свой список",
|
||||
"host_whitelisted": "Хост занесен в белый список",
|
||||
"check_ip": "IP-адреса: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Включить DNSSEC",
|
||||
"dnssec_enable_desc": "Установите флаг DNSSEC в исходящих DNS-запросах и проверьте результат (требуется резолвер с поддержкой DNSSEC)",
|
||||
"validated_with_dnssec": "Подтверждено с помощью DNSSEC",
|
||||
"show_all_responses": "Все ответы",
|
||||
"all_queries": "Все запросы",
|
||||
"show_blocked_responses": "Blocked",
|
||||
"show_whitelisted_responses": "В белом списке",
|
||||
"show_processed_responses": "Обработан",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Переписанные",
|
||||
"safe_search": "Безопасный поиск",
|
||||
"blocklist": "Черный список",
|
||||
"milliseconds_abbreviation": "мс"
|
||||
}
|
||||
"milliseconds_abbreviation": "мс",
|
||||
"cache_size": "Размер кеша",
|
||||
"cache_size_desc": "Размера кеша DNS (в байтах)",
|
||||
"cache_ttl_min_override": "Переопределить минимальный TTL",
|
||||
"cache_ttl_max_override": "Переопределить максимальный TTL",
|
||||
"enter_cache_size": "Введите размер кеша",
|
||||
"enter_cache_ttl_min_override": "Введите минимальный TTL",
|
||||
"enter_cache_ttl_max_override": "Введите максимальный TTL",
|
||||
"cache_ttl_min_override_desc": "Переопределить TTL-значение (минимальное), полученное с upstream-сервера. Это значение не может быть больше 3600 (1 часа)",
|
||||
"cache_ttl_max_override_desc": "Переопределить TTL-значение (максимальное), полученное с upstream-сервера",
|
||||
"min_exceeds_max_value": "Минимальное значение превышает максимальное значение",
|
||||
"value_not_larger_than": "Значение не может быть больше {{maximum}}",
|
||||
"filter_category_general": "Общие",
|
||||
"filter_category_security": "Безопасность",
|
||||
"filter_category_regional": "Региональные",
|
||||
"filter_category_other": "Другие",
|
||||
"filter_category_general_desc": "Списки, которые блокируют отслеживание и рекламу на большинстве устройств",
|
||||
"filter_category_security_desc": "Списки, которые специализируются на блокировке вредоносных программ, фишинговых или мошеннических доменов",
|
||||
"filter_category_regional_desc": "Списки, которые фокусируются на региональной рекламе и серверах отслеживания",
|
||||
"filter_category_other_desc": "Другие списки блокировки",
|
||||
"original_response": "Первоначальный ответ",
|
||||
"click_to_view_queries": "Нажмите, чтобы просмотреть запросы",
|
||||
"port_53_faq_link": "Порт 53 часто занят службами \"DNSStubListener\" или \"systemd-resolved\". Ознакомьтесь с <0>инструкцией</0> о том, как это разрешить."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nový zoznam povolených DNS",
|
||||
"edit_blocklist": "Upraviť zoznam blokovaných DNS",
|
||||
"edit_allowlist": "Upraviť zoznam povolených DNS",
|
||||
"choose_blocklist": "Vybrať blokovací zoznam",
|
||||
"choose_allowlist": "Vybrať povolený zoznam",
|
||||
"enter_valid_blocklist": "Zadajte platnú URL adresu do zoznamu blokovaných DNS.",
|
||||
"enter_valid_allowlist": "Zadajte platnú URL adresu do zoznamu povolených DNS.",
|
||||
"form_error_url_format": "Neplatný URL formát",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonymizujte IP klienta",
|
||||
"anonymize_client_ip_desc": "Neukladať úplnú IP adresu klienta do protokolov a štatistík",
|
||||
"dns_config": "Konfigurácia DNS servera",
|
||||
"dns_cache_config": "Konfigurácia DNS cache",
|
||||
"dns_cache_config_desc": "Tu môžete nakonfigurovať DNS cache",
|
||||
"blocking_mode": "Spôsob blokovania",
|
||||
"default": "Predvolené",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Doména",
|
||||
"answer": "Odpoveď",
|
||||
"filter_added_successfully": "Filter bol úspešne pridaný",
|
||||
"filter_removed_successfully": "Zoznam bol úspešne odstránený",
|
||||
"filter_updated": "Filter bol úspešne aktualizovaný",
|
||||
"statistics_configuration": "Konfigurácia štatistiky",
|
||||
"statistics_retention": "Štatistika za obdobie",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
|
||||
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
|
||||
"blocked_by_cname_or_ip": "Zablokované na základe CNAME alebo IP",
|
||||
"try_again": "Skúste znova",
|
||||
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
|
||||
"example_rewrite_domain": "prepísať odpovede iba pre toto meno domény.",
|
||||
"example_rewrite_wildcard": "prepísať odpovede pre všetky subdomény <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP adresa: použite túto IP v odpovedi A alebo AAAA",
|
||||
"rewrite_domain_name": "Meno domény: pridajte záznam CNAME",
|
||||
"rewrite_A": "<0>A</0>: špeciálna hodnota, uchovávajte záznamy <0>A</0> z upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: špeciálna hodnota, uchovávajte záznamy <0>AAAA</0> z upstream",
|
||||
"disable_ipv6": "Vypnúť IPv6",
|
||||
"disable_ipv6_desc": "Ak je táto funkcia zapnutá, všetky dotazy DNS na adresy IPv6 (typ AAAA) budú zrušené.",
|
||||
"fastest_addr": "Najrýchlejšia IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Kontrola",
|
||||
"form_enter_host": "Zadajte meno hostiteľa",
|
||||
"filtered_custom_rules": "Filtrované podľa vlastných filtračných pravidiel",
|
||||
"choose_from_list": "Vybrať zo zoznamu",
|
||||
"add_custom_list": "Pridať vlastný zoznam",
|
||||
"host_whitelisted": "Hostiteľ je na bielej listine",
|
||||
"check_ip": "IP adresy: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Zapnúť DNSSEC",
|
||||
"dnssec_enable_desc": "Nastavte príznak DNSSEC v nasledujúcich DNS dopytoch a skontrolujte výsledok (je potrebný prekladač so zapnutým DNSSEC)",
|
||||
"validated_with_dnssec": "Overené pomocou DNSSEC",
|
||||
"show_all_responses": "Všetky odpovede",
|
||||
"all_queries": "Všetky dopyty",
|
||||
"show_blocked_responses": "Zablokované",
|
||||
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
||||
"show_processed_responses": "Spracované",
|
||||
@@ -529,5 +541,26 @@
|
||||
"rewritten": "Prepísané",
|
||||
"safe_search": "Bezpečné vyhľadávanie",
|
||||
"blocklist": "Zoznam blokovaní",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veľkosť cache",
|
||||
"cache_size_desc": "Veľkosť DNS cache (v bajtoch)",
|
||||
"cache_ttl_min_override": "Prepísať minimálne TTL",
|
||||
"cache_ttl_max_override": "Prepísať maximálne TTL",
|
||||
"enter_cache_size": "Zadať veľkosť cache",
|
||||
"enter_cache_ttl_min_override": "Zadať minimálne TTL",
|
||||
"enter_cache_ttl_max_override": "Zadať maximálne TTL",
|
||||
"cache_ttl_min_override_desc": "Prepíše hodnotu TTL (minimálnu) prijatú z upstream servera. Táto hodnota nemôže byť väčšia ako 3600 (1 hodina)",
|
||||
"cache_ttl_max_override_desc": "Prepíše hodnotu TTL (maximálnu) prijatú z upstream servera",
|
||||
"min_exceeds_max_value": "Minimálna hodnota je väčšia ako maximálna",
|
||||
"value_not_larger_than": "Hodnota nemôže byť väčšia ako {{maximum}}",
|
||||
"filter_category_general": "Všeobecné",
|
||||
"filter_category_security": "Bezpečnosť",
|
||||
"filter_category_regional": "Regionálne",
|
||||
"filter_category_other": "Iné",
|
||||
"filter_category_general_desc": "Zoznamy, ktoré blokujú sledovanie a reklamu na väčšine zariadení",
|
||||
"filter_category_security_desc": "Zoznamy, ktoré sa špecializujú na blokovanie domén škodlivého softvéru alebo podvodov",
|
||||
"filter_category_regional_desc": "Zoznamy zamerané na regionálne reklamy a sledovacie servery",
|
||||
"filter_category_other_desc": "Iné blokovacie zoznamy",
|
||||
"original_response": "Pôvodná odozva",
|
||||
"click_to_view_queries": "Kliknite pre zobrazenie dopytov"
|
||||
}
|
||||
@@ -67,6 +67,8 @@
|
||||
"filters": "Filtri",
|
||||
"filter": "Filtriraj",
|
||||
"query_log": "Dnevnik poizvedb",
|
||||
"compact": "Stisni",
|
||||
"nothing_found": "Nič ni bilo najdeno",
|
||||
"faq": "Pogosta vprašanja in odgovori (FAQ)",
|
||||
"version": "različica",
|
||||
"address": "Naslov",
|
||||
@@ -160,6 +162,8 @@
|
||||
"new_allowlist": "Nov seznam dovoljenih",
|
||||
"edit_blocklist": "Uredi seznam nedovoljenih",
|
||||
"edit_allowlist": "Uredi seznam dovoljenih",
|
||||
"choose_blocklist": "Izberite sezname za zaviranje",
|
||||
"choose_allowlist": "Izberite sezname dovoljenih",
|
||||
"enter_valid_blocklist": "Vnesite veljaven URL naslov seznama nedovoljenih.",
|
||||
"enter_valid_allowlist": "Vnesite veljaven URL naslov seznama dovoljenih.",
|
||||
"form_error_url_format": "Neveljaven format URL naslova",
|
||||
@@ -191,6 +195,7 @@
|
||||
"domain_or_client": "Domena ali odjemalec",
|
||||
"type_table_header": "Vrsta",
|
||||
"response_table_header": "Odgovor",
|
||||
"response_code": "Koda odziva",
|
||||
"client_table_header": "Odjemalec",
|
||||
"empty_response_status": "Prazno",
|
||||
"show_all_filter_type": "Prikaži vse",
|
||||
@@ -209,6 +214,7 @@
|
||||
"query_log_filtered": "Filtriran z {{filter}}",
|
||||
"query_log_confirm_clear": "Ali ste prepričani, da želite počistiti celoten dnevnik poizvedb?",
|
||||
"query_log_cleared": "Dnevnik poizvedb je uspešno izbrisan",
|
||||
"query_log_updated": "Dnevnik poizvedb je bil uspešno posodobljen",
|
||||
"query_log_clear": "Počisti dnevnike poizvedb",
|
||||
"query_log_retention": "Zadrževanje dnevnikov poizvedb",
|
||||
"query_log_enable": "Omogoči dnevni",
|
||||
@@ -219,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimiziraj odjemalca IP",
|
||||
"anonymize_client_ip_desc": "Ne shrani celotnega naslova IP odjemalca v dnevnikih in statistiki",
|
||||
"dns_config": "Konfiguracija strežnika DNS",
|
||||
"dns_cache_config": "Konfiguracija strežnika DNS",
|
||||
"dns_cache_config_desc": "Tu lahko konfigurirate predpomnilnik DNS",
|
||||
"blocking_mode": "Način zaviranja",
|
||||
"default": "Privzeto",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -241,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "Prazen IP: Odziv z ničelnim naslovom IP (0.0.0.0 za A; :: za AAAA)",
|
||||
"blocking_mode_custom_ip": "IP po meri: Odziv z ročno nastavljenim naslovom IP",
|
||||
"upstream_dns_client_desc": "Če pustite to polje prazno, bo AdGuard Home uporabil strežnike, konfigurirane v <0>nastavitvah DNS</0>.",
|
||||
"tracker_source": "Vir sledilca",
|
||||
"source_label": "Vir",
|
||||
"found_in_known_domain_db": "Najdeno v zbirki podatkov znanih domen.",
|
||||
"category_label": "Kategorija",
|
||||
@@ -440,6 +449,7 @@
|
||||
"domain": "Domena",
|
||||
"answer": "Odgovor",
|
||||
"filter_added_successfully": "Seznam je bil uspešno dodan",
|
||||
"filter_removed_successfully": "Seznam je bil uspešno odstranjen",
|
||||
"filter_updated": "Filter je bil uspešno posodobljen",
|
||||
"statistics_configuration": "Nastavitve statistike",
|
||||
"statistics_retention": "Statistika zadrževanja",
|
||||
@@ -469,11 +479,16 @@
|
||||
"descr": "Opis",
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Več o</0> ustvarjanju lastnih seznamov gostiteljev.",
|
||||
"blocked_by_response": "Onemogočeno z CNAME ali IP v odgovoru",
|
||||
"blocked_by_response": "Onemogočeno s CNAME ali IP v odgovoru",
|
||||
"blocked_by_cname_or_ip": "Onemogočeno s CNAME ali IP naslovom",
|
||||
"try_again": "Poskusi ponovno",
|
||||
"domain_desc": "Vnesite ime domene ali nadomestni znak, ki ga želite prepisati.",
|
||||
"example_rewrite_domain": "prepiše odgovore samo za to ime domene.",
|
||||
"example_rewrite_wildcard": "prepiše odgovore za vse poddomene <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP naslov: uporabi ta IP v odzivu A ali AAAA",
|
||||
"rewrite_domain_name": "Ime domene: dodaj CNAME zapis",
|
||||
"rewrite_A": ">A</0>: posebna vrednost, obdrži <0>A</0> zapise iz gorvodnega toka",
|
||||
"rewrite_AAAA": ">A</0>: posebna vrednost, obdrži <0>AAAA</0> zapise iz gorvodnega toka",
|
||||
"disable_ipv6": "Onemogoči IPv6",
|
||||
"disable_ipv6_desc": "Če je ta funkcija omogočena, bodo vse poizvedbe DNS za naslove IPv6 (vrste AAAA) izpadle.",
|
||||
"fastest_addr": "Najhitrejši IP naslov",
|
||||
@@ -489,6 +504,8 @@
|
||||
"check": "Preveri",
|
||||
"form_enter_host": "Vnesite ime gostitelja",
|
||||
"filtered_custom_rules": "Filtrirano s pravili filtriranja po meri",
|
||||
"choose_from_list": "Izberi s seznama",
|
||||
"add_custom_list": "Dodaj seznam po meri",
|
||||
"host_whitelisted": "Gostitelj je na seznamu dovoljenih",
|
||||
"check_ip": "IP naslovi: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -512,10 +529,10 @@
|
||||
"dnssec_enable": "Omogoči DNSSEC",
|
||||
"dnssec_enable_desc": "V odhodnih poizvedbah DNS nastavite zastavico DNSSEC in preverite rezultat (zahtevan je omogočen reševalnik DNSSEC)",
|
||||
"validated_with_dnssec": "Potrjen z DNSSEC",
|
||||
"show_all_responses": "Vsi odgovori",
|
||||
"all_queries": "Vse poizvedbe",
|
||||
"show_blocked_responses": "Onemogočen",
|
||||
"show_whitelisted_responses": "Na seznamu dovoljenih",
|
||||
"show_processed_responses": "Obdelan",
|
||||
"show_processed_responses": "Obdelana",
|
||||
"blocked_safebrowsing": "Onemogočeno z 'Varnim brskanjem'",
|
||||
"blocked_adult_websites": "Onemogočeno spletnih strani za odrasle",
|
||||
"blocked_threats": "Onemogočeno groženj",
|
||||
@@ -524,5 +541,27 @@
|
||||
"rewritten": "Znova napisano",
|
||||
"safe_search": "Varno iskanje",
|
||||
"blocklist": "Seznam nedovoljenih",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Velikost predpomnilnika",
|
||||
"cache_size_desc": "Velikost predpomnilnika DNS (v bajtih)",
|
||||
"cache_ttl_min_override": "Preglasi najmanjši TTL",
|
||||
"cache_ttl_max_override": "Preglasi največji TTL",
|
||||
"enter_cache_size": "Vnesite velikost predpomnilnika",
|
||||
"enter_cache_ttl_min_override": "Vnesite najmanjši TTL",
|
||||
"enter_cache_ttl_max_override": "Vnesite največji TTL",
|
||||
"cache_ttl_min_override_desc": "Vrednost preglasovanja TTL (najmanjša), prejeta od zgornjega strežnika. Ta vrednost ne sme presegati 3600 (1 ura)",
|
||||
"cache_ttl_max_override_desc": "Vrednost preglasovanja TTL (največja), prejeta od zgornjega strežnika",
|
||||
"min_exceeds_max_value": "Najmanjša vrednost presega največjo vrednost",
|
||||
"value_not_larger_than": "Vrednost ne sme biti večja od {{maximum}}",
|
||||
"filter_category_general": "Splošno",
|
||||
"filter_category_security": "Varnost",
|
||||
"filter_category_regional": "Področno",
|
||||
"filter_category_other": "Drugo",
|
||||
"filter_category_general_desc": "Seznami, ki zavirajo sledenje in oglaševanje na večini naprav",
|
||||
"filter_category_security_desc": "Seznami, ki so specializirani za onemogočanje domen zlonamernih programov, lažnega predstavljanja ali prevar",
|
||||
"filter_category_regional_desc": "Seznami, ki so osredotočeni na področne oglase in strežnike za sledenje",
|
||||
"filter_category_other_desc": "Drugi seznami za zaviranje",
|
||||
"original_response": "Izviren odgovor",
|
||||
"click_to_view_queries": "Kliknite za prikaz poizvedb",
|
||||
"port_53_faq_link": "Vrata 53 pogosto zasedajo storitve 'DNSStubListener' ali 'Sistemsko razrešene storitve'. Preberite <0>to navodilo</0> o tem, kako to rešiti."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nova lista dozvoljenih",
|
||||
"edit_blocklist": "Uredi blok listu",
|
||||
"edit_allowlist": "Uredi listu dozvoljenih",
|
||||
"choose_blocklist": "Izaberite liste blokiranja",
|
||||
"choose_allowlist": "Izaberite liste dozvoljenih",
|
||||
"enter_valid_blocklist": "Unesite važeći URL do blok liste.",
|
||||
"enter_valid_allowlist": "Unesite važeći URL do liste dozvoljenih.",
|
||||
"form_error_url_format": "Nevažeći format Url-a",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizuj IP klijenta",
|
||||
"anonymize_client_ip_desc": "Ne čuvaj punu IP adresu klijenta u dnevnicima i statistikama",
|
||||
"dns_config": "Konfiguracija DNS servera",
|
||||
"dns_cache_config": "Konfigurisanje DNS predmemorije",
|
||||
"dns_cache_config_desc": "Ovde možete konfigurisati DNS predmemoriju",
|
||||
"blocking_mode": "Način blokiranja",
|
||||
"default": "Podrazumevano",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domen",
|
||||
"answer": "Odgovor",
|
||||
"filter_added_successfully": "Filter je uspešno dodat",
|
||||
"filter_removed_successfully": "Lista je uspešno uklonjena",
|
||||
"filter_updated": "Filter je uspešno ažuriran",
|
||||
"statistics_configuration": "Konfiguracija statistike",
|
||||
"statistics_retention": "Zadržavanje statistike",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vaše lične blokliste hostova.",
|
||||
"blocked_by_response": "Blokirano od CNAME ili IP u odgovoru",
|
||||
"blocked_by_cname_or_ip": "Blokirano od CNAME ili IP",
|
||||
"try_again": "Pokušaj ponovo",
|
||||
"domain_desc": "Unesite domen ili džoker koji želite da prepišete.",
|
||||
"example_rewrite_domain": "prepiši odgovore samo za ovaj domen.",
|
||||
"example_rewrite_wildcard": "prepiši odgovore za sve poddomene na <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP adresa: kkoristite ovaj IP u A ili AAAA odgovoru",
|
||||
"rewrite_domain_name": "Ime domena: dodajte CNAME zapis",
|
||||
"rewrite_A": "<0>A</0>: posebna vrednost, zadrži <0>A</0> records iz apstrima",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: posebna vrednost, zadržite <0>AAAA</0> records iz apstrima",
|
||||
"disable_ipv6": "Isključi IPv6",
|
||||
"disable_ipv6_desc": "Ako je ovo uključeno, svi DNS unosi za IPv6 adrese (type AAAA) će biti odbačeni.",
|
||||
"fastest_addr": "Najbrža IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Proveri",
|
||||
"form_enter_host": "Unesite host",
|
||||
"filtered_custom_rules": "Filtrirano od strane prilagođenog pravila",
|
||||
"choose_from_list": "Izaberite sa liste",
|
||||
"add_custom_list": "Dodaj prilagođenu listu",
|
||||
"host_whitelisted": "Host je na beloj listi",
|
||||
"check_ip": "IP adrese: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Uključi DNSSEC",
|
||||
"dnssec_enable_desc": "Postavlja DNSSEC zastavicu u odlaznim DNS zahtevima i proverava rezultat (DNSSEC rešavač je potreban)",
|
||||
"validated_with_dnssec": "Potvrđeno sa DNSSEC",
|
||||
"show_all_responses": "Svi odgovori",
|
||||
"all_queries": "Svi zahtevi",
|
||||
"show_blocked_responses": "Blokirano",
|
||||
"show_whitelisted_responses": "Na beloj listi",
|
||||
"show_processed_responses": "Obrađeno",
|
||||
@@ -529,5 +541,26 @@
|
||||
"rewritten": "Prepisano",
|
||||
"safe_search": "Sigurna pretraga",
|
||||
"blocklist": "Lista blokiranih",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veličina predmemorije",
|
||||
"cache_size_desc": "Veličina DNS predmemorije (u bitovima)",
|
||||
"cache_ttl_min_override": "Prepiši najmanji TTL",
|
||||
"cache_ttl_max_override": "Prepiši najveći TTL",
|
||||
"enter_cache_size": "Unesite veličinu predmemorije",
|
||||
"enter_cache_ttl_min_override": "Unesite najmanji TTL",
|
||||
"enter_cache_ttl_max_override": "Unesite najveći TTL",
|
||||
"cache_ttl_min_override_desc": "Prepiši TTL vrednost (minimum) dobijen od apstrim servera. Ova vrednost ne može biti veća od 3600 (1 sat)",
|
||||
"cache_ttl_max_override_desc": "Prepiši TTL vrednost (maksimum) dobijen od apstrim servera",
|
||||
"min_exceeds_max_value": "Najmanja vrednost je dosegla najveću vrednost",
|
||||
"value_not_larger_than": "Vrednost ne može biti veća od {{maximum}}",
|
||||
"filter_category_general": "Opšte",
|
||||
"filter_category_security": "Bezbednost",
|
||||
"filter_category_regional": "Region",
|
||||
"filter_category_other": "Ostalo",
|
||||
"filter_category_general_desc": "Lista koja blokira praćenja i reklame na većini uređaja",
|
||||
"filter_category_security_desc": "Lista specijalizovana za blokiranje štetnog softvera, štetnih i fišing domena",
|
||||
"filter_category_regional_desc": "Lista koja se usredsređuje na regionalne reklame i servere praćenja",
|
||||
"filter_category_other_desc": "Ostale liste blokiranja",
|
||||
"original_response": "Izvorni odgovor",
|
||||
"click_to_view_queries": "Kliknite da pogledate zahteve"
|
||||
}
|
||||
@@ -505,5 +505,6 @@
|
||||
"blocked_adult_websites": "Yetişkin içerikli site engellendi",
|
||||
"blocked_threats": "Engellenen Tehditler",
|
||||
"allowed": "İzin verildi",
|
||||
"blocklist": "Engellenen listesi"
|
||||
"blocklist": "Engellenen listesi",
|
||||
"port_53_faq_link": "Port 53 genellikle \"DNSStubListener\" veya \"sistemd-resolved\" hizmetler tarafından kullanılır. Lütfen problemin nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "新的允许清单",
|
||||
"edit_blocklist": "编辑阻止列表",
|
||||
"edit_allowlist": "编辑允许列表",
|
||||
"choose_blocklist": "选择拦截列表",
|
||||
"choose_allowlist": "选择允许列表",
|
||||
"enter_valid_blocklist": "输入有效的阻止列表URL",
|
||||
"enter_valid_allowlist": "输入有效的允许列表URL",
|
||||
"form_error_url_format": "无效的URL格式",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "匿名化客户端IP",
|
||||
"anonymize_client_ip_desc": "不要在日志和统计信息中保存客户端的完整IP地址",
|
||||
"dns_config": "DNS服务设定",
|
||||
"dns_cache_config": "DNS缓存配置",
|
||||
"dns_cache_config_desc": "你可以在此处配置 DNS缓存",
|
||||
"blocking_mode": "拦截模式",
|
||||
"default": "默认",
|
||||
"nxdomain": "无效域名",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "域名",
|
||||
"answer": "应答",
|
||||
"filter_added_successfully": "已成功添加过滤器",
|
||||
"filter_removed_successfully": "已成功删除该列表",
|
||||
"filter_updated": "成功更新过滤器",
|
||||
"statistics_configuration": "统计配置",
|
||||
"statistics_retention": "统计保留",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>了解更多</0>关于创建自己的hosts清单。",
|
||||
"blocked_by_response": "因响应的CNAME或IP被屏蔽",
|
||||
"blocked_by_cname_or_ip": "按CNAME或IP拦截",
|
||||
"try_again": "重试",
|
||||
"domain_desc": "输入您要重写的域名或通配符。",
|
||||
"example_rewrite_domain": "仅重写此域名的响应。",
|
||||
"example_rewrite_wildcard": "重写所有<0>example.org</0> 子域的响应。",
|
||||
"rewrite_ip_address": "IP地址:在 A或AAAA响应中使用这个IP",
|
||||
"rewrite_domain_name": "域名 :添加一个CNAME记录",
|
||||
"rewrite_A": "<0>A</0>:特殊值,保持来自上游的<0>A</0>记录",
|
||||
"rewrite_AAAA": "<0>AAAA</0>:特殊值,保持来自上游的<0>AAAA</0>记录",
|
||||
"disable_ipv6": "禁用 IPv6",
|
||||
"disable_ipv6_desc": "启用后,所有IPv6地址 (type AAAA) 的DNS查询都会被丢弃。",
|
||||
"fastest_addr": "最快的 IP 地址",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "检查",
|
||||
"form_enter_host": "输入主机名称",
|
||||
"filtered_custom_rules": "被自定义过滤规则过滤",
|
||||
"choose_from_list": "从列表中选择",
|
||||
"add_custom_list": "添加一个自定义列表",
|
||||
"host_whitelisted": "主机在白名单内",
|
||||
"check_ip": "IP地址:{{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "启用DNSSEC",
|
||||
"dnssec_enable_desc": "在发出DNS查询中设置DNSSEC标志并检查结果(需要启用DNSSEC的解析器)",
|
||||
"validated_with_dnssec": "通过DNSSEC验证",
|
||||
"show_all_responses": "所有响应",
|
||||
"all_queries": "所有查询记录",
|
||||
"show_blocked_responses": "已拦截",
|
||||
"show_whitelisted_responses": "已列入白名单",
|
||||
"show_processed_responses": "已处理",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "重写项",
|
||||
"safe_search": "安全搜索",
|
||||
"blocklist": "拦截列表",
|
||||
"milliseconds_abbreviation": "毫秒"
|
||||
}
|
||||
"milliseconds_abbreviation": "毫秒",
|
||||
"cache_size": "缓存大小",
|
||||
"cache_size_desc": "DNS缓存大小 (单位:字节)",
|
||||
"cache_ttl_min_override": "覆盖最小TTL值",
|
||||
"cache_ttl_max_override": "覆盖最大TTL值",
|
||||
"enter_cache_size": "输入缓存大小",
|
||||
"enter_cache_ttl_min_override": "输入最小TTL值",
|
||||
"enter_cache_ttl_max_override": "输入最大TTL值",
|
||||
"cache_ttl_min_override_desc": "覆盖从上游服务器接收到的TTL值 (最小)。这个值不能超过3600秒(1小时)",
|
||||
"cache_ttl_max_override_desc": "覆盖从上游服务器接收到的TTL值(最大)",
|
||||
"min_exceeds_max_value": "最小值超过最大值",
|
||||
"value_not_larger_than": "值不能大于{{maximum}}",
|
||||
"filter_category_general": "常规",
|
||||
"filter_category_security": "安全",
|
||||
"filter_category_regional": "区域",
|
||||
"filter_category_other": "其它",
|
||||
"filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表",
|
||||
"filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表",
|
||||
"filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表",
|
||||
"filter_category_other_desc": "其他阻止列表",
|
||||
"original_response": "原始响应",
|
||||
"click_to_view_queries": "点击查看查询",
|
||||
"port_53_faq_link": "53端口常被DNSStubListener或systemdn解析的服务占用。请阅读<0>这份关于如何解决这一问题的说明</0>"
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "新的允許清單",
|
||||
"edit_blocklist": "編輯封鎖清單",
|
||||
"edit_allowlist": "編輯允許清單",
|
||||
"choose_blocklist": "選擇封鎖清單",
|
||||
"choose_allowlist": "選擇允許清單",
|
||||
"enter_valid_blocklist": "輸入一個到該封鎖清單之有效的網址。",
|
||||
"enter_valid_allowlist": "輸入一個到該允許清單之有效的網址。",
|
||||
"form_error_url_format": "無效的網址格式",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "將用戶端 IP 匿名",
|
||||
"anonymize_client_ip_desc": "不要在記錄和統計資料中儲存用戶端之完整的 IP 位址",
|
||||
"dns_config": "DNS 伺服器配置",
|
||||
"dns_cache_config": "DNS 快取配置",
|
||||
"dns_cache_config_desc": "於此您可配置 DNS 快取",
|
||||
"blocking_mode": "封鎖模式",
|
||||
"default": "預設",
|
||||
"nxdomain": "不存在的網域(NXDOMAIN)",
|
||||
@@ -276,7 +280,7 @@
|
||||
"install_submit_title": "恭喜!",
|
||||
"install_submit_desc": "該設置程序被完成,且您準備好開始使用 AdGuard Home。",
|
||||
"install_devices_router": "路由器",
|
||||
"install_devices_router_desc": "該設置將自動地涵蓋被連線至您的家庭路由器之所有的裝置,且您將無需手動地配置它們每個。",
|
||||
"install_devices_router_desc": "此設置將自動地涵蓋所有被連線至您的家庭路由器之裝置,且您將無需手動地配置它們每個。",
|
||||
"install_devices_address": "AdGuard Home DNS 伺服器正在監聽下列的位址",
|
||||
"install_devices_router_list_1": "開啟關於您的路由器之偏好設定。通常地,您可透過網址(如 http://192.168.0.1/ 或 http://192.168.1.1/)從您的瀏覽器中存取它。您可能被要求輸入該密碼。如果您不記得它,您經常可透過按壓於該路由器本身上的按鈕來重置密碼。某些路由器需要特定的應用程式,既然如此其應已被安裝於您的電腦/手機上。",
|
||||
"install_devices_router_list_2": "找到 DHCP/DNS 設定。尋找緊鄰著允許兩組或三組數字集的欄位之 DNS 字母,每組被拆成四個含有一至三個數字的群集。",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "網域",
|
||||
"answer": "回應",
|
||||
"filter_added_successfully": "該清單已被成功地加入",
|
||||
"filter_removed_successfully": "該清單已被成功地移除",
|
||||
"filter_updated": "該清單已被成功地更新",
|
||||
"statistics_configuration": "統計資料配置",
|
||||
"statistics_retention": "統計資料保留",
|
||||
@@ -474,11 +479,16 @@
|
||||
"descr": "說明",
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>了解更多</0>有關創建您自己的主機(hosts)清單。",
|
||||
"blocked_by_response": "被正規名稱(CNAME)或 IP 封鎖作為回應",
|
||||
"blocked_by_response": "在回應過程中被正規名稱(CNAME)或 IP 封鎖",
|
||||
"blocked_by_cname_or_ip": "被正規名稱(CNAME)或 IP 封鎖",
|
||||
"try_again": "再次嘗試",
|
||||
"domain_desc": "輸入您想要被改寫的域名或萬用字元(wildcard)。",
|
||||
"example_rewrite_domain": "僅對於此域名改寫回應。",
|
||||
"example_rewrite_wildcard": "對於所有的 <0>example.org</0> 子網域改寫回應。",
|
||||
"rewrite_ip_address": "IP 位址:在一個 A 或 AAAA 回應中使用此 IP",
|
||||
"rewrite_domain_name": "域名:增加一筆正規名稱(CNAME)記錄",
|
||||
"rewrite_A": "<0>A</0>:特殊的數值,阻止 <0>A</0> 記錄免於該上游",
|
||||
"rewrite_AAAA": "<0>AAAA</0>:特殊的數值,阻止 <0>AAAA</0> 記錄免於該上游",
|
||||
"disable_ipv6": "禁用 IPv6",
|
||||
"disable_ipv6_desc": "如果此功能被啟用,所有對於 IPv6 位址(類型 AAAA)的 DNS 查詢將被丟棄。",
|
||||
"fastest_addr": "最快的 IP 位址",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "檢查",
|
||||
"form_enter_host": "輸入主機名稱",
|
||||
"filtered_custom_rules": "被自訂的過濾規則過濾",
|
||||
"choose_from_list": "從該清單中選擇",
|
||||
"add_custom_list": "增加一個自訂的清單",
|
||||
"host_whitelisted": "該主機被列入白名單",
|
||||
"check_ip": "IP 位址:{{ip}}",
|
||||
"check_cname": "正規名稱(CNAME):{{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "啟用網域名稱系統安全性擴充功能(DNSSEC)",
|
||||
"dnssec_enable_desc": "在發出的 DNS 查詢中設定 DNSSEC 標記並檢查該結果(已啟用 DNSSEC 的解析器是必須的)",
|
||||
"validated_with_dnssec": "已用網域名稱系統安全性擴充功能(DNSSEC)驗證",
|
||||
"show_all_responses": "所有的回應",
|
||||
"all_queries": "所有的查詢",
|
||||
"show_blocked_responses": "已封鎖的",
|
||||
"show_whitelisted_responses": "已列入白名單的",
|
||||
"show_processed_responses": "已處理的",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "已改寫的",
|
||||
"safe_search": "安全搜尋",
|
||||
"blocklist": "封鎖清單",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "快取大小",
|
||||
"cache_size_desc": "DNS 快取大小(以位元組)",
|
||||
"cache_ttl_min_override": "覆寫最小的存活時間(TTL)",
|
||||
"cache_ttl_max_override": "覆寫最大的存活時間(TTL)",
|
||||
"enter_cache_size": "輸入快取大小",
|
||||
"enter_cache_ttl_min_override": "輸入最小的存活時間(TTL)",
|
||||
"enter_cache_ttl_max_override": "輸入最大的存活時間(TTL)",
|
||||
"cache_ttl_min_override_desc": "覆寫從上游的伺服器收到的存活時間(TTL)數值(最小值)。此數值不能大於 3600(1 小時)",
|
||||
"cache_ttl_max_override_desc": "覆寫從上游的伺服器收到的存活時間(TTL)數值(最大值)",
|
||||
"min_exceeds_max_value": "最小值超過最大值",
|
||||
"value_not_larger_than": "數值不能大於 {{maximum}}",
|
||||
"filter_category_general": "一般的",
|
||||
"filter_category_security": "安全性",
|
||||
"filter_category_regional": "區域性的",
|
||||
"filter_category_other": "其它的",
|
||||
"filter_category_general_desc": "封鎖大多數朝向裝置的追蹤和廣告之清單",
|
||||
"filter_category_security_desc": "專精於封鎖惡意軟體、網路釣魚或詐騙網域之清單",
|
||||
"filter_category_regional_desc": "專注於區域性的廣告和追蹤伺服器之清單",
|
||||
"filter_category_other_desc": "其它的封鎖清單",
|
||||
"original_response": "原始的回應",
|
||||
"click_to_view_queries": "點擊以檢視查詢",
|
||||
"port_53_faq_link": "連接埠 53 常被 \"DNSStubListener\" 或 \"systemd-resolved\" 服務佔用。請閱讀有關如何解決這個的<0>用法說明</0>。"
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import { createAction } from 'redux-actions';
|
||||
import i18next from 'i18next';
|
||||
|
||||
import apiClient from '../api/Api';
|
||||
import { normalizeTextarea } from '../helpers/helpers';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
import { BLOCK_ACTIONS } from '../helpers/constants';
|
||||
import { splitByNewLine } from '../helpers/helpers';
|
||||
|
||||
export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST');
|
||||
export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE');
|
||||
@@ -31,9 +31,9 @@ export const setAccessList = (config) => async (dispatch) => {
|
||||
const { allowed_clients, disallowed_clients, blocked_hosts } = config;
|
||||
|
||||
const values = {
|
||||
allowed_clients: normalizeTextarea(allowed_clients),
|
||||
disallowed_clients: normalizeTextarea(disallowed_clients),
|
||||
blocked_hosts: normalizeTextarea(blocked_hosts),
|
||||
allowed_clients: splitByNewLine(allowed_clients),
|
||||
disallowed_clients: splitByNewLine(disallowed_clients),
|
||||
blocked_hosts: splitByNewLine(blocked_hosts),
|
||||
};
|
||||
|
||||
await apiClient.setAccessList(values);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createAction } from 'redux-actions';
|
||||
|
||||
import apiClient from '../api/Api';
|
||||
import { normalizeTextarea } from '../helpers/helpers';
|
||||
import { splitByNewLine } from '../helpers/helpers';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
|
||||
export const getDnsConfigRequest = createAction('GET_DNS_CONFIG_REQUEST');
|
||||
@@ -30,11 +30,11 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
||||
|
||||
let hasDnsSettings = false;
|
||||
if (Object.prototype.hasOwnProperty.call(data, 'bootstrap_dns')) {
|
||||
data.bootstrap_dns = normalizeTextarea(config.bootstrap_dns);
|
||||
data.bootstrap_dns = splitByNewLine(config.bootstrap_dns);
|
||||
hasDnsSettings = true;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(data, 'upstream_dns')) {
|
||||
data.upstream_dns = normalizeTextarea(config.upstream_dns);
|
||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||
hasDnsSettings = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import { createAction } from 'redux-actions';
|
||||
import i18next from 'i18next';
|
||||
import axios from 'axios';
|
||||
|
||||
import { isVersionGreater, normalizeTextarea, sortClients } from '../helpers/helpers';
|
||||
import { splitByNewLine, sortClients } from '../helpers/helpers';
|
||||
import { CHECK_TIMEOUT, SETTINGS_NAMES } from '../helpers/constants';
|
||||
import { areEqualVersions } from '../helpers/version';
|
||||
import { getTlsStatus } from './encryption';
|
||||
import apiClient from '../api/Api';
|
||||
import { addErrorToast, addNoticeToast, addSuccessToast } from './toasts';
|
||||
@@ -121,7 +122,7 @@ export const getVersion = (recheck = false) => async (dispatch, getState) => {
|
||||
const { dnsVersion } = getState().dashboard;
|
||||
const currentVersion = dnsVersion === 'undefined' ? 0 : dnsVersion;
|
||||
|
||||
if (data && isVersionGreater(currentVersion, data.new_version)) {
|
||||
if (data && !areEqualVersions(currentVersion, data.new_version)) {
|
||||
dispatch(addSuccessToast('updates_checked'));
|
||||
} else {
|
||||
dispatch(addSuccessToast('updates_version_equal'));
|
||||
@@ -279,8 +280,8 @@ export const testUpstream = (config) => async (dispatch) => {
|
||||
dispatch(testUpstreamRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns);
|
||||
values.upstream_dns = normalizeTextarea(values.upstream_dns);
|
||||
values.bootstrap_dns = splitByNewLine(values.bootstrap_dns);
|
||||
values.upstream_dns = splitByNewLine(values.upstream_dns);
|
||||
|
||||
const upstreamResponse = await apiClient.testUpstream(values);
|
||||
const testMessages = Object.keys(upstreamResponse)
|
||||
|
||||
@@ -2,12 +2,19 @@ import { createAction } from 'redux-actions';
|
||||
|
||||
import apiClient from '../api/Api';
|
||||
import { normalizeLogs, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers';
|
||||
import { TABLE_DEFAULT_PAGE_SIZE, TABLE_FIRST_PAGE } from '../helpers/constants';
|
||||
import {
|
||||
DEFAULT_LOGS_FILTER,
|
||||
TABLE_DEFAULT_PAGE_SIZE,
|
||||
TABLE_FIRST_PAGE,
|
||||
} from '../helpers/constants';
|
||||
import { addErrorToast, addSuccessToast } from './toasts';
|
||||
|
||||
const getLogsWithParams = async (config) => {
|
||||
const { older_than, filter, ...values } = config;
|
||||
const rawLogs = await apiClient.getQueryLog({ ...filter, older_than });
|
||||
const rawLogs = await apiClient.getQueryLog({
|
||||
...filter,
|
||||
older_than,
|
||||
});
|
||||
const { data, oldest } = rawLogs;
|
||||
let logs = normalizeLogs(data);
|
||||
const clientsParams = getParamsForClientsSearch(logs, 'client');
|
||||
@@ -18,7 +25,11 @@ const getLogsWithParams = async (config) => {
|
||||
}
|
||||
|
||||
return {
|
||||
logs, oldest, older_than, filter, ...values,
|
||||
logs,
|
||||
oldest,
|
||||
older_than,
|
||||
filter,
|
||||
...values,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -38,7 +49,10 @@ const checkFilteredLogs = async (data, filter, dispatch, total) => {
|
||||
dispatch(getAdditionalLogsRequest());
|
||||
|
||||
try {
|
||||
const additionalLogs = await getLogsWithParams({ older_than: oldest, filter });
|
||||
const additionalLogs = await getLogsWithParams({
|
||||
older_than: oldest,
|
||||
filter,
|
||||
});
|
||||
if (additionalLogs.oldest.length > 0) {
|
||||
return await checkFilteredLogs(additionalLogs, filter, dispatch, {
|
||||
logs: [...totalData.logs, ...additionalLogs.logs],
|
||||
@@ -69,13 +83,19 @@ export const getLogs = (config) => async (dispatch, getState) => {
|
||||
dispatch(getLogsRequest());
|
||||
try {
|
||||
const { isFiltered, filter, page } = getState().queryLogs;
|
||||
const data = await getLogsWithParams({ ...config, filter });
|
||||
const data = await getLogsWithParams({
|
||||
...config,
|
||||
filter,
|
||||
});
|
||||
|
||||
if (isFiltered) {
|
||||
const additionalData = await checkFilteredLogs(data, filter, dispatch);
|
||||
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
||||
dispatch(getLogsSuccess(updatedData));
|
||||
dispatch(setLogsPagination({ page, pageSize: TABLE_DEFAULT_PAGE_SIZE }));
|
||||
dispatch(setLogsPagination({
|
||||
page,
|
||||
pageSize: TABLE_DEFAULT_PAGE_SIZE,
|
||||
}));
|
||||
} else {
|
||||
dispatch(getLogsSuccess(data));
|
||||
}
|
||||
@@ -86,24 +106,48 @@ export const getLogs = (config) => async (dispatch, getState) => {
|
||||
};
|
||||
|
||||
export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST');
|
||||
export const setLogsFilterFailure = createAction('SET_LOGS_FILTER_FAILURE');
|
||||
export const setLogsFilterSuccess = createAction('SET_LOGS_FILTER_SUCCESS');
|
||||
|
||||
export const setLogsFilter = (filter) => async (dispatch) => {
|
||||
dispatch(setLogsFilterRequest());
|
||||
/**
|
||||
*
|
||||
* @param filter
|
||||
* @param {string} filter.search
|
||||
* @param {string} filter.response_status query field of RESPONSE_FILTER object
|
||||
* @returns function
|
||||
*/
|
||||
export const setLogsFilter = (filter) => setLogsFilterRequest(filter);
|
||||
|
||||
export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST');
|
||||
export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE');
|
||||
export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS');
|
||||
|
||||
export const setFilteredLogs = (filter) => async (dispatch) => {
|
||||
dispatch(setFilteredLogsRequest());
|
||||
try {
|
||||
const data = await getLogsWithParams({ older_than: '', filter });
|
||||
const data = await getLogsWithParams({
|
||||
older_than: '',
|
||||
filter,
|
||||
});
|
||||
const additionalData = await checkFilteredLogs(data, filter, dispatch);
|
||||
const updatedData = additionalData.logs ? { ...data, ...additionalData } : data;
|
||||
|
||||
dispatch(setLogsFilterSuccess({ ...updatedData, filter }));
|
||||
dispatch(setFilteredLogsSuccess({
|
||||
...updatedData,
|
||||
filter,
|
||||
}));
|
||||
dispatch(setLogsPage(TABLE_FIRST_PAGE));
|
||||
} catch (error) {
|
||||
dispatch(addErrorToast({ error }));
|
||||
dispatch(setLogsFilterFailure(error));
|
||||
dispatch(setFilteredLogsFailure(error));
|
||||
}
|
||||
};
|
||||
|
||||
export const resetFilteredLogs = () => setFilteredLogs(DEFAULT_LOGS_FILTER);
|
||||
|
||||
export const refreshFilteredLogs = () => async (dispatch, getState) => {
|
||||
const { filter } = getState().queryLogs;
|
||||
await dispatch(setFilteredLogs(filter));
|
||||
};
|
||||
|
||||
export const clearLogsRequest = createAction('CLEAR_LOGS_REQUEST');
|
||||
export const clearLogsFailure = createAction('CLEAR_LOGS_FAILURE');
|
||||
export const clearLogsSuccess = createAction('CLEAR_LOGS_SUCCESS');
|
||||
|
||||
@@ -36,7 +36,7 @@ import i18n from '../../i18n';
|
||||
import Loading from '../ui/Loading';
|
||||
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
|
||||
import Services from '../Filters/Services';
|
||||
import { setHtmlLangAttr } from '../../helpers/helpers';
|
||||
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
|
||||
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
@@ -111,7 +111,9 @@ class App extends Component {
|
||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
||||
<>
|
||||
<Route path={MENU_URLS.root} exact component={Dashboard} />
|
||||
<Route path={MENU_URLS.logs} component={Logs} />
|
||||
<Route
|
||||
path={[`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs]}
|
||||
component={Logs} />
|
||||
<Route path={MENU_URLS.guide} component={SetupGuide} />
|
||||
<Route path={SETTINGS_URLS.settings} component={Settings} />
|
||||
<Route path={SETTINGS_URLS.dns} component={Dns} />
|
||||
|
||||
@@ -14,7 +14,11 @@ const CountCell = (totalBlocked) => function cell(row) {
|
||||
const { value } = row;
|
||||
const percent = getPercent(totalBlocked, value);
|
||||
|
||||
return <Cell value={value} percent={percent} color={STATUS_COLORS.red} />;
|
||||
return <Cell value={value}
|
||||
percent={percent}
|
||||
color={STATUS_COLORS.red}
|
||||
search={row.original.domain}
|
||||
/>;
|
||||
};
|
||||
|
||||
const BlockedDomains = ({
|
||||
|
||||
@@ -25,7 +25,7 @@ const countCell = (dnsQueries) => function cell(row) {
|
||||
const percent = getPercent(dnsQueries, value);
|
||||
const percentColor = getClientsPercentColor(percent);
|
||||
|
||||
return <Cell value={value} percent={percent} color={percentColor} />;
|
||||
return <Cell value={value} percent={percent} color={percentColor} search={row.original.ip} />;
|
||||
};
|
||||
|
||||
const renderBlockingButton = (ipMatchListStatus, ip, handleClick, processing) => {
|
||||
@@ -62,7 +62,7 @@ const clientCell = (t, toggleClientStatus, processing, disallowedClients) => fun
|
||||
return (
|
||||
<>
|
||||
<div className="logs__row logs__row--overflow logs__row--column">
|
||||
{formatClientCell(row, t)}
|
||||
{formatClientCell(row, true, false)}
|
||||
</div>
|
||||
{ipMatchListStatus !== IP_MATCH_LIST_STATUS.CIDR
|
||||
&& renderBlockingButton(ipMatchListStatus, value, toggleClientStatus, processing)}
|
||||
|
||||
@@ -1,31 +1,85 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import propTypes from 'prop-types';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import round from 'lodash/round';
|
||||
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import Card from '../ui/Card';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
import LogsSearchLink from '../ui/LogsSearchLink';
|
||||
import { RESPONSE_FILTER } from '../../helpers/constants';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
|
||||
const tooltipType = 'tooltip-custom--narrow';
|
||||
const Row = ({
|
||||
label, count, response_status, tooltipTitle, translationComponents,
|
||||
}) => {
|
||||
const content = response_status
|
||||
? <LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
|
||||
: count;
|
||||
|
||||
const Counters = (props) => {
|
||||
return <tr key={label}>
|
||||
<td>
|
||||
<Trans components={translationComponents}>{label}</Trans>
|
||||
<Tooltip content={tooltipTitle} placement="top"
|
||||
className="tooltip-container tooltip-custom--narrow text-center">
|
||||
<svg className="icons icon--20 icon--lightgray ml-1">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</td>
|
||||
<td className="text-right"><strong>{content}</strong></td>
|
||||
</tr>;
|
||||
};
|
||||
|
||||
const Counters = ({ refreshButton, subtitle }) => {
|
||||
const {
|
||||
t,
|
||||
interval,
|
||||
refreshButton,
|
||||
subtitle,
|
||||
dnsQueries,
|
||||
blockedFiltering,
|
||||
replacedSafebrowsing,
|
||||
replacedParental,
|
||||
replacedSafesearch,
|
||||
numDnsQueries,
|
||||
numBlockedFiltering,
|
||||
numReplacedSafebrowsing,
|
||||
numReplacedParental,
|
||||
numReplacedSafesearch,
|
||||
avgProcessingTime,
|
||||
} = props;
|
||||
} = useSelector((state) => state.stats, shallowEqual);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tooltipTitle = interval === 1
|
||||
? t('number_of_dns_query_24_hours')
|
||||
: t('number_of_dns_query_days', { count: interval });
|
||||
const rows = [
|
||||
{
|
||||
label: 'dns_query',
|
||||
count: numDnsQueries,
|
||||
tooltipTitle: interval === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: interval }),
|
||||
response_status: RESPONSE_FILTER.ALL.query,
|
||||
},
|
||||
{
|
||||
label: 'blocked_by',
|
||||
count: numBlockedFiltering,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
|
||||
response_status: RESPONSE_FILTER.BLOCKED.query,
|
||||
translationComponents: [<a href="#filters" key="0">link</a>],
|
||||
},
|
||||
{
|
||||
label: 'stats_malware_phishing',
|
||||
count: numReplacedSafebrowsing,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_THREATS.query,
|
||||
},
|
||||
{
|
||||
label: 'stats_adult',
|
||||
count: numReplacedParental,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.query,
|
||||
},
|
||||
{
|
||||
label: 'enforced_save_search',
|
||||
count: numReplacedSafesearch,
|
||||
tooltipTitle: 'number_of_dns_query_to_safe_search',
|
||||
response_status: RESPONSE_FILTER.SAFE_SEARCH.query,
|
||||
},
|
||||
{
|
||||
label: 'average_processing_time',
|
||||
count: avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0,
|
||||
tooltipTitle: 'average_processing_time_hint',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -35,104 +89,23 @@ const Counters = (props) => {
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<table className="table card-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>dns_query</Trans>
|
||||
<Tooltip text={tooltipTitle} type={tooltipType} />
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(dnsQueries)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans components={[<a href="#filters" key="0">link</a>]}>
|
||||
blocked_by
|
||||
</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(blockedFiltering)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>stats_malware_phishing</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours_by_sec')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedSafebrowsing)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>stats_adult</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours_adult')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedParental)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>enforced_save_search</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_to_safe_search')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedSafesearch)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>average_processing_time</Trans>
|
||||
<Tooltip text={t('average_processing_time_hint')} type={tooltipType} />
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>{rows.map(Row)}</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
Counters.propTypes = {
|
||||
dnsQueries: PropTypes.number.isRequired,
|
||||
blockedFiltering: PropTypes.number.isRequired,
|
||||
replacedSafebrowsing: PropTypes.number.isRequired,
|
||||
replacedParental: PropTypes.number.isRequired,
|
||||
replacedSafesearch: PropTypes.number.isRequired,
|
||||
avgProcessingTime: PropTypes.number.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
interval: PropTypes.number.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
Row.propTypes = {
|
||||
label: propTypes.string.isRequired,
|
||||
count: propTypes.string.isRequired,
|
||||
response_status: propTypes.string,
|
||||
tooltipTitle: propTypes.string.isRequired,
|
||||
translationComponents: propTypes.arrayOf(propTypes.element),
|
||||
};
|
||||
|
||||
export default withTranslation()(Counters);
|
||||
Counters.propTypes = {
|
||||
refreshButton: propTypes.node.isRequired,
|
||||
subtitle: propTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Counters;
|
||||
|
||||
@@ -1,18 +1,67 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getTrackerData } from '../../helpers/trackers/trackers';
|
||||
import Popover from '../ui/Popover';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { getSourceData, getTrackerData } from '../../helpers/trackers/trackers';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
import { captitalizeWords } from '../../helpers/helpers';
|
||||
|
||||
const renderLabel = (value) => <strong><Trans>{value}</Trans></strong>;
|
||||
|
||||
const renderLink = ({ url, name }) => <a
|
||||
className="tooltip-custom__content-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={url}
|
||||
>
|
||||
<strong>{name}</strong>
|
||||
</a>;
|
||||
|
||||
|
||||
const getTrackerInfo = (trackerData) => [{
|
||||
key: 'name_table_header',
|
||||
value: trackerData,
|
||||
render: renderLink,
|
||||
},
|
||||
{
|
||||
key: 'category_label',
|
||||
value: captitalizeWords(trackerData.category),
|
||||
render: renderLabel,
|
||||
},
|
||||
{
|
||||
key: 'source_label',
|
||||
value: getSourceData(trackerData),
|
||||
render: renderLink,
|
||||
}];
|
||||
|
||||
const DomainCell = ({ value }) => {
|
||||
const trackerData = getTrackerData(value);
|
||||
|
||||
const content = trackerData && <div className="popover__list">
|
||||
<div className="tooltip-custom__content-title mb-1">
|
||||
<Trans>found_in_known_domain_db</Trans>
|
||||
</div>
|
||||
{getTrackerInfo(trackerData)
|
||||
.map(({ key, value, render }) => <div
|
||||
key={key}
|
||||
className="tooltip-custom__content-item"
|
||||
>
|
||||
<Trans>{key}</Trans>: {render(value)}
|
||||
</div>)}
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<div className="logs__row">
|
||||
<div className="logs__text logs__text--domain" title={value}>
|
||||
<div className="logs__text" title={value}>
|
||||
{value}
|
||||
</div>
|
||||
{trackerData && <Popover data={trackerData} />}
|
||||
{trackerData
|
||||
&& <Tooltip content={content} placement="top"
|
||||
className="tooltip-container tooltip-custom--wide">
|
||||
<svg className="icons icon--24 icon--green ml-1">
|
||||
<use xlinkHref="#privacy" />
|
||||
</svg>
|
||||
</Tooltip>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -21,4 +70,9 @@ DomainCell.propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
renderLink.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default DomainCell;
|
||||
|
||||
@@ -13,7 +13,8 @@ import { getPercent } from '../../helpers/helpers';
|
||||
const getQueriedPercentColor = (percent) => {
|
||||
if (percent > 10) {
|
||||
return STATUS_COLORS.red;
|
||||
} if (percent > 5) {
|
||||
}
|
||||
if (percent > 5) {
|
||||
return STATUS_COLORS.yellow;
|
||||
}
|
||||
return STATUS_COLORS.green;
|
||||
@@ -24,7 +25,8 @@ const countCell = (dnsQueries) => function cell(row) {
|
||||
const percent = getPercent(dnsQueries, value);
|
||||
const percentColor = getQueriedPercentColor(percent);
|
||||
|
||||
return <Cell value={value} percent={percent} color={percentColor} />;
|
||||
return <Cell value={value} percent={percent} color={percentColor}
|
||||
search={row.original.domain} />;
|
||||
};
|
||||
|
||||
const QueriedDomains = ({
|
||||
|
||||
@@ -111,13 +111,6 @@ class Dashboard extends Component {
|
||||
<div className="col-lg-6">
|
||||
<Counters
|
||||
subtitle={subtitle}
|
||||
interval={stats.interval}
|
||||
dnsQueries={stats.numDnsQueries}
|
||||
blockedFiltering={stats.numBlockedFiltering}
|
||||
replacedSafebrowsing={stats.numReplacedSafebrowsing}
|
||||
replacedParental={stats.numReplacedParental}
|
||||
replacedSafesearch={stats.numReplacedSafesearch}
|
||||
avgProcessingTime={stats.avgProcessingTime}
|
||||
refreshButton={refreshButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -63,11 +63,11 @@ class Table extends Component {
|
||||
defaultPageSize={10}
|
||||
minRows={5}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<svg className="icons icon--24 icon--gray">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<svg className="icons icon--24 icon--gray">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
|
||||
@@ -139,11 +139,11 @@ class Table extends Component {
|
||||
noDataText={whitelist ? t('no_whitelist_added') : t('no_blocklist_added')}
|
||||
getPaginationProps={() => ({ className: 'custom-pagination' })}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.nav-tabs .nav-link.active {
|
||||
border-color: #66b574;
|
||||
color: #66b574;
|
||||
border-color: var(--green-74);
|
||||
color: var(--green-74);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
.nav-tabs .nav-link.active .nav-icon,
|
||||
.nav-tabs .nav-item.show .nav-icon {
|
||||
stroke: #66b574;
|
||||
stroke: var(--green-74);
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active:hover .nav-icon,
|
||||
@@ -74,9 +74,9 @@
|
||||
}
|
||||
|
||||
.nav-tabs .nav-item.show .nav-link {
|
||||
color: #66b574;
|
||||
color: var(--green-74);
|
||||
background-color: #fff;
|
||||
border-bottom-color: #66b574;
|
||||
border-bottom-color: var(--green-74);
|
||||
}
|
||||
|
||||
.header__right {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import enhanceWithClickOutside from 'react-click-outside';
|
||||
import classnames from 'classnames';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import { SETTINGS_URLS, FILTERS_URLS, MENU_URLS } from '../../helpers/constants';
|
||||
import Dropdown from '../ui/Dropdown';
|
||||
|
||||
const MENU_ITEMS = [
|
||||
{
|
||||
route: MENU_URLS.root, exact: true, icon: 'dashboard', text: 'dashboard', order: 0,
|
||||
route: MENU_URLS.root,
|
||||
exact: true,
|
||||
icon: 'dashboard',
|
||||
text: 'dashboard',
|
||||
order: 0,
|
||||
},
|
||||
|
||||
// Settings dropdown should have visual order 1
|
||||
@@ -18,27 +21,63 @@ const MENU_ITEMS = [
|
||||
// Filters dropdown should have visual order 2
|
||||
|
||||
{
|
||||
route: MENU_URLS.logs, icon: 'log', text: 'query_log', order: 3,
|
||||
route: MENU_URLS.logs,
|
||||
icon: 'log',
|
||||
text: 'query_log',
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
route: MENU_URLS.guide, icon: 'setup', text: 'setup_guide', order: 4,
|
||||
route: MENU_URLS.guide,
|
||||
icon: 'setup',
|
||||
text: 'setup_guide',
|
||||
order: 4,
|
||||
},
|
||||
];
|
||||
|
||||
const SETTINGS_ITEMS = [
|
||||
{ route: SETTINGS_URLS.settings, text: 'general_settings' },
|
||||
{ route: SETTINGS_URLS.dns, text: 'dns_settings' },
|
||||
{ route: SETTINGS_URLS.encryption, text: 'encryption_settings' },
|
||||
{ route: SETTINGS_URLS.clients, text: 'client_settings' },
|
||||
{ route: SETTINGS_URLS.dhcp, text: 'dhcp_settings' },
|
||||
{
|
||||
route: SETTINGS_URLS.settings,
|
||||
text: 'general_settings',
|
||||
},
|
||||
{
|
||||
route: SETTINGS_URLS.dns,
|
||||
text: 'dns_settings',
|
||||
},
|
||||
{
|
||||
route: SETTINGS_URLS.encryption,
|
||||
text: 'encryption_settings',
|
||||
},
|
||||
{
|
||||
route: SETTINGS_URLS.clients,
|
||||
text: 'client_settings',
|
||||
},
|
||||
{
|
||||
route: SETTINGS_URLS.dhcp,
|
||||
text: 'dhcp_settings',
|
||||
},
|
||||
];
|
||||
|
||||
const FILTERS_ITEMS = [
|
||||
{ route: FILTERS_URLS.dns_blocklists, text: 'dns_blocklists' },
|
||||
{ route: FILTERS_URLS.dns_allowlists, text: 'dns_allowlists' },
|
||||
{ route: FILTERS_URLS.dns_rewrites, text: 'dns_rewrites' },
|
||||
{ route: FILTERS_URLS.blocked_services, text: 'blocked_services' },
|
||||
{ route: FILTERS_URLS.custom_rules, text: 'custom_filtering_rules' },
|
||||
{
|
||||
route: FILTERS_URLS.dns_blocklists,
|
||||
text: 'dns_blocklists',
|
||||
},
|
||||
{
|
||||
route: FILTERS_URLS.dns_allowlists,
|
||||
text: 'dns_allowlists',
|
||||
},
|
||||
{
|
||||
route: FILTERS_URLS.dns_rewrites,
|
||||
text: 'dns_rewrites',
|
||||
},
|
||||
{
|
||||
route: FILTERS_URLS.blocked_services,
|
||||
text: 'blocked_services',
|
||||
},
|
||||
{
|
||||
route: FILTERS_URLS.custom_rules,
|
||||
text: 'custom_filtering_rules',
|
||||
},
|
||||
];
|
||||
|
||||
class Menu extends Component {
|
||||
@@ -52,7 +91,8 @@ class Menu extends Component {
|
||||
|
||||
getActiveClassForDropdown = (URLS) => {
|
||||
const { pathname } = this.props.location;
|
||||
const isActivePage = Object.values(URLS).some((item) => item === pathname);
|
||||
const isActivePage = Object.values(URLS)
|
||||
.some((item) => item === pathname);
|
||||
|
||||
return isActivePage ? 'active' : '';
|
||||
};
|
||||
@@ -79,18 +119,18 @@ class Menu extends Component {
|
||||
getDropdown = ({
|
||||
label, order, URLS, icon, ITEMS,
|
||||
}) => (
|
||||
<Dropdown
|
||||
label={this.props.t(label)}
|
||||
baseClassName={`dropdown nav-item order-${order}`}
|
||||
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
|
||||
icon={icon}>
|
||||
{ITEMS.map((item) => (
|
||||
this.getNavLink({
|
||||
...item,
|
||||
order,
|
||||
className: 'dropdown-item',
|
||||
})))}
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
label={this.props.t(label)}
|
||||
baseClassName='dropdown'
|
||||
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
|
||||
icon={icon}>
|
||||
{ITEMS.map((item) => (
|
||||
this.getNavLink({
|
||||
...item,
|
||||
order,
|
||||
className: 'dropdown-item',
|
||||
})))}
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
render() {
|
||||
@@ -99,7 +139,7 @@ class Menu extends Component {
|
||||
'mobile-menu--active': this.props.isMenuOpen,
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<div className={menuClass}>
|
||||
<ul className="nav nav-tabs border-0 flex-column flex-lg-row flex-nowrap">
|
||||
{MENU_ITEMS.map((item) => (
|
||||
@@ -108,26 +148,33 @@ class Menu extends Component {
|
||||
key={item.text}
|
||||
onClick={this.closeMenu}
|
||||
>
|
||||
{this.getNavLink({ ...item, className: 'nav-link' })}
|
||||
{this.getNavLink({
|
||||
...item,
|
||||
className: 'nav-link',
|
||||
})}
|
||||
</li>
|
||||
))}
|
||||
{this.getDropdown({
|
||||
order: 1,
|
||||
label: 'settings',
|
||||
icon: 'settings',
|
||||
URLS: SETTINGS_URLS,
|
||||
ITEMS: SETTINGS_ITEMS,
|
||||
})}
|
||||
{this.getDropdown({
|
||||
order: 2,
|
||||
label: 'filters',
|
||||
icon: 'filters',
|
||||
URLS: FILTERS_URLS,
|
||||
ITEMS: FILTERS_ITEMS,
|
||||
})}
|
||||
<li className="nav-item order-1">
|
||||
{this.getDropdown({
|
||||
order: 1,
|
||||
label: 'settings',
|
||||
icon: 'settings',
|
||||
URLS: SETTINGS_URLS,
|
||||
ITEMS: SETTINGS_ITEMS,
|
||||
})}
|
||||
</li>
|
||||
<li className="nav-item order-2">
|
||||
{this.getDropdown({
|
||||
order: 2,
|
||||
label: 'filters',
|
||||
icon: 'filters',
|
||||
URLS: FILTERS_URLS,
|
||||
ITEMS: FILTERS_ITEMS,
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.tooltip__container {
|
||||
.tooltip-custom__container {
|
||||
padding: 1rem 1.5rem 1.25rem 1.5rem;
|
||||
font-size: 16px !important;
|
||||
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
|
||||
@@ -3,7 +3,7 @@ import { nanoid } from 'nanoid';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { formatClientCell } from '../../../helpers/formatClientCell';
|
||||
import getHintElement from './getHintElement';
|
||||
import getIconTooltip from './getIconTooltip';
|
||||
import { checkFiltered } from '../../../helpers/helpers';
|
||||
import { BLOCK_ACTIONS } from '../../../helpers/constants';
|
||||
|
||||
@@ -33,11 +33,11 @@ const getClientCell = ({
|
||||
const isFiltered = checkFiltered(reason);
|
||||
|
||||
const nameClass = classNames('w-90 o-hidden d-flex flex-column', {
|
||||
'mt-2': isDetailed && !name,
|
||||
'mt-2': isDetailed && !name && !whois_info,
|
||||
'white-space--nowrap': isDetailed,
|
||||
});
|
||||
|
||||
const hintClass = classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', {
|
||||
const hintClass = classNames('icons mr-4 icon--24 icon--lightgray', {
|
||||
'my-3': isDetailed,
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ const getClientCell = ({
|
||||
|
||||
return (
|
||||
<div className="logs__row o-hidden h-100">
|
||||
{processedData && getHintElement({
|
||||
{getIconTooltip({
|
||||
className: hintClass,
|
||||
columnClass: 'grid grid--limited',
|
||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75',
|
||||
@@ -80,9 +80,9 @@ const getClientCell = ({
|
||||
})}
|
||||
<div
|
||||
className={nameClass}>
|
||||
<div data-tip={true} data-for={id}>{formatClientCell(row, t, isDetailed)}</div>
|
||||
<div data-tip={true} data-for={id}>{formatClientCell(row, isDetailed)}</div>
|
||||
{isDetailed && name
|
||||
&& <div className="detailed-info d-none d-sm-block logs__text"
|
||||
&& !whois_info && <div className="detailed-info d-none d-sm-block logs__text"
|
||||
title={name}>{name}</div>}
|
||||
</div>
|
||||
{renderBlockingButton(isFiltered, domain)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import getHintElement from './getHintElement';
|
||||
import getIconTooltip from './getIconTooltip';
|
||||
import {
|
||||
DEFAULT_SHORT_DATE_FORMAT_OPTIONS,
|
||||
LONG_TIME_FORMAT,
|
||||
@@ -21,28 +21,18 @@ const getDomainCell = (props) => {
|
||||
|
||||
const hasTracker = !!tracker;
|
||||
|
||||
const lockIconClass = classNames('icons', 'icon--small', 'd-none', 'd-sm-block', 'cursor--pointer', {
|
||||
'icon--active': answer_dnssec,
|
||||
const lockIconClass = classNames('icons icon--24 d-none d-sm-block', {
|
||||
'icon--green': answer_dnssec,
|
||||
'icon--disabled': !answer_dnssec,
|
||||
'my-3': isDetailed,
|
||||
});
|
||||
|
||||
const privacyIconClass = classNames('icons', 'mx-2', 'icon--small', 'd-none', 'd-sm-block', 'cursor--pointer', {
|
||||
'icon--active': hasTracker,
|
||||
const privacyIconClass = classNames('icons mx-2 icon--24 d-none d-sm-block', {
|
||||
'icon--green': hasTracker,
|
||||
'icon--disabled': !hasTracker,
|
||||
'my-3': isDetailed,
|
||||
});
|
||||
|
||||
const dnssecHint = getHintElement({
|
||||
className: lockIconClass,
|
||||
tooltipClass: 'py-4 px-5 pb-45',
|
||||
canShowTooltip: answer_dnssec,
|
||||
xlinkHref: 'lock',
|
||||
columnClass: 'w-100',
|
||||
content: 'validated_with_dnssec',
|
||||
placement: 'bottom',
|
||||
});
|
||||
|
||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||
const ip = type ? `${t('type_table_header')}: ${type}` : '';
|
||||
|
||||
@@ -66,7 +56,7 @@ const getDomainCell = (props) => {
|
||||
|
||||
const renderGrid = (content, idx) => {
|
||||
const preparedContent = typeof content === 'string' ? t(content) : content;
|
||||
const className = classNames('text-truncate key-colon o-hidden', {
|
||||
const className = classNames('text-truncate o-hidden', {
|
||||
'overflow-break': preparedContent.length > 100,
|
||||
});
|
||||
return <div key={idx} className={className}>{preparedContent}</div>;
|
||||
@@ -82,7 +72,7 @@ const getDomainCell = (props) => {
|
||||
|
||||
const renderContent = hasTracker ? requestDetails.concat(getGrid(knownTrackerDataObj, 'known_tracker', 'pt-4')) : requestDetails;
|
||||
|
||||
const trackerHint = getHintElement({
|
||||
const trackerHint = getIconTooltip({
|
||||
className: privacyIconClass,
|
||||
tooltipClass: 'pt-4 pb-5 px-5 mw-75',
|
||||
xlinkHref: 'privacy',
|
||||
@@ -100,7 +90,15 @@ const getDomainCell = (props) => {
|
||||
|
||||
return (
|
||||
<div className="logs__row o-hidden">
|
||||
{dnssec_enabled && dnssecHint}
|
||||
{dnssec_enabled && getIconTooltip({
|
||||
className: lockIconClass,
|
||||
tooltipClass: 'py-4 px-5 pb-45',
|
||||
canShowTooltip: answer_dnssec,
|
||||
xlinkHref: 'lock',
|
||||
columnClass: 'w-100',
|
||||
content: 'validated_with_dnssec',
|
||||
placement: 'bottom',
|
||||
})}
|
||||
{trackerHint}
|
||||
<div className={valueClass}>
|
||||
<div className="text-truncate" title={domain}>{domain}</div>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TooltipTrigger from 'react-popper-tooltip';
|
||||
import { Trans } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
import './Tooltip.css';
|
||||
import 'react-popper-tooltip/dist/styles.css';
|
||||
import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants';
|
||||
|
||||
const getHintElement = ({
|
||||
className,
|
||||
contentItemClass,
|
||||
columnClass,
|
||||
canShowTooltip = true,
|
||||
xlinkHref,
|
||||
title,
|
||||
placement,
|
||||
tooltipClass,
|
||||
content,
|
||||
renderContent = React.Children.map(
|
||||
content,
|
||||
(item, idx) => <div key={idx} className={contentItemClass}>
|
||||
<Trans>{item || '—'}</Trans>
|
||||
</div>,
|
||||
),
|
||||
}) => <TooltipTrigger placement={placement} trigger="hover" delayHide={HIDE_TOOLTIP_DELAY} tooltip={
|
||||
({
|
||||
tooltipRef,
|
||||
getTooltipProps,
|
||||
}) => <div {...getTooltipProps({
|
||||
ref: tooltipRef,
|
||||
className: classNames('tooltip__container', tooltipClass, { 'd-none': !canShowTooltip }),
|
||||
})}
|
||||
>
|
||||
{title && <div className="pb-4 h-25 grid-content font-weight-bold">
|
||||
<Trans>{title}</Trans>
|
||||
</div>}
|
||||
<div className={classNames(columnClass)}>{renderContent}</div>
|
||||
</div>
|
||||
}>{({
|
||||
getTriggerProps, triggerRef,
|
||||
}) => <span {...getTriggerProps({ ref: triggerRef })}>
|
||||
{xlinkHref && <svg className={className}>
|
||||
<use xlinkHref={`#${xlinkHref}`} />
|
||||
</svg>}
|
||||
</span>}
|
||||
</TooltipTrigger>;
|
||||
|
||||
getHintElement.propTypes = {
|
||||
className: PropTypes.string,
|
||||
contentItemClass: PropTypes.string,
|
||||
columnClass: PropTypes.string,
|
||||
tooltipClass: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
placement: PropTypes.string,
|
||||
canShowTooltip: PropTypes.string,
|
||||
xlinkHref: PropTypes.string,
|
||||
content: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array,
|
||||
]),
|
||||
renderContent: PropTypes.arrayOf(PropTypes.element),
|
||||
};
|
||||
|
||||
export default getHintElement;
|
||||
62
client/src/components/Logs/Cells/getIconTooltip.js
Normal file
62
client/src/components/Logs/Cells/getIconTooltip.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
import { processContent } from '../../../helpers/helpers';
|
||||
import Tooltip from '../../ui/Tooltip';
|
||||
import 'react-popper-tooltip/dist/styles.css';
|
||||
import './IconTooltip.css';
|
||||
|
||||
const getIconTooltip = ({
|
||||
className,
|
||||
contentItemClass,
|
||||
columnClass,
|
||||
canShowTooltip = true,
|
||||
xlinkHref,
|
||||
title,
|
||||
placement,
|
||||
tooltipClass,
|
||||
content,
|
||||
renderContent = content ? React.Children.map(
|
||||
processContent(content),
|
||||
(item, idx) => <div key={idx} className={contentItemClass}>
|
||||
<Trans>{item || '—'}</Trans>
|
||||
</div>,
|
||||
) : null,
|
||||
}) => {
|
||||
const tooltipContent = <>
|
||||
{title
|
||||
&& <div className="pb-4 h-25 grid-content font-weight-bold"><Trans>{title}</Trans></div>}
|
||||
<div className={classNames(columnClass)}>{renderContent}</div>
|
||||
</>;
|
||||
|
||||
const tooltipClassName = classNames('tooltip-custom__container', tooltipClass, { 'd-none': !canShowTooltip });
|
||||
|
||||
return <Tooltip
|
||||
className={tooltipClassName}
|
||||
content={tooltipContent}
|
||||
placement={placement}
|
||||
>
|
||||
{xlinkHref && <svg className={className}>
|
||||
<use xlinkHref={`#${xlinkHref}`} />
|
||||
</svg>}
|
||||
</Tooltip>;
|
||||
};
|
||||
|
||||
getIconTooltip.propTypes = {
|
||||
className: PropTypes.string,
|
||||
contentItemClass: PropTypes.string,
|
||||
columnClass: PropTypes.string,
|
||||
tooltipClass: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
placement: PropTypes.string,
|
||||
canShowTooltip: PropTypes.string,
|
||||
xlinkHref: PropTypes.string,
|
||||
content: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array,
|
||||
]),
|
||||
renderContent: PropTypes.arrayOf(PropTypes.element),
|
||||
};
|
||||
|
||||
export default getIconTooltip;
|
||||
@@ -5,22 +5,27 @@ import {
|
||||
FILTERED_STATUS,
|
||||
FILTERED_STATUS_TO_META_MAP,
|
||||
} from '../../../helpers/constants';
|
||||
import getHintElement from './getHintElement';
|
||||
import getIconTooltip from './getIconTooltip';
|
||||
|
||||
const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||
const {
|
||||
reason, filterId, rule, status, upstream, elapsedMs, domain, response,
|
||||
reason, filterId, rule, status, upstream, elapsedMs, response, originalResponse,
|
||||
} = row.original;
|
||||
|
||||
const { filters, whitelistFilters } = filtering;
|
||||
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
||||
|
||||
const statusLabel = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
|
||||
|
||||
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
|
||||
|
||||
const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
const boldStatusLabel = <span className="font-weight-bold">{statusLabel}</span>;
|
||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||
|
||||
const renderResponses = (responseArr) => {
|
||||
if (responseArr.length === 0) {
|
||||
if (responseArr?.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -35,7 +40,6 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||
|
||||
const FILTERED_STATUS_TO_FIELDS_MAP = {
|
||||
[FILTERED_STATUS.NOT_FILTERED_NOT_FOUND]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
@@ -43,7 +47,15 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||
response_table_header: renderResponses(response),
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_BLOCKED_SERVICE]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
original_response: renderResponses(originalResponse),
|
||||
},
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
@@ -52,62 +64,52 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
response_table_header: renderResponses(response),
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_BLACK_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
original_response: renderResponses(originalResponse),
|
||||
},
|
||||
};
|
||||
|
||||
const fields = FILTERED_STATUS_TO_FIELDS_MAP[reason]
|
||||
const content = FILTERED_STATUS_TO_FIELDS_MAP[reason]
|
||||
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
|
||||
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
|
||||
|
||||
const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE
|
||||
|| reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||
? filter : formattedElapsedMs;
|
||||
const detailedInfo = isBlocked ? filter : formattedElapsedMs;
|
||||
|
||||
return (
|
||||
<div className="logs__row">
|
||||
{fields && getHintElement({
|
||||
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
|
||||
{getIconTooltip({
|
||||
className: classNames('icons mr-4 icon--24 icon--lightgray', { 'my-3': isDetailed }),
|
||||
columnClass: 'grid grid--limited',
|
||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
|
||||
contentItemClass: 'text-truncate key-colon o-hidden',
|
||||
xlinkHref: 'question',
|
||||
title: 'response_details',
|
||||
content: fields,
|
||||
content,
|
||||
placement: 'bottom',
|
||||
})}
|
||||
<div className="text-truncate">
|
||||
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
|
||||
{isDetailed && <div
|
||||
className="detailed-info d-none d-sm-block pt-1 text-truncate" title={detailedInfo}>{detailedInfo}</div>}
|
||||
className="detailed-info d-none d-sm-block pt-1 text-truncate"
|
||||
title={detailedInfo}>{detailedInfo}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,17 +2,20 @@ import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
DEBOUNCE_FILTER_TIMEOUT,
|
||||
DEFAULT_LOGS_FILTER,
|
||||
FORM_NAME,
|
||||
RESPONSE_FILTER,
|
||||
RESPONSE_FILTER_QUERIES,
|
||||
} from '../../../helpers/constants';
|
||||
import Tooltip from '../../ui/Tooltip';
|
||||
import { setLogsFilter } from '../../../actions/queryLogs';
|
||||
import useDebounce from '../../../helpers/useDebounce';
|
||||
import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers';
|
||||
import Tooltip from '../../ui/Tooltip';
|
||||
|
||||
const renderFilterField = ({
|
||||
input,
|
||||
@@ -25,34 +28,47 @@ const renderFilterField = ({
|
||||
tooltip,
|
||||
meta: { touched, error },
|
||||
onClearInputClick,
|
||||
}) => <>
|
||||
<div className="input-group-search input-group-search__icon--magnifier">
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<use xlinkHref="#magnifier" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
{...input}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
aria-label={placeholder} />
|
||||
<div
|
||||
className={classNames('input-group-search input-group-search__icon--cross', { invisible: input.value.length < 1 })}>
|
||||
<svg className="icons icon--smallest icon--gray" onClick={onClearInputClick}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="input-group-search input-group-search__icon--tooltip">
|
||||
<Tooltip text={tooltip} type='tooltip-custom--logs' />
|
||||
onKeyDown,
|
||||
normalizeOnBlur,
|
||||
}) => {
|
||||
const onBlur = (event) => createOnBlurHandler(event, input, normalizeOnBlur);
|
||||
|
||||
return <>
|
||||
<div className="input-group-search input-group-search__icon--magnifier">
|
||||
<svg className="icons icon--24 icon--gray">
|
||||
<use xlinkHref="#magnifier" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
{...input}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
aria-label={placeholder}
|
||||
onKeyDown={onKeyDown}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<div
|
||||
className={classNames('input-group-search input-group-search__icon--cross', { invisible: input.value.length < 1 })}>
|
||||
<svg className="icons icon--20 icon--gray" onClick={onClearInputClick}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="input-group-search input-group-search__icon--tooltip">
|
||||
<Tooltip content={tooltip} className="tooltip-container">
|
||||
<svg className="icons icon--20 icon--gray">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</span>
|
||||
{!disabled
|
||||
&& touched
|
||||
&& (error && <span className="form__message form__message--error">{error}</span>)}
|
||||
</>;
|
||||
{!disabled
|
||||
&& touched
|
||||
&& (error && <span className="form__message form__message--error">{error}</span>)}
|
||||
</>;
|
||||
};
|
||||
|
||||
renderFilterField.propTypes = {
|
||||
input: PropTypes.object.isRequired,
|
||||
@@ -64,65 +80,93 @@ renderFilterField.propTypes = {
|
||||
disabled: PropTypes.string,
|
||||
autoComplete: PropTypes.string,
|
||||
tooltip: PropTypes.string,
|
||||
onKeyDown: PropTypes.func,
|
||||
normalizeOnBlur: PropTypes.func,
|
||||
meta: PropTypes.shape({
|
||||
touched: PropTypes.bool,
|
||||
error: PropTypes.object,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
const FORM_NAMES = {
|
||||
search: 'search',
|
||||
response_status: 'response_status',
|
||||
};
|
||||
|
||||
const Form = (props) => {
|
||||
const {
|
||||
className = '',
|
||||
responseStatusClass,
|
||||
submit,
|
||||
reset,
|
||||
setIsLoading,
|
||||
change,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const debouncedSubmit = debounce(submit, DEBOUNCE_FILTER_TIMEOUT);
|
||||
const zeroDelaySubmit = () => setTimeout(submit, 0);
|
||||
const {
|
||||
response_status, search,
|
||||
} = useSelector((state) => state.form[FORM_NAME.LOGS_FILTER].values, shallowEqual);
|
||||
|
||||
const clearInput = async () => {
|
||||
await dispatch(setLogsFilter(DEFAULT_LOGS_FILTER));
|
||||
await reset();
|
||||
};
|
||||
const [
|
||||
debouncedSearch,
|
||||
setDebouncedSearch,
|
||||
] = useDebounce(search.trim(), DEBOUNCE_FILTER_TIMEOUT);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setLogsFilter({
|
||||
response_status,
|
||||
search: debouncedSearch,
|
||||
}));
|
||||
|
||||
history.replace(`${getLogsUrlParams(debouncedSearch, response_status)}`);
|
||||
}, [response_status, debouncedSearch]);
|
||||
|
||||
if (response_status && !(response_status in RESPONSE_FILTER_QUERIES)) {
|
||||
change(FORM_NAMES.response_status, DEFAULT_LOGS_FILTER[FORM_NAMES.response_status]);
|
||||
}
|
||||
|
||||
const onInputClear = async () => {
|
||||
setIsLoading(true);
|
||||
await clearInput();
|
||||
setDebouncedSearch(DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => clearInput, []);
|
||||
const onEnterPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setDebouncedSearch(search);
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeOnBlur = (data) => data.trim();
|
||||
|
||||
return (
|
||||
<form className="d-flex flex-wrap form-control--container"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
zeroDelaySubmit();
|
||||
debouncedSubmit.cancel();
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
id="search"
|
||||
name="search"
|
||||
component={renderFilterField}
|
||||
type="text"
|
||||
className={classNames('form-control--search form-control--transparent', className)}
|
||||
placeholder={t('domain_or_client')}
|
||||
tooltip={t('query_log_strict_search')}
|
||||
onChange={debouncedSubmit}
|
||||
onClearInputClick={onInputClear}
|
||||
/>
|
||||
<div className="field__search">
|
||||
<Field
|
||||
id={FORM_NAMES.search}
|
||||
name={FORM_NAMES.search}
|
||||
component={renderFilterField}
|
||||
type="text"
|
||||
className={classNames('form-control--search form-control--transparent', className)}
|
||||
placeholder={t('domain_or_client')}
|
||||
tooltip={t('query_log_strict_search')}
|
||||
onClearInputClick={onInputClear}
|
||||
onKeyDown={onEnterPress}
|
||||
normalizeOnBlur={normalizeOnBlur}
|
||||
/>
|
||||
</div>
|
||||
<div className="field__select">
|
||||
<Field
|
||||
name="response_status"
|
||||
name={FORM_NAMES.response_status}
|
||||
component="select"
|
||||
className={classNames('form-control custom-select custom-select--logs custom-select__arrow--left ml-small form-control--transparent', responseStatusClass)}
|
||||
onChange={zeroDelaySubmit}
|
||||
>
|
||||
{Object.values(RESPONSE_FILTER)
|
||||
.map(({
|
||||
@@ -136,14 +180,13 @@ const Form = (props) => {
|
||||
};
|
||||
|
||||
Form.propTypes = {
|
||||
handleChange: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
responseStatusClass: PropTypes.string,
|
||||
submit: PropTypes.func.isRequired,
|
||||
reset: PropTypes.func.isRequired,
|
||||
change: PropTypes.func.isRequired,
|
||||
setIsLoading: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default reduxForm({
|
||||
form: FORM_NAME.LOGS_FILTER,
|
||||
enableReinitialize: true,
|
||||
})(Form);
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import { setLogsFilter } from '../../../actions/queryLogs';
|
||||
|
||||
const Filters = ({ filter, refreshLogs, setIsLoading }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onSubmit = async (values) => {
|
||||
setIsLoading(true);
|
||||
await dispatch(setLogsFilter(values));
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
const Filters = ({ filter, refreshLogs, setIsLoading }) => (
|
||||
<div className="page-header page-header--logs">
|
||||
<h1 className="page-title page-title--large">
|
||||
<Trans>query_log</Trans>
|
||||
@@ -23,21 +12,18 @@ const Filters = ({ filter, refreshLogs, setIsLoading }) => {
|
||||
className="btn btn-icon--green ml-3 bg-transparent"
|
||||
onClick={refreshLogs}
|
||||
>
|
||||
<svg className="icons icon--small">
|
||||
<svg className="icons icon--24">
|
||||
<use xlinkHref="#update" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</h1>
|
||||
<Form
|
||||
responseStatusClass="d-sm-block"
|
||||
initialValues={filter}
|
||||
onSubmit={onSubmit}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
Filters.propTypes = {
|
||||
filter: PropTypes.object.isRequired,
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
}
|
||||
|
||||
.card-table .logs__row {
|
||||
overflow: visible;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.logs__row--center {
|
||||
@@ -57,10 +58,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logs__text--domain {
|
||||
max-width: 285px;
|
||||
}
|
||||
|
||||
.logs__text--wrap,
|
||||
.logs__text--whois {
|
||||
line-height: 1.4;
|
||||
@@ -202,6 +199,7 @@
|
||||
.logs__whois {
|
||||
display: inline;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.logs__whois::after {
|
||||
@@ -401,10 +399,6 @@
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
.icon--light-gray {
|
||||
color: var(--gray-8);
|
||||
}
|
||||
|
||||
.link--green {
|
||||
color: var(--green79);
|
||||
}
|
||||
@@ -455,15 +449,11 @@
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.ml-small {
|
||||
margin-left: 3.3125rem;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
width: 39.125rem;
|
||||
box-shadow: 0 1px 0 #ddd;
|
||||
padding: 0 2.5rem;
|
||||
height: 2.25rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.form-control--transparent {
|
||||
@@ -493,31 +483,12 @@
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 100%;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1279.98px) {
|
||||
.form-control--search {
|
||||
max-width: 30.125rem;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.form-control--search {
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 100%;
|
||||
}
|
||||
.field__search {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
@@ -528,6 +499,19 @@
|
||||
.ml-small {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.field__select {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
@@ -544,16 +528,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.form-control--search {
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
.field__select {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.loading__container > .-loading-inner {
|
||||
top: 10rem !important;
|
||||
bottom: initial !important;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
FILTERED_STATUS_TO_META_MAP,
|
||||
TABLE_DEFAULT_PAGE_SIZE,
|
||||
SCHEME_TO_PROTOCOL_MAP,
|
||||
CUSTOM_FILTERING_RULES_ID,
|
||||
CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS,
|
||||
} from '../../helpers/constants';
|
||||
import getDateCell from './Cells/getDateCell';
|
||||
import getDomainCell from './Cells/getDomainCell';
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
formatDateTime,
|
||||
formatElapsedMs,
|
||||
formatTime,
|
||||
|
||||
processContent,
|
||||
} from '../../helpers/helpers';
|
||||
import Loading from '../ui/Loading';
|
||||
import { getSourceData } from '../../helpers/trackers/trackers';
|
||||
@@ -49,7 +49,7 @@ const Table = (props) => {
|
||||
isLoading,
|
||||
} = props;
|
||||
|
||||
const [t] = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toggleBlocking = (type, domain) => {
|
||||
const {
|
||||
@@ -154,27 +154,23 @@ const Table = (props) => {
|
||||
headerClassName: 'logs__text',
|
||||
},
|
||||
{
|
||||
Header: () => {
|
||||
const plainSelected = classNames('cursor--pointer', {
|
||||
'icon--selected': !isDetailed,
|
||||
});
|
||||
|
||||
const detailedSelected = classNames('cursor--pointer', {
|
||||
'icon--selected': isDetailed,
|
||||
});
|
||||
|
||||
Header: function Header() {
|
||||
return <div className="d-flex justify-content-between">
|
||||
{t('client_table_header')}
|
||||
{<span>
|
||||
<svg
|
||||
className={`icons icon--small icon--active mr-2 cursor--pointer ${plainSelected}`}
|
||||
className={classNames('icons icon--24 icon--green mr-2 cursor--pointer', {
|
||||
'icon--selected': !isDetailed,
|
||||
})}
|
||||
onClick={() => toggleDetailedLogs(false)}
|
||||
>
|
||||
<title><Trans>compact</Trans></title>
|
||||
<use xlinkHref='#list' />
|
||||
</svg>
|
||||
<svg
|
||||
className={`icons icon--small icon--active cursor--pointer ${detailedSelected}`}
|
||||
className={classNames('icons icon--24 icon--green cursor--pointer', {
|
||||
'icon--selected': isDetailed,
|
||||
})}
|
||||
onClick={() => toggleDetailedLogs(true)}
|
||||
>
|
||||
<title><Trans>default</Trans></title>
|
||||
@@ -239,7 +235,7 @@ const Table = (props) => {
|
||||
sortable={false}
|
||||
resizable={false}
|
||||
data={logs || []}
|
||||
loading={isLoading}
|
||||
loading={isLoading || processingGetLogs}
|
||||
showPageJump={false}
|
||||
showPageSizeOptions={false}
|
||||
onPageChange={changePage}
|
||||
@@ -261,12 +257,12 @@ const Table = (props) => {
|
||||
getPaginationProps={() => ({ className: 'custom-pagination custom-pagination--padding' })}
|
||||
getTbodyProps={() => ({ className: 'd-block' })}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
|
||||
<title><Trans>previous_btn</Trans></title>
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
|
||||
<title><Trans>next_btn</Trans></title>
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
@@ -300,6 +296,9 @@ const Table = (props) => {
|
||||
type,
|
||||
client_proto,
|
||||
filterId,
|
||||
rule,
|
||||
originalResponse,
|
||||
status,
|
||||
} = rowInfo.original;
|
||||
|
||||
const hasTracker = !!tracker;
|
||||
@@ -317,22 +316,29 @@ const Table = (props) => {
|
||||
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
||||
const isFiltered = checkFiltered(reason);
|
||||
|
||||
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
|
||||
|
||||
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
||||
const onToggleBlock = () => {
|
||||
toggleBlocking(buttonType, domain);
|
||||
};
|
||||
|
||||
const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
const statusBlocked = <div className="bg--danger">{status}</div>;
|
||||
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
|
||||
const requestStatus = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
|
||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||
|
||||
const sourceData = getSourceData(tracker);
|
||||
|
||||
const { filters, whitelistFilters } = filtering;
|
||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||
|
||||
const detailedData = {
|
||||
time_table_header: formatTime(time, LONG_TIME_FORMAT),
|
||||
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
|
||||
encryption_status: status,
|
||||
encryption_status: isBlocked
|
||||
? <div className="bg--danger">{requestStatus}</div> : requestStatus,
|
||||
domain,
|
||||
type_table_header: type,
|
||||
protocol,
|
||||
@@ -340,12 +346,19 @@ const Table = (props) => {
|
||||
table_name: tracker?.name,
|
||||
category_label: hasTracker && captitalizeWords(tracker.category),
|
||||
tracker_source: hasTracker && sourceData
|
||||
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}</a>,
|
||||
&& <a
|
||||
href={sourceData.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}
|
||||
</a>,
|
||||
response_details: 'title',
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter: isBlocked ? filter : null,
|
||||
rule_label: rule,
|
||||
response_table_header: response?.join('\n'),
|
||||
response_code: status,
|
||||
client_details: 'title',
|
||||
ip_address: client,
|
||||
name: info?.name,
|
||||
@@ -354,39 +367,14 @@ const Table = (props) => {
|
||||
network,
|
||||
source_label: source,
|
||||
validated_with_dnssec: dnssec_enabled ? Boolean(answer_dnssec) : false,
|
||||
original_response: originalResponse?.join('\n'),
|
||||
[buttonType]: <div onClick={onToggleBlock}
|
||||
className="title--border bg--danger text-center">{t(buttonType)}</div>,
|
||||
className={classNames('title--border text-center', {
|
||||
'bg--danger': isBlocked,
|
||||
})}>{t(buttonType)}</div>,
|
||||
};
|
||||
|
||||
const { filters, whitelistFilters } = filtering;
|
||||
|
||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||
|
||||
const detailedDataBlocked = {
|
||||
time_table_header: formatTime(time, LONG_TIME_FORMAT),
|
||||
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
|
||||
encryption_status: statusBlocked,
|
||||
domain,
|
||||
type_table_header: type,
|
||||
protocol,
|
||||
known_tracker: 'title',
|
||||
table_name: tracker?.name,
|
||||
category_label: hasTracker && captitalizeWords(tracker.category),
|
||||
source_label: hasTracker && sourceData
|
||||
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}</a>,
|
||||
response_details: 'title',
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
response_table_header: response?.join('\n'),
|
||||
[buttonType]: <div onClick={onToggleBlock}
|
||||
className="title--border text-center">{t(buttonType)}</div>,
|
||||
};
|
||||
|
||||
const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData;
|
||||
|
||||
setDetailedDataCurrent(detailedDataCurrent);
|
||||
setDetailedDataCurrent(processContent(detailedData));
|
||||
setButtonType(buttonType);
|
||||
setModalOpened(true);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ import React, { Fragment, useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import Modal from 'react-modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
BLOCK_ACTIONS, smallScreenSize,
|
||||
BLOCK_ACTIONS,
|
||||
TABLE_DEFAULT_PAGE_SIZE,
|
||||
TABLE_FIRST_PAGE,
|
||||
smallScreenSize,
|
||||
} from '../../helpers/constants';
|
||||
import Loading from '../ui/Loading';
|
||||
import Filters from './Filters';
|
||||
@@ -15,15 +19,21 @@ import Disabled from './Disabled';
|
||||
import { getFilteringStatus } from '../../actions/filtering';
|
||||
import { getClients } from '../../actions';
|
||||
import { getDnsConfig } from '../../actions/dnsConfig';
|
||||
import { getLogsConfig } from '../../actions/queryLogs';
|
||||
import {
|
||||
getLogsConfig,
|
||||
refreshFilteredLogs,
|
||||
resetFilteredLogs,
|
||||
setFilteredLogs,
|
||||
} from '../../actions/queryLogs';
|
||||
import { addSuccessToast } from '../../actions/toasts';
|
||||
import './Logs.css';
|
||||
|
||||
const INITIAL_REQUEST = true;
|
||||
const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST];
|
||||
|
||||
export const processContent = (data, buttonType) => Object.entries(data)
|
||||
const processContent = (data, buttonType) => Object.entries(data)
|
||||
.map(([key, value]) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isTitle = value === 'title';
|
||||
const isButton = key === buttonType;
|
||||
const isBoolean = typeof value === 'boolean';
|
||||
@@ -40,7 +50,9 @@ export const processContent = (data, buttonType) => Object.entries(data)
|
||||
|
||||
return isHidden ? null : <Fragment key={key}>
|
||||
<div
|
||||
className={`key__${key} ${keyClass} ${(isBoolean && value === true) ? 'font-weight-bold' : ''}`}>
|
||||
className={classNames(`key__${key}`, keyClass, {
|
||||
'font-weight-bold': isBoolean && value === true,
|
||||
})}>
|
||||
<Trans>{isButton ? value : key}</Trans>
|
||||
</div>
|
||||
<div className={`value__${key} text-pre text-truncate`}>
|
||||
@@ -52,22 +64,44 @@ export const processContent = (data, buttonType) => Object.entries(data)
|
||||
|
||||
const Logs = (props) => {
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const {
|
||||
response_status: response_status_url_param = '',
|
||||
search: search_url_param = '',
|
||||
} = queryString.parse(history.location.search);
|
||||
|
||||
const { filter } = useSelector((state) => state.queryLogs, shallowEqual);
|
||||
|
||||
const search = filter?.search || search_url_param;
|
||||
const response_status = filter?.response_status || response_status_url_param;
|
||||
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < smallScreenSize);
|
||||
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
|
||||
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
|
||||
const [isModalOpened, setModalOpened] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setIsLoading(true);
|
||||
await dispatch(setFilteredLogs({
|
||||
search,
|
||||
response_status,
|
||||
}));
|
||||
setIsLoading(false);
|
||||
})();
|
||||
}, [response_status, search]);
|
||||
|
||||
const {
|
||||
filtering,
|
||||
setLogsPage,
|
||||
setLogsPagination,
|
||||
setLogsFilter,
|
||||
toggleDetailedLogs,
|
||||
dashboard,
|
||||
dnsConfig,
|
||||
queryLogs: {
|
||||
filter,
|
||||
enabled,
|
||||
processingGetConfig,
|
||||
processingAdditionalLogs,
|
||||
@@ -88,16 +122,10 @@ const Logs = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
mediaQuery.addListener(mediaQueryHandler);
|
||||
|
||||
return () => mediaQuery.removeListener(mediaQueryHandler);
|
||||
}, []);
|
||||
|
||||
const closeModal = () => setModalOpened(false);
|
||||
|
||||
const getLogs = (older_than, page, initial) => {
|
||||
if (props.queryLogs.enabled) {
|
||||
if (enabled) {
|
||||
props.getLogs({
|
||||
older_than,
|
||||
page,
|
||||
@@ -108,6 +136,17 @@ const Logs = (props) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
mediaQuery.addEventListener('change', mediaQueryHandler);
|
||||
} catch (e1) {
|
||||
try {
|
||||
// Safari 13.1 do not support mediaQuery.addEventListener('change', handler)
|
||||
mediaQuery.addListener(mediaQueryHandler);
|
||||
} catch (e2) {
|
||||
console.error(e2);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
setIsLoading(true);
|
||||
dispatch(setLogsPage(TABLE_FIRST_PAGE));
|
||||
@@ -115,7 +154,6 @@ const Logs = (props) => {
|
||||
dispatch(getClients());
|
||||
try {
|
||||
await Promise.all([
|
||||
getLogs(...INITIAL_REQUEST_DATA),
|
||||
dispatch(getLogsConfig()),
|
||||
dispatch(getDnsConfig()),
|
||||
]);
|
||||
@@ -125,13 +163,27 @@ const Logs = (props) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
try {
|
||||
mediaQuery.removeEventListener('change', mediaQueryHandler);
|
||||
} catch (e1) {
|
||||
try {
|
||||
mediaQuery.removeListener(mediaQueryHandler);
|
||||
} catch (e2) {
|
||||
console.error(e2);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(resetFilteredLogs());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refreshLogs = async () => {
|
||||
setIsLoading(true);
|
||||
await Promise.all([
|
||||
dispatch(setLogsPage(TABLE_FIRST_PAGE)),
|
||||
getLogs(...INITIAL_REQUEST_DATA),
|
||||
dispatch(refreshFilteredLogs()),
|
||||
]);
|
||||
dispatch(addSuccessToast('query_log_updated'));
|
||||
setIsLoading(false);
|
||||
@@ -141,13 +193,15 @@ const Logs = (props) => {
|
||||
<>
|
||||
{enabled && processingGetConfig && <Loading />}
|
||||
{enabled && !processingGetConfig && (
|
||||
<Fragment>
|
||||
<>
|
||||
<Filters
|
||||
filter={filter}
|
||||
filter={{
|
||||
response_status,
|
||||
search,
|
||||
}}
|
||||
setIsLoading={setIsLoading}
|
||||
processingGetLogs={processingGetLogs}
|
||||
processingAdditionalLogs={processingAdditionalLogs}
|
||||
setLogsFilter={setLogsFilter}
|
||||
refreshLogs={refreshLogs}
|
||||
/>
|
||||
<Table
|
||||
@@ -191,13 +245,13 @@ const Logs = (props) => {
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
className="icon icon--small icon-cross d-block d-md-none cursor--pointer"
|
||||
className="icon icon--24 icon-cross d-block d-md-none cursor--pointer"
|
||||
onClick={closeModal}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
{processContent(detailedDataCurrent, buttonType)}
|
||||
</Modal>
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
{!enabled && !processingGetConfig && (
|
||||
<Disabled />
|
||||
@@ -215,7 +269,6 @@ Logs.propTypes = {
|
||||
setRules: PropTypes.func.isRequired,
|
||||
addSuccessToast: PropTypes.func.isRequired,
|
||||
setLogsPagination: PropTypes.func.isRequired,
|
||||
setLogsFilter: PropTypes.func.isRequired,
|
||||
setLogsPage: PropTypes.func.isRequired,
|
||||
toggleDetailedLogs: PropTypes.func.isRequired,
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
|
||||
@@ -7,6 +7,7 @@ import Card from '../../ui/Card';
|
||||
import CellWrap from '../../ui/CellWrap';
|
||||
|
||||
import whoisCell from './whoisCell';
|
||||
import LogsSearchLink from '../../ui/LogsSearchLink';
|
||||
|
||||
const COLUMN_MIN_WIDTH = 200;
|
||||
|
||||
@@ -49,7 +50,9 @@ class AutoClients extends Component {
|
||||
return (
|
||||
<div className="logs__row">
|
||||
<div className="logs__text" title={clientStats}>
|
||||
{clientStats}
|
||||
<LogsSearchLink search={row.original.ip}>
|
||||
{clientStats}
|
||||
</LogsSearchLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -86,11 +89,11 @@ class AutoClients extends Component {
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
|
||||
@@ -4,10 +4,11 @@ import { Trans, withTranslation } from 'react-i18next';
|
||||
import ReactTable from 'react-table';
|
||||
|
||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||
import { normalizeTextarea } from '../../../helpers/helpers';
|
||||
import { splitByNewLine } from '../../../helpers/helpers';
|
||||
import Card from '../../ui/Card';
|
||||
import Modal from './Modal';
|
||||
import CellWrap from '../../ui/CellWrap';
|
||||
import LogsSearchLink from '../../ui/LogsSearchLink';
|
||||
|
||||
class ClientsTable extends Component {
|
||||
handleFormAdd = (values) => {
|
||||
@@ -29,7 +30,7 @@ class ClientsTable extends Component {
|
||||
}
|
||||
|
||||
if (values.upstreams && typeof values.upstreams === 'string') {
|
||||
config.upstreams = normalizeTextarea(values.upstreams);
|
||||
config.upstreams = splitByNewLine(values.upstreams);
|
||||
} else {
|
||||
config.upstreams = [];
|
||||
}
|
||||
@@ -49,7 +50,10 @@ class ClientsTable extends Component {
|
||||
};
|
||||
|
||||
getOptionsWithLabels = (options) => (
|
||||
options.map((option) => ({ value: option, label: option }))
|
||||
options.map((option) => ({
|
||||
value: option,
|
||||
label: option,
|
||||
}))
|
||||
);
|
||||
|
||||
getClient = (name, clients) => {
|
||||
@@ -203,7 +207,15 @@ class ClientsTable extends Component {
|
||||
accessor: (row) => this.props.normalizedTopClients.configured[row.name] || 0,
|
||||
sortMethod: (a, b) => b - a,
|
||||
minWidth: 120,
|
||||
Cell: CellWrap,
|
||||
Cell: (row) => {
|
||||
const content = CellWrap(row);
|
||||
|
||||
if (!row.value) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: this.props.t('actions_table_header'),
|
||||
@@ -289,11 +301,11 @@ class ClientsTable extends Component {
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
@@ -311,7 +323,6 @@ class ClientsTable extends Component {
|
||||
>
|
||||
<Trans>client_add</Trans>
|
||||
</button>
|
||||
|
||||
<Modal
|
||||
isModalOpen={isModalOpen}
|
||||
modalType={modalType}
|
||||
|
||||
@@ -88,7 +88,7 @@ const renderFieldsWrapper = (placeholder, buttonTitle) => function cell(row) {
|
||||
onClick={() => fields.push()}
|
||||
title={buttonTitle}
|
||||
>
|
||||
<svg className="icon icon--small">
|
||||
<svg className="icon icon--24">
|
||||
<use xlinkHref="#plus" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@@ -4,7 +4,10 @@ import { Field, reduxForm } from 'redux-form';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import { renderTextareaField } from '../../../../helpers/form';
|
||||
import { normalizeMultiline } from '../../../../helpers/helpers';
|
||||
import {
|
||||
trimMultilineString,
|
||||
removeEmptyLines,
|
||||
} from '../../../../helpers/helpers';
|
||||
import { FORM_NAME } from '../../../../helpers/constants';
|
||||
|
||||
const fields = [
|
||||
@@ -12,16 +15,19 @@ const fields = [
|
||||
id: 'allowed_clients',
|
||||
title: 'access_allowed_title',
|
||||
subtitle: 'access_allowed_desc',
|
||||
normalizeOnBlur: removeEmptyLines,
|
||||
},
|
||||
{
|
||||
id: 'disallowed_clients',
|
||||
title: 'access_disallowed_title',
|
||||
subtitle: 'access_disallowed_desc',
|
||||
normalizeOnBlur: trimMultilineString,
|
||||
},
|
||||
{
|
||||
id: 'blocked_hosts',
|
||||
title: 'access_blocked_title',
|
||||
subtitle: 'access_blocked_desc',
|
||||
normalizeOnBlur: removeEmptyLines,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -31,7 +37,7 @@ const Form = (props) => {
|
||||
} = props;
|
||||
|
||||
const renderField = ({
|
||||
id, title, subtitle, disabled = processingSet,
|
||||
id, title, subtitle, disabled = processingSet, normalizeOnBlur,
|
||||
}) => <div key={id} className="form__group mb-5">
|
||||
<label className="form__label form__label--with-desc" htmlFor={id}>
|
||||
<Trans>{title}</Trans>
|
||||
@@ -46,7 +52,7 @@ const Form = (props) => {
|
||||
type="text"
|
||||
className="form-control form-control--textarea font-monospace"
|
||||
disabled={disabled}
|
||||
normalizeOnBlur={id === 'disallowed_clients' ? normalizeMultiline : undefined}
|
||||
normalizeOnBlur={normalizeOnBlur}
|
||||
/>
|
||||
</div>;
|
||||
|
||||
@@ -55,6 +61,7 @@ const Form = (props) => {
|
||||
title: PropTypes.string,
|
||||
subtitle: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
normalizeOnBlur: PropTypes.func,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,9 +6,10 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Examples from './Examples';
|
||||
import { renderRadioField } from '../../../../helpers/form';
|
||||
import { renderRadioField, renderTextareaField } from '../../../../helpers/form';
|
||||
import { DNS_REQUEST_OPTIONS, FORM_NAME } from '../../../../helpers/constants';
|
||||
import { testUpstream } from '../../../../actions';
|
||||
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||
|
||||
const getInputFields = () => [{
|
||||
// eslint-disable-next-line react/display-name
|
||||
@@ -17,9 +18,10 @@ const getInputFields = () => [{
|
||||
</label>,
|
||||
name: 'upstream_dns',
|
||||
type: 'text',
|
||||
component: 'textarea',
|
||||
component: renderTextareaField,
|
||||
className: 'form-control form-control--textarea font-monospace',
|
||||
placeholder: 'upstream_dns',
|
||||
normalizeOnBlur: removeEmptyLines,
|
||||
},
|
||||
{
|
||||
name: 'upstream_mode',
|
||||
@@ -69,7 +71,8 @@ const Form = ({
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
{INPUT_FIELDS.map(({
|
||||
name, component, type, className, placeholder, getTitle, subtitle, disabled, value,
|
||||
name, component, type, className, placeholder,
|
||||
getTitle, subtitle, disabled, value, normalizeOnBlur,
|
||||
}) => <div className="col-12 mb-4" key={placeholder}>
|
||||
{typeof getTitle === 'function' && getTitle()}
|
||||
<Field
|
||||
@@ -82,6 +85,7 @@ const Form = ({
|
||||
placeholder={t(placeholder)}
|
||||
subtitle={t(subtitle)}
|
||||
disabled={processingSetConfig || processingTestUpstream || disabled}
|
||||
normalizeOnBlur={normalizeOnBlur}
|
||||
/>
|
||||
</div>)}
|
||||
<div className="col-12">
|
||||
@@ -101,11 +105,12 @@ const Form = ({
|
||||
<Field
|
||||
id="bootstrap_dns"
|
||||
name="bootstrap_dns"
|
||||
component="textarea"
|
||||
component={renderTextareaField}
|
||||
type="text"
|
||||
className="form-control form-control--textarea form-control--textarea-small font-monospace"
|
||||
placeholder={t('bootstrap_dns')}
|
||||
disabled={processingSetConfig}
|
||||
normalizeOnBlur={removeEmptyLines}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,10 @@ const Dns = (props) => {
|
||||
dnsConfig={dnsConfig}
|
||||
setDnsConfig={setDnsConfig}
|
||||
/>
|
||||
<Access access={access} setAccessList={setAccessList} />
|
||||
<Access
|
||||
access={access}
|
||||
setAccessList={setAccessList}
|
||||
/>
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import LogsSearchLink from './LogsSearchLink';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
|
||||
const Cell = ({ value, percent, color }) => (
|
||||
<div className="stats__row">
|
||||
<div className="stats__row-value mb-1">
|
||||
<strong>{formatNumber(value)}</strong>
|
||||
<small className="ml-3 text-muted">{percent}%</small>
|
||||
</div>
|
||||
<div className="progress progress-xs">
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={{
|
||||
width: `${percent}%`,
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
const Cell = ({
|
||||
value, percent, color, search,
|
||||
}) => <div className="stats__row">
|
||||
<div className="stats__row-value mb-1">
|
||||
<strong><LogsSearchLink search={search}>{formatNumber(value)}</LogsSearchLink></strong>
|
||||
<small className="ml-3 text-muted">{percent}%</small>
|
||||
</div>
|
||||
);
|
||||
<div className="progress progress-xs">
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={{
|
||||
width: `${percent}%`,
|
||||
backgroundColor: color,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
Cell.propTypes = {
|
||||
value: PropTypes.number.isRequired,
|
||||
percent: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired,
|
||||
search: PropTypes.string,
|
||||
onSearchRedirect: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Cell;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.dropdown-item.active,
|
||||
.dropdown-item:active {
|
||||
background-color: #66b574;
|
||||
background-color: var(--green-74);
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
||||
@@ -4,24 +4,36 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icon--small {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.icon--24 {
|
||||
--size: 1.5rem;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
.icon--smallest {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
.icon--20 {
|
||||
--size: 1.25rem;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
.icon--18 {
|
||||
--size: 1.125rem;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
.icon--gray {
|
||||
color: var(--gray-a5);
|
||||
}
|
||||
|
||||
.icon--green {
|
||||
color: var(--green-74);
|
||||
}
|
||||
|
||||
.icon--disabled {
|
||||
color: var(--gray-d8);
|
||||
}
|
||||
|
||||
.icon--active {
|
||||
color: #66b574;
|
||||
.icon--lightgray {
|
||||
color: var(--gray-8);
|
||||
}
|
||||
|
||||
7
client/src/components/ui/LogsSearchLink.css
Normal file
7
client/src/components/ui/LogsSearchLink.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.stats__link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.stats__link:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
33
client/src/components/ui/LogsSearchLink.js
Normal file
33
client/src/components/ui/LogsSearchLink.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import './LogsSearchLink.css';
|
||||
import { getLogsUrlParams } from '../../helpers/helpers';
|
||||
import { MENU_URLS } from '../../helpers/constants';
|
||||
|
||||
const LogsSearchLink = ({
|
||||
search = '', response_status = '', children, link = MENU_URLS.logs,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const to = link === MENU_URLS.logs ? `${MENU_URLS.logs}${getLogsUrlParams(search && `"${search}"`, response_status)}` : link;
|
||||
|
||||
return <Link to={to}
|
||||
className={'stats__link'}
|
||||
tabIndex={0}
|
||||
title={t('click_to_view_queries')}
|
||||
aria-label={t('click_to_view_queries')}>{children}</Link>;
|
||||
};
|
||||
|
||||
LogsSearchLink.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.element]).isRequired,
|
||||
search: PropTypes.string,
|
||||
response_status: PropTypes.string,
|
||||
link: PropTypes.string,
|
||||
};
|
||||
|
||||
export default LogsSearchLink;
|
||||
@@ -1,151 +0,0 @@
|
||||
.popover-wrap {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.popover__trigger {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popover__trigger:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: -3px;
|
||||
width: 26px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.popover__trigger--address {
|
||||
top: 0;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.popover__trigger--address:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popover__body {
|
||||
content: "";
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: calc(100% + 3px);
|
||||
left: 50%;
|
||||
z-index: 1;
|
||||
min-width: 275px;
|
||||
padding: 10px 15px;
|
||||
font-size: 0.8rem;
|
||||
white-space: normal;
|
||||
color: #fff;
|
||||
background-color: #585965;
|
||||
border-radius: 3px;
|
||||
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
|
||||
transform: translateX(-50%);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.popover__body--filter {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.popover__body:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 6px);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 6px solid #585965;
|
||||
}
|
||||
|
||||
.popover__body--address {
|
||||
top: calc(100% + 10px);
|
||||
right: 0;
|
||||
left: initial;
|
||||
bottom: initial;
|
||||
z-index: 1;
|
||||
min-width: 100px;
|
||||
padding: 12px 18px;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
transform: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.popover__body--address:after {
|
||||
top: -11px;
|
||||
left: initial;
|
||||
right: 40px;
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid #585965;
|
||||
}
|
||||
|
||||
.popover__body--address:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.popover__trigger:hover + .popover__body,
|
||||
.popover__body:hover {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popover__icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
stroke: #9aa0ac;
|
||||
color: #9aa0ac;
|
||||
}
|
||||
|
||||
.popover__icon--green {
|
||||
color: #66b574;
|
||||
stroke: #66b574;
|
||||
}
|
||||
|
||||
.popover__list--bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.popover__list-title {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.popover__list-item {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.popover__list-item--nowrap {
|
||||
max-width: 300px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.popover__list-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.popover__link {
|
||||
color: #66b586;
|
||||
}
|
||||
|
||||
.popover__link:hover,
|
||||
.popover__link:focus {
|
||||
color: #66b586;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { getSourceData } from '../../helpers/trackers/trackers';
|
||||
import { captitalizeWords } from '../../helpers/helpers';
|
||||
|
||||
import './Popover.css';
|
||||
|
||||
class Popover extends Component {
|
||||
render() {
|
||||
const { data } = this.props;
|
||||
|
||||
const sourceData = getSourceData(data);
|
||||
|
||||
const source = (
|
||||
<div className="popover__list-item">
|
||||
<Trans>source_label</Trans>: <a className="popover__link" target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={sourceData.url}>
|
||||
<strong>{sourceData.name}</strong>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
const tracker = (
|
||||
<div className="popover__list-item">
|
||||
<Trans>name_table_header</Trans>: <a className="popover__link" target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={data.url}>
|
||||
<strong>{data.name}</strong>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
const categoryName = captitalizeWords(data.category);
|
||||
|
||||
return (
|
||||
<div className="popover-wrap">
|
||||
<div className="popover__trigger">
|
||||
<svg className="popover__icon" xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24" fill="none" strokeWidth="2" strokeLinecap="round"
|
||||
strokeLinejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="popover__body">
|
||||
<div className="popover__list">
|
||||
<div className="popover__list-title">
|
||||
<Trans>found_in_known_domain_db</Trans>
|
||||
</div>
|
||||
{tracker}
|
||||
<div className="popover__list-item">
|
||||
<Trans>category_label</Trans>: <strong>
|
||||
<Trans>{categoryName}</Trans></strong>
|
||||
</div>
|
||||
{source}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Popover.propTypes = {
|
||||
data: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Popover);
|
||||
@@ -1,52 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import './Popover.css';
|
||||
|
||||
class PopoverFilter extends Component {
|
||||
render() {
|
||||
const { rule, filter, service } = this.props;
|
||||
|
||||
if (!rule && !service) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="popover-wrap">
|
||||
<div className="popover__trigger popover__trigger--filter">
|
||||
<svg className="popover__icon popover__icon--green">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="popover__body popover__body--filter">
|
||||
<div className="popover__list">
|
||||
{rule && (
|
||||
<div className="popover__list-item popover__list-item--nowrap">
|
||||
<Trans>rule_label</Trans>: <strong>{rule}</strong>
|
||||
</div>
|
||||
)}
|
||||
{filter && (
|
||||
<div className="popover__list-item popover__list-item--nowrap">
|
||||
<Trans>list_label</Trans>: <strong>{filter}</strong>
|
||||
</div>
|
||||
)}
|
||||
{service && (
|
||||
<div className="popover__list-item popover__list-item--nowrap">
|
||||
<Trans>blocked_service</Trans>: <strong>{service}</strong>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PopoverFilter.propTypes = {
|
||||
rule: PropTypes.string,
|
||||
filter: PropTypes.string,
|
||||
service: PropTypes.string,
|
||||
};
|
||||
|
||||
export default withTranslation()(PopoverFilter);
|
||||
@@ -19,10 +19,13 @@ Dashboard UI
|
||||
--orange: #fd9644;
|
||||
--yellow: #f1c40f;
|
||||
--green: #5eba00;
|
||||
--green-74: #66b574;
|
||||
--green-86: #66b586;
|
||||
--teal: #2bcbba;
|
||||
--cyan: #17a2b8;
|
||||
--white: #fff;
|
||||
--gray: #868e96;
|
||||
--gray-ac: #9aa0ac;
|
||||
--gray-dark: #343a40;
|
||||
--azure: #45aaf2;
|
||||
--lime: #7bd235;
|
||||
|
||||
@@ -1,73 +1,27 @@
|
||||
.tooltip-custom {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
background-image: url("./svg/help-circle.svg");
|
||||
background-size: 100%;
|
||||
.tooltip-custom--narrow {
|
||||
max-width: 13.75rem;
|
||||
}
|
||||
|
||||
.tooltip-custom--wide {
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.tooltip-custom__trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tooltip-custom:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: calc(100% + 10px);
|
||||
left: 50%;
|
||||
padding: 10px 15px;
|
||||
font-size: 0.85rem;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background-color: #585965;
|
||||
border-radius: 3px;
|
||||
transform: translateX(-50%);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
.tooltip-custom__content-title {
|
||||
margin-bottom: 0.1875rem;
|
||||
}
|
||||
|
||||
.tooltip-custom:after {
|
||||
content: "";
|
||||
position: relative;
|
||||
top: -7px;
|
||||
left: calc(50% - 6px);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 6px solid #585965;
|
||||
.tooltip-custom__content-item {
|
||||
margin-bottom: 0.125rem;
|
||||
}
|
||||
|
||||
.tooltip-custom:hover:before,
|
||||
.tooltip-custom:hover:after {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
.tooltip-custom__content-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tooltip-custom--narrow:before {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.tooltip-custom--logs {
|
||||
border-radius: 50%;
|
||||
background-image: url("./svg/help-circle-gray.svg");
|
||||
}
|
||||
|
||||
.tooltip-custom--logs:before {
|
||||
bottom: initial;
|
||||
top: calc(100% + 10px);
|
||||
right: -10px;
|
||||
left: initial;
|
||||
width: 255px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.tooltip-custom--logs:after {
|
||||
top: 8px;
|
||||
border-top: none;
|
||||
border-bottom: 6px solid #585965;
|
||||
.tooltip-custom__content-link {
|
||||
color: var(--green-86);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,58 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TooltipTrigger from 'react-popper-tooltip';
|
||||
import propTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { HIDE_TOOLTIP_DELAY } from '../../helpers/constants';
|
||||
import 'react-popper-tooltip/dist/styles.css';
|
||||
import './Tooltip.css';
|
||||
|
||||
const Tooltip = ({ text, type = '' }) => <div data-tooltip={text}
|
||||
className={`tooltip-custom ml-1 ${type}`} />;
|
||||
const Tooltip = ({
|
||||
children,
|
||||
content,
|
||||
triggerClass = 'tooltip-custom__trigger',
|
||||
className = 'tooltip-container',
|
||||
placement = 'bottom',
|
||||
trigger = 'hover',
|
||||
delayHide = HIDE_TOOLTIP_DELAY,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return <TooltipTrigger
|
||||
placement={placement}
|
||||
trigger={trigger}
|
||||
delayHide={delayHide}
|
||||
tooltip={({
|
||||
tooltipRef,
|
||||
getTooltipProps,
|
||||
}) => <div {...getTooltipProps({
|
||||
ref: tooltipRef,
|
||||
className,
|
||||
})}>
|
||||
{typeof content === 'string' ? t(content) : content}
|
||||
</div>
|
||||
}>{({ getTriggerProps, triggerRef }) => <span
|
||||
{...getTriggerProps({
|
||||
ref: triggerRef,
|
||||
className: triggerClass,
|
||||
})}
|
||||
>{children}</span>}
|
||||
</TooltipTrigger>;
|
||||
};
|
||||
|
||||
Tooltip.propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
type: PropTypes.string,
|
||||
children: propTypes.element.isRequired,
|
||||
content: propTypes.oneOfType(
|
||||
[
|
||||
propTypes.string,
|
||||
propTypes.element,
|
||||
propTypes.arrayOf(propTypes.element),
|
||||
],
|
||||
).isRequired,
|
||||
placement: propTypes.string,
|
||||
trigger: propTypes.string,
|
||||
delayHide: propTypes.string,
|
||||
className: propTypes.string,
|
||||
triggerClass: propTypes.string,
|
||||
};
|
||||
|
||||
export default Tooltip;
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#66b574" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-help-circle"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12" y2="17"></line></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#66b574"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-help-circle">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
|
||||
<line x1="12" y1="17" x2="12" y2="17"></line>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 379 B |
@@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getFilteringStatus, setRules } from '../actions/filtering';
|
||||
import {
|
||||
getLogs, setLogsPagination, setLogsFilter, setLogsPage, toggleDetailedLogs,
|
||||
getLogs, setLogsPagination, setLogsPage, toggleDetailedLogs,
|
||||
} from '../actions/queryLogs';
|
||||
import Logs from '../components/Logs';
|
||||
import { addSuccessToast } from '../actions/toasts';
|
||||
@@ -26,7 +26,6 @@ const mapDispatchToProps = {
|
||||
setRules,
|
||||
addSuccessToast,
|
||||
setLogsPagination,
|
||||
setLogsFilter,
|
||||
setLogsPage,
|
||||
toggleDetailedLogs,
|
||||
};
|
||||
|
||||
@@ -35,6 +35,10 @@ export const REPOSITORY = {
|
||||
};
|
||||
|
||||
export const PRIVACY_POLICY_LINK = 'https://adguard.com/privacy/home.html';
|
||||
export const PORT_53_FAQ_LINK = 'https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse';
|
||||
|
||||
export const ADDRESS_IN_USE_TEXT = 'address already in use';
|
||||
export const UBUNTU_SYSTEM_PORT = 53;
|
||||
|
||||
export const INSTALL_FIRST_STEP = 1;
|
||||
export const INSTALL_TOTAL_STEPS = 5;
|
||||
@@ -356,7 +360,7 @@ export const FILTERED_STATUS = {
|
||||
export const RESPONSE_FILTER = {
|
||||
ALL: {
|
||||
query: 'all',
|
||||
label: 'show_all_responses',
|
||||
label: 'all_queries',
|
||||
},
|
||||
FILTERED: {
|
||||
query: 'filtered',
|
||||
@@ -397,6 +401,11 @@ export const RESPONSE_FILTER = {
|
||||
},
|
||||
};
|
||||
|
||||
export const RESPONSE_FILTER_QUERIES = Object.values(RESPONSE_FILTER).reduce((acc, { query }) => {
|
||||
acc[query] = query;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const FILTERED_STATUS_TO_META_MAP = {
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
label: RESPONSE_FILTER.ALLOWED.label,
|
||||
|
||||
@@ -147,8 +147,8 @@
|
||||
"CHN-anti-ad" : {
|
||||
"name": "CHN: anti-AD",
|
||||
"categoryId": "regional",
|
||||
"homepage": "https://github.com/privacy-protection-tools/anti-AD",
|
||||
"source": "https://gitee.com/privacy-protection-tools/anti-ad/raw/master/easylist.txt"
|
||||
"homepage": "https://anti-ad.net/",
|
||||
"source": "https://anti-ad.net/easylist.txt"
|
||||
},
|
||||
"BarbBlock": {
|
||||
"name": "BarbBlock",
|
||||
|
||||
@@ -90,7 +90,7 @@ export const renderGroupField = ({
|
||||
className="btn btn-secondary btn-icon btn-icon--green"
|
||||
onClick={removeField}
|
||||
>
|
||||
<svg className="icon icon--small">
|
||||
<svg className="icon icon--24">
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@@ -2,14 +2,14 @@ import React from 'react';
|
||||
import { normalizeWhois } from './helpers';
|
||||
import { WHOIS_ICONS } from './constants';
|
||||
|
||||
const getFormattedWhois = (whois, t) => {
|
||||
const getFormattedWhois = (whois) => {
|
||||
const whoisInfo = normalizeWhois(whois);
|
||||
return (
|
||||
Object.keys(whoisInfo)
|
||||
.map((key) => {
|
||||
const icon = WHOIS_ICONS[key];
|
||||
return (
|
||||
<span className="logs__whois text-muted" key={key} title={t(key)}>
|
||||
<span className="logs__whois text-muted " key={key} title={whoisInfo[key]}>
|
||||
{icon && (
|
||||
<>
|
||||
<svg className="logs__whois-icon icons">
|
||||
@@ -24,7 +24,7 @@ const getFormattedWhois = (whois, t) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const formatClientCell = (row, t, isDetailed = false) => {
|
||||
export const formatClientCell = (row, isDetailed = false, isLogs = true) => {
|
||||
const { value, original: { info } } = row;
|
||||
let whoisContainer = '';
|
||||
let nameContainer = value;
|
||||
@@ -33,26 +33,38 @@ export const formatClientCell = (row, t, isDetailed = false) => {
|
||||
const { name, whois_info } = info;
|
||||
|
||||
if (name) {
|
||||
nameContainer = isDetailed
|
||||
? <small title={value}>{value}</small>
|
||||
: <div className="logs__text logs__text--nowrap" title={`${name} (${value})`}>
|
||||
{name}
|
||||
{' '}
|
||||
<small>{`(${value})`}</small>
|
||||
</div>;
|
||||
if (isLogs) {
|
||||
nameContainer = !whois_info && isDetailed
|
||||
? (
|
||||
<small title={value}>{value}</small>
|
||||
) : (
|
||||
<div className="logs__text logs__text--nowrap" title={`${name} (${value})`}>
|
||||
{name} <small>{`(${value})`}</small>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
nameContainer = (
|
||||
<div
|
||||
className="logs__text logs__text--nowrap"
|
||||
title={`${name} (${value})`}
|
||||
>
|
||||
{name} <small>{`(${value})`}</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (whois_info) {
|
||||
if (whois_info && isDetailed) {
|
||||
whoisContainer = (
|
||||
<div className="logs__text logs__text--wrap logs__text--whois">
|
||||
{getFormattedWhois(whois_info, t)}
|
||||
{getFormattedWhois(whois_info)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="logs__text" title={value}>
|
||||
<div className="logs__text mw-100" title={value}>
|
||||
<>
|
||||
{nameContainer}
|
||||
{whoisContainer}
|
||||
|
||||
@@ -11,7 +11,7 @@ import axios from 'axios';
|
||||
import i18n from 'i18next';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import ipaddr from 'ipaddr.js';
|
||||
import versionCompare from './versionCompare';
|
||||
import queryString from 'query-string';
|
||||
import { getTrackerData } from './trackers/trackers';
|
||||
|
||||
import {
|
||||
@@ -86,18 +86,16 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||
|
||||
const { host: domain, type } = question;
|
||||
|
||||
const response = answer ? answer.map((response) => {
|
||||
const processResponse = (data) => (data ? data.map((response) => {
|
||||
const { value, type, ttl } = response;
|
||||
return `${type}: ${value} (ttl=${ttl})`;
|
||||
}) : [];
|
||||
|
||||
const tracker = getTrackerData(domain);
|
||||
}) : []);
|
||||
|
||||
return {
|
||||
time,
|
||||
domain,
|
||||
type,
|
||||
response,
|
||||
response: processResponse(answer),
|
||||
reason,
|
||||
client,
|
||||
client_proto,
|
||||
@@ -106,7 +104,8 @@ export const normalizeLogs = (logs) => logs.map((log) => {
|
||||
status,
|
||||
serviceName: service_name,
|
||||
originalAnswer: original_answer,
|
||||
tracker,
|
||||
originalResponse: processResponse(original_answer),
|
||||
tracker: getTrackerData(domain),
|
||||
answer_dnssec,
|
||||
elapsedMs,
|
||||
upstream,
|
||||
@@ -306,15 +305,27 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const normalizeTextarea = (text) => {
|
||||
if (!text) {
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns []string
|
||||
*/
|
||||
export const splitByNewLine = (text) => text.split('\n')
|
||||
.filter((n) => n.trim());
|
||||
|
||||
return text.replace(/[;, ]/g, '\n')
|
||||
.split('\n')
|
||||
.filter((n) => n);
|
||||
};
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
export const trimMultilineString = (text) => splitByNewLine(text)
|
||||
.map((line) => line.trim())
|
||||
.join('\n');
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
export const removeEmptyLines = (text) => splitByNewLine(text)
|
||||
.join('\n');
|
||||
|
||||
/**
|
||||
* Normalizes the topClients array
|
||||
@@ -406,10 +417,6 @@ export const secondsToMilliseconds = (seconds) => {
|
||||
export const normalizeRulesTextarea = (text) => text?.replace(/^\n/g, '')
|
||||
.replace(/\n\s*\n/g, '\n');
|
||||
|
||||
export const isVersionGreater = (currentVersion, previousVersion) => (
|
||||
versionCompare(currentVersion, previousVersion) === -1
|
||||
);
|
||||
|
||||
export const normalizeWhois = (whois) => {
|
||||
if (Object.keys(whois).length > 0) {
|
||||
const {
|
||||
@@ -533,10 +540,6 @@ export const getMap = (arr, key, value) => arr.reduce((acc, curr) => {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const normalizeMultiline = (multiline) => `${normalizeTextarea(multiline)
|
||||
.map((line) => line.trim())
|
||||
.join('\n')}\n`;
|
||||
|
||||
/**
|
||||
* @param parsedIp {object} ipaddr.js IPv4 or IPv6 object
|
||||
* @param cidr {array} ipaddr.js CIDR array
|
||||
@@ -618,3 +621,17 @@ export const selectCompletedFields = (values) => Object.entries(values)
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* @param {string} search
|
||||
* @param {string} [response_status]
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getLogsUrlParams = (search, response_status) => `?${queryString.stringify({
|
||||
search,
|
||||
response_status,
|
||||
})}`;
|
||||
|
||||
export const processContent = (content) => (Array.isArray(content)
|
||||
? content.filter(([, value]) => value)
|
||||
.flat() : content);
|
||||
|
||||
22
client/src/helpers/useDebounce.js
Normal file
22
client/src/helpers/useDebounce.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const useDebounce = (value, delay) => {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
},
|
||||
[value, delay],
|
||||
);
|
||||
|
||||
return [debouncedValue, setDebouncedValue];
|
||||
};
|
||||
|
||||
export default useDebounce;
|
||||
17
client/src/helpers/version.js
Normal file
17
client/src/helpers/version.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Checks if versions are equal.
|
||||
* Please note, that this method strips the "v" prefix.
|
||||
*
|
||||
* @param left {string} - left version
|
||||
* @param right {string} - right version
|
||||
* @return {boolean} true if versions are equal
|
||||
*/
|
||||
export const areEqualVersions = (left, right) => {
|
||||
if (!left || !right) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const leftVersion = left.replace(/^v/, '');
|
||||
const rightVersion = right.replace(/^v/, '');
|
||||
return leftVersion === rightVersion;
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Project: tiny-version-compare https://github.com/bfred-it/tiny-version-compare
|
||||
* License (MIT) https://github.com/bfred-it/tiny-version-compare/blob/master/LICENSE
|
||||
*/
|
||||
const split = (v) => String(v).replace(/^[vr]/, '') // Drop initial 'v' or 'r'
|
||||
.replace(/([a-z]+)/gi, '.$1.') // Sort each word separately
|
||||
.replace(/[-.]+/g, '.') // Consider dashes as separators (+ trim multiple separators)
|
||||
.split('.');
|
||||
|
||||
// Development versions are considered "negative",
|
||||
// but localeCompare doesn't handle negative numbers.
|
||||
// This offset is applied to reset the lowest development version to 0
|
||||
const offset = (part) => {
|
||||
// Not numeric, return as is
|
||||
if (Number.isNaN(part)) {
|
||||
return part;
|
||||
}
|
||||
return 5 + Number(part);
|
||||
};
|
||||
|
||||
const parsePart = (part) => {
|
||||
// Missing, consider it zero
|
||||
if (typeof part === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
// Sort development versions
|
||||
switch (part.toLowerCase()) {
|
||||
case 'dev':
|
||||
return -5;
|
||||
case 'alpha':
|
||||
return -4;
|
||||
case 'beta':
|
||||
return -3;
|
||||
case 'rc':
|
||||
return -2;
|
||||
case 'pre':
|
||||
return -1;
|
||||
default:
|
||||
}
|
||||
// Return as is, it’s either a plain number or text that will be sorted alphabetically
|
||||
return part;
|
||||
};
|
||||
|
||||
const versionCompare = (prev, next) => {
|
||||
const a = split(prev);
|
||||
const b = split(next);
|
||||
for (let i = 0; i < a.length || i < b.length; i += 1) {
|
||||
const ai = offset(parsePart(a[i]));
|
||||
const bi = offset(parsePart(b[i]));
|
||||
const sort = String(ai).localeCompare(bi, 'en', {
|
||||
numeric: true,
|
||||
});
|
||||
// Once the difference is found,
|
||||
// stop comparing the rest of the parts
|
||||
if (sort !== 0) {
|
||||
return sort;
|
||||
}
|
||||
}
|
||||
// No difference found
|
||||
return 0;
|
||||
};
|
||||
|
||||
export default versionCompare;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||
@@ -9,7 +9,9 @@ import Controls from './Controls';
|
||||
import AddressList from './AddressList';
|
||||
|
||||
import { getInterfaceIp } from '../../helpers/helpers';
|
||||
import { ALL_INTERFACES_IP, FORM_NAME } from '../../helpers/constants';
|
||||
import {
|
||||
ALL_INTERFACES_IP, FORM_NAME, ADDRESS_IN_USE_TEXT, PORT_53_FAQ_LINK, UBUNTU_SYSTEM_PORT,
|
||||
} from '../../helpers/constants';
|
||||
import { renderInputField, toNumber } from '../../helpers/form';
|
||||
import { validateRequiredValue, validateInstallPort } from '../../helpers/validators';
|
||||
|
||||
@@ -20,37 +22,38 @@ const STATIC_STATUS = {
|
||||
};
|
||||
|
||||
const renderInterfaces = ((interfaces) => (
|
||||
Object.keys(interfaces).map((item) => {
|
||||
const option = interfaces[item];
|
||||
const {
|
||||
name,
|
||||
ip_addresses,
|
||||
flags,
|
||||
} = option;
|
||||
Object.keys(interfaces)
|
||||
.map((item) => {
|
||||
const option = interfaces[item];
|
||||
const {
|
||||
name,
|
||||
ip_addresses,
|
||||
flags,
|
||||
} = option;
|
||||
|
||||
if (option && ip_addresses?.length > 0) {
|
||||
const ip = getInterfaceIp(option);
|
||||
const isDown = flags?.includes('down');
|
||||
if (option && ip_addresses?.length > 0) {
|
||||
const ip = getInterfaceIp(option);
|
||||
const isDown = flags?.includes('down');
|
||||
|
||||
if (isDown) {
|
||||
return (
|
||||
<option value={ip} key={name} disabled>
|
||||
<>
|
||||
{name} - {ip} (<Trans>down</Trans>)
|
||||
</>
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDown) {
|
||||
return (
|
||||
<option value={ip} key={name} disabled>
|
||||
<Fragment>
|
||||
{name} - {ip} (<Trans>down</Trans>)
|
||||
</Fragment>
|
||||
<option value={ip} key={name}>
|
||||
{name} - {ip}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<option value={ip} key={name}>
|
||||
{name} - {ip}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
return false;
|
||||
})
|
||||
));
|
||||
|
||||
class Settings extends Component {
|
||||
@@ -79,9 +82,9 @@ class Settings extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
{status === STATIC_STATUS.DISABLED && (
|
||||
<Fragment>
|
||||
<>
|
||||
<div className="mb-2">
|
||||
<Trans values={{ ip }} components={[<strong key="0">text</strong>]}>
|
||||
install_static_configure
|
||||
@@ -94,7 +97,7 @@ class Settings extends Component {
|
||||
>
|
||||
<Trans>set_static_ip</Trans>
|
||||
</button>
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
{status === STATIC_STATUS.ERROR && (
|
||||
<div className="text-danger">
|
||||
@@ -108,7 +111,7 @@ class Settings extends Component {
|
||||
</Trans>
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -121,8 +124,16 @@ class Settings extends Component {
|
||||
handleFix,
|
||||
} = this.props;
|
||||
|
||||
const web = { ip: webIp, port: webPort, autofix: false };
|
||||
const dns = { ip: dnsIp, port: dnsPort, autofix: false };
|
||||
const web = {
|
||||
ip: webIp,
|
||||
port: webPort,
|
||||
autofix: false,
|
||||
};
|
||||
const dns = {
|
||||
ip: dnsIp,
|
||||
port: dnsPort,
|
||||
autofix: false,
|
||||
};
|
||||
const set_static_ip = false;
|
||||
|
||||
if (type === 'web') {
|
||||
@@ -143,8 +154,16 @@ class Settings extends Component {
|
||||
handleFix,
|
||||
} = this.props;
|
||||
|
||||
const web = { ip: webIp, port: webPort, autofix: false };
|
||||
const dns = { ip: dnsIp, port: dnsPort, autofix: false };
|
||||
const web = {
|
||||
ip: webIp,
|
||||
port: webPort,
|
||||
autofix: false,
|
||||
};
|
||||
const dns = {
|
||||
ip: dnsIp,
|
||||
port: dnsPort,
|
||||
autofix: false,
|
||||
};
|
||||
const set_static_ip = true;
|
||||
|
||||
if (window.confirm(this.props.t('confirm_static_ip', { ip }))) {
|
||||
@@ -228,11 +247,9 @@ class Settings extends Component {
|
||||
onClick={() => this.handleAutofix('web')}
|
||||
>
|
||||
<Trans>fix</Trans>
|
||||
</button>
|
||||
}
|
||||
<hr className="divider--small" />
|
||||
</div>
|
||||
}
|
||||
</button>}
|
||||
</div>}
|
||||
<hr className="divider--small" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="setup__desc">
|
||||
@@ -289,7 +306,7 @@ class Settings extends Component {
|
||||
</div>
|
||||
<div className="col-12">
|
||||
{dnsStatus
|
||||
&& <Fragment>
|
||||
&& <>
|
||||
<div className="setup__error text-danger">
|
||||
{dnsStatus}
|
||||
{isDnsFixAvailable
|
||||
@@ -314,9 +331,14 @@ class Settings extends Component {
|
||||
<Trans>autofix_warning_result</Trans>
|
||||
</p>
|
||||
</div>}
|
||||
<hr className="divider--small" />
|
||||
</Fragment>
|
||||
}
|
||||
</>}
|
||||
{dnsPort === UBUNTU_SYSTEM_PORT && !isDnsFixAvailable
|
||||
&& dnsStatus.includes(ADDRESS_IN_USE_TEXT)
|
||||
&& <Trans
|
||||
components={[<a href={PORT_53_FAQ_LINK} key="0">link</a>]}>
|
||||
port_53_faq_link
|
||||
</Trans>}
|
||||
<hr className="divider--small" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="setup__desc">
|
||||
|
||||
@@ -2,7 +2,6 @@ import { combineReducers } from 'redux';
|
||||
import { handleActions } from 'redux-actions';
|
||||
import { loadingBarReducer } from 'react-redux-loading-bar';
|
||||
import { reducer as formReducer } from 'redux-form';
|
||||
import { isVersionGreater } from '../helpers/helpers';
|
||||
|
||||
import * as actions from '../actions';
|
||||
import toasts from './toasts';
|
||||
@@ -15,6 +14,7 @@ import stats from './stats';
|
||||
import queryLogs from './queryLogs';
|
||||
import dnsConfig from './dnsConfig';
|
||||
import filtering from './filtering';
|
||||
import { areEqualVersions } from '../helpers/version';
|
||||
|
||||
const settings = handleActions(
|
||||
{
|
||||
@@ -82,7 +82,7 @@ const dashboard = handleActions(
|
||||
[actions.getVersionSuccess]: (state, { payload }) => {
|
||||
const currentVersion = state.dnsVersion === 'undefined' ? 0 : state.dnsVersion;
|
||||
|
||||
if (payload && isVersionGreater(currentVersion, payload.new_version)) {
|
||||
if (!payload.disabled && !areEqualVersions(currentVersion, payload.new_version)) {
|
||||
const {
|
||||
announcement_url: announcementUrl,
|
||||
new_version: newVersion,
|
||||
@@ -96,7 +96,7 @@ const dashboard = handleActions(
|
||||
canAutoUpdate,
|
||||
isUpdateAvailable: true,
|
||||
processingVersion: false,
|
||||
checkUpdateFlag: !!payload,
|
||||
checkUpdateFlag: !payload.disabled,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
@@ -104,6 +104,7 @@ const dashboard = handleActions(
|
||||
return {
|
||||
...state,
|
||||
processingVersion: false,
|
||||
checkUpdateFlag: !payload.disabled,
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ const queryLogs = handleActions(
|
||||
page: payload,
|
||||
}),
|
||||
|
||||
[actions.setLogsFilterRequest]: (state) => ({ ...state, processingGetLogs: true }),
|
||||
[actions.setLogsFilterFailure]: (state) => ({ ...state, processingGetLogs: false }),
|
||||
[actions.setFilteredLogsRequest]: (state) => ({ ...state, processingGetLogs: true }),
|
||||
[actions.setFilteredLogsFailure]: (state) => ({ ...state, processingGetLogs: false }),
|
||||
[actions.toggleDetailedLogs]: (state, { payload }) => ({
|
||||
...state,
|
||||
isDetailed: payload,
|
||||
}),
|
||||
|
||||
[actions.setLogsFilterSuccess]: (state, { payload }) => {
|
||||
[actions.setFilteredLogsSuccess]: (state, { payload }) => {
|
||||
const { logs, oldest, filter } = payload;
|
||||
const pageSize = TABLE_DEFAULT_PAGE_SIZE;
|
||||
const page = 0;
|
||||
@@ -57,6 +57,12 @@ const queryLogs = handleActions(
|
||||
};
|
||||
},
|
||||
|
||||
[actions.setLogsFilterRequest]: (state, { payload }) => {
|
||||
const { filter } = payload;
|
||||
|
||||
return { ...state, filter };
|
||||
},
|
||||
|
||||
[actions.getLogsRequest]: (state) => ({ ...state, processingGetLogs: true }),
|
||||
[actions.getLogsFailure]: (state) => ({ ...state, processingGetLogs: false }),
|
||||
[actions.getLogsSuccess]: (state, { payload }) => {
|
||||
|
||||
4
go.mod
4
go.mod
@@ -5,12 +5,12 @@ go 1.14
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.29.1
|
||||
github.com/AdguardTeam/golibs v0.4.2
|
||||
github.com/AdguardTeam/urlfilter v0.11.0
|
||||
github.com/AdguardTeam/urlfilter v0.11.2
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gobuffalo/packr v1.30.1
|
||||
github.com/joomcode/errorx v1.0.1
|
||||
github.com/kardianos/service v1.0.0
|
||||
github.com/kardianos/service v1.1.0
|
||||
github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414
|
||||
github.com/miekg/dns v1.1.29
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
||||
8
go.sum
8
go.sum
@@ -4,8 +4,8 @@ github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKU
|
||||
github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
|
||||
github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||
github.com/AdguardTeam/urlfilter v0.11.0 h1:tgZss6uZs1UZAaxpovD/QuX+VVIQLDOlKc7rdF8dwNw=
|
||||
github.com/AdguardTeam/urlfilter v0.11.0/go.mod h1:aMuejlNxpWppOVjiEV87X6z0eMf7wsXHTAIWQuylfZY=
|
||||
github.com/AdguardTeam/urlfilter v0.11.2 h1:gCrWGh63Yqw3z4yi9pgikfsbshIEyvAu/KYV3MvTBlc=
|
||||
github.com/AdguardTeam/urlfilter v0.11.2/go.mod h1:aMuejlNxpWppOVjiEV87X6z0eMf7wsXHTAIWQuylfZY=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
@@ -57,8 +57,8 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joomcode/errorx v1.0.1 h1:CalpDWz14ZHd68fIqluJasJosAewpz2TFaJALrUxjrk=
|
||||
github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
|
||||
github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0=
|
||||
github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo=
|
||||
github.com/kardianos/service v1.1.0 h1:QV2SiEeWK42P0aEmGcsAgjApw/lRxkwopvT+Gu6t1/0=
|
||||
github.com/kardianos/service v1.1.0/go.mod h1:RrJI2xn5vve/r32U5suTbeaSGoMU6GbNPoj36CVYcHc=
|
||||
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
||||
@@ -395,6 +395,7 @@ func optionalAuth(handler func(http.ResponseWriter, *http.Request)) func(http.Re
|
||||
|
||||
if glProcessCookie(r) {
|
||||
log.Debug("Auth: authentification was handled by GL-Inet submodule")
|
||||
ok = true
|
||||
|
||||
} else if err == nil {
|
||||
r := Context.auth.CheckSession(cookie.Value)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
@@ -40,10 +39,6 @@ type configuration struct {
|
||||
// It's reset after config is parsed
|
||||
fileData []byte
|
||||
|
||||
// cached version.json to avoid hammering github.io for each page reload
|
||||
versionCheckJSON []byte
|
||||
versionCheckLastTime time.Time
|
||||
|
||||
BindHost string `yaml:"bind_host"` // BindHost is the IP address of the HTTP server to bind to
|
||||
BindPort int `yaml:"bind_port"` // BindPort is the port the HTTP server
|
||||
Users []User `yaml:"users"` // Users that can access HTTP server
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -15,25 +9,12 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/update"
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
type updateInfo struct {
|
||||
pkgURL string // URL for the new package
|
||||
pkgName string // Full path to package file
|
||||
newVer string // New version string
|
||||
updateDir string // Full path to the directory containing unpacked files from the new package
|
||||
backupDir string // Full path to backup directory
|
||||
configName string // Full path to the current configuration file
|
||||
updateConfigName string // Full path to the configuration file to check by the new binary
|
||||
curBinName string // Full path to the current executable file
|
||||
bkpBinName string // Full path to the current executable file in backup directory
|
||||
newBinName string // Full path to the new executable file
|
||||
}
|
||||
|
||||
type getVersionJSONRequest struct {
|
||||
RecheckNow bool `json:"recheck_now"`
|
||||
}
|
||||
@@ -41,6 +22,10 @@ type getVersionJSONRequest struct {
|
||||
// Get the latest available version from the Internet
|
||||
func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
if Context.disableUpdate {
|
||||
resp := make(map[string]interface{})
|
||||
resp["disabled"] = true
|
||||
d, _ := json.Marshal(resp)
|
||||
_, _ = w.Write(d)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -54,28 +39,11 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if !req.RecheckNow {
|
||||
Context.controlLock.Lock()
|
||||
cached := now.Sub(config.versionCheckLastTime) <= versionCheckPeriod && len(config.versionCheckJSON) != 0
|
||||
data := config.versionCheckJSON
|
||||
Context.controlLock.Unlock()
|
||||
|
||||
if cached {
|
||||
log.Tracef("Returning cached data")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write(getVersionResp(data))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
var info update.VersionInfo
|
||||
for i := 0; i != 3; i++ {
|
||||
log.Tracef("Downloading data from %s", versionCheckURL)
|
||||
resp, err = Context.client.Get(versionCheckURL)
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
Context.controlLock.Lock()
|
||||
info, err = Context.updater.GetVersionResponse(req.RecheckNow)
|
||||
Context.controlLock.Unlock()
|
||||
if err != nil && strings.HasSuffix(err.Error(), "i/o timeout") {
|
||||
// This case may happen while we're restarting DNS server
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/934
|
||||
@@ -88,39 +56,21 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// read the body entirely
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusBadGateway, "Couldn't read response body from %s: %s", versionCheckURL, err)
|
||||
return
|
||||
}
|
||||
|
||||
Context.controlLock.Lock()
|
||||
config.versionCheckLastTime = now
|
||||
config.versionCheckJSON = body
|
||||
Context.controlLock.Unlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err = w.Write(getVersionResp(body))
|
||||
_, err = w.Write(getVersionResp(info))
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Perform an update procedure to the latest available version
|
||||
func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
if len(config.versionCheckJSON) == 0 {
|
||||
func handleUpdate(w http.ResponseWriter, _ *http.Request) {
|
||||
if len(Context.updater.NewVersion) == 0 {
|
||||
httpError(w, http.StatusBadRequest, "/update request isn't allowed now")
|
||||
return
|
||||
}
|
||||
|
||||
u, err := getUpdateInfo(config.versionCheckJSON)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = doUpdate(u)
|
||||
err := Context.updater.DoUpdate()
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "%s", err)
|
||||
return
|
||||
@@ -131,33 +81,18 @@ func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
f.Flush()
|
||||
}
|
||||
|
||||
go finishUpdate(u)
|
||||
go finishUpdate()
|
||||
}
|
||||
|
||||
// Convert version.json data to our JSON response
|
||||
func getVersionResp(data []byte) []byte {
|
||||
versionJSON := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &versionJSON)
|
||||
if err != nil {
|
||||
log.Error("version.json: %s", err)
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func getVersionResp(info update.VersionInfo) []byte {
|
||||
ret := make(map[string]interface{})
|
||||
ret["can_autoupdate"] = false
|
||||
ret["new_version"] = info.NewVersion
|
||||
ret["announcement"] = info.Announcement
|
||||
ret["announcement_url"] = info.AnnouncementURL
|
||||
|
||||
var ok1, ok2, ok3 bool
|
||||
ret["new_version"], ok1 = versionJSON["version"].(string)
|
||||
ret["announcement"], ok2 = versionJSON["announcement"].(string)
|
||||
ret["announcement_url"], ok3 = versionJSON["announcement_url"].(string)
|
||||
selfUpdateMinVersion, ok4 := versionJSON["selfupdate_min_version"].(string)
|
||||
if !ok1 || !ok2 || !ok3 || !ok4 {
|
||||
log.Error("version.json: invalid data")
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
_, ok := getDownloadURL(versionJSON)
|
||||
if ok && ret["new_version"] != versionString && versionString >= selfUpdateMinVersion {
|
||||
if info.CanAutoUpdate {
|
||||
canUpdate := true
|
||||
|
||||
tlsConf := tlsConfigSettings{}
|
||||
@@ -181,373 +116,18 @@ func getVersionResp(data []byte) []byte {
|
||||
return d
|
||||
}
|
||||
|
||||
// Copy file on disk
|
||||
func copyFile(src, dst string) error {
|
||||
d, e := ioutil.ReadFile(src)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = ioutil.WriteFile(dst, d, 0644)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fill in updateInfo object
|
||||
func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
|
||||
var u updateInfo
|
||||
|
||||
workDir := Context.workDir
|
||||
|
||||
versionJSON := make(map[string]interface{})
|
||||
err := json.Unmarshal(jsonData, &versionJSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("JSON parse: %s", err)
|
||||
}
|
||||
|
||||
pkgURL, ok := getDownloadURL(versionJSON)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to get download URL")
|
||||
}
|
||||
|
||||
u.pkgURL = pkgURL
|
||||
u.newVer = versionJSON["version"].(string)
|
||||
if len(u.pkgURL) == 0 || len(u.newVer) == 0 {
|
||||
return nil, fmt.Errorf("invalid JSON")
|
||||
}
|
||||
|
||||
if u.newVer == versionString {
|
||||
return nil, fmt.Errorf("no need to update")
|
||||
}
|
||||
|
||||
u.updateDir = filepath.Join(workDir, fmt.Sprintf("agh-update-%s", u.newVer))
|
||||
u.backupDir = filepath.Join(workDir, "agh-backup")
|
||||
|
||||
_, pkgFileName := filepath.Split(u.pkgURL)
|
||||
if len(pkgFileName) == 0 {
|
||||
return nil, fmt.Errorf("invalid JSON")
|
||||
}
|
||||
u.pkgName = filepath.Join(u.updateDir, pkgFileName)
|
||||
|
||||
u.configName = config.getConfigFilename()
|
||||
u.updateConfigName = filepath.Join(u.updateDir, "AdGuardHome", "AdGuardHome.yaml")
|
||||
if strings.HasSuffix(pkgFileName, ".zip") {
|
||||
u.updateConfigName = filepath.Join(u.updateDir, "AdGuardHome.yaml")
|
||||
}
|
||||
|
||||
binName := "AdGuardHome"
|
||||
if runtime.GOOS == "windows" {
|
||||
binName = "AdGuardHome.exe"
|
||||
}
|
||||
u.curBinName = filepath.Join(workDir, binName)
|
||||
if !util.FileExists(u.curBinName) {
|
||||
return nil, fmt.Errorf("executable file %s doesn't exist", u.curBinName)
|
||||
}
|
||||
u.bkpBinName = filepath.Join(u.backupDir, binName)
|
||||
u.newBinName = filepath.Join(u.updateDir, "AdGuardHome", binName)
|
||||
if strings.HasSuffix(pkgFileName, ".zip") {
|
||||
u.newBinName = filepath.Join(u.updateDir, binName)
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
// getDownloadURL - gets download URL for the current GOOS/GOARCH
|
||||
// returns
|
||||
func getDownloadURL(json map[string]interface{}) (string, bool) {
|
||||
var key string
|
||||
|
||||
if runtime.GOARCH == "arm" && ARMVersion != "" {
|
||||
// the key is:
|
||||
// download_linux_armv5 for ARMv5
|
||||
// download_linux_armv6 for ARMv6
|
||||
// download_linux_armv7 for ARMv7
|
||||
key = fmt.Sprintf("download_%s_%sv%s", runtime.GOOS, runtime.GOARCH, ARMVersion)
|
||||
}
|
||||
|
||||
u, ok := json[key]
|
||||
if !ok {
|
||||
// the key is download_linux_arm or download_linux_arm64 for regular ARM versions
|
||||
key = fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)
|
||||
u, ok = json[key]
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return u.(string), true
|
||||
}
|
||||
|
||||
// Unpack all files from .zip file to the specified directory
|
||||
// Existing files are overwritten
|
||||
// Return the list of files (not directories) written
|
||||
func zipFileUnpack(zipfile, outdir string) ([]string, error) {
|
||||
|
||||
r, err := zip.OpenReader(zipfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zip.OpenReader(): %s", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
var files []string
|
||||
var err2 error
|
||||
var zr io.ReadCloser
|
||||
for _, zf := range r.File {
|
||||
zr, err = zf.Open()
|
||||
if err != nil {
|
||||
err2 = fmt.Errorf("zip file Open(): %s", err)
|
||||
break
|
||||
}
|
||||
|
||||
fi := zf.FileInfo()
|
||||
if len(fi.Name()) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fn := filepath.Join(outdir, fi.Name())
|
||||
|
||||
if fi.IsDir() {
|
||||
err = os.Mkdir(fn, fi.Mode())
|
||||
if err != nil && !os.IsExist(err) {
|
||||
err2 = fmt.Errorf("os.Mkdir(): %s", err)
|
||||
break
|
||||
}
|
||||
log.Tracef("created directory %s", fn)
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
||||
if err != nil {
|
||||
err2 = fmt.Errorf("os.OpenFile(): %s", err)
|
||||
break
|
||||
}
|
||||
_, err = io.Copy(f, zr)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
err2 = fmt.Errorf("io.Copy(): %s", err)
|
||||
break
|
||||
}
|
||||
f.Close()
|
||||
|
||||
log.Tracef("created file %s", fn)
|
||||
files = append(files, fi.Name())
|
||||
}
|
||||
|
||||
zr.Close()
|
||||
return files, err2
|
||||
}
|
||||
|
||||
// Unpack all files from .tar.gz file to the specified directory
|
||||
// Existing files are overwritten
|
||||
// Return the list of files (not directories) written
|
||||
func targzFileUnpack(tarfile, outdir string) ([]string, error) {
|
||||
|
||||
f, err := os.Open(tarfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("os.Open(): %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
gzReader, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gzip.NewReader(): %s", err)
|
||||
}
|
||||
|
||||
var files []string
|
||||
var err2 error
|
||||
tarReader := tar.NewReader(gzReader)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
err2 = nil
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
err2 = fmt.Errorf("tarReader.Next(): %s", err)
|
||||
break
|
||||
}
|
||||
if len(header.Name) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fn := filepath.Join(outdir, header.Name)
|
||||
|
||||
if header.Typeflag == tar.TypeDir {
|
||||
err = os.Mkdir(fn, os.FileMode(header.Mode&0777))
|
||||
if err != nil && !os.IsExist(err) {
|
||||
err2 = fmt.Errorf("os.Mkdir(%s): %s", fn, err)
|
||||
break
|
||||
}
|
||||
log.Tracef("created directory %s", fn)
|
||||
continue
|
||||
} else if header.Typeflag != tar.TypeReg {
|
||||
log.Tracef("%s: unknown file type %d, skipping", header.Name, header.Typeflag)
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode&0777))
|
||||
if err != nil {
|
||||
err2 = fmt.Errorf("os.OpenFile(%s): %s", fn, err)
|
||||
break
|
||||
}
|
||||
_, err = io.Copy(f, tarReader)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
err2 = fmt.Errorf("io.Copy(): %s", err)
|
||||
break
|
||||
}
|
||||
f.Close()
|
||||
|
||||
log.Tracef("created file %s", fn)
|
||||
files = append(files, header.Name)
|
||||
}
|
||||
|
||||
gzReader.Close()
|
||||
return files, err2
|
||||
}
|
||||
|
||||
func copySupportingFiles(files []string, srcdir, dstdir string, useSrcNameOnly, useDstNameOnly bool) error {
|
||||
for _, f := range files {
|
||||
_, name := filepath.Split(f)
|
||||
if name == "AdGuardHome" || name == "AdGuardHome.exe" || name == "AdGuardHome.yaml" {
|
||||
continue
|
||||
}
|
||||
|
||||
src := filepath.Join(srcdir, f)
|
||||
if useSrcNameOnly {
|
||||
src = filepath.Join(srcdir, name)
|
||||
}
|
||||
|
||||
dst := filepath.Join(dstdir, f)
|
||||
if useDstNameOnly {
|
||||
dst = filepath.Join(dstdir, name)
|
||||
}
|
||||
|
||||
err := copyFile(src, dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Tracef("Copied: %s -> %s", src, dst)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Download package file and save it to disk
|
||||
func getPackageFile(u *updateInfo) error {
|
||||
resp, err := Context.client.Get(u.pkgURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HTTP request failed: %s", err)
|
||||
}
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
log.Tracef("Reading HTTP body")
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ioutil.ReadAll() failed: %s", err)
|
||||
}
|
||||
|
||||
log.Tracef("Saving package to file")
|
||||
err = ioutil.WriteFile(u.pkgName, body, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ioutil.WriteFile() failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform an update procedure
|
||||
func doUpdate(u *updateInfo) error {
|
||||
log.Info("Updating from %s to %s. URL:%s Package:%s",
|
||||
versionString, u.newVer, u.pkgURL, u.pkgName)
|
||||
|
||||
_ = os.Mkdir(u.updateDir, 0755)
|
||||
|
||||
var err error
|
||||
err = getPackageFile(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Tracef("Unpacking the package")
|
||||
_, file := filepath.Split(u.pkgName)
|
||||
var files []string
|
||||
if strings.HasSuffix(file, ".zip") {
|
||||
files, err = zipFileUnpack(u.pkgName, u.updateDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("zipFileUnpack() failed: %s", err)
|
||||
}
|
||||
} else if strings.HasSuffix(file, ".tar.gz") {
|
||||
files, err = targzFileUnpack(u.pkgName, u.updateDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("targzFileUnpack() failed: %s", err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unknown package extension")
|
||||
}
|
||||
|
||||
log.Tracef("Checking configuration")
|
||||
err = copyFile(u.configName, u.updateConfigName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copyFile() failed: %s", err)
|
||||
}
|
||||
cmd := exec.Command(u.newBinName, "--check-config")
|
||||
err = cmd.Run()
|
||||
if err != nil || cmd.ProcessState.ExitCode() != 0 {
|
||||
return fmt.Errorf("exec.Command(): %s %d", err, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
|
||||
log.Tracef("Backing up the current configuration")
|
||||
_ = os.Mkdir(u.backupDir, 0755)
|
||||
err = copyFile(u.configName, filepath.Join(u.backupDir, "AdGuardHome.yaml"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("copyFile() failed: %s", err)
|
||||
}
|
||||
|
||||
// ./README.md -> backup/README.md
|
||||
err = copySupportingFiles(files, Context.workDir, u.backupDir, true, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %s",
|
||||
Context.workDir, u.backupDir, err)
|
||||
}
|
||||
|
||||
// update/[AdGuardHome/]README.md -> ./README.md
|
||||
err = copySupportingFiles(files, u.updateDir, Context.workDir, false, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %s",
|
||||
u.updateDir, Context.workDir, err)
|
||||
}
|
||||
|
||||
log.Tracef("Renaming: %s -> %s", u.curBinName, u.bkpBinName)
|
||||
err = os.Rename(u.curBinName, u.bkpBinName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// rename fails with "File in use" error
|
||||
err = copyFile(u.newBinName, u.curBinName)
|
||||
} else {
|
||||
err = os.Rename(u.newBinName, u.curBinName)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Tracef("Renamed: %s -> %s", u.newBinName, u.curBinName)
|
||||
|
||||
_ = os.Remove(u.pkgName)
|
||||
_ = os.RemoveAll(u.updateDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete an update procedure
|
||||
func finishUpdate(u *updateInfo) {
|
||||
func finishUpdate() {
|
||||
log.Info("Stopping all tasks")
|
||||
cleanup()
|
||||
cleanupAlways()
|
||||
|
||||
exeName := "AdGuardHome"
|
||||
if runtime.GOOS == "windows" {
|
||||
exeName = "AdGuardHome.exe"
|
||||
}
|
||||
curBinName := filepath.Join(Context.workDir, exeName)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if Context.runningAsService {
|
||||
// Note:
|
||||
@@ -561,7 +141,7 @@ func finishUpdate(u *updateInfo) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
cmd := exec.Command(u.curBinName, os.Args[1:]...)
|
||||
cmd := exec.Command(curBinName, os.Args[1:]...)
|
||||
log.Info("Restarting: %v", cmd.Args)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -573,7 +153,7 @@ func finishUpdate(u *updateInfo) {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
log.Info("Restarting: %v", os.Args)
|
||||
err := syscall.Exec(u.curBinName, os.Args, os.Environ())
|
||||
err := syscall.Exec(curBinName, os.Args, os.Environ())
|
||||
if err != nil {
|
||||
log.Fatalf("syscall.Exec() failed: %s", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ package home
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDoUpdate(t *testing.T) {
|
||||
@@ -16,16 +18,28 @@ func TestDoUpdate(t *testing.T) {
|
||||
"version": "v0.96",
|
||||
"announcement": "AdGuard Home v0.96 is now available!",
|
||||
"announcement_url": "",
|
||||
"download_windows_amd64": "",
|
||||
"download_windows_386": "",
|
||||
"download_darwin_amd64": "",
|
||||
"download_linux_amd64": "https://github.com/AdguardTeam/AdGuardHome/releases/download/v0.96/AdGuardHome_linux_amd64.tar.gz",
|
||||
"download_linux_386": "",
|
||||
"download_linux_arm": "",
|
||||
"download_linux_arm64": "",
|
||||
"download_linux_mips": "",
|
||||
"download_linux_mipsle": "",
|
||||
"selfupdate_min_version": "v0.0"
|
||||
"download_windows_amd64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_amd64.zip",
|
||||
"download_windows_386": "https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_386.zip",
|
||||
"download_darwin_amd64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_amd64.zip",
|
||||
"download_darwin_386": "https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_386.zip",
|
||||
"download_linux_amd64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz",
|
||||
"download_linux_386": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_386.tar.gz",
|
||||
"download_linux_arm": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz",
|
||||
"download_linux_armv5": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz",
|
||||
"download_linux_armv6": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz",
|
||||
"download_linux_armv7": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv7.tar.gz",
|
||||
"download_linux_arm64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz",
|
||||
"download_linux_mips": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips_softfloat.tar.gz",
|
||||
"download_linux_mipsle": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mipsle_softfloat.tar.gz",
|
||||
"download_linux_mips64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64_softfloat.tar.gz",
|
||||
"download_linux_mips64le": "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64le_softfloat.tar.gz",
|
||||
"download_freebsd_386": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_386.tar.gz",
|
||||
"download_freebsd_amd64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_amd64.tar.gz",
|
||||
"download_freebsd_arm": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv6.tar.gz",
|
||||
"download_freebsd_armv5": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv5.tar.gz",
|
||||
"download_freebsd_armv6": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv6.tar.gz",
|
||||
"download_freebsd_armv7": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv7.tar.gz",
|
||||
"download_freebsd_arm64": "https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_arm64.tar.gz"
|
||||
}`
|
||||
uu, err := getUpdateInfo([]byte(data))
|
||||
if err != nil {
|
||||
@@ -33,7 +47,7 @@ func TestDoUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
u := updateInfo{
|
||||
pkgURL: "https://github.com/AdguardTeam/AdGuardHome/releases/download/" + newver + "/AdGuardHome_linux_amd64.tar.gz",
|
||||
pkgURL: "https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz",
|
||||
pkgName: Context.workDir + "/agh-update-" + newver + "/AdGuardHome_linux_amd64.tar.gz",
|
||||
newVer: newver,
|
||||
updateDir: Context.workDir + "/agh-update-" + newver,
|
||||
@@ -45,18 +59,16 @@ func TestDoUpdate(t *testing.T) {
|
||||
newBinName: Context.workDir + "/agh-update-" + newver + "/AdGuardHome/AdGuardHome",
|
||||
}
|
||||
|
||||
if uu.pkgURL != u.pkgURL ||
|
||||
uu.pkgName != u.pkgName ||
|
||||
uu.newVer != u.newVer ||
|
||||
uu.updateDir != u.updateDir ||
|
||||
uu.backupDir != u.backupDir ||
|
||||
uu.configName != u.configName ||
|
||||
uu.updateConfigName != u.updateConfigName ||
|
||||
uu.curBinName != u.curBinName ||
|
||||
uu.bkpBinName != u.bkpBinName ||
|
||||
uu.newBinName != u.newBinName {
|
||||
t.Fatalf("getUpdateInfo: %v != %v", uu, u)
|
||||
}
|
||||
assert.Equal(t, uu.pkgURL, u.pkgURL)
|
||||
assert.Equal(t, uu.pkgName, u.pkgName)
|
||||
assert.Equal(t, uu.newVer, u.newVer)
|
||||
assert.Equal(t, uu.updateDir, u.updateDir)
|
||||
assert.Equal(t, uu.backupDir, u.backupDir)
|
||||
assert.Equal(t, uu.configName, u.configName)
|
||||
assert.Equal(t, uu.updateConfigName, u.updateConfigName)
|
||||
assert.Equal(t, uu.curBinName, u.curBinName)
|
||||
assert.Equal(t, uu.bkpBinName, u.bkpBinName)
|
||||
assert.Equal(t, uu.newBinName, u.newBinName)
|
||||
|
||||
e := doUpdate(&u)
|
||||
if e != nil {
|
||||
@@ -66,20 +78,20 @@ func TestDoUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTargzFileUnpack(t *testing.T) {
|
||||
fn := "./dist/AdGuardHome_v0.95_linux_amd64.tar.gz"
|
||||
outdir := "./test-unpack"
|
||||
fn := "../dist/AdGuardHome_linux_amd64.tar.gz"
|
||||
outdir := "../test-unpack"
|
||||
defer os.RemoveAll(outdir)
|
||||
_ = os.Mkdir(outdir, 0755)
|
||||
files, e := targzFileUnpack(fn, outdir)
|
||||
if e != nil {
|
||||
t.Fatalf("FAILED: %s", e)
|
||||
}
|
||||
t.Logf("%v", files)
|
||||
os.RemoveAll(outdir)
|
||||
}
|
||||
|
||||
func TestZipFileUnpack(t *testing.T) {
|
||||
fn := "./dist/AdGuardHome_v0.95_Windows_amd64.zip"
|
||||
outdir := "./test-unpack"
|
||||
fn := "../dist/AdGuardHome_windows_amd64.zip"
|
||||
outdir := "../test-unpack"
|
||||
_ = os.Mkdir(outdir, 0755)
|
||||
files, e := zipFileUnpack(fn, outdir)
|
||||
if e != nil {
|
||||
|
||||
@@ -242,7 +242,7 @@ func applyAdditionalFiltering(clientAddr string, setts *dnsfilter.RequestFilteri
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("Using settings for client with IP %s", clientAddr)
|
||||
log.Debug("Using settings for client %s with IP %s", c.Name, clientAddr)
|
||||
|
||||
if c.UseOwnBlockedServices {
|
||||
Context.dnsFilter.ApplyBlockedServices(setts, c.BlockedServices, false)
|
||||
|
||||
16
home/home.go
16
home/home.go
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/update"
|
||||
"github.com/AdguardTeam/AdGuardHome/util"
|
||||
|
||||
"github.com/joomcode/errorx"
|
||||
@@ -47,8 +48,6 @@ var (
|
||||
ARMVersion = ""
|
||||
)
|
||||
|
||||
const versionCheckPeriod = time.Hour * 8
|
||||
|
||||
// Global context
|
||||
type homeContext struct {
|
||||
// Modules
|
||||
@@ -67,6 +66,7 @@ type homeContext struct {
|
||||
web *Web // Web (HTTP, HTTPS) module
|
||||
tls *TLSMod // TLS module
|
||||
autoHosts util.AutoHosts // IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files
|
||||
updater *update.Updater
|
||||
|
||||
// Runtime properties
|
||||
// --
|
||||
@@ -225,6 +225,18 @@ func run(args options) {
|
||||
os.Exit(1)
|
||||
}
|
||||
Context.autoHosts.Init("")
|
||||
|
||||
Context.updater = update.NewUpdater(update.Config{
|
||||
Client: Context.client,
|
||||
WorkDir: Context.workDir,
|
||||
VersionURL: versionCheckURL,
|
||||
VersionString: versionString,
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
ARMVersion: ARMVersion,
|
||||
ConfigName: config.getConfigFilename(),
|
||||
})
|
||||
|
||||
Context.clients.Init(config.Clients, Context.dhcpServer, &Context.autoHosts)
|
||||
config.Clients = nil
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user