Files
NexusGate/panel-go/static.go
aaaamoooo 3951134940
CI / test (push) Canceled after 0s
CI / anytls-smoke (push) Canceled after 0s
feat: Go port of the panel in panel-go/ — drop-in replacement for server.js
Pure-stdlib Go implementation of the whole control plane: same
nexusgate.json schema (unknown fields preserved verbatim, explicit
null/false tri-states modeled), same HTTP API and error strings,
embedded frontend (NG_PUBLIC_DIR override), self-implemented scrypt
auth, X25519 Reality keys, panel-hosted keyStore and heartbeat
reconciliation included. Version stays 0.6.8.

Verified: Go tests 33/33 (scrypt + Reality vectors generated from
the Node implementation, subscription goldens byte-identical),
Node suite 52/52 untouched, differential E2E against the Node
panel 50/50 including two-way DB swap. See panel-go/PARITY.md
and panel-go/INSTALL-GO.md.
2026-10-20 08:21:54 +00:00

131 lines
3.1 KiB
Go

package main
// Static file serving: the Go port of lib/http.js serveStatic. Assets are
// embedded from embed_public/ (populated by build.sh from ../public);
// when NG_PUBLIC_DIR is set, files are read from that directory instead.
import (
"embed"
"io/fs"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
)
//go:embed embed_public
var embeddedPublic embed.FS
const embedRoot = "embed_public"
var staticMIME = map[string]string{
".html": "text/html; charset=utf-8",
".js": "text/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".svg": "image/svg+xml",
".json": "application/json; charset=utf-8",
}
// ServeStatic serves pathname from the public root; returns false when no
// regular file matches (mirroring serveStatic's false), and an error for
// unexpected I/O failures (mirroring its throw).
func ServeStatic(w http.ResponseWriter, pathname string) (bool, error) {
if dir := os.Getenv("NG_PUBLIC_DIR"); dir != "" {
return serveStaticDisk(w, dir, pathname)
}
relative := "index.html"
if pathname != "/" {
relative = strings.TrimLeft(pathname, "/")
}
// Reject any path that would escape the root (JS path.resolve check).
if escapesRoot(relative) {
return false, nil
}
name := embedRoot + "/" + relative
info, err := fs.Stat(embeddedPublic, name)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
if !info.Mode().IsRegular() {
return false, nil
}
body, err := fs.ReadFile(embeddedPublic, name)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
writeStatic(w, relative, body)
return true, nil
}
// escapesRoot reports whether a slash-separated relative path climbs above
// its root via ".." segments.
func escapesRoot(relative string) bool {
depth := 0
for _, seg := range strings.Split(relative, "/") {
switch seg {
case "..":
if depth == 0 {
return true
}
depth--
case "", ".":
default:
depth++
}
}
return false
}
func serveStaticDisk(w http.ResponseWriter, publicDir, pathname string) (bool, error) {
relative := "index.html"
if pathname != "/" {
relative = strings.TrimLeft(pathname, "/")
}
root := filepath.Clean(publicDir)
target := filepath.Join(root, filepath.FromSlash(relative))
if !strings.HasPrefix(target, root+string(filepath.Separator)) && target != filepath.Join(root, "index.html") {
return false, nil
}
info, err := os.Stat(target)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
if !info.Mode().IsRegular() {
return false, nil
}
body, err := os.ReadFile(target)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
writeStatic(w, relative, body)
return true, nil
}
func writeStatic(w http.ResponseWriter, name string, body []byte) {
contentType := staticMIME[path.Ext(name)]
if contentType == "" {
contentType = "application/octet-stream"
}
h := w.Header()
h.Set("Content-Type", contentType)
h.Set("Content-Length", strconv.Itoa(len(body)))
h.Set("Cache-Control", "no-store")
h.Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(200)
_, _ = w.Write(body)
}