feat: 新增 GET /api/v1/profile 用户信息查询接口,支持前端凭据查询当前用户
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yourname/cyrene-ai/gateway/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/lib/pq"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -91,7 +93,7 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 存入 users 表
|
||||
_, err = store.CreateUser(h.db, req.Username, string(passwordHash), false)
|
||||
_, err = store.CreateUser(h.db, req.Username, req.Nickname, string(passwordHash), false)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败: " + err.Error()})
|
||||
return
|
||||
@@ -143,6 +145,7 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
}
|
||||
|
||||
var userID string
|
||||
var nickname string
|
||||
|
||||
// 尝试从 users 表查询用户
|
||||
authenticated, err := h.verifyUserPassword(req.Username, req.Password)
|
||||
@@ -158,6 +161,12 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
} else {
|
||||
userID = "user_" + req.Username
|
||||
}
|
||||
// 获取用户昵称
|
||||
if h.db != nil {
|
||||
if u, err := store.GetUserByUsername(h.db, req.Username); err == nil && u != nil {
|
||||
nickname = u.Nickname
|
||||
}
|
||||
}
|
||||
} else if req.Username == h.cfg.AdminUsername && h.db != nil {
|
||||
// 管理员用户尚未迁移到 users 表,尝试用配置中的密码验证
|
||||
if req.Password != h.cfg.AdminPassword {
|
||||
@@ -169,13 +178,14 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
if err != nil {
|
||||
logger.Printf("⚠ 迁移管理员密码哈希失败: %v", err)
|
||||
} else {
|
||||
if _, err := store.CreateUser(h.db, req.Username, string(passwordHash), true); err != nil {
|
||||
if _, err := store.CreateUser(h.db, req.Username, "管理员", string(passwordHash), true); err != nil {
|
||||
logger.Printf("⚠ 迁移管理员到 users 表失败: %v", err)
|
||||
} else {
|
||||
logger.Println("✅ 管理员已迁移到 users 表")
|
||||
}
|
||||
}
|
||||
userID = "admin"
|
||||
nickname = "管理员"
|
||||
} else if req.Username == h.cfg.AdminUsername {
|
||||
// 数据库不可用时的回退:使用配置中的管理员密码
|
||||
if req.Password != h.cfg.AdminPassword {
|
||||
@@ -183,6 +193,7 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
userID = "admin"
|
||||
nickname = "管理员"
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
|
||||
return
|
||||
@@ -203,6 +214,7 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user_id": userID,
|
||||
"nickname": nickname,
|
||||
"token": token,
|
||||
"refresh_token": refreshToken,
|
||||
"expires": time.Now().Add(h.cfg.JWTExpiryHours).Unix(),
|
||||
@@ -288,3 +300,63 @@ func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
"expires": time.Now().Add(h.cfg.JWTExpiryHours).Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetProfile 查询当前登录用户信息
|
||||
// GET /api/v1/profile
|
||||
func (h *AuthHandler) GetProfile(c *gin.Context) {
|
||||
userID := middleware.GetUserID(c)
|
||||
isAdmin := middleware.GetIsAdmin(c)
|
||||
|
||||
var username string
|
||||
var nickname string
|
||||
|
||||
if isAdmin {
|
||||
username = "admin"
|
||||
} else if strings.HasPrefix(userID, "user_") {
|
||||
username = strings.TrimPrefix(userID, "user_")
|
||||
} else {
|
||||
username = userID
|
||||
}
|
||||
|
||||
// 从数据库查询详细信息
|
||||
if h.db != nil {
|
||||
u, err := store.GetUserByUsername(h.db, username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
|
||||
return
|
||||
}
|
||||
if u != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user_id": userID,
|
||||
"username": u.Username,
|
||||
"nickname": u.Nickname,
|
||||
"is_admin": u.IsAdmin,
|
||||
"created_at": u.CreatedAt.UTC().Format(time.RFC3339),
|
||||
})
|
||||
return
|
||||
}
|
||||
// 用户不存在(可能 admin 未迁移或数据库问题)
|
||||
if !isAdmin {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 数据库不可用时的回退
|
||||
if isAdmin {
|
||||
nickname = h.cfg.AdminNickname
|
||||
if nickname == "" {
|
||||
nickname = "管理员"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user_id": userID,
|
||||
"username": username,
|
||||
"nickname": nickname,
|
||||
"is_admin": true,
|
||||
"created_at": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ func Setup(r *gin.Engine, hub *ws.Hub, cfg *config.Config, sessionStore *store.S
|
||||
// Token刷新
|
||||
protected.POST("/auth/refresh", authHandler.RefreshToken)
|
||||
|
||||
// 当前用户信息
|
||||
protected.GET("/profile", authHandler.GetProfile)
|
||||
|
||||
// 会话管理
|
||||
sessions := protected.Group("/sessions")
|
||||
{
|
||||
|
||||
@@ -107,6 +107,33 @@ Auth: JWT(可接受已过期的 token,或在 body 中提供 refresh_token)
|
||||
|
||||
---
|
||||
|
||||
### GET /profile — 查询当前用户
|
||||
|
||||
Auth: JWT。根据 token 返回当前登录用户的信息。
|
||||
|
||||
```json
|
||||
// 响应 200
|
||||
{
|
||||
"user_id": "admin",
|
||||
"username": "admin",
|
||||
"nickname": "叶酱",
|
||||
"is_admin": true,
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `user_id` | string | 用户 ID(admin 为 `"admin"`,普通用户为 `"user_<username>"`) |
|
||||
| `username` | string | 用户名 |
|
||||
| `nickname` | string | 显示昵称 |
|
||||
| `is_admin` | bool | 是否管理员 |
|
||||
| `created_at` | string | 注册时间 (RFC3339),数据库不可用时为 null |
|
||||
|
||||
错误: 401 `未提供认证令牌`, 404 `用户不存在`
|
||||
|
||||
---
|
||||
|
||||
## 2. WebSocket 实时通信
|
||||
|
||||
### GET /ws/chat — 建立连接
|
||||
|
||||
Reference in New Issue
Block a user