feat(bj_power_mes): 嵌入前端静态资源并配置SPA路由回退
实现将前端构建产物打包进二进制文件,同时配置兜底路由支持Vue Router的history模式,让8888端口可以同时提供API服务和前端页面访问
This commit is contained in:
@@ -4,9 +4,13 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"bj_power_mes/common/logx"
|
"bj_power_mes/common/logx"
|
||||||
@@ -22,6 +26,12 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/rest"
|
"github.com/zeromicro/go-zero/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 前端静态资源(Vue 构建产物:frontend/ npm run build 输出到 public/)。
|
||||||
|
// 8888 一个端口同时提供管理后台页面与 API。
|
||||||
|
//
|
||||||
|
//go:embed all:public
|
||||||
|
var webFS embed.FS
|
||||||
|
|
||||||
var configFile = flag.String("f", "etc/bj_power_mes-api.yaml", "the config file")
|
var configFile = flag.String("f", "etc/bj_power_mes-api.yaml", "the config file")
|
||||||
|
|
||||||
// promptPassword 从终端读取一行输入(不隐藏回显,用于运维终端操作)
|
// promptPassword 从终端读取一行输入(不隐藏回显,用于运维终端操作)
|
||||||
@@ -116,6 +126,30 @@ func runResetAll(c config.Config) {
|
|||||||
fmt.Println("reset-all: 已清空并重建全部表,写入基础数据(管理员 admin/123456)。")
|
fmt.Println("reset-all: 已清空并重建全部表,写入基础数据(管理员 admin/123456)。")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newStaticHandler 嵌入式静态文件:命中则直接返回;未命中的非 /api 路径
|
||||||
|
// 回退到 index.html(SPA history 路由),由浏览器端 Vue Router 接管。
|
||||||
|
func newStaticHandler(fsys embed.FS) http.Handler {
|
||||||
|
sub, err := fs.Sub(fsys, "public")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("嵌入静态目录不可用: %v", err))
|
||||||
|
}
|
||||||
|
fileServer := http.FileServer(http.FS(sub))
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := strings.TrimPrefix(path.Clean(r.URL.Path), "/")
|
||||||
|
if name == "" {
|
||||||
|
name = "index.html"
|
||||||
|
}
|
||||||
|
if f, openErr := sub.Open(name); openErr == nil {
|
||||||
|
f.Close()
|
||||||
|
fileServer.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r2 := r.Clone(r.Context())
|
||||||
|
r2.URL.Path = "/"
|
||||||
|
fileServer.ServeHTTP(w, r2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@@ -158,7 +192,19 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
server := rest.MustNewServer(c.RestConf)
|
staticHandler := newStaticHandler(webFS)
|
||||||
|
|
||||||
|
// 兜底路由:已注册的 /api/* 走正常匹配;未注册的非 /api 路径走嵌入式静态资源
|
||||||
|
//(/api 下未注册的路径仍返回 404,不回退页面)
|
||||||
|
notFound := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/api" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
staticHandler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(notFound))
|
||||||
defer server.Stop()
|
defer server.Stop()
|
||||||
|
|
||||||
ctx := svc.NewServiceContext(c)
|
ctx := svc.NewServiceContext(c)
|
||||||
|
|||||||
Reference in New Issue
Block a user