Compare commits
108 Commits
v0.103.0-b
...
1228-proxy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f85de51452 | ||
|
|
c3123473cf | ||
|
|
8d0c8ad438 | ||
|
|
1e2e965ea7 | ||
|
|
0b539ced92 | ||
|
|
9e09dffbc3 | ||
|
|
473d881871 | ||
|
|
a1ca7862f8 | ||
|
|
8ea1e64c7b | ||
|
|
2f8e34e73b | ||
|
|
705a9d909d | ||
|
|
97df19898f | ||
|
|
de92c85256 | ||
|
|
d3f5b40700 | ||
|
|
20a0ba5f60 | ||
|
|
9b9902f004 | ||
|
|
020a30fb6d | ||
|
|
39f2d5c4ae | ||
|
|
6ce3c52456 | ||
|
|
5188da60cf | ||
|
|
e57cbc36d9 | ||
|
|
57e43a66c3 | ||
|
|
4f3d503916 | ||
|
|
6bb6c700d6 | ||
|
|
ed76a3cb8b | ||
|
|
335d62b08e | ||
|
|
99625da1e4 | ||
|
|
9fecab8675 | ||
|
|
b9aa969a56 | ||
|
|
ce21514246 | ||
|
|
3ff0c964dc | ||
|
|
552280e9a3 | ||
|
|
d154456ae5 | ||
|
|
3cecd6f090 | ||
|
|
dc1fc82b9e | ||
|
|
a033b68bfd | ||
|
|
4ca4fb8a11 | ||
|
|
8d10a269ed | ||
|
|
a536357427 | ||
|
|
cecf848364 | ||
|
|
ddb9a2e872 | ||
|
|
cfdfd250a0 | ||
|
|
cb01d05ef4 | ||
|
|
c7e7b76cec | ||
|
|
f5128d27f1 | ||
|
|
005f8fb279 | ||
|
|
244fe093cd | ||
|
|
ff9d1c234c | ||
|
|
1c9d3acaa8 | ||
|
|
0dab36a108 | ||
|
|
611ed94884 | ||
|
|
22935c5fed | ||
|
|
4a8dcbeeed | ||
|
|
1ab650bb86 | ||
|
|
4743743b1f | ||
|
|
dd2c9d96e7 | ||
|
|
946bda37a3 | ||
|
|
ad4e85d8f5 | ||
|
|
4b9ab97271 | ||
|
|
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;
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -44,4 +44,4 @@ jobs:
|
||||
fields: repo,message,commit,author
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
@@ -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'
|
||||
|
||||
163
AGHTechDoc.md
163
AGHTechDoc.md
@@ -22,7 +22,8 @@ Contents:
|
||||
* Update client
|
||||
* Delete client
|
||||
* API: Find clients by IP
|
||||
* Enable DHCP server
|
||||
* DHCP server
|
||||
* DHCP server in DNS
|
||||
* "Show DHCP status" command
|
||||
* "Check DHCP" command
|
||||
* "Enable DHCP" command
|
||||
@@ -51,19 +52,26 @@ Contents:
|
||||
* API: Get query log
|
||||
* API: Set querylog parameters
|
||||
* API: Get querylog parameters
|
||||
* Filtering
|
||||
* DNS Filtering
|
||||
* Filters update mechanism
|
||||
* API: Get filtering parameters
|
||||
* API: Set filtering parameters
|
||||
* API: Refresh filters
|
||||
* API: Add Filter
|
||||
* API: Set URL parameters
|
||||
* API: Delete URL
|
||||
* API: Set Filter parameters
|
||||
* API: Delete Filter
|
||||
* API: Domain Check
|
||||
* HTTP Proxy
|
||||
* API: Get Proxy settings
|
||||
* API: Set Proxy settings
|
||||
* API: Get Proxy filtering parameters
|
||||
* API: Add Proxy Filter
|
||||
* API: Delete Proxy Filter
|
||||
* Log-in page
|
||||
* API: Log in
|
||||
* API: Log out
|
||||
* API: Get current user info
|
||||
* Safe services
|
||||
|
||||
|
||||
## Relations between subsystems
|
||||
@@ -344,10 +352,14 @@ Response:
|
||||
|
||||
If `can_autoupdate` is true, then the server can automatically upgrade to a new version.
|
||||
|
||||
Response with empty body:
|
||||
Response when auto-update is disabled by command-line argument:
|
||||
|
||||
200 OK
|
||||
|
||||
{
|
||||
"disabled":true
|
||||
}
|
||||
|
||||
It means that update check is disabled by user. UI should do nothing.
|
||||
|
||||
|
||||
@@ -370,9 +382,9 @@ Error response:
|
||||
UI shows error message "Auto-update has failed"
|
||||
|
||||
|
||||
## Enable DHCP server
|
||||
## DHCP server
|
||||
|
||||
Algorithm:
|
||||
Enable DHCP server algorithm:
|
||||
|
||||
* UI shows DHCP configuration screen with "Enabled DHCP" button disabled, and "Check DHCP" button enabled
|
||||
* User clicks on "Check DHCP"; UI sends request to server
|
||||
@@ -384,6 +396,21 @@ Algorithm:
|
||||
* UI shows the status
|
||||
|
||||
|
||||
### DHCP server in DNS
|
||||
|
||||
DHCP leases are used in several ways by DNS module.
|
||||
|
||||
* For "A" DNS reqeust we reply with an IP address leased by our DHCP server.
|
||||
|
||||
< A bills-notebook.lan.
|
||||
> A bills-notebook.lan. = 192.168.1.100
|
||||
|
||||
* For "PTR" DNS request we reply with a hostname from an active DHCP lease.
|
||||
|
||||
< PTR 100.1.168.192.in-addr.arpa.
|
||||
> PTR 100.1.168.192.in-addr.arpa. = bills-notebook.
|
||||
|
||||
|
||||
### "Show DHCP status" command
|
||||
|
||||
Request:
|
||||
@@ -1456,7 +1483,7 @@ Response:
|
||||
}
|
||||
|
||||
|
||||
## Filtering
|
||||
## DNS Filtering
|
||||
|
||||

