package types import ( "encoding/json" "fmt" "sort" "strings" ) const ( DefaultAPIPath = "/control" ) // Config application configuration struct type Config struct { Origin AdGuardInstance `json:"origin" yaml:"origin"` Replica AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty"` Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty"` Cron string `json:"cron,omitempty" yaml:"cron,omitempty"` 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.URL != "" { dedup[cfg.Replica.Key()] = cfg.Replica } for _, replica := range cfg.Replicas { if replica.URL != "" { dedup[replica.Key()] = replica } } var r []AdGuardInstance for _, replica := range dedup { if replica.APIPath == "" { replica.APIPath = DefaultAPIPath } r = append(r, replica) } return r } // AdGuardInstance adguard home config instance type AdGuardInstance struct { URL string `json:"url" yaml:"url"` APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty"` Username string `json:"username,omitempty" yaml:"username,omitempty"` Password string `json:"password,omitempty" yaml:"password,omitempty"` 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"` DNSPort int `json:"dns_port"` HTTPPort int `json:"http_port"` DhcpAvailable bool `json:"dhcp_available"` Running bool `json:"running"` Version string `json:"version"` 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) var adds RewriteEntries var removes RewriteEntries for _, rr := range *rwe { current[rr.Key()] = rr } for _, rr := range *other { if _, ok := current[rr.Key()]; ok { delete(current, rr.Key()) } else { adds = append(adds, rr) } } for _, rr := range current { removes = append(removes, rr) } 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 // Merge merge Filters func (f Filters) Merge(other Filters) (Filters, Filters, Filters) { current := make(map[string]Filter) var adds Filters var updates Filters var removes Filters for _, f := range f { current[f.URL] = f } for _, rr := range other { if c, ok := current[rr.URL]; ok { if !c.Equals(&rr) { updates = append(updates, rr) } delete(current, rr.URL) } else { adds = append(adds, rr) } } for _, rr := range current { removes = append(removes, rr) } return adds, updates, removes } // Filter API struct type Filter struct { ID int `json:"id"` Enabled bool `json:"enabled"` URL string `json:"url"` // needed for add Name string `json:"name"` // needed for add RulesCount int `json:"rules_count"` Whitelist bool `json:"whitelist"` // needed for add } // Equals Filter equal check func (f *Filter) Equals(o *Filter) bool { return f.Enabled == o.Enabled && f.URL == o.URL && f.Name == o.Name } // FilterUpdate API struct type FilterUpdate struct { URL string `json:"url"` Data Filter `json:"data"` Whitelist bool `json:"whitelist"` } // FilteringStatus API struct type FilteringStatus struct { FilteringConfig Filters Filters `json:"filters"` WhitelistFilters Filters `json:"whitelist_filters"` 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") } // EnableConfig API struct type EnableConfig struct { Enabled bool `json:"enabled"` } // 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"` } // 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 { IP string `json:"ip"` Name string `json:"name"` Source string `json:"source"` WhoisInfo struct { } `json:"whois_info"` } `json:"auto_clients"` SupportedTags []string `json:"supported_tags"` } // Client API struct type Client struct { Ids []string `json:"ids"` Tags []string `json:"tags"` BlockedServices []string `json:"blocked_services"` Upstreams []string `json:"upstreams"` UseGlobalSettings bool `json:"use_global_settings"` UseGlobalBlockedServices bool `json:"use_global_blocked_services"` Name string `json:"name"` FilteringEnabled bool `json:"filtering_enabled"` ParentalEnabled bool `json:"parental_enabled"` SafesearchEnabled bool `json:"safesearch_enabled"` SafebrowsingEnabled bool `json:"safebrowsing_enabled"` Disallowed bool `json:"disallowed"` DisallowedRule string `json:"disallowed_rule"` } // Sort sort clients func (cl *Client) Sort() { sort.Strings(cl.Ids) sort.Strings(cl.Tags) sort.Strings(cl.BlockedServices) sort.Strings(cl.Upstreams) } // Equal Clients equal check func (cl *Client) Equal(o *Client) bool { cl.Sort() o.Sort() a, _ := json.Marshal(cl) b, _ := json.Marshal(o) 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 { current[client.Name] = client } expected := make(map[string]Client) for _, client := range other.Clients { expected[client.Name] = client } var adds []Client var removes []Client var updates []Client for _, cl := range expected { if oc, ok := current[cl.Name]; ok { if !cl.Equal(&oc) { updates = append(updates, cl) } delete(current, cl.Name) } else { adds = append(adds, cl) } } for _, rr := range current { removes = append(removes, rr) } return adds, updates, removes } // ClientUpdate API struct type ClientUpdate struct { Name string `json:"name"` Data Client `json:"data"` } func equals(a []string, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true }