mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-22 16:28:30 +02:00
simplify setup process with WebUI
This commit is contained in:
@@ -77,7 +77,9 @@ func NewHandler(cfg config.ConfigInstance) http.Handler {
|
||||
mux.HandleFunc("GET", "/v1/logs", memlogger.Handler(), true)
|
||||
mux.HandleFunc("GET", "/v1/favicon", favicon.GetFavIcon, true)
|
||||
mux.HandleFunc("POST", "/v1/homepage/set", v1.SetHomePageOverrides, true)
|
||||
mux.HandleFunc("GET", "/v1/agents", v1.AgentsWS, true)
|
||||
mux.HandleFunc("GET", "/v1/agents", v1.ListAgents, true)
|
||||
mux.HandleFunc("GET", "/v1/agents/new", v1.NewAgent, true)
|
||||
mux.HandleFunc("POST", "/v1/agents/add", v1.AddAgent, true)
|
||||
mux.HandleFunc("GET", "/v1/metrics/system_info", v1.SystemInfo, true)
|
||||
mux.HandleFunc("GET", "/v1/metrics/uptime", uptime.Poller.ServeHTTP, true)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
)
|
||||
|
||||
func AgentsWS(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
func ListAgents(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
wsjson.Write(r.Context(), conn, cfg.ListAgents())
|
||||
|
||||
@@ -28,7 +28,6 @@ const (
|
||||
ListHomepageCategories = "homepage_categories"
|
||||
ListIcons = "icons"
|
||||
ListTasks = "tasks"
|
||||
ListAgents = "agents"
|
||||
)
|
||||
|
||||
func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -78,8 +77,6 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
U.RespondJSON(w, r, icons)
|
||||
case ListTasks:
|
||||
U.RespondJSON(w, r, task.DebugTaskList())
|
||||
case ListAgents:
|
||||
U.RespondJSON(w, r, cfg.ListAgents())
|
||||
default:
|
||||
U.HandleErr(w, r, U.ErrInvalidKey("what"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
135
internal/api/v1/new_agent.go
Normal file
135
internal/api/v1/new_agent.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
name := q.Get("name")
|
||||
if name == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("name"))
|
||||
return
|
||||
}
|
||||
host := q.Get("host")
|
||||
if host == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("host"))
|
||||
return
|
||||
}
|
||||
portStr := q.Get("port")
|
||||
if portStr == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("port"))
|
||||
return
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
U.RespondError(w, U.ErrInvalidKey("port"))
|
||||
return
|
||||
}
|
||||
hostport := fmt.Sprintf("%s:%d", host, port)
|
||||
if _, ok := config.GetInstance().GetAgent(hostport); ok {
|
||||
U.RespondError(w, U.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
||||
return
|
||||
}
|
||||
t := q.Get("type")
|
||||
switch t {
|
||||
case "docker":
|
||||
break
|
||||
case "system":
|
||||
U.RespondError(w, U.Errorf("system agent is not supported yet"), http.StatusNotImplemented)
|
||||
return
|
||||
case "":
|
||||
U.RespondError(w, U.ErrMissingKey("type"))
|
||||
return
|
||||
default:
|
||||
U.RespondError(w, U.ErrInvalidKey("type"))
|
||||
return
|
||||
}
|
||||
|
||||
nightly := strutils.ParseBool(q.Get("nightly"))
|
||||
var image string
|
||||
if nightly {
|
||||
image = agent.DockerImageNightly
|
||||
} else {
|
||||
image = agent.DockerImageProduction
|
||||
}
|
||||
|
||||
ca, srv, client, err := agent.NewAgent()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := agent.AgentComposeConfig{
|
||||
Image: image,
|
||||
Name: name,
|
||||
Port: port,
|
||||
CACert: ca.String(),
|
||||
SSLCert: srv.String(),
|
||||
}
|
||||
|
||||
template, err := cfg.Generate()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
U.RespondJSON(w, r, map[string]any{
|
||||
"compose": template,
|
||||
"ca": ca,
|
||||
"client": client,
|
||||
})
|
||||
}
|
||||
|
||||
func AddAgent(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
clientPEMData, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
var data struct {
|
||||
Host string `json:"host"`
|
||||
CA agent.PEMPair `json:"ca"`
|
||||
Client agent.PEMPair `json:"client"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(clientPEMData, &data); err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
nRoutesAdded, err := config.GetInstance().AddAgent(data.Host, data.CA, data.Client)
|
||||
if err != nil {
|
||||
U.RespondError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
zip, err := certs.ZipCert(data.CA.Cert, data.Client.Cert, data.Client.Key)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(certs.AgentCertsFilename(data.Host), zip, 0600); err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf("Added %d routes", nRoutesAdded)))
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"syscall"
|
||||
@@ -42,25 +41,26 @@ func RespondError(w http.ResponseWriter, err error, code ...int) {
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusBadRequest}
|
||||
}
|
||||
buf, err := json.Marshal(err)
|
||||
if err != nil { // just in case
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
http.Error(w, ansi.StripANSI(err.Error()), code[0])
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(code[0])
|
||||
_, _ = w.Write(buf)
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
http.Error(w, ansi.StripANSI(err.Error()), code[0])
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...any) error {
|
||||
return E.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func ErrMissingKey(k string) error {
|
||||
return E.New("missing key '" + k + "' in query or request body")
|
||||
return E.New(k + " is required")
|
||||
}
|
||||
|
||||
func ErrInvalidKey(k string) error {
|
||||
return E.New("invalid key '" + k + "' in query or request body")
|
||||
return E.New(k + " is invalid")
|
||||
}
|
||||
|
||||
func ErrAlreadyExists(k, v string) error {
|
||||
return E.Errorf("%s %q already exists", k, v)
|
||||
}
|
||||
|
||||
func ErrNotFound(k, v string) error {
|
||||
return E.Errorf("key %q with value %q not found", k, v)
|
||||
return E.Errorf("%s %q not found", k, v)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user