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) }