fixed bug for register page code verification

This commit is contained in:
RockYang 2024-09-12 15:42:09 +08:00
parent 49f2e1a71e
commit 961cee5e41
15 changed files with 259 additions and 20 deletions

View File

@ -1,6 +1,7 @@
# 更新日志 # 更新日志
## v4.1.4 ## v4.1.4
* 功能优化:用户文件列表组件增加分页功能支持 * 功能优化:用户文件列表组件增加分页功能支持
* Bug修复修复用户注册失败Bug注册操作只弹出一次行为验证码
## v4.1.3 ## v4.1.3
* 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信 * 功能优化:重构用户登录模块,给所有的登录组件增加行为验证码功能,支持用户绑定手机,邮箱和微信

View File

@ -22,16 +22,16 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type ChatRoleHandler struct { type ChatAppHandler struct {
handler.BaseHandler handler.BaseHandler
} }
func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler { func NewChatAppHandler(app *core.AppServer, db *gorm.DB) *ChatAppHandler {
return &ChatRoleHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} return &ChatAppHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}}
} }
// Save 创建或者更新某个角色 // Save 创建或者更新某个角色
func (h *ChatRoleHandler) Save(c *gin.Context) { func (h *ChatAppHandler) Save(c *gin.Context) {
var data vo.ChatRole var data vo.ChatRole
if err := c.ShouldBindJSON(&data); err != nil { if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs) resp.ERROR(c, types.InvalidArgs)
@ -64,7 +64,7 @@ func (h *ChatRoleHandler) Save(c *gin.Context) {
resp.SUCCESS(c, data) resp.SUCCESS(c, data)
} }
func (h *ChatRoleHandler) List(c *gin.Context) { func (h *ChatAppHandler) List(c *gin.Context) {
var items []model.ChatRole var items []model.ChatRole
var roles = make([]vo.ChatRole, 0) var roles = make([]vo.ChatRole, 0)
res := h.DB.Order("sort_num ASC").Find(&items) res := h.DB.Order("sort_num ASC").Find(&items)
@ -108,7 +108,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
} }
// Sort 更新角色排序 // Sort 更新角色排序
func (h *ChatRoleHandler) Sort(c *gin.Context) { func (h *ChatAppHandler) Sort(c *gin.Context) {
var data struct { var data struct {
Ids []uint `json:"ids"` Ids []uint `json:"ids"`
Sorts []int `json:"sorts"` Sorts []int `json:"sorts"`
@ -130,7 +130,7 @@ func (h *ChatRoleHandler) Sort(c *gin.Context) {
resp.SUCCESS(c) resp.SUCCESS(c)
} }
func (h *ChatRoleHandler) Set(c *gin.Context) { func (h *ChatAppHandler) Set(c *gin.Context) {
var data struct { var data struct {
Id uint `json:"id"` Id uint `json:"id"`
Filed string `json:"filed"` Filed string `json:"filed"`
@ -150,7 +150,7 @@ func (h *ChatRoleHandler) Set(c *gin.Context) {
resp.SUCCESS(c) resp.SUCCESS(c)
} }
func (h *ChatRoleHandler) Remove(c *gin.Context) { func (h *ChatAppHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0) id := h.GetInt(c, "id", 0)
if id <= 0 { if id <= 0 {

View File

@ -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)
}

View File

@ -73,6 +73,8 @@ func (h *UserHandler) Save(c *gin.Context) {
Id uint `json:"id"` Id uint `json:"id"`
Password string `json:"password"` Password string `json:"password"`
Username string `json:"username"` Username string `json:"username"`
Mobile string `json:"mobile"`
Email string `json:"email"`
ChatRoles []string `json:"chat_roles"` ChatRoles []string `json:"chat_roles"`
ChatModels []int `json:"chat_models"` ChatModels []int `json:"chat_models"`
ExpiredTime string `json:"expired_time"` ExpiredTime string `json:"expired_time"`
@ -102,6 +104,8 @@ func (h *UserHandler) Save(c *gin.Context) {
} }
var oldPower = user.Power var oldPower = user.Power
user.Username = data.Username user.Username = data.Username
user.Email = data.Email
user.Mobile = data.Mobile
user.Status = data.Status user.Status = data.Status
user.Vip = data.Vip user.Vip = data.Vip
user.Power = data.Power user.Power = data.Power
@ -109,7 +113,8 @@ func (h *UserHandler) Save(c *gin.Context) {
user.ChatModels = utils.JsonEncode(data.ChatModels) user.ChatModels = utils.JsonEncode(data.ChatModels)
user.ExpiredTime = utils.Str2stamp(data.ExpiredTime) 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 { if res.Error != nil {
logger.Error("error with update database", res.Error) logger.Error("error with update database", res.Error)
resp.ERROR(c, res.Error.Error()) resp.ERROR(c, res.Error.Error())
@ -147,6 +152,8 @@ func (h *UserHandler) Save(c *gin.Context) {
u := model.User{ u := model.User{
Username: data.Username, Username: data.Username,
Password: utils.GenPassword(data.Password, salt), Password: utils.GenPassword(data.Password, salt),
Mobile: data.Mobile,
Email: data.Email,
Avatar: "/images/avatar/user.png", Avatar: "/images/avatar/user.png",
Salt: salt, Salt: salt,
Power: data.Power, Power: data.Power,

View File

@ -74,6 +74,20 @@ func (h *UserHandler) Register(c *gin.Context) {
resp.ERROR(c, types.InvalidArgs) resp.ERROR(c, types.InvalidArgs)
return 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) data.Password = strings.TrimSpace(data.Password)
if len(data.Password) < 8 { if len(data.Password) < 8 {
resp.ERROR(c, "密码长度不能少于8个字符") resp.ERROR(c, "密码长度不能少于8个字符")

View File

@ -146,7 +146,7 @@ func main() {
fx.Provide(admin.NewAdminHandler), fx.Provide(admin.NewAdminHandler),
fx.Provide(admin.NewApiKeyHandler), fx.Provide(admin.NewApiKeyHandler),
fx.Provide(admin.NewUserHandler), fx.Provide(admin.NewUserHandler),
fx.Provide(admin.NewChatRoleHandler), fx.Provide(admin.NewChatAppHandler),
fx.Provide(admin.NewRedeemHandler), fx.Provide(admin.NewRedeemHandler),
fx.Provide(admin.NewDashboardHandler), fx.Provide(admin.NewDashboardHandler),
fx.Provide(admin.NewChatModelHandler), fx.Provide(admin.NewChatModelHandler),
@ -338,7 +338,7 @@ func main() {
group.GET("loginLog", h.LoginLog) group.GET("loginLog", h.LoginLog)
group.POST("resetPass", h.ResetPass) 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 := s.Engine.Group("/api/admin/role/")
group.GET("list", h.List) group.GET("list", h.List)
group.POST("save", h.Save) group.POST("save", h.Save)
@ -502,6 +502,15 @@ func main() {
group.GET("remove", h.Remove) group.GET("remove", h.Remove)
group.GET("publish", h.Publish) 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.Provide(handler.NewTestHandler),
fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) { fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) {
group := s.Engine.Group("/api/test") group := s.Engine.Group("/api/test")

View File

@ -0,0 +1,11 @@
package model
import "time"
type AppType struct {
Id uint `gorm:"primarykey"`
Name string
Icon string
SortNum int
CreatedAt time.Time
}

12
api/store/vo/app_type.go Normal file
View File

@ -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"` // 最后使用时间
}

View File

@ -384,7 +384,7 @@ const submitRegister = () => {
if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') { if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') {
return ElMessage.error('请输入验证码'); return ElMessage.error('请输入验证码');
} }
if (enableVerify.value) { if (enableVerify.value && activeName.value === 'username') {
captchaRef.value.loadCaptcha() captchaRef.value.loadCaptcha()
action.value = "register" action.value = "register"
} else { } else {

View File

@ -90,12 +90,22 @@ const items = [
index: '/admin/user', index: '/admin/user',
title: '用户管理', title: '用户管理',
}, },
{ {
icon: 'menu', icon: 'menu',
index: '/admin/app', index: '1',
title: '应用管理', title: '应用管理',
subs: [
{
index: '/admin/app',
title: '应用列表',
},
{
index: '/admin/app/type',
title: '应用分类',
},
],
}, },
{ {
icon: 'api-key', icon: 'api-key',
index: '/admin/apikey', index: '/admin/apikey',

View File

@ -173,6 +173,12 @@ const routes = [
meta: {title: '应用管理'}, meta: {title: '应用管理'},
component: () => import('@/views/admin/Apps.vue'), 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', path: '/admin/apikey',
name: 'admin-apikey', name: 'admin-apikey',

View File

@ -16,7 +16,7 @@
<div class="block"> <div class="block">
<el-input placeholder="手机号码" <el-input placeholder="手机号码"
size="large" size="large"
v-model="data.username" v-model="data.mobile"
maxlength="11" maxlength="11"
autocomplete="off"> autocomplete="off">
<template #prefix> <template #prefix>
@ -41,7 +41,7 @@
</el-input> </el-input>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<send-msg size="large" :receiver="data.username" type="mobile"/> <send-msg size="large" :receiver="data.mobile" type="mobile"/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -195,6 +195,8 @@ const title = ref('');
const logo = ref("") const logo = ref("")
const data = ref({ const data = ref({
username: '', username: '',
mobile: '',
email: '',
password: '', password: '',
code: '', code: '',
repass: '', repass: '',
@ -250,15 +252,15 @@ getLicenseInfo().then(res => {
// //
const submitRegister = () => { const submitRegister = () => {
if (data.value.username === '') { if (activeName.value === 'username' && data.value.username === '') {
return showMessageError('请输入用户名'); return showMessageError('请输入用户名');
} }
if (activeName.value === 'mobile' && !validateMobile(data.value.username)) { if (activeName.value === 'mobile' && !validateMobile(data.value.mobile)) {
return showMessageError('请输入合法的手机号'); return showMessageError('请输入合法的手机号');
} }
if (activeName.value === 'email' && !validateEmail(data.value.username)) { if (activeName.value === 'email' && !validateEmail(data.value.email)) {
return showMessageError('请输入合法的邮箱地址'); return showMessageError('请输入合法的邮箱地址');
} }
@ -273,7 +275,8 @@ const submitRegister = () => {
return showMessageError('请输入验证码'); return showMessageError('请输入验证码');
} }
if (enableVerify.value) { //
if (enableVerify.value && activeName.value === 'username') {
captchaRef.value.loadCaptcha() captchaRef.value.loadCaptcha()
} else { } else {
doSubmitRegister({}) doSubmitRegister({})

View File

@ -0,0 +1,13 @@
<template>
<div class="container app-type" >
</div>
</template>
<script setup>
</script>
<style lang="stylus" scoped>
</style>

View File

@ -18,6 +18,8 @@
<el-image v-if="scope.row.vip" :src="vipImg" style="height: 20px;position: relative; top:5px; left: 5px"/> <el-image v-if="scope.row.vip" :src="vipImg" style="height: 20px;position: relative; top:5px; left: 5px"/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="mobile" label="手机"/>
<el-table-column prop="email" label="邮箱"/>
<el-table-column prop="nickname" label="昵称"/> <el-table-column prop="nickname" label="昵称"/>
<el-table-column prop="power" label="剩余算力"/> <el-table-column prop="power" label="剩余算力"/>
<el-table-column label="状态" width="80"> <el-table-column label="状态" width="80">
@ -73,6 +75,12 @@
<el-form-item label="账号:" prop="username"> <el-form-item label="账号:" prop="username">
<el-input v-model="user.username" autocomplete="off"/> <el-input v-model="user.username" autocomplete="off"/>
</el-form-item> </el-form-item>
<el-form-item label="手机:" prop="mobile">
<el-input v-model="user.mobile" autocomplete="off"/>
</el-form-item>
<el-form-item label="邮箱:" prop="email">
<el-input v-model="user.email" autocomplete="off"/>
</el-form-item>
<el-form-item v-if="add" label="密码:" prop="password"> <el-form-item v-if="add" label="密码:" prop="password">
<el-input v-model="user.password" autocomplete="off" placeholder="8-16位"/> <el-input v-model="user.password" autocomplete="off" placeholder="8-16位"/>
</el-form-item> </el-form-item>