Ctrl K
ComunidadGuías…

adguardhome/adguardhome_client.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package adguardhome

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	log "github.com/sirupsen/logrus"
)

// Client is an interface for the AdguardHome API.
// See OpenAPI spec for details: https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/openapi/openapi.yaml
type Client interface {
	GetFilteringRules(ctx context.Context) ([]string, error)
	SaveFilteringRules(ctx context.Context, rules []string) error
}

type client struct {
	hc           *http.Client
	lifecycleCtx context.Context

	endpoint string
	user     string
	pass     string
	dryRun   bool
}

type filteringStatus struct {
	UserRules []string `json:"user_rules"`
}

type setRules struct {
	Rules []string `json:"rules"`
}

// requestContext preserves operation deadlines and values while linking shutdown
// cancellation. Release it only after the response body has been closed.
func (c *client) requestContext(ctx context.Context) (context.Context, context.CancelFunc) {
	requestCtx, cancel := context.WithCancel(ctx)
	stop := context.AfterFunc(c.lifecycleCtx, cancel)
	if c.lifecycleCtx.Err() != nil {
		cancel()
	}
	return requestCtx, func() {
		stop()
		cancel()
	}
}

func (c *client) doRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	log.Debugf("making %s request to %s", method, path)

	req, err := http.NewRequestWithContext(ctx, method, c.endpoint+path, body)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(c.user, c.pass)
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.hc.Do(req)
	if err != nil {
		return nil, err
	}

	log.Debugf("response status code %d", resp.StatusCode)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
	}

	return resp, nil
}

func (c *client) GetFilteringRules(ctx context.Context) ([]string, error) {
	if c.dryRun {
		return []string{}, nil
	}

	ctx, release := c.requestContext(ctx)
	defer release()
	r, err := c.doRequest(ctx, http.MethodGet, "filtering/status", nil)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()

	var resp filteringStatus
	err = json.NewDecoder(r.Body).Decode(&resp)
	if err != nil {
		return nil, err
	}

	return resp.UserRules, nil
}

func (c *client) SaveFilteringRules(ctx context.Context, rules []string) error {
	if c.dryRun {
		return nil
	}

	ctx, release := c.requestContext(ctx)
	defer release()
	body := setRules{Rules: rules}

	b := bytes.NewBuffer(nil)
	err := json.NewEncoder(b).Encode(body)
	if err != nil {
		return err
	}

	r, err := c.doRequest(ctx, http.MethodPost, "filtering/set_rules", b)
	if err != nil {
		return err
	}
	_ = r.Body.Close()
	return nil
}

func newAdguardHomeClient(ctx context.Context, endpoint, user, pass string, dryRun bool) *client {
	hc := http.Client{}
	return &client{
		hc:           &hc,
		lifecycleCtx: ctx,
		endpoint:     endpoint,
		user:         user,
		pass:         pass,

		dryRun: dryRun,
	}
}