Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01dbf8e50a | ||
|
|
58d0302c74 | ||
|
|
90ea6a13de | ||
|
|
4f80a7979f | ||
|
|
3812101c25 | ||
|
|
8c79c8b32d | ||
|
|
0afc437252 | ||
|
|
6ae53fea6e | ||
|
|
d925ccf45a | ||
|
|
24243a4b4d | ||
|
|
ef9ebc29b7 | ||
|
|
bd4e0f2b28 | ||
|
|
e952c9f85a | ||
|
|
df67e9a0bd | ||
|
|
7e47ed0879 | ||
|
|
1952ec1527 | ||
|
|
9ca5205d6a | ||
|
|
8a8da9d162 | ||
|
|
6440c71492 | ||
|
|
0b66b2debb | ||
|
|
351c142b91 | ||
|
|
8ce6aed66f | ||
|
|
778d18d816 | ||
|
|
2ea436bbbc | ||
|
|
b5a820b6f4 | ||
|
|
37f5043493 | ||
|
|
7638fb75a3 | ||
|
|
fdad6d3b7b | ||
|
|
074e300974 | ||
|
|
523e068195 | ||
|
|
9a7c617311 | ||
|
|
c214899b75 | ||
|
|
e31a8c8064 | ||
|
|
31cc27df1b | ||
|
|
d60051b5cb | ||
|
|
6770ae2a99 | ||
|
|
84738809ef | ||
|
|
43a08f7eef | ||
|
|
89aeec5f97 | ||
|
|
00ee5415a5 | ||
|
|
b5bd79b61e | ||
|
|
7080c236eb | ||
|
|
bb073eb51e | ||
|
|
7372d49aca | ||
|
|
1d771d4a0a | ||
|
|
dcffdbd98e | ||
|
|
abcc2e4125 | ||
|
|
e453381382 | ||
|
|
9a9913998a | ||
|
|
a3cdb66fe6 | ||
|
|
39aa84e2fd | ||
|
|
1ec2270eb5 | ||
|
|
5d1ee9b538 | ||
|
|
03d3cf57e9 | ||
|
|
2a5465ddb0 | ||
|
|
84efc786d4 | ||
|
|
12e5fb6f7f | ||
|
|
5c66900fdb | ||
|
|
2710ead089 | ||
|
|
839aa420e0 | ||
|
|
4ee42d4092 | ||
|
|
b2950f0718 | ||
|
|
7b1793476d | ||
|
|
f8750ef231 | ||
|
|
cc9283530f | ||
|
|
5a61e56766 | ||
|
|
8f71d514ee | ||
|
|
1b122b228f | ||
|
|
e88c0cdd38 | ||
|
|
3ceb188b81 | ||
|
|
0bc52ecb3c | ||
|
|
36d6c37df3 | ||
|
|
8fd793fdab | ||
|
|
be1909aedb | ||
|
|
e83b4d84ec | ||
|
|
b39b3f38fe | ||
|
|
d50765925b | ||
|
|
e0f8971155 | ||
|
|
f1d53f5610 | ||
|
|
4b4e6b1c72 | ||
|
|
095af716c9 | ||
|
|
e4e9f050df | ||
|
|
638b9b9428 | ||
|
|
0d4d18c595 | ||
|
|
33350c168d | ||
|
|
1bbb882225 | ||
|
|
6e86e8e5c5 | ||
|
|
7cb4c8793a | ||
|
|
4d2c636d9b | ||
|
|
9895d6de43 | ||
|
|
f62b6f94c5 | ||
|
|
e00d9a6c59 | ||
|
|
d2e573bbed | ||
|
|
8c0b6ee70d | ||
|
|
b9c7b1f559 | ||
|
|
7b5b876b0c | ||
|
|
848fb50132 | ||
|
|
6206402cae | ||
|
|
b24dc91ef2 | ||
|
|
d2d58f9058 | ||
|
|
e6fb75f715 | ||
|
|
3b31049636 | ||
|
|
5db091d800 | ||
|
|
8278ecb6a2 | ||
|
|
084eeba24a | ||
|
|
0134840300 | ||
|
|
05335aaf3f | ||
|
|
fb38741036 | ||
|
|
5a07feaffd | ||
|
|
e555c287bf | ||
|
|
ad52f32a3b | ||
|
|
665b27cfbe | ||
|
|
871d558ffd | ||
|
|
881e00e7e3 | ||
|
|
29963b9d75 | ||
|
|
bea8ec2d98 | ||
|
|
851f5fad58 | ||
|
|
1125837420 | ||
|
|
34f9a13da3 | ||
|
|
1432ab4b52 | ||
|
|
8f3a8e3572 | ||
|
|
87d5903cfc | ||
|
|
a50639af23 | ||
|
|
c4b1a70dac | ||
|
|
94677c7a39 | ||
|
|
cd2295ef05 | ||
|
|
944bb42074 | ||
|
|
1350364804 | ||
|
|
3625395d32 | ||
|
|
c32ed90a2e | ||
|
|
9af8c00bca | ||
|
|
0af476ac8e | ||
|
|
3b58e8ce3c | ||
|
|
1fffb80066 | ||
|
|
5f7e6e69e3 | ||
|
|
14e84b23d1 | ||
|
|
562576b8d4 | ||
|
|
532962527f | ||
|
|
99d1914542 | ||
|
|
6fa534dac1 | ||
|
|
3b6da33535 | ||
|
|
ded4a22a67 | ||
|
|
ab0589916f | ||
|
|
3226690d70 | ||
|
|
4d235491f2 | ||
|
|
96b7890404 | ||
|
|
760cc01217 | ||
|
|
c394a05fc1 | ||
|
|
11ab4483e0 | ||
|
|
65747a7f28 | ||
|
|
ca9ddc4222 | ||
|
|
6a2237b513 | ||
|
|
4afccfad8d | ||
|
|
9a32a1d638 | ||
|
|
e091914a06 | ||
|
|
7b584668ac | ||
|
|
30019327da | ||
|
|
fc8eeeaf0a | ||
|
|
11201881c0 | ||
|
|
498dcb8bcb | ||
|
|
5564239bea |
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
dist
|
||||||
|
bin
|
||||||
13
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -17,7 +17,9 @@ body:
|
|||||||
id: adguardhome-sync-version
|
id: adguardhome-sync-version
|
||||||
attributes:
|
attributes:
|
||||||
label: AdguardHome-Sync Version
|
label: AdguardHome-Sync Version
|
||||||
description: What version of adguardhome-sync was running when you discovered this issue?
|
description: |
|
||||||
|
- What version of adguardhome-sync was running when you discovered this issue?
|
||||||
|
- Are you running the docker or binary version?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -27,6 +29,15 @@ body:
|
|||||||
description: What version of adguardhome was running when you discovered this issue?
|
description: What version of adguardhome was running when you discovered this issue?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: os-information
|
||||||
|
attributes:
|
||||||
|
label: OS Information
|
||||||
|
description: |
|
||||||
|
- What Operating System are you running? `cat /etc/os-release`
|
||||||
|
- What is the architecture of your CPU? `uname -m`
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: config
|
id: config
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
28
.github/dependabot.yml
vendored
28
.github/dependabot.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gomod" # See documentation for possible values
|
|
||||||
directory: "/" # Location of package manifests
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
groups:
|
|
||||||
k8s:
|
|
||||||
patterns:
|
|
||||||
- "k8s.io/*"
|
|
||||||
update-types:
|
|
||||||
- "minor"
|
|
||||||
- "patch"
|
|
||||||
onsi:
|
|
||||||
patterns:
|
|
||||||
- "github.com/onsi/*"
|
|
||||||
update-types:
|
|
||||||
- "minor"
|
|
||||||
- "patch"
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -20,6 +20,10 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '32 19 * * 6'
|
- cron: '32 19 * * 6'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
name: docker-image
|
name: docker-images
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: # allows manual triggering
|
workflow_dispatch: # allows manual triggering
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
#pull_request:
|
||||||
|
# branches:
|
||||||
|
# - main
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- published
|
- published
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
images:
|
images:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -44,16 +51,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sed -i -e "s|FROM scratch|FROM ${{ matrix.build.fromImage }}|g" Dockerfile
|
sed -i -e "s|FROM scratch|FROM ${{ matrix.build.fromImage }}|g" Dockerfile
|
||||||
|
|
||||||
- name: Build and push ${{github.event.release.tag_name }}
|
- name: Build images ${{github.event.release.tag_name }}
|
||||||
id: docker_build_release
|
id: docker_build_release
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
if: ${{ github.event.release.tag_name != '' }}
|
if: ${{ github.event.release.tag_name != '' }}
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
pull: true
|
pull: true
|
||||||
push: true
|
push: true
|
||||||
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }},ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }}
|
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }},ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }}
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/arm64,linux/ppc64le
|
||||||
provenance: false
|
provenance: false
|
||||||
build-args: |
|
build-args: |
|
||||||
VERSION=${{ github.event.release.tag_name }}
|
VERSION=${{ github.event.release.tag_name }}
|
||||||
@@ -63,16 +70,16 @@ jobs:
|
|||||||
run: echo "NEW_COMMIT_COUNT=$(git log --oneline --since '24 hours ago' | wc -l)" >> $GITHUB_ENV
|
run: echo "NEW_COMMIT_COUNT=$(git log --oneline --since '24 hours ago' | wc -l)" >> $GITHUB_ENV
|
||||||
if: ${{ github.event.release.tag_name == '' }}
|
if: ${{ github.event.release.tag_name == '' }}
|
||||||
|
|
||||||
- name: Build and push main
|
- name: Build images
|
||||||
id: docker_build_main
|
id: docker_build_main
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
if: ${{ github.event.release.tag_name == '' && env.NEW_COMMIT_COUNT > 0 }}
|
if: ${{ github.event.release.tag_name == '' && env.NEW_COMMIT_COUNT > 0 }}
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
pull: true
|
pull: true
|
||||||
push: true
|
push: ${{ github.ref == 'refs/heads/main' }}
|
||||||
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main
|
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/arm64,linux/ppc64le
|
||||||
provenance: false
|
provenance: false
|
||||||
build-args: |
|
build-args: |
|
||||||
VERSION=main
|
VERSION=main
|
||||||
@@ -80,4 +87,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||||
|
|
||||||
14
.github/workflows/e2e.yaml
vendored
14
.github/workflows/e2e.yaml
vendored
@@ -8,6 +8,10 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
e2e:
|
e2e:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -23,7 +27,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup jq
|
- name: Setup jq
|
||||||
uses: dcarbone/install-jq-action@v2
|
uses: dcarbone/install-jq-action@v3
|
||||||
|
|
||||||
- name: Install kind with registry
|
- name: Install kind with registry
|
||||||
uses: bakito/kind-with-registry-action@main
|
uses: bakito/kind-with-registry-action@main
|
||||||
@@ -33,10 +37,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Helm Chart
|
- name: Install Helm Chart
|
||||||
run: ./testdata/e2e/bin/install-chart.sh ${{ matrix.build.mode }}
|
run: ./testdata/e2e/bin/install-chart.sh ${{ matrix.build.mode }}
|
||||||
|
- name: Wait for pod to start
|
||||||
|
run: ./testdata/e2e/bin/wait-for-start.sh ${{ matrix.build.protocol }}
|
||||||
|
- name: Show origin pre Logs
|
||||||
|
run: ./testdata/e2e/bin/show-origin-logs.sh pre
|
||||||
- name: Wait for sync to finish
|
- name: Wait for sync to finish
|
||||||
run: ./testdata/e2e/bin/wait-for-sync.sh ${{ matrix.build.protocol }}
|
run: ./testdata/e2e/bin/wait-for-sync.sh ${{ matrix.build.protocol }}
|
||||||
- name: Show origin Logs
|
- name: Show origin post Logs
|
||||||
run: ./testdata/e2e/bin/show-origin-logs.sh
|
run: ./testdata/e2e/bin/show-origin-logs.sh post
|
||||||
- name: Show Replica Logs
|
- name: Show Replica Logs
|
||||||
run: ./testdata/e2e/bin/show-replica-logs.sh
|
run: ./testdata/e2e/bin/show-replica-logs.sh
|
||||||
- name: Show Sync Logs
|
- name: Show Sync Logs
|
||||||
|
|||||||
16
.github/workflows/go.yml
vendored
16
.github/workflows/go.yml
vendored
@@ -6,6 +6,10 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
golangci:
|
golangci:
|
||||||
name: lint
|
name: lint
|
||||||
@@ -19,9 +23,9 @@ jobs:
|
|||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
skip-pkg-cache: true
|
skip-cache: true
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: test
|
name: test
|
||||||
@@ -36,6 +40,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
|
|
||||||
|
- name: Model
|
||||||
|
run: make model
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: make test-ci
|
run: make test-ci
|
||||||
|
|
||||||
@@ -58,7 +65,4 @@ jobs:
|
|||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v5
|
run: make test-release
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
args: --skip-publish --snapshot --rm-dist
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,7 +9,7 @@ main
|
|||||||
.adguardhome-sync.yaml
|
.adguardhome-sync.yaml
|
||||||
tmp
|
tmp
|
||||||
bin
|
bin
|
||||||
|
!testdata/e2e/bin
|
||||||
config*.yaml
|
config*.yaml
|
||||||
*.log
|
*.log
|
||||||
wiki
|
wiki
|
||||||
Taskfile.yml
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ linters:
|
|||||||
- govet
|
- govet
|
||||||
- importas
|
- importas
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- megacheck
|
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
version: 2
|
||||||
# This is an example goreleaser.yaml file with some sane defaults.
|
# This is an example goreleaser.yaml file with some sane defaults.
|
||||||
# Make sure to check the documentation at http://goreleaser.com
|
# Make sure to check the documentation at http://goreleaser.com
|
||||||
builds:
|
builds:
|
||||||
@@ -34,12 +35,13 @@ builds:
|
|||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: 'checksums.txt'
|
||||||
snapshot:
|
snapshot:
|
||||||
name_template: "{{ .Tag }}-next"
|
version_template: "{{ .Tag }}-next"
|
||||||
changelog:
|
changelog:
|
||||||
sort: asc
|
sort: asc
|
||||||
filters:
|
filters:
|
||||||
exclude:
|
exclude:
|
||||||
- '^docs:'
|
- '^docs:'
|
||||||
- '^test:'
|
- '^test:'
|
||||||
|
- '^chore'
|
||||||
release:
|
release:
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
|
|||||||
104
.toolbox.mk
Normal file
104
.toolbox.mk
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
## toolbox - start
|
||||||
|
## Generated with https://github.com/bakito/toolbox
|
||||||
|
|
||||||
|
## Current working directory
|
||||||
|
TB_LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
|
||||||
|
## Location to install dependencies to
|
||||||
|
TB_LOCALBIN ?= $(TB_LOCALDIR)/bin
|
||||||
|
$(TB_LOCALBIN):
|
||||||
|
mkdir -p $(TB_LOCALBIN)
|
||||||
|
|
||||||
|
## Tool Binaries
|
||||||
|
TB_DEEPCOPY_GEN ?= $(TB_LOCALBIN)/deepcopy-gen
|
||||||
|
TB_GINKGO ?= $(TB_LOCALBIN)/ginkgo
|
||||||
|
TB_GOFUMPT ?= $(TB_LOCALBIN)/gofumpt
|
||||||
|
TB_GOLANGCI_LINT ?= $(TB_LOCALBIN)/golangci-lint
|
||||||
|
TB_GOLINES ?= $(TB_LOCALBIN)/golines
|
||||||
|
TB_GORELEASER ?= $(TB_LOCALBIN)/goreleaser
|
||||||
|
TB_MOCKGEN ?= $(TB_LOCALBIN)/mockgen
|
||||||
|
TB_OAPI_CODEGEN ?= $(TB_LOCALBIN)/oapi-codegen
|
||||||
|
TB_SEMVER ?= $(TB_LOCALBIN)/semver
|
||||||
|
|
||||||
|
## Tool Versions
|
||||||
|
# renovate: packageName=k8s.io/code-generator/cmd/deepcopy-gen
|
||||||
|
TB_DEEPCOPY_GEN_VERSION ?= v0.32.2
|
||||||
|
# renovate: packageName=mvdan.cc/gofumpt
|
||||||
|
TB_GOFUMPT_VERSION ?= v0.7.0
|
||||||
|
# renovate: packageName=github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
TB_GOLANGCI_LINT_VERSION ?= v1.64.5
|
||||||
|
# renovate: packageName=github.com/segmentio/golines
|
||||||
|
TB_GOLINES_VERSION ?= v0.12.2
|
||||||
|
# renovate: packageName=github.com/goreleaser/goreleaser/v2
|
||||||
|
TB_GORELEASER_VERSION ?= v2.7.0
|
||||||
|
# renovate: packageName=go.uber.org/mock/mockgen
|
||||||
|
TB_MOCKGEN_VERSION ?= v0.5.0
|
||||||
|
# renovate: packageName=github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
|
||||||
|
TB_OAPI_CODEGEN_VERSION ?= v2.4.1
|
||||||
|
# renovate: packageName=github.com/bakito/semver
|
||||||
|
TB_SEMVER_VERSION ?= v1.1.3
|
||||||
|
|
||||||
|
## Tool Installer
|
||||||
|
.PHONY: tb.deepcopy-gen
|
||||||
|
tb.deepcopy-gen: $(TB_DEEPCOPY_GEN) ## Download deepcopy-gen locally if necessary.
|
||||||
|
$(TB_DEEPCOPY_GEN): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/deepcopy-gen || GOBIN=$(TB_LOCALBIN) go install k8s.io/code-generator/cmd/deepcopy-gen@$(TB_DEEPCOPY_GEN_VERSION)
|
||||||
|
.PHONY: tb.ginkgo
|
||||||
|
tb.ginkgo: $(TB_GINKGO) ## Download ginkgo locally if necessary.
|
||||||
|
$(TB_GINKGO): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/ginkgo || GOBIN=$(TB_LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo
|
||||||
|
.PHONY: tb.gofumpt
|
||||||
|
tb.gofumpt: $(TB_GOFUMPT) ## Download gofumpt locally if necessary.
|
||||||
|
$(TB_GOFUMPT): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/gofumpt || GOBIN=$(TB_LOCALBIN) go install mvdan.cc/gofumpt@$(TB_GOFUMPT_VERSION)
|
||||||
|
.PHONY: tb.golangci-lint
|
||||||
|
tb.golangci-lint: $(TB_GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||||
|
$(TB_GOLANGCI_LINT): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/golangci-lint || GOBIN=$(TB_LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(TB_GOLANGCI_LINT_VERSION)
|
||||||
|
.PHONY: tb.golines
|
||||||
|
tb.golines: $(TB_GOLINES) ## Download golines locally if necessary.
|
||||||
|
$(TB_GOLINES): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/golines || GOBIN=$(TB_LOCALBIN) go install github.com/segmentio/golines@$(TB_GOLINES_VERSION)
|
||||||
|
.PHONY: tb.goreleaser
|
||||||
|
tb.goreleaser: $(TB_GORELEASER) ## Download goreleaser locally if necessary.
|
||||||
|
$(TB_GORELEASER): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/goreleaser || GOBIN=$(TB_LOCALBIN) go install github.com/goreleaser/goreleaser/v2@$(TB_GORELEASER_VERSION)
|
||||||
|
.PHONY: tb.mockgen
|
||||||
|
tb.mockgen: $(TB_MOCKGEN) ## Download mockgen locally if necessary.
|
||||||
|
$(TB_MOCKGEN): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/mockgen || GOBIN=$(TB_LOCALBIN) go install go.uber.org/mock/mockgen@$(TB_MOCKGEN_VERSION)
|
||||||
|
.PHONY: tb.oapi-codegen
|
||||||
|
tb.oapi-codegen: $(TB_OAPI_CODEGEN) ## Download oapi-codegen locally if necessary.
|
||||||
|
$(TB_OAPI_CODEGEN): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/oapi-codegen || GOBIN=$(TB_LOCALBIN) go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@$(TB_OAPI_CODEGEN_VERSION)
|
||||||
|
.PHONY: tb.semver
|
||||||
|
tb.semver: $(TB_SEMVER) ## Download semver locally if necessary.
|
||||||
|
$(TB_SEMVER): $(TB_LOCALBIN)
|
||||||
|
test -s $(TB_LOCALBIN)/semver || GOBIN=$(TB_LOCALBIN) go install github.com/bakito/semver@$(TB_SEMVER_VERSION)
|
||||||
|
|
||||||
|
## Reset Tools
|
||||||
|
.PHONY: tb.reset
|
||||||
|
tb.reset:
|
||||||
|
@rm -f \
|
||||||
|
$(TB_LOCALBIN)/deepcopy-gen \
|
||||||
|
$(TB_LOCALBIN)/ginkgo \
|
||||||
|
$(TB_LOCALBIN)/gofumpt \
|
||||||
|
$(TB_LOCALBIN)/golangci-lint \
|
||||||
|
$(TB_LOCALBIN)/golines \
|
||||||
|
$(TB_LOCALBIN)/goreleaser \
|
||||||
|
$(TB_LOCALBIN)/mockgen \
|
||||||
|
$(TB_LOCALBIN)/oapi-codegen \
|
||||||
|
$(TB_LOCALBIN)/semver
|
||||||
|
|
||||||
|
## Update Tools
|
||||||
|
.PHONY: tb.update
|
||||||
|
tb.update: tb.reset
|
||||||
|
toolbox makefile --renovate -f $(TB_LOCALDIR)/Makefile \
|
||||||
|
k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator \
|
||||||
|
mvdan.cc/gofumpt@github.com/mvdan/gofumpt \
|
||||||
|
github.com/golangci/golangci-lint/cmd/golangci-lint \
|
||||||
|
github.com/segmentio/golines \
|
||||||
|
github.com/goreleaser/goreleaser/v2 \
|
||||||
|
go.uber.org/mock/mockgen@github.com/uber-go/mock \
|
||||||
|
github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen \
|
||||||
|
github.com/bakito/semver
|
||||||
|
## toolbox - end
|
||||||
12
Dockerfile
12
Dockerfile
@@ -1,10 +1,8 @@
|
|||||||
FROM golang:1.22-bullseye as builder
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apk update && apk add upx ca-certificates tzdata
|
||||||
apt-get install -y upx ca-certificates tzdata && \
|
|
||||||
apt-get upgrade -y # upgrade to get latest ca-certs
|
|
||||||
|
|
||||||
ARG VERSION=main
|
ARG VERSION=main
|
||||||
ARG BUILD="N/A"
|
ARG BUILD="N/A"
|
||||||
@@ -15,8 +13,10 @@ ENV GO111MODULE=on \
|
|||||||
|
|
||||||
COPY . /go/src/app/
|
COPY . /go/src/app/
|
||||||
|
|
||||||
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/bakito/adguardhome-sync/version.Version=${VERSION} -X github.com/bakito/adguardhome-sync/version.Build=${BUILD}" -o adguardhome-sync . \
|
|
||||||
&& upx -q adguardhome-sync
|
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/bakito/adguardhome-sync/version.Version=${VERSION} -X github.com/bakito/adguardhome-sync/version.Build=${BUILD}" -o adguardhome-sync .
|
||||||
|
|
||||||
|
RUN go version && upx -q adguardhome-sync
|
||||||
|
|
||||||
# application image
|
# application image
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
|||||||
112
Makefile
112
Makefile
@@ -1,101 +1,42 @@
|
|||||||
|
# Include toolbox tasks
|
||||||
|
include ./.toolbox.mk
|
||||||
|
|
||||||
# Run go lint against code
|
# Run go lint against code
|
||||||
lint: golangci-lint
|
lint: tb.golangci-lint
|
||||||
$(GOLANGCI_LINT) run --fix
|
$(TB_GOLANGCI_LINT) run --fix
|
||||||
|
|
||||||
# Run go mod tidy
|
# Run go mod tidy
|
||||||
tidy:
|
tidy:
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
generate: deepcopy-gen
|
generate: tb.deepcopy-gen
|
||||||
@mkdir -p ./tmp
|
@mkdir -p ./tmp
|
||||||
@touch ./tmp/deepcopy-gen-boilerplate.go.txt
|
@touch ./tmp/deepcopy-gen-boilerplate.go.txt
|
||||||
$(DEEPCOPY_GEN) -h ./tmp/deepcopy-gen-boilerplate.go.txt -i ./pkg/types
|
$(TB_DEEPCOPY_GEN) --go-header-file ./tmp/deepcopy-gen-boilerplate.go.txt --bounding-dirs ./pkg/types
|
||||||
|
|
||||||
|
fmt: tb.golines tb.gofumpt
|
||||||
|
$(TB_GOLINES) --base-formatter="$(TB_GOFUMPT)" --max-len=120 --write-output .
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
test: generate lint test-ci
|
test: generate fmt lint test-ci
|
||||||
|
|
||||||
# Run ci tests
|
# Run ci tests
|
||||||
test-ci: mocks tidy ginkgo
|
test-ci: mocks tidy tb.ginkgo
|
||||||
$(GINKGO) --cover --coverprofile coverage.out.tmp ./...
|
$(TB_GINKGO) --cover --coverprofile coverage.out.tmp ./...
|
||||||
cat coverage.out.tmp | grep -v "_generated.go" > coverage.out
|
cat coverage.out.tmp | grep -v "_generated.go" > coverage.out
|
||||||
go tool cover -func=coverage.out
|
go tool cover -func=coverage.out
|
||||||
|
|
||||||
mocks: mockgen
|
mocks: tb.mockgen
|
||||||
$(MOCKGEN) -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client
|
$(TB_MOCKGEN) -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client
|
||||||
$(MOCKGEN) -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags
|
$(TB_MOCKGEN) -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags
|
||||||
|
|
||||||
release: semver goreleaser
|
release: tb.semver tb.goreleaser
|
||||||
@version=$$($(LOCALBIN)/semver); \
|
@version=$$($(TB_SEMVER)); \
|
||||||
git tag -s $$version -m"Release $$version"
|
git tag -s $$version -m"Release $$version"
|
||||||
$(GORELEASER) --clean
|
$(TB_GORELEASER) --clean
|
||||||
|
|
||||||
test-release: goreleaser
|
test-release: tb.goreleaser
|
||||||
$(GORELEASER) --skip=publish --snapshot --clean
|
$(TB_GORELEASER) --skip=publish --snapshot --clean
|
||||||
|
|
||||||
## toolbox - start
|
|
||||||
## Current working directory
|
|
||||||
LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
|
|
||||||
## Location to install dependencies to
|
|
||||||
LOCALBIN ?= $(LOCALDIR)/bin
|
|
||||||
$(LOCALBIN):
|
|
||||||
mkdir -p $(LOCALBIN)
|
|
||||||
|
|
||||||
## Tool Binaries
|
|
||||||
DEEPCOPY_GEN ?= $(LOCALBIN)/deepcopy-gen
|
|
||||||
GINKGO ?= $(LOCALBIN)/ginkgo
|
|
||||||
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
|
|
||||||
GORELEASER ?= $(LOCALBIN)/goreleaser
|
|
||||||
MOCKGEN ?= $(LOCALBIN)/mockgen
|
|
||||||
OAPI_CODEGEN ?= $(LOCALBIN)/oapi-codegen
|
|
||||||
SEMVER ?= $(LOCALBIN)/semver
|
|
||||||
|
|
||||||
## Tool Versions
|
|
||||||
GORELEASER_VERSION ?= v1.25.1
|
|
||||||
|
|
||||||
## Tool Installer
|
|
||||||
.PHONY: deepcopy-gen
|
|
||||||
deepcopy-gen: $(DEEPCOPY_GEN) ## Download deepcopy-gen locally if necessary.
|
|
||||||
$(DEEPCOPY_GEN): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/deepcopy-gen || GOBIN=$(LOCALBIN) go install k8s.io/code-generator/cmd/deepcopy-gen
|
|
||||||
.PHONY: ginkgo
|
|
||||||
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.
|
|
||||||
$(GINKGO): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/ginkgo || GOBIN=$(LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo
|
|
||||||
.PHONY: golangci-lint
|
|
||||||
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
|
||||||
$(GOLANGCI_LINT): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint
|
|
||||||
.PHONY: goreleaser
|
|
||||||
goreleaser: $(GORELEASER) ## Download goreleaser locally if necessary.
|
|
||||||
$(GORELEASER): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/goreleaser || GOBIN=$(LOCALBIN) go install github.com/goreleaser/goreleaser@$(GORELEASER_VERSION)
|
|
||||||
.PHONY: mockgen
|
|
||||||
mockgen: $(MOCKGEN) ## Download mockgen locally if necessary.
|
|
||||||
$(MOCKGEN): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/mockgen || GOBIN=$(LOCALBIN) go install go.uber.org/mock/mockgen
|
|
||||||
.PHONY: oapi-codegen
|
|
||||||
oapi-codegen: $(OAPI_CODEGEN) ## Download oapi-codegen locally if necessary.
|
|
||||||
$(OAPI_CODEGEN): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/oapi-codegen || GOBIN=$(LOCALBIN) go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen
|
|
||||||
.PHONY: semver
|
|
||||||
semver: $(SEMVER) ## Download semver locally if necessary.
|
|
||||||
$(SEMVER): $(LOCALBIN)
|
|
||||||
test -s $(LOCALBIN)/semver || GOBIN=$(LOCALBIN) go install github.com/bakito/semver
|
|
||||||
|
|
||||||
## Update Tools
|
|
||||||
.PHONY: update-toolbox-tools
|
|
||||||
update-toolbox-tools:
|
|
||||||
@rm -f \
|
|
||||||
$(LOCALBIN)/deepcopy-gen \
|
|
||||||
$(LOCALBIN)/ginkgo \
|
|
||||||
$(LOCALBIN)/golangci-lint \
|
|
||||||
$(LOCALBIN)/goreleaser \
|
|
||||||
$(LOCALBIN)/mockgen \
|
|
||||||
$(LOCALBIN)/oapi-codegen \
|
|
||||||
$(LOCALBIN)/semver
|
|
||||||
toolbox makefile -f $(LOCALDIR)/Makefile \
|
|
||||||
github.com/goreleaser/goreleaser
|
|
||||||
## toolbox - end
|
|
||||||
|
|
||||||
start-replica:
|
start-replica:
|
||||||
docker run --pull always --name adguardhome-replica -p 9091:3000 --rm adguard/adguardhome:latest
|
docker run --pull always --name adguardhome-replica -p 9091:3000 --rm adguard/adguardhome:latest
|
||||||
@@ -126,12 +67,15 @@ kind-create:
|
|||||||
kind-test:
|
kind-test:
|
||||||
@./testdata/e2e/bin/install-chart.sh
|
@./testdata/e2e/bin/install-chart.sh
|
||||||
|
|
||||||
model: oapi-codegen
|
# renovate: packageName=AdguardTeam/AdGuardHome
|
||||||
|
ADGUARD_HOME_VERSION ?= v0.107.57
|
||||||
|
|
||||||
|
model: tb.oapi-codegen
|
||||||
@mkdir -p tmp
|
@mkdir -p tmp
|
||||||
go run openapi/main.go v0.107.46
|
go run openapi/main.go $(ADGUARD_HOME_VERSION)
|
||||||
$(OAPI_CODEGEN) -package model -generate types,client -config .oapi-codegen.yaml tmp/schema.yaml > pkg/client/model/model_generated.go
|
$(TB_OAPI_CODEGEN) -package model -generate types,client -config .oapi-codegen.yaml tmp/schema.yaml > pkg/client/model/model_generated.go
|
||||||
|
|
||||||
model-diff:
|
model-diff:
|
||||||
go run openapi/main.go v0.107.46
|
go run openapi/main.go $(ADGUARD_HOME_VERSION)
|
||||||
go run openapi/main.go
|
go run openapi/main.go
|
||||||
diff tmp/schema.yaml tmp/schema-master.yaml
|
diff tmp/schema.yaml tmp/schema-master.yaml
|
||||||
|
|||||||
95
README.md
95
README.md
@@ -22,6 +22,7 @@ and [Deprecations](https://github.com/bakito/adguardhome-sync/wiki/Deprecations)
|
|||||||
- Clients
|
- Clients
|
||||||
- DNS Config
|
- DNS Config
|
||||||
- DHCP Config
|
- DHCP Config
|
||||||
|
- Theme
|
||||||
|
|
||||||
By default, all features are enabled. Single features can be disabled in the config.
|
By default, all features are enabled. Single features can be disabled in the config.
|
||||||
|
|
||||||
@@ -71,9 +72,54 @@ export REPLICA1_PASSWORD=password
|
|||||||
adguardhome-sync run
|
adguardhome-sync run
|
||||||
|
|
||||||
# run as daemon
|
# run as daemon
|
||||||
adguardhome-sync run --cron "*/10 * * * *"
|
adguardhome-sync run --cron "0 */2 * * *"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Run as Linux Service via Systemd
|
||||||
|
|
||||||
|
> Verified on Ubuntu Linux 24.04
|
||||||
|
|
||||||
|
Assume you have downloaded the the `adguardhome-sync` binary to `/opt/adguardhome-sync`.
|
||||||
|
|
||||||
|
Create systemd service file `/opt/adguardhome-sync/adguardhome-sync.service`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description = AdGuardHome Sync
|
||||||
|
After = network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart = /opt/adguardhome-sync/adguardhome-sync --config /opt/adguardhome-sync/adguardhome-sync.yaml run
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy = multi-user.target
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a configuration file `/opt/adguardhome-sync/adguardhome-sync.yaml`, please follow [Config file](#config-file-1)
|
||||||
|
section below for details.
|
||||||
|
|
||||||
|
Install and enable service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp /opt/adguardhome-sync/adguardhome-sync.service /etc/systemd/system/
|
||||||
|
|
||||||
|
sudo systemctl enable adguardhome-sync.service
|
||||||
|
|
||||||
|
sudo systemctl start adguardhome-sync.service
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can check the status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl status adguardhome-sync.service
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If web UI has been enabled in configuration (default port is 8080), can also check the status via
|
||||||
|
`http://<server-IP>:8080`
|
||||||
|
|
||||||
## Run Windows
|
## Run Windows
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -103,7 +149,7 @@ set FEATURES_DHCP_STATICLEASES=false
|
|||||||
adguardhome-sync run
|
adguardhome-sync run
|
||||||
|
|
||||||
# run as daemon
|
# run as daemon
|
||||||
adguardhome-sync run --cron "*/10 * * * *"
|
adguardhome-sync run --cron "0 */2 * * *"
|
||||||
```
|
```
|
||||||
|
|
||||||
## docker cli
|
## docker cli
|
||||||
@@ -164,13 +210,13 @@ services:
|
|||||||
# REPLICA2_AUTO_SETUP: true # if true, AdGuardHome is automatically initialized.
|
# REPLICA2_AUTO_SETUP: true # if true, AdGuardHome is automatically initialized.
|
||||||
# REPLICA2_INTERFACE_NAME: 'ens18' # use custom dhcp interface name
|
# REPLICA2_INTERFACE_NAME: 'ens18' # use custom dhcp interface name
|
||||||
# REPLICA2_DHCP_SERVER_ENABLED: true/false (optional) enables/disables the dhcp server on the replica
|
# REPLICA2_DHCP_SERVER_ENABLED: true/false (optional) enables/disables the dhcp server on the replica
|
||||||
CRON: "*/10 * * * *" # run every 10 minutes
|
CRON: "0 */2 * * *" # run every 2 hours
|
||||||
RUNONSTART: true
|
RUN_ON_START: "true"
|
||||||
# CONTINUE_ON_ERROR: false # If enabled, the synchronisation task will not fail on single errors, but will log the errors and continue
|
# CONTINUE_ON_ERROR: false # If enabled, the synchronisation task will not fail on single errors, but will log the errors and continue
|
||||||
|
|
||||||
# Configure the sync API server, disabled if api port is 0
|
# Configure the sync API server, disabled if api port is 0
|
||||||
API_PORT: 8080
|
API_PORT: 8080
|
||||||
# API_DARK_MODE: true
|
# API_DARK_MODE: "true"
|
||||||
# API_USERNAME: admin
|
# API_USERNAME: admin
|
||||||
# API_PASSWORD: secret
|
# API_PASSWORD: secret
|
||||||
# the directory of the provided tls certs
|
# the directory of the provided tls certs
|
||||||
@@ -179,19 +225,21 @@ services:
|
|||||||
# API_TLS_CERT_NAME: foo.crt
|
# API_TLS_CERT_NAME: foo.crt
|
||||||
# the name of the key file (default: tls.key)
|
# the name of the key file (default: tls.key)
|
||||||
# API_TLS_KEY_NAME: bar.key
|
# API_TLS_KEY_NAME: bar.key
|
||||||
|
# API_METRICS_ENABLED: "true"
|
||||||
|
|
||||||
# Configure sync features; by default all features are enabled.
|
# Configure sync features; by default all features are enabled.
|
||||||
# FEATURES_GENERAL_SETTINGS: true
|
# FEATURES_GENERAL_SETTINGS: "true"
|
||||||
# FEATURES_QUERY_LOG_CONFIG: true
|
# FEATURES_QUERY_LOG_CONFIG: "true"
|
||||||
# FEATURES_STATS_CONFIG: true
|
# FEATURES_STATS_CONFIG: "true"
|
||||||
# FEATURES_CLIENT_SETTINGS: true
|
# FEATURES_CLIENT_SETTINGS: "true"
|
||||||
# FEATURES_SERVICES: true
|
# FEATURES_SERVICES: "true"
|
||||||
# FEATURES_FILTERS: true
|
# FEATURES_FILTERS: "true"
|
||||||
# FEATURES_DHCP_SERVER_CONFIG: true
|
# FEATURES_DHCP_SERVER_CONFIG: "true"
|
||||||
# FEATURES_DHCP_STATIC_LEASES: true
|
# FEATURES_DHCP_STATIC_LEASES: "true"
|
||||||
# FEATURES_DNS_SERVER_CONFIG: true
|
# FEATURES_DNS_SERVER_CONFIG: "true"
|
||||||
# FEATURES_DNS_ACCESS_LISTS: true
|
# FEATURES_DNS_ACCESS_LISTS: "true"
|
||||||
# FEATURES_DNS_REWRITES: true
|
# FEATURES_DNS_REWRITES: "true"
|
||||||
|
# FEATURES_THEME: "true" # if false the UI theme is not synced
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -203,7 +251,7 @@ location: $HOME/.adguardhome-sync.yaml
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# cron expression to run in daemon mode. (default; "" = runs only once)
|
# cron expression to run in daemon mode. (default; "" = runs only once)
|
||||||
cron: "*/10 * * * *"
|
cron: "0 */2 * * *"
|
||||||
|
|
||||||
# runs the synchronisation on startup
|
# runs the synchronisation on startup
|
||||||
runOnStart: true
|
runOnStart: true
|
||||||
@@ -246,9 +294,9 @@ api:
|
|||||||
|
|
||||||
# enable metrics on path '/metrics' (api port must be != 0)
|
# enable metrics on path '/metrics' (api port must be != 0)
|
||||||
# metrics:
|
# metrics:
|
||||||
# enabled: true
|
# enabled: true
|
||||||
# scrapeInterval: 30s
|
# scrapeInterval: 30s
|
||||||
# queryLogLimit: 10000
|
# queryLogLimit: 10000
|
||||||
|
|
||||||
# enable tls for the api server
|
# enable tls for the api server
|
||||||
# tls:
|
# tls:
|
||||||
@@ -290,4 +338,9 @@ The following log levels are supported (default: info)
|
|||||||
## Log Format
|
## Log Format
|
||||||
|
|
||||||
Default log format is `console`.
|
Default log format is `console`.
|
||||||
It can be changed to `json`by setting the environment variable: `LOG_FORMAT=json`
|
It can be changed to `json` by setting the environment variable: `LOG_FORMAT=json`.
|
||||||
|
|
||||||
|
## Video Tutorials
|
||||||
|
|
||||||
|
- [Como replicar la configuración de tu servidor DNS Adguard automáticamente - Tu servidor Part #12](https://www.youtube.com/watch?v=1LPeu_JG064) (
|
||||||
|
Spanish) by [Jonatan Castro](https://github.com/jcastro)
|
||||||
|
|||||||
135
Taskfile.yml
135
Taskfile.yml
@@ -1,135 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
env:
|
|
||||||
AGH_MODEL_VERSION: v0.107.43
|
|
||||||
GOBIN: '{{.USER_WORKING_DIR}}/bin'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
|
|
||||||
install-go-tool:
|
|
||||||
label: "Install {{ .TOOL_NAME }}"
|
|
||||||
cmds:
|
|
||||||
- go install {{ .TOOL_MODULE }}
|
|
||||||
status:
|
|
||||||
- test -f {{.GOBIN}}/{{.TOOL_NAME}}
|
|
||||||
|
|
||||||
deepcopy-gen:
|
|
||||||
desc: Install deepcopy-gen
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: deepcopy-gen
|
|
||||||
TOOL_MODULE: k8s.io/code-generator/cmd/deepcopy-gen
|
|
||||||
|
|
||||||
ginkgo:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: ginkgo
|
|
||||||
TOOL_MODULE: github.com/onsi/ginkgo/v2/ginkgo
|
|
||||||
|
|
||||||
goreleaser:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: goreleaser
|
|
||||||
TOOL_MODULE: github.com/goreleaser/goreleaser
|
|
||||||
|
|
||||||
golangci-lint:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: golangci-lint
|
|
||||||
TOOL_MODULE: github.com/golangci/golangci-lint/cmd/golangci-lint
|
|
||||||
|
|
||||||
mockgen:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: mockgen
|
|
||||||
TOOL_MODULE: go.uber.org/mock/mockgen
|
|
||||||
|
|
||||||
oapi-codegen:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: oapi-codegen
|
|
||||||
TOOL_MODULE: github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen
|
|
||||||
|
|
||||||
semver:
|
|
||||||
cmd:
|
|
||||||
task: install-go-tool
|
|
||||||
vars:
|
|
||||||
TOOL_NAME: semver
|
|
||||||
TOOL_MODULE: github.com/bakito/semver
|
|
||||||
|
|
||||||
lint:
|
|
||||||
deps:
|
|
||||||
- golangci-lint
|
|
||||||
cmds:
|
|
||||||
- '{{.GOBIN}}/golangci-lint run --fix'
|
|
||||||
|
|
||||||
tidy:
|
|
||||||
desc: Run go mod tidy
|
|
||||||
cmd: go mod tidy
|
|
||||||
|
|
||||||
generate:
|
|
||||||
deps:
|
|
||||||
- deepcopy-gen
|
|
||||||
cmds:
|
|
||||||
- mkdir -p ./tmp
|
|
||||||
- touch ./tmp/deepcopy-gen-boilerplate.go.txt
|
|
||||||
- '{{.GOBIN}}/deepcopy-gen -h ./tmp/deepcopy-gen-boilerplate.go.txt -i ./pkg/types'
|
|
||||||
|
|
||||||
mocks:
|
|
||||||
deps:
|
|
||||||
- mockgen
|
|
||||||
cmds:
|
|
||||||
- '{{.GOBIN}}/mockgen -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client'
|
|
||||||
- '{{.GOBIN}}/mockgen -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags'
|
|
||||||
|
|
||||||
test:
|
|
||||||
cmds:
|
|
||||||
- task: generate
|
|
||||||
- task: lint
|
|
||||||
- task: test-ci
|
|
||||||
|
|
||||||
test-ci:
|
|
||||||
deps:
|
|
||||||
- ginkgo
|
|
||||||
- tidy
|
|
||||||
- mocks
|
|
||||||
cmds:
|
|
||||||
- '{{.GOBIN}}/ginkgo --cover --coverprofile coverage.out.tmp ./...'
|
|
||||||
- cat coverage.out.tmp | grep -v "_generated.go" > coverage.out
|
|
||||||
- go tool cover -func=coverage.out
|
|
||||||
|
|
||||||
release:
|
|
||||||
deps:
|
|
||||||
- semver
|
|
||||||
- goreleaser
|
|
||||||
cmds:
|
|
||||||
- git tag -s $$version -m"Release $({{.GOBIN}}/semver)
|
|
||||||
- '{{.GOBIN}}/goreleaser --clean'
|
|
||||||
|
|
||||||
test-release:
|
|
||||||
deps:
|
|
||||||
- goreleaser
|
|
||||||
- semver
|
|
||||||
cmds:
|
|
||||||
- '{{.GOBIN}}/goreleaser --skip=publish --snapshot --clean'
|
|
||||||
|
|
||||||
model:
|
|
||||||
deps:
|
|
||||||
- oapi-codegen
|
|
||||||
cmds:
|
|
||||||
- mkdir -p tmp
|
|
||||||
- go run openapi/main.go {{.AGH_MODEL_VERSION}}
|
|
||||||
- '{{.GOBIN}}/oapi-codegen -package model -generate types,client -config .oapi-codegen.yaml tmp/schema.yaml > pkg/client/model/model_generated.go'
|
|
||||||
|
|
||||||
model-diff:
|
|
||||||
deps:
|
|
||||||
- oapi-codegen
|
|
||||||
cmds:
|
|
||||||
- go run openapi/main.go {{.AGH_MODEL_VERSION}}
|
|
||||||
- go run openapi/main.go
|
|
||||||
- diff tmp/schema.yaml tmp/schema-master.yaml
|
|
||||||
15
cmd/run.go
15
cmd/run.go
@@ -50,7 +50,8 @@ func init() {
|
|||||||
doCmd.PersistentFlags().Bool(config.FlagContinueOnError, false, "If enabled, the synchronisation task "+
|
doCmd.PersistentFlags().Bool(config.FlagContinueOnError, false, "If enabled, the synchronisation task "+
|
||||||
"will not fail on single errors, but will log the errors and continue.")
|
"will not fail on single errors, but will log the errors and continue.")
|
||||||
|
|
||||||
doCmd.PersistentFlags().Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
|
doCmd.PersistentFlags().
|
||||||
|
Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
|
||||||
doCmd.PersistentFlags().String(config.FlagApiUsername, "", "Sync API username")
|
doCmd.PersistentFlags().String(config.FlagApiUsername, "", "Sync API username")
|
||||||
doCmd.PersistentFlags().String(config.FlagApiPassword, "", "Sync API password")
|
doCmd.PersistentFlags().String(config.FlagApiPassword, "", "Sync API password")
|
||||||
doCmd.PersistentFlags().String(config.FlagApiDarkMode, "", "API UI in dark mode")
|
doCmd.PersistentFlags().String(config.FlagApiDarkMode, "", "API UI in dark mode")
|
||||||
@@ -70,7 +71,8 @@ func init() {
|
|||||||
doCmd.PersistentFlags().Bool(config.FlagFeatureFilters, true, "Enable filters sync feature")
|
doCmd.PersistentFlags().Bool(config.FlagFeatureFilters, true, "Enable filters sync feature")
|
||||||
|
|
||||||
doCmd.PersistentFlags().String(config.FlagOriginURL, "", "Origin instance url")
|
doCmd.PersistentFlags().String(config.FlagOriginURL, "", "Origin instance url")
|
||||||
doCmd.PersistentFlags().String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)")
|
doCmd.PersistentFlags().
|
||||||
|
String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)")
|
||||||
doCmd.PersistentFlags().String(config.FlagOriginApiPath, "/control", "Origin instance API path")
|
doCmd.PersistentFlags().String(config.FlagOriginApiPath, "/control", "Origin instance API path")
|
||||||
doCmd.PersistentFlags().String(config.FlagOriginUsername, "", "Origin instance username")
|
doCmd.PersistentFlags().String(config.FlagOriginUsername, "", "Origin instance username")
|
||||||
doCmd.PersistentFlags().String(config.FlagOriginPassword, "", "Origin instance password")
|
doCmd.PersistentFlags().String(config.FlagOriginPassword, "", "Origin instance password")
|
||||||
@@ -78,12 +80,15 @@ func init() {
|
|||||||
doCmd.PersistentFlags().Bool(config.FlagOriginISV, false, "Enable Origin instance InsecureSkipVerify")
|
doCmd.PersistentFlags().Bool(config.FlagOriginISV, false, "Enable Origin instance InsecureSkipVerify")
|
||||||
|
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaURL, "", "Replica instance url")
|
doCmd.PersistentFlags().String(config.FlagReplicaURL, "", "Replica instance url")
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)")
|
doCmd.PersistentFlags().
|
||||||
|
String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)")
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaApiPath, "/control", "Replica instance API path")
|
doCmd.PersistentFlags().String(config.FlagReplicaApiPath, "/control", "Replica instance API path")
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaUsername, "", "Replica instance username")
|
doCmd.PersistentFlags().String(config.FlagReplicaUsername, "", "Replica instance username")
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaPassword, "", "Replica instance password")
|
doCmd.PersistentFlags().String(config.FlagReplicaPassword, "", "Replica instance password")
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication")
|
doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication")
|
||||||
doCmd.PersistentFlags().Bool(config.FlagReplicaISV, false, "Enable Replica instance InsecureSkipVerify")
|
doCmd.PersistentFlags().Bool(config.FlagReplicaISV, false, "Enable Replica instance InsecureSkipVerify")
|
||||||
doCmd.PersistentFlags().Bool(config.FlagReplicaAutoSetup, false, "Enable automatic setup of new AdguardHome instances. This replaces the setup wizard.")
|
doCmd.PersistentFlags().
|
||||||
doCmd.PersistentFlags().String(config.FlagReplicaInterfaceName, "", "Optional change the interface name of the replica if it differs from the master")
|
Bool(config.FlagReplicaAutoSetup, false, "Enable automatic setup of new AdguardHome instances. This replaces the setup wizard.")
|
||||||
|
doCmd.PersistentFlags().
|
||||||
|
String(config.FlagReplicaInterfaceName, "", "Optional change the interface name of the replica if it differs from the master")
|
||||||
}
|
}
|
||||||
|
|||||||
251
go.mod
251
go.mod
@@ -1,241 +1,78 @@
|
|||||||
module github.com/bakito/adguardhome-sync
|
module github.com/bakito/adguardhome-sync
|
||||||
|
|
||||||
go 1.22
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bakito/semver v1.1.3
|
github.com/caarlos0/env/v11 v11.3.1
|
||||||
github.com/caarlos0/env/v10 v10.0.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/deepmap/oapi-codegen/v2 v2.1.0
|
github.com/go-faker/faker/v4 v4.6.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
github.com/go-resty/resty/v2 v2.12.0
|
|
||||||
github.com/golangci/golangci-lint v1.57.2
|
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/jinzhu/copier v0.4.0
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/oapi-codegen/runtime v1.1.1
|
github.com/oapi-codegen/runtime v1.1.1
|
||||||
github.com/onsi/ginkgo/v2 v2.17.1
|
github.com/onsi/ginkgo/v2 v2.22.2
|
||||||
github.com/onsi/gomega v1.32.0
|
github.com/onsi/gomega v1.36.2
|
||||||
github.com/prometheus/client_golang v1.19.0
|
github.com/prometheus/client_golang v1.21.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||||
go.uber.org/mock v0.4.0
|
github.com/spf13/cobra v1.9.1
|
||||||
|
go.uber.org/mock v0.5.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
|
golang.org/x/mod v0.23.0
|
||||||
golang.org/x/mod v0.17.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
k8s.io/apimachinery v0.29.3
|
k8s.io/apimachinery v0.32.2
|
||||||
k8s.io/code-generator v0.29.3
|
k8s.io/utils v0.0.0-20241210054802-24370beab758
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
|
|
||||||
4d63.com/gochecknoglobals v0.2.1 // indirect
|
|
||||||
github.com/4meepo/tagalign v1.3.3 // indirect
|
|
||||||
github.com/Abirdcfly/dupword v0.0.14 // indirect
|
|
||||||
github.com/Antonboom/errname v0.1.12 // indirect
|
|
||||||
github.com/Antonboom/nilnil v0.1.7 // indirect
|
|
||||||
github.com/Antonboom/testifylint v1.2.0 // indirect
|
|
||||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
|
||||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
|
||||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect
|
|
||||||
github.com/Masterminds/semver v1.5.0 // indirect
|
|
||||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
|
|
||||||
github.com/alecthomas/go-check-sumtype v0.1.4 // indirect
|
|
||||||
github.com/alexkohler/nakedret/v2 v2.0.4 // indirect
|
|
||||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
|
||||||
github.com/alingse/asasalint v0.0.11 // indirect
|
|
||||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||||
github.com/ashanbrown/forbidigo v1.6.0 // indirect
|
|
||||||
github.com/ashanbrown/makezero v1.1.1 // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bkielbasa/cyclop v1.2.1 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
github.com/bombsimon/wsl/v4 v4.2.1 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/breml/bidichk v0.2.7 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/breml/errchkjson v0.3.6 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/butuzov/ireturn v0.3.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/butuzov/mirror v1.1.0 // indirect
|
|
||||||
github.com/bytedance/sonic v1.10.2 // indirect
|
|
||||||
github.com/catenacyber/perfsprint v0.7.1 // indirect
|
|
||||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
|
||||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
|
||||||
github.com/chavacava/garif v0.1.0 // indirect
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
|
||||||
github.com/ckaznocha/intrange v0.1.1 // indirect
|
|
||||||
github.com/coreos/go-semver v0.3.0 // indirect
|
|
||||||
github.com/curioswitch/go-reassign v0.2.0 // indirect
|
|
||||||
github.com/daixiang0/gci v0.12.3 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
||||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
|
||||||
github.com/ettle/strcase v0.2.0 // indirect
|
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
|
||||||
github.com/fatih/structtag v1.2.0 // indirect
|
|
||||||
github.com/firefart/nonamedreturns v1.0.4 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
|
||||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/getkin/kin-openapi v0.122.0 // indirect
|
|
||||||
github.com/ghostiam/protogetter v0.3.5 // indirect
|
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-critic/go-critic v0.11.2 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/go-test/deep v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/astequal v1.2.0 // indirect
|
|
||||||
github.com/go-toolsmith/astfmt v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/astp v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
|
||||||
github.com/go-toolsmith/typep v1.1.0 // indirect
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
|
||||||
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
|
|
||||||
github.com/gobwas/glob v0.2.3 // indirect
|
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/gofrs/flock v0.8.1 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
|
|
||||||
github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect
|
|
||||||
github.com/golangci/misspell v0.4.1 // indirect
|
|
||||||
github.com/golangci/plugin-module-register v0.1.1 // indirect
|
|
||||||
github.com/golangci/revgrep v0.5.2 // indirect
|
|
||||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
|
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
|
||||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
|
||||||
github.com/gostaticanalysis/comment v1.4.2 // indirect
|
|
||||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
|
|
||||||
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
|
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
|
||||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/invopop/yaml v0.2.0 // indirect
|
|
||||||
github.com/jgautheron/goconst v1.7.1 // indirect
|
|
||||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
|
||||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
|
|
||||||
github.com/jjti/go-spancheck v0.5.3 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/julz/importas v0.1.0 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/karamaru-alpha/copyloopvar v1.0.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/kisielk/errcheck v1.7.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/kkHAIKE/contextcheck v1.1.5 // indirect
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
|
||||||
github.com/kulti/thelper v0.6.3 // indirect
|
|
||||||
github.com/kunwardeep/paralleltest v1.0.10 // indirect
|
|
||||||
github.com/kyoh86/exportloopref v0.1.11 // indirect
|
|
||||||
github.com/ldez/gomoddirectives v0.2.4 // indirect
|
|
||||||
github.com/ldez/tagliatelle v0.5.0 // indirect
|
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
|
||||||
github.com/leonklingele/grouper v1.1.1 // indirect
|
|
||||||
github.com/lufeee/execinquery v1.2.1 // indirect
|
|
||||||
github.com/macabu/inamedparam v0.1.3 // indirect
|
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
|
||||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
|
||||||
github.com/maratori/testpackage v1.1.1 // indirect
|
|
||||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
|
||||||
github.com/mgechev/revive v1.3.7 // indirect
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/moricho/tparallel v0.3.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/nunnatsa/ginkgolinter v0.16.2 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
|
|
||||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
||||||
github.com/polyfloyd/go-errorlint v1.4.8 // indirect
|
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
|
||||||
github.com/quasilyte/go-ruleguard v0.4.2 // indirect
|
|
||||||
github.com/quasilyte/gogrep v0.5.0 // indirect
|
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
|
||||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.2 // indirect
|
|
||||||
github.com/ryancurrah/gomodguard v1.3.1 // indirect
|
|
||||||
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
|
||||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
|
|
||||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
|
|
||||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
|
||||||
github.com/sashamelentyev/usestdlibvars v1.25.0 // indirect
|
|
||||||
github.com/securego/gosec/v2 v2.19.0 // indirect
|
|
||||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
|
||||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
|
||||||
github.com/sivchari/tenv v1.7.1 // indirect
|
|
||||||
github.com/sonatard/noctx v0.0.2 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/spf13/viper v1.17.0 // indirect
|
|
||||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
|
||||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
|
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
|
|
||||||
github.com/tdakkota/asciicheck v0.2.0 // indirect
|
|
||||||
github.com/tetafro/godot v1.4.16 // indirect
|
|
||||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
|
|
||||||
github.com/timonwong/loggercheck v0.9.4 // indirect
|
|
||||||
github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect
|
|
||||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/ultraware/funlen v0.1.0 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/ultraware/whitespace v0.1.0 // indirect
|
|
||||||
github.com/uudashr/gocognit v1.1.2 // indirect
|
|
||||||
github.com/xen0n/gosmopolitan v1.2.2 // indirect
|
|
||||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
|
||||||
github.com/yeya24/promlinter v0.2.0 // indirect
|
|
||||||
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
|
||||||
gitlab.com/bosi/decorder v0.4.1 // indirect
|
|
||||||
go-simpler.org/musttag v0.9.0 // indirect
|
|
||||||
go-simpler.org/sloglint v0.5.0 // indirect
|
|
||||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/arch v0.7.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.21.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/net v0.22.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
golang.org/x/tools v0.28.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
golang.org/x/tools v0.19.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||||
honnef.co/go/tools v0.4.7 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||||
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect
|
|
||||||
k8s.io/klog/v2 v2.110.1 // indirect
|
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
|
||||||
mvdan.cc/gofumpt v0.6.0 // indirect
|
|
||||||
mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 // indirect
|
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Printf("Patching schema version %s\n", version)
|
log.Printf("Patching schema version %s\n", version)
|
||||||
|
|
||||||
resp, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version))
|
resp, err := http.Get(
|
||||||
|
fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,10 @@ func (cl *client) Stats() (*model.Stats, error) {
|
|||||||
|
|
||||||
func (cl *client) QueryLog(limit int) (*model.QueryLog, error) {
|
func (cl *client) QueryLog(limit int) (*model.QueryLog, error) {
|
||||||
ql := &model.QueryLog{}
|
ql := &model.QueryLog{}
|
||||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(ql), fmt.Sprintf(`querylog?limit=%d&response_status="all"`, limit))
|
err := cl.doGet(
|
||||||
|
cl.client.R().EnableTrace().SetResult(ql),
|
||||||
|
fmt.Sprintf(`querylog?limit=%d&response_status="all"`, limit),
|
||||||
|
)
|
||||||
return ql, err
|
return ql, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +263,10 @@ func (cl *client) UpdateFilter(whitelist bool, f model.Filter) error {
|
|||||||
|
|
||||||
func (cl *client) RefreshFilters(whitelist bool) error {
|
func (cl *client) RefreshFilters(whitelist bool) error {
|
||||||
cl.log.With("whitelist", whitelist).Info("Refresh filter")
|
cl.log.With("whitelist", whitelist).Info("Refresh filter")
|
||||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(&model.FilterRefreshRequest{Whitelist: utils.Ptr(whitelist)}), "/filtering/refresh")
|
return cl.doPost(
|
||||||
|
cl.client.R().EnableTrace().SetBody(&model.FilterRefreshRequest{Whitelist: utils.Ptr(whitelist)}),
|
||||||
|
"/filtering/refresh",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cl *client) ToggleProtection(enable bool) error {
|
func (cl *client) ToggleProtection(enable bool) error {
|
||||||
@@ -309,7 +315,10 @@ func (cl *client) AddClient(client *model.Client) error {
|
|||||||
|
|
||||||
func (cl *client) UpdateClient(client *model.Client) error {
|
func (cl *client) UpdateClient(client *model.Client) error {
|
||||||
cl.log.With("name", *client.Name).Info("Update client settings")
|
cl.log.With("name", *client.Name).Info("Update client settings")
|
||||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(&model.ClientUpdate{Name: client.Name, Data: client}), "/clients/update")
|
return cl.doPost(
|
||||||
|
cl.client.R().EnableTrace().SetBody(&model.ClientUpdate{Name: client.Name, Data: client}),
|
||||||
|
"/clients/update",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cl *client) DeleteClient(client *model.Client) error {
|
func (cl *client) DeleteClient(client *model.Client) error {
|
||||||
@@ -324,7 +333,8 @@ func (cl *client) QueryLogConfig() (*model.QueryLogConfigWithIgnored, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cl *client) SetQueryLogConfig(qlc *model.QueryLogConfigWithIgnored) error {
|
func (cl *client) SetQueryLogConfig(qlc *model.QueryLogConfigWithIgnored) error {
|
||||||
cl.log.With("enabled", *qlc.Enabled, "interval", *qlc.Interval, "anonymizeClientIP", *qlc.AnonymizeClientIp).Info("Set query log config")
|
cl.log.With("enabled", *qlc.Enabled, "interval", *qlc.Interval, "anonymizeClientIP", *qlc.AnonymizeClientIp).
|
||||||
|
Info("Set query log config")
|
||||||
return cl.doPut(cl.client.R().EnableTrace().SetBody(qlc), "/querylog/config/update")
|
return cl.doPut(cl.client.R().EnableTrace().SetBody(qlc), "/querylog/config/update")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,14 @@ var _ = Describe("Client", func() {
|
|||||||
|
|
||||||
Context("Setup", func() {
|
Context("Setup", func() {
|
||||||
It("should add setup the instance", func() {
|
It("should add setup the instance", func() {
|
||||||
ts, cl = ClientPost("/install/configure", fmt.Sprintf(`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`, username, password))
|
ts, cl = ClientPost(
|
||||||
|
"/install/configure",
|
||||||
|
fmt.Sprintf(
|
||||||
|
`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
),
|
||||||
|
)
|
||||||
err := cl.Setup()
|
err := cl.Setup()
|
||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
@@ -303,7 +310,10 @@ var _ = Describe("Client", func() {
|
|||||||
Ω(*qlc.Interval).Should(Equal(model.QueryLogConfigInterval(90)))
|
Ω(*qlc.Interval).Should(Equal(model.QueryLogConfigInterval(90)))
|
||||||
})
|
})
|
||||||
It("should set QueryLogConfig", func() {
|
It("should set QueryLogConfig", func() {
|
||||||
ts, cl = ClientPut("/querylog/config/update", `{"anonymize_client_ip":true,"enabled":true,"interval":123,"ignored":["foo.bar"]}`)
|
ts, cl = ClientPut(
|
||||||
|
"/querylog/config/update",
|
||||||
|
`{"anonymize_client_ip":true,"enabled":true,"interval":123,"ignored":["foo.bar"]}`,
|
||||||
|
)
|
||||||
|
|
||||||
var interval model.QueryLogConfigInterval = 123
|
var interval model.QueryLogConfigInterval = 123
|
||||||
err := cl.SetQueryLogConfig(&model.QueryLogConfigWithIgnored{
|
err := cl.SetQueryLogConfig(&model.QueryLogConfigWithIgnored{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Clone the config
|
// Clone the config
|
||||||
@@ -167,11 +168,40 @@ func (cl *Client) Sort() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrepareDiff timezone BlockedServicesSchedule might differ if all other fields are empty,
|
||||||
|
// so we skip it in diff
|
||||||
|
func (cl *Client) PrepareDiff() *string {
|
||||||
|
var tz *string
|
||||||
|
bss := cl.BlockedServicesSchedule
|
||||||
|
if bss != nil && bss.Mon == nil && bss.Tue == nil && bss.Wed == nil &&
|
||||||
|
bss.Thu == nil && bss.Fri == nil && bss.Sat == nil && bss.Sun == nil {
|
||||||
|
|
||||||
|
tz = cl.BlockedServicesSchedule.TimeZone
|
||||||
|
cl.BlockedServicesSchedule.TimeZone = nil
|
||||||
|
}
|
||||||
|
return tz
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterDiff reset after diff
|
||||||
|
func (cl *Client) AfterDiff(tz *string) {
|
||||||
|
if cl.BlockedServicesSchedule != nil {
|
||||||
|
cl.BlockedServicesSchedule.TimeZone = tz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Equals Clients equal check
|
// Equals Clients equal check
|
||||||
func (cl *Client) Equals(o *Client) bool {
|
func (cl *Client) Equals(o *Client) bool {
|
||||||
cl.Sort()
|
cl.Sort()
|
||||||
o.Sort()
|
o.Sort()
|
||||||
|
|
||||||
|
bssCl := cl.PrepareDiff()
|
||||||
|
bssO := o.PrepareDiff()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cl.AfterDiff(bssCl)
|
||||||
|
o.AfterDiff(bssO)
|
||||||
|
}()
|
||||||
|
|
||||||
return utils.JsonEquals(cl, o)
|
return utils.JsonEquals(cl, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,23 +399,22 @@ func (ssc *SafeSearchConfig) Equals(o *SafeSearchConfig) bool {
|
|||||||
ptrEquals(ssc.Youtube, o.Youtube)
|
ptrEquals(ssc.Youtube, o.Youtube)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pi *ProfileInfo) Equals(o *ProfileInfo) bool {
|
func (pi *ProfileInfo) Equals(o *ProfileInfo, withTheme bool) bool {
|
||||||
return pi.Language == o.Language &&
|
return pi.Language == o.Language && (!withTheme || pi.Theme == o.Theme)
|
||||||
pi.Theme == o.Theme
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pi *ProfileInfo) ShouldSyncFor(o *ProfileInfo) *ProfileInfo {
|
func (pi *ProfileInfo) ShouldSyncFor(o *ProfileInfo, withTheme bool) *ProfileInfo {
|
||||||
if pi.Equals(o) {
|
if pi.Equals(o, withTheme) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
merged := &ProfileInfo{Name: pi.Name, Language: pi.Language, Theme: pi.Theme}
|
merged := &ProfileInfo{Name: pi.Name, Language: pi.Language, Theme: pi.Theme}
|
||||||
if o.Language != "" {
|
if o.Language != "" {
|
||||||
merged.Language = o.Language
|
merged.Language = o.Language
|
||||||
}
|
}
|
||||||
if o.Theme != "" {
|
if withTheme && o.Theme != "" {
|
||||||
merged.Theme = o.Theme
|
merged.Theme = o.Theme
|
||||||
}
|
}
|
||||||
if merged.Name == "" || merged.Language == "" || merged.Theme == "" || merged.Equals(pi) {
|
if merged.Name == "" || merged.Language == "" || merged.Equals(pi, false) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return merged
|
return merged
|
||||||
@@ -410,9 +439,12 @@ func ArrayString(a *[]string) string {
|
|||||||
|
|
||||||
func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
|
func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
|
||||||
// disable UsePrivatePtrResolvers if not configured
|
// disable UsePrivatePtrResolvers if not configured
|
||||||
|
// https://github.com/AdguardTeam/AdGuardHome/issues/6820
|
||||||
if c.UsePrivatePtrResolvers != nil && *c.UsePrivatePtrResolvers &&
|
if c.UsePrivatePtrResolvers != nil && *c.UsePrivatePtrResolvers &&
|
||||||
(c.LocalPtrUpstreams == nil || len(*c.LocalPtrUpstreams) == 0) {
|
(c.LocalPtrUpstreams == nil || len(*c.LocalPtrUpstreams) == 0) {
|
||||||
l.Warn("disabling replica 'Use private reverse DNS resolvers' as no 'Private reverse DNS servers' are configured on origin")
|
l.Warn(
|
||||||
|
"disabling replica 'Use private reverse DNS resolvers' as no 'Private reverse DNS servers' are configured on origin",
|
||||||
|
)
|
||||||
c.UsePrivatePtrResolvers = utils.Ptr(false)
|
c.UsePrivatePtrResolvers = utils.Ptr(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,3 +453,53 @@ func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
|
|||||||
func (sc *GetStatsConfigResponse) Equals(o *GetStatsConfigResponse) bool {
|
func (sc *GetStatsConfigResponse) Equals(o *GetStatsConfigResponse) bool {
|
||||||
return utils.JsonEquals(sc, o)
|
return utils.JsonEquals(sc, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewStats() *Stats {
|
||||||
|
return &Stats{
|
||||||
|
NumBlockedFiltering: ptr.To(0),
|
||||||
|
NumReplacedParental: ptr.To(0),
|
||||||
|
NumReplacedSafesearch: ptr.To(0),
|
||||||
|
NumReplacedSafebrowsing: ptr.To(0),
|
||||||
|
NumDnsQueries: ptr.To(0),
|
||||||
|
|
||||||
|
BlockedFiltering: ptr.To(make([]int, 24)),
|
||||||
|
DnsQueries: ptr.To(make([]int, 24)),
|
||||||
|
ReplacedParental: ptr.To(make([]int, 24)),
|
||||||
|
ReplacedSafebrowsing: ptr.To(make([]int, 24)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stats) Add(other *Stats) {
|
||||||
|
s.NumBlockedFiltering = addInt(s.NumBlockedFiltering, other.NumBlockedFiltering)
|
||||||
|
s.NumReplacedSafebrowsing = addInt(s.NumReplacedSafebrowsing, other.NumReplacedSafebrowsing)
|
||||||
|
s.NumDnsQueries = addInt(s.NumDnsQueries, other.NumDnsQueries)
|
||||||
|
s.NumReplacedSafesearch = addInt(s.NumReplacedSafesearch, other.NumReplacedSafesearch)
|
||||||
|
s.NumReplacedParental = addInt(s.NumReplacedParental, other.NumReplacedParental)
|
||||||
|
|
||||||
|
s.BlockedFiltering = sumUp(s.BlockedFiltering, other.BlockedFiltering)
|
||||||
|
s.DnsQueries = sumUp(s.DnsQueries, other.DnsQueries)
|
||||||
|
s.ReplacedParental = sumUp(s.ReplacedParental, other.ReplacedParental)
|
||||||
|
s.ReplacedSafebrowsing = sumUp(s.ReplacedSafebrowsing, other.ReplacedSafebrowsing)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addInt(t *int, add *int) *int {
|
||||||
|
if add != nil {
|
||||||
|
return ptr.To(*t + *add)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumUp(t *[]int, o *[]int) *[]int {
|
||||||
|
if o != nil {
|
||||||
|
tt := *t
|
||||||
|
oo := *o
|
||||||
|
var sum []int
|
||||||
|
for i := 0; i < len(tt); i++ {
|
||||||
|
if len(oo) >= i {
|
||||||
|
sum = append(sum, tt[i]+oo[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &sum
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Package model provides primitives to interact with the openapi HTTP API.
|
// Package model provides primitives to interact with the openapi HTTP API.
|
||||||
//
|
//
|
||||||
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
|
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -397,6 +397,17 @@ type ClientsFindEntry map[string]ClientFindSubEntry
|
|||||||
// ClientsFindResponse Client search results.
|
// ClientsFindResponse Client search results.
|
||||||
type ClientsFindResponse = []ClientsFindEntry
|
type ClientsFindResponse = []ClientsFindEntry
|
||||||
|
|
||||||
|
// ClientsSearchRequest Client search request
|
||||||
|
type ClientsSearchRequest struct {
|
||||||
|
Clients *[]ClientsSearchRequestItem `json:"clients,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientsSearchRequestItem defines model for ClientsSearchRequestItem.
|
||||||
|
type ClientsSearchRequestItem struct {
|
||||||
|
// Id Client IP address, CIDR, MAC address, or ClientID
|
||||||
|
Id *string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// DNSConfig DNS server configuration
|
// DNSConfig DNS server configuration
|
||||||
type DNSConfig struct {
|
type DNSConfig struct {
|
||||||
// BlockedResponseTtl TTL for blocked responses.
|
// BlockedResponseTtl TTL for blocked responses.
|
||||||
@@ -443,8 +454,11 @@ type DNSConfig struct {
|
|||||||
UpstreamDnsFile *string `json:"upstream_dns_file,omitempty"`
|
UpstreamDnsFile *string `json:"upstream_dns_file,omitempty"`
|
||||||
|
|
||||||
// UpstreamMode Upstream modes enumeration.
|
// UpstreamMode Upstream modes enumeration.
|
||||||
UpstreamMode *DNSConfigUpstreamMode `json:"upstream_mode,omitempty"`
|
UpstreamMode *DNSConfigUpstreamMode `json:"upstream_mode,omitempty"`
|
||||||
UsePrivatePtrResolvers *bool `json:"use_private_ptr_resolvers,omitempty"`
|
|
||||||
|
// UpstreamTimeout The number of seconds to wait for a response from the upstream server
|
||||||
|
UpstreamTimeout *int `json:"upstream_timeout,omitempty"`
|
||||||
|
UsePrivatePtrResolvers *bool `json:"use_private_ptr_resolvers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSConfigBlockingMode defines model for DNSConfig.BlockingMode.
|
// DNSConfigBlockingMode defines model for DNSConfig.BlockingMode.
|
||||||
@@ -725,11 +739,18 @@ type Login struct {
|
|||||||
// NetInterface Network interface info
|
// NetInterface Network interface info
|
||||||
type NetInterface struct {
|
type NetInterface struct {
|
||||||
// Flags Flags could be any combination of the following values, divided by the "|" character: "up", "broadcast", "loopback", "pointtopoint" and "multicast".
|
// Flags Flags could be any combination of the following values, divided by the "|" character: "up", "broadcast", "loopback", "pointtopoint" and "multicast".
|
||||||
Flags string `json:"flags"`
|
Flags string `json:"flags"`
|
||||||
HardwareAddress string `json:"hardware_address"`
|
|
||||||
IpAddresses *[]string `json:"ip_addresses,omitempty"`
|
// GatewayIp The IP address of the gateway.
|
||||||
Mtu int `json:"mtu"`
|
GatewayIp string `json:"gateway_ip"`
|
||||||
Name string `json:"name"`
|
HardwareAddress string `json:"hardware_address"`
|
||||||
|
|
||||||
|
// Ipv4Addresses The addresses of the interface of v4 family.
|
||||||
|
Ipv4Addresses []string `json:"ipv4_addresses"`
|
||||||
|
|
||||||
|
// Ipv6Addresses The addresses of the interface of v6 family.
|
||||||
|
Ipv6Addresses []string `json:"ipv6_addresses"`
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetInterfaces Network interfaces dictionary, keys are interface names.
|
// NetInterfaces Network interfaces dictionary, keys are interface names.
|
||||||
@@ -904,6 +925,7 @@ type RewriteUpdate struct {
|
|||||||
type SafeSearchConfig struct {
|
type SafeSearchConfig struct {
|
||||||
Bing *bool `json:"bing,omitempty"`
|
Bing *bool `json:"bing,omitempty"`
|
||||||
Duckduckgo *bool `json:"duckduckgo,omitempty"`
|
Duckduckgo *bool `json:"duckduckgo,omitempty"`
|
||||||
|
Ecosia *bool `json:"ecosia,omitempty"`
|
||||||
Enabled *bool `json:"enabled,omitempty"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
Google *bool `json:"google,omitempty"`
|
Google *bool `json:"google,omitempty"`
|
||||||
Pixabay *bool `json:"pixabay,omitempty"`
|
Pixabay *bool `json:"pixabay,omitempty"`
|
||||||
@@ -1204,6 +1226,9 @@ type ClientsAddJSONRequestBody = Client
|
|||||||
// ClientsDeleteJSONRequestBody defines body for ClientsDelete for application/json ContentType.
|
// ClientsDeleteJSONRequestBody defines body for ClientsDelete for application/json ContentType.
|
||||||
type ClientsDeleteJSONRequestBody = ClientDelete
|
type ClientsDeleteJSONRequestBody = ClientDelete
|
||||||
|
|
||||||
|
// ClientsSearchJSONRequestBody defines body for ClientsSearch for application/json ContentType.
|
||||||
|
type ClientsSearchJSONRequestBody = ClientsSearchRequest
|
||||||
|
|
||||||
// ClientsUpdateJSONRequestBody defines body for ClientsUpdate for application/json ContentType.
|
// ClientsUpdateJSONRequestBody defines body for ClientsUpdate for application/json ContentType.
|
||||||
type ClientsUpdateJSONRequestBody = ClientUpdate
|
type ClientsUpdateJSONRequestBody = ClientUpdate
|
||||||
|
|
||||||
@@ -1493,6 +1518,11 @@ type ClientInterface interface {
|
|||||||
// ClientsFind request
|
// ClientsFind request
|
||||||
ClientsFind(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*http.Response, error)
|
ClientsFind(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||||
|
|
||||||
|
// ClientsSearchWithBody request with any body
|
||||||
|
ClientsSearchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||||
|
|
||||||
|
ClientsSearch(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||||
|
|
||||||
// ClientsUpdateWithBody request with any body
|
// ClientsUpdateWithBody request with any body
|
||||||
ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||||
|
|
||||||
@@ -1985,6 +2015,30 @@ func (c *AdguardHomeClient) ClientsFind(ctx context.Context, params *ClientsFind
|
|||||||
return c.Client.Do(req)
|
return c.Client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *AdguardHomeClient) ClientsSearchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||||
|
req, err := NewClientsSearchRequestWithBody(c.Server, contentType, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Client.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AdguardHomeClient) ClientsSearch(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||||
|
req, err := NewClientsSearchRequest(c.Server, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Client.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *AdguardHomeClient) ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
func (c *AdguardHomeClient) ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||||
req, err := NewClientsUpdateRequestWithBody(c.Server, contentType, body)
|
req, err := NewClientsUpdateRequestWithBody(c.Server, contentType, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3673,6 +3727,46 @@ func NewClientsFindRequest(server string, params *ClientsFindParams) (*http.Requ
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewClientsSearchRequest calls the generic ClientsSearch builder with application/json body
|
||||||
|
func NewClientsSearchRequest(server string, body ClientsSearchJSONRequestBody) (*http.Request, error) {
|
||||||
|
var bodyReader io.Reader
|
||||||
|
buf, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bodyReader = bytes.NewReader(buf)
|
||||||
|
return NewClientsSearchRequestWithBody(server, "application/json", bodyReader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientsSearchRequestWithBody generates requests for ClientsSearch with any type of body
|
||||||
|
func NewClientsSearchRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
serverURL, err := url.Parse(server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
operationPath := fmt.Sprintf("/clients/search")
|
||||||
|
if operationPath[0] == '/' {
|
||||||
|
operationPath = "." + operationPath
|
||||||
|
}
|
||||||
|
|
||||||
|
queryURL, err := serverURL.Parse(operationPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", queryURL.String(), body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", contentType)
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewClientsUpdateRequest calls the generic ClientsUpdate builder with application/json body
|
// NewClientsUpdateRequest calls the generic ClientsUpdate builder with application/json body
|
||||||
func NewClientsUpdateRequest(server string, body ClientsUpdateJSONRequestBody) (*http.Request, error) {
|
func NewClientsUpdateRequest(server string, body ClientsUpdateJSONRequestBody) (*http.Request, error) {
|
||||||
var bodyReader io.Reader
|
var bodyReader io.Reader
|
||||||
@@ -5983,6 +6077,11 @@ type ClientWithResponsesInterface interface {
|
|||||||
// ClientsFindWithResponse request
|
// ClientsFindWithResponse request
|
||||||
ClientsFindWithResponse(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*ClientsFindResp, error)
|
ClientsFindWithResponse(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*ClientsFindResp, error)
|
||||||
|
|
||||||
|
// ClientsSearchWithBodyWithResponse request with any body
|
||||||
|
ClientsSearchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error)
|
||||||
|
|
||||||
|
ClientsSearchWithResponse(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error)
|
||||||
|
|
||||||
// ClientsUpdateWithBodyWithResponse request with any body
|
// ClientsUpdateWithBodyWithResponse request with any body
|
||||||
ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error)
|
ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error)
|
||||||
|
|
||||||
@@ -6559,6 +6658,28 @@ func (r ClientsFindResp) StatusCode() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientsSearchResp struct {
|
||||||
|
Body []byte
|
||||||
|
HTTPResponse *http.Response
|
||||||
|
JSON200 *ClientsFindResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns HTTPResponse.Status
|
||||||
|
func (r ClientsSearchResp) Status() string {
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
return r.HTTPResponse.Status
|
||||||
|
}
|
||||||
|
return http.StatusText(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusCode returns HTTPResponse.StatusCode
|
||||||
|
func (r ClientsSearchResp) StatusCode() int {
|
||||||
|
if r.HTTPResponse != nil {
|
||||||
|
return r.HTTPResponse.StatusCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type ClientsUpdateResp struct {
|
type ClientsUpdateResp struct {
|
||||||
Body []byte
|
Body []byte
|
||||||
HTTPResponse *http.Response
|
HTTPResponse *http.Response
|
||||||
@@ -8098,6 +8219,23 @@ func (c *ClientWithResponses) ClientsFindWithResponse(ctx context.Context, param
|
|||||||
return ParseClientsFindResp(rsp)
|
return ParseClientsFindResp(rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientsSearchWithBodyWithResponse request with arbitrary body returning *ClientsSearchResp
|
||||||
|
func (c *ClientWithResponses) ClientsSearchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error) {
|
||||||
|
rsp, err := c.ClientsSearchWithBody(ctx, contentType, body, reqEditors...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ParseClientsSearchResp(rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientWithResponses) ClientsSearchWithResponse(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error) {
|
||||||
|
rsp, err := c.ClientsSearch(ctx, body, reqEditors...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ParseClientsSearchResp(rsp)
|
||||||
|
}
|
||||||
|
|
||||||
// ClientsUpdateWithBodyWithResponse request with arbitrary body returning *ClientsUpdateResp
|
// ClientsUpdateWithBodyWithResponse request with arbitrary body returning *ClientsUpdateResp
|
||||||
func (c *ClientWithResponses) ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error) {
|
func (c *ClientWithResponses) ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error) {
|
||||||
rsp, err := c.ClientsUpdateWithBody(ctx, contentType, body, reqEditors...)
|
rsp, err := c.ClientsUpdateWithBody(ctx, contentType, body, reqEditors...)
|
||||||
@@ -9243,6 +9381,32 @@ func ParseClientsFindResp(rsp *http.Response) (*ClientsFindResp, error) {
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseClientsSearchResp parses an HTTP response from a ClientsSearchWithResponse call
|
||||||
|
func ParseClientsSearchResp(rsp *http.Response) (*ClientsSearchResp, error) {
|
||||||
|
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||||
|
defer func() { _ = rsp.Body.Close() }()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ClientsSearchResp{
|
||||||
|
Body: bodyBytes,
|
||||||
|
HTTPResponse: rsp,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||||
|
var dest ClientsFindResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.JSON200 = &dest
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ParseClientsUpdateResp parses an HTTP response from a ClientsUpdateWithResponse call
|
// ParseClientsUpdateResp parses an HTTP response from a ClientsUpdateWithResponse call
|
||||||
func ParseClientsUpdateResp(rsp *http.Response) (*ClientsUpdateResp, error) {
|
func ParseClientsUpdateResp(rsp *http.Response) (*ClientsUpdateResp, error) {
|
||||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||||
|
|||||||
@@ -242,7 +242,10 @@ var _ = Describe("Types", func() {
|
|||||||
})
|
})
|
||||||
It("should return 3 one replicas if urls are different", func() {
|
It("should return 3 one replicas if urls are different", func() {
|
||||||
cfg.Replica = &types.AdGuardInstance{URL: url, APIPath: apiPath}
|
cfg.Replica = &types.AdGuardInstance{URL: url, APIPath: apiPath}
|
||||||
cfg.Replicas = []types.AdGuardInstance{{URL: url + "1", APIPath: apiPath}, {URL: url, APIPath: apiPath + "1"}}
|
cfg.Replicas = []types.AdGuardInstance{
|
||||||
|
{URL: url + "1", APIPath: apiPath},
|
||||||
|
{URL: url, APIPath: apiPath + "1"},
|
||||||
|
}
|
||||||
r := cfg.UniqueReplicas()
|
r := cfg.UniqueReplicas()
|
||||||
Ω(r).Should(HaveLen(3))
|
Ω(r).Should(HaveLen(3))
|
||||||
})
|
})
|
||||||
@@ -303,6 +306,29 @@ var _ = Describe("Types", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("Client", func() {
|
||||||
|
Context("Equals", func() {
|
||||||
|
var (
|
||||||
|
cl1 *model.Client
|
||||||
|
cl2 *model.Client
|
||||||
|
)
|
||||||
|
BeforeEach(func() {
|
||||||
|
cl1 = &model.Client{
|
||||||
|
Name: utils.Ptr("foo"),
|
||||||
|
BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("UTC")},
|
||||||
|
}
|
||||||
|
cl2 = &model.Client{
|
||||||
|
Name: utils.Ptr("foo"),
|
||||||
|
BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("Local")},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should equal if only timezone differs on empty blocked service schedule", func() {
|
||||||
|
Ω(cl1.Equals(cl2)).Should(BeTrue())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Context("BlockedServices", func() {
|
Context("BlockedServices", func() {
|
||||||
Context("Equals", func() {
|
Context("Equals", func() {
|
||||||
It("should be equal", func() {
|
It("should be equal", func() {
|
||||||
|
|||||||
175
pkg/config/config-schema.json
Normal file
175
pkg/config/config-schema.json
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"definitions": {
|
||||||
|
"Instance": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"apiPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"autoSetup": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"dhcpServerEnabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"insecureSkipVerify": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"interfaceName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"format": "uri",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"webURL": {
|
||||||
|
"format": "uri",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "validates only for valid schema. No required fields, as the can be defined via env ars afterwards.",
|
||||||
|
"properties": {
|
||||||
|
"api": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"darkMode": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"queryLogLimit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"scrapeInterval": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"certDir": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"certName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"keyName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"continueOnError": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"cron": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"clientSettings": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"dhcp": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"serverConfig": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"staticLeases": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"dns": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"accessLists": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rewrites": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"serverConfig": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"filters": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"generalSettings": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"queryLogConfig": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"statsConfig": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"$ref": "#/definitions/Instance"
|
||||||
|
},
|
||||||
|
"printConfigOnly": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"replica": {
|
||||||
|
"$ref": "#/definitions/Instance"
|
||||||
|
},
|
||||||
|
"replicas": {
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Instance"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"runOnStart": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "adguardhome-sync Configuration",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||||
"github.com/caarlos0/env/v10"
|
"github.com/caarlos0/env/v11"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,6 +20,10 @@ func Get(configFile string, flags Flags) (*types.Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = validateSchema(path); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
cfg := initialConfig()
|
cfg := initialConfig()
|
||||||
|
|
||||||
// read yaml config
|
// read yaml config
|
||||||
@@ -39,19 +43,26 @@ func Get(configFile string, flags Flags) (*types.Config, error) {
|
|||||||
replicaDhcpServer := cfg.Replica.DHCPServerEnabled
|
replicaDhcpServer := cfg.Replica.DHCPServerEnabled
|
||||||
cfg.Replica.DHCPServerEnabled = nil
|
cfg.Replica.DHCPServerEnabled = nil
|
||||||
|
|
||||||
|
// ignore replicas form env parsing as they are handled separately
|
||||||
|
replicas := cfg.Replicas
|
||||||
|
cfg.Replicas = nil
|
||||||
|
|
||||||
// overwrite from env vars
|
// overwrite from env vars
|
||||||
if err := env.Parse(cfg); err != nil {
|
if err = env.Parse(cfg); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil {
|
if err = env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// restore the replica
|
||||||
|
cfg.Replicas = replicas
|
||||||
|
|
||||||
// if not set from env, use previous value
|
// if not set from env, use previous value
|
||||||
if cfg.Replica.DHCPServerEnabled == nil {
|
if cfg.Replica.DHCPServerEnabled == nil {
|
||||||
cfg.Replica.DHCPServerEnabled = replicaDhcpServer
|
cfg.Replica.DHCPServerEnabled = replicaDhcpServer
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil {
|
if err = env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ var envVars = []string{
|
|||||||
"FEATURES_CLIENT_SETTINGS",
|
"FEATURES_CLIENT_SETTINGS",
|
||||||
"FEATURES_SERVICES",
|
"FEATURES_SERVICES",
|
||||||
"FEATURES_FILTERS",
|
"FEATURES_FILTERS",
|
||||||
|
"FEATURES_THEME",
|
||||||
"FEATURES_DHCP_SERVER_CONFIG",
|
"FEATURES_DHCP_SERVER_CONFIG",
|
||||||
"FEATURES_DHCP_STATIC_LEASES",
|
"FEATURES_DHCP_STATIC_LEASES",
|
||||||
"FEATURES_DNS_SERVER_CONFIG",
|
"FEATURES_DNS_SERVER_CONFIG",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||||
"github.com/caarlos0/env/v10"
|
"github.com/caarlos0/env/v11"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleDeprecatedEnvVars(cfg *types.Config) {
|
func handleDeprecatedEnvVars(cfg *types.Config) {
|
||||||
|
|||||||
52
pkg/config/validate.go
Normal file
52
pkg/config/validate.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const schemaURL = "config-schema.json"
|
||||||
|
|
||||||
|
//go:embed config-schema.json
|
||||||
|
var schemaData string
|
||||||
|
|
||||||
|
func validateSchema(cfgFile string) error {
|
||||||
|
// ignore if file not exists
|
||||||
|
if _, err := os.Stat(cfgFile); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Load YAML file
|
||||||
|
yamlContent, err := os.ReadFile(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateYAML(yamlContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateYAML(yamlContent []byte) error {
|
||||||
|
// Convert YAML to JSON
|
||||||
|
var yamlData interface{}
|
||||||
|
err := yaml.Unmarshal(yamlContent, &yamlData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load JSON schema
|
||||||
|
sch, err := jsonschema.UnmarshalJSON(strings.NewReader(schemaData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := jsonschema.NewCompiler()
|
||||||
|
if err := c.AddResource(schemaURL, sch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
schema := c.MustCompile(schemaURL)
|
||||||
|
// validateSchema
|
||||||
|
return schema.Validate(yamlData)
|
||||||
|
}
|
||||||
39
pkg/config/validate_test.go
Normal file
39
pkg/config/validate_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||||
|
"github.com/go-faker/faker/v4"
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Config", func() {
|
||||||
|
Context("validateSchema", func() {
|
||||||
|
DescribeTable("validateSchema config",
|
||||||
|
func(configFile string, expectFail bool) {
|
||||||
|
err := validateSchema(configFile)
|
||||||
|
if expectFail {
|
||||||
|
Ω(err).Should(HaveOccurred())
|
||||||
|
} else {
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Entry(`Should be valid`, "../../testdata/config/config-valid.yaml", false),
|
||||||
|
Entry(`Should be valid if file doesn't exist`, "../../testdata/config/foo.bar", false),
|
||||||
|
Entry(`Should fail if file is not yaml`, "../../go.mod", true),
|
||||||
|
)
|
||||||
|
It("validate config with all fields randomly populated", func() {
|
||||||
|
cfg := &types.Config{}
|
||||||
|
|
||||||
|
err := faker.FakeData(cfg)
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(&cfg)
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
|
||||||
|
err = validateYAML(data)
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -106,6 +106,11 @@ func Logs() []string {
|
|||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the current logs
|
||||||
|
func Clear() {
|
||||||
|
logs = nil
|
||||||
|
}
|
||||||
|
|
||||||
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
fields[i].AddTo(enc)
|
fields[i].AddTo(enc)
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import (
|
|||||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const StatsTotal = "total"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
l = log.GetLogger("metrics")
|
l = log.GetLogger("metrics")
|
||||||
|
|
||||||
@@ -129,6 +130,8 @@ var (
|
|||||||
},
|
},
|
||||||
[]string{"hostname"},
|
[]string{"hostname"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stats = OverallStats{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init initializes all Prometheus metrics made available by AdGuard exporter.
|
// Init initializes all Prometheus metrics made available by AdGuard exporter.
|
||||||
@@ -155,6 +158,7 @@ func initMetric(name string, metric *prometheus.GaugeVec) {
|
|||||||
func Update(ims ...InstanceMetrics) {
|
func Update(ims ...InstanceMetrics) {
|
||||||
for _, im := range ims {
|
for _, im := range ims {
|
||||||
update(im)
|
update(im)
|
||||||
|
stats[im.HostName] = im.Stats
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Debug("updated")
|
l.Debug("updated")
|
||||||
@@ -233,13 +237,28 @@ type InstanceMetrics struct {
|
|||||||
QueryLog *model.QueryLog
|
QueryLog *model.QueryLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func safeMetric[T Number](v *T) float64 {
|
type OverallStats map[string]*model.Stats
|
||||||
|
|
||||||
|
func (os OverallStats) consolidate() OverallStats {
|
||||||
|
consolidated := OverallStats{StatsTotal: model.NewStats()}
|
||||||
|
for host, stats := range os {
|
||||||
|
consolidated[host] = stats
|
||||||
|
consolidated[StatsTotal].Add(stats)
|
||||||
|
}
|
||||||
|
return consolidated
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeMetric[T int | float64 | float32](v *T) float64 {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return float64(*v)
|
return float64(*v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Number interface {
|
func GetStats() OverallStats {
|
||||||
constraints.Float | constraints.Integer
|
return stats.consolidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os OverallStats) Total() *model.Stats {
|
||||||
|
return os[StatsTotal]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
type MockClient struct {
|
type MockClient struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockClientMockRecorder
|
recorder *MockClientMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockClientMockRecorder is the mock recorder for MockClient.
|
// MockClientMockRecorder is the mock recorder for MockClient.
|
||||||
@@ -55,52 +56,52 @@ func (mr *MockClientMockRecorder) AccessList() *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddClient mocks base method.
|
// AddClient mocks base method.
|
||||||
func (m *MockClient) AddClient(arg0 *model.Client) error {
|
func (m *MockClient) AddClient(client *model.Client) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AddClient", arg0)
|
ret := m.ctrl.Call(m, "AddClient", client)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddClient indicates an expected call of AddClient.
|
// AddClient indicates an expected call of AddClient.
|
||||||
func (mr *MockClientMockRecorder) AddClient(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) AddClient(client any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClient", reflect.TypeOf((*MockClient)(nil).AddClient), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClient", reflect.TypeOf((*MockClient)(nil).AddClient), client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDHCPStaticLease mocks base method.
|
// AddDHCPStaticLease mocks base method.
|
||||||
func (m *MockClient) AddDHCPStaticLease(arg0 model.DhcpStaticLease) error {
|
func (m *MockClient) AddDHCPStaticLease(lease model.DhcpStaticLease) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AddDHCPStaticLease", arg0)
|
ret := m.ctrl.Call(m, "AddDHCPStaticLease", lease)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDHCPStaticLease indicates an expected call of AddDHCPStaticLease.
|
// AddDHCPStaticLease indicates an expected call of AddDHCPStaticLease.
|
||||||
func (mr *MockClientMockRecorder) AddDHCPStaticLease(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) AddDHCPStaticLease(lease any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).AddDHCPStaticLease), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).AddDHCPStaticLease), lease)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFilter mocks base method.
|
// AddFilter mocks base method.
|
||||||
func (m *MockClient) AddFilter(arg0 bool, arg1 model.Filter) error {
|
func (m *MockClient) AddFilter(whitelist bool, f model.Filter) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "AddFilter", arg0, arg1)
|
ret := m.ctrl.Call(m, "AddFilter", whitelist, f)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFilter indicates an expected call of AddFilter.
|
// AddFilter indicates an expected call of AddFilter.
|
||||||
func (mr *MockClientMockRecorder) AddFilter(arg0, arg1 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) AddFilter(whitelist, f any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFilter", reflect.TypeOf((*MockClient)(nil).AddFilter), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFilter", reflect.TypeOf((*MockClient)(nil).AddFilter), whitelist, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRewriteEntries mocks base method.
|
// AddRewriteEntries mocks base method.
|
||||||
func (m *MockClient) AddRewriteEntries(arg0 ...model.RewriteEntry) error {
|
func (m *MockClient) AddRewriteEntries(e ...model.RewriteEntry) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
varargs := []any{}
|
varargs := []any{}
|
||||||
for _, a := range arg0 {
|
for _, a := range e {
|
||||||
varargs = append(varargs, a)
|
varargs = append(varargs, a)
|
||||||
}
|
}
|
||||||
ret := m.ctrl.Call(m, "AddRewriteEntries", varargs...)
|
ret := m.ctrl.Call(m, "AddRewriteEntries", varargs...)
|
||||||
@@ -109,9 +110,9 @@ func (m *MockClient) AddRewriteEntries(arg0 ...model.RewriteEntry) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddRewriteEntries indicates an expected call of AddRewriteEntries.
|
// AddRewriteEntries indicates an expected call of AddRewriteEntries.
|
||||||
func (mr *MockClientMockRecorder) AddRewriteEntries(arg0 ...any) *gomock.Call {
|
func (mr *MockClientMockRecorder) AddRewriteEntries(e ...any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewriteEntries", reflect.TypeOf((*MockClient)(nil).AddRewriteEntries), arg0...)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewriteEntries", reflect.TypeOf((*MockClient)(nil).AddRewriteEntries), e...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockedServicesSchedule mocks base method.
|
// BlockedServicesSchedule mocks base method.
|
||||||
@@ -160,52 +161,52 @@ func (mr *MockClientMockRecorder) DNSConfig() *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteClient mocks base method.
|
// DeleteClient mocks base method.
|
||||||
func (m *MockClient) DeleteClient(arg0 *model.Client) error {
|
func (m *MockClient) DeleteClient(client *model.Client) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "DeleteClient", arg0)
|
ret := m.ctrl.Call(m, "DeleteClient", client)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteClient indicates an expected call of DeleteClient.
|
// DeleteClient indicates an expected call of DeleteClient.
|
||||||
func (mr *MockClientMockRecorder) DeleteClient(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) DeleteClient(client any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClient", reflect.TypeOf((*MockClient)(nil).DeleteClient), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClient", reflect.TypeOf((*MockClient)(nil).DeleteClient), client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDHCPStaticLease mocks base method.
|
// DeleteDHCPStaticLease mocks base method.
|
||||||
func (m *MockClient) DeleteDHCPStaticLease(arg0 model.DhcpStaticLease) error {
|
func (m *MockClient) DeleteDHCPStaticLease(lease model.DhcpStaticLease) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "DeleteDHCPStaticLease", arg0)
|
ret := m.ctrl.Call(m, "DeleteDHCPStaticLease", lease)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDHCPStaticLease indicates an expected call of DeleteDHCPStaticLease.
|
// DeleteDHCPStaticLease indicates an expected call of DeleteDHCPStaticLease.
|
||||||
func (mr *MockClientMockRecorder) DeleteDHCPStaticLease(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) DeleteDHCPStaticLease(lease any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).DeleteDHCPStaticLease), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).DeleteDHCPStaticLease), lease)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFilter mocks base method.
|
// DeleteFilter mocks base method.
|
||||||
func (m *MockClient) DeleteFilter(arg0 bool, arg1 model.Filter) error {
|
func (m *MockClient) DeleteFilter(whitelist bool, f model.Filter) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "DeleteFilter", arg0, arg1)
|
ret := m.ctrl.Call(m, "DeleteFilter", whitelist, f)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFilter indicates an expected call of DeleteFilter.
|
// DeleteFilter indicates an expected call of DeleteFilter.
|
||||||
func (mr *MockClientMockRecorder) DeleteFilter(arg0, arg1 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) DeleteFilter(whitelist, f any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFilter", reflect.TypeOf((*MockClient)(nil).DeleteFilter), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFilter", reflect.TypeOf((*MockClient)(nil).DeleteFilter), whitelist, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRewriteEntries mocks base method.
|
// DeleteRewriteEntries mocks base method.
|
||||||
func (m *MockClient) DeleteRewriteEntries(arg0 ...model.RewriteEntry) error {
|
func (m *MockClient) DeleteRewriteEntries(e ...model.RewriteEntry) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
varargs := []any{}
|
varargs := []any{}
|
||||||
for _, a := range arg0 {
|
for _, a := range e {
|
||||||
varargs = append(varargs, a)
|
varargs = append(varargs, a)
|
||||||
}
|
}
|
||||||
ret := m.ctrl.Call(m, "DeleteRewriteEntries", varargs...)
|
ret := m.ctrl.Call(m, "DeleteRewriteEntries", varargs...)
|
||||||
@@ -214,9 +215,9 @@ func (m *MockClient) DeleteRewriteEntries(arg0 ...model.RewriteEntry) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRewriteEntries indicates an expected call of DeleteRewriteEntries.
|
// DeleteRewriteEntries indicates an expected call of DeleteRewriteEntries.
|
||||||
func (mr *MockClientMockRecorder) DeleteRewriteEntries(arg0 ...any) *gomock.Call {
|
func (mr *MockClientMockRecorder) DeleteRewriteEntries(e ...any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRewriteEntries", reflect.TypeOf((*MockClient)(nil).DeleteRewriteEntries), arg0...)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRewriteEntries", reflect.TypeOf((*MockClient)(nil).DeleteRewriteEntries), e...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DhcpConfig mocks base method.
|
// DhcpConfig mocks base method.
|
||||||
@@ -294,18 +295,18 @@ func (mr *MockClientMockRecorder) ProfileInfo() *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// QueryLog mocks base method.
|
// QueryLog mocks base method.
|
||||||
func (m *MockClient) QueryLog(arg0 int) (*model.QueryLog, error) {
|
func (m *MockClient) QueryLog(limit int) (*model.QueryLog, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "QueryLog", arg0)
|
ret := m.ctrl.Call(m, "QueryLog", limit)
|
||||||
ret0, _ := ret[0].(*model.QueryLog)
|
ret0, _ := ret[0].(*model.QueryLog)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryLog indicates an expected call of QueryLog.
|
// QueryLog indicates an expected call of QueryLog.
|
||||||
func (mr *MockClientMockRecorder) QueryLog(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) QueryLog(limit any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryLog", reflect.TypeOf((*MockClient)(nil).QueryLog), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryLog", reflect.TypeOf((*MockClient)(nil).QueryLog), limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryLogConfig mocks base method.
|
// QueryLogConfig mocks base method.
|
||||||
@@ -324,17 +325,17 @@ func (mr *MockClientMockRecorder) QueryLogConfig() *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RefreshFilters mocks base method.
|
// RefreshFilters mocks base method.
|
||||||
func (m *MockClient) RefreshFilters(arg0 bool) error {
|
func (m *MockClient) RefreshFilters(whitelist bool) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "RefreshFilters", arg0)
|
ret := m.ctrl.Call(m, "RefreshFilters", whitelist)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshFilters indicates an expected call of RefreshFilters.
|
// RefreshFilters indicates an expected call of RefreshFilters.
|
||||||
func (mr *MockClientMockRecorder) RefreshFilters(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) RefreshFilters(whitelist any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshFilters", reflect.TypeOf((*MockClient)(nil).RefreshFilters), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshFilters", reflect.TypeOf((*MockClient)(nil).RefreshFilters), whitelist)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RewriteList mocks base method.
|
// RewriteList mocks base method.
|
||||||
@@ -397,31 +398,31 @@ func (mr *MockClientMockRecorder) SetAccessList(arg0 any) *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBlockedServicesSchedule mocks base method.
|
// SetBlockedServicesSchedule mocks base method.
|
||||||
func (m *MockClient) SetBlockedServicesSchedule(arg0 *model.BlockedServicesSchedule) error {
|
func (m *MockClient) SetBlockedServicesSchedule(schedule *model.BlockedServicesSchedule) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SetBlockedServicesSchedule", arg0)
|
ret := m.ctrl.Call(m, "SetBlockedServicesSchedule", schedule)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBlockedServicesSchedule indicates an expected call of SetBlockedServicesSchedule.
|
// SetBlockedServicesSchedule indicates an expected call of SetBlockedServicesSchedule.
|
||||||
func (mr *MockClientMockRecorder) SetBlockedServicesSchedule(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) SetBlockedServicesSchedule(schedule any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockedServicesSchedule", reflect.TypeOf((*MockClient)(nil).SetBlockedServicesSchedule), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockedServicesSchedule", reflect.TypeOf((*MockClient)(nil).SetBlockedServicesSchedule), schedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCustomRules mocks base method.
|
// SetCustomRules mocks base method.
|
||||||
func (m *MockClient) SetCustomRules(arg0 *[]string) error {
|
func (m *MockClient) SetCustomRules(rules *[]string) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SetCustomRules", arg0)
|
ret := m.ctrl.Call(m, "SetCustomRules", rules)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCustomRules indicates an expected call of SetCustomRules.
|
// SetCustomRules indicates an expected call of SetCustomRules.
|
||||||
func (mr *MockClientMockRecorder) SetCustomRules(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) SetCustomRules(rules any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCustomRules", reflect.TypeOf((*MockClient)(nil).SetCustomRules), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCustomRules", reflect.TypeOf((*MockClient)(nil).SetCustomRules), rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDNSConfig mocks base method.
|
// SetDNSConfig mocks base method.
|
||||||
@@ -453,17 +454,17 @@ func (mr *MockClientMockRecorder) SetDhcpConfig(arg0 any) *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetProfileInfo mocks base method.
|
// SetProfileInfo mocks base method.
|
||||||
func (m *MockClient) SetProfileInfo(arg0 *model.ProfileInfo) error {
|
func (m *MockClient) SetProfileInfo(settings *model.ProfileInfo) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SetProfileInfo", arg0)
|
ret := m.ctrl.Call(m, "SetProfileInfo", settings)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProfileInfo indicates an expected call of SetProfileInfo.
|
// SetProfileInfo indicates an expected call of SetProfileInfo.
|
||||||
func (mr *MockClientMockRecorder) SetProfileInfo(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) SetProfileInfo(settings any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProfileInfo", reflect.TypeOf((*MockClient)(nil).SetProfileInfo), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProfileInfo", reflect.TypeOf((*MockClient)(nil).SetProfileInfo), settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetQueryLogConfig mocks base method.
|
// SetQueryLogConfig mocks base method.
|
||||||
@@ -481,31 +482,31 @@ func (mr *MockClientMockRecorder) SetQueryLogConfig(arg0 any) *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetSafeSearchConfig mocks base method.
|
// SetSafeSearchConfig mocks base method.
|
||||||
func (m *MockClient) SetSafeSearchConfig(arg0 *model.SafeSearchConfig) error {
|
func (m *MockClient) SetSafeSearchConfig(settings *model.SafeSearchConfig) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SetSafeSearchConfig", arg0)
|
ret := m.ctrl.Call(m, "SetSafeSearchConfig", settings)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSafeSearchConfig indicates an expected call of SetSafeSearchConfig.
|
// SetSafeSearchConfig indicates an expected call of SetSafeSearchConfig.
|
||||||
func (mr *MockClientMockRecorder) SetSafeSearchConfig(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) SetSafeSearchConfig(settings any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSafeSearchConfig", reflect.TypeOf((*MockClient)(nil).SetSafeSearchConfig), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSafeSearchConfig", reflect.TypeOf((*MockClient)(nil).SetSafeSearchConfig), settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStatsConfig mocks base method.
|
// SetStatsConfig mocks base method.
|
||||||
func (m *MockClient) SetStatsConfig(arg0 *model.GetStatsConfigResponse) error {
|
func (m *MockClient) SetStatsConfig(sc *model.GetStatsConfigResponse) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SetStatsConfig", arg0)
|
ret := m.ctrl.Call(m, "SetStatsConfig", sc)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStatsConfig indicates an expected call of SetStatsConfig.
|
// SetStatsConfig indicates an expected call of SetStatsConfig.
|
||||||
func (mr *MockClientMockRecorder) SetStatsConfig(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) SetStatsConfig(sc any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatsConfig", reflect.TypeOf((*MockClient)(nil).SetStatsConfig), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatsConfig", reflect.TypeOf((*MockClient)(nil).SetStatsConfig), sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup mocks base method.
|
// Setup mocks base method.
|
||||||
@@ -568,85 +569,85 @@ func (mr *MockClientMockRecorder) Status() *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToggleFiltering mocks base method.
|
// ToggleFiltering mocks base method.
|
||||||
func (m *MockClient) ToggleFiltering(arg0 bool, arg1 int) error {
|
func (m *MockClient) ToggleFiltering(enabled bool, interval int) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ToggleFiltering", arg0, arg1)
|
ret := m.ctrl.Call(m, "ToggleFiltering", enabled, interval)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleFiltering indicates an expected call of ToggleFiltering.
|
// ToggleFiltering indicates an expected call of ToggleFiltering.
|
||||||
func (mr *MockClientMockRecorder) ToggleFiltering(arg0, arg1 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) ToggleFiltering(enabled, interval any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleFiltering", reflect.TypeOf((*MockClient)(nil).ToggleFiltering), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleFiltering", reflect.TypeOf((*MockClient)(nil).ToggleFiltering), enabled, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleParental mocks base method.
|
// ToggleParental mocks base method.
|
||||||
func (m *MockClient) ToggleParental(arg0 bool) error {
|
func (m *MockClient) ToggleParental(enable bool) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ToggleParental", arg0)
|
ret := m.ctrl.Call(m, "ToggleParental", enable)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleParental indicates an expected call of ToggleParental.
|
// ToggleParental indicates an expected call of ToggleParental.
|
||||||
func (mr *MockClientMockRecorder) ToggleParental(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) ToggleParental(enable any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleParental", reflect.TypeOf((*MockClient)(nil).ToggleParental), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleParental", reflect.TypeOf((*MockClient)(nil).ToggleParental), enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleProtection mocks base method.
|
// ToggleProtection mocks base method.
|
||||||
func (m *MockClient) ToggleProtection(arg0 bool) error {
|
func (m *MockClient) ToggleProtection(enable bool) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ToggleProtection", arg0)
|
ret := m.ctrl.Call(m, "ToggleProtection", enable)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleProtection indicates an expected call of ToggleProtection.
|
// ToggleProtection indicates an expected call of ToggleProtection.
|
||||||
func (mr *MockClientMockRecorder) ToggleProtection(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) ToggleProtection(enable any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleProtection", reflect.TypeOf((*MockClient)(nil).ToggleProtection), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleProtection", reflect.TypeOf((*MockClient)(nil).ToggleProtection), enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleSafeBrowsing mocks base method.
|
// ToggleSafeBrowsing mocks base method.
|
||||||
func (m *MockClient) ToggleSafeBrowsing(arg0 bool) error {
|
func (m *MockClient) ToggleSafeBrowsing(enable bool) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "ToggleSafeBrowsing", arg0)
|
ret := m.ctrl.Call(m, "ToggleSafeBrowsing", enable)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleSafeBrowsing indicates an expected call of ToggleSafeBrowsing.
|
// ToggleSafeBrowsing indicates an expected call of ToggleSafeBrowsing.
|
||||||
func (mr *MockClientMockRecorder) ToggleSafeBrowsing(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) ToggleSafeBrowsing(enable any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleSafeBrowsing", reflect.TypeOf((*MockClient)(nil).ToggleSafeBrowsing), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleSafeBrowsing", reflect.TypeOf((*MockClient)(nil).ToggleSafeBrowsing), enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateClient mocks base method.
|
// UpdateClient mocks base method.
|
||||||
func (m *MockClient) UpdateClient(arg0 *model.Client) error {
|
func (m *MockClient) UpdateClient(client *model.Client) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UpdateClient", arg0)
|
ret := m.ctrl.Call(m, "UpdateClient", client)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateClient indicates an expected call of UpdateClient.
|
// UpdateClient indicates an expected call of UpdateClient.
|
||||||
func (mr *MockClientMockRecorder) UpdateClient(arg0 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) UpdateClient(client any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClient", reflect.TypeOf((*MockClient)(nil).UpdateClient), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClient", reflect.TypeOf((*MockClient)(nil).UpdateClient), client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFilter mocks base method.
|
// UpdateFilter mocks base method.
|
||||||
func (m *MockClient) UpdateFilter(arg0 bool, arg1 model.Filter) error {
|
func (m *MockClient) UpdateFilter(whitelist bool, f model.Filter) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UpdateFilter", arg0, arg1)
|
ret := m.ctrl.Call(m, "UpdateFilter", whitelist, f)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFilter indicates an expected call of UpdateFilter.
|
// UpdateFilter indicates an expected call of UpdateFilter.
|
||||||
func (mr *MockClientMockRecorder) UpdateFilter(arg0, arg1 any) *gomock.Call {
|
func (mr *MockClientMockRecorder) UpdateFilter(whitelist, f any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFilter", reflect.TypeOf((*MockClient)(nil).UpdateFilter), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFilter", reflect.TypeOf((*MockClient)(nil).UpdateFilter), whitelist, f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
type MockFlags struct {
|
type MockFlags struct {
|
||||||
ctrl *gomock.Controller
|
ctrl *gomock.Controller
|
||||||
recorder *MockFlagsMockRecorder
|
recorder *MockFlagsMockRecorder
|
||||||
|
isgomock struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockFlagsMockRecorder is the mock recorder for MockFlags.
|
// MockFlagsMockRecorder is the mock recorder for MockFlags.
|
||||||
@@ -39,60 +40,60 @@ func (m *MockFlags) EXPECT() *MockFlagsMockRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Changed mocks base method.
|
// Changed mocks base method.
|
||||||
func (m *MockFlags) Changed(arg0 string) bool {
|
func (m *MockFlags) Changed(name string) bool {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Changed", arg0)
|
ret := m.ctrl.Call(m, "Changed", name)
|
||||||
ret0, _ := ret[0].(bool)
|
ret0, _ := ret[0].(bool)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changed indicates an expected call of Changed.
|
// Changed indicates an expected call of Changed.
|
||||||
func (mr *MockFlagsMockRecorder) Changed(arg0 any) *gomock.Call {
|
func (mr *MockFlagsMockRecorder) Changed(name any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Changed", reflect.TypeOf((*MockFlags)(nil).Changed), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Changed", reflect.TypeOf((*MockFlags)(nil).Changed), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBool mocks base method.
|
// GetBool mocks base method.
|
||||||
func (m *MockFlags) GetBool(arg0 string) (bool, error) {
|
func (m *MockFlags) GetBool(name string) (bool, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetBool", arg0)
|
ret := m.ctrl.Call(m, "GetBool", name)
|
||||||
ret0, _ := ret[0].(bool)
|
ret0, _ := ret[0].(bool)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBool indicates an expected call of GetBool.
|
// GetBool indicates an expected call of GetBool.
|
||||||
func (mr *MockFlagsMockRecorder) GetBool(arg0 any) *gomock.Call {
|
func (mr *MockFlagsMockRecorder) GetBool(name any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBool", reflect.TypeOf((*MockFlags)(nil).GetBool), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBool", reflect.TypeOf((*MockFlags)(nil).GetBool), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt mocks base method.
|
// GetInt mocks base method.
|
||||||
func (m *MockFlags) GetInt(arg0 string) (int, error) {
|
func (m *MockFlags) GetInt(name string) (int, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetInt", arg0)
|
ret := m.ctrl.Call(m, "GetInt", name)
|
||||||
ret0, _ := ret[0].(int)
|
ret0, _ := ret[0].(int)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInt indicates an expected call of GetInt.
|
// GetInt indicates an expected call of GetInt.
|
||||||
func (mr *MockFlagsMockRecorder) GetInt(arg0 any) *gomock.Call {
|
func (mr *MockFlagsMockRecorder) GetInt(name any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInt", reflect.TypeOf((*MockFlags)(nil).GetInt), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInt", reflect.TypeOf((*MockFlags)(nil).GetInt), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetString mocks base method.
|
// GetString mocks base method.
|
||||||
func (m *MockFlags) GetString(arg0 string) (string, error) {
|
func (m *MockFlags) GetString(name string) (string, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetString", arg0)
|
ret := m.ctrl.Call(m, "GetString", name)
|
||||||
ret0, _ := ret[0].(string)
|
ret0, _ := ret[0].(string)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetString indicates an expected call of GetString.
|
// GetString indicates an expected call of GetString.
|
||||||
func (mr *MockFlagsMockRecorder) GetString(arg0 any) *gomock.Call {
|
func (mr *MockFlagsMockRecorder) GetString(name any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetString", reflect.TypeOf((*MockFlags)(nil).GetString), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetString", reflect.TypeOf((*MockFlags)(nil).GetString), name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var (
|
|||||||
actionProfileInfo = func(ac *actionContext) error {
|
actionProfileInfo = func(ac *actionContext) error {
|
||||||
if pro, err := ac.client.ProfileInfo(); err != nil {
|
if pro, err := ac.client.ProfileInfo(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if merged := pro.ShouldSyncFor(ac.origin.profileInfo); merged != nil {
|
} else if merged := pro.ShouldSyncFor(ac.origin.profileInfo, ac.cfg.Features.Theme); merged != nil {
|
||||||
return ac.client.SetProfileInfo(merged)
|
return ac.client.SetProfileInfo(merged)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -94,10 +94,10 @@ var (
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syncFilterType(ac.rl, ac.origin.filters.Filters, rf.Filters, false, ac.client, ac.continueOnError); err != nil {
|
if err = syncFilterType(ac.rl, ac.origin.filters.Filters, rf.Filters, false, ac.client, ac.cfg.ContinueOnError); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = syncFilterType(ac.rl, ac.origin.filters.WhitelistFilters, rf.WhitelistFilters, true, ac.client, ac.continueOnError); err != nil {
|
if err = syncFilterType(ac.rl, ac.origin.filters.WhitelistFilters, rf.WhitelistFilters, true, ac.client, ac.cfg.ContinueOnError); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,8 @@ var (
|
|||||||
return ac.client.SetCustomRules(ac.origin.filters.UserRules)
|
return ac.client.SetCustomRules(ac.origin.filters.UserRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ac.origin.filters.Enabled != rf.Enabled || ac.origin.filters.Interval != rf.Interval {
|
if !utils.PtrEquals(ac.origin.filters.Enabled, rf.Enabled) ||
|
||||||
|
!utils.PtrEquals(ac.origin.filters.Interval, rf.Interval) {
|
||||||
return ac.client.ToggleFiltering(*ac.origin.filters.Enabled, *ac.origin.filters.Interval)
|
return ac.client.ToggleFiltering(*ac.origin.filters.Enabled, *ac.origin.filters.Interval)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -133,7 +134,7 @@ var (
|
|||||||
for _, client := range r {
|
for _, client := range r {
|
||||||
if err := ac.client.DeleteClient(client); err != nil {
|
if err := ac.client.DeleteClient(client); err != nil {
|
||||||
ac.rl.With("client-name", client.Name, "error", err).Error("error deleting client setting")
|
ac.rl.With("client-name", client.Name, "error", err).Error("error deleting client setting")
|
||||||
if !ac.continueOnError {
|
if !ac.cfg.ContinueOnError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +143,7 @@ var (
|
|||||||
for _, client := range a {
|
for _, client := range a {
|
||||||
if err := ac.client.AddClient(client); err != nil {
|
if err := ac.client.AddClient(client); err != nil {
|
||||||
ac.rl.With("client-name", client.Name, "error", err).Error("error adding client setting")
|
ac.rl.With("client-name", client.Name, "error", err).Error("error adding client setting")
|
||||||
if !ac.continueOnError {
|
if !ac.cfg.ContinueOnError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +152,7 @@ var (
|
|||||||
for _, client := range u {
|
for _, client := range u {
|
||||||
if err := ac.client.UpdateClient(client); err != nil {
|
if err := ac.client.UpdateClient(client); err != nil {
|
||||||
ac.rl.With("client-name", client.Name, "error", err).Error("error updating client setting")
|
ac.rl.With("client-name", client.Name, "error", err).Error("error updating client setting")
|
||||||
if !ac.continueOnError {
|
if !ac.cfg.ContinueOnError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,7 @@ var (
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.Sanitize(ac.rl)
|
// dc.Sanitize(ac.rl)
|
||||||
|
|
||||||
if !dc.Equals(ac.origin.dnsConfig) {
|
if !dc.Equals(ac.origin.dnsConfig) {
|
||||||
if err = ac.client.SetDNSConfig(ac.origin.dnsConfig); err != nil {
|
if err = ac.client.SetDNSConfig(ac.origin.dnsConfig); err != nil {
|
||||||
@@ -218,7 +219,7 @@ var (
|
|||||||
for _, lease := range r {
|
for _, lease := range r {
|
||||||
if err := ac.client.DeleteDHCPStaticLease(lease); err != nil {
|
if err := ac.client.DeleteDHCPStaticLease(lease); err != nil {
|
||||||
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error deleting dhcp static lease")
|
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error deleting dhcp static lease")
|
||||||
if !ac.continueOnError {
|
if !ac.cfg.ContinueOnError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +228,7 @@ var (
|
|||||||
for _, lease := range a {
|
for _, lease := range a {
|
||||||
if err := ac.client.AddDHCPStaticLease(lease); err != nil {
|
if err := ac.client.AddDHCPStaticLease(lease); err != nil {
|
||||||
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error adding dhcp static lease")
|
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error adding dhcp static lease")
|
||||||
if !ac.continueOnError {
|
if !ac.cfg.ContinueOnError {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,14 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func syncFilterType(rl *zap.SugaredLogger, of *[]model.Filter, rFilters *[]model.Filter, whitelist bool, replica client.Client, continueOnError bool) error {
|
func syncFilterType(
|
||||||
|
rl *zap.SugaredLogger,
|
||||||
|
of *[]model.Filter,
|
||||||
|
rFilters *[]model.Filter,
|
||||||
|
whitelist bool,
|
||||||
|
replica client.Client,
|
||||||
|
continueOnError bool,
|
||||||
|
) error {
|
||||||
fa, fu, fd := model.MergeFilters(rFilters, of)
|
fa, fu, fd := model.MergeFilters(rFilters, of)
|
||||||
|
|
||||||
for _, f := range fd {
|
for _, f := range fd {
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ func setupActions(cfg *types.Config) (actions []syncAction) {
|
|||||||
action("safe browsing", actionSafeBrowsing),
|
action("safe browsing", actionSafeBrowsing),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if cfg.Features.DNS.ServerConfig {
|
||||||
|
actions = append(actions,
|
||||||
|
action("DNS server config", actionDNSServerConfig),
|
||||||
|
)
|
||||||
|
}
|
||||||
if cfg.Features.QueryLogConfig {
|
if cfg.Features.QueryLogConfig {
|
||||||
actions = append(actions,
|
actions = append(actions,
|
||||||
action("query log config", actionQueryLogConfig),
|
action("query log config", actionQueryLogConfig),
|
||||||
@@ -52,12 +57,6 @@ func setupActions(cfg *types.Config) (actions []syncAction) {
|
|||||||
action("DNS access lists", actionDNSAccessLists),
|
action("DNS access lists", actionDNSAccessLists),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Features.DNS.ServerConfig {
|
|
||||||
actions = append(actions,
|
|
||||||
action("DNS server config", actionDNSServerConfig),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if cfg.Features.DHCP.ServerConfig {
|
if cfg.Features.DHCP.ServerConfig {
|
||||||
actions = append(actions,
|
actions = append(actions,
|
||||||
action("DHCP server config", actionDHCPServerConfig),
|
action("DHCP server config", actionDHCPServerConfig),
|
||||||
@@ -77,12 +76,12 @@ type syncAction interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type actionContext struct {
|
type actionContext struct {
|
||||||
rl *zap.SugaredLogger
|
rl *zap.SugaredLogger
|
||||||
origin *origin
|
origin *origin
|
||||||
client client.Client
|
client client.Client
|
||||||
replicaStatus *model.ServerStatus
|
replicaStatus *model.ServerStatus
|
||||||
continueOnError bool
|
replica types.AdGuardInstance
|
||||||
replica types.AdGuardInstance
|
cfg *types.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaultAction struct {
|
type defaultAction struct {
|
||||||
|
|||||||
@@ -33,11 +33,38 @@ func (w *worker) handleSync(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) handleRoot(c *gin.Context) {
|
func (w *worker) handleRoot(c *gin.Context) {
|
||||||
|
total, dns, blocked, malware, adult := statsGraph()
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "index.html", map[string]interface{}{
|
c.HTML(http.StatusOK, "index.html", map[string]interface{}{
|
||||||
"DarkMode": w.cfg.API.DarkMode,
|
"DarkMode": w.cfg.API.DarkMode,
|
||||||
|
"Metrics": w.cfg.API.Metrics.Enabled,
|
||||||
"Version": version.Version,
|
"Version": version.Version,
|
||||||
"Build": version.Build,
|
"Build": version.Build,
|
||||||
"SyncStatus": w.status(),
|
"SyncStatus": w.status(),
|
||||||
|
"Stats": map[string]interface{}{
|
||||||
|
"Labels": getLast24Hours(),
|
||||||
|
"DNS": dns,
|
||||||
|
"Blocked": blocked,
|
||||||
|
"BlockedPercentage": fmt.Sprintf(
|
||||||
|
"%.2f",
|
||||||
|
(float64(*total.NumBlockedFiltering)*100.0)/float64(*total.NumDnsQueries),
|
||||||
|
),
|
||||||
|
"Malware": malware,
|
||||||
|
"MalwarePercentage": fmt.Sprintf(
|
||||||
|
"%.2f",
|
||||||
|
(float64(*total.NumReplacedSafebrowsing)*100.0)/float64(*total.NumDnsQueries),
|
||||||
|
),
|
||||||
|
"Adult": adult,
|
||||||
|
"AdultPercentage": fmt.Sprintf(
|
||||||
|
"%.2f",
|
||||||
|
(float64(*total.NumReplacedParental)*100.0)/float64(*total.NumDnsQueries),
|
||||||
|
),
|
||||||
|
|
||||||
|
"TotalDNS": total.NumDnsQueries,
|
||||||
|
"TotalBlocked": total.NumBlockedFiltering,
|
||||||
|
"TotalMalware": total.NumReplacedSafebrowsing,
|
||||||
|
"TotalAdult": total.NumReplacedParental,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -50,6 +77,11 @@ func (w *worker) handleLogs(c *gin.Context) {
|
|||||||
c.Data(http.StatusOK, "text/plain", []byte(strings.Join(log.Logs(), "")))
|
c.Data(http.StatusOK, "text/plain", []byte(strings.Join(log.Logs(), "")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *worker) handleClearLogs(c *gin.Context) {
|
||||||
|
log.Clear()
|
||||||
|
c.Status(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *worker) handleStatus(c *gin.Context) {
|
func (w *worker) handleStatus(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, w.status())
|
c.JSON(http.StatusOK, w.status())
|
||||||
}
|
}
|
||||||
@@ -80,6 +112,7 @@ func (w *worker) listenAndServe() {
|
|||||||
r.SetHTMLTemplate(template.Must(template.New("index.html").Parse(string(index))))
|
r.SetHTMLTemplate(template.Must(template.New("index.html").Parse(string(index))))
|
||||||
r.POST("/api/v1/sync", w.handleSync)
|
r.POST("/api/v1/sync", w.handleSync)
|
||||||
r.GET("/api/v1/logs", w.handleLogs)
|
r.GET("/api/v1/logs", w.handleLogs)
|
||||||
|
r.POST("/api/v1/clear-logs", w.handleClearLogs)
|
||||||
r.GET("/api/v1/status", w.handleStatus)
|
r.GET("/api/v1/status", w.handleStatus)
|
||||||
r.GET("/favicon.ico", w.handleFavicon)
|
r.GET("/favicon.ico", w.handleFavicon)
|
||||||
r.GET("/", w.handleRoot)
|
r.GET("/", w.handleRoot)
|
||||||
@@ -153,3 +186,26 @@ type replicaStatus struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
ProtectionEnabled *bool `json:"protection_enabled"`
|
ProtectionEnabled *bool `json:"protection_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLast24Hours() []string {
|
||||||
|
var result []string
|
||||||
|
currentTime := time.Now()
|
||||||
|
|
||||||
|
// Loop to get the last 24 hours
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
// Calculate the time for the current hour in the loop
|
||||||
|
timeInstance := currentTime.Add(time.Duration(-i) * time.Hour)
|
||||||
|
timeInstance = timeInstance.Truncate(time.Hour)
|
||||||
|
|
||||||
|
// Format the time as "14 Dec 17:00"
|
||||||
|
formattedTime := timeInstance.Format("02 Jan 15:04")
|
||||||
|
result = append(result, formattedTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the slice to get the correct order (from oldest to latest)
|
||||||
|
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
result[i], result[j] = result[j], result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>AdGuardHome sync</title>
|
<title>AdGuardHome sync</title>
|
||||||
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js">
|
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.1.min.js">
|
||||||
</script>
|
</script>
|
||||||
{{- if .DarkMode }}
|
{{- if .DarkMode }}
|
||||||
<link rel="stylesheet" href="https://bootswatch.com/5/darkly/bootstrap.min.css"
|
<link rel="stylesheet" href="https://bootswatch.com/5/darkly/bootstrap.min.css"
|
||||||
crossorigin="anonymous">
|
crossorigin="anonymous">
|
||||||
{{- else }}
|
{{- else }}
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||||
crossOrigin="anonymous">
|
crossorigin="anonymous">
|
||||||
{{- end }}
|
{{- end }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
@@ -30,6 +30,12 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
$("#clearLogs").click(function () {
|
||||||
|
$.post("api/v1/clear-logs", {}, function () {
|
||||||
|
$('#logs').html("");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
$("#sync").click(function () {
|
$("#sync").click(function () {
|
||||||
$.post("api/v1/sync", {}, function (data) {
|
$.post("api/v1/sync", {}, function (data) {
|
||||||
});
|
});
|
||||||
@@ -39,6 +45,43 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<link rel="shortcut icon" href="favicon.ico">
|
<link rel="shortcut icon" href="favicon.ico">
|
||||||
|
<style>
|
||||||
|
.stat-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 15px;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
{{- if .Metrics }}
|
||||||
|
.stat-card h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.stat-card p {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.percentage {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-align: right;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100px !important;
|
||||||
|
}
|
||||||
|
{{- end }}
|
||||||
|
.button-row {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.btn-group {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid px-4">
|
<div class="container-fluid px-4">
|
||||||
@@ -48,15 +91,60 @@
|
|||||||
<p class="h6">{{ .Version }} ({{ .Build }})</p>
|
<p class="h6">{{ .Version }} ({{ .Build }})</p>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
{{- if .Metrics }}
|
||||||
|
<div class="row g-4 d-flex">
|
||||||
|
<div class="col-12 col-md-3 d-flex">
|
||||||
|
<div class="stat-card flex-fill">
|
||||||
|
<div class="percentage"></div>
|
||||||
|
<h3 style="color: rgb(78, 141, 245);">{{.Stats.TotalDNS}}</h3>
|
||||||
|
<p>DNS Queries</p>
|
||||||
|
<canvas id="dnsQueriesChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 d-flex">
|
||||||
|
<div class="stat-card flex-fill">
|
||||||
|
<div class="percentage" style="color: rgb(255, 94, 94);">{{.Stats.BlockedPercentage}}%</div>
|
||||||
|
<h3 style="color: rgb(255, 94, 94);">{{.Stats.TotalBlocked}}</h3>
|
||||||
|
<p>Blocked by Filters</p>
|
||||||
|
<canvas id="blockedFiltersChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 d-flex">
|
||||||
|
<div class="stat-card flex-fill">
|
||||||
|
<div class="percentage" style="color: rgb(110, 224, 122);">{{.Stats.MalwarePercentage}}%</div>
|
||||||
|
<h3 style="color: rgb(110, 224, 122);">{{.Stats.TotalMalware}}</h3>
|
||||||
|
<p>Blocked malware/phishing</p>
|
||||||
|
<canvas id="malwareChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 d-flex">
|
||||||
|
<div class="stat-card flex-fill">
|
||||||
|
<div class="percentage" style="color: rgb(232, 198, 78);">{{.Stats.AdultPercentage}}%</div>
|
||||||
|
<h3 style="color: rgb(232, 198, 78);">{{.Stats.TotalAdult}}</h3>
|
||||||
|
<p>Blocked adult websites</p>
|
||||||
|
<canvas id="adultWebsitesChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
||||||
|
<div class="row button-row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<input class="btn btn-success" type="button" id="sync" value="Synchronize"/>
|
<button type="button" class="btn btn-success" id="sync">Synchronize</button>
|
||||||
<input class="btn btn-secondary" type="button" id="showLogs" value="Update Logs"/>
|
<button type="button" class="btn btn-secondary" id="showLogs">Update Logs</button>
|
||||||
|
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="#" id="clearLogs">Clear Logs</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-md-auto">
|
<div class="col col-md-auto">
|
||||||
<div class="float-right">
|
<div class="btn-group float-right" role="group">
|
||||||
<a href="{{ .SyncStatus.Origin.URL }}" target="_blank" class="btn btn-{{ .SyncStatus.Origin.Status }}"
|
<a href="{{ .SyncStatus.Origin.URL }}" target="_blank" class="btn btn-{{ .SyncStatus.Origin.Status }}"
|
||||||
type="button" id="origin"
|
type="button" id="origin"
|
||||||
{{ if .SyncStatus.Origin.Error }} title="{{ .SyncStatus.Origin.Error }}" {{ end }}>Origin {{ .SyncStatus.Origin.Host }}</a>
|
{{ if .SyncStatus.Origin.Error }} title="{{ .SyncStatus.Origin.Error }}" {{ end }}>Origin {{ .SyncStatus.Origin.Host }}</a>
|
||||||
@@ -69,10 +157,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-12">
|
<div class="col-12 col-md-12">
|
||||||
<pre class="p-3 border"><code id="logs"></code></pre>
|
<div class="stat-card">
|
||||||
|
<pre class="p-3 border"><code id="logs"></code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- openssl dgst -sha384 -binary popper.min.js | openssl base64 -A -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"
|
||||||
|
integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
|
{{- if .Metrics }}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"ŝ
|
||||||
|
integrity="sha384-vsrfeLOOY6KuIYKDlmVH5UiBmgIdB1oEf7p01YgWHuqmOHfZr374+odEv96n9tNC" crossorigin="anonymous">
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
// Function to create minimal line charts
|
||||||
|
function createChart(canvasId, data) {
|
||||||
|
const ctx = document.getElementById(canvasId).getContext('2d');
|
||||||
|
|
||||||
|
const datasets = Array(data.length);
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
datasets[i] = {
|
||||||
|
data: data[i].data,
|
||||||
|
title: data[i].title,
|
||||||
|
backgroundColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 0.2)`,
|
||||||
|
borderColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 1)`,
|
||||||
|
borderWidth: 3,
|
||||||
|
fill: data[i].fill,
|
||||||
|
pointRadius: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: {{.Stats.Labels}},
|
||||||
|
datasets: datasets
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
bodyFont: {
|
||||||
|
size: 20
|
||||||
|
},
|
||||||
|
titleFont: {
|
||||||
|
size: 20
|
||||||
|
},
|
||||||
|
displayColors: false,
|
||||||
|
callbacks: {
|
||||||
|
label: function(tooltipItem) {
|
||||||
|
if (tooltipItem.dataset.title) {
|
||||||
|
return tooltipItem.raw + " - " + tooltipItem.dataset.title;
|
||||||
|
}
|
||||||
|
return tooltipItem.raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: { display: false,
|
||||||
|
title: {
|
||||||
|
display: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: { display: false,
|
||||||
|
min: 0,
|
||||||
|
title: {
|
||||||
|
display: true
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createChart('dnsQueriesChart', {{.Stats.DNS}});
|
||||||
|
createChart('blockedFiltersChart', {{.Stats.Blocked}});
|
||||||
|
createChart('malwareChart', {{.Stats.Malware}});
|
||||||
|
createChart('adultWebsitesChart', {{.Stats.Adult}});
|
||||||
|
</script>
|
||||||
|
{{- end }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
129
pkg/sync/stats.go
Normal file
129
pkg/sync/stats.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||||
|
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blue = []int{78, 141, 245}
|
||||||
|
blueAlternatives = [][]int{
|
||||||
|
{44, 95, 163},
|
||||||
|
{122, 166, 247},
|
||||||
|
{30, 61, 92},
|
||||||
|
{93, 158, 255},
|
||||||
|
{58, 123, 213},
|
||||||
|
}
|
||||||
|
|
||||||
|
red = []int{255, 94, 94}
|
||||||
|
redAlternatives = [][]int{
|
||||||
|
{204, 59, 59},
|
||||||
|
{255, 127, 127},
|
||||||
|
{140, 36, 36},
|
||||||
|
{255, 153, 153},
|
||||||
|
{255, 66, 66},
|
||||||
|
}
|
||||||
|
|
||||||
|
yellow = []int{232, 198, 78}
|
||||||
|
yellowAlternatives = [][]int{
|
||||||
|
{196, 163, 60},
|
||||||
|
{255, 220, 110},
|
||||||
|
{140, 114, 36},
|
||||||
|
{250, 233, 156},
|
||||||
|
{212, 180, 84},
|
||||||
|
}
|
||||||
|
|
||||||
|
green = []int{110, 224, 122}
|
||||||
|
greenAlternatives = [][]int{
|
||||||
|
{68, 160, 80},
|
||||||
|
{142, 255, 158},
|
||||||
|
{44, 140, 63},
|
||||||
|
{163, 255, 192},
|
||||||
|
{85, 198, 102},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func statsGraph() (*model.Stats, []line, []line, []line, []line) {
|
||||||
|
s := metrics.GetStats()
|
||||||
|
t := s.Total()
|
||||||
|
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
|
||||||
|
return safeStats(s.DnsQueries)
|
||||||
|
})
|
||||||
|
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
|
||||||
|
return safeStats(s.BlockedFiltering)
|
||||||
|
})
|
||||||
|
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
|
||||||
|
return safeStats(s.ReplacedSafebrowsing)
|
||||||
|
})
|
||||||
|
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
|
||||||
|
return safeStats(s.ReplacedParental)
|
||||||
|
})
|
||||||
|
|
||||||
|
return t, dns, blocked, malware, adult
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeStats(stats *[]int) []int {
|
||||||
|
if stats == nil {
|
||||||
|
return make([]int, 0)
|
||||||
|
}
|
||||||
|
return *stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func graphLines(
|
||||||
|
t *model.Stats,
|
||||||
|
s metrics.OverallStats,
|
||||||
|
baseColor []int,
|
||||||
|
altColors [][]int,
|
||||||
|
dataCB func(s *model.Stats) []int,
|
||||||
|
) []line {
|
||||||
|
g := &graph{
|
||||||
|
total: line{
|
||||||
|
Fill: true,
|
||||||
|
Title: "Total",
|
||||||
|
Data: dataCB(t),
|
||||||
|
R: baseColor[0],
|
||||||
|
G: baseColor[1],
|
||||||
|
B: baseColor[2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var i int
|
||||||
|
for name, data := range s {
|
||||||
|
if name != metrics.StatsTotal {
|
||||||
|
g.replicas = append(g.replicas, line{
|
||||||
|
Fill: false,
|
||||||
|
Title: name,
|
||||||
|
Data: dataCB(data),
|
||||||
|
R: altColors[i%len(altColors)][0],
|
||||||
|
G: altColors[i%len(altColors)][1],
|
||||||
|
B: altColors[i%len(altColors)][2],
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := []line{g.total}
|
||||||
|
|
||||||
|
slices.SortFunc(g.replicas, func(a, b line) int {
|
||||||
|
return strings.Compare(a.Title, b.Title)
|
||||||
|
})
|
||||||
|
lines = append(lines, g.replicas...)
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
type graph struct {
|
||||||
|
total line
|
||||||
|
replicas []line
|
||||||
|
}
|
||||||
|
|
||||||
|
type line struct {
|
||||||
|
Data []int `json:"data"`
|
||||||
|
R int `json:"r"`
|
||||||
|
G int `json:"g"`
|
||||||
|
B int `json:"b"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Fill bool `json:"fill"`
|
||||||
|
}
|
||||||
@@ -66,16 +66,12 @@ func Sync(cfg *types.Config) error {
|
|||||||
if cfg.API.Port != 0 {
|
if cfg.API.Port != 0 {
|
||||||
w.cron.Start()
|
w.cron.Start()
|
||||||
} else {
|
} else {
|
||||||
|
runOnStartAsync(cfg, w)
|
||||||
w.cron.Run()
|
w.cron.Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cfg.API.Port != 0 {
|
if cfg.API.Port != 0 {
|
||||||
if cfg.RunOnStart {
|
runOnStartAsync(cfg, w)
|
||||||
go func() {
|
|
||||||
l.Info("Running sync on startup")
|
|
||||||
w.sync()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
w.listenAndServe()
|
w.listenAndServe()
|
||||||
} else if cfg.RunOnStart {
|
} else if cfg.RunOnStart {
|
||||||
l.Info("Running sync on startup")
|
l.Info("Running sync on startup")
|
||||||
@@ -85,6 +81,15 @@ func Sync(cfg *types.Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runOnStartAsync(cfg *types.Config, w *worker) {
|
||||||
|
if cfg.RunOnStart {
|
||||||
|
go func() {
|
||||||
|
l.Info("Running sync on startup")
|
||||||
|
w.sync()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type worker struct {
|
type worker struct {
|
||||||
cfg *types.Config
|
cfg *types.Config
|
||||||
running bool
|
running bool
|
||||||
@@ -167,7 +172,8 @@ func (w *worker) sync() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if versions.IsNewerThan(versions.MinAgh, o.status.Version) {
|
if versions.IsNewerThan(versions.MinAgh, o.status.Version) {
|
||||||
sl.With("error", err, "version", o.status.Version).Errorf("Origin AdGuard Home version must be >= %s", versions.MinAgh)
|
sl.With("error", err, "version", o.status.Version).
|
||||||
|
Errorf("Origin AdGuard Home version must be >= %s", versions.MinAgh)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,21 +281,23 @@ func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardIn
|
|||||||
rl.With("version", replicaStatus.Version).Info("Connected to replica")
|
rl.With("version", replicaStatus.Version).Info("Connected to replica")
|
||||||
|
|
||||||
if versions.IsNewerThan(versions.MinAgh, replicaStatus.Version) {
|
if versions.IsNewerThan(versions.MinAgh, replicaStatus.Version) {
|
||||||
rl.With("error", err, "version", replicaStatus.Version).Errorf("Replica AdGuard Home version must be >= %s", versions.MinAgh)
|
rl.With("error", err, "version", replicaStatus.Version).
|
||||||
|
Errorf("Replica AdGuard Home version must be >= %s", versions.MinAgh)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.status.Version != replicaStatus.Version {
|
if o.status.Version != replicaStatus.Version {
|
||||||
rl.With("originVersion", o.status.Version, "replicaVersion", replicaStatus.Version).Warn("Versions do not match")
|
rl.With("originVersion", o.status.Version, "replicaVersion", replicaStatus.Version).
|
||||||
|
Warn("Versions do not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
ac := &actionContext{
|
ac := &actionContext{
|
||||||
continueOnError: w.cfg.ContinueOnError,
|
cfg: w.cfg,
|
||||||
rl: rl,
|
rl: rl,
|
||||||
origin: o,
|
origin: o,
|
||||||
replicaStatus: replicaStatus,
|
replicaStatus: replicaStatus,
|
||||||
client: rc,
|
client: rc,
|
||||||
replica: replica,
|
replica: replica,
|
||||||
}
|
}
|
||||||
for _, action := range w.actions {
|
for _, action := range w.actions {
|
||||||
if err := action.sync(ac); err != nil {
|
if err := action.sync(ac); err != nil {
|
||||||
@@ -303,7 +311,11 @@ func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardIn
|
|||||||
rl.Info("Sync done")
|
rl.Info("Sync done")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) statusWithSetup(rl *zap.SugaredLogger, replica types.AdGuardInstance, rc client.Client) (*model.ServerStatus, error) {
|
func (w *worker) statusWithSetup(
|
||||||
|
rl *zap.SugaredLogger,
|
||||||
|
replica types.AdGuardInstance,
|
||||||
|
rc client.Client,
|
||||||
|
) (*model.ServerStatus, error) {
|
||||||
rs, err := rc.Status()
|
rs, err := rc.Status()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if replica.AutoSetup && errors.Is(err, client.ErrSetupNeeded) {
|
if replica.AutoSetup && errors.Is(err, client.ErrSetupNeeded) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ var _ = Describe("Sync", func() {
|
|||||||
GeneralSettings: true,
|
GeneralSettings: true,
|
||||||
StatsConfig: true,
|
StatsConfig: true,
|
||||||
QueryLogConfig: true,
|
QueryLogConfig: true,
|
||||||
|
Theme: true,
|
||||||
},
|
},
|
||||||
Replicas: []types.AdGuardInstance{
|
Replicas: []types.AdGuardInstance{
|
||||||
{},
|
{},
|
||||||
@@ -57,8 +58,8 @@ var _ = Describe("Sync", func() {
|
|||||||
te = errors.New(uuid.NewString())
|
te = errors.New(uuid.NewString())
|
||||||
|
|
||||||
ac = &actionContext{
|
ac = &actionContext{
|
||||||
continueOnError: false,
|
cfg: w.cfg,
|
||||||
rl: l,
|
rl: l,
|
||||||
origin: &origin{
|
origin: &origin{
|
||||||
profileInfo: &model.ProfileInfo{
|
profileInfo: &model.ProfileInfo{
|
||||||
Name: "origin",
|
Name: "origin",
|
||||||
@@ -258,16 +259,32 @@ var _ = Describe("Sync", func() {
|
|||||||
err := actionProfileInfo(ac)
|
err := actionProfileInfo(ac)
|
||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
It("should not change theme if feature is disabled", func() {
|
||||||
|
ac.origin.profileInfo.Language = "de"
|
||||||
|
ac.cfg.Features.Theme = false
|
||||||
|
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en"}, nil)
|
||||||
|
cl.EXPECT().SetProfileInfo(&model.ProfileInfo{
|
||||||
|
Language: "de",
|
||||||
|
Name: "replica",
|
||||||
|
Theme: "",
|
||||||
|
})
|
||||||
|
err := actionProfileInfo(ac)
|
||||||
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
|
})
|
||||||
It("should not sync profileInfo if language is not set", func() {
|
It("should not sync profileInfo if language is not set", func() {
|
||||||
ac.origin.profileInfo.Language = ""
|
ac.origin.profileInfo.Language = ""
|
||||||
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
cl.EXPECT().
|
||||||
|
ProfileInfo().
|
||||||
|
Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||||
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
||||||
err := actionProfileInfo(ac)
|
err := actionProfileInfo(ac)
|
||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
It("should not sync profileInfo if theme is not set", func() {
|
It("should not sync profileInfo if theme is not set", func() {
|
||||||
ac.origin.profileInfo.Theme = ""
|
ac.origin.profileInfo.Theme = ""
|
||||||
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
cl.EXPECT().
|
||||||
|
ProfileInfo().
|
||||||
|
Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||||
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
||||||
err := actionProfileInfo(ac)
|
err := actionProfileInfo(ac)
|
||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
@@ -302,7 +319,8 @@ var _ = Describe("Sync", func() {
|
|||||||
var interval model.QueryLogConfigInterval = 123
|
var interval model.QueryLogConfigInterval = 123
|
||||||
ac.origin.queryLogConfig.Interval = &interval
|
ac.origin.queryLogConfig.Interval = &interval
|
||||||
cl.EXPECT().QueryLogConfig().Return(qlc, nil)
|
cl.EXPECT().QueryLogConfig().Return(qlc, nil)
|
||||||
cl.EXPECT().SetQueryLogConfig(&model.QueryLogConfigWithIgnored{QueryLogConfig: model.QueryLogConfig{AnonymizeClientIp: nil, Interval: &interval, Enabled: nil}})
|
cl.EXPECT().
|
||||||
|
SetQueryLogConfig(&model.QueryLogConfigWithIgnored{QueryLogConfig: model.QueryLogConfig{AnonymizeClientIp: nil, Interval: &interval, Enabled: nil}})
|
||||||
err := actionQueryLogConfig(ac)
|
err := actionQueryLogConfig(ac)
|
||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
@@ -421,7 +439,9 @@ var _ = Describe("Sync", func() {
|
|||||||
Ω(err).ShouldNot(HaveOccurred())
|
Ω(err).ShouldNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
It("should update a filter", func() {
|
It("should update a filter", func() {
|
||||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar", Enabled: true}})
|
ac.origin.filters.Filters = utils.Ptr(
|
||||||
|
[]model.Filter{{Name: "foo", Url: "https://foo.bar", Enabled: true}},
|
||||||
|
)
|
||||||
rf.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
rf.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
||||||
cl.EXPECT().Filtering().Return(rf, nil)
|
cl.EXPECT().Filtering().Return(rf, nil)
|
||||||
cl.EXPECT().UpdateFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar", Enabled: true})
|
cl.EXPECT().UpdateFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar", Enabled: true})
|
||||||
@@ -431,19 +451,25 @@ var _ = Describe("Sync", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("should abort after failed added filter", func() {
|
It("should abort after failed added filter", func() {
|
||||||
ac.continueOnError = false
|
ac.cfg.ContinueOnError = false
|
||||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
||||||
cl.EXPECT().Filtering().Return(rf, nil)
|
cl.EXPECT().Filtering().Return(rf, nil)
|
||||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).Return(errors.New("test failure"))
|
cl.EXPECT().
|
||||||
|
AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).
|
||||||
|
Return(errors.New("test failure"))
|
||||||
err := actionFilters(ac)
|
err := actionFilters(ac)
|
||||||
Ω(err).Should(HaveOccurred())
|
Ω(err).Should(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should continue after failed added filter", func() {
|
It("should continue after failed added filter", func() {
|
||||||
ac.continueOnError = true
|
ac.cfg.ContinueOnError = true
|
||||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}, {Name: "bar", Url: "https://bar.foo"}})
|
ac.origin.filters.Filters = utils.Ptr(
|
||||||
|
[]model.Filter{{Name: "foo", Url: "https://foo.bar"}, {Name: "bar", Url: "https://bar.foo"}},
|
||||||
|
)
|
||||||
cl.EXPECT().Filtering().Return(rf, nil)
|
cl.EXPECT().Filtering().Return(rf, nil)
|
||||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).Return(errors.New("test failure"))
|
cl.EXPECT().
|
||||||
|
AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).
|
||||||
|
Return(errors.New("test failure"))
|
||||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "bar", Url: "https://bar.foo"})
|
cl.EXPECT().AddFilter(false, model.Filter{Name: "bar", Url: "https://bar.foo"})
|
||||||
cl.EXPECT().RefreshFilters(gm.Any())
|
cl.EXPECT().RefreshFilters(gm.Any())
|
||||||
err := actionFilters(ac)
|
err := actionFilters(ac)
|
||||||
|
|||||||
@@ -21,19 +21,21 @@ func NewFeatures(enabled bool) Features {
|
|||||||
ClientSettings: enabled,
|
ClientSettings: enabled,
|
||||||
Services: enabled,
|
Services: enabled,
|
||||||
Filters: enabled,
|
Filters: enabled,
|
||||||
|
Theme: enabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Features feature flags
|
// Features feature flags
|
||||||
type Features struct {
|
type Features struct {
|
||||||
DNS DNS `json:"dns" yaml:"dns"`
|
DNS DNS `json:"dns" yaml:"dns"`
|
||||||
DHCP DHCP `json:"dhcp" yaml:"dhcp"`
|
DHCP DHCP `json:"dhcp" yaml:"dhcp"`
|
||||||
GeneralSettings bool `json:"generalSettings" yaml:"generalSettings" env:"FEATURES_GENERAL_SETTINGS"`
|
GeneralSettings bool `json:"generalSettings" yaml:"generalSettings" env:"FEATURES_GENERAL_SETTINGS"`
|
||||||
QueryLogConfig bool `json:"queryLogConfig" yaml:"queryLogConfig" env:"FEATURES_QUERY_LOG_CONFIG"`
|
QueryLogConfig bool `json:"queryLogConfig" yaml:"queryLogConfig" env:"FEATURES_QUERY_LOG_CONFIG"`
|
||||||
StatsConfig bool `json:"statsConfig" yaml:"statsConfig" env:"FEATURES_STATS_CONFIG"`
|
StatsConfig bool `json:"statsConfig" yaml:"statsConfig" env:"FEATURES_STATS_CONFIG"`
|
||||||
ClientSettings bool `json:"clientSettings" yaml:"clientSettings" env:"FEATURES_CLIENT_SETTINGS"`
|
ClientSettings bool `json:"clientSettings" yaml:"clientSettings" env:"FEATURES_CLIENT_SETTINGS"`
|
||||||
Services bool `json:"services" yaml:"services" env:"FEATURES_SERVICES"`
|
Services bool `json:"services" yaml:"services" env:"FEATURES_SERVICES"`
|
||||||
Filters bool `json:"filters" yaml:"filters" env:"FEATURES_FILTERS"`
|
Filters bool `json:"filters" yaml:"filters" env:"FEATURES_FILTERS"`
|
||||||
|
Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHCP features
|
// DHCP features
|
||||||
@@ -44,9 +46,9 @@ type DHCP struct {
|
|||||||
|
|
||||||
// DNS features
|
// DNS features
|
||||||
type DNS struct {
|
type DNS struct {
|
||||||
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"`
|
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"`
|
||||||
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DNS_SERVER_CONFIG"`
|
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DNS_SERVER_CONFIG"`
|
||||||
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"`
|
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogDisabled log all disabled features
|
// LogDisabled log all disabled features
|
||||||
|
|||||||
@@ -18,39 +18,39 @@ const (
|
|||||||
// Config application configuration struct
|
// Config application configuration struct
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Origin AdGuardInstance `json:"origin" yaml:"origin" env:"ORIGIN"`
|
Origin AdGuardInstance `json:"origin" yaml:"origin" env:"ORIGIN"`
|
||||||
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty" env:"REPLICA"`
|
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty" env:"REPLICA"`
|
||||||
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty"`
|
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty" faker:"slice_len=2"`
|
||||||
Cron string `json:"cron,omitempty" yaml:"cron,omitempty" env:"CRON"`
|
Cron string `json:"cron,omitempty" yaml:"cron,omitempty" env:"CRON"`
|
||||||
RunOnStart bool `json:"runOnStart,omitempty" yaml:"runOnStart,omitempty" env:"RUN_ON_START"`
|
RunOnStart bool `json:"runOnStart,omitempty" yaml:"runOnStart,omitempty" env:"RUN_ON_START"`
|
||||||
PrintConfigOnly bool `json:"printConfigOnly,omitempty" yaml:"printConfigOnly,omitempty" env:"PRINT_CONFIG_ONLY"`
|
PrintConfigOnly bool `json:"printConfigOnly,omitempty" yaml:"printConfigOnly,omitempty" env:"PRINT_CONFIG_ONLY"`
|
||||||
ContinueOnError bool `json:"continueOnError,omitempty" yaml:"continueOnError,omitempty" env:"CONTINUE_ON_ERROR"`
|
ContinueOnError bool `json:"continueOnError,omitempty" yaml:"continueOnError,omitempty" env:"CONTINUE_ON_ERROR"`
|
||||||
API API `json:"api,omitempty" yaml:"api,omitempty" env:"API"`
|
API API `json:"api,omitempty" yaml:"api,omitempty" env:"API"`
|
||||||
Features Features `json:"features,omitempty" yaml:"features,omitempty" env:"FEATURES_"`
|
Features Features `json:"features,omitempty" yaml:"features,omitempty" env:"FEATURES_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// API configuration
|
// API configuration
|
||||||
type API struct {
|
type API struct {
|
||||||
Port int `json:"port,omitempty" yaml:"port,omitempty" env:"API_PORT"`
|
Port int `json:"port,omitempty" yaml:"port,omitempty" env:"API_PORT"`
|
||||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"API_USERNAME"`
|
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"API_USERNAME"`
|
||||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"API_PASSWORD"`
|
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"API_PASSWORD"`
|
||||||
DarkMode bool `json:"darkMode,omitempty" yaml:"darkMode,omitempty" env:"API_DARK_MODE"`
|
DarkMode bool `json:"darkMode,omitempty" yaml:"darkMode,omitempty" env:"API_DARK_MODE"`
|
||||||
Metrics Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty" env:"API_METRICS"`
|
Metrics Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty" env:"API_METRICS"`
|
||||||
TLS TLS `json:"tls,omitempty" yaml:"tls,omitempty" env:"API_TLS"`
|
TLS TLS `json:"tls,omitempty" yaml:"tls,omitempty" env:"API_TLS"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics configuration
|
// Metrics configuration
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty" env:"API_METRICS_ENABLED"`
|
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty" env:"API_METRICS_ENABLED"`
|
||||||
ScrapeInterval time.Duration `json:"scrapeInterval,omitempty" yaml:"scrapeInterval,omitempty" env:"API_METRICS_SCRAPE_INTERVAL"`
|
ScrapeInterval time.Duration `json:"scrapeInterval,omitempty" yaml:"scrapeInterval,omitempty" env:"API_METRICS_SCRAPE_INTERVAL"`
|
||||||
QueryLogLimit int `json:"queryLogLimit,omitempty" yaml:"queryLogLimit,omitempty" env:"API_METRICS_QUERY_LOG_LIMIT"`
|
QueryLogLimit int `json:"queryLogLimit,omitempty" yaml:"queryLogLimit,omitempty" env:"API_METRICS_QUERY_LOG_LIMIT"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLS configuration
|
// TLS configuration
|
||||||
type TLS struct {
|
type TLS struct {
|
||||||
CertDir string `json:"certDir,omitempty" yaml:"certDir,omitempty" env:"API_TLS_CERT_DIR"`
|
CertDir string `json:"certDir,omitempty" yaml:"certDir,omitempty" env:"API_TLS_CERT_DIR"`
|
||||||
CertName string `json:"certName,omitempty" yaml:"certName,omitempty" env:"API_TLS_CERT_NAME"`
|
CertName string `json:"certName,omitempty" yaml:"certName,omitempty" env:"API_TLS_CERT_NAME"`
|
||||||
KeyName string `json:"keyName,omitempty" yaml:"keyName,omitempty" env:"API_TLS_KEY_NAME"`
|
KeyName string `json:"keyName,omitempty" yaml:"keyName,omitempty" env:"API_TLS_KEY_NAME"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t TLS) Enabled() bool {
|
func (t TLS) Enabled() bool {
|
||||||
@@ -140,15 +140,15 @@ func (cfg *Config) Init() error {
|
|||||||
// AdGuardInstance AdguardHome config instance
|
// AdGuardInstance AdguardHome config instance
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
type AdGuardInstance struct {
|
type AdGuardInstance struct {
|
||||||
URL string `json:"url" yaml:"url" env:"URL"`
|
URL string `json:"url" yaml:"url" env:"URL" faker:"url"`
|
||||||
WebURL string `json:"webURL" yaml:"webURL" env:"WEB_URL"`
|
WebURL string `json:"webURL" yaml:"webURL" env:"WEB_URL" faker:"url"`
|
||||||
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty" env:"API_PATH"`
|
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty" env:"API_PATH"`
|
||||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"`
|
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"`
|
||||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD"`
|
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD"`
|
||||||
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty" env:"COOKIE"`
|
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty" env:"COOKIE"`
|
||||||
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify" env:"INSECURE_SKIP_VERIFY"`
|
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify" env:"INSECURE_SKIP_VERIFY"`
|
||||||
AutoSetup bool `json:"autoSetup" yaml:"autoSetup" env:"AUTO_SETUP"`
|
AutoSetup bool `json:"autoSetup" yaml:"autoSetup" env:"AUTO_SETUP"`
|
||||||
InterfaceName string `json:"interfaceName,omitempty" yaml:"interfaceName,omitempty" env:"INTERFACE_NAME"`
|
InterfaceName string `json:"interfaceName,omitempty" yaml:"interfaceName,omitempty" env:"INTERFACE_NAME"`
|
||||||
DHCPServerEnabled *bool `json:"dhcpServerEnabled,omitempty" yaml:"dhcpServerEnabled,omitempty" env:"DHCP_SERVER_ENABLED"`
|
DHCPServerEnabled *bool `json:"dhcpServerEnabled,omitempty" yaml:"dhcpServerEnabled,omitempty" env:"DHCP_SERVER_ENABLED"`
|
||||||
|
|
||||||
Host string `json:"-" yaml:"-"`
|
Host string `json:"-" yaml:"-"`
|
||||||
|
|||||||
@@ -12,3 +12,13 @@ func PtrToString[I interface{}](i *I) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%v", i)
|
return fmt.Sprintf("%v", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PtrEquals[I comparable](a *I, b *I) bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *a == *b
|
||||||
|
}
|
||||||
|
|||||||
28
renovate.json
Normal file
28
renovate.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"customManagers": [
|
||||||
|
{
|
||||||
|
"customType": "regex",
|
||||||
|
"datasourceTemplate": "go",
|
||||||
|
"description": "Update toolbox tools in .toolbox.mk",
|
||||||
|
"fileMatch": [
|
||||||
|
"^\\.toolbox\\.mk$"
|
||||||
|
],
|
||||||
|
"matchStrings": [
|
||||||
|
"# renovate: packageName=(?<packageName>.+?)\\s+.+?_VERSION \\?= (?<currentValue>.+?)\\s"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"customType": "regex",
|
||||||
|
"datasourceTemplate": "github-releases",
|
||||||
|
"description": "Update github _VERSION Makefile",
|
||||||
|
"fileMatch": [
|
||||||
|
"^Makefile$"
|
||||||
|
],
|
||||||
|
"matchStrings": [
|
||||||
|
"# renovate: packageName=(?<packageName>.+?)\\s+.+?_VERSION \\?= (?<currentValue>.+?)\\s"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencyDashboard": true
|
||||||
|
}
|
||||||
1
testdata/config_test_replica.yaml
vendored
1
testdata/config_test_replica.yaml
vendored
@@ -35,3 +35,4 @@ features:
|
|||||||
clientSettings: true
|
clientSettings: true
|
||||||
services: true
|
services: true
|
||||||
filters: true
|
filters: true
|
||||||
|
theme: true
|
||||||
|
|||||||
1
testdata/config_test_replicas.yaml
vendored
1
testdata/config_test_replicas.yaml
vendored
@@ -35,3 +35,4 @@ features:
|
|||||||
clientSettings: true
|
clientSettings: true
|
||||||
services: true
|
services: true
|
||||||
filters: true
|
filters: true
|
||||||
|
theme: true
|
||||||
|
|||||||
@@ -45,3 +45,4 @@ features:
|
|||||||
clientSettings: true
|
clientSettings: true
|
||||||
services: true
|
services: true
|
||||||
filters: true
|
filters: true
|
||||||
|
theme: true
|
||||||
|
|||||||
1
testdata/e2e/bin/install-chart.sh
vendored
1
testdata/e2e/bin/install-chart.sh
vendored
@@ -7,3 +7,4 @@ if [[ $(helm list --no-headers -n agh-e2e | grep agh-e2e | wc -l) == "1" ]]; the
|
|||||||
helm delete agh-e2e -n agh-e2e --wait
|
helm delete agh-e2e -n agh-e2e --wait
|
||||||
fi
|
fi
|
||||||
helm install agh-e2e testdata/e2e -n agh-e2e --create-namespace --set mode=${1}
|
helm install agh-e2e testdata/e2e -n agh-e2e --create-namespace --set mode=${1}
|
||||||
|
|
||||||
|
|||||||
2
testdata/e2e/bin/show-origin-logs.sh
vendored
2
testdata/e2e/bin/show-origin-logs.sh
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "## Pod adguardhome-origin logs" >> $GITHUB_STEP_SUMMARY
|
echo "## Pod adguardhome-origin ${1} logs" >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
kubectl logs adguardhome-origin >> $GITHUB_STEP_SUMMARY
|
kubectl logs adguardhome-origin >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
21
testdata/e2e/bin/show-replica-logs.sh
vendored
21
testdata/e2e/bin/show-replica-logs.sh
vendored
@@ -4,15 +4,18 @@ set -e
|
|||||||
for pod in $(kubectl get pods -l bakito.net/adguardhome-sync=replica -o name); do
|
for pod in $(kubectl get pods -l bakito.net/adguardhome-sync=replica -o name); do
|
||||||
echo "## Pod ${pod} logs" >> $GITHUB_STEP_SUMMARY
|
echo "## Pod ${pod} logs" >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
LOGS=$(kubectl logs ${pod})
|
K8S_LOGS=$(kubectl logs ${pod})
|
||||||
# ignore certain errors
|
# ignore certain errors
|
||||||
LOGS=$(echo -e "${LOGS}" | grep -v -e "error.* deleting filter .* no such file or directory" )
|
LOGS=$(echo -e "${K8S_LOGS}" |
|
||||||
# https://github.com/AdguardTeam/AdGuardHome/issues/4944
|
grep -v -e "error.* deleting filter .* no such file or directory" |
|
||||||
LOGS=$(echo -e "${LOGS}" | grep -v -e "error.* creating dhcpv4 srv")
|
grep -v -e '\[error\] storage: recovered from panic: runtime' # https://github.com/AdguardTeam/AdGuardHome/issues/7315
|
||||||
echo -e "${LOGS}" >> $GITHUB_STEP_SUMMARY
|
)
|
||||||
ERRORS=$(echo -e "${LOGS}"} | grep '\[error\]' | wc -l)
|
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Found ${ERRORS} error(s) in ${pod} log" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "----------------------------------------------" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
|
echo -e "${K8S_LOGS}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
ERRORS=$(echo -e "${LOGS}"} | grep '\[error\]' | wc -l)
|
||||||
|
TOTAL_ERRORS=$(echo -e "${K8S_LOGS}"} | grep '\[error\]' | wc -l)
|
||||||
|
IGNORED_ERRORS=$(echo "${TOTAL_ERRORS} - ${ERRORS}" | bc)
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Found ${ERRORS} error(s) (${IGNORED_ERRORS} ignored) in ${pod} log" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "----------------------------------------------" >> $GITHUB_STEP_SUMMARY
|
||||||
done
|
done
|
||||||
|
|||||||
3
testdata/e2e/bin/wait-for-start.sh
vendored
Executable file
3
testdata/e2e/bin/wait-for-start.sh
vendored
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/adguardhome-sync --timeout=1m
|
||||||
2
testdata/e2e/bin/wait-for-sync.sh
vendored
2
testdata/e2e/bin/wait-for-sync.sh
vendored
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/adguardhome-sync --timeout=1m
|
|
||||||
|
|
||||||
kubectl port-forward pod/adguardhome-sync 9090:9090 &
|
kubectl port-forward pod/adguardhome-sync 9090:9090 &
|
||||||
|
|
||||||
for i in {1..6}; do
|
for i in {1..6}; do
|
||||||
|
|||||||
44
testdata/e2e/resources/AdGuardHome.yaml
vendored
44
testdata/e2e/resources/AdGuardHome.yaml
vendored
@@ -18,19 +18,18 @@ dns:
|
|||||||
port: 53
|
port: 53
|
||||||
anonymize_client_ip: false
|
anonymize_client_ip: false
|
||||||
ratelimit: 20
|
ratelimit: 20
|
||||||
ratelimit_whitelist: [ ]
|
ratelimit_whitelist: []
|
||||||
refuse_any: true
|
refuse_any: true
|
||||||
upstream_dns:
|
upstream_dns:
|
||||||
- https://dns10.quad9.net/dns-query
|
- 8.8.8.8
|
||||||
upstream_dns_file: ""
|
upstream_dns_file: ""
|
||||||
bootstrap_dns:
|
bootstrap_dns:
|
||||||
- 1.1.1.1:53
|
- 1.1.1.1:53
|
||||||
fallback_dns: [ ]
|
fallback_dns: []
|
||||||
all_servers: false
|
upstream_mode: load_balance
|
||||||
fastest_addr: false
|
|
||||||
fastest_timeout: 1s
|
fastest_timeout: 1s
|
||||||
allowed_clients: [ ]
|
allowed_clients: []
|
||||||
disallowed_clients: [ ]
|
disallowed_clients: []
|
||||||
blocked_hosts:
|
blocked_hosts:
|
||||||
- version.bind
|
- version.bind
|
||||||
- id.server
|
- id.server
|
||||||
@@ -42,7 +41,7 @@ dns:
|
|||||||
cache_ttl_min: 0
|
cache_ttl_min: 0
|
||||||
cache_ttl_max: 0
|
cache_ttl_max: 0
|
||||||
cache_optimistic: true
|
cache_optimistic: true
|
||||||
bogus_nxdomain: [ ]
|
bogus_nxdomain: []
|
||||||
aaaa_disabled: false
|
aaaa_disabled: false
|
||||||
enable_dnssec: false
|
enable_dnssec: false
|
||||||
edns_client_subnet:
|
edns_client_subnet:
|
||||||
@@ -51,17 +50,19 @@ dns:
|
|||||||
use_custom: false
|
use_custom: false
|
||||||
max_goroutines: 300
|
max_goroutines: 300
|
||||||
handle_ddr: true
|
handle_ddr: true
|
||||||
ipset: [ ]
|
ipset: []
|
||||||
ipset_file: ""
|
ipset_file: ""
|
||||||
bootstrap_prefer_ipv6: false
|
bootstrap_prefer_ipv6: false
|
||||||
upstream_timeout: 10s
|
upstream_timeout: 10s
|
||||||
private_networks: [ ]
|
private_networks: []
|
||||||
use_private_ptr_resolvers: true
|
use_private_ptr_resolvers: true
|
||||||
local_ptr_upstreams: [ ]
|
local_ptr_upstreams: []
|
||||||
use_dns64: false
|
use_dns64: false
|
||||||
dns64_prefixes: [ ]
|
dns64_prefixes: []
|
||||||
serve_http3: false
|
serve_http3: false
|
||||||
use_http3_upstreams: false
|
use_http3_upstreams: false
|
||||||
|
serve_plain_dns: true
|
||||||
|
hostsfile_enabled: true
|
||||||
tls:
|
tls:
|
||||||
enabled: false
|
enabled: false
|
||||||
server_name: ""
|
server_name: ""
|
||||||
@@ -78,13 +79,15 @@ tls:
|
|||||||
private_key_path: ""
|
private_key_path: ""
|
||||||
strict_sni_check: false
|
strict_sni_check: false
|
||||||
querylog:
|
querylog:
|
||||||
ignored: [ ]
|
dir_path: ""
|
||||||
|
ignored: []
|
||||||
interval: 6h
|
interval: 6h
|
||||||
size_memory: 1000
|
size_memory: 1000
|
||||||
enabled: true
|
enabled: true
|
||||||
file_enabled: true
|
file_enabled: true
|
||||||
statistics:
|
statistics:
|
||||||
ignored: [ ]
|
dir_path: ""
|
||||||
|
ignored: []
|
||||||
interval: 24h
|
interval: 24h
|
||||||
enabled: true
|
enabled: true
|
||||||
filters:
|
filters:
|
||||||
@@ -96,7 +99,7 @@ filters:
|
|||||||
url: https://adaway.org/hosts.txt
|
url: https://adaway.org/hosts.txt
|
||||||
name: AdAway Default Blocklist
|
name: AdAway Default Blocklist
|
||||||
id: 2
|
id: 2
|
||||||
whitelist_filters: [ ]
|
whitelist_filters: []
|
||||||
user_rules:
|
user_rules:
|
||||||
- '||metrics2.data.hicloud.com^$important'
|
- '||metrics2.data.hicloud.com^$important'
|
||||||
- '||www.curiouscorrespondence.com^$important'
|
- '||www.curiouscorrespondence.com^$important'
|
||||||
@@ -113,7 +116,7 @@ dhcp:
|
|||||||
range_end: 1.2.3.56
|
range_end: 1.2.3.56
|
||||||
lease_duration: 86400
|
lease_duration: 86400
|
||||||
icmp_timeout_msec: 1000
|
icmp_timeout_msec: 1000
|
||||||
options: [ ]
|
options: []
|
||||||
dhcpv6:
|
dhcpv6:
|
||||||
range_start: ""
|
range_start: ""
|
||||||
lease_duration: 86400
|
lease_duration: 86400
|
||||||
@@ -150,7 +153,7 @@ filtering:
|
|||||||
blocking_mode: default
|
blocking_mode: default
|
||||||
parental_block_host: family-block.dns.adguard.com
|
parental_block_host: family-block.dns.adguard.com
|
||||||
safebrowsing_block_host: standard-block.dns.adguard.com
|
safebrowsing_block_host: standard-block.dns.adguard.com
|
||||||
rewrites: [ ]
|
rewrites: []
|
||||||
safebrowsing_cache_size: 1048576
|
safebrowsing_cache_size: 1048576
|
||||||
safesearch_cache_size: 1048576
|
safesearch_cache_size: 1048576
|
||||||
parental_cache_size: 1048576
|
parental_cache_size: 1048576
|
||||||
@@ -170,8 +173,6 @@ clients:
|
|||||||
hosts: true
|
hosts: true
|
||||||
persistent:
|
persistent:
|
||||||
- name: Device 1
|
- name: Device 1
|
||||||
tags:
|
|
||||||
- device_1
|
|
||||||
ids:
|
ids:
|
||||||
- 2.2.2.2
|
- 2.2.2.2
|
||||||
blocked_services:
|
blocked_services:
|
||||||
@@ -183,7 +184,7 @@ clients:
|
|||||||
- qq
|
- qq
|
||||||
- vk
|
- vk
|
||||||
- ok
|
- ok
|
||||||
upstreams: [ ]
|
upstreams: []
|
||||||
use_global_settings: true
|
use_global_settings: true
|
||||||
filtering_enabled: false
|
filtering_enabled: false
|
||||||
parental_enabled: false
|
parental_enabled: false
|
||||||
@@ -192,6 +193,7 @@ clients:
|
|||||||
ignore_querylog: false
|
ignore_querylog: false
|
||||||
ignore_statistics: false
|
ignore_statistics: false
|
||||||
log:
|
log:
|
||||||
|
enabled: true
|
||||||
file: ""
|
file: ""
|
||||||
max_backups: 0
|
max_backups: 0
|
||||||
max_size: 100
|
max_size: 100
|
||||||
@@ -203,4 +205,4 @@ os:
|
|||||||
group: ""
|
group: ""
|
||||||
user: ""
|
user: ""
|
||||||
rlimit_nofile: 0
|
rlimit_nofile: 0
|
||||||
schema_version: 27
|
schema_version: 28
|
||||||
|
|||||||
2
testdata/e2e/values.yaml
vendored
2
testdata/e2e/values.yaml
vendored
@@ -8,4 +8,4 @@ mode: env
|
|||||||
|
|
||||||
kubectl:
|
kubectl:
|
||||||
repository: bitnami/kubectl
|
repository: bitnami/kubectl
|
||||||
tag: 1.27
|
tag: "1.30"
|
||||||
|
|||||||
5
tools.go
5
tools.go
@@ -4,10 +4,5 @@
|
|||||||
package tools
|
package tools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/bakito/semver"
|
|
||||||
_ "github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen"
|
|
||||||
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
|
|
||||||
_ "github.com/onsi/ginkgo/v2/ginkgo"
|
_ "github.com/onsi/ginkgo/v2/ginkgo"
|
||||||
_ "go.uber.org/mock/mockgen"
|
|
||||||
_ "k8s.io/code-generator/cmd/deepcopy-gen"
|
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user