后端 - /api/usage/stats:当前用户按日聚合统计(请求数/输入/输出/缓存 tokens/费用) - /api/usage/logs:当前用户用量明细分页 - /api/admin/usage/logs:全量明细(分页 + 协议/状态/模型/用户筛选 + 用户名关联) - /api/admin/usage/summary:全量汇总(按用户分组) - dao:UsageFilter 支持筛选分页;DailyUsageDAO.ListAll - 路由加固:新增 middleware.AdminOnly,既有 /api/admin/* 全部迁移到 带管理员角色校验的分组(此前登录即可访问,属安全隐患) 前端 - 普通用户「用量统计」页:统计卡片 + 纯 CSS 每日请求量条形图 + 明细表格分页 - 管理后台「用量明细」页:汇总卡片 + 协议/状态/模型/用户筛选 + 明细表格 + 原始请求/响应查看弹窗(记录开关开启时) - stores/usage.ts 与类型定义;控制台/管理菜单挂载
73 lines
1.8 KiB
Go
73 lines
1.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"opencatd-open/internal/auth"
|
|
"opencatd-open/internal/store"
|
|
"opencatd-open/internal/pkg/jwt"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func Auth(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
authToken := c.GetHeader("Authorization")
|
|
if authToken == "" {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
|
"code": http.StatusUnauthorized,
|
|
"error": "未提供认证信息",
|
|
})
|
|
return
|
|
}
|
|
if len(authToken) > 7 {
|
|
authToken = authToken[7:]
|
|
}
|
|
claim, err := jwt.ValidateToken(authToken, auth.GetSecretKey())
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
|
"code": http.StatusUnauthorized,
|
|
"error": "无效的认证信息",
|
|
})
|
|
return
|
|
}
|
|
var user store.User
|
|
if err := db.First(&user, claim.UserID).Error; err != nil {
|
|
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
|
"code": http.StatusUnauthorized,
|
|
"error": "无效的认证信息",
|
|
})
|
|
return
|
|
}
|
|
c.Set("user", &user)
|
|
c.Set("user_id", claim.UserID)
|
|
c.Set("user_role", user.Role)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func CheckRole(role string) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
userRole, _ := c.Get("user_role")
|
|
if roleStr, ok := userRole.(string); !ok || roleStr != role {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "权限不足"})
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// AdminOnly 管理后台中间件:要求 user_role 为 admin。
|
|
// 由 middleware.Auth 先行设置 user_role;缺失时拒绝。
|
|
func AdminOnly() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
role, _ := c.Get("user_role")
|
|
roleStr, _ := role.(string)
|
|
if roleStr != store.RoleAdmin {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|