easyai-ai-gateway/apps/api/internal/httpapi/identity_admin_handlers.go
chensipeng 918dfbfee1 docs(api): 补全 OpenAPI 注释与生成文档
为接口、模型与脚本补齐 Swagger/OpenAPI 注释,生成最新文档,并增加一键生成与查看入口。
2026-05-14 18:18:27 +08:00

359 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httpapi
import (
"encoding/json"
"net/http"
"strings"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)
// createTenant godoc
// @Summary 创建租户
// @Description 管理端创建网关租户tenantKey 和 name 必填。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.GatewayTenantInput true "租户请求"
// @Success 201 {object} store.GatewayTenant
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/tenants [post]
func (s *Server) createTenant(w http.ResponseWriter, r *http.Request) {
var input store.GatewayTenantInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validTenantInput(input) {
writeError(w, http.StatusBadRequest, "tenantKey and name are required")
return
}
item, err := s.store.CreateTenant(r.Context(), input)
if err != nil {
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "tenant key or external tenant id already exists")
return
}
s.logger.Error("create tenant failed", "error", err)
writeError(w, http.StatusInternalServerError, "create tenant failed")
return
}
writeJSON(w, http.StatusCreated, item)
}
// updateTenant godoc
// @Summary 更新租户
// @Description 管理端更新网关租户信息。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param tenantID path string true "租户 ID"
// @Param input body store.GatewayTenantInput true "租户请求"
// @Success 200 {object} store.GatewayTenant
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/tenants/{tenantID} [patch]
func (s *Server) updateTenant(w http.ResponseWriter, r *http.Request) {
var input store.GatewayTenantInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validTenantInput(input) {
writeError(w, http.StatusBadRequest, "tenantKey and name are required")
return
}
item, err := s.store.UpdateTenant(r.Context(), r.PathValue("tenantID"), input)
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "tenant not found")
return
}
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "tenant key or external tenant id already exists")
return
}
s.logger.Error("update tenant failed", "error", err)
writeError(w, http.StatusInternalServerError, "update tenant failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// deleteTenant godoc
// @Summary 删除租户
// @Description 管理端删除网关租户。
// @Tags identity
// @Produce json
// @Security BearerAuth
// @Param tenantID path string true "租户 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/tenants/{tenantID} [delete]
func (s *Server) deleteTenant(w http.ResponseWriter, r *http.Request) {
if err := s.store.DeleteTenant(r.Context(), r.PathValue("tenantID")); err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "tenant not found")
return
}
s.logger.Error("delete tenant failed", "error", err)
writeError(w, http.StatusInternalServerError, "delete tenant failed")
return
}
w.WriteHeader(http.StatusNoContent)
}
// createGatewayUser godoc
// @Summary 创建用户
// @Description 管理端创建网关用户password 为空时不设置本地密码,非空时至少 8 位。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.GatewayUserInput true "用户请求"
// @Success 201 {object} store.GatewayUser
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/users [post]
func (s *Server) createGatewayUser(w http.ResponseWriter, r *http.Request) {
var input store.GatewayUserInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validGatewayUserInput(input) {
writeError(w, http.StatusBadRequest, "username is required")
return
}
if !validOptionalPassword(input.Password) {
writeError(w, http.StatusBadRequest, store.ErrWeakPassword.Error())
return
}
item, err := s.store.CreateGatewayUser(r.Context(), input)
if err != nil {
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "user key, email or external user id already exists")
return
}
s.logger.Error("create gateway user failed", "error", err)
writeError(w, http.StatusInternalServerError, "create gateway user failed")
return
}
writeJSON(w, http.StatusCreated, item)
}
// updateGatewayUser godoc
// @Summary 更新用户
// @Description 管理端更新网关用户资料、角色、默认用户组和可选本地密码。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param userID path string true "用户 ID"
// @Param input body store.GatewayUserInput true "用户请求"
// @Success 200 {object} store.GatewayUser
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/users/{userID} [patch]
func (s *Server) updateGatewayUser(w http.ResponseWriter, r *http.Request) {
var input store.GatewayUserInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validGatewayUserInput(input) {
writeError(w, http.StatusBadRequest, "username is required")
return
}
if !validOptionalPassword(input.Password) {
writeError(w, http.StatusBadRequest, store.ErrWeakPassword.Error())
return
}
item, err := s.store.UpdateGatewayUser(r.Context(), r.PathValue("userID"), input)
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "user not found")
return
}
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "user key, email or external user id already exists")
return
}
s.logger.Error("update gateway user failed", "error", err)
writeError(w, http.StatusInternalServerError, "update gateway user failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// deleteGatewayUser godoc
// @Summary 删除用户
// @Description 管理端删除网关用户。
// @Tags identity
// @Produce json
// @Security BearerAuth
// @Param userID path string true "用户 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/users/{userID} [delete]
func (s *Server) deleteGatewayUser(w http.ResponseWriter, r *http.Request) {
if err := s.store.DeleteGatewayUser(r.Context(), r.PathValue("userID")); err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "user not found")
return
}
s.logger.Error("delete gateway user failed", "error", err)
writeError(w, http.StatusInternalServerError, "delete gateway user failed")
return
}
w.WriteHeader(http.StatusNoContent)
}
// createUserGroup godoc
// @Summary 创建用户组
// @Description 管理端创建用户组,可配置默认定价、运行策略、限流和配额策略。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.UserGroupInput true "用户组请求"
// @Success 201 {object} store.UserGroup
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/user-groups [post]
func (s *Server) createUserGroup(w http.ResponseWriter, r *http.Request) {
var input store.UserGroupInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validUserGroupInput(input) {
writeError(w, http.StatusBadRequest, "groupKey and name are required")
return
}
item, err := s.store.CreateUserGroup(r.Context(), input)
if err != nil {
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "user group key already exists")
return
}
s.logger.Error("create user group failed", "error", err)
writeError(w, http.StatusInternalServerError, "create user group failed")
return
}
writeJSON(w, http.StatusCreated, item)
}
// updateUserGroup godoc
// @Summary 更新用户组
// @Description 管理端更新用户组基础信息和策略配置。
// @Tags identity
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param groupID path string true "用户组 ID"
// @Param input body store.UserGroupInput true "用户组请求"
// @Success 200 {object} store.UserGroup
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/user-groups/{groupID} [patch]
func (s *Server) updateUserGroup(w http.ResponseWriter, r *http.Request) {
var input store.UserGroupInput
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
if !validUserGroupInput(input) {
writeError(w, http.StatusBadRequest, "groupKey and name are required")
return
}
item, err := s.store.UpdateUserGroup(r.Context(), r.PathValue("groupID"), input)
if err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "user group not found")
return
}
if store.IsUniqueViolation(err) {
writeError(w, http.StatusConflict, "user group key already exists")
return
}
s.logger.Error("update user group failed", "error", err)
writeError(w, http.StatusInternalServerError, "update user group failed")
return
}
writeJSON(w, http.StatusOK, item)
}
// deleteUserGroup godoc
// @Summary 删除用户组
// @Description 管理端删除用户组。
// @Tags identity
// @Produce json
// @Security BearerAuth
// @Param groupID path string true "用户组 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/user-groups/{groupID} [delete]
func (s *Server) deleteUserGroup(w http.ResponseWriter, r *http.Request) {
if err := s.store.DeleteUserGroup(r.Context(), r.PathValue("groupID")); err != nil {
if store.IsNotFound(err) {
writeError(w, http.StatusNotFound, "user group not found")
return
}
s.logger.Error("delete user group failed", "error", err)
writeError(w, http.StatusInternalServerError, "delete user group failed")
return
}
w.WriteHeader(http.StatusNoContent)
}
func validTenantInput(input store.GatewayTenantInput) bool {
return strings.TrimSpace(input.TenantKey) != "" && strings.TrimSpace(input.Name) != ""
}
func validGatewayUserInput(input store.GatewayUserInput) bool {
return strings.TrimSpace(input.Username) != ""
}
func validOptionalPassword(password string) bool {
password = strings.TrimSpace(password)
return password == "" || len(password) >= 8
}
func validUserGroupInput(input store.UserGroupInput) bool {
return strings.TrimSpace(input.GroupKey) != "" && strings.TrimSpace(input.Name) != ""
}