Skip to content

Commit

Permalink
feat: 新增限流功能 (Chanzhaoyu#718)
Browse files Browse the repository at this point in the history
* 请求速率限制

* perf: 优化代码

---------

Co-authored-by: ChenZhaoYu <790348264@qq.com>
  • Loading branch information
2 people authored and pzcn committed Mar 22, 2023
1 parent c5235a4 commit 0027bf8
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 60 deletions.
81 changes: 23 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@
</div>
</br>

## 说明
> **此项目 Fork 自 [Chanzhaoyu/chatgpt-web](https://github.com/Chanzhaoyu/chatgpt-web), 除了定时合并该分支, 新增了部分特色功能, 注册&登录, 前端页面设置apikey 等**
</br>
## 截图
> 声明:此项目只发布于 Github,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号、付费服务、讨论群、讨论组等行为。谨防受骗。
![cover3](./docs/login.jpg)
![cover](./docs/c1.png)
![cover2](./docs/c2.png)
![cover3](./docs/basesettings.jpg)

- [ChatGPT Web](#chatgpt-web)
- [介绍](#介绍)
Expand Down Expand Up @@ -87,9 +80,7 @@

[] 对代码等消息类型的格式化美化处理

[] 支持用户登录注册

[] 前端页面设置 apikey 等信息
[] 访问权限控制

[] 数据导入、导出

Expand Down Expand Up @@ -179,6 +170,7 @@ pnpm dev
通用:

- `AUTH_SECRET_KEY` 访问权限密钥,可选
- `MAX_REQUEST_PER_HOUR` 每小时最大请求次数,可选,默认无限
- `TIMEOUT_MS` 超时,单位毫秒,可选
- `SOCKS_PROXY_HOST``SOCKS_PROXY_PORT` 一起时生效,可选
- `SOCKS_PROXY_PORT``SOCKS_PROXY_HOST` 一起时生效,可选
Expand Down Expand Up @@ -210,19 +202,16 @@ http://localhost:3002/

#### Docker compose

[Hub 地址](https://hub.docker.com/repository/docker/kerwin1202/chatgpt-web/general)
[Hub 地址](https://hub.docker.com/repository/docker/chenzhaoyu94/chatgpt-web/general)

```yml
version: '3'

services:
app:
image: kerwin1202/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
container_name: chatgptweb
image: chenzhaoyu94/chatgpt-web # 总是使用 latest ,更新时重新 pull 该 tag 镜像即可
ports:
- 3002:3002
depends_on:
- database
environment:
# 二选一
OPENAI_API_KEY: sk-xxx
Expand All @@ -234,6 +223,10 @@ services:
OPENAI_API_MODEL: xxx
# 反向代理,可选
API_REVERSE_PROXY: xxx
# 访问权限密钥,可选
AUTH_SECRET_KEY: xxx
# 每小时最大请求次数,可选,默认无限
MAX_REQUEST_PER_HOUR: 0
# 超时,单位毫秒,可选
TIMEOUT_MS: 60000
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
Expand All @@ -242,47 +235,6 @@ services:
SOCKS_PROXY_PORT: xxx
# HTTPS 代理,可选,支持 http,https,socks5
HTTPS_PROXY: http://xxx:7890
# 访问jwt加密参数,可选 不为空则允许登录 同时需要设置 MONGODB_URL
AUTH_SECRET_KEY: xxx
# 网站名称
SITE_TITLE: ChatGpt Web
# mongodb 的连接字符串
MONGODB_URL: 'mongodb://chatgpt:xxxx@database:27017'
# 网站是否开启注册
REGISTER_ENABLED: true
# 开启注册之后 网站注册允许的邮箱后缀 如果空 则允许任意后缀
REGISTER_MAILS: '@qq.com,@sina.com,@163.com'
# 开启注册之后 密码加密的盐
PASSWORD_MD5_SALT: xxx
# 开启注册之后 超级管理邮箱
ROOT_USER: me@example.com
# 开启注册之后 网站域名 不含 / 注册的时候发送验证邮箱使用
SITE_DOMAIN: http://127.0.0.1:3002
# 开启注册之后 发送验证邮箱配置
SMTP_HOST: smtp.exmail.qq.com
SMTP_PORT: 465
SMTP_TSL: true
SMTP_USERNAME: noreply@examile.com
SMTP_PASSWORD: xxx
links:
- database

database:
image: mongo
container_name: chatgptweb-database
ports:
- '27017:27017'
expose:
- '27017'
volumes:
- mongodb:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: chatgpt
MONGO_INITDB_ROOT_PASSWORD: xxxx
MONGO_INITDB_DATABASE: chatgpt

volumes:
mongodb: {}
```
- `OPENAI_API_BASE_URL` 可选,设置 `OPENAI_API_KEY` 时可用
- `OPENAI_API_MODEL` 可选,设置 `OPENAI_API_KEY` 时可用
Expand All @@ -296,6 +248,7 @@ volumes:
| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------- |
| `PORT` | 必填 | 默认 `3002`
| `AUTH_SECRET_KEY` | 可选 | 访问权限密钥 |
| `MAX_REQUEST_PER_HOUR` | 可选 | 每小时最大请求次数,可选,默认无限 |
| `TIMEOUT_MS` | 可选 | 超时时间,单位毫秒 |
| `OPENAI_API_KEY` | `OpenAI API` 二选一 | 使用 `OpenAI API` 所需的 `apiKey` [(获取 apiKey)](https://platform.openai.com/overview) |
| `OPENAI_ACCESS_TOKEN` | `Web API` 二选一 | 使用 `Web API` 所需的 `accessToken` [(获取 accessToken)](https://chat.openai.com/api/auth/session) |
Expand Down Expand Up @@ -368,7 +321,19 @@ A: 一种可能原因是经过 Nginx 反向代理,开启了 buffer,则 Nginx
</a>

## 赞助
如果你觉得这个项目对你有帮助,请给我点个Star。

如果你觉得这个项目对你有帮助,并且情况允许的话,可以给我一点点支持,总之非常感谢支持~

<div style="display: flex; gap: 20px;">
<div style="text-align: center">
<img style="max-width: 100%" src="./docs/wechat.png" alt="微信" />
<p>WeChat Pay</p>
</div>
<div style="text-align: center">
<img style="max-width: 100%" src="./docs/alipay.png" alt="支付宝" />
<p>Alipay</p>
</div>
</div>

## License
MIT © [Kerwin1202](./license)
MIT © [ChenZhaoYu](./license)
2 changes: 2 additions & 0 deletions docker-compose/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ services:
API_REVERSE_PROXY: xxx
# 访问jwt加密参数,可选 不为空则允许登录 同时需要设置 MONGODB_URL
AUTH_SECRET_KEY: xxx
# 每小时最大请求次数,可选,默认无限
MAX_REQUEST_PER_HOUR: 0
# 超时,单位毫秒,可选
TIMEOUT_MS: 60000
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
Expand Down
4 changes: 3 additions & 1 deletion service/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,6 @@ SMTP_TSL=true
SMTP_USERNAME=yourname@example.com
SMTP_PASSWORD=yourpassword

# ----- Only valid after setting AUTH_SECRET_KEY end ----
# ----- Only valid after setting AUTH_SECRET_KEY end ----
# Rate Limit
MAX_REQUEST_PER_HOUR=
1 change: 1 addition & 0 deletions service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dotenv": "^16.0.3",
"esno": "^0.16.3",
"express": "^4.18.2",
"express-rate-limit": "^6.7.0",
"https-proxy-agent": "^5.0.1",
"isomorphic-fetch": "^3.0.0",
"mongodb": "^5.1.0",
Expand Down
11 changes: 11 additions & 0 deletions service/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { clearConfigCache, getCacheConfig, getOriginConfig } from './storage/con
import type { ChatOptions, Config, MailConfig, SiteConfig, UserInfo } from './storage/model'
import { Status } from './storage/model'
import { clearChat, createChatRoom, createUser, deleteChat, deleteChatRoom, existsChatRoom, getChat, getChatRooms, getChats, getUser, getUserById, insertChat, renameChatRoom, updateChat, updateConfig, updateUserInfo, verifyUser } from './storage/mongo'
import { limiter } from './middleware/limiter'
import { isNotEmptyString } from './utils/is'
import { sendMail } from './utils/mail'
import { checkUserVerify, getUserVerifyUrl, md5 } from './utils/security'
Expand Down Expand Up @@ -150,7 +151,7 @@ router.post('/chat', auth, async (req, res) => {
}
})

router.post('/chat-process', auth, async (req, res) => {
router.post('/chat-process', [auth, limiter], async (req, res) => {
res.setHeader('Content-type', 'application/octet-stream')

try {
Expand Down
19 changes: 19 additions & 0 deletions service/src/middleware/limiter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { rateLimit } from 'express-rate-limit'
import { isNotEmptyString } from '../utils/is'

const MAX_REQUEST_PER_HOUR = process.env.MAX_REQUEST_PER_HOUR

const maxCount = (isNotEmptyString(MAX_REQUEST_PER_HOUR) && !isNaN(Number(MAX_REQUEST_PER_HOUR)))
? parseInt(MAX_REQUEST_PER_HOUR)
: 0 // 0 means unlimited

const limiter = rateLimit({
windowMs: 60 * 60 * 1000, // Maximum number of accesses within an hour
max: maxCount,
statusCode: 200, // 200 means success,but the message is 'Too many request from this IP in 1 hour'
message: async (req, res) => {
res.send({ status: 'Fail', message: 'Too many request from this IP in 1 hour', data: null })
},
})

export { limiter }

0 comments on commit 0027bf8

Please sign in to comment.