Merge branch 'songquanpeng:main' into main

This commit is contained in:
quzard 2023-06-22 21:08:16 +08:00 committed by GitHub
commit e0b12c5f87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 38 deletions

View File

@ -152,12 +152,11 @@ sudo service nginx restart
### 多机部署 ### 多机部署
1. 所有服务器 `SESSION_SECRET` 设置一样的值。 1. 所有服务器 `SESSION_SECRET` 设置一样的值。
2. 必须设置 `SQL_DSN`,使用 MySQL 数据库而非 SQLite所有服务器连接同一个数据库。 2. 必须设置 `SQL_DSN`,使用 MySQL 数据库而非 SQLite所有服务器连接同一个数据库。
3. 所有从服务器必须设置 `SYNC_FREQUENCY`,以定期从数据库同步配置。 3. 所有从服务器必须设置 `NODE_TYPE``slave`
4. 从服务器可以选择设置 `FRONTEND_BASE_URL`,以重定向页面请求到主服务器。 4. 设置 `SYNC_FREQUENCY` 后服务器将定期从数据库同步配置。
5. 从服务器上**分别**装好 Redis设置好 `REDIS_CONN_STRING`,这样可以做到在缓存未过期的情况下数据库零访问,可以减少延迟。 5. 从服务器可以选择设置 `FRONTEND_BASE_URL`,以重定向页面请求到主服务器。
6. 如果主服务器访问数据库延迟也比较高,则也需要启用 Redis并设置 `SYNC_FREQUENCY`,以定期从数据库同步配置。 6. 从服务器上**分别**装好 Redis设置好 `REDIS_CONN_STRING`,这样可以做到在缓存未过期的情况下数据库零访问,可以减少延迟。
7. 如果主服务器访问数据库延迟也比较高,则也需要启用 Redis并设置 `SYNC_FREQUENCY`,以定期从数据库同步配置。
注意,设置 `SYNC_FREQUENCY` 后会导致从服务器的状态不会实时更新,而是定期同步。
环境变量的具体使用方法详见[此处](#环境变量)。 环境变量的具体使用方法详见[此处](#环境变量)。
@ -249,6 +248,8 @@ graph LR
+ 例子:`FRONTEND_BASE_URL=https://openai.justsong.cn` + 例子:`FRONTEND_BASE_URL=https://openai.justsong.cn`
5. `SYNC_FREQUENCY`:设置之后将定期与数据库同步配置,单位为秒,未设置则不进行同步。 5. `SYNC_FREQUENCY`:设置之后将定期与数据库同步配置,单位为秒,未设置则不进行同步。
+ 例子:`SYNC_FREQUENCY=60` + 例子:`SYNC_FREQUENCY=60`
6. `NODE_TYPE`:设置之后将指定节点类型,可选值为 `master``slave`,未设置则默认为 `master`
+ 例子:`NODE_TYPE=slave`
### 命令行参数 ### 命令行参数
1. `--port <port_number>`: 指定服务器监听的端口号,默认为 `3000` 1. `--port <port_number>`: 指定服务器监听的端口号,默认为 `3000`

View File

@ -68,7 +68,7 @@ var PreConsumedQuota = 500
var RootUserEmail = "" var RootUserEmail = ""
var IsMasterNode = os.Getenv("SYNC_FREQUENCY") == "" var IsMasterNode = os.Getenv("NODE_TYPE") != "slave"
const ( const (
RoleGuestUser = 0 RoleGuestUser = 0

View File

@ -17,9 +17,9 @@ func InitRedisClient() (err error) {
SysLog("REDIS_CONN_STRING not set, Redis is not enabled") SysLog("REDIS_CONN_STRING not set, Redis is not enabled")
return nil return nil
} }
if IsMasterNode { if os.Getenv("SYNC_FREQUENCY") == "" {
SysLog("Redis is disabled on master node")
RedisEnabled = false RedisEnabled = false
SysLog("SYNC_FREQUENCY not set, Redis is disabled")
return nil return nil
} }
SysLog("Redis is enabled") SysLog("Redis is enabled")

View File

@ -16,8 +16,10 @@ services:
- REDIS_CONN_STRING=redis://redis - REDIS_CONN_STRING=redis://redis
- SESSION_SECRET=random_string # 修改为随机字符串 - SESSION_SECRET=random_string # 修改为随机字符串
- TZ=Asia/Shanghai - TZ=Asia/Shanghai
# - SYNC_FREQUENCY=60 # 多机部署时从节点取消注释该行 # - NODE_TYPE=slave # 多机部署时从节点取消注释该行
# - SYNC_FREQUENCY=60 # 需要定期从数据库加载数据时取消注释该行
# - FRONTEND_BASE_URL=https://openai.justsong.cn # 多机部署时从节点取消注释该行 # - FRONTEND_BASE_URL=https://openai.justsong.cn # 多机部署时从节点取消注释该行
depends_on: depends_on:
- redis - redis
healthcheck: healthcheck:

View File

@ -36,15 +36,15 @@ const EditChannel = () => {
let res = await API.get(`/api/channel/${channelId}`); let res = await API.get(`/api/channel/${channelId}`);
const { success, message, data } = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
if (data.models === "") { if (data.models === '') {
data.models = [] data.models = [];
} else { } else {
data.models = data.models.split(",") data.models = data.models.split(',');
} }
if (data.group === "") { if (data.group === '') {
data.groups = [] data.groups = [];
} else { } else {
data.groups = data.group.split(",") data.groups = data.group.split(',');
} }
setInputs(data); setInputs(data);
} else { } else {
@ -59,10 +59,10 @@ const EditChannel = () => {
setModelOptions(res.data.data.map((model) => ({ setModelOptions(res.data.data.map((model) => ({
key: model.id, key: model.id,
text: model.id, text: model.id,
value: model.id, value: model.id
}))); })));
setFullModels(res.data.data.map((model) => model.id)); setFullModels(res.data.data.map((model) => model.id));
setBasicModels(res.data.data.filter((model) => !model.id.startsWith("gpt-4")).map((model) => model.id)); setBasicModels(res.data.data.filter((model) => !model.id.startsWith('gpt-4')).map((model) => model.id));
} catch (error) { } catch (error) {
showError(error.message); showError(error.message);
} }
@ -74,7 +74,7 @@ const EditChannel = () => {
setGroupOptions(res.data.data.map((group) => ({ setGroupOptions(res.data.data.map((group) => ({
key: group, key: group,
text: group, text: group,
value: group, value: group
}))); })));
} catch (error) { } catch (error) {
showError(error.message); showError(error.message);
@ -94,6 +94,10 @@ const EditChannel = () => {
showInfo('请填写渠道名称和渠道密钥!'); showInfo('请填写渠道名称和渠道密钥!');
return; return;
} }
if (inputs.models.length === 0) {
showInfo('请至少选择一个模型!');
return;
}
let localInputs = inputs; let localInputs = inputs;
if (localInputs.base_url.endsWith('/')) { if (localInputs.base_url.endsWith('/')) {
localInputs.base_url = localInputs.base_url.slice(0, localInputs.base_url.length - 1); localInputs.base_url = localInputs.base_url.slice(0, localInputs.base_url.length - 1);
@ -102,8 +106,8 @@ const EditChannel = () => {
localInputs.other = '2023-03-15-preview'; localInputs.other = '2023-03-15-preview';
} }
let res; let res;
localInputs.models = localInputs.models.join(",") localInputs.models = localInputs.models.join(',');
localInputs.group = localInputs.groups.join(",") localInputs.group = localInputs.groups.join(',');
if (isEdit) { if (isEdit) {
res = await API.put(`/api/channel/`, { ...localInputs, id: parseInt(channelId) }); res = await API.put(`/api/channel/`, { ...localInputs, id: parseInt(channelId) });
} else { } else {
@ -190,9 +194,9 @@ const EditChannel = () => {
inputs.type !== 3 && inputs.type !== 8 && ( inputs.type !== 3 && inputs.type !== 8 && (
<Form.Field> <Form.Field>
<Form.Input <Form.Input
label='Base URL' label='镜像'
name='base_url' name='base_url'
placeholder={'请输入自定义 Base URL格式为https://domain.com可不填不填使用渠道默认值'} placeholder={'请输入镜像站地址格式为https://domain.com可不填不填使用渠道默认值'}
onChange={handleInputChange} onChange={handleInputChange}
value={inputs.base_url} value={inputs.base_url}
autoComplete='new-password' autoComplete='new-password'
@ -257,21 +261,10 @@ const EditChannel = () => {
<Button type={'button'} onClick={() => { <Button type={'button'} onClick={() => {
handleInputChange(null, { name: 'models', value: fullModels }); handleInputChange(null, { name: 'models', value: fullModels });
}}>填入所有模型</Button> }}>填入所有模型</Button>
<Button type={'button'} onClick={() => {
handleInputChange(null, { name: 'models', value: [] });
}}>清除所有模型</Button>
</div> </div>
{
inputs.type === 1 && (
<Form.Field>
<Form.Input
label='代理'
name='base_url'
placeholder={'请输入 OpenAI API 代理地址如果不需要请留空格式为https://api.openai.com'}
onChange={handleInputChange}
value={inputs.base_url}
autoComplete='new-password'
/>
</Form.Field>
)
}
{ {
batch ? <Form.Field> batch ? <Form.Field>
<Form.TextArea <Form.TextArea