package main import ( "embed" "fmt" "io/fs" "log" "net/http" "os" "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" "golang.org/x/crypto/bcrypt" ) //go:embed embedded/dist/* var embeddedFiles embed.FS func main() { if len(os.Args) > 1 { switch os.Args[1] { case "reset-password": resetPassword() case "help", "-h", "--help": printHelp() default: fmt.Printf("未知命令: %s\n", os.Args[1]) printHelp() os.Exit(1) } return } startServer() } func printHelp() { fmt.Println("用法:") fmt.Println(" verify 启动服务器") fmt.Println(" verify reset-password <用户名> [新密码] 重置管理员密码") fmt.Println(" verify help 显示帮助信息") fmt.Println() fmt.Println("示例:") fmt.Println(" verify reset-password admin 随机生成新密码") fmt.Println(" verify reset-password admin newpass 设置指定密码") } func resetPassword() { config.Init() database.Init() if len(os.Args) < 3 { fmt.Println("用法: verify reset-password <用户名> [新密码]") os.Exit(1) } username := os.Args[2] newPassword := "" if len(os.Args) >= 4 { newPassword = os.Args[3] } var user struct { ID uint Username string Role string } if err := database.DB.Table("users").Where("username = ? AND role = ?", username, "admin").First(&user).Error; err != nil { fmt.Printf("错误: 未找到管理员用户 '%s'\n", username) os.Exit(1) } if newPassword == "" { newPassword = generateRandomPassword(12) } if len(newPassword) < 6 { fmt.Println("错误: 密码长度至少6位") os.Exit(1) } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { fmt.Printf("错误: 密码加密失败 - %v\n", err) os.Exit(1) } if err := database.DB.Table("users").Where("id = ?", user.ID).Update("password", string(hashedPassword)).Error; err != nil { fmt.Printf("错误: 更新密码失败 - %v\n", err) os.Exit(1) } fmt.Printf("成功: 用户 '%s' 的密码已重置\n", username) fmt.Printf("新密码: %s\n", newPassword) } func generateRandomPassword(length int) string { const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%" result := make([]byte, length) for i := range result { result[i] = chars[i%len(chars)] } for i := len(result) - 1; i > 0; i-- { j := i % len(chars) result[i], result[j] = result[j], result[i] } return string(result) } func startServer() { config.Init() logger.Init() database.Init() if config.GetString("app.env") == "production" { gin.SetMode(gin.ReleaseMode) } r := gin.Default() 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 }