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 /frp_client
/configs /configs
/dist /web/dist
agent.md agent.md
super-frpc super-frpc
super-frpc.exe super-frpc.exe
+2
View File
@@ -37,6 +37,8 @@ Create a `config.json` file in the project root:
## Build ## Build
**You have to put the compiled frontend in the `web/dist` directory before building the backend.**
```bash ```bash
go build -o super-frpc.exe go build -o super-frpc.exe
``` ```
+33 -32
View File
@@ -4,49 +4,50 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"super-frpc/frpLogger" "super-frpc/frpLogger"
"super-frpc/utils" "super-frpc/handlers"
"super-frpc/postLog" "super-frpc/postLog"
"super-frpc/session" "super-frpc/session"
"super-frpc/handlers" "super-frpc/utils"
"super-frpc/web"
) )
func setupRoutes() { func setupRoutes() {
systemLogHandler := postLog.NewLogSocketHandler(postLog.GetLogBroadcaster()) systemLogHandler := postLog.NewLogSocketHandler(postLog.GetLogBroadcaster())
http.HandleFunc("/system/getLogs", systemLogHandler.Handle) http.HandleFunc("/api/system/getLogs", systemLogHandler.Handle)
http.HandleFunc("/system/getStatus", GetStatusHandler) http.HandleFunc("/api/system/getStatus", GetStatusHandler)
http.HandleFunc("/system/getSoftwareInfo", GetSoftwareInfoHandler) http.HandleFunc("/api/system/getSoftwareInfo", GetSoftwareInfoHandler)
http.HandleFunc("/system/settings/get", handlers.GetSettingsHandler) http.HandleFunc("/api/system/settings/get", handlers.GetSettingsHandler)
http.HandleFunc("/system/settings/set", handlers.SetSettingsHandler) http.HandleFunc("/api/system/settings/set", handlers.SetSettingsHandler)
http.HandleFunc("/register", handlers.RegisterHandler) http.HandleFunc("/api/register", handlers.RegisterHandler)
http.HandleFunc("/login", handlers.LoginHandler) http.HandleFunc("/api/login", handlers.LoginHandler)
http.HandleFunc("/logout", handlers.LogoutHandler) http.HandleFunc("/api/logout", handlers.LogoutHandler)
http.HandleFunc("/userMgr/create", handlers.CreateUserHandler) http.HandleFunc("/api/userMgr/create", handlers.CreateUserHandler)
http.HandleFunc("/userMgr/remove", handlers.RemoveUserHandler) http.HandleFunc("/api/userMgr/remove", handlers.RemoveUserHandler)
http.HandleFunc("/userMgr/list", handlers.ListUserHandler) http.HandleFunc("/api/userMgr/list", handlers.ListUserHandler)
http.HandleFunc("/userMgr/modify", handlers.ModifyUserHandler) http.HandleFunc("/api/userMgr/modify", handlers.ModifyUserHandler)
http.HandleFunc("/userMgr/modifyType", handlers.ModifyUserTypeHandler) http.HandleFunc("/api/userMgr/modifyType", handlers.ModifyUserTypeHandler)
http.HandleFunc("/sessionMgr/list", handlers.ListActiveSessionsHandler) http.HandleFunc("/api/sessionMgr/list", handlers.ListActiveSessionsHandler)
http.HandleFunc("/sessionMgr/remove", handlers.RemoveSessionHandler) http.HandleFunc("/api/sessionMgr/remove", handlers.RemoveSessionHandler)
http.HandleFunc("/frpcAct/instanceMgr/create", handlers.CreateInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/create", handlers.CreateInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/delete", handlers.DeleteInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/delete", handlers.DeleteInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/modify", handlers.ModifyInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/modify", handlers.ModifyInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/list", handlers.ListInstancesHandler) http.HandleFunc("/api/frpcAct/instanceMgr/list", handlers.ListInstancesHandler)
http.HandleFunc("/frpcAct/instanceMgr/start", handlers.StartInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/start", handlers.StartInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/stop", handlers.StopInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/stop", handlers.StopInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/restart", handlers.RestartInstanceHandler) http.HandleFunc("/api/frpcAct/instanceMgr/restart", handlers.RestartInstanceHandler)
http.HandleFunc("/frpcAct/instanceMgr/status", handlers.GetInstanceStatusHandler) http.HandleFunc("/api/frpcAct/instanceMgr/status", handlers.GetInstanceStatusHandler)
http.HandleFunc("/frpcAct/instanceMgr/getInfo", handlers.GetInstanceInfoHandler) http.HandleFunc("/api/frpcAct/instanceMgr/getInfo", handlers.GetInstanceInfoHandler)
http.HandleFunc("/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(session.ValidateTokenFromMap).ServeHTTP) http.HandleFunc("/api/frpcAct/instanceMgr/logs", frpLogger.NewInstanceLogHandler(session.ValidateTokenFromMap).ServeHTTP)
http.HandleFunc("/frpcAct/proxyMgr/create", handlers.CreateProxyHandler) http.HandleFunc("/api/frpcAct/proxyMgr/create", handlers.CreateProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/modify", handlers.ModifyProxyHandler) http.HandleFunc("/api/frpcAct/proxyMgr/modify", handlers.ModifyProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/delete", handlers.DeleteProxyHandler) http.HandleFunc("/api/frpcAct/proxyMgr/delete", handlers.DeleteProxyHandler)
http.HandleFunc("/frpcAct/proxyMgr/list", handlers.ListProxiesHandler) http.HandleFunc("/api/frpcAct/proxyMgr/list", handlers.ListProxiesHandler)
http.HandleFunc("/", NotFoundHandler) http.HandleFunc("/", web.ServeStatic)
postLog.Info("Routes setup successfully") 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")
}
}