Files
adguardhome-sync/pkg/client/model/client/client.go
T
Marc Brugger da289017a5 Generate types from openapi (#201)
* generate model from openAPI schema
* implement replica status #231
* Sync "Pause service blocking schedule" #234
* Sync "Safe Search Provider" #200
2023-11-13 21:09:08 +01:00

145 lines
3.2 KiB
Go

package client
import (
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/types"
"go.uber.org/zap"
)
var l = log.GetLogger("client")
// New create a new api client
func New(config types.AdGuardInstance) (Client, error) {
var apiURL string
if config.APIPath == "" {
apiURL = fmt.Sprintf("%s/control", config.URL)
} else {
apiURL = fmt.Sprintf("%s/%s", config.URL, config.APIPath)
}
u, err := url.Parse(apiURL)
if err != nil {
return nil, err
}
u.Path = path.Clean(u.Path)
httpClient := &http.Client{}
if config.InsecureSkipVerify {
// #nosec G402 has to be explicitly enabled
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
aghClient, err := model.NewClient(u.String(), func(client *model.AdguardHomeClient) error {
client.Client = httpClient
client.RequestEditors = append(client.RequestEditors, func(ctx context.Context, req *http.Request) error {
if config.Username != "" && config.Password != "" {
req.Header.Add("Authorization", "Basic "+basicAuth(config.Username, config.Password))
}
return nil
})
return nil
})
if err != nil {
return nil, err
}
return &apiClient{
host: u.Host,
client: aghClient,
log: l.With("host", u.Host),
}, nil
}
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
type apiClient struct {
host string
client *model.AdguardHomeClient
log *zap.SugaredLogger
}
func (a apiClient) Host(context.Context) string {
return a.host
}
func (a apiClient) GetServerStatus(ctx context.Context) (*model.ServerStatus, error) {
sr, err := read(ctx, a.client.Status, model.ParseStatusResp)
if err != nil {
return nil, err
}
return sr.JSON200, nil
}
func (a apiClient) GetFilteringStatus(ctx context.Context) (*model.FilterStatus, error) {
sr, err := read(ctx, a.client.FilteringStatus, model.ParseFilteringStatusResp)
if err != nil {
return nil, err
}
return sr.JSON200, nil
}
func (a apiClient) SetFilteringConfig(ctx context.Context, config model.FilterConfig) error {
return write(ctx, config, a.client.FilteringConfig)
}
func write[B interface{}](
ctx context.Context,
body B,
req func(ctx context.Context, body B, reqEditors ...model.RequestEditorFn) (*http.Response, error),
) error {
resp, err := req(ctx, body)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return detailedError(resp)
}
return nil
}
func read[I interface{}](
ctx context.Context,
req func(ctx context.Context, reqEditors ...model.RequestEditorFn) (*http.Response, error),
parse func(rsp *http.Response) (*I, error),
) (*I, error) {
resp, err := req(ctx)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, detailedError(resp)
}
return parse(resp)
}
func detailedError(resp *http.Response) error {
e := resp.Status
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if len(body) > 0 {
e += fmt.Sprintf("(%s)", string(body))
}
return errors.New(e)
}