feat(routers): 平台后台 SPA 由 Go 自己吐,绕开 nginx 反代缺失
This commit is contained in:
1 parent
d48c012e68
commit
5718a00e31
4 files changed
+106
No files matched your search
@@ -2,6 +2,16 @@
|
|||||||
httpport = 9000
|
httpport = 9000
|
||||||
runmode = dev
|
runmode = dev
|
||||||
|
|
||||||
|
# ==================== 平台后台 SPA 静态托管 ====================
|
||||||
|
# 之前 platform.yunzer.cn 的 SPA 静态文件依赖 nginx 在对应 server 块里
|
||||||
|
# 配 location ~ ^/(platform|backend|auth|api|app)/ { proxy_pass ... } 反代到 Go,
|
||||||
|
# 而 nginx 默认 server 块经常只有 root + static,缺这条就会让所有 API 请求 404。
|
||||||
|
# 现在由 Go 自己吐这些 host 的 SPA 静态;/platform|/backend|/auth|/api|/app
|
||||||
|
# 仍走 beego Router;/uploads|/themes 走 main.go 注册的 SetStaticPath。
|
||||||
|
# 多 host 用英文逗号分隔;目录留空则关闭该功能,回落到由 nginx 自行处理。
|
||||||
|
platform_spa_dir = /www/wwwroot/platform.yunzer.cn/output
|
||||||
|
platform_spa_hosts = platform.yunzer.cn
|
||||||
|
|
||||||
# 启用请求体复制(允许多次读取请求体)
|
# 启用请求体复制(允许多次读取请求体)
|
||||||
copyrequestbody = true
|
copyrequestbody = true
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -2,6 +2,7 @@ package routers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"server/controllers"
|
"server/controllers"
|
||||||
"server/middleware"
|
"server/middleware"
|
||||||
@@ -87,4 +88,18 @@ func init() {
|
|||||||
platform.Register()
|
platform.Register()
|
||||||
api.Register()
|
api.Register()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 平台后台 SPA 静态托管(详见 routers/spa.go 注释)。
|
||||||
|
// 由 app.conf 的 platform_spa_dir / platform_spa_hosts 控制;任一为空则不启用。
|
||||||
|
if spaDir, _ := beego.AppConfig.String("platform_spa_dir"); strings.TrimSpace(spaDir) != "" {
|
||||||
|
var hosts []string
|
||||||
|
if raw, _ := beego.AppConfig.String("platform_spa_hosts"); raw != "" {
|
||||||
|
for _, h := range strings.Split(raw, ",") {
|
||||||
|
if t := strings.TrimSpace(h); t != "" {
|
||||||
|
hosts = append(hosts, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegisterPlatformSPA(spaDir, hosts...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
beego "github.com/beego/beego/v2/server/web"
|
||||||
|
"github.com/beego/beego/v2/server/web/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterPlatformSPA 让 Go 自己吐指定 hosts 的平台后台 SPA 静态资源,
|
||||||
|
// 替代 nginx 在对应 server 块里的
|
||||||
|
//
|
||||||
|
// location ~ ^/(platform|backend|auth|api|app)/ { proxy_pass http://127.0.0.1:8081; ... }
|
||||||
|
//
|
||||||
|
// 那段反代规则(曾因宝塔重生成 vhost 被冲掉而出现 "页面 404")。
|
||||||
|
//
|
||||||
|
// 规则:
|
||||||
|
// - 仅对 hosts 列表里的 host 生效;
|
||||||
|
// - 这些前缀直接放行交给 beego Router / Static 处理,不接管:
|
||||||
|
// /platform/、/backend/、/auth/、/api/、/app/ → 业务 API
|
||||||
|
// /uploads/、/themes/ → main.go SetStaticPath 注册的静态目录
|
||||||
|
// /.well-known/ → ACME 证书验证目录
|
||||||
|
// - 其余请求从 spaDir 直接 ServeFile;
|
||||||
|
// - 文件不存在时回 index.html(SPA hash 路由 fallback)。
|
||||||
|
//
|
||||||
|
// 调用方在 router.go init() 里读 app.conf 的 platform_spa_dir / platform_spa_hosts。
|
||||||
|
// spaDir 为空或 hosts 为空则视为关闭功能,行为不变。
|
||||||
|
func RegisterPlatformSPA(spaDir string, hosts ...string) {
|
||||||
|
spaDir = strings.TrimSpace(spaDir)
|
||||||
|
if spaDir == "" || len(hosts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hostSet := make(map[string]struct{}, len(hosts))
|
||||||
|
for _, h := range hosts {
|
||||||
|
h = strings.ToLower(strings.TrimSpace(h))
|
||||||
|
if h != "" {
|
||||||
|
hostSet[h] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
passThrough := []string{
|
||||||
|
"/platform/", "/backend/", "/auth/", "/api/", "/app/",
|
||||||
|
"/uploads/", "/themes/",
|
||||||
|
"/.well-known/",
|
||||||
|
}
|
||||||
|
|
||||||
|
beego.InsertFilter("*", beego.BeforeStatic, func(ctx *context.Context) {
|
||||||
|
host := strings.ToLower(ctx.Request.Host)
|
||||||
|
if i := strings.IndexByte(host, ':'); i > 0 {
|
||||||
|
host = host[:i]
|
||||||
|
}
|
||||||
|
if _, ok := hostSet[host]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := ctx.Request.URL.Path
|
||||||
|
for _, p := range passThrough {
|
||||||
|
if strings.HasPrefix(path, p) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态资源或入口
|
||||||
|
rel := path
|
||||||
|
if rel == "" || rel == "/" {
|
||||||
|
rel = "/index.html"
|
||||||
|
}
|
||||||
|
target := spaDir + rel
|
||||||
|
|
||||||
|
if f, err := os.Stat(target); err == nil && !f.IsDir() {
|
||||||
|
http.ServeFile(ctx.ResponseWriter, ctx.Request, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPA fallback:未知路径(hash 路由直接访问 /xxx/yyy)回 index.html
|
||||||
|
http.ServeFile(ctx.ResponseWriter, ctx.Request, spaDir+"/index.html")
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in new issue
Block a user