feat: 添加重置管理员密码命令
用法: docker exec verify-app ./main reset-password admin # 随机生成密码 docker exec verify-app ./main reset-password admin newpass # 指定新密码 docker exec verify-app ./main help # 查看帮助
This commit is contained in:
+95
-48
@@ -1,24 +1,12 @@
|
|||||||
package main
|
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 (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"verification-platform-backend/internal/config"
|
"verification-platform-backend/internal/config"
|
||||||
"verification-platform-backend/internal/database"
|
"verification-platform-backend/internal/database"
|
||||||
@@ -28,51 +16,116 @@ import (
|
|||||||
"verification-platform-backend/pkg/logger"
|
"verification-platform-backend/pkg/logger"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed embedded/dist/*
|
//go:embed embedded/dist/*
|
||||||
var embeddedFiles embed.FS
|
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() {
|
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()
|
config.Init()
|
||||||
|
|
||||||
// 初始化日志
|
|
||||||
logger.Init()
|
|
||||||
|
|
||||||
// 初始化数据库
|
|
||||||
database.Init()
|
database.Init()
|
||||||
|
|
||||||
// 设置Gin模式
|
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" {
|
if config.GetString("app.env") == "production" {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建Gin引擎
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
// 设置请求体大小限制 (32MB)
|
|
||||||
r.MaxMultipartMemory = 32 << 20
|
r.MaxMultipartMemory = 32 << 20
|
||||||
|
|
||||||
// 添加中间件
|
|
||||||
r.Use(middleware.Logger())
|
r.Use(middleware.Logger())
|
||||||
r.Use(middleware.Recovery())
|
r.Use(middleware.Recovery())
|
||||||
r.Use(middleware.Cors())
|
r.Use(middleware.Cors())
|
||||||
@@ -82,16 +135,10 @@ func main() {
|
|||||||
c.Next()
|
c.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 静态文件服务 - 用于访问上传的图标
|
|
||||||
r.Static("/uploads", "uploads")
|
r.Static("/uploads", "uploads")
|
||||||
|
|
||||||
// 设置路由
|
|
||||||
router.SetupRoutes(r)
|
router.SetupRoutes(r)
|
||||||
|
|
||||||
// 嵌入前端静态文件服务
|
|
||||||
setupEmbeddedFrontend(r)
|
setupEmbeddedFrontend(r)
|
||||||
|
|
||||||
// 启动服务器
|
|
||||||
port := config.GetString("app.port")
|
port := config.GetString("app.port")
|
||||||
if port == "" {
|
if port == "" {
|
||||||
port = "8080"
|
port = "8080"
|
||||||
|
|||||||
Reference in New Issue
Block a user