package main // 重要提示:请始终从 backend/ 目录运行此服务 // 正确的运行方式: // cd d:\Code\verify\verification-platform\backend // go run cmd/main.go // // 如果从其他目录运行,可能会导致数据库文件路径错误,造成数据丢失。 // // 嵌入前端静态文件: // 1. 编译前端:cd frontend-new && npm run build // 2. 将 dist 目录内容复制到 backend/embedded/dist // 3. 取消下方 embed 相关代码的注释 // 4. 重新编译后端 import ( "embed" "fmt" "io/fs" "log" "net/http" "strings" "verification-platform-backend/internal/config" "verification-platform-backend/internal/database" "verification-platform-backend/internal/middleware" "verification-platform-backend/internal/router" "verification-platform-backend/internal/service" "verification-platform-backend/pkg/logger" "github.com/gin-gonic/gin" ) //go:embed embedded/dist/* var embeddedFiles embed.FS // @title 网络验证平台API // @version 1.0 // @description 网络验证平台后端API接口文档 // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8080 // @BasePath /api/v1 // @securityDefinitions.apikey ApiKeyAuth // @in header // @name Authorization func main() { // 初始化配置 config.Init() // 初始化日志 logger.Init() // 初始化数据库 database.Init() // 设置Gin模式 if config.GetString("app.env") == "production" { gin.SetMode(gin.ReleaseMode) } // 创建Gin引擎 r := gin.Default() // 设置请求体大小限制 (32MB) r.MaxMultipartMemory = 32 << 20 // 添加中间件 r.Use(middleware.Logger()) r.Use(middleware.Recovery()) r.Use(middleware.Cors()) r.Use(func(c *gin.Context) { fmt.Printf("全局中间件: %s %s\n", c.Request.Method, c.Request.URL.Path) c.Next() }) // 静态文件服务 - 用于访问上传的图标 r.Static("/uploads", "uploads") // 设置路由 router.SetupRoutes(r) // 嵌入前端静态文件服务 setupEmbeddedFrontend(r) // 启动服务器 port := config.GetString("app.port") if port == "" { port = "8080" } log.Printf("Server starting on port %s", port) if err := r.Run(":" + port); err != nil { log.Fatal("Failed to start server: ", err) } } func setupEmbeddedFrontend(r *gin.Engine) { distFS, err := fs.Sub(embeddedFiles, "embedded/dist") if err != nil { log.Printf("Warning: embedded dist not found, frontend will not be served") return } indexHTML, err := fs.ReadFile(distFS, "index.html") if err != nil { log.Printf("Warning: index.html not found in embedded dist") return } r.NoRoute(func(c *gin.Context) { if c.Request.Method == "GET" && !strings.HasPrefix(c.Request.URL.Path, "/api/") { serveIndexHTML(c, string(indexHTML)) return } c.JSON(404, gin.H{"error": "not found"}) }) r.GET("/assets/*filepath", func(c *gin.Context) { c.FileFromFS(c.Request.URL.Path, http.FS(distFS)) }) } func serveIndexHTML(c *gin.Context, indexHTML string) { settingService := service.NewSettingService() settings, err := settingService.GetSettingsByCategory("basic") if err != nil { settings = make(map[string]interface{}) } siteName := getSettingString(settings, "siteName", "微授权") siteDescription := getSettingString(settings, "siteDescription", "专业的应用验证平台") siteKeywords := getSettingString(settings, "siteKeywords", "验证平台,软件授权,卡密验证") logoLight := getSettingString(settings, "logoLight", "") logoDark := getSettingString(settings, "logoDark", "") favicon := getSettingString(settings, "favicon", "") html := indexHTML html = strings.ReplaceAll(html, "{{.SiteName}}", siteName) html = strings.ReplaceAll(html, "{{.SiteDescription}}", siteDescription) html = strings.ReplaceAll(html, "{{.SiteKeywords}}", siteKeywords) html = strings.ReplaceAll(html, "{{.LogoLight}}", logoLight) html = strings.ReplaceAll(html, "{{.LogoDark}}", logoDark) html = strings.ReplaceAll(html, "{{.Favicon}}", favicon) c.Data(200, "text/html; charset=utf-8", []byte(html)) } func getSettingString(settings map[string]interface{}, key, defaultValue string) string { if val, ok := settings[key]; ok { if str, ok := val.(string); ok && str != "" { return str } } return defaultValue }