add new panelapi package
should eventually replace the api package
This commit is contained in:
parent
217ca72eb3
commit
94f4207d60
34
panelapi/backup.go
Normal file
34
panelapi/backup.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *client) GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (api.BackupRemoteUploadResponse, error) {
|
||||||
|
res, err := c.get(ctx, fmt.Sprintf("/backups/%s", backup), q{"size": strconv.FormatInt(size, 10)})
|
||||||
|
if err != nil {
|
||||||
|
return api.BackupRemoteUploadResponse{}, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.HasError() {
|
||||||
|
return api.BackupRemoteUploadResponse{}, res.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
r := api.BackupRemoteUploadResponse{}
|
||||||
|
err = res.BindJSON(&r)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SetBackupStatus(ctx context.Context, backup string, data api.BackupRequest) error {
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/backups/%s", backup), data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
55
panelapi/client.go
Normal file
55
panelapi/client.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
GetBackupRemoteUploadURLs(ctx context.Context, backup string, size int64) (api.BackupRemoteUploadResponse, error)
|
||||||
|
GetInstallationScript(ctx context.Context, uuid string) (api.InstallationScript, error)
|
||||||
|
GetServerConfiguration(ctx context.Context, uuid string) (api.ServerConfigurationResponse, error)
|
||||||
|
GetServers(context context.Context, perPage int) ([]api.RawServerData, error)
|
||||||
|
SetArchiveStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
|
SetBackupStatus(ctx context.Context, backup string, data api.BackupRequest) error
|
||||||
|
SetInstallationStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
|
SetTransferStatus(ctx context.Context, uuid string, successful bool) error
|
||||||
|
ValidateSftpCredentials(ctx context.Context, request api.SftpAuthRequest) (api.SftpAuthResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
httpClient *http.Client
|
||||||
|
baseUrl string
|
||||||
|
tokenId string
|
||||||
|
token string
|
||||||
|
retries int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientOption func(c *client)
|
||||||
|
|
||||||
|
func CreateClient(base, tokenId, token string, opts ...ClientOption) Client {
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Timeout: time.Second * 15,
|
||||||
|
}
|
||||||
|
c := &client{
|
||||||
|
baseUrl: filepath.Join(base, "api/remote"),
|
||||||
|
tokenId: tokenId,
|
||||||
|
token: token,
|
||||||
|
httpClient: httpClient,
|
||||||
|
retries: 3,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTimeout(timeout time.Duration) ClientOption {
|
||||||
|
return func(c *client) {
|
||||||
|
c.httpClient.Timeout = timeout
|
||||||
|
}
|
||||||
|
}
|
46
panelapi/errors.go
Normal file
46
panelapi/errors.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestErrors struct {
|
||||||
|
Errors []RequestError `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestError struct {
|
||||||
|
response *http.Response
|
||||||
|
Code string `json:"code"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Detail string `json:"detail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRequestError(err error) bool {
|
||||||
|
_, ok := err.(*RequestError)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the error response in a string form that can be more easily consumed.
|
||||||
|
func (re *RequestError) Error() string {
|
||||||
|
c := 0
|
||||||
|
if re.response != nil {
|
||||||
|
c = re.response.StatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Error response from Panel: %s: %s (HTTP/%d)", re.Code, re.Detail, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sftpInvalidCredentialsError struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ice sftpInvalidCredentialsError) Error() string {
|
||||||
|
return "the credentials provided were invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsInvalidCredentialsError(err error) bool {
|
||||||
|
_, ok := err.(*sftpInvalidCredentialsError)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
158
panelapi/http.go
Normal file
158
panelapi/http.go
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A custom response type that allows for commonly used error handling and response
|
||||||
|
// parsing from the Panel API. This just embeds the normal HTTP response from Go and
|
||||||
|
// we attach a few helper functions to it.
|
||||||
|
type Response struct {
|
||||||
|
*http.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
// A generic type allowing for easy binding use when making requests to API endpoints
|
||||||
|
// that only expect a singular argument or something that would not benefit from being
|
||||||
|
// a typed struct.
|
||||||
|
//
|
||||||
|
// Inspired by gin.H, same concept.
|
||||||
|
type d map[string]interface{}
|
||||||
|
|
||||||
|
// Same concept as d, but a map of strings, used for querying GET requests.
|
||||||
|
type q map[string]string
|
||||||
|
|
||||||
|
// requestOnce creates a http request and executes it once.
|
||||||
|
// Prefer request() over this method when possible.
|
||||||
|
// It appends the path to the endpoint of the client and adds the authentication token to the request.
|
||||||
|
func (c *client) requestOnce(ctx context.Context, method, path string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) {
|
||||||
|
req, err := http.NewRequest(method, filepath.Join(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))
|
||||||
|
|
||||||
|
// Call all opts functions to allow modifying the request
|
||||||
|
for _, o := range opts {
|
||||||
|
o(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLogRequest(req)
|
||||||
|
|
||||||
|
res, err := c.httpClient.Do(req.WithContext(ctx))
|
||||||
|
return &Response{res}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// request executes a http request and retries when errors occur.
|
||||||
|
// It appends the path to the endpoint of the client and adds the authentication token to the request.
|
||||||
|
func (c *client) request(ctx context.Context, method, path string, body io.Reader, opts ...func(r *http.Request)) (*Response, error) {
|
||||||
|
var doErr error
|
||||||
|
var res *Response
|
||||||
|
|
||||||
|
for i := 0; i < c.retries; i++ {
|
||||||
|
res, doErr = c.requestOnce(ctx, method, path, body, opts...)
|
||||||
|
|
||||||
|
if doErr == nil &&
|
||||||
|
res.StatusCode < http.StatusInternalServerError &&
|
||||||
|
res.StatusCode != http.StatusTooManyRequests {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, doErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// get executes a http get request.
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// post executes a http post request.
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if the API call encountered an error. If no request has been made
|
||||||
|
// the response will be false. This function will evaluate to true if the response
|
||||||
|
// code is anything 300 or higher.
|
||||||
|
func (r *Response) HasError() bool {
|
||||||
|
if r.Response == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.StatusCode >= 300 || r.StatusCode < 200
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the body from the response and returns it, then replaces it on the response
|
||||||
|
// so that it can be read again later. This does not close the response body, so any
|
||||||
|
// functions calling this should be sure to manually defer a Body.Close() call.
|
||||||
|
func (r *Response) Read() ([]byte, error) {
|
||||||
|
var b []byte
|
||||||
|
if r.Response == nil {
|
||||||
|
return nil, errors.New("no response exists on interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Response.Body != nil {
|
||||||
|
b, _ = ioutil.ReadAll(r.Response.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Response.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binds a given interface with the data returned in the response. This is a shortcut
|
||||||
|
// for calling Read and then manually calling json.Unmarshal on the raw bytes.
|
||||||
|
func (r *Response) BindJSON(v interface{}) error {
|
||||||
|
b, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the first error message from the API call as a string.
|
||||||
|
// The error message will be formatted similar to the below example:
|
||||||
|
//
|
||||||
|
// HttpNotFoundException: The requested resource does not exist. (HTTP/404)
|
||||||
|
func (r *Response) Error() error {
|
||||||
|
if !r.HasError() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs RequestErrors
|
||||||
|
_ = r.BindJSON(&errs)
|
||||||
|
|
||||||
|
e := &RequestError{}
|
||||||
|
if len(errs.Errors) > 0 {
|
||||||
|
e = &errs.Errors[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
e.response = r.Response
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
167
panelapi/servers.go
Normal file
167
panelapi/servers.go
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pterodactyl/wings/api"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProcessStopCommand = "command"
|
||||||
|
ProcessStopSignal = "signal"
|
||||||
|
ProcessStopNativeStop = "stop"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Holds the server configuration data returned from the Panel. When a server process
|
||||||
|
// is started, Wings communicates with the Panel to fetch the latest build information
|
||||||
|
// as well as get all of the details needed to parse the given Egg.
|
||||||
|
//
|
||||||
|
// This means we do not need to hit Wings each time part of the server is updated, and
|
||||||
|
// the Panel serves as the source of truth at all times. This also means if a configuration
|
||||||
|
// is accidentally wiped on Wings we can self-recover without too much hassle, so long
|
||||||
|
// as Wings is aware of what servers should exist on it.
|
||||||
|
type ServerConfigurationResponse struct {
|
||||||
|
Settings json.RawMessage `json:"settings"`
|
||||||
|
ProcessConfiguration *api.ProcessConfiguration `json:"process_configuration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines installation script information for a server process. This is used when
|
||||||
|
// a server is installed for the first time, and when a server is marked for re-installation.
|
||||||
|
type InstallationScript struct {
|
||||||
|
ContainerImage string `json:"container_image"`
|
||||||
|
Entrypoint string `json:"entrypoint"`
|
||||||
|
Script string `json:"script"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type allServerResponse struct {
|
||||||
|
Data []api.RawServerData `json:"data"`
|
||||||
|
Meta api.Pagination `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawServerData struct {
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
Settings json.RawMessage `json:"settings"`
|
||||||
|
ProcessConfiguration json.RawMessage `json:"process_configuration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetServersPaged(ctx context.Context, page, limit int) ([]api.RawServerData, api.Pagination, error) {
|
||||||
|
res, err := c.get(ctx, "/servers", q{
|
||||||
|
"page": strconv.Itoa(page),
|
||||||
|
"per_page": strconv.Itoa(limit),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, api.Pagination{}, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.HasError() {
|
||||||
|
return nil, api.Pagination{}, res.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var r allServerResponse
|
||||||
|
if err := res.BindJSON(&r); err != nil {
|
||||||
|
return nil, api.Pagination{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Data, r.Meta, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetServers(ctx context.Context, perPage int) ([]api.RawServerData, error) {
|
||||||
|
servers, pageMeta, err := c.GetServersPaged(ctx, 0, perPage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the amount of servers exceeds the page limit, get the remaining pages in parallel
|
||||||
|
if pageMeta.LastPage > 1 {
|
||||||
|
eg, _ := errgroup.WithContext(ctx)
|
||||||
|
serversMu := sync.Mutex{}
|
||||||
|
|
||||||
|
for page := pageMeta.CurrentPage + 1; page <= pageMeta.LastPage; page++ {
|
||||||
|
eg.Go(func() error {
|
||||||
|
ps, _, err := c.GetServersPaged(ctx, perPage, int(page))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serversMu.Lock()
|
||||||
|
servers = append(servers, ps...)
|
||||||
|
serversMu.Unlock()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetServerConfiguration(ctx context.Context, uuid string) (api.ServerConfigurationResponse, error) {
|
||||||
|
res, err := c.get(ctx, fmt.Sprintf("/servers/%s", uuid), nil)
|
||||||
|
if err != nil {
|
||||||
|
return api.ServerConfigurationResponse{}, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.HasError() {
|
||||||
|
return api.ServerConfigurationResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := api.ServerConfigurationResponse{}
|
||||||
|
err = res.BindJSON(&config)
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetInstallationScript(ctx context.Context, uuid string) (api.InstallationScript, error) {
|
||||||
|
res, err := c.get(ctx, fmt.Sprintf("/servers/%s/install", uuid), nil)
|
||||||
|
if err != nil {
|
||||||
|
return api.InstallationScript{}, err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.HasError() {
|
||||||
|
return api.InstallationScript{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := api.InstallationScript{}
|
||||||
|
err = res.BindJSON(&config)
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SetInstallationStatus(ctx context.Context, uuid string, successful bool) error {
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/servers/%s/install", uuid), d{"successful": successful})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SetArchiveStatus(ctx context.Context, uuid string, successful bool) error {
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/servers/%s/archive", uuid), d{"successful": successful})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SetTransferStatus(ctx context.Context, uuid string, successful bool) error {
|
||||||
|
state := "failure"
|
||||||
|
if successful {
|
||||||
|
state = "success"
|
||||||
|
}
|
||||||
|
resp, err := c.post(ctx, fmt.Sprintf("/servers/%s/transfer/%s", uuid), state)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.Error()
|
||||||
|
}
|
50
panelapi/sftp.go
Normal file
50
panelapi/sftp.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/pterodactyl/wings/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
|
||||||
|
// at least in the expected format. This is very basic protection against random bots finding the SFTP
|
||||||
|
// server and sending a flood of usernames.
|
||||||
|
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
|
||||||
|
|
||||||
|
func (c *client) ValidateSftpCredentials(ctx context.Context, request api.SftpAuthRequest) (api.SftpAuthResponse, error) {
|
||||||
|
if !validUsernameRegexp.MatchString(request.User) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"subsystem": "sftp",
|
||||||
|
"username": request.User,
|
||||||
|
"ip": request.IP,
|
||||||
|
}).Warn("failed to validate user credentials (invalid format)")
|
||||||
|
return api.SftpAuthResponse{}, new(sftpInvalidCredentialsError)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.post(ctx, "/sftp/auth", request)
|
||||||
|
if err != nil {
|
||||||
|
return api.SftpAuthResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
e := res.Error()
|
||||||
|
if e != nil {
|
||||||
|
if res.StatusCode >= 400 && res.StatusCode < 500 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"subsystem": "sftp",
|
||||||
|
"username": request.User,
|
||||||
|
"ip": request.IP,
|
||||||
|
}).Warn(e.Error())
|
||||||
|
|
||||||
|
return api.SftpAuthResponse{}, &sftpInvalidCredentialsError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.SftpAuthResponse{}, errors.New(e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
r := api.SftpAuthResponse{}
|
||||||
|
err = res.BindJSON(&r)
|
||||||
|
return r, err
|
||||||
|
}
|
29
panelapi/util.go
Normal file
29
panelapi/util.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package panelapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/apex/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logs the request into the debug log with all of the important request bits.
|
||||||
|
// The authorization key will be cleaned up before being output.
|
||||||
|
//
|
||||||
|
// TODO(schrej): Somehow only execute the logic when log level is debug.
|
||||||
|
func debugLogRequest(req *http.Request) {
|
||||||
|
headers := make(map[string][]string)
|
||||||
|
for k, v := range req.Header {
|
||||||
|
if k != "Authorization" || len(v) == 0 {
|
||||||
|
headers[k] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
headers[k] = []string{v[0][0:15] + "(redacted)"}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"method": req.Method,
|
||||||
|
"endpoint": req.URL.String(),
|
||||||
|
"headers": headers,
|
||||||
|
}).Debug("making request to external HTTP endpoint")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user