Compare commits

...

1172 Commits

Author SHA1 Message Date
RockYang b18b8ccb02 release: v4.2.8
整合开源版 v4.2.8 功能:Sora2 视频、路由重构、手机站开关、DALL-E 参考图,以及启动时自动同步数据表字段等修复与优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:12:37 +08:00
RockYang f8a01cb9a2 feat: 启动时自动同步 model 与数据表字段
用 AutoMigrate 补齐缺列,并删除 model 中已不存在的多余列(保护 id/时间戳),修复 ChatModel.desc 等字段缺失导致的 1054 错误。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 10:37:38 +08:00
RockYang 64832a2a04 update readme 2026-05-24 10:14:28 +08:00
RockYang b1dd781a3d update readme 2026-05-24 10:12:51 +08:00
RockYang 71dc082e40 Merge tag 'v4.2.7' 2026-05-09 20:56:07 +08:00
RockYang 10836bbb4a merge v4.2.6
整合 v4.2.6 的后端中间件与服务层重构、前端样式体系迁移和管理端/移动端功能更新,统一清理历史冲突并完成版本升级。

Made-with: Cursor
2026-04-08 15:08:34 +08:00
RockYang d8237ae301 merge v4.2.5 2026-04-05 21:35:30 +08:00
RockYang 3974f1be87 merge v4.2.4 2026-02-14 17:20:53 +08:00
RockYang 126c9921a8 merge v4.2.3 2026-01-29 20:11:50 +08:00
RockYang c84754fc0c merge v4.2.2 2025-12-02 11:18:30 +08:00
RockYang 172807d326 add EasyDevelop deploy docs 2025-12-02 11:15:54 +08:00
RockYang 72608af77e merge v4.2.1 2025-11-26 20:09:48 +08:00
RockYang 7879459c3f update docker images version to docker-compose.yaml 2025-11-11 11:07:22 +08:00
RockYang ad482dad7f merge v4.2.0 2025-11-11 10:17:03 +08:00
RockYang 58e99976b3 更新数据库文件 2025-10-27 16:25:58 +08:00
RockYang 567803ebeb 添加备案信息配置项,给登录页面 Logo 增加圆角 2025-10-27 16:25:58 +08:00
RockYang 8e3d67cc7f 优化编译指令,减少程序体积 2025-10-27 16:25:58 +08:00
RockYang b463608ab7 支持微模型绑定 Dalle 绘图的 API KEY 2025-10-27 16:25:58 +08:00
RockYang eefd36562d 管理后台用户算力日志页面增加过滤查询功能 2025-10-27 16:25:58 +08:00
RockYang ebf4497b34 允许配置登录注册页面的自定义 Logo 2025-10-27 16:25:58 +08:00
RockYang 194b0343bf 修复微信登录功能 2025-10-27 16:25:58 +08:00
RockYang adb1dfbded 首页增加码云连接地址 2025-10-27 16:25:58 +08:00
RockYang 9e689f6022 支持在 Chat 页面显示,隐藏对话列表 2025-10-27 16:25:55 +08:00
RockYang 499f5c915f merge and resolve conflicts 2025-10-27 16:21:46 +08:00
RockYang a9f11d79a9 optimize login page styles 2025-10-27 16:19:34 +08:00
RockYang db5e9ea2d0 更改文档地址 2025-10-27 16:19:34 +08:00
GeekMaster 64f8693871 Merge branch 'main' of gitee.com:blackfox/geekai-plus 2025-09-17 16:31:41 +08:00
GeekMaster 4270847b5d optimize regenerate for mobile chat page 2025-09-17 16:18:16 +08:00
GeekMaster 3eb0177188 add prompt edit function 2025-09-17 16:04:04 +08:00
GeekMaster 48203e0d31 optimize jimeng power config 2025-09-16 20:35:53 +08:00
GeekMaster 6e6a496f1b remove reset password page 2025-09-16 19:31:06 +08:00
GeekMaster ddfa588163 update version 2025-09-16 19:25:46 +08:00
GeekMaster 809d8d71bd Jimeng AI 4.0 for mobile is ready 2025-09-16 19:13:41 +08:00
GeekMaster 49254b2a32 optimize ParamBuilderMobile components 2025-09-15 22:18:46 +08:00
GeekMaster 5979451ea6 ParamBuilder component support hidden input 2025-09-15 21:05:34 +08:00
GeekMaster 46c3d65bea update change log file 2025-09-15 20:36:34 +08:00
GeekMaster c4b44d84e3 Jimeng VirtualHuman and actionTransfer is ready 2025-09-15 20:29:46 +08:00
RockYang 822d1831cd rm unuse certs 2025-09-13 10:48:00 +08:00
RockYang 0d28402aa3 fixed bug for sub user power to negtive nuber 2025-09-13 10:35:55 +08:00
RockYang 359999c045 check user power for create ai3d job 2025-09-13 10:29:40 +08:00
GeekMaster 2c6eee7fc1 acommpelish jimeng AI refactor for PC 2025-09-12 18:58:52 +08:00
GeekMaster c5badb3e13 add user lock for chat api, Prevent insufficient deduction of user power caused by submitting multiple requests at one time 2025-09-12 15:05:14 +08:00
GeekMaster 65fb58585c refactor parameters for JimengCreate page 2025-09-11 15:48:07 +08:00
RockYang 02aedd9edc Merge branch 'bug-fix' 2025-09-10 19:19:21 +08:00
GeekMaster 896b5de0a4 param builder component is ready 2025-09-10 17:04:37 +08:00
GeekMaster 1ca58606da optimize jimeng AI task params build 2025-09-09 18:05:51 +08:00
GeekMaster 3d8c37cb38 allow user to set default index page 2025-09-09 11:11:34 +08:00
GeekMaster c8eec6c97e Merge branch 'bug-fix' 2025-09-09 10:40:47 +08:00
RockYang 330ac397d4 fixed bug for change admin user password 2025-09-08 22:10:29 +08:00
RockYang 8aa75a49d2 fixed bug for can not change admin user password 2025-09-08 18:59:31 +08:00
GeekMaser 31fbf7e48a Merge pull request #247 from zayn-code/themechange
主题切换按钮:月亮图标重复了
2025-06-03 09:20:57 +08:00
GeekMaser e2e24078c5 Merge pull request #248 from zayn-code/chatlist
移动端会话列表:切换tab的时候有时会出现列表一直在加载中
2025-06-03 09:20:19 +08:00
zhangyao 39ef92e1ce 移动端会话列表:切换tab的时候有时会出现列表一直在加载中 2025-05-30 15:55:18 +08:00
zhangyao f63ba187dd 移动端会话列表:切换tab的时候有时会出现列表一直在加载中 2025-05-30 15:48:50 +08:00
zhangyao 23d1ac1021 主题切换按钮:月亮图标重复了 2025-05-30 15:46:33 +08:00
GeekMaser f08329eef4 Merge pull request #241 from coderwei99/chore/migrate-to-pnpm
Chore/migrate to pnpm
2025-05-18 12:24:19 +08:00
coderwei e993a582cd chore: 修正环境变量配置文件 2025-05-16 14:42:58 +08:00
coderwei 1524e189dc chore: migrate from npm to pnpm 2025-05-16 14:32:55 +08:00
GeekMaster 2b5165324c update docker image version 2025-04-17 10:22:41 +08:00
GeekMaster 8f67826072 update docker image version 2025-04-17 10:08:38 +08:00
GeekMaster ac9a31f049 merge code for v4.1.8 2025-04-17 10:07:04 +08:00
RockYang ed2cdbbc31 更新 README 2025-04-07 17:52:06 +08:00
RockYang 8bd2feaf7e Merge branch 'main' of gitee.com:blackfox/geekai 2025-03-25 10:48:46 +08:00
RockYang 4eccfe6d2c remove sensitive message for openai API KEY 2025-03-25 10:48:18 +08:00
RockYang 5ba2e000a1 update database file 2025-03-24 20:29:03 +08:00
RockYang fe9adadb3a update database file 2025-03-24 20:29:03 +08:00
RockYang 805c2a045e merge release v4.1.7 2025-03-24 17:13:16 +08:00
RockYang fd7b196134 merge release v4.1.7 2025-03-24 17:13:16 +08:00
RockYang 1b1c327c35 Merge tag 'v4.1.7' of gitee.com:blackfox/geekai-plus 2025-03-24 11:27:11 +08:00
RockYang a228f603d2 Merge tag 'v4.1.7' of gitee.com:blackfox/geekai-plus 2025-03-24 11:27:11 +08:00
RockYang 7d0a05ee11 update v4.1.5 sql file 2025-03-06 14:03:44 +08:00
RockYang 5f0a62b63e update v4.1.5 sql file 2025-03-06 14:03:44 +08:00
RockYang 825a1b1027 文档对话不显示文档列表的 bug 2025-03-06 14:00:13 +08:00
RockYang 38dde1a373 文档对话不显示文档列表的 bug 2025-03-06 14:00:13 +08:00
RockYang 950e7d1b00 Merge branch 'dev' 2025-03-05 18:42:44 +08:00
RockYang 89ded74345 Merge branch 'dev' 2025-03-05 18:42:44 +08:00
RockYang d8f9f48278 merge v4.1.6 2025-03-05 18:42:30 +08:00
RockYang 818bb38617 merge v4.1.6 2025-03-05 18:42:30 +08:00
GeekMaser bdd76addf3 Merge pull request #238 from gotoworld/main
v4.1.5中,存在Merge冲突脚本 #237
2025-02-25 09:27:15 +08:00
GeekMaser 0202d75ff4 Merge pull request #238 from gotoworld/main
v4.1.5中,存在Merge冲突脚本 #237
2025-02-25 09:27:15 +08:00
Welcome Aboard 2936f21f12 Merge pull request #1 from gotoworld/gotoworld-patch-1
Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql
2025-02-24 19:59:00 +08:00
Welcome Aboard a0831ec6cd Merge pull request #1 from gotoworld/gotoworld-patch-1
Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql
2025-02-24 19:59:00 +08:00
Welcome Aboard 705ca4d20a Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql 2025-02-24 19:57:40 +08:00
Welcome Aboard 2926aab7a4 Bugs: fixed merge conflict in geekai_plus-v4.1.5.sql 2025-02-24 19:57:40 +08:00
RockYang 6a066f1b8e 默认允许跨域请求API 2025-02-11 14:34:31 +08:00
RockYang 775fb1f853 默认允许跨域请求API 2025-02-11 14:34:31 +08:00
RockYang b7d137247a 优化 docker 容器依赖关系 2025-02-11 10:10:43 +08:00
RockYang 22278c40bf 优化 docker 容器依赖关系 2025-02-11 10:10:43 +08:00
RockYang 4373642ebd 优化打包脚本,新增 arm64 架构打包脚本 2025-02-11 10:01:05 +08:00
RockYang e29b32bd2d 优化打包脚本,新增 arm64 架构打包脚本 2025-02-11 10:01:05 +08:00
RockYang 5bf90920f5 resolve conflicts 2025-02-11 09:53:41 +08:00
RockYang fa6bb5e46a resolve conflicts 2025-02-11 09:53:41 +08:00
RockYang edcbb3e226 merge v4.1.5 2025-02-11 09:45:26 +08:00
RockYang cebf8497a4 merge v4.1.5 2025-02-11 09:45:26 +08:00
RockYang 5213bdf08b fixed bug for calculate chat message tokens 2025-01-19 13:08:38 +08:00
RockYang cded4bdcc7 fixed bug for calculate chat message tokens 2025-01-19 13:08:38 +08:00
RockYang cf817fd8ea merge code for v4.1.4 2025-01-18 23:20:41 +08:00
RockYang 596b700d63 merge code for v4.1.4 2025-01-18 23:20:41 +08:00
RockYang a2481ff1cf update readme 2025-01-07 11:58:16 +08:00
RockYang 59dfa95bf8 update readme 2025-01-07 11:58:16 +08:00
RockYang bc7d06d3e5 修复登录失效的 Bug 2024-12-21 21:44:52 +08:00
RockYang f6b5a94b29 修复登录失效的 Bug 2024-12-21 21:44:52 +08:00
RockYang 8e81dfa12a update database name 2024-12-16 11:26:54 +08:00
RockYang 188fb23f08 update database name 2024-12-16 11:26:54 +08:00
RockYang 0ff76f0f21 update database file 2024-12-16 11:11:42 +08:00
RockYang 5347a12035 update database file 2024-12-16 11:11:42 +08:00
RockYang 787caa84c8 merge config.toml file 2024-12-16 10:09:23 +08:00
RockYang 691d453f41 merge config.toml file 2024-12-16 10:09:23 +08:00
RockYang c2503e663a merge v4.1.3 2024-12-16 10:07:52 +08:00
RockYang 5b7f2603ae merge v4.1.3 2024-12-16 10:07:52 +08:00
RockYang 405a88862b Merge branch 'dev' 2024-11-27 15:04:26 +08:00
RockYang 3fd7f810f0 Merge branch 'dev' 2024-11-27 15:04:26 +08:00
RockYang 296eabe09a merge v4.1.2 2024-11-27 15:00:02 +08:00
RockYang e04a48623d merge v4.1.2 2024-11-27 15:00:02 +08:00
RockYang 2e1bad387c redeem export function is ready 2024-11-27 11:52:18 +08:00
RockYang 54b45ec2ff update docker-compose.yaml 2024-11-14 16:37:43 +08:00
RockYang 5c18a50330 update docker-compose.yaml 2024-11-14 16:37:43 +08:00
RockYang c434f85045 update database 2024-11-14 16:01:27 +08:00
RockYang d78d3ffe02 update database 2024-11-14 16:01:27 +08:00
RockYang 4d10279870 merge v4.1.1 and fixed conflicts 2024-11-13 18:40:04 +08:00
RockYang 44240f65d5 merge v4.1.1 and fixed conflicts 2024-11-13 18:40:04 +08:00
RockYang 97e81a7dcc fixed bug for chat context not work for chating with image 2024-11-12 18:23:27 +08:00
RockYang 9e8f1ed6bf meta prompt function is ready 2024-11-12 17:13:38 +08:00
RockYang 4dbfdab50d auto restore user's power for failure tasks 2024-11-11 18:12:35 +08:00
RockYang c39814ce2b save task origin info for AI generating jobs 2024-11-11 17:22:08 +08:00
RockYang 95a071014c enable to set the translate model 2024-11-08 18:06:39 +08:00
RockYang 6e03f4b363 fixed bug for audio and video downloading 2024-11-05 11:38:32 +08:00
RockYang 9de9489673 remove sensitive words 2024-11-04 10:05:57 +08:00
RockYang bb8644dea0 remove sensitive words 2024-11-04 10:05:57 +08:00
RockYang ff9142ddd4 micro fix 2024-10-30 18:11:36 +08:00
RockYang 9814fec930 update the default api url to https://api.geekai.pro 2024-10-29 14:09:55 +08:00
RockYang 35f469fb82 update the default api url to https://api.geekai.pro 2024-10-29 14:09:55 +08:00
RockYang 53ba731159 update database sql file 2024-10-29 14:07:49 +08:00
RockYang 705ad58a8f update database sql file 2024-10-29 14:07:49 +08:00
RockYang c96f86fbbf 优化实时语音对话组件,处理异常 2024-10-23 18:04:09 +08:00
RockYang 4b3b64e9e2 modify text link color for register page 2024-10-21 18:26:19 +08:00
RockYang 2f2e146951 Merge branch 'main' of gitee.com:blackfox/geekai-plus 2024-10-21 18:21:21 +08:00
RockYang a09c529414 更换登录页面背景图片 2024-10-21 18:21:04 +08:00
RockYang a8d1d58e95 给 realtime 语音对话增加音效 2024-10-18 06:26:05 +08:00
RockYang 6695be815e 优化充值产品定价逻辑,确保手机端和PC端显示的价格一致 2024-10-17 18:15:25 +08:00
RockYang fbd3478772 the relay server for openai websocket is ready 2024-10-17 16:46:41 +08:00
RockYang 2102e1afbb add websocket relayer for openai realtime api 2024-10-16 18:16:09 +08:00
RockYang 155c56f502 integrated openai realtime console 2024-10-15 19:25:18 +08:00
RockYang 37a9b0e485 add PCM16 audio stream to wave is reday 2024-10-14 18:39:50 +08:00
RockYang 52a3f13f1e add voice chat test case 2024-10-12 19:07:29 +08:00
RockYang a678a11c33 suno and luma task management funtion in admin console is ready 2024-10-10 17:07:40 +08:00
RockYang d34b785238 image task list page for admin console is ready 2024-10-09 18:17:44 +08:00
RockYang 1086e1d631 fixed bug in FileSelect component for deleting files 2024-10-08 18:00:46 +08:00
RockYang b2f57aa483 merge v4.1.0 and fixed conflicts 2024-10-08 17:54:08 +08:00
RockYang 3094b9c8fd merge v4.1.0 and fixed conflicts 2024-10-08 17:54:08 +08:00
RockYang 4c2dba1004 merge v4.1.0 and fixed conflicts 2024-10-08 17:51:14 +08:00
RockYang 0f0a4c0a7e merge v4.1.0 and fixed conflicts 2024-10-08 17:51:14 +08:00
RockYang c374126f69 fixed bug for websocket message handler rebind 2024-10-08 16:41:19 +08:00
RockYang 8498cd71dc fixed webscoket event re-bind bug 2024-10-05 21:18:59 +08:00
RockYang bf30517393 fixed alipay mobile payment 2024-10-05 11:45:44 +08:00
RockYang 495f86e7fc fixed alipay mobile payment 2024-10-05 10:23:00 +08:00
RockYang 471017657f auto jump to mobile page when use mobile device access the page 2024-10-04 11:25:01 +08:00
RockYang 4861dd75be fixed bug for: websocket is not auto connected when user not login 2024-10-02 07:26:34 +08:00
RockYang 342ceea371 update database 2024-09-30 17:12:23 +08:00
RockYang 880c34dfee add message handler ONLY when websocket connect successfully 2024-09-30 16:33:26 +08:00
RockYang a1e487100d support wechat and alipay payment for mobile page 2024-09-30 16:20:40 +08:00
RockYang 77948b1e16 optimize the vue component communication, replace event listening with share data 2024-09-30 14:20:59 +08:00
RockYang e28a12a1ee websocket api refactor is ready 2024-09-29 19:28:47 +08:00
RockYang 00a8bc6784 mj websocket refactor is ready 2024-09-29 07:51:08 +08:00
RockYang 8fffa60569 sd websocket refactor is finished 2024-09-27 18:28:54 +08:00
RockYang 2debe7e927 refactor websocket message protocol, keep the only connection for all clients 2024-09-27 17:50:54 +08:00
RockYang 478bc32ddd add ws handler 2024-09-25 18:43:12 +08:00
RockYang dfd2be1265 update database sql file for v4.1.4 2024-09-23 15:54:22 +08:00
RockYang 33ff2e29e5 logout the user when it has been disabled 2024-09-20 16:49:03 +08:00
RockYang 4df1c7a136 add release v4.1.4 2024-09-20 15:50:04 +08:00
RockYang c522248d39 add email white list check in register handler 2024-09-20 14:10:40 +08:00
RockYang 246d96ee63 add email white list 2024-09-20 10:40:37 +08:00
RockYang 909ae4aa00 payment for mobile page is ready 2024-09-19 19:03:03 +08:00
RockYang 26e3ababcf wechat payment for mobile page is ready 2024-09-19 17:59:27 +08:00
RockYang 4d9f89f630 wechat payment for pc is ready 2024-09-19 14:42:25 +08:00
RockYang b989199edb fixed bug geek-plus#6, register page first tab not auto active 2024-09-19 09:03:14 +08:00
RockYang 0d81fe6c8e geek payment notify api is ready 2024-09-18 22:24:05 +08:00
RockYang be45f41e34 urgent bug fix: remove suno and luma task will recharge user power 2024-09-18 20:33:29 +08:00
RockYang e9ac58b1ef Geek Pay notify is ready 2024-09-18 18:07:49 +08:00
RockYang 59d9ae96ac add geek payment 2024-09-18 07:03:46 +08:00
RockYang 3cff6f7189 recommend user to use Google Chrome 2024-09-15 10:25:50 +08:00
RockYang 14aee28289 merge app type function branch 2024-09-14 18:17:55 +08:00
RockYang 8c1b4d4516 refactor AI chat message struct, allow users to set whether the AI responds in stream, compatible with the GPT-o1 model 2024-09-14 17:06:13 +08:00
胡双明 068df8fa15 feat: 应用分类功能 2024-09-14 11:05:49 +08:00
RockYang e371310d02 refactor chat message body struct 2024-09-14 07:11:45 +08:00
RockYang 96b8121210 return at least one chat role for getUserRoles API 2024-09-14 05:55:56 +08:00
RockYang 1960a85ead add tid field for chat app role 2024-09-13 18:32:13 +08:00
RockYang 69cb479e01 fixed bug, filelist page support pagination, do not load captcha component for user login first time 2024-09-13 17:03:05 +08:00
胡双明 db90131bd6 Merge branch 'main' into husm_2024-09-02 2024-09-13 10:25:15 +08:00
RockYang c18d272413 fixed bug for reset password 2024-09-12 17:25:19 +08:00
RockYang cd0952e170 fixed bug for register page code verification 2024-09-12 15:42:09 +08:00
胡双明 e6a6d0485d Merge branch 'main' into husm_2024-09-02 2024-09-11 09:57:01 +08:00
RockYang a642ae0332 optimize download function for suno 2024-09-11 08:39:28 +08:00
胡双明 67f508c7da feat: 210 AI对话页面文件列表增加分页功能 2024-09-10 18:14:34 +08:00
RockYang 2225f09797 file list api support pagination 2024-09-10 15:24:36 +08:00
胡双明 fe7fa46a0c Merge branch 'main' into husm_2024-09-02 2024-09-10 14:29:08 +08:00
RockYang 6d8901fa21 update v4.1.3 database sql file 2024-09-10 11:15:26 +08:00
RockYang 7750cb0021 update v4.1.3 database sql file 2024-09-10 11:11:17 +08:00
RockYang 327d68a347 support multiple delete users, update database sql file 2024-09-10 10:56:04 +08:00
RockYang 7c272b0efd update config file 2024-09-09 18:58:03 +08:00
RockYang 5eb4ed157e merge luma page code for v4.1.3 2024-09-09 18:07:10 +08:00
RockYang d4e00b960c Merge remote-tracking branch 'inet/husm_2024-09-02' into dev-4.1.3 2024-09-09 10:54:43 +08:00
RockYang 79adc871ef export the newest database sql file 2024-09-05 15:29:00 +08:00
RockYang af0eff88b8 export the newest database sql file 2024-09-05 15:29:00 +08:00
RockYang c755befa28 优化聊天页面代码,刷新页面之后自动加载当前对话的 role_id 和 model_id 2024-09-05 14:50:37 +08:00
胡双明 3f6890da27 feat: 生成视频页面2 2024-09-05 11:56:02 +08:00
RockYang 8144fada25 merged v4.0.9 and fixed conflicts 2024-09-05 11:02:32 +08:00
RockYang 08319391ea merged v4.0.9 and fixed conflicts 2024-09-05 11:02:32 +08:00
RockYang ae7f1c03e2 add auto execute task to downloading video files 2024-09-05 10:54:58 +08:00
胡双明 151017ed47 feat: 生成视频页面 2024-09-05 08:50:49 +08:00
RockYang 941d15b635 remove unused css file 2024-09-04 22:42:56 +08:00
RockYang d8058400dd adjust chat page styles 2024-09-04 18:07:39 +08:00
RockYang 5a1ed953de user can select function tools by themself 2024-09-04 14:53:21 +08:00
RockYang 58d127b602 add page and total field for pagination vo 2024-09-03 18:32:15 +08:00
RockYang 13bf73e6cf add sync lock for sub or add user's power 2024-09-03 12:09:36 +08:00
RockYang aa46fdb113 luma create and list api is ready 2024-09-02 18:08:50 +08:00
胡双明 4a09da3a25 feat(Luma): 加上传前后帧调换功能 2024-09-02 17:33:03 +08:00
RockYang d70fcbb9f3 optimize ChatPlus page, fixed bug for websocket reconnection 2024-09-02 16:35:15 +08:00
RockYang a16ef6476d add luma api service 2024-08-30 18:12:14 +08:00
RockYang dae75343d0 update change log file 2024-08-30 16:47:52 +08:00
RockYang d7564127d2 suno add new function for merging full songs and upload custom music 2024-08-30 16:46:48 +08:00
RockYang c6bf54fd9d add logo for favirate icon 2024-08-29 13:36:35 +08:00
RockYang 8d47072e1c luma page, upload image and remove image function is ready 2024-08-26 17:59:05 +08:00
RockYang d601226187 add download for video 2024-08-26 07:24:04 +08:00
RockYang 4801832d9d luma page video list component is ready 2024-08-23 18:25:58 +08:00
RockYang 6b0f42d0b8 luma page is ready 2024-08-20 14:31:40 +08:00
RockYang 08379c21ac refactor reset password functions 2024-08-19 12:05:00 +08:00
RockYang 2499bd9ad3 fixed styles 2024-08-19 06:42:53 +08:00
RockYang ad6da0d320 add bind mobile, bind email, bind wechat function is ready 2024-08-14 15:56:50 +08:00
RockYang a2583c0591 add mobile and email filed for user 2024-08-13 18:40:50 +08:00
RockYang 0ea4a29152 add verification code for login and register page 2024-08-13 14:55:47 +08:00
RockYang 7fe50788a5 add Captcha components 2024-08-13 10:01:07 +08:00
RockYang c4a68076d6 fixed conflicts 2024-08-13 06:48:22 +08:00
RockYang 9e365895af add wechat login for login dialog 2024-08-12 18:00:34 +08:00
RockYang 9ece25e17d add drag icon for dragable rows 2024-08-12 14:00:50 +08:00
RockYang 2140eff51b release v4.1.2 2024-08-09 18:50:02 +08:00
RockYang dfba3c30a5 redeem code function is ready 2024-08-09 18:19:51 +08:00
RockYang 4b717109d2 add redeem code function 2024-08-08 18:28:50 +08:00
RockYang e54e908fbc add cache for getting user info and system configs 2024-08-08 14:37:33 +08:00
RockYang 7a11a9ef15 add clear unpaid order functions 2024-08-07 18:00:28 +08:00
RockYang c9d0700fd9 refactor stable diffusion service, use api key instead of configs 2024-08-07 17:30:59 +08:00
RockYang f9b809801d refactor midjourney service, use api key in database 2024-08-06 18:30:57 +08:00
RockYang cc551ba266 show sql error message 2024-08-05 16:14:44 +08:00
RockYang 754ba02263 fixed build script 2024-08-01 18:45:53 +08:00
RockYang 23787ff462 fixed build script 2024-08-01 18:45:53 +08:00
RockYang 7ddf57ae06 merge v4.0.8 2024-08-01 18:09:00 +08:00
RockYang 9f603c5e77 merge v4.0.8 2024-08-01 18:09:00 +08:00
RockYang 3d720bdd81 update change log 2024-08-01 08:54:04 +08:00
RockYang cc5180a6f7 update readme 2024-08-01 08:52:46 +08:00
RockYang a8eccbe43b update readme 2024-08-01 08:52:46 +08:00
RockYang 2ac77ac39f restore use power when removed not finish jobs 2024-07-31 16:08:46 +08:00
RockYang 4eaa518cf3 remove platform field for api key and chat model 2024-07-30 17:24:21 +08:00
RockYang 5622f94fc8 update datebasesl 2024-07-30 14:55:49 +08:00
RockYang 95457b7dcd add back-to-top component for all list page 2024-07-29 11:00:53 +08:00
RockYang 6a9de72c78 remove chat debug log 2024-07-28 18:55:17 +08:00
RockYang 088a614160 add function to generate lyrics 2024-07-28 10:04:53 +08:00
RockYang 013ee98f53 song detail page is ready 2024-07-26 19:12:44 +08:00
RockYang dc46ed0e05 adjust chat records layout styles 2024-07-25 11:01:27 +08:00
RockYang 2a0c657ca3 add put url file for oss interface 2024-07-23 18:36:26 +08:00
RockYang a0aee80c63 remove other platform supports, ONLY use chatGPT API 2024-07-22 18:36:58 +08:00
RockYang 4910be3403 optimize foot copyright snaps 2024-07-22 17:54:09 +08:00
RockYang 1541d74c84 add close button for music player 2024-07-22 07:12:21 +08:00
RockYang 34c9151dc1 enable use random pure color background for index page 2024-07-19 18:43:01 +08:00
RockYang 6c69770ed6 the music player is ready 2024-07-18 18:34:11 +08:00
RockYang 5b7c38c67f add suno page 2024-07-17 18:58:09 +08:00
RockYang b7bec8ecb7 support upload file from clipboard 2024-07-17 10:23:02 +08:00
RockYang 82fe9c3596 allow user to use chat role directly, no need to add to workspace 2024-07-16 18:28:08 +08:00
RockYang 9f44c34d34 update docs url 2024-07-16 18:15:34 +08:00
RockYang afd84516b0 update docs url 2024-07-16 18:15:34 +08:00
RockYang 024f00b73c show error message for Midjourney task list page 2024-07-16 17:16:58 +08:00
RockYang 24a21bf2ee fixed bug for function call for openai 2024-07-16 06:25:40 +08:00
RockYang f22c6bf658 优化版权显示逻辑,允许激活用户更改自定义版权 2024-07-15 18:44:14 +08:00
RockYang 0df700ec18 update docker-compose file 2024-07-12 18:13:43 +08:00
RockYang 46141f87b8 update database file, add tika host config 2024-07-12 18:10:32 +08:00
RockYang eecce10018 tidy apis 2024-07-12 14:39:14 +08:00
RockYang b793b81768 update geekai image version 2024-07-05 11:09:13 +08:00
RockYang c8506e3d3b update geekai image version 2024-07-05 11:09:13 +08:00
RockYang bddd611cc1 wechat login is ready 2024-07-04 15:34:32 +08:00
RockYang b399fc557a feat: support wechat login function 2024-07-02 18:27:06 +08:00
RockYang 233f6e00f0 fixed conflicts 2024-06-30 06:09:12 +08:00
RockYang b20ea734fd fixed conflicts 2024-06-30 06:09:12 +08:00
RockYang b7dba68549 optimize ngin configuration for chat-plus.conf 2024-06-30 05:44:39 +08:00
RockYang 7ab0acd6a4 optimize ngin configuration for chat-plus.conf 2024-06-30 05:44:39 +08:00
RockYang 1d60ccc516 docs: update change log file 2024-06-28 16:21:43 +08:00
RockYang 2a8d2213b1 feat: optimize chat page data list style, support list style and chat style 2024-06-28 15:53:49 +08:00
RockYang a27ce36a32 feat: chat with file function is ready 2024-06-27 18:01:49 +08:00
RockYang 3fdcc895ed enable set custom index background image 2024-06-27 10:49:31 +08:00
RockYang 88e510d07a add test code for reading pdf files 2024-06-26 18:50:48 +08:00
RockYang 2526feb0d9 feat: new UI for chat file manager is ready 2024-06-25 18:59:27 +08:00
RockYang 4171b2bdfb fixed bug: mobile chat list could not update chat title 2024-06-25 09:53:08 +08:00
RockYang 0611f8de6c update version 2024-06-23 17:54:42 +08:00
RockYang df9e495dc5 add wechat payment configs sample 2024-06-22 16:04:04 +08:00
RockYang 8a48476105 update docker-compose file 2024-06-22 12:37:41 +08:00
RockYang 9d4a1705a5 add database files 2024-06-22 12:17:35 +08:00
RockYang 871e5733c7 finish mobile wechat payment 2024-06-22 12:10:43 +08:00
RockYang bdea12c51a update docker image mirror url 2024-06-19 08:35:05 +08:00
RockYang e8a663e3c7 update docker image mirror url 2024-06-19 08:35:05 +08:00
RockYang a27d9ea259 Merge branch 'main' of gitee.com:blackfox/geekai 2024-06-19 08:22:00 +08:00
RockYang 422d439627 Merge branch 'main' of gitee.com:blackfox/geekai 2024-06-19 08:22:00 +08:00
RockYang 7cd824c284 update docker image version to 4.0.6 2024-06-15 15:35:32 +08:00
RockYang edeff3c169 update docker image version to 4.0.6 2024-06-15 15:35:32 +08:00
RockYang e27d95e2b5 update docker image version to 4.0.6 2024-06-15 15:33:38 +08:00
RockYang 13076421a1 update docker image version to 4.0.6 2024-06-15 15:33:38 +08:00
RockYang 3943e7f05f update change log files 2024-06-14 18:23:54 +08:00
GeekMaster 6839827db0 Merge branch 'main' of https://github.com/yangjian102621/geekai into main 2024-06-12 15:44:26 +08:00
GeekMaster cd0d9dad98 Merge branch 'main' of https://github.com/yangjian102621/geekai into main 2024-06-12 15:44:26 +08:00
RockYang 857da34b9c wechat payment is ready for PC 2024-06-12 14:20:37 +08:00
RockYang ccad7e7bb5 change payment component, upgrade golang to 1.22.4 2024-06-11 11:48:41 +08:00
RockYang b8de15d66e fix bug: free model not record the chat history 2024-06-06 15:01:32 +08:00
RockYang c02661ea29 fixe page styles 2024-06-05 18:08:23 +08:00
RockYang 3c70c8ae59 fixed bug markmap generation 2024-06-04 16:21:08 +08:00
RockYang d6a04f96fe Merge pull request #208 from mari1995/main
图片墙,选择框单选问题
2024-06-04 08:33:51 +08:00
RockYang b985b62068 Merge pull request #208 from mari1995/main
图片墙,选择框单选问题
2024-06-04 08:33:51 +08:00
RockYang 297b760293 dalle3 and gptt-4o api compatible with azure 2024-06-03 18:34:37 +08:00
RockYang 5f820b9dc1 merge v4.0.6 2024-06-03 14:22:08 +08:00
RockYang 088abfe7ab merge v4.0.6 2024-06-03 14:22:08 +08:00
RockYang 17340ae0ac fixed bug for function call error None is not of type 'array' 2024-05-30 09:59:44 +08:00
RockYang fe687af8ca fixed bug for mobile chat share 2024-05-30 08:37:14 +08:00
RockYang 7e26029268 add v4.0.8 database sql file 2024-05-29 17:41:37 +08:00
RockYang 03f7f2a53e feat: add dalle3 page for h5 2024-05-29 17:25:01 +08:00
RockYang 296260bf6a feat: add system config for enable rand background image for index page 2024-05-29 16:24:56 +08:00
RockYang dd07acd21a feat: add system config for enable rand background image for index page 2024-05-29 16:23:42 +08:00
RockYang 89f6402fbf fix markdown formula parse plugin 2024-05-29 13:49:45 +08:00
SSMario 6788edbe9d Update Image.vue 2024-05-27 21:23:11 +08:00
SSMario 853059c814 Update Image.vue 2024-05-27 21:23:11 +08:00
SSMario 3895305882 Update Image.vue 2024-05-27 21:00:59 +08:00
SSMario 5a0876f8dc Update Image.vue 2024-05-27 21:00:59 +08:00
RockYang 2e32238652 micro fixs 2024-05-27 17:39:17 +08:00
SSMario c2acbaaa94 Update ImagesWall.vue 2024-05-27 17:04:15 +08:00
SSMario ad1a99aa44 Update ImagesWall.vue 2024-05-27 17:04:15 +08:00
RockYang 24e4be019a fixed bug for dalle prompt translate 2024-05-27 11:42:14 +08:00
RockYang 13c917ad7e put model and app selector on the top of chat page 2024-05-24 12:33:22 +08:00
RockYang 0a8cf6870f fixed bug for payment api authorization 2024-05-24 11:31:38 +08:00
RockYang 061291cebb chore: use config value for order pay timeout 2024-05-22 18:15:06 +08:00
RockYang b8e0d7760b feat: add sign check for PC QR code payment 2024-05-22 17:47:53 +08:00
RockYang 962de0183c extract code for saving chat history 2024-05-22 15:32:44 +08:00
RockYang 627396dbf7 check if the api url in whitelist for mj plus client 2024-05-22 11:47:04 +08:00
RockYang 6c300bb018 fixed bug for mobile chat page change chat model not work 2024-05-21 17:54:03 +08:00
RockYang 9273df4af2 auto resize the input element rows, when use inputed more than one line 2024-05-21 17:36:47 +08:00
RockYang 4a99be2f15 remove license code 2024-05-21 16:20:29 +08:00
RockYang 669d784f54 remove license code 2024-05-21 16:20:29 +08:00
RockYang 5253d657b6 add logs for updating database failed 2024-05-21 11:55:38 +08:00
RockYang 27c816cf3b merge conflicts for v4.0.5 2024-05-21 11:30:40 +08:00
RockYang c2548c5007 merge conflicts for v4.0.5 2024-05-21 11:30:40 +08:00
RockYang 0d81776212 update docker image name 2024-05-21 11:21:27 +08:00
RockYang bdec823148 update docker image name 2024-05-21 11:21:27 +08:00
RockYang a6de958daa update docker image url 2024-05-21 11:03:11 +08:00
RockYang cccab31c0f rename project name to geekai 2024-05-20 15:14:02 +08:00
RockYang 50c25d4574 rename project name to geekai 2024-05-20 15:14:02 +08:00
RockYang 68100f7f24 rename project name to geekai 2024-05-20 15:11:14 +08:00
RockYang 38777ea285 update database file 2024-05-19 19:37:16 +08:00
RockYang 00c8f08179 remove code for set left component fixed height 2024-05-18 08:07:09 +08:00
RockYang 45d6579fb6 refactor login dialog for front page 2024-05-18 00:27:32 +08:00
RockYang dad9254128 finished refactor chat page UI 2024-05-17 19:25:38 +08:00
RockYang 4ddf3bf2bf fixed bugs for send message captcha component 2024-05-17 08:41:09 +08:00
RockYang 9934143f00 fixed bugs for send message captcha component 2024-05-17 08:41:09 +08:00
RockYang f617bde81b fixed bugs for send message captcha component 2024-05-16 22:33:00 +08:00
RockYang b2771b7b3f feat: add top navbar for front page 2024-05-16 20:10:00 +08:00
RockYang 44b3237dd6 feat: support add external link menu 2024-05-16 10:53:00 +08:00
RockYang b987a2d365 handler chat error in the chat entry func 2024-05-15 15:30:34 +08:00
RockYang 1cf299f090 add charge link for insufficient of power 2024-05-15 07:10:31 +08:00
RockYang c87d78c666 fix bug for white-list api key check 2024-05-14 22:30:42 +08:00
RockYang 3d37a3d367 update readme 2024-05-14 18:23:12 +08:00
RockYang 79c8d90049 update readme 2024-05-14 18:23:12 +08:00
RockYang 73d8236697 update change log 2024-05-14 18:20:52 +08:00
RockYang 1afc3e5e1c update change log 2024-05-14 18:20:52 +08:00
RockYang 87e8bbfa41 update version 2024-05-14 18:03:58 +08:00
RockYang 3e3d3e162e refactor: use waterflow component in mj, sd and dall image drawing page 2024-05-13 19:04:00 +08:00
RockYang cc80b87fee fixed sd page waterfall component 2024-05-11 18:27:35 +08:00
RockYang b614ecccc2 enable to update AI Drawing configuarations in admin console page 2024-05-11 17:27:14 +08:00
RockYang 8251c6589b fix: markmap do not cost power in front page 2024-05-11 07:08:14 +08:00
RockYang 4edf2ea02a optimize chat handler error handle code 2024-05-10 18:26:19 +08:00
RockYang 12dd4659cd fix bug: remove chat role failed 2024-05-10 17:38:55 +08:00
RockYang 5d4f40c004 fixed bug for dalle3 task not decrease power 2024-05-10 11:18:37 +08:00
RockYang 3c34e8e0e7 fixed bug for dalle3 task not decrease power 2024-05-10 11:17:52 +08:00
RockYang 7441a3a717 fixed bug for dalle3 task not decrease power 2024-05-10 11:17:52 +08:00
RockYang 3eef9a836b add toolbar for markmap component 2024-05-10 06:38:34 +08:00
RockYang 922202734a fixed bug for markmap 2024-05-09 21:55:40 +08:00
RockYang fffd7e375f fixed bug for markmap 2024-05-09 21:55:40 +08:00
RockYang d87f09c957 use proxy for downloading discord images 2024-05-09 18:48:53 +08:00
RockYang b29884f8df add stable diffusion default negtive prompt system config 2024-05-07 16:49:54 +08:00
RockYang b270960a04 remove license code 2024-05-07 16:41:35 +08:00
RockYang c0ee2c2d8e remove license code 2024-05-07 16:41:35 +08:00
RockYang 5c4899df6e upgrade to v4.0.4 2024-05-07 16:32:05 +08:00
RockYang be1580e949 upgrade to v4.0.4 2024-05-07 16:32:05 +08:00
RockYang 6424eb871c add stable diffusion default negtive prompt system config 2024-05-07 16:21:31 +08:00
RockYang 8a8c43c7a5 handle the exception for web front page 2024-05-06 17:39:58 +08:00
RockYang 9a503ddc72 fixed conflicts 2024-05-06 14:44:09 +08:00
RockYang b130466c8f chore: change module name to geekai, add copyright in source code 2024-05-06 14:41:27 +08:00
RockYang 2f0215ac87 Update README.md 2024-05-06 10:45:50 +08:00
RockYang 3ab5459778 Update README.md 2024-05-06 10:45:50 +08:00
RockYang dd5cc206e5 update LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-05-05 03:05:23 +00:00
RockYang 4eb1bf2de2 update LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-05-05 03:05:23 +00:00
RockYang 6c47551b03 fix bug for waterflow component 2024-05-05 10:52:29 +08:00
RockYang 657ecccee3 update changelog 2024-05-04 21:32:43 +08:00
RockYang b4ba70c0f3 update changelog 2024-05-04 21:32:43 +08:00
RockYang c2bf5e845a fix bug for license synchronize 2024-05-04 21:30:29 +08:00
RockYang 8900e72e45 opt: optimize mobile images page styles 2024-05-03 08:14:33 +08:00
RockYang 0cb192135d fix: fix bug for dalle power refund 2024-05-01 08:48:32 +08:00
RockYang c1ca687630 chore: update docker image name 2024-05-01 07:59:20 +08:00
RockYang 29cc24508b opt: styles and view micro optimization 2024-05-01 07:40:56 +08:00
RockYang 3db55cbd48 opt: close unused websocket connections 2024-04-30 22:54:39 +08:00
RockYang c0a7f41747 chore: replace docker image url with AliYun 2024-04-30 19:08:33 +08:00
RockYang 1ba08bbfa6 feat: change theme for mobile site is ready 2024-04-30 18:57:15 +08:00
RockYang 51a8f42d89 feat: mobile page refactor is finished 2024-04-29 19:22:00 +08:00
RockYang 3b081ff0f4 opt: add chat config for mobile chat session 2024-04-29 09:39:23 +08:00
RockYang 7f31a301e3 feat: add index page for mobile 2024-04-28 19:09:26 +08:00
RockYang 039d949523 theme change is ready 2024-04-27 13:13:28 +08:00
RockYang 5b16dc6ee7 feat: admin console dark theme 2024-04-26 18:10:17 +08:00
RockYang 34648c704f opt: optimize index and login page UI 2024-04-26 16:07:02 +08:00
RockYang 1966e69460 synchronize license every 10 secs 2024-04-26 14:35:01 +08:00
RockYang 74b1b07b31 change index page background 2024-04-25 18:54:33 +08:00
RockYang 2e023cb8dc update readme 2024-04-25 06:27:30 +08:00
RockYang 118c0e1ff1 update readme 2024-04-25 06:27:30 +08:00
RockYang e933f32d9c remove unused files 2024-04-24 21:22:56 +08:00
RockYang 81d075c028 remove unused files 2024-04-24 21:22:56 +08:00
RockYang 514dd6c76a show license info in admin active page, optimize markdown generate prompt 2024-04-24 19:00:28 +08:00
RockYang 0b2501c1d8 output the error to chat page directly, replace the common error message 'AI开小差' 2024-04-24 10:10:03 +08:00
RockYang ddc323a8c3 output the error to chat page directly, replace the common error message 'AI开小差' 2024-04-24 10:10:03 +08:00
RockYang 2933c057a2 fixed bug for markmap 2024-04-23 20:47:06 +08:00
RockYang c1d892069e release v4.0.4 2024-04-23 18:47:23 +08:00
RockYang 20ed6d1d3d release v4.0.4 2024-04-23 18:47:23 +08:00
RockYang 90f5275201 optimize styles and release v4.0.4 2024-04-23 18:46:32 +08:00
RockYang fe64b203da feat: hide more navigator items 2024-04-22 16:27:53 +08:00
RockYang 9eb1353455 feat: support markmap svg export and download as png image 2024-04-22 14:20:51 +08:00
RockYang 8a1c55c731 allow users to select a chatApp to chat in chat app list page 2024-04-22 11:18:55 +08:00
RockYang 9059bebd07 optimize index page UI 2024-04-22 10:43:33 +08:00
RockYang d18f79fe74 optimize code for remove timeout and failed image drawing job 2024-04-21 21:44:28 +08:00
RockYang 3a8f0be28a image wall page add dalle 2024-04-21 20:42:42 +08:00
RockYang 60cf380f96 dalle image page is ready 2024-04-21 20:23:47 +08:00
RockYang ab8240613e DO NOT refresh finished jobs when job is running 2024-04-20 21:30:55 +08:00
RockYang d6e9dc6839 fixed markdown generating styles 2024-04-19 18:22:45 +08:00
RockYang 6891bdde53 support send email use TLS 2024-04-19 12:04:59 +08:00
RockYang bda335212d Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-19 10:56:05 +08:00
RockYang ecadfeab19 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-19 10:56:05 +08:00
RockYang 06f4cdc649 fixed bug for QWen response blank quotes 2024-04-19 10:55:29 +08:00
RockYang a38d547200 fixed bug for QWen response blank quotes 2024-04-19 10:55:29 +08:00
RockYang a15f431a7f fixed bug for QWen response blank quotes 2024-04-19 10:30:02 +08:00
RockYang 3bb5b66e8b fixed bug for chat handler doRequest method 2024-04-16 23:49:56 +08:00
RockYang 9bf7fa4081 compatible freeGPT35 API 2024-04-15 21:03:19 +08:00
RockYang d9ff3242c6 compatible freeGPT35 API 2024-04-15 21:03:19 +08:00
RockYang cd562ab8ed fixed bug for websocket close tip message 2024-04-15 18:15:15 +08:00
RockYang 3a8a69ac2e feat: markmap function is ready 2024-04-15 17:23:59 +08:00
RockYang 4ca9dfd9c0 fixed upscale and variation action url 2024-04-15 15:14:49 +08:00
RockYang 0d5b4936bd fixed upscale and variation action url 2024-04-15 15:14:49 +08:00
RockYang b5f6eaf159 opt: close the old connection for mj and sd clients 2024-04-15 09:34:20 +08:00
RockYang adfee8bf58 update version 2024-04-15 09:05:54 +08:00
RockYang 25c80921e0 update version 2024-04-15 09:05:54 +08:00
RockYang fbfa2a71a9 release v4.0.3 2024-04-15 08:26:07 +08:00
RockYang 5c51ed48c3 release v4.0.3 2024-04-15 08:26:07 +08:00
RockYang 22febfc42a markmap enable to select ai model 2024-04-15 06:16:53 +08:00
RockYang de482dd9f4 fixed chat page styles 2024-04-12 21:57:41 +08:00
RockYang 74af67a41f feat: markmap page view is ready 2024-04-12 18:49:24 +08:00
RockYang 170b41441b feat: allow chat model bind a fixed api key 2024-04-12 17:09:22 +08:00
RockYang 4991e50e16 chore: update build.sh 2024-04-11 21:26:33 +08:00
RockYang 1f236ef929 feat: use custom mode for mj upscale and variation operarions 2024-04-11 17:32:34 +08:00
RockYang 7f86c5984d feat: add index page 2024-04-10 18:23:55 +08:00
RockYang 6fcee49b19 feat: support for freeGPT35 API 2024-04-10 14:49:07 +08:00
RockYang ac527851a0 feat: support gpt-4-turbo-2014-04-09 vision function 2024-04-10 11:47:10 +08:00
RockYang 64bbeaedcb fixed bug for gpt-4-turbo-2024-0409 model function calls 2024-04-10 10:23:45 +08:00
RockYang 3e984a1bcb fix bug: remove timeout task ONLY for unfinished(progress < 100) 2024-04-10 06:25:54 +08:00
RockYang a7237fe62f Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-04-07 18:33:55 +08:00
RockYang 79165214d7 Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-04-07 18:33:55 +08:00
RockYang c3c454b7d7 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-07 18:32:20 +08:00
RockYang 3f7855dd6e Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2024-04-07 18:32:20 +08:00
RockYang d4d708d44b update database files 2024-04-07 18:26:45 +08:00
RockYang f0610281e5 update database files 2024-04-07 18:26:45 +08:00
RockYang 7f0b6a3a46 set the enable status for adding new api key with default value true 2024-04-07 10:17:10 +08:00
RockYang e87e7e2164 set the enable status for adding new api key with default value true 2024-04-07 10:17:10 +08:00
RockYang c2a7c089d2 update change log 2024-04-07 08:40:43 +08:00
RockYang 1a3ad390fd update change log 2024-04-07 08:40:43 +08:00
RockYang df5bd4df60 Merge branch 'dev' 2024-04-07 08:03:14 +08:00
RockYang c383367e9f Merge branch 'dev' 2024-04-07 08:03:14 +08:00
RockYang 79b6010104 optimize ngin configuration for chat-plus.conf 2024-04-07 08:02:47 +08:00
RockYang 7a9f45c96b optimize ngin configuration for chat-plus.conf 2024-04-07 08:02:47 +08:00
RockYang 97b0a98793 fix 404 error with remove api keys 2024-04-07 08:01:25 +08:00
RockYang 7277cb289f fix 404 error with remove api keys 2024-04-07 08:01:25 +08:00
RockYang 86b2d5eff2 fix bug for remove api 404 errorc 2024-04-06 20:36:52 +08:00
RockYang 7873847616 feat: show bind model on chat role list page 2024-04-05 21:21:28 +08:00
RockYang 5e46f149d9 feat: support uploading role icon 2024-04-05 17:41:23 +08:00
RockYang 574fc52332 feat: allow bind a chat model for chat role 2024-04-05 12:51:18 +08:00
RockYang 8c924ca98f update change log 2024-04-05 06:58:13 +08:00
RockYang a9adc0c1a2 feat: stable diffusion image drawing on mobile is ready 2024-04-03 18:13:48 +08:00
RockYang ed286c8894 feat: midjourney role and style consistency is ready 2024-04-02 19:01:28 +08:00
RockYang d900a3d08e update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-04-02 10:00:45 +00:00
RockYang a55da15202 update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-04-02 10:00:45 +00:00
RockYang cdf5b66729 Update README.md 2024-04-02 17:59:53 +08:00
RockYang 9728118f94 Update README.md 2024-04-02 17:59:53 +08:00
RockYang 9cc2ea412b feat: image preview for stable-diffusion task is ready 2024-04-02 17:24:38 +08:00
RockYang 3f1ad4b7dc feat: support midjourney --cref and --sref for role consistency 2024-04-02 14:59:53 +08:00
RockYang 56c225bf20 feat: update menu icons, add version in site titles 2024-04-01 18:20:00 +08:00
RockYang 8b68bfb28c show power for chat and imaging page 2024-03-31 20:49:12 +08:00
RockYang a5299b52d4 opt: change the relative path with absolute path for midjourney image uploading 2024-03-31 17:45:22 +08:00
RockYang 9e9bc52737 Merge branch 'main' into dev 2024-03-30 11:57:31 +08:00
RockYang 76106e4504 remove dead code 2024-03-30 11:57:23 +08:00
RockYang a6c00c42fa fixed conflicts 2024-03-29 18:10:59 +08:00
RockYang 4e23005b5b fixed conflicts 2024-03-29 18:10:59 +08:00
RockYang f27e2de220 update readme file 2024-03-29 18:06:55 +08:00
RockYang eefcabcff3 update databases 2024-03-29 17:43:38 +08:00
RockYang 63e4669e3f feat: support custom menu 2024-03-29 15:41:58 +08:00
RockYang 7d5428f775 add tip for mj image buttons 2024-03-28 21:41:49 +08:00
RockYang ab3c4562fa fix: fix overflow hidden for admin page 2024-03-28 18:51:09 +08:00
RockYang 13f89bf335 fix: fix overflow hidden for mobile page 2024-03-28 18:13:33 +08:00
RockYang e6a18445c3 fix: use slide captcha for iphone 2024-03-28 15:00:53 +08:00
RockYang c8ab209426 feat: add mj_action_power system config item 2024-03-28 09:53:41 +08:00
RockYang 360fea4085 feat: change midjourney origin implements, replace midjourney bot with midjourney-proxy 2024-03-27 18:57:15 +08:00
RockYang 9794d67eaa feat: auto translate and rewrite prompt for midjourney and stable-diffusion 2024-03-27 13:45:52 +08:00
RockYang b60a639312 feat: stable-diffusion refactored, replace websocket api with sdapi 2024-03-26 18:23:08 +08:00
RockYang 870706c4ff fix: can not change user's power in admin console 2024-03-25 11:40:03 +08:00
RockYang 491471fa10 fix: fix bug for update user's power in admin page did not work 2024-03-23 16:21:37 +08:00
RockYang 6dbf61d4e4 No need to login with Stable-Diffusion page and Invite page 2024-03-23 15:45:37 +08:00
RockYang 81545d192b fix: fix code highlight error when add formule detecting 2024-03-22 19:09:04 +08:00
RockYang 37cdc26160 chore: correct prompt messages 2024-03-22 18:27:57 +08:00
RockYang fb6a35ebe4 feat: add chart for admin dashbord 2024-03-22 16:57:30 +08:00
RockYang f7a565bb80 feat: integrate xxl-job-admin to implements automatic task scheduling 2024-03-22 13:47:16 +08:00
RockYang bf5e72b7e0 feat: save prompt in power log for dalle-3 2024-03-21 15:55:39 +08:00
RockYang ec6186596d feat: add manager list page in console page 2024-03-21 15:24:28 +08:00
RockYang dece19cec6 feat: add powerlog page for admin console 2024-03-21 13:46:39 +08:00
RockYang 6aa1d27711 opt: optimize the formula show styles 2024-03-21 11:04:12 +08:00
RockYang 264e77f383 always parse authorization token for all request 2024-03-20 21:11:52 +08:00
RockYang 0cbc284e42 fixed conflicts 2024-03-20 20:40:22 +08:00
RockYang 9e42a334fa update README.md.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2024-03-20 20:39:44 +08:00
RockYang 49438d2ee1 feat: Async loading midjourney job for mobile MidJourney page 2024-03-20 18:39:14 +08:00
RockYang 735aa24020 feat: h5 payment for payjs is ready 2024-03-20 17:46:39 +08:00
RockYang 14f63a203d feat: payment for mobile pages is ready 2024-03-20 16:14:02 +08:00
RockYang e11c0a3633 feat: the power log page is ready 2024-03-20 14:14:30 +08:00
RockYang ca83f139f7 feat: no need login refactor with member and chatApps page 2024-03-19 18:59:02 +08:00
RockYang 58e44b7d12 feat: load preview page do not require user to login 2024-03-19 18:25:01 +08:00
RockYang 8f47474edd feat: optimize login dialog 2024-03-19 10:47:13 +08:00
RockYang c5200aada8 feat: refactoring adjustments for reward page is ready 2024-03-18 18:28:34 +08:00
RockYang 4e39b93673 feat: refactoring adjustments for member pages 2024-03-18 16:59:07 +08:00
RockYang f215643f2e feat: The 'chat_models' field of user table, holds the model IDS in place of the model values 2024-03-18 15:37:46 +08:00
RockYang aa72be6ff3 remove new-ui files 2024-03-18 12:01:34 +08:00
RockYang b567e56b60 chore: adjust page styles 2024-03-18 06:46:08 +08:00
RockYang 17431c1707 重构主体工作完成 2024-03-15 18:35:10 +08:00
RockYang 7c9ebef5e9 restore new ui files 2024-03-15 11:13:02 +08:00
廖彦棋 72b0e11543 fix(ui): 交互修复调整 2024-03-15 11:07:41 +08:00
RockYang 9e2af9dbca feat: refactor user list page for new UI 2024-03-15 09:29:19 +08:00
廖彦棋 b7cc987132 fix(ui): 用户管理有效期传参调整,权限标识补充 2024-03-15 09:25:06 +08:00
廖彦棋 8476c73b0c Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:49:18 +08:00
廖彦棋 e76b49afd2 refactor(ui): 无权限页面调整 2024-03-14 17:49:15 +08:00
吴汉强 c712b95e0d feat(ui): 后台首页去掉权限判断 2024-03-14 17:24:40 +08:00
廖彦棋 1ee1a8d6d9 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:19:41 +08:00
廖彦棋 f01c764589 feat(ui): 无权限判断 2024-03-14 17:19:39 +08:00
吴汉强 6df66d150b Merge remote-tracking branch 'origin/ui' into ui 2024-03-14 17:12:53 +08:00
吴汉强 6132f94eff feat(ui): 新增 sql 2024-03-14 17:12:48 +08:00
廖彦棋 ac5e67ea73 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 17:06:15 +08:00
吴汉强 143d2b44d0 feat(ui): 403,没权限 2024-03-14 16:41:38 +08:00
吴汉强 ef130fe377 feat(ui): 网站配置需授权,去掉 2024-03-14 16:28:49 +08:00
廖彦棋 d7d1e2100c fix(ui): 调整 2024-03-14 16:14:49 +08:00
吴汉强 af5afd7700 feat(ui): 后端加权限验证 2024-03-14 15:39:12 +08:00
廖彦棋 a64df5cf0c feat(ui): 角色管理 2024-03-14 15:25:17 +08:00
廖彦棋 3866319373 feat(ui): 新增系统分类菜单 2024-03-14 15:17:53 +08:00
廖彦棋 ec5ad809f2 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-14 10:56:56 +08:00
廖彦棋 268acb174d fix(ui): 修复 2024-03-14 10:56:54 +08:00
吴汉强 3733877be1 Merge remote-tracking branch 'origin/ui' into ui 2024-03-14 10:28:39 +08:00
吴汉强 6d5579a8d6 feat(ui): 登录接口返回权限 2024-03-14 10:28:32 +08:00
廖彦棋 306cd2f945 feat(ui): 管理后台新增权限及部分组合式函数优化 2024-03-14 10:27:09 +08:00
廖彦棋 5d4dd1e66f Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 17:30:26 +08:00
廖彦棋 5f371bdc4a feat(ui): web移动端初始化 2024-03-13 17:30:24 +08:00
吴汉强 aa58ba392e Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 17:24:38 +08:00
吴汉强 0485610818 feat(ui): 增加角色管理,管理员方法新增角色关联 2024-03-13 17:24:30 +08:00
chenzifan 7cb8becc09 Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 14:40:45 +08:00
chenzifan 8fa535a01b refactor: remove api change to post request 2024-03-13 14:40:38 +08:00
吴汉强 5f202e03a2 Merge remote-tracking branch 'origin/ui' into ui 2024-03-13 11:41:07 +08:00
吴汉强 ef2dfe330b feat(ui): 增加系统权限管理 2024-03-13 11:41:01 +08:00
廖彦棋 fcd86fbebd fix(ui): ui调整 2024-03-13 09:54:20 +08:00
RockYang 960d294aa2 docs: update sql file 2024-03-13 08:49:40 +08:00
RockYang 27f8e63f3d Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 08:48:10 +08:00
chenzifan e8d7ad58be fix: 删除系统管理员失效的问题 2024-03-13 08:47:17 +08:00
chenzifan f4d537e0f5 Merge remote-tracking branch 'origin/ui' into ui
# Conflicts:
#	api/handler/admin/admin_user_handler.go
2024-03-13 08:46:16 +08:00
chenzifan d6c93dd537 fix: 删除系统管理员失效的问题 2024-03-13 08:45:09 +08:00
廖彦棋 aa5fff47de Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-13 08:44:50 +08:00
廖彦棋 c920d8423d feat(ui): 调整 2024-03-13 08:44:48 +08:00
chenzf@pvc123.com b8fcfe2e7c feat: 超级管理员不支持修改和删除 2024-03-12 21:16:05 +08:00
RockYang 072c96e1ec Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-12 18:07:24 +08:00
RockYang 38c8ece642 fix conflicts 2024-03-12 18:07:19 +08:00
chenzifan 9bf1576fb4 feat: 增加系统管理员 2024-03-12 18:06:49 +08:00
RockYang d648e177c4 fix conflicts 2024-03-12 18:03:24 +08:00
RockYang e8f62af7f6 refactor: use power replace calls for front pages 2024-03-12 17:47:06 +08:00
RockYang e746aafa2f refactor: 重构项目,为所有的 AI 工具都引入算力,采用算力统一结算各个工具的调用次数和权限 2024-03-12 15:40:44 +08:00
廖彦棋 71f7a1b166 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-12 08:37:29 +08:00
廖彦棋 678f570a90 refactor(ui): 调整 2024-03-12 08:37:27 +08:00
huangqj 4bc1f195db fix(ui):环境变量 2024-03-11 16:10:49 +08:00
廖彦棋 b2ff49ee94 feat(ui): 新增系统管理员 2024-03-11 15:59:15 +08:00
廖彦棋 d9558f13ad fix(ui): 删除冗余 2024-03-11 14:23:11 +08:00
廖彦棋 8dc8f2567e fix(ui): 删除冗余 2024-03-11 14:22:39 +08:00
RockYang 316636f83c feat: replace Tools param with Function param for OpenAI chat API 2024-03-11 14:09:19 +08:00
廖彦棋 e3c2fbf82a Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-11 13:52:24 +08:00
廖彦棋 1b6175e598 fix(ui): type 2024-03-11 13:52:22 +08:00
chenzifan 26dc479596 feat: 优化后台UI 2024-03-11 13:51:26 +08:00
chenzifan dca25dbb78 Merge remote-tracking branch 'origin/ui' into ui 2024-03-11 13:46:46 +08:00
廖彦棋 c55275872e refactor(ui): 调整优化 2024-03-11 13:46:08 +08:00
廖彦棋 d4d1fb26f6 feat(ui): 细节优化 2024-03-11 12:02:20 +08:00
廖彦棋 a48cb7bc34 feat(ui): 上传功能补充 2024-03-11 11:41:50 +08:00
廖彦棋 8af6e9290f feat(ui): 登录新增验证码及记住密码功能 2024-03-11 10:49:13 +08:00
廖彦棋 14b277d813 feat(ul): 顶部信息 2024-03-11 09:00:00 +08:00
huangqj 20915038a3 feat(ui):看板图表 样式调整 2024-03-11 08:35:21 +08:00
chenzifan 15723457cb Merge remote-tracking branch 'origin/ui' into ui 2024-03-11 08:01:54 +08:00
RockYang be8a0ec184 opt: remove global keyup event bind 2024-03-10 09:45:59 +08:00
RockYang ec79fc933c opt: remove global keyup event bind 2024-03-10 09:45:59 +08:00
RockYang b02e3aad95 docs: update config.toml 2024-03-10 09:45:59 +08:00
RockYang 3ed5a420a7 docs: update config.toml 2024-03-10 09:45:59 +08:00
RockYang 08eca511ad docs: add database sql file for v3.2.7 2024-03-10 09:45:59 +08:00
RockYang 711e701933 docs: add database sql file for v3.2.7 2024-03-10 09:45:59 +08:00
RockYang c34e911596 feat: allow to view chat message in manager console 2024-03-10 09:45:59 +08:00
RockYang e6bbd83c6b feat: allow to view chat message in manager console 2024-03-10 09:45:59 +08:00
RockYang 8a452c3072 feat: download image which ai generated in dialog and replace the image url 2024-03-10 09:45:59 +08:00
RockYang b99b310306 feat: download image which ai generated in dialog and replace the image url 2024-03-10 09:45:59 +08:00
RockYang 13bfb14107 feat: image-wall page for mobile is ready 2024-03-10 09:45:59 +08:00
RockYang e3c46016e0 feat: image-wall page for mobile is ready 2024-03-10 09:45:59 +08:00
RockYang 4188b0969e feat: allow user config third-party platform openai and mj api key 2024-03-10 09:45:59 +08:00
RockYang eb739fcccb feat: allow user config third-party platform openai and mj api key 2024-03-10 09:45:59 +08:00
RockYang 0c27795a10 feat: added delete file function 2024-03-10 09:45:59 +08:00
RockYang 9a94505f43 feat: added delete file function 2024-03-10 09:45:59 +08:00
RockYang d05693c5c1 fix: verifycation component touch event coordinates misplace in iphone browser 2024-03-10 09:45:59 +08:00
RockYang 7c5c3d8a3c fix: verifycation component touch event coordinates misplace in iphone browser 2024-03-10 09:45:59 +08:00
RockYang c0b2063b38 fix: fix bug for regenerate button did not work 2024-03-10 09:45:59 +08:00
RockYang 7027f9a55b fix: fix bug for regenerate button did not work 2024-03-10 09:45:59 +08:00
RockYang 4d183747b1 fix: Upscale and Variation task overrite each other 2024-03-10 09:45:59 +08:00
RockYang cf3d8af260 fix: Upscale and Variation task overrite each other 2024-03-10 09:45:59 +08:00
RockYang 08fe1b2f75 feat: midjourney mobile page all function is ready 2024-03-10 09:45:59 +08:00
RockYang 4ea7352674 feat: midjourney mobile page all function is ready 2024-03-10 09:45:59 +08:00
RockYang db3e8a267e feat: mobile mj list page is ready 2024-03-10 09:45:59 +08:00
RockYang 6e6885849b feat: mobile mj list page is ready 2024-03-10 09:45:59 +08:00
RockYang 8fc62682c4 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-03-10 09:45:59 +08:00
RockYang 827f2b9739 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-03-10 09:45:59 +08:00
RockYang 75031914a3 feat: add functions mj page for mobile 2024-03-10 09:45:59 +08:00
RockYang 54d70623ab feat: add functions mj page for mobile 2024-03-10 09:45:59 +08:00
RockYang a4c9fdd95a opt: add default extension for mj image 2024-03-10 09:45:59 +08:00
RockYang ed819e8c79 opt: add default extension for mj image 2024-03-10 09:45:59 +08:00
RockYang 6a9bfeb5aa feat: mj for mobile page payout is ready 2024-03-10 09:45:59 +08:00
RockYang 94680c4411 feat: mj for mobile page payout is ready 2024-03-10 09:45:59 +08:00
RockYang e654766f60 add docs and github link 2024-03-10 09:45:59 +08:00
RockYang 8bcbb68438 add docs and github link 2024-03-10 09:45:59 +08:00
RockYang 0ef6955f96 opt: enable use cdn url for mj-plus 2024-03-10 09:45:59 +08:00
RockYang b9f31d42f4 opt: enable use cdn url for mj-plus 2024-03-10 09:45:59 +08:00
RockYang b4501557c9 feat: LaTeX parse is ready 2024-03-10 09:45:59 +08:00
RockYang dc2b768c6b feat: LaTeX parse is ready 2024-03-10 09:45:59 +08:00
RockYang a2ed99e6cb feat: add model field for chat_item and and chat_history data table 2024-03-10 09:45:59 +08:00
RockYang 60df732db7 feat: add model field for chat_item and and chat_history data table 2024-03-10 09:45:59 +08:00
RockYang 6bd6bb3885 feat: add err_msg field for mj and sd jobs 2024-03-10 09:45:59 +08:00
RockYang 6d85d128cc feat: add err_msg field for mj and sd jobs 2024-03-10 09:45:59 +08:00
RockYang 399cf65fc9 feat: blend and swap face function for midjourney-plus is ready 2024-03-10 09:45:59 +08:00
RockYang 96785ca037 feat: blend and swap face function for midjourney-plus is ready 2024-03-10 09:45:59 +08:00
RockYang 24906a6df1 feat: add blend and swapface task implements for midjourney 2024-03-10 09:45:59 +08:00
RockYang 7cfc747653 feat: add blend and swapface task implements for midjourney 2024-03-10 09:45:59 +08:00
RockYang d772bbebe6 opt: refactor chat session page for mobile device 2024-03-10 09:45:59 +08:00
RockYang 05f911628a opt: refactor chat session page for mobile device 2024-03-10 09:45:59 +08:00
RockYang 14988853a3 opt: optimize chat list page for mobile 2024-03-10 09:45:59 +08:00
RockYang 18b718a328 opt: optimize chat list page for mobile 2024-03-10 09:45:59 +08:00
RockYang 7b3f16ac9f feat: use vant replace element-plus as mobile UI framework 2024-03-10 09:45:59 +08:00
RockYang 6d9b9bd3f7 feat: use vant replace element-plus as mobile UI framework 2024-03-10 09:45:59 +08:00
RockYang 82b2755c18 feat: add websocket heartbeat message for mj page 2024-03-10 09:45:59 +08:00
RockYang 04e990aeb3 feat: add websocket heartbeat message for mj page 2024-03-10 09:45:59 +08:00
廖彦棋 cbbcd4c571 fix(ui): 细节调整 2024-03-08 17:46:48 +08:00
huangqj 5ad9adeb4a feat(ui):细节调整 2024-03-08 10:24:38 +08:00
廖彦棋 13c9594be0 feat(ui): prettier 2024-03-08 09:59:40 +08:00
廖彦棋 e4f5f78bfa refactor(ui): 登录页重构 2024-03-08 09:45:09 +08:00
廖彦棋 bfa0208692 refactor(ui): 优化 2024-03-08 09:12:39 +08:00
huangqj 64994cdeda feat(ui):路由 2024-03-08 08:35:41 +08:00
廖彦棋 9796a841d9 Merge branch 'ui' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-07 18:03:08 +08:00
廖彦棋 404b493d23 feat(ui): 过期跳转登录 2024-03-07 18:03:06 +08:00
huangqj c04f9eb993 feat(ui):apiKey 语言模型 角色管理 产品 2024-03-07 17:58:25 +08:00
廖彦棋 825da42208 feat(ui): 新增系统设置 2024-03-07 17:24:50 +08:00
廖彦棋 721a8f1ecb feat(ui): 对话管理 2024-03-07 15:32:32 +08:00
huangqj 586a174d6c feat(ui):用户 2024-03-07 15:05:01 +08:00
huangqj bec6966542 feat(ui):simpleTable 2024-03-07 14:59:45 +08:00
huangqj b1f4e1d4d8 feat(ui):searchtable 2024-03-07 14:59:16 +08:00
huangqj d7d64e8259 feat(ui):用户 2024-03-07 14:03:55 +08:00
廖彦棋 08b498fccd feat(ui): 函数管理 2024-03-07 11:40:57 +08:00
廖彦棋 ecc5a52232 feat(ui): 新增弹窗及时间格式化 2024-03-07 09:23:45 +08:00
chenzifan 7327b90255 Merge branch 'main' of 172.28.1.6:yangjian/chatgpt-plus into ui 2024-03-07 08:37:54 +08:00
chenzifan 6ce7a9c16f feat: 增加系统用户管理 2024-03-07 08:37:48 +08:00
廖彦棋 eb2658a91c fix(ui): ts类型 2024-03-06 18:20:07 +08:00
RockYang 9f59a18756 feat: update changelogs 2024-03-06 17:58:17 +08:00
廖彦棋 ef06f0da98 feat(ui): 新增登录 2024-03-06 17:54:38 +08:00
RockYang 4dec0e2c69 feat: Mj and sd jobs data loading in pages 2024-03-06 17:31:54 +08:00
RockYang 5f36f1af11 add prompt translating function for mobile midjourney page 2024-03-06 16:22:03 +08:00
huangqj 5a1a596098 feat(ui):simpleTable 2024-03-06 15:33:37 +08:00
廖彦棋 7a25c6e062 feat(ui): 新增请求方法及表格 2024-03-06 13:55:38 +08:00
廖彦棋 a73ed71b9d feat(ui): 管理后台基础配置 2024-03-06 10:23:55 +08:00
廖彦棋 2abbb95ea2 chore(ui): 目录结构调整 2024-03-06 09:32:47 +08:00
廖彦棋 1f80ba9463 feat(ui): 初始化 2024-03-06 09:27:11 +08:00
chenzifan 4327fdac12 refactor: 初始化UI重构 2024-03-06 08:57:46 +08:00
RockYang a6f05a5874 Merge branch 'dev' of gitee.com:blackfox/chatgpt-plus-pro into dev 2024-03-04 08:34:12 +08:00
RockYang 3fb74a1598 feat: add removing order button in admin order list page 2024-03-03 19:27:22 +08:00
RockYang 2ac44cdeb6 fix: fix major bugs for unauthorized access to data 2024-03-03 10:40:32 +08:00
RockYang 4e4dc4cb73 Update README.md 2024-03-01 23:08:05 +08:00
RockYang 855a953010 Update README.md 2024-03-01 23:08:05 +08:00
RockYang 3264fda06e fix: fixed bug image preview im mobile chat session page 2024-02-29 15:41:45 +08:00
RockYang 2c7d472069 feat: add draw same image for midjourney page 2024-02-29 11:44:09 +08:00
RockYang 8182e6797f opt: add logs for mj-plus api error 2024-02-28 15:50:42 +08:00
RockYang 99cb48ce88 opt: replace proxy url for discord image url 2024-02-27 17:45:57 +08:00
RockYang e391bfca80 feat: add change password in with mobile page 2024-02-27 15:36:20 +08:00
RockYang 0181ad3715 feat: add image preview for mobile chat page 2024-02-26 18:11:37 +08:00
RockYang b95dff0751 feat: replace http polling with webscoket notify in sd image page 2024-02-26 15:45:54 +08:00
RockYang 668d4c9c64 chore: replace 'token' with power 2024-02-23 18:11:57 +08:00
RockYang 66c0d1b2f7 docs: update mj-plus api domain 2024-02-23 15:41:02 +08:00
RockYang bef0996375 opt: remove global keyup event bind 2024-02-23 11:43:27 +08:00
RockYang 391a1e4cf0 docs: update config.toml 2024-02-22 17:53:39 +08:00
RockYang 38facf517a docs: add database sql file for v3.2.7 2024-02-22 17:28:22 +08:00
RockYang 5c9b1e8764 feat: allow to view chat message in manager console 2024-02-22 17:16:44 +08:00
RockYang dbcf14b566 feat: download image which ai generated in dialog and replace the image url 2024-02-20 18:38:03 +08:00
RockYang cc59f0c761 feat: image-wall page for mobile is ready 2024-02-20 17:38:18 +08:00
RockYang f263fc067a feat: allow user config third-party platform openai and mj api key 2024-02-20 11:23:55 +08:00
RockYang 6635b88baa feat: added delete file function 2024-02-19 16:43:03 +08:00
RockYang 87ea2a611f fix: verifycation component touch event coordinates misplace in iphone browser 2024-02-19 14:04:50 +08:00
RockYang ea8a2135cd fix: fix bug for regenerate button did not work 2024-02-19 11:22:42 +08:00
RockYang 76d86395ef fix: Upscale and Variation task overrite each other 2024-02-16 18:08:29 +08:00
RockYang e268870636 feat: midjourney mobile page all function is ready 2024-02-16 15:55:04 +08:00
RockYang 49e9f41ef2 feat: mobile mj list page is ready 2024-02-15 18:11:22 +08:00
RockYang 68cda968a1 feat: add mj image list component for mobile page. fixed bug for html tag escape 2024-02-15 11:39:04 +08:00
RockYang f6826fcefc feat: add functions mj page for mobile 2024-01-31 07:24:35 +08:00
RockYang 56a91db7e7 opt: add default extension for mj image 2024-01-30 21:46:17 +08:00
RockYang 8f4d94f046 feat: mj for mobile page payout is ready 2024-01-30 18:34:01 +08:00
RockYang 9446bf5f98 add docs and github link 2024-01-30 16:18:27 +08:00
RockYang 23e353b18b opt: enable use cdn url for mj-plus 2024-01-28 21:56:25 +08:00
RockYang 46daaa4ba1 feat: LaTeX parse is ready 2024-01-26 18:04:53 +08:00
RockYang f66d59531d feat: add model field for chat_item and and chat_history data table 2024-01-26 16:54:00 +08:00
RockYang b76cd1d5ae feat: add err_msg field for mj and sd jobs 2024-01-26 14:50:36 +08:00
RockYang 55ba0d08a5 feat: blend and swap face function for midjourney-plus is ready 2024-01-26 11:57:08 +08:00
RockYang cc78780a8e feat: add blend and swapface task implements for midjourney 2024-01-25 18:50:24 +08:00
RockYang 4a00809061 opt: refactor chat session page for mobile device 2024-01-25 14:07:10 +08:00
RockYang cd760bbd84 opt: optimize chat list page for mobile 2024-01-24 18:23:24 +08:00
RockYang 84bdc6be7f feat: use vant replace element-plus as mobile UI framework 2024-01-24 17:34:30 +08:00
RockYang 8dd6bf8933 feat: add websocket heartbeat message for mj page 2024-01-24 09:33:04 +08:00
RockYang 08f170d217 add v3.2.6 database sql file 2024-01-23 18:00:49 +08:00
RockYang 20da6a1c20 doc: update config sample file 2024-01-23 17:56:22 +08:00
RockYang 8586931630 fix: auto fill apiURL when platform changed for ApiKey add page 2024-01-23 17:30:54 +08:00
RockYang 667fc79a6f feat: merge sms branch,add DuanXinBao sms service implemetation 2024-01-23 16:16:47 +08:00
RockYang ac7430dded opt: add heartbeat message for websocket connects 2024-01-22 18:42:51 +08:00
whale_fall 9c73e16560 feat: 添加支持多个短信服务商支持 添加短信宝服务商支持,同时添加配置示例 2024-01-22 16:38:44 +08:00
RockYang c605e222a7 fix: fix bug for wechat transfer message parse failed 2024-01-22 16:10:08 +08:00
RockYang 578f26a738 feat: HuPiPay order check function is ready 2024-01-22 15:17:26 +08:00
RockYang 24218e7570 opt: verify the order in notify callback 2024-01-22 13:58:25 +08:00
RockYang 16f946550e chore: print error detail when call http api failed with mj 2024-01-21 22:30:24 +08:00
RockYang 9960a31485 opt: add image upload support for md-editor-3 2024-01-19 18:43:13 +08:00
RockYang 437632f859 fixed conflicts 2024-01-19 18:21:49 +08:00
RockYang 98f0f442eb feat: system notice function is ready 2024-01-19 18:19:51 +08:00
RockYang 3b999dc5e2 feat: system notice function is ready 2024-01-19 18:18:10 +08:00
RockYang 20943ad3d3 fix: fixed bug for img_call increased when upscale task run failed 2024-01-19 17:10:52 +08:00
RockYang 3b1544b5e4 feat: add system config item for wechat qrcode 2024-01-19 16:58:13 +08:00
RockYang b317d597ac chore: optimize variable name 2024-01-19 11:26:22 +08:00
RockYang 3bb19811e7 Merge branch 'main' into dev 2024-01-19 10:09:18 +08:00
RockYang 065a01de48 Merge branch 'main' of gitee.com:blackfox/chatgpt-plus 2024-01-19 10:09:01 +08:00
RockYang 09fb2f6557 !4 添加支持阿里旗下的大模型 通义千问对话
Merge pull request !4 from 鲸落/qwen
2024-01-19 02:06:17 +00:00
whale_fall 9ca97bf4bc feat: 添加支持阿里的通义千问对话 2024-01-19 09:52:16 +08:00
RockYang 0c59503edb feat: add image publish function, ONLY published image show in image wall page 2024-01-19 06:52:23 +08:00
RockYang 6fa08861f8 update change log 2024-01-18 18:11:25 +08:00
RockYang 72b95151a1 feat: add system config disable user registeration 2024-01-18 17:24:02 +08:00
RockYang 378e6ec9af opt: compatible wechat old message format for parsing wechat transfer message 2024-01-18 16:58:20 +08:00
RockYang 27b68c1174 opt: optimize order query alg, reduce polling times 2024-01-18 09:39:36 +08:00
RockYang 1ae8eabe99 update config 2024-01-16 15:24:06 +08:00
RockYang 72726c1cf9 docs: update change log 2024-01-16 15:05:54 +08:00
RockYang 6203d9a150 docs: update config files 2024-01-16 14:38:18 +08:00
RockYang 861f11af66 opt: optimize markdown image parser, identify image and blockquote tags 2024-01-16 10:13:00 +08:00
RockYang 39fdfae541 feat: attachments manage function is ready 2024-01-15 18:48:01 +08:00
RockYang 9f57fb1421 feat: gpt-4-gizmo-g-* model is supported 2024-01-15 15:03:05 +08:00
RockYang 059f57db2d feat: gpt-4-all model is ready 2024-01-15 14:07:24 +08:00
RockYang 13c8be8d06 opt: optimize vip recharge logic 2024-01-15 11:01:57 +08:00
RockYang e0dbfb0488 Merge branch 'dev' 2024-01-15 10:29:01 +08:00
RockYang 722ed128a1 opt: optimize vip recharge logic 2024-01-15 10:28:46 +08:00
RockYang cc628821e6 feat: add asynchronously pull midjourney task progress in case the synchronization callback is fails 2024-01-12 18:24:28 +08:00
RockYang 857af61af1 feat: midjourney plus service is ready 2024-01-11 18:16:48 +08:00
RockYang f6fc484cb3 feat: update video tutorial 2024-01-10 08:50:13 +08:00
RockYang ba07946f1e feat: update video tutorial 2024-01-10 08:48:05 +08:00
RockYang 600f668c63 remove api key for hupipay 2024-01-09 17:16:27 +08:00
RockYang 8a8990b12b fix: fixed bug for gorm insert record failed and Error is not nil 2024-01-08 18:10:32 +08:00
RockYang fe85c1bbb9 feat: change mobile field to username 2024-01-08 17:34:09 +08:00
RockYang a89ff72308 add changelog 2024-01-08 12:01:58 +08:00
RockYang a99e58184b chore: do not close pop window when click model 2024-01-08 11:01:19 +08:00
RockYang d981bc8fd0 fix: function call 兼容中转 API 2024-01-07 22:32:59 +08:00
RockYang ff22480d89 opt: add support to disable code verify 2024-01-07 17:31:26 +08:00
RockYang 8ec85e2829 feat: payjs payment channel is ready 2024-01-07 14:36:02 +08:00
RockYang 706aacd8e6 fix: add user failed in admin user list page 2024-01-07 10:49:36 +08:00
RockYang 803bc2d9fc release v3.2.4 2024-01-06 21:09:19 +08:00
RockYang 7cc67cf8f0 chore: remove useless system config items 2024-01-06 17:38:55 +08:00
RockYang 9909c3a0ed chore: error recover is enable ONLY in debug mode 2024-01-06 17:16:02 +08:00
RockYang 60a3751839 feat: payjs service is ready 2024-01-06 15:53:30 +08:00
RockYang ce87e0c40b chore: rename bind username api 2024-01-05 18:21:47 +08:00
RockYang 71cd548c00 feat: email registration function is ready 2024-01-05 18:17:11 +08:00
RockYang 1ddd05302b feat: update api key last_use_time after dalle3 call 2024-01-04 18:15:00 +08:00
RockYang ea3301c75c feat: support dall-e3 api mirrors, add name field for ApiKey 2024-01-04 16:29:57 +08:00
RockYang 4b1c4f7ccc feat: refactor LLM api request code, get API URL from ApiKey object 2024-01-04 14:51:33 +08:00
RockYang 21f2622a4b feat: api key manage page funciton is ready 2024-01-04 10:48:04 +08:00
RockYang 79f6a43019 fix: fixed bug for concurrency risk for getting token for chat histroy with issue #92 2024-01-04 09:03:19 +08:00
RockYang 3d75093b2c fix: add unique key for MidJourney task_id 2024-01-03 18:06:10 +08:00
RockYang 023dc89c3e feat: show notice in chat page 2024-01-03 15:19:24 +08:00
RockYang b5641be30d feat: fixed bug for wechat bot to parse transactions. enable user to exchange reward with img_calls 2024-01-03 11:15:54 +08:00
RockYang ff4515bbf1 fix: fixed chat export page styles 2024-01-02 11:32:36 +08:00
RockYang 5dc0fe05af fix: fixed for img_call repeated reductions 2024-01-01 18:54:48 +08:00
RockYang 25bba912f6 feat: add switch for enable|disable chat role 2023-12-29 17:51:56 +08:00
RockYang cb6e323596 feat: add nickname field for user 2023-12-29 17:39:37 +08:00
RockYang cf4b04e047 feat: add authorization for local function call 2023-12-29 17:21:29 +08:00
RockYang 243b5be31c update changelog 2023-12-29 11:53:37 +08:00
RockYang c3d753cf38 feat: add router for function manager 2023-12-29 11:22:26 +08:00
RockYang 70c46d098b fix: restore user img_calls quota when image task run failed 2023-12-29 10:41:29 +08:00
RockYang 816e4a096e merge pull request #72 2023-12-29 10:09:37 +08:00
RockYang 1d1e819817 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-12-29 09:39:52 +08:00
RockYang 342daf1be8 Merge pull request #72 from Unclesimonlau/main
重新设计了移动端web页面,新增了移动端CSS,增加移动端SD绘图页面
2023-12-29 09:39:33 +08:00
RockYang f6bb9b582a merge pull request #71 2023-12-29 09:31:25 +08:00
RockYang aad62bc976 Merge pull request #71 from JingHong0202/main
fix: Azure Api request failure after changing the API-version parameter
2023-12-29 09:27:23 +08:00
RockYang 38a49a7f37 chore: remove dead code 2023-12-29 09:02:55 +08:00
RockYang f603bf6be7 feat: function manager refactor is ready 2023-12-28 18:14:38 +08:00
JingHong 6cc05e9e27 Merge branch 'yangjian102621:main' into main 2023-12-27 23:33:46 +08:00
UncleSimonlau c00e67542f Merge branch 'main' of https://github.com/Unclesimonlau/chatgpt-plus 2023-12-26 14:19:25 +08:00
RockYang 22660362a3 fix: fixed bug #70, XunFei 1.5 url version map error 2023-12-26 14:19:00 +08:00
RockYang b1ab9975b7 fix: fixed bug #70, XunFei 1.5 url version map error 2023-12-25 08:54:17 +08:00
RockYang 7ca89d8b54 feat: function CRUD operation is ready 2023-12-24 22:12:12 +08:00
jinghong0202 998b07ab8c fix: Azure Api 更换api-version参数后请求失败的问题 2023-12-24 08:36:34 +01:00
RockYang 4936896ff7 feat: function add for admin page is ready 2023-12-23 22:30:27 +08:00
RockYang 18b7484c5b feat: support CDN reverse proxy for MidJourney and OpenAI API 2023-12-22 17:25:31 +08:00
RockYang 6754c8e85e feat: add function list page in admin console 2023-12-21 18:06:09 +08:00
RockYang 57243ff010 opt: optimize image compress alg, add cache control for image 2023-12-21 15:00:46 +08:00
RockYang 7c4dfe96ee feat: add funcitons manger page 2023-12-21 08:58:24 +08:00
RockYang bf19120c27 feat: auto translate image creating prompt 2023-12-19 18:54:19 +08:00
RockYang 3d37087916 fix: fixed bug for HuPiPay qrcode generation. set field 'openid' of result struct to Any data type 2023-12-19 11:31:57 +08:00
RockYang 6949f1328c chore: update copyright information 2023-12-18 18:19:41 +08:00
RockYang b63090c20f feat: add remove action to remove task and images for MJ and SD task list page 2023-12-18 17:44:52 +08:00
RockYang b15ebda948 opt: merge RAG branch 2023-12-18 16:41:40 +08:00
RockYang 252eef2e5e opt: make sure the Upscale and Variation task is assign to the same mj service with Image task 2023-12-18 16:34:33 +08:00
RockYang 3bea8f9706 fix: fixed bug for mj service pool config pointer 2023-12-15 22:52:57 +08:00
RockYang dfe808bee7 feat: add img_calls field for recharge products 2023-12-15 16:56:56 +08:00
RockYang 58f3dd5336 chore: update default config.toml 2023-12-15 11:23:13 +08:00
RockYang b00f16648f opt: limit the image display size in reply component 2023-12-15 10:48:13 +08:00
RockYang fc66c31e89 docs: update changelog 2023-12-15 09:04:02 +08:00
RockYang a26c849532 docs: add sql file 2023-12-14 17:05:51 +08:00
RockYang d2991e60b6 refactor: refactor stable diffusion service, add service pool support 2023-12-14 16:48:54 +08:00
RockYang 10ba1430f9 chore: add sub dir support for OSS 2023-12-13 17:02:49 +08:00
RockYang 6d71f24f75 refactor: add midjourney pool implementation, add translate prompt for mj drawing 2023-12-13 16:38:27 +08:00
RockYang 8f4d20e411 fix: fixed bug for aliyun OSS img url 2023-12-13 09:49:55 +08:00
RockYang cf758d773e refactor mj service, add mj service pool support 2023-12-12 18:33:24 +08:00
RockYang c012f0c4c5 opt: remove default value for stable-diffusion page 2023-12-12 09:59:20 +08:00
RockYang 9e85900057 docs: update readme file 2023-12-12 09:52:28 +08:00
RockYang 68e3b787f9 docs: update build config.toml 2023-12-12 07:25:36 +08:00
RockYang 6706704ad6 docs: update changelog file 2023-12-11 17:17:17 +08:00
RockYang 1067fa015e add translate api for midjourney 2023-12-11 17:01:02 +08:00
RockYang 411335ebcb feat: add prompt translate handler 2023-12-11 06:56:00 +08:00
RockYang f19c15491e feat: add system config item for dall e3 generate image num 2023-12-10 17:13:25 +08:00
RockYang 0da81a4d10 chore: change default params for stable diffusion 2023-12-10 14:45:22 +08:00
RockYang 049839a5c7 feat: add HuPiPay payment support 2023-12-08 19:43:13 +08:00
RockYang a901927844 feat: add image generation API URL in chat configurations 2023-12-07 16:31:32 +08:00
RockYang 07ab0bc5a1 docs: add arm64 build script 2023-12-07 15:44:20 +08:00
RockYang a6025e6fab fix: fixed bug for prompt code format, prevent xss attacks 2023-12-07 14:02:13 +08:00
RockYang e841a61bf0 opt: save chat ID when the chat websocket disconnect 2023-12-07 11:07:08 +08:00
RockYang 2f1c2110c6 feat: adjust task list component styles 2023-12-06 19:05:51 +08:00
RockYang 8e573eea4c Merge branch 'pr_3' into dev 2023-12-06 18:54:45 +08:00
RockYang 149765764e feat: refactor midjourney image creating page 2023-12-06 18:54:30 +08:00
RockYang 45d6444c7e docs: update readme file 2023-12-06 14:44:06 +08:00
liyuwanglan aa65f49190 修改 2023-12-05 11:08:03 +08:00
RockYang 1bda41ff67 docs: update comments 2023-11-30 17:35:56 +08:00
RockYang 5a799139cd fix: fixed bug for upload image failed 2023-11-29 17:46:46 +08:00
RockYang eb36d0742a opt: create new chat session when change role or model, fix bug for mobile no validate 2023-11-29 17:36:27 +08:00
RockYang d8a9123852 docs: update change logs 2023-11-28 15:33:30 +08:00
RockYang 19031c2197 docs: 添加一键部署脚本 2023-11-28 15:25:51 +08:00
RockYang 7a2ffdf39c opt: 缩略图生成算法 2023-11-28 14:50:19 +08:00
RockYang ce8fa79206 feat: 为大图片生成缩略图,加快前端图片加载速度 2023-11-28 12:04:02 +08:00
RockYang b05c63549d fix: fix bug for oss image domain 2023-11-28 07:27:18 +08:00
RockYang 9f98660423 docs: update build script 2023-11-27 18:24:52 +08:00
RockYang 7b0fa862d2 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-27 12:03:23 +08:00
RockYang bfe2d4d573 feat: merge mysql and redis docker service to docker-compose.yaml file 2023-11-27 10:56:18 +08:00
RockYang 26950c5673 Merge pull request #55 from openjst/main
修改code显示颜色样式
2023-11-27 10:27:13 +08:00
RockYang 86788362a5 docs: make a full docker-compose.yaml 2023-11-27 07:21:37 +08:00
openjst deedc5fb2e 修改code显示颜色样式 2023-11-26 23:26:08 +08:00
RockYang f4fbe67db9 feat: implements image function replace Mj with DALL-E-3 2023-11-26 20:37:48 +08:00
RockYang 599ce0eade feat: add type field for api key 2023-11-24 18:05:59 +08:00
RockYang 11f3ab8dc7 feat: add support for registing use force use invite code 2023-11-24 12:02:28 +08:00
RockYang 75b4a6dd46 opt: optimize image preview for MidJourney image list page, only preview current image not for all images 2023-11-23 17:55:12 +08:00
RockYang 86bbea337d opt: 增加中间件自动对HTTP请求的参数去掉首尾空格 2023-11-23 17:50:55 +08:00
RockYang e63a30064b opt: optimize styles for invitation page 2023-11-23 17:40:15 +08:00
RockYang 222b1ddbd9 feat: add invitation and promotion functions 2023-11-23 16:30:15 +08:00
RockYang a88dd88a07 docs: add database sql file for v3.1.9 2023-11-23 09:58:01 +08:00
RockYang a47c01fd41 docs: update change log 2023-11-23 09:46:49 +08:00
RockYang 35e385f7a7 docs: update change log and readme file 2023-11-23 09:14:42 +08:00
RockYang 191e3b7d2c feat: 支持讯飞大模型 v3.0 2023-11-23 07:11:13 +08:00
RockYang 13fbbc190a feat: reset password function is ready 2023-11-22 18:00:45 +08:00
RockYang 61c6a6e7f3 feat: add copy code btn in chat page, fixed bug for code wrap in model of ChatGLM and XunFei 2023-11-22 17:00:43 +08:00
RockYang 911f218f0b feat: add feekback orcode in chat page 2023-11-22 12:05:58 +08:00
RockYang af41350eb9 fix: fixed bug for enable user vip in admin console not work 2023-11-22 11:30:58 +08:00
RockYang 8f63e348b3 option: replace leveldb with redis in storing message code 2023-11-22 10:57:24 +08:00
RockYang 8901cf8e81 Merge pull request #52 from KunMingStar/fix_migrate
fix: remove dead code
2023-11-22 08:23:01 +08:00
tongkunming e11736e1d2 fix: remove dead code 2023-11-21 22:18:53 +08:00
RockYang c062fba69c chore: remove dead code 2023-11-21 19:04:53 +08:00
RockYang 4628095fe7 feat: add wechat id card for closing register 2023-11-20 16:38:04 +08:00
RockYang 787ed9bc0f docs: update docs installation docs url 2023-11-17 18:42:13 +08:00
RockYang daaff8974e docs: update readme and config sample files 2023-11-16 17:20:38 +08:00
RockYang 4fcbd511c0 fix: fix bug with missing chat context 2023-11-15 18:20:34 +08:00
RockYang 64d6bff6d9 fix: unbind event for login page when the component is unmount 2023-11-14 18:19:40 +08:00
RockYang 998cbace92 feat: add link on logo 2023-11-14 13:50:44 +08:00
RockYang 2204038951 fix: fix member page flex styles 2023-11-14 11:21:16 +08:00
RockYang 998f67ac5d fix: deducating the user's img call quota after stable diffusion callback 2023-11-14 08:59:39 +08:00
RockYang be981a2c9b feat: 更改 MJ 和 SD 菜单图标 2023-11-13 11:27:42 +08:00
RockYang da3cf0d4a3 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-13 10:11:45 +08:00
RockYang 7124b9a36c feat: 更改 MJ 和 SD 菜单图标 2023-11-13 10:11:21 +08:00
RockYang 712a6b6c39 chore: change xxl-job name 2023-11-12 15:39:12 +08:00
RockYang ed0a1c57d6 opt: 优化 ItemList 组件样式,调整支付页面布局 2023-11-11 22:11:04 +08:00
RockYang 7858228505 docs: update change log file 2023-11-11 12:58:57 +08:00
RockYang 2525a22d78 fix: fix bug for issue 49, stable diffusion service not decrease img_calls 2023-11-11 11:00:26 +08:00
RockYang 7b4ef8fc31 opt: 优化 ItemList 组件样式 2023-11-11 10:53:33 +08:00
RockYang ce14cd02e4 feat: add order list compponent 2023-11-10 18:06:32 +08:00
RockYang 05f501af52 Merge branch 'alipay' 2023-11-10 16:51:00 +08:00
RockYang e94fa4844f docs: export database, update readme doc, remove useless configs for alipay 2023-11-10 16:49:07 +08:00
RockYang e0965aae5e feat: add switch to disable xxl-job service, update readme 2023-11-10 15:22:35 +08:00
RockYang 414c1de963 feat: 增加订单倒计时组件,自动清理过期未支付订单 2023-11-10 14:39:27 +08:00
RockYang b589102be8 feat: add system configration switch option for order pay service, support sandbox env for alipay 2023-11-09 18:28:56 +08:00
RockYang df367e0d47 feat: check if the user's chat quota is gt than current chat model required before starting a conversation 2023-11-09 16:56:44 +08:00
RockYang 680219ebcb Merge pull request #47 from KunMingStar/fix/sendMessage
fix: 添加messages为空校验
2023-11-09 08:21:24 +08:00
RockYang ef87487f60 fix: fix bug for token expired with QiNiu oss upload file 2023-11-08 21:14:09 +08:00
RockYang 8bafe72434 feat: adjust layout for user profile page 2023-11-08 18:33:26 +08:00
RockYang 76dcc69e44 feat: order payment function is ready 2023-11-08 17:48:07 +08:00
tongkunming 682ef22194 fix: 添加messages为空校验 2023-11-08 16:51:36 +08:00
RockYang ee8dd41605 feat: update stable diffusion api version 2023-11-08 14:54:10 +08:00
RockYang d0b8d666e4 feat: finish payment page layout 2023-11-07 18:10:28 +08:00
RockYang 4a81826d19 feat: adjust table styles for markdown 2023-11-07 12:02:16 +08:00
RockYang 848a10c3a8 feat: integrated Alipay payment module 2023-11-06 17:55:46 +08:00
RockYang a4baa29678 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-11-06 15:04:11 +08:00
RockYang a9b5280691 opt:update mj api version, wrapper chat api error message as a constant 2023-11-06 15:03:56 +08:00
RockYang 264706210b chore: remove invalid filepath for windows 2023-11-02 19:16:02 +08:00
RockYang 7677ae254f feat: different AI model consuming different amounts of use_calls 2023-10-26 14:38:06 +08:00
RockYang 7ccb4c5f06 docs: add database full backup 2023-10-26 14:00:06 +08:00
RockYang 01205af018 feat: 支持文心4.0模型,不同的用户可以订阅不同的AI模型 2023-10-26 13:41:49 +08:00
RockYang 5b5150e6d4 feat: add system configration item to close rewarding function 2023-10-19 11:19:29 +08:00
RockYang 9d8e3f5049 docs: add alipay reward qrcode 2023-10-17 17:58:03 +08:00
RockYang 99ede83bdc chore: set app list page as the index page 2023-10-16 14:20:44 +08:00
RockYang 61488e840d update changelog file 2023-10-16 13:40:44 +08:00
RockYang a80d01209c opt: adjust styles for ItemList component, cut string for chat role's hello message 2023-10-16 10:46:10 +08:00
RockYang d2a8d655c8 feat: finish adding chat role to user function 2023-10-16 06:56:42 +08:00
RockYang 80e2b34abc opt: adjust ItemList component styles 2023-10-15 15:47:42 +08:00
RockYang fb3d43d2d5 opt: optimize imgae loading for image wall page, loading thumb image replace source image 2023-10-13 23:07:41 +08:00
RockYang ebdec4fa44 feat: create chat app list page, build the layout of page 2023-10-13 18:05:40 +08:00
RockYang ead8dbbaa5 feat: auto login when register successfully 2023-10-13 16:27:40 +08:00
RockYang 95615a5501 docs: update images for readme 2023-10-13 15:55:30 +08:00
RockYang 75487b1f58 Merge branch 'main' into image-wall 2023-10-13 15:39:34 +08:00
RockYang c7daaa00cd feat: image wall stable diffusion image list component is ready 2023-10-13 15:16:40 +08:00
RockYang 606f24e981 feat: optimize the midjourney image list styles 2023-10-13 11:14:39 +08:00
RockYang c83d9287a7 docs: update readme, database files 2023-10-13 09:59:05 +08:00
RockYang fa8092b2cd docs: update readme file 2023-10-13 06:49:21 +08:00
RockYang b2b07a2be6 docs: update change log 2023-10-13 06:39:44 +08:00
RockYang a21628e350 style: fix style for update chat title input element 2023-10-13 06:29:25 +08:00
RockYang 9fcd686fda feat: image wall page is ready 2023-10-12 18:09:50 +08:00
RockYang 1759fd4cf9 opt: close websocket connection when finish a chat call for XunFei API 2023-10-12 10:02:12 +08:00
RockYang 12e7837602 feat: XunFei ai mode api implements is ready 2023-10-11 18:17:03 +08:00
RockYang ff349f2edf feat: adjust package struct, put chat code the seperate 'chatimpl' package, fix bug: baidu api chat context number must be even number 2023-10-11 15:46:40 +08:00
RockYang 088d8ca207 feat: add system configration for enable/disable funciton in chat session 2023-10-11 14:35:47 +08:00
RockYang c37902d3a4 finish baidu ai model api implementation 2023-10-11 14:21:16 +08:00
RockYang da38684cdd add baidu ai model api configrations 2023-10-10 18:19:56 +08:00
RockYang cb2cb72333 docs: update readme 2023-10-09 12:13:21 +08:00
RockYang d53c8f9830 docs: add 3.1.4 release version change logs 2023-10-09 12:05:51 +08:00
RockYang c8136d4231 doc: update database sql file 2023-10-08 20:44:55 +08:00
RockYang e4b8380530 docs: udpate readme and config.toml template 2023-10-08 18:09:46 +08:00
RockYang fbd599194c feat: add system config item for reward image, add app config item to use custom text2img param json file 2023-10-08 17:48:50 +08:00
RockYang 0270ec26fb feat: add configuration handler for AliYun SMS signature and template ID 2023-10-08 12:01:09 +08:00
RockYang 79e342be57 optimze the style for stable diffusion image dialog 2023-10-08 11:39:55 +08:00
RockYang c89f23b018 feat: stable diffusion page function is ready 2023-10-06 22:25:37 +08:00
RockYang f971ec5d34 fix: fixed bug for generating the upload file path 2023-10-05 18:09:42 +08:00
RockYang c5776ce41f feat: stable diffusion page is ready 2023-09-28 18:09:45 +08:00
RockYang 75c5ebbffa feat: migrate the chatgpt-plus-ext project code to this project 2023-09-27 18:14:07 +08:00
RockYang d51a724ade feat: add implements for stable diffusion service 2023-09-26 18:16:51 +08:00
RockYang c1143d7a6d fix: fixed bug for register error with parse args 2023-09-26 09:30:18 +08:00
RockYang f3697431a4 fix: fixed bug for add user in admin console page that calls and img_calls parameter not work 2023-09-22 18:15:32 +08:00
RockYang e3d6e5f420 feat: the upload handler for AliYun OSS is ready 2023-09-22 09:56:45 +08:00
RockYang af6f7a7146 docs: adjust ui styles for mj image page 2023-09-20 14:33:15 +08:00
RockYang c18d95e89a docs: update docker-compose.yaml, change image version to v3.1.3 2023-09-20 11:42:23 +08:00
RockYang fa42d03a65 some page ui optimization, add release v3.1.3 2023-09-20 11:39:11 +08:00
RockYang 22f7de2c09 fix socket connect for mj task notify 2023-09-20 06:59:30 +08:00
RockYang b4b9df81cb opt: add sessionId for mj task 2023-09-19 18:15:08 +08:00
RockYang 2a71c2b0e7 feat: mj advance drawing page function is ready, use better task scheduling argorithm 2023-09-17 18:03:45 +08:00
RockYang c37b3d3df5 feat: add task type params for add new mj task 2023-09-16 14:01:53 +08:00
RockYang ae135b89d6 feat: adjust UI for task list page 2023-09-15 18:16:59 +08:00
RockYang 4112426848 feat: midjourney page task and image list component is ready 2023-09-15 17:40:39 +08:00
RockYang 638b399b31 feat: midjourney page parameter ui is ready 2023-09-14 18:28:24 +08:00
RockYang 8a5713ef7c feat: finish mj model layout 2023-09-14 06:55:03 +08:00
RockYang 382d7bfaa1 feat: add ImageMj page view 2023-09-13 18:04:31 +08:00
RockYang 3daf2f7738 feat: optimize mj notidy api, use job queue to send ai drawing request 2023-09-13 15:50:00 +08:00
RockYang 944d35971d feat: chat chrawing function is refactored 2023-09-13 06:57:25 +08:00
RockYang 9ce8af0b82 feat: refactor MidJourney service for conpatible drawing in chat and draw in app 2023-09-12 18:01:24 +08:00
RockYang d825b9ec26 fix: fixed bug for jwt token expire caculation 2023-09-12 10:49:55 +08:00
RockYang 9afe4aea4b feat: new WebUI for the main page, add MJ and SD drawing function pages 2023-09-11 16:22:11 +08:00
RockYang 3cc8c3284a opt: optimize the styles of chat page; caculate all tokens of context as chat history's token 2023-09-11 13:34:20 +08:00
RockYang e2c18c4e1e chore: update gpt avatar 2023-09-10 12:02:37 +08:00
RockYang 81fe768f6a opt: refactor the web page's router and layout 2023-09-08 22:14:58 +08:00
RockYang 8923779ab0 opt: return error when download mj image failed 2023-09-08 21:02:21 +08:00
RockYang 583218a045 opt: add lock for mj task callback 2023-09-08 18:12:18 +08:00
RockYang 153b2bfa53 opt: add lock for mj task callback 2023-09-08 17:23:32 +08:00
RockYang 0a9b325360 opt: Only create actived upload service 2023-09-08 06:46:24 +08:00
RockYang af9e6f2c46 update desktop package deps 2023-09-07 18:05:13 +08:00
RockYang 0e31726fd3 fix: remove username and nickname field for updating user info 2023-09-07 15:41:01 +08:00
RockYang 590d9c2e61 Update README.md 2023-09-07 15:23:38 +08:00
RockYang c12fe9f25e adjust home page styles 2023-09-07 15:12:43 +08:00
RockYang 9bd83f3acf use absolute path for all routes 2023-09-07 10:27:18 +08:00
RockYang aada15fc41 feat: add linux package configs for desktop 2023-09-07 09:45:11 +08:00
RockYang 6a2d383be6 feat: extract css to a single file, add chat_id path variable support 2023-09-07 09:29:16 +08:00
RockYang 545c8fa482 feat: use eletron to build desktop version for chatgpt-plus 2023-09-06 18:19:27 +08:00
RockYang e3d18643b8 opt: clear space in the reward transfer number 2023-09-06 17:09:55 +08:00
RockYang 2f318bbe9f feat: add QiNiu OSS storage implements 2023-09-06 14:37:13 +08:00
RockYang fac7eb61b3 remove username search for console user list page 2023-09-05 17:52:46 +08:00
RockYang 4450d03a54 update sql file, remove truncate table sql 2023-09-05 17:05:33 +08:00
RockYang e4e7868b30 fix: 采用弹窗的方式显示验证码,解决验证码在低分辨率下被掩盖的Bug 2023-09-05 16:47:40 +08:00
RockYang 3d7fa680cb docs: update change log and create a new release version 2023-09-05 14:28:30 +08:00
RockYang c38035e25e fix: replace session handler with jwt authorization 2023-09-05 11:47:03 +08:00
RockYang 8d4fdaf902 refactor: refactor mobile pages for the chat model updating 2023-09-04 18:15:56 +08:00
RockYang 2820adad53 feat: allow user to set custom api keys for different platforms 2023-09-04 17:34:29 +08:00
RockYang f7a427d2c0 refactor: refactor chat model, replace mode value with mode id. refactored system config module, add seperate configration for every chat model 2023-09-04 16:32:20 +08:00
RockYang 59ed8c9660 refactor: refactor chat_handler, add support for Azure and ChatGLM 2023-09-04 06:43:15 +08:00
RockYang 71562ab0e5 docs: add log dir to docker 2023-08-24 08:55:11 +08:00
RockYang 278ddf037f docs: update docker-compose file 2023-08-23 06:56:04 +08:00
RockYang ac3151af92 chore: update bin file name 2023-08-22 22:18:07 +08:00
RockYang 1189ad7862 feat: change order by for login log and reward records 2023-08-22 22:04:46 +08:00
RockYang 88c13e7b9a opt: optimize element ui dialog styles, make it to the center of screen 2023-08-22 08:57:14 +08:00
RockYang 596baac62e opt: upgrade element-plus to 2.3.0, optimize the styles for for element dialog 2023-08-22 07:14:13 +08:00
RockYang 5a8503ec14 chore: remove debug codes 2023-08-21 10:55:49 +08:00
RockYang e44f95724d doc: update readme docs 2023-08-21 06:57:04 +08:00
RockYang dec0d0dea7 feat: delete the old avatar image file when update the user profile 2023-08-21 06:27:30 +08:00
RockYang 46f96e94ec feat: add oss service factory implements, add support for setting custom upload handler, localstorage and minio oss 2023-08-20 22:29:08 +08:00
RockYang 89b30bcf58 opt: optimize code for remove chat item 2023-08-20 19:06:18 +08:00
RockYang 473d8f5f85 feat: add minio service implementation, download midjourney image to local storage 2023-08-20 16:17:42 +08:00
RockYang 08d8d65599 opt:Optimize the algorithm to check whether Midjourney images can continue to variation 2023-08-17 14:20:16 +08:00
RockYang 2964c1d82e docs: update demo url 2023-08-17 07:18:44 +08:00
RockYang 75ab8552d9 chore: show img_calls num for user profile page 2023-08-17 07:09:53 +08:00
RockYang b78d5b3d03 docs: update docs 2023-08-17 06:55:11 +08:00
RockYang 064081c771 feat: add authorization for MidJourney function calls 2023-08-16 23:16:44 +08:00
RockYang 3525d35d15 chore: update database sql file 2023-08-15 22:53:32 +08:00
RockYang 9d9ee7c585 opt: unset timeout for websocket connection 2023-08-15 18:29:53 +08:00
RockYang e65c32c4c7 feat: midjourney image variation function is ready 2023-08-15 17:31:02 +08:00
RockYang 113769791f feat: midjourney image upscale function is ready 2023-08-15 15:28:40 +08:00
RockYang 5d2a1d21d5 feat: midjourney drawing image function is ready 2023-08-14 17:59:21 +08:00
RockYang 6e40f92aaf feat: add midjourney message receive handler 2023-08-14 07:09:52 +08:00
RockYang 302cb8a5be feat: add midjouney api implements, optimize function calls 2023-08-11 18:46:56 +08:00
RockYang 0b27890484 feat: remove wechat bot, replace with api callback for receive wechat payment transactions 2023-08-10 17:24:23 +08:00
RockYang 5db247e632 feat: add samples and function introduce in welcome page 2023-08-08 14:19:09 +08:00
RockYang c1155a4338 docs: add dashboard image 2023-08-07 20:07:05 +08:00
RockYang da806b9492 feat: enabled user to set default GPT model for chat 2023-08-04 14:25:54 +08:00
RockYang 3d6d46b30d feat: chat export function is ready 2023-08-04 12:08:07 +08:00
RockYang 57c69738ba feat: add chat export button 2023-08-03 18:24:30 +08:00
RockYang 0c8157dbc0 feat: add rewards statistic in dashboard page 2023-08-03 10:19:37 +08:00
RockYang d7b278f2f7 opt: add enabled_msg_service config var to system config database 2023-08-03 10:04:45 +08:00
RockYang 18d74f1057 opt: optimize build script, auto push to aliyun docker hub 2023-08-02 18:09:19 +08:00
RockYang e04856f794 opt: optimize the layout for regiseter page. add function to disable registration 2023-08-02 17:44:13 +08:00
RockYang d7bbfb0fc3 feat: the dashboard page is ready for admin console 2023-08-02 16:37:47 +08:00
RockYang a0857817de refactor: refactor admin console page layout 2023-08-02 15:00:18 +08:00
RockYang 8d39234fd0 fix: fixed bug for chat request's token reach the max token limit for OpenAI 2023-08-01 17:58:03 +08:00
RockYang 0fce1c0fd8 feat: new function for add user in manger console user list page 2023-08-01 16:02:49 +08:00
RockYang c23c285f82 doc: add video tutorial for deploying system 2023-08-01 10:15:43 +08:00
RockYang 81b560d791 docs: remove docs for deploy manually 2023-07-31 20:04:22 +08:00
RockYang 55459a4f42 opt: optimize the build script and docker-compose yaml file 2023-07-31 17:31:02 +08:00
RockYang 9eafd3e6ca feat: add env var to set log level 2023-07-31 08:34:11 +08:00
RockYang e7ac26ff5a feat: update system config cache for AppServer when updating system config in admin console 2023-07-31 08:13:20 +08:00
RockYang fad78641d6 add system config cache for AppServer object 2023-07-31 06:56:28 +08:00
RockYang a32cb0142b feat: allow user to login with username and mobile no 2023-07-27 15:37:50 +08:00
RockYang 4a3c133152 fix: deauthorize some apis 2023-07-27 10:53:14 +08:00
RockYang ae79c34508 fix: fix bug for mobile verify code function not available in register page 2023-07-27 10:29:59 +08:00
RockYang 7eb69c1ad7 style: adjust reward image styles 2023-07-26 17:57:19 +08:00
RockYang 8aaff0af89 chore: optimize build script, compress reward img 2023-07-26 17:48:16 +08:00
RockYang 4b77280c65 feat: add reset password function to user list page 2023-07-26 15:22:11 +08:00
RockYang a0df84e4f2 docs: add change log file 2023-07-25 17:37:20 +08:00
RockYang 8876e6a098 docs: 更新文档和数据库文件 2023-07-25 17:25:23 +08:00
RockYang 9bee2a90f9 feat: 完成人机交互验证 API 接入,增加短信防刷验证 2023-07-25 17:00:24 +08:00
RockYang cd6a811bbd feat: 头条,微博热搜等函数 API 实现 2023-07-25 15:02:43 +08:00
RockYang 84f9a83f55 opt: 取消强制手机号验证,更新配置 2023-07-24 18:18:09 +08:00
RockYang 2efc669ab2 feat: 完成众筹后台管理功能 2023-07-24 15:59:29 +08:00
RockYang 5444ed77ad chore: add error log for reading chat response buffer 2023-07-24 12:05:38 +08:00
RockYang dd71fe80a5 opt: record user_id for reward verify 2023-07-22 08:42:30 +08:00
RockYang b73092eb64 feat: reward verify is ready 2023-07-21 22:29:14 +08:00
RockYang aa28f87b0c feat: 增加打赏核销功能 2023-07-21 18:26:51 +08:00
RockYang 1e1bcd4a30 feat: 集成微信收款服务 2023-07-20 17:46:32 +08:00
RockYang d24b3c46bf put logs in log file 2023-07-20 16:36:12 +08:00
RockYang 37222f07d9 fix: 修复阿里云短信发送失败的 bug 2023-07-16 16:05:11 +08:00
RockYang 49646a79e5 fix: 用户使用自己绑定的 API 调用,则不计算 calls 和 token 消耗 2023-07-16 11:55:45 +08:00
RockYang 9940e1210e Merge branch 'main' into prod 2023-07-16 09:57:27 +08:00
RockYang 10892812c3 chore: 更新项目打包脚本 2023-07-16 09:55:00 +08:00
RockYang d8ff5987dd feat: plugin function is ready 2023-07-15 21:52:30 +08:00
RockYang d014d418e9 feat: 支持上下文深度配置,计算每轮对话消耗的总 token 数量 2023-07-15 18:37:25 +08:00
RockYang cc1b56501d refactor: 更改 OpenAI 请求 Body 数据结构,兼容函数调用请求 2023-07-15 18:00:40 +08:00
RockYang a5ad9648bf chore: small fixs 2023-07-14 18:00:01 +08:00
RockYang 3630099234 feat: 完成每日早报函数开发 2023-07-10 18:59:53 +08:00
RockYang 941a24c75b fix: fixed conflicts 2023-07-10 10:11:17 +08:00
RockYang 50bdc01484 docs: update readme file 2023-07-10 09:44:24 +08:00
RockYang d9e340ddc4 refactor: 调整项目目录结构,移除其他语言 API 目录 2023-07-10 09:42:11 +08:00
RockYang beee1e91d6 test: add plugin test code 2023-07-10 07:05:56 +08:00
RockYang 35935d2bac fixed conflicts 2023-07-06 10:49:38 +08:00
RockYang 5d0b0ad33e opt: add tip message when no available key 2023-07-06 10:47:36 +08:00
RockYang d1a6ac531f opt: add tip message when no available key 2023-07-06 10:34:01 +08:00
RockYang ae255a3bd9 fix: 取消 ElMessage 的 appendTo 属性,防止被 Dialog 组件覆盖 2023-07-04 17:59:55 +08:00
RockYang fa733afa51 opt: 优化验证码发送逻辑,加入防刷验证 2023-07-04 17:15:02 +08:00
RockYang b60c723457 Merge branch 'main' into feat-sms 2023-07-04 10:55:20 +08:00
RockYang 2bb7ff3c13 Merge branch 'main' of github.com:yangjian102621/chatgpt-plus 2023-07-04 10:54:35 +08:00
RockYang 68b4cf00e2 docs: update README, 增加免责申明 2023-07-04 10:52:52 +08:00
RockYang 440169ff60 feat: 短信验证码功能已完成,手机端同步实现。 2023-07-03 15:18:15 +08:00
RockYang 1fc361242e opt: 将短信发送按钮封装成组件 2023-07-03 06:55:15 +08:00
RockYang 72cc6f3d75 feat: 注册短信验证码验证功能已经开启 2023-07-02 20:51:13 +08:00
RockYang a2b1924e00 Merge branch 'main' into prod 2023-07-02 00:04:59 +08:00
RockYang 7e926167f0 fix: 修复 nodejs apple M1 跨平台打包,运行报错 exec format error 2023-07-02 00:04:12 +08:00
RockYang 32484bd32e feat: 增加用户 token 消耗统计功能 2023-07-01 23:29:24 +08:00
RockYang 04ade1008b Merge branch 'main' into prod 2023-07-01 10:24:08 +08:00
RockYang 4603de0e70 Merge pull request #18 from ly307787186/main
Update Setting.vue
2023-07-01 10:22:01 +08:00
RockYang 12c1ff65e9 opt: remove chat role info from user login api's response 2023-06-30 18:52:43 +08:00
ly307787186 7b8cbf7f86 Update Setting.vue
关掉aip-key 必需的验证
2023-06-30 13:20:30 +08:00
RockYang c198e83f70 fix: fixed bug for Emoji can not insert to MySQL 2023-06-30 09:15:57 +08:00
RockYang e60863a290 chore: change ubuntu docker image with aliyun 2023-06-29 15:56:11 +08:00
RockYang 946563d3b2 chore: add test code for fix role icon url of db 2023-06-29 08:58:19 +08:00
RockYang 3f2ef1d54e fix: 修正前端 user_init_call 字段错误和用户注册初始化头像路径问题 2023-06-28 20:01:44 +08:00
RockYang 0d2b60b905 fix: 修复 PC 端聊天界面滚动条问题 2023-06-28 18:16:28 +08:00
RockYang 161b11e428 opt: 优化启动参数接收处理 2023-06-28 05:51:55 +08:00
RockYang f7748d51df opt: 通过环境变量来传参,修正 docker compose 配置参数 2023-06-27 18:29:46 +08:00
RockYang 5d13b4b705 docs: 更新文档,新增移动端预览图 2023-06-27 14:28:00 +08:00
RockYang 92e52a2284 fix: 修复 markdown 换行符不解析的 Bug,修复新发布的模型 token 统计失败错误 2023-06-27 14:18:20 +08:00
RockYang 3efd5fb77a feat: vue-mobile => 完成用户信息修改功能,前后端都添加文件上传功能。 2023-06-27 12:11:55 +08:00
RockYang c3d62bb8d8 feat: vue-mobile => 完成移动端聊天配置功能 2023-06-26 18:18:45 +08:00
RockYang c099e843d5 feat: vue-mobile => 完成会话聊天页面功能,增加主题切换功能 2023-06-26 16:39:00 +08:00
RockYang 5f3a5871f1 feat: vue-mobile => 优化聊天记录拍版样式 2023-06-25 18:21:38 +08:00
RockYang f01fdd0070 feat: vue-mobile => 完善移动端聊天列表页功能 2023-06-25 17:01:04 +08:00
RockYang d20cc367b8 fixed: go-api => 增加全局错误处理 handler,修复业务处理异常导致服务退出的 Bug 2023-06-25 11:34:55 +08:00
RockYang eadb9a733f docs: 增加容器部署文档 2023-06-25 11:06:18 +08:00
RockYang 32ac454b5b opt: 优化前端登录判断逻辑 2023-06-25 09:46:23 +08:00
RockYang 2cadd6af44 feat: chat list page for mobile is ready 2023-06-25 06:53:22 +08:00
RockYang 3c8b5cb313 feat: 完成移动端前段框架搭建 2023-06-24 11:45:26 +08:00
RockYang fcf2387794 style: 调整聊天侧边栏样式 2023-06-23 18:31:50 +08:00
RockYang f9783b4806 opt: 优化 docker-compse 构建脚本,修复后端路由 Bug 2023-06-23 18:04:16 +08:00
RockYang cbdc532f83 chore: 添加 docker 镜像构建脚本 2023-06-23 07:08:16 +08:00
RockYang 124554bae5 opt: 抽离 session 验证函数,修正前端路由覆盖 bug 2023-06-23 06:31:25 +08:00
RockYang 2725536d7d chore: 使用阿里云镜像仓库 2023-06-22 22:23:48 +08:00
RockYang beeef7a4f7 docs: 增加 docker-compose 部署支持 2023-06-22 22:14:18 +08:00
RockYang 7f2ebb6aeb docs: 完善新版本文档 2023-06-22 15:47:51 +08:00
RockYang c8e30383ba refactor: embed xdb file for ip2region 2023-06-22 11:08:44 +08:00
RockYang a7d5a6ccb9 style: 调整后台管理框架样式 2023-06-21 18:44:18 +08:00
RockYang be3380aaf3 refactor: 更新 iconfont 图标,增加打赏二维码弹窗 2023-06-21 15:34:38 +08:00
RockYang 40a4ab5410 refactor: refactor the frame layout of admin module 2023-06-21 14:22:28 +08:00
RockYang 54a2181960 refactor: user login log list for admin is ready 2023-06-21 06:53:41 +08:00
RockYang e490e15bc4 feat: API Key manage is ready 2023-06-20 18:05:33 +08:00
RockYang 469844d97b opt: optimize role sorting 2023-06-20 16:07:26 +08:00
RockYang 892ea29ba8 refactor: chat role manage for admin is ready 2023-06-20 11:46:13 +08:00
RockYang acde1c6742 refactor: user remove is ready for console 2023-06-20 07:08:37 +08:00
RockYang 07da11d852 refactor: 管理后台用户编辑功能 is ready 2023-06-19 21:53:07 +08:00
RockYang 502b8c2270 refactor: 管理后台用户列表页面重构 2023-06-19 18:23:09 +08:00
RockYang af3f7ac810 refactor: 完成管理后台的系统设置页面重构 2023-06-19 15:58:52 +08:00
RockYang 935c6caf96 feat: admin login page is ready 2023-06-19 11:09:23 +08:00
RockYang 90bce1d437 refactor: refactor controller handler module and admin module 2023-06-19 07:06:59 +08:00
RockYang 831dd3e2e0 style: optimize code styles 2023-06-18 15:17:59 +08:00
RockYang b8e208eb32 style: optimize styles for change password page 2023-06-17 00:02:49 +08:00
RockYang 542c908e30 fix: fixed bu for 'slice bounds out of range' 2023-06-16 23:30:04 +08:00
RockYang 08529242bf style: add logo at login and register page 2023-06-16 18:14:43 +08:00
RockYang 1ae9449d92 chore: tracke the logo file 2023-06-16 17:44:38 +08:00
RockYang 7fd0b1fa08 feat: import iconfont styles, change password function is ready 2023-06-16 17:28:21 +08:00
RockYang 111572e3f2 fix: add lock map data structure, fixed bug for 'concurrent map writes' 2023-06-16 15:32:11 +08:00
RockYang c9875d24b4 feat: add footbar for login and register page, add send button for chat page 2023-06-16 13:57:05 +08:00
RockYang 94a75603c3 chore: compress bg images 2023-06-16 11:14:45 +08:00
RockYang d55925bccd chore: delete bin file 2023-06-16 10:01:34 +08:00
RockYang 0b5adcbfab fix: fixed bug for ssl websocket url 2023-06-16 10:00:05 +08:00
RockYang 8d84562f32 chore: update config sample file 2023-06-15 16:03:42 +08:00
RockYang 136d159833 opt: add email and mobile validation for register 2023-06-15 15:09:00 +08:00
RockYang e3026062d8 opt: automatic get the host for api and websocket from the 'location' var 2023-06-15 14:39:05 +08:00
RockYang 8a69c69b7f opt: update the main chat compnent after updating the user profile 2023-06-15 11:29:16 +08:00
RockYang 197714a57a refactor: add system config key 'user_init_calls' to init the new register user's api calls 2023-06-15 10:06:21 +08:00
RockYang 567cdc6f8d refactor: V3 版本重构已基本完成 2023-06-15 09:41:30 +08:00
RockYang be6e8c7713 Merge pull request #6 from EyreFree/master
feat: docker supported
2023-06-05 08:32:34 +08:00
EyreFree f667b35403 docker 2023-06-03 20:48:32 +08:00
RockYang 942e482ca4 chore: 打招呼的时候输出本项目地址 2023-05-09 16:11:50 +08:00
RockYang 10b381965e Update 1.bug.yml 2023-05-09 15:43:48 +08:00
RockYang 79c3a99a38 Update 1.bug.yml 2023-05-09 15:39:47 +08:00
RockYang 463d3c8e97 Update 1.bug.yml 2023-05-09 15:35:07 +08:00
RockYang da007729c6 Update 1.bug.yml 2023-05-09 15:32:57 +08:00
RockYang b7b42b5fd4 chore: 重命名 issue 模版 2023-05-09 15:29:57 +08:00
RockYang 735f9bd053 chore: 添加 issue 和 PR 模板 2023-05-09 15:26:38 +08:00
RockYang 6096b65374 chore: 替换文字描述 用户 => 2023-05-09 14:30:09 +08:00
RockYang 727d342eb3 docs: 优化文档 2023-05-06 18:33:27 +08:00
RockYang 58815ba8bc docs: 优化文档 2023-05-06 18:13:44 +08:00
RockYang 49d0502775 docs: 优化文档排版 2023-05-06 16:36:04 +08:00
RockYang 0b6c6e6d2c docs: 完善文档 2023-05-06 16:27:44 +08:00
RockYang 45ede6047a opt: 优化配置文档加载 2023-05-06 13:58:56 +08:00
RockYang 8f469d4ebb Merge branch 'master' of gitee.com:blackfox/wechat-gpt 2023-05-06 12:07:17 +08:00
RockYang f3264d056d add LICENSE.
Signed-off-by: RockYang <yangjian102621@gmail.com>
2023-05-06 04:03:33 +00:00
183 changed files with 8996 additions and 6614 deletions
+5
View File
@@ -0,0 +1,5 @@
web/src/assets/fonts filter=lfs diff=lfs merge=lfs -text
web/src/assets/iconfont filter=lfs diff=lfs merge=lfs -text
web/src/assets/img filter=lfs diff=lfs merge=lfs -text
web/public filter=lfs diff=lfs merge=lfs -text
api/res filter=lfs diff=lfs merge=lfs -text
+1
View File
@@ -14,3 +14,4 @@ logs
*.njsproj
*.sln
*.sw?
miniprogram
+22
View File
@@ -1,5 +1,27 @@
# 更新日志
## v4.2.8
- Bug 修复:修复管理后台邮件配置报“参数错误”问题
- 功能优化:手机端也不显示后端禁用的菜单功能页面
- 功能优化:**聊天页面支持 Sora2 文生视频/图生视频功能** 🔥🔥🔥
- 功能优化:**重构路由代码,优化路由加载逻辑,支持关闭手机站功能** 🔥🔥🔥
- Bug 修复:修复用户算力扣减日志记录逻辑错误问题
- 功能优化:优化 404 页面样式,兼容 PC 端和移动端
- Bug 修复:ipv6 地址登录报错:Data too long for column 'last_login_ip'
- Bug 修复:移动端登录页面输入密码的时候会覆盖在确认按钮上层
- 功能优化:**移动端的 DALL-E 绘图页面支持上传参考图** 🔥🔥🔥
- 功能优化:优化 WebFooter 组件,如果配置了备案号,则显示备案号,否则不显示
- 功能优化:启动时自动同步 model 与数据表字段,缺列自动新建、多余列自动删除
## v4.2.7
- Bug 修复:修复超级管理员无法修改密码的 Bug
- Bug 修复:微信登录配置更新后,没有同步更新到系统配置
- 功能优化: 给 AI 对话 API 加上线程锁,确保同一个用户同时只有一个对话请求
- 功能新增:支持即梦 AI 4.0 图片编辑,即梦 AI 数字人,动作迁移功能。🔥🔥🔥
- 功能新增:新增 AI 对话编辑功能,并优化了重新生成逻辑
## v4.2.6
- 功能重构:优化系统配置管理功能,把 OSS,支付,短信,邮件等配置全部迁移到管理后台,无需通过修改配置文档的方式修改 🎉🎉🎉
+1
View File
@@ -16,6 +16,7 @@ tmp
bin
data
config.toml
config.toml.*.bak
static/upload
static/audio
storage.json
+1 -1
View File
@@ -1,6 +1,6 @@
Listen = "0.0.0.0:5678"
ProxyURL = "" # 如 http://127.0.0.1:7777
MysqlDns = "root:12345678@tcp(172.22.11.200:3307)/chatgpt_plus?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local"
MysqlDns = "root:12345678@tcp(localhost:3306)/geekai?charset=utf8mb4&collation=utf8mb4_unicode_ci&parseTime=True&loc=Local"
StaticDir = "./static" # 静态资源的目录
StaticUrl = "/static" # 静态资源访问 URL
TikaHost = "http://tika:9998"
+1 -1
View File
@@ -44,11 +44,11 @@ func NewServer(appConfig *types.AppConfig, redis *redis.Client, sysConfig *types
}
func (s *AppServer) Init(client *redis.Client) {
s.Engine.Use(middleware.ThumbMiddleware())
s.Engine.Use(middleware.ParameterHandlerMiddleware())
s.Engine.Use(errorHandler)
// 添加静态资源访问
s.Engine.Static("/static", s.Config.StaticDir)
s.Engine.Use(middleware.StaticMiddleware())
}
func (s *AppServer) Run(db *gorm.DB) error {
-10
View File
@@ -85,15 +85,6 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig {
logger.Error("load system config error: ", err)
}
// 加载许可证配置
var license types.License
sysConfig.Id = 0
db.Where("name", types.ConfigKeyLicense).First(&sysConfig)
err = utils.JsonDecode(sysConfig.Value, &license)
if err != nil {
logger.Error("load license config error: ", err)
}
// 加载验证码配置
var captchaConfig types.CaptchaConfig
sysConfig.Id = 0
@@ -168,7 +159,6 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig {
return &types.SystemConfig{
Base: baseConfig,
License: license,
SMS: smsConfig,
OSS: ossConfig,
SMTP: smtpConfig,
+3
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"geekai/core/types"
logger2 "geekai/logger"
"geekai/utils"
"geekai/utils/resp"
"time"
@@ -13,6 +14,8 @@ import (
"github.com/golang-jwt/jwt"
)
var logger = logger2.GetLogger()
// 前端用户授权验证
func UserAuthMiddleware(secretKey string, redis *redis.Client) gin.HandlerFunc {
return func(c *gin.Context) {
@@ -14,8 +14,8 @@ import (
"golang.org/x/image/webp"
)
// 静态资源中间件
func StaticMiddleware() gin.HandlerFunc {
// 缩略图中间件
func ThumbMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
url := c.Request.URL.String()
+3 -18
View File
@@ -37,22 +37,6 @@ type RedisConfig struct {
DB int
}
// LicenseKey 存储许可证书的 KEY
const LicenseKey = "Geek-AI-License"
type License struct {
Key string `json:"key"` // 许可证书密钥
MachineId string `json:"machine_id"` // 机器码
ExpiredAt int64 `json:"expired_at"` // 过期时间
IsActive bool `json:"is_active"` // 是否激活
Configs LicenseConfig `json:"configs"`
}
type LicenseConfig struct {
UserNum int `json:"user_num"` // 用户数量
DeCopy bool `json:"de_copy"` // 去版权
}
func (c RedisConfig) Url() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
@@ -89,6 +73,7 @@ type BaseConfig struct {
MjMode string `json:"mj_mode"` // midjourney 默认的API模式,relax, fast, turbo
IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单
IndexPage string `json:"index_page"` // 首页显示的页面
Copyright string `json:"copyright"` // 版权信息
ICP string `json:"icp"` // ICP 备案号
GaBeian string `json:"ga_beian"` // 公安备案号
@@ -96,6 +81,8 @@ type BaseConfig struct {
EmailWhiteList []string `json:"email_white_list"` // 邮箱白名单列表
AssistantModelId int `json:"assistant_model_id"` // 用来做提示词,翻译的AI模型 id
MaxFileSize int `json:"max_file_size"` // 最大文件大小,单位:MB
EnableMobileSite bool `json:"enable_mobile_site"` // 是否开启手机站点
}
type SystemConfig struct {
@@ -107,7 +94,6 @@ type SystemConfig struct {
Captcha CaptchaConfig
WxLogin WxLoginConfig
Jimeng JimengConfig
License License
Moderation ModerationConfig
}
@@ -120,7 +106,6 @@ const (
ConfigKeyMarkMap = "mark_map"
ConfigKeyCaptcha = "captcha"
ConfigKeyWxLogin = "wx_login"
ConfigKeyLicense = "license"
ConfigKeySms = "sms"
ConfigKeySmtp = "smtp"
ConfigKeyOss = "oss"
+6 -6
View File
@@ -20,14 +20,14 @@ func init() {
// CaptchaConfig 行为验证码配置
type CaptchaConfig struct {
ApiKey string `json:"api_key"`
Type string `json:"type"` // 验证码类型, 可选值: "dot" 或 "slide"
Enabled bool `json:"enabled"`
ApiKey string `json:"api_key,omitempty"`
Type string `json:"type,omitempty"` // 验证码类型, 可选值: "dot" 或 "slide"
Enabled bool `json:"enabled,omitempty"`
}
// WxLoginConfig 微信登录配置
type WxLoginConfig struct {
ApiKey string `json:"api_key"`
NotifyURL string `json:"notify_url"` // 登录成功回调 URL
Enabled bool `json:"enabled"` // 是否启用微信登录
ApiKey string `json:"api_key,omitempty"`
NotifyURL string `json:"notify_url,omitempty"` // 登录成功回调 URL
Enabled bool `json:"enabled,omitempty"` // 是否启用微信登录
}
+59 -11
View File
@@ -2,17 +2,65 @@ package types
// JimengConfig 即梦AI配置
type JimengConfig struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
Power JimengPower `json:"power"`
// 即梦AI的AccessKey和SecretKey
AccessKey string `json:"access_key,omitempty"`
SecretKey string `json:"secret_key,omitempty"`
// 火山引擎大模型专用的验证方式
ApiKey string `json:"api_key,omitempty"`
// 算力配置
Powers map[string]int `json:"powers,omitempty"`
}
// JimengPower 即梦AI算力配置
type JimengPower struct {
TextToImage int `json:"text_to_image"`
ImageToImage int `json:"image_to_image"`
ImageEdit int `json:"image_edit"`
ImageEffects int `json:"image_effects"`
TextToVideo int `json:"text_to_video"`
ImageToVideo int `json:"image_to_video"`
// JMTaskStatus 任务状态
type JMTaskStatus string
const (
JMTaskStatusSubmited = JMTaskStatus("submited") // 任务已提交
JMTaskStatusInQueue = JMTaskStatus("in_queue") // 任务排队中
JMTaskStatusGenerating = JMTaskStatus("generating") // 任务处理中
JMTaskStatusDone = JMTaskStatus("done") // 处理完成
JMTaskStatusNotFound = JMTaskStatus("not_found") // 任务未找到
JMTaskStatusSuccess = JMTaskStatus("success") // 任务成功
JMTaskStatusFailed = JMTaskStatus("failed") // 任务失败
JMTaskStatusExpired = JMTaskStatus("expired") // 任务过期
)
// JMTaskType 任务类型
type JMTaskType string
const (
JMTaskTypeImage = JMTaskType("image") // 文生图
JMTaskTypeVideo = JMTaskType("video") // 图生图
JMTaskTypeVirtualHuman = JMTaskType("virtual_human") // 图像编辑
JMTaskTypeActionTransfer = JMTaskType("action_transfer") // 图像特效
)
// JimengTaskRequest 即梦AI任务请求
type JimengTaskRequest struct {
TaskType JMTaskType `json:"type"` // 任务类型
ReqKey string `json:"req_key"` // 请求Key
Action string `json:"action"` // 请求Action
Power int `json:"power"` // 消耗算力
// 公共参数
Prompt string `json:"prompt,omitempty"`
ImageUrls []string `json:"image_urls,omitempty"`
// 图片生成参数
Size string `json:"size,omitempty"`
UsePreLLM bool `json:"use_pre_llm,omitempty"`
Scale float64 `json:"scale,omitempty"`
ForceSingle bool `json:"force_single,omitempty"`
// 视频生成参数
Duration int `json:"duration,omitempty"` // 视频时长,单位:秒
TemplateId string `json:"template_id,omitempty"` // 运镜模板ID
AspectRatio string `json:"aspect_ratio,omitempty"`
CameraStrength string `json:"camera_strength,omitempty"` // 运镜强度
// 数字人视频生成参数
AudioURL string `json:"audio_url,omitempty"` // 音频URL
RecognizeKey string `json:"recognize_key,omitempty"` // 识别主体请求Key
// 视频动作迁移参数
VideoURL string `json:"video_url,omitempty"` // 动作视频URL
}
+16 -16
View File
@@ -9,13 +9,13 @@ package types
// 文本审查
type ModerationConfig struct {
Enable bool `json:"enable"` // 是否启用文本审查
Active string `json:"active"`
EnableGuide bool `json:"enable_guide"` // 是否启用模型引导提示词
GuidePrompt string `json:"guide_prompt"` // 模型引导提示词
Gitee ModerationGiteeConfig `json:"gitee"`
Baidu ModerationBaiduConfig `json:"baidu"`
Tencent ModerationTencentConfig `json:"tencent"`
Enable bool `json:"enable,omitempty"` // 是否启用文本审查
Active string `json:"active,omitempty"`
EnableGuide bool `json:"enable_guide,omitempty"` // 是否启用模型引导提示词
GuidePrompt string `json:"guide_prompt,omitempty"` // 模型引导提示词
Gitee ModerationGiteeConfig `json:"gitee,omitempty"`
Baidu ModerationBaiduConfig `json:"baidu,omitempty"`
Tencent ModerationTencentConfig `json:"tencent,omitempty"`
}
const (
@@ -26,26 +26,26 @@ const (
// GiteeAI 文本审查配置
type ModerationGiteeConfig struct {
ApiKey string `json:"api_key"`
Model string `json:"model"` // 文本审核模型
ApiKey string `json:"api_key,omitempty"`
Model string `json:"model,omitempty"` // 文本审核模型
}
// 百度文本审查配置
type ModerationBaiduConfig struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
AccessKey string `json:"access_key,omitempty"`
SecretKey string `json:"secret_key,omitempty"`
}
// 腾讯云文本审查配置
type ModerationTencentConfig struct {
AccessKey string `json:"access_key"`
SecretKey string `json:"secret_key"`
AccessKey string `json:"access_key,omitempty"`
SecretKey string `json:"secret_key,omitempty"`
}
type ModerationResult struct {
Flagged bool `json:"flagged"`
Categories map[string]bool `json:"categories"`
CategoryScores map[string]float64 `json:"category_scores"`
Flagged bool `json:"flagged,omitempty"`
Categories map[string]bool `json:"categories,omitempty"`
CategoryScores map[string]float64 `json:"category_scores,omitempty"`
}
var ModerationCategories = map[string]string{
+23 -23
View File
@@ -8,39 +8,39 @@ package types
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
type OSSConfig struct {
Active string `json:"active"`
Local LocalStorageConfig `json:"local"`
Minio MiniOssConfig `json:"minio"`
QiNiu QiNiuOssConfig `json:"qiniu"`
AliYun AliYunOssConfig `json:"aliyun"`
Active string `json:"active,omitempty"`
Local LocalStorageConfig `json:"local,omitempty"`
Minio MiniOssConfig `json:"minio,omitempty"`
QiNiu QiNiuOssConfig `json:"qiniu,omitempty"`
AliYun AliYunOssConfig `json:"aliyun,omitempty"`
}
type MiniOssConfig struct {
Endpoint string `json:"endpoint"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
UseSSL bool `json:"use_ssl"`
Domain string `json:"domain"`
Endpoint string `json:"endpoint,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Bucket string `json:"bucket,omitempty"`
UseSSL bool `json:"use_ssl,omitempty"`
Domain string `json:"domain,omitempty"`
}
type QiNiuOssConfig struct {
Zone string `json:"zone"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
Domain string `json:"domain"`
Zone string `json:"zone,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Bucket string `json:"bucket,omitempty"`
Domain string `json:"domain,omitempty"`
}
type AliYunOssConfig struct {
Endpoint string `json:"endpoint"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
Domain string `json:"domain"`
Endpoint string `json:"endpoint,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Bucket string `json:"bucket,omitempty"`
Domain string `json:"domain,omitempty"`
}
type LocalStorageConfig struct {
BasePath string `json:"base_path"`
BaseURL string `json:"base_url"`
BasePath string `json:"base_path,omitempty"`
BaseURL string `json:"base_url,omitempty"`
}
+21 -21
View File
@@ -1,19 +1,19 @@
package types
type PaymentConfig struct {
Alipay AlipayConfig `json:"alipay"` // 支付宝支付渠道配置
Epay EpayConfig `json:"epay"` // 易支付配置
WxPay WxPayConfig `json:"wxpay"` // 微信支付渠道配置
Alipay AlipayConfig `json:"alipay,omitempty"` // 支付宝支付渠道配置
Epay EpayConfig `json:"epay,omitempty"` // 易支付配置
WxPay WxPayConfig `json:"wxpay,omitempty"` // 微信支付渠道配置
}
// AlipayConfig 支付宝支付配置
type AlipayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
SandBox bool `json:"sandbox"` // 是否沙盒环境
AppId string `json:"app_id"` // 应用 ID
PrivateKey string `json:"private_key"` // 应用私钥
AlipayPublicKey string `json:"alipay_public_key"` // 支付宝公钥
Domain string `json:"domain"` // 支付回调域名
Enabled bool `json:"enabled,omitempty"` // 是否启用该支付通道
SandBox bool `json:"sandbox,omitempty"` // 是否沙盒环境
AppId string `json:"app_id,omitempty"` // 应用 ID
PrivateKey string `json:"private_key,omitempty"` // 应用私钥
AlipayPublicKey string `json:"alipay_public_key,omitempty"` // 支付宝公钥
Domain string `json:"domain,omitempty"` // 支付回调域名
}
func (c *AlipayConfig) Equal(other *AlipayConfig) bool {
@@ -25,13 +25,13 @@ func (c *AlipayConfig) Equal(other *AlipayConfig) bool {
// WxPayConfig 微信支付配置
type WxPayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
AppId string `json:"app_id"` // 公众号的APPID,如:wxd678efh567hg6787
MchId string `json:"mch_id"` // 直连商户的商户号,由微信支付生成并下发
SerialNo string `json:"serial_no"` // 商户证书的证书序列号
PrivateKey string `json:"private_key"` // 商户证书私钥
ApiV3Key string `json:"api_v3_key"` // API V3 秘钥
Domain string `json:"domain"` // 支付回调域名
Enabled bool `json:"enabled,omitempty"` // 是否启用该支付通道
AppId string `json:"app_id,omitempty"` // 公众号的APPID,如:wxd678efh567hg6787
MchId string `json:"mch_id,omitempty"` // 直连商户的商户号,由微信支付生成并下发
SerialNo string `json:"serial_no,omitempty"` // 商户证书的证书序列号
PrivateKey string `json:"private_key,omitempty"` // 商户证书私钥
ApiV3Key string `json:"api_v3_key,omitempty"` // API V3 秘钥
Domain string `json:"domain,omitempty"` // 支付回调域名
}
func (c *WxPayConfig) Equal(other *WxPayConfig) bool {
@@ -45,11 +45,11 @@ func (c *WxPayConfig) Equal(other *WxPayConfig) bool {
// EpayConfig 易支付配置
type EpayConfig struct {
Enabled bool `json:"enabled"` // 是否启用该支付通道
AppId string `json:"app_id"` // 商户 ID
PrivateKey string `json:"private_key"` // 私钥
ApiURL string `json:"api_url"` // z支付 API 网关
Domain string `json:"domain"` // 支付回调域名
Enabled bool `json:"enabled,omitempty"` // 是否启用该支付通道
AppId string `json:"app_id,omitempty"` // 商户 ID
PrivateKey string `json:"private_key,omitempty"` // 私钥
ApiURL string `json:"api_url,omitempty"` // z支付 API 网关
Domain string `json:"domain,omitempty"` // 支付回调域名
}
func (c *EpayConfig) Equal(other *EpayConfig) bool {
+11 -11
View File
@@ -8,23 +8,23 @@ package types
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
type SMSConfig struct {
Active string `json:"active"`
Ali SmsConfigAli `json:"aliyun"`
Bao SmsConfigBao `json:"bao"`
Active string `json:"active,omitempty"`
Ali SmsConfigAli `json:"aliyun,omitempty"`
Bao SmsConfigBao `json:"bao,omitempty"`
}
// SmsConfigAli 阿里云短信平台配置
type SmsConfigAli struct {
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Sign string `json:"sign"` // 短信签名
CodeTempId string `json:"code_temp_id"` // 验证码短信模板 ID
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Sign string `json:"sign,omitempty"` // 短信签名
CodeTempId string `json:"code_temp_id,omitempty"` // 验证码短信模板 ID
}
// SmsConfigBao 短信宝平台配置
type SmsConfigBao struct {
Username string `json:"username"` //短信宝平台注册的用户名
Password string `json:"password"` //短信宝平台注册的密码
Sign string `json:"sign"` // 短信签名
CodeTemplate string `json:"code_template"` // 验证码短信模板 匹配
Username string `json:"username,omitempty"` //短信宝平台注册的用户名
Password string `json:"password,omitempty"` //短信宝平台注册的密码
Sign string `json:"sign,omitempty"` // 短信签名
CodeTemplate string `json:"code_template,omitempty"` // 验证码短信模板 匹配
}
+6 -6
View File
@@ -8,12 +8,12 @@ package types
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
type SmtpConfig struct {
UseTls bool `json:"use_tls"` // 是否使用 TLS 发送
Host string `json:"host"` // 邮件服务器地址
Port int `json:"port"` // 邮件服务器端口
AppName string `json:"app_name"` // 应用名称
From string `json:"from"` // 发件人邮箱地址
Password string `json:"password"` // 发件人邮箱密码
UseTls bool `json:"use_tls,omitempty"` // 是否使用 TLS 发送
Host string `json:"host,omitempty"` // 邮件服务器地址
Port int `json:"port,omitempty"` // 邮件服务器端口
AppName string `json:"app_name,omitempty"` // 应用名称
From string `json:"from,omitempty"` // 发件人邮箱地址
Password string `json:"password,omitempty"` // 发件人邮箱密码
}
func (s *SmtpConfig) Equal(other *SmtpConfig) bool {
+2 -3
View File
@@ -72,14 +72,13 @@ type SdTaskParams struct {
type DallTask struct {
ModelId uint `json:"model_id"`
ModelName string `json:"model_name"`
ModelValue string `json:"model_value"`
Image []string `json:"image,omitempty"`
Id uint `json:"id"`
UserId uint `json:"user_id"`
Prompt string `json:"prompt"`
N int `json:"n"`
Quality string `json:"quality"`
AspectRatio string `json:"aspect_ratio"`
Size string `json:"size"`
Style string `json:"style"`
Power int `json:"power"`
TranslateModelId int `json:"translate_model_id"` // 提示词翻译模型ID
}
+45
View File
@@ -0,0 +1,45 @@
package types
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// * 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 "sync"
// UserLockManager 提供基于用户ID的TryLock功能,确保同一用户并发请求串行化
type UserLockManager struct {
mu sync.Mutex
locks map[uint]bool
}
func NewUserLockManager() *UserLockManager {
return &UserLockManager{mu: sync.Mutex{}, locks: make(map[uint]bool)}
}
// TryLock 尝试为指定用户加锁。若已被占用返回 false
func (m *UserLockManager) TryLock(userId uint) bool {
if userId == 0 {
return true
}
m.mu.Lock()
defer m.mu.Unlock()
if m.locks[userId] {
return false
}
m.locks[userId] = true
return true
}
// Unlock 释放指定用户的锁
func (m *UserLockManager) Unlock(userId uint) {
if userId == 0 {
return
}
m.mu.Lock()
delete(m.locks, userId)
m.mu.Unlock()
}
+2
View File
@@ -33,6 +33,7 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/shopspring/decimal v1.3.1
github.com/syndtr/goleveldb v1.0.0
github.com/volcengine/volcengine-go-sdk v1.1.34
golang.org/x/image v0.15.0
)
@@ -50,6 +51,7 @@ require (
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.4.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
require (
+6
View File
@@ -100,6 +100,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -110,6 +111,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-tika v0.3.1 h1:l+jr10hDhZjcgxFRfcQChRLo1bPXQeLFluMyvDhXTTA=
@@ -259,6 +261,8 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/volcengine/volcengine-go-sdk v1.1.34 h1:ha90JycCCTJNCse0UDziBgBsuX98ITOrkwYlDWcm7NI=
github.com/volcengine/volcengine-go-sdk v1.1.34/go.mod h1:oxoVo+A17kvkwPkIeIHPVLjSw7EQAm+l/Vau1YGHN+A=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
@@ -390,6 +394,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+20 -14
View File
@@ -31,7 +31,7 @@ import (
var logger = logger2.GetLogger()
const SuperManagerID = 1
const SuperUsername = "admin"
type ManagerHandler struct {
handler.BaseHandler
@@ -94,7 +94,7 @@ func (h *ManagerHandler) Login(c *gin.Context) {
}
// 超级管理员默认是ID:1
if manager.Id != SuperManagerID && manager.Status == false {
if manager.Username != SuperUsername && !manager.Status {
resp.ERROR(c, "该用户已被禁止登录,请联系超级管理员")
return
}
@@ -125,7 +125,7 @@ func (h *ManagerHandler) Login(c *gin.Context) {
IsSuperAdmin bool `json:"is_super_admin"`
Token string `json:"token"`
}{
IsSuperAdmin: manager.Id == 1,
IsSuperAdmin: manager.Username == SuperUsername,
Token: tokenString,
}
@@ -227,12 +227,19 @@ func (h *ManagerHandler) Remove(c *gin.Context) {
return
}
if id == SuperManagerID {
var user model.AdminUser
res := h.DB.Where("id", id).First(&user)
if res.Error != nil {
resp.ERROR(c, res.Error.Error())
return
}
if user.Username == SuperUsername {
resp.ERROR(c, "超级管理员不能删除")
return
}
res := h.DB.Where("id", id).Delete(&model.AdminUser{})
res = h.DB.Where("id", id).Delete(&model.AdminUser{})
if res.Error != nil {
resp.ERROR(c, res.Error.Error())
return
@@ -263,8 +270,14 @@ func (h *ManagerHandler) Enable(c *gin.Context) {
// ResetPass 重置密码
func (h *ManagerHandler) ResetPass(c *gin.Context) {
id := h.GetLoginUserId(c)
if id != SuperManagerID {
id := h.GetAdminId(c)
var user model.AdminUser
res := h.DB.Where("id", id).First(&user)
if res.Error != nil {
resp.ERROR(c, res.Error.Error())
return
}
if user.Username != SuperUsername {
resp.ERROR(c, "只有超级管理员能够进行该操作")
return
}
@@ -278,13 +291,6 @@ func (h *ManagerHandler) ResetPass(c *gin.Context) {
return
}
var user model.AdminUser
res := h.DB.Where("id", data.Id).First(&user)
if res.Error != nil {
resp.ERROR(c, res.Error.Error())
return
}
password := utils.GenPassword(data.Password, user.Salt)
user.Password = password
res = h.DB.Updates(&user)
+1 -55
View File
@@ -26,7 +26,6 @@ import (
type ConfigHandler struct {
handler.BaseHandler
licenseService *service.LicenseService
sysConfig *types.SystemConfig
alipayService *payment.AlipayService
wxpayService *payment.WxPayService
@@ -41,7 +40,6 @@ type ConfigHandler struct {
func NewConfigHandler(
app *core.AppServer,
db *gorm.DB,
licenseService *service.LicenseService,
sysConfig *types.SystemConfig,
alipayService *payment.AlipayService,
wxpayService *payment.WxPayService,
@@ -54,7 +52,6 @@ func NewConfigHandler(
) *ConfigHandler {
return &ConfigHandler{
BaseHandler: handler.BaseHandler{App: app, DB: db},
licenseService: licenseService,
sysConfig: sysConfig,
alipayService: alipayService,
wxpayService: wxpayService,
@@ -87,8 +84,6 @@ func (h *ConfigHandler) RegisterRoutes() {
rg.POST("update/oss", h.UpdateOss)
rg.POST("update/smtp", h.UpdateStmp)
rg.GET("get", h.Get)
rg.POST("license/active", h.Active)
rg.GET("license/get", h.GetLicense)
}
}
@@ -101,19 +96,6 @@ func (h *ConfigHandler) UpdateBase(c *gin.Context) {
return
}
// 未授权的话不允许修改版权
license := h.licenseService.GetLicense()
if !license.IsActive && data.Copyright != h.sysConfig.Base.Copyright {
resp.ERROR(c, "未授权系统不允许修改版权信息")
return
}
// 未授权的话不允许修改 Logo
if !license.IsActive && data.Logo != h.sysConfig.Base.Logo {
resp.ERROR(c, "未授权系统不允许修改 Logo")
return
}
err := h.Update(types.ConfigKeySystem, data)
if err != nil {
resp.ERROR(c, err.Error())
@@ -368,10 +350,7 @@ func (h *ConfigHandler) UpdateWxLogin(c *gin.Context) {
return
}
if data.Enabled {
h.wxLoginService.UpdateConfig(data)
}
h.wxLoginService.UpdateConfig(data)
h.sysConfig.WxLogin = data
resp.SUCCESS(c, data)
}
@@ -410,36 +389,3 @@ func (h *ConfigHandler) Get(c *gin.Context) {
resp.SUCCESS(c, value)
}
// Active 激活系统
func (h *ConfigHandler) Active(c *gin.Context) {
var data struct {
License string `json:"license"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
err := h.licenseService.ActiveLicense(data.License)
license := h.licenseService.GetLicense()
if err != nil {
resp.ERROR(c, err.Error())
return
}
if err := h.Update(types.ConfigKeyLicense, license); err != nil {
resp.ERROR(c, err.Error())
return
}
// 更新系统配置
h.sysConfig.License = *license
resp.SUCCESS(c, license.MachineId)
}
// GetLicense 获取 License 信息
func (h *ConfigHandler) GetLicense(c *gin.Context) {
license := h.licenseService.GetLicense()
resp.SUCCESS(c, license)
}
+13 -27
View File
@@ -131,7 +131,7 @@ func (h *AdminJimengHandler) BatchRemove(c *gin.Context) {
continue // 跳过不存在的
}
tx := h.DB.Begin()
if job.Status != model.JMTaskStatusSuccess && job.Power > 0 {
if job.Status != types.JMTaskStatusSuccess && job.Power > 0 {
remark := fmt.Sprintf("任务未成功,退回算力。任务ID%dErr: %s", job.Id, job.ErrMsg)
err = h.userService.IncreasePower(job.UserId, job.Power, model.PowerLog{
Type: types.PowerRefund,
@@ -172,7 +172,7 @@ func (h *AdminJimengHandler) BatchRemove(c *gin.Context) {
// Stats 获取统计信息
func (h *AdminJimengHandler) Stats(c *gin.Context) {
type StatResult struct {
Status model.JMTaskStatus `json:"status"`
Status types.JMTaskStatus `json:"status"`
Count int64 `json:"count"`
}
@@ -198,13 +198,13 @@ func (h *AdminJimengHandler) Stats(c *gin.Context) {
for _, stat := range stats {
result["totalTasks"] = result["totalTasks"].(int64) + stat.Count
switch stat.Status {
case model.JMTaskStatusInQueue:
case types.JMTaskStatusInQueue:
result["pendingTasks"] = stat.Count
case model.JMTaskStatusSuccess:
case types.JMTaskStatusSuccess:
result["completedTasks"] = stat.Count
case model.JMTaskStatusGenerating:
case types.JMTaskStatusGenerating:
result["processingTasks"] = stat.Count
case model.JMTaskStatusFailed:
case types.JMTaskStatusFailed:
result["failedTasks"] = stat.Count
}
}
@@ -231,29 +231,15 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) {
}
// 验证算力配置
if req.Power.TextToImage <= 0 {
resp.ERROR(c, "文生图算力必须大于0")
if len(req.Powers) == 0 {
resp.ERROR(c, "请至少配置一个模型的积分")
return
}
if req.Power.ImageToImage <= 0 {
resp.ERROR(c, "图生图算力必须大于0")
return
}
if req.Power.ImageEdit <= 0 {
resp.ERROR(c, "图片编辑算力必须大于0")
return
}
if req.Power.ImageEffects <= 0 {
resp.ERROR(c, "图片特效算力必须大于0")
return
}
if req.Power.TextToVideo <= 0 {
resp.ERROR(c, "文生视频算力必须大于0")
return
}
if req.Power.ImageToVideo <= 0 {
resp.ERROR(c, "图生视频算力必须大于0")
return
for key, val := range req.Powers {
if val <= 0 {
resp.ERROR(c, fmt.Sprintf("模型 %s 的积分必须大于0", key))
return
}
}
// 保存配置
+4 -17
View File
@@ -13,7 +13,6 @@ import (
"geekai/core/middleware"
"geekai/core/types"
"geekai/handler"
"geekai/service"
"geekai/store/model"
"geekai/store/vo"
"geekai/utils"
@@ -28,12 +27,11 @@ import (
type UserHandler struct {
handler.BaseHandler
licenseService *service.LicenseService
redis *redis.Client
redis *redis.Client
}
func NewUserHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService, redisCli *redis.Client) *UserHandler {
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, licenseService: licenseService, redis: redisCli}
func NewUserHandler(app *core.AppServer, db *gorm.DB, redisCli *redis.Client) *UserHandler {
return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, redis: redisCli}
}
// RegisterRoutes 注册路由
@@ -114,13 +112,6 @@ func (h *UserHandler) Save(c *gin.Context) {
resp.ERROR(c, types.InvalidArgs)
return
}
// 检测最大注册人数
var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return
}
var user = model.User{}
var res *gorm.DB
var userVo vo.User
@@ -198,11 +189,7 @@ 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))
}
u.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6))
res = h.DB.Create(&u)
_ = utils.CopyObject(u, &userVo)
userVo.Id = u.Id
+67 -27
View File
@@ -19,6 +19,7 @@ import (
"geekai/service"
"geekai/service/moderation"
"geekai/service/oss"
"geekai/service/sora"
"geekai/store/model"
"geekai/store/vo"
"geekai/utils"
@@ -65,34 +66,39 @@ type ChatHandler struct {
BaseHandler
redis *redis.Client
uploadManager *oss.UploaderManager
licenseService *service.LicenseService
ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
userService *service.UserService
moderationManager *moderation.ServiceManager
userLocks *types.UserLockManager
soraService *sora.SoraService
}
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService, userService *service.UserService, moderationManager *moderation.ServiceManager) *ChatHandler {
func NewChatHandler(app *core.AppServer,
db *gorm.DB,
redis *redis.Client,
manager *oss.UploaderManager,
userService *service.UserService,
soraService *sora.SoraService,
moderationManager *moderation.ServiceManager) *ChatHandler {
return &ChatHandler{
BaseHandler: BaseHandler{App: app, DB: db},
redis: redis,
uploadManager: manager,
licenseService: licenseService,
ReqCancelFunc: types.NewLMap[string, context.CancelFunc](),
userService: userService,
moderationManager: moderationManager,
userLocks: types.NewUserLockManager(),
soraService: soraService,
}
}
// RegisterRoutes 注册路由
func (h *ChatHandler) RegisterRoutes() {
group := h.App.Engine.Group("/api/chat/")
// 聊天接口不需要授权(已在authConfig中配置)
group.Any("message", h.Chat)
// 其他接口需要用户授权
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
{
group.Any("message", h.Chat)
group.GET("list", h.List)
group.GET("detail", h.Detail)
group.POST("update", h.Update)
@@ -120,6 +126,14 @@ func (h *ChatHandler) Chat(c *gin.Context) {
return
}
// 用户级并发锁,确保同一用户同时只有一个对话请求
if !h.userLocks.TryLock(input.UserId) {
pushMessage(c, ChatEventError, "您有一个对话请求正在进行中,请稍后再试或先停止当前生成!")
c.Abort()
return
}
defer h.userLocks.Unlock(input.UserId)
ctx, cancel := context.WithCancel(c.Request.Context())
defer cancel()
@@ -262,9 +276,9 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C
if h.App.SysConfig.Base.ContextDeep > 0 {
var historyMessages []model.ChatMessage
dbSession := h.DB.Session(&gorm.Session{}).Where("chat_id", input.ChatId)
if input.LastMsgId > 0 { // 重新生成逻辑
if input.LastMsgId > 0 { // 重新生成和编辑逻辑
var lastMessage model.ChatMessage
err = dbSession.Where("id <= ?", input.LastMsgId).Where("type", types.PromptMsg).First(&lastMessage).Error
err = dbSession.Where("id < ?", input.LastMsgId).Where("type", types.ReplyMsg).Order("id DESC").First(&lastMessage).Error
if err != nil {
input.LastMsgId = 0
} else {
@@ -272,7 +286,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C
}
dbSession = dbSession.Where("id < ?", input.LastMsgId)
// 删除对应的聊天记录
h.DB.Debug().Where("chat_id", input.ChatId).Where("id >= ?", input.LastMsgId).Delete(&model.ChatMessage{})
h.DB.Debug().Where("chat_id", input.ChatId).Where("id > ?", input.LastMsgId).Delete(&model.ChatMessage{})
}
err = dbSession.Limit(h.App.SysConfig.Base.ContextDeep).Order("id DESC").Find(&historyMessages).Error
if err == nil {
@@ -330,14 +344,14 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C
for _, file := range input.Files {
logger.Debugf("detected file: %+v", file.URL)
// 处理图片
if isImageURL(file.URL) {
if isImageURL(file.URL) || isVideoURL(file.URL) {
imgList = append(imgList, gin.H{
"type": "image_url",
"image_url": gin.H{
"url": file.URL,
},
})
} else {
} else if isTextURL(file.URL) {
// 处理文件,提取文件内容
content, err := utils.ReadFileContent(file.URL, h.App.Config.TikaHost)
if err != nil {
@@ -398,23 +412,43 @@ func isImageURL(url string) bool {
".svg": true,
".ico": true,
}
return validImageExts[ext]
}
if !validImageExts[ext] {
// 判断是个链接是否是文本链接
func isTextURL(url string) bool {
// 检查是否是有效的URL
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return false
}
// 发送HEAD请求检查Content-Type
client := &http.Client{
Timeout: 5 * time.Second,
// 检查文件扩展名
ext := strings.ToLower(path.Ext(url))
validTextExts := map[string]bool{
".txt": true,
".doc": true,
".docx": true,
".pdf": true,
}
resp, err := client.Head(url)
if err != nil {
return validTextExts[ext]
}
// 判断是个链接是否是视频
func isVideoURL(url string) bool {
// 检查是否是有效的URL
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
return false
}
defer resp.Body.Close()
contentType := resp.Header.Get("Content-Type")
return strings.HasPrefix(contentType, "image/")
// 检查文件扩展名
ext := strings.ToLower(path.Ext(url))
validVideoExts := map[string]bool{
".mp4": true,
".avi": true,
".mov": true,
".wmv": true,
}
return validVideoExts[ext]
}
// Tokens 统计 token 数量
@@ -495,11 +529,6 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, input
return nil, errors.New("no available key, please import key")
}
// ONLY allow apiURL in blank list
err := h.licenseService.IsValidApiURL(apiKey.ApiURL)
if err != nil {
return nil, err
}
logger.Debugf("对话请求消息体:%+v", req)
var apiURL string
p, _ := url.Parse(apiKey.ApiURL)
@@ -634,6 +663,16 @@ func (h *ChatHandler) saveChatHistory(
replyTokens, _ = utils.CalcTokens(message.Content, req.Model)
totalTokens = replyTokens + getTotalTokens(req)
}
// 如果是 Sora 相关模型,则下载视频
files := make([]vo.File, 0)
if strings.HasPrefix(req.Model, "sora") {
video, err := h.soraService.DownloadVideoURL(message.Content)
if err == nil {
files = append(files, *video)
}
}
historyReplyMsg := model.ChatMessage{
UserId: userVo.Id,
ChatId: input.ChatId,
@@ -642,7 +681,7 @@ func (h *ChatHandler) saveChatHistory(
Icon: input.ChatRole.Icon,
Content: utils.JsonEncode(vo.MsgContent{
Text: message.Content,
Files: input.Files,
Files: files,
}),
Tokens: replyTokens,
TotalTokens: totalTokens,
@@ -666,6 +705,7 @@ func (h *ChatHandler) saveChatHistory(
if err != nil {
content.Text = historyReplyMsg.Content
}
content.Files = files
messageVo.Content = content
messageVo.CreatedAt = historyReplyMsg.CreatedAt.Unix()
messageVo.UpdatedAt = historyReplyMsg.UpdatedAt.Unix()
+8
View File
@@ -212,6 +212,14 @@ func (h *ChatHandler) sendOpenAiMessage(
} else {
errMsg = utils.InterfaceToString(apiRes.Data)
contents = append(contents, errMsg)
// 扣减算力
if function.Power > 0 {
h.userService.DecreasePower(userVo.Id, function.Power, model.PowerLog{
Type: types.PowerConsume,
Model: function.Name,
Remark: fmt.Sprintf("函数调用,%s(%s)", function.Label, function.Name),
})
}
}
pushMessage(c, "text", errMsg)
}
+2 -11
View File
@@ -9,7 +9,6 @@ package handler
import (
"geekai/core"
"geekai/service"
"geekai/store/model"
"geekai/utils"
"geekai/utils/resp"
@@ -20,11 +19,10 @@ import (
type ConfigHandler struct {
BaseHandler
licenseService *service.LicenseService
}
func NewConfigHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService) *ConfigHandler {
return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}, licenseService: licenseService}
func NewConfigHandler(app *core.AppServer, db *gorm.DB) *ConfigHandler {
return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}}
}
// RegisterRoutes 注册路由
@@ -33,7 +31,6 @@ func (h *ConfigHandler) RegisterRoutes() {
// 无需授权的接口
group.GET("get", h.Get)
group.GET("license", h.License)
}
// Get 获取指定的系统配置
@@ -55,9 +52,3 @@ func (h *ConfigHandler) Get(c *gin.Context) {
resp.SUCCESS(c, value)
}
// License 获取 License 配置
func (h *ConfigHandler) License(c *gin.Context) {
license := h.licenseService.GetLicense()
resp.SUCCESS(c, license.Configs)
}
+4 -4
View File
@@ -117,12 +117,12 @@ func (h *DallJobHandler) Image(c *gin.Context) {
task := types.DallTask{
UserId: uint(userId),
ModelId: chatModel.Id,
ModelName: chatModel.Value,
ModelName: chatModel.Name,
ModelValue: chatModel.Value,
Image: data.Image,
Prompt: data.Prompt,
Quality: data.Quality,
AspectRatio: data.AspectRatio,
Size: data.Size,
Style: data.Style,
TranslateModelId: h.App.SysConfig.Base.AssistantModelId,
Power: chatModel.Power,
}
@@ -144,7 +144,7 @@ func (h *DallJobHandler) Image(c *gin.Context) {
// 扣减算力
err = h.userService.DecreasePower(user.Id, chatModel.Power, model.PowerLog{
Type: types.PowerConsume,
Model: chatModel.Value,
Model: chatModel.Name,
Remark: fmt.Sprintf("绘画提示词:%s", utils.CutWords(task.Prompt, 10)),
})
if err != nil {
+2 -5
View File
@@ -215,12 +215,9 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
UserId: user.Id,
Prompt: prompt,
ModelId: chatModel.Id,
ModelName: chatModel.Value,
ModelValue: chatModel.Value,
ModelName: chatModel.Name,
TranslateModelId: h.App.SysConfig.Base.AssistantModelId,
N: 1,
Quality: "standard",
Size: "1024x1024",
Style: "vivid",
Power: chatModel.Power,
}
job := model.DallJob{
+99 -212
View File
@@ -1,6 +1,7 @@
package handler
import (
"errors"
"fmt"
"geekai/core"
"geekai/core/middleware"
@@ -38,49 +39,28 @@ func NewJimengHandler(app *core.AppServer, jimengService *jimeng.Service, db *go
// RegisterRoutes 注册路由,新增统一任务接口
func (h *JimengHandler) RegisterRoutes() {
group := h.App.Engine.Group("/api/jimeng/")
group.GET("power-config", h.GetPowerConfig)
// 需要用户授权的接口
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
{
group.POST("task", h.CreateTask)
group.GET("power-config", h.GetPowerConfig)
group.POST("jobs", h.Jobs)
group.GET("remove", h.Remove)
group.GET("retry", h.Retry)
}
}
// JimengTaskRequest 统一任务请求结构体
// 支持所有生图和生成视频类型
type JimengTaskRequest struct {
TaskType string `json:"task_type" binding:"required"`
Prompt string `json:"prompt"`
ImageInput string `json:"image_input"`
ImageUrls []string `json:"image_urls"`
BinaryDataBase64 []string `json:"binary_data_base64"`
Scale float64 `json:"scale"`
Width int `json:"width"`
Height int `json:"height"`
Gpen float64 `json:"gpen"`
Skin float64 `json:"skin"`
SkinUnifi float64 `json:"skin_unifi"`
GenMode string `json:"gen_mode"`
Seed int64 `json:"seed"`
UsePreLLM bool `json:"use_pre_llm"`
TemplateId string `json:"template_id"`
AspectRatio string `json:"aspect_ratio"`
}
// CreateTask 统一任务创建接口
func (h *JimengHandler) CreateTask(c *gin.Context) {
var req JimengTaskRequest
var req types.JimengTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
// 文本审核
if h.App.SysConfig.Moderation.Enable {
if h.App.SysConfig.Moderation.Enable && req.Prompt != "" {
moderationResult, err := h.moderationManager.GetService().Moderate(req.Prompt)
if err != nil {
logger.Error("failed to moderate content: ", err)
@@ -103,136 +83,21 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
}
// 新增:除图像特效外,其他任务类型必须有提示词
if req.TaskType != "image_effects" && req.Prompt == "" {
resp.ERROR(c, "提示词不能为空")
if req.Prompt == "" && len(req.ImageUrls) == 0 {
resp.ERROR(c, "提示词和图片不能同时为空")
return
}
user, err := h.GetLoginUser(c)
if err != nil {
resp.NotAuth(c)
return
}
if req.Width == 0 {
req.Width = 1328
}
if req.Height == 0 {
req.Height = 1328
}
if req.Seed == 0 {
req.Seed = -1
}
var powerCost int
var taskType model.JMTaskType
var params map[string]any
var reqKey string
var modelName string
switch req.TaskType {
case "text_to_image":
powerCost = h.getPowerFromConfig(model.JMTaskTypeTextToImage)
taskType = model.JMTaskTypeTextToImage
reqKey = jimeng.ReqKeyTextToImage
modelName = "即梦文生图"
if req.Scale == 0 {
req.Scale = 2.5
}
params = map[string]any{
"seed": req.Seed,
"scale": req.Scale,
"width": req.Width,
"height": req.Height,
"use_pre_llm": req.UsePreLLM,
}
case "image_to_image":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageToImage)
taskType = model.JMTaskTypeImageToImage
reqKey = jimeng.ReqKeyImageToImagePortrait
modelName = "即梦图生图"
if req.Gpen == 0 {
req.Gpen = 0.4
}
if req.Skin == 0 {
req.Skin = 0.3
}
if req.GenMode == "" {
if req.Prompt != "" {
req.GenMode = jimeng.GenModeCreative
} else {
req.GenMode = jimeng.GenModeReference
}
}
params = map[string]any{
"image_input": req.ImageInput,
"width": req.Width,
"height": req.Height,
"gpen": req.Gpen,
"skin": req.Skin,
"skin_unifi": req.SkinUnifi,
"gen_mode": req.GenMode,
"seed": req.Seed,
}
case "image_edit":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageEdit)
taskType = model.JMTaskTypeImageEdit
reqKey = jimeng.ReqKeyImageEdit
modelName = "即梦图像编辑"
if req.Scale == 0 {
req.Scale = 0.5
}
params = map[string]any{
"seed": req.Seed,
"scale": req.Scale,
}
params["image_urls"] = []string{req.ImageInput}
case "image_effects":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageEffects)
taskType = model.JMTaskTypeImageEffects
reqKey = jimeng.ReqKeyImageEffects
modelName = "即梦图像特效"
if req.Width == 0 {
req.Width = 1328
}
if req.Height == 0 {
req.Height = 1328
}
params = map[string]any{
"image_input1": req.ImageInput,
"template_id": req.TemplateId,
"width": req.Width,
"height": req.Height,
}
case "text_to_video":
powerCost = h.getPowerFromConfig(model.JMTaskTypeTextToVideo)
taskType = model.JMTaskTypeTextToVideo
reqKey = jimeng.ReqKeyTextToVideo
modelName = "即梦文生视频"
if req.AspectRatio == "" {
req.AspectRatio = jimeng.AspectRatio16_9
}
params = map[string]any{
"seed": req.Seed,
"aspect_ratio": req.AspectRatio,
}
case "image_to_video":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageToVideo)
taskType = model.JMTaskTypeImageToVideo
reqKey = jimeng.ReqKeyImageToVideo
modelName = "即梦图生视频"
params = map[string]any{
"seed": req.Seed,
"aspect_ratio": req.AspectRatio,
}
if len(req.ImageUrls) > 0 {
params["image_urls"] = req.ImageUrls
}
if len(req.BinaryDataBase64) > 0 {
params["binary_data_base64"] = req.BinaryDataBase64
}
default:
resp.ERROR(c, "不支持的任务类型")
// 获取算力消耗
powerCost, err := h.getTaskPower(req)
if err != nil {
resp.ERROR(c, "计算任务消耗积分失败: "+err.Error())
return
}
@@ -240,16 +105,9 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
resp.ERROR(c, fmt.Sprintf("算力不足,需要%d算力", powerCost))
return
}
req.Power = powerCost
taskReq := &jimeng.CreateTaskRequest{
Type: taskType,
Prompt: req.Prompt,
Params: params,
ReqKey: reqKey,
Power: powerCost,
}
job, err := h.jimengService.CreateTask(user.Id, taskReq)
job, err := h.jimengService.CreateTask(user.Id, &req)
if err != nil {
logger.Errorf("create jimeng task failed: %v", err)
resp.ERROR(c, "创建任务失败")
@@ -258,11 +116,42 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
h.userService.DecreasePower(user.Id, powerCost, model.PowerLog{
Type: types.PowerConsume,
Model: "jimeng",
Remark: fmt.Sprintf("%s,任务ID%d", modelName, job.Id),
Model: job.ReqKey,
Remark: h.getTaskRemark(req, job.Id),
})
resp.SUCCESS(c, job)
resp.SUCCESS(c)
}
func (h *JimengHandler) getTaskRemark(req types.JimengTaskRequest, jobId uint) string {
remark := fmt.Sprintf("即梦任务%s,任务ID%d", req.ReqKey, jobId)
perUnit, ok := h.App.SysConfig.Jimeng.Powers[req.ReqKey]
if !ok || perUnit <= 0 {
return remark // Fallback if power not found or invalid
}
switch req.TaskType {
case types.JMTaskTypeImage:
remark = fmt.Sprintf("即梦图片生成,任务ID%d%d积分/张", jobId, perUnit)
case types.JMTaskTypeVideo:
seconds := 0
if perUnit > 0 {
seconds = req.Power / perUnit
}
remark = fmt.Sprintf("即梦视频生成,任务ID%d%d积分/秒, %d秒", jobId, perUnit, seconds)
case types.JMTaskTypeVirtualHuman:
seconds := 0
if perUnit > 0 {
seconds = req.Power / perUnit
}
remark = fmt.Sprintf("即梦数字人视频生成,任务ID%d,%d积分/秒, %d秒", jobId, perUnit, seconds)
case types.JMTaskTypeActionTransfer:
seconds := 0
if perUnit > 0 {
seconds = req.Power / perUnit
}
remark = fmt.Sprintf("即梦视频动作迁移,任务ID%d%d积分/秒, %d秒", jobId, perUnit, seconds)
}
return remark
}
// Jobs 获取任务列表
@@ -287,17 +176,13 @@ func (h *JimengHandler) Jobs(c *gin.Context) {
switch req.Filter {
case "image":
query = query.Where("type IN (?)", []model.JMTaskType{
model.JMTaskTypeTextToImage,
model.JMTaskTypeImageToImage,
model.JMTaskTypeImageEdit,
model.JMTaskTypeImageEffects,
})
query = query.Where("type = ?", types.JMTaskTypeImage)
case "video":
query = query.Where("type IN (?)", []model.JMTaskType{
model.JMTaskTypeTextToVideo,
model.JMTaskTypeImageToVideo,
})
query = query.Where("type = ?", types.JMTaskTypeVideo)
case "virtual_human":
query = query.Where("type = ?", types.JMTaskTypeVirtualHuman)
case "action_transfer":
query = query.Where("type = ?", types.JMTaskTypeActionTransfer)
}
if len(req.Ids) > 0 {
@@ -357,34 +242,17 @@ func (h *JimengHandler) Remove(c *gin.Context) {
}
// 正在运行中的任务不能删除
if job.Status == model.JMTaskStatusGenerating || job.Status == model.JMTaskStatusInQueue {
if job.Status == types.JMTaskStatusGenerating || job.Status == types.JMTaskStatusInQueue {
resp.ERROR(c, "正在运行中的任务不能删除,否则无法退回算力")
return
}
tx := h.DB.Begin()
if err := tx.Where("id = ? AND user_id = ?", jobId, user.Id).Delete(&model.JimengJob{}).Error; err != nil {
if err := h.DB.Where("id = ? AND user_id = ?", jobId, user.Id).Delete(&model.JimengJob{}).Error; err != nil {
logger.Errorf("delete jimeng job failed: %v", err)
resp.ERROR(c, "删除任务失败")
return
}
// 失败任务删除后退回算力
if job.Status != model.JMTaskStatusFailed {
err = h.userService.IncreasePower(user.Id, job.Power, model.PowerLog{
Type: types.PowerRefund,
Model: "jimeng",
Remark: fmt.Sprintf("删除任务,退回%d算力", job.Power),
})
if err != nil {
resp.ERROR(c, "退回算力失败")
tx.Rollback()
return
}
}
tx.Commit()
resp.SUCCESS(c, gin.H{})
}
@@ -411,13 +279,13 @@ func (h *JimengHandler) Retry(c *gin.Context) {
}
// 只有失败的任务才能重试
if job.Status != model.JMTaskStatusFailed {
if job.Status != types.JMTaskStatusFailed {
resp.ERROR(c, "只有失败的任务才能重试")
return
}
// 重置任务状态
if err := h.jimengService.UpdateJobStatus(uint(jobId), model.JMTaskStatusInQueue, ""); err != nil {
if err := h.jimengService.UpdateJobStatus(uint(jobId), types.JMTaskStatusInQueue, ""); err != nil {
logger.Errorf("reset job status failed: %v", err)
resp.ERROR(c, "重置任务状态失败")
return
@@ -433,25 +301,49 @@ func (h *JimengHandler) Retry(c *gin.Context) {
resp.SUCCESS(c, gin.H{"message": "重试任务已提交"})
}
// getPowerFromConfig 从配置中获取指定类型的算力消耗
func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int {
func (h *JimengHandler) getTaskPower(req types.JimengTaskRequest) (int, error) {
logger.Debugf("getTaskPower req: %+v", req)
config := h.App.SysConfig.Jimeng
switch taskType {
case model.JMTaskTypeTextToImage:
return config.Power.TextToImage
case model.JMTaskTypeImageToImage:
return config.Power.ImageToImage
case model.JMTaskTypeImageEdit:
return config.Power.ImageEdit
case model.JMTaskTypeImageEffects:
return config.Power.ImageEffects
case model.JMTaskTypeTextToVideo:
return config.Power.TextToVideo
case model.JMTaskTypeImageToVideo:
return config.Power.ImageToVideo
basePower, ok := config.Powers[req.ReqKey]
if !ok || basePower <= 0 {
return 0, errors.New("未配置模型积分或配置不合法")
}
switch req.TaskType {
case types.JMTaskTypeImage:
return basePower, nil
case types.JMTaskTypeVideo:
if req.Duration == 0 {
return 0, errors.New("视频时长不能为0")
}
return basePower * req.Duration, nil
case types.JMTaskTypeVirtualHuman:
if req.AudioURL == "" {
return 0, errors.New("音频URL不能为空")
}
audioDuration, err := utils.AudioDurationFromURL(req.AudioURL)
if err != nil {
return 0, err
}
seconds := int(audioDuration.Seconds())
if seconds <= 0 {
return 0, errors.New("音频时长无效")
}
return basePower * seconds, nil
case types.JMTaskTypeActionTransfer:
if req.VideoURL == "" {
return 0, errors.New("视频URL不能为空")
}
videoDuration, err := utils.VideoDurationMP4FromURL(req.VideoURL)
if err != nil {
return 0, err
}
seconds := int(videoDuration.Seconds())
if seconds <= 0 {
return 0, errors.New("视频时长无效")
}
return basePower * seconds, nil
default:
return 10
return 0, errors.New("任务类型不支持")
}
}
@@ -459,11 +351,6 @@ func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int {
func (h *JimengHandler) GetPowerConfig(c *gin.Context) {
config := h.App.SysConfig.Jimeng
resp.SUCCESS(c, gin.H{
"text_to_image": config.Power.TextToImage,
"image_to_image": config.Power.ImageToImage,
"image_edit": config.Power.ImageEdit,
"image_effects": config.Power.ImageEffects,
"text_to_video": config.Power.TextToVideo,
"image_to_video": config.Power.ImageToVideo,
"powers": config.Powers,
})
}
+18
View File
@@ -30,6 +30,7 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
func (h *MenuHandler) RegisterRoutes() {
group := h.App.Engine.Group("/api/menu/")
group.GET("list", h.List)
group.GET("list/all", h.ListAll)
}
// List 数据列表
@@ -54,3 +55,20 @@ func (h *MenuHandler) List(c *gin.Context) {
}
resp.SUCCESS(c, list)
}
// ListAll 获取所有的菜单列表
func (h *MenuHandler) ListAll(c *gin.Context) {
var items []model.Menu
var list = make([]vo.Menu, 0)
res := h.DB.Order("sort_num ASC").Find(&items)
if res.Error == nil {
for _, item := range items {
var product vo.Menu
err := utils.CopyObject(item, &product)
if err == nil {
list = append(list, product)
}
}
}
resp.SUCCESS(c, list)
}
+1
View File
@@ -138,6 +138,7 @@ func (h *PaymentHandler) SyncOrders() error {
}
case payment.PayChannelAL:
res, err = h.alipayService.Query(order.OrderNo)
logger.Debugf("支付宝支付订单状态:%+v", res)
if err != nil {
logger.Errorf("error with query order info: %v", err)
continue
+9 -17
View File
@@ -35,7 +35,6 @@ type UserHandler struct {
searcher *xdb.Searcher
redis *redis.Client
levelDB *store.LevelDB
licenseService *service.LicenseService
captchaService *service.CaptchaService
userService *service.UserService
wxLoginService *service.WxLoginService
@@ -51,15 +50,13 @@ func NewUserHandler(
captcha *service.CaptchaService,
userService *service.UserService,
wxLoginService *service.WxLoginService,
ipSearcher *xdb.Searcher,
licenseService *service.LicenseService) *UserHandler {
ipSearcher *xdb.Searcher) *UserHandler {
return &UserHandler{
BaseHandler: BaseHandler{DB: db, App: app},
searcher: searcher,
redis: client,
levelDB: levelDB,
captchaService: captcha,
licenseService: licenseService,
userService: userService,
wxLoginService: wxLoginService,
ipSearcher: ipSearcher,
@@ -132,14 +129,6 @@ func (h *UserHandler) Register(c *gin.Context) {
return
}
// 检测最大注册人数
var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return
}
// 检查验证码
var key string
if data.RegWay == "email" {
@@ -327,7 +316,7 @@ func (h *UserHandler) GetWxLoginState(c *gin.Context) {
}
// createNewUser 创建新用户
func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.User, error) {
func (h *UserHandler) createNewUser(user model.User, code string) (model.User, error) {
if user.OpenId != "" {
user.Platform = "wechat"
user.Nickname = fmt.Sprintf("微信用户@%d", utils.RandomNumber(6))
@@ -353,19 +342,22 @@ func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.U
// 创建用户
tx := h.DB.Begin()
if err := tx.Create(&user).Error; err != nil {
tx.Rollback()
return user, err
}
// 记录邀请关系
if inviteCode != "" {
inviteCode := model.InviteCode{}
err := h.DB.Where("code = ?", inviteCode).First(&inviteCode).Error
if code != "" {
var inviteCode model.InviteCode
err := h.DB.Where("code = ?", code).First(&inviteCode).Error
if err != nil {
logger.Error("无效的邀请码:", err.Error())
tx.Rollback()
return user, fmt.Errorf("无效的邀请码")
}
// 增加邀请数量
h.DB.Model(&model.InviteCode{}).Where("code = ?", inviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
h.DB.Model(&model.InviteCode{}).Where("code = ?", code).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
if h.App.SysConfig.Base.InvitePower > 0 {
err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.Base.InvitePower, model.PowerLog{
Type: types.PowerInvite,
+6 -12
View File
@@ -24,6 +24,7 @@ import (
"geekai/service/payment"
"geekai/service/sd"
"geekai/service/sms"
"geekai/service/sora"
"geekai/service/suno"
"geekai/service/video"
"geekai/store"
@@ -166,12 +167,6 @@ func main() {
// 邮件服务
fx.Provide(service.NewSmtpService),
// License 服务
fx.Provide(service.NewLicenseService),
fx.Invoke(func(licenseService *service.LicenseService) {
licenseService.SyncLicense()
}),
// Dalle 服务
fx.Provide(dalle.NewService),
fx.Invoke(func(s *dalle.Service) {
@@ -189,6 +184,9 @@ func main() {
s.DownloadImages()
}),
// Sora service
fx.Provide(sora.NewSoraService),
// Stable Diffusion 机器人
fx.Provide(sd.NewService),
fx.Invoke(func(s *sd.Service, config *types.AppConfig) {
@@ -222,12 +220,8 @@ func main() {
fx.Provide(sms.NewAliYunSmsService),
fx.Provide(sms.NewBaoSmsService),
fx.Provide(sms.NewSmsManager),
fx.Provide(func(config *types.SystemConfig) *service.CaptchaService {
return service.NewCaptchaService(config.Captcha)
}),
fx.Provide(func(config *types.SystemConfig, client *redis.Client) *service.WxLoginService {
return service.NewWxLoginService(config.WxLogin, client)
}),
fx.Provide(service.NewCaptchaService),
fx.Provide(service.NewWxLoginService),
// 支付服务
fx.Provide(payment.NewAlipayService),
-38
View File
@@ -1,38 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDszCCApugAwIBAgIQICMRB0rBU2/rZJbfJGMYIzANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMxMTA3MDYzNTQxWhcNMjQxMTA2MDYzNTQxWjCB
hDELMAkGA1UEBhMCQ04xHzAdBgNVBAoMFm1ib25meTkwMTVAc2FuZGJveC5jb20xDzANBgNVBAsM
BkFsaXBheTFDMEEGA1UEAww65pSv5LuY5a6dKOS4reWbvSnnvZHnu5zmioDmnK/mnInpmZDlhazl
j7gtMjA4ODcyMTAyMDc1MDU4MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsoKcw5
sxaiyV7mpWzDtnQ1K518eQLP0+dJlZAf06aBep/Aj9DIqrba/k7DHt8dKQvILMLAMpN1+2IRxbaO
yxMa/laj3lZ1eHrB6F077O3D62oHcE3noZtXL0N1zZAxpmkNmYIHeLZS2oLMS4ANu47O/wpDC7BV
HjdpZugtdPJ4mxdCpM9GDdLs7W4s5QI4PUPK4skFNMFoKI+0cYP/9ju87UP//IHC/K510GWNl+Gn
Cvgag3AmiIB0utJNsGhxm6zT1T9tUWjW9iz/BxBKiPatsCX9VpPQzGnW7ZonRQtiZSokIlP2IPvl
H5DcwpWUz3/LUY0SmKxnKOEYeOOqCW8CAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
DQEBCwUAA4IBAQAtgxF2EzjOndEFxBUD9tFwcSt6XKGggOp52oft1pvynPg4ALTLafOtfEPDrFBH
PwpYrSu9s9C8NJtaA2HrlCfBjIuwEFTXiN+HPvS0SwSPKt9AXEiTcOF8vDcGamEen8QI4fo5Jia7
2VRKkerkww5/+FzSaVO7ZUKuL80M1QJStmAZc8kPPwdYOTTW2bGf8BcmSDL6SPElBkt7tCCRd4sn
+jq4cZ0yb2i77rBZCwHcTvfTqIBblPwLv4uGvg3+83BxIB5w6Kqp06bKEAPmobFY5IVHa+ON0/qi
BXxXr+WQ3piKRVQEN64+PTAjSc67Ix1umvpLl3Ko6Ry7NJmpDcUn
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDszCCApugAwIBAgIQIBkIGbgVxq210KxLJ+YA/TANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxJTAjBgNVBAsMHENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IHRlc3QxNjA0BgNVBAMMLUFudCBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgUjEgdGVzdDAeFw0xOTA4MTkxMTE2MDBaFw0yNDA4MDExMTE2MDBaMIGRMQswCQYDVQQGEwJD
TjEbMBkGA1UECgwSQW50IEZpbmFuY2lhbCB0ZXN0MSUwIwYDVQQLDBxDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSB0ZXN0MT4wPAYDVQQDDDVBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IENsYXNzIDIgUjEgdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh4FKYO
ZyRQHD6eFbPKZeSAnrfjfU7xmS9Yoozuu+iuqZlb6Z0SPLUqqTZAFZejOcmr07ln/pwZxluqplxC
5+B48End4nclDMlT5HPrDr3W0frs6Xsa2ZNcyil/iKNB5MbGll8LRAxntsKvZZj6vUTMb705gYgm
VUMILwi/ZxKTQqBtkT/kQQ5y6nOZsj7XI5rYdz6qqOROrpvS/d7iypdHOMIM9Iz9DlL1mrCykbBi
t25y+gTeXmuisHUwqaRpwtCGK4BayCqxRGbNipe6W73EK9lBrrzNtTr9NaysesT/v+l25JHCL9tG
wpNr1oWFzk4IHVOg0ORiQ6SUgxZUTYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
DQEBCwUAA4IBAQBWThEoIaQoBX2YeRY/I8gu6TYnFXtyuCljANnXnM38ft+ikhE5mMNgKmJYLHvT
yWWWgwHoSAWEuml7EGbE/2AK2h3k0MdfiWLzdmpPCRG/RJHk6UB1pMHPilI+c0MVu16OPpKbg5Vf
LTv7dsAB40AzKsvyYw88/Ezi1osTXo6QQwda7uefvudirtb8FcQM9R66cJxl3kt1FXbpYwheIm/p
j1mq64swCoIYu4NrsUYtn6CV542DTQMI5QdXkn+PzUUly8F6kDp+KpMNd0avfWNL5+O++z+F5Szy
1CPta1D7EQ/eYmMP+mOQ35oifWIoFCpN6qQVBS/Hob1J/UUyg7BW
-----END CERTIFICATE-----
-88
View File
@@ -1,88 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
pDoiVhsLwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
kT9qhqn+lw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
Yf4Zr0fJsGuv
-----END CERTIFICATE-----
-19
View File
@@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDmTCCAoGgAwIBAgIQICMRB2LW76yahgdg3IFNPDANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMxMTA3MDU0NjE5WhcNMjQxMTExMDU0NjE5WjBr
MQswCQYDVQQGEwJDTjEfMB0GA1UECgwWbWJvbmZ5OTAxNUBzYW5kYm94LmNvbTEPMA0GA1UECwwG
QWxpcGF5MSowKAYDVQQDDCEyMDg4NzIxMDIwNzUwNTgxLTkwMjEwMDAxMzE2NTgwMjMwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxihQPf1Q+g9ArgM46shVqL5sbRha/df95D1PsWyEq
ANmWmG4zZ+ksYDVQrc4KzhSRoi56sm/7TDFYTmM6bW99e/nKW58WxyZB4ie5qA3F4n17psPyDqb8
IokcQmCphSFDaXQD6AoXoLNtTM0vAI2cWxAgebZ/vsrdj5Ntjt+Rp3NYMCk1i5xovHcfILzLEGbX
QXoT9fo5AhHotTWa6xHVLPUGY9qwLzQxHzBmvy5ZMfnOfJkm/mDisTSqAUB59F3dzU/1ARVkEZ1w
Mgb4XohWBw6iurQfbMnH2mIomAAwwZVFv+sXDbL9yMbSMo/SjVsTQprn0Q0EnwLo7nmmOM6HAgMB
AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAn3Y4/C1h9R6ONsBqX3/q
XfHX7yX1FM0Y1x48X3/Yxk6HivAkTukhhhVYVKJsbrbzRqHDp9vhAP/FR6o6pAevaYMmLov0VMXU
7oAuetgkaYEYkDuNen5/Hpdhqi2vTtdT+q9w8zHJd6MDQ0aoHgIxpLKw5vof2R1N4fwSgNXMiXE5
kmllKQMem/+on2p+Sj80/2asxryHIGlH87qPzkffv+kIOkZthbTApTFLLjdVri2QHGe8/cc4xy01
/9iR3IUzNahotT41lJ4bMevBY7XMAS3n5ekyABN/9ZRJqhWdXgmFCRN/u56qd6lDgu7R2M2QUoyc
LuW5DfgRItKlmUB7sw==
-----END CERTIFICATE-----
-1
View File
@@ -1 +0,0 @@
MIIEpQIBAAKCAQEAsYoUD39UPoPQK4DOOrIVai+bG0YWv3X/eQ9T7FshKgDZlphuM2fpLGA1UK3OCs4UkaIuerJv+0wxWE5jOm1vfXv5ylufFscmQeInuagNxeJ9e6bD8g6m/CKJHEJgqYUhQ2l0A+gKF6CzbUzNLwCNnFsQIHm2f77K3Y+TbY7fkadzWDApNYucaLx3HyC8yxBm10F6E/X6OQIR6LU1musR1Sz1BmPasC80MR8wZr8uWTH5znyZJv5g4rE0qgFAefRd3c1P9QEVZBGdcDIG+F6IVgcOorq0H2zJx9piKJgAMMGVRb/rFw2y/cjG0jKP0o1bE0Ka59ENBJ8C6O55pjjOhwIDAQABAoIBAFetNfz1R7hbxjlFshMAkVzQR8wvT9qbvl+dtzdZRcaFhu89NecDIP7+QDYor0FcxoGpU0TazDyRQyk2BQD8vHt+9zv9BVLtZLJSqoWgPbUFBi1DjS8EF2ka8RVYnn35NhUhhd7L//ftL88Bh673mfembQ9srDjoEy1Z01feoABAnCMkNFl986DmEwnarvEufXSDIgeN4ioMxha4NvfIPuI0zpVdV1O9sv+SGC+VEWZBtN3GNsaf4zS/f8FVGvTiU/Abz0gSw/iwSPHclDWQDTN3yFHf/tfqlzh0mH0WfhnuOBFWXzK+R7fbnM+asI9ttvzRcfpzgRGXdPcNcOv/6cECgYEA3DVqpi1k8MYfJixju6SG5gfyhM4VFksFmCMaNPgtatDMBKLMTgV/Ej6LXREojcy29uZl83F09pVlpd41eG39ULIPktixA/BqErQ2UaWh6kOxifycpu22Jh0r09hax6UgVrcBrrnCJEjcFsuJlrZvXQSzc3PBxjWy5gjabS5h9iECgYEAzmVAIh2frF01Y95zsLueAhhZwCtPanm6kf7ivR4r1plIX3b2sNRhWGmEHFgaCE6Braa0ogQ73Hd26kw4ZW+D6QMGC/zjCBEzDLLf++SjdVUHiY5AR4WHqXzq1jdAlsVyo9R661oAOp3lhiJVGLNXkHyEfEVPHsaxJh4osYSbX6cCgYEAx32Qx0i6eDFTyLZQB46uMrgiaVN04QRH5iJuvGvUYT8UhGKjaU8rZfDJOh+wOH2rhxMEaz1uc3C2bERY9mfWI4Ob/jFWc7YZsiYWS3Mcsuhubw4tMECLUg39RWZsHw8ls8kIuixIh6yFzhTH6YQOcRswIrhMZG8DScfdcSmiz2ECgYEAkWP1t5KSpkLKl11etcKUXfl1T8+yk9jIOowIgRw92WAFAWq2AH67TCKYM7dEL1HOO9tRJ0hAOt/U3ttuZtYVYBEHM26jJ02mXm2rJrA7DS4mrxmL4lYH6LbcXqZxU0Qnq4zEQgIWYzRTORf6Rfof1uJAGaJhR9bDd4yLMfGt2cUCgYEAo216Y61xOHUTA4AF1eekk+r+uOcQgQDvLXfs9FkDdJLk0mPG48/+eIYpPFnANJ/riF/DWOp8WGEe2IzA9yUFexzDbNQK8ha9kGcxaSAyiCwzjZ/t9/+hScDSV8kNqWSRSisu/YOFleEHbokT6mbLZ+gdqES8mUUanaEBzRQYGxo=
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

+3 -3
View File
@@ -20,9 +20,9 @@ type CaptchaService struct {
client *req.Client
}
func NewCaptchaService(captchaConfig types.CaptchaConfig) *CaptchaService {
func NewCaptchaService(sysConfig *types.SystemConfig) *CaptchaService {
return &CaptchaService{
config: captchaConfig,
config: sysConfig.Captcha,
client: req.C().SetTimeout(10 * time.Second),
}
}
@@ -35,7 +35,7 @@ func (s *CaptchaService) GetConfig() types.CaptchaConfig {
return s.config
}
func (s *CaptchaService) Get() (interface{}, error) {
func (s *CaptchaService) Get() (any, error) {
url := fmt.Sprintf("%s/api/captcha/get", types.GeekAPIURL)
var res types.BizVo
r, err := s.client.R().
@@ -16,7 +16,6 @@ import (
"geekai/store"
"geekai/store/model"
"geekai/utils"
"strings"
"time"
"github.com/go-redis/redis/v8"
@@ -98,10 +97,8 @@ type imgReq struct {
Model string `json:"model"`
Image []string `json:"image,omitempty"`
Prompt string `json:"prompt"`
N int `json:"n,omitempty"`
AspectRatio string `json:"aspect_ratio,omitempty"`
Size string `json:"size,omitempty"`
Quality string `json:"quality,omitempty"`
Style string `json:"style,omitempty"`
ResponseFormat string `json:"response_format,omitempty"`
}
@@ -116,10 +113,10 @@ type imgRes struct {
type ErrRes struct {
Error struct {
Code interface{} `json:"code"`
Message string `json:"message"`
Param interface{} `json:"param"`
Type string `json:"type"`
Code any `json:"code"`
Message string `json:"message"`
Param any `json:"param"`
Type string `json:"type"`
} `json:"error"`
}
@@ -130,7 +127,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
if task.ModelId > 0 {
s.db.Where("id", task.ModelId).First(&chatModel)
} else {
s.db.Where("value", task.ModelName).First(&chatModel)
s.db.Where("value", task.ModelValue).First(&chatModel)
}
// get image generation API KEY
@@ -153,16 +150,16 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
}
apiURL := fmt.Sprintf("%s/v1/images/generations", apiKey.ApiURL)
reqBody := imgReq{
Model: chatModel.Value,
Prompt: task.Prompt,
N: 1,
Size: task.Size,
Style: task.Style,
Quality: task.Quality,
Model: chatModel.Value,
Prompt: task.Prompt,
AspectRatio: task.AspectRatio,
Size: task.Size,
ResponseFormat: "url",
}
// 图片编辑
if len(task.Image) > 0 {
reqBody.Prompt = fmt.Sprintf("%s, %s", strings.Join(task.Image, " "), task.Prompt)
reqBody.Image = task.Image
}
logger.Infof("Channel:%s, API KEY:%s, BODY: %+v", apiURL, apiKey.Value, reqBody)
@@ -185,7 +182,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
// update the api key last use time
s.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
var imgURL string
var data = map[string]interface{}{
var data = map[string]any{
"progress": 100,
"prompt": task.Prompt,
}
+137 -28
View File
@@ -1,15 +1,21 @@
package jimeng
import (
"context"
"encoding/json"
"errors"
"fmt"
"geekai/core/types"
"net/http"
"net/url"
"strings"
"time"
"github.com/volcengine/volc-sdk-golang/base"
"github.com/volcengine/volc-sdk-golang/service/visual"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
"github.com/volcengine/volcengine-go-sdk/volcengine"
)
// Client 即梦API客户端
@@ -50,6 +56,22 @@ func (c *Client) UpdateConfig(config types.JimengConfig) error {
"Version": []string{"2022-08-31"},
},
},
"CVSubmitTask": {
Method: http.MethodPost,
Path: "/",
Query: url.Values{
"Action": []string{"CVSubmitTask"},
"Version": []string{"2022-08-31"},
},
},
"CVGetResult": {
Method: http.MethodPost,
Path: "/",
Query: url.Values{
"Action": []string{"CVGetResult"},
"Version": []string{"2022-08-31"},
},
},
"CVProcess": {
Method: http.MethodPost,
Path: "/",
@@ -71,6 +93,22 @@ func (c *Client) UpdateConfig(config types.JimengConfig) error {
return c.testConnection()
}
// GetErrorMessage 根据错误代码获取对应的错误信息
func GetErrorMessage(code int) string {
if message, exists := errorCodeMessages[code]; exists {
return message
}
return fmt.Sprintf("未知错误代码: %d", code)
}
// HandleResponseError 处理响应错误,根据错误代码返回详细的错误信息
func HandleResponseError(code int, message string) error {
if code == ECSuccess {
return nil
}
return errors.New(GetErrorMessage(code))
}
// testConnection 测试即梦AI连接
func (c *Client) testConnection() error {
@@ -80,7 +118,7 @@ func (c *Client) testConnection() error {
TaskId: "test_task_id_12345",
}
_, err := c.QueryTask(testReq)
_, err := c.QueryTask(testReq, ASyncActionGetResult)
// 即使任务不存在,只要不是认证错误就说明连接正常
if err != nil {
// 检查是否是认证错误
@@ -94,7 +132,7 @@ func (c *Client) testConnection() error {
}
// SubmitTask 提交异步任务
func (c *Client) SubmitTask(req *SubmitTaskRequest) (*SubmitTaskResponse, error) {
func (c *Client) SubmitTask(req map[string]any) (*SubmitTaskResponse, error) {
// 直接将请求转为map[string]interface{}
reqBodyBytes, err := json.Marshal(req)
if err != nil {
@@ -103,9 +141,14 @@ func (c *Client) SubmitTask(req *SubmitTaskRequest) (*SubmitTaskResponse, error)
// 直接使用序列化后的字节
jsonBody := reqBodyBytes
action := ASyncActionSubmit
if v, ok := req["action"]; ok {
action = v.(string)
delete(req, "action")
}
// 调用SDK的JSON方法
respBody, statusCode, err := c.visual.Client.Json("CVSync2AsyncSubmitTask", nil, string(jsonBody))
respBody, statusCode, err := c.visual.Client.Json(action, nil, string(jsonBody))
if err != nil {
return nil, fmt.Errorf("submit task failed (status: %d): %w", statusCode, err)
}
@@ -118,11 +161,70 @@ func (c *Client) SubmitTask(req *SubmitTaskRequest) (*SubmitTaskResponse, error)
return nil, fmt.Errorf("unmarshal response failed: %w", err)
}
// 检查响应错误代码
if err := HandleResponseError(result.Code, result.Message); err != nil {
return nil, err
}
return &result, nil
}
// 识别数字人主体
func (c *Client) AvatarRecognition(imgUrl string, reqKey string) error {
params := map[string]any{
"image_url": imgUrl,
"req_key": reqKey,
}
reqBodyBytes, err := json.Marshal(params)
if err != nil {
return fmt.Errorf("marshal request failed: %w", err)
}
// 调用SDK的JSON方法
respBody, statusCode, err := c.visual.Client.Json(SyncActionSubmit, nil, string(reqBodyBytes))
if err != nil {
return fmt.Errorf("submit task failed (status: %d): %w", statusCode, err)
}
// 解析响应
var result SubmitTaskResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return fmt.Errorf("unmarshal response failed: %w", err)
}
// 检查响应错误代码
if err := HandleResponseError(result.Code, result.Message); err != nil {
return err
}
// 等待任务完成
for {
resp, err := c.QueryTask(&QueryTaskRequest{
ReqKey: reqKey,
TaskId: result.Data.TaskId,
}, SyncActionGetResult)
if err != nil {
return fmt.Errorf("query task failed: %w", err)
}
if resp.Data.Status != types.JMTaskStatusDone {
time.Sleep(time.Second * 3)
continue
}
var respData map[string]int
if err := json.Unmarshal([]byte(resp.Data.RespData), &respData); err != nil {
return fmt.Errorf("unmarshal response failed: %w", err)
}
logger.Debugf("Jimeng AvatarRecognition Response: %+v", resp)
if respData["status"] == 1 {
return nil
} else {
return errors.New("不包含人、类人、拟人等主体")
}
}
}
// QueryTask 查询任务结果
func (c *Client) QueryTask(req *QueryTaskRequest) (*QueryTaskResponse, error) {
func (c *Client) QueryTask(req *QueryTaskRequest, action string) (*QueryTaskResponse, error) {
// 序列化请求
jsonBody, err := json.Marshal(req)
if err != nil {
@@ -130,7 +232,7 @@ func (c *Client) QueryTask(req *QueryTaskRequest) (*QueryTaskResponse, error) {
}
// 调用SDK的JSON方法
respBody, statusCode, err := c.visual.Client.Json("CVSync2AsyncGetResult", nil, string(jsonBody))
respBody, statusCode, err := c.visual.Client.Json(action, nil, string(jsonBody))
if err != nil {
return nil, fmt.Errorf("query task failed (status: %d): %w", statusCode, err)
}
@@ -143,30 +245,37 @@ func (c *Client) QueryTask(req *QueryTaskRequest) (*QueryTaskResponse, error) {
return nil, fmt.Errorf("unmarshal response failed: %w", err)
}
return &result, nil
}
// SubmitSyncTask 提交同步任务(仅用于文生图)
func (c *Client) SubmitSyncTask(req *SubmitTaskRequest) (*QueryTaskResponse, error) {
// 序列化请求
jsonBody, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("marshal request failed: %w", err)
}
// 调用SDK的JSON方法
respBody, statusCode, err := c.visual.Client.Json("CVProcess", nil, string(jsonBody))
if err != nil {
return nil, fmt.Errorf("submit sync task failed (status: %d): %w", statusCode, err)
}
logger.Infof("Jimeng SubmitSyncTask Response: %s", string(respBody))
// 解析响应,同步任务直接返回结果
var result QueryTaskResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("unmarshal response failed: %w", err)
// 检查响应错误代码
if err := HandleResponseError(result.Code, result.Message); err != nil {
return nil, err
}
return &result, nil
}
// SubmitSyncImageTask 提交同步生图任务
func (c *Client) SubmitSyncImageTask(req types.JimengTaskRequest) (*model.ImagesResponse, error) {
// 配置火山引擎访问密钥,目前只支持API Key验证
client := arkruntime.NewClientWithApiKey(c.config.ApiKey)
// 构造生图请求
sequentialImageGeneration := model.SequentialImageGeneration("disabled")
generateReq := model.GenerateImagesRequest{
Model: req.ReqKey, // 模型名称
Prompt: req.Prompt, // 提示词
Size: volcengine.String(req.Size), // 图片尺寸
SequentialImageGeneration: &sequentialImageGeneration, // 禁用序列生成
ResponseFormat: volcengine.String(model.GenerateImagesResponseFormatURL), // 响应格式为 URL
Watermark: volcengine.Bool(false), // 不添加水印
OptimizePrompt: volcengine.Bool(true), // 优化提示词
}
if len(req.ImageUrls) > 0 {
generateReq.Image = req.ImageUrls
}
// 调用生图 API
resp, err := client.GenerateImages(context.Background(), generateReq)
if err != nil {
return nil, err
}
return &resp, nil
}
+155 -213
View File
@@ -4,12 +4,14 @@ import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"gorm.io/gorm"
"geekai/core/types"
logger2 "geekai/logger"
"geekai/service"
"geekai/service/oss"
"geekai/store"
"geekai/store/model"
@@ -22,29 +24,31 @@ var logger = logger2.GetLogger()
// Service 即梦服务(合并了消费者功能)
type Service struct {
db *gorm.DB
redis *redis.Client
taskQueue *store.RedisQueue
client *Client
ctx context.Context
cancel context.CancelFunc
running bool
uploader *oss.UploaderManager
db *gorm.DB
redis *redis.Client
taskQueue *store.RedisQueue
client *Client
ctx context.Context
cancel context.CancelFunc
running bool
uploader *oss.UploaderManager
userService *service.UserService
}
// NewService 创建即梦服务
func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client) *Service {
func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client, userService *service.UserService) *Service {
taskQueue := store.NewRedisQueue("JimengTaskQueue", redisCli)
ctx, cancel := context.WithCancel(context.Background())
return &Service{
db: db,
redis: redisCli,
taskQueue: taskQueue,
client: client,
ctx: ctx,
cancel: cancel,
running: false,
uploader: uploader,
db: db,
redis: redisCli,
taskQueue: taskQueue,
client: client,
ctx: ctx,
cancel: cancel,
running: false,
uploader: uploader,
userService: userService,
}
}
@@ -95,35 +99,29 @@ func (s *Service) processNextTask() {
if err := s.ProcessTask(jobId); err != nil {
logger.Errorf("process jimeng task failed: job_id=%d, error=%v", jobId, err)
s.UpdateJobStatus(jobId, model.JMTaskStatusFailed, err.Error())
s.UpdateJobStatus(jobId, types.JMTaskStatusFailed, err.Error())
} else {
logger.Infof("Jimeng task processed successfully: job_id=%d", jobId)
}
}
// CreateTask 创建任务
func (s *Service) CreateTask(userId uint, req *CreateTaskRequest) (*model.JimengJob, error) {
func (s *Service) CreateTask(userId uint, req *types.JimengTaskRequest) (*model.JimengJob, error) {
// 生成任务ID
taskId := utils.RandString(20)
// 序列化任务参数
paramsJson, err := json.Marshal(req.Params)
if err != nil {
return nil, fmt.Errorf("marshal task params failed: %w", err)
}
// 创建任务记录
job := &model.JimengJob{
UserId: userId,
TaskId: taskId,
Type: req.Type,
ReqKey: req.ReqKey,
Prompt: req.Prompt,
TaskParams: string(paramsJson),
Status: model.JMTaskStatusInQueue,
Power: req.Power,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
UserId: userId,
TaskId: taskId,
Type: req.TaskType,
ReqKey: req.ReqKey,
Prompt: req.Prompt,
Params: utils.JsonEncode(req),
Status: types.JMTaskStatusSubmited,
Power: req.Power,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 保存到数据库
@@ -148,25 +146,71 @@ func (s *Service) ProcessTask(jobId uint) error {
}
// 更新任务状态为处理中
if err := s.UpdateJobStatus(job.Id, model.JMTaskStatusGenerating, ""); err != nil {
if err := s.UpdateJobStatus(job.Id, types.JMTaskStatusGenerating, ""); err != nil {
return fmt.Errorf("update job status failed: %w", err)
}
// 解析任务参数
var req types.JimengTaskRequest
err := utils.JsonDecode(job.Params, &req)
if err != nil {
return fmt.Errorf("parse task params failed: %w", err)
}
// 构建请求并提交任务
req, err := s.buildTaskRequest(&job)
params, err := s.buildTaskRequest(&req)
if err != nil {
return s.handleTaskError(job.Id, fmt.Sprintf("build task request failed: %v", err))
}
logger.Infof("提交即梦任务: %+v", req)
// 数字人任务,先识别主体
if req.TaskType == types.JMTaskTypeVirtualHuman {
if err := s.client.AvatarRecognition(req.ImageUrls[0], req.RecognizeKey); err != nil {
return s.handleTaskError(job.Id, fmt.Sprintf("avatar recognition failed: %v", err))
}
}
// 提交异步任务
resp, err := s.client.SubmitTask(req)
// 同步任务 ,后台执行
if req.ReqKey == DoubaoSeedream40ReqKey {
go func() {
resp, err := s.client.SubmitSyncImageTask(req)
if err != nil {
_ = s.handleTaskError(job.Id, fmt.Sprintf("submit task failed: %v", err))
return
}
logger.Infof("同步任务提交成功: %+v", resp)
// 更新原始数据
rawData, _ := json.Marshal(resp)
updates := map[string]any{
"raw_data": string(rawData),
}
if resp.Error != nil {
updates["status"] = types.JMTaskStatusFailed
updates["err_msg"] = resp.Error.Message
s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Updates(updates)
return
}
// 更新任务状态
updates["status"] = types.JMTaskStatusSuccess
// 下载图片
imgUrl, err := s.uploader.GetUploadHandler().PutUrlFile(*resp.Data[0].Url, ".png", false)
if err == nil {
updates["img_url"] = imgUrl
}
s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Updates(updates)
}()
return nil
}
logger.Debugf("提交即梦任务: %+v", params)
// 异步任务 ,前台执行
resp, err := s.client.SubmitTask(params)
if err != nil {
return s.handleTaskError(job.Id, fmt.Sprintf("submit task failed: %v", err))
}
if resp.Code != 10000 {
if resp.Code != CodeSuccess {
return s.handleTaskError(job.Id, fmt.Sprintf("submit task failed: %s", resp.Message))
}
@@ -175,6 +219,7 @@ func (s *Service) ProcessTask(jobId uint) error {
if err := s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Updates(map[string]any{
"task_id": resp.Data.TaskId,
"raw_data": string(rawData),
"status": types.JMTaskStatusInQueue, // 把任务状态改成排队中,以便开启轮询
"updated_at": time.Now(),
}).Error; err != nil {
logger.Errorf("update jimeng job task_id failed: %v", err)
@@ -184,172 +229,51 @@ func (s *Service) ProcessTask(jobId uint) error {
}
// buildTaskRequest 构建任务请求(统一的参数解析)
func (s *Service) buildTaskRequest(job *model.JimengJob) (*SubmitTaskRequest, error) {
// 解析任务参数
func (s *Service) buildTaskRequest(req *types.JimengTaskRequest) (map[string]any, error) {
var params map[string]any
if err := json.Unmarshal([]byte(job.TaskParams), &params); err != nil {
err := utils.JsonDecode(utils.JsonEncode(req), &params)
if err != nil {
return nil, fmt.Errorf("parse task params failed: %w", err)
}
// 构建基础请求
req := &SubmitTaskRequest{
ReqKey: job.ReqKey,
Prompt: job.Prompt,
}
// 根据任务类型设置特定参数
switch job.Type {
case model.JMTaskTypeTextToImage:
s.setTextToImageParams(req, params)
case model.JMTaskTypeImageToImage:
s.setImageToImageParams(req, params)
case model.JMTaskTypeImageEdit:
s.setImageEditParams(req, params)
case model.JMTaskTypeImageEffects:
s.setImageEffectsParams(req, params)
case model.JMTaskTypeTextToVideo:
s.setTextToVideoParams(req, params)
case model.JMTaskTypeImageToVideo:
s.setImageToVideoParams(req, params)
default:
return nil, fmt.Errorf("unsupported task type: %s", job.Type)
}
return req, nil
}
// setTextToImageParams 设置文生图参数
func (s *Service) setTextToImageParams(req *SubmitTaskRequest, params map[string]any) {
if seed, ok := params["seed"]; ok {
if seedVal, err := strconv.ParseInt(fmt.Sprintf("%.0f", seed), 10, 64); err == nil {
req.Seed = seedVal
}
}
if scale, ok := params["scale"]; ok {
if scaleVal, ok := scale.(float64); ok {
req.Scale = scaleVal
}
}
if width, ok := params["width"]; ok {
if widthVal, ok := width.(float64); ok {
req.Width = int(widthVal)
}
}
if height, ok := params["height"]; ok {
if heightVal, ok := height.(float64); ok {
req.Height = int(heightVal)
}
}
if usePreLlm, ok := params["use_pre_llm"]; ok {
if usePreLlmVal, ok := usePreLlm.(bool); ok {
req.UsePreLLM = usePreLlmVal
}
}
}
// setImageToImageParams 设置图生图参数
func (s *Service) setImageToImageParams(req *SubmitTaskRequest, params map[string]any) {
if imageInput, ok := params["image_input"].(string); ok {
req.ImageInput = imageInput
}
if gpen, ok := params["gpen"]; ok {
if gpenVal, ok := gpen.(float64); ok {
req.Gpen = gpenVal
}
}
if skin, ok := params["skin"]; ok {
if skinVal, ok := skin.(float64); ok {
req.Skin = skinVal
}
}
if skinUnifi, ok := params["skin_unifi"]; ok {
if skinUnifiVal, ok := skinUnifi.(float64); ok {
req.SkinUnifi = skinUnifiVal
}
}
if genMode, ok := params["gen_mode"].(string); ok {
req.GenMode = genMode
}
s.setCommonParams(req, params) // 复用通用参数
}
// setImageEditParams 设置图像编辑参数
func (s *Service) setImageEditParams(req *SubmitTaskRequest, params map[string]any) {
if imageUrls, ok := params["image_urls"].([]any); ok {
for _, url := range imageUrls {
if urlStr, ok := url.(string); ok {
req.ImageUrls = append(req.ImageUrls, urlStr)
// 把 size 转成 width 和 height
if size, ok := params["size"]; ok {
if sizeStr, ok := size.(string); ok {
if strings.Contains(sizeStr, "x") {
sizes := strings.Split(sizeStr, "x")
params["width"] = sizes[0]
params["height"] = sizes[1]
}
}
delete(params, "size")
}
if binaryData, ok := params["binary_data_base64"].([]any); ok {
for _, data := range binaryData {
if dataStr, ok := data.(string); ok {
req.BinaryDataBase64 = append(req.BinaryDataBase64, dataStr)
}
}
}
if scale, ok := params["scale"]; ok {
if scaleVal, ok := scale.(float64); ok {
req.Scale = scaleVal
}
}
s.setCommonParams(req, params)
}
// setImageEffectsParams 设置图像特效参数
func (s *Service) setImageEffectsParams(req *SubmitTaskRequest, params map[string]any) {
if imageInput1, ok := params["image_input1"].(string); ok {
req.ImageInput1 = imageInput1
}
if templateId, ok := params["template_id"].(string); ok {
req.TemplateId = templateId
}
if width, ok := params["width"]; ok {
if widthVal, ok := width.(float64); ok {
req.Width = int(widthVal)
// duration 转成 frames
if duration, ok := params["duration"]; ok {
if secs, ok := duration.(int); ok {
params["frames"] = secs*24 + 1
}
delete(params, "duration")
}
if height, ok := params["height"]; ok {
if heightVal, ok := height.(float64); ok {
req.Height = int(heightVal)
}
}
}
// setTextToVideoParams 设置文生视频参数
func (s *Service) setTextToVideoParams(req *SubmitTaskRequest, params map[string]any) {
if aspectRatio, ok := params["aspect_ratio"].(string); ok {
req.AspectRatio = aspectRatio
// 单独处理图片特效任务
if req.ReqKey == ImageEffectReqKey {
params["image_input1"] = req.ImageUrls[0]
delete(params, "image_urls")
}
s.setCommonParams(req, params)
}
// setImageToVideoParams 设置图生视频参数
func (s *Service) setImageToVideoParams(req *SubmitTaskRequest, params map[string]any) {
s.setImageEditParams(req, params) // 复用图像编辑的参数设置
if aspectRatio, ok := params["aspect_ratio"].(string); ok {
req.AspectRatio = aspectRatio
// 动作迁移,数字人任务参数处理
if req.TaskType == types.JMTaskTypeVirtualHuman || req.TaskType == types.JMTaskTypeActionTransfer {
params["image_url"] = req.ImageUrls[0]
delete(params, "image_urls")
}
if req.RecognizeKey != "" {
delete(params, "recognize_key")
}
}
// setCommonParams 设置通用参数(seed, width, height等)
func (s *Service) setCommonParams(req *SubmitTaskRequest, params map[string]any) {
if seed, ok := params["seed"]; ok {
if seedVal, err := strconv.ParseInt(fmt.Sprintf("%.0f", seed), 10, 64); err == nil {
req.Seed = seedVal
}
}
if width, ok := params["width"]; ok {
if widthVal, ok := width.(float64); ok {
req.Width = int(widthVal)
}
}
if height, ok := params["height"]; ok {
if heightVal, ok := height.(float64); ok {
req.Height = int(heightVal)
}
}
// 删除多余参数,剩下的就是各个任务自己专有参数了
delete(params, "type")
delete(params, "power")
return params, nil
}
// pollTaskStatus 轮询任务状态
@@ -357,12 +281,10 @@ func (s *Service) pollTaskStatus() {
for {
var jobs []model.JimengJob
s.db.Where("status IN (?)", []model.JMTaskStatus{model.JMTaskStatusGenerating, model.JMTaskStatusInQueue}).Find(&jobs)
if len(jobs) == 0 {
logger.Debugf("no jimeng task to poll, sleep 10s")
time.Sleep(10 * time.Second)
continue
}
// 找出排队中和处理中的任务进行轮询
s.db.Where("status IN (?)", []types.JMTaskStatus{
types.JMTaskStatusGenerating,
types.JMTaskStatusInQueue}).Find(&jobs)
for _, job := range jobs {
// 任务超时处理
@@ -371,12 +293,17 @@ func (s *Service) pollTaskStatus() {
continue
}
// 豆包生图 4.0 是同步任务,不需要轮询
if job.ReqKey == DoubaoSeedream40ReqKey {
continue
}
// 查询任务状态
resp, err := s.client.QueryTask(&QueryTaskRequest{
ReqKey: job.ReqKey,
TaskId: job.TaskId,
ReqJson: `{"return_url":true}`,
})
}, ASyncActionGetResult)
if err != nil {
s.handleTaskError(job.Id, fmt.Sprintf("query task failed: %s", err.Error()))
@@ -387,13 +314,13 @@ func (s *Service) pollTaskStatus() {
rawData, _ := json.Marshal(resp)
s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Update("raw_data", string(rawData))
if resp.Code != 10000 {
if resp.Code != CodeSuccess {
s.handleTaskError(job.Id, fmt.Sprintf("query task failed: %s", resp.Message))
continue
}
switch resp.Data.Status {
case model.JMTaskStatusDone:
case types.JMTaskStatusDone:
// 判断任务是否成功
if resp.Message != "Success" {
s.handleTaskError(job.Id, fmt.Sprintf("task failed: %s", resp.Data.AlgorithmBaseResp.StatusMessage))
@@ -402,7 +329,7 @@ func (s *Service) pollTaskStatus() {
// 任务完成,更新结果
updates := map[string]any{
"status": model.JMTaskStatusSuccess,
"status": types.JMTaskStatusSuccess,
"updated_at": time.Now(),
}
@@ -425,15 +352,15 @@ func (s *Service) pollTaskStatus() {
}
s.db.Model(&model.JimengJob{}).Where("id = ?", job.Id).Updates(updates)
case model.JMTaskStatusInQueue, model.JMTaskStatusGenerating:
case types.JMTaskStatusInQueue, types.JMTaskStatusGenerating:
// 任务处理中
s.UpdateJobStatus(job.Id, model.JMTaskStatusGenerating, "")
s.UpdateJobStatus(job.Id, types.JMTaskStatusGenerating, "")
case model.JMTaskStatusNotFound:
case types.JMTaskStatusNotFound:
// 任务未找到
s.handleTaskError(job.Id, "task not found")
case model.JMTaskStatusExpired:
case types.JMTaskStatusExpired:
continue
default:
logger.Warnf("unknown task status: %s", resp.Data.Status)
@@ -441,6 +368,21 @@ func (s *Service) pollTaskStatus() {
}
// 找出失败的任务,并恢复其扣减算力
s.db.Where("status = ?", types.JMTaskStatusFailed).Where("power > ?", 0).Find(&jobs)
for _, job := range jobs {
err := s.userService.IncreasePower(job.UserId, job.Power, model.PowerLog{
Type: types.PowerRefund,
Model: job.ReqKey,
Remark: fmt.Sprintf("任务失败,退回算力。任务ID%d", job.Id),
})
if err != nil {
continue
}
// 更新任务状态
s.db.Model(&job).UpdateColumn("power", 0)
}
time.Sleep(5 * time.Second)
}
@@ -448,7 +390,7 @@ func (s *Service) pollTaskStatus() {
}
// UpdateJobStatus 更新任务状态
func (s *Service) UpdateJobStatus(jobId uint, status model.JMTaskStatus, errMsg string) error {
func (s *Service) UpdateJobStatus(jobId uint, status types.JMTaskStatus, errMsg string) error {
updates := map[string]any{
"status": status,
"updated_at": time.Now(),
@@ -462,7 +404,7 @@ func (s *Service) UpdateJobStatus(jobId uint, status model.JMTaskStatus, errMsg
// handleTaskError 处理任务错误
func (s *Service) handleTaskError(jobId uint, errMsg string) error {
logger.Errorf("Jimeng task error (job_id: %d): %s", jobId, errMsg)
return s.UpdateJobStatus(jobId, model.JMTaskStatusFailed, errMsg)
return s.UpdateJobStatus(jobId, types.JMTaskStatusFailed, errMsg)
}
// PushTaskToQueue 推送任务到队列(用于手动重试)
@@ -473,8 +415,8 @@ func (s *Service) PushTaskToQueue(jobId uint) error {
// GetTaskStats 获取任务统计信息
func (s *Service) GetTaskStats() (map[string]any, error) {
type StatResult struct {
Status string `json:"status"`
Count int64 `json:"count"`
Status types.JMTaskStatus `json:"status"`
Count int64 `json:"count"`
}
var stats []StatResult
@@ -496,7 +438,7 @@ func (s *Service) GetTaskStats() (map[string]any, error) {
for _, stat := range stats {
result["total"] = result["total"].(int64) + stat.Count
result[stat.Status] = stat.Count
result[string(stat.Status)] = stat.Count
}
return result, nil
+74 -83
View File
@@ -1,43 +1,9 @@
package jimeng
import "geekai/store/model"
// ReqKey 常量定义
const (
ReqKeyTextToImage = "high_aes_general_v30l_zt2i" // 文生图
ReqKeyImageToImagePortrait = "i2i_portrait_photo" // 图生图人像写真
ReqKeyImageEdit = "seededit_v3.0" // 图像编辑
ReqKeyImageEffects = "i2i_multi_style_zx2x" // 图像特效
ReqKeyTextToVideo = "jimeng_vgfm_t2v_l20" // 文生视频
ReqKeyImageToVideo = "jimeng_vgfm_i2v_l20" // 图生视频
import (
"geekai/core/types"
)
// SubmitTaskRequest 提交任务请求
type SubmitTaskRequest struct {
ReqKey string `json:"req_key"`
// 文生图参数
Prompt string `json:"prompt,omitempty"`
Seed int64 `json:"seed,omitempty"`
Scale float64 `json:"scale,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
UsePreLLM bool `json:"use_pre_llm,omitempty"`
// 图生图参数
ImageInput string `json:"image_input,omitempty"`
ImageUrls []string `json:"image_urls,omitempty"`
BinaryDataBase64 []string `json:"binary_data_base64,omitempty"`
Gpen float64 `json:"gpen,omitempty"`
Skin float64 `json:"skin,omitempty"`
SkinUnifi float64 `json:"skin_unifi,omitempty"`
GenMode string `json:"gen_mode,omitempty"`
// 图像编辑参数
// 图像特效参数
ImageInput1 string `json:"image_input1,omitempty"`
TemplateId string `json:"template_id,omitempty"`
// 视频生成参数
AspectRatio string `json:"aspect_ratio,omitempty"`
}
// SubmitTaskResponse 提交任务响应
type SubmitTaskResponse struct {
Code int `json:"code"`
@@ -73,7 +39,7 @@ type QueryTaskResponse struct {
ImageUrls []string `json:"image_urls"`
VideoUrl string `json:"video_url"`
RespData string `json:"resp_data"`
Status model.JMTaskStatus `json:"status"`
Status types.JMTaskStatus `json:"status"`
LlmResult string `json:"llm_result"`
PeResult string `json:"pe_result"`
PredictTagsResult string `json:"predict_tags_result"`
@@ -83,9 +49,73 @@ type QueryTaskResponse struct {
} `json:"data"`
}
const CodeSuccess = 10000
// 即梦AI错误代码常量
const (
// 成功
ECSuccess = 10000
// 请求参数错误 (50200-50215)
ECReqInvalidArgs = 50200 // 参数错误
ECReqMissingArgs = 50201 // 缺少参数
ECParseArgs = 50204 // 参数类型错误/参数缺失
ECImageSizeLimited = 50205 // 图像尺寸超过限制
ECImageEmpty = 50206 // 请求参数中没有获取到图像
ECImageDecodeError = 50207 // 图像解码错误
ECVideoEmpty = 50209 // 请求参数中没有获取到视频
ECVideoDecodeError = 50210 // 视频解码错误
ECVideoSizeLimited = 50211 // 视频尺寸超过限制
ECReqBodySizeLimited = 50213 // 请求Body过大
ECVideoTimeTooLong = 50214 // 输入视频时长过大
ECRPCProcess = 50215 // 请求处理失败
// 算法服务错误 (60102-60208)
ECJPFaceDetect = 60102 // 算法服务需要输入人脸图,但未检测到
ECFSLeaderRiskError = 60208 // 输入图片中包含敏感信息,未通过审核
// 权限和系统错误 (50400-50501)
ECAuth = 50400 // 权限校验失败
ECReqMethod = 50402 // 访问的接口不存在
ECReqLimit = 50429 // 超过调用QPS限制
ECInternal = 50500 // 服务器内部错误
ECRPCInternal = 50501 // 服务器内部RPC错误
)
// 错误代码到错误信息的映射
var errorCodeMessages = map[int]string{
// 成功
ECSuccess: "请求成功",
// 请求参数错误
ECReqInvalidArgs: "参数错误,检查入参及MIME类型",
ECReqMissingArgs: "缺少参数,检查入参及MIME类型",
ECParseArgs: "参数类型错误/参数缺失,检查入参及MIME类型",
ECImageSizeLimited: "图像尺寸超过限制,参考接口文档入参要求部分",
ECImageEmpty: "请求参数中没有获取到图像,检查入参",
ECImageDecodeError: "图像解码错误:没有获取到图像或者通过image_base64参数传递图像是base64解码错误,检查输出图片或检查base64是否错误携带前缀",
ECVideoEmpty: "请求参数中没有获取到视频。输入为视频时可能返回此错误,检查入参",
ECVideoDecodeError: "视频解码错误。输入为视频时可能返回此错误,检查输入视频是否不正确",
ECVideoSizeLimited: "视频尺寸超过限制。输入为视频时可能返回此错误,检查输入视频大小",
ECReqBodySizeLimited: "请求Body过大,超出接口限制,检查请求Body大小",
ECVideoTimeTooLong: "输入视频时长过大,检查输入视频时长",
ECRPCProcess: "由于输入的图片、视频、参数等不满足要求,导致请求处理失败。若接口文档中有具体说明,优先参考其具体含义,按照具体服务说明进行检查",
// 算法服务错误
ECJPFaceDetect: "算法服务需要输入人脸图,但未检测到,检查输入图片是否包含人脸",
ECFSLeaderRiskError: "输入图片中包含敏感信息,未通过审核",
// 权限和系统错误
ECAuth: "权限校验失败,请检查是否已创建应用并开通服务或签名,参考接入指南及快速接入",
ECReqMethod: "访问的接口不存在,检查入参",
ECReqLimit: "超过调用QPS限制,购买QPS增项包",
ECInternal: "服务器内部错误,提工单",
ECRPCInternal: "服务器内部RPC错误,提工单",
}
// CreateTaskRequest 创建任务请求
type CreateTaskRequest struct {
Type model.JMTaskType `json:"type"`
Type types.JMTaskType `json:"type"`
Prompt string `json:"prompt"`
Params map[string]any `json:"params"`
ReqKey string `json:"req_key"`
@@ -93,53 +123,14 @@ type CreateTaskRequest struct {
Power int `json:"power,omitempty"`
}
// LogoInfo 水印信息
type LogoInfo struct {
AddLogo bool `json:"add_logo"`
Position int `json:"position"`
Language int `json:"language"`
Opacity float64 `json:"opacity"`
LogoTextContent string `json:"logo_text_content"`
}
// ReqJsonConfig 查询配置
type ReqJsonConfig struct {
ReturnUrl bool `json:"return_url"`
LogoInfo *LogoInfo `json:"logo_info,omitempty"`
}
// ImageEffectTemplate 图像特效模板
const (
TemplateIdFelt3DPolaroid = "felt_3d_polaroid" // 毛毡3d拍立得风格
TemplateIdMyWorld = "my_world" // 像素世界风
TemplateIdMyWorldUniversal = "my_world_universal" // 像素世界-万物通用版
TemplateIdPlasticBubbleFigure = "plastic_bubble_figure" // 盲盒玩偶风
TemplateIdPlasticBubbleFigureCartoon = "plastic_bubble_figure_cartoon_text" // 塑料泡罩人偶-文字卡头版
TemplateIdFurryDreamDoll = "furry_dream_doll" // 毛绒玩偶风
TemplateIdMicroLandscapeMiniWorld = "micro_landscape_mini_world" // 迷你世界玩偶风
TemplateIdMicroLandscapeProfessional = "micro_landscape_mini_world_professional" // 微型景观小世界-职业版
TemplateIdAcrylicOrnaments = "acrylic_ornaments" // 亚克力挂饰
TemplateIdFeltKeychain = "felt_keychain" // 毛毡钥匙扣
TemplateIdLofiPixelCharacter = "lofi_pixel_character_mini_card" // Lofi像素人物小卡
TemplateIdAngelFigurine = "angel_figurine" // 天使形象手办
TemplateIdLyingInFluffyBelly = "lying_in_fluffy_belly" // 躺在毛茸茸肚皮里
TemplateIdGlassBall = "glass_ball" // 玻璃球
ImageEffectReqKey = "i2i_multi_style_zx2x"
DoubaoSeedream40ReqKey = "doubao-seedream-4-0-250828"
)
// AspectRatio 视频宽高比
const (
AspectRatio16_9 = "16:9" // 1280×720
AspectRatio9_16 = "9:16" // 720×1280
AspectRatio1_1 = "1:1" // 960×960
AspectRatio4_3 = "4:3" // 960×720
AspectRatio3_4 = "3:4" // 720×960
AspectRatio21_9 = "21:9" // 1680×720
AspectRatio9_21 = "9:21" // 720×1680
)
// GenMode 生成模式
const (
GenModeCreative = "creative" // 提示词模式
GenModeReference = "reference" // 全参考模式
GenModeReferenceChar = "reference_char" // 人物参考模式
ASyncActionSubmit = "CVSync2AsyncSubmitTask" // 异步提交任务
SyncActionSubmit = "CVSubmitTask" // 同步提交任务
ASyncActionGetResult = "CVSync2AsyncGetResult" // 异步获取结果
SyncActionGetResult = "CVGetResult" // 同步获取结果
)
-224
View File
@@ -1,224 +0,0 @@
package service
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// * 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 (
"errors"
"fmt"
"geekai/core/types"
"geekai/store/model"
"geekai/utils"
"strings"
"time"
"github.com/imroc/req/v3"
"github.com/shirou/gopsutil/host"
"gorm.io/gorm"
)
type LicenseService struct {
license *types.License
urlWhiteList []string
machineId string
db *gorm.DB
}
func NewLicenseService(sysConfig *types.SystemConfig, db *gorm.DB) *LicenseService {
var machineId string
info, err := host.Info()
if err == nil {
machineId = info.HostID
}
logger.Infof("License: %+v", sysConfig.License)
return &LicenseService{
license: &sysConfig.License,
machineId: machineId,
db: db,
}
}
type License struct {
Name string `json:"name"`
License string `json:"license"`
MachineId string `json:"mid"`
ActiveAt int64 `json:"active_at"`
ExpiredAt int64 `json:"expired_at"`
UserNum int `json:"user_num"`
Configs types.LicenseConfig `json:"configs"`
}
// ActiveLicense 激活 License
func (s *LicenseService) ActiveLicense(license string) error {
var res struct {
Code types.BizCode `json:"code"`
Message string `json:"message"`
Data License `json:"data"`
}
apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/active")
response, err := req.C().R().
SetBody(map[string]string{"license": license, "machine_id": s.machineId}).
SetSuccessResult(&res).Post(apiURL)
if err != nil {
return fmt.Errorf("发送激活请求失败: %v", err)
}
if response.IsErrorState() {
return fmt.Errorf("发送激活请求失败:%v", response.Status)
}
if res.Code != types.Success {
return fmt.Errorf("激活失败:%v", res.Message)
}
if res.Data.ExpiredAt > 0 && res.Data.ExpiredAt < time.Now().Unix() {
return fmt.Errorf("License 已过期")
}
s.license = &types.License{
Key: license,
MachineId: s.machineId,
Configs: res.Data.Configs,
ExpiredAt: res.Data.ExpiredAt,
IsActive: true,
}
// 保存 License 到数据库
err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error
if err != nil {
return fmt.Errorf("保存 License 到数据库失败: %v", err)
}
return nil
}
// SyncLicense 定期同步 License
func (s *LicenseService) SyncLicense() {
go func() {
retryCounter := 0
for {
license, err := s.fetchLicense()
if err != nil {
retryCounter++
if retryCounter < 5 {
logger.Debug(err)
}
s.license.IsActive = false
} else {
s.license = license
// 保存 License 到数据库
err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error
if err != nil {
logger.Errorf("保存 License 到数据库失败: %v", err)
}
}
urls, err := s.fetchUrlWhiteList()
if err == nil {
s.urlWhiteList = urls
}
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", types.GeekAPIURL, "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("License 同步失败: %v", err)
}
if response.IsErrorState() {
return nil, fmt.Errorf("License 同步失败:%v", response.Status)
}
if res.Code != types.Success {
return nil, fmt.Errorf("License 同步失败:%v", res.Message)
}
return &types.License{
Key: res.Data.License,
MachineId: res.Data.MachineId,
Configs: res.Data.Configs,
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", types.GeekAPIURL, "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 {
if s.license == nil {
var config model.Config
s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).First(&config)
if config.Value != "" {
utils.JsonDecode(config.Value, &s.license)
}
}
return s.license
}
func (s *LicenseService) SetLicense(licenseKey string) {
s.license.Key = licenseKey
}
// IsValidApiURL 判断是否合法的中转 URL
func (s *LicenseService) IsValidApiURL(uri string) error {
// 获得许可授权的直接放行
if s.license.IsActive {
if s.license.MachineId != s.machineId {
return errors.New("系统使用了盗版的许可证书")
}
if time.Now().Unix() > s.license.ExpiredAt {
return errors.New("系统许可证书已经过期")
}
return nil
}
if len(s.urlWhiteList) == 0 {
urls, err := s.fetchUrlWhiteList()
if err == nil {
s.urlWhiteList = urls
}
}
for _, v := range s.urlWhiteList {
if strings.HasPrefix(uri, v) {
return nil
}
}
return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri)
}
+167 -101
View File
@@ -8,16 +8,17 @@ package service
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import (
"context"
"encoding/json"
"fmt"
"geekai/core/types"
"geekai/store"
"geekai/store/model"
"strings"
"sync"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
const (
@@ -29,61 +30,29 @@ const (
// MigrationService 配置迁移服务
type MigrationService struct {
db *gorm.DB
redisClient *redis.Client
appConfig *types.AppConfig
levelDB *store.LevelDB
licenseService *LicenseService
db *gorm.DB
redisClient *redis.Client
appConfig *types.AppConfig
levelDB *store.LevelDB
}
func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB, licenseService *LicenseService) *MigrationService {
func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB) *MigrationService {
return &MigrationService{
db: db,
redisClient: redisClient,
appConfig: appConfig,
levelDB: levelDB,
licenseService: licenseService,
db: db,
redisClient: redisClient,
appConfig: appConfig,
levelDB: levelDB,
}
}
func (s *MigrationService) StartMigrate() {
// 表结构同步必须在对外服务前完成,避免缺列导致业务报错
s.TableMigration()
go func() {
s.MigrateConfig(s.appConfig)
s.TableMigration()
s.MigrateLicense()
_ = s.MigrateConfig(s.appConfig)
}()
}
// 迁移 License
func (s *MigrationService) MigrateLicense() {
key := "migrate:license"
if s.redisClient.Get(context.Background(), key).Val() == "1" {
logger.Info("License 已迁移,跳过迁移")
return
}
logger.Info("开始迁移 License...")
var license types.License
err := s.levelDB.Get(types.LicenseKey, &license)
if err != nil {
license = types.License{
Key: "",
MachineId: "",
Configs: types.LicenseConfig{UserNum: 0, DeCopy: false},
ExpiredAt: 0,
IsActive: false,
}
}
logger.Infof("迁移 License: %+v", license)
if err := s.saveConfig(types.ConfigKeyLicense, license); err != nil {
logger.Errorf("迁移 License 失败: %v", err)
return
}
s.licenseService.SetLicense(license.Key)
logger.Info("迁移 License 完成")
s.redisClient.Set(context.Background(), key, "1", 0)
}
// 迁移配置内容
func (s *MigrationService) MigrateConfigContent() error {
// 用户协议
@@ -157,66 +126,163 @@ func (s *MigrationService) MigrateConfigContent() error {
return nil
}
// 数据表迁移
func (s *MigrationService) TableMigration() {
// 新数据表
s.db.AutoMigrate(&model.Moderation{})
// 永不删除的保护列(大小写不敏感)
var protectedColumns = map[string]struct{}{
"id": {},
"created_at": {},
"updated_at": {},
}
// 订单字段整理
if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") {
s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel")
}
if !s.db.Migrator().HasColumn(&model.Order{}, "checked") {
s.db.Migrator().AddColumn(&model.Order{}, "checked")
}
// 重命名 config 表字段
if s.db.Migrator().HasColumn(&model.Config{}, "config_json") {
s.db.Migrator().RenameColumn(&model.Config{}, "config_json", "value")
}
if s.db.Migrator().HasColumn(&model.Config{}, "marker") {
s.db.Migrator().RenameColumn(&model.Config{}, "marker", "name")
}
if s.db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
s.db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
}
if s.db.Migrator().HasIndex(&model.Config{}, "marker") {
s.db.Migrator().DropIndex(&model.Config{}, "marker")
}
// 手动删除字段
if s.db.Migrator().HasColumn(&model.Order{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.Order{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") {
s.db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at")
}
if s.db.Migrator().HasColumn(&model.User{}, "chat_config") {
s.db.Migrator().DropColumn(&model.User{}, "chat_config")
}
if s.db.Migrator().HasColumn(&model.ChatModel{}, "category") {
s.db.Migrator().DropColumn(&model.ChatModel{}, "category")
}
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
}
if s.db.Migrator().HasColumn(&model.Product{}, "discount") {
s.db.Migrator().DropColumn(&model.Product{}, "discount")
}
if s.db.Migrator().HasColumn(&model.Product{}, "days") {
s.db.Migrator().DropColumn(&model.Product{}, "days")
}
if s.db.Migrator().HasColumn(&model.Product{}, "app_url") {
s.db.Migrator().DropColumn(&model.Product{}, "app_url")
}
if s.db.Migrator().HasColumn(&model.Product{}, "url") {
s.db.Migrator().DropColumn(&model.Product{}, "url")
// allModels 全部需要同步的数据表 model
func allModels() []any {
return []any{
&model.AdminUser{},
&model.ApiKey{},
&model.AppType{},
&model.ChatApp{},
&model.ChatItem{},
&model.ChatMessage{},
&model.ChatModel{},
&model.Config{},
&model.DallJob{},
&model.File{},
&model.Function{},
&model.InviteCode{},
&model.InviteLog{},
&model.JimengJob{},
&model.Menu{},
&model.MidJourneyJob{},
&model.Moderation{},
&model.Order{},
&model.PowerLog{},
&model.Product{},
&model.Redeem{},
&model.SdJob{},
&model.SunoJob{},
&model.User{},
&model.UserLoginLog{},
&model.VideoJob{},
}
}
// 数据表迁移:先处理字段重命名(保数据),再全量同步 schema
func (s *MigrationService) TableMigration() {
logger.Info("开始数据表迁移...")
s.renameColumns()
if err := s.SyncAllModels(); err != nil {
logger.Errorf("同步数据表字段失败: %v", err)
return
}
logger.Info("数据表迁移完成")
}
// renameColumns 只处理「改名」场景:删旧加新会丢数据,必须先 Rename
func (s *MigrationService) renameColumns() {
m := s.db.Migrator()
if m.HasColumn(&model.JimengJob{}, "task_params") {
_ = m.RenameColumn(&model.JimengJob{}, "task_params", "params")
}
if m.HasColumn(&model.Order{}, "pay_type") {
_ = m.RenameColumn(&model.Order{}, "pay_type", "channel")
}
if m.HasColumn(&model.Config{}, "config_json") {
_ = m.RenameColumn(&model.Config{}, "config_json", "value")
}
if m.HasColumn(&model.Config{}, "marker") {
_ = m.RenameColumn(&model.Config{}, "marker", "name")
}
if m.HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
_ = m.DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
}
if m.HasIndex(&model.Config{}, "marker") {
_ = m.DropIndex(&model.Config{}, "marker")
}
}
// SyncAllModels 按 model 定义同步所有数据表:缺列新建,多余列删除
func (s *MigrationService) SyncAllModels() error {
var firstErr error
for _, m := range allModels() {
if err := s.syncModel(m); err != nil {
logger.Errorf("同步 model %T 失败: %v", m, err)
if firstErr == nil {
firstErr = err
}
}
}
return firstErr
}
func (s *MigrationService) syncModel(dst any) error {
tableName := s.tableName(dst)
if err := s.db.AutoMigrate(dst); err != nil {
return fmt.Errorf("AutoMigrate %s: %w", tableName, err)
}
if err := s.dropUnusedColumns(dst); err != nil {
return fmt.Errorf("drop unused columns %s: %w", tableName, err)
}
logger.Infof("已同步数据表: %s", tableName)
return nil
}
func (s *MigrationService) dropUnusedColumns(dst any) error {
dbCols, err := s.db.Migrator().ColumnTypes(dst)
if err != nil {
return err
}
modelCols, err := s.modelColumnNames(dst)
if err != nil {
return err
}
for _, col := range dbCols {
name := col.Name()
if s.isProtectedColumn(name) {
continue
}
if _, ok := modelCols[strings.ToLower(name)]; ok {
continue
}
logger.Infof("删除多余字段: %s.%s", s.tableName(dst), name)
if err := s.db.Migrator().DropColumn(dst, name); err != nil {
return fmt.Errorf("DropColumn %s: %w", name, err)
}
}
return nil
}
func (s *MigrationService) modelColumnNames(dst any) (map[string]struct{}, error) {
parsed, err := schema.Parse(dst, &schemaCache, s.db.Config.NamingStrategy)
if err != nil {
return nil, err
}
cols := make(map[string]struct{}, len(parsed.Fields))
for _, field := range parsed.Fields {
if field.DBName == "" || field.IgnoreMigration {
continue
}
cols[strings.ToLower(field.DBName)] = struct{}{}
}
return cols, nil
}
func (s *MigrationService) isProtectedColumn(name string) bool {
_, ok := protectedColumns[strings.ToLower(name)]
return ok
}
func (s *MigrationService) tableName(dst any) string {
stmt := &gorm.Statement{DB: s.db}
if err := stmt.Parse(dst); err != nil {
return fmt.Sprintf("%T", dst)
}
return stmt.Schema.Table
}
// schema.Parse 进程内复用的 schema cache
var schemaCache sync.Map
// 迁移配置数据
func (s *MigrationService) MigrateConfig(config *types.AppConfig) error {
+5 -12
View File
@@ -13,7 +13,6 @@ import (
"fmt"
"geekai/core/types"
logger2 "geekai/logger"
"geekai/service"
"geekai/store/model"
"geekai/utils"
"github.com/imroc/req/v3"
@@ -26,9 +25,8 @@ import (
// Client MidJourney client
type Client struct {
client *req.Client
licenseService *service.LicenseService
db *gorm.DB
client *req.Client
db *gorm.DB
}
type ImageReq struct {
@@ -77,11 +75,10 @@ type QueryRes struct {
var logger = logger2.GetLogger()
func NewClient(licenseService *service.LicenseService, db *gorm.DB) *Client {
func NewClient(db *gorm.DB) *Client {
return &Client{
client: req.C().SetTimeout(time.Minute).SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"),
licenseService: licenseService,
db: db,
client: req.C().SetTimeout(time.Minute).SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"),
db: db,
}
}
@@ -198,10 +195,6 @@ func (c *Client) doRequest(body interface{}, apiPath string, channel string) (Im
return ImageRes{}, fmt.Errorf("no available MidJourney api key: %v", err)
}
if err = c.licenseService.IsValidApiURL(apiKey.ApiURL); err != nil {
return ImageRes{}, err
}
apiURL := fmt.Sprintf("%s/%s", apiKey.ApiURL, apiPath)
logger.Info("API URL: ", apiURL)
r, err := req.C().R().
+90
View File
@@ -0,0 +1,90 @@
package sora
import (
"encoding/json"
"errors"
"geekai/service/oss"
"geekai/store/vo"
"geekai/utils"
"io"
"net/http"
"path/filepath"
"regexp"
"time"
logger2 "geekai/logger"
)
var logger = logger2.GetLogger()
type SoraService struct {
uploadManager *oss.UploaderManager
}
func NewSoraService(uploadManager *oss.UploaderManager) *SoraService {
return &SoraService{
uploadManager: uploadManager,
}
}
// 下载视频地址
func (s *SoraService) DownloadVideoURL(text string) (*vo.File, error) {
videoDataURL, err := s.ExtractVideoURL(text)
if err != nil {
return nil, err
}
// 获取 JSON 数据
resp, err := http.Get(videoDataURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// 解析视频下载地址
var videoData map[string]any
err = json.Unmarshal(body, &videoData)
if err != nil {
return nil, err
}
if v, ok := videoData["url"].(string); ok && v != "" {
logger.Infof("try to download video: %s", v)
videoURL, err := s.uploadManager.GetUploadHandler().PutUrlFile(v, ".mp4", true)
if err != nil {
return nil, err
}
// 获取文件大小
size, _ := utils.GetFileSize(videoURL)
name := filepath.Base(videoURL)
return &vo.File{
Name: name,
ObjKey: name,
URL: videoURL,
Ext: ".mp4",
Size: size,
CreatedAt: time.Now().Unix(),
}, nil
}
return nil, errors.New("no video URL found: " + string(body))
}
// 从文本中提取视频URL
func (s *SoraService) ExtractVideoURL(text string) (string, error) {
// 提取原始 JSON 数据地址
//[原始数据](https://asyncdata.net/source/task_01k8pye324ef7t6heq6jyaxbfe) 类似这样的链接
re := regexp.MustCompile(`\[原始数据\]\((https?://.*?)\)`)
matches := re.FindAllStringSubmatch(text, 1)
if len(matches) == 0 {
return "", errors.New("no video URL found")
}
return matches[0][1], nil
}
+8 -1
View File
@@ -57,12 +57,19 @@ func (s *UserService) DecreasePower(userId uint, power int, log model.PowerLog)
defer s.lock.Unlock()
tx := s.db.Begin()
var user model.User
tx.Where("id", userId).First(&user)
if user.Power < power {
tx.Rollback()
return fmt.Errorf("用户算力不足")
}
err := tx.Model(&model.User{}).Where("id", userId).UpdateColumn("power", gorm.Expr("power - ?", power)).Error
if err != nil {
tx.Rollback()
return fmt.Errorf("扣减算力失败:%v", err)
}
var user model.User
tx.Where("id", userId).First(&user)
err = tx.Create(&model.PowerLog{
UserId: user.Id,
+2 -2
View File
@@ -39,9 +39,9 @@ const (
LoginStatusExpired = "expired" // 登录失效,需要重新登录
)
func NewWxLoginService(config types.WxLoginConfig, redisClient *redis.Client) *WxLoginService {
func NewWxLoginService(sysConfig *types.SystemConfig, redisClient *redis.Client) *WxLoginService {
return &WxLoginService{
config: config,
config: sysConfig.WxLogin,
client: req.C().SetTimeout(10 * time.Second),
redisClient: redisClient,
}
+1 -1
View File
@@ -11,7 +11,7 @@ type AdminUser struct {
Salt string `gorm:"column:salt;type:char(12);not null;comment:密码盐" json:"salt"`
Status bool `gorm:"column:status;type:tinyint(1);not null;comment:当前状态" json:"status"`
LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(32);not null;comment:最后登录 IP" json:"last_login_ip"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"`
}
+1
View File
@@ -9,6 +9,7 @@ type Function struct {
Token string `gorm:"column:token;type:varchar(255);comment:API授权token" json:"token"`
Action string `gorm:"column:action;type:varchar(255);comment:函数处理 API" json:"action"`
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启用" json:"enabled"`
Power int `gorm:"column:power;type:int(11);not null;default:0;comment:消费算力点数" json:"power"`
}
func (m *Function) TableName() string {
+17 -41
View File
@@ -1,54 +1,30 @@
package model
import (
"geekai/core/types"
"time"
)
// JimengJob 即梦AI任务模型
type JimengJob struct {
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserId uint `gorm:"column:user_id;type:int(11);not null;index;comment:用户ID" json:"user_id"`
TaskId string `gorm:"column:task_id;type:varchar(100);not null;index;comment:任务ID" json:"task_id"`
Type JMTaskType `gorm:"column:type;type:varchar(50);not null;comment:任务类型" json:"type"`
ReqKey string `gorm:"column:req_key;type:varchar(100);comment:请求Key" json:"req_key"`
Prompt string `gorm:"column:prompt;type:text;comment:提示词" json:"prompt"`
TaskParams string `gorm:"column:task_params;type:text;comment:任务参数JSON" json:"task_params"`
ImgURL string `gorm:"column:img_url;type:varchar(1024);comment:图片或封面URL" json:"img_url"`
VideoURL string `gorm:"column:video_url;type:varchar(1024);comment:视频URL" json:"video_url"`
RawData string `gorm:"column:raw_data;type:text;comment:原始API响应" json:"raw_data"`
Progress int `gorm:"column:progress;type:int;default:0;comment:进度百分比" json:"progress"`
Status JMTaskStatus `gorm:"column:status;type:varchar(20);default:'pending';comment:任务状态" json:"status"`
ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"`
Power int `gorm:"column:power;type:int(11);default:0;comment:消耗算力" json:"power"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"`
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserId uint `gorm:"column:user_id;type:int(11);not null;index;comment:用户ID" json:"user_id"`
TaskId string `gorm:"column:task_id;type:varchar(100);not null;index;comment:任务ID" json:"task_id"`
Type types.JMTaskType `gorm:"column:type;type:varchar(50);not null;comment:任务类型" json:"type"`
ReqKey string `gorm:"column:req_key;type:varchar(100);comment:请求Key" json:"req_key"`
Prompt string `gorm:"column:prompt;type:text;comment:提示词" json:"prompt"`
Params string `gorm:"column:params;type:text;comment:任务参数JSON" json:"params"`
ImgURL string `gorm:"column:img_url;type:varchar(1024);comment:图片或封面URL" json:"img_url"`
VideoURL string `gorm:"column:video_url;type:varchar(1024);comment:视频URL" json:"video_url"`
RawData string `gorm:"column:raw_data;type:text;comment:原始API响应" json:"raw_data"`
Progress int `gorm:"column:progress;type:int;default:0;comment:进度百分比" json:"progress"`
Status types.JMTaskStatus `gorm:"column:status;type:varchar(20);default:'pending';comment:任务状态" json:"status"`
ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"`
Power int `gorm:"column:power;type:int(11);default:0;comment:消耗算力" json:"power"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"`
}
// JMTaskStatus 任务状态
type JMTaskStatus string
const (
JMTaskStatusInQueue = JMTaskStatus("in_queue") // 任务已提交
JMTaskStatusGenerating = JMTaskStatus("generating") // 任务处理中
JMTaskStatusDone = JMTaskStatus("done") // 处理完成
JMTaskStatusNotFound = JMTaskStatus("not_found") // 任务未找到
JMTaskStatusSuccess = JMTaskStatus("success") // 任务成功
JMTaskStatusFailed = JMTaskStatus("failed") // 任务失败
JMTaskStatusExpired = JMTaskStatus("expired") // 任务过期
)
// JMTaskType 任务类型
type JMTaskType string
const (
JMTaskTypeTextToImage = JMTaskType("text_to_image") // 文生图
JMTaskTypeImageToImage = JMTaskType("image_to_image") // 图生图
JMTaskTypeImageEdit = JMTaskType("image_edit") // 图像编辑
JMTaskTypeImageEffects = JMTaskType("image_effects") // 图像特效
JMTaskTypeTextToVideo = JMTaskType("text_to_video") // 文生视频
JMTaskTypeImageToVideo = JMTaskType("image_to_video") // 图生视频
)
// TableName 返回数据表名称
func (JimengJob) TableName() string {
return "geekai_jimeng_jobs"
+1 -1
View File
@@ -21,7 +21,7 @@ type User struct {
ChatModels string `gorm:"column:chat_models_json;type:text;default:null;comment:AI模型 json" json:"chat_models"`
LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"`
Vip bool `gorm:"column:vip;type:tinyint(1);not null;default:0;comment:是否会员" json:"vip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"`
LastLoginIp string `gorm:"column:last_login_ip;type:char(32);not null;comment:最后登录 IP" json:"last_login_ip"`
OpenId string `gorm:"column:openid;type:varchar(100);comment:第三方登录账号ID" json:"openid"`
Platform string `gorm:"column:platform;type:varchar(30);comment:登录平台" json:"platform"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
+1 -1
View File
@@ -8,7 +8,7 @@ type UserLoginLog struct {
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"`
Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"`
LoginIp string `gorm:"column:login_ip;type:char(16);not null;comment:登录IP" json:"login_ip"`
LoginIp string `gorm:"column:login_ip;type:char(32);not null;comment:登录IP" json:"login_ip"`
LoginAddress string `gorm:"column:login_address;type:varchar(30);not null;comment:登录地址" json:"login_address"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
+1
View File
@@ -20,4 +20,5 @@ type Function struct {
Action string `json:"action"`
Token string `json:"token"`
Enabled bool `json:"enabled"`
Power int `json:"power"`
}
+17 -17
View File
@@ -1,23 +1,23 @@
package vo
import "geekai/store/model"
import "geekai/core/types"
// JimengJob 即梦AI任务VO
type JimengJob struct {
Id uint `json:"id"`
UserId uint `json:"user_id"`
TaskId string `json:"task_id"`
Type model.JMTaskType `json:"type"`
ReqKey string `json:"req_key"`
Prompt string `json:"prompt"`
TaskParams string `json:"task_params"`
ImgURL string `json:"img_url"`
VideoURL string `json:"video_url"`
RawData string `json:"raw_data"`
Progress int `json:"progress"`
Status model.JMTaskStatus `json:"status"`
ErrMsg string `json:"err_msg"`
Power int `json:"power"`
CreatedAt int64 `json:"created_at"` // 时间戳
UpdatedAt int64 `json:"updated_at"` // 时间戳
Id uint `json:"id"`
UserId uint `json:"user_id"`
TaskId string `json:"task_id"`
Type types.JMTaskType `json:"type"`
ReqKey string `json:"req_key"`
Prompt string `json:"prompt"`
Params map[string]any `json:"params"`
ImgURL string `json:"img_url"`
VideoURL string `json:"video_url"`
RawData string `json:"raw_data"`
Progress int `json:"progress"`
Status types.JMTaskStatus `json:"status"`
ErrMsg string `json:"err_msg"`
Power int `json:"power"`
CreatedAt int64 `json:"created_at"` // 时间戳
UpdatedAt int64 `json:"updated_at"` // 时间戳
}
+817
View File
@@ -0,0 +1,817 @@
package utils
import (
"context"
"encoding/binary"
"errors"
"fmt"
"io"
"net/http"
"os"
"time"
)
// AudioDuration returns duration of an audio file.
// Supported formats: MP3, WAV (auto-detected by header)
func AudioDuration(path string) (time.Duration, error) {
f, err := os.Open(path)
if err != nil {
return 0, err
}
defer f.Close()
// Peek first 12 bytes to detect format
head := make([]byte, 12)
n, err := io.ReadFull(f, head)
if err != nil {
return 0, err
}
if n < 12 {
return 0, errors.New("file too small")
}
// WAV: RIFF....WAVE
if string(head[0:4]) == "RIFF" && string(head[8:12]) == "WAVE" {
if _, err := f.Seek(0, io.SeekStart); err != nil {
return 0, err
}
return wavDuration(f)
}
// MP3 can start with ID3 or frame sync 0xFFEx
if string(head[0:3]) == "ID3" || (head[0] == 0xFF && (head[1]&0xE0) == 0xE0) {
if _, err := f.Seek(0, io.SeekStart); err != nil {
return 0, err
}
return mp3Duration(f)
}
return 0, errors.New("unsupported audio format")
}
// AudioDurationFromURL downloads the url to a temp file and returns duration.
func AudioDurationFromURL(url string) (time.Duration, error) {
path, err := fetchURLToTemp(url, 30*time.Second)
if err != nil {
return 0, err
}
defer os.Remove(path)
return AudioDuration(path)
}
// VideoDurationMP4 returns duration of an MP4 file (MOV/MP4 base media).
func VideoDurationMP4(path string) (time.Duration, error) {
f, err := os.Open(path)
if err != nil {
return 0, err
}
defer f.Close()
return mp4Duration(f)
}
// VideoDurationMP4FromURL downloads the url to a temp file and returns duration.
func VideoDurationMP4FromURL(url string) (time.Duration, error) {
path, err := fetchURLToTemp(url, 30*time.Second)
if err != nil {
return 0, err
}
defer os.Remove(path)
return VideoDurationMP4(path)
}
// ---------------------- helpers ----------------------
func fetchURLToTemp(url string, timeout time.Duration) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("http status: %d", resp.StatusCode)
}
tmp, err := os.CreateTemp("", "media-*")
if err != nil {
return "", err
}
defer tmp.Close()
if _, err := io.Copy(tmp, resp.Body); err != nil {
path := tmp.Name()
_ = os.Remove(path)
return "", err
}
return tmp.Name(), nil
}
// ---------------------- WAV ----------------------
func wavDuration(r io.ReadSeeker) (time.Duration, error) {
// RIFF header already checked outside if needed. We parse chunks to get fmt and data.
// WAV little-endian
if _, err := r.Seek(0, io.SeekStart); err != nil {
return 0, err
}
// Read RIFF header (12 bytes)
head := make([]byte, 12)
if _, err := io.ReadFull(r, head); err != nil {
return 0, err
}
if string(head[0:4]) != "RIFF" || string(head[8:12]) != "WAVE" {
return 0, errors.New("invalid wav header")
}
var sampleRate uint32
var numChans uint16
var bitsPerSample uint16
var byteRate uint32
var dataSize uint32
for {
chunkHdr := make([]byte, 8)
if _, err := io.ReadFull(r, chunkHdr); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
break
}
return 0, err
}
ckID := string(chunkHdr[0:4])
ckSize := binary.LittleEndian.Uint32(chunkHdr[4:8])
switch ckID {
case "fmt ":
fmtData := make([]byte, ckSize)
if _, err := io.ReadFull(r, fmtData); err != nil {
return 0, err
}
// audioFormat := binary.LittleEndian.Uint16(fmtData[0:2]) // 1 = PCM
numChans = binary.LittleEndian.Uint16(fmtData[2:4])
sampleRate = binary.LittleEndian.Uint32(fmtData[4:8])
byteRate = binary.LittleEndian.Uint32(fmtData[8:12])
// blockAlign := binary.LittleEndian.Uint16(fmtData[12:14])
if len(fmtData) >= 16 {
bitsPerSample = binary.LittleEndian.Uint16(fmtData[14:16])
}
case "data":
dataSize = ckSize
// Skip data content
if _, err := r.Seek(int64(ckSize), io.SeekCurrent); err != nil {
return 0, err
}
default:
// Skip other chunks
if _, err := r.Seek(int64(ckSize), io.SeekCurrent); err != nil {
return 0, err
}
}
// Chunks are word-aligned (pad byte if odd size)
if ckSize%2 == 1 {
if _, err := r.Seek(1, io.SeekCurrent); err != nil { // skip pad
return 0, err
}
}
}
if sampleRate == 0 || numChans == 0 {
return 0, errors.New("invalid wav fmt")
}
var durationSeconds float64
if byteRate != 0 {
durationSeconds = float64(dataSize) / float64(byteRate)
} else {
bytesPerSec := float64(sampleRate) * float64(numChans) * float64(bitsPerSample) / 8.0
if bytesPerSec == 0 {
return 0, errors.New("invalid wav parameters")
}
durationSeconds = float64(dataSize) / bytesPerSec
}
return time.Duration(durationSeconds * float64(time.Second)), nil
}
// ---------------------- MP3 ----------------------
func mp3Duration(r io.ReadSeeker) (time.Duration, error) {
// Strategy:
// 1) Skip ID3v2 header if present.
// 2) Try read first frame and detect XING/Info or VBRI to get total frames and duration.
// 3) If VBR headers not present, fall back to CBR estimation: (audioDataBytes * 8) / bitrate.
// File size
fi, err := fileSizeFromSeeker(r)
if err != nil {
return 0, err
}
// Skip ID3v2
var id3v2Size int64
if _, err := r.Seek(0, io.SeekStart); err != nil {
return 0, err
}
id3v2Size, err = skipID3v2(r)
if err != nil {
return 0, err
}
// Remember audio start offset
startOffset, _ := r.Seek(0, io.SeekCurrent)
// Read first frame header (search sync)
off, fh, err := findNextMP3Frame(r)
if err != nil {
return 0, err
}
if _, err := r.Seek(off, io.SeekStart); err != nil {
return 0, err
}
// Check for XING/Info header in first frame (for VBR)
totalFrames, sampleRate, samplesPerFrame, bitrateKbps, vbrFound, err := parseFirstFrameForVBR(r, fh)
if err != nil {
return 0, err
}
if vbrFound && totalFrames > 0 && sampleRate > 0 && samplesPerFrame > 0 {
seconds := (float64(totalFrames) * float64(samplesPerFrame)) / float64(sampleRate)
return time.Duration(seconds * float64(time.Second)), nil
}
// Fall back to CBR estimate using bitrate and data size (excluding ID3v2 and ID3v1)
// Detect ID3v1 at end (128 bytes TAG)
var id3v1Size int64
if fi >= 128 {
if _, err := r.Seek(fi-128, io.SeekStart); err == nil {
buf := make([]byte, 3)
if _, err := io.ReadFull(r, buf); err == nil {
if string(buf) == "TAG" {
id3v1Size = 128
}
}
}
}
audioBytes := fi - id3v2Size - id3v1Size - startOffset
if audioBytes <= 0 || bitrateKbps == 0 {
return 0, errors.New("unable to estimate mp3 duration")
}
// bitrateKbps in kbps, bytes -> bits
seconds := float64(audioBytes*8) / float64(bitrateKbps*1000)
return time.Duration(seconds * float64(time.Second)), nil
}
type mp3FrameHeader struct {
Version int // 1: MPEG1, 2: MPEG2, 25: MPEG2.5
Layer int // 1,2,3
BitrateKbps int
SampleRate int
Padding int
ChannelMode int // 0:Stereo,1:Joint,2:Dual,3:Mono
}
func findNextMP3Frame(r io.ReadSeeker) (int64, mp3FrameHeader, error) {
var hdr mp3FrameHeader
// Start from current pos and scan up to 64KB
start, _ := r.Seek(0, io.SeekCurrent)
limit := int64(64 * 1024)
buf := make([]byte, limit)
n, err := r.Read(buf)
if err != nil && !errors.Is(err, io.EOF) {
return 0, hdr, err
}
for i := 0; i+4 <= n; i++ {
if buf[i] == 0xFF && (buf[i+1]&0xE0) == 0xE0 { // sync
if h, ok := parseMP3Header(buf[i : i+4]); ok {
offset := start + int64(i)
return offset, h, nil
}
}
}
return 0, hdr, errors.New("mp3 frame not found")
}
func parseMP3Header(b []byte) (mp3FrameHeader, bool) {
var h mp3FrameHeader
if len(b) < 4 {
return h, false
}
if b[0] != 0xFF || (b[1]&0xE0) != 0xE0 {
return h, false
}
versionBits := (b[1] >> 3) & 0x03
layerBits := (b[1] >> 1) & 0x03
bitrateBits := (b[2] >> 4) & 0x0F
sampleRateBits := (b[2] >> 2) & 0x03
paddingBit := (b[2] >> 1) & 0x01
channelMode := (b[3] >> 6) & 0x03
var version int
switch versionBits {
case 0x00:
version = 25 // MPEG 2.5
case 0x02:
version = 2 // MPEG 2
case 0x03:
version = 1 // MPEG 1
default:
return h, false
}
var layer int
switch layerBits {
case 0x01:
layer = 3
case 0x02:
layer = 2
case 0x03:
layer = 1
default:
return h, false
}
br := mp3BitrateKbps(version, layer, int(bitrateBits))
if br == 0 {
return h, false
}
sr := mp3SampleRate(version, int(sampleRateBits))
if sr == 0 {
return h, false
}
h = mp3FrameHeader{
Version: version,
Layer: layer,
BitrateKbps: br,
SampleRate: sr,
Padding: int(paddingBit),
ChannelMode: int(channelMode),
}
return h, true
}
func mp3BitrateKbps(version, layer, index int) int {
// index: 1..14 valid; 0,15 invalid
if index <= 0 || index == 15 {
return 0
}
// Tables per ISO/IEC 11172-3/13818-3 (common subset)
var tbl [15]int
if layer == 1 { // Layer I
if version == 1 { // MPEG1
tbl = [15]int{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}
} else { // MPEG2/2.5
tbl = [15]int{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}
}
} else if layer == 2 { // Layer II
if version == 1 {
tbl = [15]int{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}
} else {
tbl = [15]int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}
}
} else { // Layer III
if version == 1 {
tbl = [15]int{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}
} else {
tbl = [15]int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}
}
}
return tbl[index]
}
func mp3SampleRate(version, index int) int {
if index == 3 {
return 0
}
// base table for MPEG1
base := [3]int{44100, 48000, 32000}
sr := base[index]
if version == 2 { // MPEG2
sr /= 2
} else if version == 25 { // MPEG2.5
sr /= 4
}
return sr
}
func samplesPerMP3Frame(version, layer int) int {
switch layer {
case 1:
return 384
case 2:
return 1152
case 3:
if version == 1 {
return 1152
}
return 576 // MPEG2/2.5 Layer III
default:
return 0
}
}
func parseFirstFrameForVBR(r io.ReadSeeker, fh mp3FrameHeader) (totalFrames uint32, sampleRate int, samplesPerFrame int, bitrateKbps int, vbrFound bool, err error) {
// After the 4-byte header, possible side info and then XING/Info
if _, err = r.Seek(0, io.SeekCurrent); err != nil {
return
}
// Re-read header
hdr := make([]byte, 4)
if _, err = io.ReadFull(r, hdr); err != nil {
return
}
// side info size depends on MPEG version and channel mode (for Layer III)
sideInfoSize := 0
if fh.Layer == 3 { // Layer III
if fh.Version == 1 { // MPEG1
if fh.ChannelMode == 3 { // mono
sideInfoSize = 17
} else {
sideInfoSize = 32
}
} else { // MPEG2/2.5
if fh.ChannelMode == 3 {
sideInfoSize = 9
} else {
sideInfoSize = 17
}
}
}
// Read next up to 120 bytes to search for XING/Info or VBRI
buf := make([]byte, sideInfoSize+120)
if _, err = io.ReadFull(r, buf); err != nil {
// If short, still try within available
if !errors.Is(err, io.ErrUnexpectedEOF) && !errors.Is(err, io.EOF) {
return
}
}
// Search XING/Info signature
sigs := [][]byte{[]byte("Xing"), []byte("Info")}
for _, sig := range sigs {
idx := indexOf(buf, sig)
if idx >= 0 {
// flags after signature (4 bytes), then if frames flag set, 4 bytes frames
if len(buf) >= idx+4+4 {
flags := binary.BigEndian.Uint32(buf[idx+4 : idx+8])
var frames uint32
if (flags & 0x01) != 0 { // frames present
if len(buf) >= idx+8+4 {
frames = binary.BigEndian.Uint32(buf[idx+8 : idx+12])
}
}
if frames > 0 {
vbrFound = true
totalFrames = frames
sampleRate = fh.SampleRate
samplesPerFrame = samplesPerMP3Frame(fh.Version, fh.Layer)
bitrateKbps = fh.BitrateKbps
return
}
}
}
}
// Check VBRI (usually at 32 bytes after header for MPEG1 Layer III)
if len(buf) >= 4 {
idx := indexOf(buf, []byte("VBRI"))
if idx >= 0 {
if len(buf) >= idx+4+2+2+4+4 {
// VBRI layout: 'VBRI'(4) + version(2) + delay(2) + quality(2?) varies; but at offset 10 comes bytes: bytes (4), frames (4)
// Some docs: offset 10: bytes, offset 14: frames (big-endian)
bytesOffset := idx + 10
framesOffset := idx + 14
if len(buf) >= framesOffset+4 {
frames := binary.BigEndian.Uint32(buf[framesOffset : framesOffset+4])
if frames > 0 {
vbrFound = true
totalFrames = frames
sampleRate = fh.SampleRate
samplesPerFrame = samplesPerMP3Frame(fh.Version, fh.Layer)
bitrateKbps = fh.BitrateKbps
_ = bytesOffset // not used
return
}
}
}
}
}
// No VBR header. Provide header info for CBR fallback
sampleRate = fh.SampleRate
samplesPerFrame = samplesPerMP3Frame(fh.Version, fh.Layer)
bitrateKbps = fh.BitrateKbps
return
}
func indexOf(haystack []byte, needle []byte) int {
for i := 0; i+len(needle) <= len(haystack); i++ {
match := true
for j := 0; j < len(needle); j++ {
if haystack[i+j] != needle[j] {
match = false
break
}
}
if match {
return i
}
}
return -1
}
func skipID3v2(r io.ReadSeeker) (int64, error) {
if _, err := r.Seek(0, io.SeekStart); err != nil {
return 0, err
}
head := make([]byte, 10)
if _, err := io.ReadFull(r, head); err != nil {
return 0, nil // no header
}
if string(head[0:3]) != "ID3" {
if _, err := r.Seek(0, io.SeekStart); err != nil {
return 0, err
}
return 0, nil
}
// size: 4 synchsafe bytes
sz := int64((int(head[6]&0x7F) << 21) | (int(head[7]&0x7F) << 14) | (int(head[8]&0x7F) << 7) | int(head[9]&0x7F))
// total header size = 10 + sz (+ footer 10 if flag set)
footer := int64(0)
if (head[5] & 0x10) != 0 { // footer present
footer = 10
}
total := 10 + sz + footer
if _, err := r.Seek(total, io.SeekStart); err != nil {
return 0, err
}
return total, nil
}
func fileSizeFromSeeker(r io.ReadSeeker) (int64, error) {
cur, err := r.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
end, err := r.Seek(0, io.SeekEnd)
if err != nil {
return 0, err
}
if _, err := r.Seek(cur, io.SeekStart); err != nil {
return 0, err
}
return end, nil
}
// ---------------------- MP4 ----------------------
type mp4BoxHeader struct {
Size uint64
Type [4]byte
}
func readBoxHeader(r io.ReadSeeker) (mp4BoxHeader, error) {
var h mp4BoxHeader
buf := make([]byte, 8)
if _, err := io.ReadFull(r, buf); err != nil {
return h, err
}
sz := binary.BigEndian.Uint32(buf[0:4])
copy(h.Type[:], buf[4:8])
if sz == 1 {
// 64-bit size follows
ext := make([]byte, 8)
if _, err := io.ReadFull(r, ext); err != nil {
return h, err
}
h.Size = binary.BigEndian.Uint64(ext)
} else {
h.Size = uint64(sz)
}
return h, nil
}
func skipBox(r io.ReadSeeker, boxSize uint64, alreadyRead int64) error {
toSkip := int64(boxSize) - alreadyRead
if toSkip < 0 {
return fmt.Errorf("invalid box size")
}
_, err := r.Seek(toSkip, io.SeekCurrent)
return err
}
func mp4Duration(r io.ReadSeeker) (time.Duration, error) {
if _, err := r.Seek(0, io.SeekStart); err != nil {
return 0, err
}
// Find moov box
var moovStart int64
var moovSize uint64
for {
pos, _ := r.Seek(0, io.SeekCurrent)
h, err := readBoxHeader(r)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
break
}
return 0, err
}
if string(h.Type[:]) == "moov" {
moovStart = pos
moovSize = h.Size
break
}
if h.Size < 8 {
return 0, errors.New("invalid mp4 box size")
}
if err := skipBox(r, h.Size, 8); err != nil {
return 0, err
}
}
if moovStart == 0 && moovSize == 0 {
return 0, errors.New("moov not found")
}
// Parse inside moov for video trak mdhd, else mvhd
if _, err := r.Seek(moovStart+8, io.SeekStart); err != nil { // skip moov header
return 0, err
}
end := moovStart + int64(moovSize)
var movieTimescale uint32
var movieDuration uint64
var foundVideoMdhd bool
var mdhdTimescale uint32
var mdhdDuration uint64
for {
pos, _ := r.Seek(0, io.SeekCurrent)
if pos >= end {
break
}
h, err := readBoxHeader(r)
if err != nil {
return 0, err
}
switch string(h.Type[:]) {
case "mvhd":
// movie header
ver := make([]byte, 1)
if _, err := io.ReadFull(r, ver); err != nil {
return 0, err
}
if _, err := r.Seek(3, io.SeekCurrent); err != nil { // flags
return 0, err
}
if ver[0] == 1 {
// version 1: 64-bit duration
buf := make([]byte, 8+8+4+8) // ctime(8) mtime(8) timescale(4) duration(8)
if _, err := io.ReadFull(r, buf); err != nil {
return 0, err
}
movieTimescale = binary.BigEndian.Uint32(buf[16:20])
movieDuration = binary.BigEndian.Uint64(buf[20:28])
} else {
buf := make([]byte, 4+4+4+4) // ctime mtime timescale duration
if _, err := io.ReadFull(r, buf); err != nil {
return 0, err
}
movieTimescale = binary.BigEndian.Uint32(buf[8:12])
movieDuration = uint64(binary.BigEndian.Uint32(buf[12:16]))
}
// skip rest of mvhd
read := int64(1 + 3)
if ver[0] == 1 {
read += int64(8 + 8 + 4 + 8)
} else {
read += int64(4 + 4 + 4 + 4)
}
if err := skipBox(r, h.Size, 8+read); err != nil {
return 0, err
}
case "trak":
// parse trak for hdlr and mdhd
tEnd := int64(0)
if h.Size < 8 {
return 0, errors.New("invalid trak size")
}
tEnd = pos + int64(h.Size)
var isVideo bool
var tMdhdTimescale uint32
var tMdhdDuration uint64
for {
cpos, _ := r.Seek(0, io.SeekCurrent)
if cpos >= tEnd {
break
}
ch, err := readBoxHeader(r)
if err != nil {
return 0, err
}
switch string(ch.Type[:]) {
case "mdia":
mEnd := cpos + int64(ch.Size)
for {
mpos, _ := r.Seek(0, io.SeekCurrent)
if mpos >= mEnd {
break
}
mh, err := readBoxHeader(r)
if err != nil {
return 0, err
}
switch string(mh.Type[:]) {
case "hdlr":
// skip version+flags (4), pre_defined(4)
if _, err := r.Seek(8, io.SeekCurrent); err != nil {
return 0, err
}
handler := make([]byte, 4)
if _, err := io.ReadFull(r, handler); err != nil {
return 0, err
}
if string(handler) == "vide" {
isVideo = true
}
if err := skipBox(r, mh.Size, 8+8+4); err != nil { // header + skipped + read handler
return 0, err
}
case "mdhd":
ver := make([]byte, 1)
if _, err := io.ReadFull(r, ver); err != nil {
return 0, err
}
if _, err := r.Seek(3, io.SeekCurrent); err != nil { // flags
return 0, err
}
if ver[0] == 1 {
buf := make([]byte, 8+8+4+8)
if _, err := io.ReadFull(r, buf); err != nil {
return 0, err
}
tMdhdTimescale = binary.BigEndian.Uint32(buf[16:20])
tMdhdDuration = binary.BigEndian.Uint64(buf[20:28])
} else {
buf := make([]byte, 4+4+4+4)
if _, err := io.ReadFull(r, buf); err != nil {
return 0, err
}
tMdhdTimescale = binary.BigEndian.Uint32(buf[8:12])
tMdhdDuration = uint64(binary.BigEndian.Uint32(buf[12:16]))
}
if err := skipBox(r, mh.Size, 8+1+3+int64(lenVersionPayload(ver[0]))); err != nil {
return 0, err
}
default:
if err := skipBox(r, mh.Size, 8); err != nil {
return 0, err
}
}
}
default:
if err := skipBox(r, ch.Size, 8); err != nil {
return 0, err
}
}
}
if isVideo && tMdhdTimescale != 0 && tMdhdDuration != 0 {
foundVideoMdhd = true
mdhdTimescale = tMdhdTimescale
mdhdDuration = tMdhdDuration
}
// Skip remaining of trak if any
if _, err := r.Seek(tEnd, io.SeekStart); err != nil {
return 0, err
}
default:
if err := skipBox(r, h.Size, 8); err != nil {
return 0, err
}
}
}
if foundVideoMdhd && mdhdTimescale != 0 {
sec := float64(mdhdDuration) / float64(mdhdTimescale)
return time.Duration(sec * float64(time.Second)), nil
}
if movieTimescale != 0 {
sec := float64(movieDuration) / float64(movieTimescale)
return time.Duration(sec * float64(time.Second)), nil
}
return 0, errors.New("failed to read mp4 duration")
}
func lenVersionPayload(ver byte) int {
if ver == 1 {
return 8 + 8 + 4 + 8
}
return 4 + 4 + 4 + 4
}
+13
View File
@@ -101,3 +101,16 @@ func GetImgExt(filename string) string {
}
return ext
}
func GetFileSize(url string) (int64, error) {
resp, err := http.Get(url)
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0, err
}
return int64(len(body)), nil
}
+6 -6
View File
@@ -14,15 +14,15 @@ npm run build
cd ../build
# remove docker image if exists
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-api:$version-$arch
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-plus-api:$version-$arch -f dockerfile-api-go ../
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch -f dockerfile-api-go ../
# build docker image for Geek-AI-web
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-web:$version-$arch
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-web:$version-$arch -f dockerfile-web-$arch ../
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-web-$arch ../
if [ "$3" = "push" ];then
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-plus-api:$version-$arch
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-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
+3 -3
View File
@@ -18,10 +18,10 @@ SET time_zone = "+00:00";
/*!40101 SET NAMES utf8mb4 */;
--
-- 数据库: `geekai_plus`
-- 数据库: `geekai`
--
CREATE DATABASE IF NOT EXISTS `geekai_plus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE `geekai_plus`;
CREATE DATABASE IF NOT EXISTS `geekai` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE `geekai`;
-- --------------------------------------------------------
+46
View File
@@ -0,0 +1,46 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'websocket' upgrade;
}
server {
# listen 443 ssl;
listen 8080;
# server_name www.chatgpt.com; #替换成你自己的域名
# ssl_certificate xxx.pem; # 替换成自己的 SSL 证书
# ssl_certificate_key xxx.key;
# ssl_session_timeout 5m;
# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_prefer_server_ciphers on;
# 日志地址
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
index index.html;
root /var/www/app/dist; # 这里改成前端静态页面的地址
location / {
try_files $uri $uri/ /index.html;
# 后端 API 的转发
location /api/ {
proxy_http_version 1.1;
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_send_timeout 12s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://geekai-api:5678;
}
# 静态资源转发
location /static/ {
proxy_pass http://geekai-api:5678;
}
}
}
+83
View File
@@ -0,0 +1,83 @@
version: '3'
services:
# mysql
geekai-mysql:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/mysql:8.0.33
container_name: geekai-mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_ROOT_PASSWORD=mhSCk0NheGhmtsha
ports:
- '3307:3306'
volumes:
- ./conf/mysql/my.cnf:/etc/mysql/my.cnf
- ./data/mysql/data:/var/lib/mysql
- ./logs/mysql:/var/log/mysql
- ./data/mysql/init.d:/docker-entrypoint-initdb.d/
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 5s
timeout: 10s
retries: 10
# redis
geekai-redis:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/redis:6.0.6
restart: always
container_name: geekai-redis
command: redis-server --requirepass mhSCk0NheGhmtsha
volumes:
- ./data/redis:/data
ports:
- '6380:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 3s
timeout: 10s
retries: 5
geekai-tika:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/tika:latest
container_name: geekai-tika
restart: always
ports:
- '9999:9998'
# 后端 API 程序
geekai-api:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.2.8-amd64
container_name: geekai-api
restart: always
depends_on:
geekai-mysql:
condition: service_healthy
geekai-redis:
condition: service_healthy
environment:
- DEBUG=false
- LOG_LEVEL=info
- CONFIG_FILE=config.toml
ports:
- '5678:5678'
volumes:
- /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
- ./conf/config.toml:/var/www/app/config.toml
- ./logs/app:/var/www/app/logs
- ./static:/var/www/app/static
- ./data/leveldb:/var/www/app/data
# 前端应用
geekai-web:
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.2.8-amd64
container_name: geekai-web
restart: always
depends_on:
- geekai-api
ports:
- '8080:8080'
volumes:
- ./logs/nginx:/var/log/nginx
- ./conf/nginx/conf.d:/etc/nginx/conf.d
- ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./conf/nginx/ssl:/etc/nginx/ssl
+1 -1
View File
@@ -6,7 +6,7 @@ VITE_ADMIN_USER=admin
VITE_ADMIN_PASS=admin123
VITE_KEY_PREFIX=GeekAI_DEV_
VITE_TITLE="Geek-AI 创作系统"
VITE_VERSION=v4.2.6
VITE_VERSION=v4.2.8
VITE_DOCS_URL=https://docs.geekai.me
VITE_GITHUB_URL=https://github.com/yangjian102621/geekai
VITE_GITEE_URL=https://gitee.com/blackfox/geekai
+2 -1
View File
@@ -1,7 +1,8 @@
VITE_API_HOST=
VITE_WS_HOST=
VITE_KEY_PREFIX=GeekAI_
VITE_VERSION=v4.2.6
VITE_VERSION=v4.2.8
VITE_TITLE="Geek-AI 创作系统"
VITE_DOCS_URL=https://docs.geekai.me
VITE_GITHUB_URL=https://github.com/yangjian102621/geekai
VITE_GITEE_URL=https://gitee.com/blackfox/geekai
+4 -3
View File
@@ -83,7 +83,7 @@
width: 100%;
}
.el-input__wrapper {
background: var(--card-bg);
//background: var(--card-bg);
}
.el-dialog__title {
font-weight: bold;
@@ -138,7 +138,8 @@
border: none;
}
.el-tag, .el-tag.el-tag--primary {
.el-tag,
.el-tag.el-tag--primary {
--el-tag-bg-color: #f0ebff;
}
.box-card {
@@ -148,4 +149,4 @@
}
.el-table th.el-table__cell {
background-color: var(--chat-bg);
}
}
+152 -111
View File
@@ -49,16 +49,17 @@
.category-btn {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 15px 10px;
padding: 10px 15px;
border: 2px solid var(--border-color, #f0f0f0);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
background: var(--card-bg-secondary, #fafafa);
/* 暗色主题支持 */
[data-theme="dark"] & {
[data-theme='dark'] & {
background: var(--card-bg-secondary-dark, #23242a);
border-color: var(--border-color-dark, #33343a);
}
@@ -66,7 +67,7 @@
&:hover {
border-color: var(--primary-color, #5865f2);
background: var(--card-bg-hover, #f8f9ff);
[data-theme="dark"] & {
[data-theme='dark'] & {
background: var(--card-bg-hover-dark, #2a2b31);
}
transform: translateY(-2px);
@@ -76,8 +77,11 @@
border-color: var(--primary-color, #5865f2);
background: var(--primary-gradient, linear-gradient(135deg, #5865f2 0%, #7289da 100%));
color: var(--primary-text-on-primary, #fff);
[data-theme="dark"] & {
background: var(--primary-gradient-dark, linear-gradient(135deg, #23242a 0%, #2a2b31 100%));
[data-theme='dark'] & {
background: var(
--primary-gradient-dark,
linear-gradient(135deg, #23242a 0%, #2a2b31 100%)
);
color: var(--primary-text-on-primary-dark, #fff);
}
transform: translateY(-2px);
@@ -96,108 +100,6 @@
}
}
}
// 功能开关
.function-switch {
margin-bottom: 25px;
.switch-label {
display: flex;
align-items: center;
margin-bottom: 15px;
font-size: 16px;
font-weight: 600;
color: var(--text-theme-color);
.el-icon {
margin-right: 8px;
color: var(--primary-color, #5865f2);
}
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 15px;
border: 1px solid var(--border-color, #e0e0e0);
border-radius: 10px;
background: var(--card-bg-secondary, #f9f9f9);
[data-theme="dark"] & {
background: var(--card-bg-secondary-dark, #23242a);
border-color: var(--border-color-dark, #33343a);
}
.switch-info {
flex: 1;
.switch-title {
font-size: 14px;
font-weight: 600;
color: var(--text-theme-color);
margin-bottom: 4px;
}
.switch-desc {
font-size: 12px;
color: var(--text-sub-color, #666);
}
}
}
}
// 参数容器
.params-container {
.function-panel {
.param-line {
margin-bottom: 15px;
&.pt {
margin-top: 20px;
}
.label {
display: flex;
align-items: center;
margin-bottom: 8px;
font-weight: 600;
color: var(--text-theme-color);
}
}
.item-group {
display: flex;
align-items: center;
margin-bottom: 15px;
.label {
margin-right: 15px;
font-weight: 600;
color: var(--text-theme-color);
min-width: 80px;
}
}
.text-info {
margin: 20px 0;
padding: 15px;
background: var(--info-bg, #f0f8ff);
border-radius: 8px;
border-left: 4px solid var(--primary-color, #5865f2);
}
.submit-btn {
margin-top: 30px;
.el-button {
width: 100%;
height: 50px;
font-size: 16px;
font-weight: 600;
}
}
}
}
}
// 右侧主要内容区域
@@ -239,6 +141,23 @@
&:hover {
box-shadow: 0 4px 24px rgba(88, 101, 242, 0.12);
}
// 增强任务项悬停动画
transition: box-shadow 3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.5s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
border: 1.5px solid transparent;
border-radius: 12px;
// background: #fff;
position: relative;
z-index: 1;
&:hover {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18), 0 1.5px 8px rgba(0, 0, 0, 0.1);
border-color: #a259ff;
transform: scale(1.025) translateY(-2px);
z-index: 10;
}
.task-left {
width: 100%;
flex: none;
@@ -253,18 +172,69 @@
display: flex;
align-items: center;
justify-content: center;
.preview-image, .preview-video {
.preview-image,
.preview-video {
width: 100%;
height: 100%;
object-fit: cover;
}
// 视频预览包装器
.preview-video-wrapper {
position: relative;
width: 100%;
height: 100%;
.video-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s;
z-index: 2;
}
&:hover .video-mask {
opacity: 1;
}
.play-btn {
width: 64px;
height: 64px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
cursor: pointer;
z-index: 3;
transition: background 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.4);
}
img {
width: 36px;
height: 36px;
}
}
}
.preview-placeholder {
display: flex;
flex-direction: column;
align-items: center;
color: var(--text-disabled-color, #999);
font-size: 16px;
.el-icon, .iconfont {
.el-icon,
.iconfont {
font-size: 32px;
margin-bottom: 5px;
}
@@ -346,4 +316,75 @@
max-height: 220px;
}
}
}
}
// 错误信息样式
.err-msg-clip {
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: normal;
}
// 模板选择器样式
.jimeng-template-select {
.el-select-dropdown__item {
height: 60px;
line-height: 60px;
}
}
// 提示词指南样式
.prompt-guide {
margin: 12px 0 16px;
background-color: var(--el-fill-color-blank);
.el-collapse {
--el-collapse-border-color: none;
}
.guide-title {
display: flex;
align-items: center;
font-weight: 600;
color: #666;
}
.guide-content {
max-height: 220px;
overflow: auto;
line-height: 1.6;
font-size: 12px;
color: #555;
padding-right: 4px;
}
.guide-section {
margin-bottom: 10px;
}
.guide-subtitle {
font-weight: 600;
margin-bottom: 6px;
color: #333;
}
ul {
list-style: disc;
padding-left: 18px;
margin: 4px 0;
}
.quote {
margin: 8px 0;
padding: 8px 10px;
border-left: 3px solid #a3a3a3;
background: #f8f8f8;
border-radius: 4px;
color: #444;
}
}
+3 -2
View File
@@ -189,10 +189,11 @@
}
blockquote {
border-left: 4px solid #42b983;
border-left: 5px solid #c1c1c1;
padding: 10px 15px;
color: #777;
background-color: rgba(66, 185, 131, 0.1);
background-color: rgba(193, 193, 193, 0.2);
border-radius: 10px;
}
table {
+281
View File
@@ -887,3 +887,284 @@
transform: rotate(360deg);
}
}
/* Dark 主题样式 - 按照 theme-dark.scss 的模式 */
:root[data-theme='dark'] .jimeng-create {
background-color: rgb(13, 20, 53);
/* 页面头部样式 */
.sticky {
background-color: rgb(31, 41, 55) !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
h1 {
color: rgb(255, 255, 255) !important;
}
.iconfont {
color: rgb(156, 163, 175) !important;
}
button:hover {
background-color: rgb(75, 85, 99) !important;
}
}
/* 功能分类选择 */
.jimeng-create__content {
.bg-white {
background-color: rgb(55, 65, 81) !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.text-gray-700 {
color: rgb(209, 213, 219) !important;
}
.text-gray-900 {
color: rgb(255, 255, 255) !important;
}
.text-gray-600 {
color: rgb(156, 163, 175) !important;
}
.text-gray-500 {
color: rgb(156, 163, 175) !important;
}
.bg-gray-100:hover {
background-color: rgb(75, 85, 99) !important;
}
/* Element Plus 组件样式覆盖 */
:deep(.el-input__wrapper) {
background-color: rgb(31, 41, 55) !important;
border-color: rgb(75, 85, 99) !important;
box-shadow: none !important;
}
:deep(.el-input__inner) {
color: rgb(209, 213, 219) !important;
background-color: transparent !important;
}
:deep(.el-input__inner::placeholder) {
color: rgb(156, 163, 175) !important;
}
:deep(.el-textarea__inner) {
color: rgb(209, 213, 219) !important;
background-color: transparent !important;
}
:deep(.el-textarea__inner::placeholder) {
color: rgb(156, 163, 175) !important;
}
:deep(.el-switch__core) {
background-color: rgb(75, 85, 99) !important;
border-color: rgb(75, 85, 99) !important;
}
:deep(.el-switch.is-checked .el-switch__core) {
background-color: rgb(139, 92, 246) !important;
border-color: rgb(139, 92, 246) !important;
}
:deep(.el-slider__runway) {
background-color: rgb(75, 85, 99) !important;
}
:deep(.el-slider__bar) {
background-color: rgb(139, 92, 246) !important;
}
:deep(.el-slider__button) {
border-color: rgb(139, 92, 246) !important;
}
:deep(.el-tooltip__trigger) {
color: rgb(156, 163, 175) !important;
}
}
/* 提交按钮 */
.bg-gradient-to-r {
background: linear-gradient(88deg, #af61f0 1.44%, #5b62ce) !important;
&:hover {
background: linear-gradient(88deg, #9f51e0 1.44%, #4b52be) !important;
}
&:disabled {
background: linear-gradient(88deg, #6b7280 1.44%, #4b5563) !important;
}
}
/* 作品列表 */
.jimeng-create__works {
&-title {
color: rgb(255, 255, 255) !important;
}
&-item {
background-color: rgb(55, 65, 81) !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
&-content {
.jimeng-create__works-item-info {
&-title {
color: rgb(255, 255, 255) !important;
}
&-prompt {
color: rgb(209, 213, 219) !important;
}
&-tags {
&-item {
background-color: rgb(75, 85, 99) !important;
color: rgb(209, 213, 219) !important;
&--warning {
background-color: rgb(239, 68, 68) !important;
color: rgb(255, 255, 255) !important;
}
&--primary {
background-color: rgb(59, 130, 246) !important;
color: rgb(255, 255, 255) !important;
}
&--power {
background-color: rgb(139, 92, 246) !important;
color: rgb(255, 255, 255) !important;
}
}
}
}
}
&-quick-actions {
button {
color: rgb(156, 163, 175) !important;
&:hover {
color: rgb(209, 213, 219) !important;
}
}
}
&-error {
&-content {
background-color: rgb(31, 41, 55) !important;
border-color: rgb(239, 68, 68) !important;
.jimeng-create__works-item-error-text {
color: rgb(239, 68, 68) !important;
}
.jimeng-create__works-item-error-copy-btn {
color: rgb(156, 163, 175) !important;
&:hover {
color: rgb(209, 213, 219) !important;
}
}
}
}
}
&-loading {
color: rgb(156, 163, 175) !important;
}
&-finished {
color: rgb(156, 163, 175) !important;
}
}
/* 媒体预览弹窗 */
.jimeng-create__media-dialog {
background-color: rgba(0, 0, 0, 0.8) !important;
&-content {
background-color: rgb(55, 65, 81) !important;
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
}
&-header {
background-color: rgb(31, 41, 55) !important;
border-bottom-color: rgb(75, 85, 99) !important;
h3 {
color: rgb(255, 255, 255) !important;
}
button {
color: rgb(156, 163, 175) !important;
&:hover {
color: rgb(209, 213, 219) !important;
}
}
}
}
/* 图片上传组件 */
:deep(.image-upload) {
.upload-area {
background-color: rgb(31, 41, 55) !important;
border-color: rgb(75, 85, 99) !important;
&:hover {
border-color: rgb(139, 92, 246) !important;
background-color: rgb(55, 65, 81) !important;
}
}
.upload-text {
color: rgb(209, 213, 219) !important;
}
.upload-icon {
color: rgb(139, 92, 246) !important;
}
}
/* 自定义选择组件 */
:deep(.custom-select) {
.select-trigger {
background-color: rgb(31, 41, 55) !important;
border-color: rgb(75, 85, 99) !important;
color: rgb(209, 213, 219) !important;
}
.select-dropdown {
background-color: rgb(55, 65, 81) !important;
border-color: rgb(75, 85, 99) !important;
box-shadow: 0 0 15px rgba(107, 80, 225, 0.8) !important;
}
.select-option {
color: rgb(209, 213, 219) !important;
&:hover {
background-color: rgb(75, 85, 99) !important;
}
&.selected {
background-color: rgb(139, 92, 246) !important;
color: rgb(255, 255, 255) !important;
}
}
}
/* 空状态组件 */
:deep(.van-empty) {
.van-empty__description {
color: rgb(156, 163, 175) !important;
}
}
}
+1
View File
@@ -26,6 +26,7 @@
--btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce);
--border-active: rgba(255, 255, 255, 0.1);
--card-bg: #252d58;
--card-bg-secondary: #313a6b;
--chat-bg: #1f243f;
--chat-wel-bg: #2d2f38;
--card-bg-table: rgba(17, 28, 68, 1);
+2 -1
View File
@@ -24,7 +24,8 @@
--btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce);
--border-active: rgba(134, 140, 255, 1);
--code-btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce);
--card-bg: #fff;
--card-bg: #f5f5f5;
--card-bg-secondary: #e5e5e5;
--chat-bg: #fff;
--theme-bg: linear-gradient(88deg, #fff3f3 1.44%, #e7e8ff);
--theme-bg-all: #f5f7fd;
+8 -5
View File
@@ -14,7 +14,7 @@
}
:deep(.el-textarea__inner) {
background: transparent;
// background: transparent;
color: var(--text-theme-color);
}
@@ -104,7 +104,8 @@
color: var(--text-theme-color);
}
.el-input, .el-slider {
.el-input,
.el-slider {
width: 100%;
}
@@ -422,7 +423,8 @@
flex-flow: column;
padding: 0 20px;
.prompt, .failed {
.prompt,
.failed {
padding: 0;
font-size: 16px;
max-height: 80px;
@@ -547,7 +549,8 @@
.left .container {
width: 120px;
.video, .el-image {
.video,
.el-image {
width: 120px;
}
}
@@ -562,4 +565,4 @@
}
}
}
}
}
+39 -3
View File
@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1756954977612') format('woff2'),
url('iconfont.woff?t=1756954977612') format('woff'),
url('iconfont.ttf?t=1756954977612') format('truetype');
src: url('iconfont.woff2?t=1757571432313') format('woff2'),
url('iconfont.woff?t=1757571432313') format('woff'),
url('iconfont.ttf?t=1757571432313') format('truetype');
}
.iconfont {
@@ -13,6 +13,42 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-resize:before {
content: "\e718";
}
.icon-template:before {
content: "\e8a6";
}
.icon-error-line:before {
content: "\e868";
}
.icon-success-line:before {
content: "\e88c";
}
.icon-yunjing:before {
content: "\e69b";
}
.icon-action:before {
content: "\e658";
}
.icon-dancing:before {
content: "\e659";
}
.icon-running:before {
content: "\e65e";
}
.icon-shuziren:before {
content: "\e6df";
}
.icon-cube:before {
content: "\e72c";
}
File diff suppressed because one or more lines are too long
+63
View File
@@ -5,6 +5,69 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "10564356",
"name": "resize",
"font_class": "resize",
"unicode": "e718",
"unicode_decimal": 59160
},
{
"icon_id": "1727381",
"name": "34模板、框架",
"font_class": "template",
"unicode": "e8a6",
"unicode_decimal": 59558
},
{
"icon_id": "9626841",
"name": "错误",
"font_class": "error-line",
"unicode": "e868",
"unicode_decimal": 59496
},
{
"icon_id": "9626990",
"name": "正确",
"font_class": "success-line",
"unicode": "e88c",
"unicode_decimal": 59532
},
{
"icon_id": "40613765",
"name": "运镜控制",
"font_class": "yunjing",
"unicode": "e69b",
"unicode_decimal": 59035
},
{
"icon_id": "5215282",
"name": "动作",
"font_class": "action",
"unicode": "e658",
"unicode_decimal": 58968
},
{
"icon_id": "7250581",
"name": "跳舞",
"font_class": "dancing",
"unicode": "e659",
"unicode_decimal": 58969
},
{
"icon_id": "8153037",
"name": "动作",
"font_class": "running",
"unicode": "e65e",
"unicode_decimal": 58974
},
{
"icon_id": "42680536",
"name": "数字人",
"font_class": "shuziren",
"unicode": "e6df",
"unicode_decimal": 59103
},
{
"icon_id": "544492",
"name": "cube",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Some files were not shown because too many files have changed in this diff Show More