package common import ( "os" "strings" ) // webStaticDirs 候选前端静态目录(相对 server 工作目录,按序取第一个存在者): // 1. 本地开发:直接服务 Flutter 构建产物 app/build/web(scripts/build_web.sh 或 dev.sh 构建) // 2. Docker 镜像:Dockerfile web-builder 阶段产物(/app/web) var webStaticDirs = []string{ "../app/build/web", "web", } // WebStaticDir 返回可用的前端静态目录;全部不存在时返回 ""(仅 API 模式) func WebStaticDir() string { for _, dir := range webStaticDirs { if _, err := os.Stat(dir); err == nil { return dir } } return "" } // apiPathPrefixes 后端 API 路径前缀,需与 RouteRegister 控制器注册的 kebab-case 保持一致。 // web 静态资源与 history 路由的识别都依赖它:带扩展名的路径视为静态资源, // 无扩展名且命中 API 前缀的视为后端接口,其余视为前端 history 路由。 var apiPathPrefixes = []string{ "/user", "/user-photo", "/wardrobe", "/body-measurement", "/avatar", "/hairstyle", "/outfit", "/partner-store", "/member", "/ad", "/cps", "/workspace", "/api.json", } // IsAPIRequest 是否后端 API/受保护路径(不参与 web 静态与 history 回退) func IsAPIRequest(path string) bool { for _, p := range apiPathPrefixes { if path == p || strings.HasPrefix(path, p+"/") { return true } } return false } // IsWebAsset web 静态资源:根路径或末段带扩展名(main.dart.js / assets/* / favicon.png),免鉴权放行 func IsWebAsset(path string) bool { if path == "/" { return true } base := path[strings.LastIndex(path, "/")+1:] return strings.Contains(base, ".") } // IsWebHistoryRoute 前端 history 路由(无扩展名且非 API 路径),回退服务 index.html func IsWebHistoryRoute(path string) bool { return !IsWebAsset(path) && !IsAPIRequest(path) }