diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d3dd7b2..9b9b7840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 更新日志 +## v4.0.6 + +* Bug修复:修复PC端画廊页面的瀑布流组件样式错乱问题 + ## v4.0.5 * 功能优化:已授权系统在后台显示授权信息 diff --git a/api/Makefile b/api/Makefile index deafd9ff..e72ae935 100644 --- a/api/Makefile +++ b/api/Makefile @@ -1,5 +1,5 @@ SHELL=/usr/bin/env bash -NAME := chatgpt-plus +NAME := geekai all: amd64 arm64 amd64: diff --git a/api/core/app_server.go b/api/core/app_server.go index 0b8932aa..7c6b99af 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -207,10 +207,13 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc { func needLogin(c *gin.Context) bool { if c.Request.URL.Path == "/api/user/login" || + c.Request.URL.Path == "/api/user/logout" || c.Request.URL.Path == "/api/user/resetPass" || c.Request.URL.Path == "/api/admin/login" || + c.Request.URL.Path == "/api/admin/logout" || c.Request.URL.Path == "/api/admin/login/captcha" || c.Request.URL.Path == "/api/user/register" || + c.Request.URL.Path == "/api/user/session" || c.Request.URL.Path == "/api/chat/history" || c.Request.URL.Path == "/api/chat/detail" || c.Request.URL.Path == "/api/chat/list" || diff --git a/api/handler/chatimpl/chat_handler.go b/api/handler/chatimpl/chat_handler.go index b81f530b..8f420ee7 100644 --- a/api/handler/chatimpl/chat_handler.go +++ b/api/handler/chatimpl/chat_handler.go @@ -144,6 +144,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) { for { _, msg, err := client.Receive() if err != nil { + logger.Debugf("close connection: %s", client.Conn.RemoteAddr()) client.Close() h.App.ChatClients.Delete(sessionId) h.App.ChatSession.Delete(sessionId) diff --git a/api/handler/function_handler.go b/api/handler/function_handler.go index f1702079..6917efde 100644 --- a/api/handler/function_handler.go +++ b/api/handler/function_handler.go @@ -188,6 +188,11 @@ func (h *FunctionHandler) Dall3(c *gin.Context) { return } + if user.Power < h.App.SysConfig.DallPower { + resp.ERROR(c, "创建 DALL-E 绘图任务失败,算力不足") + return + } + // create dall task prompt := utils.InterfaceToString(params["prompt"]) job := model.DallJob{ diff --git a/api/service/license_service.go b/api/service/license_service.go index 482ab428..d31eaaf7 100644 --- a/api/service/license_service.go +++ b/api/service/license_service.go @@ -13,21 +13,22 @@ import ( "geekai/core" "geekai/core/types" "geekai/store" - "github.com/imroc/req/v3" - "github.com/shirou/gopsutil/host" "strings" "time" + + "github.com/imroc/req/v3" + "github.com/shirou/gopsutil/host" ) type LicenseService struct { config types.ApiConfig levelDB *store.LevelDB - license types.License + license *types.License urlWhiteList []string machineId string } -func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) * LicenseService { +func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) *LicenseService { var license types.License var machineId string _ = levelDB.Get(types.LicenseKey, &license) @@ -35,18 +36,20 @@ func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) * License if err == nil { machineId = info.HostID } + logger.Infof("License: %+v", license) return &LicenseService{ config: server.Config.ApiConfig, levelDB: levelDB, - license: license, + license: &license, machineId: machineId, } } type License struct { Name string `json:"name"` - Value string `json:"license"` + License string `json:"license"` Mid string `json:"mid"` + ActiveAt int64 `json:"active_at"` ExpiredAt int64 `json:"expired_at"` UserNum int `json:"user_num"` } @@ -67,14 +70,14 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error { } if response.IsErrorState() { - return fmt.Errorf( "发送激活请求失败:%v", response.Status) + return fmt.Errorf("发送激活请求失败:%v", response.Status) } if res.Code != types.Success { return fmt.Errorf("激活失败:%v", res.Message) } - s.license = types.License{ + s.license = &types.License{ Key: license, MachineId: machineId, UserNum: res.Data.UserNum, @@ -91,50 +94,81 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error { // SyncLicense 定期同步 License func (s *LicenseService) SyncLicense() { go func() { + retryCounter := 0 for { - var res struct { - Code types.BizCode `json:"code"` - Message string `json:"message"` - Data struct { - License License `json:"license"` - Urls []string `json:"urls"` - } - } - apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/check") - response, err := req.C().R(). - SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}). - SetSuccessResult(&res).Post(apiURL) + license, err := s.fetchLicense() if err != nil { - logger.Errorf("发送激活请求失败: %v", err) - goto next - } - if response.IsErrorState() { - logger.Errorf("激活失败:%v", response.Status) - goto next - } - if res.Code != types.Success { - logger.Errorf("激活失败:%v", res.Message) + retryCounter++ + if retryCounter < 5 { + logger.Error(err) + } s.license.IsActive = false - goto next + } else { + s.license = license } - s.license = types.License{ - Key: res.Data.License.Value, - MachineId: res.Data.License.Mid, - UserNum: res.Data.License.UserNum, - ExpiredAt: res.Data.License.ExpiredAt, - IsActive: true, + urls, err := s.fetchUrlWhiteList() + if err == nil { + s.urlWhiteList = urls } - s.urlWhiteList = res.Data.Urls - logger.Debugf("同步 License 成功:%v\n%v", s.license, s.urlWhiteList) - next: + time.Sleep(time.Second * 10) } }() } +func (s *LicenseService) fetchLicense() (*types.License, error) { + var res struct { + Code types.BizCode `json:"code"` + Message string `json:"message"` + Data License `json:"data"` + } + apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/check") + response, err := req.C().R(). + SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}). + SetSuccessResult(&res).Post(apiURL) + if err != nil { + return nil, fmt.Errorf("发送激活请求失败: %v", err) + } + if response.IsErrorState() { + return nil, fmt.Errorf("激活失败:%v", response.Status) + } + if res.Code != types.Success { + return nil, fmt.Errorf("激活失败:%v", res.Message) + } + + return &types.License{ + Key: res.Data.License, + MachineId: res.Data.Mid, + UserNum: res.Data.UserNum, + ExpiredAt: res.Data.ExpiredAt, + IsActive: true, + }, nil +} + +func (s *LicenseService) fetchUrlWhiteList() ([]string, error) { + var res struct { + Code types.BizCode `json:"code"` + Message string `json:"message"` + Data []string `json:"data"` + } + apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/urls") + response, err := req.C().R().SetSuccessResult(&res).Get(apiURL) + if err != nil { + return nil, fmt.Errorf("发送请求失败: %v", err) + } + if response.IsErrorState() { + return nil, fmt.Errorf("发送请求失败:%v", response.Status) + } + if res.Code != types.Success { + return nil, fmt.Errorf("获取白名单失败:%v", res.Message) + } + + return res.Data, nil +} + // GetLicense 获取许可信息 -func (s *LicenseService) GetLicense() types.License { +func (s *LicenseService) GetLicense() *types.License { return s.license } @@ -158,4 +192,4 @@ func (s *LicenseService) IsValidApiURL(uri string) error { } } return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri) -} \ No newline at end of file +} diff --git a/build/build.sh b/build/build.sh index a1d37431..6505f9f3 100755 --- a/build/build.sh +++ b/build/build.sh @@ -14,15 +14,15 @@ npm run build cd ../build # remove docker image if exists -docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version-$arch -# build docker image for chatgpt-plus-go -docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version-$arch -f dockerfile-api-go ../ +docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch +# build docker image for Geek-AI API +docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch -f dockerfile-api-go ../ -# build docker image for chatgpt-plus-vue -docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version-$arch -docker build --platform linux/amd64 -t registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version-$arch -f dockerfile-vue ../ +# build docker image for Geek-AI-web +docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch +docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch -f dockerfile-vue ../ if [ "$3" = "push" ];then - docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-api:$version-$arch - docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/chatgpt-plus-web:$version-$arch + docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch + docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch fi diff --git a/build/dockerfile-api-go b/build/dockerfile-api-go index 6c701581..a865f430 100644 --- a/build/dockerfile-api-go +++ b/build/dockerfile-api-go @@ -4,9 +4,9 @@ FROM registry.cn-hangzhou.aliyuncs.com/geekmaster/alpine:3.18.2 MAINTAINER yangjian WORKDIR /var/www/app -COPY ./api/bin/chatgpt-plus-linux /var/www/app +COPY ./api/bin/geekai-linux /var/www/app EXPOSE 5678 # 容器启动时执行的命令 -CMD ["./chatgpt-plus-linux"] +CMD ["./geekai-linux"] diff --git a/web/src/App.vue b/web/src/App.vue index ae0a0856..c57704ea 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -66,4 +66,14 @@ html, body { white-space: nowrap; } +.van-toast--fail { + background #fef0f0 + color #f56c6c +} + +.van-toast--success { + background #D6FBCC + color #07C160 +} + diff --git a/web/src/assets/css/mobile/chat-session.styl b/web/src/assets/css/mobile/chat-session.styl index 25aa54d0..65a73fd3 100644 --- a/web/src/assets/css/mobile/chat-session.styl +++ b/web/src/assets/css/mobile/chat-session.styl @@ -9,7 +9,7 @@ .chat-list-wrapper { padding 10px 0 10px 0 - background #F5F5F5; + background var(--van-background); overflow hidden @@ -81,7 +81,7 @@ .van-theme-dark { .mobile-chat { - .message-list-box { + .chat-list-wrapper { background #232425; } } diff --git a/web/src/assets/css/mobile/image-mj.styl b/web/src/assets/css/mobile/image-mj.styl index c98f1587..8989946b 100644 --- a/web/src/assets/css/mobile/image-mj.styl +++ b/web/src/assets/css/mobile/image-mj.styl @@ -74,6 +74,7 @@ } + color var(--van-text-color) .pt-6 { padding 15px 10px } diff --git a/web/src/assets/css/mobile/image-sd.styl b/web/src/assets/css/mobile/image-sd.styl index 60d5fcc1..d6f07cc3 100644 --- a/web/src/assets/css/mobile/image-sd.styl +++ b/web/src/assets/css/mobile/image-sd.styl @@ -80,6 +80,8 @@ } + color var(--van-text-color) + .pt-6 { padding 15px 10px } diff --git a/web/src/components/SendMsg.vue b/web/src/components/SendMsg.vue index 19364531..435fccc8 100644 --- a/web/src/components/SendMsg.vue +++ b/web/src/components/SendMsg.vue @@ -38,11 +38,11 @@ import {ref} from "vue"; import lodash from 'lodash' import {validateEmail, validateMobile} from "@/utils/validate"; -import {ElMessage} from "element-plus"; import {httpGet, httpPost} from "@/utils/http"; import CaptchaPlus from "@/components/CaptchaPlus.vue"; import SlideCaptcha from "@/components/SlideCaptcha.vue"; import {isMobile} from "@/utils/libs"; +import {showMessageError, showMessageOK} from "@/utils/dialog"; // eslint-disable-next-line no-undef const props = defineProps({ @@ -66,13 +66,13 @@ const handleRequestCaptCode = () => { thumbBase64.value = data.thumb captKey.value = data.key }).catch(e => { - ElMessage.error('获取人机验证数据失败:' + e.message) + showMessageError('获取人机验证数据失败:' + e.message) }) } const handleConfirm = (dots) => { if (lodash.size(dots) <= 0) { - return ElMessage.error('请进行人机验证再操作') + return showMessageError('请进行人机验证再操作') } let dotArr = [] @@ -88,14 +88,14 @@ const handleConfirm = (dots) => { showCaptcha.value = false sendMsg() }).catch(() => { - ElMessage.error('人机验证失败') + showMessageError('人机验证失败') handleRequestCaptCode() }) } const loadCaptcha = () => { if (!validateMobile(props.receiver) && !validateEmail(props.receiver)) { - return ElMessage.error("请输入合法的手机号/邮箱地址") + return showMessageError("请输入合法的手机号/邮箱地址") } showCaptcha.value = true @@ -114,7 +114,7 @@ const sendMsg = () => { canSend.value = false httpPost('/api/sms/code', {receiver: props.receiver, key: captKey.value, dots: dots.value}).then(() => { - ElMessage.success('验证码发送成功') + showMessageOK('验证码发送成功') let time = 120 btnText.value = time const handler = setInterval(() => { @@ -129,7 +129,7 @@ const sendMsg = () => { }, 1000) }).catch(e => { canSend.value = true - ElMessage.error('验证码发送失败:' + e.message) + showMessageError('验证码发送失败:' + e.message) }) } @@ -145,7 +145,7 @@ const getSlideCaptcha = () => { bgImg.value = res.data.bgImg captKey.value = res.data.key }).catch(e => { - ElMessage.error('获取人机验证数据失败:' + e.message) + showMessageError('获取人机验证数据失败:' + e.message) }) } diff --git a/web/src/utils/dialog.js b/web/src/utils/dialog.js new file mode 100644 index 00000000..7be47aee --- /dev/null +++ b/web/src/utils/dialog.js @@ -0,0 +1,43 @@ +/** + * Util lib functions + */ +import {showConfirmDialog, showFailToast, showSuccessToast, showToast} from "vant"; +import {isMobile} from "@/utils/libs"; +import {ElMessage} from "element-plus"; + +export function showLoginDialog(router) { + showConfirmDialog({ + title: '登录', + message: + '此操作需要登录才能进行,前往登录?', + }).then(() => { + router.push("/login") + }).catch(() => { + // on cancel + }); +} + +export function showMessageOK(message) { + if (isMobile()) { + showSuccessToast(message) + } else { + ElMessage.success(message) + } +} + + +export function showMessageInfo(message) { + if (isMobile()) { + showToast(message) + } else { + ElMessage.info(message) + } +} + +export function showMessageError(message) { + if (isMobile()) { + showFailToast(message) + } else { + ElMessage.error(message) + } +} diff --git a/web/src/utils/http.js b/web/src/utils/http.js index 665f9a8e..d144217d 100644 --- a/web/src/utils/http.js +++ b/web/src/utils/http.js @@ -6,7 +6,7 @@ // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import axios from 'axios' -import {getAdminToken, getSessionId, getUserToken} from "@/store/session"; +import {getAdminToken, getSessionId, getUserToken, removeAdminToken, removeUserToken} from "@/store/session"; axios.defaults.timeout = 180000 axios.defaults.baseURL = process.env.VUE_APP_API_HOST @@ -29,9 +29,14 @@ axios.interceptors.response.use( let data = response.data; if (data.code === 0) { return response - } else { - return Promise.reject(response.data) + } else if (data.code === 400) { + if (response.request.responseURL.indexOf("/api/admin") !== -1) { + removeAdminToken() + } else { + removeUserToken() + } } + return Promise.reject(response.data) }, error => { return Promise.reject(error) }) diff --git a/web/src/views/ChatApps.vue b/web/src/views/ChatApps.vue index e0225094..9fe0cea0 100644 --- a/web/src/views/ChatApps.vue +++ b/web/src/views/ChatApps.vue @@ -13,7 +13,7 @@ {{ scope.item.name }}
- 使用 + 使用 移除
{ } const router = useRouter() -const useRole = (roleId) => { - router.push({name: "chat", params: {role_id: roleId}}) +const useRole = (role) => { + router.push(`/chat?role_id=${role.id}`) } diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index ae08ffdc..49f31f0d 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -330,7 +330,10 @@ onMounted(() => { }); onUnmounted(() => { - socket.value = null + if (socket.value !== null) { + socket.value.close() + socket.value = null + } }) // 初始化数据 @@ -355,8 +358,8 @@ const initData = () => { // 加载角色列表 httpGet(`/api/role/list`).then((res) => { roles.value = res.data; - if (router.currentRoute.value.params.role_id) { - roleId.value = parseInt(router.currentRoute.value.params["role_id"]) + if (router.currentRoute.value.query.role_id) { + roleId.value = parseInt(router.currentRoute.value.query.role_id) } else { roleId.value = roles.value[0]['id'] } diff --git a/web/src/views/Dalle.vue b/web/src/views/Dalle.vue index 8e219175..1d56f412 100644 --- a/web/src/views/Dalle.vue +++ b/web/src/views/Dalle.vue @@ -105,7 +105,7 @@

创作记录

-
+