diff --git a/api/core/app_server.go b/api/core/app_server.go index 6d236e73..dd99a755 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -237,6 +237,7 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/payment/doPay" || c.Request.URL.Path == "/api/payment/payWays" || strings.HasPrefix(c.Request.URL.Path, "/api/test") || + strings.HasPrefix(c.Request.URL.Path, "/api/user/clogin") || strings.HasPrefix(c.Request.URL.Path, "/api/config/") || strings.HasPrefix(c.Request.URL.Path, "/api/function/") || strings.HasPrefix(c.Request.URL.Path, "/api/sms/") || diff --git a/api/go.mod b/api/go.mod index 28516363..2a8efd45 100644 --- a/api/go.mod +++ b/api/go.mod @@ -29,7 +29,8 @@ require github.com/xxl-job/xxl-job-executor-go v1.2.0 require ( github.com/go-pay/gopay v1.5.101 github.com/google/go-tika v0.3.1 - github.com/mojocn/base64Captcha v1.3.1 + github.com/microcosm-cc/bluemonday v1.0.26 + github.com/mojocn/base64Captcha v1.3.6 github.com/shirou/gopsutil v3.21.11+incompatible github.com/shopspring/decimal v1.3.1 github.com/syndtr/goleveldb v1.0.0 @@ -47,7 +48,6 @@ require ( github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect diff --git a/api/go.sum b/api/go.sum index e88deff9..c0179e87 100644 --- a/api/go.sum +++ b/api/go.sum @@ -157,8 +157,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0= -github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw= +github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -265,7 +265,7 @@ golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -321,6 +321,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/api/handler/admin/captcha_handler.go b/api/handler/admin/captcha_handler.go deleted file mode 100644 index e6b81cbf..00000000 --- a/api/handler/admin/captcha_handler.go +++ /dev/null @@ -1,46 +0,0 @@ -package admin - -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -import ( - "geekai/core" - "geekai/handler" - "geekai/utils/resp" - "github.com/gin-gonic/gin" - "github.com/mojocn/base64Captcha" -) - -type CaptchaHandler struct { - handler.BaseHandler -} - -func NewCaptchaHandler(app *core.AppServer) *CaptchaHandler { - return &CaptchaHandler{BaseHandler: handler.BaseHandler{App: app}} -} - -type CaptchaVo struct { - CaptchaId string `json:"captcha_id"` - PicPath string `json:"pic_path"` -} - -// GetCaptcha 获取验证码 -func (h *CaptchaHandler) GetCaptcha(c *gin.Context) { - var captchaVo CaptchaVo - driver := base64Captcha.NewDriverDigit(48, 130, 4, 0.4, 10) - cp := base64Captcha.NewCaptcha(driver, base64Captcha.DefaultMemStore) - // b64s是图片的base64编码 - id, b64s, err := cp.Generate() - if err != nil { - resp.ERROR(c, "生成验证码错误!") - return - } - captchaVo.CaptchaId = id - captchaVo.PicPath = b64s - - resp.SUCCESS(c, captchaVo) -} diff --git a/api/handler/admin/chat_model_handler.go b/api/handler/admin/chat_model_handler.go index 74f187cc..3a1e6328 100644 --- a/api/handler/admin/chat_model_handler.go +++ b/api/handler/admin/chat_model_handler.go @@ -54,7 +54,6 @@ func (h *ChatModelHandler) Save(c *gin.Context) { Name: data.Name, Value: data.Value, Enabled: data.Enabled, - SortNum: data.SortNum, Open: data.Open, MaxTokens: data.MaxTokens, MaxContext: data.MaxContext, @@ -64,6 +63,7 @@ func (h *ChatModelHandler) Save(c *gin.Context) { var res *gorm.DB if data.Id > 0 { item.Id = data.Id + item.SortNum = data.SortNum res = h.DB.Select("*").Omit("created_at").Updates(&item) } else { res = h.DB.Create(&item) diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go index 95da105f..34747d77 100644 --- a/api/handler/admin/user_handler.go +++ b/api/handler/admin/user_handler.go @@ -139,7 +139,6 @@ func (h *UserHandler) Save(c *gin.Context) { salt := utils.RandString(8) u := model.User{ Username: data.Username, - Nickname: fmt.Sprintf("极客学长@%d", utils.RandomNumber(6)), Password: utils.GenPassword(data.Password, salt), Avatar: "/images/avatar/user.png", Salt: salt, @@ -149,6 +148,11 @@ func (h *UserHandler) Save(c *gin.Context) { ChatModels: utils.JsonEncode(data.ChatModels), ExpiredTime: utils.Str2stamp(data.ExpiredTime), } + if h.licenseService.GetLicense().Configs.DeCopy { + u.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6)) + } else { + u.Nickname = fmt.Sprintf("极客学长@%d", utils.RandomNumber(6)) + } res = h.DB.Create(&u) _ = utils.CopyObject(u, &userVo) userVo.Id = u.Id diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go index 707e7f4f..e220009a 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_role_handler.go @@ -65,7 +65,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) { } for _, r := range roles { - if !utils.ContainsStr(roleKeys, r.Key) { + if !utils.Contains(roleKeys, r.Key) { continue } var v vo.ChatRole diff --git a/api/handler/chatimpl/openai_handler.go b/api/handler/chatimpl/openai_handler.go index 86089410..97f76531 100644 --- a/api/handler/chatimpl/openai_handler.go +++ b/api/handler/chatimpl/openai_handler.go @@ -74,6 +74,9 @@ func (h *ChatHandler) sendOpenAiMessage( if len(responseBody.Choices) == 0 { // Fixed: 兼容 Azure API 第一个输出空行 continue } + if responseBody.Choices[0].Delta.Content == nil { + continue + } if responseBody.Choices[0].FinishReason == "stop" && len(contents) == 0 { utils.ReplyMessage(ws, "抱歉😔😔😔,AI助手由于未知原因已经停止输出内容。") diff --git a/api/handler/sms_handler.go b/api/handler/sms_handler.go index 7740a77e..38a61da6 100644 --- a/api/handler/sms_handler.go +++ b/api/handler/sms_handler.go @@ -64,13 +64,13 @@ func (h *SmsHandler) SendCode(c *gin.Context) { code := utils.RandomNumber(6) var err error if strings.Contains(data.Receiver, "@") { // email - if !utils.ContainsStr(h.App.SysConfig.RegisterWays, "email") { + if !utils.Contains(h.App.SysConfig.RegisterWays, "email") { resp.ERROR(c, "系统已禁用邮箱注册!") return } err = h.smtp.SendVerifyCode(data.Receiver, code) } else { - if !utils.ContainsStr(h.App.SysConfig.RegisterWays, "mobile") { + if !utils.Contains(h.App.SysConfig.RegisterWays, "mobile") { resp.ERROR(c, "系统已禁用手机号注册!") return } diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 74723c78..deae433d 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -16,6 +16,7 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "github.com/imroc/req/v3" "strings" "time" @@ -97,7 +98,7 @@ func (h *UserHandler) Register(c *gin.Context) { } } - // check if the username is exists + // check if the username is existing var item model.User res := h.DB.Where("username = ?", data.Username).First(&item) if item.Id > 0 { @@ -255,6 +256,40 @@ func (h *UserHandler) Logout(c *gin.Context) { resp.SUCCESS(c) } +// CLoginRequest 第三方登录请求二维码 +func (h *UserHandler) CLoginRequest(c *gin.Context) { + returnURL := h.GetTrim(c, "return_url") + var res types.BizVo + apiURL := fmt.Sprintf("%s/api/clogin/request", h.App.Config.ApiConfig.ApiURL) + r, err := req.C().R().SetBody(gin.H{"login_type": "wx", "return_url": returnURL}). + SetHeader("AppId", h.App.Config.ApiConfig.AppId). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.App.Config.ApiConfig.Token)). + SetSuccessResult(&res). + Post(apiURL) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + if r.IsErrorState() { + resp.ERROR(c, "error with login http status: "+r.Status) + return + } + + if res.Code != types.Success { + resp.ERROR(c, "error with http response: "+res.Message) + return + } + + resp.SUCCESS(c, res.Data) +} + +// CLoginCallback 第三方登录回调 +func (h *UserHandler) CLoginCallback(c *gin.Context) { + //platform := h.GetTrim(c, "type") + //code := h.GetTrim(c, "code") + +} + // Session 获取/验证会话 func (h *UserHandler) Session(c *gin.Context) { user, err := h.GetLoginUser(c) diff --git a/api/main.go b/api/main.go index b2c40dd6..d95358f9 100644 --- a/api/main.go +++ b/api/main.go @@ -240,6 +240,8 @@ func main() { group.POST("password", h.UpdatePass) group.POST("bind/username", h.BindUsername) group.POST("resetPass", h.ResetPass) + group.GET("clogin/request", h.CLoginRequest) + group.GET("clogin/callback", h.CLoginCallback) }), fx.Invoke(func(s *core.AppServer, h *chatimpl.ChatHandler) { group := s.Engine.Group("/api/chat/") @@ -416,13 +418,6 @@ func main() { group.GET("token", h.GenToken) }), - // 验证码 - fx.Provide(admin.NewCaptchaHandler), - fx.Invoke(func(s *core.AppServer, h *admin.CaptchaHandler) { - group := s.Engine.Group("/api/admin/login/") - group.GET("captcha", h.GetCaptcha) - }), - fx.Provide(admin.NewUploadHandler), fx.Invoke(func(s *core.AppServer, h *admin.UploadHandler) { s.Engine.POST("/api/admin/upload", h.Upload) diff --git a/api/service/mj/pool.go b/api/service/mj/pool.go index 3eacfd78..7cf84a60 100644 --- a/api/service/mj/pool.go +++ b/api/service/mj/pool.go @@ -16,6 +16,7 @@ import ( "geekai/service/sd" "geekai/store" "geekai/store/model" + "geekai/utils" "github.com/go-redis/redis/v8" "strings" "time" @@ -58,13 +59,13 @@ func (p *ServicePool) InitServices(plusConfigs []types.MjPlusConfig, proxyConfig } p.services = make([]*Service, 0) - for k, config := range plusConfigs { + for _, config := range plusConfigs { if config.Enabled == false { continue } cli := NewPlusClient(config, p.licenseService) - name := fmt.Sprintf("mj-plus-service-%d", k) + name := utils.Md5(config.ApiURL) plusService := NewService(name, p.taskQueue, p.notifyQueue, p.db, cli) go func() { plusService.Run() @@ -73,12 +74,12 @@ func (p *ServicePool) InitServices(plusConfigs []types.MjPlusConfig, proxyConfig } // for mid-journey proxy - for k, config := range proxyConfigs { + for _, config := range proxyConfigs { if config.Enabled == false { continue } cli := NewProxyClient(config) - name := fmt.Sprintf("mj-proxy-service-%d", k) + name := utils.Md5(config.ApiURL) proxyService := NewService(name, p.taskQueue, p.notifyQueue, p.db, cli) go func() { proxyService.Run() @@ -209,7 +210,6 @@ func (p *ServicePool) SyncTaskProgress() { } continue } - if servicePlus := p.getService(job.ChannelId); servicePlus != nil { _ = servicePlus.Notify(job) } diff --git a/api/store/model/user.go b/api/store/model/user.go index 41d09905..bc2bafec 100644 --- a/api/store/model/user.go +++ b/api/store/model/user.go @@ -15,5 +15,7 @@ type User struct { Status bool `gorm:"default:true"` // 当前状态 LastLoginAt int64 // 最后登录时间 LastLoginIp string // 最后登录 IP + OpenId string `gorm:"column:openid"` + Platform string `json:"platform"` Vip bool // 是否 VIP 会员 } diff --git a/api/store/vo/user.go b/api/store/vo/user.go index 560f57db..8e0faabe 100644 --- a/api/store/vo/user.go +++ b/api/store/vo/user.go @@ -14,4 +14,6 @@ type User struct { LastLoginAt int64 `json:"last_login_at"` // 最后登录时间 LastLoginIp string `json:"last_login_ip"` // 最后登录 IP Vip bool `json:"vip"` + OpenId string `json:"openid"` // 第三方登录 OpenID + Platform string `json:"platform"` // 第三方登录平台 } diff --git a/api/utils/strings.go b/api/utils/strings.go index ff5c28e6..feb377de 100644 --- a/api/utils/strings.go +++ b/api/utils/strings.go @@ -31,11 +31,11 @@ func RandString(length int) string { } func RandomNumber(bit int) int { - min := intPow(10, bit-1) - max := intPow(10, bit) - 1 + minNum := intPow(10, bit-1) + maxNum := intPow(10, bit) - 1 - rand.Seed(time.Now().UnixNano()) - return rand.Intn(max-min+1) + min + rand.NewSource(time.Now().UnixNano()) + return rand.Intn(maxNum-minNum+1) + minNum } func intPow(x, y int) int { @@ -46,7 +46,7 @@ func intPow(x, y int) int { return result } -func ContainsStr(slice []string, item string) bool { +func Contains(slice []string, item string) bool { for _, e := range slice { if e == item { return true diff --git a/database/update-v4.1.0.sql b/database/update-v4.1.0.sql index ba309dc9..61ed1a8a 100644 --- a/database/update-v4.1.0.sql +++ b/database/update-v4.1.0.sql @@ -1 +1,3 @@ -ALTER TABLE `chatgpt_chat_models` CHANGE `power` `power` SMALLINT NOT NULL COMMENT '消耗算力点数'; \ No newline at end of file +ALTER TABLE `chatgpt_chat_models` CHANGE `power` `power` SMALLINT NOT NULL COMMENT '消耗算力点数'; +ALTER TABLE `chatgpt_users` ADD `openid` VARCHAR(100) NULL COMMENT '第三方登录账号ID' AFTER `last_login_ip`; +ALTER TABLE `chatgpt_users` ADD `platform` VARCHAR(30) NULL COMMENT '登录平台' AFTER `openid`; \ No newline at end of file diff --git a/web/src/components/ChatPrompt.vue b/web/src/components/ChatPrompt.vue index 56fc4987..746a9907 100644 --- a/web/src/components/ChatPrompt.vue +++ b/web/src/components/ChatPrompt.vue @@ -312,6 +312,7 @@ onMounted(() => { .chat-item { padding: 0; overflow: hidden; + max-width 60% .file-list-box { display flex diff --git a/web/src/components/ChatReply.vue b/web/src/components/ChatReply.vue index 60982347..033fefe5 100644 --- a/web/src/components/ChatReply.vue +++ b/web/src/components/ChatReply.vue @@ -364,6 +364,7 @@ const reGenerate = (prompt) => { position: relative; padding: 0; overflow: hidden; + max-width 60% .content-wrapper { display flex diff --git a/web/src/views/Login.vue b/web/src/views/Login.vue index bb71ed43..ee8a0332 100644 --- a/web/src/views/Login.vue +++ b/web/src/views/Login.vue @@ -34,12 +34,17 @@ 登录 - - 注册 - + + + + + + + 注册 + 重置密码 - + 首页 @@ -57,7 +62,7 @@