Compare commits

...

5 Commits

Author SHA1 Message Date
Ainar Garipov
a21558f418 all: sync with master; upd chlog 2023-03-09 15:39:35 +03:00
Ainar Garipov
4f928be393 bamboo-specs: do not require make where not needed 2023-02-21 15:12:18 +03:00
Ainar Garipov
f543b47261 dnsforward: fix panic; take Host into account 2023-02-21 14:55:10 +03:00
Ainar Garipov
66b831072c all: sync with master; upd chlog 2023-02-15 16:53:29 +03:00
Ainar Garipov
80eb339896 all: sync with master; upd chlog 2023-02-01 15:41:34 +03:00
427 changed files with 6282 additions and 25120 deletions

View File

@@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.18.9'
'GO_VERSION': '1.19.7'
'NODE_VERSION': '14'
'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.18.9'
'GO_VERSION': '1.19.7'
'on':
'push':

2
.gitignore vendored
View File

@@ -12,7 +12,6 @@
/agh-backup/
/bin/
/build/*
/build2/*
/data/
/dist/
/filtering/tests/filtering.TestLotsOfRules*.pprof
@@ -26,4 +25,3 @@ leases.db
node_modules/
!/build/gitkeep
!/build2/gitkeep

View File

@@ -14,17 +14,237 @@ and this project adheres to
<!--
## [v0.108.0] - TBA
## [v0.107.23] - 2023-02-15 (APPROX.)
## [v0.107.27] - 2023-03-29 (APPROX.)
See also the [v0.107.23 GitHub milestone][ms-v0.107.23].
See also the [v0.107.27 GitHub milestone][ms-v0.107.27].
[ms-v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/milestone/59?closed=1
[ms-v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/milestone/63?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
## [v0.107.26] - 2023-03-09
See also the [v0.107.26 GitHub milestone][ms-v0.107.26].
### Security
- Go version has been updated to prevent the possibility of exploiting the
CVE-2023-24532 Go vulnerability fixed in [Go 1.19.7][go-1.19.7].
### Added
- The ability to set custom IP for EDNS Client Subnet by using the new
`dns.edns_client_subnet.use_custom` and `dns.edns_client_subnet.custom_ip`
fields ([#1472]). The UI changes are coming in the upcoming releases.
- The ability to use `dnstype` rules in the disallowed domains list ([#5468]).
This allows dropping requests based on their question types.
### Changed
#### Configuration Changes
In this release, the schema version has changed from 16 to 17.
- Property `edns_client_subnet`, which in schema versions 16 and earlier used
to be a part of the `dns` object, is now part of the `dns.edns_client_subnet`
object:
```yaml
# BEFORE:
'dns':
# …
'edns_client_subnet': false
# AFTER:
'dns':
# …
'edns_client_subnet':
'enabled': false
'use_custom': false
'custom_ip': ''
```
To rollback this change, move the value of `dns.edns_client_subnet.enabled`
into the `dns.edns_client_subnet`, remove the fields
`dns.edns_client_subnet.enabled`, `dns.edns_client_subnet.use_custom`,
`dns.edns_client_subnet.custom_ip`, and change the `schema_version` back to
`16`.
### Fixed
- Obsolete value of the Interface MTU DHCP option is now omitted ([#5281]).
- Various dark theme bugs ([#5439], [#5441], [#5442], [#5515]).
- Automatic update on MIPS64 and little-endian 32-bit MIPS architectures
([#5270], [#5373]).
- Requirements to domain names in domain-specific upstream configurations have
been relaxed to meet those from [RFC 3696][rfc3696] ([#4884]).
- Failing service installation via script on FreeBSD ([#5431]).
[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472
[#4884]: https://github.com/AdguardTeam/AdGuardHome/issues/4884
[#5270]: https://github.com/AdguardTeam/AdGuardHome/issues/5270
[#5281]: https://github.com/AdguardTeam/AdGuardHome/issues/5281
[#5373]: https://github.com/AdguardTeam/AdGuardHome/issues/5373
[#5431]: https://github.com/AdguardTeam/AdGuardHome/issues/5431
[#5439]: https://github.com/AdguardTeam/AdGuardHome/issues/5439
[#5441]: https://github.com/AdguardTeam/AdGuardHome/issues/5441
[#5442]: https://github.com/AdguardTeam/AdGuardHome/issues/5442
[#5468]: https://github.com/AdguardTeam/AdGuardHome/issues/5468
[#5515]: https://github.com/AdguardTeam/AdGuardHome/issues/5515
[go-1.19.7]: https://groups.google.com/g/golang-announce/c/3-TpUx48iQY
[ms-v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/milestone/62?closed=1
[rfc3696]: https://datatracker.ietf.org/doc/html/rfc3696
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.25] - 2023-02-21
See also the [v0.107.25 GitHub milestone][ms-v0.107.25].
### Fixed
- Panic when using unencrypted DNS-over-HTTPS ([#5518]).
[#5518]: https://github.com/AdguardTeam/AdGuardHome/issues/5518
[ms-v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/milestone/61?closed=1
## [v0.107.24] - 2023-02-15
See also the [v0.107.24 GitHub milestone][ms-v0.107.24].
### Security
- Go version has been updated, both because Go 1.18 has reached end of life an
to prevent the possibility of exploiting the Go vulnerabilities fixed in [Go
1.19.6][go-1.19.6].
### Added
- The ability to disable statistics by using the new `statistics.enabled`
field. Previously it was necessary to set the `statistics_interval` to 0,
losing the previous value ([#1717], [#4299]).
- The ability to exclude domain names from the query log or statistics by using
the new `querylog.ignored` or `statistics.ignored` fields ([#1717], [#4299]).
The UI changes are coming in the upcoming releases.
### Changed
#### Configuration Changes
In this release, the schema version has changed from 14 to 16.
- Property `statistics_interval`, which in schema versions 15 and earlier used
to be a part of the `dns` object, is now a part of the `statistics` object:
```yaml
# BEFORE:
'dns':
# …
'statistics_interval': 1
# AFTER:
'statistics':
# …
'interval': 1
```
To rollback this change, move the property back into the `dns` object and
change the `schema_version` back to `15`.
- The fields `dns.querylog_enabled`, `dns.querylog_file_enabled`,
`dns.querylog_interval`, and `dns.querylog_size_memory` have been moved to the
new `querylog` object.
```yaml
# BEFORE:
'dns':
'querylog_enabled': true
'querylog_file_enabled': true
'querylog_interval': '2160h'
'querylog_size_memory': 1000
# AFTER:
'querylog':
'enabled': true
'file_enabled': true
'interval': '2160h'
'size_memory': 1000
'ignored': []
```
To rollback this change, rename and move properties back into the `dns`
object, remove `querylog` object and `querylog.ignored` property, and change
the `schema_version` back to `14`.
### Deprecated
- Go 1.19 support. Future versions will require at least Go 1.20 to build.
### Fixed
- Setting the AD (Authenticated Data) flag on responses that have the DO (DNSSEC
OK) flag set but not the AD flag ([#5479]).
- Client names resolved via reverse DNS not being updated ([#4939]).
- The icon for League Of Legends on the Blocked services page ([#5433]).
### Removed
- Go 1.18 support, as it has reached end of life.
[#1717]: https://github.com/AdguardTeam/AdGuardHome/issues/1717
[#4299]: https://github.com/AdguardTeam/AdGuardHome/issues/4299
[#4939]: https://github.com/AdguardTeam/AdGuardHome/issues/4939
[#5433]: https://github.com/AdguardTeam/AdGuardHome/issues/5433
[#5479]: https://github.com/AdguardTeam/AdGuardHome/issues/5479
[go-1.19.6]: https://groups.google.com/g/golang-announce/c/V0aBFqaFs_E
[ms-v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/milestone/60?closed=1
## [v0.107.23] - 2023-02-01
See also the [v0.107.23 GitHub milestone][ms-v0.107.23].
### Added
- DNS64 support ([#5117]). The function may be enabled with new `use_dns64`
field under `dns` object in the configuration along with `dns64_prefixes`, the
set of exclusion prefixes to filter AAAA responses. The Well-Known Prefix
(`64:ff9b::/96`) is used if no custom prefixes are specified.
### Fixed
- Filtering rules with `*` as the hostname not working properly ([#5245]).
- Various dark theme bugs ([#5375]).
### Removed
- The “beta frontend” and the corresponding APIs. They never quite worked
properly, and the future new version of AdGuard Home API will probably be
different.
Correspondingly, the configuration parameter `beta_bind_port` has been removed
as well.
[#5117]: https://github.com/AdguardTeam/AdGuardHome/issues/5117
[#5245]: https://github.com/AdguardTeam/AdGuardHome/issues/5245
[#5375]: https://github.com/AdguardTeam/AdGuardHome/issues/5375
[ms-v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/milestone/59?closed=1
## [v0.107.22] - 2023-01-19
See also the [v0.107.22 GitHub milestone][ms-v0.107.22].
@@ -48,7 +268,7 @@ See also the [v0.107.22 GitHub milestone][ms-v0.107.22].
running ([#4223], [#5191]).
- The `--update` flag performing an update even when there is no version change.
- Failing HTTPS redirection on saving the encryption settings ([#4898]).
- Zeroing rules counter of erroneusly edited filtering rule lists ([#5290]).
- Zeroing rules counter of erroneously edited filtering rule lists ([#5290]).
- Filters updating strategy, which could sometimes lead to use of broken or
incompletely downloaded lists ([#5258]).
@@ -61,7 +281,6 @@ See also the [v0.107.22 GitHub milestone][ms-v0.107.22].
## [v0.107.21] - 2022-12-15
See also the [v0.107.21 GitHub milestone][ms-v0.107.21].
@@ -995,7 +1214,6 @@ In this release, the schema version has changed from 10 to 12.
To rollback this change, convert the property back into days and change the
`schema_version` back to `11`.
- Property `rlimit_nofile`, which in schema versions 10 and earlier used to be
on the top level, is now moved to the new `os` object:
@@ -1542,11 +1760,15 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...HEAD
[v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.27...HEAD
[v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...v0.107.27
-->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...HEAD
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...HEAD
[v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...v0.107.26
[v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...v0.107.25
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24
[v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23
[v0.107.22]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.21...v0.107.22
[v0.107.21]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.20...v0.107.21
[v0.107.20]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.19...v0.107.20

View File

@@ -4,18 +4,26 @@
# See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html.
.POSIX:
CHANNEL = development
CLIENT_BETA_DIR = client2
CLIENT_DIR = client
COMMIT = $$( git rev-parse --short HEAD )
DIST_DIR = dist
# Don't name this macro "GO", because GNU Make apparenly makes it an
# exported environment variable with the literal value of "${GO:-go}",
# which is not what we need. Use a dot in the name to make sure that
# users don't have an environment variable with the same name.
# This comment is used to simplify checking local copies of the
# Makefile. Bump this number every time a significant change is made to
# this Makefile.
#
# AdGuard-Project-Version: 2
# Don't name these macros "GO" etc., because GNU Make apparently makes
# them exported environment variables with the literal value of
# "${GO:-go}" and so on, which is not what we need. Use a dot in the
# name to make sure that users don't have an environment variable with
# the same name.
#
# See https://unix.stackexchange.com/q/646255/105635.
GO.MACRO = $${GO:-go}
VERBOSE.MACRO = $${VERBOSE:-0}
CHANNEL = development
CLIENT_DIR = client
COMMIT = $$( git rev-parse --short HEAD )
DIST_DIR = dist
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn
GPG_KEY = devteam@adguard.com
@@ -26,13 +34,8 @@ NPM_INSTALL_FLAGS = $(NPM_FLAGS) --quiet --no-progress --ignore-engines\
--ignore-optional --ignore-platform --ignore-scripts
RACE = 0
SIGN = 1
VERBOSE = 0
VERSION = v0.0.0
YARN = yarn
YARN_FLAGS = --cwd $(CLIENT_BETA_DIR)
YARN_INSTALL_FLAGS = $(YARN_FLAGS) --network-timeout 120000 --silent\
--ignore-engines --ignore-optional --ignore-platform\
--ignore-scripts
# Macros for the build-release target. If FRONTEND_PREBUILT is 0, the
# default, the macro $(BUILD_RELEASE_DEPS_$(FRONTEND_PREBUILT)) expands
@@ -61,13 +64,13 @@ ENV = env\
PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\
RACE='$(RACE)'\
SIGN='$(SIGN)'\
VERBOSE='$(VERBOSE)'\
VERBOSE="$(VERBOSE.MACRO)"\
VERSION='$(VERSION)'\
# Keep the line above blank.
# Keep this target first, so that a naked make invocation triggers
# a full build.
# Keep this target first, so that a naked make invocation triggers a
# full build.
build: deps quick-build
quick-build: js-build go-build
@@ -90,17 +93,13 @@ init: ; git config core.hooksPath ./scripts/hooks
js-build:
$(NPM) $(NPM_FLAGS) run build-prod
$(YARN) $(YARN_FLAGS) build
js-deps:
$(NPM) $(NPM_INSTALL_FLAGS) ci
$(YARN) $(YARN_INSTALL_FLAGS) install
# TODO(a.garipov): Remove the legacy client tasks support once the new
# client is done and the old one is removed.
js-lint: ; $(NPM) $(NPM_FLAGS) run lint
js-test: ; $(NPM) $(NPM_FLAGS) run test
js-beta-lint: ; $(YARN) $(YARN_FLAGS) lint
js-beta-test: ; # TODO(v.abdulmyanov): Add tests for the new client.
go-build: ; $(ENV) "$(SHELL)" ./scripts/make/go-build.sh
go-deps: ; $(ENV) "$(SHELL)" ./scripts/make/go-deps.sh
@@ -125,4 +124,4 @@ go-os-check:
openapi-lint: ; cd ./openapi/ && $(YARN) test
openapi-show: ; cd ./openapi/ && $(YARN) start
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh
txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh

View File

@@ -81,12 +81,24 @@ code.
### <a href="#automated-install-linux-and-mac" id="automated-install-linux-and-mac" name="automated-install-linux-and-mac">Automated install (Unix)</a>
Run the following command in your terminal:
To install with `curl` run the following command:
```sh
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
```
To install with `wget` run the following command:
```sh
wget --no-verbose -O - https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
```
To install with `fetch` run the following command:
```sh
fetch -o - https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
```
The script also accepts some options:
* `-c <channel>` to use specified channel;
@@ -249,7 +261,7 @@ Run `make init` to prepare the development environment.
You will need this to build AdGuard Home:
* [Go](https://golang.org/dl/) v1.18 or later;
* [Go](https://golang.org/dl/) v1.19 or later;
* [Node.js](https://nodejs.org/en/download/) v10.16.2 or later;
* [npm](https://www.npmjs.com/) v6.14 or later;
* [yarn](https://yarnpkg.com/) v1.22.5 or later.

View File

@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:5.4'
'dockerGo': 'adguard/golang-ubuntu:6.2'
'stages':
- 'Build frontend':
@@ -136,8 +136,12 @@
set -e -f -u -x
COMMIT="${bamboo.repository.revision.number}"
export COMMIT
readonly COMMIT
# Explicitly checkout the revision that we need.
git checkout "${bamboo.repository.revision.number}"
git checkout "$COMMIT"
# Install Qemu, create builder.
docker version -f '{{ .Server.Experimental }}'
@@ -157,12 +161,13 @@
docker info
# Prepare and push the build.
make\
env\
CHANNEL="${bamboo.channel}"\
DIST_DIR='dist'\
DOCKER_IMAGE_NAME='adguard/adguardhome'\
DOCKER_OUTPUT="type=image,name=adguard/adguardhome,push=true"\
VERBOSE='1'\
build-docker
sh ./scripts/make/build-docker.sh
'environment':
DOCKER_CLI_EXPERIMENTAL=enabled
'final-tasks':
@@ -223,6 +228,7 @@
channel="${bamboo.channel}"
readonly channel
case "$channel"
in
('release')
@@ -269,8 +275,10 @@
set -e -f -u -x
export CHANNEL="${bamboo.channel}"
if [ "$CHANNEL" != 'release' ] && [ "${CHANNEL}" != 'beta' ]
channel="${bamboo.channel}"
readonly channel
if [ "$channel" != 'release' ] && [ "${channel}" != 'beta' ]
then
echo "don't publish to GitHub Releases for this channel"
@@ -323,7 +331,7 @@
# need to build a few of these.
'variables':
'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:5.4'
'dockerGo': 'adguard/golang-ubuntu:6.2'
# release-vX.Y.Z branches are the branches from which the actual final release
# is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -338,4 +346,4 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:5.4'
'dockerGo': 'adguard/golang-ubuntu:6.2'

View File

@@ -5,7 +5,7 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerGo': 'adguard/golang-ubuntu:5.4'
'dockerGo': 'adguard/golang-ubuntu:6.2'
'stages':
- 'Tests':

View File

@@ -1 +0,0 @@
Keep this file non-hidden for Go's embedding to work.

View File

@@ -37,8 +37,6 @@
"dhcp_ipv6_settings": "DHCP IPv6 إعدادات",
"form_error_required": "الحقل مطلوب",
"form_error_ip4_format": "عنوان IPv4 غير صالح",
"form_error_ip4_range_start_format": "عناوين البداية لـIPv4 غير صالحة للنطاق",
"form_error_ip4_range_end_format": "عناوين IPv4 غير صالحة لنطاق النهاية",
"form_error_ip4_gateway_format": "عنوان IPv4 غير صالح للبوابة",
"form_error_ip6_format": "عنوان IPv6 غير صالح",
"form_error_ip_format": "عنوان IP غير صحيح",
@@ -51,7 +49,6 @@
"out_of_range_error": "يجب أن يكون خارج النطاق \"{{start}}\" - \"{{end}}\"",
"lower_range_start_error": "يجب أن يكون أقل من نطاق البداية",
"greater_range_start_error": "يجب أن يكون أكبر من نطاق البداية",
"greater_range_end_error": "يجب أن يكون أكبر من نطاق النهاية",
"subnet_error": "يجب أن تكون العناوين في شبكة فرعية واحدة",
"gateway_or_subnet_invalid": "قناع الشبكة الفرعية غير صالح",
"dhcp_form_gateway_input": "IP البوابة",
@@ -300,6 +297,8 @@
"blocking_mode_nxdomain": "NXDOMAIN: الرد باستخدام رمز NXDOMAIN",
"blocking_mode_null_ip": "IP Null: الاستجابة بعنوان IP صفري (0.0.0.0 لـ A ؛ :: لـ AAAA)",
"blocking_mode_custom_ip": "استجابة IP مخصصة بعنوان IP تم تعيينه يدويًا",
"theme_light": "فاتح",
"theme_dark": "ليلي",
"upstream_dns_client_desc": "إذا احتفظت بهذا الحقل فارغًا ، فسيستخدم AdGuard Home الخوادم التي تم تكوينها في<0>DNS إعدادات</0>.",
"tracker_source": "مصدر المتعقب",
"source_label": "المصدر",

View File

@@ -261,7 +261,7 @@
"query_log_configuration": "Налада часопіса",
"query_log_disabled": "Часопіс запытаў выключаны, яго можна ўключыць у <0>наладах</0>",
"query_log_strict_search": "Ужывайце падвойныя двукоссі для строгага пошуку",
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены",
"query_log_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання запытаў? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"anonymize_client_ip": "Ананімізацыя IP-адрасы кліента",
"anonymize_client_ip_desc": "Не захоўвайце поўныя IP-адрасы гэтых удзельнікаў у часопісах або статыстыцы",
"dns_config": "Налады DNS-сервера",
@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Адказвае з кодам NXDOMAIN\n",
"blocking_mode_null_ip": "Нулёвы IP: Адказвае з нулёвым IP-адрасам (0.0.0.0 для A; :: для AAAA)",
"blocking_mode_custom_ip": "Карыстацкі IP: Адказвае з ручна наладжаным IP-адрасам",
"theme_auto": "Аўто",
"theme_light": "Светлая",
"theme_dark": "Цёмная",
"upstream_dns_client_desc": "Калі пакінуць поле пустым, AdGuard Home будзе звяртацца да сервераў, паказаных у <0>наладах DNS</0>.",
"tracker_source": "Крыніца трэкінгу",
"source_label": "Крыніца",
@@ -353,7 +356,7 @@
"install_devices_android_list_5": "Зараз можна змяніць палі «DNS 1» і «DNS 2». Увядзіце ў іх адрасы AdGuard Home.",
"install_devices_ios_list_1": "Увайдзіце ў меню налад прылады.",
"install_devices_ios_list_2": "Абярыце пункт «Wi-Fi» (для мабільных сетак ручная наладка DNS немагчыма).",
"install_devices_ios_list_3": "Націсніце на назву сетцы, да якой прылада падлучана ў дадзены момант.",
"install_devices_ios_list_3": "Націсніце на назву актыўнай у дадзены момант сеткі.",
"install_devices_ios_list_4": "У поле «DNS» увядзіце ўвядзіце адрасы AdGuard Home.",
"get_started": "Паехалі",
"next": "Далей",
@@ -451,6 +454,7 @@
"updates_checked": "Даступная новая версія AdGuard Home",
"updates_version_equal": "Версія AdGuard Home актуальная",
"check_updates_now": "Праверыць абнаўленні",
"version_request_error": "Памылка пры праверцы наяўнасці абнаўленняў. Праверце ваша інтэрнэт-злучэнне.",
"dns_privacy": "Зашыфраваны DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Ужывайце радок <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Ужывайце радок <1>{{address}}</1>.",
@@ -513,10 +517,10 @@
"filter_updated": "Спіс паспяхова абноўлены",
"statistics_configuration": "Канфігурацыя статыстыкі",
"statistics_retention": "Захаванне статыстыкі",
"statistics_retention_desc": "Калі вы зменшыце значэнне інтэрвалу, некаторыя дадзеныя могуць быць згублены",
"statistics_retention_desc": "Калі вы паменшыце значэнне інтэрвалу, некаторыя даныя могуць быць страчаны",
"statistics_clear": "Ачысціць статыстыку",
"statistics_clear_confirm": "Вы ўпэўнены, што хочаце ачысціць статыстыку?",
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры скарачэнні інтэрвалу дадзеныя могуць быць згублены",
"statistics_retention_confirm": "Вы ўпэўнены, што хочаце змяніць тэрмін захоўвання статыстыкі? Пры памяншэнні інтэрвалу, некаторыя даныя могуць быць страчаны",
"statistics_cleared": "Статыстыка паспяхова вычышчана",
"statistics_enable": "Уключыць статыстыку",
"interval_hours": "{{count}} гадзіна",
@@ -594,7 +598,7 @@
"show_blocked_responses": "Заблакавана",
"show_whitelisted_responses": "Белы спіс",
"show_processed_responses": "Апрацавана",
"blocked_safebrowsing": "Заблакавана згодна базе дадзеных Safe Browsing",
"blocked_safebrowsing": "Заблакіравана згодна з базай даных Safe Browsing",
"blocked_adult_websites": "Заблакавана Бацькоўскім кантролем",
"blocked_threats": "Заблакавана пагроз",
"allowed": "Дазволены",
@@ -635,7 +639,7 @@
"safe_browsing": "Бяспечны інтэрнэт",
"served_from_cache": "{{value}} <i>(атрымана з кэша)</i>",
"form_error_password_length": "Пароль павінен быць не менш за {{value}} сімвалаў",
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яго ў <1>Агульных наладах</1> .",
"anonymizer_notification": "<0>Заўвага:</0> Ананімізацыя IP уключана. Вы можаце адключыць яе ў <1>Агульных наладах</1>.",
"confirm_dns_cache_clear": "Вы ўпэўнены, што хочаце ачысціць кэш DNS?",
"cache_cleared": "Кэш DNS паспяхова ачышчаны",
"clear_cache": "Ачысціць кэш"

View File

@@ -165,8 +165,8 @@
"enabled_safe_browsing_toast": "Zapnuté bezpečné prohlížení",
"disabled_parental_toast": "Vypnutá Rodičovská kontrola",
"enabled_parental_toast": "Zapnutá Rodičovská kontrola",
"disabled_safe_search_toast": "Zapnuté bezpečné vyhledávání",
"enabled_save_search_toast": "Vypnuté bezpečné vyhledávání",
"disabled_safe_search_toast": "Vypnuté bezpečné vyhledávání",
"enabled_save_search_toast": "Zapnuté bezpečné vyhledávání",
"enabled_table_header": "Zapnuto",
"name_table_header": "Název",
"list_url_table_header": "Seznam URL",
@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odezva s kódem NXDOMAIN",
"blocking_mode_null_ip": "Nulová IP: Odezva s nulovou IP adresou (0.0.0.0 pro A; :: pro AAAA)",
"blocking_mode_custom_ip": "Vlastní IP. odezva s ručně nastavenou IP adresou",
"theme_auto": "Autom.",
"theme_light": "Světlý",
"theme_dark": "Tmavý",
"upstream_dns_client_desc": "Pokud toto pole ponecháte prázdné, AdGuard Home použije servery nakonfigurované v<0>DNS nastavení</0>.",
"tracker_source": "Zdroj slídiče",
"source_label": "Zdroj",
@@ -451,6 +454,7 @@
"updates_checked": "Nová verze AdGuard Home je k dispozici\n",
"updates_version_equal": "AdGuard Home je aktuální",
"check_updates_now": "Zkontrolovat aktualizace nyní",
"version_request_error": "Kontrola aktualizace se nezdařila. Zkontrolujte prosím připojení k Internetu.",
"dns_privacy": "Soukromí DNS",
"setup_dns_privacy_1": "<0>DNS skrze TLS:</0> Použít <1>{{address}}</1> řetězec.",
"setup_dns_privacy_2": "<0>DNS skrze HTTPS:</0> Použít <1>{{address}}</1> řetězec.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Svar med NXDOMAIN-kode",
"blocking_mode_null_ip": "Null IP: Svar med nul IP-adresse (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt indstillet IP-adresse",
"theme_auto": "Auto",
"theme_light": "Lyst",
"theme_dark": "Mørkt",
"upstream_dns_client_desc": "Holdes dette felt tomt, bruger AdGuard Home de i <0>DNS-indstillingerne</0> opsatte servere.",
"tracker_source": "Tracker-kilde",
"source_label": "Kilde",
@@ -451,6 +454,7 @@
"updates_checked": "En ny version af AdGuard Home er tilgængelig\n",
"updates_version_equal": "AdGuard Home er opdateret",
"check_updates_now": "Søg efter opdateringer nu",
"version_request_error": "Opdateringstjek mislykkedes. Tjek internetforbindelsen.",
"dns_privacy": "DNS-fortrolighed",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Brug <1>{{address}}</1> streng.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Brug <1>{{address}}</1> streng.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Mit NXDOMAIN-Code antworten",
"blocking_mode_null_ip": "Null-IP: Antworten mit Null-IP-Adresse (0.0.0.0.0 für A; :: für AAAA)",
"blocking_mode_custom_ip": "Benutzerdefinierte IP: Mit einer manuell eingestellten IP-Adresse antworten",
"theme_auto": "Auto",
"theme_light": "Hell",
"theme_dark": "Dunkel",
"upstream_dns_client_desc": "Wenn Sie dieses Feld leer lassen, verwendet AdGuard Home die Server, die in den <0>DNS-Einstellungen</0> konfiguriert sind.",
"tracker_source": "Tracker-Quelle",
"source_label": "Quelle",
@@ -451,6 +454,7 @@
"updates_checked": "Neue Version von AdGuard Home ist jetzt verfügbar",
"updates_version_equal": "AdGuard Home ist aktuell",
"check_updates_now": "Jetzt nach Aktualisierungen suchen",
"version_request_error": "Aktualisierungsprüfung fehlgeschlagen. Bitte überprüfen Sie Ihre Internetverbindung.",
"dns_privacy": "DNS-Datenschutz",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Zeichenkette <1>{{address}}</1> verwenden.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Zeichenkette <1>{{address}}</1> verwenden.",

View File

@@ -454,6 +454,7 @@
"updates_checked": "A new version of AdGuard Home is available",
"updates_version_equal": "AdGuard Home is up-to-date",
"check_updates_now": "Check for updates now",
"version_request_error": "Update check failed. Please check your Internet connection.",
"dns_privacy": "DNS Privacy",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Use <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Use <1>{{address}}</1> string.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Responde con el código NXDOMAIN",
"blocking_mode_null_ip": "IP nulo: Responde con dirección IP cero (0.0.0.0 para A; :: para AAAA)",
"blocking_mode_custom_ip": "IP personalizada: Responde con una dirección IP establecida manualmente",
"theme_auto": "Auto",
"theme_light": "Claro",
"theme_dark": "Oscuro",
"upstream_dns_client_desc": "Si se mantiene este campo vacío, AdGuard Home utilizará los servidores configurados en la <0>configuración del DNS</0>.",
"tracker_source": "Fuente del rastreador",
"source_label": "Fuente",
@@ -451,6 +454,7 @@
"updates_checked": "La nueva versión de AdGuard Home está disponible",
"updates_version_equal": "AdGuard Home está actualizado",
"check_updates_now": "Buscar actualizaciones ahora",
"version_request_error": "Error buscar la actualización. Comprueba tu conexión a Internet.",
"dns_privacy": "DNS cifrado",
"setup_dns_privacy_1": "<0>DNS mediante TLS:</0> Utiliza la cadena <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS mediante HTTPS:</0> Utiliza la cadena <1>{{address}}</1>.",
@@ -636,7 +640,7 @@
"served_from_cache": "{{value}} <i>(servido desde la caché)</i>",
"form_error_password_length": "La contraseña debe tener al menos {{value}} caracteres",
"anonymizer_notification": "<0>Nota:</0> La anonimización de IP está habilitada. Puedes deshabilitarla en <1>Configuración general</1>.",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché de DNS?",
"cache_cleared": "Caché DNS borrado con éxito",
"confirm_dns_cache_clear": "¿Estás seguro de que deseas borrar la caché DNS?",
"cache_cleared": "Caché DNS borrado correctamente",
"clear_cache": "Borrar caché"
}

View File

@@ -32,8 +32,6 @@
"dhcp_config_saved": "پیکربندی سرور DHCP ذخیره شده است",
"form_error_required": "فیلد مورد نیاز",
"form_error_ip4_format": "فرمت نامعتبر IPv4",
"form_error_ip4_range_start_format": "قالب IPv4 شروع دامنه نامعتبر است",
"form_error_ip4_range_end_format": "قالب IPv4 پایان دامنه نامعتبر است",
"form_error_ip4_gateway_format": "قالب IPv4 درگاه نامعتبر است",
"form_error_ip6_format": "فرمت نامعتبر IPv6",
"form_error_ip_format": "فرمت IPv4 نامعتبر است",
@@ -44,7 +42,6 @@
"out_of_range_error": "باید خارج از دامنه باشد\"{{start}}\"-\"{{end}}\"",
"lower_range_start_error": "باید کمتر از شروع دامنه باشد",
"greater_range_start_error": "باید بیشتر از شروع دامنه باشد",
"greater_range_end_error": "باید بیشتر از پایان دامنه باشد",
"subnet_error": "آدرس ها باید در یک زیرشبکه باشند",
"gateway_or_subnet_invalid": "پوشش زیرشبکه نامعتبر است",
"dhcp_form_gateway_input": "آی پی دروازه",
@@ -422,6 +419,7 @@
"updates_checked": "نسخه جدیدی از AdGuard Home در دسترس است",
"updates_version_equal": "AdGuard Home بروز است",
"check_updates_now": "حالا بررسی برای بروز رسانی",
"version_request_error": "بررسی بروزرسانی موفق نشد.لطفا ارتباط اینترنتی خود را بررسی کنید",
"dns_privacy": "حریم خصوصی DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> استفاده از<1>{{address}}</1> .",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> استفاده از <1>{{address}}</1> .",

View File

@@ -24,7 +24,7 @@
"unavailable_dhcp": "DHCP ei ole käytettävissä",
"unavailable_dhcp_desc": "AdGuard Home ei voi suorittaa DHCP-palvelinta käyttöjärjestelmässäsi",
"dhcp_title": "DHCP-palvelin (kokeellinen!)",
"dhcp_description": "Jos reitittimessäsi ei ole DHCP-asetuksia, voit käyttää AdGuard Homen omaa sisäänrakennettua DHCP-palvelinta.",
"dhcp_description": "Jollei reitittimesi tarjoa DHCP-asetuksia, voit käyttää AdGuard Homen omaa sisäänrakennettua DHCP-palvelinta.",
"dhcp_enable": "Ota DHCP-palvelin käyttöön",
"dhcp_disable": "Poista DHCP-palvelin käytöstä",
"dhcp_not_found": "On turvallista ottaa sisäänrakennettu DHCP-palvelin käyttöön, koska AdGuard Home ei havainnut verkossa muita aktiivisia DHCP-palvelimia. Suosittelemme, että varmistat tämän vielä itse, koska automaattinen tunnistus ei ole 100% varma.",
@@ -272,7 +272,7 @@
"nxdomain": "NXDOMAIN",
"refused": "REFUSED",
"null_ip": "Tyhjä IP",
"custom_ip": "Oma IP-osoite",
"custom_ip": "Mukautettu IP-osoite",
"blocking_ipv4": "IPv4-esto",
"blocking_ipv6": "IPv6-esto",
"dnscrypt": "DNSCrypt",
@@ -297,7 +297,10 @@
"blocking_mode_refused": "REFUSED: Vastaa REFUSED-koodilla",
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-koodilla",
"blocking_mode_null_ip": "Tyhjä IP: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA)",
"blocking_mode_custom_ip": "Oma IP: Vastaa itse määritetyllä IP-osoitteella",
"blocking_mode_custom_ip": "Mukautettu IP: Vastaa manuaalisesti määritetyllä IP-osoitteella",
"theme_auto": "Automaattinen",
"theme_light": "Vaalea",
"theme_dark": "Tumma",
"upstream_dns_client_desc": "Jos tämä on tyhjä, käyttää AdGuard Home <0>DNS-asetuksissa</0> määritettyjä palvelimia.",
"tracker_source": "Seurannan lähde",
"source_label": "Lähde",
@@ -451,6 +454,7 @@
"updates_checked": "Uusi versio AdGuard Home -ohjelmasta on saatavana\n",
"updates_version_equal": "AdGuard Home on ajan tasalla",
"check_updates_now": "Tarkista päivitykset nyt",
"version_request_error": "Päivitystarkistus epäonnistui. Tarkista Internet-yhteytesi.",
"dns_privacy": "DNS-tietosuoja",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Käytä merkkijonoa <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Käytä merkkijonoa <1>{{address}}</1>.",
@@ -614,7 +618,7 @@
"cache_ttl_max_override_desc": "Määritä DNS-välimuistin kohteiden enimmäiselinaika (sekunteina).",
"ttl_cache_validation": "Välimuistin vähimmäiselinajan on oltava pienempi tai sama kuin enimmäiselinajan",
"cache_optimistic": "Optimistinen välimuisti",
"cache_optimistic_desc": "Pakota AdGuard Home vastaamaan välimuistista vaikka sen tiedot olisivat vanhentuneet. Pyri samalla myös päivittämään tiedot.",
"cache_optimistic_desc": "Pakota AdGuard Home vastaamaan välimuistista vaikka tiedot olisivat vanhentuneet. Pyri samalla myös päivittämään tiedot.",
"filter_category_general": "Yleiset",
"filter_category_security": "Turvallisuus",
"filter_category_regional": "Alueelliset",

View File

@@ -222,7 +222,7 @@
"updated_upstream_dns_toast": "Serveurs en amont enregistrés",
"dns_test_ok_toast": "Les serveurs DNS spécifiés fonctionnent correctement",
"dns_test_not_ok_toast": "Impossible d'utiliser le serveur « {{key}} »: veuillez vérifier si le nom saisi est bien correct",
"dns_test_warning_toast": "L'amont «{{key}}» ne répond pas aux demandes de test et peut ne pas fonctionner correctement",
"dns_test_warning_toast": "L'amont « {{key}} » ne répond pas aux demandes de test et peut ne pas fonctionner correctement",
"unblock": "Débloquer",
"block": "Bloquer",
"disallow_this_client": "Interdire ce client",
@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN : Répondre avec le code NXDOMAIN",
"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",
"theme_auto": "Auto",
"theme_light": "Thème clair",
"theme_dark": "Thème sombre",
"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",
@@ -451,6 +454,7 @@
"updates_checked": "Une nouvelle version de AdGuard Home est disponible",
"updates_version_equal": "AdGuard Home est à jour",
"check_updates_now": "Vérifier les mises à jour",
"version_request_error": "Impossible de vérifier les mises à jour. Veuillez vérifier votre connexion internet.",
"dns_privacy": "Confidentialité DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS :</0> Utiliser le string <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS :</0> Utiliser le string <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odgovor s NXDOMAIN kôdom",
"blocking_mode_null_ip": "Nuliran IP: Odgovor s nuliranom IP adresom (0.0.0.0 za A; :: za AAAA)",
"blocking_mode_custom_ip": "Prilagođeni IP: Odgovor s ručno postavljenom IP adresom",
"theme_auto": "Auto",
"theme_light": "Svijetla",
"theme_dark": "Tamna",
"upstream_dns_client_desc": "Ako ovo polje ostane prazno, AdGuard Home će upotrijebiti poslužitelje postavljene u <0>DNS postavkama</0>.",
"tracker_source": "Izvor pratitelja",
"source_label": "Izvor",
@@ -451,6 +454,7 @@
"updates_checked": "Dostupna je nova verzija AdGuard Home-a",
"updates_version_equal": "AdGuard Home je ažuriran",
"check_updates_now": "Provjeri ažuriranja sada",
"version_request_error": "Ne uspješna provjera ažuriranja. Provjerite vašu Internetsku vezu.",
"dns_privacy": "DNS privatnost",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Koristite <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Koristite <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Az NXDOMAIN kóddal fog válaszolni",
"blocking_mode_null_ip": "Null IP: Nullákból álló IP-címmel válaszol (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Egyedi IP: Válasz egy kézzel beállított IP címmel",
"theme_auto": "Auto",
"theme_light": "Világos",
"theme_dark": "Sötét",
"upstream_dns_client_desc": "Ha üresen hagyja ezt a mezőt, az AdGuard Home azokat a szervereket fogja használni, amik a <0>DNS beállításokban</0> vannak beállítva.",
"tracker_source": "Követő forrása",
"source_label": "Forrás",
@@ -451,6 +454,7 @@
"updates_checked": "Elérhető az AdGuard Home új verziója",
"updates_version_equal": "Az AdGuard Home naprakész",
"check_updates_now": "Frissítések ellenőrzése most",
"version_request_error": "A frissítések ellenőrzése sikertelen. Ellenőrizze az internetkapcsolatot.",
"dns_privacy": "DNS Adatvédelem",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Használja a(z) <1>{{address}}</1> szöveget.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Használja a(z) <1>{{address}}</1> szöveget.",

View File

@@ -298,6 +298,9 @@
"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",
"theme_auto": "Auto",
"theme_light": "Terang",
"theme_dark": "Gelap",
"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",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Rispondi con il codice NXDOMAIN",
"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",
"theme_auto": "Auto",
"theme_light": "Chiaro",
"theme_dark": "Scuro",
"upstream_dns_client_desc": "Se lasci questo spazio vuoto, AdGuard Home utilizzerà i server configurati nelle <0>impostazioni DNS</0>.",
"tracker_source": "Origine del tracciatore",
"source_label": "Fonte",
@@ -451,6 +454,7 @@
"updates_checked": "Nuova versione di AdGuard Home è disponibile",
"updates_version_equal": "AdGuard Home è aggiornato",
"check_updates_now": "Ricerca aggiornamenti ora",
"version_request_error": "Ricerca aggiornamenti non riuscita. Per favore controlla la tua connessione internet.",
"dns_privacy": "Privacy DNS",
"setup_dns_privacy_1": "<0>DNS su TLS:</0> Utilizza la stringa <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS su HTTPS:</0> Utilizza la stringa <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAINNXDOMAINコードで応答します",
"blocking_mode_null_ip": "Null IPゼロのIPアドレスで応答しますAの場合は0.0.0.0; AAAAの場合は::",
"blocking_mode_custom_ip": "カスタムIP手動で設定されたIPアドレスで応答します",
"theme_auto": "自動",
"theme_light": "ライト",
"theme_dark": "ダーク",
"upstream_dns_client_desc": "このフィールドを未入力のままにすると、AdGuard Homeは<0>DNS設定</0>で構成されたサーバを使用します。",
"tracker_source": "追跡元",
"source_label": "ソース",
@@ -451,6 +454,7 @@
"updates_checked": "AdGuard Homeの新バージョンが利用可能です。",
"updates_version_equal": "AdGuard Homeは既に最新です",
"check_updates_now": "今すぐアップデートを確認する",
"version_request_error": "アップデート確認に失敗しました。インターネット接続を確認してください。",
"dns_privacy": "DNSプライバシー",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> <1>{{address}}</1>という文字列を使用してください。",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> <1>{{address}}</1>という文字列を使用してください。",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN 코드로 응답",
"blocking_mode_null_ip": "Null IP: 제로 IP 주소 (A는 0.0.0.0; AAAA는 ::) 로 응답합니다",
"blocking_mode_custom_ip": "커스텀 IP: 직접 설정한 IP 주소로 응답합니다",
"theme_auto": "자동",
"theme_light": "라이트 테마",
"theme_dark": "다크 테마",
"upstream_dns_client_desc": "이 값을 비워둔다면 AdGuard Home은 <0>DNS 설정</0>에 설정되어 있는 값을 사용합니다.",
"tracker_source": "추적기 소스",
"source_label": "소스",
@@ -451,6 +454,7 @@
"updates_checked": "AdGuard Home의 새 버전을 사용할 수 있습니다",
"updates_version_equal": "AdGuard Home 최신 상태입니다.",
"check_updates_now": "지금 업데이트 확인",
"version_request_error": "업데이트 체크에 실패했습니다. 인터넷 연결 상태를 확인해주세요.",
"dns_privacy": "DNS 프라이버시",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> <1>{{address}}</1> 사용하세요.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> <1>{{address}}</1> 사용하세요.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Reageer met NXDOMAIN code",
"blocking_mode_null_ip": "Nul IP: Reageer met een nul IP address (0.0.0.0 voor A; :: voor AAAA)",
"blocking_mode_custom_ip": "Aangepast IP: Reageer met een handmatige ingesteld IP adres",
"theme_auto": "Automatisch",
"theme_light": "Licht",
"theme_dark": "Donker",
"upstream_dns_client_desc": "Indien je dit veld leeglaat zal AdGuard Home de servers welke zijn ingesteld in de <0>DNS instellingen</0> gebruiken.",
"tracker_source": "Bron volger",
"source_label": "Bron",
@@ -451,6 +454,7 @@
"updates_checked": "Een nieuwe versie van AdGuard Home is beschikbaar\n",
"updates_version_equal": "AdGuard Home is actueel",
"check_updates_now": "Controleer op updates",
"version_request_error": "Updatecontrole mislukt. Controleer je internetverbinding.",
"dns_privacy": "DNS Privacy",
"setup_dns_privacy_1": "<0>DNS-via-TLS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-via-HTTPS:</0> Gebruik <1>{{address}}</1> string.",

View File

@@ -281,6 +281,7 @@
"blocking_mode_nxdomain": "NXDOMAIN: Svar med NXDOMAIN-koden",
"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",
"theme_auto": "Auto",
"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",
@@ -433,6 +434,7 @@
"updates_checked": "En ny versjon av AdGuard Home er tilgjengelig",
"updates_version_equal": "AdGuard Home er fullt oppdatert",
"check_updates_now": "Se etter oppdateringer nå",
"version_request_error": "Oppdateringssjekken mislyktes. Vennligst sjekk internettforbindelsen din.",
"dns_privacy": "DNS-privatliv",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Benytt <1>{{address}}</1>-strengen.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Benytt <1>{{address}}</1>-strengen.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odpowiedz kodem NXDOMAIN",
"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",
"theme_auto": "Auto",
"theme_light": "Jasny",
"theme_dark": "Ciemny",
"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",
@@ -451,6 +454,7 @@
"updates_checked": "Dostępna jest nowa wersja programu AdGuard Home\n",
"updates_version_equal": "AdGuard Home jest aktualny",
"check_updates_now": "Sprawdź aktualizacje teraz",
"version_request_error": "Sprawdzanie aktualizacji zakończone niepowodzeniem. Sprawdź swoje połączenie z internetem.",
"dns_privacy": "Prywatny DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Skorzystaj z adresu <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Skorzystaj z adresu <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Responder com o código NXDOMAIN",
"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",
"theme_auto": "Automático",
"theme_light": "Claro",
"theme_dark": "Escuro",
"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",
@@ -451,6 +454,7 @@
"updates_checked": "Uma nova versão do AdGuard Home está disponível\n",
"updates_version_equal": "O AdGuard Home está atualizado.",
"check_updates_now": "Verificar atualizações",
"version_request_error": "A verificação de atualização falhou. Por favor, verifique sua conexão com a internet.",
"dns_privacy": "Privacidade de DNS",
"setup_dns_privacy_1": "<0>DNS-sobre-TLS:</0> Use <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-sobre-HTTPS:</0> Use <1>{{address}}</1> string.",

View File

@@ -49,7 +49,7 @@
"out_of_range_error": "Deve estar fora do intervalo \"{{start}}\"-\"{{end}}\"",
"lower_range_start_error": "Deve ser inferior ao início do intervalo",
"greater_range_start_error": "Deve ser maior que o início do intervalo",
"subnet_error": "Os endereços devem estar em uma sub-rede",
"subnet_error": "Os endereços devem estar numa sub-rede",
"gateway_or_subnet_invalid": "Máscara de sub-rede inválida",
"dhcp_form_gateway_input": "IP do gateway",
"dhcp_form_subnet_input": "Máscara de sub-rede",
@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Responder com o código NXDOMAIN",
"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",
"theme_auto": "Automático",
"theme_light": "Claro",
"theme_dark": "Escuro",
"upstream_dns_client_desc": "Se mantiver esse campo vazio, o AdGuard Home usará os servidores configurados nas <0>Definições de DNS</0>.",
"tracker_source": "Fonte do rastreador",
"source_label": "Fonte",
@@ -332,10 +335,10 @@
"install_devices_router": "Router",
"install_devices_router_desc": "Esta configuração cobre automaticamente todos os dispositivos conectados ao seu router doméstico, sem a necessidade de configurar cada um deles manualmente.",
"install_devices_address": "O servidor de DNS do AdGuard Home está a capturar os seguintes endereços",
"install_devices_router_list_1": "Abra as preferências do seu roteador. Normalmente, tu podes acessá-lo de teu navegador por meio de um URL, como http://192.168.0.1/ ou http://192.168.1.1/. Tu podes ser solicitado a inserir uma palavra-passe. Se tu não se lembrar, muitas vezes tu podes repor a palavra-passe pressionando um botão no próprio roteador, mas esteja ciente de que se esse procedimento for escolhido, tu provavelmente perderás toda a definição do roteador. Se o teu roteador requer uma aplicação para configurá-lo, instale a aplicação no seu telefone ou PC e use-o para acessar as definições do roteador.",
"install_devices_router_list_1": "Abra as preferências do seu router. Normalmente, tu podes acessá-lo de teu navegador por meio de um URL, como http://192.168.0.1/ ou http://192.168.1.1/. Tu podes ser solicitado a inserir uma palavra-passe. Se tu não se lembrar, muitas vezes tu podes repor a palavra-passe pressionando um botão no próprio roteador, mas esteja ciente de que se esse procedimento for escolhido, tu provavelmente perderás toda a definição do router. Se o teu router requer uma aplicação para configurá-lo, instale a aplicação no seu telefone ou PC e use-o para acessar as definições do router.",
"install_devices_router_list_2": "Encontre as configurações de DNS. Procure as letras DNS ao lado de um campo que permite dois ou três conjuntos de números, cada um dividido em quatro grupos de um a três números.",
"install_devices_router_list_3": "Insira aqui seu servidor do AdGuard Home.",
"install_devices_router_list_4": "Em alguns tipos de roteador, um servidor DNS personalizado não pode ser configurado. Nesse caso, configurar o AdGuard Home como um <0>Servidor DHCP</0> pode ajudar. Caso contrário, tu deve verificar o manual do router sobre como personalizar os servidores DNS em seu modelo de router específico.",
"install_devices_router_list_4": "Em alguns tipos de router, um servidor DNS personalizado não pode ser configurado. Nesse caso, configurar o AdGuard Home como um <0>Servidor DHCP</0> pode ajudar. Caso contrário, tu deves verificar o manual do router sobre como personalizar os servidores DNS no seu modelo de router específico.",
"install_devices_windows_list_1": "Abra o Painel de Controlo através do Menu Iniciar ou pela Pesquisa do Windows.",
"install_devices_windows_list_2": "Entre na categoria Rede e Internet e depois clique em Central de Rede e Partilha.",
"install_devices_windows_list_3": "No painel esquerdo, clique em \"Alterar configurações do adaptador\".",
@@ -451,6 +454,7 @@
"updates_checked": "Uma nova versão do AdGuard Home está disponível\n",
"updates_version_equal": "O AdGuard Home está atualizado",
"check_updates_now": "Verificar atualizações",
"version_request_error": "A verificação de atualização falhou. Verifique a sua ligação à internet.",
"dns_privacy": "Privacidade de DNS",
"setup_dns_privacy_1": "<0>DNS-sobre-TLS:</0> Use <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-sobre-HTTPS:</0> Use <1>{{address}}</1> string.",
@@ -579,7 +583,7 @@
"client_blocked": "Cliente \"{{ip}}\" foi bloqueado com sucesso",
"client_unblocked": "Cliente \"{{ip}}\" foi desbloqueado com sucesso",
"static_ip": "Endereço de IP estático",
"static_ip_desc": "O AdGuard Home é um servidor, portanto, ele precisa de um endereço de IP estático para funcionar corretamente. Caso contrário, em algum momento, seu roteador poderá atribuir um novo endereço de IP neste dispositivo.",
"static_ip_desc": "O AdGuard Home é um servidor, portanto, ele precisa de um endereço de IP estático para funcionar corretamente. Caso contrário, em algum momento, seu router poderá atribuir um novo endereço de IP neste dispositivo.",
"set_static_ip": "Definir um endereço de IP estático",
"install_static_ok": "Boas notícias! O endereço de IP estático já está configurado",
"install_static_error": "O AdGuard Home não pode configurar automaticamente para esta interface de rede. Por favor, procure uma instrução sobre como fazer isso manualmente.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Răspunde cu codul NXDOMAIN",
"blocking_mode_null_ip": "IP nul: răspunde cu o adresă IP zero (0.0.0.0 pentru A; :: pentru AAAA)",
"blocking_mode_custom_ip": "IP personalizat: răspunde cu o adresă IP setată manual",
"theme_auto": "Auto",
"theme_light": "Luminoasă",
"theme_dark": "Sombră",
"upstream_dns_client_desc": "Dacă mențineți acest câmp gol, AdGuard Home va folosi serverele configurate în <0>setările DNS</0>.",
"tracker_source": "Sursă tracker",
"source_label": "Sursă",
@@ -451,6 +454,7 @@
"updates_checked": "Este disponibilă o nouă versiune de AdGuard Home\n",
"updates_version_equal": "AdGuard Home este la zi",
"check_updates_now": "Verificați actualizările acum",
"version_request_error": "Verificarea actualizării nu a reușit. Verificați conexiunea internet.",
"dns_privacy": "Confidențialitate DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Folosiți stringul <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Folosiți stringul <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Отвечает с кодом NXDOMAIN\n",
"blocking_mode_null_ip": "Нулевой IP: Отвечает с нулевым IP-адресом (0.0.0.0 для A; :: для AAAA)",
"blocking_mode_custom_ip": "Пользовательский IP: Отвечает с вручную настроенным IP-адресом",
"theme_auto": "Авто",
"theme_light": "Светлая",
"theme_dark": "Тёмная",
"upstream_dns_client_desc": "Если оставить поле пустым, AdGuard Home будет обращаться к серверам, указанным в <0>настройках DNS</0>.",
"tracker_source": "Источник трекинга",
"source_label": "Источник",
@@ -451,6 +454,7 @@
"updates_checked": "Доступна новая версия AdGuard Home",
"updates_version_equal": "Версия AdGuard Home актуальна",
"check_updates_now": "Проверить обновления",
"version_request_error": "Ошибка при проверке наличия обновлений. Проверьте ваше интернет-соединение.",
"dns_privacy": "Зашифрованный DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Используйте строку <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Используйте строку <1>{{address}}</1>.",

View File

@@ -30,8 +30,6 @@
"dhcp_ipv6_settings": "ග.ධා.වි.කෙ. අ.ජා.කෙ. 6 සැකසුම්",
"form_error_required": "ඇවැසි ක්‍ෂේත්‍රයකි",
"form_error_ip4_format": "IPv4 ලිපිනය වලංගු නොවේ",
"form_error_ip4_range_start_format": "පරාසය ආරම්භයේ වලංගු නොවන අ.ජා.කෙ.4 ලිපිනයකි",
"form_error_ip4_range_end_format": "පරාසය අවසානයේ වලංගු නොවන අ.ජා.කෙ.4 ලිපිනයකි",
"form_error_ip6_format": "වලංගු නොවන අ.ජා.කෙ.6 ලිපිනයකි",
"form_error_ip_format": "අ.ජා.කෙ. (IP) ලිපිනය වලංගු නොවේ",
"form_error_mac_format": "මා.ප්‍ර.පා. ලිපිනය වලංගු නොවේ",
@@ -42,7 +40,6 @@
"out_of_range_error": "\"{{start}}\"-\"{{end}}\" පරාසයෙන් පිට විය යුතුය",
"lower_range_start_error": "පරාසය ආරම්භයට වඩා අඩු විය යුතුය",
"greater_range_start_error": "පරාසය ආරම්භයට වඩා වැඩි විය යුතුය",
"greater_range_end_error": "පරාසය අවසානයට වඩා වැඩි විය යුතුය",
"subnet_error": "ලිපින එක් අනුජාලයක තිබිය යුතුය",
"dhcp_form_range_title": "අ.ජා. කෙ. (IP) ලිපින පරාසය",
"dhcp_form_range_start": "පරාසය ආරම්භය",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odpovedať kódom NXDOMAIN",
"blocking_mode_null_ip": "Null IP: Odpoveď s nulovou IP adresou (0.0.0.0 pre A; :: pre AAAA)",
"blocking_mode_custom_ip": "Vlastná IP adresa: Odpovedzte s manuálne nastavenou IP adresou",
"theme_auto": "Auto",
"theme_light": "Svetlá",
"theme_dark": "Tmavá",
"upstream_dns_client_desc": "Ak ponecháte toto pole prázdne, AdGuard Home použije servery nakonfigurované v <0>nastaveniach DNS</0>.",
"tracker_source": "Zdroj sledovania",
"source_label": "Zdroj",
@@ -451,6 +454,7 @@
"updates_checked": "K dispozícii je nová verzia aplikácie AdGuard Home\n",
"updates_version_equal": "AdGuard Home je aktuálny",
"check_updates_now": "Skontrolovať aktualizácie teraz",
"version_request_error": "Kontrola aktualizácie zlyhala. Skontrolujte svoje internetové pripojenie.",
"dns_privacy": "DNS súkromie",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Použiť <1>{{address}}</1> reťazec.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Použiť <1>{{address}}</1> reťazec.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odziv s kodo NXDOMAIN",
"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",
"theme_auto": "Auto",
"theme_light": "Svetla tema",
"theme_dark": "Temna tema",
"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",
@@ -451,6 +454,7 @@
"updates_checked": "Na voljo je nova različica programa AdGuard Home\n",
"updates_version_equal": "AdGuard Home je posodobljen",
"check_updates_now": "Preveri obstoj posodobitev zdaj",
"version_request_error": "Posodobitev ni uspela. Preverite vašo internetno povezavo.",
"dns_privacy": "Zasebnost DNS",
"setup_dns_privacy_1": "<0>DNS-prek-TLS:</0> Uporabite niz <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-prek-HTTPS:</0> Uporabite niz <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Odgovara sa NXDOMAIN kodom",
"blocking_mode_null_ip": "Null IP: Odgovara sa zero IP adresom (0.0.0.0 za A; :: za AAAA)",
"blocking_mode_custom_ip": "Prilagođeni IP: Odgovara sa ručno podešenom IP adresom",
"theme_auto": "Auto",
"theme_light": "Svetla tema",
"theme_dark": "Tamna tema",
"upstream_dns_client_desc": "AKo ovo polje ostavite prazno, AdGuard Home će koristiti servere konfigurisane u <0>DNS postavkama</0>.",
"tracker_source": "Izvor praćenja",
"source_label": "Izvor",
@@ -451,6 +454,7 @@
"updates_checked": "Dostupna je nova verzija AdGuard Home-a",
"updates_version_equal": "AdGuard Home je ažuriran na najnoviju verziju",
"check_updates_now": "Proveri da li postoje ispravke",
"version_request_error": "Provera ažuriranja nije uspela. Proverite svoju vezu sa internetom.",
"dns_privacy": "DNS privatnost",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> koristi <1>{{address}}</1> string.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> koristi <1>{{address}}</1> string.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Svara med NXDOMAIN kod",
"blocking_mode_null_ip": "Null IP: Svara med noll IP adress (0.0.0.0 för A; :: för AAAA)",
"blocking_mode_custom_ip": "Anpassad IP: Svara med en manuellt inställd IP adress",
"theme_auto": "Auto",
"theme_light": "Ljust",
"theme_dark": "Mörkt",
"upstream_dns_client_desc": "Om detta fält är tomt kommer AdGuard Home att använda de servrar som konfigurerats i <0>DNS inställningarna</0>.",
"tracker_source": "Spårningskälla",
"source_label": "Källa",
@@ -451,6 +454,7 @@
"updates_checked": "En ny version av AdGuard Home är tillgänglig\n",
"updates_version_equal": "AdGuard Home är uppdaterat",
"check_updates_now": "Sök efter uppdateringar nu",
"version_request_error": "Uppdateringskontroll misslyckades. Kontrollera din internetanslutning.",
"dns_privacy": "DNS-Integritet",
"setup_dns_privacy_1": "<0>DNS-över-TLS:</0> Använd: <1>{{address}}</1>",
"setup_dns_privacy_2": "<0>DNS-över-HTTPS:</0> Använd: <1>{{address}}</1>",

View File

@@ -128,14 +128,14 @@
"number_of_dns_query_days": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_days_plural": "Son {{count}} gün boyunca işlenen DNS sorgularının sayısı",
"number_of_dns_query_24_hours": "Son 24 saat içinde işlenen DNS sorgularının sayısı",
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtreleri ve ana makine engel listeleri tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours": "Reklam engelleme filtreleri ve hosts engel listeleri tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_by_sec": "AdGuard gezinti koruması modülü tarafından engellenen DNS isteklerinin sayısı",
"number_of_dns_query_blocked_24_hours_adult": "Engellenen yetişkin içerikli sitelerin sayısı",
"enforced_save_search": "Uygulanan güvenli arama",
"number_of_dns_query_to_safe_search": "Güvenli Aramanın uygulandığı arama motorlarına gönderilen DNS isteklerinin sayısı",
"average_processing_time": "Ortalama işlem süresi",
"average_processing_time_hint": "Bir DNS isteğinin milisaniye cinsinden ortalama işlem süresi",
"block_domain_use_filters_and_hosts": "Filtre ve ana makine listelerini kullanarak alan adlarını engelle",
"block_domain_use_filters_and_hosts": "Filtre ve hosts dosyalarını kullanarak alan adlarını engelle",
"filters_block_toggle_hint": "<a>Filtreler</a> ayarlarında engelleme kuralları oluşturabilirsiniz.",
"use_adguard_browsing_sec": "AdGuard gezinti koruması web hizmetini kullan",
"use_adguard_browsing_sec_hint": "AdGuard Home, alan adının gezinti koruması web hizmeti tarafından engellenip engellenmediğini kontrol eder. Kontrolü gerçekleştirmek için gizlilik dostu arama API'sini kullanır: sunucuya yalnızca SHA256 karma alan adının kısa bir ön eki gönderilir.",
@@ -177,7 +177,7 @@
"edit_table_action": "Düzenle",
"delete_table_action": "Sil",
"elapsed": "Geçen süre",
"filters_and_hosts_hint": "AdGuard Home, temel reklam engelleme kurallarını ve ana makine dosyalarının söz dizimini anlar.",
"filters_and_hosts_hint": "AdGuard Home, temel reklam engelleme kurallarını ve hosts dosyalarının söz dizimini anlar.",
"no_blocklist_added": "Engel listesi eklenmedi",
"no_whitelist_added": "İzin listesi eklenmedi",
"add_blocklist": "Engel listesi ekle",
@@ -197,8 +197,8 @@
"form_error_url_format": "Geçersiz URL biçimi",
"form_error_url_or_path_format": "Geçersiz URL adresi veya dosya yolu",
"custom_filter_rules": "Özel filtreleme kuralları",
"custom_filter_rules_hint": "Her satıra bir kural girin. Reklam engelleme kuralı veya ana makine dosyası söz dizimi kullanabilirsiniz.",
"system_host_files": "Sistem ana makine dosyaları",
"custom_filter_rules_hint": "Her satıra bir kural girin. Reklam engelleme kuralı veya hosts dosyası söz dizimi kullanabilirsiniz.",
"system_host_files": "Sistem hosts dosyaları",
"examples_title": "Örnekler",
"example_meaning_filter_block": "example.org'a ve tüm alt alanlarına erişimi engeller;",
"example_meaning_filter_whitelist": "example.org'a ve tüm alt alanlarına erişimin engelini kaldırır;",
@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: NXDOMAIN koduyla yanıt verin",
"blocking_mode_null_ip": "Boş IP: Sıfır IP adresiyle yanıt verin (A için 0.0.0.0; :: AAAA için)",
"blocking_mode_custom_ip": "Özel IP: El ile ayarlanmış bir IP adresiyle yanıt verin",
"theme_auto": "Otomatik",
"theme_light": "Açık",
"theme_dark": "Koyu",
"upstream_dns_client_desc": "Bu alanı boş bırakırsanız, AdGuard Home, <0>DNS ayarlarında</0> yapılandırılan sunucuları kullanır.",
"tracker_source": "İzleyici kaynağı",
"source_label": "Kaynak",
@@ -347,12 +350,12 @@
"install_devices_macos_list_3": "Listedeki ilk bağlantıyı seçin ve Gelişmiş öğesine tıklayın.",
"install_devices_macos_list_4": "DNS sekmesini seçin ve AdGuard Home sunucunuzun adreslerini girin.",
"install_devices_android_list_1": "Android Menüsü ana ekranından Ayarlar'a dokunun.",
"install_devices_android_list_2": "Menüde bulunan Wi-Fi öğesine dokunun. Mevcut tüm ağlar listelenecektir (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_android_list_2": "Menüde bulunan Wi-Fi öğesine dokunun. Mevcut tüm ağlar listelenecektir (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_android_list_3": "Bağlı olduğunuz ağın üzerine basılı tutun ve Ağı Değiştir'e dokunun.",
"install_devices_android_list_4": "Bazı cihazlarda, diğer ayarları görmek için \"Gelişmiş\" seçeneğini seçmeniz gerekebilir. Android DNS ayarlarınızı yapmak için IP ayarlarını DHCP modundan Statik moda değiştirmeniz gerekir.",
"install_devices_android_list_5": "DNS 1 ve DNS 2 değerlerini AdGuard Home sunucunuzun adresleriyle değiştirin.",
"install_devices_ios_list_1": "Ana ekrandan Ayarlar'a dokunun.",
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (mobil ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_ios_list_2": "Sol menüde bulunan Wi-Fi bölümüne girin (telefon ağlar için özel DNS sunucusu ayarlanamaz).",
"install_devices_ios_list_3": "O anda aktif olan ağın adına dokunun.",
"install_devices_ios_list_4": "DNS alanına AdGuard Home sunucunuzun adreslerini girin.",
"get_started": "Başlayın",
@@ -446,11 +449,12 @@
"access_disallowed_title": "İzin verilmeyen istemciler",
"access_disallowed_desc": "CIDR'lerin, IP adreslerinin veya <a>İstemci Kimliklerin</a> listesi. Bu listede girişler varsa, AdGuard Home bu istemcilerden gelen istekleri keser. İzin verilen istemcilerde girişler varsa, bu alan yok sayılır.",
"access_blocked_title": "İzin verilmeyen alan adları",
"access_blocked_desc": "Bu işlem filtrelerle ilgili değildir. AdGuard Home, bu alan adlarından gelen DNS sorgularını yanıtsız bırakır ve bu sorgular sorgu günlüğünde görünmez. Tam alan adlarını, joker karakterleri veya URL filtre kurallarını belirtebilirsiniz, ör. \"example.org\", \"*.example.org\" veya \"||example.org^\".",
"access_blocked_desc": "Bu işlem filtrelerle ilgili değildir. AdGuard Home, bu alan adlarından gelen DNS sorgularını yanıtsız bırakır ve bu sorgular sorgu günlüğünde görünmez. Tam alan adlarını, joker karakterleri veya URL filtre kurallarını belirtebilirsiniz, örn. \"example.org\", \"*.example.org\" veya \"||example.org^\".",
"access_settings_saved": "Erişim ayarları başarıyla kaydedildi!",
"updates_checked": "AdGuard Home'un yeni bir sürümü mevcut",
"updates_version_equal": "AdGuard Home yazılımı güncel durumda",
"check_updates_now": "Güncellemeleri şimdi denetle",
"version_request_error": "Güncelleme denetimi başarısız. Lütfen internet bağlantınızı kontrol edin.",
"dns_privacy": "DNS Gizliliği",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> <1>{{address}}</1> dizesini kullan.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> <1>{{address}}</1> dizesini kullan.",
@@ -476,7 +480,7 @@
"rewrite_confirm_delete": "\"{{key}}\" için DNS yeniden yazımını silmek istediğinize emin misiniz?",
"rewrite_desc": "Belirli bir alan adı için özel DNS yanıtını kolayca yapılandırmanızı sağlar.",
"rewrite_applied": "Yeniden yazım kuralı uygulandı",
"rewrite_hosts_applied": "Ana makine dosyası kuralı tarafından yeniden yazıldı",
"rewrite_hosts_applied": "Hosts dosyası kuralı tarafından yeniden yazıldı",
"dns_rewrites": "DNS yeniden yazımları",
"form_domain": "Alan adı veya joker karakter girin",
"form_answer": "IP adresi veya alan adı girin",
@@ -539,7 +543,7 @@
"network": "Ağ",
"descr": "Açıklama",
"whois": "WHOIS",
"filtering_rules_learn_more": "Kendi ana makine listelerinizi oluşturma hakkında <0>daha fazla bilgi edinin</0>.",
"filtering_rules_learn_more": "Kendi hosts listelerinizi oluşturma hakkında <0>daha fazla bilgi edinin</0>.",
"blocked_by_response": "Yanıt olarak CNAME veya IP tarafından engellendi",
"blocked_by_cname_or_ip": "CNAME veya IP tarafından engellendi",
"try_again": "Tekrar dene",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Відповісти з кодом NXDOMAIN",
"blocking_mode_null_ip": "Нульовий IP: Відповісти з нульовою IP-адресою (0.0.0.0 для A; :: для AAAA)",
"blocking_mode_custom_ip": "Спеціальна IP-адреса: Відповісти із вручну встановленою IP-адресою",
"theme_auto": "Авто",
"theme_light": "Світла",
"theme_dark": "Темна",
"upstream_dns_client_desc": "Якщо це поле залишатиметься порожнім, AdGuard Home використовуватиме сервери, вказані в <0>налаштуваннях DNS</0>.",
"tracker_source": "Джерело відстежувача",
"source_label": "Джерело",
@@ -451,6 +454,7 @@
"updates_checked": "Доступна нова версія AdGuard Home",
"updates_version_equal": "AdGuard Home останньої версії",
"check_updates_now": "Перевірити наявність оновлень",
"version_request_error": "Не вдалося перевірити оновлення. Будь ласка, перевірте з'єднання з інтернетом.",
"dns_privacy": "Конфіденційність DNS",
"setup_dns_privacy_1": "<0>DNS-over-TLS: </0>Використайте рядок <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Використайте рядок <1>{{address}}</1>.",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "NXDOMAIN: Phản hổi với mã NXDOMAIN",
"blocking_mode_null_ip": "Null IP: Trả lời bằng không địa chỉ IP (0.0.0.0 cho A; :: cho AAAA)",
"blocking_mode_custom_ip": "IP tùy chỉnh: Phản hồi với địa chỉ IP đã được tiết lập",
"theme_auto": "Tự động",
"theme_light": "Light theme",
"theme_dark": "Dark theme",
"upstream_dns_client_desc": "Nếu để trống trường này, AdGuardHome sẽ sử dụng nhũng máy chủ được cấu hình ở <0>Cấu hình DNS</0>.",
"tracker_source": "Nguồn theo dõi",
"source_label": "Nguồn",
@@ -451,6 +454,7 @@
"updates_checked": "Phiên bản mới của AdGuard Home có sẵn",
"updates_version_equal": "AdGuard Home đã được cập nhật",
"check_updates_now": "Kiểm tra cập nhật ngay bây giờ",
"version_request_error": "Cập nhật không thành công. Hãy kiểm tra kết nối internet của bạn.",
"dns_privacy": "DNS Riêng Tư",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> Sử dụng chuỗi <1>{{address}}</1>.",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> Sử dụng chuỗi <1>{{address}}</1>.",

View File

@@ -16,7 +16,7 @@
"resolve_clients_title": "启用客户端的 IP 地址的反向解析",
"resolve_clients_desc": "通过发送 PTR 查询到对应的解析器 (本地客户端的私人 DNS 服务器,公共 IP 客户端的上游服务器) 将 IP 地址反向解析成其客户端主机名。",
"use_private_ptr_resolvers_title": "使用私人反向 DNS 解析器",
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,则 AdGuard Home会以 NXDOMAIN 响应所有此类PTR请求从 DHCP、/ etc / hosts 等获知的客户端除外。",
"use_private_ptr_resolvers_desc": "使用这些上游服务器对本地服务的地址执行反向 DNS 查找。 如果禁用,则 AdGuard Home 会以 NXDOMAIN 响应所有此类 PTR 请求,从 DHCP、/etc/hosts 等获知的客户端除外。",
"check_dhcp_servers": "检查 DHCP 服务器",
"save_config": "保存配置",
"enabled_dhcp": "DHCP 服务器已启用",
@@ -128,7 +128,7 @@
"number_of_dns_query_days": "过去 {{count}} 天内处理的 DNS 查询总数",
"number_of_dns_query_days_plural": "在过去的 {{count}} 天内处理了多少个 DNS 查询",
"number_of_dns_query_24_hours": "过去 24 小时内处理的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours": "被广告过滤器和 Hosts 拦截清单阻止的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours": "被广告过滤器和 Hosts 黑名单阻止的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_by_sec": "被 AdGuard 安全浏览模块阻止的 DNS 请求总数",
"number_of_dns_query_blocked_24_hours_adult": "被阻止的成人网站总数",
"enforced_save_search": "强制安全搜索",
@@ -146,10 +146,10 @@
"no_servers_specified": "未找到指定的服务器",
"general_settings": "常规设置",
"dns_settings": "DNS 设置",
"dns_blocklists": "DNS 拦截列表",
"dns_allowlists": "DNS 允许列表",
"dns_blocklists": "DNS 黑名单",
"dns_allowlists": "DNS 白名单",
"dns_blocklists_desc": "AdGuard Home将阻止匹配DNS拦截清单的域名",
"dns_allowlists_desc": "来自DNS允许列表的域将被允许,即使它们位于任意阻止列表中也是如此",
"dns_allowlists_desc": "来自 DNS 白名单的域将被允许,即使它们位于任意黑名单中也是如此",
"custom_filtering_rules": "自定义过滤规则",
"encryption_settings": "加密设置",
"dhcp_settings": "DHCP 设置",
@@ -172,28 +172,28 @@
"list_url_table_header": "清单网址",
"rules_count_table_header": "规则数",
"last_time_updated_table_header": "上次更新时间",
"actions_table_header": "活跃状态",
"actions_table_header": "操作",
"request_table_header": "请求",
"edit_table_action": "编辑",
"delete_table_action": "删除",
"elapsed": "耗时",
"filters_and_hosts_hint": "AdGuard Home 可以解析基础的 adblock 规则和 Hosts 语法。",
"no_blocklist_added": "未添加阻止列表",
"no_whitelist_added": "未添加允许列表",
"add_blocklist": "添加阻止列表",
"add_allowlist": "添加允许列表",
"no_blocklist_added": "未添加黑名单",
"no_whitelist_added": "未添加白名单",
"add_blocklist": "添加黑名单",
"add_allowlist": "添加白名单",
"cancel_btn": "取消",
"enter_name_hint": "输入名称",
"enter_url_or_path_hint": "请输入URL或列表的绝对路径",
"check_updates_btn": "检查更新",
"new_blocklist": "新封锁清单",
"new_allowlist": "新的允许清单",
"edit_blocklist": "编辑阻止列表",
"edit_allowlist": "编辑允许列表",
"choose_blocklist": "选择拦截列表",
"choose_allowlist": "选择允许列表",
"enter_valid_blocklist": "输入有效的阻止列表URL",
"enter_valid_allowlist": "输入有效的允许列表URL",
"new_allowlist": "新增白名单",
"edit_blocklist": "编辑黑名单",
"edit_allowlist": "编辑白名单",
"choose_blocklist": "选择黑名单",
"choose_allowlist": "选择白名单",
"enter_valid_blocklist": "输入有效的黑名单 URL",
"enter_valid_allowlist": "输入有效的白名单 URL",
"form_error_url_format": "无效的 URL 格式",
"form_error_url_or_path_format": "无效的 URL 或列表的绝对路径",
"custom_filter_rules": "自定义过滤器规则",
@@ -269,9 +269,9 @@
"dns_cache_config_desc": "您可以在此处配置 DNS 缓存",
"blocking_mode": "拦截模式",
"default": "默认",
"nxdomain": "无效域名",
"nxdomain": "NXDOMAIN",
"refused": "REFUSED",
"null_ip": "无效 IP",
"null_ip": " IP",
"custom_ip": "自定义 IP",
"blocking_ipv4": "拦截 IPv4",
"blocking_ipv6": "拦截 IPv6",
@@ -296,8 +296,11 @@
"blocking_mode_default": "默认:被 Adblock 规则拦截时反应为零 IP 地址A记录0.0.0.0AAAA记录::);被/etc/hosts 规则拦截时反应为规则中指定 IP 地址",
"blocking_mode_refused": "REFUSED以 REFUSED 码响应请求",
"blocking_mode_nxdomain": "NXDOMAIN以NXDOMAIN码响应",
"blocking_mode_null_ip": "空IP以零IP地址响应(A记录 0.0.0.0AAAA记录 ::)",
"blocking_mode_null_ip": "空 IP以零 IP 地址响应A 记录 0.0.0.0AAAA 记录 ::",
"blocking_mode_custom_ip": "自定IP以手动设置的IP地址响应",
"theme_auto": "自动",
"theme_light": "浅色主题",
"theme_dark": "深色主题",
"upstream_dns_client_desc": "如果将此字段留空AdGuard Home 将使用在<0>DNS设置</0>中配置的服务器。",
"tracker_source": "追踪器来源",
"source_label": "源",
@@ -451,6 +454,7 @@
"updates_checked": "AdGuard Home 的新版本现在可用",
"updates_version_equal": "AdGuard Home已经是最新版本",
"check_updates_now": "立即检查更新",
"version_request_error": "检查更新失败。请检查您的因特网连接。",
"dns_privacy": "DNS 隐私",
"setup_dns_privacy_1": "<0>DNS-over-TLS:</0> 使用 <1>{{address}}</1> 字符串。",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS:</0> 使用 <1>{{address}}</1> 字符串。",
@@ -601,7 +605,7 @@
"filtered": "已过滤",
"rewritten": "重写项",
"safe_search": "安全搜索",
"blocklist": "拦截列表",
"blocklist": "黑名单",
"milliseconds_abbreviation": "毫秒",
"cache_size": "缓存大小",
"cache_size_desc": "DNS 缓存大小(单位:字节)。要关闭缓存,请留空。",
@@ -622,7 +626,7 @@
"filter_category_general_desc": "在大多数设备上阻止跟踪和广告的列表",
"filter_category_security_desc": "专用于拦截恶意软件、钓鱼或欺诈域名的列表",
"filter_category_regional_desc": "专注于区域广告和跟踪服务器的列表",
"filter_category_other_desc": "其他阻止列表",
"filter_category_other_desc": "其他黑名单",
"setup_config_to_enable_dhcp_server": "设置配置以启用 DHCP 服务器",
"original_response": "原始响应",
"click_to_view_queries": "点击查看查询",

View File

@@ -38,8 +38,6 @@
"form_error_required": "必要欄位",
"form_error_ip4_format": "無效的 IPv4 格式",
"form_error_ip6_format": "無效的 IPv6 格式",
"form_error_ip4_range_start_format": "無效的 IPv4 範圍起始位址",
"form_error_ip4_range_end_format": "無效的 IPv4 範圍結束位址",
"form_error_ip4_gateway_format": "閘道的 IPv4 位址無效",
"form_error_ip_format": "無效的 IP 位址",
"form_error_mac_format": "無效的 「MAC 位址」格式",

View File

@@ -298,6 +298,9 @@
"blocking_mode_nxdomain": "不存在的網域NXDOMAIN以 NXDOMAIN 碼回覆",
"blocking_mode_null_ip": "無效的 IP以零值 IP 位址0.0.0.0 供 A:: 供 AAAA回覆",
"blocking_mode_custom_ip": "自訂的 IP以一組手動地被設定的 IP 位址回覆",
"theme_auto": "自動",
"theme_light": "淺色",
"theme_dark": "深色",
"upstream_dns_client_desc": "如果您將此欄位留空AdGuard Home 將使用在 <0>DNS 設定</0>中被配置的伺服器。",
"tracker_source": "追蹤器來源",
"source_label": "來源",
@@ -451,6 +454,7 @@
"updates_checked": "AdGuard Home 的新版本為可用的",
"updates_version_equal": "AdGuard Home 為最新的",
"check_updates_now": "立即檢查更新",
"version_request_error": "更新檢查已失敗。請檢查您的網際網路連線。",
"dns_privacy": "DNS 隱私",
"setup_dns_privacy_1": "<0>DNS-over-TLS</0>使用 <1>{{address}}</1> 字串。",
"setup_dns_privacy_2": "<0>DNS-over-HTTPS</0>使用 <1>{{address}}</1> 字串。",

View File

@@ -141,7 +141,7 @@ export const getVersion = (recheck = false) => async (dispatch, getState) => {
}
}
} catch (error) {
dispatch(addErrorToast({ error }));
dispatch(addErrorToast({ error: 'version_request_error' }));
dispatch(getVersionFailure());
}
};

View File

@@ -1,4 +1,5 @@
:root {
--black: #131313;
--bgcolor: #f5f7fb;
--mcolor: #495057;
--scolor: rgba(74, 74, 74, 0.7);
@@ -26,11 +27,18 @@
--gray-a5: #a5a5a5;
--gray-d8: #d8d8d8;
--gray-f3: #f3f3f3;
--loading-bg: rgba(255, 255, 255, 0.48);
--font-family-monospace: Monaco, Menlo, "Ubuntu Mono", Consolas, source-code-pro, monospace;
--font-size-disable-autozoom: 1rem;
--alert-message-color: #24426c;
--alert-message-border: #cbdbf2;
--alert-message-bg: #dae5f5;
--checkbox-bg: #e2e2e2;
--radio-bg: #ffffff;
}
[data-theme="dark"] {
--black: #ffffff;
--bgcolor: #131313;
--mcolor: #e6e6e6;
--scolor: #a5a5a5;
@@ -44,16 +52,23 @@
--ctrl-dropdown-bgcolor-focus: #000;
--ctrl-dropdown-color-focus: #fff;
--btn-success-bgcolor: #67b279;
--form-disabled-bgcolor: #3d3d3d;
--form-disabled-bgcolor: #2d2d2d;
--form-disabled-color: #a5a5a5;
--logs__text-color: #f3f3f3;
--rt-nodata-bgcolor: #1c1c1c;
--rt-nodata-color: #fff;
--modal-overlay-bgcolor: #1c1c1c;
--modal-overlay-bgcolor: rgba(19, 19, 19, 0.75);
--logs__table-bgcolor: #3d3d3d;
--logs__row--blue-bgcolor: #467fcf;
--logs__row--white-bgcolor: #1c1c1c;
--detailed-info-color: #fff;
--gray300: #f3f3f3;
--loading-bg: #131313;
--alert-message-color: #e6e6e6;
--alert-message-border: #363648;
--alert-message-bg: #363648;
--checkbox-bg: #a4a4a4;
--radio-bg: #a4a4a4;
}
body {

View File

@@ -54,7 +54,7 @@
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid #dee2e6;
border-top: 1px solid var(--card-border-color);
padding: 0.75rem 1.5rem;
}

View File

@@ -60,7 +60,7 @@ const Dashboard = ({
title={t('refresh_btn')}
onClick={() => getAllStats()}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#refresh" />
</svg>
</button>;

View File

@@ -100,7 +100,7 @@ class Table extends Component {
})
}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#edit" />
</svg>
</button>
@@ -110,7 +110,7 @@ class Table extends Component {
onClick={() => handleDelete(url)}
title={t('delete_table_action')}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#delete" />
</svg>
</button>

View File

@@ -1,12 +1,12 @@
.nav-tabs .nav-link.active {
border-color: var(--green-74);
color: var(--green-74);
border-color: var(--btn-success-bgcolor);
color: var(--btn-success-bgcolor);
background: transparent;
}
.nav-tabs .nav-link.active:hover {
border-color: #58a273;
color: #58a273;
border-color: #4b9400;
color: #4b9400;
}
.nav-icon {

View File

@@ -162,7 +162,7 @@ const ClientCell = ({
{content && (
<button className={buttonArrowClass} disabled={processingRules}>
<IconTooltip
className="h-100"
className="icon24"
tooltipClass="button-action--arrow-option-container"
xlinkHref="chevron-down"
triggerClass="button-action--icon"

View File

@@ -5,7 +5,7 @@
border-radius: 4px !important;
pointer-events: auto !important;
background-color: var(--ctrl-bgcolor);
color: var(--scolor);
color: var(--mcolor);
z-index: 102;
overflow-y: auto;
max-height: 100%;

View File

@@ -129,7 +129,6 @@ const Form = (props) => {
const onInputClear = async () => {
setIsLoading(true);
setDebouncedSearch(DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
setIsLoading(false);
};

View File

@@ -24,6 +24,12 @@
--option-border-radius: 4px;
}
[data-theme="dark"] {
--red: rgba(223, 56, 18, 0.25);
--green-pale: rgba(103, 178, 121, 0.25);
--yellow: rgba(247, 181, 0, 0.2);
}
.logs__text {
padding: 0 1px;
text-overflow: ellipsis;
@@ -36,6 +42,14 @@
line-height: 1.5rem;
}
[data-theme="dark"] .logs__text a {
color: var(--gray-f3);
}
[data-theme="dark"] .logs__text a:hover {
color: var(--gray-f3);
}
.logs__text--bold {
font-weight: 600;
}
@@ -66,6 +80,10 @@
border-radius: 4px;
}
[data-theme=dark] .icon--selected {
opacity: 0.75;
}
.text-pre {
white-space: pre-wrap !important;
overflow-wrap: break-word;
@@ -88,6 +106,16 @@
max-height: 100% !important;
}
.icon24 {
width: 24px;
height: 24px;
}
.icon12 {
width: 12px;
height: 12px;
}
.cursor--pointer {
cursor: pointer;
}
@@ -112,6 +140,10 @@
background-color: transparent !important;
}
[data-theme="dark"] .form-control--transparent option {
background-color: var(--card-bgcolor);
}
.input-group-search {
background-color: transparent;
position: relative;
@@ -205,6 +237,12 @@
position: relative;
}
@media screen and (min-width: 1025px) {
.logs__cell--client {
width: 13rem;
}
}
.logs__cell--header__container > .logs__cell--header__item {
border-right: 0;
font-size: 1rem;
@@ -283,7 +321,6 @@
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.button-action:active {
@@ -340,6 +377,10 @@
overflow: hidden;
}
[data-theme="dark"] .tooltip-custom__container .button-action--arrow-option:not(:disabled):hover {
background: var(--ctrl-dropdown-bgcolor-focus);
}
.button-action--arrow-option-container {
overflow: visible;
transform-origin: left;
@@ -372,6 +413,10 @@
background-color: var(--logs__row--blue-bgcolor);
}
[data-theme="dark"] .logs__row--blue .logs__text--link {
color: var(--white);
}
.logs__row--green {
background-color: var(--green-pale);
}
@@ -482,6 +527,10 @@
color: var(--green79);
}
[data-theme="dark"] .logs__question.icon--lightgray {
color: var(--gray-f3);
}
@media (max-width: 1024px) {
.logs__question {
display: none;
@@ -491,3 +540,8 @@
.logs__modal {
max-width: 720px;
}
.logs__modal-wrap {
padding: 1rem 1.5rem;
background-color: var(--card-bgcolor);
}

View File

@@ -195,11 +195,11 @@ const Logs = () => {
onRequestClose={closeModal}
style={{
content: {
width: '100%',
width: 'calc(100% - 32px)',
height: 'fit-content',
left: '50%',
top: 47,
padding: '1rem 1.5rem 1rem',
padding: '0',
maxWidth: '720px',
transform: 'translateX(-50%)',
},

View File

@@ -290,7 +290,7 @@ const ClientsTable = ({
disabled={processingUpdating}
title={t('edit_table_action')}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#edit" />
</svg>
</button>
@@ -301,7 +301,7 @@ const ClientsTable = ({
disabled={processingDeleting}
title={t('delete_table_action')}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#delete" />
</svg>
</button>

View File

@@ -4,7 +4,7 @@
align-items: center;
margin-bottom: 15px;
padding: 10px 15px;
border: 1px solid #eee;
border: 1px solid var(--card-border-color);
border-radius: 4px;
cursor: pointer;
}
@@ -54,6 +54,12 @@
color: #495057;
}
.service__icon svg {
width: 20px;
height: 20px;
fill: #495057;
}
.service--global .service__icon {
display: none;
}

View File

@@ -48,7 +48,7 @@
height: 20px;
min-width: 20px;
margin-right: 10px;
background-color: #e2e2e2;
background-color: var(--checkbox-bg);
background-repeat: no-repeat;
background-position: center center;
background-size: 12px 10px;

View File

@@ -1,6 +1,6 @@
.dropdown-item.active,
.dropdown-item:active {
background-color: var(--green-74);
background-color: var(--btn-success-bgcolor);
}
.dropdown-menu {

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import classNames from 'classnames';
@@ -10,7 +10,7 @@ import i18n from '../../i18n';
import Version from './Version';
import './Footer.css';
import './Select.css';
import { setHtmlLangAttr } from '../../helpers/helpers';
import { setHtmlLangAttr, setUITheme } from '../../helpers/helpers';
import { changeTheme } from '../../actions';
const linksData = [
@@ -36,6 +36,13 @@ const Footer = () => {
const currentTheme = useSelector((state) => (state.dashboard ? state.dashboard.theme : 'auto'));
const profileName = useSelector((state) => (state.dashboard ? state.dashboard.name : ''));
const isLoggedIn = profileName !== '';
const [currentThemeLocal, setCurrentThemeLocal] = useState('auto');
useEffect(() => {
if (!isLoggedIn) {
setUITheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? THEMES.dark : THEMES.light);
}
}, []);
const getYear = () => {
const today = new Date();
@@ -53,6 +60,12 @@ const Footer = () => {
dispatch(changeTheme(value));
};
const onThemeChangedLocal = (event) => {
const { value } = event.target;
setUITheme(value);
setCurrentThemeLocal(value);
};
const renderCopyright = () => <div className="footer__column">
<div className="footer__copyright">
{t('copyright')} &copy; {getYear()}{' '}
@@ -70,24 +83,34 @@ const Footer = () => {
{t(name)}
</a>);
const renderThemeSelect = (currentTheme, isLoggedIn) => {
if (!isLoggedIn) {
return '';
}
const themeSelectOptions = () => (
Object.values(THEMES)
.map((theme) => (
<option key={theme} value={theme}>
{t(`theme_${theme}`)}
</option>
))
);
return <select
const renderThemeSelect = () => (
<select
className="form-control select select--theme"
value={currentTheme}
onChange={onThemeChanged}
>
{Object.values(THEMES)
.map((theme) => (
<option key={theme} value={theme}>
{t(`theme_${theme}`)}
</option>
))}
</select>;
};
{themeSelectOptions()}
</select>
);
const renderThemeSelectLocal = () => (
<select
className="form-control select select--theme"
value={currentThemeLocal}
onChange={onThemeChangedLocal}
>
{themeSelectOptions()}
</select>
);
return (
<>
@@ -98,7 +121,7 @@ const Footer = () => {
{renderLinks(linksData)}
</div>
<div className="footer__column footer__column--theme">
{renderThemeSelect(currentTheme, isLoggedIn)}
{isLoggedIn ? renderThemeSelect() : renderThemeSelectLocal()}
</div>
<div className="footer__column footer__column--language">
<select

View File

@@ -86,10 +86,10 @@ const Icons = () => (
d="m19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1 -2.83 0l-.06-.06a1.65 1.65 0 0 0 -1.82-.33 1.65 1.65 0 0 0 -1 1.51v.17a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2v-.09a1.65 1.65 0 0 0 -1.08-1.51 1.65 1.65 0 0 0 -1.82.33l-.06.06a2 2 0 0 1 -2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0 -1.51-1h-.17a2 2 0 0 1 -2-2 2 2 0 0 1 2-2h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0 -.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h.08a1.65 1.65 0 0 0 1-1.51v-.17a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 -.33 1.82v.08a1.65 1.65 0 0 0 1.51 1h.17a2 2 0 0 1 2 2 2 2 0 0 1 -2 2h-.09a1.65 1.65 0 0 0 -1.51 1z" />
</symbol>
<symbol id="refresh" viewBox="0 0 24 24" stroke="currentColor" fill="none"
strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<path d="M23 4v6h-6M1 20v-6h6" />
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
<symbol id="refresh" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<polyline points="23 4 23 10 17 10"></polyline>
<polyline points="1 20 1 14 7 14"></polyline>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
</symbol>
<symbol id="dns_privacy" viewBox="0 0 30 30" stroke="none" fill="currentColor"
@@ -198,7 +198,7 @@ const Icons = () => (
</svg>
</symbol>
<symbol id="chevron-down" viewBox="0 0 24 24">
<symbol id="chevron-down" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fillRule="evenodd">
<path d="M0 0h24v24H0z" fill="#878787" fillOpacity=".01" />
<path stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"

View File

@@ -11,6 +11,10 @@
font-size: 0.7rem;
}
.card-chart-bg {
color: var(--black);
}
.card-chart-bg path[d^="M0,32"] {
transform: translateY(32px);
}

View File

@@ -25,9 +25,9 @@ const Line = ({
theme={{
crosshair: {
line: {
stroke: 'black',
stroke: 'currentColor',
strokeWidth: 1,
strokeOpacity: 0.35,
strokeOpacity: 0.5,
},
},
}}

View File

@@ -13,7 +13,7 @@
z-index: 100;
width: 100%;
min-height: 100vh;
background-color: rgba(255, 255, 255, 0.48);
background-color: var(--loading-bg);
}
.loading:after {

View File

@@ -23,14 +23,29 @@
background-color: var(--rt-nodata-bgcolor);
}
.ReactTable .-loading .-loading-inner {
color: var(--gray300);
}
.ReactTable .-pagination input, .ReactTable .-pagination select {
color: var(--rt-nodata-color);
background-color: var(--rt-nodata-bgcolor);
}
[data-theme=dark] .ReactTable .rt-table::-webkit-scrollbar-track {
background-color: var(--card-bgcolor);
}
[data-theme=dark] .ReactTable .rt-table::-webkit-scrollbar-thumb {
background-color: #888888;
}
[data-theme=dark] .ReactTable .-pagination .-btn {
color: var(--scolor);
background-color: var(--ctrl-bgcolor);
filter: invert(1);
}
[data-theme=dark] .ReactTable .-pagination .-btn:disabled {
opacity: 1;
}
.rt-tr-group.logs__row--red {

View File

@@ -2579,16 +2579,58 @@ fieldset:disabled a.btn {
box-shadow: 0 0 0 2px rgba(134, 142, 150, 0.5);
}
[data-theme=dark] .btn-secondary {
color: #868e96;
background-color: transparent;
background-image: none;
border-color: #868e96;
}
[data-theme=dark] .btn-secondary:hover {
color: #fff;
background-color: #868e96;
border-color: #868e96;
}
[data-theme=dark] .btn-secondary:focus,
[data-theme=dark] .btn-secondary.focus {
box-shadow: none;
}
[data-theme=dark] .btn-secondary:focus-visible,
[data-theme=dark] .btn-secondary.focus {
box-shadow: 0 0 0 2px rgba(134, 142, 150, 0.5);
}
[data-theme=dark] .btn-secondary.disabled,
[data-theme=dark] .btn-secondary:disabled {
color: #868e96;
background-color: transparent;
border: none;
}
[data-theme=dark] .btn-secondary:not(:disabled):not(.disabled):active,
[data-theme=dark] .btn-secondary:not(:disabled):not(.disabled).active {
color: #fff;
background-color: #868e96;
border-color: #868e96;
}
[data-theme=dark] .btn-secondary:not(:disabled):not(.disabled):active:focus,
[data-theme=dark] .btn-secondary:not(:disabled):not(.disabled).active:focus {
box-shadow: 0 0 0 2px rgba(134, 142, 150, 0.5);
}
.btn-success {
color: #fff;
background-color: var(--btn-success-bgcolor);
border-color: #5eba00;
border-color: var(--btn-success-bgcolor);
}
.btn-success:hover {
color: #fff;
background-color: #4b9400;
border-color: #448700;
border-color: #4b9400;
}
.btn-success:focus,
@@ -2608,7 +2650,7 @@ fieldset:disabled a.btn {
.show>.btn-success.dropdown-toggle {
color: #fff;
background-color: #448700;
border-color: #3e7a00;
border-color: #448700;
}
.btn-success:not(:disabled):not(.disabled):active:focus,
@@ -3251,6 +3293,10 @@ tbody.collapse.show {
border-radius: 3px;
}
[data-theme=dark] .dropdown-menu {
border: 1px solid var(--card-border-color);
}
.dropup .dropdown-menu {
margin-top: 0;
margin-bottom: 0.125rem;
@@ -5001,9 +5047,9 @@ tbody.collapse.show {
}
.alert-primary {
color: #24426c;
background-color: #dae5f5;
border-color: #cbdbf2;
color: var(--alert-message-color);
background-color: var(--alert-message-bg);
border-color: var(--alert-message-border);
}
.alert-primary hr {
@@ -5043,9 +5089,9 @@ tbody.collapse.show {
}
.alert-info {
color: #24587e;
background-color: #daeefc;
border-color: #cbe7fb;
color: var(--alert-message-color);
background-color: var(--alert-message-bg);
border-color: var(--alert-message-border);
}
.alert-info hr {
@@ -5509,7 +5555,7 @@ button.close {
-ms-flex-pack: justify;
justify-content: space-between;
padding: 1rem;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid var(--card-border-color);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
@@ -5539,7 +5585,7 @@ button.close {
-ms-flex-pack: end;
justify-content: flex-end;
padding: 1rem;
border-top: 1px solid #e9ecef;
border-top: 1px solid var(--card-border-color);
}
.modal-footer> :not(:first-child) {
@@ -13679,6 +13725,10 @@ Card alert
color: #6e7687;
}
[data-theme=dark] .dropdown-item {
color: #ffffff;
}
.dropdown-menu-arrow:before {
position: absolute;
top: -6px;
@@ -13706,6 +13756,10 @@ Card alert
content: "";
}
[data-theme=dark] .dropdown-menu-arrow:after {
border-bottom: 5px solid var(--card-border-color);
}
.dropdown-menu-arrow.dropdown-menu-right:before,
.dropdown-menu-arrow.dropdown-menu-right:after {
left: auto;
@@ -14263,7 +14317,7 @@ textarea[cols] {
.custom-control-label:before {
border: 1px solid rgba(0, 40, 100, 0.12);
background-color: #fff;
background-color: var(--radio-bg);
background-size: 0.5rem;
}
@@ -15473,6 +15527,10 @@ a.tag-addon:hover {
transition: 0.3s border-color, 0.3s background-color;
}
[data-theme=dark] .custom-switch-indicator {
opacity: 0.75;
}
.custom-switch-indicator:before {
content: "";
position: absolute;

View File

@@ -3,7 +3,7 @@
justify-content: space-between;
margin-bottom: 15px;
padding: 10px 0;
border-bottom: 1px solid #e8e8e8;
border-bottom: 1px solid var(--card-border-color);
overflow: auto;
}
@@ -40,6 +40,10 @@
opacity: 0.6;
}
[data-theme=dark] .tab__control {
filter: invert(1);
}
@media screen and (min-width: 768px) {
.tab__control {
white-space: normal;

View File

@@ -39,7 +39,7 @@ const Version = () => {
disabled={processingVersion}
title={t('check_updates_now')}
>
<svg className="icons">
<svg className="icons icon12">
<use xlinkHref="#refresh" />
</svg>
</button>}

View File

@@ -53,7 +53,7 @@ export const STATUS_COLORS = {
export const REPOSITORY = {
URL: 'https://github.com/AdguardTeam/AdGuardHome',
TRACKERS_DB:
'https://github.com/AdguardTeam/AdGuardHome/tree/master/client/src/helpers/trackers/adguard.json',
'https://github.com/AdguardTeam/AdGuardHome/tree/master/client/src/helpers/trackers/trackers.json',
ISSUES: 'https://github.com/AdguardTeam/AdGuardHome/issues/new/choose',
};

View File

@@ -101,7 +101,7 @@ export default {
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_13.txt"
},
"POL_polish_filters_for_pi_hole": {
"name": "POL: Polish filters for Pi hole",
"name": "POL: Polish filters for Pi-hole",
"categoryId": "regional",
"homepage": "https://www.certyficate.it/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_14.txt"
@@ -160,12 +160,6 @@ export default {
"homepage": "https://github.com/DandelionSprout/adfilt",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_6.txt"
},
"energized_spark": {
"name": "Energized Spark",
"categoryId": "general",
"homepage": "https://energized.pro/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_28.txt"
},
"hagezi_personal": {
"name": "HaGeZi Personal Black \u0026 White",
"categoryId": "general",
@@ -241,7 +235,7 @@ export default {
"urlhaus_filter_online": {
"name": "Malicious URL Blocklist (URLHaus)",
"categoryId": "security",
"homepage": "https://gitlab.com/malware-filter/urlhaus-filter",
"homepage": "https://urlhaus.abuse.ch/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt"
},
"windowsspyblocker_hosts_spy_rules": {

View File

@@ -1,172 +0,0 @@
{
"timeUpdated": "2021-12-15",
"categories": {
"0": "audio_video_player",
"1": "comments",
"2": "customer_interaction",
"3": "pornvertising",
"4": "advertising",
"5": "essential",
"6": "site_analytics",
"7": "social_media",
"8": "misc",
"9": "cdn",
"10": "hosting",
"11": "unknown",
"12": "extensions",
"13": "email",
"101": "mobile_analytics"
},
"trackers": {
"akamai_technologies": {
"name": "Akamai Technologies",
"categoryId": 9,
"url": "https://www.akamai.com/",
"companyId": "akamai"
},
"apple": {
"name": "Apple",
"categoryId": 8,
"url": "https://www.apple.com/",
"companyId": "apple"
},
"apple_ads": {
"name": "Apple Search Ads",
"categoryId": 4,
"url": "https://searchads.apple.com/",
"companyId": "apple"
},
"facebook_audience": {
"name": "Facebook Audience Network",
"categoryId": 4,
"url": "https://www.facebook.com/business/products/audience-network",
"companyId": "facebook"
},
"crashlytics": {
"name": "Crashlytics",
"categoryId": 101,
"url": "https://crashlytics.com/",
"companyId": null
},
"flurry": {
"name": "Flurry",
"categoryId": 101,
"url": "http://www.flurry.com/",
"companyId": "verizon"
},
"hockeyapp": {
"name": "HockeyApp",
"categoryId": 101,
"url": "https://hockeyapp.net/",
"companyId": null
},
"firebase": {
"name": "Firebase",
"categoryId": 101,
"url": "https://firebase.google.com/",
"companyId": "google"
},
"appsflyer": {
"name": "AppsFlyer",
"categoryId": 101,
"url": "https://www.appsflyer.com/",
"companyId": "appsflyer"
},
"yandex_appmetrica": {
"name": "Yandex AppMetrica",
"categoryId": 101,
"url": "https://appmetrica.yandex.com/",
"companyId": "yandex"
},
"adjust": {
"name": "Adjust",
"categoryId": 101,
"url": "https://www.adjust.com/",
"companyId": "adjust"
},
"branch": {
"name": "Branch.io",
"categoryId": 101,
"url": "https://branch.io/",
"companyId": "branch_metrics_inc"
},
"markmonitor": {
"name": "MarkMonitor",
"categoryId": 4,
"url": "https://www.markmonitor.com/",
"companyId": "markmonitor"
},
"appcenter": {
"name": "Microsoft App Center",
"categoryId": 5,
"url": "https://appcenter.ms/",
"companyId": null
},
"unity_ads": {
"name": "Unity Ads",
"categoryId": 4,
"url": "https://unity.com/solutions/mobile-business/monetize-your-game",
"companyId": null
},
"azure": {
"name": "Microsoft Azure",
"categoryId": 10,
"url": "https://azure.microsoft.com/",
"companyId": "microsoft"
},
"button": {
"name": "Button",
"categoryId": 4,
"url": "https://www.usebutton.com/",
"companyId": null
},
"netflix": {
"name": "Netflix",
"categoryId": 8,
"url": "https://www.netflix.com/",
"companyId": null
},
"mail.ru_banner": {
"name": "Mail.Ru Banner Network",
"categoryId": 4,
"url": "http://mail.ru/",
"companyId": "vk"
},
"mail.ru_counter": {
"name": "Mail.Ru Counter",
"categoryId": 2,
"url": "http://mail.ru/",
"companyId": "vk"
},
"mail.ru_group": {
"name": "Mail.Ru Group",
"categoryId": 7,
"url": "http://mail.ru/",
"companyId": "vk"
}
},
"trackerDomains": {
"akadns.net": "akamai_technologies",
"akamaiedge.net": "akamai_technologies",
"apple.com": "apple",
"apple-dns.net": "apple",
"aaplimg.com": "apple",
"icloud.com": "apple",
"mzstatic.com": "apple",
"iadsdk.apple.com": "apple_ads",
"graph.facebook.com": "facebook_audience",
"crashlytics.com": "crashlytics",
"flurry.com": "flurry",
"hockeyapp.net": "hockeyapp",
"app-measurement.com": "firebase",
"appsflyer.com": "appsflyer",
"appmetrica.yandex.com": "yandex_appmetrica",
"adjust.com": "adjust",
"mobileapptracking.com": "branch",
"edgecastcdn.net": "markmonitor",
"appcenter.ms": "appcenter",
"unityads.unity3d.com": "unity_ads",
"azure.com": "azure",
"bttn.io": "button"
}
}

View File

@@ -1,6 +1,5 @@
import whotracksmeDb from './whotracksme.json';
import whotracksmeWebsites from './whotracksme_web.json';
import adguardDb from './adguard.json';
import trackersDb from './trackers.json';
import { REPOSITORY } from '../constants';
/**
@@ -39,6 +38,7 @@ const getWhotracksmeUrl = (trackerId) => {
/**
* Gets the source metadata for the specified tracker
*
* @param {TrackerData} trackerData tracker data
* @returns {source} source metadata or null if no matching tracker found
*/
@@ -64,14 +64,26 @@ export const getSourceData = (trackerData) => {
};
/**
* Gets tracker data in the specified database
* Converts the JSON string source into numeric source for AdGuard Home
*
* @param {TrackerData} trackerData tracker data
* @returns {number} source number
*/
const convertSource = (sourceStr) => {
if (!sourceStr || sourceStr !== 'AdGuard') {
return sources.WHOTRACKSME;
}
return sources.ADGUARD;
};
/**
* Gets tracker data from the trackers database
*
* @param {String} domainName domain name to check
* @param {*} trackersDb trackers database
* @param {number} source source ID
* @returns {TrackerData} tracker data or null if no matching tracker found
*/
const getTrackerDataFromDb = (domainName, trackersDb, source) => {
export const getTrackerData = (domainName) => {
if (!domainName) {
return null;
}
@@ -88,7 +100,7 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
if (trackerId) {
const trackerData = trackersDb.trackers[trackerId];
const categoryName = trackersDb.categories[trackerData.categoryId];
trackerData.source = source;
const source = convertSource(trackerData.source);
const sourceData = getSourceData(trackerData);
return {
@@ -105,22 +117,3 @@ const getTrackerDataFromDb = (domainName, trackersDb, source) => {
// No tracker found for the specified domain
return null;
};
/**
* Gets tracker data from the trackers database
*
* @param {String} domainName domain name to check
* @returns {TrackerData} tracker data or null if no matching tracker found
*/
export const getTrackerData = (domainName) => {
if (!domainName) {
return null;
}
let data = getTrackerDataFromDb(domainName, adguardDb, sources.ADGUARD);
if (!data) {
data = getTrackerDataFromDb(domainName, whotracksmeDb, sources.WHOTRACKSME);
}
return data;
};

View File

@@ -1,5 +1,5 @@
{
"timeUpdated": "2022-10-15T00:14:03.765Z",
"timeUpdated": "2023-03-08T00:09:48.692Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@@ -16,7 +16,8 @@
"12": "extensions",
"13": "email",
"14": "consent",
"15": "telemetry"
"15": "telemetry",
"101": "mobile_analytics"
},
"trackers": {
"163": {
@@ -872,10 +873,11 @@
"companyId": "adgoto"
},
"adguard": {
"name": "Adguard",
"categoryId": 12,
"name": "AdGuard",
"categoryId": 8,
"url": "https://adguard.com/",
"companyId": null
"companyId": "adguard",
"source": "AdGuard"
},
"adhands": {
"name": "AdHands",
@@ -951,9 +953,10 @@
},
"adjust": {
"name": "Adjust",
"categoryId": 6,
"categoryId": 101,
"url": "https://www.adjust.com/",
"companyId": "adjust"
"companyId": "adjust",
"source": "AdGuard"
},
"adk2": {
"name": "adk2",
@@ -2141,7 +2144,8 @@
"name": "Akamai Technologies",
"categoryId": 9,
"url": "https://www.akamai.com/",
"companyId": "akamai"
"companyId": "akamai",
"source": "AdGuard"
},
"akamoihd.net": {
"name": "akamoihd.net",
@@ -2541,9 +2545,10 @@
},
"appsflyer": {
"name": "AppsFlyer",
"categoryId": 6,
"categoryId": 101,
"url": "https://www.appsflyer.com/",
"companyId": "appsflyer"
"companyId": "appsflyer",
"source": "AdGuard"
},
"apptv": {
"name": "appTV",
@@ -6767,55 +6772,64 @@
"name": "Facebook",
"categoryId": 4,
"url": "https://www.facebook.com",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_beacon": {
"name": "Facebook Beacon",
"categoryId": 7,
"url": "http://www.facebook.com/beacon/faq.php",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_cdn": {
"name": "Facebook CDN",
"categoryId": 9,
"url": "https://www.facebook.com",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_connect": {
"name": "Facebook Connect",
"categoryId": 6,
"url": "https://developers.facebook.com/connect.php",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_conversion_tracking": {
"name": "Facebook Conversion Tracking",
"categoryId": 4,
"url": "http://www.facebook.com/",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_custom_audience": {
"name": "Facebook Custom Audience",
"categoryId": 4,
"url": "https://www.facebook.com",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_graph": {
"name": "Facebook Social Graph",
"categoryId": 7,
"url": "https://developers.facebook.com/docs/reference/api/",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_impressions": {
"name": "Facebook Impressions",
"categoryId": 4,
"url": "https://www.facebook.com/",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facebook_social_plugins": {
"name": "Facebook Social Plugins",
"categoryId": 7,
"url": "https://developers.facebook.com/plugins",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"facetz.dca": {
"name": "Facetz.DCA",
@@ -7095,9 +7109,10 @@
},
"flurry": {
"name": "Flurry",
"categoryId": 6,
"categoryId": 101,
"url": "http://www.flurry.com/",
"companyId": "verizon"
"companyId": "verizon",
"source": "AdGuard"
},
"flxone": {
"name": "FLXONE",
@@ -8903,7 +8918,8 @@
"name": "Instagram",
"categoryId": 8,
"url": "https://www.facebook.com/",
"companyId": "facebook"
"companyId": "meta",
"source": "AdGuard"
},
"instant_check_mate": {
"name": "Instant Check Mate",
@@ -10493,19 +10509,22 @@
"name": "Mail.Ru Banner Network",
"categoryId": 4,
"url": "http://mail.ru/",
"companyId": "megafon"
"companyId": "vk",
"source": "AdGuard"
},
"mail.ru_counter": {
"name": "Mail.Ru Counter",
"categoryId": 2,
"url": "https://corp.megafon.com/",
"companyId": "megafon"
"url": "http://mail.ru/",
"companyId": "vk",
"source": "AdGuard"
},
"mail.ru_group": {
"name": "Mail.Ru Group",
"categoryId": 7,
"url": "http://mail.ru/",
"companyId": "megafon"
"companyId": "vk",
"source": "AdGuard"
},
"mailchimp_tracking": {
"name": "MailChimp Tracking",
@@ -10631,7 +10650,8 @@
"name": "MarkMonitor",
"categoryId": 4,
"url": "https://www.markmonitor.com/",
"companyId": "markmonitor"
"companyId": "markmonitor",
"source": "AdGuard"
},
"marktest": {
"name": "Marktest",
@@ -11691,9 +11711,10 @@
},
"netflix": {
"name": "Netflix",
"categoryId": 8,
"url": null,
"companyId": null
"categoryId": 0,
"url": "https://www.netflix.com/",
"companyId": "netflix",
"source": "AdGuard"
},
"netletix": {
"name": "Netletix",
@@ -17969,19 +17990,22 @@
"name": "Vk.com",
"categoryId": 7,
"url": "https://vk.com/",
"companyId": "megafon"
"companyId": "vk",
"source": "AdGuard"
},
"vkontakte": {
"name": "VKontakte",
"categoryId": 7,
"url": "https://vk.com/",
"companyId": "megafon"
"companyId": "vk",
"source": "AdGuard"
},
"vkontakte_widgets": {
"name": "VKontakte Widgets",
"categoryId": 7,
"url": "http://vk.com/developers.php",
"companyId": "megafon"
"url": "https://dev.vk.com/",
"companyId": "vk",
"source": "AdGuard"
},
"vntsm.com": {
"name": "Venatus Media",
@@ -19200,6 +19224,363 @@
"categoryId": 4,
"url": "http://www.zypmedia.com/",
"companyId": "zypmedia"
},
"adguard_dns": {
"name": "AdGuard DNS",
"categoryId": 8,
"url": "https://adguard-dns.io/",
"companyId": "adguard",
"source": "AdGuard"
},
"adguard_vpn": {
"name": "AdGuard VPN",
"categoryId": 8,
"url": "https://adguard-vpn.com/",
"companyId": "adguard",
"source": "AdGuard"
},
"appcenter": {
"name": "Microsoft App Center",
"categoryId": 5,
"url": "https://appcenter.ms/",
"companyId": null,
"source": "AdGuard"
},
"alibaba_cloud": {
"name": "Alibaba Cloud",
"categoryId": 10,
"url": "https://www.alibabacloud.com/",
"companyId": "alibaba",
"source": "AdGuard"
},
"alibaba_ucbrowser": {
"name": "UC Browser",
"categoryId": 8,
"url": "https://ucweb.com/",
"companyId": "alibaba",
"source": "AdGuard"
},
"apple": {
"name": "Apple",
"categoryId": 8,
"url": "https://www.apple.com/",
"companyId": "apple",
"source": "AdGuard"
},
"apple_ads": {
"name": "Apple Search Ads",
"categoryId": 4,
"url": "https://searchads.apple.com/",
"companyId": "apple",
"source": "AdGuard"
},
"azure": {
"name": "Microsoft Azure",
"categoryId": 10,
"url": "https://azure.microsoft.com/",
"companyId": "microsoft",
"source": "AdGuard"
},
"azure_blob_storage": {
"name": "Azure Blob Storage",
"categoryId": 8,
"url": "https://azure.microsoft.com/en-us/products/storage/blobs",
"companyId": "microsoft",
"source": "AdGuard"
},
"bitwarden": {
"name": "Bitwarden",
"categoryId": 8,
"url": "https://bitwarden.com/",
"companyId": "bitwarden",
"source": "AdGuard"
},
"branch": {
"name": "Branch.io",
"categoryId": 101,
"url": "https://branch.io/",
"companyId": "branch_metrics_inc",
"source": "AdGuard"
},
"button": {
"name": "Button",
"categoryId": 4,
"url": "https://www.usebutton.com/",
"companyId": null,
"source": "AdGuard"
},
"crashlytics": {
"name": "Crashlytics",
"categoryId": 101,
"url": "https://crashlytics.com/",
"companyId": null,
"source": "AdGuard"
},
"edgio": {
"name": "Edgio",
"categoryId": 9,
"url": "https://edg.io/",
"companyId": "edgio",
"source": "AdGuard"
},
"element": {
"name": "Element",
"categoryId": 7,
"url": "https://element.io/",
"companyId": "element",
"source": "AdGuard"
},
"facebook_audience": {
"name": "Facebook Audience Network",
"categoryId": 4,
"url": "https://www.facebook.com/business/products/audience-network",
"companyId": "meta",
"source": "AdGuard"
},
"firebase": {
"name": "Firebase",
"categoryId": 101,
"url": "https://firebase.google.com/",
"companyId": "google",
"source": "AdGuard"
},
"gmail": {
"name": "Gmail",
"categoryId": 13,
"url": "https://mail.google.com/",
"companyId": "google",
"source": "AdGuard"
},
"google_trust_services": {
"name": "Google Trust Services",
"categoryId": 5,
"url": "https://pki.goog/",
"companyId": "google",
"source": "AdGuard"
},
"hockeyapp": {
"name": "HockeyApp",
"categoryId": 101,
"url": "https://hockeyapp.net/",
"companyId": null,
"source": "AdGuard"
},
"kik": {
"name": "Kik",
"categoryId": 7,
"url": "https://kik.com/",
"companyId": "kik",
"source": "AdGuard"
},
"lets_encrypt": {
"name": "Let's Encrypt",
"categoryId": 5,
"url": "https://letsencrypt.org/",
"companyId": "lets_encrypt",
"source": "AdGuard"
},
"lgtv": {
"name": "LG TV",
"categoryId": 8,
"url": "https://www.lg.com/",
"companyId": "lgcorp",
"source": "AdGuard"
},
"matrix": {
"name": "Matrix",
"categoryId": 5,
"url": "https://matrix.org/",
"companyId": "matrix",
"source": "AdGuard"
},
"medialab": {
"name": "MediaLab.AI Inc.",
"categoryId": 8,
"url": "https://medialab.la/",
"companyId": "medialab",
"source": "AdGuard"
},
"meganz": {
"name": "Mega Ltd.",
"categoryId": 8,
"url": "https://mega.io/",
"companyId": "meganz",
"source": "AdGuard"
},
"msedge": {
"name": "Microsoft Edge",
"categoryId": 8,
"url": "https://www.microsoft.com/en-us/edge",
"companyId": "microsoft",
"source": "AdGuard"
},
"mozilla": {
"name": "Mozilla Foundation",
"categoryId": 8,
"url": "https://www.mozilla.org/",
"companyId": "mozilla",
"source": "AdGuard"
},
"nab": {
"name": "National Australia Bank",
"categoryId": 8,
"url": "https://www.nab.com.au/",
"companyId": "nab",
"source": "AdGuard"
},
"notion": {
"name": "Notion",
"categoryId": 8,
"url": "https://www.notion.so/",
"companyId": "notion",
"source": "AdGuard"
},
"ntppool": {
"name": "Network Time Protocol",
"categoryId": 5,
"url": "https://ntp.org/",
"companyId": "ntppool",
"source": "AdGuard"
},
"oppo": {
"name": "OPPO",
"categoryId": 101,
"url": "https://www.oppo.com/",
"companyId": "oppo",
"source": "AdGuard"
},
"outlook": {
"name": "Microsoft Outlook",
"categoryId": 13,
"url": "https://outlook.live.com/",
"companyId": "microsoft",
"source": "AdGuard"
},
"oztam": {
"name": "OzTAM",
"categoryId": 8,
"url": "https://oztam.com.au/",
"companyId": "oztam",
"source": "AdGuard"
},
"plex": {
"name": "Plex",
"categoryId": 0,
"url": "https://www.plex.tv/",
"companyId": "plex",
"source": "AdGuard"
},
"qualcomm": {
"name": "Qualcomm",
"categoryId": 8,
"url": "https://www.qualcomm.com/",
"companyId": "qualcomm",
"source": "AdGuard"
},
"recaptcha": {
"name": "reCAPTCHA",
"categoryId": 8,
"url": "https://www.google.com/recaptcha/about/",
"companyId": "google",
"source": "AdGuard"
},
"sectigo": {
"name": "Sectigo Limited",
"categoryId": 5,
"url": "https://www.solaredge.com/",
"companyId": "sectigo",
"source": "AdGuard"
},
"showrss": {
"name": "showRSS",
"categoryId": 8,
"url": "https://showrss.info/",
"companyId": "showrss",
"source": "AdGuard"
},
"similarweb": {
"name": "SimilarWeb",
"categoryId": 6,
"url": "https://www.similarweb.com/",
"companyId": "similarweb",
"source": "AdGuard"
},
"slack": {
"name": "Slack",
"categoryId": 8,
"url": "https://www.slack.com/",
"companyId": "salesforce",
"source": "AdGuard"
},
"solaredge": {
"name": "SolarEdge Technologies, Inc.",
"categoryId": 8,
"url": "https://www.solaredge.com/",
"companyId": "solaredge",
"source": "AdGuard"
},
"telstra": {
"name": "Telstra",
"categoryId": 8,
"url": "https://www.telstra.com.au/",
"companyId": "telstra",
"source": "AdGuard"
},
"unity_ads": {
"name": "Unity Ads",
"categoryId": 4,
"url": "https://unity.com/solutions/mobile-business/monetize-your-game",
"companyId": null,
"source": "AdGuard"
},
"vscode": {
"name": "Visual Studio Code",
"categoryId": 8,
"url": "https://code.visualstudio.com/",
"companyId": "microsoft",
"source": "AdGuard"
},
"whatsapp": {
"name": "WhatsApp",
"categoryId": 8,
"url": "https://www.whatsapp.com/",
"companyId": "meta",
"source": "AdGuard"
},
"windows_maps": {
"name": "Windows Maps",
"categoryId": 8,
"url": "https://www.microsoft.com/store/apps/9wzdncrdtbvb",
"companyId": "microsoft",
"source": "AdGuard"
},
"windows_notifications": {
"name": "The Windows Push Notification Services",
"categoryId": 8,
"url": "https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/windows-push-notification-services--wns--overview",
"companyId": "microsoft",
"source": "AdGuard"
},
"windows_time": {
"name": "Windows Time Service",
"categoryId": 8,
"url": "https://learn.microsoft.com/en-us/windows-server/networking/windows-time-service/how-the-windows-time-service-works",
"companyId": "microsoft",
"source": "AdGuard"
},
"windowsupdate": {
"name": "Windows Update",
"categoryId": 9,
"url": "https://support.microsoft.com/en-us/windows/windows-update-faq-8a903416-6f45-0718-f5c7-375e92dddeb2",
"companyId": "microsoft",
"source": "AdGuard"
},
"yandex_appmetrica": {
"name": "Yandex AppMetrica",
"categoryId": 101,
"url": "https://appmetrica.yandex.com/",
"companyId": "yandex",
"source": "AdGuard"
}
},
"trackerDomains": {
@@ -20740,7 +21121,7 @@
"facebook.net": "facebook",
"fbcdn.net": "facebook_cdn",
"fbsbx.com": "facebook_cdn",
"graph.facebook.com": "facebook_graph",
"graph.facebook.com": "facebook_audience",
"facetz.net": "facetz.dca",
"adsfac.eu": "facilitate_digital",
"adsfac.net": "facilitate_digital",
@@ -21273,7 +21654,7 @@
"isocket.com": "isocket",
"ispot.tv": "ispot.tv",
"itineraire.info": "itineraire.info",
"apple.com": "itunes_link_maker",
"apple.com": "apple",
"autolinkmaker.itunes.apple.com": "itunes_link_maker",
"ity.im": "ity.im",
"iubenda.com": "iubenda.com",
@@ -21727,7 +22108,7 @@
"azurewebsites.net": "microsoft",
"cloudapp.net": "microsoft",
"gfx.ms": "microsoft",
"live.com": "microsoft",
"live.com": "outlook",
"microsoft.com": "microsoft",
"microsoftonline-p.com": "microsoft",
"microsoftonline.com": "microsoft",
@@ -23529,6 +23910,191 @@
"zukxd6fkxqn.com": "zukxd6fkxqn.com",
"zwaar.net": "zwaar",
"zwaar.org": "zwaar",
"extend.tv": "zypmedia"
"extend.tv": "zypmedia",
"adtidy.org": "adguard",
"agrd.io": "adguard",
"adguard.app": "adguard",
"adguard.io": "adguard",
"adguard.org": "adguard",
"adguard-dns.com": "adguard_dns",
"adguard-dns.io": "adguard_dns",
"adguardvpn.com": "adguard_vpn",
"adguard-vpn.com": "adguard_vpn",
"adguard-vpn.online": "adguard_vpn",
"adjust.net.in": "adjust",
"adj.st": "adjust",
"adjust.io": "adjust",
"adjust.world": "adjust",
"apptrace.com": "adjust",
"akadns.net": "akamai_technologies",
"akamaiedge.net": "akamai_technologies",
"akaquill.net": "akamai_technologies",
"aliapp.org": "alibaba.com",
"alibabachengdun.com": "alibaba.com",
"alibabausercontent.com": "alibaba.com",
"aliexpress.com": "alibaba.com",
"alikunlun.com": "alibaba.com",
"aliyuncs.com": "alibaba.com",
"alibabacloud.com": "alibaba_cloud",
"alibabadns.com": "alibaba_cloud",
"aliyun.com": "alibaba_cloud",
"ucweb.com": "alibaba_ucbrowser",
"alipayobjects.com": "alipay.com",
"amazoncrl.com": "amazon",
"aamazoncognito.com": "amazon",
"amazonbrowserapp.es": "amazon",
"amazonbrowserapp.co.uk": "amazon",
"amazon.sa": "amazon",
"amazon.nl": "amazon",
"amazon.in": "amazon",
"amazon.com.mx": "amazon",
"amazon.com.au": "amazon",
"amazon-corp.com": "amazon",
"a2z.com": "amazon",
"amazontrust.com": "amazon_cdn",
"associates-amazon.com": "amazon_cdn",
"amazonpay.in": "amazon_payments",
"amazonvideo.com": "amazon_video",
"taobao.com": "taobao",
"appcenter.ms": "appcenter",
"iadsdk.apple.com": "apple_ads",
"me.com": "apple",
"apple.news": "apple",
"apple-dns.net": "apple",
"aaplimg.com": "apple",
"icloud.com": "apple",
"itunes.com": "apple",
"icloud-content.com": "apple",
"mzstatic.com": "apple",
"cdn-apple.com": "apple",
"apple-mapkit.com": "apple",
"icons.axm-usercontent-apple.com": "apple",
"apple-cloudkit.com": "apple",
"apzones.com": "apple",
"apple-livephotoskit.com": "apple",
"safebrowsing.apple": "apple",
"safebrowsing.g.applimg.com": "apple",
"applvn.com": "applovin",
"applovin.com": "applovin",
"blob.core.windows.net": "azure_blob_storage",
"azure.com": "azure",
"trafficmanager.net": "azure",
"bitwarden.com": "bitwarden",
"mobileapptracking.com": "branch",
"bttn.io": "button",
"cloudflare-dns.com": "cloudflare",
"crashlytics.com": "crashlytics",
"phicdn.net": "digicert_trust_seal",
"alphacdn.net": "edgio",
"edg.io": "edgio",
"edgecast.com": "edgio",
"edgecastcdn.net": "edgio",
"edgecastdns.net": "edgio",
"sigmacdn.net": "edgio",
"element.io": "element",
"riot.im": "element",
"app-measurement.com": "firebase",
"flipboard.com": "flipboard",
"flurry.com": "flurry",
"ghcr.io": "github",
"github.dev": "github",
"gmail.com": "gmail",
"googlehosted.com": "google_appspot",
"gvt1.com": "google_servers",
"gvt2.com": "google_servers",
"gvt3.com": "google_servers",
"pki.goog": "google_trust_services",
"hockeyapp.net": "hockeyapp",
"kik.com": "kik",
"apikik.com": "kik",
"kik-live.com": "kik",
"slatic.net": "lazada",
"lencr.org": "lets_encrypt",
"letsencrypt.org": "lets_encrypt",
"lgsmartad.com": "lgtv",
"lgtvcommon.com": "lgtv",
"lgtvsdp.com": "lgtv",
"lge.com": "lgtv",
"lg.com": "lgtv",
"markmonitor.com": "markmonitor",
"matrix.org": "matrix",
"medialab.la": "medialab",
"media-lab.ai": "medialab",
"mega.co.nz": "meganz",
"mega.io": "meganz",
"mega.nz": "meganz",
"e-msedge.net": "msedge",
"l-msedge.net": "msedge",
"firefox.com": "mozilla",
"mozaws.net": "mozilla",
"mozgcp.net": "mozilla",
"mozilla.com": "mozilla",
"mozilla.net": "mozilla",
"mozilla.org": "mozilla",
"nflximg.com": "netflix",
"nab.com": "nab",
"nab.com.au": "nab",
"nab.net": "nab",
"nabgroup.com": "nab",
"national.com.au": "nab",
"nationalaustraliabank.com.au": "nab",
"nationalbank.com.au": "nab",
"notion.so": "notion",
"ntp.org": "ntppool",
"ntppool.org": "ntppool",
"oppomobile.com": "oppo",
"heytapmobi.com": "oppo",
"heytapmobile.com": "oppo",
"heytapdl.com": "oppo",
"allawnos.com": "oppo",
"allawntech.com": "oppo",
"hotmail.com": "outlook",
"outlook.com": "outlook",
"oztam.com.au": "oztam",
"plex.tv": "plex",
"plex.direct": "plex",
"xtracloud.net": "qualcomm",
"qualcomm.com": "qualcomm",
"recaptcha.net": "recaptcha",
"sectigo.com": "sectigo",
"showrss.info": "showrss",
"similarweb.io": "similarweb",
"similarweb.com": "similarweb",
"slack.com": "slack",
"slackb.com": "slack",
"slack-edge.com": "slack",
"slack-imgs.com": "slack",
"addlive.io": "snap",
"feelinsonice.com": "snap",
"sc-cdn.net": "snap",
"sc-corp.net": "snap",
"sc-gw.com": "snap",
"sc-jpl.com": "snap",
"sc-prod.net": "snap",
"snap-dev.net": "snap",
"snapads.com": "snap",
"snapkit.com": "snap",
"solaredge.com": "solaredge",
"telstra.com.au": "telstra",
"telstra.com": "telstra",
"usertrust.com": "trustlogo",
"unityads.unity3d.com": "unity_ads",
"exp-tas.com": "vscode",
"vscode-unpkg.net": "vscode",
"v0cdn.net": "vscode",
"vscode-cdn.net": "vscode",
"whatsapp.net": "whatsapp",
"whatsapp.com": "whatsapp",
"maps.windows.com": "windows_maps",
"client.wns.windows.com": "windows_notifications",
"time.windows.com": "windows_time",
"windowsupdate.com": "windowsupdate",
"ya.ru": "yandex",
"yandex.by": "yandex",
"yandex.com": "yandex",
"yandex.com.tr": "yandex",
"yandex.fr": "yandex",
"yandex.kz": "yandex",
"appmetrica.yandex.com": "yandex_appmetrica"
}
}

View File

@@ -21,11 +21,16 @@
margin: 0 auto;
padding: 30px 20px;
line-height: 1.6;
background-color: #fff;
background-color: var(--card-bgcolor);
box-shadow: 0 1px 4px rgba(74, 74, 74, 0.36);
border-radius: 3px;
}
[data-theme=dark] .setup__container {
box-shadow: none;
border: 1px solid var(--card-border-color);
}
@media screen and (min-width: 768px) {
.setup__container {
width: 650px;
@@ -39,6 +44,10 @@
max-width: 140px;
}
[data-theme=dark] .setup__logo {
filter: invert(1);
}
.setup__nav {
text-align: center;
}

View File

@@ -13,6 +13,10 @@
min-height: 100vh;
}
[data-theme=dark] .login__logo {
filter: invert(1);
}
.login__form {
margin: auto;
padding: 40px 15px 100px;

View File

@@ -36,7 +36,7 @@ class Login extends Component {
<div className="login">
<div className="login__form">
<div className="text-center mb-6">
<img src={logo} className="h-6" alt="logo" />
<img src={logo} className="h-6 login__logo" alt="logo" />
</div>
<Form onSubmit={this.handleSubmit} processing={processingLogin} />
<div className="login__info">

View File

@@ -1,6 +0,0 @@
scripts
node_modules
postcss.config.js
src/lib/entities
src/lib/apis
openApi

View File

@@ -1,5 +0,0 @@
{
"extends": [
"./scripts/lint/dev.js"
]
}

View File

@@ -1,18 +0,0 @@
declare module '*.pcss' {
const content: {[className: string]: string};
export default content;
}
declare module '*.css' {
const content: {[className: string]: string};
export default content;
}
declare module '*.png'
declare module '*.jpg'
declare let AUTH_TOKEN: string;
declare let MAIN_TOKEN: string | undefined;
declare let NO_CAPTCHA: boolean | undefined;
declare module 'dygraphs';
declare module '@novnc/novnc/core/rfb';
// cp - CloudPayments script
declare let cp: any;
declare const DEV: any;

View File

@@ -1,89 +0,0 @@
{
"author": "Performix",
"private": true,
"name": "adguard-home",
"version": "0.1.0",
"scripts": {
"build": "webpack --config ./scripts/webpack/webpack.config.prod.js",
"start": "webpack serve --config ./scripts/webpack/webpack.config.dev.js",
"generate": "rm -rf ./src/lib/entities ./src/lib/apis && ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/generator/index.ts",
"translations:check": "ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/plugins/checkTranslations.ts",
"lint": "eslint -c ./scripts/lint/prod.js --ext .tsx --ext .ts ./",
"go:build": "cd .. && make REBUILD_CLIENT=0 build",
"go:run": "sudo ../AdguardHome"
},
"license": "ISC",
"dependencies": {
"@adguard/translate": "^0.2.0",
"@ant-design/icons": "^4.4.0",
"@sentry/react": "^5.27.0",
"antd": "^4.7.2",
"classnames": "^2.2.6",
"dayjs": "^1.9.3",
"formik": "^2.2.0",
"mobx": "^6.0.1",
"mobx-react-lite": "^3.0.1",
"qs": "^6.9.4",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-router-dom": "^5.2.0",
"recharts": "^2.0.3"
},
"devDependencies": {
"@types/classnames": "^2.2.10",
"@types/qs": "^6.9.5",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"@types/react-redux": "^7.1.9",
"@types/react-router-dom": "^5.1.6",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"antd-dayjs-webpack-plugin": "^1.0.1",
"autoprefixer": "^10.0.1",
"connect-history-api-fallback": "^1.6.0",
"copy-webpack-plugin": "^6.2.1",
"css-loader": "^5.0.0",
"eslint": "^7.11.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-config-airbnb-typescript": "^12.0.0",
"eslint-import-resolver-typescript": "^2.3.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"file-loader": "^6.1.1",
"html-webpack-plugin": "^4.5.0",
"http-proxy-middleware": "^1.0.6",
"less": "^3.12.2",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^1.1.1",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss": "^8.1.2",
"postcss-calc": "^7.0.5",
"postcss-css-variables": "^0.17.0",
"postcss-custom-media": "^7.0.8",
"postcss-import": "^13.0.0",
"postcss-inline-svg": "^4.1.0",
"postcss-loader": "^4.0.4",
"postcss-mixins": "^7.0.1",
"postcss-modules": "^3.2.2",
"postcss-nested": "^5.0.1",
"postcss-preset-env": "^6.7.0",
"postcss-reporter": "^7.0.1",
"postcss-variables": "^1.1.1",
"style-loader": "^2.0.0",
"stylelint": "^13.7.2",
"stylelint-webpack-plugin": "^2.1.1",
"terser-webpack-plugin": "^5.0.0",
"ts-loader": "^8.0.6",
"ts-morph": "^8.1.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.3",
"url-loader": "^4.1.1",
"webpack": "^5.10.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.2.0",
"yaml": "^1.10.0"
}
}

View File

@@ -1,17 +0,0 @@
module.exports = {
plugins: [
['postcss-import', {}],
['postcss-nested', {}],
['postcss-custom-media', {}],
['postcss-variables', {}],
['postcss-calc', {}],
['postcss-mixins', {}],
['postcss-preset-env', { stage: 3, features: { 'nesting-rules': true } }],
['postcss-reporter', { clearMessages: true }],
['postcss-inline-svg', {
paths: ['frontend/icons', 'vendor/adguard/utils-bundle/src/Resources/frontend/icons'],
svgo: { plugins: [{ cleanupAttrs: true }] }
}],
['autoprefixer'],
]
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16pt" height="16pt"
viewBox="0 0 16 16" version="1.1">
<g id="surface1">
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
d="M 8 0 C 10.5 0 13.515625 0.574219 16 1.835938 L 15.996094 2.542969 C 15.957031 5.605469 15.410156 11.71875 8 16 C 0.5 11.667969 0.03125 5.460938 0.00390625 2.433594 L 0 1.835938 C 2.484375 0.574219 5.5 0 8 0 Z M 11.769531 4.203125 L 11.761719 4.203125 L 7.890625 8.160156 L 6.433594 6.4375 C 5.738281 5.644531 4.792969 6.25 4.570312 6.40625 L 7.929688 10.285156 L 12.570312 4.136719 C 12.230469 3.867188 11.933594 4.054688 11.769531 4.203125 Z M 11.769531 4.203125 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 801 B

View File

@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<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 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">
<title>AdGuard Home</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="app"></div>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<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">
<title>Setup AdGuard Home</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="app"></div>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<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">
<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>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="app"></div>
</body>
</html>

View File

@@ -1,12 +0,0 @@
export const OPEN_API_PATH = '../openapi/openapi.yaml';
export const ENT_DIR = './src/lib/entities';
export const API_DIR = './src/lib/apis';
export const LOCALE_FOLDER_PATH = './src/lib/intl/__locales';
export const TRANSLATOR_CLASS_NAME = 'Translator';
export const USE_INTL_NAME = 'useIntl';
export const trimQuotes = (str: string) => {
return str.replace(/\'|\"/g, '');
};
export const GENERATOR_ENTITY_ALLIAS = 'Entities/';

View File

@@ -1,18 +0,0 @@
import * as fs from 'fs';
import * as YAML from 'yaml';
import { OPEN_API_PATH } from '../consts';
import EntitiesGenerator from './src/generateEntities';
import ApisGenerator from './src/generateApis';
const generateApi = (openApi: Record<string, any>) => {
const ent = new EntitiesGenerator(openApi);
ent.save();
const api = new ApisGenerator(openApi);
api.save();
}
const openApiFile = fs.readFileSync(OPEN_API_PATH, 'utf8');
generateApi(YAML.parse(openApiFile));

View File

@@ -1,317 +0,0 @@
/* eslint-disable no-template-curly-in-string */
/* eslint-disable @typescript-eslint/no-unused-expressions */
import * as fs from 'fs';
import * as path from 'path';
import { stringify } from 'qs';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as morph from 'ts-morph';
import {
API_DIR as API_DIR_CONST,
GENERATOR_ENTITY_ALLIAS,
} from '../../consts';
import { toCamel, capitalize, schemaParamParser } from './utils';
const API_DIR = path.resolve(API_DIR_CONST);
if (!fs.existsSync(API_DIR)) {
fs.mkdirSync(API_DIR);
}
const { Project, QuoteKind } = morph;
class ApiGenerator {
project = new Project({
tsConfigFilePath: './tsconfig.json',
addFilesFromTsConfig: false,
manipulationSettings: {
quoteKind: QuoteKind.Single,
usePrefixAndSuffixTextForRename: false,
useTrailingCommas: true,
},
});
openapi: Record<string, any>;
serverUrl: string;
paths: any;
/* interface Controllers {
[controller: string]: {
[operationId: string]: { parameters - from opneApi, responses - from opneApi, method }
}
} */
controllers: Record<string, any> = {};
apis: morph.SourceFile[] = [];
constructor(openapi: Record<string, any>) {
this.openapi = openapi;
this.paths = openapi.paths;
this.serverUrl = openapi.servers[0].url;
Object.keys(this.paths).forEach((pathKey) => {
Object.keys(this.paths[pathKey]).forEach((method) => {
const {
tags, operationId, parameters, responses, requestBody, security,
} = this.paths[pathKey][method];
const controller = toCamel((tags ? tags[0] : pathKey.split('/')[1]).replace('-controller', ''));
if (this.controllers[controller]) {
this.controllers[controller][operationId] = {
parameters,
responses,
method,
requestBody,
security,
pathKey: pathKey.replace(/{/g, '${'),
};
} else {
this.controllers[controller] = { [operationId]: {
parameters,
responses,
method,
requestBody,
security,
pathKey: pathKey.replace(/{/g, '${'),
} };
}
});
});
this.generateApiFiles();
}
generateApiFiles = () => {
Object.keys(this.controllers).forEach(this.generateApiFile);
};
generateApiFile = (cName: string) => {
const apiFile = this.project.createSourceFile(`${API_DIR}/${cName}.ts`);
apiFile.addStatements([
'// This file was autogenerated. Please do not change.',
'// All changes will be overwrited on commit.',
'',
]);
// const schemaProperties = schemas[schemaName].properties;
const importEntities: any[] = [];
// add api class to file
const apiClass = apiFile.addClass({
name: `${capitalize(cName)}Api`,
isDefaultExport: true,
});
// get operations of controller
const controllerOperations = this.controllers[cName];
const operationList = Object.keys(controllerOperations).sort();
// for each operation add fetcher
operationList.forEach((operation) => {
const {
requestBody, responses, parameters, method, pathKey, security,
} = controllerOperations[operation];
const queryParams: any[] = []; // { name, type }
const bodyParam: any[] = []; // { name, type }
let hasResponseBodyType: /* boolean | ReturnType<schemaParamParser> */ false | [string, boolean, boolean, boolean, boolean] = false;
let contentType = '';
if (parameters) {
parameters.forEach((p: any) => {
const [
pType, isArray, isClass, isImport,
] = schemaParamParser(p.schema, this.openapi);
if (isImport) {
importEntities.push({ type: pType, isClass });
}
if (p.in === 'query') {
queryParams.push({
name: p.name, type: `${pType}${isArray ? '[]' : ''}`, hasQuestionToken: !p.required });
}
});
}
if (queryParams.length > 0) {
const imp = apiFile.getImportDeclaration((i) => {
return i.getModuleSpecifierValue() === 'qs';
}); if (!imp) {
apiFile.addImportDeclaration({
moduleSpecifier: 'qs',
defaultImport: 'qs',
});
}
}
if (requestBody) {
let content = requestBody.content;
const { $ref }: { $ref: string } = requestBody;
if (!content && $ref) {
const name = $ref.split('/').pop() as string;
content = this.openapi.components.requestBodies[name].content;
}
[contentType] = Object.keys(content);
const data = content[contentType];
const [
pType, isArray, isClass, isImport,
] = schemaParamParser(data.schema, this.openapi);
if (isImport) {
importEntities.push({ type: pType, isClass });
bodyParam.push({ name: pType.toLowerCase(), type: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`, isClass, pType });
} else {
bodyParam.push({ name: 'data', type: `${pType}${isArray ? '[]' : ''}` });
}
}
if (responses['200']) {
const { content, headers } = responses['200'];
if (content && (content['*/*'] || content['application/json'])) {
const { schema, examples } = content['*/*'] || content['application/json'];
if (!schema) {
process.exit(0);
}
const propType = schemaParamParser(schema, this.openapi);
const [pType, , isClass, isImport] = propType;
if (isImport) {
importEntities.push({ type: pType, isClass });
}
hasResponseBodyType = propType;
}
}
let returnType = '';
if (hasResponseBodyType) {
const [pType, isArray, isClass] = hasResponseBodyType as any;
let data = `Promise<${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
returnType = data;
} else {
returnType = 'Promise<number';
}
const shouldValidate = bodyParam.filter(b => b.isClass);
if (shouldValidate.length > 0) {
returnType += ' | string[]';
}
// append Error to default type return;
returnType += ' | Error>';
const fetcher = apiClass.addMethod({
isAsync: true,
isStatic: true,
name: operation,
returnType,
});
const params = [...queryParams, ...bodyParam].sort((a, b) => (Number(!!a.hasQuestionToken) - Number(!!b.hasQuestionToken)));
fetcher.addParameters(params);
fetcher.setBodyText((w) => {
// Add data to URLSearchParams
if (contentType === 'text/plain') {
bodyParam.forEach((b) => {
w.writeLine(`const params = String(${b.name});`);
});
} else {
if (shouldValidate.length > 0) {
w.writeLine(`const haveError: string[] = [];`);
shouldValidate.forEach((b) => {
w.writeLine(`const ${b.name}Valid = new ${b.pType}(${b.name});`);
w.writeLine(`haveError.push(...${b.name}Valid.validate());`);
});
w.writeLine(`if (haveError.length > 0) {`);
w.writeLine(` return Promise.resolve(haveError);`)
w.writeLine(`}`);
}
}
// Switch return of fetch in case on queryParams
if (queryParams.length > 0) {
w.writeLine('const queryParams = {');
queryParams.forEach((q) => {
w.writeLine(` ${q.name}: ${q.name},`);
});
w.writeLine('}');
w.writeLine(`return await fetch(\`${this.serverUrl}${pathKey}?\${qs.stringify(queryParams, { arrayFormat: 'comma' })}\`, {`);
} else {
w.writeLine(`return await fetch(\`${this.serverUrl}${pathKey}\`, {`);
}
// Add method
w.writeLine(` method: '${method.toUpperCase()}',`);
// add Fetch options
if (contentType && contentType !== 'multipart/form-data') {
w.writeLine(' headers: {');
w.writeLine(` 'Content-Type': '${contentType}',`);
w.writeLine(' },');
}
if (contentType) {
switch (contentType) {
case 'text/plain':
w.writeLine(' body: params,');
break;
default:
w.writeLine(` body: JSON.stringify(${bodyParam.map((b) => b.isClass ? `${b.name}Valid.serialize()` : b.name).join(', ')}),`);
break;
}
}
// Handle response
if (hasResponseBodyType) {
w.writeLine('}).then(async (res) => {');
w.writeLine(' if (res.status === 200) {');
w.writeLine(' return res.json();');
} else {
w.writeLine('}).then(async (res) => {');
w.writeLine(' if (res.status === 200) {');
w.writeLine(' return res.status;');
}
// Handle Error
w.writeLine(' } else {');
w.writeLine(' return new Error(String(res.status));');
w.writeLine(' }');
w.writeLine('})');
});
});
const imports: any[] = [];
const types: string[] = [];
importEntities.forEach((i) => {
const { type } = i;
if (!types.includes(type)) {
imports.push(i);
types.push(type);
}
});
imports.sort((a,b) => a.type > b.type ? 1 : -1).forEach((ie) => {
const { type: pType, isClass } = ie;
if (isClass) {
apiFile.addImportDeclaration({
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
defaultImport: pType,
namedImports: [`I${pType}`],
});
} else {
apiFile.addImportDeclaration({
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
namedImports: [pType],
});
}
});
this.apis.push(apiFile);
};
save = () => {
this.apis.forEach(async (e) => {
await e.saveSync();
});
};
}
export default ApiGenerator;

View File

@@ -1,603 +0,0 @@
import * as fs from 'fs';
import * as path from 'path';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as morph from 'ts-morph';
import { ENT_DIR } from '../../consts';
import { TYPES, toCamel, schemaParamParser, uncapitalize } from './utils';
const { Project, QuoteKind } = morph;
const EntDir = path.resolve(ENT_DIR);
if (!fs.existsSync(EntDir)) {
fs.mkdirSync(EntDir);
}
class EntitiesGenerator {
project = new Project({
tsConfigFilePath: './tsconfig.json',
addFilesFromTsConfig: false,
manipulationSettings: {
quoteKind: QuoteKind.Single,
usePrefixAndSuffixTextForRename: false,
useTrailingCommas: true,
},
});
openapi: Record<string, any>;
schemas: Record<string, any>;
schemaNames: string[];
entities: morph.SourceFile[] = [];
constructor(openapi: Record<string, any>) {
this.openapi = openapi;
this.schemas = openapi.components.schemas;
this.schemaNames = Object.keys(this.schemas);
this.generateEntities();
}
generateEntities = () => {
this.schemaNames.forEach(this.generateEntity);
};
generateEntity = (sName: string) => {
const { properties, type, oneOf } = this.schemas[sName];
const notAClass = !properties && TYPES[type as keyof typeof TYPES];
if (oneOf) {
this.generateOneOf(sName);
return;
}
if (notAClass) {
this.generateEnum(sName);
} else {
this.generateClass(sName);
}
};
generateEnum = (sName: string) => {
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
entityFile.addStatements([
'// This file was autogenerated. Please do not change.',
'// All changes will be overwrited on commit.',
'',
]);
const { enum: enumMembers } = this.schemas[sName];
entityFile.addEnum({
name: sName,
members: enumMembers.map((e: string) => ({ name: e.toUpperCase(), value: e })),
isExported: true,
});
this.entities.push(entityFile);
};
generateOneOf = (sName: string) => {
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
entityFile.addStatements([
'// This file was autogenerated. Please do not change.',
'// All changes will be overwrited on commit.',
'',
]);
const importEntities: { type: string, isClass: boolean }[] = [];
const entities = this.schemas[sName].oneOf.map((elem: any) => {
const [
pType, isArray, isClass, isImport,
] = schemaParamParser(elem, this.openapi);
importEntities.push({ type: pType, isClass });
return { type: pType, isArray };
});
entityFile.addTypeAlias({
name: sName,
isExported: true,
type: entities.map((e: any) => e.isArray ? `I${e.type}[]` : `I${e.type}`).join(' | '),
})
// add import
importEntities.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
const { type: pType, isClass } = ie;
if (isClass) {
entityFile.addImportDeclaration({
moduleSpecifier: `./${pType}`,
namedImports: [`I${pType}`],
});
} else {
entityFile.addImportDeclaration({
moduleSpecifier: `./${pType}`,
namedImports: [pType],
});
}
});
this.entities.push(entityFile);
}
generateClass = (sName: string) => {
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
entityFile.addStatements([
'// This file was autogenerated. Please do not change.',
'// All changes will be overwrited on commit.',
'',
]);
const { properties: sProps, required, $ref, additionalProperties } = this.schemas[sName];
if ($ref) {
const temp = $ref.split('/');
const importSchemaName = `${temp[temp.length - 1]}`;
entityFile.addImportDeclaration({
defaultImport: importSchemaName,
moduleSpecifier: `./${importSchemaName}`,
namedImports: [`I${importSchemaName}`],
});
entityFile.addTypeAlias({
name: `I${sName}`,
type: `I${importSchemaName}`,
isExported: true,
})
entityFile.addStatements(`export default ${importSchemaName};`);
this.entities.push(entityFile);
return;
}
const importEntities: { type: string, isClass: boolean }[] = [];
const entityInterface = entityFile.addInterface({
name: `I${sName}`,
isExported: true,
});
const sortedSProps = Object.keys(sProps || {}).sort();
const additionalPropsOnly = additionalProperties && sortedSProps.length === 0;
// add server response interface to entityFile
sortedSProps.forEach((sPropName) => {
const [
pType, isArray, isClass, isImport, isAdditional
] = schemaParamParser(sProps[sPropName], this.openapi);
if (isImport) {
importEntities.push({ type: pType, isClass });
}
const propertyType = isAdditional
? `{ [key: string]: ${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''} }`
: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
entityInterface.addProperty({
name: sPropName,
type: propertyType,
hasQuestionToken: !(
(required && required.includes(sPropName)) || sProps[sPropName].required
),
});
});
if (additionalProperties) {
const [
pType, isArray, isClass, isImport, isAdditional
] = schemaParamParser(additionalProperties, this.openapi);
if (isImport) {
importEntities.push({ type: pType, isClass });
}
const type = isAdditional
? `{ [key: string]: ${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''} }`
: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
entityInterface.addIndexSignature({
keyName: 'key',
keyType: 'string',
returnType: additionalPropsOnly ? type : `${type} | undefined`,
});
}
// add import
const imports: { type: string, isClass: boolean }[] = [];
const types: string[] = [];
importEntities.forEach((i) => {
const { type } = i;
if (!types.includes(type)) {
imports.push(i);
types.push(type);
}
});
imports.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
const { type: pType, isClass } = ie;
if (isClass) {
entityFile.addImportDeclaration({
defaultImport: pType,
moduleSpecifier: `./${pType}`,
namedImports: [`I${pType}`],
});
} else {
entityFile.addImportDeclaration({
moduleSpecifier: `./${pType}`,
namedImports: [pType],
});
}
});
const entityClass = entityFile.addClass({
name: sName,
isDefaultExport: true,
});
// addProperties to class;
sortedSProps.forEach((sPropName) => {
const [pType, isArray, isClass, isImport, isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
const isRequred = (required && required.includes(sPropName))
|| sProps[sPropName].required;
const propertyType = isAdditional
? `{ [key: string]: ${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'} }`
: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`;
entityClass.addProperty({
name: `_${sPropName}`,
isReadonly: true,
type: propertyType,
});
const getter = entityClass.addGetAccessor({
name: toCamel(sPropName),
returnType: propertyType,
statements: [`return this._${sPropName};`],
});
const { description, example, minItems, maxItems, maxLength, minLength, maximum, minimum } = sProps[sPropName];
if (description || example) {
getter.addJsDoc(`${example ? `Description: ${description}` : ''}${example ? `\nExample: ${example}` : ''}`);
}
if (minItems) {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MinItems`,
statements: [`return ${minItems};`],
});
}
if (maxItems) {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MaxItems`,
statements: [`return ${maxItems};`],
});
}
if (typeof minLength === 'number') {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MinLength`,
statements: [`return ${minLength};`],
});
}
if (maxLength) {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MaxLength`,
statements: [`return ${maxLength};`],
});
}
if (typeof minimum === 'number') {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MinValue`,
statements: [`return ${minimum};`],
});
}
if (maximum) {
entityClass.addGetAccessor({
isStatic: true,
name: `${toCamel(sPropName)}MaxValue`,
statements: [`return ${maximum};`],
});
}
if (!(isArray && isClass) && !isClass) {
const isEnum = !isClass && isImport;
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
const haveValidationFields = maxLength || typeof minLength === 'number' || maximum || typeof minimum === 'number';
if (isRequired || haveValidationFields) {
const prop = toCamel(sPropName);
const validateField = entityClass.addMethod({
isStatic: true,
name: `${prop}Validate`,
returnType: `boolean`,
parameters: [{
name: prop,
type: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`,
}],
})
validateField.setBodyText((w) => {
w.write('return ');
const nonRequiredCall = isRequired ? prop : `!${prop} ? true : ${prop}`;
if (pType === 'string') {
if (isArray) {
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && (typeof p === 'string' && !!p.trim()), true)`);
} else {
if (typeof minLength === 'number' && maxLength) {
w.write(`(${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength})`);
}
if (typeof minLength !== 'number' || !maxLength) {
w.write(`${isRequired ? `typeof ${prop} === 'string'` : `!${prop} ? true : typeof ${prop} === 'string'`} && !!${nonRequiredCall}.trim()`);
}
}
} else if (pType === 'number') {
if (isArray) {
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && typeof p === 'number', true)`);
} else {
if (typeof minimum === 'number' && maximum) {
w.write(`${isRequired ? `${prop} >= ${minimum} && ${prop} <= ${maximum}` : `!${prop} ? true : ((${prop} >= ${minimum}) && (${prop} <= ${maximum}))`}`);
}
if (typeof minimum !== 'number' || !maximum) {
w.write(`${isRequired ? `typeof ${prop} === 'number'` : `!${prop} ? true : typeof ${prop} === 'number'`}`);
}
}
} else if (pType === 'boolean') {
w.write(`${isRequired ? `typeof ${prop} === 'boolean'` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
} else if (isEnum) {
if (isArray){
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && Object.keys(${pType}).includes(${prop}), true)`);
} else {
w.write(`${isRequired ? `Object.keys(${pType}).includes(${prop})` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
}
}
w.write(';');
});
}
}
});
if (additionalProperties) {
const [
pType, isArray, isClass, isImport, isAdditional
] = schemaParamParser(additionalProperties, this.openapi);
const type = `Record<string, ${pType}${isArray ? '[]' : ''}>`;
entityClass.addProperty({
name: additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`,
isReadonly: true,
type: type,
});
}
// add constructor;
const ctor = entityClass.addConstructor({
parameters: [{
name: 'props',
type: `I${sName}`,
}],
});
ctor.setBodyText((w) => {
if (additionalProperties) {
const [
pType, isArray, isClass, isImport, isAdditional
] = schemaParamParser(additionalProperties, this.openapi);
w.writeLine(`this.${additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`} = Object.entries(props).reduce<Record<string, ${pType}>>((prev, [key, value]) => {`);
if (isClass) {
w.writeLine(` prev[key] = new ${pType}(value!);`);
} else {
w.writeLine(' prev[key] = value!;')
}
w.writeLine(' return prev;');
w.writeLine('}, {})');
return;
}
sortedSProps.forEach((sPropName) => {
const [
pType, isArray, isClass, , isAdditional
] = schemaParamParser(sProps[sPropName], this.openapi);
const req = (required && required.includes(sPropName))
|| sProps[sPropName].required;
if (!req) {
if ((pType === 'boolean' || pType === 'number' || pType ==='string') && !isClass && !isArray) {
w.writeLine(`if (typeof props.${sPropName} === '${pType}') {`);
} else {
w.writeLine(`if (props.${sPropName}) {`);
}
}
if (isAdditional) {
if (isArray && isClass) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => {
return { ...prev, [key]: new ${pType}(p[key])};
},{}))`);
} else if (isClass) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
return { ...prev, [key]: new ${pType}(props.${sPropName}[key])};
},{})`);
} else {
if (pType === 'string' && !isArray) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
return { ...prev, [key]: props.${sPropName}[key].trim()};
},{})`);
} else {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
return { ...prev, [key]: props.${sPropName}[key]};
},{})`);
}
}
} else {
if (isArray && isClass) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => new ${pType}(p));`);
} else if (isClass) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = new ${pType}(props.${sPropName});`);
} else {
if (pType === 'string' && !isArray) {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.trim();`);
} else {
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName};`);
}
}
}
if (!req) {
w.writeLine('}');
}
});
});
// add serialize method;
const serialize = entityClass.addMethod({
isStatic: false,
name: 'serialize',
returnType: `I${sName}`,
});
serialize.setBodyText((w) => {
if (additionalProperties) {
const [
pType, isArray, isClass, isImport, isAdditional
] = schemaParamParser(additionalProperties, this.openapi);
w.writeLine(`return Object.entries(this.${additionalPropsOnly ? 'data' : `${uncapitalize(pType)}Data`}).reduce<Record<string, ${isClass ? 'I' : ''}${pType}>>((prev, [key, value]) => {`);
if (isClass) {
w.writeLine(` prev[key] = value.serialize();`);
} else {
w.writeLine(' prev[key] = value;')
}
w.writeLine(' return prev;');
w.writeLine('}, {})');
return;
}
w.writeLine(`const data: I${sName} = {`);
const unReqFields: string[] = [];
sortedSProps.forEach((sPropName) => {
const req = (required && required.includes(sPropName))
|| sProps[sPropName].required;
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
if (!req) {
unReqFields.push(sPropName);
return;
}
if (isAdditional) {
if (isArray && isClass) {
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }))),`);
} else if (isClass) {
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce<Record<string, any>>((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {}),`);
} else {
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] })),`);
}
} else {
if (isArray && isClass) {
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => p.serialize()),`);
} else if (isClass) {
w.writeLine(` ${sPropName}: this._${sPropName}.serialize(),`);
} else {
w.writeLine(` ${sPropName}: this._${sPropName},`);
}
}
});
w.writeLine('};');
unReqFields.forEach((sPropName) => {
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
w.writeLine(`if (typeof this._${sPropName} !== 'undefined') {`);
if (isAdditional) {
if (isArray && isClass) {
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }), {}));`);
} else if (isClass) {
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {});`);
} else {
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] }), {});`);
}
} else {
if (isArray && isClass) {
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => p.serialize());`);
} else if (isClass) {
w.writeLine(` data.${sPropName} = this._${sPropName}.serialize();`);
} else {
w.writeLine(` data.${sPropName} = this._${sPropName};`);
}
}
w.writeLine(`}`);
});
w.writeLine('return data;');
});
// add validate method
const validate = entityClass.addMethod({
isStatic: false,
name: 'validate',
returnType: `string[]`,
})
validate.setBodyText((w) => {
if (additionalPropsOnly) {
w.writeLine('return []')
return;
}
w.writeLine('const validate = {');
Object.keys(sProps || {}).forEach((sPropName) => {
const [pType, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
const nonRequiredCall = isRequired ? `this._${sPropName}` : `!this._${sPropName} ? true : this._${sPropName}`;
if (isArray && isClass) {
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && p.validate().length === 0, true),`);
} else if (isClass && !isAdditional) {
w.writeLine(` ${sPropName}: ${nonRequiredCall}.validate().length === 0,`);
} else {
if (pType === 'string') {
if (isArray) {
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'string', true),`);
} else {
if (typeof minLength === 'number' && maxLength) {
w.writeLine(` ${sPropName}: (${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength}),`);
}
if (typeof minLength !== 'number' || !maxLength) {
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'string'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'string'`} && !this._${sPropName} ? true : this._${sPropName},`);
}
}
} else if (pType === 'number') {
if (isArray) {
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'number', true),`);
} else {
if (typeof minimum === 'number' && maximum) {
w.writeLine(` ${sPropName}: ${isRequired ? `this._${sPropName} >= ${minimum} && this._${sPropName} <= ${maximum}` : `!this._${sPropName} ? true : ((this._${sPropName} >= ${minimum}) && (this._${sPropName} <= ${maximum}))`},`);
}
if (typeof minimum !== 'number' || !maximum) {
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'number'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'number'`},`);
}
}
} else if (pType === 'boolean') {
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'boolean'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'boolean'`},`);
}
}
});
w.writeLine('};');
w.writeLine('const isError: string[] = [];')
w.writeLine('Object.keys(validate).forEach((key) => {');
w.writeLine(' if (!(validate as any)[key]) {');
w.writeLine(' isError.push(key);');
w.writeLine(' }');
w.writeLine('});');
w.writeLine('return isError;');
});
// add update method;
const update = entityClass.addMethod({
isStatic: false,
name: 'update',
returnType: `${sName}`,
});
update.addParameter({
name: 'props',
type: additionalPropsOnly ? `I${sName}` : `Partial<I${sName}>`,
});
update.setBodyText((w) => { w.writeLine(`return new ${sName}({ ...this.serialize(), ...props });`); });
this.entities.push(entityFile);
};
save = () => {
this.entities.forEach(async (e) => {
await e.saveSync();
});
};
}
export default EntitiesGenerator;

View File

@@ -1,83 +0,0 @@
const toCamel = (s: string) => {
return s.replace(/([-_][a-z])/ig, ($1) => {
return $1.toUpperCase()
.replace('-', '')
.replace('_', '');
});
};
const capitalize = (s: string) => {
return s[0].toUpperCase() + s.slice(1);
};
const uncapitalize = (s: string) => {
return s[0].toLowerCase() + s.slice(1);
};
const TYPES = {
integer: 'number',
float: 'number',
number: 'number',
string: 'string',
boolean: 'boolean',
};
/**
* @param schemaProp: valueof shema.properties[key]
* @param openApi: openapi object
* @returns [propType - basicType or import one, isArray, isClass, isImport]
*/
const schemaParamParser = (schemaProp: any, openApi: any): [string, boolean, boolean, boolean, boolean] => {
let type = '';
let isImport = false;
let isClass = false;
let isArray = false;
let isAdditional = false;
if (schemaProp.$ref || schemaProp.additionalProperties?.$ref) {
const temp = (schemaProp.$ref || schemaProp.additionalProperties?.$ref).split('/');
if (schemaProp.additionalProperties) {
isAdditional = true;
}
type = `${temp[temp.length - 1]}`;
const cl = openApi ? openApi.components.schemas[type] : {};
if (cl.$ref) {
const link = schemaParamParser(cl, openApi);
link.shift();
return [type, ...link] as any;
}
if (cl.type === 'string' && cl.enum) {
isImport = true;
}
if (cl.type === 'object' && !cl.oneOf) {
isClass = true;
isImport = true;
} else if (cl.type === 'array') {
const temp: any = schemaParamParser(cl.items, openApi);
type = `${temp[0]}`;
isArray = true;
isClass = isClass || temp[2];
isImport = isImport || temp[3];
}
} else if (schemaProp.type === 'array') {
const temp: any = schemaParamParser(schemaProp.items, openApi);
type = `${temp[0]}`;
isArray = true;
isClass = isClass || temp[2];
isImport = isImport || temp[3];
} else {
type = (TYPES as Record<any, string>)[schemaProp.type];
}
if (!type) {
// TODO: Fix bug with Error fields.
type = 'any';
// throw new Error('Failed to find entity type');
}
return [type, isArray, isClass, isImport, isAdditional];
};
export { TYPES, toCamel, capitalize, uncapitalize, schemaParamParser };

View File

@@ -1,226 +0,0 @@
import * as fs from 'fs';
import {
Project,
VariableStatement,
SyntaxKind,
Node,
Statement,
ts,
Identifier,
SourceFile,
} from 'ts-morph';
import {
LOCALE_FOLDER_PATH,
TRANSLATOR_CLASS_NAME,
USE_INTL_NAME,
trimQuotes,
} from '../consts';
import { checkForms, AvailableLocales } from '../../src/localization/Translator';
const project = new Project({
tsConfigFilePath: './tsconfig.json',
});
let lang = 'ru';
let option = '';
if (process.argv.length > 2) {
lang = process.argv[2];
option = process.argv[3];
}
const usedTranslations: string[] = [];
const usedPluralTranslations: string[] = [];
const problemFiles: string[] = [];
const sourceFiles = project.getSourceFiles();
const sourceFilesWithIntl = sourceFiles.filter((sf) => {
return !!sf.getImportDeclarations().find((id) => {
return !!id.getNamedImports().find((ni) => ni.getName() === USE_INTL_NAME)
})
});
const getFileUsedIntl = (statements: Statement<ts.Statement>[]) => {
statements.forEach((s) => {
if (s instanceof VariableStatement) {
s.forEachDescendant((node) => {
let intVariableDeclaration: Identifier = null;
switch (node.getKind()) {
case SyntaxKind.VariableDeclaration:
if (node.getSymbol()) {
const name = node.getSymbol().getName();
const callExp = node.getChildren().find((n) => n.getKind() === SyntaxKind.CallExpression);
if (callExp) {
const callExpIden = callExp.getChildren().find(n => n.getKind() === SyntaxKind.Identifier);
if (callExpIden && callExpIden.getSymbol().getName() === USE_INTL_NAME) {
intVariableDeclaration = node as Identifier;
}
}
}
break;
default:
break;
}
if (intVariableDeclaration) {
intVariableDeclaration.findReferencesAsNodes().forEach((fr) => {
if (fr instanceof Node) {
const parent = fr.getParentIfKind(SyntaxKind.PropertyAccessExpression);
if (parent && (parent.getName() === 'getMessage' || parent.getName() === 'getPlural')) {
const syntaxList = parent.getNextSiblings().find((n) => n.getKind() === SyntaxKind.SyntaxList);
if (syntaxList) {
const id = syntaxList.getChildren()[0];
if (id && id.getKind() !== SyntaxKind.StringLiteral) {
problemFiles.push(fr.getSourceFile().getFilePath());
}
if (id) {
usedTranslations.push(trimQuotes(id.getText()));
if (parent.getName() === 'getPlural') {
usedPluralTranslations.push(trimQuotes(id.getText()));
}
}
}
}
}
})
}
});
}
})
}
const getFileUsedTranslations = (file: SourceFile) => {
const namedImport = file.getImportDeclarations().find((id) => !!id.getNamedImports().find((ni) => ni.getName() === TRANSLATOR_CLASS_NAME));
if (namedImport) {
const identifier = namedImport.getImportClause().getNamedImports().find((iden) => iden.getName() === TRANSLATOR_CLASS_NAME);
const translateReferences = identifier.getNodeProperty('name').findReferencesAsNodes();
if (translateReferences.length > 0) {
translateReferences.forEach((identifierNode) => {
if (identifierNode.getParentIfKind(SyntaxKind.TypeReference)) {
const translatorVariable = identifierNode.getParent().getPreviousSibling().getPreviousSiblingIfKind(SyntaxKind.Identifier);
if (translatorVariable) {
translatorVariable.findReferencesAsNodes().forEach((node) => {
const parent = node.getParentIfKind(SyntaxKind.PropertyAccessExpression);
if (parent && (parent.getName() === 'getMessage' || parent.getName() === 'getPlural')) {
const syntaxList = parent.getNextSiblings().find((n) => n.getKind() === SyntaxKind.SyntaxList);
if (syntaxList) {
const id = syntaxList.getChildren()[0];
if (id && id.getKind() !== SyntaxKind.StringLiteral) {
problemFiles.push(parent.getSourceFile().getFilePath());
}
if (id) {
usedTranslations.push(trimQuotes(id.getText()));
if (parent.getName() === 'getPlural') {
usedPluralTranslations.push(trimQuotes(id.getText()));
}
}
}
}
})
}
}
})
}
}
}
sourceFilesWithIntl.forEach((file) => {
getFileUsedIntl(file.getStatements());
})
const sourceFilesWithTranslator = project.getSourceFiles().filter((sf) => {
return !!sf.getImportDeclarations().find((id) => {
return !!id.getNamedImports().find((ni) => ni.getName() === TRANSLATOR_CLASS_NAME)
})
});
sourceFilesWithTranslator.forEach((file) => {
getFileUsedTranslations(file);
})
const filteredUsedTranslations = Array.from(new Set(usedTranslations));
const filteredUsedPluralTranslations = Array.from(new Set(usedPluralTranslations));
if (problemFiles.length) {
console.warn(`\n============== Files where translation id provided not as string ==============\n`);
console.log(problemFiles.join('\n'));
process.exit(255);
}
const allFiles = fs.readdirSync(LOCALE_FOLDER_PATH);
// Use ru or needed language
const translationFile = allFiles.find((file) => file.includes(`${lang}.json`));
if (!translationFile) {
console.error('File not found');
process.exit(255);
}
const translationsObject = JSON.parse(fs.readFileSync(`./src/lib/intl/__locales/${translationFile}`, { flag: 'r+' }) as unknown as string);
const translations = {
locale: translationFile,
messages: Object.keys(translationsObject),
};
const someMessagesNotFound: string[] = [];
const notUsed: string[] = [];
const notFound: string[] = [];
const checkLocaleMessages = (locale: string, messages: string[]) => {
filteredUsedTranslations.forEach(f => {
if (!messages.includes(f)) {
notFound.push(f);
}
});
messages.forEach(t => {
if (!filteredUsedTranslations.includes(t)) {
notUsed.push(t);
}
});
if (notFound.length > 0) {
someMessagesNotFound.push(locale);
}
}
const render = (data: string[], title: string) => {
console.log(`============ ${title} ============`);
console.table(data);
console.log(`============ ${title} ============`);
}
checkLocaleMessages(translations.locale, translations.messages);
const checkPluralForm = () => {
const pluralFormWrong: string[] = [];
filteredUsedPluralTranslations.forEach((id) => {
const message = translationsObject[id];
if (!checkForms(message, lang as AvailableLocales, id)) {
pluralFormWrong.push(id)
}
});
return pluralFormWrong;
}
const plural = checkPluralForm();
if (!option && (someMessagesNotFound.length || plural.length > 0 )) {
someMessagesNotFound.forEach(locale => console.error(`\nSome translatins for ${locale} was not found!\n`));
plural.forEach(id => console.error(`\nTranslation with id: "${id}" - have wrong number of plural forms!\n`));
process.exit(255);
}
if (option) {
switch (option) {
case '--show-missing': {
render(notFound, 'NotFound')
break;
}
case '--show-unused': {
render(notUsed, 'notUsed')
break;
}
case '--check-plurals': {
render(plural, 'Wrong Plural Form')
}
default: {
if (someMessagesNotFound.length) {
someMessagesNotFound.forEach(locale => console.error(`\nSome translatins for ${locale} was not found!\n\n`));
process.exit(255);
}
}
}
}

View File

@@ -1,79 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
ecmaFeatures: {
jsx: true
},
extraFileExtensions: ['mjs', 'tsx', 'ts'],
ecmaVersion: 2020,
sourceType: 'module'
},
plugins: ['react', '@typescript-eslint', 'import'],
env: {
browser: true,
commonjs: true,
es6: true,
es2020: true,
jest: true,
},
settings: {
react: {
pragma: 'React',
version: 'detect',
},
'import/resolver': {
typescript: {
alwaysTryTypes: true
}
},
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
},
rules: {
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/explicit-function-return-type': [0, { allowExpressions: true }],
'@typescript-eslint/indent': ['error', 4],
'@typescript-eslint/interface-name-prefix': [0, { prefixWithI: 'never' }],
'@typescript-eslint/no-explicit-any': [0],
'@typescript-eslint/naming-convention': [2, {
selector: 'enum', format: ['UPPER_CASE', 'PascalCase'],
}],
'@typescript-eslint/no-non-null-assertion': 0,
'arrow-body-style': 'off',
'consistent-return': 0,
curly: [2, 'all'],
'default-case': 0,
'import/no-cycle': 0,
'import/prefer-default-export': 'off',
'import/no-named-as-default': 0,
indent: [0, 4],
'no-alert': 2,
'no-console': 2,
'no-debugger': 2,
'no-underscore-dangle': 'off',
'no-useless-escape': 'off',
'object-curly-newline': 'off',
'react-hooks/exhaustive-deps': 0,
'react/display-name': 0,
'react/jsx-indent-props': ['error', 4],
'react/jsx-indent': ['error', 4],
'react/jsx-one-expression-per-line': 'off',
'react/jsx-props-no-spreading': 0,
'react/prop-types': 'off',
'react/state-in-constructor': 'off',
},
extends: [
'airbnb-base',
'airbnb-typescript/base',
'airbnb/hooks',
'plugin:react/recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
],
globals: {},
};

View File

@@ -1,10 +0,0 @@
module.exports = {
rules: {
'no-alert': 0,
'no-debugger': 0,
'no-console': 0,
},
extends: [
'./common',
],
};

View File

@@ -1,5 +0,0 @@
module.exports = {
extends: [
'./common.js',
],
};

View File

@@ -1,40 +0,0 @@
const yaml = require('yaml');
const fs = require('fs');
const ZERO_HOST = '0.0.0.0';
const LOCALHOST = '127.0.0.1';
const DEFAULT_PORT = 80;
const importConfig = () => {
try {
const doc = yaml.parse(fs.readFileSync('../AdguardHome.yaml', 'utf8'));
const { bind_host, bind_port } = doc;
return {
bind_host,
bind_port,
};
} catch (e) {
return {
bind_host: ZERO_HOST,
bind_port: DEFAULT_PORT,
};
}
};
const getDevServerConfig = () => {
const { bind_host: host, bind_port: port } = importConfig();
const { DEV_SERVER_PORT } = process.env;
const devServerHost = host === ZERO_HOST ? LOCALHOST : host;
const devServerPort = 3000 || port + 8000;
return {
host: devServerHost,
port: devServerPort
};
};
module.exports = {
importConfig,
getDevServerConfig
};

View File

@@ -1,74 +0,0 @@
const path = require('path');
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const tsconfig = require('../../tsconfig.json');
const RESOURCES_PATH = path.resolve(__dirname, '../../');
const HTML_PATH = path.resolve(RESOURCES_PATH, 'public/index.html');
const HTML_INSTALL_PATH = path.resolve(RESOURCES_PATH, 'public/install.html');
module.exports = {
entry: {
install: './src/Install.tsx',
main: './src/App.tsx'
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.pcss'],
alias: Object.keys(tsconfig.compilerOptions.paths).reduce((aliases, key) => {
// Reduce to load aliases from ./tsconfig.json in appropriate for webpack form
const paths = tsconfig.compilerOptions.paths[key].map(p => p.replace('/*', ''));
aliases[key.replace('/*', '')] = path.resolve(
__dirname,
'../../',
tsconfig.compilerOptions.baseUrl,
...paths,
);
return aliases;
}, {}),
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.(woff|woff2)$/,
use: [{
loader: 'file-loader',
options:{
outputPath:'./',
}
}],
},
{
test:/\.(png|jpe?g|gif)$/,
exclude: /(node_modules)/,
use:[{
loader:'file-loader',
options:{
outputPath:'./images',
}
}]
}
],
},
plugins: [
// new AntdDayjsWebpackPlugin()
new HtmlWebpackPlugin({
inject: true,
cache: false,
chunks: ['main'],
template: HTML_PATH,
}),
new HtmlWebpackPlugin({
inject: true,
cache: false,
chunks: ['install'],
filename: 'install.html',
template: HTML_INSTALL_PATH,
}),
],
};

View File

@@ -1,114 +0,0 @@
const history = require('connect-history-api-fallback');
const { merge } = require('webpack-merge');
const path = require('path');
const proxy = require('http-proxy-middleware');
const Webpack = require('webpack');
const { getDevServerConfig } = require('./helpers');
const baseConfig = require('./webpack.config.base');
const devHost = process.env.DEV_HOST
const target = getDevServerConfig();
const options = {
target: devHost || `http://${target.host}:${target.port}`, // target host
changeOrigin: true, // needed for virtual hosted sites
};
const apiProxy = proxy.createProxyMiddleware(options);
module.exports = merge(baseConfig, {
mode: 'development',
output: {
path: path.resolve(__dirname, '../../build2'),
filename: '[name].bundle.js',
},
optimization: {
noEmitOnErrors: true,
},
devServer: {
port: 4000,
historyApiFallback: true,
before: (app) => {
app.use('/control', apiProxy);
app.use(history({
rewrites: [
{
from: /\.(png|jpe?g|gif)$/,
to: (context) => {
const name = context.parsedUrl.pathname.split('/');
return `/images/${name[name.length - 1]}`
}
}, {
from: /\.(woff|woff2)$/,
to: (context) => {
const name = context.parsedUrl.pathname.split('/');
return `/${name[name.length - 1]}`
}
}, {
from: /\.(js|css)$/,
to: (context) => {
const name = context.parsedUrl.pathname.split('/');
return `/${name[name.length - 1]}`
}
}
],
}));
}
},
devtool: 'eval-source-map',
module: {
rules: [
{
enforce: 'pre',
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
configFile: path.resolve(__dirname, '../lint/dev.js'),
}
},
{
test: (resource) => {
return (
resource.indexOf('.pcss')+1
|| resource.indexOf('.css')+1
|| resource.indexOf('.less')+1
) && !(resource.indexOf('.module.')+1);
},
use: ['style-loader', 'css-loader', 'postcss-loader', {
loader: 'less-loader',
options: {
javascriptEnabled: true,
},
}],
},
{
test: /\.module\.p?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]",
}
},
},
'postcss-loader',
],
exclude: /node_modules/,
},
]
},
plugins: [
new Webpack.DefinePlugin({
DEV: true,
'process.env.DEV_SERVER_PORT': JSON.stringify(3000),
}),
new Webpack.HotModuleReplacementPlugin(),
new Webpack.ProgressPlugin(),
],
});

