Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1986e87518 | ||
|
|
cf0a381f80 | ||
|
|
5e591e04c3 | ||
|
|
fcf25538c0 | ||
|
|
aa95031136 | ||
|
|
6fb2dd12a8 |
@@ -1,29 +1,38 @@
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X github.com/bakito/adguardhome-sync/version.Version={{.Version}}
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
hooks:
|
||||
post: upx {{ .Path }}
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X github.com/bakito/adguardhome-sync/version.Version={{.Version}}
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
hooks:
|
||||
post: upx {{ .Path }}
|
||||
archives:
|
||||
- replacements:
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
- replacements:
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
@@ -32,5 +41,5 @@ changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
@@ -16,6 +16,7 @@ var (
|
||||
l = log.GetLogger("client")
|
||||
)
|
||||
|
||||
// New create a new client
|
||||
func New(config types.AdGuardInstance) (Client, error) {
|
||||
|
||||
var apiURL string
|
||||
@@ -46,6 +47,7 @@ func New(config types.AdGuardInstance) (Client, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Client AdGuard Home API client interface
|
||||
type Client interface {
|
||||
Host() string
|
||||
|
||||
@@ -76,6 +78,11 @@ type Client interface {
|
||||
AddClients(client ...types.Client) error
|
||||
UpdateClients(client ...types.Client) error
|
||||
DeleteClients(client ...types.Client) error
|
||||
|
||||
QueryLogConfig() (*types.QueryLogConfig, error)
|
||||
SetQueryLogConfig(enabled bool, interval int, anonymizeClientIP bool) error
|
||||
StatsConfig() (*types.IntervalConfig, error)
|
||||
SetStatsConfig(interval int) error
|
||||
}
|
||||
|
||||
type client struct {
|
||||
@@ -147,13 +154,13 @@ func (cl *client) ToggleSafeSearch(enable bool) error {
|
||||
}
|
||||
|
||||
func (cl *client) toggleStatus(mode string) (bool, error) {
|
||||
fs := &types.FeatureStatus{}
|
||||
fs := &types.EnableConfig{}
|
||||
_, err := cl.client.R().EnableTrace().SetResult(fs).Get(fmt.Sprintf("/%s/status", mode))
|
||||
return fs.Enabled, err
|
||||
}
|
||||
|
||||
func (cl *client) toggleBool(mode string, enable bool) error {
|
||||
cl.log.With("mode", mode, "enable", enable).Info("Toggle")
|
||||
cl.log.With("enable", enable).Info(fmt.Sprintf("Toggle %s", mode))
|
||||
var target string
|
||||
if enable {
|
||||
target = "enable"
|
||||
@@ -214,7 +221,10 @@ func (cl *client) SetCustomRules(rules types.UserRules) error {
|
||||
|
||||
func (cl *client) ToggleFiltering(enabled bool, interval int) error {
|
||||
cl.log.With("enabled", enabled, "interval", interval).Info("Toggle filtering")
|
||||
_, err := cl.client.R().EnableTrace().SetBody(&types.FilteringConfig{FeatureStatus: types.FeatureStatus{Enabled: enabled}, Interval: interval}).Post("/filtering/config")
|
||||
_, err := cl.client.R().EnableTrace().SetBody(&types.FilteringConfig{
|
||||
EnableConfig: types.EnableConfig{Enabled: enabled},
|
||||
IntervalConfig: types.IntervalConfig{Interval: interval},
|
||||
}).Post("/filtering/config")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -268,3 +278,31 @@ func (cl *client) DeleteClients(clients ...types.Client) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cl *client) QueryLogConfig() (*types.QueryLogConfig, error) {
|
||||
qlc := &types.QueryLogConfig{}
|
||||
_, err := cl.client.R().EnableTrace().SetResult(qlc).Get("/querylog_info")
|
||||
return qlc, err
|
||||
}
|
||||
|
||||
func (cl *client) SetQueryLogConfig(enabled bool, interval int, anonymizeClientIP bool) error {
|
||||
cl.log.With("enabled", enabled, "interval", interval, "anonymizeClientIP", anonymizeClientIP).Info("Set query log config")
|
||||
_, err := cl.client.R().EnableTrace().SetBody(&types.QueryLogConfig{
|
||||
EnableConfig: types.EnableConfig{Enabled: enabled},
|
||||
IntervalConfig: types.IntervalConfig{Interval: interval},
|
||||
AnonymizeClientIP: anonymizeClientIP,
|
||||
}).Post("/querylog_config")
|
||||
return err
|
||||
}
|
||||
|
||||
func (cl *client) StatsConfig() (*types.IntervalConfig, error) {
|
||||
stats := &types.IntervalConfig{}
|
||||
_, err := cl.client.R().EnableTrace().SetResult(stats).Get("/stats_info")
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func (cl *client) SetStatsConfig(interval int) error {
|
||||
cl.log.With("interval", interval).Info("Set stats config")
|
||||
_, err := cl.client.R().EnableTrace().SetBody(&types.IntervalConfig{Interval: interval}).Post("/stats_config")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,9 +2,15 @@ package log
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var rootLogger *zap.Logger
|
||||
const logHistorySize = 50
|
||||
|
||||
var (
|
||||
rootLogger *zap.Logger
|
||||
logs []string
|
||||
)
|
||||
|
||||
// GetLogger returns a named logger
|
||||
func GetLogger(name string) *zap.SugaredLogger {
|
||||
@@ -22,7 +28,65 @@ func init() {
|
||||
OutputPaths: []string{"stdout"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
opt := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewTee(c, &logList{
|
||||
enc: zapcore.NewConsoleEncoder(cfg.EncoderConfig),
|
||||
LevelEnabler: cfg.Level,
|
||||
})
|
||||
})
|
||||
|
||||
rootLogger, _ = cfg.Build()
|
||||
rootLogger.Sugar()
|
||||
rootLogger, _ = cfg.Build(opt)
|
||||
}
|
||||
|
||||
type logList struct {
|
||||
zapcore.LevelEnabler
|
||||
enc zapcore.Encoder
|
||||
}
|
||||
|
||||
func (l *logList) clone() *logList {
|
||||
return &logList{
|
||||
LevelEnabler: l.LevelEnabler,
|
||||
enc: l.enc.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logList) With(fields []zapcore.Field) zapcore.Core {
|
||||
clone := l.clone()
|
||||
addFields(clone.enc, fields)
|
||||
return clone
|
||||
}
|
||||
|
||||
func (l *logList) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||
if l.Enabled(ent.Level) {
|
||||
return ce.AddCore(ent, l)
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
func (l *logList) Write(ent zapcore.Entry, fields []zapcore.Field) error {
|
||||
buf, err := l.enc.EncodeEntry(ent, fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logs = append(logs, buf.String())
|
||||
|
||||
if len(logs) > logHistorySize {
|
||||
logs = logs[len(logs)-logHistorySize:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *logList) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logs get the current logs
|
||||
func Logs() []string {
|
||||
return logs
|
||||
}
|
||||
|
||||
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
||||
for i := range fields {
|
||||
fields[i].AddTo(enc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,11 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
)
|
||||
|
||||
func (w *worker) handleSync(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -25,6 +28,10 @@ func (w *worker) handleRoot(rw http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = rw.Write([]byte("adguardhome-sync"))
|
||||
}
|
||||
|
||||
func (w *worker) handleLogs(rw http.ResponseWriter, _ *http.Request) {
|
||||
_, _ = rw.Write([]byte(strings.Join(log.Logs(), "")))
|
||||
}
|
||||
|
||||
func (w *worker) basicAuth(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -64,13 +71,15 @@ func (w *worker) listenAndServe() {
|
||||
BaseContext: func(_ net.Listener) context.Context { return ctx },
|
||||
}
|
||||
|
||||
var mw []func(http.HandlerFunc) http.HandlerFunc
|
||||
if w.cfg.API.Username != "" && w.cfg.API.Password != "" {
|
||||
mux.HandleFunc("/api/v1/sync", use(w.handleSync, w.basicAuth))
|
||||
mux.HandleFunc("/", use(w.handleRoot, w.basicAuth))
|
||||
} else {
|
||||
mux.HandleFunc("/api/v1/sync", w.handleSync)
|
||||
mux.HandleFunc("/", w.handleRoot)
|
||||
mw = append(mw, w.basicAuth)
|
||||
}
|
||||
|
||||
mux.HandleFunc("/api/v1/sync", use(w.handleSync, mw...))
|
||||
mux.HandleFunc("/api/v1/logs", use(w.handleLogs, mw...))
|
||||
mux.HandleFunc("/", use(w.handleRoot, mw...))
|
||||
|
||||
go func() {
|
||||
if err := httpServer.ListenAndServe(); err != http.ErrServerClosed {
|
||||
l.With("error", err).Fatalf("HTTP server ListenAndServe")
|
||||
|
||||
@@ -109,6 +109,16 @@ func (w *worker) sync() {
|
||||
sl.With("error", err).Error("Error getting origin clients")
|
||||
return
|
||||
}
|
||||
o.queryLogConfig, err = oc.QueryLogConfig()
|
||||
if err != nil {
|
||||
sl.With("error", err).Error("Error getting query log config")
|
||||
return
|
||||
}
|
||||
o.statsConfig, err = oc.StatsConfig()
|
||||
if err != nil {
|
||||
sl.With("error", err).Error("Error getting stats config")
|
||||
return
|
||||
}
|
||||
|
||||
replicas := w.cfg.UniqueReplicas()
|
||||
for _, replica := range replicas {
|
||||
@@ -142,6 +152,12 @@ func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardIn
|
||||
return
|
||||
}
|
||||
|
||||
err = w.syncConfigs(o, rs, rc)
|
||||
if err != nil {
|
||||
l.With("error", err).Error("Error syncing configs")
|
||||
return
|
||||
}
|
||||
|
||||
err = w.syncRewrites(o.rewrites, rc)
|
||||
if err != nil {
|
||||
l.With("error", err).Error("Error syncing rewrites")
|
||||
@@ -298,13 +314,39 @@ func (w *worker) syncGeneralSettings(o *origin, rs *types.Status, replica client
|
||||
return nil
|
||||
}
|
||||
|
||||
type origin struct {
|
||||
status *types.Status
|
||||
rewrites *types.RewriteEntries
|
||||
services *types.Services
|
||||
filters *types.FilteringStatus
|
||||
clients *types.Clients
|
||||
parental bool
|
||||
safeSearch bool
|
||||
safeBrowsing bool
|
||||
func (w *worker) syncConfigs(o *origin, rs *types.Status, replica client.Client) error {
|
||||
qlc, err := replica.QueryLogConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !o.queryLogConfig.Equals(qlc) {
|
||||
if err = replica.SetQueryLogConfig(o.queryLogConfig.Enabled, o.queryLogConfig.Interval, o.queryLogConfig.AnonymizeClientIP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sc, err := replica.StatsConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.statsConfig.Interval != sc.Interval {
|
||||
if err = replica.SetStatsConfig(o.statsConfig.Interval); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type origin struct {
|
||||
status *types.Status
|
||||
rewrites *types.RewriteEntries
|
||||
services *types.Services
|
||||
filters *types.FilteringStatus
|
||||
clients *types.Clients
|
||||
queryLogConfig *types.QueryLogConfig
|
||||
statsConfig *types.IntervalConfig
|
||||
parental bool
|
||||
safeSearch bool
|
||||
safeBrowsing bool
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config application configuration struct
|
||||
type Config struct {
|
||||
Origin AdGuardInstance `json:"origin" yaml:"origin"`
|
||||
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty"`
|
||||
@@ -16,12 +17,14 @@ type Config struct {
|
||||
API API `json:"api,omitempty" yaml:"api,omitempty"`
|
||||
}
|
||||
|
||||
// API configuration
|
||||
type API struct {
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty"`
|
||||
}
|
||||
|
||||
// UniqueReplicas get unique replication instances
|
||||
func (cfg *Config) UniqueReplicas() []AdGuardInstance {
|
||||
dedup := make(map[string]AdGuardInstance)
|
||||
if cfg.Replica != nil {
|
||||
@@ -38,6 +41,7 @@ func (cfg *Config) UniqueReplicas() []AdGuardInstance {
|
||||
return r
|
||||
}
|
||||
|
||||
// AdGuardInstance adguard home config instance
|
||||
type AdGuardInstance struct {
|
||||
URL string `json:"url" yaml:"url"`
|
||||
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty"`
|
||||
@@ -46,14 +50,17 @@ type AdGuardInstance struct {
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
|
||||
}
|
||||
|
||||
// Key AdGuardInstance key
|
||||
func (i *AdGuardInstance) Key() string {
|
||||
return fmt.Sprintf("%s%s", i.URL, i.APIPath)
|
||||
}
|
||||
|
||||
// Protection API struct
|
||||
type Protection struct {
|
||||
ProtectionEnabled bool `json:"protection_enabled"`
|
||||
}
|
||||
|
||||
// Status API struct
|
||||
type Status struct {
|
||||
Protection
|
||||
DNSAddresses []string `json:"dns_addresses"`
|
||||
@@ -65,8 +72,10 @@ type Status struct {
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
// RewriteEntries list of RewriteEntry
|
||||
type RewriteEntries []RewriteEntry
|
||||
|
||||
// Merge RewriteEntries
|
||||
func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, RewriteEntries) {
|
||||
current := make(map[string]RewriteEntry)
|
||||
|
||||
@@ -91,17 +100,21 @@ func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, Rewrite
|
||||
return adds, removes
|
||||
}
|
||||
|
||||
// RewriteEntry API struct
|
||||
type RewriteEntry struct {
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
}
|
||||
|
||||
// Key RewriteEntry key
|
||||
func (re *RewriteEntry) Key() string {
|
||||
return fmt.Sprintf("%s#%s", re.Domain, re.Answer)
|
||||
}
|
||||
|
||||
// Filters list of Filter
|
||||
type Filters []Filter
|
||||
|
||||
// Filter API struct
|
||||
type Filter struct {
|
||||
ID int `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -112,6 +125,7 @@ type Filter struct {
|
||||
Whitelist bool `json:"whitelist"` // needed for add
|
||||
}
|
||||
|
||||
// FilteringStatus API struct
|
||||
type FilteringStatus struct {
|
||||
FilteringConfig
|
||||
Filters Filters `json:"filters"`
|
||||
@@ -119,25 +133,48 @@ type FilteringStatus struct {
|
||||
UserRules UserRules `json:"user_rules"`
|
||||
}
|
||||
|
||||
// UserRules API struct
|
||||
type UserRules []string
|
||||
|
||||
// String toString of Users
|
||||
func (ur UserRules) String() string {
|
||||
return strings.Join(ur, "\n")
|
||||
}
|
||||
|
||||
type FeatureStatus struct {
|
||||
// EnableConfig API struct
|
||||
type EnableConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type FilteringConfig struct {
|
||||
FeatureStatus
|
||||
// IntervalConfig API struct
|
||||
type IntervalConfig struct {
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
// FilteringConfig API struct
|
||||
type FilteringConfig struct {
|
||||
EnableConfig
|
||||
IntervalConfig
|
||||
}
|
||||
|
||||
// QueryLogConfig API struct
|
||||
type QueryLogConfig struct {
|
||||
EnableConfig
|
||||
IntervalConfig
|
||||
AnonymizeClientIP bool `json:"anonymize_client_ip"`
|
||||
}
|
||||
|
||||
// Equals QueryLogConfig equal check
|
||||
func (qlc *QueryLogConfig) Equals(o *QueryLogConfig) bool {
|
||||
return qlc.Enabled == o.Enabled && qlc.AnonymizeClientIP == o.AnonymizeClientIP && qlc.Interval == o.Interval
|
||||
}
|
||||
|
||||
// RefreshFilter API struct
|
||||
type RefreshFilter struct {
|
||||
Whitelist bool `json:"whitelist"`
|
||||
}
|
||||
|
||||
// Merge merge RefreshFilters
|
||||
func (fs *Filters) Merge(other Filters) (Filters, Filters) {
|
||||
current := make(map[string]Filter)
|
||||
|
||||
@@ -162,18 +199,22 @@ func (fs *Filters) Merge(other Filters) (Filters, Filters) {
|
||||
return adds, removes
|
||||
}
|
||||
|
||||
// Services API struct
|
||||
type Services []string
|
||||
|
||||
// Sort sort Services
|
||||
func (s Services) Sort() {
|
||||
sort.Strings(s)
|
||||
}
|
||||
|
||||
// Equals Services equal check
|
||||
func (s *Services) Equals(o *Services) bool {
|
||||
s.Sort()
|
||||
o.Sort()
|
||||
return equals(*s, *o)
|
||||
}
|
||||
|
||||
// Clients API struct
|
||||
type Clients struct {
|
||||
Clients []Client `json:"clients"`
|
||||
AutoClients []struct {
|
||||
@@ -186,6 +227,7 @@ type Clients struct {
|
||||
SupportedTags []string `json:"supported_tags"`
|
||||
}
|
||||
|
||||
// Client API struct
|
||||
type Client struct {
|
||||
Ids []string `json:"ids"`
|
||||
Tags []string `json:"tags"`
|
||||
@@ -203,6 +245,7 @@ type Client struct {
|
||||
DisallowedRule string `json:"disallowed_rule"`
|
||||
}
|
||||
|
||||
// Sort sort clients
|
||||
func (cl *Client) Sort() {
|
||||
sort.Strings(cl.Ids)
|
||||
sort.Strings(cl.Tags)
|
||||
@@ -210,6 +253,7 @@ func (cl *Client) Sort() {
|
||||
sort.Strings(cl.Upstreams)
|
||||
}
|
||||
|
||||
// Equal Clients equal check
|
||||
func (cl *Client) Equal(o *Client) bool {
|
||||
cl.Sort()
|
||||
o.Sort()
|
||||
@@ -219,6 +263,7 @@ func (cl *Client) Equal(o *Client) bool {
|
||||
return string(a) == string(b)
|
||||
}
|
||||
|
||||
// Merge merge Clients
|
||||
func (clients *Clients) Merge(other *Clients) ([]Client, []Client, []Client) {
|
||||
current := make(map[string]Client)
|
||||
for _, client := range clients.Clients {
|
||||
@@ -252,6 +297,7 @@ func (clients *Clients) Merge(other *Clients) ([]Client, []Client, []Client) {
|
||||
return adds, updates, removes
|
||||
}
|
||||
|
||||
// ClientUpdate API struct
|
||||
type ClientUpdate struct {
|
||||
Name string `json:"name"`
|
||||
Data Client `json:"data"`
|
||||
|
||||
Reference in New Issue
Block a user