Files
OpenFlare/internal/router/frontend_embedded.go
T
2026-06-08 20:38:17 +08:00

122 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build embed_frontend
/*
Copyright 2026 Arctel.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"embed"
"io"
"io/fs"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
//go:embed all:dist
var frontendFS embed.FS
func serveFileDirect(c *gin.Context, subFS fs.FS, filePath string) bool {
file, err := subFS.Open(filePath)
if err != nil {
return false
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
return false
}
if stat.IsDir() {
return false
}
seeker, ok := file.(io.ReadSeeker)
if !ok {
return false
}
// 使用 http.ServeContent 直接输出文件内容,不进行路径规范化重定向
http.ServeContent(c.Writer, c.Request, filePath, stat.ModTime(), seeker)
return true
}
func registerFrontend(r *gin.Engine) {
subFS, err := fs.Sub(frontendFS, "dist")
if err != nil {
panic(err)
}
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
// API 接口路由或文件服务路由 -> 直接返回,由 Gin 处理标准 404
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/f/") {
return
}
// 只处理 GET 和 HEAD 请求
if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead {
c.JSON(http.StatusMethodNotAllowed, gin.H{"error_msg": "Method not allowed"})
return
}
// 移除开头的斜杠以在嵌入文件系统中查找
cleanPath := strings.TrimPrefix(path, "/")
// 1. 根路径 -> 直接输出 index.html
if cleanPath == "" {
if serveFileDirect(c, subFS, "index.html") {
return
}
}
// 2. 精确匹配(如果对应的文件存在,直接输出)
if serveFileDirect(c, subFS, cleanPath) {
return
}
// 如果是个目录(例如请求了 "/login",同时 dist 目录下存在一个叫 "login" 的文件夹目录),
// 则查找是否有对应的 ".html" 文件(例如 "login.html")并进行输出。
if cleanPath != "" {
htmlPath := cleanPath + ".html"
if serveFileDirect(c, subFS, htmlPath) {
return
}
}
// 3. Next.js Clean URLs 兜底逻辑(例如访问 /settings/security -> 实际映射输出 settings/security.html)
if !strings.Contains(cleanPath, ".") {
htmlPath := cleanPath + ".html"
if serveFileDirect(c, subFS, htmlPath) {
return
}
indexPath := cleanPath + "/index.html"
if serveFileDirect(c, subFS, indexPath) {
return
}
}
// 4. 单页应用(SPA)前端路由兜底:返回 index.html
if serveFileDirect(c, subFS, "index.html") {
return
}
})
}