diff --git a/.gitignore b/.gitignore index 70ea303..e86769b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,107 +1,38 @@ # 编译后的二进制文件 NetworkAuth NetworkAuth.exe -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Go 相关 -*.test -*.out -go.work -go.work.sum # 数据库文件 -/database.db -/recharge.db *.db -*.sqlite -*.sqlite3 +*.db-shm +*.db-wal # 配置文件 (包含敏感信息) config.json -config.yaml -config.yml -.env -.env.local -.env.production # 日志文件 logs/ *.log -log/ -# 临时文件 -*.tmp -*.temp -*.swp -*.swo -*~ -node.txt -frontend +# 前端工程源码 (独立维护) +frontend/ + +# 升级/迁移临时缓存目录 +更新内容/ # IDE 和编辑器 -.vscode/settings.json -.vscode/tasks.json -.vscode/extensions.json -# .vscode/launch.json # 保留调试配置 +.vscode/ +!.vscode/launch.json .idea/ -*.iml -*.ipr -*.iws -.project -.classpath -.settings/ -*.sublime-project -*.sublime-workspace # 操作系统 .DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db -Desktop.ini # 依赖和包管理 vendor/ node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -# 测试覆盖率 -coverage.txt -coverage.out -*.cover -*.coverprofile - -# 文档生成 -docs/_build/ -site/ - -# 备份文件 -*.bak -*.backup -*.orig - -# 压缩文件 -*.zip -*.tar.gz -*.rar -*.7z - -# 证书和密钥文件 -*.pem -*.key -*.crt -*.p12 -*.pfx - -# 运行时文件 -*.pid -*.sock \ No newline at end of file +# 允许提交 Go embed 依赖的静态资源 +# 屏蔽构建的前端产物 +public/dist/ +public/dist/** diff --git a/public/public.go b/public/public.go new file mode 100644 index 0000000..e94146c --- /dev/null +++ b/public/public.go @@ -0,0 +1,6 @@ +package public + +import "embed" + +//go:embed all:dist +var Public embed.FS diff --git a/server/routes.go b/server/routes.go index f107d0c..b2fb43f 100644 --- a/server/routes.go +++ b/server/routes.go @@ -1,15 +1,94 @@ package server import ( + "NetworkAuth/public" + "io" + "io/fs" + "net/http" + "strings" + "github.com/gin-gonic/gin" ) // RegisterRoutes 聚合注册所有路由 func RegisterRoutes(r *gin.Engine) { - // 所有路由基于 /api + // 1. 所有接口路由基于 /api apiGroup := r.Group("/api") - RegisterInstallRoutes(apiGroup) RegisterDefaultRoutes(apiGroup) RegisterAdminRoutes(apiGroup) + + // 2. 注册前端静态资源及兜底路由 + registerFrontendRoutes(r) +} + +// registerFrontendRoutes 注册前端静态资源及兜底路由 +func registerFrontendRoutes(r *gin.Engine) { + // 提取嵌入的 dist 目录 + distFS, err := fs.Sub(public.Public, "dist") + if err != nil { + panic("Failed to initialize embedded static files: " + err.Error()) + } + + // 挂载静态资源目录 (如 assets) + // 根据 Vue 构建产物,通常有 /assets 或 /static 目录,这里我们直接把整个 distFS 映射到根路由 + // 但为了避免与 /api 冲突,我们可以使用中间件和 NoRoute 来处理兜底 + + // 提供静态文件服务器 + fileServer := http.FileServer(http.FS(distFS)) + + // 拦截并处理静态资源请求 + r.Use(func(c *gin.Context) { + path := c.Request.URL.Path + // 如果是 API 请求,直接放行 + if strings.HasPrefix(path, "/api") { + c.Next() + return + } + + // 检查静态文件中是否存在该路径 + // 移除开头的 "/" + cleanPath := strings.TrimPrefix(path, "/") + if cleanPath == "" { + cleanPath = "index.html" + } + + // 尝试在嵌入的文件系统中查找文件 + if _, err := fs.Stat(distFS, cleanPath); err == nil { + // 文件存在,交由 FileServer 处理 + // 设置一些常见的缓存头 + if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/assets/") { + c.Header("Cache-Control", "public, max-age=31536000") + } + fileServer.ServeHTTP(c.Writer, c.Request) + c.Abort() + return + } + + c.Next() + }) + + // SPA 前端路由兜底 (处理 History 模式) + r.NoRoute(func(c *gin.Context) { + // 如果是 API 请求找不到路由,返回 404 JSON + if strings.HasPrefix(c.Request.URL.Path, "/api") { + c.JSON(http.StatusNotFound, gin.H{ + "code": 404, + "msg": "API Not Found", + }) + return + } + + // 其他所有非 API 请求,都返回 index.html 交给前端 Vue Router 处理 + c.Header("Content-Type", "text/html; charset=utf-8") + indexFile, err := distFS.Open("index.html") + if err != nil { + c.String(http.StatusInternalServerError, "Failed to load index.html") + return + } + defer indexFile.Close() + + stat, _ := indexFile.Stat() + http.ServeContent(c.Writer, c.Request, "index.html", stat.ModTime(), indexFile.(io.ReadSeeker)) + }) }