93 lines
2.7 KiB
Go
93 lines
2.7 KiB
Go
|
|
package handler
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"net/http"
|
|||
|
|
|
|||
|
|
"soul-api/internal/auth"
|
|||
|
|
"soul-api/internal/config"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 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_USERNAME/PASSWORD,返回 JWT,前端存 token 并带 Authorization: Bearer)
|
|||
|
|
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
|
|||
|
|
if username != cfg.AdminUsername || password != cfg.AdminPassword {
|
|||
|
|
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "用户名或密码错误"})
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
token, err := auth.IssueAdminJWT(cfg.AdminSessionSecret, username)
|
|||
|
|
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": "admin", "username": cfg.AdminUsername, "role": "admin", "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]
|
|||
|
|
}
|