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) != "" }