diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3fa220..08e9116e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 更新日志 ## v4.1.4 * 功能优化:用户文件列表组件增加分页功能支持 +* Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码 ## v4.1.3 * 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信 diff --git a/api/handler/admin/chat_role_handler.go b/api/handler/admin/chat_app_handler.go similarity index 88% rename from api/handler/admin/chat_role_handler.go rename to api/handler/admin/chat_app_handler.go index 3776ddc9..8b54eec9 100644 --- a/api/handler/admin/chat_role_handler.go +++ b/api/handler/admin/chat_app_handler.go @@ -22,16 +22,16 @@ import ( "gorm.io/gorm" ) -type ChatRoleHandler struct { +type ChatAppHandler struct { handler.BaseHandler } -func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler { - return &ChatRoleHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} +func NewChatAppHandler(app *core.AppServer, db *gorm.DB) *ChatAppHandler { + return &ChatAppHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } // Save 创建或者更新某个角色 -func (h *ChatRoleHandler) Save(c *gin.Context) { +func (h *ChatAppHandler) Save(c *gin.Context) { var data vo.ChatRole if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) @@ -64,7 +64,7 @@ func (h *ChatRoleHandler) Save(c *gin.Context) { resp.SUCCESS(c, data) } -func (h *ChatRoleHandler) List(c *gin.Context) { +func (h *ChatAppHandler) List(c *gin.Context) { var items []model.ChatRole var roles = make([]vo.ChatRole, 0) res := h.DB.Order("sort_num ASC").Find(&items) @@ -108,7 +108,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) { } // Sort 更新角色排序 -func (h *ChatRoleHandler) Sort(c *gin.Context) { +func (h *ChatAppHandler) Sort(c *gin.Context) { var data struct { Ids []uint `json:"ids"` Sorts []int `json:"sorts"` @@ -130,7 +130,7 @@ func (h *ChatRoleHandler) Sort(c *gin.Context) { resp.SUCCESS(c) } -func (h *ChatRoleHandler) Set(c *gin.Context) { +func (h *ChatAppHandler) Set(c *gin.Context) { var data struct { Id uint `json:"id"` Filed string `json:"filed"` @@ -150,7 +150,7 @@ func (h *ChatRoleHandler) Set(c *gin.Context) { resp.SUCCESS(c) } -func (h *ChatRoleHandler) Remove(c *gin.Context) { +func (h *ChatAppHandler) Remove(c *gin.Context) { id := h.GetInt(c, "id", 0) if id <= 0 { diff --git a/api/handler/admin/chat_app_type_handler.go b/api/handler/admin/chat_app_type_handler.go new file mode 100644 index 00000000..2e121a13 --- /dev/null +++ b/api/handler/admin/chat_app_type_handler.go @@ -0,0 +1,145 @@ +package admin + +import ( + "geekai/core" + "geekai/core/types" + "geekai/handler" + "geekai/store/model" + "geekai/store/vo" + "geekai/utils" + "geekai/utils/resp" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type ChatAppTypeHandler struct { + handler.BaseHandler +} + +func NewChatAppTypeHandler(app *core.AppServer, db *gorm.DB) *ChatAppTypeHandler { + return &ChatAppTypeHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} +} + +// Save 创建或更新App类型 +func (h *ChatAppTypeHandler) Save(c *gin.Context) { + var data struct { + Id uint `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + SortNum int `json:"sort_num"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Id == 0 { // for add + err := h.DB.Where("name", data.Name).First(&model.AppType{}).Error + if err == nil { + resp.ERROR(c, "App类型已存在") + return + } + err = h.DB.Create(&model.AppType{ + Name: data.Name, + Icon: data.Icon, + SortNum: data.SortNum, + }).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } else { // for update + err := h.DB.Where("id", data.Id).Updates(map[string]interface{}{ + "name": data.Name, + "icon": data.Icon, + }).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } + resp.SUCCESS(c) +} + +// List 获取App类型列表 +func (h *ChatAppTypeHandler) List(c *gin.Context) { + var items []model.AppType + var appTypes = make([]vo.AppType, 0) + err := h.DB.Order("created_at DESC").Find(&items).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + for _, v := range items { + var appType vo.AppType + err = utils.CopyObject(v, &appType) + if err != nil { + continue + } + appType.Id = v.Id + appType.CreatedAt = v.CreatedAt.Unix() + appTypes = append(appTypes, appType) + } + + resp.SUCCESS(c, appTypes) +} + +// Remove 删除App类型 +func (h *ChatAppTypeHandler) Remove(c *gin.Context) { + id := h.GetInt(c, "id", 0) + + if id <= 0 { + resp.ERROR(c, types.InvalidArgs) + return + } + err := h.DB.Where("id", id).Delete(&model.AppType{}).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + resp.SUCCESS(c) +} + +// Enable 启用|禁用 +func (h *ChatAppTypeHandler) Enable(c *gin.Context) { + var data struct { + Id uint `json:"id"` + Enabled bool `json:"enabled"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.DB.Model(&model.AppType{}).Where("id", data.Id).UpdateColumn("enabled", data.Enabled).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + resp.SUCCESS(c) +} + +// Sort 更新排序 +func (h *ChatAppTypeHandler) Sort(c *gin.Context) { + var data struct { + Ids []uint `json:"ids"` + Sorts []int `json:"sorts"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + for index, id := range data.Ids { + err := h.DB.Model(&model.AppType{}).Where("id", id).Update("sort_num", data.Sorts[index]).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } + + resp.SUCCESS(c) +} diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go index 275be140..6cd03e6a 100644 --- a/api/handler/admin/user_handler.go +++ b/api/handler/admin/user_handler.go @@ -73,6 +73,8 @@ func (h *UserHandler) Save(c *gin.Context) { Id uint `json:"id"` Password string `json:"password"` Username string `json:"username"` + Mobile string `json:"mobile"` + Email string `json:"email"` ChatRoles []string `json:"chat_roles"` ChatModels []int `json:"chat_models"` ExpiredTime string `json:"expired_time"` @@ -102,6 +104,8 @@ func (h *UserHandler) Save(c *gin.Context) { } var oldPower = user.Power user.Username = data.Username + user.Email = data.Email + user.Mobile = data.Mobile user.Status = data.Status user.Vip = data.Vip user.Power = data.Power @@ -109,7 +113,8 @@ func (h *UserHandler) Save(c *gin.Context) { user.ChatModels = utils.JsonEncode(data.ChatModels) user.ExpiredTime = utils.Str2stamp(data.ExpiredTime) - res = h.DB.Select("username", "status", "vip", "power", "chat_roles_json", "chat_models_json", "expired_time").Updates(&user) + res = h.DB.Select("username", "mobile", "email", "status", "vip", "power", "chat_roles_json", "chat_models_json", "expired_time").Updates(&user) + if res.Error != nil { logger.Error("error with update database:", res.Error) resp.ERROR(c, res.Error.Error()) @@ -147,6 +152,8 @@ func (h *UserHandler) Save(c *gin.Context) { u := model.User{ Username: data.Username, Password: utils.GenPassword(data.Password, salt), + Mobile: data.Mobile, + Email: data.Email, Avatar: "/images/avatar/user.png", Salt: salt, Power: data.Power, diff --git a/api/handler/upload_handler.go b/api/handler/net_handler.go similarity index 100% rename from api/handler/upload_handler.go rename to api/handler/net_handler.go diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 096da7c5..6b4ab0a9 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -74,6 +74,20 @@ func (h *UserHandler) Register(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } + + if h.App.SysConfig.EnabledVerify && data.RegWay == "username" { + var check bool + if data.X != 0 { + check = h.captcha.SlideCheck(data) + } else { + check = h.captcha.Check(data) + } + if !check { + resp.ERROR(c, "请先完人机验证") + return + } + } + data.Password = strings.TrimSpace(data.Password) if len(data.Password) < 8 { resp.ERROR(c, "密码长度不能少于8个字符") diff --git a/api/main.go b/api/main.go index 274f67b2..f240f893 100644 --- a/api/main.go +++ b/api/main.go @@ -146,7 +146,7 @@ func main() { fx.Provide(admin.NewAdminHandler), fx.Provide(admin.NewApiKeyHandler), fx.Provide(admin.NewUserHandler), - fx.Provide(admin.NewChatRoleHandler), + fx.Provide(admin.NewChatAppHandler), fx.Provide(admin.NewRedeemHandler), fx.Provide(admin.NewDashboardHandler), fx.Provide(admin.NewChatModelHandler), @@ -338,7 +338,7 @@ func main() { group.GET("loginLog", h.LoginLog) group.POST("resetPass", h.ResetPass) }), - fx.Invoke(func(s *core.AppServer, h *admin.ChatRoleHandler) { + fx.Invoke(func(s *core.AppServer, h *admin.ChatAppHandler) { group := s.Engine.Group("/api/admin/role/") group.GET("list", h.List) group.POST("save", h.Save) @@ -502,6 +502,15 @@ func main() { group.GET("remove", h.Remove) group.GET("publish", h.Publish) }), + fx.Provide(admin.NewChatAppTypeHandler), + fx.Invoke(func(s *core.AppServer, h *admin.ChatAppTypeHandler) { + group := s.Engine.Group("/api/admin/app/type") + group.POST("save", h.Save) + group.GET("list", h.List) + group.GET("remove", h.Remove) + group.POST("enable", h.Enable) + group.POST("sort", h.Sort) + }), fx.Provide(handler.NewTestHandler), fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) { group := s.Engine.Group("/api/test") diff --git a/api/store/model/app_type.go b/api/store/model/app_type.go new file mode 100644 index 00000000..de10cb16 --- /dev/null +++ b/api/store/model/app_type.go @@ -0,0 +1,11 @@ +package model + +import "time" + +type AppType struct { + Id uint `gorm:"primarykey"` + Name string + Icon string + SortNum int + CreatedAt time.Time +} diff --git a/api/store/vo/app_type.go b/api/store/vo/app_type.go new file mode 100644 index 00000000..714b398a --- /dev/null +++ b/api/store/vo/app_type.go @@ -0,0 +1,12 @@ +package vo + +type AppType struct { + BaseVo + Name string `json:"name"` + Type string `json:"type"` + Value string `json:"value"` // API Key 的值 + ApiURL string `json:"api_url"` + Enabled bool `json:"enabled"` + ProxyURL string `json:"proxy_url"` + LastUsedAt int64 `json:"last_used_at"` // 最后使用时间 +} diff --git a/web/src/components/LoginDialog.vue b/web/src/components/LoginDialog.vue index 9c9de180..6af69740 100644 --- a/web/src/components/LoginDialog.vue +++ b/web/src/components/LoginDialog.vue @@ -384,7 +384,7 @@ const submitRegister = () => { if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') { return ElMessage.error('请输入验证码'); } - if (enableVerify.value) { + if (enableVerify.value && activeName.value === 'username') { captchaRef.value.loadCaptcha() action.value = "register" } else { diff --git a/web/src/components/admin/AdminSidebar.vue b/web/src/components/admin/AdminSidebar.vue index 7034fc10..22b7bea8 100644 --- a/web/src/components/admin/AdminSidebar.vue +++ b/web/src/components/admin/AdminSidebar.vue @@ -90,12 +90,22 @@ const items = [ index: '/admin/user', title: '用户管理', }, - { icon: 'menu', - index: '/admin/app', + index: '1', title: '应用管理', + subs: [ + { + index: '/admin/app', + title: '应用列表', + }, + { + index: '/admin/app/type', + title: '应用分类', + }, + ], }, + { icon: 'api-key', index: '/admin/apikey', diff --git a/web/src/router.js b/web/src/router.js index fe33991c..8507382a 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -173,6 +173,12 @@ const routes = [ meta: {title: '应用管理'}, component: () => import('@/views/admin/Apps.vue'), }, + { + path: '/admin/app/type', + name: 'admin-app-type', + meta: {title: '应用管理'}, + component: () => import('@/views/admin/AppType.vue'), + }, { path: '/admin/apikey', name: 'admin-apikey', diff --git a/web/src/views/Register.vue b/web/src/views/Register.vue index e5c8354e..53840151 100644 --- a/web/src/views/Register.vue +++ b/web/src/views/Register.vue @@ -16,7 +16,7 @@