Some checks failed
Go / build (.exe, 386, windows, windows-386) (push) Has been cancelled
Go / build (.exe, amd64, windows, windows-amd64) (push) Has been cancelled
Go / build (.exe, arm64, windows, windows-arm64) (push) Has been cancelled
Go / build (386, freebsd, freebsd-386) (push) Has been cancelled
Go / build (386, linux, linux-386) (push) Has been cancelled
Go / build (386, netbsd, netbsd-386) (push) Has been cancelled
Go / build (386, openbsd, openbsd-386) (push) Has been cancelled
Go / build (386, plan9, plan9-386) (push) Has been cancelled
Go / build (amd64, darwin, darwin-amd64) (push) Has been cancelled
Go / build (amd64, dragonfly, dragonfly-amd64) (push) Has been cancelled
Go / build (amd64, freebsd, freebsd-amd64) (push) Has been cancelled
Go / build (amd64, illumos, illumos-amd64) (push) Has been cancelled
Go / build (amd64, linux, linux-amd64) (push) Has been cancelled
Go / build (amd64, netbsd, netbsd-amd64) (push) Has been cancelled
Go / build (amd64, openbsd, openbsd-amd64) (push) Has been cancelled
Go / build (amd64, plan9, plan9-amd64) (push) Has been cancelled
Go / build (amd64, solaris, solaris-amd64) (push) Has been cancelled
Go / build (arm, 6, linux, linux-armv6) (push) Has been cancelled
Go / build (arm, 7, linux, linux-armv7) (push) Has been cancelled
Go / build (arm, freebsd, freebsd-arm) (push) Has been cancelled
Go / build (arm, netbsd, netbsd-arm) (push) Has been cancelled
Go / build (arm, openbsd, openbsd-arm) (push) Has been cancelled
Go / build (arm, plan9, plan9-arm) (push) Has been cancelled
Go / build (arm64, darwin, darwin-arm64) (push) Has been cancelled
Go / build (arm64, freebsd, freebsd-arm64) (push) Has been cancelled
Go / build (arm64, linux, linux-arm64) (push) Has been cancelled
Go / build (arm64, netbsd, netbsd-arm64) (push) Has been cancelled
Go / build (arm64, openbsd, openbsd-arm64) (push) Has been cancelled
Go / build (loong64, linux, linux-loong64) (push) Has been cancelled
Go / build (mips, linux, linux-mips) (push) Has been cancelled
Go / build (mips64, linux, linux-mips64) (push) Has been cancelled
Go / build (mips64le, linux, linux-mips64le) (push) Has been cancelled
Go / build (mipsle, linux, linux-mipsle) (push) Has been cancelled
Go / build (ppc64, aix, aix-ppc64) (push) Has been cancelled
Go / build (ppc64, linux, linux-ppc64) (push) Has been cancelled
Go / build (ppc64, openbsd, openbsd-ppc64) (push) Has been cancelled
Go / build (ppc64le, linux, linux-ppc64le) (push) Has been cancelled
Go / build (riscv64, freebsd, freebsd-riscv64) (push) Has been cancelled
Go / build (riscv64, linux, linux-riscv64) (push) Has been cancelled
Go / build (riscv64, openbsd, openbsd-riscv64) (push) Has been cancelled
Go / build (s390x, linux, linux-s390x) (push) Has been cancelled
Docker Image / docker (push) Has been cancelled
Go / merge-artifacts (push) Has been cancelled
140 lines
3.7 KiB
Go
140 lines
3.7 KiB
Go
// internal/adminhttp/gateway.go 把仓库驱动的网关依赖适配为 Admin API 处理器集合。
|
|
|
|
package adminhttp
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
defaultStaticDir = "cmd/gateway/admin_static"
|
|
staticConfigFile = "config.js"
|
|
staticIndexFile = "index.html"
|
|
)
|
|
|
|
type GatewayHandlerOptions struct {
|
|
AdminPath string
|
|
AdminAPIPrefix string
|
|
StaticDir string
|
|
APIHandler http.HandlerFunc
|
|
WebSocketEnabled bool
|
|
WebSocketPath string
|
|
WebSocketHandler http.HandlerFunc
|
|
}
|
|
|
|
func NewGatewayHandler(opts GatewayHandlerOptions) http.Handler {
|
|
mux := http.NewServeMux()
|
|
registerAdminHandlers(mux, opts)
|
|
|
|
if opts.WebSocketEnabled &&
|
|
opts.WebSocketHandler != nil &&
|
|
!WebSocketPathConflictsWithAdmin(opts.WebSocketPath, opts.AdminPath, opts.AdminAPIPrefix) {
|
|
mux.HandleFunc(opts.WebSocketPath, opts.WebSocketHandler)
|
|
}
|
|
|
|
return mux
|
|
}
|
|
|
|
func WebSocketPathConflictsWithAdmin(webSocketPath, adminPath, adminAPIPrefix string) bool {
|
|
adminRoot := strings.TrimRight(adminPath, "/")
|
|
if webSocketPath == adminPath || webSocketPath == adminRoot {
|
|
return true
|
|
}
|
|
if webSocketPath == adminAPIPrefix || strings.HasPrefix(adminAPIPrefix+"/", webSocketPath+"/") {
|
|
return webSocketPath != "/"
|
|
}
|
|
return false
|
|
}
|
|
|
|
func registerAdminHandlers(mux *http.ServeMux, opts GatewayHandlerOptions) {
|
|
adminRoot := strings.TrimRight(opts.AdminPath, "/")
|
|
|
|
mux.HandleFunc(opts.AdminAPIPrefix, opts.APIHandler)
|
|
mux.HandleFunc(opts.AdminAPIPrefix+"/", opts.APIHandler)
|
|
if adminRoot != "" {
|
|
mux.HandleFunc(adminRoot, func(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, opts.AdminPath, http.StatusMovedPermanently)
|
|
})
|
|
}
|
|
mux.HandleFunc(opts.AdminPath, func(w http.ResponseWriter, r *http.Request) {
|
|
serveAdminStatic(w, r, opts)
|
|
})
|
|
}
|
|
|
|
func serveAdminStatic(w http.ResponseWriter, r *http.Request, opts GatewayHandlerOptions) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
WriteAPIError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
return
|
|
}
|
|
|
|
if r.URL.Path == opts.AdminPath {
|
|
serveAdminFile(w, r, opts, staticIndexFile)
|
|
return
|
|
}
|
|
|
|
rel := strings.TrimPrefix(r.URL.Path, opts.AdminPath)
|
|
if rel == staticConfigFile {
|
|
serveAdminConfig(w, opts)
|
|
return
|
|
}
|
|
serveAdminFile(w, r, opts, rel)
|
|
}
|
|
|
|
func serveAdminConfig(w http.ResponseWriter, opts GatewayHandlerOptions) {
|
|
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
_, _ = w.Write([]byte(`window.MCGatewayAdmin={"apiPrefix":` + strconv.Quote(opts.AdminAPIPrefix) + `};`))
|
|
}
|
|
|
|
func serveAdminFile(w http.ResponseWriter, r *http.Request, opts GatewayHandlerOptions, rel string) {
|
|
name, ok := cleanStaticPath(rel)
|
|
if !ok {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
file := filepath.Join(staticDir(opts), name)
|
|
info, err := os.Stat(file)
|
|
if err != nil || info.IsDir() {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
http.ServeFile(w, r, file)
|
|
}
|
|
|
|
func cleanStaticPath(rel string) (string, bool) {
|
|
if rel == "" {
|
|
return "", false
|
|
}
|
|
cleaned := path.Clean("/" + rel)
|
|
if cleaned == "/" || strings.HasPrefix(cleaned, "/../") {
|
|
return "", false
|
|
}
|
|
name := strings.TrimPrefix(cleaned, "/")
|
|
if name == staticConfigFile {
|
|
return "", false
|
|
}
|
|
return name, true
|
|
}
|
|
|
|
func staticDir(opts GatewayHandlerOptions) string {
|
|
if strings.TrimSpace(opts.StaticDir) != "" {
|
|
return opts.StaticDir
|
|
}
|
|
if value := strings.TrimSpace(os.Getenv("MC_GATEWAY_ADMIN_STATIC_DIR")); value != "" {
|
|
return value
|
|
}
|
|
if _, err := os.Stat(defaultStaticDir); err == nil {
|
|
return defaultStaticDir
|
|
}
|
|
if _, err := os.Stat("admin_static"); err == nil {
|
|
return "admin_static"
|
|
}
|
|
return defaultStaticDir
|
|
}
|