216 lines
5.2 KiB
Go
216 lines
5.2 KiB
Go
package globals
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"platform/web/core"
|
|
"strings"
|
|
)
|
|
|
|
var ErrGostNotFound = errors.New("gost resource not found")
|
|
|
|
func IsGostNotFound(err error) bool {
|
|
return errors.Is(err, ErrGostNotFound)
|
|
}
|
|
|
|
type GostClient interface {
|
|
GetChain(name string) (*GostChainConfig, error)
|
|
CreateService(service *GostServiceConfig) error
|
|
DeleteService(name string) error
|
|
CreateAuther(auther *GostAutherConfig) error
|
|
DeleteAuther(name string) error
|
|
CreateAdmission(admission *GostAdmissionConfig) error
|
|
DeleteAdmission(name string) error
|
|
}
|
|
|
|
type gostClient struct {
|
|
baseURL string
|
|
pathPrefix string
|
|
username string
|
|
password string
|
|
}
|
|
|
|
var GostInitializer = func(host string, port int, pathPrefix, username, password string) GostClient {
|
|
baseURL := strings.TrimSpace(host)
|
|
if !strings.Contains(baseURL, "://") {
|
|
baseURL = fmt.Sprintf("http://%s:%d", baseURL, port)
|
|
}
|
|
|
|
return &gostClient{
|
|
baseURL: strings.TrimRight(baseURL, "/"),
|
|
pathPrefix: normalizeGostPathPrefix(pathPrefix),
|
|
username: username,
|
|
password: password,
|
|
}
|
|
}
|
|
|
|
func NewGost(host string, port int, pathPrefix, username, password string) GostClient {
|
|
return GostInitializer(host, port, pathPrefix, username, password)
|
|
}
|
|
|
|
type GostChainConfig struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type GostServiceConfig struct {
|
|
Name string `json:"name"`
|
|
Addr string `json:"addr"`
|
|
Admission string `json:"admission,omitempty"`
|
|
Handler GostHandlerConfig `json:"handler"`
|
|
Listener GostListenerConfig `json:"listener"`
|
|
}
|
|
|
|
type GostHandlerConfig struct {
|
|
Type string `json:"type"`
|
|
Chain string `json:"chain,omitempty"`
|
|
Auther string `json:"auther,omitempty"`
|
|
}
|
|
|
|
type GostListenerConfig struct {
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
type GostAutherConfig struct {
|
|
Name string `json:"name"`
|
|
Auths []GostAuthConfig `json:"auths"`
|
|
}
|
|
|
|
type GostAuthConfig struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type GostAdmissionConfig struct {
|
|
Name string `json:"name"`
|
|
Whitelist bool `json:"whitelist"`
|
|
Matchers []string `json:"matchers"`
|
|
}
|
|
|
|
func (c *gostClient) GetChain(name string) (*GostChainConfig, error) {
|
|
body, err := c.get("/config/chains/" + url.PathEscape(name))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(body) == 0 {
|
|
return &GostChainConfig{Name: name}, nil
|
|
}
|
|
|
|
var direct GostChainConfig
|
|
if err := json.Unmarshal(body, &direct); err == nil && direct.Name != "" {
|
|
return &direct, nil
|
|
}
|
|
|
|
var wrapper struct {
|
|
Data *GostChainConfig `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(body, &wrapper); err == nil && wrapper.Data != nil && wrapper.Data.Name != "" {
|
|
return wrapper.Data, nil
|
|
}
|
|
|
|
return &GostChainConfig{Name: name}, nil
|
|
}
|
|
|
|
func (c *gostClient) CreateService(service *GostServiceConfig) error {
|
|
return c.create("/config/services", service)
|
|
}
|
|
|
|
func (c *gostClient) DeleteService(name string) error {
|
|
return c.delete("/config/services/" + url.PathEscape(name))
|
|
}
|
|
|
|
func (c *gostClient) CreateAuther(auther *GostAutherConfig) error {
|
|
return c.create("/config/authers", auther)
|
|
}
|
|
|
|
func (c *gostClient) DeleteAuther(name string) error {
|
|
return c.delete("/config/authers/" + url.PathEscape(name))
|
|
}
|
|
|
|
func (c *gostClient) CreateAdmission(admission *GostAdmissionConfig) error {
|
|
return c.create("/config/admissions", admission)
|
|
}
|
|
|
|
func (c *gostClient) DeleteAdmission(name string) error {
|
|
return c.delete("/config/admissions/" + url.PathEscape(name))
|
|
}
|
|
|
|
func (c *gostClient) create(path string, payload any) error {
|
|
_, err := c.request(http.MethodPost, path, payload)
|
|
return err
|
|
}
|
|
|
|
func (c *gostClient) get(path string) ([]byte, error) {
|
|
body, err := c.request(http.MethodGet, path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
func (c *gostClient) delete(path string) error {
|
|
_, err := c.request(http.MethodDelete, path, nil)
|
|
return err
|
|
}
|
|
|
|
func (c *gostClient) request(method string, path string, payload any) ([]byte, error) {
|
|
var bodyReader io.Reader
|
|
if payload != nil {
|
|
data, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bodyReader = bytes.NewReader(data)
|
|
}
|
|
|
|
req, err := http.NewRequest(method, c.endpoint(path), bodyReader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.SetBasicAuth(c.username, c.password)
|
|
if payload != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
|
|
resp, err := core.Fetch(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
_ = Body.Close()
|
|
}(resp.Body)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode == http.StatusBadRequest {
|
|
return nil, fmt.Errorf("%w: %s", ErrGostNotFound, string(body))
|
|
}
|
|
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
|
return nil, fmt.Errorf("gost api %s %s failed: %d %s", method, path, resp.StatusCode, string(body))
|
|
}
|
|
|
|
return body, nil
|
|
}
|
|
|
|
func (c *gostClient) endpoint(path string) string {
|
|
return c.baseURL + c.pathPrefix + path
|
|
}
|
|
|
|
func normalizeGostPathPrefix(prefix string) string {
|
|
prefix = strings.TrimSpace(prefix)
|
|
if prefix == "" {
|
|
return ""
|
|
}
|
|
if !strings.HasPrefix(prefix, "/") {
|
|
prefix = "/" + prefix
|
|
}
|
|
return strings.TrimRight(prefix, "/")
|
|
}
|