func (c *client) request(ctx context.Context, method, path string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) {
	var res *Response
	err := backoff.Retry(func() error {
		r, err := c.requestOnce(ctx, method, path, body, opts...)
		if err != nil {
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return backoff.Permanent(err)
			}
			return errors.WrapIf(err, "http: request creation failed")
		}
		res = r
		if r.HasError() {
			defer r.Body.Close()
			if r.StatusCode == http.StatusForbidden ||
				r.StatusCode == http.StatusTooManyRequests ||
				r.StatusCode == http.StatusUnauthorized {
				return backoff.Permanent(r.Error())
			}
			return r.Error()
		}
		return nil
	}, c.backoff(ctx))
	if err != nil {
		if v, ok := err.(*backoff.PermanentError); ok {
			return nil, v.Unwrap()
		}
		return nil, err
	}
	return res, nil
}
request executes an HTTP request against the Panel API. If there is an error
encountered with the request it will be retried using an exponential backoff.
If the error returned from the Panel is due to API throttling or because there
are invalid authentication credentials provided the request will _not_ be
retried by the backoff.

This function automatically appends the path to the current client endpoint
and adds the required authentication headers to the request that is being
created. Errors returned will be of the RequestError type if there was some
type of response from the API that can be parsed.
package remote
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
	"emperror.dev/errors"
	"github.com/apex/log"
	"github.com/cenkalti/backoff/v4"
	"github.com/pterodactyl/wings/system"
)
type Client interface {
	GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (BackupRemoteUploadResponse, error)
	GetInstallationScript(ctx context.Context, uuid string) (InstallationScript, error)
	GetServerConfiguration(ctx context.Context, uuid string) (ServerConfigurationResponse, error)
	GetServers(context context.Context, perPage int) ([]RawServerData, error)
	ResetServersState(ctx context.Context) error
	SetArchiveStatus(ctx context.Context, uuid string, successful bool) error
	SetBackupStatus(ctx context.Context, backup string, data BackupRequest) error
	SendRestorationStatus(ctx context.Context, backup string, successful bool) error
	SetInstallationStatus(ctx context.Context, uuid string, successful bool) error
	SetTransferStatus(ctx context.Context, uuid string, successful bool) error
	ValidateSftpCredentials(ctx context.Context, request SftpAuthRequest) (SftpAuthResponse, error)
}
type client struct {
	httpClient  *http.Client
	baseUrl     string
	tokenId     string
	token       string
	maxAttempts int
}
func New(base string, opts ...ClientOption) Client {
	c := client{
		baseUrl: strings.TrimSuffix(base, "/") + "/api/remote",
		httpClient: &http.Client{
			Timeout: time.Second * 15,
		},
		maxAttempts: 0,
	}
	for _, opt := range opts {
		opt(&c)
	}
	return &c
}
func WithCredentials(id, token string) ClientOption {
	return func(c *client) {
		c.tokenId = id
		c.token = token
	}
}
func WithHttpClient(httpClient *http.Client) ClientOption {
	return func(c *client) {
		c.httpClient = httpClient
	}
}
func (c *client) Get(ctx context.Context, path string, query q) (*Response, error) {
	return c.request(ctx, http.MethodGet, path, nil, func(r *http.Request) {
		q := r.URL.Query()
		for k, v := range query {
			q.Set(k, v)
		}
		r.URL.RawQuery = q.Encode()
	})
}
func (c *client) Post(ctx context.Context, path string, data interface{}) (*Response, error) {
	b, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return c.request(ctx, http.MethodPost, path, bytes.NewBuffer(b))
}
func (c *client) requestOnce(ctx context.Context, method, path string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) {
	req, err := http.NewRequestWithContext(ctx, method, c.baseUrl+path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", fmt.Sprintf("Pterodactyl Wings/v%s (id:%s)", system.Version, c.tokenId))
	req.Header.Set("Accept", "application/vnd.pterodactyl.v1+json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s.%s", c.tokenId, c.token))
	for _, o := range opts {
		o(req)
	}
	debugLogRequest(req)
	res, err := c.httpClient.Do(req)
	return &Response{res}, err
}