diff --git a/backend/plugins/domain/admin/handler/auth_source.go b/backend/plugins/domain/admin/handler/auth_source.go index 27eadc46..71fd7a64 100644 --- a/backend/plugins/domain/admin/handler/auth_source.go +++ b/backend/plugins/domain/admin/handler/auth_source.go @@ -14,7 +14,17 @@ import ( "github.com/gin-gonic/gin" ) -// ListAuthSources lists all configured authentication sources. +// ListAuthSources 获取认证源列表 +// @Summary 获取认证源列表 +// @Description 返回所有已配置的 OAuth/OIDC 认证源列表,包括已启用和未启用的,需要管理员权限 +// @Tags admin +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any "认证源列表" +// @Failure 401 {object} response.Any "未登录" +// @Failure 403 {object} response.Any "无管理员权限" +// @Failure 500 {object} response.Any "内部错误" +// @Router /api/v1/admin/auth-sources [get] func ListAuthSources(c *gin.Context) { views, err := service.ListAuthSources(c.Request.Context()) if err != nil { @@ -25,7 +35,19 @@ func ListAuthSources(c *gin.Context) { c.JSON(http.StatusOK, response.OK(views)) } -// CreateAuthSource creates a new authentication source. +// CreateAuthSource 创建认证源 +// @Summary 创建认证源 +// @Description 创建一个新的 OAuth/OIDC 认证源配置,认证源名称必须唯一且符合命名规范,需要管理员权限 +// @Tags admin +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param request body contracts.AuthSourceDTO true "创建认证源参数" +// @Success 200 {object} response.Any "创建成功,返回认证源信息" +// @Failure 400 {object} response.Any "参数错误或验证失败" +// @Failure 401 {object} response.Any "未登录" +// @Failure 403 {object} response.Any "无管理员权限" +// @Router /api/v1/admin/auth-sources [post] func CreateAuthSource(c *gin.Context) { var source contracts.AuthSourceDTO if err := c.ShouldBindJSON(&source); err != nil { @@ -42,7 +64,21 @@ func CreateAuthSource(c *gin.Context) { c.JSON(http.StatusOK, response.OK(created)) } -// UpdateAuthSource updates an authentication source. +// UpdateAuthSource 更新认证源 +// @Summary 更新认证源 +// @Description 更新指定 ID 的认证源配置。若 client_secret 字段为空,则保留原有密钥不变,需要管理员权限 +// @Tags admin +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "认证源 ID 或名称" +// @Param request body contracts.AuthSourceDTO true "更新认证源参数" +// @Success 200 {object} response.Any "更新成功,返回更新后的认证源信息" +// @Failure 400 {object} response.Any "参数错误或验证失败" +// @Failure 401 {object} response.Any "未登录" +// @Failure 403 {object} response.Any "无管理员权限" +// @Failure 500 {object} response.Any "内部错误" +// @Router /api/v1/admin/auth-sources/{id} [put] func UpdateAuthSource(c *gin.Context) { id, ok := parseAuthSourceID(c) if !ok { @@ -64,7 +100,19 @@ func UpdateAuthSource(c *gin.Context) { c.JSON(http.StatusOK, response.OK(updated)) } -// ToggleAuthSource toggles the active state of an auth source. +// ToggleAuthSource 切换认证源启用状态 +// @Summary 切换认证源启用状态 +// @Description 启用或禁用指定认证源。尝试启用时将验证 Client ID 和 Client Secret 是否已配置,需要管理员权限 +// @Tags admin +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "认证源 ID 或名称" +// @Success 200 {object} response.Any{data=string} "切换成功" +// @Failure 400 {object} response.Any "验证失败或认证源不存在" +// @Failure 401 {object} response.Any "未登录" +// @Failure 403 {object} response.Any "无管理员权限" +// @Router /api/v1/admin/auth-sources/{id}/toggle [put] func ToggleAuthSource(c *gin.Context) { id, ok := parseAuthSourceID(c) if !ok { @@ -80,7 +128,18 @@ func ToggleAuthSource(c *gin.Context) { c.JSON(http.StatusOK, response.OK(gin.H{"is_active": toggled.IsActive})) } -// DeleteAuthSource deletes an authentication source. +// DeleteAuthSource 删除认证源 +// @Summary 删除认证源 +// @Description 删除指定认证源及其关联的所有外部帐号绑定记录,警告:删除后相关用户将无法通过该源登录,需要管理员权限 +// @Tags admin +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "认证源 ID 或名称" +// @Success 200 {object} response.Any{data=string} "删除成功" +// @Failure 400 {object} response.Any "ID 无效或删除失败" +// @Failure 401 {object} response.Any "未登录" +// @Failure 403 {object} response.Any "无管理员权限" +// @Router /api/v1/admin/auth-sources/{id} [delete] func DeleteAuthSource(c *gin.Context) { id, ok := parseAuthSourceID(c) if !ok { diff --git a/backend/plugins/domain/auth/handlers.go b/backend/plugins/domain/auth/handlers.go index c7a8f2a4..d96fe321 100644 --- a/backend/plugins/domain/auth/handlers.go +++ b/backend/plugins/domain/auth/handlers.go @@ -25,11 +25,26 @@ import ( ) // GetLoginSources 获取可用登录源列表 +// @Summary 获取可用登录源 +// @Description 返回当前系统已启用的所有 OAuth 登录源,前端展示登录按钮列表时调用 +// @Tags oauth +// @Produce json +// @Success 200 {object} response.Any{data=[]auth.AuthSourceView} "登录源列表" +// @Router /api/v1/oauth/sources [get] func GetLoginSources(c *gin.Context) { c.JSON(http.StatusOK, response.OK(activeLoginSources(c.Request.Context()))) } // GetLoginURL 获取登录授权地址 +// @Summary 获取登录授权地址 +// @Description 根据指定认证源生成 OAuth 授权 URL,前端跳转到该 URL 完成 OAuth 登录授权。source 参数为空时使用第一个启用的认证源。 +// @Tags oauth +// @Produce json +// @Param source query string false "认证源名称,为空使用第一个启用的认证源" +// @Success 200 {object} response.Any{data=auth.OAuthAuthorizeResponse} "授权 URL" +// @Failure 400 {object} response.Any "认证源不存在或未配置" +// @Failure 500 {object} response.Any "构造 URL 失败" +// @Router /api/v1/oauth/login [get] func GetLoginURL(c *gin.Context) { ctx := c.Request.Context() if !isOIDCLoginEnabled(ctx) { @@ -126,6 +141,16 @@ func reserveOAuthStateSlot(ctx context.Context, sessionHash string) error { } // Authorize 发起指定认证源授权 +// @Summary 发起指定认证源授权 +// @Description 根据指定认证源名称发起 OAuth 授权,支持 purpose 参数用于区分登录和账号绑定场景。认证源必须已启用。 +// @Tags oauth +// @Produce json +// @Param source path string true "认证源名称" +// @Param purpose query string false "授权目的:login(登录)或 bind(绑定账号),默认 login" +// @Success 200 {object} response.Any{data=auth.OAuthAuthorizeResponse} "授权 URL" +// @Failure 400 {object} response.Any "认证源不存在或未启用" +// @Failure 500 {object} response.Any "构造 URL 失败" +// @Router /api/v1/oauth/{source}/authorize [get] func Authorize(c *gin.Context) { ctx := c.Request.Context() if !isOIDCLoginEnabled(ctx) { @@ -197,6 +222,17 @@ func Authorize(c *gin.Context) { } // Callback OAuth 回调处理 +// @Summary OAuth 回调处理 +// @Description 接收前端传回的 state 和 code,完成 OAuth/OIDC 认证并建立会话。支持登录(login)和账号绑定(bind)两种场景。 +// @Tags oauth +// @Accept json +// @Produce json +// @Param request body auth.CallbackRequest true "回调请求参数" +// @Success 200 {object} response.Any{data=auth.OAuthCallbackResult} "登录或绑定成功" +// @Failure 400 {object} response.Any "state 无效、参数错误或认证源错误" +// @Failure 401 {object} response.Any "绑定场景未登录" +// @Failure 500 {object} response.Any "OAuth 认证失败或内部错误" +// @Router /api/v1/oauth/callback [post] func Callback(c *gin.Context) { var req CallbackRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -437,6 +473,15 @@ func handleCallbackRegister(ctx context.Context, c *gin.Context, source *AuthSou } // UserInfo 获取当前登录用户信息 +// @Summary 获取当前登录用户信息 +// @Description 返回当前登录用户的基本信息,需要登录。 +// @Tags oauth +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=auth.BasicUserInfo} "用户信息" +// @Failure 401 {object} response.Any "未登录" +// @Router /api/v1/oauth/user-info [get] +// @Router /api/v1/user-info [get] func UserInfo(c *gin.Context) { user, _ := ginutil.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey) session := sessions.Default(c) @@ -449,6 +494,14 @@ func UserInfo(c *gin.Context) { } // Logout 退出登录 +// @Summary 退出登录 +// @Description 清除当前用户的登录会话,完成退出。清除 Cookie 中的 Session 数据。 +// @Tags oauth +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=string} "退出成功" +// @Failure 500 {object} response.Any "Session 清除失败" +// @Router /api/v1/oauth/logout [get] func Logout(c *gin.Context) { session := sessions.Default(c) userID := session.Get(UserIDKey) @@ -469,6 +522,15 @@ func Logout(c *gin.Context) { } // ListExternalAccounts 获取当前用户的外部帐号绑定列表 +// @Summary 获取外部帐号列表 +// @Description 返回当前登录用户已绑定的所有外部 OAuth 帐号信息,需要登录 +// @Tags oauth +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any "外部帐号列表" +// @Failure 401 {object} response.Any "未登录" +// @Failure 500 {object} response.Any "内部错误" +// @Router /api/v1/oauth/external-accounts [get] func ListExternalAccounts(c *gin.Context) { userID := GetUserIDFromContext(c) accounts, err := ListExternalAccountsByUserID(c.Request.Context(), userID) @@ -480,6 +542,16 @@ func ListExternalAccounts(c *gin.Context) { } // DeleteExternalAccount 解除外部帐号绑定 +// @Summary 解除外部帐号绑定 +// @Description 解除当前登录用户与指定外部帐号的绑定关系,需要登录 +// @Tags oauth +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "外部帐号绑定记录 ID" +// @Success 200 {object} response.Any{data=string} "解除绑定成功" +// @Failure 400 {object} response.Any "ID 无效或解除失败" +// @Failure 401 {object} response.Any "未登录" +// @Router /api/v1/oauth/external-accounts/{id}/delete [post] func DeleteExternalAccount(c *gin.Context) { userID := GetUserIDFromContext(c) if userID == 0 { diff --git a/backend/plugins/domain/message_gateway/handler/push_channel.go b/backend/plugins/domain/message_gateway/handler/push_channel.go index cdfe0c8f..c3c07a72 100644 --- a/backend/plugins/domain/message_gateway/handler/push_channel.go +++ b/backend/plugins/domain/message_gateway/handler/push_channel.go @@ -15,12 +15,26 @@ import ( "github.com/gin-gonic/gin" ) -// ListPushChannelDefinitions returns channel definitions. +// ListPushChannelDefinitions 获取各种消息通道的表单配置定义列表 +// @Summary 获取所有消息通道配置字段定义 +// @Description 返回系统支持的所有消息通道类型的动态表单定义,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any "通道配置定义列表" +// @Router /api/v1/admin/push/channels/definitions [get] func ListPushChannelDefinitions(c *gin.Context) { c.JSON(http.StatusOK, response.OK(model.ListPushDefinitions())) } -// ListPushChannels lists configured push channels. +// ListPushChannels 获取消息通道列表 +// @Summary 获取所有消息通道 +// @Description 返回系统配置的所有消息通道列表,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=[]model.PushChannel} "消息通道列表" +// @Router /api/v1/admin/push/channels [get] func ListPushChannels(c *gin.Context) { channels, err := service.ListPushChannels(c.Request.Context()) if err != nil { @@ -49,19 +63,46 @@ func handlePushChannelNotFoundError(c *gin.Context, err error, fallback func(c * fallback(c, err.Error()) } -// CreatePushChannel creates a push channel. +// CreatePushChannel 创建消息通道 +// @Summary 创建消息通道 +// @Description 新建一个消息通道配置,需要管理员权限 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param request body model.CreatePushChannelRequest true "创建参数" +// @Success 200 {object} response.Any{data=model.PushChannel} "创建成功" +// @Router /api/v1/admin/push/channels [post] func CreatePushChannel(c *gin.Context) { handleJSONRequest(c, service.CreatePushChannel) } -// UpdatePushChannel updates a push channel. +// UpdatePushChannel 更新消息通道 +// @Summary 更新消息通道 +// @Description 修改消息通道配置,需要管理员权限 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "通道ID" +// @Param request body model.UpdatePushChannelRequest true "更新参数" +// @Success 200 {object} response.Any{data=model.PushChannel} "更新成功" +// @Router /api/v1/admin/push/channels/{id} [put] func UpdatePushChannel(c *gin.Context) { handleEntityUpdate(c, parsePushChannelID, service.UpdatePushChannel, func(c *gin.Context, err error) { handlePushChannelNotFoundError(c, err, response.AbortInternal) }) } -// DeletePushChannel deletes a push channel. +// DeletePushChannel 删除消息通道 +// @Summary 删除消息通道 +// @Description 根据ID删除消息通道,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Param id path uint64 true "通道ID" +// @Success 200 {object} response.Any "删除成功" +// @Router /api/v1/admin/push/channels/{id} [delete] func DeletePushChannel(c *gin.Context) { id, ok := parsePushChannelID(c) if !ok { @@ -75,7 +116,16 @@ func DeletePushChannel(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// TestPushChannel tests connectivity of a push channel. +// TestPushChannel 测试通道连通性 +// @Summary 测试通道连通性 +// @Description 触发一次临时的或现有的通道连通性推送测试,需要管理员权限 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param request body model.TestPushChannelRequest true "测试参数" +// @Success 200 {object} response.Any "测试触发成功" +// @Router /api/v1/admin/push/channels/test [post] func TestPushChannel(c *gin.Context) { var req model.TestPushChannelRequest if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/plugins/domain/message_gateway/handler/push_event.go b/backend/plugins/domain/message_gateway/handler/push_event.go index d7a6213b..04c6e717 100644 --- a/backend/plugins/domain/message_gateway/handler/push_event.go +++ b/backend/plugins/domain/message_gateway/handler/push_event.go @@ -15,7 +15,14 @@ import ( "github.com/gin-gonic/gin" ) -// ListPushEvents lists configured push events. +// ListPushEvents 获取通知事件列表 +// @Summary 获取所有通知事件 +// @Description 返回系统配置的通知事件列表,包括预置和自定义事件,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=[]model.PushEvent} "通知事件列表" +// @Router /api/v1/admin/push/events [get] func ListPushEvents(c *gin.Context) { ctx := c.Request.Context() events, err := service.ListPushEvents(ctx) @@ -26,7 +33,14 @@ func ListPushEvents(c *gin.Context) { c.JSON(http.StatusOK, response.OK(events)) } -// ListBuiltInPushEvents lists system built-in push event definitions. +// ListBuiltInPushEvents 获取内置通知事件列表 +// @Summary 获取所有内置通知事件 +// @Description 返回系统定义的所有内置通知事件元数据,供前端下拉框选择,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any "内置通知事件列表" +// @Router /api/v1/admin/push/events/builtin [get] func ListBuiltInPushEvents(c *gin.Context) { c.JSON(http.StatusOK, response.OK(service.GetBuiltInEvents())) } @@ -50,12 +64,29 @@ func handlePushEventNotFoundError(c *gin.Context, err error, fallback func(c *gi fallback(c, err.Error()) } -// CreatePushEvent creates a new push event configuration. +// CreatePushEvent 创建通知事件 +// @Summary 创建通知事件 +// @Description 绑定系统内置事件或异步任务、推送渠道、接收目标并创建通知事件配置,需要管理员权限 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param request body model.CreatePushEventRequest true "创建参数" +// @Success 200 {object} response.Any{data=model.PushEvent} "创建成功" +// @Router /api/v1/admin/push/events [post] func CreatePushEvent(c *gin.Context) { handleJSONRequest(c, service.CreatePushEvent) } -// DeletePushEvent deletes a push event configuration by ID. +// DeletePushEvent 删除通知事件配置 +// @Summary 删除通知事件配置 +// @Description 删除数据库中的特定通知事件配置,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Param id path int true "事件 ID" +// @Success 200 {object} response.Any{data=string} "删除成功" +// @Router /api/v1/admin/push/events/{id} [delete] func DeletePushEvent(c *gin.Context) { id, ok := parsePushEventID(c) if !ok { @@ -69,7 +100,17 @@ func DeletePushEvent(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// UpdatePushEvent updates an existing push event. +// UpdatePushEvent 更新通知事件 +// @Summary 更新通知事件 +// @Description 更新已有通知事件的推送渠道、接收目标和内容模板,需要管理员权限 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param id path int true "事件 ID" +// @Param request body model.UpdatePushEventRequest true "更新参数" +// @Success 200 {object} response.Any{data=string} "修改成功" +// @Router /api/v1/admin/push/events/{id} [put] func UpdatePushEvent(c *gin.Context) { id, ok := parsePushEventID(c) if !ok { @@ -89,7 +130,15 @@ func UpdatePushEvent(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// TogglePushEvent toggles the enabled state of a push event. +// TogglePushEvent 快捷切换通知事件启用状态 +// @Summary 快捷切换通知事件启用状态 +// @Description 启用或禁用指定的通知事件 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Param id path int true "事件 ID" +// @Success 200 {object} response.Any{data=string} "切换成功" +// @Router /api/v1/admin/push/events/{id}/toggle [post] func TogglePushEvent(c *gin.Context) { id, ok := parsePushEventID(c) if !ok { @@ -104,7 +153,18 @@ func TogglePushEvent(c *gin.Context) { c.JSON(http.StatusOK, response.OK(enabled)) } -// ListPushHistories returns paginated push notification delivery histories. +// ListPushHistories 分页获取通知推送历史 +// @Summary 分页获取通知推送历史 +// @Description 返回分页的通知历史日志数据,需要管理员权限 +// @Tags admin-push +// @Produce json +// @Security SessionCookie +// @Param page query int false "当前页码" +// @Param page_size query int false "分页大小" +// @Param event_key query string false "过滤事件名称" +// @Param status query string false "过滤发送状态" +// @Success 200 {object} response.Any "推送历史列表" +// @Router /api/v1/admin/push/histories [get] func ListPushHistories(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) @@ -132,7 +192,16 @@ func ListPushHistories(c *gin.Context) { })) } -// TestPush executes a synchronous push test using the specified config. +// TestPush 测试推送通道发送 +// @Summary 测试推送通道发送 +// @Description 接收临时通知渠道配置并在本地同步调用 Pusher.Send 发送测试消息 +// @Tags admin-push +// @Accept json +// @Produce json +// @Security SessionCookie +// @Param request body model.TestPushRequest true "测试请求体" +// @Success 200 {object} response.Any{data=string} "测试成功" +// @Router /api/v1/admin/push/test [post] func TestPush(c *gin.Context) { var req model.TestPushRequest if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/plugins/domain/system/plugin.go b/backend/plugins/domain/system/plugin.go index 5e636967..71763b86 100644 --- a/backend/plugins/domain/system/plugin.go +++ b/backend/plugins/domain/system/plugin.go @@ -56,9 +56,7 @@ func (p *Plugin) Apply(ctx *core.Context) error { ctx.Router().GET("/api/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) }) - ctx.Router().GET("/api/health", func(c *gin.Context) { - c.JSON(http.StatusOK, response.OKNil()) - }) + ctx.Router().GET("/api/health", Health) ctx.Router().RegisterWhitelist("/api/health") // 2. Public config @@ -92,3 +90,14 @@ func (p *Plugin) Apply(ctx *core.Context) error { return nil } + +// Health 健康检查 +// @Summary 健康检查 +// @Description 检查服务是否正常运行,可用于负载均衡存活探测 +// @Tags health +// @Produce json +// @Success 200 {object} response.Any{data=string} "服务正常" +// @Router /api/health [get] +func Health(c *gin.Context) { + c.JSON(http.StatusOK, response.OKNil()) +} diff --git a/backend/plugins/domain/upload/handler/file_management.go b/backend/plugins/domain/upload/handler/file_management.go index 69f9aecf..1666cb7d 100644 --- a/backend/plugins/domain/upload/handler/file_management.go +++ b/backend/plugins/domain/upload/handler/file_management.go @@ -50,6 +50,7 @@ type listFilesResponse struct { // @Security SessionCookie // @Success 200 {object} response.Any{data=listFilesResponse} "查询成功" // @Failure 400 {object} response.Any "参数错误" +// @Router /api/v1/admin/uploads [get] // @Router /api/v1/admin/uploads/files [get] func ListFiles(c *gin.Context) { ctx := c.Request.Context() @@ -96,6 +97,7 @@ func ListFiles(c *gin.Context) { // @Security SessionCookie // @Success 200 {object} response.Any "删除成功" // @Failure 404 {object} response.Any "文件不存在" +// @Router /api/v1/admin/uploads/{id} [delete] // @Router /api/v1/admin/uploads/files/{id} [delete] func DeleteFile(c *gin.Context) { ctx := c.Request.Context() diff --git a/backend/plugins/domain/user/handlers.go b/backend/plugins/domain/user/handlers.go index 198f9431..8ddea90b 100644 --- a/backend/plugins/domain/user/handlers.go +++ b/backend/plugins/domain/user/handlers.go @@ -77,7 +77,17 @@ func invalidateTokenCache(ctx context.Context, tokenHash string) { } } -// Login handles username and password authentication. +// Login 用户密码登录 +// @Summary 用户密码登录 +// @Description 使用用户名和密码登录,登录成功后建立 Session。若管理员已关闭密码登录功能则返回错误。 +// @Tags user +// @Accept json +// @Produce json +// @Param request body user.loginRequest true "登录请求参数" +// @Success 200 {object} response.Any "登录成功,返回用户信息" +// @Failure 400 {object} response.Any "用户名或密码错误" +// @Failure 500 {object} response.Any "服务内部错误" +// @Router /api/v1/user/login [post] func Login(c *gin.Context) { var req loginRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -109,7 +119,17 @@ func Login(c *gin.Context) { c.JSON(http.StatusOK, response.OK(user)) } -// Register registers a new user. +// Register 用户注册 +// @Summary 用户注册 +// @Description 使用用户名和密码注册新账号,注册成功后自动登录并建立 Session。 +// @Tags user +// @Accept json +// @Produce json +// @Param request body user.registerRequest true "注册请求参数" +// @Success 200 {object} response.Any "注册并登录成功,返回用户信息" +// @Failure 400 {object} response.Any "参数错误、用户名已存在或注册已关闭" +// @Failure 500 {object} response.Any "服务内部错误" +// @Router /api/v1/user/register [post] func Register(c *gin.Context) { var req registerRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -142,7 +162,15 @@ func Register(c *gin.Context) { c.JSON(http.StatusOK, response.OK(newUser)) } -// Logout logs out the current session. +// Logout 用户退出登录 +// @Summary 用户退出登录 +// @Description 清除用户登录 Session,完成退出 +// @Tags user +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=string} "退出成功" +// @Failure 500 {object} response.Any "Session 清除失败" +// @Router /api/v1/user/logout [get] func Logout(c *gin.Context) { sess := sessions.Default(c) sess.Options(sessions.Options{ @@ -156,12 +184,30 @@ func Logout(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// SendEmailCode sends an email verification code. +// SendEmailCode 发送邮箱验证码 +// @Summary 发送邮箱验证码 +// @Description 向指定邮箱发送验证码(用于注册场景) +// @Tags user +// @Accept json +// @Produce json +// @Success 200 {object} response.Any "发送成功" +// @Failure 400 {object} response.Any "参数错误" +// @Router /api/v1/user/send-email-code [post] func SendEmailCode(c *gin.Context) { c.JSON(http.StatusOK, response.OK(gin.H{"sent": true})) } -// ChangePassword changes the current user password. +// ChangePassword 修改用户密码 +// @Summary 修改用户密码 +// @Description 修改当前登录用户的密码。 +// @Tags user +// @Accept json +// @Produce json +// @Param request body user.changePasswordRequest true "修改密码请求参数" +// @Success 200 {object} response.Any{data=string} "修改密码成功" +// @Failure 400 {object} response.Any "原密码错误或新密码不符合要求" +// @Failure 401 {object} response.Any "请先登录" +// @Router /api/v1/user/change-password [post] func ChangePassword(c *gin.Context) { var req changePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -200,7 +246,15 @@ func ChangePassword(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// Self returns the current authenticated user. +// Self 获取当前登录用户信息 +// @Summary 获取当前登录用户信息 +// @Description 返回当前登录用户的基本信息,需要登录。 +// @Tags user +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any "用户信息" +// @Failure 401 {object} response.Any "未登录" +// @Router /api/v1/user/self [get] func Self(c *gin.Context) { svc := getAuthService() if svc == nil { @@ -216,7 +270,17 @@ func Self(c *gin.Context) { c.JSON(http.StatusOK, response.OK(user)) } -// UpdateProfile updates profile info. +// UpdateProfile 修改当前登录用户的个人资料 +// @Summary 修改当前登录用户的个人资料 +// @Description 修改当前登录用户的昵称、头像、简介、电话、性别、个人网站和所在地。 +// @Tags user +// @Accept json +// @Produce json +// @Param request body user.updateProfileRequest true "更新请求参数" +// @Success 200 {object} response.Any "修改成功,返回更新后的用户信息" +// @Failure 400 {object} response.Any "参数错误" +// @Failure 401 {object} response.Any "未登录" +// @Router /api/v1/user/profile [put] func UpdateProfile(c *gin.Context) { var req updateProfileRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -247,7 +311,15 @@ func UpdateProfile(c *gin.Context) { c.JSON(http.StatusOK, response.OK(user)) } -// ListAccessTokens lists access tokens for the current user. +// ListAccessTokens 获取当前用户的 AccessToken 列表 +// @Summary 获取当前用户的 AccessToken 列表 +// @Description 返回当前登录用户的所有 active access tokens(脱敏后) +// @Tags user +// @Produce json +// @Security SessionCookie +// @Success 200 {object} response.Any{data=[]user.AccessToken} "令牌列表" +// @Failure 401 {object} response.Any "未登录" +// @Router /api/v1/user/access-tokens [get] func ListAccessTokens(c *gin.Context) { userID := getUserIDFromSession(c) tokens, err := listAccessTokensByUser(c.Request.Context(), userID) @@ -262,7 +334,17 @@ const ( tokenMaskMinLength = 8 ) -// CreateAccessToken generates a new access token. +// CreateAccessToken 创建一个新的 AccessToken +// @Summary 创建一个新的 AccessToken +// @Description 为当前用户新建一个 API 访问令牌,仅在此接口返回一次明文令牌值,请妥善保存。 +// @Tags user +// @Accept json +// @Produce json +// @Param request body user.createAccessTokenRequest true "令牌名称" +// @Security SessionCookie +// @Success 200 {object} response.Any "新建令牌成功" +// @Failure 400 {object} response.Any "参数错误或超限" +// @Router /api/v1/user/access-tokens [post] func CreateAccessToken(c *gin.Context) { var req createAccessTokenRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -301,7 +383,16 @@ func CreateAccessToken(c *gin.Context) { })) } -// DeleteAccessToken deletes a specific access token. +// DeleteAccessToken 删除一个 AccessToken +// @Summary 删除一个 AccessToken +// @Description 撤销并删除一个属于当前用户的 API 访问令牌 +// @Tags user +// @Produce json +// @Param id path string true "令牌ID" +// @Security SessionCookie +// @Success 200 {object} response.Any{data=string} "删除成功" +// @Failure 400 {object} response.Any "参数错误" +// @Router /api/v1/user/access-tokens/{id} [delete] func DeleteAccessToken(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) @@ -324,7 +415,16 @@ func DeleteAccessToken(c *gin.Context) { c.JSON(http.StatusOK, response.OKNil()) } -// RotateAccessToken rotates an access token value. +// RotateAccessToken 轮换一个 AccessToken +// @Summary 轮换一个 AccessToken +// @Description 轮换(重新生成)一个属于当前用户的 API 访问令牌的密钥,旧令牌将立即失效 +// @Tags user +// @Produce json +// @Param id path string true "令牌ID" +// @Security SessionCookie +// @Success 200 {object} response.Any "令牌轮换成功" +// @Failure 400 {object} response.Any "参数错误" +// @Router /api/v1/user/access-tokens/{id}/rotate [post] func RotateAccessToken(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64)