Files
verify/backend/cmd/main.go
T
admin 67a67517b0 feat: 添加重置管理员密码命令
用法:
  docker exec verify-app ./main reset-password admin          # 随机生成密码
  docker exec verify-app ./main reset-password admin newpass  # 指定新密码
  docker exec verify-app ./main help                          # 查看帮助
2026-05-03 12:08:32 +08:00

212 lines
5.4 KiB
Go

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
}