mirror of
https://github.com/bakito/adguardhome-sync.git
synced 2025-01-10 01:02:11 +08:00
372 lines
7.9 KiB
Go
372 lines
7.9 KiB
Go
package sync
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/bakito/adguardhome-sync/pkg/client"
|
|
"github.com/bakito/adguardhome-sync/pkg/log"
|
|
"github.com/bakito/adguardhome-sync/pkg/types"
|
|
"github.com/bakito/adguardhome-sync/version"
|
|
"github.com/robfig/cron/v3"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
var (
|
|
l = log.GetLogger("sync")
|
|
)
|
|
|
|
// Sync config from origin to replica
|
|
func Sync(cfg *types.Config) error {
|
|
|
|
if cfg.Origin.URL == "" {
|
|
return fmt.Errorf("origin URL is required")
|
|
}
|
|
|
|
if len(cfg.UniqueReplicas()) == 0 {
|
|
return fmt.Errorf("no replicas configured")
|
|
}
|
|
|
|
w := &worker{
|
|
cfg: cfg,
|
|
}
|
|
if cfg.Cron != "" {
|
|
w.cron = cron.New()
|
|
cl := l.With("version", version.Version, "cron", cfg.Cron)
|
|
_, err := w.cron.AddFunc(cfg.Cron, func() {
|
|
w.sync()
|
|
})
|
|
if err != nil {
|
|
cl.With("error", err).Error("Error during cron job setup")
|
|
return err
|
|
}
|
|
cl.Info("Setup cronjob")
|
|
if cfg.API.Port != 0 {
|
|
w.cron.Start()
|
|
} else {
|
|
w.cron.Run()
|
|
}
|
|
} else {
|
|
w.sync()
|
|
}
|
|
if cfg.API.Port != 0 {
|
|
w.listenAndServe()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type worker struct {
|
|
cfg *types.Config
|
|
running bool
|
|
cron *cron.Cron
|
|
}
|
|
|
|
func (w *worker) sync() {
|
|
if w.running {
|
|
l.Info("Sync already running")
|
|
return
|
|
}
|
|
w.running = true
|
|
defer func() { w.running = false }()
|
|
|
|
oc, err := client.New(w.cfg.Origin)
|
|
if err != nil {
|
|
l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
|
|
return
|
|
}
|
|
|
|
sl := l.With("from", oc.Host())
|
|
|
|
o := &origin{}
|
|
|
|
o.status, err = oc.Status()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting origin status")
|
|
return
|
|
}
|
|
|
|
o.parental, err = oc.Parental()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting parental status")
|
|
return
|
|
}
|
|
o.safeSearch, err = oc.SafeSearch()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting safe search status")
|
|
return
|
|
}
|
|
o.safeBrowsing, err = oc.SafeBrowsing()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting safe browsing status")
|
|
return
|
|
}
|
|
|
|
o.rewrites, err = oc.RewriteList()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting origin rewrites")
|
|
return
|
|
}
|
|
|
|
o.services, err = oc.Services()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting origin services")
|
|
return
|
|
}
|
|
|
|
o.filters, err = oc.Filtering()
|
|
if err != nil {
|
|
sl.With("error", err).Error("Error getting origin filters")
|
|
return
|
|
}
|
|
o.clients, err = oc.Clients()
|
|
if err != nil {
|
|
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 {
|
|
w.syncTo(sl, o, replica)
|
|
}
|
|
}
|
|
|
|
func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardInstance) {
|
|
|
|
rc, err := client.New(replica)
|
|
if err != nil {
|
|
l.With("error", err, "url", replica.URL).Error("Error creating replica client")
|
|
return
|
|
}
|
|
|
|
rl := l.With("to", rc.Host())
|
|
rl.Info("Start sync")
|
|
|
|
rs, err := rc.Status()
|
|
if err != nil {
|
|
l.With("error", err).Error("Error getting replica status")
|
|
return
|
|
}
|
|
|
|
if o.status.Version != rs.Version {
|
|
l.With("originVersion", o.status.Version, "replicaVersion", rs.Version).Warn("Versions do not match")
|
|
}
|
|
|
|
err = w.syncGeneralSettings(o, rs, rc)
|
|
if err != nil {
|
|
l.With("error", err).Error("Error syncing general settings")
|
|
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")
|
|
return
|
|
}
|
|
err = w.syncFilters(o.filters, rc)
|
|
if err != nil {
|
|
l.With("error", err).Error("Error syncing filters")
|
|
return
|
|
}
|
|
|
|
err = w.syncServices(o.services, rc)
|
|
if err != nil {
|
|
l.With("error", err).Error("Error syncing services")
|
|
return
|
|
}
|
|
|
|
if err = w.syncClients(o.clients, rc); err != nil {
|
|
l.With("error", err).Error("Error syncing clients")
|
|
return
|
|
}
|
|
|
|
rl.Info("Sync done")
|
|
}
|
|
|
|
func (w *worker) syncServices(os types.Services, replica client.Client) error {
|
|
rs, err := replica.Services()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !os.Equals(rs) {
|
|
if err := replica.SetServices(os); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *worker) syncFilters(of *types.FilteringStatus, replica client.Client) error {
|
|
rf, err := replica.Filtering()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fa, fu, fd := rf.Filters.Merge(of.Filters)
|
|
|
|
if err = replica.AddFilters(false, fa...); err != nil {
|
|
return err
|
|
}
|
|
if err = replica.UpdateFilters(false, fu...); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(fa) > 0 || len(fu) > 0 {
|
|
if err = replica.RefreshFilters(false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err = replica.DeleteFilters(false, fd...); err != nil {
|
|
return err
|
|
}
|
|
|
|
fa, fu, fd = rf.WhitelistFilters.Merge(of.WhitelistFilters)
|
|
if err = replica.AddFilters(true, fa...); err != nil {
|
|
return err
|
|
}
|
|
if err = replica.UpdateFilters(true, fu...); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(fa) > 0 || len(fu) > 0 {
|
|
if err = replica.RefreshFilters(true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err = replica.DeleteFilters(true, fd...); err != nil {
|
|
return err
|
|
}
|
|
|
|
if of.UserRules.String() != rf.UserRules.String() {
|
|
return replica.SetCustomRules(of.UserRules)
|
|
}
|
|
|
|
if of.Enabled != rf.Enabled || of.Interval != rf.Interval {
|
|
if err = replica.ToggleFiltering(of.Enabled, of.Interval); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *worker) syncRewrites(or *types.RewriteEntries, replica client.Client) error {
|
|
|
|
replicaRewrites, err := replica.RewriteList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
a, r := replicaRewrites.Merge(or)
|
|
|
|
if err = replica.AddRewriteEntries(a...); err != nil {
|
|
return err
|
|
}
|
|
if err = replica.DeleteRewriteEntries(r...); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *worker) syncClients(oc *types.Clients, replica client.Client) error {
|
|
rc, err := replica.Clients()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
a, u, r := rc.Merge(oc)
|
|
|
|
if err = replica.AddClients(a...); err != nil {
|
|
return err
|
|
}
|
|
if err = replica.UpdateClients(u...); err != nil {
|
|
return err
|
|
}
|
|
if err = replica.DeleteClients(r...); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *worker) syncGeneralSettings(o *origin, rs *types.Status, replica client.Client) error {
|
|
if o.status.ProtectionEnabled != rs.ProtectionEnabled {
|
|
if err := replica.ToggleProtection(o.status.ProtectionEnabled); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if rp, err := replica.Parental(); err != nil {
|
|
return err
|
|
} else if o.parental != rp {
|
|
if err = replica.ToggleParental(o.parental); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if rs, err := replica.SafeSearch(); err != nil {
|
|
return err
|
|
} else if o.safeSearch != rs {
|
|
if err = replica.ToggleSafeSearch(o.safeSearch); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if rs, err := replica.SafeBrowsing(); err != nil {
|
|
return err
|
|
} else if o.safeBrowsing != rs {
|
|
if err = replica.ToggleSafeBrowsing(o.safeBrowsing); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|