feat: initial commit
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
This commit is contained in:
312
internal/admin/proxy_route.go
Normal file
312
internal/admin/proxy_route.go
Normal file
@ -0,0 +1,312 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/api"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
type QueryProxyResponse struct {
|
||||
Proxies []*store.ProxyHeader `json:"proxies"`
|
||||
}
|
||||
|
||||
func (s *Server) queryProxy(w http.ResponseWriter, r *http.Request) {
|
||||
options := []store.QueryProxyOptionFunc{}
|
||||
|
||||
names, ok := getStringableSliceValues(w, r, "names", nil, validateProxyName)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if names != nil {
|
||||
options = append(options, store.WithProxyQueryNames(names...))
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
proxies, err := s.proxyRepository.QueryProxy(
|
||||
ctx,
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "could not list proxies", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
sort.Sort(store.ByProxyWeight(proxies))
|
||||
|
||||
api.DataResponse(w, http.StatusOK, QueryProxyResponse{
|
||||
Proxies: proxies,
|
||||
})
|
||||
}
|
||||
|
||||
func validateProxyName(v string) (store.ProxyName, error) {
|
||||
name, err := store.ValidateName(v)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
return store.ProxyName(name), nil
|
||||
}
|
||||
|
||||
type GetProxyResponse struct {
|
||||
Proxy *store.Proxy `json:"proxy"`
|
||||
}
|
||||
|
||||
func (s *Server) getProxy(w http.ResponseWriter, r *http.Request) {
|
||||
proxyName, ok := getProxyName(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
proxy, err := s.proxyRepository.GetProxy(ctx, proxyName)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
api.ErrorResponse(w, http.StatusNotFound, api.ErrCodeNotFound, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logger.Error(ctx, "could not get proxy", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
api.DataResponse(w, http.StatusOK, GetProxyResponse{
|
||||
Proxy: proxy,
|
||||
})
|
||||
}
|
||||
|
||||
type DeleteProxyResponse struct {
|
||||
ProxyName store.ProxyName `json:"proxyName"`
|
||||
}
|
||||
|
||||
func (s *Server) deleteProxy(w http.ResponseWriter, r *http.Request) {
|
||||
proxyName, ok := getProxyName(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
if err := s.proxyRepository.DeleteProxy(ctx, proxyName); err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
api.ErrorResponse(w, http.StatusNotFound, api.ErrCodeNotFound, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logger.Error(ctx, "could not delete proxy", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
api.DataResponse(w, http.StatusOK, DeleteProxyResponse{
|
||||
ProxyName: proxyName,
|
||||
})
|
||||
}
|
||||
|
||||
type CreateProxyRequest struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
To string `json:"to" validate:"required"`
|
||||
From []string `json:"from" validate:"required"`
|
||||
}
|
||||
|
||||
type CreateProxyResponse struct {
|
||||
Proxy *store.Proxy `json:"proxy"`
|
||||
}
|
||||
|
||||
func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
createProxyReq := &CreateProxyRequest{}
|
||||
if ok := api.Bind(w, r, createProxyReq); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
name, err := store.ValidateName(createProxyReq.Name)
|
||||
if err != nil {
|
||||
logger.Error(r.Context(), "could not parse 'name' parameter", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := url.Parse(createProxyReq.To); err != nil {
|
||||
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
proxy, err := s.proxyRepository.CreateProxy(ctx, store.ProxyName(name), createProxyReq.To, createProxyReq.From...)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrAlreadyExist) {
|
||||
api.ErrorResponse(w, http.StatusConflict, ErrCodeAlreadyExist, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logger.Error(ctx, "could not create proxy", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
api.DataResponse(w, http.StatusOK, struct {
|
||||
Proxy *store.Proxy `json:"proxy"`
|
||||
}{
|
||||
Proxy: proxy,
|
||||
})
|
||||
}
|
||||
|
||||
type UpdateProxyRequest struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
Weight *int `json:"weight"`
|
||||
To *string `json:"to"`
|
||||
From []string `json:"from"`
|
||||
}
|
||||
|
||||
type UpdateProxyResponse struct {
|
||||
Proxy *store.Proxy `json:"proxy"`
|
||||
}
|
||||
|
||||
func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
proxyName, ok := getProxyName(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
updateProxyReq := &UpdateProxyRequest{}
|
||||
if ok := api.Bind(w, r, updateProxyReq); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
options := make([]store.UpdateProxyOptionFunc, 0)
|
||||
|
||||
if updateProxyReq.Enabled != nil {
|
||||
options = append(options, store.WithProxyUpdateEnabled(*updateProxyReq.Enabled))
|
||||
}
|
||||
|
||||
if updateProxyReq.To != nil {
|
||||
_, err := url.Parse(*updateProxyReq.To)
|
||||
if err != nil {
|
||||
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
options = append(options, store.WithProxyUpdateTo(*updateProxyReq.To))
|
||||
}
|
||||
|
||||
if updateProxyReq.From != nil {
|
||||
options = append(options, store.WithProxyUpdateFrom(updateProxyReq.From...))
|
||||
}
|
||||
|
||||
if updateProxyReq.Weight != nil {
|
||||
options = append(options, store.WithProxyUpdateWeight(*updateProxyReq.Weight))
|
||||
}
|
||||
|
||||
proxy, err := s.proxyRepository.UpdateProxy(
|
||||
ctx, proxyName,
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
api.ErrorResponse(w, http.StatusNotFound, api.ErrCodeNotFound, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logger.Error(ctx, "could not update proxy", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
api.DataResponse(w, http.StatusOK, UpdateProxyResponse{Proxy: proxy})
|
||||
}
|
||||
|
||||
func getProxyName(w http.ResponseWriter, r *http.Request) (store.ProxyName, bool) {
|
||||
rawProxyName := chi.URLParam(r, "proxyName")
|
||||
|
||||
name, err := store.ValidateName(rawProxyName)
|
||||
if err != nil {
|
||||
logger.Error(r.Context(), "could not parse proxy name", logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
return store.ProxyName(name), true
|
||||
}
|
||||
|
||||
func getIntQueryParam(w http.ResponseWriter, r *http.Request, param string, defaultValue int64) (int64, bool) {
|
||||
rawValue := r.URL.Query().Get(param)
|
||||
if rawValue != "" {
|
||||
value, err := strconv.ParseInt(rawValue, 10, 64)
|
||||
if err != nil {
|
||||
logger.Error(r.Context(), "could not parse int param", logger.F("param", param), logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
||||
return defaultValue, true
|
||||
}
|
||||
|
||||
func getStringSliceValues(w http.ResponseWriter, r *http.Request, param string, defaultValue []string) ([]string, bool) {
|
||||
rawValue := r.URL.Query().Get(param)
|
||||
if rawValue != "" {
|
||||
values := strings.Split(rawValue, ",")
|
||||
|
||||
return values, true
|
||||
}
|
||||
|
||||
return defaultValue, true
|
||||
}
|
||||
|
||||
func getStringableSliceValues[T ~string](w http.ResponseWriter, r *http.Request, param string, defaultValue []T, validate func(string) (T, error)) ([]T, bool) {
|
||||
rawValue := r.URL.Query().Get(param)
|
||||
|
||||
if rawValue != "" {
|
||||
rawValues := strings.Split(rawValue, ",")
|
||||
values := make([]T, 0, len(rawValues))
|
||||
|
||||
for _, rv := range rawValues {
|
||||
v, err := validate(rv)
|
||||
if err != nil {
|
||||
logger.Error(r.Context(), "could not parse ids slice param", logger.F("param", param), logger.E(errors.WithStack(err)))
|
||||
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
values = append(values, v)
|
||||
}
|
||||
|
||||
return values, true
|
||||
}
|
||||
|
||||
return defaultValue, true
|
||||
}
|
Reference in New Issue
Block a user