更新.gitignore文件,移除不必要的soul-api目录,确保版本控制的清晰性与一致性。
This commit is contained in:
152
soul-api/internal/handler/admin.go
Normal file
152
soul-api/internal/handler/admin.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"soul-api/internal/auth"
|
||||
"soul-api/internal/config"
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminCheck GET /api/admin 鉴权检查(JWT:Authorization Bearer 或 Cookie),已登录返回 success 或概览占位
|
||||
func AdminCheck(c *gin.Context) {
|
||||
cfg := config.Get()
|
||||
if cfg == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
return
|
||||
}
|
||||
token := auth.GetAdminJWTFromRequest(c.Request)
|
||||
if _, ok := auth.ParseAdminJWT(token, cfg.AdminSessionSecret); !ok {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"success": false, "error": "未授权访问,请先登录"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"content": gin.H{
|
||||
"totalChapters": 0, "totalWords": 0, "publishedChapters": 0, "draftChapters": 0,
|
||||
"lastUpdate": nil,
|
||||
},
|
||||
"payment": gin.H{
|
||||
"totalRevenue": 0, "todayRevenue": 0, "totalOrders": 0, "todayOrders": 0, "averagePrice": 0,
|
||||
},
|
||||
"referral": gin.H{
|
||||
"totalReferrers": 0, "activeReferrers": 0, "totalCommission": 0, "paidCommission": 0, "pendingCommission": 0,
|
||||
},
|
||||
"users": gin.H{
|
||||
"totalUsers": 0, "purchasedUsers": 0, "activeUsers": 0, "todayNewUsers": 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AdminLogin POST /api/admin 登录(优先校验 admin_users 表,表空时回退 ADMIN_USERNAME/PASSWORD 并自动初始化)
|
||||
func AdminLogin(c *gin.Context) {
|
||||
cfg := config.Get()
|
||||
if cfg == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "配置未加载"})
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "参数错误"})
|
||||
return
|
||||
}
|
||||
username := trimSpace(body.Username)
|
||||
password := body.Password
|
||||
db := database.DB()
|
||||
|
||||
// 1. 尝试从 admin_users 表校验
|
||||
var u model.AdminUser
|
||||
err := db.Where("username = ?", username).First(&u).Error
|
||||
if err == nil {
|
||||
if u.Status != "active" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "账号已禁用"})
|
||||
return
|
||||
}
|
||||
if bcryptErr := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)); bcryptErr != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
token, err := auth.IssueAdminJWT(cfg.AdminSessionSecret, u.Username, u.Role)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "签发失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"token": token,
|
||||
"user": gin.H{"id": u.ID, "username": u.Username, "role": u.Role, "name": u.Name},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 表内无匹配:若表为空且 env 账号正确,则创建初始 super_admin 并登录
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "系统错误"})
|
||||
return
|
||||
}
|
||||
if cfg.AdminUsername == "" || cfg.AdminPassword == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
if username != cfg.AdminUsername || password != cfg.AdminPassword {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
// 表为空时初始化超级管理员
|
||||
var cnt int64
|
||||
if db.Model(&model.AdminUser{}).Count(&cnt).Error != nil || cnt > 0 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "初始化失败"})
|
||||
return
|
||||
}
|
||||
initial := model.AdminUser{
|
||||
Username: cfg.AdminUsername,
|
||||
PasswordHash: string(hash),
|
||||
Role: "super_admin",
|
||||
Name: "卡若",
|
||||
Status: "active",
|
||||
}
|
||||
if err := db.Create(&initial).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "初始化失败"})
|
||||
return
|
||||
}
|
||||
token, err := auth.IssueAdminJWT(cfg.AdminSessionSecret, initial.Username, initial.Role)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "签发失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"token": token,
|
||||
"user": gin.H{"id": initial.ID, "username": initial.Username, "role": initial.Role, "name": initial.Name},
|
||||
})
|
||||
}
|
||||
|
||||
// AdminLogout POST /api/admin/logout 服务端无状态,仅返回成功;前端需清除本地 token
|
||||
func AdminLogout(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
func trimSpace(s string) string {
|
||||
start := 0
|
||||
for start < len(s) && (s[start] == ' ' || s[start] == '\t') {
|
||||
start++
|
||||
}
|
||||
end := len(s)
|
||||
for end > start && (s[end-1] == ' ' || s[end-1] == '\t') {
|
||||
end--
|
||||
}
|
||||
return s[start:end]
|
||||
}
|
||||
Reference in New Issue
Block a user