docs: add Swagger annotations to 13 API handlers
Added @Summary, @Description, @Tags, @Param, @Success, @Failure, @Router annotations to all major handler endpoints for OpenAPI/Swagger auto-generation. Covers 86 annotations across: - auth_handler.go (25): all auth endpoints - user_handler.go (14): CRUD + roles + admin management - device_handler.go (13): device CRUD + trust management - role_handler.go (8): role CRUD + permissions - custom_field_handler.go (7): field CRUD + user values - permission_handler.go (7): permission CRUD + tree - log_handler.go (3): login/operation logs - captcha_handler.go (3): generate/verify - stats_handler.go (2): dashboard + user stats - avatar_handler.go (1): upload avatar - totp_handler.go (1): totp status - password_reset_handler.go (1): forgot password Partially addresses P2: missing Swagger annotations (PRODUCTION_GAP_ANALYSIS_2026-04-08)
This commit is contained in:
@@ -30,6 +30,17 @@ func NewAuthHandler(authService *service.AuthService) *AuthHandler {
|
||||
return &AuthHandler{authService: authService}
|
||||
}
|
||||
|
||||
// Register 用户注册
|
||||
// @Summary 用户注册
|
||||
// @Description 用户注册新账号,支持用户名+密码或手机号注册
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body service.RegisterRequest true "注册请求"
|
||||
// @Success 201 {object} Response{data=service.UserInfo} "注册成功"
|
||||
// @Failure 400 {object} Response{code=int,message=string} "请求参数错误"
|
||||
// @Failure 409 {object} Response{code=int,message=string} "用户已存在"
|
||||
// @Router /api/v1/auth/register [post]
|
||||
func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
@@ -65,6 +76,18 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
// @Summary 用户登录
|
||||
// @Description 用户使用账号密码登录,支持多种认证方式(用户名/邮箱/手机号)
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body service.LoginRequest true "登录请求"
|
||||
// @Success 200 {object} Response{data=service.LoginResponse} "登录成功"
|
||||
// @Failure 400 {object} Response{code=int,message=string} "请求参数错误"
|
||||
// @Failure 401 {object} Response{code=int,message=string} "认证失败"
|
||||
// @Failure 429 {object} Response{code=int,message=string} "登录尝试过多"
|
||||
// @Router /api/v1/auth/login [post]
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req struct {
|
||||
Account string `json:"account"`
|
||||
@@ -109,6 +132,16 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// Logout 用户登出
|
||||
// @Summary 用户登出
|
||||
// @Description 使当前 access_token 和 refresh_token 失效
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param request body service.LogoutRequest false "登出请求(token可从header获取)"
|
||||
// @Success 200 {object} Response{code=int,message=string} "登出成功"
|
||||
// @Router /api/v1/auth/logout [post]
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
var req struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
@@ -136,6 +169,17 @@ func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "logged out"})
|
||||
}
|
||||
|
||||
// RefreshToken 刷新访问令牌
|
||||
// @Summary 刷新访问令牌
|
||||
// @Description 使用 refresh_token 获取新的 access_token
|
||||
// @Tags 认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body RefreshTokenRequest true "刷新令牌请求"
|
||||
// @Success 200 {object} Response{data=service.LoginResponse} "刷新成功"
|
||||
// @Failure 400 {object} Response{code=int,message=string} "请求参数错误"
|
||||
// @Failure 401 {object} Response{code=int,message=string} "refresh_token无效或已过期"
|
||||
// @Router /api/v1/auth/refresh-token [post]
|
||||
func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
var req struct {
|
||||
RefreshToken string `json:"refresh_token" binding:"required"`
|
||||
@@ -159,6 +203,15 @@ func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserInfo 获取当前用户信息
|
||||
// @Summary 获取当前用户信息
|
||||
// @Description 获取已登录用户的详细信息
|
||||
// @Tags 认证
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} Response{data=service.UserInfo} "用户信息"
|
||||
// @Failure 401 {object} Response{code=int,message=string} "未认证"
|
||||
// @Router /api/v1/auth/userinfo [get]
|
||||
func (h *AuthHandler) GetUserInfo(c *gin.Context) {
|
||||
userID, ok := getUserIDFromContext(c)
|
||||
if !ok {
|
||||
@@ -179,6 +232,13 @@ func (h *AuthHandler) GetUserInfo(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetCSRFToken 获取CSRF令牌
|
||||
// @Summary 获取CSRF令牌
|
||||
// @Description 由于系统使用JWT Bearer Token认证,不存在CSRF风险,返回空token
|
||||
// @Tags 认证
|
||||
// @Produce json
|
||||
// @Success 200 {object} map "CSRF token(为空)"
|
||||
// @Router /api/v1/auth/csrf-token [get]
|
||||
func (h *AuthHandler) GetCSRFToken(c *gin.Context) {
|
||||
// 系统使用 JWT Bearer Token 认证,Bearer Token 不会被浏览器自动携带(非 cookie)
|
||||
// 因此不存在传统意义上的 CSRF 风险,此端点返回空 token 作为兼容响应
|
||||
@@ -188,6 +248,13 @@ func (h *AuthHandler) GetCSRFToken(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuthCapabilities 获取认证能力
|
||||
// @Summary 获取系统认证能力
|
||||
// @Description 返回系统支持的认证方式和配置(如是否需要邮件激活、是否支持OAuth等)
|
||||
// @Tags 认证
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response{data=service.AuthCapabilities} "认证能力配置"
|
||||
// @Router /api/v1/auth/capabilities [get]
|
||||
func (h *AuthHandler) GetAuthCapabilities(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
caps := h.authService.GetAuthCapabilities(ctx)
|
||||
@@ -198,23 +265,65 @@ func (h *AuthHandler) GetAuthCapabilities(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// OAuthLogin OAuth登录初始化
|
||||
// @Summary OAuth登录初始化
|
||||
// @Description 发起OAuth登录流程(当前未配置)
|
||||
// @Tags OAuth
|
||||
// @Produce json
|
||||
// @Param provider path string true "OAuth提供商(如 github, google)"
|
||||
// @Success 200 {object} Response "OAuth未配置"
|
||||
// @Router /api/v1/auth/oauth/{provider} [get]
|
||||
func (h *AuthHandler) OAuthLogin(c *gin.Context) {
|
||||
provider := c.Param("provider")
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "OAuth not configured", "data": gin.H{"provider": provider}})
|
||||
}
|
||||
|
||||
// OAuthCallback OAuth回调
|
||||
// @Summary OAuth回调处理
|
||||
// @Description 处理OAuth provider回调(当前未配置)
|
||||
// @Tags OAuth
|
||||
// @Produce json
|
||||
// @Param provider path string true "OAuth提供商"
|
||||
// @Success 200 {object} Response "OAuth未配置"
|
||||
// @Router /api/v1/auth/oauth/{provider}/callback [get]
|
||||
func (h *AuthHandler) OAuthCallback(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "OAuth not configured"})
|
||||
}
|
||||
|
||||
// OAuthExchange OAuth令牌交换
|
||||
// @Summary OAuth令牌交换
|
||||
// @Description 使用OAuth code交换access_token(当前未配置)
|
||||
// @Tags OAuth
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param provider path string true "OAuth提供商"
|
||||
// @Success 200 {object} Response "OAuth未配置"
|
||||
// @Router /api/v1/auth/oauth/{provider}/exchange [post]
|
||||
func (h *AuthHandler) OAuthExchange(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "OAuth not configured"})
|
||||
}
|
||||
|
||||
// GetEnabledOAuthProviders 获取已启用的OAuth提供商
|
||||
// @Summary 获取OAuth提供商列表
|
||||
// @Description 返回系统已配置并启用的OAuth提供商列表
|
||||
// @Tags OAuth
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response{data=map} "提供商列表"
|
||||
// @Router /api/v1/auth/oauth/providers [get]
|
||||
func (h *AuthHandler) GetEnabledOAuthProviders(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": gin.H{"providers": []string{}}})
|
||||
}
|
||||
|
||||
// ActivateEmail 激活邮箱
|
||||
// @Summary 激活用户邮箱
|
||||
// @Description 使用邮箱激活token激活用户账号
|
||||
// @Tags 邮箱认证
|
||||
// @Produce json
|
||||
// @Param token query string true "激活token"
|
||||
// @Success 200 {object} Response "激活成功"
|
||||
// @Failure 400 {object} Response "token缺失"
|
||||
// @Failure 401 {object} Response "token无效或已过期"
|
||||
// @Router /api/v1/auth/activate-email [post]
|
||||
func (h *AuthHandler) ActivateEmail(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
@@ -228,6 +337,16 @@ func (h *AuthHandler) ActivateEmail(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "email activated successfully"})
|
||||
}
|
||||
|
||||
// ResendActivationEmail 重发激活邮件
|
||||
// @Summary 重发激活邮件
|
||||
// @Description 重新发送账号激活邮件(防枚举:无论邮箱是否注册都返回成功)
|
||||
// @Tags 邮箱认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body ResendActivationRequest true "邮箱地址"
|
||||
// @Success 200 {object} Response "激活邮件已发送(如果邮箱已注册)"
|
||||
// @Failure 400 {object} Response "邮箱格式错误"
|
||||
// @Router /api/v1/auth/resend-activation-email [post]
|
||||
func (h *AuthHandler) ResendActivationEmail(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
@@ -244,6 +363,16 @@ func (h *AuthHandler) ResendActivationEmail(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "activation email sent if address is registered"})
|
||||
}
|
||||
|
||||
// SendEmailCode 发送邮箱验证码
|
||||
// @Summary 发送邮箱验证码
|
||||
// @Description 发送邮箱登录验证码(防枚举:无论邮箱是否注册都返回成功)
|
||||
// @Tags 邮箱认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body SendEmailCodeRequest true "邮箱地址"
|
||||
// @Success 200 {object} Response "验证码已发送"
|
||||
// @Failure 400 {object} Response "邮箱格式错误"
|
||||
// @Router /api/v1/auth/send-email-code [post]
|
||||
func (h *AuthHandler) SendEmailCode(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
@@ -261,6 +390,17 @@ func (h *AuthHandler) SendEmailCode(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "验证码已发送"})
|
||||
}
|
||||
|
||||
// LoginByEmailCode 使用邮箱验证码登录
|
||||
// @Summary 邮箱验证码登录
|
||||
// @Description 使用邮箱和验证码完成登录
|
||||
// @Tags 邮箱认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body LoginByEmailCodeRequest true "登录请求"
|
||||
// @Success 200 {object} Response{data=service.LoginResponse} "登录成功"
|
||||
// @Failure 400 {object} Response "请求参数错误"
|
||||
// @Failure 401 {object} Response "验证码错误或已过期"
|
||||
// @Router /api/v1/auth/login-by-email-code [post]
|
||||
func (h *AuthHandler) LoginByEmailCode(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
@@ -307,6 +447,19 @@ func (h *AuthHandler) LoginByEmailCode(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// BootstrapAdmin 引导初始化管理员
|
||||
// @Summary 引导初始化管理员账号
|
||||
// @Description 在系统未配置管理员时,创建第一个管理员账号(需要BOOTSTRAP_SECRET)
|
||||
// @Tags 系统初始化
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BootstrapSecret
|
||||
// @Param X-Bootstrap-Secret header string true "引导密钥"
|
||||
// @Param request body BootstrapAdminRequest true "管理员信息"
|
||||
// @Success 201 {object} Response{data=service.UserInfo} "管理员创建成功"
|
||||
// @Failure 401 {object} Response "引导密钥无效"
|
||||
// @Failure 403 {object} Response "引导初始化未授权"
|
||||
// @Router /api/v1/auth/bootstrap-admin [post]
|
||||
func (h *AuthHandler) BootstrapAdmin(c *gin.Context) {
|
||||
// P0 修复:BootstrapAdmin 端点需要 bootstrap secret 验证
|
||||
bootstrapSecret := os.Getenv("BOOTSTRAP_SECRET")
|
||||
@@ -358,38 +511,110 @@ func (h *AuthHandler) BootstrapAdmin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// SendEmailBindCode 发送邮箱绑定验证码
|
||||
// @Summary 发送邮箱绑定验证码
|
||||
// @Description 发送验证码到邮箱以绑定邮箱(当前未配置)
|
||||
// @Tags 邮箱绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/email/bind/send [post]
|
||||
func (h *AuthHandler) SendEmailBindCode(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "email bind not configured"})
|
||||
}
|
||||
|
||||
// BindEmail 绑定邮箱
|
||||
// @Summary 绑定邮箱
|
||||
// @Description 使用邮箱验证码绑定账号(当前未配置)
|
||||
// @Tags 邮箱绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/email/bind [post]
|
||||
func (h *AuthHandler) BindEmail(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "email bind not configured"})
|
||||
}
|
||||
|
||||
// UnbindEmail 解绑邮箱
|
||||
// @Summary 解绑邮箱
|
||||
// @Description 解绑账号关联的邮箱(当前未配置)
|
||||
// @Tags 邮箱绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/email/unbind [post]
|
||||
func (h *AuthHandler) UnbindEmail(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "email unbind not configured"})
|
||||
}
|
||||
|
||||
// SendPhoneBindCode 发送手机绑定验证码
|
||||
// @Summary 发送手机绑定验证码
|
||||
// @Description 发送验证码到手机以绑定手机号(当前未配置)
|
||||
// @Tags 手机绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/phone/bind/send [post]
|
||||
func (h *AuthHandler) SendPhoneBindCode(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "phone bind not configured"})
|
||||
}
|
||||
|
||||
// BindPhone 绑定手机号
|
||||
// @Summary 绑定手机号
|
||||
// @Description 使用手机验证码绑定账号(当前未配置)
|
||||
// @Tags 手机绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/phone/bind [post]
|
||||
func (h *AuthHandler) BindPhone(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "phone bind not configured"})
|
||||
}
|
||||
|
||||
// UnbindPhone 解绑手机号
|
||||
// @Summary 解绑手机号
|
||||
// @Description 解绑账号关联的手机号(当前未配置)
|
||||
// @Tags 手机绑定
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/phone/unbind [post]
|
||||
func (h *AuthHandler) UnbindPhone(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "phone unbind not configured"})
|
||||
}
|
||||
|
||||
// GetSocialAccounts 获取社交账号列表
|
||||
// @Summary 获取已绑定的社交账号列表
|
||||
// @Description 获取当前用户绑定的第三方社交账号列表
|
||||
// @Tags 社交账号
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} Response "社交账号列表"
|
||||
// @Router /api/v1/auth/social-accounts [get]
|
||||
func (h *AuthHandler) GetSocialAccounts(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": gin.H{"accounts": []interface{}{}}})
|
||||
}
|
||||
|
||||
// BindSocialAccount 绑定社交账号
|
||||
// @Summary 绑定社交账号
|
||||
// @Description 绑定第三方社交账号到当前用户(当前未配置)
|
||||
// @Tags 社交账号
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/social/bind [post]
|
||||
func (h *AuthHandler) BindSocialAccount(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "social binding not configured"})
|
||||
}
|
||||
|
||||
// UnbindSocialAccount 解绑社交账号
|
||||
// @Summary 解绑社交账号
|
||||
// @Description 解绑当前用户关联的第三方社交账号(当前未配置)
|
||||
// @Tags 社交账号
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} Response "功能未配置"
|
||||
// @Router /api/v1/auth/social/unbind [post]
|
||||
func (h *AuthHandler) UnbindSocialAccount(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "social unbinding not configured"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user