|
||||
|
||||
@@ -1527,7 +1554,19 @@ Response:
|
||||
}
|
||||
...
|
||||
],
|
||||
"user_rules":["...", ...]
|
||||
"user_rules":["...", ...],
|
||||
|
||||
"proxy_filtering_enabled": true | false
|
||||
"proxy_filters":[
|
||||
{
|
||||
"enabled":true,
|
||||
"url":"https://...",
|
||||
"name":"...",
|
||||
"rules_count":1234,
|
||||
"last_updated":"2019-09-04T18:29:30+00:00",
|
||||
}
|
||||
...
|
||||
],
|
||||
}
|
||||
|
||||
For both arrays `filters` and `whitelist_filters` there are unique values: id, url.
|
||||
@@ -1542,6 +1581,7 @@ Request:
|
||||
|
||||
{
|
||||
"enabled": true | false
|
||||
"proxy_filtering_enabled": true | false
|
||||
"interval": 0 | 1 | 12 | 1*24 || 3*24 || 7*24
|
||||
}
|
||||
|
||||
@@ -1557,7 +1597,7 @@ Request:
|
||||
POST /control/filtering/refresh
|
||||
|
||||
{
|
||||
"whitelist": true
|
||||
"type": blocklist | whitelist | proxylist
|
||||
}
|
||||
|
||||
Response:
|
||||
@@ -1578,7 +1618,7 @@ Request:
|
||||
{
|
||||
"name": "..."
|
||||
"url": "..." // URL or an absolute file path
|
||||
"whitelist": true
|
||||
"type": blocklist | whitelist | proxylist
|
||||
}
|
||||
|
||||
Response:
|
||||
@@ -1586,7 +1626,7 @@ Response:
|
||||
200 OK
|
||||
|
||||
|
||||
### API: Set URL parameters
|
||||
### API: Set Filter parameters
|
||||
|
||||
Request:
|
||||
|
||||
@@ -1594,11 +1634,11 @@ Request:
|
||||
|
||||
{
|
||||
"url": "..."
|
||||
"whitelist": true
|
||||
"type": blocklist | whitelist | proxylist
|
||||
"data": {
|
||||
"name": "..."
|
||||
"url": "..."
|
||||
"enabled": true | false
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1607,7 +1647,7 @@ Response:
|
||||
200 OK
|
||||
|
||||
|
||||
### API: Delete URL
|
||||
### API: Delete Filter
|
||||
|
||||
Request:
|
||||
|
||||
@@ -1615,7 +1655,7 @@ Request:
|
||||
|
||||
{
|
||||
"url": "..."
|
||||
"whitelist": true
|
||||
"type": blocklist | whitelist | proxylist
|
||||
}
|
||||
|
||||
Response:
|
||||
@@ -1647,6 +1687,60 @@ Response:
|
||||
}
|
||||
|
||||
|
||||
## HTTP Proxy
|
||||
|
||||
Browser <-(HTTP)-> AGH Proxy <-(HTTP)-> Internet Server
|
||||
|
||||
HTTPS MITM:
|
||||
|
||||
. Browser --(CONNECT...)-> AGH Proxy --(handshake)-> Internet Server
|
||||
. Browser <-(handshake,cert/AGH)-- AGH Proxy <-(cert/issuer)-- Internet Server
|
||||
. Browser <-(TLS/session2)-> AGH Proxy <-(TLS/session1)-> Internet Server
|
||||
|
||||
|
||||
### API: Get Proxy settings
|
||||
|
||||
Request:
|
||||
|
||||
GET /control/proxy_info
|
||||
|
||||
Response:
|
||||
|
||||
200 OK
|
||||
|
||||
{
|
||||
"enabled": true|false,
|
||||
"listen_address": "ip",
|
||||
"listen_port": 12345,
|
||||
|
||||
"auth_username": "",
|
||||
"auth_password": ""
|
||||
}
|
||||
|
||||
|
||||
### API: Set Proxy settings
|
||||
|
||||
Request:
|
||||
|
||||
POST /control/proxy_config
|
||||
|
||||
{
|
||||
"enabled": true|false,
|
||||
"listen_address": "ip",
|
||||
"listen_port": 12345,
|
||||
|
||||
"auth_username": "",
|
||||
"auth_password": "",
|
||||
|
||||
"cert_data":"...", // user-specified certificate. "": generate new
|
||||
"pkey_data":"...",
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
200 OK
|
||||
|
||||
|
||||
## Log-in page
|
||||
|
||||
After user completes the steps of installation wizard, he must log in into dashboard using his name and password. After user successfully logs in, he gets the Cookie which allows the server to authenticate him next time without password. After the Cookie is expired, user needs to perform log-in operation again.
|
||||
@@ -1743,3 +1837,40 @@ Response:
|
||||
}
|
||||
|
||||
If no client is configured then authentication is disabled and server sends an empty response.
|
||||
|
||||
|
||||
### Safe services
|
||||
|
||||
Check if host name is blocked by SB/PC service:
|
||||
|
||||
* For each host name component, search for the result in cache by the first 2 bytes of SHA-256 hashes of host name components (max. is 4, i.e. sub2.sub1.host.com), excluding TLD:
|
||||
|
||||
hashes[] = cache_search(sha256(host.com)[0..1])
|
||||
...
|
||||
|
||||
If hash prefix is found, search for a full hash sum in the cached data.
|
||||
If found, the host is blocked.
|
||||
If not found, the host is not blocked - don't request data for this prefix from the Family server again.
|
||||
If hash prefix is not found, request data for this prefix from the Family server.
|
||||
|
||||
* Prepare query string which is generated from the first 2 bytes (converted to a 4-character string) of SHA-256 hashes of host name components (max. is 4, i.e. sub2.sub1.host.com), excluding TLD:
|
||||
|
||||
qs = ... + string(sha256(sub.host.com)[0..1]) + "." + string(sha256(host.com)[0..1]) + ".sb.dns.adguard.com."
|
||||
|
||||
For PC `.pc.dns.adguard.com` suffix is used.
|
||||
|
||||
* Send TXT query to Family server, receive response which contains the array of complete hash sums of the blocked hosts
|
||||
|
||||
* Check if one of received hash sums (`hashes[]`) matches hash sums for our host name
|
||||
|
||||
hashes[0] <> sha256(host.com)
|
||||
hashes[0] <> sha256(sub.host.com)
|
||||
hashes[1] <> sha256(host.com)
|
||||
hashes[1] <> sha256(sub.host.com)
|
||||
...
|
||||
|
||||
* Store all received hash sums in cache:
|
||||
|
||||
sha256(host.com)[0..1] -> hashes[0],hashes[1],...
|
||||
sha256(sub.host.com)[0..1] -> hashes[2],...
|
||||
...
|
||||
|
||||
142
Makefile
142
Makefile
@@ -14,6 +14,13 @@
|
||||
# Building releases:
|
||||
#
|
||||
# * release -- builds AdGuard Home distros. CHANNEL must be specified (edge, release or beta).
|
||||
# * release_and_sign -- builds AdGuard Home distros and signs the binary files.
|
||||
# CHANNEL must be specified (edge, release or beta).
|
||||
# * sign -- Repacks all release archive files and signs the binary files inside them.
|
||||
# For signing to work, the public+private key pair for $(GPG_KEY) must be imported:
|
||||
# gpg --import public.txt
|
||||
# gpg --import private.txt
|
||||
# GPG_KEY_PASSPHRASE must contain the GPG key passphrase
|
||||
# * docker-multi-arch -- builds a multi-arch image. If you want it to be pushed to docker hub,
|
||||
# you must specify:
|
||||
# * DOCKER_IMAGE_NAME - adguard/adguard-home
|
||||
@@ -23,6 +30,9 @@ GOPATH := $(shell go env GOPATH)
|
||||
PWD := $(shell pwd)
|
||||
TARGET=AdGuardHome
|
||||
BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
|
||||
GPG_KEY := devteam@adguard.com
|
||||
GPG_KEY_PASSPHRASE :=
|
||||
GPG_CMD := gpg --detach-sig --default-key $(GPG_KEY) --pinentry-mode loopback --passphrase $(GPG_KEY_PASSPHRASE)
|
||||
|
||||
# See release target
|
||||
DIST_DIR=dist
|
||||
@@ -31,7 +41,7 @@ DIST_DIR=dist
|
||||
CHANNEL ?= edge
|
||||
|
||||
# Validate channel
|
||||
ifneq ($(CHANNEL),relese)
|
||||
ifneq ($(CHANNEL),release)
|
||||
ifneq ($(CHANNEL),beta)
|
||||
ifneq ($(CHANNEL),edge)
|
||||
$(error CHANNEL value is not valid. Valid values are release,beta or edge)
|
||||
@@ -39,6 +49,12 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# Version history URL (see
|
||||
VERSION_HISTORY_URL="https://github.com/AdguardTeam/AdGuardHome/releases"
|
||||
ifeq ($(CHANNEL),edge)
|
||||
VERSION_HISTORY_URL="https://github.com/AdguardTeam/AdGuardHome/commits/master"
|
||||
endif
|
||||
|
||||
# goreleaser command depends on the $CHANNEL
|
||||
GORELEASER_COMMAND=goreleaser release --rm-dist --skip-publish --snapshot
|
||||
ifneq ($(CHANNEL),edge)
|
||||
@@ -55,7 +71,11 @@ SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
|
||||
# Set proper version
|
||||
VERSION=
|
||||
ifeq ($(TAG_NAME),$(shell git describe --abbrev=4))
|
||||
VERSION=$(RELEASE_VERSION)
|
||||
ifeq ($(CHANNEL),edge)
|
||||
VERSION=$(SNAPSHOT_VERSION)
|
||||
else
|
||||
VERSION=$(RELEASE_VERSION)
|
||||
endif
|
||||
else
|
||||
VERSION=$(SNAPSHOT_VERSION)
|
||||
endif
|
||||
@@ -88,17 +108,25 @@ ifndef DOCKER_IMAGE_NAME
|
||||
$(error DOCKER_IMAGE_NAME value is not set)
|
||||
endif
|
||||
|
||||
.PHONY: all build client client-watch docker lint test dependencies clean release docker-multi-arch
|
||||
.PHONY: all build client client-watch docker lint lint-js lint-go test dependencies clean release docker-multi-arch
|
||||
all: build
|
||||
|
||||
build: dependencies client
|
||||
go generate ./...
|
||||
init:
|
||||
git config core.hooksPath .githooks
|
||||
|
||||
build: client_with_deps
|
||||
go mod download
|
||||
PATH=$(GOPATH)/bin:$(PATH) go generate ./...
|
||||
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
|
||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||
|
||||
client:
|
||||
npm --prefix client run build-prod
|
||||
|
||||
client_with_deps:
|
||||
npm --prefix client ci
|
||||
npm --prefix client run build-prod
|
||||
|
||||
client-watch:
|
||||
npm --prefix client run watch
|
||||
|
||||
@@ -116,16 +144,25 @@ docker:
|
||||
@echo Now you can run the docker image:
|
||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||
|
||||
lint:
|
||||
@echo Running linters
|
||||
golangci-lint run ./...
|
||||
lint: lint-js lint-go
|
||||
|
||||
lint-js:
|
||||
@echo Running js linter
|
||||
npm --prefix client run lint
|
||||
|
||||
lint-go:
|
||||
@echo Running go linter
|
||||
golangci-lint run
|
||||
|
||||
test:
|
||||
@echo Running unit-tests
|
||||
@echo Running JS unit-tests
|
||||
npm run test --prefix client
|
||||
@echo Running Go unit-tests
|
||||
go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
ci: dependencies client test
|
||||
ci: client_with_deps
|
||||
go mod download
|
||||
$(MAKE) test
|
||||
|
||||
dependencies:
|
||||
npm --prefix client ci
|
||||
@@ -162,12 +199,20 @@ docker-multi-arch:
|
||||
@echo If the image was pushed to the registry, you can now run it:
|
||||
@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
|
||||
|
||||
release: dependencies client
|
||||
release: client_with_deps
|
||||
go mod download
|
||||
@echo Starting release build: version $(VERSION), channel $(CHANNEL)
|
||||
CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
|
||||
$(call write_version_file,$(VERSION))
|
||||
PATH=$(GOPATH)/bin:$(PATH) packr clean
|
||||
|
||||
release_and_sign: client_with_deps
|
||||
$(MAKE) release
|
||||
$(call repack_dist)
|
||||
|
||||
sign:
|
||||
$(call repack_dist)
|
||||
|
||||
define write_version_file
|
||||
$(eval version := $(1))
|
||||
|
||||
@@ -182,8 +227,8 @@ define write_version_file
|
||||
echo "{" >> $(DIST_DIR)/version.json
|
||||
echo " \"version\": \"$(version)\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"announcement\": \"AdGuard Home $(version) is now available!\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"selfupdate_min_version\": \"v0.0\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"announcement_url\": \"$(VERSION_HISTORY_URL)\"," >> $(DIST_DIR)/version.json
|
||||
echo " \"selfupdate_min_version\": \"0.0\"," >> $(DIST_DIR)/version.json
|
||||
|
||||
# Windows builds
|
||||
echo " \"download_windows_amd64\": \"$(BASE_URL)/AdGuardHome_windows_amd64.zip\"," >> $(DIST_DIR)/version.json
|
||||
@@ -223,4 +268,73 @@ define write_version_file
|
||||
|
||||
# Finish
|
||||
echo "}" >> $(DIST_DIR)/version.json
|
||||
endef
|
||||
endef
|
||||
|
||||
define repack_dist
|
||||
# Repack archive files
|
||||
# A temporary solution for our auto-update code to be able to unpack these archive files
|
||||
# The problem is that goreleaser doesn't add directory AdGuardHome/ to the archive file
|
||||
# and we can't create it
|
||||
rm -rf $(DIST_DIR)/AdGuardHome
|
||||
|
||||
# Windows builds
|
||||
$(call zip_repack_windows,AdGuardHome_windows_amd64.zip)
|
||||
$(call zip_repack_windows,AdGuardHome_windows_386.zip)
|
||||
|
||||
# MacOS builds
|
||||
$(call zip_repack,AdGuardHome_darwin_amd64.zip)
|
||||
$(call zip_repack,AdGuardHome_darwin_386.zip)
|
||||
|
||||
# Linux
|
||||
$(call tar_repack,AdGuardHome_linux_amd64.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_386.tar.gz)
|
||||
|
||||
# Linux, all kinds of ARM
|
||||
$(call tar_repack,AdGuardHome_linux_armv5.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_armv6.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_armv7.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_arm64.tar.gz)
|
||||
|
||||
# Linux, MIPS
|
||||
$(call tar_repack,AdGuardHome_linux_mips_softfloat.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_mipsle_softfloat.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_mips64_softfloat.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_linux_mips64le_softfloat.tar.gz)
|
||||
|
||||
# FreeBSD
|
||||
$(call tar_repack,AdGuardHome_freebsd_386.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_freebsd_amd64.tar.gz)
|
||||
|
||||
# FreeBSD, all kinds of ARM
|
||||
$(call tar_repack,AdGuardHome_freebsd_armv5.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_freebsd_armv6.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_freebsd_armv7.tar.gz)
|
||||
$(call tar_repack,AdGuardHome_freebsd_arm64.tar.gz)
|
||||
endef
|
||||
|
||||
define zip_repack_windows
|
||||
$(eval ARC := $(1))
|
||||
cd $(DIST_DIR) && \
|
||||
unzip $(ARC) && \
|
||||
$(GPG_CMD) AdGuardHome/AdGuardHome.exe && \
|
||||
zip -r $(ARC) AdGuardHome/ && \
|
||||
rm -rf AdGuardHome
|
||||
endef
|
||||
|
||||
define zip_repack
|
||||
$(eval ARC := $(1))
|
||||
cd $(DIST_DIR) && \
|
||||
unzip $(ARC) && \
|
||||
$(GPG_CMD) AdGuardHome/AdGuardHome && \
|
||||
zip -r $(ARC) AdGuardHome/ && \
|
||||
rm -rf AdGuardHome
|
||||
endef
|
||||
|
||||
define tar_repack
|
||||
$(eval ARC := $(1))
|
||||
cd $(DIST_DIR) && \
|
||||
tar xzf $(ARC) && \
|
||||
$(GPG_CMD) AdGuardHome/AdGuardHome && \
|
||||
tar czf $(ARC) AdGuardHome/ && \
|
||||
rm -rf AdGuardHome
|
||||
endef
|
||||
|
||||
20
README.md
20
README.md
@@ -150,11 +150,17 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Run `make init` to prepare the development environment.
|
||||
|
||||
You will need this to build AdGuard Home:
|
||||
|
||||
* [go](https://golang.org/dl/) v1.14 or later.
|
||||
* [node.js](https://nodejs.org/en/download/) v10 or later.
|
||||
* [node.js](https://nodejs.org/en/download/) v10.16.2 or later.
|
||||
* [npm](https://www.npmjs.com/) v6.14 or later.
|
||||
|
||||
Optionally, for Go devs:
|
||||
* [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
|
||||
### Building
|
||||
|
||||
Open Terminal and execute these commands:
|
||||
@@ -167,6 +173,14 @@ make
|
||||
|
||||
Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
|
||||
|
||||
**Building for a different platform.** You can build AdGuard for any OS/ARCH just like any other Golang project.
|
||||
In order to do this, specify `GOOS` and `GOARCH` env variables before running make.
|
||||
|
||||
For example:
|
||||
```
|
||||
GOOS=linux GOARCH=arm64 make
|
||||
```
|
||||
|
||||
#### Preparing release
|
||||
|
||||
You'll need this to prepare a release build:
|
||||
@@ -203,6 +217,8 @@ You may need to prepare before using these builds:
|
||||
|
||||
You are welcome to fork this repository, make your changes and submit a pull request — https://github.com/AdguardTeam/AdGuardHome/pulls
|
||||
|
||||
Please note, that we don't expect people to contribute to both UI and golang parts of the program simultaneously. Ideally, the golang part is implemented first, i.e. configuration, API, and the functionality itself. The UI part can be implemented later in a different pull request by a different person.
|
||||
|
||||
<a id="test-unstable-versions"></a>
|
||||
### Test unstable versions
|
||||
|
||||
@@ -219,7 +235,7 @@ There are three options how you can install an unstable version:
|
||||
|
||||
There are three options how you can install an unstable version.
|
||||
|
||||
1. You can either install a beta version of AdGuard Home which we update periodically.
|
||||
1. You can either install a beta or edge version of AdGuard Home which we update periodically. If you're already using stable version of AdGuard Home, just replace the executable with a new one.
|
||||
2. You can use the Docker image from the `edge` tag, which is synced with the repo master branch.
|
||||
3. You can install AdGuard Home from `beta` or `edge` channels on the Snap Store.
|
||||
|
||||
|
||||
@@ -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": "- "
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
1
client/babel.config.js
vendored
1
client/babel.config.js
vendored
@@ -11,6 +11,7 @@ module.exports = (api) => {
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'react-hot-loader/babel',
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
11
client/constants.js
vendored
Normal file
11
client/constants.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const BUILD_ENVS = {
|
||||
dev: 'development',
|
||||
prod: 'production',
|
||||
};
|
||||
|
||||
const BASE_URL = '/control';
|
||||
|
||||
module.exports = {
|
||||
BUILD_ENVS,
|
||||
BASE_URL,
|
||||
};
|
||||
213
client/package-lock.json
generated
vendored
213
client/package-lock.json
generated
vendored
@@ -1356,6 +1356,17 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||
},
|
||||
"@hot-loader/react-dom": {
|
||||
"version": "16.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@hot-loader/react-dom/-/react-dom-16.13.0.tgz",
|
||||
"integrity": "sha512-lJZrmkucz2MrQJTQtJobx5MICXcfQvKihszqv655p557HPi0hMOWxrNpiHv3DWD8ugNWjtWcVWqRnFvwsHq1mQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.19.0"
|
||||
}
|
||||
},
|
||||
"@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
@@ -1909,6 +1920,11 @@
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
|
||||
@@ -2252,6 +2268,12 @@
|
||||
"@types/istanbul-lib-report": "*"
|
||||
}
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
|
||||
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@@ -2723,7 +2745,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
@@ -3097,6 +3118,11 @@
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
|
||||
@@ -4818,8 +4844,7 @@
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.1.1",
|
||||
@@ -5099,6 +5124,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dom-walk": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
|
||||
"dev": true
|
||||
},
|
||||
"domain-browser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||
@@ -5192,9 +5223,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
||||
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@@ -5207,9 +5238,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.8",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
||||
"version": "4.11.9",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5878,8 +5909,7 @@
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"esquery": {
|
||||
"version": "1.3.1",
|
||||
@@ -6835,6 +6865,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
|
||||
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"min-document": "^2.19.0",
|
||||
"process": "^0.11.10"
|
||||
}
|
||||
},
|
||||
"global-modules": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
|
||||
@@ -7348,18 +7388,6 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
|
||||
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"http-proxy": "^1.17.0",
|
||||
"is-glob": "^4.0.0",
|
||||
"lodash": "^4.17.11",
|
||||
"micromatch": "^3.1.10"
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
@@ -9889,10 +9917,9 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"dev": true,
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
@@ -10159,9 +10186,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.15",
|
||||
@@ -10619,6 +10646,15 @@
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"dev": true
|
||||
},
|
||||
"min-document": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
|
||||
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dom-walk": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"min-indent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
||||
@@ -11044,6 +11080,24 @@
|
||||
"prepend-http": "^1.0.0",
|
||||
"query-string": "^4.1.0",
|
||||
"sort-keys": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"query-string": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"npm-run-path": {
|
||||
@@ -12138,13 +12192,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"query-string": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
||||
"dev": true,
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
|
||||
"integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
|
||||
"requires": {
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"split-on-first": "^1.0.0",
|
||||
"strict-uri-encode": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"querystring": {
|
||||
@@ -12260,6 +12314,39 @@
|
||||
"scheduler": "^0.19.1"
|
||||
}
|
||||
},
|
||||
"react-hot-loader": {
|
||||
"version": "4.12.21",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz",
|
||||
"integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"global": "^4.3.0",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"shallowequal": "^1.1.0",
|
||||
"source-map": "^0.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "11.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.4.0.tgz",
|
||||
@@ -13301,6 +13388,12 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -13455,6 +13548,18 @@
|
||||
"is-data-descriptor": "^1.0.0",
|
||||
"kind-of": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13696,6 +13801,11 @@
|
||||
"integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==",
|
||||
"dev": true
|
||||
},
|
||||
"split-on-first": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
|
||||
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
|
||||
},
|
||||
"split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
@@ -13708,8 +13818,7 @@
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
@@ -13833,10 +13942,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "4.0.1",
|
||||
@@ -14037,12 +14145,13 @@
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
|
||||
"integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.0",
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
}
|
||||
@@ -15888,6 +15997,18 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
|
||||
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"http-proxy": "^1.17.0",
|
||||
"is-glob": "^4.0.0",
|
||||
"lodash": "^4.17.11",
|
||||
"micromatch": "^3.1.10"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
||||
9
client/package.json
vendored
9
client/package.json
vendored
@@ -4,14 +4,16 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-dev": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js",
|
||||
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
|
||||
"build-prod": "cross-env BUILD_ENV=prod webpack --config webpack.prod.js",
|
||||
"watch": "cross-env BUILD_ENV=dev webpack --config webpack.dev.js --watch",
|
||||
"watch:hot": "cross-env BUILD_ENV=dev webpack-dev-server --config webpack.dev.js",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@nivo/line": "^0.49.1",
|
||||
"axios": "^0.19.2",
|
||||
"classnames": "^2.2.6",
|
||||
@@ -19,9 +21,11 @@
|
||||
"i18next": "^19.4.4",
|
||||
"i18next-browser-languagedetector": "^4.2.0",
|
||||
"ipaddr.js": "^1.9.1",
|
||||
"lodash": "^4.17.15",
|
||||
"js-yaml": "^3.14.0",
|
||||
"lodash": "^4.17.19",
|
||||
"nanoid": "^3.1.9",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-click-outside": "^3.0.1",
|
||||
"react-dom": "^16.13.1",
|
||||
@@ -72,6 +76,7 @@
|
||||
"path": "^0.12.7",
|
||||
"postcss-flexbugs-fixes": "4.2.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"style-loader": "^1.2.1",
|
||||
"stylelint": "^13.5.0",
|
||||
"stylelint-webpack-plugin": "2.0.0",
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
@@ -84,7 +84,7 @@
|
||||
"disable_protection": "Onemogući zaštitu",
|
||||
"disabled_protection": "Onemogućena zaštita",
|
||||
"refresh_statics": "Osvježi statistiku",
|
||||
"dns_query": "DNS Upiti",
|
||||
"dns_query": "DNS upiti",
|
||||
"blocked_by": "<0>Blokirano filtrima</0>",
|
||||
"stats_malware_phishing": "Blokiran zločudni program/krađe identiteta",
|
||||
"stats_adult": "Blokirane web stranice za odrasle",
|
||||
@@ -111,7 +111,7 @@
|
||||
"block_domain_use_filters_and_hosts": "Blokiraj domene koristeći filtre ili hosts datoteke",
|
||||
"filters_block_toggle_hint": "Pravila blokiranja možete postaviti u postavkama <a href='#filters'>filtara</a>.",
|
||||
"use_adguard_browsing_sec": "Koristi AdGuard uslugu zaštite pregledavanja",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home će provjeriti nalazi li se domena na popisu neželjenih domena od usluge zaštite pregledavanja. Za provjeru će se koristiti API za provjeru koji poštuje vašu privatnost. Samo mali dio SHA256 hash-a odnaziva domene se šalje poslužitelju.",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home će provjeriti nalazi li se domena na popisu neželjenih domena od usluge zaštite pregledavanja. Za provjeru će se koristiti API za provjeru koji poštuje vašu privatnost. Samo mali dio SHA256 hash-a od naziva domene se šalje poslužitelju.",
|
||||
"use_adguard_parental": "Koristi web uslugu AdGuard roditeljske zaštite",
|
||||
"use_adguard_parental_hint": "AdGuard Home provjeriti će sadrži li domena sadržaj za odrasle. Koristi isti API za zaštitu privatnosti kao i naša usluga zaštite pregledavanja.",
|
||||
"enforce_safe_search": "Omogući sigurno pretraživanje",
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Novi popis omogućenih",
|
||||
"edit_blocklist": "Uredi popis blokiranih",
|
||||
"edit_allowlist": "Uredi popis omogućenih",
|
||||
"choose_blocklist": "Odaberite popis blokiranih",
|
||||
"choose_allowlist": "Odaberite popis dopuštenih",
|
||||
"enter_valid_blocklist": "Unesite valjani URL za popis blokiranih.",
|
||||
"enter_valid_allowlist": "Unesite valjani URL za popis omogućenih.",
|
||||
"form_error_url_format": "Nevažeći URL format",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimiraj IP klijenta",
|
||||
"anonymize_client_ip_desc": "Ne spremajte cijelu IP adresu klijenta u zapisnike i statistike",
|
||||
"dns_config": "DNS postavke poslužitelja",
|
||||
"dns_cache_config": "DNS predmemorija poslužitelja",
|
||||
"dns_cache_config_desc": "Ovdje možete postaviti DNS predmemoriju",
|
||||
"blocking_mode": "Način blokiranja",
|
||||
"default": "Zadano",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Domena",
|
||||
"answer": "Odgovor",
|
||||
"filter_added_successfully": "Popis je uspješno dodan",
|
||||
"filter_removed_successfully": "Ovaj popis je uspješno uklonjen",
|
||||
"filter_updated": "Ovaj popis je uspješno ažuriran",
|
||||
"statistics_configuration": "Postavke statistike",
|
||||
"statistics_retention": "Spremanje statistike",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Saznajte više</0> o stvaranju vlastitog popisa poslužitelja.",
|
||||
"blocked_by_response": "Blokirano od strane CNAME-a ili IP-a u odgovoru",
|
||||
"blocked_by_cname_or_ip": "Blokirao CNAME ili IP",
|
||||
"try_again": "Pokušajte ponovno",
|
||||
"domain_desc": "Unesite naziv domene ili zamjenski znak koji želite prepisati.",
|
||||
"example_rewrite_domain": "prepiši odgovore samo za ovaj naziv domene.",
|
||||
"example_rewrite_wildcard": "prepiši odgovore za sve <0>example.org</0> poddomene.",
|
||||
"rewrite_ip_address": "IP adresa: koristite ovu IP adresu u A ili AAAA odgovoru",
|
||||
"rewrite_domain_name": "Naziv domene: Dodajte CNAME zapis",
|
||||
"rewrite_A": "<0>A</0>: posebna vrijednost, ukloni <0>A</0> zapis od upstreama",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: posebna vrijednost, ukloni <0>AAAA</0> zapis od upstreama",
|
||||
"disable_ipv6": "Onemogući IPv6",
|
||||
"disable_ipv6_desc": "Ukoliko je ova značajka omogućena, svi DNS upiti za IPv6 adrese (AAAA tip) će biti odbačeni.",
|
||||
"fastest_addr": "Najbrža IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Provjeri",
|
||||
"form_enter_host": "Unesite naziv računala",
|
||||
"filtered_custom_rules": "Filtrirano prilagođenim pravilima filtriranja",
|
||||
"choose_from_list": "Odaberite s popisa",
|
||||
"add_custom_list": "Dodajte prilagođeni popis",
|
||||
"host_whitelisted": "Računalo je na popisu dopuštenih",
|
||||
"check_ip": "IP adrese: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Omogući DNSSEC",
|
||||
"dnssec_enable_desc": "Omogućite DNSSEC u izlaznim DNS upitima i provjerite rezultat (potreban je resolver s omogućenim DNSSEC-om)",
|
||||
"validated_with_dnssec": "Potvrđeno s DNSSEC-om",
|
||||
"show_all_responses": "Svi odgovori",
|
||||
"all_queries": "Svi upiti",
|
||||
"show_blocked_responses": "Blokirano",
|
||||
"show_whitelisted_responses": "Na popisu dopuštenih",
|
||||
"show_processed_responses": "Obrađeno",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Prepisano",
|
||||
"safe_search": "Sigurno pretraživanje",
|
||||
"blocklist": "Popis neželjenih",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veličina predmemorije",
|
||||
"cache_size_desc": "Veličina DNS predmemorije (u bajtovima)",
|
||||
"cache_ttl_min_override": "Nadjačaj minimalni TTL",
|
||||
"cache_ttl_max_override": "Nadjačaj maksimalni TTL",
|
||||
"enter_cache_size": "Unesite veličinu predmemorije",
|
||||
"enter_cache_ttl_min_override": "Unesite minimalni TTL",
|
||||
"enter_cache_ttl_max_override": "Unesite maksimalni TTL",
|
||||
"cache_ttl_min_override_desc": "Nadjačaj TTL vrijednost (minimum) dobivenu od upstream poslužitelja. Ova vrijednost ne može biti veća od 3600 (1 sat)",
|
||||
"cache_ttl_max_override_desc": "Nadjačaj TTL vrijednost (maksimum) dobivenu od upstream poslužitelja",
|
||||
"min_exceeds_max_value": "Minimalna vrijednost je veća od maksimalne",
|
||||
"value_not_larger_than": "Vrijednost ne može biti veća od {{maximum}}",
|
||||
"filter_category_general": "Općenito",
|
||||
"filter_category_security": "Sigurnost",
|
||||
"filter_category_regional": "Regionalno",
|
||||
"filter_category_other": "Ostalo",
|
||||
"filter_category_general_desc": "Popisi koji blokiraju pratitelje i oglase na većini uređaja",
|
||||
"filter_category_security_desc": "Popisi koju su specijalizirani za blokiranje zlonamjernih programa, krađe identiteta ili domena za obmanu",
|
||||
"filter_category_regional_desc": "Popisi koji se fokusiraju na regionalne oglase i poslužitelje za praćenje",
|
||||
"filter_category_other_desc": "Ostali popisi blokiranih",
|
||||
"original_response": "Originalni odgovor",
|
||||
"click_to_view_queries": "Kliknite za pregled upita",
|
||||
"port_53_faq_link": "Port 53 često zauzimaju usluge \"DNSStubListener\" ili \"systemd-resolved\". Molimo pročitajte <0>ove upute</0> o tome kako to riješiti."
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
"example_upstream_reserved": "Anda dapat menetapkan DNS upstream <0>untuk domain spesifik</0>",
|
||||
"upstream_parallel": "Gunakan kueri paralel untuk mempercepat resoluasi dengan menanyakan semua server upstream secara bersamaan",
|
||||
"parallel_requests": "Permintaan paralel",
|
||||
"load_balancing": "Penyeimbang beban",
|
||||
"load_balancing_desc": "Permintaan satu server pada satu waktu. AdGuard Home akan menggunakan algoritma acak tertimbang untuk memilih server sehingga server tercepat akan lebih sering digunakan.",
|
||||
"bootstrap_dns": "Server DNS bootstrap",
|
||||
"bootstrap_dns_desc": "Server Bootstrap DNS dapat digunakan untuk meresolve alamat IP pada DoH/DoT resolvers yang Anda tentukan sebagai upstreams.",
|
||||
"check_dhcp_servers": "Cek untuk server DHCP",
|
||||
@@ -51,9 +53,11 @@
|
||||
"dhcp_add_static_lease": "Tambah static lease",
|
||||
"dhcp_reset": "Apakah anda yakin ingin mengatur ulang konfigurasi DHCP anda?",
|
||||
"country": "Negara",
|
||||
"city": "Kota",
|
||||
"delete_confirm": "Apakah anda yakin ingin menghapus \"{{key}}\"?",
|
||||
"form_enter_hostname": "Masukkan hostname",
|
||||
"error_details": "Detail kesalahan",
|
||||
"response_details": "Detail respon",
|
||||
"request_details": "Detai permintaan",
|
||||
"client_details": "Detail klien",
|
||||
"details": "Detail",
|
||||
@@ -63,6 +67,8 @@
|
||||
"filters": "Penyaring",
|
||||
"filter": "Filter",
|
||||
"query_log": "Catatan Kueri",
|
||||
"compact": "Rapat",
|
||||
"nothing_found": "Tidak ditemukan",
|
||||
"faq": "Tanya Jawab",
|
||||
"version": "versi",
|
||||
"address": "Alamat",
|
||||
@@ -114,12 +120,16 @@
|
||||
"general_settings": "Pengaturan umum",
|
||||
"dns_settings": "Pengaturan DNS",
|
||||
"dns_blocklists": "Daftar blokir DNS",
|
||||
"dns_allowlists": "Daftar putih DNS",
|
||||
"dns_blocklists_desc": "AdGuard Home akan memblokir domain yang cocok dengan daftar hitam.",
|
||||
"dns_allowlists_desc": "Domain dari daftar putih DNS akan diizinkan bahkan jika mereka ada juga di daftar hitam.",
|
||||
"custom_filtering_rules": "Aturan penyaringan khusus",
|
||||
"encryption_settings": "Pengaturan enkripsi",
|
||||
"dhcp_settings": "Pengaturan DHCP",
|
||||
"upstream_dns": "Server DNS hulu",
|
||||
"upstream_dns_hint": "Jika Anda mengosongkan kolom ini, AdGuard Home akan menggunakan <a href='https://1.1.1.1/' target='_blank'>Cloudflare DNS</a> sebagai hulu. Gunakan tls:// untuk server DNS over TLS.",
|
||||
"test_upstream_btn": "Uji hulu",
|
||||
"upstreams": "Upstream",
|
||||
"apply_btn": "Terapkan",
|
||||
"disabled_filtering_toast": "Penyaringan nonaktif",
|
||||
"enabled_filtering_toast": "Penyaringan aktif",
|
||||
@@ -131,6 +141,7 @@
|
||||
"enabled_save_search_toast": "Pencarian aman diaktifkan",
|
||||
"enabled_table_header": "Diaktifkan",
|
||||
"name_table_header": "Nama",
|
||||
"list_url_table_header": "Daftar URL",
|
||||
"rules_count_table_header": "Jumlah Aturan",
|
||||
"last_time_updated_table_header": "Terakhir diperbaharui",
|
||||
"actions_table_header": "Aksi",
|
||||
@@ -139,10 +150,23 @@
|
||||
"delete_table_action": "Hapus",
|
||||
"elapsed": "Berlalu",
|
||||
"filters_and_hosts_hint": "AdGuard Home memahami aturan dasar adblock dan sintak file hosts.",
|
||||
"no_blocklist_added": "Tiada daftar hitam ditambahkan",
|
||||
"no_whitelist_added": "Tiada daftar putih ditambahkan",
|
||||
"add_blocklist": "Tambah daftar hitam",
|
||||
"add_allowlist": "Tambah daftar putih",
|
||||
"cancel_btn": "Batal",
|
||||
"enter_name_hint": "Masukkan nama",
|
||||
"enter_url_or_path_hint": "Masukan sebuah URL atau jalur absolut dari daftar",
|
||||
"check_updates_btn": "Cek pembaruan",
|
||||
"new_blocklist": "Daftar hitam baru",
|
||||
"new_allowlist": "Daftar putih baru",
|
||||
"edit_blocklist": "Edit daftar hitam",
|
||||
"edit_allowlist": "Edit daftar putih",
|
||||
"choose_blocklist": "Pilih daftar hitam",
|
||||
"choose_allowlist": "Pilih daftar putih",
|
||||
"enter_valid_blocklist": "Masukkan valid URL ke daftar hitam.",
|
||||
"enter_valid_allowlist": "Masukkan valid URL ke daftar putih.",
|
||||
"form_error_url_format": "Format URL tidak valid",
|
||||
"form_error_url_or_path_format": "URL atau jalur absolut dari daftar tidak valid",
|
||||
"custom_filter_rules": "Aturan penyaringan khusus",
|
||||
"custom_filter_rules_hint": "Masukkan satu aturan dalam sebuah baris. Anda dapat menggunakan baik aturan adblock maupun sintaks file hosts.",
|
||||
@@ -159,6 +183,7 @@
|
||||
"example_upstream_doh": "terenkripsi <0>DNS-over-HTTPS</0>",
|
||||
"example_upstream_sdns": "anda bisa menggunakan <0>Stempel DNS</0> untuk <1>DNSCrypt</1> atau pengarah <2>DNS-over-HTTPS</2>",
|
||||
"example_upstream_tcp": "DNS reguler (melalui TCP)",
|
||||
"all_lists_up_to_date_toast": "Semua daftar sudah diperbarui",
|
||||
"updated_upstream_dns_toast": "Server DNS hulu terbarui",
|
||||
"dns_test_ok_toast": "Server DNS yang ditentukan bekerja dengan benar",
|
||||
"dns_test_not_ok_toast": "Server \"{{key}}\": tidak dapat digunakan, mohon cek bahwa Anda telah menulisnya dengan benar",
|
||||
@@ -170,6 +195,7 @@
|
||||
"domain_or_client": "Domain atau klien",
|
||||
"type_table_header": "Tipe",
|
||||
"response_table_header": "Respon",
|
||||
"response_code": "Kode respon",
|
||||
"client_table_header": "Klien",
|
||||
"empty_response_status": "Kosong",
|
||||
"show_all_filter_type": "Tampilkan semua",
|
||||
@@ -188,6 +214,7 @@
|
||||
"query_log_filtered": "Difilter oleh {{filter}}",
|
||||
"query_log_confirm_clear": "Apakah Anda yakin ingin menghapus seluruh kueri log?",
|
||||
"query_log_cleared": "Kueri log telah berhasil dihapus",
|
||||
"query_log_updated": "Log permintaan telah berhasil diperbarui",
|
||||
"query_log_clear": "Hapus kueri log",
|
||||
"query_log_retention": "Retensi kueri log",
|
||||
"query_log_enable": "Aktifkan log",
|
||||
@@ -195,19 +222,39 @@
|
||||
"query_log_disabled": "Kueri log dinonaktifkan dan dapat dikonfigurasi di <0>pengaturan</0>",
|
||||
"query_log_strict_search": "Gunakan tanda kutip ganda untuk pencarian ketat",
|
||||
"query_log_retention_confirm": "Apakah Anda yakin ingin mengubah retensi kueri log? Jika Anda menurunkan nilai interval, beberapa data akan hilang",
|
||||
"anonymize_client_ip": "Anonim IP klien",
|
||||
"anonymize_client_ip_desc": "Jangan simpan alamat lengkap IP klien dalam log dan statistik",
|
||||
"dns_config": "Konfigurasi server DNS",
|
||||
"dns_cache_config": "Konfigurasi cache DNS",
|
||||
"dns_cache_config_desc": "Disini Anda bisa mengonfigurasi cache DNS",
|
||||
"blocking_mode": "Mode blokir",
|
||||
"default": "Standar",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
"null_ip": "Null IP",
|
||||
"custom_ip": "Custom IP",
|
||||
"blocking_ipv4": "Blokiran IPv4",
|
||||
"blocking_ipv6": "Blokiran IPv6",
|
||||
"dns_over_https": "DNS-over-HTTPS",
|
||||
"dns_over_tls": "DNS-over-TLS",
|
||||
"plain_dns": "Plain DNS",
|
||||
"form_enter_rate_limit": "Masukkan batas nilai",
|
||||
"rate_limit": "Batas nilai",
|
||||
"edns_enable": "Aktifkan EDNS Klien Subnet",
|
||||
"edns_cs_desc": "Apabila dinyalakan, AdGuard Home akan mengirim subnet klien ke server-server DNS.",
|
||||
"rate_limit_desc": "Jumlah permintaan per detik yang diperbolehkan untuk satu klien (0: tidak terbatas)",
|
||||
"blocking_ipv4_desc": "Alamat IP akan dikembalikan untuk permintaan A yang diblokir",
|
||||
"blocking_ipv6_desc": "Alamat IP akan dipulihkan untuk permintaan AAAA yang diblokir",
|
||||
"blocking_mode_default": "Standar: Respon pakai NXDOMAIN saat diblokir oleh aturan gaya Adblock; membalas dengan alamat IP yang ditentukan dalam aturan ketika diblokir oleh /et /aturan hosts-style",
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: Respon pakai kode NXDOMAIN",
|
||||
"blocking_mode_null_ip": "Null IP: Respon pakai alamat IP kosong (0.0.0.0 untuk A; :: untuk AAAA)",
|
||||
"blocking_mode_custom_ip": "IP kustom: respon dengan alamat IP yang diset secara manual",
|
||||
"upstream_dns_client_desc": "Jika Anda biarkan bidang ini kosong, AdGuard Home akan memakai server yang dikonfigurasi di<0>Pengaturan DNS</0>.",
|
||||
"tracker_source": "Sumber pelacak",
|
||||
"source_label": "Sumber",
|
||||
"found_in_known_domain_db": "Ditemukan di database domain dikenal",
|
||||
"category_label": "Kategori",
|
||||
"rule_label": "Aturan",
|
||||
"list_label": "Daftar",
|
||||
"unknown_filter": "Penyaringan {{filterId}} tidak dikenal",
|
||||
"known_tracker": "Pelacak yang dikenal",
|
||||
"install_welcome_title": "Selamat datang di AdGuard Home!",
|
||||
@@ -324,6 +371,8 @@
|
||||
"client_identifier_desc": "Klien dapat diidentifikasi dengan alamat IP atau alamat MAC. Harap dicatat bahwa menggunakan MAC sebagai pengidentifikasi hanya dimungkinkan jika AdGuard Home juga merupakan <0>server DHCP</0>",
|
||||
"form_enter_ip": "Masukkan IP",
|
||||
"form_enter_mac": "Masukkan MAC",
|
||||
"form_enter_id": "Masukkan pengenal",
|
||||
"form_add_id": "Tambah pengenal",
|
||||
"form_client_name": "Masukkan nama klien",
|
||||
"name": "Nama",
|
||||
"client_global_settings": "Gunakan pengaturan global",
|
||||
@@ -332,6 +381,7 @@
|
||||
"client_updated": "Klien \"{{key}}\" berhasil diperbarui",
|
||||
"clients_not_found": "Tidak ada klien ditemukan",
|
||||
"client_confirm_delete": "Apakah anda yakin ingin menghapus klien \"{{key}}\"?",
|
||||
"list_confirm_delete": "Anda yakin ingin menghapus daftar ini?",
|
||||
"auto_clients_title": "Klien (waktu berjalan)",
|
||||
"auto_clients_desc": "Data pada klien yang menggunakan AdGuard Home, tetapi tidak disimpan dalam konfigurasi",
|
||||
"access_title": "Pengaturan akses",
|
||||
@@ -399,6 +449,8 @@
|
||||
"domain": "Domain",
|
||||
"answer": "Jawab",
|
||||
"filter_added_successfully": "Filter telah berhasil ditambahkan",
|
||||
"filter_removed_successfully": "Daftar ini telah sukses dihapus",
|
||||
"filter_updated": "Daftar telah sukses diperbarui",
|
||||
"statistics_configuration": "Konfigurasi statistik",
|
||||
"statistics_retention": "Statistik disimpan",
|
||||
"statistics_retention_desc": "Jika Anda menurunkan nilai interval, beberapa data akan hilang",
|
||||
@@ -428,13 +480,33 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Pelajari lebih lanjut</0> tentang membuat daftar hitam host Anda sendiri.",
|
||||
"blocked_by_response": "Diblokir oleh CNAME atau IP sebagai respon",
|
||||
"blocked_by_cname_or_ip": "Diblokir oleh CNAME atau IP",
|
||||
"try_again": "Coba lagi",
|
||||
"domain_desc": "Masukkan nama domain atau wildcard yang ingin Anda tulis ulang.",
|
||||
"example_rewrite_domain": "tulis ulang respon hanya untuk domain ini saja.",
|
||||
"example_rewrite_wildcard": "tulis ulang respon untuk semua subdomain <0>contoh.org</0>.",
|
||||
"rewrite_ip_address": "Alamat IP: pakai IP ini dalam respons A atau AAAA",
|
||||
"rewrite_domain_name": "Nama domain: tambah ke rekaman CNAME",
|
||||
"rewrite_A": "<0>A</0>: nilai khusus, biarkan <0>A</0> merekam dari upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: nilai khusus, biarkan <0>AAAA</0> merekam dari upstream",
|
||||
"disable_ipv6": "Matikan IPv6",
|
||||
"disable_ipv6_desc": "Apabila fitur ini dinyalakan, semua permintaan DNS untuk alamat-alamat IPv6 (tipe AAAA) akan diputus.",
|
||||
"fastest_addr": "Alamat IP tercepat",
|
||||
"fastest_addr_desc": "Permintaan semua server DNS dan kembalinya alamat IP tercepat di antara semua respons",
|
||||
"autofix_warning_text": "Apabila anda menekan \"Perbaiki\", AdGuardHome akan mengatur sistem anda untuk menggunakan server DNS AdGuardHome.",
|
||||
"autofix_warning_list": "Ini akan melakukan tugas berikut: <0>Nonaktifkan sistem DNSStubListener</0> <0> Atur alamat server DNS ke 127.0.0.1</0> <0>Ganti target tautan simbolis /etc/resolv.conf pakai /run/systemd/resolve/resolv.conf</0> <0>Hentikan DNSStubListener (muat ulang layanan sistemd-resolve service)</0>",
|
||||
"autofix_warning_result": "Hasilnya, semua permintaan DNS dari sistem anda akan diproses oleh AdGuardHome secara standar.",
|
||||
"tags_title": "Tag",
|
||||
"tags_desc": "Anda dapat memilih tag sesuai dengan klien. Tag dapat dimasukkan dalam aturan pemfilteran dan memungkinkan Anda untuk menerapkannya lebih akurat. <0>Pelajari lebih</0>",
|
||||
"form_select_tags": "Pilih tag klien",
|
||||
"check_title": "Periksa penyaringan",
|
||||
"check_desc": "Periksa apakah nama host telah tersaring",
|
||||
"check": "Periksa",
|
||||
"form_enter_host": "Masukkan nama host",
|
||||
"filtered_custom_rules": "Tersaring oleh aturan penyaring Buatan",
|
||||
"choose_from_list": "Pilih dari daftar",
|
||||
"add_custom_list": "Tambah daftar buatan",
|
||||
"host_whitelisted": "Host didaftar putihkan",
|
||||
"check_ip": "Alamat IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
"check_reason": "Alasan: {{reason}}",
|
||||
@@ -446,12 +518,50 @@
|
||||
"client_blocked": "Klien \"{{ip}}\" sukses di blokir",
|
||||
"client_unblocked": "Klien \"{{ip}}\" sukses di unblock",
|
||||
"static_ip": "Alamat IP statis",
|
||||
"static_ip_desc": "AdGuard Home adalah server jadi perlu alamat IP statis agar berfungsi dengan benar. Jika tidak, pada titik tertentu, router Anda dapat menetapkan alamat IP yang berbeda untuk perangkat ini.",
|
||||
"set_static_ip": "Atur alamat IP statik",
|
||||
"install_static_ok": "Kabar baik! Alamat IP statis sudah dikonfigurasi",
|
||||
"install_static_error": "AdGuard Home tidak dapat mengonfigurasinya secara otomatis untuk antarmuka jaringan ini. Silakan cari instruksi tentang cara melakukan ini secara manual.",
|
||||
"install_static_configure": "Kami mendeteksi alamat IP dinamis digunakan - <0>{{ip}}</0>. Anda ingin menggunakannya sebagai alamat statis Anda?",
|
||||
"confirm_static_ip": "AdGuard Home akan mengonfigurasi {{ip}} menjadi alamat IP statis Anda. Anda ingin melanjutkan?",
|
||||
"list_updated": "{{count}} daftar terbarui",
|
||||
"list_updated_plural": "{{count}} daftar terbarui",
|
||||
"dnssec_enable": "Aktifkan DNSSEC",
|
||||
"dnssec_enable_desc": "Atur bendera DNSSEC di permintaan keluar DNS dan periksa hasilnya (resolver berkemampuan DNSSEC diperlukan)",
|
||||
"validated_with_dnssec": "Tervalidasi dengan DNSSEC",
|
||||
"show_all_responses": "Semua respon",
|
||||
"all_queries": "Semua permintaan",
|
||||
"show_blocked_responses": "Diblokir",
|
||||
"show_whitelisted_responses": "Dalam Daftar Putih",
|
||||
"show_processed_responses": "Terproses",
|
||||
"blocked_safebrowsing": "Terblokir oleh Safebrowsing",
|
||||
"blocked_adult_websites": "Situs Dewasa Terblokir",
|
||||
"blocked_threats": "Blokir Ancaman",
|
||||
"allowed": "Dibolehkan",
|
||||
"filtered": "Tersaring",
|
||||
"rewritten": "Tulis ulang",
|
||||
"safe_search": "Pencarian aman",
|
||||
"blocklist": "Daftar blokir",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Ukuran cache",
|
||||
"cache_size_desc": "Ukuran DNS cache (dalam bit)",
|
||||
"cache_ttl_min_override": "Tumpuk TTL minimum",
|
||||
"cache_ttl_max_override": "Tumpuk TTL maksimum",
|
||||
"enter_cache_size": "Masukkan ukuran cache",
|
||||
"enter_cache_ttl_min_override": "Masukkan TTL minimum",
|
||||
"enter_cache_ttl_max_override": "Masukkan TTL maksimum",
|
||||
"cache_ttl_min_override_desc": "Ganti nilai TTL (minimum) yang diterima dari server upstream. Nilai ini tidak boleh lebih dari 3600 (1 jam)",
|
||||
"cache_ttl_max_override_desc": "Ganti nilai TTL (maksimum) yang diterima dari server upstream",
|
||||
"min_exceeds_max_value": "Nilai minimum melebihi nilai maksimum",
|
||||
"value_not_larger_than": "Nilai tidak bisa lebih dari {{maximum}}",
|
||||
"filter_category_general": "Umum",
|
||||
"filter_category_security": "Keamanan",
|
||||
"filter_category_regional": "Wilayah",
|
||||
"filter_category_other": "Lainnya",
|
||||
"filter_category_general_desc": "Daftar yang memblokir pelacakan dan iklan di sebagian besar perangkat",
|
||||
"filter_category_security_desc": "Daftar yang khusus pada pemblokiran malware, phishing, atau domain penipuan",
|
||||
"filter_category_regional_desc": "Daftar yang berfokus pada iklan regional dan server pelacakan",
|
||||
"filter_category_other_desc": "Daftar hitam lain",
|
||||
"original_response": "Respon asli",
|
||||
"click_to_view_queries": "Klik untuk lihat permintaan",
|
||||
"port_53_faq_link": "Port 53 sering ditempati oleh layanan \"DNSStubListener\" atau \"systemd-resolved\". Silakan baca <0>instruksi ini</0> tentang cara menyelesaikan ini."
|
||||
}
|
||||
@@ -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,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>をお読みください。"
|
||||
}
|
||||
@@ -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,14 +101,14 @@
|
||||
"number_of_dns_query_days": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_days_plural": "Liczba przetworzonych zapytań DNS w ciągu ostatnich {{count}} dni",
|
||||
"number_of_dns_query_24_hours": "Liczba zapytań DNS przetworzonych w ciągu ostatnich 24 godzin",
|
||||
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy bloków hosta",
|
||||
"number_of_dns_query_blocked_24_hours": "Liczba żądań DNS zablokowanych przez filtry blokowania reklam i listy zablokowanych hostów",
|
||||
"number_of_dns_query_blocked_24_hours_by_sec": "Liczba żądań DNS zablokowanych przez moduł AdGuard Bezpieczne Przeglądanie",
|
||||
"number_of_dns_query_blocked_24_hours_adult": "Liczba zablokowanych witryn dla dorosłych",
|
||||
"enforced_save_search": "Wymuszone bezpieczne wyszukiwanie",
|
||||
"number_of_dns_query_to_safe_search": "Liczba żądań DNS do wyszukiwarek, dla których zastosowano wymuszenie bezpiecznego wyszukiwania",
|
||||
"average_processing_time": "Średni czas przetwarzania",
|
||||
"average_processing_time_hint": "Średni czas przetwarzania żądania DNS liczony w milisekundach",
|
||||
"block_domain_use_filters_and_hosts": "Blokuj domeny za pomocą filtrów i plików host",
|
||||
"block_domain_use_filters_and_hosts": "Zablokuj domeny za pomocą filtrów i plików host",
|
||||
"filters_block_toggle_hint": "Możesz skonfigurować reguły blokowania w ustawieniach <a href='#filters'>Filtry</a> ",
|
||||
"use_adguard_browsing_sec": "Użyj usługi sieciowej Bezpieczne Przeglądanie AdGuard",
|
||||
"use_adguard_browsing_sec_hint": "AdGuard Home sprawdzi, czy domena jest na czarnej liście przez serwis internetowy Bezpieczne Przeglądanie. Będzie korzystać z interfejsu API przyjaznego dla prywatności w celu przeprowadzenia kontroli: na serwer wysyłany jest tylko krótki prefiks nazwy domeny SHA256.",
|
||||
@@ -118,10 +119,10 @@
|
||||
"no_servers_specified": "Nie określono serwerów",
|
||||
"general_settings": "Ustawienia główne",
|
||||
"dns_settings": "Ustawienia DNS",
|
||||
"dns_blocklists": "Lista zablokowanych DNS",
|
||||
"dns_allowlists": "Lista dozwolonych DNS",
|
||||
"dns_blocklists": "Listy zablokowanych DNS",
|
||||
"dns_allowlists": "Listy dozwolonych DNS",
|
||||
"dns_blocklists_desc": "AdGuard Home zablokuje domeny pasujące do listy zablokowanych.",
|
||||
"dns_allowlists_desc": "Domeny z białej listy DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
|
||||
"dns_allowlists_desc": "Domeny z listy dozwolonych DNS będą dozwolone, nawet jeśli znajdują się na jednej z zablokowanych list.",
|
||||
"custom_filtering_rules": "Niestandardowe reguły filtrowania",
|
||||
"encryption_settings": "Ustawienia szyfrowania",
|
||||
"dhcp_settings": "Ustawienia DHCP",
|
||||
@@ -149,8 +150,8 @@
|
||||
"delete_table_action": "Usuń",
|
||||
"elapsed": "Upłynęło",
|
||||
"filters_and_hosts_hint": "AdGuard Home rozumie podstawowe reguły adblocka i składnię plików hostów.",
|
||||
"no_blocklist_added": "Nie dodano listy zablokowanych",
|
||||
"no_whitelist_added": "Nie dodano listy dozwolonych",
|
||||
"no_blocklist_added": "Nie dodano list zablokowanych",
|
||||
"no_whitelist_added": "Nie dodano list dozwolonych",
|
||||
"add_blocklist": "Dodaj listę zablokowanych",
|
||||
"add_allowlist": "Dodaj listę dozwolonych",
|
||||
"cancel_btn": "Anuluj",
|
||||
@@ -161,6 +162,8 @@
|
||||
"new_allowlist": "Nowa lista dozwolonych",
|
||||
"edit_blocklist": "Edytuj listę zablokowanych",
|
||||
"edit_allowlist": "Edytuj listę dozwolonych",
|
||||
"choose_blocklist": "Wybierz listy zablokowanych",
|
||||
"choose_allowlist": "Wybierz listy dozwolonych",
|
||||
"enter_valid_blocklist": "Wpisz prawidłowy adres URL do listy zablokowanych.",
|
||||
"enter_valid_allowlist": "Wpisz prawidłowy adres URL do listy dozwolonych.",
|
||||
"form_error_url_format": "Format adresu URL jest nieprawidłowy",
|
||||
@@ -174,7 +177,7 @@
|
||||
"example_comment": "! Tutaj jest komentarz",
|
||||
"example_comment_meaning": "komentarz",
|
||||
"example_comment_hash": "# Również komentarz",
|
||||
"example_regex_meaning": "blokuj dostęp do domen pasujących do określonego wyrażenia regularnego",
|
||||
"example_regex_meaning": "zablokuj dostęp do domen pasujących do określonego wyrażenia regularnego",
|
||||
"example_upstream_regular": "normalny DNS (przez UDP)",
|
||||
"example_upstream_dot": "zaszyfrowany <0>DNS-over-TLS</0>",
|
||||
"example_upstream_doh": "zaszyfrowany <0>DNS-over-HTTPS</0>",
|
||||
@@ -211,6 +214,7 @@
|
||||
"query_log_filtered": "Filtrowane przez {{filter}}",
|
||||
"query_log_confirm_clear": "Czy na pewno chcesz wyczyścić cały dziennik zapytań?",
|
||||
"query_log_cleared": "Dziennik zapytań został pomyślnie wyczyszczony",
|
||||
"query_log_updated": "Dziennik zapytań został zaktualizowany",
|
||||
"query_log_clear": "Wyczyść dzienniki zapytań",
|
||||
"query_log_retention": "Przechowywanie dzienników zapytań",
|
||||
"query_log_enable": "Włącz dziennik",
|
||||
@@ -221,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonimizuj adres IP klienta",
|
||||
"anonymize_client_ip_desc": "Nie zapisuj pełnego adresu IP w dziennikach i statystykach",
|
||||
"dns_config": "Konfiguracja serwera DNS",
|
||||
"dns_cache_config": "Konfiguracja pamięci podręcznej DNS",
|
||||
"dns_cache_config_desc": "Tutaj możesz skonfigurować pamięć podręczną DNS",
|
||||
"blocking_mode": "Tryb blokowania",
|
||||
"default": "Domyślny",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -243,6 +249,7 @@
|
||||
"blocking_mode_null_ip": "Null IP: Odpowiedz z zerowym adresem IP (0.0.0.0 dla A; :: dla AAAA)",
|
||||
"blocking_mode_custom_ip": "Niestandardowy adres IP: Odpowiedz ręcznie ustawionym adresem IP",
|
||||
"upstream_dns_client_desc": "Jeśli to pole pozostanie puste, AdGuard Home użyje serwerów skonfigurowanych w <0>Ustawieniach DNS</0>.",
|
||||
"tracker_source": "Źródło skryptu śledzącego",
|
||||
"source_label": "Źródło",
|
||||
"found_in_known_domain_db": "Znaleziono w bazie danych znanych domen.",
|
||||
"category_label": "Kategoria",
|
||||
@@ -420,7 +427,7 @@
|
||||
"form_error_answer_format": "Nieprawidłowy format odpowiedzi",
|
||||
"configure": "Skonfiguruj",
|
||||
"main_settings": "Ustawienia główne",
|
||||
"block_services": "Blokuj określone usługi",
|
||||
"block_services": "Zablokuj określone usługi",
|
||||
"blocked_services": "Zablokowane usługi",
|
||||
"blocked_services_desc": "Pozwala szybko zablokować popularne witryny i usługi.",
|
||||
"blocked_services_saved": "Zablokowane usługi zostały pomyślnie zapisane",
|
||||
@@ -437,11 +444,12 @@
|
||||
"stats_params": "Konfiguracja statystyk",
|
||||
"config_successfully_saved": "Konfiguracja została pomyślnie zapisana",
|
||||
"interval_24_hour": "24 godziny",
|
||||
"interval_days": "{{count}} dzień",
|
||||
"interval_days": "{{count}} dni",
|
||||
"interval_days_plural": "{{count}} dni",
|
||||
"domain": "Domena",
|
||||
"answer": "Odpowiedź",
|
||||
"filter_added_successfully": "Lista została pomyślnie dodana",
|
||||
"filter_removed_successfully": "Lista została usunięta",
|
||||
"filter_updated": "Filtr został pomyślnie zaktualizowany",
|
||||
"statistics_configuration": "Konfiguracja statystyk",
|
||||
"statistics_retention": "Przechowywanie statystyk",
|
||||
@@ -472,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Dowiedz się więcej</0> o tworzeniu własnych list blokowania hostów.",
|
||||
"blocked_by_response": "W odpowiedzi zablokowany przez CNAME lub IP",
|
||||
"blocked_by_cname_or_ip": "Zablokowany przez rekord CNAME lub adres IP",
|
||||
"try_again": "Spróbuj ponownie",
|
||||
"domain_desc": "Wpisz nazwę domeny lub symbol wieloznaczny, który chcesz przepisać.",
|
||||
"example_rewrite_domain": "przepisz odpowiedzi tylko dla tej nazwy domeny.",
|
||||
"example_rewrite_wildcard": "przepisz odpowiedzi dla wszystkich subdomen <0>example.org</0>.",
|
||||
"rewrite_ip_address": "Adres IP: użyj tego adresu IP w odpowiedzi A lub AAAA",
|
||||
"rewrite_domain_name": "Nazwa domeny: dodaj rekord CNAME",
|
||||
"rewrite_A": "<0>A</0>: wartość specjalna, zachowaj rekord <0>A</0> z głównego serwera DNS",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: wartość specjalna, zachowaj rekord <0>AAAA</0> z głównego serwera DNS",
|
||||
"disable_ipv6": "Wyłącz IPv6",
|
||||
"disable_ipv6_desc": "Jeśli ta funkcja jest włączona, wszystkie zapytania DNS dotyczące adresów IPv6 (typ AAAA) zostaną usunięte.",
|
||||
"fastest_addr": "Najszybszy adres IP",
|
||||
@@ -491,6 +504,8 @@
|
||||
"check": "Sprawdź",
|
||||
"form_enter_host": "Wpisz nazwę hosta",
|
||||
"filtered_custom_rules": "Filtrowane według niestandardowych reguł filtrowania",
|
||||
"choose_from_list": "Wybierz z listy",
|
||||
"add_custom_list": "Dodaj listę niestandardową",
|
||||
"host_whitelisted": "Host znajduje się na białej liście",
|
||||
"check_ip": "Adresy IP: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -514,7 +529,7 @@
|
||||
"dnssec_enable": "Włącz DNSSEC",
|
||||
"dnssec_enable_desc": "Ustaw flagę DNSSEC w wychodzących zapytaniach DNS i sprawdź wynik (wymagany jest usługodawca z obsługą zabezpieczania DNSSEC)",
|
||||
"validated_with_dnssec": "Zweryfikowany przez DNSSEC",
|
||||
"show_all_responses": "Wszystkie odpowiedzi",
|
||||
"all_queries": "Wszystkie zapytania",
|
||||
"show_blocked_responses": "Zablokowane",
|
||||
"show_whitelisted_responses": "Biała lista",
|
||||
"show_processed_responses": "Przetworzono",
|
||||
@@ -526,5 +541,27 @@
|
||||
"rewritten": "Przepisane",
|
||||
"safe_search": "Bezpieczne wyszukiwanie",
|
||||
"blocklist": "Lista zablokowanych",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
}
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Rozmiar pamięci podręcznej",
|
||||
"cache_size_desc": "Rozmiar pamięci podręcznej DNS (w bajtach)",
|
||||
"cache_ttl_min_override": "Nadpisz minimalną wartość TTL",
|
||||
"cache_ttl_max_override": "Nadpisz maksymalną wartość TTL",
|
||||
"enter_cache_size": "Wpisz rozmiar pamięci podręcznej",
|
||||
"enter_cache_ttl_min_override": "Wpisz minimalną wartość TTL",
|
||||
"enter_cache_ttl_max_override": "Wpisz maksymalną wartość TTL",
|
||||
"cache_ttl_min_override_desc": "Nadpisz wartość TTL (minimalną) otrzymaną od serwera nadrzędnego. Wartość nie może być większa niż 3600 (1 godzina)",
|
||||
"cache_ttl_max_override_desc": "Nadpisz wartość TTL (maksymalną) otrzymaną od serwera nadrzędnego",
|
||||
"min_exceeds_max_value": "Minimalna wartość przekracza maksymalną wartość",
|
||||
"value_not_larger_than": "Wartość nie może być większa niż {{maximum}}",
|
||||
"filter_category_general": "Ogólne",
|
||||
"filter_category_security": "Bezpieczeństwo",
|
||||
"filter_category_regional": "Regionalne",
|
||||
"filter_category_other": "Inne",
|
||||
"filter_category_general_desc": "Listy, które blokują skrypty śledzące i reklamy na większości urządzeń",
|
||||
"filter_category_security_desc": "Listy, które specjalizują się w blokowaniu domen ze złośliwym oprogramowaniem, phishingiem lub oszustwami",
|
||||
"filter_category_regional_desc": "Listy, które koncentrują się na reklamach regionalnych i serwerach ze skryptami śledzącymi",
|
||||
"filter_category_other_desc": "Inne listy zablokowanych",
|
||||
"original_response": "Oryginalna odpowiedź",
|
||||
"click_to_view_queries": "Kliknij, aby wyświetlić zapytania",
|
||||
"port_53_faq_link": "Port 53 jest często zajęty przez usługi \"DNSStubListener\" lub \"systemd-resolved\". Przeczytaj <0>tę instrukcję</0> jak to rozwiązać."
|
||||
}
|
||||
@@ -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,8 +529,8 @@
|
||||
"dnssec_enable": "Включить DNSSEC",
|
||||
"dnssec_enable_desc": "Установите флаг DNSSEC в исходящих DNS-запросах и проверьте результат (требуется резолвер с поддержкой DNSSEC)",
|
||||
"validated_with_dnssec": "Подтверждено с помощью DNSSEC",
|
||||
"show_all_responses": "Все ответы",
|
||||
"show_blocked_responses": "Blocked",
|
||||
"all_queries": "Все запросы",
|
||||
"show_blocked_responses": "Заблокировано",
|
||||
"show_whitelisted_responses": "В белом списке",
|
||||
"show_processed_responses": "Обработан",
|
||||
"blocked_safebrowsing": "Заблокировано согласно базе данных Safebrowsing",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Переписанные",
|
||||
"safe_search": "Безопасный поиск",
|
||||
"blocklist": "Черный список",
|
||||
"milliseconds_abbreviation": "мс"
|
||||
}
|
||||
"milliseconds_abbreviation": "мс",
|
||||
"cache_size": "Размер кеша",
|
||||
"cache_size_desc": "Размера кеша DNS (в байтах)",
|
||||
"cache_ttl_min_override": "Переопределить минимальный TTL",
|
||||
"cache_ttl_max_override": "Переопределить максимальный TTL",
|
||||
"enter_cache_size": "Введите размер кеша",
|
||||
"enter_cache_ttl_min_override": "Введите минимальный TTL",
|
||||
"enter_cache_ttl_max_override": "Введите максимальный TTL",
|
||||
"cache_ttl_min_override_desc": "Переопределить TTL-значение (минимальное), полученное с upstream-сервера. Это значение не может быть больше 3600 (1 часа)",
|
||||
"cache_ttl_max_override_desc": "Переопределить TTL-значение (максимальное), полученное с upstream-сервера",
|
||||
"min_exceeds_max_value": "Минимальное значение превышает максимальное значение",
|
||||
"value_not_larger_than": "Значение не может быть больше {{maximum}}",
|
||||
"filter_category_general": "Общие",
|
||||
"filter_category_security": "Безопасность",
|
||||
"filter_category_regional": "Региональные",
|
||||
"filter_category_other": "Другие",
|
||||
"filter_category_general_desc": "Списки, которые блокируют отслеживание и рекламу на большинстве устройств",
|
||||
"filter_category_security_desc": "Списки, которые специализируются на блокировке вредоносных программ, фишинговых или мошеннических доменов",
|
||||
"filter_category_regional_desc": "Списки, которые фокусируются на региональной рекламе и серверах отслеживания",
|
||||
"filter_category_other_desc": "Другие списки блокировки",
|
||||
"original_response": "Первоначальный ответ",
|
||||
"click_to_view_queries": "Нажмите, чтобы просмотреть запросы",
|
||||
"port_53_faq_link": "Порт 53 часто занят службами \"DNSStubListener\" или \"systemd-resolved\". Ознакомьтесь с <0>инструкцией</0> о том, как это разрешить."
|
||||
}
|
||||
@@ -162,6 +162,8 @@
|
||||
"new_allowlist": "Nový zoznam povolených DNS",
|
||||
"edit_blocklist": "Upraviť zoznam blokovaných DNS",
|
||||
"edit_allowlist": "Upraviť zoznam povolených DNS",
|
||||
"choose_blocklist": "Vybrať blokovací zoznam",
|
||||
"choose_allowlist": "Vybrať povolený zoznam",
|
||||
"enter_valid_blocklist": "Zadajte platnú URL adresu do zoznamu blokovaných DNS.",
|
||||
"enter_valid_allowlist": "Zadajte platnú URL adresu do zoznamu povolených DNS.",
|
||||
"form_error_url_format": "Neplatný URL formát",
|
||||
@@ -223,6 +225,8 @@
|
||||
"anonymize_client_ip": "Anonymizujte IP klienta",
|
||||
"anonymize_client_ip_desc": "Neukladať úplnú IP adresu klienta do protokolov a štatistík",
|
||||
"dns_config": "Konfigurácia DNS servera",
|
||||
"dns_cache_config": "Konfigurácia DNS cache",
|
||||
"dns_cache_config_desc": "Tu môžete nakonfigurovať DNS cache",
|
||||
"blocking_mode": "Spôsob blokovania",
|
||||
"default": "Predvolené",
|
||||
"nxdomain": "NXDOMAIN",
|
||||
@@ -445,6 +449,7 @@
|
||||
"domain": "Doména",
|
||||
"answer": "Odpoveď",
|
||||
"filter_added_successfully": "Filter bol úspešne pridaný",
|
||||
"filter_removed_successfully": "Zoznam bol úspešne odstránený",
|
||||
"filter_updated": "Filter bol úspešne aktualizovaný",
|
||||
"statistics_configuration": "Konfigurácia štatistiky",
|
||||
"statistics_retention": "Štatistika za obdobie",
|
||||
@@ -475,10 +480,15 @@
|
||||
"whois": "Whois",
|
||||
"filtering_rules_learn_more": "<0>Dozvedieť sa viac</0> o tvorbe vlastných zoznamov hostiteľov.",
|
||||
"blocked_by_response": "Blokované pomocou CNAME alebo IP v odpovedi",
|
||||
"blocked_by_cname_or_ip": "Zablokované na základe CNAME alebo IP",
|
||||
"try_again": "Skúste znova",
|
||||
"domain_desc": "Zadajte meno domény alebo zástupný znak, ktorý chcete prepísať.",
|
||||
"example_rewrite_domain": "prepísať odpovede iba pre toto meno domény.",
|
||||
"example_rewrite_wildcard": "prepísať odpovede pre všetky subdomény <0>example.org</0>.",
|
||||
"rewrite_ip_address": "IP adresa: použite túto IP v odpovedi A alebo AAAA",
|
||||
"rewrite_domain_name": "Meno domény: pridajte záznam CNAME",
|
||||
"rewrite_A": "<0>A</0>: špeciálna hodnota, uchovávajte záznamy <0>A</0> z upstream",
|
||||
"rewrite_AAAA": "<0>AAAA</0>: špeciálna hodnota, uchovávajte záznamy <0>AAAA</0> z upstream",
|
||||
"disable_ipv6": "Vypnúť IPv6",
|
||||
"disable_ipv6_desc": "Ak je táto funkcia zapnutá, všetky dotazy DNS na adresy IPv6 (typ AAAA) budú zrušené.",
|
||||
"fastest_addr": "Najrýchlejšia IP adresa",
|
||||
@@ -494,6 +504,8 @@
|
||||
"check": "Kontrola",
|
||||
"form_enter_host": "Zadajte meno hostiteľa",
|
||||
"filtered_custom_rules": "Filtrované podľa vlastných filtračných pravidiel",
|
||||
"choose_from_list": "Vybrať zo zoznamu",
|
||||
"add_custom_list": "Pridať vlastný zoznam",
|
||||
"host_whitelisted": "Hostiteľ je na bielej listine",
|
||||
"check_ip": "IP adresy: {{ip}}",
|
||||
"check_cname": "CNAME: {{cname}}",
|
||||
@@ -517,7 +529,7 @@
|
||||
"dnssec_enable": "Zapnúť DNSSEC",
|
||||
"dnssec_enable_desc": "Nastavte príznak DNSSEC v nasledujúcich DNS dopytoch a skontrolujte výsledok (je potrebný prekladač so zapnutým DNSSEC)",
|
||||
"validated_with_dnssec": "Overené pomocou DNSSEC",
|
||||
"show_all_responses": "Všetky odpovede",
|
||||
"all_queries": "Všetky dopyty",
|
||||
"show_blocked_responses": "Zablokované",
|
||||
"show_whitelisted_responses": "Obsiahnuté v bielej listine",
|
||||
"show_processed_responses": "Spracované",
|
||||
@@ -529,5 +541,27 @@
|
||||
"rewritten": "Prepísané",
|
||||
"safe_search": "Bezpečné vyhľadávanie",
|
||||
"blocklist": "Zoznam blokovaní",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veľkosť cache",
|
||||
"cache_size_desc": "Veľkosť DNS cache (v bajtoch)",
|
||||
"cache_ttl_min_override": "Prepísať minimálne TTL",
|
||||
"cache_ttl_max_override": "Prepísať maximálne TTL",
|
||||
"enter_cache_size": "Zadať veľkosť cache",
|
||||
"enter_cache_ttl_min_override": "Zadať minimálne TTL",
|
||||
"enter_cache_ttl_max_override": "Zadať maximálne TTL",
|
||||
"cache_ttl_min_override_desc": "Prepíše hodnotu TTL (minimálnu) prijatú z upstream servera. Táto hodnota nemôže byť väčšia ako 3600 (1 hodina)",
|
||||
"cache_ttl_max_override_desc": "Prepíše hodnotu TTL (maximálnu) prijatú z upstream servera",
|
||||
"min_exceeds_max_value": "Minimálna hodnota je väčšia ako maximálna",
|
||||
"value_not_larger_than": "Hodnota nemôže byť väčšia ako {{maximum}}",
|
||||
"filter_category_general": "Všeobecné",
|
||||
"filter_category_security": "Bezpečnosť",
|
||||
"filter_category_regional": "Regionálne",
|
||||
"filter_category_other": "Iné",
|
||||
"filter_category_general_desc": "Zoznamy, ktoré blokujú sledovanie a reklamu na väčšine zariadení",
|
||||
"filter_category_security_desc": "Zoznamy, ktoré sa špecializujú na blokovanie domén škodlivého softvéru alebo podvodov",
|
||||
"filter_category_regional_desc": "Zoznamy zamerané na regionálne reklamy a sledovacie servery",
|
||||
"filter_category_other_desc": "Iné blokovacie zoznamy",
|
||||
"original_response": "Pôvodná odozva",
|
||||
"click_to_view_queries": "Kliknite pre zobrazenie dopytov",
|
||||
"port_53_faq_link": "Port 53 je často obsadený službami \"DNSStubListener\" alebo \"systemd-resolved\". Prečítajte si <0>tento návod</0> o tom, ako to vyriešiť."
|
||||
}
|
||||
@@ -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,27 @@
|
||||
"rewritten": "Prepisano",
|
||||
"safe_search": "Sigurna pretraga",
|
||||
"blocklist": "Lista blokiranih",
|
||||
"milliseconds_abbreviation": "ms"
|
||||
"milliseconds_abbreviation": "ms",
|
||||
"cache_size": "Veličina predmemorije",
|
||||
"cache_size_desc": "Veličina DNS predmemorije (u bitovima)",
|
||||
"cache_ttl_min_override": "Prepiši najmanji TTL",
|
||||
"cache_ttl_max_override": "Prepiši najveći TTL",
|
||||
"enter_cache_size": "Unesite veličinu predmemorije",
|
||||
"enter_cache_ttl_min_override": "Unesite najmanji TTL",
|
||||
"enter_cache_ttl_max_override": "Unesite najveći TTL",
|
||||
"cache_ttl_min_override_desc": "Prepiši TTL vrednost (minimum) dobijen od apstrim servera. Ova vrednost ne može biti veća od 3600 (1 sat)",
|
||||
"cache_ttl_max_override_desc": "Prepiši TTL vrednost (maksimum) dobijen od apstrim servera",
|
||||
"min_exceeds_max_value": "Najmanja vrednost je dosegla najveću vrednost",
|
||||
"value_not_larger_than": "Vrednost ne može biti veća od {{maximum}}",
|
||||
"filter_category_general": "Opšte",
|
||||
"filter_category_security": "Bezbednost",
|
||||
"filter_category_regional": "Region",
|
||||
"filter_category_other": "Ostalo",
|
||||
"filter_category_general_desc": "Lista koja blokira praćenja i reklame na većini uređaja",
|
||||
"filter_category_security_desc": "Lista specijalizovana za blokiranje štetnog softvera, štetnih i fišing domena",
|
||||
"filter_category_regional_desc": "Lista koja se usredsređuje na regionalne reklame i servere praćenja",
|
||||
"filter_category_other_desc": "Ostale liste blokiranja",
|
||||
"original_response": "Izvorni odgovor",
|
||||
"click_to_view_queries": "Kliknite da pogledate zahteve",
|
||||
"port_53_faq_link": "Port 53 je najčešće zauzet od \"DNSStubListener\" ili \"systemd-resolved\" usluga. Pročitajte <0>ovo uputstvo</0> kako da to rešite."
|
||||
}
|
||||
@@ -239,7 +239,7 @@
|
||||
"rule_label": "Kural",
|
||||
"list_label": "Liste",
|
||||
"unknown_filter": "Bilinmeyen filtre {{filterId}}",
|
||||
"install_welcome_title": "AdGuard Home'a hoşgeldiniz!",
|
||||
"install_welcome_title": "AdGuard Home'a hoş geldiniz!",
|
||||
"install_welcome_desc": "AdGuard Home, ağ genelinde reklam ve izleyicileri engelleyen bir DNS sunucusudur. Tüm ağınızı ve tüm cihazlarınızı kontrol etmenize yarayan bir araçtır, istemci tarafında bir program kullanmanıza gerek duymaz.",
|
||||
"install_settings_title": "Yönetici Web Arayüzü",
|
||||
"install_settings_listen": "Dinleme arayüzü",
|
||||
@@ -505,5 +505,6 @@
|
||||
"blocked_adult_websites": "Yetişkin içerikli site engellendi",
|
||||
"blocked_threats": "Engellenen Tehditler",
|
||||
"allowed": "İzin verildi",
|
||||
"blocklist": "Engellenen listesi"
|
||||
"blocklist": "Engellenen listesi",
|
||||
"port_53_faq_link": "Port 53 genellikle \"DNSStubListener\" veya \"sistemd-resolved\" hizmetler tarafından kullanılır. Lütfen problemin nasıl çözüleceğine ilişkin <0>bu talimatı</0> okuyun."
|
||||
}
|
||||
@@ -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>。"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getIpMatchListStatus } from '../helpers/helpers';
|
||||
import { getIpMatchListStatus, sortIp } from '../helpers/helpers';
|
||||
import { IP_MATCH_LIST_STATUS } from '../helpers/constants';
|
||||
|
||||
describe('getIpMatchListStatus', () => {
|
||||
@@ -129,3 +129,356 @@ describe('getIpMatchListStatus', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortIp', () => {
|
||||
describe('ipv4', () => {
|
||||
test('one octet differ', () => {
|
||||
const arr = [
|
||||
'127.0.2.0',
|
||||
'127.0.3.0',
|
||||
'127.0.1.0',
|
||||
];
|
||||
const sortedArr = [
|
||||
'127.0.1.0',
|
||||
'127.0.2.0',
|
||||
'127.0.3.0',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('few octets differ', () => {
|
||||
const arr = [
|
||||
'192.168.11.10',
|
||||
'192.168.10.0',
|
||||
'192.168.11.11',
|
||||
'192.168.10.10',
|
||||
'192.168.1.10',
|
||||
'192.168.0.1',
|
||||
'192.168.1.0',
|
||||
'192.168.1.1',
|
||||
'192.168.11.0',
|
||||
'192.168.0.10',
|
||||
'192.168.10.11',
|
||||
'192.168.0.11',
|
||||
'192.168.1.11',
|
||||
'192.168.0.0',
|
||||
'192.168.10.1',
|
||||
'192.168.11.1',
|
||||
];
|
||||
const sortedArr = [
|
||||
'192.168.0.0',
|
||||
'192.168.0.1',
|
||||
'192.168.0.10',
|
||||
'192.168.0.11',
|
||||
'192.168.1.0',
|
||||
'192.168.1.1',
|
||||
'192.168.1.10',
|
||||
'192.168.1.11',
|
||||
'192.168.10.0',
|
||||
'192.168.10.1',
|
||||
'192.168.10.10',
|
||||
'192.168.10.11',
|
||||
'192.168.11.0',
|
||||
'192.168.11.1',
|
||||
'192.168.11.10',
|
||||
'192.168.11.11',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
|
||||
// Example from issue https://github.com/AdguardTeam/AdGuardHome/issues/1778#issuecomment-640937599
|
||||
const arr2 = [
|
||||
'192.168.2.11',
|
||||
'192.168.3.1',
|
||||
'192.168.2.100',
|
||||
'192.168.2.2',
|
||||
'192.168.2.1',
|
||||
'192.168.2.10',
|
||||
'192.168.2.99',
|
||||
'192.168.2.200',
|
||||
'192.168.2.199',
|
||||
];
|
||||
const sortedArr2 = [
|
||||
'192.168.2.1',
|
||||
'192.168.2.2',
|
||||
'192.168.2.10',
|
||||
'192.168.2.11',
|
||||
'192.168.2.99',
|
||||
'192.168.2.100',
|
||||
'192.168.2.199',
|
||||
'192.168.2.200',
|
||||
'192.168.3.1',
|
||||
];
|
||||
expect(arr2.sort(sortIp)).toStrictEqual(sortedArr2);
|
||||
});
|
||||
});
|
||||
describe('ipv6', () => {
|
||||
test('only long form', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'2001:db8:11a3:9d7:0:0:0:3',
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
];
|
||||
const sortedArr = [
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'2001:db8:11a3:9d7:0:0:0:3',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('only short form', () => {
|
||||
const arr = [
|
||||
'2001:db8::',
|
||||
'2001:db7::',
|
||||
'2001:db9::',
|
||||
];
|
||||
const sortedArr = [
|
||||
'2001:db7::',
|
||||
'2001:db8::',
|
||||
'2001:db9::',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('long and short forms', () => {
|
||||
const arr = [
|
||||
'2001:db8::',
|
||||
'2001:db7:11a3:9d7:0:0:0:2',
|
||||
'2001:db6:11a3:9d7:0:0:0:1',
|
||||
'2001:db6::',
|
||||
'2001:db7:11a3:9d7:0:0:0:1',
|
||||
'2001:db7::',
|
||||
];
|
||||
const sortedArr = [
|
||||
'2001:db6::',
|
||||
'2001:db6:11a3:9d7:0:0:0:1',
|
||||
'2001:db7::',
|
||||
'2001:db7:11a3:9d7:0:0:0:1',
|
||||
'2001:db7:11a3:9d7:0:0:0:2',
|
||||
'2001:db8::',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
});
|
||||
describe('ipv4 and ipv6', () => {
|
||||
test('ipv6 long form', () => {
|
||||
const arr = [
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'2001:db8:11a3:9d7:0:0:0:3',
|
||||
'127.0.0.1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'127.0.0.2',
|
||||
];
|
||||
const sortedArr = [
|
||||
'127.0.0.1',
|
||||
'127.0.0.2',
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'2001:db8:11a3:9d7:0:0:0:3',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('ipv6 short form', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7::3',
|
||||
'127.0.0.1',
|
||||
'2001:db8:11a3:9d7::2',
|
||||
'127.0.0.2',
|
||||
];
|
||||
const sortedArr = [
|
||||
'127.0.0.1',
|
||||
'127.0.0.2',
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'2001:db8:11a3:9d7::2',
|
||||
'2001:db8:11a3:9d7::3',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('ipv6 long and short forms', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'127.0.0.1',
|
||||
'2001:db8:11a3:9d7::3',
|
||||
'127.0.0.2',
|
||||
];
|
||||
const sortedArr = [
|
||||
'127.0.0.1',
|
||||
'127.0.0.2',
|
||||
'127.0.0.3',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'2001:db8:11a3:9d7::3',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('always put ipv4 before ipv6', () => {
|
||||
const arr = [
|
||||
'::1',
|
||||
'0.0.0.2',
|
||||
'127.0.0.1',
|
||||
'::2',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'0.0.0.1',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
];
|
||||
const sortedArr = [
|
||||
'0.0.0.1',
|
||||
'0.0.0.2',
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
'::2',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
});
|
||||
describe('cidr', () => {
|
||||
test('only ipv4 cidr', () => {
|
||||
const arr = [
|
||||
'192.168.0.1/9',
|
||||
'192.168.0.1/7',
|
||||
'192.168.0.1/8',
|
||||
];
|
||||
const sortedArr = [
|
||||
'192.168.0.1/7',
|
||||
'192.168.0.1/8',
|
||||
'192.168.0.1/9',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('ipv4 and cidr ipv4', () => {
|
||||
const arr = [
|
||||
'192.168.0.1/9',
|
||||
'192.168.0.1',
|
||||
'192.168.0.1/32',
|
||||
'192.168.0.1/7',
|
||||
'192.168.0.1/8',
|
||||
];
|
||||
const sortedArr = [
|
||||
'192.168.0.1/7',
|
||||
'192.168.0.1/8',
|
||||
'192.168.0.1/9',
|
||||
'192.168.0.1/32',
|
||||
'192.168.0.1',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('only ipv6 cidr', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7::1/32',
|
||||
'2001:db8:11a3:9d7::1/64',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
];
|
||||
const sortedArr = [
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
'2001:db8:11a3:9d7::1/32',
|
||||
'2001:db8:11a3:9d7::1/64',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
test('ipv6 and cidr ipv6', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7::1/32',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
'2001:db8:11a3:9d7::1/64',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
];
|
||||
const sortedArr = [
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
'2001:db8:11a3:9d7::1/32',
|
||||
'2001:db8:11a3:9d7::1/64',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
'2001:db8:11a3:9d7::1',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
});
|
||||
describe('invalid input', () => {
|
||||
const originalError = console.error;
|
||||
|
||||
beforeEach(() => {
|
||||
console.error = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
expect(console.error).toHaveBeenCalled();
|
||||
console.error = originalError;
|
||||
});
|
||||
|
||||
test('invalid strings', () => {
|
||||
const arr = ['invalid ip', 'invalid cidr'];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(arr);
|
||||
});
|
||||
test('invalid ip', () => {
|
||||
const arr = ['127.0.0.2.', '.127.0.0.1.', '.2001:db8:11a3:9d7:0:0:0:0'];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(arr);
|
||||
});
|
||||
test('invalid cidr', () => {
|
||||
const arr = ['127.0.0.2/33', '2001:db8:11a3:9d7:0:0:0:0/129'];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(arr);
|
||||
});
|
||||
test('valid and invalid ip', () => {
|
||||
const arr = ['127.0.0.4.', '127.0.0.1', '.127.0.0.3', '127.0.0.2'];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(arr);
|
||||
});
|
||||
});
|
||||
describe('mixed', () => {
|
||||
test('ipv4, ipv6 in short and long forms and cidr', () => {
|
||||
const arr = [
|
||||
'2001:db8:11a3:9d7:0:0:0:1/32',
|
||||
'192.168.1.2',
|
||||
'127.0.0.2',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'127.0.0.1/12',
|
||||
'192.168.1.1',
|
||||
'2001:db8::/32',
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
'192.168.1.2/12',
|
||||
'2001:db7::/32',
|
||||
'127.0.0.1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
'192.168.1.1/24',
|
||||
'2001:db7::/64',
|
||||
'2001:db7::',
|
||||
'2001:db8::',
|
||||
'2001:db8:11a3:9d7:0:0:0:1/128',
|
||||
'192.168.1.1/12',
|
||||
'127.0.0.1/32',
|
||||
'::1',
|
||||
];
|
||||
const sortedArr = [
|
||||
'127.0.0.1/12',
|
||||
'127.0.0.1/32',
|
||||
'127.0.0.1',
|
||||
'127.0.0.2',
|
||||
'192.168.1.1/12',
|
||||
'192.168.1.1/24',
|
||||
'192.168.1.1',
|
||||
'192.168.1.2/12',
|
||||
'192.168.1.2',
|
||||
'::1',
|
||||
'2001:db7::/32',
|
||||
'2001:db7::/64',
|
||||
'2001:db7::',
|
||||
'2001:db8::/32',
|
||||
'2001:db8::',
|
||||
'2001:db8:11a3:9d7::1/24',
|
||||
'2001:db8:11a3:9d7:0:0:0:1/32',
|
||||
'2001:db8:11a3:9d7::1/128',
|
||||
'2001:db8:11a3:9d7:0:0:0:1/128',
|
||||
'2001:db8:11a3:9d7:0:0:0:1',
|
||||
'2001:db8:11a3:9d7:0:0:0:2',
|
||||
];
|
||||
expect(arr.sort(sortIp)).toStrictEqual(sortedArr);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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'));
|
||||
@@ -147,7 +148,7 @@ const checkStatus = async (handleRequestSuccess, handleRequestError, attempts =
|
||||
const rmTimeout = (t) => t && clearTimeout(t);
|
||||
|
||||
try {
|
||||
const response = await axios.get('control/status');
|
||||
const response = await axios.get(`${apiClient.baseUrl}/status`);
|
||||
rmTimeout(timeout);
|
||||
if (response?.status === 200) {
|
||||
handleRequestSuccess(response);
|
||||
@@ -279,8 +280,8 @@ export const testUpstream = (config) => async (dispatch) => {
|
||||
dispatch(testUpstreamRequest());
|
||||
try {
|
||||
const values = { ...config };
|
||||
values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns);
|
||||
values.upstream_dns = normalizeTextarea(values.upstream_dns);
|
||||
values.bootstrap_dns = splitByNewLine(values.bootstrap_dns);
|
||||
values.upstream_dns = splitByNewLine(values.upstream_dns);
|
||||
|
||||
const upstreamResponse = await apiClient.testUpstream(values);
|
||||
const testMessages = Object.keys(upstreamResponse)
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -2,9 +2,10 @@ import axios from 'axios';
|
||||
|
||||
import { getPathWithQueryString } from '../helpers/helpers';
|
||||
import { R_PATH_LAST_PART } from '../helpers/constants';
|
||||
import { BASE_URL } from '../../constants';
|
||||
|
||||
class Api {
|
||||
baseUrl = 'control';
|
||||
baseUrl = BASE_URL;
|
||||
|
||||
async makeRequest(path, method = 'POST', config) {
|
||||
try {
|
||||
@@ -26,18 +27,30 @@ class Api {
|
||||
|
||||
throw new Error(`${errorPath} | ${error.response.data} | ${error.response.status}`);
|
||||
}
|
||||
throw new Error(`${errorPath} | ${error.message ? error.message : error}`);
|
||||
throw new Error(`${errorPath} | ${error.message || error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Global methods
|
||||
GLOBAL_STATUS = { path: 'status', method: 'GET' };
|
||||
GLOBAL_STATUS = {
|
||||
path: 'status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
||||
GLOBAL_TEST_UPSTREAM_DNS = {
|
||||
path: 'test_upstream_dns',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
||||
GLOBAL_VERSION = {
|
||||
path: 'version.json',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
||||
GLOBAL_UPDATE = {
|
||||
path: 'update',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getGlobalStatus() {
|
||||
const { path, method } = this.GLOBAL_STATUS;
|
||||
@@ -68,21 +81,45 @@ class Api {
|
||||
}
|
||||
|
||||
// Filtering
|
||||
FILTERING_STATUS = { path: 'filtering/status', method: 'GET' };
|
||||
FILTERING_STATUS = {
|
||||
path: 'filtering/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' };
|
||||
FILTERING_ADD_FILTER = {
|
||||
path: 'filtering/add_url',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' };
|
||||
FILTERING_REMOVE_FILTER = {
|
||||
path: 'filtering/remove_url',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' };
|
||||
FILTERING_SET_RULES = {
|
||||
path: 'filtering/set_rules',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' };
|
||||
FILTERING_REFRESH = {
|
||||
path: 'filtering/refresh',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' };
|
||||
FILTERING_SET_URL = {
|
||||
path: 'filtering/set_url',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' };
|
||||
FILTERING_CONFIG = {
|
||||
path: 'filtering/config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
FILTERING_CHECK_HOST = { path: 'filtering/check_host', method: 'GET' };
|
||||
FILTERING_CHECK_HOST = {
|
||||
path: 'filtering/check_host',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
getFilteringStatus() {
|
||||
const { path, method } = this.FILTERING_STATUS;
|
||||
@@ -153,11 +190,20 @@ class Api {
|
||||
}
|
||||
|
||||
// Parental
|
||||
PARENTAL_STATUS = { path: 'parental/status', method: 'GET' };
|
||||
PARENTAL_STATUS = {
|
||||
path: 'parental/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
PARENTAL_ENABLE = { path: 'parental/enable', method: 'POST' };
|
||||
PARENTAL_ENABLE = {
|
||||
path: 'parental/enable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
PARENTAL_DISABLE = { path: 'parental/disable', method: 'POST' };
|
||||
PARENTAL_DISABLE = {
|
||||
path: 'parental/disable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getParentalStatus() {
|
||||
const { path, method } = this.PARENTAL_STATUS;
|
||||
@@ -180,11 +226,20 @@ class Api {
|
||||
}
|
||||
|
||||
// Safebrowsing
|
||||
SAFEBROWSING_STATUS = { path: 'safebrowsing/status', method: 'GET' };
|
||||
SAFEBROWSING_STATUS = {
|
||||
path: 'safebrowsing/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
SAFEBROWSING_ENABLE = { path: 'safebrowsing/enable', method: 'POST' };
|
||||
SAFEBROWSING_ENABLE = {
|
||||
path: 'safebrowsing/enable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
SAFEBROWSING_DISABLE = { path: 'safebrowsing/disable', method: 'POST' };
|
||||
SAFEBROWSING_DISABLE = {
|
||||
path: 'safebrowsing/disable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getSafebrowsingStatus() {
|
||||
const { path, method } = this.SAFEBROWSING_STATUS;
|
||||
@@ -202,11 +257,20 @@ class Api {
|
||||
}
|
||||
|
||||
// Safesearch
|
||||
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
|
||||
SAFESEARCH_STATUS = {
|
||||
path: 'safesearch/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
SAFESEARCH_ENABLE = { path: 'safesearch/enable', method: 'POST' };
|
||||
SAFESEARCH_ENABLE = {
|
||||
path: 'safesearch/enable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
SAFESEARCH_DISABLE = { path: 'safesearch/disable', method: 'POST' };
|
||||
SAFESEARCH_DISABLE = {
|
||||
path: 'safesearch/disable',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getSafesearchStatus() {
|
||||
const { path, method } = this.SAFESEARCH_STATUS;
|
||||
@@ -224,9 +288,15 @@ class Api {
|
||||
}
|
||||
|
||||
// Language
|
||||
CURRENT_LANGUAGE = { path: 'i18n/current_language', method: 'GET' };
|
||||
CURRENT_LANGUAGE = {
|
||||
path: 'i18n/current_language',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
CHANGE_LANGUAGE = { path: 'i18n/change_language', method: 'POST' };
|
||||
CHANGE_LANGUAGE = {
|
||||
path: 'i18n/change_language',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getCurrentLanguage() {
|
||||
const { path, method } = this.CURRENT_LANGUAGE;
|
||||
@@ -243,19 +313,40 @@ class Api {
|
||||
}
|
||||
|
||||
// DHCP
|
||||
DHCP_STATUS = { path: 'dhcp/status', method: 'GET' };
|
||||
DHCP_STATUS = {
|
||||
path: 'dhcp/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' };
|
||||
DHCP_SET_CONFIG = {
|
||||
path: 'dhcp/set_config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'POST' };
|
||||
DHCP_FIND_ACTIVE = {
|
||||
path: 'dhcp/find_active_dhcp',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
DHCP_INTERFACES = { path: 'dhcp/interfaces', method: 'GET' };
|
||||
DHCP_INTERFACES = {
|
||||
path: 'dhcp/interfaces',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
DHCP_ADD_STATIC_LEASE = { path: 'dhcp/add_static_lease', method: 'POST' };
|
||||
DHCP_ADD_STATIC_LEASE = {
|
||||
path: 'dhcp/add_static_lease',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' };
|
||||
DHCP_REMOVE_STATIC_LEASE = {
|
||||
path: 'dhcp/remove_static_lease',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
|
||||
DHCP_RESET = {
|
||||
path: 'dhcp/reset',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getDhcpStatus() {
|
||||
const { path, method } = this.DHCP_STATUS;
|
||||
@@ -309,11 +400,20 @@ class Api {
|
||||
}
|
||||
|
||||
// Installation
|
||||
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
|
||||
INSTALL_GET_ADDRESSES = {
|
||||
path: 'install/get_addresses',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' };
|
||||
INSTALL_CONFIGURE = {
|
||||
path: 'install/configure',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
INSTALL_CHECK_CONFIG = { path: 'install/check_config', method: 'POST' };
|
||||
INSTALL_CHECK_CONFIG = {
|
||||
path: 'install/check_config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getDefaultAddresses() {
|
||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||
@@ -339,11 +439,20 @@ class Api {
|
||||
}
|
||||
|
||||
// DNS-over-HTTPS and DNS-over-TLS
|
||||
TLS_STATUS = { path: 'tls/status', method: 'GET' };
|
||||
TLS_STATUS = {
|
||||
path: 'tls/status',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
TLS_CONFIG = { path: 'tls/configure', method: 'POST' };
|
||||
TLS_CONFIG = {
|
||||
path: 'tls/configure',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
TLS_VALIDATE = { path: 'tls/validate', method: 'POST' };
|
||||
TLS_VALIDATE = {
|
||||
path: 'tls/validate',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getTlsStatus() {
|
||||
const { path, method } = this.TLS_STATUS;
|
||||
@@ -369,15 +478,30 @@ class Api {
|
||||
}
|
||||
|
||||
// Per-client settings
|
||||
GET_CLIENTS = { path: 'clients', method: 'GET' };
|
||||
GET_CLIENTS = {
|
||||
path: 'clients',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
FIND_CLIENTS = { path: 'clients/find', method: 'GET' };
|
||||
FIND_CLIENTS = {
|
||||
path: 'clients/find',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
|
||||
ADD_CLIENT = {
|
||||
path: 'clients/add',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
DELETE_CLIENT = { path: 'clients/delete', method: 'POST' };
|
||||
DELETE_CLIENT = {
|
||||
path: 'clients/delete',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
UPDATE_CLIENT = { path: 'clients/update', method: 'POST' };
|
||||
UPDATE_CLIENT = {
|
||||
path: 'clients/update',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getClients() {
|
||||
const { path, method } = this.GET_CLIENTS;
|
||||
@@ -418,9 +542,15 @@ class Api {
|
||||
}
|
||||
|
||||
// DNS access settings
|
||||
ACCESS_LIST = { path: 'access/list', method: 'GET' };
|
||||
ACCESS_LIST = {
|
||||
path: 'access/list',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
ACCESS_SET = { path: 'access/set', method: 'POST' };
|
||||
ACCESS_SET = {
|
||||
path: 'access/set',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getAccessList() {
|
||||
const { path, method } = this.ACCESS_LIST;
|
||||
@@ -437,11 +567,20 @@ class Api {
|
||||
}
|
||||
|
||||
// DNS rewrites
|
||||
REWRITES_LIST = { path: 'rewrite/list', method: 'GET' };
|
||||
REWRITES_LIST = {
|
||||
path: 'rewrite/list',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
|
||||
REWRITE_ADD = {
|
||||
path: 'rewrite/add',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
|
||||
REWRITE_DELETE = {
|
||||
path: 'rewrite/delete',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getRewritesList() {
|
||||
const { path, method } = this.REWRITES_LIST;
|
||||
@@ -467,9 +606,15 @@ class Api {
|
||||
}
|
||||
|
||||
// Blocked services
|
||||
BLOCKED_SERVICES_LIST = { path: 'blocked_services/list', method: 'GET' };
|
||||
BLOCKED_SERVICES_LIST = {
|
||||
path: 'blocked_services/list',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
BLOCKED_SERVICES_SET = { path: 'blocked_services/set', method: 'POST' };
|
||||
BLOCKED_SERVICES_SET = {
|
||||
path: 'blocked_services/set',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getBlockedServices() {
|
||||
const { path, method } = this.BLOCKED_SERVICES_LIST;
|
||||
@@ -486,13 +631,25 @@ class Api {
|
||||
}
|
||||
|
||||
// Settings for statistics
|
||||
GET_STATS = { path: 'stats', method: 'GET' };
|
||||
GET_STATS = {
|
||||
path: 'stats',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
STATS_INFO = { path: 'stats_info', method: 'GET' };
|
||||
STATS_INFO = {
|
||||
path: 'stats_info',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
STATS_CONFIG = { path: 'stats_config', method: 'POST' };
|
||||
STATS_CONFIG = {
|
||||
path: 'stats_config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
STATS_RESET = { path: 'stats_reset', method: 'POST' };
|
||||
STATS_RESET = {
|
||||
path: 'stats_reset',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getStats() {
|
||||
const { path, method } = this.GET_STATS;
|
||||
@@ -519,13 +676,25 @@ class Api {
|
||||
}
|
||||
|
||||
// Query log
|
||||
GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
||||
GET_QUERY_LOG = {
|
||||
path: 'querylog',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
QUERY_LOG_CONFIG = { path: 'querylog_config', method: 'POST' };
|
||||
QUERY_LOG_CONFIG = {
|
||||
path: 'querylog_config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
QUERY_LOG_INFO = { path: 'querylog_info', method: 'GET' };
|
||||
QUERY_LOG_INFO = {
|
||||
path: 'querylog_info',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
|
||||
QUERY_LOG_CLEAR = {
|
||||
path: 'querylog_clear',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getQueryLog(params) {
|
||||
const { path, method } = this.GET_QUERY_LOG;
|
||||
@@ -553,7 +722,10 @@ class Api {
|
||||
}
|
||||
|
||||
// Login
|
||||
LOGIN = { path: 'login', method: 'POST' };
|
||||
LOGIN = {
|
||||
path: 'login',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
login(data) {
|
||||
const { path, method } = this.LOGIN;
|
||||
@@ -565,7 +737,10 @@ class Api {
|
||||
}
|
||||
|
||||
// Profile
|
||||
GET_PROFILE = { path: 'profile', method: 'GET' };
|
||||
GET_PROFILE = {
|
||||
path: 'profile',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
getProfile() {
|
||||
const { path, method } = this.GET_PROFILE;
|
||||
@@ -573,9 +748,15 @@ class Api {
|
||||
}
|
||||
|
||||
// DNS config
|
||||
GET_DNS_CONFIG = { path: 'dns_info', method: 'GET' };
|
||||
GET_DNS_CONFIG = {
|
||||
path: 'dns_info',
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
SET_DNS_CONFIG = { path: 'dns_config', method: 'POST' };
|
||||
SET_DNS_CONFIG = {
|
||||
path: 'dns_config',
|
||||
method: 'POST',
|
||||
};
|
||||
|
||||
getDnsConfig() {
|
||||
const { path, method } = this.GET_DNS_CONFIG;
|
||||
|
||||
@@ -28,10 +28,6 @@ body {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.container--wrap {
|
||||
min-height: calc(100vh);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { HashRouter, Route } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import LoadingBar from 'react-redux-loading-bar';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
|
||||
import 'react-table/react-table.css';
|
||||
import '../ui/Tabler.css';
|
||||
import '../ui/ReactTable.css';
|
||||
import './index.css';
|
||||
|
||||
import Header from '../../containers/Header';
|
||||
import Dashboard from '../../containers/Dashboard';
|
||||
import Settings from '../../containers/Settings';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import CustomRules from '../../containers/CustomRules';
|
||||
import DnsBlocklist from '../../containers/DnsBlocklist';
|
||||
import DnsAllowlist from '../../containers/DnsAllowlist';
|
||||
import DnsRewrites from '../../containers/DnsRewrites';
|
||||
|
||||
import Dns from '../../containers/Dns';
|
||||
import Encryption from '../../containers/Encryption';
|
||||
import Dhcp from '../../containers/Dhcp';
|
||||
import Clients from '../../containers/Clients';
|
||||
|
||||
import Logs from '../../containers/Logs';
|
||||
import SetupGuide from '../../containers/SetupGuide';
|
||||
import propTypes from 'prop-types';
|
||||
import Toasts from '../Toasts';
|
||||
import Footer from '../ui/Footer';
|
||||
import Status from '../ui/Status';
|
||||
@@ -35,31 +21,107 @@ import Icons from '../ui/Icons';
|
||||
import i18n from '../../i18n';
|
||||
import Loading from '../ui/Loading';
|
||||
import { FILTERS_URLS, MENU_URLS, SETTINGS_URLS } from '../../helpers/constants';
|
||||
import { getLogsUrlParams, setHtmlLangAttr } from '../../helpers/helpers';
|
||||
import Header from '../Header';
|
||||
import { changeLanguage, getDnsStatus } from '../../actions';
|
||||
|
||||
import Dashboard from '../../containers/Dashboard';
|
||||
import Logs from '../../containers/Logs';
|
||||
import SetupGuide from '../../containers/SetupGuide';
|
||||
import Settings from '../../containers/Settings';
|
||||
import Dns from '../../containers/Dns';
|
||||
import Encryption from '../../containers/Encryption';
|
||||
import Dhcp from '../../containers/Dhcp';
|
||||
import Clients from '../../containers/Clients';
|
||||
import DnsBlocklist from '../../containers/DnsBlocklist';
|
||||
import DnsAllowlist from '../../containers/DnsAllowlist';
|
||||
import DnsRewrites from '../../containers/DnsRewrites';
|
||||
import CustomRules from '../../containers/CustomRules';
|
||||
import Services from '../Filters/Services';
|
||||
import { setHtmlLangAttr } from '../../helpers/helpers';
|
||||
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
this.props.getDnsStatus();
|
||||
}
|
||||
const ROUTES = [
|
||||
{
|
||||
path: MENU_URLS.root,
|
||||
component: Dashboard,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: [`${MENU_URLS.logs}${getLogsUrlParams(':search?', ':response_status?')}`, MENU_URLS.logs],
|
||||
component: Logs,
|
||||
},
|
||||
{
|
||||
path: MENU_URLS.guide,
|
||||
component: SetupGuide,
|
||||
},
|
||||
{
|
||||
path: SETTINGS_URLS.settings,
|
||||
component: Settings,
|
||||
},
|
||||
{
|
||||
path: SETTINGS_URLS.dns,
|
||||
component: Dns,
|
||||
},
|
||||
{
|
||||
path: SETTINGS_URLS.encryption,
|
||||
component: Encryption,
|
||||
},
|
||||
{
|
||||
path: SETTINGS_URLS.dhcp,
|
||||
component: Dhcp,
|
||||
},
|
||||
{
|
||||
path: SETTINGS_URLS.clients,
|
||||
component: Clients,
|
||||
},
|
||||
{
|
||||
path: FILTERS_URLS.dns_blocklists,
|
||||
component: DnsBlocklist,
|
||||
},
|
||||
{
|
||||
path: FILTERS_URLS.dns_allowlists,
|
||||
component: DnsAllowlist,
|
||||
},
|
||||
{
|
||||
path: FILTERS_URLS.dns_rewrites,
|
||||
component: DnsRewrites,
|
||||
},
|
||||
{
|
||||
path: FILTERS_URLS.custom_rules,
|
||||
component: CustomRules,
|
||||
},
|
||||
{
|
||||
path: FILTERS_URLS.blocked_services,
|
||||
component: Services,
|
||||
},
|
||||
];
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.dashboard.language !== prevProps.dashboard.language) {
|
||||
this.setLanguage();
|
||||
}
|
||||
}
|
||||
const renderRoute = ({ path, component, exact }, idx) => <Route
|
||||
key={idx}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>;
|
||||
|
||||
reloadPage = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
const App = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
language,
|
||||
isCoreRunning,
|
||||
isUpdateAvailable,
|
||||
processing,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
|
||||
handleUpdate = () => {
|
||||
this.props.getUpdate();
|
||||
};
|
||||
const { processing: processingEncryption } = useSelector((
|
||||
state,
|
||||
) => state.encryption, shallowEqual);
|
||||
|
||||
setLanguage = () => {
|
||||
const { processing, language } = this.props.dashboard;
|
||||
const updateAvailable = isCoreRunning && isUpdateAvailable;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getDnsStatus());
|
||||
}, []);
|
||||
|
||||
const setLanguage = () => {
|
||||
if (!processing) {
|
||||
if (language) {
|
||||
i18n.changeLanguage(language);
|
||||
@@ -68,91 +130,52 @@ class App extends Component {
|
||||
}
|
||||
|
||||
i18n.on('languageChanged', (lang) => {
|
||||
this.props.changeLanguage(lang);
|
||||
dispatch(changeLanguage(lang));
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { dashboard, encryption, getVersion } = this.props;
|
||||
const updateAvailable = dashboard.isCoreRunning && dashboard.isUpdateAvailable;
|
||||
useEffect(() => {
|
||||
setLanguage();
|
||||
}, [language]);
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
<Fragment>
|
||||
{updateAvailable && (
|
||||
<Fragment>
|
||||
<UpdateTopline
|
||||
url={dashboard.announcementUrl}
|
||||
version={dashboard.newVersion}
|
||||
canAutoUpdate={dashboard.canAutoUpdate}
|
||||
getUpdate={this.handleUpdate}
|
||||
processingUpdate={dashboard.processingUpdate}
|
||||
/>
|
||||
<UpdateOverlay processingUpdate={dashboard.processingUpdate} />
|
||||
</Fragment>
|
||||
)}
|
||||
{!encryption.processing && (
|
||||
<EncryptionTopline notAfter={encryption.not_after} />
|
||||
)}
|
||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||
<Route component={Header} />
|
||||
<div className="container container--wrap pb-5">
|
||||
{dashboard.processing && <Loading />}
|
||||
{!dashboard.isCoreRunning && (
|
||||
<div className="row row-cards">
|
||||
<div className="col-lg-12">
|
||||
<Status reloadPage={this.reloadPage}
|
||||
message="dns_start"
|
||||
/>
|
||||
<Loading />
|
||||
</div>
|
||||
const reloadPage = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<HashRouter hashType="noslash">
|
||||
<>
|
||||
{updateAvailable && <>
|
||||
<UpdateTopline />
|
||||
<UpdateOverlay />
|
||||
</>}
|
||||
{!processingEncryption && <EncryptionTopline />}
|
||||
<LoadingBar className="loading-bar" updateTime={1000} />
|
||||
<Header />
|
||||
<div className="container container--wrap pb-5">
|
||||
{processing && <Loading />}
|
||||
{!isCoreRunning && (
|
||||
<div className="row row-cards">
|
||||
<div className="col-lg-12">
|
||||
<Status reloadPage={reloadPage} message="dns_start" />
|
||||
<Loading />
|
||||
</div>
|
||||
)}
|
||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
||||
<>
|
||||
<Route path={MENU_URLS.root} exact component={Dashboard} />
|
||||
<Route path={MENU_URLS.logs} component={Logs} />
|
||||
<Route path={MENU_URLS.guide} component={SetupGuide} />
|
||||
<Route path={SETTINGS_URLS.settings} component={Settings} />
|
||||
<Route path={SETTINGS_URLS.dns} component={Dns} />
|
||||
<Route path={SETTINGS_URLS.encryption} component={Encryption} />
|
||||
<Route path={SETTINGS_URLS.dhcp} component={Dhcp} />
|
||||
<Route path={SETTINGS_URLS.clients} component={Clients} />
|
||||
<Route path={FILTERS_URLS.dns_blocklists}
|
||||
component={DnsBlocklist} />
|
||||
<Route path={FILTERS_URLS.dns_allowlists}
|
||||
component={DnsAllowlist} />
|
||||
<Route path={FILTERS_URLS.dns_rewrites} component={DnsRewrites} />
|
||||
<Route path={FILTERS_URLS.custom_rules} component={CustomRules} />
|
||||
<Route path={FILTERS_URLS.blocked_services} component={Services} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Footer
|
||||
dnsVersion={dashboard.dnsVersion}
|
||||
dnsPort={dashboard.dnsPort}
|
||||
processingVersion={dashboard.processingVersion}
|
||||
getVersion={getVersion}
|
||||
checkUpdateFlag={dashboard.checkUpdateFlag}
|
||||
/>
|
||||
<Toasts />
|
||||
<Icons />
|
||||
</Fragment>
|
||||
</HashRouter>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
getDnsStatus: PropTypes.func,
|
||||
getUpdate: PropTypes.func,
|
||||
enableDns: PropTypes.func,
|
||||
dashboard: PropTypes.object,
|
||||
isCoreRunning: PropTypes.bool,
|
||||
error: PropTypes.string,
|
||||
changeLanguage: PropTypes.func,
|
||||
encryption: PropTypes.object,
|
||||
getVersion: PropTypes.func,
|
||||
</div>
|
||||
)}
|
||||
{!processing && isCoreRunning && ROUTES.map(renderRoute)}
|
||||
</div>
|
||||
<Footer />
|
||||
<Toasts />
|
||||
<Icons />
|
||||
</>
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation()(App);
|
||||
renderRoute.propTypes = {
|
||||
path: propTypes.oneOfType([propTypes.string, propTypes.arrayOf(propTypes.string)]).isRequired,
|
||||
component: propTypes.element.isRequired,
|
||||
exact: propTypes.bool,
|
||||
};
|
||||
|
||||
export default hot(App);
|
||||
|
||||
@@ -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 = ({
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Trans, withTranslation } from 'react-i18next';
|
||||
import Card from '../ui/Card';
|
||||
import Cell from '../ui/Cell';
|
||||
|
||||
import { getPercent, getIpMatchListStatus } from '../../helpers/helpers';
|
||||
import { getPercent, getIpMatchListStatus, sortIp } from '../../helpers/helpers';
|
||||
import { IP_MATCH_LIST_STATUS, STATUS_COLORS } from '../../helpers/constants';
|
||||
import { formatClientCell } from '../../helpers/formatClientCell';
|
||||
|
||||
@@ -25,7 +25,7 @@ const countCell = (dnsQueries) => function cell(row) {
|
||||
const percent = getPercent(dnsQueries, value);
|
||||
const percentColor = getClientsPercentColor(percent);
|
||||
|
||||
return <Cell value={value} percent={percent} color={percentColor} />;
|
||||
return <Cell value={value} percent={percent} color={percentColor} search={row.original.ip} />;
|
||||
};
|
||||
|
||||
const renderBlockingButton = (ipMatchListStatus, ip, handleClick, processing) => {
|
||||
@@ -62,7 +62,7 @@ const clientCell = (t, toggleClientStatus, processing, disallowedClients) => fun
|
||||
return (
|
||||
<>
|
||||
<div className="logs__row logs__row--overflow logs__row--column">
|
||||
{formatClientCell(row, t)}
|
||||
{formatClientCell(row, true, false)}
|
||||
</div>
|
||||
{ipMatchListStatus !== IP_MATCH_LIST_STATUS.CIDR
|
||||
&& renderBlockingButton(ipMatchListStatus, value, toggleClientStatus, processing)}
|
||||
@@ -99,7 +99,7 @@ const Clients = ({
|
||||
{
|
||||
Header: 'IP',
|
||||
accessor: 'ip',
|
||||
sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10),
|
||||
sortMethod: sortIp,
|
||||
Cell: clientCell(t, toggleClientStatus, processingAccessSet, disallowedClients),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,31 +1,85 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import propTypes from 'prop-types';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import round from 'lodash/round';
|
||||
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import Card from '../ui/Card';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
import { formatNumber } from '../../helpers/helpers';
|
||||
import LogsSearchLink from '../ui/LogsSearchLink';
|
||||
import { RESPONSE_FILTER } from '../../helpers/constants';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
|
||||
const tooltipType = 'tooltip-custom--narrow';
|
||||
const Row = ({
|
||||
label, count, response_status, tooltipTitle, translationComponents,
|
||||
}) => {
|
||||
const content = response_status
|
||||
? <LogsSearchLink response_status={response_status}>{formatNumber(count)}</LogsSearchLink>
|
||||
: count;
|
||||
|
||||
const Counters = (props) => {
|
||||
return <tr key={label}>
|
||||
<td>
|
||||
<Trans components={translationComponents}>{label}</Trans>
|
||||
<Tooltip content={tooltipTitle} placement="top"
|
||||
className="tooltip-container tooltip-custom--narrow text-center">
|
||||
<svg className="icons icon--20 icon--lightgray ml-2">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</td>
|
||||
<td className="text-right"><strong>{content}</strong></td>
|
||||
</tr>;
|
||||
};
|
||||
|
||||
const Counters = ({ refreshButton, subtitle }) => {
|
||||
const {
|
||||
t,
|
||||
interval,
|
||||
refreshButton,
|
||||
subtitle,
|
||||
dnsQueries,
|
||||
blockedFiltering,
|
||||
replacedSafebrowsing,
|
||||
replacedParental,
|
||||
replacedSafesearch,
|
||||
numDnsQueries,
|
||||
numBlockedFiltering,
|
||||
numReplacedSafebrowsing,
|
||||
numReplacedParental,
|
||||
numReplacedSafesearch,
|
||||
avgProcessingTime,
|
||||
} = props;
|
||||
} = useSelector((state) => state.stats, shallowEqual);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tooltipTitle = interval === 1
|
||||
? t('number_of_dns_query_24_hours')
|
||||
: t('number_of_dns_query_days', { count: interval });
|
||||
const rows = [
|
||||
{
|
||||
label: 'dns_query',
|
||||
count: numDnsQueries,
|
||||
tooltipTitle: interval === 1 ? 'number_of_dns_query_24_hours' : t('number_of_dns_query_days', { count: interval }),
|
||||
response_status: RESPONSE_FILTER.ALL.query,
|
||||
},
|
||||
{
|
||||
label: 'blocked_by',
|
||||
count: numBlockedFiltering,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours',
|
||||
response_status: RESPONSE_FILTER.BLOCKED.query,
|
||||
translationComponents: [<a href="#filters" key="0">link</a>],
|
||||
},
|
||||
{
|
||||
label: 'stats_malware_phishing',
|
||||
count: numReplacedSafebrowsing,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_by_sec',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_THREATS.query,
|
||||
},
|
||||
{
|
||||
label: 'stats_adult',
|
||||
count: numReplacedParental,
|
||||
tooltipTitle: 'number_of_dns_query_blocked_24_hours_adult',
|
||||
response_status: RESPONSE_FILTER.BLOCKED_ADULT_WEBSITES.query,
|
||||
},
|
||||
{
|
||||
label: 'enforced_save_search',
|
||||
count: numReplacedSafesearch,
|
||||
tooltipTitle: 'number_of_dns_query_to_safe_search',
|
||||
response_status: RESPONSE_FILTER.SAFE_SEARCH.query,
|
||||
},
|
||||
{
|
||||
label: 'average_processing_time',
|
||||
count: avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0,
|
||||
tooltipTitle: 'average_processing_time_hint',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -35,104 +89,23 @@ const Counters = (props) => {
|
||||
refresh={refreshButton}
|
||||
>
|
||||
<table className="table card-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>dns_query</Trans>
|
||||
<Tooltip text={tooltipTitle} type={tooltipType} />
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(dnsQueries)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans components={[<a href="#filters" key="0">link</a>]}>
|
||||
blocked_by
|
||||
</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(blockedFiltering)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>stats_malware_phishing</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours_by_sec')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedSafebrowsing)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>stats_adult</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_blocked_24_hours_adult')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedParental)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>enforced_save_search</Trans>
|
||||
<Tooltip
|
||||
text={t('number_of_dns_query_to_safe_search')}
|
||||
type={tooltipType}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{formatNumber(replacedSafesearch)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Trans>average_processing_time</Trans>
|
||||
<Tooltip text={t('average_processing_time_hint')} type={tooltipType} />
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<span className="text-muted">
|
||||
{avgProcessingTime ? `${round(avgProcessingTime)} ms` : 0}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>{rows.map(Row)}</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
Counters.propTypes = {
|
||||
dnsQueries: PropTypes.number.isRequired,
|
||||
blockedFiltering: PropTypes.number.isRequired,
|
||||
replacedSafebrowsing: PropTypes.number.isRequired,
|
||||
replacedParental: PropTypes.number.isRequired,
|
||||
replacedSafesearch: PropTypes.number.isRequired,
|
||||
avgProcessingTime: PropTypes.number.isRequired,
|
||||
refreshButton: PropTypes.node.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
interval: PropTypes.number.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
Row.propTypes = {
|
||||
label: propTypes.string.isRequired,
|
||||
count: propTypes.string.isRequired,
|
||||
response_status: propTypes.string,
|
||||
tooltipTitle: propTypes.string.isRequired,
|
||||
translationComponents: propTypes.arrayOf(propTypes.element),
|
||||
};
|
||||
|
||||
export default withTranslation()(Counters);
|
||||
Counters.propTypes = {
|
||||
refreshButton: propTypes.node.isRequired,
|
||||
subtitle: propTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Counters;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -62,23 +62,13 @@ class Table extends Component {
|
||||
showPagination
|
||||
defaultPageSize={10}
|
||||
minRows={5}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
pageText=''
|
||||
ofText=''
|
||||
ofText="/"
|
||||
previousText={t('previous_btn')}
|
||||
nextText={t('next_btn')}
|
||||
pageText={t('page_table_footer_text')}
|
||||
rowsText={t('rows_table_footer_text')}
|
||||
loadingText={t('loading_table_status')}
|
||||
noDataText={t('rewrite_not_found')}
|
||||
showPageSizeOptions={false}
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
getPaginationProps={() => ({ className: 'custom-pagination' })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,4 @@ const Services = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Services.propTypes = {};
|
||||
|
||||
export default Services;
|
||||
|
||||
@@ -128,24 +128,15 @@ class Table extends Component {
|
||||
columns={this.columns}
|
||||
showPagination
|
||||
defaultPageSize={10}
|
||||
showPageSizeOptions={false}
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
loading={loading}
|
||||
minRows={6}
|
||||
pageText=''
|
||||
ofText=''
|
||||
ofText="/"
|
||||
previousText={t('previous_btn')}
|
||||
nextText={t('next_btn')}
|
||||
pageText={t('page_table_footer_text')}
|
||||
rowsText={t('rows_table_footer_text')}
|
||||
loadingText={t('loading_table_status')}
|
||||
noDataText={whitelist ? t('no_whitelist_added') : t('no_blocklist_added')}
|
||||
getPaginationProps={() => ({ className: 'custom-pagination' })}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -51,8 +90,8 @@ class Menu extends Component {
|
||||
};
|
||||
|
||||
getActiveClassForDropdown = (URLS) => {
|
||||
const { pathname } = this.props.location;
|
||||
const isActivePage = Object.values(URLS).some((item) => item === pathname);
|
||||
const isActivePage = Object.values(URLS)
|
||||
.some((item) => item === this.props.pathname);
|
||||
|
||||
return isActivePage ? 'active' : '';
|
||||
};
|
||||
@@ -79,18 +118,18 @@ class Menu extends Component {
|
||||
getDropdown = ({
|
||||
label, order, URLS, icon, ITEMS,
|
||||
}) => (
|
||||
<Dropdown
|
||||
label={this.props.t(label)}
|
||||
baseClassName={`dropdown nav-item order-${order}`}
|
||||
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
|
||||
icon={icon}>
|
||||
{ITEMS.map((item) => (
|
||||
this.getNavLink({
|
||||
...item,
|
||||
order,
|
||||
className: 'dropdown-item',
|
||||
})))}
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
label={this.props.t(label)}
|
||||
baseClassName='dropdown'
|
||||
controlClassName={`nav-link ${this.getActiveClassForDropdown(URLS)}`}
|
||||
icon={icon}>
|
||||
{ITEMS.map((item) => (
|
||||
this.getNavLink({
|
||||
...item,
|
||||
order,
|
||||
className: 'dropdown-item',
|
||||
})))}
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
render() {
|
||||
@@ -99,7 +138,7 @@ class Menu extends Component {
|
||||
'mobile-menu--active': this.props.isMenuOpen,
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<div className={menuClass}>
|
||||
<ul className="nav nav-tabs border-0 flex-column flex-lg-row flex-nowrap">
|
||||
{MENU_ITEMS.map((item) => (
|
||||
@@ -108,34 +147,41 @@ class Menu extends Component {
|
||||
key={item.text}
|
||||
onClick={this.closeMenu}
|
||||
>
|
||||
{this.getNavLink({ ...item, className: 'nav-link' })}
|
||||
{this.getNavLink({
|
||||
...item,
|
||||
className: 'nav-link',
|
||||
})}
|
||||
</li>
|
||||
))}
|
||||
{this.getDropdown({
|
||||
order: 1,
|
||||
label: 'settings',
|
||||
icon: 'settings',
|
||||
URLS: SETTINGS_URLS,
|
||||
ITEMS: SETTINGS_ITEMS,
|
||||
})}
|
||||
{this.getDropdown({
|
||||
order: 2,
|
||||
label: 'filters',
|
||||
icon: 'filters',
|
||||
URLS: FILTERS_URLS,
|
||||
ITEMS: FILTERS_ITEMS,
|
||||
})}
|
||||
<li className="nav-item order-1">
|
||||
{this.getDropdown({
|
||||
order: 1,
|
||||
label: 'settings',
|
||||
icon: 'settings',
|
||||
URLS: SETTINGS_URLS,
|
||||
ITEMS: SETTINGS_ITEMS,
|
||||
})}
|
||||
</li>
|
||||
<li className="nav-item order-2">
|
||||
{this.getDropdown({
|
||||
order: 2,
|
||||
label: 'filters',
|
||||
icon: 'filters',
|
||||
URLS: FILTERS_URLS,
|
||||
ITEMS: FILTERS_ITEMS,
|
||||
})}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Menu.propTypes = {
|
||||
isMenuOpen: PropTypes.bool,
|
||||
closeMenu: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
isMenuOpen: PropTypes.bool.isRequired,
|
||||
closeMenu: PropTypes.func.isRequired,
|
||||
pathname: PropTypes.string.isRequired,
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,83 +1,77 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { Trans } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
|
||||
import Menu from './Menu';
|
||||
import logo from '../ui/svg/logo.svg';
|
||||
import './Header.css';
|
||||
|
||||
class Header extends Component {
|
||||
state = {
|
||||
isMenuOpen: false,
|
||||
const Header = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const {
|
||||
protectionEnabled,
|
||||
processing,
|
||||
isCoreRunning,
|
||||
processingProfile,
|
||||
name,
|
||||
} = useSelector((state) => state.dashboard, shallowEqual);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const toggleMenuOpen = () => {
|
||||
setIsMenuOpen((isMenuOpen) => !isMenuOpen);
|
||||
};
|
||||
|
||||
toggleMenuOpen = () => {
|
||||
this.setState((prevState) => ({ isMenuOpen: !prevState.isMenuOpen }));
|
||||
const closeMenu = () => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
|
||||
closeMenu = () => {
|
||||
this.setState({ isMenuOpen: false });
|
||||
};
|
||||
const badgeClass = classnames('badge dns-status', {
|
||||
'badge-success': protectionEnabled,
|
||||
'badge-danger': !protectionEnabled,
|
||||
});
|
||||
|
||||
render() {
|
||||
const { dashboard, location } = this.props;
|
||||
const { isMenuOpen } = this.state;
|
||||
const badgeClass = classnames({
|
||||
'badge dns-status': true,
|
||||
'badge-success': dashboard.protectionEnabled,
|
||||
'badge-danger': !dashboard.protectionEnabled,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="header">
|
||||
<div className="header__container">
|
||||
<div className="header__row">
|
||||
<div
|
||||
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
||||
onClick={this.toggleMenuOpen}
|
||||
>
|
||||
<span className="header-toggler-icon" />
|
||||
</div>
|
||||
<div className="header__column">
|
||||
<div className="d-flex align-items-center">
|
||||
<Link to="/" className="nav-link pl-0 pr-1">
|
||||
<img src={logo} alt="" className="header-brand-img" />
|
||||
</Link>
|
||||
{!dashboard.processing && dashboard.isCoreRunning && (
|
||||
<span className={badgeClass}>
|
||||
<Trans>{dashboard.protectionEnabled ? 'on' : 'off'}</Trans>
|
||||
return (
|
||||
<div className="header">
|
||||
<div className="header__container">
|
||||
<div className="header__row">
|
||||
<div
|
||||
className="header-toggler d-lg-none ml-lg-0 collapsed"
|
||||
onClick={toggleMenuOpen}
|
||||
>
|
||||
<span className="header-toggler-icon" />
|
||||
</div>
|
||||
<div className="header__column">
|
||||
<div className="d-flex align-items-center">
|
||||
<Link to="/" className="nav-link pl-0 pr-1">
|
||||
<img src={logo} alt="" className="header-brand-img" />
|
||||
</Link>
|
||||
{!processing && isCoreRunning && (
|
||||
<span className={badgeClass}>
|
||||
<Trans>{protectionEnabled ? 'on' : 'off'}</Trans>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Menu
|
||||
location={location}
|
||||
isMenuOpen={isMenuOpen}
|
||||
closeMenu={this.closeMenu}
|
||||
/>
|
||||
<div className="header__column">
|
||||
<div className="header__right">
|
||||
{!dashboard.processingProfile && dashboard.name
|
||||
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||
<Trans>sign_out</Trans>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<Menu
|
||||
pathname={pathname}
|
||||
isMenuOpen={isMenuOpen}
|
||||
closeMenu={closeMenu}
|
||||
/>
|
||||
<div className="header__column">
|
||||
<div className="header__right">
|
||||
{!processingProfile && name
|
||||
&& <a href="control/logout" className="btn btn-sm btn-outline-secondary">
|
||||
<Trans>sign_out</Trans>
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
dashboard: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
getVersion: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation()(Header);
|
||||
export default Header;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -16,6 +16,7 @@ const getClientCell = ({
|
||||
|
||||
const autoClient = autoClients.find((autoClient) => autoClient.name === client);
|
||||
const source = autoClient?.source;
|
||||
const whoisAvailable = whois_info && Object.keys(whois_info).length > 0;
|
||||
|
||||
const id = nanoid();
|
||||
|
||||
@@ -33,11 +34,11 @@ const getClientCell = ({
|
||||
const isFiltered = checkFiltered(reason);
|
||||
|
||||
const nameClass = classNames('w-90 o-hidden d-flex flex-column', {
|
||||
'mt-2': isDetailed && !name,
|
||||
'mt-2': isDetailed && !name && !whoisAvailable,
|
||||
'white-space--nowrap': isDetailed,
|
||||
});
|
||||
|
||||
const hintClass = classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', {
|
||||
const hintClass = classNames('icons mr-4 icon--24 icon--lightgray', {
|
||||
'my-3': isDetailed,
|
||||
});
|
||||
|
||||
@@ -68,7 +69,7 @@ const getClientCell = ({
|
||||
|
||||
return (
|
||||
<div className="logs__row o-hidden h-100">
|
||||
{processedData && getHintElement({
|
||||
{getIconTooltip({
|
||||
className: hintClass,
|
||||
columnClass: 'grid grid--limited',
|
||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75',
|
||||
@@ -78,12 +79,19 @@ const getClientCell = ({
|
||||
content: processedData,
|
||||
placement: 'bottom',
|
||||
})}
|
||||
<div
|
||||
className={nameClass}>
|
||||
<div data-tip={true} data-for={id}>{formatClientCell(row, t, isDetailed)}</div>
|
||||
{isDetailed && name
|
||||
&& <div className="detailed-info d-none d-sm-block logs__text"
|
||||
title={name}>{name}</div>}
|
||||
<div className={nameClass}>
|
||||
<div data-tip={true} data-for={id}>
|
||||
{formatClientCell(row, isDetailed)}
|
||||
</div>
|
||||
|
||||
{isDetailed && name && !whoisAvailable && (
|
||||
<div
|
||||
className="detailed-info d-none d-sm-block logs__text"
|
||||
title={name}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{renderBlockingButton(isFiltered, domain)}
|
||||
</div>
|
||||
|
||||
@@ -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 '';
|
||||
}
|
||||
|
||||
@@ -33,81 +38,39 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
|
||||
})}</div>;
|
||||
};
|
||||
|
||||
const FILTERED_STATUS_TO_FIELDS_MAP = {
|
||||
[FILTERED_STATUS.NOT_FILTERED_NOT_FOUND]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
response_table_header: renderResponses(response),
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_BLOCKED_SERVICE]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
},
|
||||
[FILTERED_STATUS.FILTERED_BLACK_LIST]: {
|
||||
domain,
|
||||
encryption_status: boldStatusLabel,
|
||||
filter,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
},
|
||||
const COMMON_CONTENT = {
|
||||
encryption_status: boldStatusLabel,
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
response_code: status,
|
||||
filter,
|
||||
rule_label: rule,
|
||||
response_table_header: renderResponses(response),
|
||||
original_response: renderResponses(originalResponse),
|
||||
};
|
||||
|
||||
const fields = FILTERED_STATUS_TO_FIELDS_MAP[reason]
|
||||
? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason])
|
||||
: Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound);
|
||||
|
||||
const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE
|
||||
|| reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||
? filter : formattedElapsedMs;
|
||||
const content = rule
|
||||
? Object.entries(COMMON_CONTENT)
|
||||
: Object.entries({ ...COMMON_CONTENT, filter: '' });
|
||||
const detailedInfo = isBlocked ? filter : formattedElapsedMs;
|
||||
|
||||
return (
|
||||
<div className="logs__row">
|
||||
{fields && getHintElement({
|
||||
className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }),
|
||||
{getIconTooltip({
|
||||
className: classNames('icons mr-4 icon--24 icon--lightgray', { 'my-3': isDetailed }),
|
||||
columnClass: 'grid grid--limited',
|
||||
tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details',
|
||||
contentItemClass: 'text-truncate key-colon o-hidden',
|
||||
xlinkHref: 'question',
|
||||
title: 'response_details',
|
||||
content: fields,
|
||||
content,
|
||||
placement: 'bottom',
|
||||
})}
|
||||
<div className="text-truncate">
|
||||
<div className="text-truncate" title={statusLabel}>{statusLabel}</div>
|
||||
{isDetailed && <div
|
||||
className="detailed-info d-none d-sm-block pt-1 text-truncate" title={detailedInfo}>{detailedInfo}</div>}
|
||||
className="detailed-info d-none d-sm-block pt-1 text-truncate"
|
||||
title={detailedInfo}>{detailedInfo}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,17 +2,20 @@ import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
DEBOUNCE_FILTER_TIMEOUT,
|
||||
DEFAULT_LOGS_FILTER,
|
||||
FORM_NAME,
|
||||
RESPONSE_FILTER,
|
||||
RESPONSE_FILTER_QUERIES,
|
||||
} from '../../../helpers/constants';
|
||||
import Tooltip from '../../ui/Tooltip';
|
||||
import { setLogsFilter } from '../../../actions/queryLogs';
|
||||
import useDebounce from '../../../helpers/useDebounce';
|
||||
import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers';
|
||||
import Tooltip from '../../ui/Tooltip';
|
||||
|
||||
const renderFilterField = ({
|
||||
input,
|
||||
@@ -25,34 +28,47 @@ const renderFilterField = ({
|
||||
tooltip,
|
||||
meta: { touched, error },
|
||||
onClearInputClick,
|
||||
}) => <>
|
||||
<div className="input-group-search input-group-search__icon--magnifier">
|
||||
<svg className="icons icon--small icon--gray">
|
||||
<use xlinkHref="#magnifier" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
{...input}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
aria-label={placeholder} />
|
||||
<div
|
||||
className={classNames('input-group-search input-group-search__icon--cross', { invisible: input.value.length < 1 })}>
|
||||
<svg className="icons icon--smallest icon--gray" onClick={onClearInputClick}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="input-group-search input-group-search__icon--tooltip">
|
||||
<Tooltip text={tooltip} type='tooltip-custom--logs' />
|
||||
onKeyDown,
|
||||
normalizeOnBlur,
|
||||
}) => {
|
||||
const onBlur = (event) => createOnBlurHandler(event, input, normalizeOnBlur);
|
||||
|
||||
return <>
|
||||
<div className="input-group-search input-group-search__icon--magnifier">
|
||||
<svg className="icons icon--24 icon--gray">
|
||||
<use xlinkHref="#magnifier" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
{...input}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
type={type}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
autoComplete={autoComplete}
|
||||
aria-label={placeholder}
|
||||
onKeyDown={onKeyDown}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<div
|
||||
className={classNames('input-group-search input-group-search__icon--cross', { invisible: input.value.length < 1 })}>
|
||||
<svg className="icons icon--20 icon--gray" onClick={onClearInputClick}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="input-group-search input-group-search__icon--tooltip">
|
||||
<Tooltip content={tooltip} className="tooltip-container">
|
||||
<svg className="icons icon--20 icon--gray">
|
||||
<use xlinkHref="#question" />
|
||||
</svg>
|
||||
</Tooltip>
|
||||
</span>
|
||||
{!disabled
|
||||
&& touched
|
||||
&& (error && <span className="form__message form__message--error">{error}</span>)}
|
||||
</>;
|
||||
{!disabled
|
||||
&& touched
|
||||
&& (error && <span className="form__message form__message--error">{error}</span>)}
|
||||
</>;
|
||||
};
|
||||
|
||||
renderFilterField.propTypes = {
|
||||
input: PropTypes.object.isRequired,
|
||||
@@ -64,71 +80,108 @@ renderFilterField.propTypes = {
|
||||
disabled: PropTypes.string,
|
||||
autoComplete: PropTypes.string,
|
||||
tooltip: PropTypes.string,
|
||||
onKeyDown: PropTypes.func,
|
||||
normalizeOnBlur: PropTypes.func,
|
||||
meta: PropTypes.shape({
|
||||
touched: PropTypes.bool,
|
||||
error: PropTypes.object,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
const FORM_NAMES = {
|
||||
search: 'search',
|
||||
response_status: 'response_status',
|
||||
};
|
||||
|
||||
const Form = (props) => {
|
||||
const {
|
||||
className = '',
|
||||
responseStatusClass,
|
||||
submit,
|
||||
reset,
|
||||
setIsLoading,
|
||||
change,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const debouncedSubmit = debounce(submit, DEBOUNCE_FILTER_TIMEOUT);
|
||||
const zeroDelaySubmit = () => setTimeout(submit, 0);
|
||||
const {
|
||||
response_status, search,
|
||||
} = useSelector((state) => state.form[FORM_NAME.LOGS_FILTER].values, shallowEqual);
|
||||
|
||||
const clearInput = async () => {
|
||||
await dispatch(setLogsFilter(DEFAULT_LOGS_FILTER));
|
||||
await reset();
|
||||
};
|
||||
const [
|
||||
debouncedSearch,
|
||||
setDebouncedSearch,
|
||||
] = useDebounce(search.trim(), DEBOUNCE_FILTER_TIMEOUT);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setLogsFilter({
|
||||
response_status,
|
||||
search: debouncedSearch,
|
||||
}));
|
||||
|
||||
history.replace(`${getLogsUrlParams(debouncedSearch, response_status)}`);
|
||||
}, [response_status, debouncedSearch]);
|
||||
|
||||
if (response_status && !(response_status in RESPONSE_FILTER_QUERIES)) {
|
||||
change(FORM_NAMES.response_status, DEFAULT_LOGS_FILTER[FORM_NAMES.response_status]);
|
||||
}
|
||||
|
||||
const onInputClear = async () => {
|
||||
setIsLoading(true);
|
||||
await clearInput();
|
||||
setDebouncedSearch(DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => clearInput, []);
|
||||
const onEnterPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setDebouncedSearch(search);
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeOnBlur = (data) => data.trim();
|
||||
|
||||
return (
|
||||
<form className="d-flex flex-wrap form-control--container"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
zeroDelaySubmit();
|
||||
debouncedSubmit.cancel();
|
||||
}}
|
||||
<form
|
||||
className="d-flex flex-wrap form-control--container"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Field
|
||||
id="search"
|
||||
name="search"
|
||||
component={renderFilterField}
|
||||
type="text"
|
||||
className={classNames('form-control--search form-control--transparent', className)}
|
||||
placeholder={t('domain_or_client')}
|
||||
tooltip={t('query_log_strict_search')}
|
||||
onChange={debouncedSubmit}
|
||||
onClearInputClick={onInputClear}
|
||||
/>
|
||||
<div className="field__search">
|
||||
<Field
|
||||
id={FORM_NAMES.search}
|
||||
name={FORM_NAMES.search}
|
||||
component={renderFilterField}
|
||||
type="text"
|
||||
className={classNames('form-control--search form-control--transparent', className)}
|
||||
placeholder={t('domain_or_client')}
|
||||
tooltip={t('query_log_strict_search')}
|
||||
onClearInputClick={onInputClear}
|
||||
onKeyDown={onEnterPress}
|
||||
normalizeOnBlur={normalizeOnBlur}
|
||||
/>
|
||||
</div>
|
||||
<div className="field__select">
|
||||
<Field
|
||||
name="response_status"
|
||||
name={FORM_NAMES.response_status}
|
||||
component="select"
|
||||
className={classNames('form-control custom-select custom-select--logs custom-select__arrow--left ml-small form-control--transparent', responseStatusClass)}
|
||||
onChange={zeroDelaySubmit}
|
||||
className={classNames('form-control custom-select custom-select--logs custom-select__arrow--left form-control--transparent', responseStatusClass)}
|
||||
>
|
||||
{Object.values(RESPONSE_FILTER)
|
||||
.map(({
|
||||
query, label, disabled,
|
||||
}) => <option key={label} value={query}
|
||||
disabled={disabled}>{t(label)}</option>)}
|
||||
}) => (
|
||||
<option
|
||||
key={label}
|
||||
value={query}
|
||||
disabled={disabled}
|
||||
>
|
||||
{t(label)}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</Field>
|
||||
</div>
|
||||
</form>
|
||||
@@ -136,14 +189,13 @@ const Form = (props) => {
|
||||
};
|
||||
|
||||
Form.propTypes = {
|
||||
handleChange: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
responseStatusClass: PropTypes.string,
|
||||
submit: PropTypes.func.isRequired,
|
||||
reset: PropTypes.func.isRequired,
|
||||
change: PropTypes.func.isRequired,
|
||||
setIsLoading: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default reduxForm({
|
||||
form: FORM_NAME.LOGS_FILTER,
|
||||
enableReinitialize: true,
|
||||
})(Form);
|
||||
|
||||
@@ -1,43 +1,29 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import { setLogsFilter } from '../../../actions/queryLogs';
|
||||
|
||||
const Filters = ({ filter, refreshLogs, setIsLoading }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onSubmit = async (values) => {
|
||||
setIsLoading(true);
|
||||
await dispatch(setLogsFilter(values));
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
const Filters = ({ filter, refreshLogs, setIsLoading }) => (
|
||||
<div className="page-header page-header--logs">
|
||||
<h1 className="page-title page-title--large">
|
||||
<Trans>query_log</Trans>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-icon--green ml-3 bg-transparent"
|
||||
className="btn btn-icon--green logs__refresh"
|
||||
onClick={refreshLogs}
|
||||
>
|
||||
<svg className="icons icon--small">
|
||||
<svg className="icons icon--24">
|
||||
<use xlinkHref="#update" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</h1>
|
||||
<Form
|
||||
responseStatusClass="d-sm-block"
|
||||
initialValues={filter}
|
||||
onSubmit={onSubmit}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
Filters.propTypes = {
|
||||
filter: PropTypes.object.isRequired,
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
}
|
||||
|
||||
.card-table .logs__row {
|
||||
overflow: visible;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.logs__row--center {
|
||||
@@ -57,12 +58,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logs__text--domain {
|
||||
max-width: 285px;
|
||||
}
|
||||
|
||||
.logs__text--wrap,
|
||||
.logs__text--whois {
|
||||
.logs__text--wrap {
|
||||
line-height: 1.4;
|
||||
white-space: normal;
|
||||
}
|
||||
@@ -74,6 +70,7 @@
|
||||
|
||||
.logs__text--whois {
|
||||
line-height: 1.2;
|
||||
color: #9aa0ac;
|
||||
}
|
||||
|
||||
.logs__row .tooltip-custom {
|
||||
@@ -202,6 +199,7 @@
|
||||
.logs__whois {
|
||||
display: inline;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.logs__whois::after {
|
||||
@@ -364,7 +362,7 @@
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.-pageInfo {
|
||||
.logs__table .-pageInfo {
|
||||
--side-size: 2rem;
|
||||
font-variant-numeric: tabular-nums !important;
|
||||
background-color: transparent !important;
|
||||
@@ -378,18 +376,18 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pagination-bottom {
|
||||
.logs__table .pagination-bottom {
|
||||
justify-content: center !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.-center:before {
|
||||
.logs__table .-center:before {
|
||||
content: '...';
|
||||
transform: translateY(-0.25rem);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.-center:after {
|
||||
.logs__table .-center:after {
|
||||
content: '...';
|
||||
transform: translateY(-0.25rem);
|
||||
margin: auto;
|
||||
@@ -401,10 +399,6 @@
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
.icon--light-gray {
|
||||
color: var(--gray-8);
|
||||
}
|
||||
|
||||
.link--green {
|
||||
color: var(--green79);
|
||||
}
|
||||
@@ -443,27 +437,24 @@
|
||||
}
|
||||
|
||||
.custom-select__arrow--left {
|
||||
background: #fff url('./chevron-down.svg') no-repeat left 0.2rem center;
|
||||
background-size: 1.5rem;
|
||||
background: var(--white) url('../ui/svg/chevron-down.svg') no-repeat;
|
||||
background-position: 5px 9px;
|
||||
background-size: 22px;
|
||||
}
|
||||
|
||||
.custom-select--logs {
|
||||
padding: 0.5rem 0.75rem 0.5rem 1.75rem !important;
|
||||
padding: 0.5rem 0.75rem 0.5rem 2rem !important;
|
||||
}
|
||||
|
||||
.bg--danger {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.ml-small {
|
||||
margin-left: 3.3125rem;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
width: 39.125rem;
|
||||
box-shadow: 0 1px 0 #ddd;
|
||||
padding: 0 2.5rem;
|
||||
height: 2.25rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.form-control--transparent {
|
||||
@@ -493,31 +484,12 @@
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 100%;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 1279.98px) {
|
||||
.form-control--search {
|
||||
max-width: 30.125rem;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.form-control--search {
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
max-width: 100%;
|
||||
}
|
||||
.field__search {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
@@ -528,6 +500,21 @@
|
||||
.ml-small {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.form-control--container {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-control--search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.field__select {
|
||||
margin-top: 1.5rem;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
@@ -544,16 +531,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.form-control--search {
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
.field__select {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.loading__container > .-loading-inner {
|
||||
top: 10rem !important;
|
||||
bottom: initial !important;
|
||||
@@ -562,3 +539,16 @@
|
||||
.loading__text {
|
||||
transform: translateY(3rem);
|
||||
}
|
||||
|
||||
.logs__refresh {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
margin-left: 15px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
FILTERED_STATUS_TO_META_MAP,
|
||||
TABLE_DEFAULT_PAGE_SIZE,
|
||||
SCHEME_TO_PROTOCOL_MAP,
|
||||
CUSTOM_FILTERING_RULES_ID,
|
||||
CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS,
|
||||
} from '../../helpers/constants';
|
||||
import getDateCell from './Cells/getDateCell';
|
||||
import getDomainCell from './Cells/getDomainCell';
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
formatDateTime,
|
||||
formatElapsedMs,
|
||||
formatTime,
|
||||
|
||||
processContent,
|
||||
} from '../../helpers/helpers';
|
||||
import Loading from '../ui/Loading';
|
||||
import { getSourceData } from '../../helpers/trackers/trackers';
|
||||
@@ -49,7 +49,7 @@ const Table = (props) => {
|
||||
isLoading,
|
||||
} = props;
|
||||
|
||||
const [t] = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toggleBlocking = (type, domain) => {
|
||||
const {
|
||||
@@ -154,27 +154,23 @@ const Table = (props) => {
|
||||
headerClassName: 'logs__text',
|
||||
},
|
||||
{
|
||||
Header: () => {
|
||||
const plainSelected = classNames('cursor--pointer', {
|
||||
'icon--selected': !isDetailed,
|
||||
});
|
||||
|
||||
const detailedSelected = classNames('cursor--pointer', {
|
||||
'icon--selected': isDetailed,
|
||||
});
|
||||
|
||||
Header: function Header() {
|
||||
return <div className="d-flex justify-content-between">
|
||||
{t('client_table_header')}
|
||||
{<span>
|
||||
<svg
|
||||
className={`icons icon--small icon--active mr-2 cursor--pointer ${plainSelected}`}
|
||||
className={classNames('icons icon--24 icon--green mr-2 cursor--pointer', {
|
||||
'icon--selected': !isDetailed,
|
||||
})}
|
||||
onClick={() => toggleDetailedLogs(false)}
|
||||
>
|
||||
<title><Trans>compact</Trans></title>
|
||||
<use xlinkHref='#list' />
|
||||
</svg>
|
||||
<svg
|
||||
className={`icons icon--small icon--active cursor--pointer ${detailedSelected}`}
|
||||
className={classNames('icons icon--24 icon--green cursor--pointer', {
|
||||
'icon--selected': isDetailed,
|
||||
})}
|
||||
onClick={() => toggleDetailedLogs(true)}
|
||||
>
|
||||
<title><Trans>default</Trans></title>
|
||||
@@ -239,7 +235,7 @@ const Table = (props) => {
|
||||
sortable={false}
|
||||
resizable={false}
|
||||
data={logs || []}
|
||||
loading={isLoading}
|
||||
loading={isLoading || processingGetLogs}
|
||||
showPageJump={false}
|
||||
showPageSizeOptions={false}
|
||||
onPageChange={changePage}
|
||||
@@ -261,12 +257,12 @@ const Table = (props) => {
|
||||
getPaginationProps={() => ({ className: 'custom-pagination custom-pagination--padding' })}
|
||||
getTbodyProps={() => ({ className: 'd-block' })}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
|
||||
<title><Trans>previous_btn</Trans></title>
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100 cursor--pointer">
|
||||
<svg className="icons icon--24 icon--gray w-100 h-100 cursor--pointer">
|
||||
<title><Trans>next_btn</Trans></title>
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
@@ -300,6 +296,9 @@ const Table = (props) => {
|
||||
type,
|
||||
client_proto,
|
||||
filterId,
|
||||
rule,
|
||||
originalResponse,
|
||||
status,
|
||||
} = rowInfo.original;
|
||||
|
||||
const hasTracker = !!tracker;
|
||||
@@ -317,22 +316,29 @@ const Table = (props) => {
|
||||
const formattedElapsedMs = formatElapsedMs(elapsedMs, t);
|
||||
const isFiltered = checkFiltered(reason);
|
||||
|
||||
const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST
|
||||
|| reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE;
|
||||
|
||||
const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK;
|
||||
const onToggleBlock = () => {
|
||||
toggleBlocking(buttonType, domain);
|
||||
};
|
||||
|
||||
const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
const statusBlocked = <div className="bg--danger">{status}</div>;
|
||||
const isBlockedByResponse = originalResponse.length > 0 && isBlocked;
|
||||
const requestStatus = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason);
|
||||
|
||||
const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || '';
|
||||
|
||||
const sourceData = getSourceData(tracker);
|
||||
|
||||
const { filters, whitelistFilters } = filtering;
|
||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||
|
||||
const detailedData = {
|
||||
time_table_header: formatTime(time, LONG_TIME_FORMAT),
|
||||
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
|
||||
encryption_status: status,
|
||||
encryption_status: isBlocked
|
||||
? <div className="bg--danger">{requestStatus}</div> : requestStatus,
|
||||
domain,
|
||||
type_table_header: type,
|
||||
protocol,
|
||||
@@ -340,12 +346,19 @@ const Table = (props) => {
|
||||
table_name: tracker?.name,
|
||||
category_label: hasTracker && captitalizeWords(tracker.category),
|
||||
tracker_source: hasTracker && sourceData
|
||||
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}</a>,
|
||||
&& <a
|
||||
href={sourceData.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}
|
||||
</a>,
|
||||
response_details: 'title',
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter: rule ? filter : null,
|
||||
rule_label: rule,
|
||||
response_table_header: response?.join('\n'),
|
||||
response_code: status,
|
||||
client_details: 'title',
|
||||
ip_address: client,
|
||||
name: info?.name,
|
||||
@@ -354,39 +367,14 @@ const Table = (props) => {
|
||||
network,
|
||||
source_label: source,
|
||||
validated_with_dnssec: dnssec_enabled ? Boolean(answer_dnssec) : false,
|
||||
original_response: originalResponse?.join('\n'),
|
||||
[buttonType]: <div onClick={onToggleBlock}
|
||||
className="title--border bg--danger text-center">{t(buttonType)}</div>,
|
||||
className={classNames('title--border text-center', {
|
||||
'bg--danger': isBlocked,
|
||||
})}>{t(buttonType)}</div>,
|
||||
};
|
||||
|
||||
const { filters, whitelistFilters } = filtering;
|
||||
|
||||
const filter = getFilterName(filters, whitelistFilters, filterId, t);
|
||||
|
||||
const detailedDataBlocked = {
|
||||
time_table_header: formatTime(time, LONG_TIME_FORMAT),
|
||||
date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS),
|
||||
encryption_status: statusBlocked,
|
||||
domain,
|
||||
type_table_header: type,
|
||||
protocol,
|
||||
known_tracker: 'title',
|
||||
table_name: tracker?.name,
|
||||
category_label: hasTracker && captitalizeWords(tracker.category),
|
||||
source_label: hasTracker && sourceData
|
||||
&& <a href={sourceData.url} target="_blank" rel="noopener noreferrer"
|
||||
className="link--green">{sourceData.name}</a>,
|
||||
response_details: 'title',
|
||||
install_settings_dns: upstream,
|
||||
elapsed: formattedElapsedMs,
|
||||
filter,
|
||||
response_table_header: response?.join('\n'),
|
||||
[buttonType]: <div onClick={onToggleBlock}
|
||||
className="title--border text-center">{t(buttonType)}</div>,
|
||||
};
|
||||
|
||||
const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData;
|
||||
|
||||
setDetailedDataCurrent(detailedDataCurrent);
|
||||
setDetailedDataCurrent(processContent(detailedData));
|
||||
setButtonType(buttonType);
|
||||
setModalOpened(true);
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#9aa0ac" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
|
Before Width: | Height: | Size: 264 B |
@@ -2,11 +2,15 @@ import React, { Fragment, useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import Modal from 'react-modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
BLOCK_ACTIONS, smallScreenSize,
|
||||
BLOCK_ACTIONS,
|
||||
TABLE_DEFAULT_PAGE_SIZE,
|
||||
TABLE_FIRST_PAGE,
|
||||
SMALL_SCREEN_SIZE,
|
||||
} from '../../helpers/constants';
|
||||
import Loading from '../ui/Loading';
|
||||
import Filters from './Filters';
|
||||
@@ -15,15 +19,21 @@ import Disabled from './Disabled';
|
||||
import { getFilteringStatus } from '../../actions/filtering';
|
||||
import { getClients } from '../../actions';
|
||||
import { getDnsConfig } from '../../actions/dnsConfig';
|
||||
import { getLogsConfig } from '../../actions/queryLogs';
|
||||
import {
|
||||
getLogsConfig,
|
||||
refreshFilteredLogs,
|
||||
resetFilteredLogs,
|
||||
setFilteredLogs,
|
||||
} from '../../actions/queryLogs';
|
||||
import { addSuccessToast } from '../../actions/toasts';
|
||||
import './Logs.css';
|
||||
|
||||
const INITIAL_REQUEST = true;
|
||||
const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST];
|
||||
|
||||
export const processContent = (data, buttonType) => Object.entries(data)
|
||||
const processContent = (data, buttonType) => Object.entries(data)
|
||||
.map(([key, value]) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isTitle = value === 'title';
|
||||
const isButton = key === buttonType;
|
||||
const isBoolean = typeof value === 'boolean';
|
||||
@@ -40,7 +50,9 @@ export const processContent = (data, buttonType) => Object.entries(data)
|
||||
|
||||
return isHidden ? null : <Fragment key={key}>
|
||||
<div
|
||||
className={`key__${key} ${keyClass} ${(isBoolean && value === true) ? 'font-weight-bold' : ''}`}>
|
||||
className={classNames(`key__${key}`, keyClass, {
|
||||
'font-weight-bold': isBoolean && value === true,
|
||||
})}>
|
||||
<Trans>{isButton ? value : key}</Trans>
|
||||
</div>
|
||||
<div className={`value__${key} text-pre text-truncate`}>
|
||||
@@ -52,22 +64,44 @@ export const processContent = (data, buttonType) => Object.entries(data)
|
||||
|
||||
const Logs = (props) => {
|
||||
const dispatch = useDispatch();
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < smallScreenSize);
|
||||
const history = useHistory();
|
||||
|
||||
const {
|
||||
response_status: response_status_url_param = '',
|
||||
search: search_url_param = '',
|
||||
} = queryString.parse(history.location.search);
|
||||
|
||||
const { filter } = useSelector((state) => state.queryLogs, shallowEqual);
|
||||
|
||||
const search = filter?.search || search_url_param;
|
||||
const response_status = filter?.response_status || response_status_url_param;
|
||||
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth < SMALL_SCREEN_SIZE);
|
||||
const [detailedDataCurrent, setDetailedDataCurrent] = useState({});
|
||||
const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK);
|
||||
const [isModalOpened, setModalOpened] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setIsLoading(true);
|
||||
await dispatch(setFilteredLogs({
|
||||
search,
|
||||
response_status,
|
||||
}));
|
||||
setIsLoading(false);
|
||||
})();
|
||||
}, [response_status, search]);
|
||||
|
||||
const {
|
||||
filtering,
|
||||
setLogsPage,
|
||||
setLogsPagination,
|
||||
setLogsFilter,
|
||||
toggleDetailedLogs,
|
||||
dashboard,
|
||||
dnsConfig,
|
||||
queryLogs: {
|
||||
filter,
|
||||
enabled,
|
||||
processingGetConfig,
|
||||
processingAdditionalLogs,
|
||||
@@ -80,7 +114,7 @@ const Logs = (props) => {
|
||||
},
|
||||
} = props;
|
||||
|
||||
const mediaQuery = window.matchMedia(`(max-width: ${smallScreenSize}px)`);
|
||||
const mediaQuery = window.matchMedia(`(max-width: ${SMALL_SCREEN_SIZE}px)`);
|
||||
const mediaQueryHandler = (e) => {
|
||||
setIsSmallScreen(e.matches);
|
||||
if (e.matches) {
|
||||
@@ -88,16 +122,10 @@ const Logs = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
mediaQuery.addListener(mediaQueryHandler);
|
||||
|
||||
return () => mediaQuery.removeListener(mediaQueryHandler);
|
||||
}, []);
|
||||
|
||||
const closeModal = () => setModalOpened(false);
|
||||
|
||||
const getLogs = (older_than, page, initial) => {
|
||||
if (props.queryLogs.enabled) {
|
||||
if (enabled) {
|
||||
props.getLogs({
|
||||
older_than,
|
||||
page,
|
||||
@@ -108,6 +136,17 @@ const Logs = (props) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
mediaQuery.addEventListener('change', mediaQueryHandler);
|
||||
} catch (e1) {
|
||||
try {
|
||||
// Safari 13.1 do not support mediaQuery.addEventListener('change', handler)
|
||||
mediaQuery.addListener(mediaQueryHandler);
|
||||
} catch (e2) {
|
||||
console.error(e2);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
setIsLoading(true);
|
||||
dispatch(setLogsPage(TABLE_FIRST_PAGE));
|
||||
@@ -115,7 +154,6 @@ const Logs = (props) => {
|
||||
dispatch(getClients());
|
||||
try {
|
||||
await Promise.all([
|
||||
getLogs(...INITIAL_REQUEST_DATA),
|
||||
dispatch(getLogsConfig()),
|
||||
dispatch(getDnsConfig()),
|
||||
]);
|
||||
@@ -125,13 +163,27 @@ const Logs = (props) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
try {
|
||||
mediaQuery.removeEventListener('change', mediaQueryHandler);
|
||||
} catch (e1) {
|
||||
try {
|
||||
mediaQuery.removeListener(mediaQueryHandler);
|
||||
} catch (e2) {
|
||||
console.error(e2);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(resetFilteredLogs());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refreshLogs = async () => {
|
||||
setIsLoading(true);
|
||||
await Promise.all([
|
||||
dispatch(setLogsPage(TABLE_FIRST_PAGE)),
|
||||
getLogs(...INITIAL_REQUEST_DATA),
|
||||
dispatch(refreshFilteredLogs()),
|
||||
]);
|
||||
dispatch(addSuccessToast('query_log_updated'));
|
||||
setIsLoading(false);
|
||||
@@ -141,13 +193,15 @@ const Logs = (props) => {
|
||||
<>
|
||||
{enabled && processingGetConfig && <Loading />}
|
||||
{enabled && !processingGetConfig && (
|
||||
<Fragment>
|
||||
<>
|
||||
<Filters
|
||||
filter={filter}
|
||||
filter={{
|
||||
response_status,
|
||||
search,
|
||||
}}
|
||||
setIsLoading={setIsLoading}
|
||||
processingGetLogs={processingGetLogs}
|
||||
processingAdditionalLogs={processingAdditionalLogs}
|
||||
setLogsFilter={setLogsFilter}
|
||||
refreshLogs={refreshLogs}
|
||||
/>
|
||||
<Table
|
||||
@@ -191,13 +245,13 @@ const Logs = (props) => {
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
className="icon icon--small icon-cross d-block d-md-none cursor--pointer"
|
||||
className="icon icon--24 icon-cross d-block d-md-none cursor--pointer"
|
||||
onClick={closeModal}>
|
||||
<use xlinkHref="#cross" />
|
||||
</svg>
|
||||
{processContent(detailedDataCurrent, buttonType)}
|
||||
</Modal>
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
{!enabled && !processingGetConfig && (
|
||||
<Disabled />
|
||||
@@ -215,7 +269,6 @@ Logs.propTypes = {
|
||||
setRules: PropTypes.func.isRequired,
|
||||
addSuccessToast: PropTypes.func.isRequired,
|
||||
setLogsPagination: PropTypes.func.isRequired,
|
||||
setLogsFilter: PropTypes.func.isRequired,
|
||||
setLogsPage: PropTypes.func.isRequired,
|
||||
toggleDetailedLogs: PropTypes.func.isRequired,
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
|
||||
@@ -7,6 +7,8 @@ import Card from '../../ui/Card';
|
||||
import CellWrap from '../../ui/CellWrap';
|
||||
|
||||
import whoisCell from './whoisCell';
|
||||
import LogsSearchLink from '../../ui/LogsSearchLink';
|
||||
import { sortIp } from '../../../helpers/helpers';
|
||||
|
||||
const COLUMN_MIN_WIDTH = 200;
|
||||
|
||||
@@ -17,6 +19,7 @@ class AutoClients extends Component {
|
||||
accessor: 'ip',
|
||||
minWidth: COLUMN_MIN_WIDTH,
|
||||
Cell: CellWrap,
|
||||
sortMethod: sortIp,
|
||||
},
|
||||
{
|
||||
Header: this.props.t('table_name'),
|
||||
@@ -49,7 +52,9 @@ class AutoClients extends Component {
|
||||
return (
|
||||
<div className="logs__row">
|
||||
<div className="logs__text" title={clientStats}>
|
||||
{clientStats}
|
||||
<LogsSearchLink search={row.original.ip}>
|
||||
{clientStats}
|
||||
</LogsSearchLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -82,23 +87,13 @@ class AutoClients extends Component {
|
||||
showPagination
|
||||
defaultPageSize={10}
|
||||
minRows={5}
|
||||
showPageSizeOptions={false}
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
pageText=''
|
||||
ofText=''
|
||||
ofText="/"
|
||||
previousText={t('previous_btn')}
|
||||
nextText={t('next_btn')}
|
||||
pageText={t('page_table_footer_text')}
|
||||
rowsText={t('rows_table_footer_text')}
|
||||
loadingText={t('loading_table_status')}
|
||||
noDataText={t('clients_not_found')}
|
||||
getPaginationProps={() => ({ className: 'custom-pagination' })}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -4,10 +4,11 @@ import { Trans, withTranslation } from 'react-i18next';
|
||||
import ReactTable from 'react-table';
|
||||
|
||||
import { MODAL_TYPE } from '../../../helpers/constants';
|
||||
import { normalizeTextarea } from '../../../helpers/helpers';
|
||||
import { splitByNewLine } from '../../../helpers/helpers';
|
||||
import Card from '../../ui/Card';
|
||||
import Modal from './Modal';
|
||||
import CellWrap from '../../ui/CellWrap';
|
||||
import LogsSearchLink from '../../ui/LogsSearchLink';
|
||||
|
||||
class ClientsTable extends Component {
|
||||
handleFormAdd = (values) => {
|
||||
@@ -29,7 +30,7 @@ class ClientsTable extends Component {
|
||||
}
|
||||
|
||||
if (values.upstreams && typeof values.upstreams === 'string') {
|
||||
config.upstreams = normalizeTextarea(values.upstreams);
|
||||
config.upstreams = splitByNewLine(values.upstreams);
|
||||
} else {
|
||||
config.upstreams = [];
|
||||
}
|
||||
@@ -49,7 +50,10 @@ class ClientsTable extends Component {
|
||||
};
|
||||
|
||||
getOptionsWithLabels = (options) => (
|
||||
options.map((option) => ({ value: option, label: option }))
|
||||
options.map((option) => ({
|
||||
value: option,
|
||||
label: option,
|
||||
}))
|
||||
);
|
||||
|
||||
getClient = (name, clients) => {
|
||||
@@ -203,7 +207,15 @@ class ClientsTable extends Component {
|
||||
accessor: (row) => this.props.normalizedTopClients.configured[row.name] || 0,
|
||||
sortMethod: (a, b) => b - a,
|
||||
minWidth: 120,
|
||||
Cell: CellWrap,
|
||||
Cell: (row) => {
|
||||
const content = CellWrap(row);
|
||||
|
||||
if (!row.value) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return <LogsSearchLink search={row.original.ids[0]}>{content}</LogsSearchLink>;
|
||||
},
|
||||
},
|
||||
{
|
||||
Header: this.props.t('actions_table_header'),
|
||||
@@ -285,23 +297,13 @@ class ClientsTable extends Component {
|
||||
showPagination
|
||||
defaultPageSize={10}
|
||||
minRows={5}
|
||||
showPageSizeOptions={false}
|
||||
showPageJump={false}
|
||||
renderTotalPagesCount={() => false}
|
||||
previousText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-left" />
|
||||
</svg>}
|
||||
nextText={
|
||||
<svg className="icons icon--small icon--gray w-100 h-100">
|
||||
<use xlinkHref="#arrow-right" />
|
||||
</svg>}
|
||||
loadingText={t('loading_table_status')}
|
||||
pageText=''
|
||||
ofText=''
|
||||
ofText="/"
|
||||
previousText={t('previous_btn')}
|
||||
nextText={t('next_btn')}
|
||||
pageText={t('page_table_footer_text')}
|
||||
rowsText={t('rows_table_footer_text')}
|
||||
loadingText={t('loading_table_status')}
|
||||
noDataText={t('clients_not_found')}
|
||||
getPaginationProps={() => ({ className: 'custom-pagination' })}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -311,7 +313,6 @@ class ClientsTable extends Component {
|
||||
>
|
||||
<Trans>client_add</Trans>
|
||||
</button>
|
||||
|
||||
<Modal
|
||||
isModalOpen={isModalOpen}
|
||||
modalType={modalType}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import ReactTable from 'react-table';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../helpers/constants';
|
||||
import { sortIp } from '../../../helpers/helpers';
|
||||
|
||||
class Leases extends Component {
|
||||
cellWrap = ({ value }) => (
|
||||
@@ -27,6 +28,7 @@ class Leases extends Component {
|
||||
Header: 'IP',
|
||||
accessor: 'ip',
|
||||
Cell: this.cellWrap,
|
||||
sortMethod: sortIp,
|
||||
}, {
|
||||
Header: <Trans>dhcp_table_hostname</Trans>,
|
||||
accessor: 'hostname',
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import ReactTable from 'react-table';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { LEASES_TABLE_DEFAULT_PAGE_SIZE } from '../../../../helpers/constants';
|
||||
|
||||
import { sortIp } from '../../../../helpers/helpers';
|
||||
import Modal from './Modal';
|
||||
|
||||
class StaticLeases extends Component {
|
||||
@@ -49,6 +49,7 @@ class StaticLeases extends Component {
|
||||
{
|
||||
Header: 'IP',
|
||||
accessor: 'ip',
|
||||
sortMethod: sortIp,
|
||||
Cell: this.cellWrap,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
@@ -34,13 +34,14 @@ class Dhcp extends Component {
|
||||
} = this.props.dhcp;
|
||||
const otherDhcpFound = check?.otherServer
|
||||
&& check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
|
||||
const filledConfig = Object.keys(config).every((key) => {
|
||||
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||
return true;
|
||||
}
|
||||
const filledConfig = Object.keys(config)
|
||||
.every((key) => {
|
||||
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return config[key];
|
||||
});
|
||||
return config[key];
|
||||
});
|
||||
|
||||
if (config.enabled) {
|
||||
return (
|
||||
@@ -114,40 +115,35 @@ class Dhcp extends Component {
|
||||
|
||||
getStaticIpWarning = (t, check, interfaceName) => {
|
||||
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="text-danger mb-2">
|
||||
<Trans>dhcp_static_ip_error</Trans>
|
||||
<div className="mt-2 mb-2">
|
||||
<Accordion label={t('error_details')}>
|
||||
<span>{check.staticIP.error}</span>
|
||||
</Accordion>
|
||||
</div>
|
||||
return <>
|
||||
<div className="text-danger mb-2">
|
||||
<Trans>dhcp_static_ip_error</Trans>
|
||||
<div className="mt-2 mb-2">
|
||||
<Accordion label={t('error_details')}>
|
||||
<span>{check.staticIP.error}</span>
|
||||
</Accordion>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</Fragment>
|
||||
);
|
||||
} if (
|
||||
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</>;
|
||||
}
|
||||
if (check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||
&& check.staticIP.ip
|
||||
&& interfaceName
|
||||
) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="text-secondary mb-2">
|
||||
<Trans
|
||||
components={[<strong key="0">example</strong>]}
|
||||
values={{
|
||||
interfaceName,
|
||||
ipAddress: check.staticIP.ip,
|
||||
}}
|
||||
>
|
||||
dhcp_dynamic_ip_found
|
||||
</Trans>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</Fragment>
|
||||
);
|
||||
&& interfaceName) {
|
||||
return <>
|
||||
<div className="text-secondary mb-2">
|
||||
<Trans
|
||||
components={[<strong key="0">example</strong>]}
|
||||
values={{
|
||||
interfaceName,
|
||||
ipAddress: check.staticIP.ip,
|
||||
}}
|
||||
>
|
||||
dhcp_dynamic_ip_found
|
||||
</Trans>
|
||||
</div>
|
||||
<hr className="mt-4 mb-4" />
|
||||
</>;
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -163,104 +159,101 @@ class Dhcp extends Component {
|
||||
removeStaticLease,
|
||||
toggleLeaseModal,
|
||||
} = this.props;
|
||||
|
||||
const statusButtonClass = classnames({
|
||||
'btn btn-primary btn-standard': true,
|
||||
'btn btn-primary btn-standard btn-loading': dhcp.processingStatus,
|
||||
});
|
||||
const { enabled, interface_name, ...values } = dhcp.config;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageTitle title={t('dhcp_settings')} />
|
||||
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
||||
{!dhcp.processing && !dhcp.processingInterfaces && (
|
||||
<Fragment>
|
||||
<Card
|
||||
title={t('dhcp_title')}
|
||||
subtitle={t('dhcp_description')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="dhcp">
|
||||
<Fragment>
|
||||
<Form
|
||||
onSubmit={this.handleFormSubmit}
|
||||
initialValues={{
|
||||
interface_name,
|
||||
...values,
|
||||
}}
|
||||
interfaces={dhcp.interfaces}
|
||||
processingConfig={dhcp.processingConfig}
|
||||
processingInterfaces={dhcp.processingInterfaces}
|
||||
enabled={enabled}
|
||||
resetDhcp={resetDhcp}
|
||||
/>
|
||||
<hr />
|
||||
<div className="card-actions mb-3">
|
||||
{this.getToggleDhcpButton()}
|
||||
<button
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={() => findActiveDhcp(interface_name)}
|
||||
disabled={
|
||||
enabled || !interface_name || dhcp.processingConfig
|
||||
}
|
||||
>
|
||||
<Trans>check_dhcp_servers</Trans>
|
||||
</button>
|
||||
</div>
|
||||
{!enabled && dhcp.check && (
|
||||
<Fragment>
|
||||
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
||||
{this.getActiveDhcpMessage(t, dhcp.check)}
|
||||
{this.getDhcpWarning(dhcp.check)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
return <>
|
||||
<PageTitle title={t('dhcp_settings')} />
|
||||
{(dhcp.processing || dhcp.processingInterfaces) && <Loading />}
|
||||
{!dhcp.processing && !dhcp.processingInterfaces && <>
|
||||
<Card
|
||||
title={t('dhcp_title')}
|
||||
subtitle={t('dhcp_description')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="dhcp">
|
||||
<>
|
||||
<Form
|
||||
onSubmit={this.handleFormSubmit}
|
||||
initialValues={{
|
||||
interface_name,
|
||||
...values,
|
||||
}}
|
||||
interfaces={dhcp.interfaces}
|
||||
processingConfig={dhcp.processingConfig}
|
||||
processingInterfaces={dhcp.processingInterfaces}
|
||||
enabled={enabled}
|
||||
resetDhcp={resetDhcp}
|
||||
/>
|
||||
<hr />
|
||||
<div className="card-actions mb-3">
|
||||
{this.getToggleDhcpButton()}
|
||||
<button
|
||||
type="button"
|
||||
className={statusButtonClass}
|
||||
onClick={() => findActiveDhcp(interface_name)}
|
||||
disabled={
|
||||
enabled || !interface_name || dhcp.processingConfig
|
||||
}
|
||||
>
|
||||
<Trans>check_dhcp_servers</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
{dhcp.config.enabled && (
|
||||
<Card
|
||||
title={t('dhcp_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Leases leases={dhcp.leases} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
<Card
|
||||
title={t('dhcp_static_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<StaticLeases
|
||||
staticLeases={dhcp.staticLeases}
|
||||
isModalOpen={dhcp.isModalOpen}
|
||||
addStaticLease={addStaticLease}
|
||||
removeStaticLease={removeStaticLease}
|
||||
toggleLeaseModal={toggleLeaseModal}
|
||||
processingAdding={dhcp.processingAdding}
|
||||
processingDeleting={dhcp.processingDeleting}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard mt-3"
|
||||
onClick={() => toggleLeaseModal()}
|
||||
>
|
||||
<Trans>dhcp_add_static_lease</Trans>
|
||||
</button>
|
||||
</div>
|
||||
{!enabled && dhcp.check && (
|
||||
<>
|
||||
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
||||
{this.getActiveDhcpMessage(t, dhcp.check)}
|
||||
{this.getDhcpWarning(dhcp.check)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</Card>
|
||||
{dhcp.config.enabled && (
|
||||
<Card
|
||||
title={t('dhcp_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Leases leases={dhcp.leases} />
|
||||
</div>
|
||||
</Card>
|
||||
</Fragment>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
<Card
|
||||
title={t('dhcp_static_leases')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<StaticLeases
|
||||
staticLeases={dhcp.staticLeases}
|
||||
isModalOpen={dhcp.isModalOpen}
|
||||
addStaticLease={addStaticLease}
|
||||
removeStaticLease={removeStaticLease}
|
||||
toggleLeaseModal={toggleLeaseModal}
|
||||
processingAdding={dhcp.processingAdding}
|
||||
processingDeleting={dhcp.processingDeleting}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success btn-standard mt-3"
|
||||
onClick={() => toggleLeaseModal()}
|
||||
>
|
||||
<Trans>dhcp_add_static_lease</Trans>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</>}
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import Card from '../../../ui/Card';
|
||||
import { setAccessList } from '../../../../actions/access';
|
||||
|
||||
class Access extends Component {
|
||||
handleFormSubmit = (values) => {
|
||||
this.props.setAccessList(values);
|
||||
const Access = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
processing,
|
||||
processingSet,
|
||||
...values
|
||||
} = useSelector((state) => state.access, shallowEqual);
|
||||
|
||||
const handleFormSubmit = (values) => {
|
||||
dispatch(setAccessList(values));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, access } = this.props;
|
||||
|
||||
const { processing, processingSet, ...values } = access;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('access_title')}
|
||||
subtitle={t('access_desc')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={values}
|
||||
onSubmit={this.handleFormSubmit}
|
||||
processingSet={processingSet}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Access.propTypes = {
|
||||
access: PropTypes.object.isRequired,
|
||||
setAccessList: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
return (
|
||||
<Card
|
||||
title={t('access_title')}
|
||||
subtitle={t('access_desc')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<Form
|
||||
initialValues={values}
|
||||
onSubmit={handleFormSubmit}
|
||||
processingSet={processingSet}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation()(Access);
|
||||
export default Access;
|
||||
|
||||
@@ -53,7 +53,7 @@ const Form = ({
|
||||
{INPUTS_FIELDS.map(({
|
||||
name, title, description, placeholder, validate, max,
|
||||
}) => <div className="col-12" key={name}>
|
||||
<div className="col-7 p-0">
|
||||
<div className="col-12 col-md-7 p-0">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor={name}
|
||||
className="form__label form__label--with-desc">{t(title)}</label>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import flow from 'lodash/flow';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { Field, reduxForm } from 'redux-form';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import {
|
||||
renderInputField,
|
||||
renderRadioField,
|
||||
@@ -18,32 +17,36 @@ import {
|
||||
} from '../../../../helpers/validators';
|
||||
import { BLOCKING_MODES, FORM_NAME } from '../../../../helpers/constants';
|
||||
|
||||
const checkboxes = [{
|
||||
name: 'edns_cs_enabled',
|
||||
placeholder: 'edns_enable',
|
||||
subtitle: 'edns_cs_desc',
|
||||
},
|
||||
{
|
||||
name: 'dnssec_enabled',
|
||||
placeholder: 'dnssec_enable',
|
||||
subtitle: 'dnssec_enable_desc',
|
||||
},
|
||||
{
|
||||
name: 'disable_ipv6',
|
||||
placeholder: 'disable_ipv6',
|
||||
subtitle: 'disable_ipv6_desc',
|
||||
}];
|
||||
const checkboxes = [
|
||||
{
|
||||
name: 'edns_cs_enabled',
|
||||
placeholder: 'edns_enable',
|
||||
subtitle: 'edns_cs_desc',
|
||||
},
|
||||
{
|
||||
name: 'dnssec_enabled',
|
||||
placeholder: 'dnssec_enable',
|
||||
subtitle: 'dnssec_enable_desc',
|
||||
},
|
||||
{
|
||||
name: 'disable_ipv6',
|
||||
placeholder: 'disable_ipv6',
|
||||
subtitle: 'disable_ipv6_desc',
|
||||
},
|
||||
];
|
||||
|
||||
const customIps = [{
|
||||
description: 'blocking_ipv4_desc',
|
||||
name: 'blocking_ipv4',
|
||||
validateIp: validateIpv4,
|
||||
},
|
||||
{
|
||||
description: 'blocking_ipv6_desc',
|
||||
name: 'blocking_ipv6',
|
||||
validateIp: validateIpv6,
|
||||
}];
|
||||
const customIps = [
|
||||
{
|
||||
description: 'blocking_ipv4_desc',
|
||||
name: 'blocking_ipv4',
|
||||
validateIp: validateIpv4,
|
||||
},
|
||||
{
|
||||
description: 'blocking_ipv6_desc',
|
||||
name: 'blocking_ipv6',
|
||||
validateIp: validateIpv6,
|
||||
},
|
||||
];
|
||||
|
||||
const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||
.map((mode) => (
|
||||
@@ -58,114 +61,107 @@ const getFields = (processing, t) => Object.values(BLOCKING_MODES)
|
||||
/>
|
||||
));
|
||||
|
||||
let Form = ({
|
||||
handleSubmit, submitting, invalid, processing, blockingMode, t,
|
||||
}) => <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor="ratelimit"
|
||||
className="form__label form__label--with-desc">
|
||||
<Trans>rate_limit</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>rate_limit_desc</Trans>
|
||||
</div>
|
||||
<Field
|
||||
name="ratelimit"
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
placeholder={t('form_enter_rate_limit')}
|
||||
normalize={toNumber}
|
||||
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
name={name}
|
||||
type="checkbox"
|
||||
component={renderSelectField}
|
||||
placeholder={t(placeholder)}
|
||||
disabled={processing}
|
||||
subtitle={t(subtitle)}
|
||||
/>
|
||||
</div>
|
||||
</div>)}
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings mb-4">
|
||||
<label className="form__label form__label--with-desc">
|
||||
<Trans>blocking_mode</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
{Object.values(BLOCKING_MODES)
|
||||
.map((mode) => (
|
||||
<li key={mode}>
|
||||
<Trans>{`blocking_mode_${mode}`}</Trans>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
<div className="custom-controls-stacked">
|
||||
{getFields(processing, t)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{blockingMode === BLOCKING_MODES.custom_ip && (
|
||||
<Fragment>
|
||||
{customIps.map(({
|
||||
description,
|
||||
name,
|
||||
validateIp,
|
||||
}) => <div className="col-12 col-sm-6" key={name}>
|
||||
<div className="form__group form__group--settings">
|
||||
<label className="form__label form__label--with-desc"
|
||||
htmlFor={name}><Trans>{name}</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>{description}</Trans>
|
||||
</div>
|
||||
<Field
|
||||
name={name}
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
placeholder={t('form_enter_ip')}
|
||||
validate={[validateIp, validateRequiredValue]}
|
||||
/>
|
||||
const Form = ({
|
||||
handleSubmit, submitting, invalid, processing,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
blocking_mode,
|
||||
} = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual);
|
||||
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6">
|
||||
<div className="form__group form__group--settings">
|
||||
<label htmlFor="ratelimit"
|
||||
className="form__label form__label--with-desc">
|
||||
<Trans>rate_limit</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>rate_limit_desc</Trans>
|
||||
</div>
|
||||
</div>)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
disabled={submitting || invalid || processing}
|
||||
>
|
||||
<Trans>save_btn</Trans>
|
||||
</button>
|
||||
</form>;
|
||||
<Field
|
||||
name="ratelimit"
|
||||
type="number"
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
placeholder={t('form_enter_rate_limit')}
|
||||
normalize={toNumber}
|
||||
validate={[validateRequiredValue, validateBiggerOrEqualZeroValue]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{checkboxes.map(({ name, placeholder, subtitle }) => <div className="col-12" key={name}>
|
||||
<div className="form__group form__group--settings">
|
||||
<Field
|
||||
name={name}
|
||||
type="checkbox"
|
||||
component={renderSelectField}
|
||||
placeholder={t(placeholder)}
|
||||
disabled={processing}
|
||||
subtitle={t(subtitle)}
|
||||
/>
|
||||
</div>
|
||||
</div>)}
|
||||
<div className="col-12">
|
||||
<div className="form__group form__group--settings mb-4">
|
||||
<label className="form__label form__label--with-desc">
|
||||
<Trans>blocking_mode</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
{Object.values(BLOCKING_MODES)
|
||||
.map((mode) => (
|
||||
<li key={mode}>
|
||||
<Trans>{`blocking_mode_${mode}`}</Trans>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
<div className="custom-controls-stacked">
|
||||
{getFields(processing, t)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{blocking_mode === BLOCKING_MODES.custom_ip && (
|
||||
<>
|
||||
{customIps.map(({
|
||||
description,
|
||||
name,
|
||||
validateIp,
|
||||
}) => <div className="col-12 col-sm-6" key={name}>
|
||||
<div className="form__group form__group--settings">
|
||||
<label className="form__label form__label--with-desc"
|
||||
htmlFor={name}><Trans>{name}</Trans>
|
||||
</label>
|
||||
<div className="form__desc form__desc--top">
|
||||
<Trans>{description}</Trans>
|
||||
</div>
|
||||
<Field
|
||||
name={name}
|
||||
component={renderInputField}
|
||||
className="form-control"
|
||||
placeholder={t('form_enter_ip')}
|
||||
validate={[validateIp, validateRequiredValue]}
|
||||
/>
|
||||
</div>
|
||||
</div>)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-success btn-standard btn-large"
|
||||
disabled={submitting || invalid || processing}
|
||||
>
|
||||
<Trans>save_btn</Trans>
|
||||
</button>
|
||||
</form>;
|
||||
};
|
||||
|
||||
Form.propTypes = {
|
||||
blockingMode: PropTypes.string.isRequired,
|
||||
handleSubmit: PropTypes.func.isRequired,
|
||||
submitting: PropTypes.bool.isRequired,
|
||||
invalid: PropTypes.bool.isRequired,
|
||||
processing: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const selector = formValueSelector(FORM_NAME.BLOCKING_MODE);
|
||||
|
||||
Form = connect((state) => {
|
||||
const blockingMode = selector(state, 'blocking_mode');
|
||||
return {
|
||||
blockingMode,
|
||||
};
|
||||
})(Form);
|
||||
|
||||
export default flow([
|
||||
withTranslation(),
|
||||
reduxForm({ form: FORM_NAME.BLOCKING_MODE }),
|
||||
])(Form);
|
||||
export default reduxForm({ form: FORM_NAME.BLOCKING_MODE })(Form);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Card from '../../../ui/Card';
|
||||
import Form from './Form';
|
||||
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||
|
||||
const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
const handleFormSubmit = (values) => {
|
||||
setDnsConfig(values);
|
||||
};
|
||||
|
||||
const Config = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
blocking_mode,
|
||||
ratelimit,
|
||||
@@ -19,7 +17,11 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
dnssec_enabled,
|
||||
disable_ipv6,
|
||||
processingSetConfig,
|
||||
} = dnsConfig;
|
||||
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||
|
||||
const handleFormSubmit = (values) => {
|
||||
dispatch(setDnsConfig(values));
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -46,10 +48,4 @@ const Config = ({ t, dnsConfig, setDnsConfig }) => {
|
||||
);
|
||||
};
|
||||
|
||||
Config.propTypes = {
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
setDnsConfig: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(Config);
|
||||
export default Config;
|
||||
|
||||
@@ -6,9 +6,10 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Examples from './Examples';
|
||||
import { renderRadioField } from '../../../../helpers/form';
|
||||
import { renderRadioField, renderTextareaField } from '../../../../helpers/form';
|
||||
import { DNS_REQUEST_OPTIONS, FORM_NAME } from '../../../../helpers/constants';
|
||||
import { testUpstream } from '../../../../actions';
|
||||
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||
|
||||
const getInputFields = () => [{
|
||||
// eslint-disable-next-line react/display-name
|
||||
@@ -17,9 +18,10 @@ const getInputFields = () => [{
|
||||
</label>,
|
||||
name: 'upstream_dns',
|
||||
type: 'text',
|
||||
component: 'textarea',
|
||||
component: renderTextareaField,
|
||||
className: 'form-control form-control--textarea font-monospace',
|
||||
placeholder: 'upstream_dns',
|
||||
normalizeOnBlur: removeEmptyLines,
|
||||
},
|
||||
{
|
||||
name: 'upstream_mode',
|
||||
@@ -50,10 +52,11 @@ const Form = ({
|
||||
submitting, invalid, processingSetConfig, processingTestUpstream, handleSubmit,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const upstream_dns = useSelector((store) => store.form[FORM_NAME.UPSTREAM].values.upstream_dns);
|
||||
const bootstrap_dns = useSelector((store) => store.form[FORM_NAME.UPSTREAM]
|
||||
.values.bootstrap_dns);
|
||||
const bootstrap_dns = useSelector(
|
||||
(store) => store.form[FORM_NAME.UPSTREAM].values.bootstrap_dns,
|
||||
);
|
||||
|
||||
const handleUpstreamTest = () => dispatch(testUpstream({
|
||||
upstream_dns,
|
||||
@@ -69,7 +72,8 @@ const Form = ({
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
{INPUT_FIELDS.map(({
|
||||
name, component, type, className, placeholder, getTitle, subtitle, disabled, value,
|
||||
name, component, type, className, placeholder,
|
||||
getTitle, subtitle, disabled, value, normalizeOnBlur,
|
||||
}) => <div className="col-12 mb-4" key={placeholder}>
|
||||
{typeof getTitle === 'function' && getTitle()}
|
||||
<Field
|
||||
@@ -82,6 +86,7 @@ const Form = ({
|
||||
placeholder={t(placeholder)}
|
||||
subtitle={t(subtitle)}
|
||||
disabled={processingSetConfig || processingTestUpstream || disabled}
|
||||
normalizeOnBlur={normalizeOnBlur}
|
||||
/>
|
||||
</div>)}
|
||||
<div className="col-12">
|
||||
@@ -101,11 +106,12 @@ const Form = ({
|
||||
<Field
|
||||
id="bootstrap_dns"
|
||||
name="bootstrap_dns"
|
||||
component="textarea"
|
||||
component={renderTextareaField}
|
||||
type="text"
|
||||
className="form-control form-control--textarea form-control--textarea-small font-monospace"
|
||||
placeholder={t('bootstrap_dns')}
|
||||
disabled={processingSetConfig}
|
||||
normalizeOnBlur={removeEmptyLines}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,56 +1,46 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import Form from './Form';
|
||||
import Card from '../../../ui/Card';
|
||||
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||
|
||||
const Upstream = (props) => {
|
||||
const [t] = useTranslation();
|
||||
const Upstream = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
upstream_mode,
|
||||
processingSetConfig,
|
||||
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||
|
||||
const { processingTestUpstream } = useSelector((state) => state.settings, shallowEqual);
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
dispatch(setDnsConfig(values));
|
||||
};
|
||||
|
||||
const {
|
||||
processingTestUpstream,
|
||||
dnsConfig: {
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
processingSetConfig,
|
||||
upstream_mode,
|
||||
},
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('upstream_dns')}
|
||||
subtitle={t('upstream_dns_hint')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Form
|
||||
initialValues={{
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
upstream_mode,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
processingTestUpstream={processingTestUpstream}
|
||||
processingSetConfig={processingSetConfig}
|
||||
/>
|
||||
</div>
|
||||
return <Card
|
||||
title={t('upstream_dns')}
|
||||
subtitle={t('upstream_dns_hint')}
|
||||
bodyType="card-body box-body--settings"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<Form
|
||||
initialValues={{
|
||||
upstream_dns,
|
||||
bootstrap_dns,
|
||||
upstream_mode,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
processingTestUpstream={processingTestUpstream}
|
||||
processingSetConfig={processingSetConfig}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
Upstream.propTypes = {
|
||||
processingTestUpstream: PropTypes.bool.isRequired,
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
</div>
|
||||
</Card>;
|
||||
};
|
||||
|
||||
export default Upstream;
|
||||
|
||||
@@ -1,64 +1,40 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Upstream from './Upstream';
|
||||
import Access from './Access';
|
||||
import Config from './Config';
|
||||
import PageTitle from '../../ui/PageTitle';
|
||||
import Loading from '../../ui/Loading';
|
||||
import CacheConfig from './Cache';
|
||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||
import { getAccessList } from '../../../actions/access';
|
||||
|
||||
const Dns = (props) => {
|
||||
const Dns = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const processing = useSelector((state) => state.access.processing);
|
||||
const processingGetConfig = useSelector((state) => state.dnsConfig.processingGetConfig);
|
||||
|
||||
const isDataLoading = processing || processingGetConfig;
|
||||
|
||||
useEffect(() => {
|
||||
props.getAccessList();
|
||||
props.getDnsConfig();
|
||||
dispatch(getAccessList());
|
||||
dispatch(getDnsConfig());
|
||||
}, []);
|
||||
|
||||
const {
|
||||
settings,
|
||||
access,
|
||||
setAccessList,
|
||||
dnsConfig,
|
||||
setDnsConfig,
|
||||
} = props;
|
||||
|
||||
const isDataLoading = access.processing || dnsConfig.processingGetConfig;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title={t('dns_settings')} />
|
||||
{isDataLoading
|
||||
? <Loading />
|
||||
: <>
|
||||
<Upstream
|
||||
processingTestUpstream={settings.processingTestUpstream}
|
||||
dnsConfig={dnsConfig}
|
||||
/>
|
||||
<Config
|
||||
dnsConfig={dnsConfig}
|
||||
setDnsConfig={setDnsConfig}
|
||||
/>
|
||||
<CacheConfig
|
||||
dnsConfig={dnsConfig}
|
||||
setDnsConfig={setDnsConfig}
|
||||
/>
|
||||
<Access access={access} setAccessList={setAccessList} />
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Dns.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
getAccessList: PropTypes.func.isRequired,
|
||||
setAccessList: PropTypes.func.isRequired,
|
||||
access: PropTypes.object.isRequired,
|
||||
dnsConfig: PropTypes.object.isRequired,
|
||||
setDnsConfig: PropTypes.func.isRequired,
|
||||
getDnsConfig: PropTypes.func.isRequired,
|
||||
return <>
|
||||
<PageTitle title={t('dns_settings')} />
|
||||
{isDataLoading
|
||||
? <Loading />
|
||||
: <>
|
||||
<Upstream />
|
||||
<Config />
|
||||
<CacheConfig />
|
||||
<Access />
|
||||
</>}
|
||||
</>;
|
||||
};
|
||||
|
||||
export default Dns;
|
||||
|
||||
@@ -10,53 +10,50 @@ import Checkbox from '../ui/Checkbox';
|
||||
import Loading from '../ui/Loading';
|
||||
import PageTitle from '../ui/PageTitle';
|
||||
import Card from '../ui/Card';
|
||||
|
||||
import { getObjectKeysSorted } from '../../helpers/helpers';
|
||||
import './Settings.css';
|
||||
|
||||
class Settings extends Component {
|
||||
settings = {
|
||||
safebrowsing: {
|
||||
enabled: false,
|
||||
title: 'use_adguard_browsing_sec',
|
||||
subtitle: 'use_adguard_browsing_sec_hint',
|
||||
},
|
||||
parental: {
|
||||
enabled: false,
|
||||
title: 'use_adguard_parental',
|
||||
subtitle: 'use_adguard_parental_hint',
|
||||
},
|
||||
safesearch: {
|
||||
enabled: false,
|
||||
title: 'enforce_safe_search',
|
||||
subtitle: 'enforce_save_search_hint',
|
||||
},
|
||||
};
|
||||
const ORDER_KEY = 'order';
|
||||
|
||||
const SETTINGS = {
|
||||
safebrowsing: {
|
||||
enabled: false,
|
||||
title: 'use_adguard_browsing_sec',
|
||||
subtitle: 'use_adguard_browsing_sec_hint',
|
||||
[ORDER_KEY]: 0,
|
||||
},
|
||||
parental: {
|
||||
enabled: false,
|
||||
title: 'use_adguard_parental',
|
||||
subtitle: 'use_adguard_parental_hint',
|
||||
[ORDER_KEY]: 1,
|
||||
},
|
||||
safesearch: {
|
||||
enabled: false,
|
||||
title: 'enforce_safe_search',
|
||||
subtitle: 'enforce_save_search_hint',
|
||||
[ORDER_KEY]: 2,
|
||||
},
|
||||
};
|
||||
|
||||
class Settings extends Component {
|
||||
componentDidMount() {
|
||||
this.props.initSettings(this.settings);
|
||||
this.props.initSettings(SETTINGS);
|
||||
this.props.getStatsConfig();
|
||||
this.props.getLogsConfig();
|
||||
this.props.getFilteringStatus();
|
||||
}
|
||||
|
||||
renderSettings = (settings) => {
|
||||
const settingsKeys = Object.keys(settings);
|
||||
|
||||
if (settingsKeys.length > 0) {
|
||||
return settingsKeys.map((key) => {
|
||||
const setting = settings[key];
|
||||
const { enabled } = setting;
|
||||
return (
|
||||
<Checkbox
|
||||
{...settings[key]}
|
||||
key={key}
|
||||
handleChange={() => this.props.toggleSetting(key, enabled)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
return '';
|
||||
};
|
||||
renderSettings = (settings) => getObjectKeysSorted(settings, ORDER_KEY)
|
||||
.map((key) => {
|
||||
const setting = settings[key];
|
||||
const { enabled } = setting;
|
||||
return <Checkbox
|
||||
{...setting}
|
||||
key={key}
|
||||
handleChange={() => this.props.toggleSetting(key, enabled)}
|
||||
/>;
|
||||
});
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -13,3 +13,17 @@
|
||||
margin-bottom: 20px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.guide__address {
|
||||
display: block;
|
||||
margin-bottom: 7px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.guide__address {
|
||||
display: list-item;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ const SetupGuide = ({
|
||||
<div className="mt-1">
|
||||
<Trans>install_devices_address</Trans>:
|
||||
</div>
|
||||
<div className="mt-2 font-weight-bold">
|
||||
{dnsAddresses.map((ip) => <li key={ip}>{ip}</li>)}
|
||||
<div className="mt-3">
|
||||
{dnsAddresses.map((ip) => <li key={ip} className="guide__address">{ip}</li>)}
|
||||
</div>
|
||||
</div>
|
||||
<Guide dnsAddresses={dnsAddresses} />
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { Trans } from 'react-i18next';
|
||||
import isAfter from 'date-fns/is_after';
|
||||
import addDays from 'date-fns/add_days';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import Topline from './Topline';
|
||||
import { EMPTY_DATE } from '../../helpers/constants';
|
||||
|
||||
const EncryptionTopline = (props) => {
|
||||
if (props.notAfter === EMPTY_DATE) {
|
||||
return false;
|
||||
const EncryptionTopline = () => {
|
||||
const not_after = useSelector((state) => state.encryption.not_after);
|
||||
|
||||
if (not_after === EMPTY_DATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), props.notAfter);
|
||||
const isExpired = isAfter(Date.now(), props.notAfter);
|
||||
const isAboutExpire = isAfter(addDays(Date.now(), 30), not_after);
|
||||
const isExpired = isAfter(Date.now(), not_after);
|
||||
|
||||
if (isExpired) {
|
||||
return (
|
||||
@@ -23,7 +24,9 @@ const EncryptionTopline = (props) => {
|
||||
</Trans>
|
||||
</Topline>
|
||||
);
|
||||
} if (isAboutExpire) {
|
||||
}
|
||||
|
||||
if (isAboutExpire) {
|
||||
return (
|
||||
<Topline type="warning">
|
||||
<Trans components={[<a href="#encryption" key="0">link</a>]}>
|
||||
@@ -36,8 +39,4 @@ const EncryptionTopline = (props) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
EncryptionTopline.propTypes = {
|
||||
notAfter: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default withTranslation()(EncryptionTopline);
|
||||
export default EncryptionTopline;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
|
||||
@@ -28,7 +27,7 @@ const linksData = [
|
||||
},
|
||||
];
|
||||
|
||||
const Footer = (props) => {
|
||||
const Footer = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getYear = () => {
|
||||
@@ -59,11 +58,6 @@ const Footer = (props) => {
|
||||
{t(name)}
|
||||
</a>);
|
||||
|
||||
|
||||
const {
|
||||
dnsVersion, processingVersion, getVersion, checkUpdateFlag,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<footer className="footer">
|
||||
@@ -94,12 +88,7 @@ const Footer = (props) => {
|
||||
<div className="footer__row">
|
||||
{renderCopyright()}
|
||||
<div className="footer__column footer__column--language">
|
||||
<Version
|
||||
dnsVersion={dnsVersion}
|
||||
processingVersion={processingVersion}
|
||||
getVersion={getVersion}
|
||||
checkUpdateFlag={checkUpdateFlag}
|
||||
/>
|
||||
<Version />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,11 +97,4 @@ const Footer = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
Footer.propTypes = {
|
||||
dnsVersion: PropTypes.string,
|
||||
processingVersion: PropTypes.bool,
|
||||
getVersion: PropTypes.func,
|
||||
checkUpdateFlag: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import './Loading.css';
|
||||
|
||||
const Loading = () => (
|
||||
<div className="loading" />
|
||||
const Loading = ({ className }) => (
|
||||
<div className={classNames('loading', className)} />
|
||||
);
|
||||
|
||||
Loading.propTypes = {
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Loading;
|
||||
|
||||
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;
|
||||
@@ -6,18 +6,24 @@
|
||||
.page-header--logs {
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
margin: 2rem 0 3rem;
|
||||
margin: 2rem 0 2.8rem;
|
||||
}
|
||||
|
||||
.page-header--logs .page-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.page-header--logs {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
margin: 1.1rem 0;
|
||||
}
|
||||
|
||||
.page-header--logs .page-title {
|
||||
padding-bottom: 2.5rem;;
|
||||
margin-bottom: 1.1rem;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user