View File

@@ -1,89 +0,0 @@
const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const Webpack = require('webpack');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = merge(baseConfig, {
mode: 'production',
devtool: 'source-map',
stats: 'minimal',
performance : {
hints : false
},
output: {
path: path.resolve(__dirname, '../../../build2/static'),
filename: '[name].bundle.[hash:5].js',
publicPath: '/'
},
optimization: {
minimizer: [new TerserJSPlugin({terserOptions: {
output: {
comments: false,
},
},
extractComments: false,
}), new OptimizeCSSAssetsPlugin({})],
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true,
},
},
},
},
module: {
rules: [
{
test: (resource) => {
return (
resource.indexOf('.pcss')+1
|| resource.indexOf('.css')+1
|| resource.indexOf('.less')+1
) && !(resource.indexOf('.module.')+1);
},
use: [{
loader: MiniCssExtractPlugin.loader,
}, 'css-loader', 'postcss-loader', {
loader: 'less-loader',
options: {
javascriptEnabled: true,
},
}],
exclude: /node_modules/,
},
{
test: /\.module\.p?css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'postcss-loader',
],
exclude: /node_modules/,
}
]
},
plugins: [
new Webpack.DefinePlugin({
DEV: false,
}),
new MiniCssExtractPlugin({
filename: '[name].[hash:5].css',
}),
]
});

View File

@@ -1,18 +0,0 @@
import './main.pcss';
import './lib/ant/ant.less';
import React from 'react';
import ReactDOM from 'react-dom';
import Store, { storeValue } from 'Store';
import './lib/ant';
import App from './components/App';
const Container = () => {
return (
<Store.Provider value={storeValue}>
<App/>
</Store.Provider>
);
};
ReactDOM.render(<Container />, document.getElementById('app'));

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