Spaces:
Runtime error
Runtime error
[feat]add project files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- CHANGELOG.md +578 -0
- CONTRIBUTING.en.md +49 -0
- CONTRIBUTING.md +49 -0
- Dockerfile +78 -0
- README.en.md +410 -0
- README.md +439 -4
- docker-compose/docker-compose-mongodb.yml +15 -0
- docker-compose/docker-compose.yml +99 -0
- docker-compose/nginx/nginx.conf +27 -0
- docker-compose/readme.md +14 -0
- docs/alipay.png +0 -0
- docs/basesettings.jpg +0 -0
- docs/c1-2.8.0.png +0 -0
- docs/c1-2.9.0.png +0 -0
- docs/c1.png +0 -0
- docs/c2-2.8.0.png +0 -0
- docs/c2-2.9.0.png +0 -0
- docs/c2.png +0 -0
- docs/docker.png +0 -0
- docs/key-manager-en.jpg +0 -0
- docs/key-manager.jpg +0 -0
- docs/login.jpg +0 -0
- docs/mailsettings.jpg +0 -0
- docs/prompt.jpg +0 -0
- docs/prompt_en.jpg +0 -0
- docs/sitesettings.jpg +0 -0
- docs/user-manager.jpg +0 -0
- docs/wechat.png +0 -0
- index.html +83 -0
- kubernetes/README.md +9 -0
- kubernetes/deploy.yaml +66 -0
- kubernetes/ingress.yaml +21 -0
- license +21 -0
- package.json +76 -0
- pnpm-lock.yaml +0 -0
- postcss.config.js +6 -0
- public/favicon.ico +0 -0
- public/favicon.svg +1 -0
- public/pwa-192x192.png +0 -0
- public/pwa-512x512.png +0 -0
- replace-title.sh +5 -0
- service/.env.example +85 -0
- service/.eslintrc.json +5 -0
- service/.gitignore +31 -0
- service/.npmrc +1 -0
- service/.vscode/extensions.json +3 -0
- service/.vscode/settings.json +22 -0
- service/package.json +54 -0
- service/pnpm-lock.yaml +0 -0
- service/src/chatgpt/index.ts +387 -0
CHANGELOG.md
ADDED
@@ -0,0 +1,578 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## v2.10.9
|
2 |
+
|
3 |
+
`2023-04-03`
|
4 |
+
|
5 |
+
> 更新默认 `accessToken` 反代地址为 [[pengzhile](https://github.com/pengzhile)] 的 `https://ai.fakeopen.com/api/conversation`
|
6 |
+
|
7 |
+
## Enhancement
|
8 |
+
- 添加 `socks5` 代理认证 [[yimiaoxiehou](https://github.com/Chanzhaoyu/chatgpt-web/pull/999)]
|
9 |
+
- 添加 `socks` 代理用户名密码的配置 [[hank-cp](https://github.com/Chanzhaoyu/chatgpt-web/pull/890)]
|
10 |
+
- 添加可选日志打印 [[zcong1993](https://github.com/Chanzhaoyu/chatgpt-web/pull/1041)]
|
11 |
+
- 更新侧边栏按钮本地化[[simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/911)]
|
12 |
+
- 优化代码块滚动条高度 [[Fog3211](https://github.com/Chanzhaoyu/chatgpt-web/pull/1153)]
|
13 |
+
## BugFix
|
14 |
+
- 修复 `PWA` 问题 [[bingo235](https://github.com/Chanzhaoyu/chatgpt-web/pull/807)]
|
15 |
+
- 修复 `ESM` 错误 [[kidonng](https://github.com/Chanzhaoyu/chatgpt-web/pull/826)]
|
16 |
+
- 修复反向代理开启时限流失效的问题 [[gitgitgogogo](https://github.com/Chanzhaoyu/chatgpt-web/pull/863)]
|
17 |
+
- 修复 `docker` 构建时 `.env` 可能被忽略的问题 [[zaiMoe](https://github.com/Chanzhaoyu/chatgpt-web/pull/877)]
|
18 |
+
- 修复导出异常错误 [[KingTwinkle](https://github.com/Chanzhaoyu/chatgpt-web/pull/938)]
|
19 |
+
- 修复空值异常 [[vchenpeng](https://github.com/Chanzhaoyu/chatgpt-web/pull/1103)]
|
20 |
+
- 移动端上的体验问题
|
21 |
+
|
22 |
+
## Other
|
23 |
+
- `Docker` 容器名字名义 [[LOVECHEN](https://github.com/Chanzhaoyu/chatgpt-web/pull/1035)]
|
24 |
+
- `kubernetes` 部署配置 [[CaoYunzhou](https://github.com/Chanzhaoyu/chatgpt-web/pull/1001)]
|
25 |
+
- 感谢 [[assassinliujie](https://github.com/Chanzhaoyu/chatgpt-web/pull/962)] 和 [[puppywang](https://github.com/Chanzhaoyu/chatgpt-web/pull/1017)] 的某些贡献
|
26 |
+
- 更新 `kubernetes/deploy.yaml` [[idawnwon](https://github.com/Chanzhaoyu/chatgpt-web/pull/1085)]
|
27 |
+
- 文档更新 [[#yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/883)]
|
28 |
+
- 文档更新 [[weifeng12x](https://github.com/Chanzhaoyu/chatgpt-web/pull/880)]
|
29 |
+
- 依赖更新
|
30 |
+
|
31 |
+
## v2.10.8
|
32 |
+
|
33 |
+
`2023-03-23`
|
34 |
+
|
35 |
+
如遇问题,请删除 `node_modules` 重新安装依赖。
|
36 |
+
|
37 |
+
## Feature
|
38 |
+
- 显示回复消息原文的选项 [[yilozt](https://github.com/Chanzhaoyu/chatgpt-web/pull/672)]
|
39 |
+
- 添加单 `IP` 每小时请求限制。环境变量: `MAX_REQUEST_PER_HOUR` [[zhuxindong ](https://github.com/Chanzhaoyu/chatgpt-web/pull/718)]
|
40 |
+
- 前端添加角色设定,仅 `API` 方式可见 [[quzard](https://github.com/Chanzhaoyu/chatgpt-web/pull/768)]
|
41 |
+
- `OPENAI_API_MODEL` 变量现在对 `ChatGPTUnofficialProxyAPI` 也生效,注意:`Token` 和 `API` 的模型命名不一致,不能直接填入 `gpt-3.5` 或者 `gpt-4` [[hncboy](https://github.com/Chanzhaoyu/chatgpt-web/pull/632)]
|
42 |
+
- 添加繁体中文 `Prompts` [[PeterDaveHello](https://github.com/Chanzhaoyu/chatgpt-web/pull/796)]
|
43 |
+
|
44 |
+
## Enhancement
|
45 |
+
- 重置回答时滚动定位至该回答 [[shunyue1320](https://github.com/Chanzhaoyu/chatgpt-web/pull/781)]
|
46 |
+
- 当 `API` 是 `gpt-4` 时增加可用的 `Max Tokens` [[simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/729)]
|
47 |
+
- 判断和忽略回复字符 [[liut](https://github.com/Chanzhaoyu/chatgpt-web/pull/474)]
|
48 |
+
- 切换会话时,自动聚焦输入框 [[JS-an](https://github.com/Chanzhaoyu/chatgpt-web/pull/735)]
|
49 |
+
- 渲染的链接新窗口打开
|
50 |
+
- 查询余额可选 `API_BASE_URL` 代理地址
|
51 |
+
- `config` 接口添加验证防止被无限制调用
|
52 |
+
- `PWA` 默认不开启,现在需手动修改 `.env` 文件 `VITE_GLOB_APP_PWA` 变量
|
53 |
+
- 当网络连接时,刷新页面,`500` 错误页自动跳转到主页
|
54 |
+
|
55 |
+
## BugFix
|
56 |
+
- `scrollToBottom` 调回 `scrollToBottomIfAtBottom` [[shunyue1320](https://github.com/Chanzhaoyu/chatgpt-web/pull/771)]
|
57 |
+
- 重置异常的 `loading` 会话
|
58 |
+
|
59 |
+
## Common
|
60 |
+
- 创建 `start.cmd` 在 `windows` 下也可以运行 [vulgatecnn](https://github.com/Chanzhaoyu/chatgpt-web/pull/656)]
|
61 |
+
- 添加 `visual-studio-code` 中调试配置 [[ChandlerVer5](https://github.com/Chanzhaoyu/chatgpt-web/pull/296)]
|
62 |
+
- 修复文档中 `docker` 端口为本地 [[kilvn](https://github.com/Chanzhaoyu/chatgpt-web/pull/802)]
|
63 |
+
## Other
|
64 |
+
- 依赖更新
|
65 |
+
|
66 |
+
|
67 |
+
## v2.10.7
|
68 |
+
|
69 |
+
`2023-03-17`
|
70 |
+
|
71 |
+
## BugFix
|
72 |
+
- 回退 `chatgpt` 版本,原因:导致 `OPENAI_API_BASE_URL` 代理失效
|
73 |
+
- 修复缺省状态的 `usingContext` 默认值
|
74 |
+
|
75 |
+
## v2.10.6
|
76 |
+
|
77 |
+
`2023-03-17`
|
78 |
+
|
79 |
+
## Feature
|
80 |
+
- 显示 `API` 余额 [[pzcn](https://github.com/Chanzhaoyu/chatgpt-web/pull/582)]
|
81 |
+
|
82 |
+
## Enhancement
|
83 |
+
- 美化滚动条样式和 `UI` 保持一致 [[haydenull](https://github.com/Chanzhaoyu/chatgpt-web/pull/617)]
|
84 |
+
- 优化移动端 `Prompt` 样式 [[CornerSkyless](https://github.com/Chanzhaoyu/chatgpt-web/pull/608)]
|
85 |
+
- 上下文开关改为全局开关,现在记录在本地缓存中
|
86 |
+
- 配置信息按接口类型显示
|
87 |
+
|
88 |
+
## Perf
|
89 |
+
- 优化函数方法 [[kirklin](https://github.com/Chanzhaoyu/chatgpt-web/pull/583)]
|
90 |
+
- 字符错误 [[pdsuwwz](https://github.com/Chanzhaoyu/chatgpt-web/pull/585)]
|
91 |
+
- 文档描述错误 [[lizhongyuan3](https://github.com/Chanzhaoyu/chatgpt-web/pull/636)]
|
92 |
+
|
93 |
+
## BugFix
|
94 |
+
- 修复 `Prompt` 导入、导出兼容性错误
|
95 |
+
- 修复 `highlight.js` 控制台兼容性警告
|
96 |
+
|
97 |
+
## Other
|
98 |
+
- 依赖更新
|
99 |
+
|
100 |
+
## v2.10.5
|
101 |
+
|
102 |
+
`2023-03-13`
|
103 |
+
|
104 |
+
更新依赖,`access_token` 默认代理为 [pengzhile](https://github.com/pengzhile) 的 `https://bypass.duti.tech/api/conversation`
|
105 |
+
|
106 |
+
## Feature
|
107 |
+
- `Prompt` 商店在线导入可以导入两种 `recommend.json`里提到的模板 [simonwu53](https://github.com/Chanzhaoyu/chatgpt-web/pull/521)
|
108 |
+
- 支持 `HTTPS_PROXY` [whatwewant](https://github.com/Chanzhaoyu/chatgpt-web/pull/308)
|
109 |
+
- `Prompt` 添加查询筛选
|
110 |
+
|
111 |
+
## Enhancement
|
112 |
+
- 调整输入框最大行数 [yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/502)
|
113 |
+
- 优化 `docker` 打包 [whatwewant](https://github.com/Chanzhaoyu/chatgpt-web/pull/520)
|
114 |
+
- `Prompt` 添加翻译和优化布局
|
115 |
+
- 「繁体中文」补全和审阅 [PeterDaveHello](https://github.com/Chanzhaoyu/chatgpt-web/pull/542)
|
116 |
+
- 语言选择调整为下路框形式
|
117 |
+
- 权限输入框类型调整为密码形式
|
118 |
+
|
119 |
+
## BugFix
|
120 |
+
- `JSON` 导入检查 [Nothing1024](https://github.com/Chanzhaoyu/chatgpt-web/pull/523)
|
121 |
+
- 修复 `AUTH_SECRET_KEY` 模式下跨域异常并添加对 `node.js 19` 版本的支持 [yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/499)
|
122 |
+
- 确定清空上下文时不应该重置会话标题
|
123 |
+
|
124 |
+
## Other
|
125 |
+
- 调整文档
|
126 |
+
- 更新依赖
|
127 |
+
|
128 |
+
## v2.10.4
|
129 |
+
|
130 |
+
`2023-03-11`
|
131 |
+
|
132 |
+
## Feature
|
133 |
+
- 感谢 [Nothing1024](https://github.com/Chanzhaoyu/chatgpt-web/pull/268) 添加 `Prompt` 模板和 `Prompt` 商店支持
|
134 |
+
|
135 |
+
## Enhancement
|
136 |
+
- 设置添加关闭按钮[#495]
|
137 |
+
|
138 |
+
## Demo
|
139 |
+
|
140 |
+
![Prompt](https://camo.githubusercontent.com/6a51af751eb29238cb7ef4f8fbd89f63db837562f97f33273095424e62dc9194/68747470733a2f2f73312e6c6f63696d672e636f6d2f323032332f30332f30342f333036326665633163613562632e676966)
|
141 |
+
|
142 |
+
## v2.10.3
|
143 |
+
|
144 |
+
`2023-03-10`
|
145 |
+
|
146 |
+
> 声明:除 `ChatGPTUnofficialProxyAPI` 使用的非官方代理外,本项目代码包括上游引用包均开源在 `GitHub`,如果你觉得本项目有监控后门或有问题导致你的账号、API被封,那我很抱歉。我可能`BUG`写的多,但我不缺德。此次主要为前端界面调整,周末愉快。
|
147 |
+
|
148 |
+
## Feature
|
149 |
+
- 支持长回复 [[yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/450)][[详情](https://github.com/Chanzhaoyu/chatgpt-web/pull/450)]
|
150 |
+
- 支持 `PWA` [[chenxch](https://github.com/Chanzhaoyu/chatgpt-web/pull/452)]
|
151 |
+
|
152 |
+
## Enhancement
|
153 |
+
- 调整移动端按钮和优化布局
|
154 |
+
- 调整 `iOS` 上安全距离
|
155 |
+
- 简化 `docker-compose` 部署 [[cloudGrin](https://github.com/Chanzhaoyu/chatgpt-web/pull/466)]
|
156 |
+
|
157 |
+
## BugFix
|
158 |
+
- 修复清空会话侧边栏标题不会重置的问题 [[RyanXinOne](https://github.com/Chanzhaoyu/chatgpt-web/pull/453)]
|
159 |
+
- 修复设置文字过长时导致的设置按钮消失的问题
|
160 |
+
|
161 |
+
## Other
|
162 |
+
- 更新依赖
|
163 |
+
|
164 |
+
## v2.10.2
|
165 |
+
|
166 |
+
`2023-03-09`
|
167 |
+
|
168 |
+
衔接 `2.10.1` 版本[详情](https://github.com/Chanzhaoyu/chatgpt-web/releases/tag/v2.10.1)
|
169 |
+
|
170 |
+
## Enhancement
|
171 |
+
- 移动端下输入框获得焦点时左侧按钮隐藏
|
172 |
+
|
173 |
+
## BugFix
|
174 |
+
- 修复 `2.10.1` 中添加 `OPENAI_API_MODEL` 变量的判断错误,会导致默认模型指定失效,抱歉
|
175 |
+
- 回退 `2.10.1` 中前端变量影响 `Docker` 打包
|
176 |
+
|
177 |
+
## v2.10.1
|
178 |
+
|
179 |
+
`2023-03-09`
|
180 |
+
|
181 |
+
注意:删除了 `.env` 文件改用 `.env.example` 代替,如果是手动部署的同学现在需要手动创建 `.env` 文件并从 `.env.example` 中复制需要的变量,并且 `.env` 文件现在会在 `Git` 提交中被忽略,原因如下:
|
182 |
+
|
183 |
+
- 在项目中添加 `.env` 从一开始就是个错误的示范
|
184 |
+
- 如果是 `Fork` 项目进行修改测试总是会被 `Git` 修改提示给打扰
|
185 |
+
- 感谢 [yi-ge](https://github.com/Chanzhaoyu/chatgpt-web/pull/395) 的提醒和修改
|
186 |
+
|
187 |
+
|
188 |
+
这两天开始,官方已经开始对第三方代理进行了拉闸, `accessToken` 即将或已经开始可能会不可使用。异常 `API` 使用也开始封号,封号缘由不明,如果出现使用 `API` 提示错误,请查看后端控制台信息,或留意邮箱。
|
189 |
+
|
190 |
+
## Feature
|
191 |
+
- 感谢 [CornerSkyless](https://github.com/Chanzhaoyu/chatgpt-web/pull/393) 添加是否发送上下文开关功能
|
192 |
+
|
193 |
+
## Enhancement
|
194 |
+
- 感谢 [nagaame](https://github.com/Chanzhaoyu/chatgpt-web/pull/415) 优化`docker`打包镜像文件过大的问题
|
195 |
+
- 感谢 [xieccc](https://github.com/Chanzhaoyu/chatgpt-web/pull/404) 新增 `API` 模型配置变量 `OPENAI_API_MODEL`
|
196 |
+
- 感谢 [acongee](https://github.com/Chanzhaoyu/chatgpt-web/pull/394) 优化输出时滚动条问题
|
197 |
+
|
198 |
+
## BugFix
|
199 |
+
- 感谢 [CornerSkyless](https://github.com/Chanzhaoyu/chatgpt-web/pull/392) 修复导出图片会丢失头像的问题
|
200 |
+
- 修复深色模式导出图片的样式问题
|
201 |
+
|
202 |
+
|
203 |
+
## v2.10.0
|
204 |
+
|
205 |
+
`2023-03-07`
|
206 |
+
|
207 |
+
- 老规矩,手动部署的同学需要删除 `node_modules` 安装包重新安装降低出错概率,其他部署不受影响,但是可能会有缓存问题。
|
208 |
+
- 虽然说了更新放缓,但是 `issues` 不看, `PR` 不改我睡不着,我的邮箱从每天早上`8`点到凌晨`12`永远在滴滴滴,所以求求各位,超时的`issues`自己关闭下哈,我真的需要缓冲一下。
|
209 |
+
- 演示图片请看��后
|
210 |
+
|
211 |
+
## Feature
|
212 |
+
- 添加权限功能,用法:`service/.env` 中的 `AUTH_SECRET_KEY` 变量添加密码
|
213 |
+
- 感谢 [PeterDaveHello](https://github.com/Chanzhaoyu/chatgpt-web/pull/348) 添加「繁体中文」翻译
|
214 |
+
- 感谢 [GermMC](https://github.com/Chanzhaoyu/chatgpt-web/pull/369) 添加聊天记录导入、导出、清空的功能
|
215 |
+
- 感谢 [CornerSkyless](https://github.com/Chanzhaoyu/chatgpt-web/pull/374) 添加会话保存为本地图片的功能
|
216 |
+
|
217 |
+
|
218 |
+
## Enhancement
|
219 |
+
- 感谢 [CornerSkyless](https://github.com/Chanzhaoyu/chatgpt-web/pull/363) 添加 `ctrl+enter` 发送消息
|
220 |
+
- 现在新消息只有在结束了之后才滚动到底部,而不是之前的强制性
|
221 |
+
- 优化部分代码
|
222 |
+
|
223 |
+
## BugFix
|
224 |
+
- 转义状态码前端显示,防止直接暴露 `key`(我可能需要更多的状态码补充)
|
225 |
+
|
226 |
+
## Other
|
227 |
+
- 更新依赖到最新
|
228 |
+
|
229 |
+
## 演示
|
230 |
+
> 不是界面最新效果,有美化改动
|
231 |
+
|
232 |
+
权限
|
233 |
+
|
234 |
+
![权限](https://user-images.githubusercontent.com/24789441/223438518-80d58d42-e344-4e39-b87c-251ff73925ed.png)
|
235 |
+
|
236 |
+
聊天记录导出
|
237 |
+
|
238 |
+
![聊天记录导出](https://user-images.githubusercontent.com/57023771/223372153-6d8e9ec1-d82c-42af-b4bd-232e50504a25.gif)
|
239 |
+
|
240 |
+
保存图片到本地
|
241 |
+
|
242 |
+
![保存图片到本地](https://user-images.githubusercontent.com/13901424/223423555-b69b95ef-8bcf-4951-a7c9-98aff2677e18.gif)
|
243 |
+
|
244 |
+
## v2.9.3
|
245 |
+
|
246 |
+
`2023-03-06`
|
247 |
+
|
248 |
+
## Enhancement
|
249 |
+
- 感谢 [ChandlerVer5](https://github.com/Chanzhaoyu/chatgpt-web/pull/305) 使用 `markdown-it` 替换 `marked`,解决代码块闪烁的问题
|
250 |
+
- 感谢 [shansing](https://github.com/Chanzhaoyu/chatgpt-web/pull/277) 改善文档
|
251 |
+
- 感谢 [nalf3in](https://github.com/Chanzhaoyu/chatgpt-web/pull/293) 添加英文翻译
|
252 |
+
|
253 |
+
## BugFix
|
254 |
+
- 感谢[sepcnt ](https://github.com/Chanzhaoyu/chatgpt-web/pull/279) 修复切换记录时编辑状态未关闭的问题
|
255 |
+
- 修复复制代码的兼容性报错问题
|
256 |
+
- 修复部分优化小问题
|
257 |
+
|
258 |
+
## v2.9.2
|
259 |
+
|
260 |
+
`2023-03-04`
|
261 |
+
|
262 |
+
手动部署的同学,务必删除根目录和`service`中的`node_modules`重新安装依赖,降低出现问题的概率,自动部署的不需要做改动。
|
263 |
+
|
264 |
+
### Feature
|
265 |
+
- 感谢 [hyln9](https://github.com/Chanzhaoyu/chatgpt-web/pull/247) 添加对渲染 `LaTex` 数学公式的支持
|
266 |
+
- 感谢 [ottocsb](https://github.com/Chanzhaoyu/chatgpt-web/pull/227) 添加支持 `webAPP` (苹果添加到主页书签访问)支持
|
267 |
+
- 添加 `OPENAI_API_BASE_URL` 可选环境变量[#249]
|
268 |
+
## Enhancement
|
269 |
+
- 优化在高分屏上主题内容的最大宽度[#257]
|
270 |
+
- 现在文字按单词截断[#215][#225]
|
271 |
+
### BugFix
|
272 |
+
- 修复动态生成时代码块不能被复制的问题[#251][#260]
|
273 |
+
- 修复 `iOS` 移动端输入框不会被键盘顶起的问题[#256]
|
274 |
+
- 修复控制台渲染警告
|
275 |
+
## Other
|
276 |
+
- 更新依赖至最新
|
277 |
+
- 修改 `README` 内容
|
278 |
+
|
279 |
+
## v2.9.1
|
280 |
+
|
281 |
+
`2023-03-02`
|
282 |
+
|
283 |
+
### Feature
|
284 |
+
- 代码块添加当前代码语言显示和复制功能[#197][#196]
|
285 |
+
- 完善多语言,现在可以切换中英文显示
|
286 |
+
|
287 |
+
## Enhancement
|
288 |
+
- 由[Zo3i](https://github.com/Chanzhaoyu/chatgpt-web/pull/187) 完善 `docker-compose` 部署文档
|
289 |
+
|
290 |
+
### BugFix
|
291 |
+
- 由 [ottocsb](https://github.com/Chanzhaoyu/chatgpt-web/pull/200) 修复头像修改不同步的问题
|
292 |
+
## Other
|
293 |
+
- 更新依赖至最新
|
294 |
+
- 修改 `README` 内容
|
295 |
+
## v2.9.0
|
296 |
+
|
297 |
+
`2023-03-02`
|
298 |
+
|
299 |
+
### Feature
|
300 |
+
- 现在能复制带格式的消息文本
|
301 |
+
- 新设计的设定页面,可以自定义姓名、描述、头像(链接方式)
|
302 |
+
- 新增`403`和`404`页面以便扩展
|
303 |
+
|
304 |
+
## Enhancement
|
305 |
+
- 更新 `chatgpt` 使 `ChatGPTAPI` 支持 `gpt-3.5-turbo-0301`(默认)
|
306 |
+
- 取消了前端超时限制设定
|
307 |
+
|
308 |
+
## v2.8.3
|
309 |
+
|
310 |
+
`2023-03-01`
|
311 |
+
|
312 |
+
### Feature
|
313 |
+
- 消息已输出内容不会因为中断而消失[#167]
|
314 |
+
- 添加复制消息按钮[#133]
|
315 |
+
|
316 |
+
### Other
|
317 |
+
- `README` 添加声明内容
|
318 |
+
|
319 |
+
## v2.8.2
|
320 |
+
|
321 |
+
`2023-02-28`
|
322 |
+
### Enhancement
|
323 |
+
- 代码主题调整为 `One Dark - light|dark` 适配深色模式
|
324 |
+
### BugFix
|
325 |
+
- 修复普通文本代码渲染和深色模式下的问题[#139][#154]
|
326 |
+
|
327 |
+
## v2.8.1
|
328 |
+
|
329 |
+
`2023-02-27`
|
330 |
+
|
331 |
+
### BugFix
|
332 |
+
- 修复 `API` 版本不是 `Markdown` 时,普通 `HTML` 代码会被渲染的问题 [#146]
|
333 |
+
|
334 |
+
## v2.8.0
|
335 |
+
|
336 |
+
`2023-02-27`
|
337 |
+
|
338 |
+
- 感谢 [puppywang](https://github.com/Chanzhaoyu/chatgpt-web/commit/628187f5c3348bda0d0518f90699a86525d19018) 修复了 `2.7.0` 版本中关于流输出数据的问题(使用 `nginx` 需要自行配置 `octet-stream` 相关内容)
|
339 |
+
|
340 |
+
- 关于为什么使用 `octet-stream` 而不是 `sse`,是因为更好的兼容之前的模式。
|
341 |
+
|
342 |
+
- 建议更新到此版本获得比较完整的体验
|
343 |
+
|
344 |
+
### Enhancement
|
345 |
+
- 优化了部份代码和类型提示
|
346 |
+
- 输入框添加换行提示
|
347 |
+
- 移动端输入框现在回车为换行,而不是直接提交
|
348 |
+
- 移动端双击标题返回顶部,箭头返回底部
|
349 |
+
|
350 |
+
### BugFix
|
351 |
+
- 流输出数据下的问题[#122]
|
352 |
+
- 修复了 `API Key` 下部份代码不换行的问题
|
353 |
+
- 修复移动端深色模式部份样式问题[#123][#126]
|
354 |
+
- 修复主题模式图标不一致的问题[#126]
|
355 |
+
|
356 |
+
## v2.7.3
|
357 |
+
|
358 |
+
`2023-02-25`
|
359 |
+
|
360 |
+
### Feature
|
361 |
+
- 适配系统深色模式 [#118](https://github.com/Chanzhaoyu/chatgpt-web/issues/103)
|
362 |
+
### BugFix
|
363 |
+
- 修复用户消息能被渲染为 `HTML` 问题 [#117](https://github.com/Chanzhaoyu/chatgpt-web/issues/117)
|
364 |
+
|
365 |
+
## v2.7.2
|
366 |
+
|
367 |
+
`2023-02-24`
|
368 |
+
### Enhancement
|
369 |
+
- 消息使用 [github-markdown-css](https://www.npmjs.com/package/github-markdown-css) 进行美化,现在支持全语法
|
370 |
+
- 移除测试无用函数
|
371 |
+
|
372 |
+
## v2.7.1
|
373 |
+
|
374 |
+
`2023-02-23`
|
375 |
+
|
376 |
+
因为消息流在 `accessToken` 中存在解析失败和消息不完整等一系列的问题,调整回正常消息形式
|
377 |
+
|
378 |
+
### Feature
|
379 |
+
- 现在可以中断请求过长没有答复的消息
|
380 |
+
- 现在可以删除单条消息
|
381 |
+
- 设置中显示当前版本信息
|
382 |
+
|
383 |
+
### BugFix
|
384 |
+
- 回退 `2.7.0` 的消息不稳定的问题
|
385 |
+
|
386 |
+
## v2.7.0
|
387 |
+
|
388 |
+
`2023-02-23`
|
389 |
+
|
390 |
+
### Feature
|
391 |
+
- 使用消息流返回信息,反应更迅速
|
392 |
+
|
393 |
+
### Enhancement
|
394 |
+
- 样式的一点小改动
|
395 |
+
|
396 |
+
## v2.6.2
|
397 |
+
|
398 |
+
`2023-02-22`
|
399 |
+
### BugFix
|
400 |
+
- 还原修改代理导致的异常问题
|
401 |
+
|
402 |
+
## v2.6.1
|
403 |
+
|
404 |
+
`2023-02-22`
|
405 |
+
|
406 |
+
### Feature
|
407 |
+
- 新增 `Railway` 部署模版
|
408 |
+
|
409 |
+
### BugFix
|
410 |
+
- 手动打包 `Proxy` 问题
|
411 |
+
|
412 |
+
## v2.6.0
|
413 |
+
|
414 |
+
`2023-02-21`
|
415 |
+
### Feature
|
416 |
+
- 新增对 `网页 accessToken` 调用 `ChatGPT`,更智能不过不太稳定 [#51](https://github.com/Chanzhaoyu/chatgpt-web/issues/51)
|
417 |
+
- 前端页面设置按钮显示查看当前后端服务配置
|
418 |
+
|
419 |
+
### Enhancement
|
420 |
+
- 新增 `TIMEOUT_MS` 环境变量设定后端超时时常(单位:毫秒)[#62](https://github.com/Chanzhaoyu/chatgpt-web/issues/62)
|
421 |
+
|
422 |
+
## v2.5.2
|
423 |
+
|
424 |
+
`2023-02-21`
|
425 |
+
### Feature
|
426 |
+
- 增加对 `markdown` 格式的支持 [Demo](https://github.com/Chanzhaoyu/chatgpt-web/pull/77)
|
427 |
+
### BugFix
|
428 |
+
- 重载会话时滚动条保持
|
429 |
+
|
430 |
+
## v2.5.1
|
431 |
+
|
432 |
+
`2023-02-21`
|
433 |
+
|
434 |
+
### Enhancement
|
435 |
+
- 调整路由模式为 `hash`
|
436 |
+
- 调整新增会话添加到
|
437 |
+
- 调整移动端样式
|
438 |
+
|
439 |
+
|
440 |
+
## v2.5.0
|
441 |
+
|
442 |
+
`2023-02-20`
|
443 |
+
|
444 |
+
### Feature
|
445 |
+
- 会话 `loading` 现在显示为光标动画
|
446 |
+
- 会话现在可以再次生成回复
|
447 |
+
- 会话异常可以再次进行请求
|
448 |
+
- 所有删除选项添加确认操作
|
449 |
+
|
450 |
+
### Enhancement
|
451 |
+
- 调整 `chat` 为路由页面而不是组件形式
|
452 |
+
- 更新依赖至最新
|
453 |
+
- 调整移动端体验
|
454 |
+
|
455 |
+
### BugFix
|
456 |
+
- 修复移动端左侧菜单显示不完整的问题
|
457 |
+
|
458 |
+
## v2.4.1
|
459 |
+
|
460 |
+
`2023-02-18`
|
461 |
+
|
462 |
+
### Enhancement
|
463 |
+
- 调整部份移动端上的样式
|
464 |
+
- 输入框支持换行
|
465 |
+
|
466 |
+
## v2.4.0
|
467 |
+
|
468 |
+
`2023-02-17`
|
469 |
+
|
470 |
+
### Feature
|
471 |
+
- 响应式支持移动端
|
472 |
+
### Enhancement
|
473 |
+
- 修改部份描述错误
|
474 |
+
|
475 |
+
## v2.3.3
|
476 |
+
|
477 |
+
`2023-02-16`
|
478 |
+
|
479 |
+
### Feature
|
480 |
+
- 添加 `README` 部份说明和贡献列表
|
481 |
+
- 添加 `docker` 镜像
|
482 |
+
- 添加 `GitHub Action` 自动化构建
|
483 |
+
|
484 |
+
### BugFix
|
485 |
+
- 回退依赖更新导致的 [Eslint 报错](https://github.com/eslint/eslint/issues/16896)
|
486 |
+
|
487 |
+
## v2.3.2
|
488 |
+
|
489 |
+
`2023-02-16`
|
490 |
+
|
491 |
+
### Enhancement
|
492 |
+
- 更新依赖至最新
|
493 |
+
- 优化部份内容
|
494 |
+
|
495 |
+
## v2.3.1
|
496 |
+
|
497 |
+
`2023-02-15`
|
498 |
+
|
499 |
+
### BugFix
|
500 |
+
- 修复多会话状态下一些意想不到的问题
|
501 |
+
|
502 |
+
## v2.3.0
|
503 |
+
|
504 |
+
`2023-02-15`
|
505 |
+
### Feature
|
506 |
+
- 代码类型信息高亮显示
|
507 |
+
- 支持 `node ^16` 版本
|
508 |
+
- 移动端响应式初步支持
|
509 |
+
- `vite` 中 `proxy` 代理
|
510 |
+
|
511 |
+
### Enhancement
|
512 |
+
- 调整超时处理范围
|
513 |
+
|
514 |
+
### BugFix
|
515 |
+
- 修复取消请求错误提示会添加到信息中
|
516 |
+
- 修复部份情况下提交请求不可用
|
517 |
+
- 修复侧边栏宽度变化闪烁的问题
|
518 |
+
|
519 |
+
## v2.2.0
|
520 |
+
|
521 |
+
`2023-02-14`
|
522 |
+
### Feature
|
523 |
+
- 会话和上下文本地储存
|
524 |
+
- 侧边栏本地储存
|
525 |
+
|
526 |
+
## v2.1.0
|
527 |
+
|
528 |
+
`2023-02-14`
|
529 |
+
### Enhancement
|
530 |
+
- 更新依赖至最新
|
531 |
+
- 联想功能移动至前端提交,后端只做转发
|
532 |
+
|
533 |
+
### BugFix
|
534 |
+
- 修复部份项目检测有关 `Bug`
|
535 |
+
- 修复清除上下文按钮失效
|
536 |
+
|
537 |
+
## v2.0.0
|
538 |
+
|
539 |
+
`2023-02-13`
|
540 |
+
### Refactor
|
541 |
+
重构并优化大部分内容
|
542 |
+
|
543 |
+
## v1.0.5
|
544 |
+
|
545 |
+
`2023-02-12`
|
546 |
+
|
547 |
+
### Enhancement
|
548 |
+
- 输入框焦点,连续提交
|
549 |
+
|
550 |
+
### BugFix
|
551 |
+
- 修复信息框样式问题
|
552 |
+
- 修复中文输入法提交问题
|
553 |
+
|
554 |
+
## v1.0.4
|
555 |
+
|
556 |
+
`2023-02-11`
|
557 |
+
|
558 |
+
### Feature
|
559 |
+
- 支持上下文联想
|
560 |
+
|
561 |
+
## v1.0.3
|
562 |
+
|
563 |
+
`2023-02-11`
|
564 |
+
|
565 |
+
### Enhancement
|
566 |
+
- 拆分 `service` 文件以便扩展
|
567 |
+
- 调整 `Eslint` 相关验证
|
568 |
+
|
569 |
+
### BugFix
|
570 |
+
- 修复部份控制台报错
|
571 |
+
|
572 |
+
## v1.0.2
|
573 |
+
|
574 |
+
`2023-02-10`
|
575 |
+
|
576 |
+
### BugFix
|
577 |
+
- 修复新增信息容器不会自动滚动到问题
|
578 |
+
- 修复文本过长不换行到问题 [#1](https://github.com/Chanzhaoyu/chatgpt-web/issues/1)
|
CONTRIBUTING.en.md
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Contribution Guide
|
2 |
+
Thank you for your valuable time. Your contributions will make this project better! Before submitting a contribution, please take some time to read the getting started guide below.
|
3 |
+
|
4 |
+
## Semantic Versioning
|
5 |
+
This project follows semantic versioning. We release patch versions for important bug fixes, minor versions for new features or non-important changes, and major versions for significant and incompatible changes.
|
6 |
+
|
7 |
+
Each major change will be recorded in the `changelog`.
|
8 |
+
|
9 |
+
## Submitting Pull Request
|
10 |
+
1. Fork [this repository](https://github.com/Chanzhaoyu/chatgpt-web) and create a branch from `main`. For new feature implementations, submit a pull request to the `feature` branch. For other changes, submit to the `main` branch.
|
11 |
+
2. Install the `pnpm` tool using `npm install pnpm -g`.
|
12 |
+
3. Install the `Eslint` plugin for `VSCode`, or enable `eslint` functionality for other editors such as `WebStorm`.
|
13 |
+
4. Execute `pnpm bootstrap` in the root directory.
|
14 |
+
5. Execute `pnpm install` in the `/service/` directory.
|
15 |
+
6. Make changes to the codebase. If applicable, ensure that appropriate testing has been done.
|
16 |
+
7. Execute `pnpm lint:fix` in the root directory to perform a code formatting check.
|
17 |
+
8. Execute `pnpm type-check` in the root directory to perform a type check.
|
18 |
+
9. Submit a git commit, following the [Commit Guidelines](#commit-guidelines).
|
19 |
+
10. Submit a `pull request`. If there is a corresponding `issue`, please link it using the [linking-a-pull-request-to-an-issue keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword).
|
20 |
+
|
21 |
+
## Commit Guidelines
|
22 |
+
|
23 |
+
Commit messages should follow the [conventional-changelog standard](https://www.conventionalcommits.org/en/v1.0.0/):
|
24 |
+
|
25 |
+
```bash
|
26 |
+
<type>[optional scope]: <description>
|
27 |
+
|
28 |
+
[optional body]
|
29 |
+
|
30 |
+
[optional footer]
|
31 |
+
```
|
32 |
+
|
33 |
+
### Commit Types
|
34 |
+
|
35 |
+
The following is a list of commit types:
|
36 |
+
|
37 |
+
- feat: New feature or functionality
|
38 |
+
- fix: Bug fix
|
39 |
+
- docs: Documentation update
|
40 |
+
- style: Code style or component style update
|
41 |
+
- refactor: Code refactoring, no new features or bug fixes introduced
|
42 |
+
- perf: Performance optimization
|
43 |
+
- test: Unit test
|
44 |
+
- chore: Other commits that do not modify src or test files
|
45 |
+
|
46 |
+
|
47 |
+
## License
|
48 |
+
|
49 |
+
[MIT](./license)
|
CONTRIBUTING.md
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 贡献指南
|
2 |
+
感谢你的宝贵时间。你的贡献将使这个项目变得更好!在提交贡献之前,请务必花点时间阅读下面的入门指南。
|
3 |
+
|
4 |
+
## 语义化版本
|
5 |
+
该项目遵循语义化版本。我们对重要的漏洞修复发布修订号,对新特性或不重要的变更发布次版本号,对重大且不兼容的变更发布主版本号。
|
6 |
+
|
7 |
+
每个重大更改都将记录在 `changelog` 中。
|
8 |
+
|
9 |
+
## 提交 Pull Request
|
10 |
+
1. Fork [此仓库](https://github.com/Chanzhaoyu/chatgpt-web),从 `main` 创建分支。新功能实现请发 pull request 到 `feature` 分支。其他更改发到 `main` 分支。
|
11 |
+
2. 使用 `npm install pnpm -g` 安装 `pnpm` 工具。
|
12 |
+
3. `vscode` 安装了 `Eslint` 插件,其它编辑器如 `webStorm` 打开了 `eslint` 功能。
|
13 |
+
4. 根目录下执行 `pnpm bootstrap`。
|
14 |
+
5. `/service/` 目录下执行 `pnpm install`。
|
15 |
+
6. 对代码库进行更改。如果适用的话,请确保进行了相应的测试。
|
16 |
+
7. 请在根目录下执行 `pnpm lint:fix` 进行代码格式检查。
|
17 |
+
8. 请在根目录下执行 `pnpm type-check` 进行类型检查。
|
18 |
+
9. 提交 git commit, 请同时遵守 [Commit 规范](#commit-指南)
|
19 |
+
10. 提交 `pull request`, 如果有对应的 `issue`,请进行[关联](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)。
|
20 |
+
|
21 |
+
## Commit 指南
|
22 |
+
|
23 |
+
Commit messages 请遵循[conventional-changelog 标准](https://www.conventionalcommits.org/en/v1.0.0/):
|
24 |
+
|
25 |
+
```bash
|
26 |
+
<类型>[可选 范围]: <描述>
|
27 |
+
|
28 |
+
[可选 正文]
|
29 |
+
|
30 |
+
[可选 脚注]
|
31 |
+
```
|
32 |
+
|
33 |
+
### Commit 类型
|
34 |
+
|
35 |
+
以下是 commit 类型列表:
|
36 |
+
|
37 |
+
- feat: 新特性或功能
|
38 |
+
- fix: 缺陷修复
|
39 |
+
- docs: 文档更新
|
40 |
+
- style: 代码风格或者组件样式更新
|
41 |
+
- refactor: 代码重构,不引入新功能和缺陷修复
|
42 |
+
- perf: 性能优化
|
43 |
+
- test: 单元测试
|
44 |
+
- chore: 其他不修改 src 或测试文件的提交
|
45 |
+
|
46 |
+
|
47 |
+
## License
|
48 |
+
|
49 |
+
[MIT](./license)
|
Dockerfile
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# build front-end
|
2 |
+
FROM node:lts-alpine AS frontend
|
3 |
+
|
4 |
+
RUN npm install pnpm -g
|
5 |
+
|
6 |
+
WORKDIR /app
|
7 |
+
|
8 |
+
COPY ./package.json /app
|
9 |
+
|
10 |
+
COPY ./pnpm-lock.yaml /app
|
11 |
+
|
12 |
+
RUN pnpm install
|
13 |
+
|
14 |
+
COPY . /app
|
15 |
+
|
16 |
+
RUN pnpm run build
|
17 |
+
|
18 |
+
# build backend
|
19 |
+
FROM node:lts-alpine as backend
|
20 |
+
|
21 |
+
RUN npm install pnpm -g
|
22 |
+
|
23 |
+
WORKDIR /app
|
24 |
+
|
25 |
+
COPY /service/package.json /app
|
26 |
+
|
27 |
+
COPY /service/pnpm-lock.yaml /app
|
28 |
+
|
29 |
+
RUN pnpm install
|
30 |
+
|
31 |
+
COPY /service /app
|
32 |
+
|
33 |
+
RUN pnpm build
|
34 |
+
|
35 |
+
# service
|
36 |
+
FROM node:lts-alpine
|
37 |
+
|
38 |
+
RUN npm install pnpm -g
|
39 |
+
|
40 |
+
WORKDIR /app
|
41 |
+
|
42 |
+
COPY /service/package.json /app
|
43 |
+
|
44 |
+
COPY /service/pnpm-lock.yaml /app
|
45 |
+
|
46 |
+
RUN pnpm install --production && rm -rf /root/.npm /root/.pnpm-store /usr/local/share/.cache /tmp/*
|
47 |
+
|
48 |
+
COPY /service /app
|
49 |
+
|
50 |
+
COPY --from=frontend /app/replace-title.sh /app
|
51 |
+
|
52 |
+
RUN chmod +x /app/replace-title.sh
|
53 |
+
|
54 |
+
COPY --from=frontend /app/dist /app/public
|
55 |
+
|
56 |
+
COPY --from=backend /app/build /app/build
|
57 |
+
|
58 |
+
COPY --from=backend /app/src/utils/templates /app/build/templates
|
59 |
+
|
60 |
+
EXPOSE 3002
|
61 |
+
|
62 |
+
RUN --mount=type=secret,id=AUTH_SECRET_KEY,mode=0444,required=true
|
63 |
+
RUN --mount=type=secret,id=API_REVERSE_PROXY,mode=0444,required=true
|
64 |
+
RUN --mount=type=secret,id=OPENAI_ACCESS_TOKEN,mode=0444,required=true
|
65 |
+
RUN --mount=type=secret,id=MONGODB_URL,mode=0444,required=true
|
66 |
+
RUN --mount=type=secret,id=OPENAI_API_MODEL,mode=0444,required=true
|
67 |
+
RUN --mount=type=secret,id=PASSWORD_MD5_SALT,mode=0444,required=true
|
68 |
+
RUN --mount=type=secret,id=REGISTER_ENABLED,mode=0444,required=true
|
69 |
+
RUN --mount=type=secret,id=ROOT_USER,mode=0444,required=true
|
70 |
+
RUN --mount=type=secret,id=SITE_DOMAIN,mode=0444,required=true
|
71 |
+
RUN --mount=type=secret,id=SITE_TITLE,mode=0444,required=true
|
72 |
+
RUN --mount=type=secret,id=SMTP_HOST,mode=0444,required=true
|
73 |
+
RUN --mount=type=secret,id=SMTP_PASSWORD,mode=0444,required=true
|
74 |
+
RUN --mount=type=secret,id=SMTP_PORT,mode=0444,required=true
|
75 |
+
RUN --mount=type=secret,id=SMTP_TSL,mode=0444,required=true
|
76 |
+
RUN --mount=type=secret,id=SMTP_USERNAME,mode=0444,required=true
|
77 |
+
|
78 |
+
CMD ["sh", "-c", "./replace-title.sh && pnpm run prod"]
|
README.en.md
ADDED
@@ -0,0 +1,410 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ChatGPT Web
|
2 |
+
|
3 |
+
<div style="font-size: 1.5rem;">
|
4 |
+
<a href="./README.md">中文</a> |
|
5 |
+
<a href="./README.en.md">English</a>
|
6 |
+
</div>
|
7 |
+
</br>
|
8 |
+
|
9 |
+
## Introduction
|
10 |
+
> **This project is forked from [Chanzhaoyu/chatgpt-web](https://github.com/Chanzhaoyu/chatgpt-web), some unique features have been added:**
|
11 |
+
|
12 |
+
[✓] Register & Login & Reset Password
|
13 |
+
|
14 |
+
[✓] Sync chat history
|
15 |
+
|
16 |
+
[✓] Front-end page setting apikey
|
17 |
+
|
18 |
+
[✓] Custom Sensitive Words
|
19 |
+
|
20 |
+
[✓] Set unique prompts for each chat room
|
21 |
+
|
22 |
+
[✓] Users manager
|
23 |
+
|
24 |
+
[✓] Random Key
|
25 |
+
|
26 |
+
</br>
|
27 |
+
|
28 |
+
## Screenshots
|
29 |
+
> Disclaimer: This project is only released on GitHub, under the MIT License, free and for open-source learning purposes. There will be no account selling, paid services, discussion groups, or forums. Beware of fraud.
|
30 |
+
|
31 |
+
![cover3](./docs/login.jpg)
|
32 |
+
![cover](./docs/c1.png)
|
33 |
+
![cover2](./docs/c2.png)
|
34 |
+
![cover3](./docs/basesettings.jpg)
|
35 |
+
![cover3](./docs/prompt_en.jpg)
|
36 |
+
![cover3](./docs/user-manager.jpg)
|
37 |
+
![cover3](./docs/key-manager-en.jpg)
|
38 |
+
|
39 |
+
- [ChatGPT Web](#chatgpt-web)
|
40 |
+
- [Introduction](#introduction)
|
41 |
+
- [Roadmap](#roadmap)
|
42 |
+
- [Prerequisites](#prerequisites)
|
43 |
+
- [Node](#node)
|
44 |
+
- [PNPM](#pnpm)
|
45 |
+
- [Fill in the Keys](#fill-in-the-keys)
|
46 |
+
- [Install Dependencies](#install-dependencies)
|
47 |
+
- [Backend](#backend)
|
48 |
+
- [Frontend](#frontend)
|
49 |
+
- [Run in Test Environment](#run-in-test-environment)
|
50 |
+
- [Backend Service](#backend-service)
|
51 |
+
- [Frontend Webpage](#frontend-webpage)
|
52 |
+
- [Packaging](#packaging)
|
53 |
+
- [Using Docker](#using-docker)
|
54 |
+
- [Docker Parameter Example](#docker-parameter-example)
|
55 |
+
- [Docker Build \& Run](#docker-build--run)
|
56 |
+
- [Docker Compose](#docker-compose)
|
57 |
+
- [Deployment with Railway](#deployment-with-railway)
|
58 |
+
- [Railway Environment Variables](#railway-environment-variables)
|
59 |
+
- [Manual packaging](#manual-packaging)
|
60 |
+
- [Backend service](#backend-service-1)
|
61 |
+
- [Frontend webpage](#frontend-webpage-1)
|
62 |
+
- [Frequently Asked Questions](#frequently-asked-questions)
|
63 |
+
- [Contributing](#contributing)
|
64 |
+
- [Sponsorship](#sponsorship)
|
65 |
+
- [License](#license)
|
66 |
+
|
67 |
+
## Introduction
|
68 |
+
|
69 |
+
Supports dual models, provides two unofficial `ChatGPT API` methods:
|
70 |
+
|
71 |
+
| Method | Free? | Reliability | Quality |
|
72 |
+
| --------------------------------------------- | ------ | ----------- | ------- |
|
73 |
+
| `ChatGPTAPI(gpt-3.5-turbo-0301)` | No | Reliable | Relatively clumsy |
|
74 |
+
| `ChatGPTUnofficialProxyAPI(Web accessToken)` | Yes | Relatively unreliable | Smart |
|
75 |
+
|
76 |
+
Comparison:
|
77 |
+
1. `ChatGPTAPI` uses `gpt-3.5-turbo-0301` to simulate `ChatGPT` through the official `OpenAI` completion `API` (the most reliable method, but it is not free and does not use models specifically tuned for chat).
|
78 |
+
2. `ChatGPTUnofficialProxyAPI` accesses `ChatGPT`'s backend `API` via an unofficial proxy server to bypass `Cloudflare` (uses the real `ChatGPT`, is very lightweight, but depends on third-party servers and has rate limits).
|
79 |
+
|
80 |
+
[Details](https://github.com/Chanzhaoyu/chatgpt-web/issues/138)
|
81 |
+
|
82 |
+
Switching Methods:
|
83 |
+
1. Go to the `service/.env.example` file and copy the contents to the `service/.env` file.
|
84 |
+
2. For `OpenAI API Key`, fill in the `OPENAI_API_KEY` field [(Get apiKey)](https://platform.openai.com/overview).
|
85 |
+
3. For `Web API`, fill in the `OPENAI_ACCESS_TOKEN` field [(Get accessToken)](https://chat.openai.com/api/auth/session).
|
86 |
+
4. When both are present, `OpenAI API Key` takes precedence.
|
87 |
+
|
88 |
+
Reverse Proxy:
|
89 |
+
|
90 |
+
Available when using `ChatGPTUnofficialProxyAPI`.[Details](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)
|
91 |
+
|
92 |
+
```shell
|
93 |
+
# service/.env
|
94 |
+
API_REVERSE_PROXY=
|
95 |
+
```
|
96 |
+
|
97 |
+
Environment Variables:
|
98 |
+
|
99 |
+
For all parameter variables, check [here](#docker-parameter-example) or see:
|
100 |
+
|
101 |
+
```
|
102 |
+
/service/.env
|
103 |
+
```
|
104 |
+
|
105 |
+
## Roadmap
|
106 |
+
[✓] Dual models
|
107 |
+
|
108 |
+
[✓] Multiple session storage and context logic
|
109 |
+
|
110 |
+
[✓] Formatting and beautifying code-like message types
|
111 |
+
|
112 |
+
[✓] Login or Register
|
113 |
+
|
114 |
+
[✓] Set API key and other information on the front-end page.
|
115 |
+
|
116 |
+
[✓] Data import and export
|
117 |
+
|
118 |
+
[✓] Save message to local image
|
119 |
+
|
120 |
+
[✓] Multilingual interface
|
121 |
+
|
122 |
+
[✓] Interface themes
|
123 |
+
|
124 |
+
[✗] More...
|
125 |
+
|
126 |
+
## Prerequisites
|
127 |
+
|
128 |
+
### Node
|
129 |
+
|
130 |
+
`node` requires version `^16 || ^18` (`node >= 14` requires installation of [fetch polyfill](https://github.com/developit/unfetch#usage-as-a-polyfill)), and multiple local `node` versions can be managed using [nvm](https://github.com/nvm-sh/nvm).
|
131 |
+
|
132 |
+
```shell
|
133 |
+
node -v
|
134 |
+
```
|
135 |
+
|
136 |
+
### PNPM
|
137 |
+
If you have not installed `pnpm` before:
|
138 |
+
```shell
|
139 |
+
npm install pnpm -g
|
140 |
+
```
|
141 |
+
|
142 |
+
### Fill in the Keys
|
143 |
+
|
144 |
+
Get `Openai Api Key` or `accessToken` and fill in the local environment variables [jump](#introduction)
|
145 |
+
|
146 |
+
```
|
147 |
+
# service/.env file
|
148 |
+
|
149 |
+
# OpenAI API Key - https://platform.openai.com/overview
|
150 |
+
OPENAI_API_KEY=
|
151 |
+
|
152 |
+
# change this to an `accessToken` extracted from the ChatGPT site's `https://chat.openai.com/api/auth/session` response
|
153 |
+
OPENAI_ACCESS_TOKEN=
|
154 |
+
```
|
155 |
+
|
156 |
+
## Install Dependencies
|
157 |
+
|
158 |
+
> To make it easier for `backend developers` to understand, we did not use the front-end `workspace` mode, but stored it in different folders. If you only need to do secondary development of the front-end page, delete the `service` folder.
|
159 |
+
|
160 |
+
### Backend
|
161 |
+
|
162 |
+
Enter the `/service` folder and run the following command
|
163 |
+
|
164 |
+
```shell
|
165 |
+
pnpm install
|
166 |
+
```
|
167 |
+
|
168 |
+
### Frontend
|
169 |
+
Run the following command in the root directory
|
170 |
+
```shell
|
171 |
+
pnpm bootstrap
|
172 |
+
```
|
173 |
+
|
174 |
+
## Run in Test Environment
|
175 |
+
### Backend Service
|
176 |
+
|
177 |
+
Enter the `/service` folder and run the following command
|
178 |
+
|
179 |
+
```shell
|
180 |
+
pnpm start
|
181 |
+
```
|
182 |
+
|
183 |
+
### Frontend Webpage
|
184 |
+
Run the following command in the root directory
|
185 |
+
```shell
|
186 |
+
pnpm dev
|
187 |
+
```
|
188 |
+
|
189 |
+
## Packaging
|
190 |
+
|
191 |
+
### Using Docker
|
192 |
+
|
193 |
+
#### Docker Parameter Example
|
194 |
+
|
195 |
+
- `OPENAI_API_KEY` one of two
|
196 |
+
- `OPENAI_ACCESS_TOKEN` one of two, `OPENAI_API_KEY` takes precedence when both are present
|
197 |
+
- `OPENAI_API_BASE_URL` optional, available when `OPENAI_API_KEY` is set
|
198 |
+
- `OPENAI_API_MODEL` `ChatGPTAPI` OR `ChatGPTUnofficialProxyAPI`
|
199 |
+
- `API_REVERSE_PROXY` optional, available when `OPENAI_ACCESS_TOKEN` is set [Reference](#introduction)
|
200 |
+
- `AUTH_SECRET_KEY` Access Password,optional
|
201 |
+
- `TIMEOUT_MS` timeout, in milliseconds, optional
|
202 |
+
- `SOCKS_PROXY_HOST` optional, effective with SOCKS_PROXY_PORT
|
203 |
+
- `SOCKS_PROXY_PORT` optional, effective with SOCKS_PROXY_HOST
|
204 |
+
- `SOCKS_PROXY_USERNAME` optional, effective with SOCKS_PROXY_HOST and SOCKS_PROXY_PORT
|
205 |
+
- `SOCKS_PROXY_PASSWORD` optional, effective with SOCKS_PROXY_HOST and SOCKS_PROXY_PORT
|
206 |
+
- `HTTPS_PROXY` optional, support http,https, socks5
|
207 |
+
|
208 |
+
![docker](./docs/docker.png)
|
209 |
+
|
210 |
+
#### Docker Build & Run
|
211 |
+
|
212 |
+
```bash
|
213 |
+
docker build -t chatgpt-web .
|
214 |
+
|
215 |
+
# foreground operation
|
216 |
+
docker run --name chatgpt-web --rm -it -p 127.0.0.1:3002:3002 --env OPENAI_API_KEY=your_api_key chatgpt-web
|
217 |
+
|
218 |
+
# background operation
|
219 |
+
docker run --name chatgpt-web -d -p 127.0.0.1:3002:3002 --env OPENAI_API_KEY=your_api_key chatgpt-web
|
220 |
+
|
221 |
+
# running address
|
222 |
+
http://localhost:3002/
|
223 |
+
```
|
224 |
+
|
225 |
+
#### Docker Compose
|
226 |
+
|
227 |
+
[Hub Address](https://hub.docker.com/repository/docker/kerwin1202/chatgpt-web/general)
|
228 |
+
|
229 |
+
```yml
|
230 |
+
version: '3'
|
231 |
+
|
232 |
+
services:
|
233 |
+
app:
|
234 |
+
image: kerwin1202/chatgpt-web # always use latest, pull the tag image again when updating
|
235 |
+
container_name: chatgptweb
|
236 |
+
restart: unless-stopped
|
237 |
+
ports:
|
238 |
+
- 3002:3002
|
239 |
+
depends_on:
|
240 |
+
- database
|
241 |
+
environment:
|
242 |
+
TZ: Asia/Shanghai
|
243 |
+
# one of two
|
244 |
+
OPENAI_API_KEY: xxxxxx
|
245 |
+
# one of two
|
246 |
+
OPENAI_ACCESS_TOKEN: xxxxxx
|
247 |
+
# api interface url, optional, available when OPENAI_API_KEY is set
|
248 |
+
OPENAI_API_BASE_URL: xxxx
|
249 |
+
# ChatGPTAPI 或者 ChatGPTUnofficialProxyAPI
|
250 |
+
OPENAI_API_MODEL: xxxx
|
251 |
+
# reverse proxy, optional
|
252 |
+
API_REVERSE_PROXY: xxx
|
253 |
+
# timeout, in milliseconds, optional
|
254 |
+
TIMEOUT_MS: 600000
|
255 |
+
# socks proxy, optional, effective with SOCKS_PROXY_PORT
|
256 |
+
SOCKS_PROXY_HOST: xxxx
|
257 |
+
# socks proxy port, optional, effective with SOCKS_PROXY_HOST
|
258 |
+
SOCKS_PROXY_PORT: xxxx
|
259 |
+
# socks proxy, optional, effective with SOCKS_PROXY_HOST and SOCKS_PROXY_PORT
|
260 |
+
SOCKS_PROXY_USERNAME: xxxx
|
261 |
+
# socks proxy port, optional, effective with SOCKS_PROXY_HOST and SOCKS_PROXY_PORT
|
262 |
+
SOCKS_PROXY_PASSWORD: xxxx
|
263 |
+
# HTTPS Proxy,optional, support http, https, socks5
|
264 |
+
HTTPS_PROXY: http://xxx:7890
|
265 |
+
# Title for site
|
266 |
+
SITE_TITLE: ChatGpt Web
|
267 |
+
# access salt,optional Allow login if not empty.
|
268 |
+
AUTH_SECRET_KEY: xxx
|
269 |
+
# mongodb's connection string
|
270 |
+
MONGODB_URL: 'mongodb://chatgpt:xxxx@database:27017'
|
271 |
+
# Register enabled
|
272 |
+
REGISTER_ENABLED: true
|
273 |
+
# After register enabled, Allowed mailbox suffixes for website registration. If empty, any suffix is allowed
|
274 |
+
REGISTER_MAILS: '@qq.com,@sina.com,@163.com'
|
275 |
+
# After register enabled, Salt for password encryption
|
276 |
+
PASSWORD_MD5_SALT: xxx
|
277 |
+
# After register enabled, super administrator
|
278 |
+
ROOT_USER: me@example.com
|
279 |
+
# After register enabled, The website's domain ending without /
|
280 |
+
SITE_DOMAIN: http://127.0.0.1:3002
|
281 |
+
# After register enabled, The smtp settings
|
282 |
+
SMTP_HOST: smtp.exmail.qq.com
|
283 |
+
SMTP_PORT: 465
|
284 |
+
SMTP_TSL: true
|
285 |
+
SMTP_USERNAME: noreply@examile.com
|
286 |
+
SMTP_PASSWORD: xxx
|
287 |
+
# Enable sensitive word review, because the response result is streaming, so there is currently no review.
|
288 |
+
AUDIT_ENABLED: false
|
289 |
+
# https://ai.baidu.com/ai-doc/ANTIPORN/Vk3h6xaga
|
290 |
+
AUDIT_PROVIDER: baidu
|
291 |
+
AUDIT_API_KEY: xxx
|
292 |
+
AUDIT_API_SECRET: xxx
|
293 |
+
AUDIT_TEXT_LABEL: xxx
|
294 |
+
links:
|
295 |
+
- database
|
296 |
+
|
297 |
+
database:
|
298 |
+
image: mongo
|
299 |
+
container_name: chatgptweb-database
|
300 |
+
restart: unless-stopped
|
301 |
+
ports:
|
302 |
+
- '27017:27017'
|
303 |
+
expose:
|
304 |
+
- '27017'
|
305 |
+
volumes:
|
306 |
+
- mongodb:/data/db
|
307 |
+
environment:
|
308 |
+
MONGO_INITDB_ROOT_USERNAME: chatgpt
|
309 |
+
MONGO_INITDB_ROOT_PASSWORD: xxxx
|
310 |
+
MONGO_INITDB_DATABASE: chatgpt
|
311 |
+
|
312 |
+
volumes:
|
313 |
+
mongodb: {}
|
314 |
+
```
|
315 |
+
The `OPENAI_API_BASE_URL` is optional and only used when setting the `OPENAI_API_KEY`.
|
316 |
+
|
317 |
+
### Deployment with Railway
|
318 |
+
|
319 |
+
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/yytmgc)
|
320 |
+
|
321 |
+
#### Railway Environment Variables
|
322 |
+
|
323 |
+
| Environment Variable | Required | Description |
|
324 |
+
|------------------------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
|
325 |
+
| `PORT` | Required | Default: `3002` |
|
326 |
+
| `AUTH_SECRET_KEY` | Optional | access password |
|
327 |
+
| `TIMEOUT_MS` | Optional | Timeout in milliseconds |
|
328 |
+
| `OPENAI_API_KEY` | Optional | Required for `OpenAI API`. `apiKey` can be obtained from [here](https://platform.openai.com/overview). |
|
329 |
+
| `OPENAI_ACCESS_TOKEN` | Optional | Required for `Web API`. `accessToken` can be obtained from [here](https://chat.openai.com/api/auth/session). |
|
330 |
+
| `OPENAI_API_BASE_URL` | Optional, only for `OpenAI API` | API endpoint. |
|
331 |
+
| `OPENAI_API_MODEL` | `ChatGPTAPI` OR `ChatGPTUnofficialProxyAPI` | API model. |
|
332 |
+
| `API_REVERSE_PROXY` | Optional, only for `Web API` | Reverse proxy address for `Web API`. [Details](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy) |
|
333 |
+
| `SOCKS_PROXY_HOST` | Optional, effective with `SOCKS_PROXY_PORT` | Socks proxy. |
|
334 |
+
| `SOCKS_PROXY_PORT` | Optional, effective with `SOCKS_PROXY_HOST` | Socks proxy port. |
|
335 |
+
| `SOCKS_PROXY_USERNAME` | Optional, effective with `SOCKS_PROXY_HOST` & `SOCKS_PROXY_PORT` | Socks proxy username. |
|
336 |
+
| `SOCKS_PROXY_PASSWORD` | Optional, effective with `SOCKS_PROXY_HOST` & `SOCKS_PROXY_PORT` | Socks proxy password. |
|
337 |
+
| `HTTPS_PROXY` | Optional | HTTPS Proxy. |
|
338 |
+
|
339 |
+
> Note: Changing environment variables in Railway will cause re-deployment.
|
340 |
+
|
341 |
+
### Manual packaging
|
342 |
+
|
343 |
+
#### Backend service
|
344 |
+
|
345 |
+
> If you don't need the `node` interface of this project, you can skip the following steps.
|
346 |
+
|
347 |
+
Copy the `service` folder to a server that has a `node` service environment.
|
348 |
+
|
349 |
+
```shell
|
350 |
+
# Install
|
351 |
+
pnpm install
|
352 |
+
|
353 |
+
# Build
|
354 |
+
pnpm build
|
355 |
+
|
356 |
+
# Run
|
357 |
+
pnpm prod
|
358 |
+
```
|
359 |
+
|
360 |
+
PS: You can also run `pnpm start` directly on the server without packaging.
|
361 |
+
|
362 |
+
#### Frontend webpage
|
363 |
+
|
364 |
+
1. Refer to the root directory `.env.example` file content to create `.env` file, modify `VITE_GLOB_API_URL` in `.env` at the root directory to your actual backend interface address.
|
365 |
+
2. Run the following command in the root directory and then copy the files in the `dist` folder to the root directory of your website service.
|
366 |
+
|
367 |
+
[Reference information](https://cn.vitejs.dev/guide/static-deploy.html#building-the-app)
|
368 |
+
|
369 |
+
```shell
|
370 |
+
pnpm build
|
371 |
+
```
|
372 |
+
|
373 |
+
## Frequently Asked Questions
|
374 |
+
|
375 |
+
Q: Why does Git always report an error when committing?
|
376 |
+
|
377 |
+
A: Because there is submission information verification, please follow the [Commit Guidelines](./CONTRIBUTING.en.md).
|
378 |
+
|
379 |
+
Q: Where to change the request interface if only the frontend page is used?
|
380 |
+
|
381 |
+
A: The `VITE_GLOB_API_URL` field in the `.env` file at the root directory.
|
382 |
+
|
383 |
+
Q: All red when saving the file?
|
384 |
+
|
385 |
+
A: For `vscode`, please install the recommended plug-in of the project or manually install the `Eslint` plug-in.
|
386 |
+
|
387 |
+
Q: Why doesn't the frontend have a typewriter effect?
|
388 |
+
|
389 |
+
A: One possible reason is that after Nginx reverse proxying, buffering is turned on, and Nginx will try to buffer a certain amount of data from the backend before sending it to the browser. Please try adding `proxy_buffering off;` after the reverse proxy parameter and then reloading Nginx. Other web server configurations are similar.
|
390 |
+
|
391 |
+
Q: The content returned is incomplete?
|
392 |
+
|
393 |
+
A: There is a length limit for the content returned by the API each time. You can modify the `VITE_GLOB_OPEN_LONG_REPLY` field in the `.env` file under the root directory, set it to `true`, and rebuild the front-end to enable the long reply feature, which can return the full content. It should be noted that using this feature may bring more API usage fees.
|
394 |
+
|
395 |
+
## Contributing
|
396 |
+
|
397 |
+
Please read the [Contributing Guidelines](./CONTRIBUTING.en.md) before contributing.
|
398 |
+
|
399 |
+
Thanks to all the contributors!
|
400 |
+
|
401 |
+
<a href="https://github.com/Chanzhaoyu/chatgpt-web/graphs/contributors">
|
402 |
+
<img src="https://contrib.rocks/image?repo=Chanzhaoyu/chatgpt-web" />
|
403 |
+
</a>
|
404 |
+
|
405 |
+
## Sponsorship
|
406 |
+
|
407 |
+
If you find this project helpful, please give me a star.
|
408 |
+
|
409 |
+
## License
|
410 |
+
MIT © [Kerwin1202](./license)
|
README.md
CHANGED
@@ -1,10 +1,445 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
|
|
|
|
8 |
---
|
9 |
|
10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: dvcchat-pro
|
3 |
+
emoji: 🐠
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: blue
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
+
app_port: 3002
|
9 |
+
duplicated_from: dvc890/chatup
|
10 |
---
|
11 |
|
12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
13 |
+
|
14 |
+
# ChatGPT Web
|
15 |
+
|
16 |
+
<div style="font-size: 1.5rem;">
|
17 |
+
<a href="./README.md">中文</a> |
|
18 |
+
<a href="./README.en.md">English</a>
|
19 |
+
</div>
|
20 |
+
</br>
|
21 |
+
|
22 |
+
## 说明
|
23 |
+
> **此项目 Fork 自 [Chanzhaoyu/chatgpt-web](https://github.com/Chanzhaoyu/chatgpt-web), 新增了部分特色功能:**
|
24 |
+
|
25 |
+
[✓] 注册&登录&重置密码
|
26 |
+
|
27 |
+
[✓] 同步历史会话
|
28 |
+
|
29 |
+
[✓] 前端页面设置apikey
|
30 |
+
|
31 |
+
[✓] 自定义敏感词
|
32 |
+
|
33 |
+
[✓] 每个会话设置独有 Prompt
|
34 |
+
|
35 |
+
[✓] 用户管理
|
36 |
+
|
37 |
+
[✓] 多 Key 随机
|
38 |
+
</br>
|
39 |
+
|
40 |
+
## 截图
|
41 |
+
> 声明:此项目只发布于 Github,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号、付费服务、讨论群、讨论组等行为。谨防受骗。
|
42 |
+
|
43 |
+
![cover3](./docs/login.jpg)
|
44 |
+
![cover](./docs/c1.png)
|
45 |
+
![cover2](./docs/c2.png)
|
46 |
+
![cover3](./docs/basesettings.jpg)
|
47 |
+
![cover3](./docs/prompt.jpg)
|
48 |
+
![cover3](./docs/user-manager.jpg)
|
49 |
+
![cover3](./docs/key-manager.jpg)
|
50 |
+
|
51 |
+
- [ChatGPT Web](#chatgpt-web)
|
52 |
+
- [说明](#说明)
|
53 |
+
- [截图](#截图)
|
54 |
+
- [介绍](#介绍)
|
55 |
+
- [待实现路线](#待实现路线)
|
56 |
+
- [前置要求](#前置要求)
|
57 |
+
- [Node](#node)
|
58 |
+
- [PNPM](#pnpm)
|
59 |
+
- [填写密钥](#填写密钥)
|
60 |
+
- [安装依赖](#安装依赖)
|
61 |
+
- [后端](#后端)
|
62 |
+
- [前端](#前端)
|
63 |
+
- [测试环境运行](#测试环境运行)
|
64 |
+
- [后端服务](#后端服务)
|
65 |
+
- [前端网页](#前端网页)
|
66 |
+
- [环境变量](#环境变量)
|
67 |
+
- [打包](#打包)
|
68 |
+
- [使用 Docker](#使用-docker)
|
69 |
+
- [Docker 参数示例](#docker-参数示例)
|
70 |
+
- [Docker build \& Run](#docker-build--run)
|
71 |
+
- [Docker compose](#docker-compose)
|
72 |
+
- [防止爬虫抓取](#防止爬虫抓取)
|
73 |
+
- [使用 Railway 部署](#使用-railway-部署)
|
74 |
+
- [Railway 环境变量](#railway-环境变量)
|
75 |
+
- [手动打包](#手动打包)
|
76 |
+
- [后端服务](#后端服务-1)
|
77 |
+
- [前端网页](#前端网页-1)
|
78 |
+
- [常见问题](#常见问题)
|
79 |
+
- [参与贡献](#参与贡献)
|
80 |
+
- [赞助](#赞助)
|
81 |
+
- [License](#license)
|
82 |
+
## 介绍
|
83 |
+
|
84 |
+
支持双模型,提供了两种非官方 `ChatGPT API` 方法
|
85 |
+
|
86 |
+
| 方式 | 免费? | 可靠性 | 质量 |
|
87 |
+
| --------------------------------------------- | ------ | ---------- | ---- |
|
88 |
+
| `ChatGPTAPI(gpt-3.5-turbo-0301)` | 否 | 可靠 | 相对较笨 |
|
89 |
+
| `ChatGPTUnofficialProxyAPI(网页 accessToken)` | 是 | 相对不可靠 | 聪明 |
|
90 |
+
|
91 |
+
对比:
|
92 |
+
1. `ChatGPTAPI` 使用 `gpt-3.5-turbo` 通过 `OpenAI` 官方 `API` 调用 `ChatGPT`
|
93 |
+
2. `ChatGPTUnofficialProxyAPI` 使用非官方代理服务器访问 `ChatGPT` 的后端`API`,绕过`Cloudflare`(依赖于第三方服务器,并且有速率限制)
|
94 |
+
|
95 |
+
警告:
|
96 |
+
1. 你应该首先使用 `API` 方式
|
97 |
+
2. 使用 `API` 时,如果网络不通,那是国内被墙了,你需要自建代理,绝对不要使用别人的公开代理,那是危险的。
|
98 |
+
3. 使用 `accessToken` 方式时反向代理将向第三方暴露您的访问令牌,这样做应该不会产生任何不良影响,但在使用这种方法之前请考虑风险。
|
99 |
+
4. 使用 `accessToken` 时,不管你是国内还是国外的机器,都会使用代理。默认代理为 [pengzhile](https://github.com/pengzhile) 大佬的 `https://ai.fakeopen.com/api/conversation`,这不是后门也不是监听,除非你有能力自己翻过 `CF` 验证,用前请知悉。[社区代理](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别)
|
100 |
+
5. 把项目发布到公共网络时,你应该设置 `AUTH_SECRET_KEY` 变量添加你的密码访问权限,你也应该修改 `index.html` 中的 `title`,防止被关键词搜索到。
|
101 |
+
|
102 |
+
切换方式:
|
103 |
+
1. 进入 `service/.env.example` 文件,复制内容到 `service/.env` 文件
|
104 |
+
2. 使用 `OpenAI API Key` 请填写 `OPENAI_API_KEY` 字段 [(获取 apiKey)](https://platform.openai.com/overview)
|
105 |
+
3. 使用 `Web API` 请填写 `OPENAI_ACCESS_TOKEN` 字段 [(获取 accessToken)](https://chat.openai.com/api/auth/session)
|
106 |
+
4. 同时存在时以 `OpenAI API Key` 优先
|
107 |
+
|
108 |
+
环境变量:
|
109 |
+
|
110 |
+
全部参数变量请查看或[这里](#环境变量)
|
111 |
+
|
112 |
+
```
|
113 |
+
/service/.env.example
|
114 |
+
```
|
115 |
+
|
116 |
+
## 待实现路线
|
117 |
+
[✓] 双模型
|
118 |
+
|
119 |
+
[✓] 多会话储存和上下文逻辑
|
120 |
+
|
121 |
+
[✓] 对代码等消息类型的格式化美化处理
|
122 |
+
|
123 |
+
[✓] 支持用户登录注册
|
124 |
+
|
125 |
+
[✓] 前端页面设置 apikey 等信息
|
126 |
+
|
127 |
+
[✓] 数据导入、导出
|
128 |
+
|
129 |
+
[✓] 保存消息到本地图片
|
130 |
+
|
131 |
+
[✓] 界面多语言
|
132 |
+
|
133 |
+
[✓] 界面主题
|
134 |
+
|
135 |
+
[✗] More...
|
136 |
+
|
137 |
+
## 前置要求
|
138 |
+
|
139 |
+
### Node
|
140 |
+
|
141 |
+
`node` 需要 `^16 || ^18 || ^19` 版本(`node >= 14` 需要安装 [fetch polyfill](https://github.com/developit/unfetch#usage-as-a-polyfill)),使用 [nvm](https://github.com/nvm-sh/nvm) 可管理本地多个 `node` 版本
|
142 |
+
|
143 |
+
```shell
|
144 |
+
node -v
|
145 |
+
```
|
146 |
+
|
147 |
+
### PNPM
|
148 |
+
如果你没有安装过 `pnpm`
|
149 |
+
```shell
|
150 |
+
npm install pnpm -g
|
151 |
+
```
|
152 |
+
|
153 |
+
### 填写密钥
|
154 |
+
获取 `Openai Api Key` 或 `accessToken` 并填写本地环境变量 [跳转](#介绍)
|
155 |
+
|
156 |
+
```
|
157 |
+
# service/.env 文件
|
158 |
+
|
159 |
+
# OpenAI API Key - https://platform.openai.com/overview
|
160 |
+
OPENAI_API_KEY=
|
161 |
+
|
162 |
+
# change this to an `accessToken` extracted from the ChatGPT site's `https://chat.openai.com/api/auth/session` response
|
163 |
+
OPENAI_ACCESS_TOKEN=
|
164 |
+
```
|
165 |
+
|
166 |
+
## 安装依赖
|
167 |
+
|
168 |
+
> 为了简便 `后端开发人员` 的了解负担,所以并没有采用前端 `workspace` 模式,而是分文件夹存放。如果只需要前端页面做二次开发,删除 `service` 文件夹即可。
|
169 |
+
|
170 |
+
### 后端
|
171 |
+
|
172 |
+
进入文件夹 `/service` 运行以下命令
|
173 |
+
|
174 |
+
```shell
|
175 |
+
pnpm install
|
176 |
+
```
|
177 |
+
|
178 |
+
### 前端
|
179 |
+
根目录下运行以下命令
|
180 |
+
```shell
|
181 |
+
pnpm bootstrap
|
182 |
+
```
|
183 |
+
|
184 |
+
## 测试环境运行
|
185 |
+
### 后端服务
|
186 |
+
|
187 |
+
进入文件夹 `/service` 运行以下命令
|
188 |
+
|
189 |
+
```shell
|
190 |
+
pnpm start
|
191 |
+
```
|
192 |
+
|
193 |
+
### 前端网页
|
194 |
+
根目录下运行以下命令
|
195 |
+
```shell
|
196 |
+
pnpm dev
|
197 |
+
```
|
198 |
+
|
199 |
+
## 环境变量
|
200 |
+
|
201 |
+
`API` 可用:
|
202 |
+
|
203 |
+
- `OPENAI_API_KEY` 和 `OPENAI_ACCESS_TOKEN` 二选一
|
204 |
+
- `OPENAI_API_BASE_URL` 设置接口地址,可选,默认:`https://api.openai.com`
|
205 |
+
- `OPENAI_API_DISABLE_DEBUG` 设置接口关闭 debug 日志,可选,默认:empty 不关闭
|
206 |
+
|
207 |
+
`ACCESS_TOKEN` 可用:
|
208 |
+
|
209 |
+
- `OPENAI_ACCESS_TOKEN` 和 `OPENAI_API_KEY` 二选一,同时存在时,`OPENAI_API_KEY` 优先
|
210 |
+
- `API_REVERSE_PROXY` 设置反向代理,可选,默认:`https://ai.fakeopen.com/api/conversation`,[社区](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy)(注意:只有这两个是推荐,其他第三方来源,请自行甄别)
|
211 |
+
|
212 |
+
通用:
|
213 |
+
|
214 |
+
- `AUTH_SECRET_KEY` 访问权限密钥,可选
|
215 |
+
- `MAX_REQUEST_PER_HOUR` 每小时最大请求次数,可选,默认无限
|
216 |
+
- `TIMEOUT_MS` 超时,单位毫秒,可选
|
217 |
+
- `SOCKS_PROXY_HOST` 和 `SOCKS_PROXY_PORT` 一起时生效,可选
|
218 |
+
- `SOCKS_PROXY_PORT` 和 `SOCKS_PROXY_HOST` 一起时生效,可选
|
219 |
+
- `HTTPS_PROXY` 支持 `http`,`https`, `socks5`,可选
|
220 |
+
|
221 |
+
## 打包
|
222 |
+
|
223 |
+
### 使用 Docker
|
224 |
+
|
225 |
+
#### Docker 参数示例
|
226 |
+
|
227 |
+
![docker](./docs/docker.png)
|
228 |
+
|
229 |
+
#### Docker build & Run
|
230 |
+
|
231 |
+
```bash
|
232 |
+
docker build -t chatgpt-web .
|
233 |
+
|
234 |
+
# 前台运行
|
235 |
+
docker run --name chatgpt-web --rm -it -p 127.0.0.1:3002:3002 --env OPENAI_API_KEY=your_api_key chatgpt-web
|
236 |
+
|
237 |
+
# 后台运行
|
238 |
+
docker run --name chatgpt-web -d -p 127.0.0.1:3002:3002 --env OPENAI_API_KEY=your_api_key chatgpt-web
|
239 |
+
|
240 |
+
# 运行地址
|
241 |
+
http://localhost:3002/
|
242 |
+
```
|
243 |
+
|
244 |
+
#### Docker compose
|
245 |
+
|
246 |
+
[Hub 地址](https://hub.docker.com/repository/docker/kerwin1202/chatgpt-web/general)
|
247 |
+
|
248 |
+
```yml
|
249 |
+
version: '3'
|
250 |
+
|
251 |
+
services:
|
252 |
+
app:
|
253 |
+
image: kerwin1202/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
254 |
+
container_name: chatgptweb
|
255 |
+
restart: unless-stopped
|
256 |
+
ports:
|
257 |
+
- 3002:3002
|
258 |
+
depends_on:
|
259 |
+
- database
|
260 |
+
environment:
|
261 |
+
TZ: Asia/Shanghai
|
262 |
+
# 二选一
|
263 |
+
OPENAI_API_KEY: sk-xxx
|
264 |
+
# 二选一
|
265 |
+
OPENAI_ACCESS_TOKEN: xxx
|
266 |
+
# API接口地址,可选,设置 OPENAI_API_KEY 时可用
|
267 |
+
OPENAI_API_BASE_URL: xxx
|
268 |
+
# ChatGPTAPI ChatGPTUnofficialProxyAPI
|
269 |
+
OPENAI_API_MODEL: ChatGPTAPI
|
270 |
+
# 反向代理,可选
|
271 |
+
API_REVERSE_PROXY: xxx
|
272 |
+
# 每小时最大请求次数,可选,默认无限
|
273 |
+
MAX_REQUEST_PER_HOUR: 0
|
274 |
+
# 超时,单位毫秒,可选
|
275 |
+
TIMEOUT_MS: 600000
|
276 |
+
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
|
277 |
+
SOCKS_PROXY_HOST: xxx
|
278 |
+
# Socks代理端口,可选,和 SOCKS_PROXY_HOST 一起时生效
|
279 |
+
SOCKS_PROXY_PORT: xxx
|
280 |
+
# HTTPS 代理,可选,支持 http,https,socks5
|
281 |
+
HTTPS_PROXY: http://xxx:7890
|
282 |
+
# 访问jwt加密参数,可选 不为空则允许登录 同时需要设置 MONGODB_URL
|
283 |
+
AUTH_SECRET_KEY: xxx
|
284 |
+
# 网站名称
|
285 |
+
SITE_TITLE: ChatGpt Web
|
286 |
+
# mongodb 的连接字符串
|
287 |
+
MONGODB_URL: 'mongodb://chatgpt:xxxx@database:27017'
|
288 |
+
# 网站是否开启注册
|
289 |
+
REGISTER_ENABLED: 'true'
|
290 |
+
# 开启注册之后 网站注册允许的邮箱后缀 如果空 则允许任意后缀
|
291 |
+
REGISTER_MAILS: '@qq.com,@sina.com,@163.com'
|
292 |
+
# 开启注册之后 密码加密的盐
|
293 |
+
PASSWORD_MD5_SALT: xxx
|
294 |
+
# 开启注册之后 超级管理邮箱
|
295 |
+
ROOT_USER: me@example.com
|
296 |
+
# 开启注册之后 网站域名 不含 / 注册的时候发送验证邮箱使用
|
297 |
+
SITE_DOMAIN: http://127.0.0.1:3002
|
298 |
+
# 开启注册之后 发送验证邮箱配置
|
299 |
+
SMTP_HOST: smtp.exmail.qq.com
|
300 |
+
SMTP_PORT: 465
|
301 |
+
SMTP_TSL: 'true'
|
302 |
+
SMTP_USERNAME: noreply@examile.com
|
303 |
+
SMTP_PASSWORD: xxx
|
304 |
+
# 是否开启敏感词审核, 因为响应结果是流式 所以暂时没审核
|
305 |
+
AUDIT_ENABLED: 'false'
|
306 |
+
# https://ai.baidu.com/ai-doc/ANTIPORN/Vk3h6xaga
|
307 |
+
AUDIT_PROVIDER: baidu
|
308 |
+
AUDIT_API_KEY: xxx
|
309 |
+
AUDIT_API_SECRET: xxx
|
310 |
+
AUDIT_TEXT_LABEL: xxx
|
311 |
+
links:
|
312 |
+
- database
|
313 |
+
|
314 |
+
database:
|
315 |
+
image: mongo
|
316 |
+
container_name: chatgptweb-database
|
317 |
+
restart: unless-stopped
|
318 |
+
ports:
|
319 |
+
- '27017:27017'
|
320 |
+
expose:
|
321 |
+
- '27017'
|
322 |
+
volumes:
|
323 |
+
- mongodb:/data/db
|
324 |
+
environment:
|
325 |
+
MONGO_INITDB_ROOT_USERNAME: chatgpt
|
326 |
+
MONGO_INITDB_ROOT_PASSWORD: xxxx
|
327 |
+
MONGO_INITDB_DATABASE: chatgpt
|
328 |
+
|
329 |
+
volumes:
|
330 |
+
mongodb: {}
|
331 |
+
```
|
332 |
+
- `OPENAI_API_BASE_URL` 可选,设置 `OPENAI_API_KEY` 时可用
|
333 |
+
|
334 |
+
#### 防止爬虫抓取
|
335 |
+
|
336 |
+
**nginx**
|
337 |
+
|
338 |
+
将下面配置填入nginx配置文件中,可以参考 `docker-compose/nginx/nginx.conf` 文件中添加反爬虫的方法
|
339 |
+
|
340 |
+
```
|
341 |
+
# 防止爬虫抓取
|
342 |
+
if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot"){
|
343 |
+
return 403;
|
344 |
+
}
|
345 |
+
```
|
346 |
+
|
347 |
+
### 使用 Railway 部署
|
348 |
+
|
349 |
+
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/yytmgc)
|
350 |
+
|
351 |
+
#### Railway 环境变量
|
352 |
+
|
353 |
+
| 环境变量名称 | 必填 | 备注 |
|
354 |
+
| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------- |
|
355 |
+
| `PORT` | 必填 | 默认 `3002`
|
356 |
+
| `AUTH_SECRET_KEY` | 可选 | 访问权限密钥 |
|
357 |
+
| `MAX_REQUEST_PER_HOUR` | 可选 | 每小时最大请求次数,可选,默认无限 |
|
358 |
+
| `TIMEOUT_MS` | 可选 | 超时时间,单位毫秒 |
|
359 |
+
| `OPENAI_API_KEY` | `OpenAI API` 二选一 | 使用 `OpenAI API` 所需的 `apiKey` [(获取 apiKey)](https://platform.openai.com/overview) |
|
360 |
+
| `OPENAI_ACCESS_TOKEN` | `Web API` 二选一 | 使用 `Web API` 所需的 `accessToken` [(获取 accessToken)](https://chat.openai.com/api/auth/session) |
|
361 |
+
| `OPENAI_API_BASE_URL` | 可选,`OpenAI API` 时可用 | `API`接口地址 |
|
362 |
+
| `OPENAI_API_MODEL` | ChatGPTAPI OR ChatGPTUnofficialProxyAPI | `API`模型 |
|
363 |
+
| `API_REVERSE_PROXY` | 可选,`Web API` 时可用 | `Web API` 反向代理地址 [详情](https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy) |
|
364 |
+
| `SOCKS_PROXY_HOST` | 可选,和 `SOCKS_PROXY_PORT` 一起时生效 | Socks代理 |
|
365 |
+
| `SOCKS_PROXY_PORT` | 可选,和 `SOCKS_PROXY_HOST` 一起时生效 | Socks代理端口 |
|
366 |
+
| `SOCKS_PROXY_USERNAME` | 可选,和 `SOCKS_PROXY_HOST` 一起时生效 | Socks代理用户名 |
|
367 |
+
| `SOCKS_PROXY_PASSWORD` | 可选,和 `SOCKS_PROXY_HOST` 一起时生效 | Socks代理密码 |
|
368 |
+
| `HTTPS_PROXY` | 可选 | HTTPS 代理,支持 http,https, socks5 |
|
369 |
+
|
370 |
+
> 注意: `Railway` 修改环境变量会重新 `Deploy`
|
371 |
+
|
372 |
+
### 手动打包
|
373 |
+
#### 后端服务
|
374 |
+
> 如果你不需要本项目的 `node` 接口,可以省略如下操作
|
375 |
+
|
376 |
+
复制 `service` 文件夹到你有 `node` 服务环境的服务器上。
|
377 |
+
|
378 |
+
```shell
|
379 |
+
# 安装
|
380 |
+
pnpm install
|
381 |
+
|
382 |
+
# 打包
|
383 |
+
pnpm build
|
384 |
+
|
385 |
+
# 运行
|
386 |
+
pnpm prod
|
387 |
+
```
|
388 |
+
|
389 |
+
PS: 不进行打包,直接在服务器上运行 `pnpm start` 也可
|
390 |
+
|
391 |
+
#### 前端网页
|
392 |
+
|
393 |
+
1、修改根目录下 `.env` 文件中的 `VITE_GLOB_API_URL` 为你的实际后端接口地址
|
394 |
+
|
395 |
+
2、根目录下运行以下命令,然后将 `dist` 文件夹内的文件复制到你网站服务的根目录下
|
396 |
+
|
397 |
+
[参考信息](https://cn.vitejs.dev/guide/static-deploy.html#building-the-app)
|
398 |
+
|
399 |
+
```shell
|
400 |
+
pnpm build
|
401 |
+
```
|
402 |
+
|
403 |
+
## 常见问题
|
404 |
+
Q: 为什么 `Git` 提交总是报错?
|
405 |
+
|
406 |
+
A: 因为有提交信息验证,请遵循 [Commit 指南](./CONTRIBUTING.md)
|
407 |
+
|
408 |
+
Q: 如果只使用前端页面,在哪里改请求接口?
|
409 |
+
|
410 |
+
A: 根目录下 `.env` 文件中的 `VITE_GLOB_API_URL` 字段。
|
411 |
+
|
412 |
+
Q: 文件保存时全部爆红?
|
413 |
+
|
414 |
+
A: `vscode` 请安装项目推荐插件,或手动安装 `Eslint` 插件。
|
415 |
+
|
416 |
+
Q: 前端没有打字机效果?
|
417 |
+
|
418 |
+
A: 一种可能原因是经过 Nginx 反向代理,开启了 buffer,则 Nginx 会尝试从后端缓冲一定大小的数据再发送给浏览器。请尝试在反代参数后添加 `proxy_buffering off;`,然后重载 Nginx。其他 web server 配置同理。
|
419 |
+
|
420 |
+
## 参与贡献
|
421 |
+
|
422 |
+
贡献之前请先阅读 [贡献指南](./CONTRIBUTING.md)
|
423 |
+
|
424 |
+
感谢所有做过贡献的人!
|
425 |
+
|
426 |
+
<a href="https://github.com/Chanzhaoyu/chatgpt-web/graphs/contributors">
|
427 |
+
<img src="https://contrib.rocks/image?repo=Chanzhaoyu/chatgpt-web" />
|
428 |
+
</a>
|
429 |
+
|
430 |
+
## 赞助
|
431 |
+
如果你觉得这个项目对你有帮助,请给我点个Star。并且情况允许的话��可以给我一点点支持,总之非常感谢支持~
|
432 |
+
|
433 |
+
<div style="display: flex; gap: 20px;">
|
434 |
+
<div style="text-align: center">
|
435 |
+
<img style="width: 200px" src="./docs/wechat.png" alt="微信" />
|
436 |
+
<p>WeChat Pay</p>
|
437 |
+
</div>
|
438 |
+
<div style="text-align: center">
|
439 |
+
<img style="width: 200px" src="./docs/alipay.png" alt="支付宝" />
|
440 |
+
<p>Alipay</p>
|
441 |
+
</div>
|
442 |
+
</div>
|
443 |
+
|
444 |
+
## License
|
445 |
+
MIT © [Kerwin1202](./license)
|
docker-compose/docker-compose-mongodb.yml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3'
|
2 |
+
|
3 |
+
services:
|
4 |
+
mongo:
|
5 |
+
image: mongo
|
6 |
+
container_name: mongodb
|
7 |
+
restart: always
|
8 |
+
ports:
|
9 |
+
- '27017:27017'
|
10 |
+
volumes:
|
11 |
+
- ./mongodb:/data/db
|
12 |
+
environment:
|
13 |
+
MONGO_INITDB_ROOT_USERNAME: chatgpt
|
14 |
+
MONGO_INITDB_ROOT_PASSWORD: password
|
15 |
+
MONGO_INITDB_DATABASE: chatgpt
|
docker-compose/docker-compose.yml
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3'
|
2 |
+
|
3 |
+
services:
|
4 |
+
app:
|
5 |
+
image: kerwin1202/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
6 |
+
container_name: chatgptweb
|
7 |
+
restart: unless-stopped
|
8 |
+
ports:
|
9 |
+
- 3002:3002
|
10 |
+
depends_on:
|
11 |
+
- database
|
12 |
+
environment:
|
13 |
+
TZ: Asia/Shanghai
|
14 |
+
# 访问jwt加密参数,可选 不为空则允许登录 同时需要设置 MONGODB_URL
|
15 |
+
AUTH_SECRET_KEY:
|
16 |
+
# 每小时最大请求次数,可选,默认无限
|
17 |
+
MAX_REQUEST_PER_HOUR: 0
|
18 |
+
# 超时,单位毫秒,可选
|
19 |
+
TIMEOUT_MS: 600000
|
20 |
+
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
|
21 |
+
SOCKS_PROXY_HOST:
|
22 |
+
# Socks代理端口,可选,和 SOCKS_PROXY_HOST 一起时生效
|
23 |
+
SOCKS_PROXY_PORT:
|
24 |
+
# Socks代理用户名,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
25 |
+
SOCKS_PROXY_USERNAME:
|
26 |
+
# Socks代理密码,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
27 |
+
SOCKS_PROXY_PASSWORD:
|
28 |
+
# 网站名称
|
29 |
+
SITE_TITLE: ChatGpt Web
|
30 |
+
# mongodb 的连接字符串
|
31 |
+
MONGODB_URL: 'mongodb://chatgpt:xxxx@database:27017'
|
32 |
+
# 网站是否开启注册
|
33 |
+
REGISTER_ENABLED: false
|
34 |
+
# 开启注册之后 网站注册允许的邮箱后缀 如果空 则允许任意后缀
|
35 |
+
REGISTER_MAILS: '@qq.com,@sina.com,@163.com'
|
36 |
+
# 开启注册之后 密码加密的盐
|
37 |
+
PASSWORD_MD5_SALT: anysalt
|
38 |
+
# 开启注册之后 超级管理邮箱
|
39 |
+
ROOT_USER: xxx@qq.com
|
40 |
+
# 开启注册之后 网站域名 不含 / 注册的时候发送验证邮箱使用
|
41 |
+
SITE_DOMAIN: http://127.0.0.1:1002
|
42 |
+
# 开启注册之后 发送验证邮箱配置
|
43 |
+
SMTP_HOST: smtp.exmail.qq.com
|
44 |
+
SMTP_PORT: 465
|
45 |
+
SMTP_TSL: true
|
46 |
+
SMTP_USERNAME: ${SMTP_USERNAME}
|
47 |
+
SMTP_PASSWORD: ${SMTP_PASSWORD}
|
48 |
+
# 是否开启敏感词审核, 因为响应结果是流式 所以暂时没审核
|
49 |
+
AUDIT_ENABLED: false
|
50 |
+
# https://ai.baidu.com/ai-doc/ANTIPORN/Vk3h6xaga
|
51 |
+
AUDIT_PROVIDER: baidu
|
52 |
+
AUDIT_API_KEY:
|
53 |
+
AUDIT_API_SECRET:
|
54 |
+
AUDIT_TEXT_LABEL:
|
55 |
+
links:
|
56 |
+
- database
|
57 |
+
|
58 |
+
database:
|
59 |
+
image: mongo
|
60 |
+
ports:
|
61 |
+
- '27017:27017'
|
62 |
+
expose:
|
63 |
+
- '27017'
|
64 |
+
volumes:
|
65 |
+
- mongodb:/data/db
|
66 |
+
environment:
|
67 |
+
MONGO_INITDB_ROOT_USERNAME: chatgpt
|
68 |
+
MONGO_INITDB_ROOT_PASSWORD: xxxx
|
69 |
+
MONGO_INITDB_DATABASE: chatgpt
|
70 |
+
|
71 |
+
mongo-gui:
|
72 |
+
container_name: mongo-gui
|
73 |
+
image: ugleiton/mongo-gui
|
74 |
+
restart: always
|
75 |
+
ports:
|
76 |
+
- '4321:4321'
|
77 |
+
environment:
|
78 |
+
- MONGO_URL=mongodb://chatgpt:xxxx@database:27017
|
79 |
+
links:
|
80 |
+
- database
|
81 |
+
depends_on:
|
82 |
+
- database
|
83 |
+
|
84 |
+
nginx:
|
85 |
+
image: nginx:alpine
|
86 |
+
container_name: chatgptweb-database
|
87 |
+
restart: unless-stopped
|
88 |
+
ports:
|
89 |
+
- '80:80'
|
90 |
+
expose:
|
91 |
+
- '80'
|
92 |
+
volumes:
|
93 |
+
- ./nginx/html:/usr/share/nginx/html
|
94 |
+
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
95 |
+
links:
|
96 |
+
- app
|
97 |
+
|
98 |
+
volumes:
|
99 |
+
mongodb: {}
|
docker-compose/nginx/nginx.conf
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
server {
|
2 |
+
listen 80;
|
3 |
+
server_name localhost;
|
4 |
+
charset utf-8;
|
5 |
+
error_page 500 502 503 504 /50x.html;
|
6 |
+
|
7 |
+
# 防止爬虫抓取
|
8 |
+
if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot"){
|
9 |
+
return 403;
|
10 |
+
}
|
11 |
+
|
12 |
+
|
13 |
+
location / {
|
14 |
+
root /usr/share/nginx/html;
|
15 |
+
try_files $uri /index.html;
|
16 |
+
}
|
17 |
+
|
18 |
+
location /api {
|
19 |
+
proxy_set_header X-Real-IP $remote_addr; #转发用户IP
|
20 |
+
proxy_pass http://app:3002;
|
21 |
+
}
|
22 |
+
|
23 |
+
proxy_set_header Host $host;
|
24 |
+
proxy_set_header X-Real-IP $remote_addr;
|
25 |
+
proxy_set_header REMOTE-HOST $remote_addr;
|
26 |
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
27 |
+
}
|
docker-compose/readme.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
### docker-compose 部署教程
|
2 |
+
- 将打包好的前端文件放到 `nginx/html` 目录下
|
3 |
+
- ```shell
|
4 |
+
# 启动
|
5 |
+
docker-compose up -d
|
6 |
+
```
|
7 |
+
- ```shell
|
8 |
+
# 查看运行状态
|
9 |
+
docker ps
|
10 |
+
```
|
11 |
+
- ```shell
|
12 |
+
# 结束运行
|
13 |
+
docker-compose down
|
14 |
+
```
|
docs/alipay.png
ADDED
docs/basesettings.jpg
ADDED
docs/c1-2.8.0.png
ADDED
docs/c1-2.9.0.png
ADDED
docs/c1.png
ADDED
docs/c2-2.8.0.png
ADDED
docs/c2-2.9.0.png
ADDED
docs/c2.png
ADDED
docs/docker.png
ADDED
docs/key-manager-en.jpg
ADDED
docs/key-manager.jpg
ADDED
docs/login.jpg
ADDED
docs/mailsettings.jpg
ADDED
docs/prompt.jpg
ADDED
docs/prompt_en.jpg
ADDED
docs/sitesettings.jpg
ADDED
docs/user-manager.jpg
ADDED
docs/wechat.png
ADDED
index.html
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="zh-cmn-Hans">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
6 |
+
<meta content="yes" name="apple-mobile-web-app-capable"/>
|
7 |
+
<link rel="apple-touch-icon" href="/favicon.ico">
|
8 |
+
<meta name="viewport"
|
9 |
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
10 |
+
<title>${SITE_TITLE}</title>
|
11 |
+
</head>
|
12 |
+
|
13 |
+
<body class="dark:bg-black">
|
14 |
+
<div id="app">
|
15 |
+
<style>
|
16 |
+
.loading-wrap {
|
17 |
+
display: flex;
|
18 |
+
justify-content: center;
|
19 |
+
align-items: center;
|
20 |
+
height: 100vh;
|
21 |
+
}
|
22 |
+
|
23 |
+
.balls {
|
24 |
+
width: 4em;
|
25 |
+
display: flex;
|
26 |
+
flex-flow: row nowrap;
|
27 |
+
align-items: center;
|
28 |
+
justify-content: space-between;
|
29 |
+
}
|
30 |
+
|
31 |
+
.balls div {
|
32 |
+
width: 0.8em;
|
33 |
+
height: 0.8em;
|
34 |
+
border-radius: 50%;
|
35 |
+
background-color: #4b9e5f;
|
36 |
+
}
|
37 |
+
|
38 |
+
.balls div:nth-of-type(1) {
|
39 |
+
transform: translateX(-100%);
|
40 |
+
animation: left-swing 0.5s ease-in alternate infinite;
|
41 |
+
}
|
42 |
+
|
43 |
+
.balls div:nth-of-type(3) {
|
44 |
+
transform: translateX(-95%);
|
45 |
+
animation: right-swing 0.5s ease-out alternate infinite;
|
46 |
+
}
|
47 |
+
|
48 |
+
@keyframes left-swing {
|
49 |
+
|
50 |
+
50%,
|
51 |
+
100% {
|
52 |
+
transform: translateX(95%);
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
@keyframes right-swing {
|
57 |
+
50% {
|
58 |
+
transform: translateX(-95%);
|
59 |
+
}
|
60 |
+
|
61 |
+
100% {
|
62 |
+
transform: translateX(100%);
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
@media (prefers-color-scheme: dark) {
|
67 |
+
body {
|
68 |
+
background: #121212;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
</style>
|
72 |
+
<div class="loading-wrap">
|
73 |
+
<div class="balls">
|
74 |
+
<div></div>
|
75 |
+
<div></div>
|
76 |
+
<div></div>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
</div>
|
80 |
+
<script type="module" src="/src/main.ts"></script>
|
81 |
+
</body>
|
82 |
+
|
83 |
+
</html>
|
kubernetes/README.md
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## 增加一个Kubernetes的部署方式
|
2 |
+
```
|
3 |
+
kubectl apply -f deploy.yaml
|
4 |
+
```
|
5 |
+
|
6 |
+
### 如果需要Ingress域名接入
|
7 |
+
```
|
8 |
+
kubectl apply -f ingress.yaml
|
9 |
+
```
|
kubernetes/deploy.yaml
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
apiVersion: apps/v1
|
2 |
+
kind: Deployment
|
3 |
+
metadata:
|
4 |
+
name: chatgpt-web
|
5 |
+
labels:
|
6 |
+
app: chatgpt-web
|
7 |
+
spec:
|
8 |
+
replicas: 1
|
9 |
+
selector:
|
10 |
+
matchLabels:
|
11 |
+
app: chatgpt-web
|
12 |
+
strategy:
|
13 |
+
type: RollingUpdate
|
14 |
+
template:
|
15 |
+
metadata:
|
16 |
+
labels:
|
17 |
+
app: chatgpt-web
|
18 |
+
spec:
|
19 |
+
containers:
|
20 |
+
- image: chenzhaoyu94/chatgpt-web
|
21 |
+
name: chatgpt-web
|
22 |
+
imagePullPolicy: Always
|
23 |
+
ports:
|
24 |
+
- containerPort: 3002
|
25 |
+
env:
|
26 |
+
- name: OPENAI_API_KEY
|
27 |
+
value: sk-xxx
|
28 |
+
- name: OPENAI_API_BASE_URL
|
29 |
+
value: 'https://api.openai.com'
|
30 |
+
- name: OPENAI_API_MODEL
|
31 |
+
value: ChatGPTAPI
|
32 |
+
- name: API_REVERSE_PROXY
|
33 |
+
value: https://ai.fakeopen.com/api/conversation
|
34 |
+
- name: AUTH_SECRET_KEY
|
35 |
+
value: '123456'
|
36 |
+
- name: TIMEOUT_MS
|
37 |
+
value: '600000'
|
38 |
+
- name: SOCKS_PROXY_HOST
|
39 |
+
value: ''
|
40 |
+
- name: SOCKS_PROXY_PORT
|
41 |
+
value: ''
|
42 |
+
- name: HTTPS_PROXY
|
43 |
+
value: ''
|
44 |
+
resources:
|
45 |
+
limits:
|
46 |
+
cpu: 500m
|
47 |
+
memory: 500Mi
|
48 |
+
requests:
|
49 |
+
cpu: 300m
|
50 |
+
memory: 300Mi
|
51 |
+
---
|
52 |
+
apiVersion: v1
|
53 |
+
kind: Service
|
54 |
+
metadata:
|
55 |
+
labels:
|
56 |
+
app: chatgpt-web
|
57 |
+
name: chatgpt-web
|
58 |
+
spec:
|
59 |
+
ports:
|
60 |
+
- name: chatgpt-web
|
61 |
+
port: 3002
|
62 |
+
protocol: TCP
|
63 |
+
targetPort: 3002
|
64 |
+
selector:
|
65 |
+
app: chatgpt-web
|
66 |
+
type: ClusterIP
|
kubernetes/ingress.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
apiVersion: networking.k8s.io/v1
|
2 |
+
kind: Ingress
|
3 |
+
metadata:
|
4 |
+
annotations:
|
5 |
+
kubernetes.io/ingress.class: nginx
|
6 |
+
nginx.ingress.kubernetes.io/proxy-connect-timeout: '5'
|
7 |
+
name: chatgpt-web
|
8 |
+
spec:
|
9 |
+
rules:
|
10 |
+
- host: chatgpt.example.com
|
11 |
+
http:
|
12 |
+
paths:
|
13 |
+
- backend:
|
14 |
+
service:
|
15 |
+
name: chatgpt-web
|
16 |
+
port:
|
17 |
+
number: 3002
|
18 |
+
path: /
|
19 |
+
pathType: ImplementationSpecific
|
20 |
+
tls:
|
21 |
+
- secretName: chatgpt-web-tls
|
license
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT Kerwin1202
|
2 |
+
|
3 |
+
Copyright (c) 2023 ChenZhaoYu
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
package.json
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "chatgpt-web",
|
3 |
+
"version": "2.13.3",
|
4 |
+
"private": false,
|
5 |
+
"description": "ChatGPT Web",
|
6 |
+
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
7 |
+
"keywords": [
|
8 |
+
"chatgpt-web",
|
9 |
+
"chatgpt",
|
10 |
+
"chatbot",
|
11 |
+
"vue"
|
12 |
+
],
|
13 |
+
"scripts": {
|
14 |
+
"dev": "vite",
|
15 |
+
"build": "run-p type-check build-only",
|
16 |
+
"preview": "vite preview",
|
17 |
+
"build-only": "vite build",
|
18 |
+
"type-check": "vue-tsc --noEmit",
|
19 |
+
"lint": "eslint .",
|
20 |
+
"lint:fix": "eslint . --fix",
|
21 |
+
"bootstrap": "pnpm install && pnpm run common:prepare",
|
22 |
+
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml",
|
23 |
+
"common:prepare": "husky install"
|
24 |
+
},
|
25 |
+
"dependencies": {
|
26 |
+
"@traptitech/markdown-it-katex": "^3.6.0",
|
27 |
+
"@vueuse/core": "^9.13.0",
|
28 |
+
"chart.js": "^4.3.0",
|
29 |
+
"dayjs": "^1.11.7",
|
30 |
+
"highlight.js": "^11.7.0",
|
31 |
+
"html2canvas": "^1.4.1",
|
32 |
+
"jwt-decode": "^3.1.2",
|
33 |
+
"katex": "^0.16.4",
|
34 |
+
"markdown-it": "^13.0.1",
|
35 |
+
"naive-ui": "^2.34.3",
|
36 |
+
"pinia": "^2.0.33",
|
37 |
+
"vue": "^3.2.47",
|
38 |
+
"vue-chartjs": "^5.2.0",
|
39 |
+
"vue-i18n": "^9.2.2",
|
40 |
+
"vue-router": "^4.1.6"
|
41 |
+
},
|
42 |
+
"devDependencies": {
|
43 |
+
"@antfu/eslint-config": "^0.35.3",
|
44 |
+
"@commitlint/cli": "^17.4.4",
|
45 |
+
"@commitlint/config-conventional": "^17.4.4",
|
46 |
+
"@iconify/vue": "^4.1.0",
|
47 |
+
"@types/chart.js": "^2.9.37",
|
48 |
+
"@types/crypto-js": "^4.1.1",
|
49 |
+
"@types/katex": "^0.16.0",
|
50 |
+
"@types/markdown-it": "^12.2.3",
|
51 |
+
"@types/markdown-it-link-attributes": "^3.0.1",
|
52 |
+
"@types/node": "^18.14.6",
|
53 |
+
"@vitejs/plugin-vue": "^4.0.0",
|
54 |
+
"autoprefixer": "^10.4.13",
|
55 |
+
"axios": "^1.3.4",
|
56 |
+
"crypto-js": "^4.1.1",
|
57 |
+
"eslint": "^8.35.0",
|
58 |
+
"husky": "^8.0.3",
|
59 |
+
"less": "^4.1.3",
|
60 |
+
"lint-staged": "^13.1.2",
|
61 |
+
"markdown-it-link-attributes": "^4.0.1",
|
62 |
+
"npm-run-all": "^4.1.5",
|
63 |
+
"postcss": "^8.4.21",
|
64 |
+
"rimraf": "^4.2.0",
|
65 |
+
"tailwindcss": "^3.2.7",
|
66 |
+
"typescript": "~4.9.5",
|
67 |
+
"vite": "^4.2.0",
|
68 |
+
"vite-plugin-pwa": "^0.14.4",
|
69 |
+
"vue-tsc": "^1.2.0"
|
70 |
+
},
|
71 |
+
"lint-staged": {
|
72 |
+
"*.{ts,tsx,vue}": [
|
73 |
+
"pnpm lint:fix"
|
74 |
+
]
|
75 |
+
}
|
76 |
+
}
|
pnpm-lock.yaml
ADDED
The diff for this file is too large to render.
See raw diff
|
|
postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
public/favicon.ico
ADDED
public/favicon.svg
ADDED
public/pwa-192x192.png
ADDED
public/pwa-512x512.png
ADDED
replace-title.sh
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/sh
|
2 |
+
|
3 |
+
SITE_TITLE=${SITE_TITLE:-ChatGPT Web}
|
4 |
+
|
5 |
+
sed -i -E "s/<title>([^<]*)<\/title>/<title>${SITE_TITLE}<\/title>/g" /app/public/index.html
|
service/.env.example
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# OpenAI API Key - https://platform.openai.com/overview
|
2 |
+
OPENAI_API_KEY=
|
3 |
+
|
4 |
+
# change this to an `accessToken` extracted from the ChatGPT site's `https://chat.openai.com/api/auth/session` response
|
5 |
+
OPENAI_ACCESS_TOKEN=
|
6 |
+
|
7 |
+
# OpenAI API Base URL - https://api.openai.com
|
8 |
+
OPENAI_API_BASE_URL=
|
9 |
+
|
10 |
+
# ChatGPTAPI 或者 ChatGPTUnofficialProxyAPI
|
11 |
+
OPENAI_API_MODEL:
|
12 |
+
|
13 |
+
# set `true` to disable OpenAI API debug log
|
14 |
+
OPENAI_API_DISABLE_DEBUG=
|
15 |
+
|
16 |
+
# Reverse Proxy - Available on accessToken
|
17 |
+
# Default: https://ai.fakeopen.com/api/conversation
|
18 |
+
# More: https://github.com/transitive-bullshit/chatgpt-api#reverse-proxy
|
19 |
+
API_REVERSE_PROXY=
|
20 |
+
|
21 |
+
# timeout
|
22 |
+
TIMEOUT_MS=100000
|
23 |
+
|
24 |
+
# Rate Limit
|
25 |
+
MAX_REQUEST_PER_HOUR=
|
26 |
+
|
27 |
+
# Auth Rate Limit
|
28 |
+
AUTH_MAX_REQUEST_PER_MINUTE=5
|
29 |
+
|
30 |
+
# Socks Proxy Host
|
31 |
+
SOCKS_PROXY_HOST=
|
32 |
+
|
33 |
+
# Socks Proxy Port
|
34 |
+
SOCKS_PROXY_PORT=
|
35 |
+
|
36 |
+
# Socks Proxy Username
|
37 |
+
SOCKS_PROXY_USERNAME=
|
38 |
+
|
39 |
+
# Socks Proxy Password
|
40 |
+
SOCKS_PROXY_PASSWORD=
|
41 |
+
|
42 |
+
# HTTPS PROXY
|
43 |
+
HTTPS_PROXY=
|
44 |
+
|
45 |
+
# Title for site
|
46 |
+
SITE_TITLE="ChatGpt Web"
|
47 |
+
|
48 |
+
# Databse connection string
|
49 |
+
# MONGODB_URL=mongodb://chatgpt:xxxx@yourip:port
|
50 |
+
MONGODB_URL=mongodb://chatgpt:xxxx@database:27017
|
51 |
+
|
52 |
+
# Secret key for jwt
|
53 |
+
# If not empty, will need login
|
54 |
+
AUTH_SECRET_KEY=
|
55 |
+
|
56 |
+
# ----- Only valid after setting AUTH_SECRET_KEY begin ----
|
57 |
+
|
58 |
+
# Allow anyone register
|
59 |
+
REGISTER_ENABLED=false
|
60 |
+
|
61 |
+
# Enable register application review
|
62 |
+
REGISTER_REVIEW=false
|
63 |
+
|
64 |
+
# The site domain, Only for registration account verification
|
65 |
+
# without end /
|
66 |
+
SITE_DOMAIN=http://127.0.0.1:1002
|
67 |
+
|
68 |
+
# Allowed Email Providers, If it is empty, any mailbox is allowed
|
69 |
+
# REGISTER_MAILS=@qq.com,@sina.com,@163.com
|
70 |
+
REGISTER_MAILS=@qq.com,@sina.com,@163.com
|
71 |
+
|
72 |
+
# The roon user only email
|
73 |
+
ROOT_USER=
|
74 |
+
|
75 |
+
# Password salt
|
76 |
+
PASSWORD_MD5_SALT=anysalt
|
77 |
+
|
78 |
+
# User register email verify
|
79 |
+
SMTP_HOST=smtp.exmail.qq.com
|
80 |
+
SMTP_PORT=465
|
81 |
+
SMTP_TSL=true
|
82 |
+
SMTP_USERNAME=yourname@example.com
|
83 |
+
SMTP_PASSWORD=yourpassword
|
84 |
+
|
85 |
+
# ----- Only valid after setting AUTH_SECRET_KEY end ----
|
service/.eslintrc.json
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"root": true,
|
3 |
+
"ignorePatterns": ["build"],
|
4 |
+
"extends": ["@antfu"]
|
5 |
+
}
|
service/.gitignore
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Logs
|
2 |
+
logs
|
3 |
+
*.log
|
4 |
+
npm-debug.log*
|
5 |
+
yarn-debug.log*
|
6 |
+
yarn-error.log*
|
7 |
+
pnpm-debug.log*
|
8 |
+
lerna-debug.log*
|
9 |
+
|
10 |
+
node_modules
|
11 |
+
.DS_Store
|
12 |
+
dist
|
13 |
+
dist-ssr
|
14 |
+
coverage
|
15 |
+
*.local
|
16 |
+
|
17 |
+
/cypress/videos/
|
18 |
+
/cypress/screenshots/
|
19 |
+
|
20 |
+
# Editor directories and files
|
21 |
+
.vscode/*
|
22 |
+
!.vscode/settings.json
|
23 |
+
!.vscode/extensions.json
|
24 |
+
.idea
|
25 |
+
*.suo
|
26 |
+
*.ntvs*
|
27 |
+
*.njsproj
|
28 |
+
*.sln
|
29 |
+
*.sw?
|
30 |
+
|
31 |
+
build
|
service/.npmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
enable-pre-post-scripts=true
|
service/.vscode/extensions.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"recommendations": ["dbaeumer.vscode-eslint"]
|
3 |
+
}
|
service/.vscode/settings.json
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"prettier.enable": false,
|
3 |
+
"editor.formatOnSave": false,
|
4 |
+
"editor.codeActionsOnSave": {
|
5 |
+
"source.fixAll.eslint": true
|
6 |
+
},
|
7 |
+
"eslint.validate": [
|
8 |
+
"javascript",
|
9 |
+
"typescript",
|
10 |
+
"json",
|
11 |
+
"jsonc",
|
12 |
+
"json5",
|
13 |
+
"yaml"
|
14 |
+
],
|
15 |
+
"cSpell.words": [
|
16 |
+
"antfu",
|
17 |
+
"chatgpt",
|
18 |
+
"esno",
|
19 |
+
"GPTAPI",
|
20 |
+
"OPENAI"
|
21 |
+
]
|
22 |
+
}
|
service/package.json
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "chatgpt-web-service",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"private": false,
|
5 |
+
"description": "ChatGPT Web Service",
|
6 |
+
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
7 |
+
"keywords": [
|
8 |
+
"chatgpt-web",
|
9 |
+
"chatgpt",
|
10 |
+
"chatbot",
|
11 |
+
"express"
|
12 |
+
],
|
13 |
+
"engines": {
|
14 |
+
"node": "^16 || ^18 || ^19"
|
15 |
+
},
|
16 |
+
"scripts": {
|
17 |
+
"start": "esno ./src/index.ts",
|
18 |
+
"dev": "esno watch ./src/index.ts",
|
19 |
+
"prod": "esno ./build/index.js",
|
20 |
+
"build": "pnpm clean && tsup",
|
21 |
+
"clean": "rimraf build",
|
22 |
+
"lint": "eslint .",
|
23 |
+
"lint:fix": "eslint . --fix",
|
24 |
+
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml"
|
25 |
+
},
|
26 |
+
"dependencies": {
|
27 |
+
"axios": "^1.3.4",
|
28 |
+
"chatgpt": "^5.2.4",
|
29 |
+
"dayjs": "^1.11.7",
|
30 |
+
"dotenv": "^16.0.3",
|
31 |
+
"esno": "^0.16.3",
|
32 |
+
"express": "^4.18.2",
|
33 |
+
"express-rate-limit": "^6.7.0",
|
34 |
+
"https-proxy-agent": "^5.0.1",
|
35 |
+
"isomorphic-fetch": "^3.0.0",
|
36 |
+
"jwt-decode": "^3.1.2",
|
37 |
+
"mongodb": "^5.1.0",
|
38 |
+
"node-fetch": "^3.3.0",
|
39 |
+
"nodemailer": "^6.9.1",
|
40 |
+
"request-ip": "^3.3.0",
|
41 |
+
"socks-proxy-agent": "^7.0.0"
|
42 |
+
},
|
43 |
+
"devDependencies": {
|
44 |
+
"@antfu/eslint-config": "^0.35.3",
|
45 |
+
"@types/express": "^4.17.17",
|
46 |
+
"@types/mongodb": "^4.0.7",
|
47 |
+
"@types/node": "^18.14.6",
|
48 |
+
"eslint": "^8.35.0",
|
49 |
+
"jsonwebtoken": "^9.0.0",
|
50 |
+
"rimraf": "^4.3.0",
|
51 |
+
"tsup": "^6.6.3",
|
52 |
+
"typescript": "^4.9.5"
|
53 |
+
}
|
54 |
+
}
|
service/pnpm-lock.yaml
ADDED
The diff for this file is too large to render.
See raw diff
|
|
service/src/chatgpt/index.ts
ADDED
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as dotenv from 'dotenv'
|
2 |
+
import 'isomorphic-fetch'
|
3 |
+
import type { ChatGPTAPIOptions, ChatMessage, SendMessageOptions } from 'chatgpt'
|
4 |
+
import { ChatGPTAPI, ChatGPTUnofficialProxyAPI } from 'chatgpt'
|
5 |
+
import { SocksProxyAgent } from 'socks-proxy-agent'
|
6 |
+
import httpsProxyAgent from 'https-proxy-agent'
|
7 |
+
import fetch from 'node-fetch'
|
8 |
+
import type { AuditConfig, CHATMODEL, KeyConfig, UserInfo } from 'src/storage/model'
|
9 |
+
import jwt_decode from 'jwt-decode'
|
10 |
+
import dayjs from 'dayjs'
|
11 |
+
import type { TextAuditService } from '../utils/textAudit'
|
12 |
+
import { textAuditServices } from '../utils/textAudit'
|
13 |
+
import { getCacheApiKeys, getCacheConfig, getOriginConfig } from '../storage/config'
|
14 |
+
import { sendResponse } from '../utils'
|
15 |
+
import { hasAnyRole, isNotEmptyString } from '../utils/is'
|
16 |
+
import type { ChatContext, ChatGPTUnofficialProxyAPIOptions, JWT, ModelConfig } from '../types'
|
17 |
+
import { getChatByMessageId, updateRoomAccountId } from '../storage/mongo'
|
18 |
+
import type { RequestOptions } from './types'
|
19 |
+
|
20 |
+
const { HttpsProxyAgent } = httpsProxyAgent
|
21 |
+
|
22 |
+
dotenv.config()
|
23 |
+
|
24 |
+
const ErrorCodeMessage: Record<string, string> = {
|
25 |
+
401: '[OpenAI] 提供错误的API密钥 | Incorrect API key provided',
|
26 |
+
403: '[OpenAI] 服务器拒绝访问,请稍后再试 | Server refused to access, please try again later',
|
27 |
+
502: '[OpenAI] 错误的网关 | Bad Gateway',
|
28 |
+
503: '[OpenAI] 服务器繁忙,请稍后再试 | Server is busy, please try again later',
|
29 |
+
504: '[OpenAI] 网关超时 | Gateway Time-out',
|
30 |
+
500: '[OpenAI] 服务器繁忙,请稍后再试 | Internal Server Error',
|
31 |
+
}
|
32 |
+
|
33 |
+
let auditService: TextAuditService
|
34 |
+
const _lockedKeys: { key: string; lockedTime: number }[] = []
|
35 |
+
|
36 |
+
export async function initApi(key: KeyConfig, chatModel: CHATMODEL) {
|
37 |
+
// More Info: https://github.com/transitive-bullshit/chatgpt-api
|
38 |
+
|
39 |
+
const config = await getCacheConfig()
|
40 |
+
const model = chatModel as string
|
41 |
+
|
42 |
+
if (key.keyModel === 'ChatGPTAPI') {
|
43 |
+
const OPENAI_API_BASE_URL = config.apiBaseUrl
|
44 |
+
|
45 |
+
const options: ChatGPTAPIOptions = {
|
46 |
+
apiKey: key.key,
|
47 |
+
completionParams: { model },
|
48 |
+
debug: !config.apiDisableDebug,
|
49 |
+
messageStore: undefined,
|
50 |
+
getMessageById,
|
51 |
+
}
|
52 |
+
// increase max token limit if use gpt-4
|
53 |
+
if (model.toLowerCase().includes('gpt-4')) {
|
54 |
+
// if use 32k model
|
55 |
+
if (model.toLowerCase().includes('32k')) {
|
56 |
+
options.maxModelTokens = 32768
|
57 |
+
options.maxResponseTokens = 8192
|
58 |
+
}
|
59 |
+
else {
|
60 |
+
options.maxModelTokens = 8192
|
61 |
+
options.maxResponseTokens = 2048
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
if (isNotEmptyString(OPENAI_API_BASE_URL))
|
66 |
+
options.apiBaseUrl = `${OPENAI_API_BASE_URL}/v1`
|
67 |
+
|
68 |
+
await setupProxy(options)
|
69 |
+
|
70 |
+
return new ChatGPTAPI({ ...options })
|
71 |
+
}
|
72 |
+
else {
|
73 |
+
const options: ChatGPTUnofficialProxyAPIOptions = {
|
74 |
+
accessToken: key.key,
|
75 |
+
apiReverseProxyUrl: isNotEmptyString(config.reverseProxy) ? config.reverseProxy : 'https://ai.fakeopen.com/api/conversation',
|
76 |
+
model,
|
77 |
+
debug: !config.apiDisableDebug,
|
78 |
+
}
|
79 |
+
|
80 |
+
await setupProxy(options)
|
81 |
+
|
82 |
+
return new ChatGPTUnofficialProxyAPI({ ...options })
|
83 |
+
}
|
84 |
+
}
|
85 |
+
const processThreads: { userId: string; abort: AbortController; messageId: string }[] = []
|
86 |
+
async function chatReplyProcess(options: RequestOptions) {
|
87 |
+
const model = options.user.config.chatModel
|
88 |
+
const key = await getRandomApiKey(options.user, options.user.config.chatModel, options.room.accountId)
|
89 |
+
const userId = options.user._id.toString()
|
90 |
+
const messageId = options.messageId
|
91 |
+
if (key == null || key === undefined)
|
92 |
+
throw new Error('没有可用的配置。请再试一次 | No available configuration. Please try again.')
|
93 |
+
|
94 |
+
if (key.keyModel === 'ChatGPTUnofficialProxyAPI') {
|
95 |
+
if (!options.room.accountId)
|
96 |
+
updateRoomAccountId(userId, options.room.roomId, getAccountId(key.key))
|
97 |
+
|
98 |
+
if (options.lastContext && ((options.lastContext.conversationId && !options.lastContext.parentMessageId)
|
99 |
+
|| (!options.lastContext.conversationId && options.lastContext.parentMessageId)))
|
100 |
+
throw new Error('无法在一个房间同时使用 AccessToken 以及 Api,请联系管理员,或新开聊天室进行对话 | Unable to use AccessToken and Api at the same time in the same room, please contact the administrator or open a new chat room for conversation')
|
101 |
+
}
|
102 |
+
|
103 |
+
const { message, lastContext, process, systemMessage, temperature, top_p } = options
|
104 |
+
|
105 |
+
try {
|
106 |
+
const timeoutMs = (await getCacheConfig()).timeoutMs
|
107 |
+
let options: SendMessageOptions = { timeoutMs }
|
108 |
+
|
109 |
+
if (key.keyModel === 'ChatGPTAPI') {
|
110 |
+
if (isNotEmptyString(systemMessage))
|
111 |
+
options.systemMessage = systemMessage
|
112 |
+
options.completionParams = { model, temperature, top_p }
|
113 |
+
}
|
114 |
+
|
115 |
+
if (lastContext != null) {
|
116 |
+
if (key.keyModel === 'ChatGPTAPI')
|
117 |
+
options.parentMessageId = lastContext.parentMessageId
|
118 |
+
else
|
119 |
+
options = { ...lastContext }
|
120 |
+
}
|
121 |
+
const api = await initApi(key, model)
|
122 |
+
|
123 |
+
const abort = new AbortController()
|
124 |
+
options.abortSignal = abort.signal
|
125 |
+
processThreads.push({ userId, abort, messageId })
|
126 |
+
const response = await api.sendMessage(message, {
|
127 |
+
...options,
|
128 |
+
onProgress: (partialResponse) => {
|
129 |
+
process?.(partialResponse)
|
130 |
+
},
|
131 |
+
})
|
132 |
+
|
133 |
+
return sendResponse({ type: 'Success', data: response })
|
134 |
+
}
|
135 |
+
catch (error: any) {
|
136 |
+
const code = error.statusCode
|
137 |
+
if (code === 429 && (error.message.includes('Too Many Requests') || error.message.includes('Rate limit'))) {
|
138 |
+
// access token Only one message at a time
|
139 |
+
if (options.tryCount++ < 3) {
|
140 |
+
_lockedKeys.push({ key: key.key, lockedTime: Date.now() })
|
141 |
+
await new Promise(resolve => setTimeout(resolve, 2000))
|
142 |
+
return await chatReplyProcess(options)
|
143 |
+
}
|
144 |
+
}
|
145 |
+
global.console.error(error)
|
146 |
+
if (Reflect.has(ErrorCodeMessage, code))
|
147 |
+
return sendResponse({ type: 'Fail', message: ErrorCodeMessage[code] })
|
148 |
+
return sendResponse({ type: 'Fail', message: error.message ?? 'Please check the back-end console' })
|
149 |
+
}
|
150 |
+
finally {
|
151 |
+
const index = processThreads.findIndex(d => d.userId === userId)
|
152 |
+
if (index > -1)
|
153 |
+
processThreads.splice(index, 1)
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
export function abortChatProcess(userId: string) {
|
158 |
+
const index = processThreads.findIndex(d => d.userId === userId)
|
159 |
+
if (index <= -1)
|
160 |
+
return
|
161 |
+
const messageId = processThreads[index].messageId
|
162 |
+
processThreads[index].abort.abort()
|
163 |
+
processThreads.splice(index, 1)
|
164 |
+
return messageId
|
165 |
+
}
|
166 |
+
|
167 |
+
export function initAuditService(audit: AuditConfig) {
|
168 |
+
if (!audit || !audit.options || !audit.options.apiKey || !audit.options.apiSecret)
|
169 |
+
return
|
170 |
+
const Service = textAuditServices[audit.provider]
|
171 |
+
auditService = new Service(audit.options)
|
172 |
+
}
|
173 |
+
|
174 |
+
async function containsSensitiveWords(audit: AuditConfig, text: string): Promise<boolean> {
|
175 |
+
if (audit.customizeEnabled && isNotEmptyString(audit.sensitiveWords)) {
|
176 |
+
const textLower = text.toLowerCase()
|
177 |
+
const notSafe = audit.sensitiveWords.split('\n').filter(d => textLower.includes(d.trim().toLowerCase())).length > 0
|
178 |
+
if (notSafe)
|
179 |
+
return true
|
180 |
+
}
|
181 |
+
if (audit.enabled) {
|
182 |
+
if (!auditService)
|
183 |
+
initAuditService(audit)
|
184 |
+
return await auditService.containsSensitiveWords(text)
|
185 |
+
}
|
186 |
+
return false
|
187 |
+
}
|
188 |
+
|
189 |
+
async function fetchAccessTokenExpiredTime() {
|
190 |
+
const config = await getCacheConfig()
|
191 |
+
const jwt = jwt_decode(config.accessToken) as JWT
|
192 |
+
if (jwt.exp)
|
193 |
+
return dayjs.unix(jwt.exp).format('YYYY-MM-DD HH:mm:ss')
|
194 |
+
return '-'
|
195 |
+
}
|
196 |
+
|
197 |
+
let cachedBalance: number | undefined
|
198 |
+
let cacheExpiration = 0
|
199 |
+
|
200 |
+
async function fetchBalance() {
|
201 |
+
const now = new Date().getTime()
|
202 |
+
if (cachedBalance && cacheExpiration > now)
|
203 |
+
return Promise.resolve(cachedBalance.toFixed(3))
|
204 |
+
|
205 |
+
// 计算起始日期和结束日期
|
206 |
+
const startDate = new Date(now - 90 * 24 * 60 * 60 * 1000)
|
207 |
+
const endDate = new Date(now + 24 * 60 * 60 * 1000)
|
208 |
+
|
209 |
+
const config = await getCacheConfig()
|
210 |
+
const OPENAI_API_KEY = config.apiKey
|
211 |
+
const OPENAI_API_BASE_URL = config.apiBaseUrl
|
212 |
+
|
213 |
+
if (!isNotEmptyString(OPENAI_API_KEY))
|
214 |
+
return Promise.resolve('-')
|
215 |
+
|
216 |
+
const API_BASE_URL = isNotEmptyString(OPENAI_API_BASE_URL)
|
217 |
+
? OPENAI_API_BASE_URL
|
218 |
+
: 'https://api.openai.com'
|
219 |
+
|
220 |
+
// 查是否订阅
|
221 |
+
const urlSubscription = `${API_BASE_URL}/v1/dashboard/billing/subscription`
|
222 |
+
// 查普通账单
|
223 |
+
// const urlBalance = `${API_BASE_URL}/dashboard/billing/credit_grants`
|
224 |
+
// 查使用量
|
225 |
+
const urlUsage = `${API_BASE_URL}/v1/dashboard/billing/usage?start_date=${formatDate(startDate)}&end_date=${formatDate(endDate)}`
|
226 |
+
|
227 |
+
const headers = {
|
228 |
+
'Authorization': `Bearer ${OPENAI_API_KEY}`,
|
229 |
+
'Content-Type': 'application/json',
|
230 |
+
}
|
231 |
+
let socksAgent
|
232 |
+
let httpsAgent
|
233 |
+
if (isNotEmptyString(config.socksProxy)) {
|
234 |
+
socksAgent = new SocksProxyAgent({
|
235 |
+
hostname: config.socksProxy.split(':')[0],
|
236 |
+
port: parseInt(config.socksProxy.split(':')[1]),
|
237 |
+
userId: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[0] : undefined,
|
238 |
+
password: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[1] : undefined,
|
239 |
+
})
|
240 |
+
}
|
241 |
+
else if (isNotEmptyString(config.httpsProxy)) {
|
242 |
+
httpsAgent = new HttpsProxyAgent(config.httpsProxy)
|
243 |
+
}
|
244 |
+
|
245 |
+
try {
|
246 |
+
// 获取API限额
|
247 |
+
let response = await fetch(urlSubscription, { agent: socksAgent === undefined ? httpsAgent : socksAgent, headers })
|
248 |
+
if (!response.ok) {
|
249 |
+
console.error('您的账户已被封禁,请登录OpenAI进行查看。')
|
250 |
+
return
|
251 |
+
}
|
252 |
+
const subscriptionData = await response.json()
|
253 |
+
const totalAmount = subscriptionData.hard_limit_usd
|
254 |
+
|
255 |
+
// 获取已使用量
|
256 |
+
response = await fetch(urlUsage, { agent: socksAgent === undefined ? httpsAgent : socksAgent, headers })
|
257 |
+
const usageData = await response.json()
|
258 |
+
const totalUsage = usageData.total_usage / 100
|
259 |
+
|
260 |
+
// 计算剩余额度
|
261 |
+
cachedBalance = totalAmount - totalUsage
|
262 |
+
cacheExpiration = now + 60 * 60 * 1000
|
263 |
+
|
264 |
+
return Promise.resolve(cachedBalance.toFixed(3))
|
265 |
+
}
|
266 |
+
catch (error) {
|
267 |
+
global.console.error(error)
|
268 |
+
return Promise.resolve('-')
|
269 |
+
}
|
270 |
+
}
|
271 |
+
|
272 |
+
function formatDate(date) {
|
273 |
+
const year = date.getFullYear()
|
274 |
+
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
275 |
+
const day = date.getDate().toString().padStart(2, '0')
|
276 |
+
|
277 |
+
return `${year}-${month}-${day}`
|
278 |
+
}
|
279 |
+
|
280 |
+
async function chatConfig() {
|
281 |
+
const config = await getOriginConfig() as ModelConfig
|
282 |
+
// if (config.apiModel === 'ChatGPTAPI')
|
283 |
+
// config.balance = await fetchBalance()
|
284 |
+
// else
|
285 |
+
// config.accessTokenExpiredTime = await fetchAccessTokenExpiredTime()
|
286 |
+
return sendResponse<ModelConfig>({
|
287 |
+
type: 'Success',
|
288 |
+
data: config,
|
289 |
+
})
|
290 |
+
}
|
291 |
+
|
292 |
+
async function setupProxy(options: ChatGPTAPIOptions | ChatGPTUnofficialProxyAPIOptions) {
|
293 |
+
const config = await getCacheConfig()
|
294 |
+
if (isNotEmptyString(config.socksProxy)) {
|
295 |
+
const agent = new SocksProxyAgent({
|
296 |
+
hostname: config.socksProxy.split(':')[0],
|
297 |
+
port: parseInt(config.socksProxy.split(':')[1]),
|
298 |
+
userId: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[0] : undefined,
|
299 |
+
password: isNotEmptyString(config.socksAuth) ? config.socksAuth.split(':')[1] : undefined,
|
300 |
+
|
301 |
+
})
|
302 |
+
options.fetch = (url, options) => {
|
303 |
+
return fetch(url, { agent, ...options })
|
304 |
+
}
|
305 |
+
}
|
306 |
+
else {
|
307 |
+
if (isNotEmptyString(config.httpsProxy)) {
|
308 |
+
const httpsProxy = config.httpsProxy
|
309 |
+
if (httpsProxy) {
|
310 |
+
const agent = new HttpsProxyAgent(httpsProxy)
|
311 |
+
options.fetch = (url, options) => {
|
312 |
+
return fetch(url, { agent, ...options })
|
313 |
+
}
|
314 |
+
}
|
315 |
+
}
|
316 |
+
}
|
317 |
+
}
|
318 |
+
|
319 |
+
async function getMessageById(id: string): Promise<ChatMessage | undefined> {
|
320 |
+
const isPrompt = id.startsWith('prompt_')
|
321 |
+
const chatInfo = await getChatByMessageId(isPrompt ? id.substring(7) : id)
|
322 |
+
|
323 |
+
if (chatInfo) {
|
324 |
+
if (isPrompt) { // prompt
|
325 |
+
return {
|
326 |
+
id,
|
327 |
+
conversationId: chatInfo.options.conversationId,
|
328 |
+
parentMessageId: chatInfo.options.parentMessageId,
|
329 |
+
role: 'user',
|
330 |
+
text: chatInfo.prompt,
|
331 |
+
}
|
332 |
+
}
|
333 |
+
else {
|
334 |
+
return { // completion
|
335 |
+
id,
|
336 |
+
conversationId: chatInfo.options.conversationId,
|
337 |
+
parentMessageId: `prompt_${id}`, // parent message is the prompt
|
338 |
+
role: 'assistant',
|
339 |
+
text: chatInfo.response,
|
340 |
+
}
|
341 |
+
}
|
342 |
+
}
|
343 |
+
else { return undefined }
|
344 |
+
}
|
345 |
+
|
346 |
+
async function randomKeyConfig(keys: KeyConfig[]): Promise<KeyConfig | null> {
|
347 |
+
if (keys.length <= 0)
|
348 |
+
return null
|
349 |
+
// cleanup old locked keys
|
350 |
+
_lockedKeys.filter(d => d.lockedTime <= Date.now() - 1000 * 20).forEach(d => _lockedKeys.splice(_lockedKeys.indexOf(d), 1))
|
351 |
+
|
352 |
+
let unsedKeys = keys.filter(d => _lockedKeys.filter(l => d.key === l.key).length <= 0)
|
353 |
+
const start = Date.now()
|
354 |
+
while (unsedKeys.length <= 0) {
|
355 |
+
if (Date.now() - start > 3000)
|
356 |
+
break
|
357 |
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
358 |
+
unsedKeys = keys.filter(d => _lockedKeys.filter(l => d.key === l.key).length <= 0)
|
359 |
+
}
|
360 |
+
if (unsedKeys.length <= 0)
|
361 |
+
return null
|
362 |
+
const thisKey = unsedKeys[Math.floor(Math.random() * unsedKeys.length)]
|
363 |
+
return thisKey
|
364 |
+
}
|
365 |
+
|
366 |
+
async function getRandomApiKey(user: UserInfo, chatModel: CHATMODEL, accountId?: string): Promise<KeyConfig | undefined> {
|
367 |
+
let keys = (await getCacheApiKeys()).filter(d => hasAnyRole(d.userRoles, user.roles))
|
368 |
+
.filter(d => d.chatModels.includes(chatModel))
|
369 |
+
if (accountId)
|
370 |
+
keys = keys.filter(d => d.keyModel === 'ChatGPTUnofficialProxyAPI' && getAccountId(d.key) === accountId)
|
371 |
+
|
372 |
+
return randomKeyConfig(keys)
|
373 |
+
}
|
374 |
+
|
375 |
+
function getAccountId(accessToken: string): string {
|
376 |
+
try {
|
377 |
+
const jwt = jwt_decode(accessToken) as JWT
|
378 |
+
return jwt['https://api.openai.com/auth'].user_id
|
379 |
+
}
|
380 |
+
catch (error) {
|
381 |
+
return ''
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
export type { ChatContext, ChatMessage }
|
386 |
+
|
387 |
+
export { chatReplyProcess, chatConfig, containsSensitiveWords }
|