86 lines
2.4 KiB
Go
86 lines
2.4 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"oci-portal/internal/service"
|
|
)
|
|
|
|
// sessionHandler 处理活跃会话查看与定点撤销接口。
|
|
type sessionHandler struct {
|
|
auth *service.AuthService
|
|
}
|
|
|
|
// bearerToken 取 Authorization 头中的原始令牌串;不存在返回空串。
|
|
func bearerToken(c *gin.Context) string {
|
|
token, ok := strings.CutPrefix(c.GetHeader("Authorization"), "Bearer ")
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return token
|
|
}
|
|
|
|
// sessionMetaOf 采集会话登记所需的客户端上下文;登录方式由 service 层填写。
|
|
func sessionMetaOf(c *gin.Context) service.SessionMeta {
|
|
return service.SessionMeta{
|
|
ClientIP: requestIP(c),
|
|
UserAgent: truncateLogField(c.Request.UserAgent(), 256),
|
|
}
|
|
}
|
|
|
|
// list 列出当前账号的活跃会话,最近活跃在前,当前会话带 current 标记。
|
|
//
|
|
// @Summary 活跃会话列表
|
|
// @Tags 认证
|
|
// @Success 200 {object} itemsResponse[service.SessionInfo] "items"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/auth/sessions [get]
|
|
func (h *sessionHandler) list(c *gin.Context) {
|
|
items, err := h.auth.ListSessions(c.Request.Context(), c.GetString(usernameKey), bearerToken(c))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"items": items})
|
|
}
|
|
|
|
// revoke 定点撤销一个其他会话;该会话令牌随即失效,不影响其余会话。
|
|
//
|
|
// @Summary 撤销单个会话
|
|
// @Tags 认证
|
|
// @Param id path int true "会话 ID"
|
|
// @Success 204 "已撤销"
|
|
// @Failure 400 {object} errorResponse "id 非法"
|
|
// @Failure 404 {object} errorResponse "会话不存在"
|
|
// @Failure 409 {object} errorResponse "不能撤销当前会话"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/auth/sessions/{id} [delete]
|
|
func (h *sessionHandler) revoke(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
|
|
return
|
|
}
|
|
err = h.auth.RevokeSession(c.Request.Context(), c.GetString(usernameKey), bearerToken(c), uint(id))
|
|
if errors.Is(err, service.ErrSessionCurrent) {
|
|
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "会话不存在或已失效"})
|
|
return
|
|
}
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|