feat(web): add static file serving and update API routes

- Implement embedded static file serving for frontend assets
- Update API routes to use '/api' prefix
- Add web handler for static file serving with proper content types
- Modify .gitignore and README to reflect new web/dist directory structure
This commit is contained in:
2026-05-05 15:45:51 +08:00
parent cdeaa5bacc
commit a0ef35627b
5 changed files with 135 additions and 33 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ go.work.sum
/frp_client
/configs
/dist
/web/dist
agent.md
super-frpc
super-frpc.exe
+2
View File
@@ -37,6 +37,8 @@ Create a `config.json` file in the project root:
## Build
**You have to put the compiled frontend in the `web/dist` directory before building the backend.**
```bash
go build -o super-frpc.exe
```
+33 -32
View File
@@ -4,49 +4,50 @@ import (
"fmt"
"net/http"
"super-frpc/frpLogger"
"super-frpc/utils"
"super-frpc/handlers"
"super-frpc/postLog"
"super-frpc/session"
"super-frpc/handlers"
"super-frpc/utils"
"super-frpc/web"
)
func setupRoutes() {
systemLogHandler := postLog.NewLogSocketHandler(postLog.GetLogBroadcaster())
http.HandleFunc("/system/getLogs", systemLogHandler.Handle)
http.HandleFunc("/system/getStatus", GetStatusHandler)
http.HandleFunc("/system/getSoftwareInfo", GetSoftwareInfoHandler)
http.HandleFunc("/system/settings/get", handlers.GetSettingsHandler)
http.HandleFunc("/system/settings/set", handlers.SetSettingsHandler)
http.HandleFunc("/api/system/getLogs", systemLogHandler.Handle)
http.HandleFunc("/api/system/getStatus", GetStatusHandler)
http.HandleFunc("/api/system/getSoftwareInfo", GetSoftwareInfoHandler)
http.HandleFunc("/api/system/settings/get", handlers.GetSettingsHandler)
http.HandleFunc("/api/system/settings/set", handlers.SetSettingsHandler)
http.HandleFunc("/register", handlers.RegisterHandler)
http.HandleFunc("/login", handlers.LoginHandler)
http.HandleFunc("/logout", handlers.LogoutHandler)
http.HandleFunc("/api/register", handlers.RegisterHandler)
http.HandleFunc("/api/login", handlers.LoginHandler)
http.HandleFunc("/api/logout", handlers.LogoutHandler)
http.HandleFunc("/userMgr/create", handlers.CreateUserHandler)
http.HandleFunc("/userMgr/remove", handlers.RemoveUserHandler)
http.HandleFunc("/userMgr/list", handlers.ListUserHandler)
http.HandleFunc("/userMgr/modify", handlers.ModifyUserHandler)
http.HandleFunc("/userMgr/modifyType", handlers.ModifyUserTypeHandler)
http.HandleFunc("/api/userMgr/create", handlers.CreateUserHandler)
http.HandleFunc("/api/userMgr/remove", handlers.RemoveUserHandler)
http.HandleFunc("/api/userMgr/list", handlers.ListUserHandler)
http.HandleFunc("/api/userMgr/modify", handlers.ModifyUserHandler)
http.HandleFunc("/api/userMgr/modifyType", handlers.ModifyUserTypeHandler)
http.HandleFunc("/sessionMgr/list", handlers.ListActiveSessionsHandler)
http.HandleFunc("/sessionMgr/remove", handlers.RemoveSessionHandler)
http.HandleFunc("/api/sessionMgr/list", handlers.ListActiveSessionsHandler)
http.HandleFunc("/api/sessionMgr/remove", handlers.RemoveSessionHandler)
http.HandleFunc("/frpcAct/instanceMgr/create", handlers.CreateInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/delete", handlers.DeleteInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/modify", handlers.ModifyInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/list", handlers.ListInstancesHandler)
http.HandleFunc("/frpcAct/instanceMgr/start", handlers.StartInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/stop", handlers.StopInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/restart", handlers.RestartInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/status", handlers.GetInstanceStatusHandler)
http.HandleFunc("/frpcAct/instanceMgr/getInfo", handlers.GetInstanceInfoHandler)
http.HandleFunc("/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(session.ValidateTokenFromMap).ServeHTTP)
http.HandleFunc("/frpcAct/proxyMgr/create", handlers.CreateProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/modify", handlers.ModifyProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/delete", handlers.DeleteProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/list", handlers.ListProxiesHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/create", handlers.CreateInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/delete", handlers.DeleteInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/modify", handlers.ModifyInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/list", handlers.ListInstancesHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/start", handlers.StartInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/stop", handlers.StopInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/restart", handlers.RestartInstanceHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/status", handlers.GetInstanceStatusHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/getInfo", handlers.GetInstanceInfoHandler)
http.HandleFunc("/api/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(session.ValidateTokenFromMap).ServeHTTP)
http.HandleFunc("/api/frpcAct/proxyMgr/create", handlers.CreateProxyHandler)
http.HandleFunc("/api/frpcAct/proxyMgr/modify", handlers.ModifyProxyHandler)
http.HandleFunc("/api/frpcAct/proxyMgr/delete", handlers.DeleteProxyHandler)
http.HandleFunc("/api/frpcAct/proxyMgr/list", handlers.ListProxiesHandler)
http.HandleFunc("/", NotFoundHandler)
http.HandleFunc("/", web.ServeStatic)
postLog.Info("Routes setup successfully")
+6
View File
@@ -0,0 +1,6 @@
package web
import "embed"
//go:embed dist/*
var StaticFiles embed.FS
+93
View File
@@ -0,0 +1,93 @@
package web
import (
"io/fs"
"net/http"
"path"
"strings"
)
var staticFS http.FileSystem
func init() {
subFS, err := fs.Sub(StaticFiles, "dist")
if err != nil {
panic(err)
}
staticFS = http.FS(subFS)
}
func ServeStatic(w http.ResponseWriter, r *http.Request) {
urlPath := r.URL.Path
if urlPath == "/" || urlPath == "/index.html" || strings.HasPrefix(urlPath, "/assets/") {
if urlPath == "/" {
urlPath = "/index.html"
}
filePath := strings.TrimPrefix(urlPath, "/")
f, err := staticFS.Open(filePath)
if err != nil {
serveIndexHTML(w)
return
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
serveIndexHTML(w)
return
}
if stat.IsDir() {
serveIndexHTML(w)
return
}
setContentType(w, path.Ext(filePath))
http.ServeContent(w, r, filePath, stat.ModTime(), f.(http.File))
return
}
serveIndexHTML(w)
}
func serveIndexHTML(w http.ResponseWriter) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
data, _ := StaticFiles.ReadFile("dist/index.html")
w.Write(data)
}
func setContentType(w http.ResponseWriter, ext string) {
switch ext {
case ".html":
w.Header().Set("Content-Type", "text/html; charset=utf-8")
case ".css":
w.Header().Set("Content-Type", "text/css; charset=utf-8")
case ".js":
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
case ".json":
w.Header().Set("Content-Type", "application/json; charset=utf-8")
case ".png":
w.Header().Set("Content-Type", "image/png")
case ".jpg", ".jpeg":
w.Header().Set("Content-Type", "image/jpeg")
case ".gif":
w.Header().Set("Content-Type", "image/gif")
case ".svg":
w.Header().Set("Content-Type", "image/svg+xml")
case ".ico":
w.Header().Set("Content-Type", "image/x-icon")
case ".woff":
w.Header().Set("Content-Type", "font/woff")
case ".woff2":
w.Header().Set("Content-Type", "font/woff2")
case ".ttf":
w.Header().Set("Content-Type", "font/ttf")
case ".eot":
w.Header().Set("Content-Type", "application/vnd.ms-fontobject")
default:
w.Header().Set("Content-Type", "application/octet-stream")
}
}