mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-27 20:01:48 +01:00
154 lines
3.9 KiB
Go
Executable File
154 lines
3.9 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
)
|
|
|
|
var panelHandler = panelRouter()
|
|
|
|
func panelRouter() *http.ServeMux {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("GET /{$}", panelServeFile)
|
|
mux.HandleFunc("GET /{file}", panelServeFile)
|
|
mux.HandleFunc("GET /panel/", panelPage)
|
|
mux.HandleFunc("GET /panel/{file}", panelServeFile)
|
|
mux.HandleFunc("HEAD /checkhealth", panelCheckTargetHealth)
|
|
mux.HandleFunc("GET /config_editor/", panelConfigEditor)
|
|
mux.HandleFunc("GET /config_editor/{file}", panelServeFile)
|
|
mux.HandleFunc("GET /config/{file}", panelConfigGet)
|
|
mux.HandleFunc("PUT /config/{file}", panelConfigUpdate)
|
|
mux.HandleFunc("POST /reload", configReload)
|
|
mux.HandleFunc("GET /codemirror/", panelServeFile)
|
|
return mux
|
|
}
|
|
|
|
func panelPage(w http.ResponseWriter, r *http.Request) {
|
|
resp := struct {
|
|
HTTPRoutes HTTPRoutes
|
|
StreamRoutes StreamRoutes
|
|
}{httpRoutes, streamRoutes}
|
|
|
|
panelRenderFile(w, r, panelTemplatePath, resp)
|
|
}
|
|
|
|
func panelCheckTargetHealth(w http.ResponseWriter, r *http.Request) {
|
|
targetUrl := r.URL.Query().Get("target")
|
|
|
|
if targetUrl == "" {
|
|
panelHandleErr(w, r, errors.New("target is required"), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
url, err := url.Parse(targetUrl)
|
|
if err != nil {
|
|
err = NewNestedError("failed to parse url").Subject(targetUrl).With(err)
|
|
panelHandleErr(w, r, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
scheme := url.Scheme
|
|
|
|
if isStreamScheme(scheme) {
|
|
err = utils.healthCheckStream(scheme, url.Host)
|
|
} else {
|
|
err = utils.healthCheckHttp(targetUrl)
|
|
}
|
|
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
} else {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
}
|
|
|
|
func panelConfigEditor(w http.ResponseWriter, r *http.Request) {
|
|
cfgFiles := make([]string, 0)
|
|
cfgFiles = append(cfgFiles, path.Base(configPath))
|
|
for _, p := range cfg.Value().Providers {
|
|
if p.Kind != ProviderKind_File {
|
|
continue
|
|
}
|
|
cfgFiles = append(cfgFiles, p.Value)
|
|
}
|
|
|
|
panelRenderFile(w, r, configEditorTemplatePath, cfgFiles)
|
|
}
|
|
|
|
func panelConfigGet(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, path.Join(configBasePath, r.PathValue("file")))
|
|
}
|
|
|
|
func panelConfigUpdate(w http.ResponseWriter, r *http.Request) {
|
|
p := r.PathValue("file")
|
|
content := make([]byte, r.ContentLength)
|
|
_, err := r.Body.Read(content)
|
|
if err != nil {
|
|
panelHandleErr(w, r, NewNestedError("unable to read request body").Subject(p).With(err))
|
|
return
|
|
}
|
|
if p == path.Base(configPath) {
|
|
err = ValidateConfig(content)
|
|
} else {
|
|
_, err = ValidateFileContent(content)
|
|
}
|
|
if err != nil {
|
|
panelHandleErr(w, r, err)
|
|
return
|
|
}
|
|
p = path.Join(configBasePath, p)
|
|
_, err = os.Stat(p)
|
|
exists := !errors.Is(err, os.ErrNotExist)
|
|
err = os.WriteFile(p, content, 0644)
|
|
if err != nil {
|
|
panelHandleErr(w, r, NewNestedError("unable to write config file").With(err))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if !exists {
|
|
w.Write([]byte(fmt.Sprintf("Config file %s created, remember to add it to config.yml!", p)))
|
|
return
|
|
}
|
|
w.Write([]byte(fmt.Sprintf("Config file %s updated", p)))
|
|
}
|
|
|
|
func panelServeFile(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, path.Join(templatesBasePath, r.URL.Path))
|
|
}
|
|
|
|
func panelRenderFile(w http.ResponseWriter, r *http.Request, f string, data any) {
|
|
tmpl, err := template.ParseFiles(f)
|
|
if err != nil {
|
|
panelHandleErr(w, r, NewNestedError("unable to parse template").With(err))
|
|
return
|
|
}
|
|
|
|
err = tmpl.Execute(w, data)
|
|
if err != nil {
|
|
panelHandleErr(w, r, NewNestedError("unable to render template").With(err))
|
|
}
|
|
}
|
|
|
|
func configReload(w http.ResponseWriter, r *http.Request) {
|
|
err := cfg.Reload()
|
|
if err != nil {
|
|
panelHandleErr(w, r, err)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func panelHandleErr(w http.ResponseWriter, r *http.Request, err error, code ...int) {
|
|
err = NewNestedErrorFrom(err).Subjectf("%s %s", r.Method, r.URL)
|
|
palog.Error(err)
|
|
if len(code) > 0 {
|
|
http.Error(w, err.Error(), code[0])
|
|
return
|
|
}
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|