From 140e843d93b956e236ca9e03ffe96905b8fbb7bf Mon Sep 17 00:00:00 2001 From: Ehco1996 Date: Fri, 1 Mar 2024 20:39:28 +0800 Subject: [PATCH 01/23] Add Telegram bot token and name --- common/constants.go | 3 ++ docker-compose.yml | 2 +- makefile | 17 +++++++ model/option.go | 4 ++ web/src/components/SystemSetting.js | 75 +++++++++++++++++++++-------- 5 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 makefile diff --git a/common/constants.go b/common/constants.go index 26684e3..55cc793 100644 --- a/common/constants.go +++ b/common/constants.go @@ -82,6 +82,9 @@ var WeChatAccountQRCodeImageURL = "" var TurnstileSiteKey = "" var TurnstileSecretKey = "" +var TelegramBotToken = "" +var TelegramBotName = "" + var QuotaForNewUser = 0 var QuotaForInviter = 0 var QuotaForInvitee = 0 diff --git a/docker-compose.yml b/docker-compose.yml index 40da248..403f372 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: - ./data:/data - ./logs:/app/logs environment: - - SQL_DSN=root:123456@tcp(host.docker.internal:3306)/new-api # 修改此行,或注释掉以使用 SQLite 作为数据库 + # - SQL_DSN=root:123456@tcp(host.docker.internal:3306)/new-api # 修改此行,或注释掉以使用 SQLite 作为数据库 - REDIS_CONN_STRING=redis://redis - SESSION_SECRET=random_string # 修改为随机字符串 - TZ=Asia/Shanghai diff --git a/makefile b/makefile new file mode 100644 index 0000000..1df2b5c --- /dev/null +++ b/makefile @@ -0,0 +1,17 @@ +FRONTEND_DIR = ./web +BACKEND_DIR = . + +.PHONY: all start-frontend start-backend + +all: start-frontend start-backend + +# 启动前端开发服务器 +start-frontend: + @echo "Starting frontend dev server..." + @cd $(FRONTEND_DIR) && npm start & + +# 启动后端开发服务器 +start-backend: + @echo "Starting backend dev server..." + @cd $(BACKEND_DIR) && go run main.go & + diff --git a/model/option.go b/model/option.go index a651b85..dcb4c34 100644 --- a/model/option.go +++ b/model/option.go @@ -215,6 +215,10 @@ func updateOptionMap(key string, value string) (err error) { common.WeChatServerToken = value case "WeChatAccountQRCodeImageURL": common.WeChatAccountQRCodeImageURL = value + case "TelegramBotToken": + common.TelegramBotToken = value + case "TelegramBotName": + common.TelegramBotName = value case "TurnstileSiteKey": common.TurnstileSiteKey = value case "TurnstileSecretKey": diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index 197050e..a8956b7 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -1,6 +1,6 @@ -import React, {useEffect, useState} from 'react'; -import {Button, Divider, Form, Grid, Header, Modal, Message} from 'semantic-ui-react'; -import {API, removeTrailingSlash, showError, verifyJSON} from '../helpers'; +import React, { useEffect, useState } from 'react'; +import { Button, Divider, Form, Grid, Header, Modal, Message } from 'semantic-ui-react'; +import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers'; const SystemSetting = () => { let [inputs, setInputs] = useState({ @@ -32,7 +32,11 @@ const SystemSetting = () => { TurnstileSecretKey: '', RegisterEnabled: '', EmailDomainRestrictionEnabled: '', - EmailDomainWhitelist: '' + EmailDomainWhitelist: '', + // telegram login + TelegramLoginEnabled: '', + TelegramBotToken: '', + TelegramBotName: '', }); const [originInputs, setOriginInputs] = useState({}); let [loading, setLoading] = useState(false); @@ -42,7 +46,7 @@ const SystemSetting = () => { const getOptions = async () => { const res = await API.get('/api/option/'); - const {success, message, data} = res.data; + const { success, message, data } = res.data; if (success) { let newInputs = {}; data.forEach((item) => { @@ -58,7 +62,7 @@ const SystemSetting = () => { setOriginInputs(newInputs); setEmailDomainWhitelist(newInputs.EmailDomainWhitelist.split(',').map((item) => { - return {key: item, text: item, value: item}; + return { key: item, text: item, value: item }; })); } else { showError(message); @@ -77,6 +81,7 @@ const SystemSetting = () => { case 'EmailVerificationEnabled': case 'GitHubOAuthEnabled': case 'WeChatAuthEnabled': + case 'TelegramLoginEnabled': case 'TurnstileCheckEnabled': case 'EmailDomainRestrictionEnabled': case 'RegisterEnabled': @@ -89,7 +94,7 @@ const SystemSetting = () => { key, value }); - const {success, message} = res.data; + const { success, message } = res.data; if (success) { if (key === 'EmailDomainWhitelist') { value = value.split(','); @@ -106,7 +111,7 @@ const SystemSetting = () => { setLoading(false); }; - const handleInputChange = async (e, {name, value}) => { + const handleInputChange = async (e, { name, value }) => { if (name === 'PasswordLoginEnabled' && inputs[name] === 'true') { // block disabling password login setShowPasswordWarningModal(true); @@ -130,7 +135,7 @@ const SystemSetting = () => { name === 'EmailDomainWhitelist' || name === 'TopupGroupRatio' ) { - setInputs((inputs) => ({...inputs, [name]: value})); + setInputs((inputs) => ({ ...inputs, [name]: value })); } else { await updateOption(name, value); } @@ -234,6 +239,12 @@ const SystemSetting = () => { } }; + const submitTelegramSettings = async () => { + await updateOption('TelegramLoginEnabled', inputs.TelegramLoginEnabled); + await updateOption('TelegramBotToken', inputs.TelegramBotToken); + await updateOption('TelegramBotName', inputs.TelegramBotName); + }; + const submitTurnstile = async () => { if (originInputs['TurnstileSiteKey'] !== inputs.TurnstileSiteKey) { await updateOption('TurnstileSiteKey', inputs.TurnstileSiteKey); @@ -279,7 +290,7 @@ const SystemSetting = () => { 更新服务器地址 - +
支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)
{ label='充值分组倍率' name='TopupGroupRatio' onChange={handleInputChange} - style={{minHeight: 250, fontFamily: 'JetBrains Mono, Consolas'}} + style={{ minHeight: 250, fontFamily: 'JetBrains Mono, Consolas' }} autoComplete='new-password' value={inputs.TopupGroupRatio} placeholder='为一个 JSON 文本,键为组名称,值为倍率' @@ -327,7 +338,7 @@ const SystemSetting = () => { 更新支付设置 - +
配置登录注册
{ open={showPasswordWarningModal} onClose={() => setShowPasswordWarningModal(false)} size={'tiny'} - style={{maxWidth: '450px'}} + style={{ maxWidth: '450px' }} > 警告 @@ -401,7 +412,33 @@ const SystemSetting = () => { onChange={handleInputChange} /> - + +
配置 Telegram 登录
+ + + + + + + 保存 Telegram 登录设置 +
配置邮箱域名白名单 用以防止恶意用户利用临时邮箱批量注册 @@ -443,13 +480,13 @@ const SystemSetting = () => { autoComplete='new-password' placeholder='输入新的允许的邮箱域名' value={restrictedDomainInput} - onChange={(e, {value}) => { + onChange={(e, { value }) => { setRestrictedDomainInput(value); }} /> 保存邮箱域名白名单设置 - +
配置 SMTP 用以支持系统的邮件发送 @@ -500,7 +537,7 @@ const SystemSetting = () => { /> 保存 SMTP 设置 - +
配置 GitHub OAuth App @@ -538,7 +575,7 @@ const SystemSetting = () => { 保存 GitHub OAuth 设置 - +
配置 WeChat Server @@ -582,7 +619,7 @@ const SystemSetting = () => { 保存 WeChat Server 设置 - +
配置 Turnstile From 690d2e6ba2c35bfee01464aa4e0a7b07acafc9d1 Mon Sep 17 00:00:00 2001 From: Ehco1996 Date: Fri, 1 Mar 2024 21:54:24 +0800 Subject: [PATCH 02/23] Add Telegram OAuth support --- common/constants.go | 1 + controller/misc.go | 2 ++ makefile | 4 +++ model/option.go | 5 +++ web/src/components/SystemSetting.js | 53 +++++++++++++++-------------- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/common/constants.go b/common/constants.go index 55cc793..9ce2003 100644 --- a/common/constants.go +++ b/common/constants.go @@ -45,6 +45,7 @@ var PasswordRegisterEnabled = true var EmailVerificationEnabled = false var GitHubOAuthEnabled = false var WeChatAuthEnabled = false +var TelegramOAuthEnabled = false var TurnstileCheckEnabled = false var RegisterEnabled = true diff --git a/controller/misc.go b/controller/misc.go index f99baa8..1aabb02 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -20,6 +20,8 @@ func GetStatus(c *gin.Context) { "email_verification": common.EmailVerificationEnabled, "github_oauth": common.GitHubOAuthEnabled, "github_client_id": common.GitHubClientId, + "telegram_oauth": common.TelegramOAuthEnabled, + "telegram_bot_name": common.TelegramBotName, "system_name": common.SystemName, "logo": common.Logo, "footer_html": common.Footer, diff --git a/makefile b/makefile index 1df2b5c..603f5aa 100644 --- a/makefile +++ b/makefile @@ -5,6 +5,10 @@ BACKEND_DIR = . all: start-frontend start-backend +build-frontend: + @echo "Building frontend..." + @cd $(FRONTEND_DIR) && npm install && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build npm run build + # 启动前端开发服务器 start-frontend: @echo "Starting frontend dev server..." diff --git a/model/option.go b/model/option.go index dcb4c34..2feb711 100644 --- a/model/option.go +++ b/model/option.go @@ -30,6 +30,7 @@ func InitOptionMap() { common.OptionMap["PasswordRegisterEnabled"] = strconv.FormatBool(common.PasswordRegisterEnabled) common.OptionMap["EmailVerificationEnabled"] = strconv.FormatBool(common.EmailVerificationEnabled) common.OptionMap["GitHubOAuthEnabled"] = strconv.FormatBool(common.GitHubOAuthEnabled) + common.OptionMap["TelegramOAuthEnabled"] = strconv.FormatBool(common.TelegramOAuthEnabled) common.OptionMap["WeChatAuthEnabled"] = strconv.FormatBool(common.WeChatAuthEnabled) common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled) common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled) @@ -62,6 +63,8 @@ func InitOptionMap() { common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientSecret"] = "" + common.OptionMap["TelegramBotToken"] = "" + common.OptionMap["TelegramBotName"] = "" common.OptionMap["WeChatServerAddress"] = "" common.OptionMap["WeChatServerToken"] = "" common.OptionMap["WeChatAccountQRCodeImageURL"] = "" @@ -151,6 +154,8 @@ func updateOptionMap(key string, value string) (err error) { common.GitHubOAuthEnabled = boolValue case "WeChatAuthEnabled": common.WeChatAuthEnabled = boolValue + case "TelegramOAuthEnabled": + common.TelegramOAuthEnabled = boolValue case "TurnstileCheckEnabled": common.TurnstileCheckEnabled = boolValue case "RegisterEnabled": diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index a8956b7..f5d87be 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -413,32 +413,6 @@ const SystemSetting = () => { /> -
配置 Telegram 登录
- - - - - - - 保存 Telegram 登录设置 -
配置邮箱域名白名单 用以防止恶意用户利用临时邮箱批量注册 @@ -620,6 +594,33 @@ const SystemSetting = () => { 保存 WeChat Server 设置 +
配置 Telegram 登录
+ + + + + + + 保存 Telegram 登录设置 + +
配置 Turnstile From 194ff1ac5749e53bdf0ff376b13a5d9c3d1d45fe Mon Sep 17 00:00:00 2001 From: Ehco1996 Date: Fri, 1 Mar 2024 22:33:30 +0800 Subject: [PATCH 03/23] Update makefile and user model, add Telegram integration --- makefile | 8 +- model/user.go | 4 +- web/src/components/LoginForm.js | 67 ++++--- web/src/components/PersonalSetting.js | 21 +++ web/src/components/SystemSetting.js | 20 +-- web/src/pages/User/EditUser.js | 248 +++++++++++++------------- 6 files changed, 204 insertions(+), 164 deletions(-) diff --git a/makefile b/makefile index 603f5aa..1e8ab1b 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,7 @@ FRONTEND_DIR = ./web BACKEND_DIR = . -.PHONY: all start-frontend start-backend +.PHONY: all build-frontend start-backend all: start-frontend start-backend @@ -9,12 +9,6 @@ build-frontend: @echo "Building frontend..." @cd $(FRONTEND_DIR) && npm install && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) npm run build npm run build -# 启动前端开发服务器 -start-frontend: - @echo "Starting frontend dev server..." - @cd $(FRONTEND_DIR) && npm start & - -# 启动后端开发服务器 start-backend: @echo "Starting backend dev server..." @cd $(BACKEND_DIR) && go run main.go & diff --git a/model/user.go b/model/user.go index 3e727a0..7b70bfe 100644 --- a/model/user.go +++ b/model/user.go @@ -3,10 +3,11 @@ package model import ( "errors" "fmt" - "gorm.io/gorm" "one-api/common" "strings" "time" + + "gorm.io/gorm" ) // User if you add sensitive fields, don't forget to clean them in setupLogin function. @@ -21,6 +22,7 @@ type User struct { Email string `json:"email" gorm:"index" validate:"max=50"` GitHubId string `json:"github_id" gorm:"column:github_id;index"` WeChatId string `json:"wechat_id" gorm:"column:wechat_id;index"` + TelegramId string `json:"telegram_id" gorm:"column:telegram_id;index"` VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! AccessToken string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management Quota int `json:"quota" gorm:"type:int;default:0"` diff --git a/web/src/components/LoginForm.js b/web/src/components/LoginForm.js index 03aec65..ec49ece 100644 --- a/web/src/components/LoginForm.js +++ b/web/src/components/LoginForm.js @@ -1,14 +1,14 @@ -import React, {useContext, useEffect, useState} from 'react'; -import {Link, useNavigate, useSearchParams} from 'react-router-dom'; -import {UserContext} from '../context/User'; -import {API, getLogo, isMobile, showError, showInfo, showSuccess, showWarning} from '../helpers'; -import {onGitHubOAuthClicked} from './utils'; +import React, { useContext, useEffect, useState } from 'react'; +import { Link, useNavigate, useSearchParams } from 'react-router-dom'; +import { UserContext } from '../context/User'; +import { API, getLogo, isMobile, showError, showInfo, showSuccess, showWarning } from '../helpers'; +import { onGitHubOAuthClicked } from './utils'; import Turnstile from "react-turnstile"; -import {Layout, Card, Image, Form, Button, Divider, Modal} from "@douyinfe/semi-ui"; +import { Layout, Card, Image, Form, Button, Divider, Modal } from "@douyinfe/semi-ui"; import Title from "@douyinfe/semi-ui/lib/es/typography/title"; import Text from "@douyinfe/semi-ui/lib/es/typography/text"; -import {IconGithubLogo} from '@douyinfe/semi-icons'; +import { IconGithubLogo } from '@douyinfe/semi-icons'; const LoginForm = () => { const [inputs, setInputs] = useState({ @@ -18,7 +18,7 @@ const LoginForm = () => { }); const [searchParams, setSearchParams] = useSearchParams(); const [submitted, setSubmitted] = useState(false); - const {username, password} = inputs; + const { username, password } = inputs; const [userState, userDispatch] = useContext(UserContext); const [turnstileEnabled, setTurnstileEnabled] = useState(false); const [turnstileSiteKey, setTurnstileSiteKey] = useState(''); @@ -56,9 +56,9 @@ const LoginForm = () => { const res = await API.get( `/api/oauth/wechat?code=${inputs.wechat_verification_code}` ); - const {success, message, data} = res.data; + const { success, message, data } = res.data; if (success) { - userDispatch({type: 'login', payload: data}); + userDispatch({ type: 'login', payload: data }); localStorage.setItem('user', JSON.stringify(data)); navigate('/'); showSuccess('登录成功!'); @@ -69,7 +69,7 @@ const LoginForm = () => { }; function handleChange(name, value) { - setInputs((inputs) => ({...inputs, [name]: value})); + setInputs((inputs) => ({ ...inputs, [name]: value })); } async function handleSubmit(e) { @@ -83,13 +83,13 @@ const LoginForm = () => { username, password }); - const {success, message, data} = res.data; + const { success, message, data } = res.data; if (success) { - userDispatch({type: 'login', payload: data}); + userDispatch({ type: 'login', payload: data }); localStorage.setItem('user', JSON.stringify(data)); showSuccess('登录成功!'); if (username === 'root' && password === '123456') { - Modal.error({title: '您正在使用默认密码!', content: '请立刻修改默认密码!', centered: true}); + Modal.error({ title: '您正在使用默认密码!', content: '请立刻修改默认密码!', centered: true }); } navigate('/token'); } else { @@ -100,16 +100,23 @@ const LoginForm = () => { } } + // 添加Telegram登录处理函数 + const onTelegramLoginClicked = async () => { + // 这里调用后端API进行Telegram登录 + // 例如: const res = await API.get(`/api/oauth/telegram`); + // 根据响应处理登录逻辑 + }; + return (
-
-
+
+
- + <Title heading={2} style={{ textAlign: 'center' }}> 用户登录
@@ -129,12 +136,12 @@ const LoginForm = () => { onChange={(value) => handleChange('password', value)} /> -
-
+
没有账号请先 注册账号 @@ -142,16 +149,16 @@ const LoginForm = () => { 忘记密码 点击重置
- {status.github_oauth || status.wechat_login ? ( + {status.github_oauth || status.wechat_login || status.telegram_oauth ? ( <> 第三方登录 -
+
{status.github_oauth ? ( + ) : ( + <> + )}
) : ( @@ -208,7 +227,7 @@ const LoginForm = () => { {/**/} {turnstileEnabled ? ( -
+
{ diff --git a/web/src/components/PersonalSetting.js b/web/src/components/PersonalSetting.js index 78d3240..3c67a3f 100644 --- a/web/src/components/PersonalSetting.js +++ b/web/src/components/PersonalSetting.js @@ -443,6 +443,27 @@ const PersonalSetting = () => {
+
+ Telegram +
+
+ +
+
+ +
+
+
+
diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index f5d87be..436cc67 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -34,7 +34,7 @@ const SystemSetting = () => { EmailDomainRestrictionEnabled: '', EmailDomainWhitelist: '', // telegram login - TelegramLoginEnabled: '', + TelegramOAuthEnabled: '', TelegramBotToken: '', TelegramBotName: '', }); @@ -81,7 +81,7 @@ const SystemSetting = () => { case 'EmailVerificationEnabled': case 'GitHubOAuthEnabled': case 'WeChatAuthEnabled': - case 'TelegramLoginEnabled': + case 'TelegramOAuthEnabled': case 'TurnstileCheckEnabled': case 'EmailDomainRestrictionEnabled': case 'RegisterEnabled': @@ -240,7 +240,7 @@ const SystemSetting = () => { }; const submitTelegramSettings = async () => { - await updateOption('TelegramLoginEnabled', inputs.TelegramLoginEnabled); + await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); await updateOption('TelegramBotToken', inputs.TelegramBotToken); await updateOption('TelegramBotName', inputs.TelegramBotName); }; @@ -397,6 +397,12 @@ const SystemSetting = () => { name='WeChatAuthEnabled' onChange={handleInputChange} /> + {
配置 Telegram 登录
- diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js index 705f7a2..6d79127 100644 --- a/web/src/pages/User/EditUser.js +++ b/web/src/pages/User/EditUser.js @@ -1,9 +1,9 @@ import React, { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import {API, isMobile, showError, showSuccess} from '../../helpers'; +import { API, isMobile, showError, showSuccess } from '../../helpers'; import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render'; import Title from "@douyinfe/semi-ui/lib/es/typography/title"; -import {SideSheet, Space, Button, Spin, Input, Typography, Select, Divider} from "@douyinfe/semi-ui"; +import { SideSheet, Space, Button, Spin, Input, Typography, Select, Divider } from "@douyinfe/semi-ui"; const EditUser = (props) => { const userId = props.editingUser.id; @@ -19,8 +19,8 @@ const EditUser = (props) => { group: 'default' }); const [groupOptions, setGroupOptions] = useState([]); - const { username, display_name, password, github_id, wechat_id, email, quota, group } = - inputs; + const { username, display_name, password, github_id, wechat_id, telegram_id, email, quota, group } = + inputs; const handleInputChange = (name, value) => { setInputs((inputs) => ({ ...inputs, [name]: value })); }; @@ -88,126 +88,132 @@ const EditUser = (props) => { }; return ( - <> - {'编辑用户'}} - headerStyle={{borderBottom: '1px solid var(--semi-color-border)'}} - bodyStyle={{borderBottom: '1px solid var(--semi-color-border)'}} - visible={props.visible} - footer={ -
- - - - + <> + {'编辑用户'}} + headerStyle={{ borderBottom: '1px solid var(--semi-color-border)' }} + bodyStyle={{ borderBottom: '1px solid var(--semi-color-border)' }} + visible={props.visible} + footer={ +
+ + + + +
+ } + closeIcon={null} + onCancel={() => handleCancel()} + width={isMobile() ? '100%' : 600} + > + +
+ 用户名 +
+ handleInputChange('username', value)} + value={username} + autoComplete='new-password' + /> +
+ 密码 +
+ handleInputChange('password', value)} + value={password} + autoComplete='new-password' + /> +
+ 显示名称 +
+ handleInputChange('display_name', value)} + value={display_name} + autoComplete='new-password' + /> + { + userId && <> +
+ 分组
- } - closeIcon={null} - onCancel={() => handleCancel()} - width={isMobile() ? '100%' : 600} - > - -
- 用户名 -
- handleInputChange('username', value)} - value={username} + handleInputChange('password', value)} - value={password} + optionList={groupOptions} + /> +
+ {`剩余额度${renderQuotaWithPrompt(quota)}`} +
+ handleInputChange('quota', value)} + value={quota} + type={'number'} autoComplete='new-password' - /> -
- 显示名称 -
- handleInputChange('display_name', value)} - value={display_name} - autoComplete='new-password' - /> - { - userId && <> -
- 分组 -
- handleInputChange('quota', value)} - value={quota} - type={'number'} - autoComplete='new-password' - /> - - } - 以下信息不可修改 -
- 已绑定的 GitHub 账户 -
- -
- 已绑定的微信账户 -
- -
- 已绑定的邮箱账户 -
- -
- -
- + /> + + } + 以下信息不可修改 +
+ 已绑定的 GitHub 账户 +
+ +
+ 已绑定的微信账户 +
+ + +
+ 已绑定的邮箱账户 +
+ + + + ); }; From 84144306a899055071f32d5ad73f5da0c0bd50f5 Mon Sep 17 00:00:00 2001 From: sljeff Date: Sat, 2 Mar 2024 17:15:52 +0800 Subject: [PATCH 04/23] feat: telegram login and bind --- controller/telegram.go | 116 ++++++++++++++++++++++++++ docker-compose.yml | 2 +- model/user.go | 15 ++++ router/api-router.go | 2 + web/package.json | 5 +- web/src/components/LoginForm.js | 31 ++++--- web/src/components/PersonalSetting.js | 13 ++- web/src/components/SystemSetting.js | 6 +- web/src/pages/Home/index.js | 6 ++ 9 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 controller/telegram.go diff --git a/controller/telegram.go b/controller/telegram.go new file mode 100644 index 0000000..b5bc0c0 --- /dev/null +++ b/controller/telegram.go @@ -0,0 +1,116 @@ +package controller + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "io" + "one-api/common" + "one-api/model" + "sort" + + "github.com/gin-contrib/sessions" + "github.com/gin-gonic/gin" +) + +func TelegramBind(c *gin.Context) { + if !common.TelegramOAuthEnabled { + c.JSON(200, gin.H{ + "message": "管理员未开启通过 Telegram 登录以及注册", + "success": false, + }) + return + } + params := c.Request.URL.Query() + if !checkTelegramAuthorization(params, common.TelegramBotToken) { + c.JSON(200, gin.H{ + "message": "无效的请求", + "success": false, + }) + return + } + telegramId := params["id"][0] + if model.IsTelegramIdAlreadyTaken(telegramId) { + c.JSON(200, gin.H{ + "message": "该 Telegram 账户已被绑定", + "success": false, + }) + return + } + + session := sessions.Default(c) + id := session.Get("id") + user := model.User{Id: id.(int)} + if err := user.FillUserById(); err != nil { + c.JSON(200, gin.H{ + "message": err.Error(), + "success": false, + }) + return + } + user.TelegramId = telegramId + if err := user.Update(false); err != nil { + c.JSON(200, gin.H{ + "message": err.Error(), + "success": false, + }) + return + } + + c.Redirect(302, "/setting") +} + +func TelegramLogin(c *gin.Context) { + if !common.TelegramOAuthEnabled { + c.JSON(200, gin.H{ + "message": "管理员未开启通过 Telegram 登录以及注册", + "success": false, + }) + return + } + params := c.Request.URL.Query() + if !checkTelegramAuthorization(params, common.TelegramBotToken) { + c.JSON(200, gin.H{ + "message": "无效的请求", + "success": false, + }) + return + } + + telegramId := params["id"][0] + user := model.User{TelegramId: telegramId} + if err := user.FillUserByTelegramId(); err != nil { + c.JSON(200, gin.H{ + "message": err.Error(), + "success": false, + }) + return + } + setupLogin(&user, c) +} + +func checkTelegramAuthorization(params map[string][]string, token string) bool { + strs := []string{} + var hash = "" + for k, v := range params { + if k == "hash" { + hash = v[0] + continue + } + strs = append(strs, k+"="+v[0]) + } + sort.Strings(strs) + var imploded = "" + for _, s := range strs { + if imploded != "" { + imploded += "\n" + } + imploded += s + } + sha256hash := sha256.New() + io.WriteString(sha256hash, token) + hmachash := hmac.New(sha256.New, sha256hash.Sum(nil)) + io.WriteString(hmachash, imploded) + ss := hex.EncodeToString(hmachash.Sum(nil)) + return hash == ss +} diff --git a/docker-compose.yml b/docker-compose.yml index 403f372..1a1ff21 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.4' services: new-api: - image: calciumion/new-api:latest + build: . container_name: new-api restart: always command: --log-dir /app/logs diff --git a/model/user.go b/model/user.go index 7b70bfe..00294b2 100644 --- a/model/user.go +++ b/model/user.go @@ -288,6 +288,17 @@ func (user *User) FillUserByUsername() error { return nil } +func (user *User) FillUserByTelegramId() error { + if user.TelegramId == "" { + return errors.New("Telegram id 为空!") + } + err := DB.Where(User{TelegramId: user.TelegramId}).First(user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return errors.New("该 Telegram 账户未绑定") + } + return nil +} + func IsEmailAlreadyTaken(email string) bool { return DB.Where("email = ?", email).Find(&User{}).RowsAffected == 1 } @@ -304,6 +315,10 @@ func IsUsernameAlreadyTaken(username string) bool { return DB.Where("username = ?", username).Find(&User{}).RowsAffected == 1 } +func IsTelegramIdAlreadyTaken(telegramId string) bool { + return DB.Where("telegram_id = ?", telegramId).Find(&User{}).RowsAffected == 1 +} + func ResetUserPasswordByEmail(email string, password string) error { if email == "" || password == "" { return errors.New("邮箱地址或密码为空!") diff --git a/router/api-router.go b/router/api-router.go index 04d3490..f72b9b3 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -26,6 +26,8 @@ func SetApiRouter(router *gin.Engine) { apiRouter.GET("/oauth/wechat", middleware.CriticalRateLimit(), controller.WeChatAuth) apiRouter.GET("/oauth/wechat/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.WeChatBind) apiRouter.GET("/oauth/email/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.EmailBind) + apiRouter.GET("/oauth/telegram/login", middleware.CriticalRateLimit(), controller.TelegramLogin) + apiRouter.GET("/oauth/telegram/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.TelegramBind) userRoute := apiRouter.Group("/user") { diff --git a/web/package.json b/web/package.json index 009092a..d6d7ad5 100644 --- a/web/package.json +++ b/web/package.json @@ -3,10 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { - "@douyinfe/semi-ui": "^2.46.1", "@douyinfe/semi-icons": "^2.46.1", - "@visactor/vchart": "~1.8.8", + "@douyinfe/semi-ui": "^2.46.1", "@visactor/react-vchart": "~1.8.8", + "@visactor/vchart": "~1.8.8", "@visactor/vchart-semi-theme": "~1.8.8", "axios": "^0.27.2", "history": "^5.3.0", @@ -17,6 +17,7 @@ "react-fireworks": "^1.0.4", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", + "react-telegram-login": "^1.1.2", "react-toastify": "^9.0.8", "react-turnstile": "^1.0.5", "semantic-ui-css": "^2.5.0", diff --git a/web/src/components/LoginForm.js b/web/src/components/LoginForm.js index ec49ece..eb94784 100644 --- a/web/src/components/LoginForm.js +++ b/web/src/components/LoginForm.js @@ -7,6 +7,7 @@ import Turnstile from "react-turnstile"; import { Layout, Card, Image, Form, Button, Divider, Modal } from "@douyinfe/semi-ui"; import Title from "@douyinfe/semi-ui/lib/es/typography/title"; import Text from "@douyinfe/semi-ui/lib/es/typography/text"; +import TelegramLoginButton from 'react-telegram-login'; import { IconGithubLogo } from '@douyinfe/semi-icons'; @@ -101,10 +102,24 @@ const LoginForm = () => { } // 添加Telegram登录处理函数 - const onTelegramLoginClicked = async () => { - // 这里调用后端API进行Telegram登录 - // 例如: const res = await API.get(`/api/oauth/telegram`); - // 根据响应处理登录逻辑 + const onTelegramLoginClicked = async (response) => { + const fields = ["id", "first_name", "last_name", "username", "photo_url", "auth_date", "hash", "lang"]; + const params = {}; + fields.forEach((field) => { + if (response[field]) { + params[field] = response[field]; + } + }); + const res = await API.get(`/api/oauth/telegram/login`, { params }); + const { success, message, data } = res.data; + if (success) { + userDispatch({ type: 'login', payload: data }); + localStorage.setItem('user', JSON.stringify(data)); + showSuccess('登录成功!'); + navigate('/'); + } else { + showError(message); + } }; return ( @@ -176,13 +191,7 @@ const LoginForm = () => { {/*)}*/} {status.telegram_oauth ? ( - + ) : ( <> )} diff --git a/web/src/components/PersonalSetting.js b/web/src/components/PersonalSetting.js index 3c67a3f..1216b99 100644 --- a/web/src/components/PersonalSetting.js +++ b/web/src/components/PersonalSetting.js @@ -21,6 +21,7 @@ import {getQuotaPerUnit, renderQuota, renderQuotaWithPrompt, stringToColor} from import EditToken from "../pages/Token/EditToken"; import EditUser from "../pages/User/EditUser"; import passwordResetConfirm from "./PasswordResetConfirm"; +import TelegramLoginButton from 'react-telegram-login'; const PersonalSetting = () => { const [userState, userDispatch] = useContext(UserContext); @@ -453,13 +454,11 @@ const PersonalSetting = () => { >
- + {status.telegram_oauth ? + userState.user.telegram_id !== '' ? + : + : + }
diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index 436cc67..4d2bbc1 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -133,7 +133,9 @@ const SystemSetting = () => { name === 'TurnstileSiteKey' || name === 'TurnstileSecretKey' || name === 'EmailDomainWhitelist' || - name === 'TopupGroupRatio' + name === 'TopupGroupRatio' || + name === 'TelegramBotToken' || + name === 'TelegramBotName' ) { setInputs((inputs) => ({ ...inputs, [name]: value })); } else { @@ -605,12 +607,14 @@ const SystemSetting = () => { diff --git a/web/src/pages/Home/index.js b/web/src/pages/Home/index.js index 6009486..5ee9f0a 100644 --- a/web/src/pages/Home/index.js +++ b/web/src/pages/Home/index.js @@ -110,6 +110,12 @@ const Home = () => { ? '已启用' : '未启用'}

+

+ Telegram 身份验证: + {statusState?.status?.telegram_oauth === true + ? '已启用' + : '未启用'} +

From cb92d6fd5f90e6ddf11ab823dbc8160c2df3df6f Mon Sep 17 00:00:00 2001 From: Ehco1996 Date: Sat, 2 Mar 2024 19:58:36 +0800 Subject: [PATCH 05/23] revert compose --- docker-compose.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1a1ff21..fff2716 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,8 @@ version: '3.4' services: new-api: - build: . + image: calciumion/new-api:latest + # build: . container_name: new-api restart: always command: --log-dir /app/logs @@ -12,7 +13,7 @@ services: - ./data:/data - ./logs:/app/logs environment: - # - SQL_DSN=root:123456@tcp(host.docker.internal:3306)/new-api # 修改此行,或注释掉以使用 SQLite 作为数据库 + - SQL_DSN=root:123456@tcp(host.docker.internal:3306)/new-api # 修改此行,或注释掉以使用 SQLite 作为数据库 - REDIS_CONN_STRING=redis://redis - SESSION_SECRET=random_string # 修改为随机字符串 - TZ=Asia/Shanghai From 7297c6269f1a7eff1774b69e2c8fea7b26ae5c21 Mon Sep 17 00:00:00 2001 From: papersnake Date: Sat, 2 Mar 2024 12:26:39 +0000 Subject: [PATCH 06/23] fix: add missing upstreamModelName --- middleware/distributor.go | 2 ++ relay/common/relay_info.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/middleware/distributor.go b/middleware/distributor.go index 1ca43dd..4930961 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -116,6 +116,8 @@ func Distribute() func(c *gin.Context) { abortWithMessage(c, http.StatusServiceUnavailable, fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道(数据库一致性已被破坏)", userGroup, modelRequest.Model)) return } + + c.Set("model_name", modelRequest.Model) } c.Set("channel", channel.Type) c.Set("channel_id", channel.Id) diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 8051cec..270a2d8 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -34,6 +34,7 @@ func GenRelayInfo(c *gin.Context) *RelayInfo { userId := c.GetInt("id") group := c.GetString("group") tokenUnlimited := c.GetBool("token_unlimited_quota") + upstreamModelName := c.GetString("model_name") startTime := time.Now() apiType := constant.ChannelType2APIType(channelType) @@ -52,6 +53,7 @@ func GenRelayInfo(c *gin.Context) *RelayInfo { ApiType: apiType, ApiVersion: c.GetString("api_version"), ApiKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "), + UpstreamModelName: upstreamModelName, } if info.BaseUrl == "" { info.BaseUrl = common.ChannelBaseURLs[channelType] From d6d8ad2b7e7230559c0b5334669494f585f43944 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 20:56:29 +0800 Subject: [PATCH 07/23] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF=E9=A2=9C=E8=89=B2=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/helpers/render.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js index 9802db5..62fb0dc 100644 --- a/web/src/helpers/render.js +++ b/web/src/helpers/render.js @@ -132,6 +132,8 @@ export const modelColorMap = { 'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色 'gpt-4-0613': 'rgb(100,149,237)', // 矢车菊蓝 'gpt-4-1106-preview': 'rgb(30,144,255)', // 道奇蓝 + 'gpt-4-0125-preview': 'rgb(2,177,236)', // 深天蓝 + 'gpt-4-turbo-preview': 'rgb(2,177,255)', // 深天蓝 'gpt-4-32k': 'rgb(104,111,238)', // 中紫色 'gpt-4-32k-0314': 'rgb(90,105,205)', // 暗灰蓝色 'gpt-4-32k-0613': 'rgb(61,71,139)', // 暗蓝灰色 From cbe0a1fa764209a00b08188d92e1ff2f679f0fae Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 21:05:02 +0800 Subject: [PATCH 08/23] fix: add missing UpstreamModelName --- middleware/distributor.go | 2 -- relay/common/relay_info.go | 2 -- relay/relay-text.go | 1 + 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/middleware/distributor.go b/middleware/distributor.go index 4930961..1ca43dd 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -116,8 +116,6 @@ func Distribute() func(c *gin.Context) { abortWithMessage(c, http.StatusServiceUnavailable, fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道(数据库一致性已被破坏)", userGroup, modelRequest.Model)) return } - - c.Set("model_name", modelRequest.Model) } c.Set("channel", channel.Type) c.Set("channel_id", channel.Id) diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 270a2d8..8051cec 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -34,7 +34,6 @@ func GenRelayInfo(c *gin.Context) *RelayInfo { userId := c.GetInt("id") group := c.GetString("group") tokenUnlimited := c.GetBool("token_unlimited_quota") - upstreamModelName := c.GetString("model_name") startTime := time.Now() apiType := constant.ChannelType2APIType(channelType) @@ -53,7 +52,6 @@ func GenRelayInfo(c *gin.Context) *RelayInfo { ApiType: apiType, ApiVersion: c.GetString("api_version"), ApiKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "), - UpstreamModelName: upstreamModelName, } if info.BaseUrl == "" { info.BaseUrl = common.ChannelBaseURLs[channelType] diff --git a/relay/relay-text.go b/relay/relay-text.go index 63b1ff6..95c29ec 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -59,6 +59,7 @@ func getAndValidateTextRequest(c *gin.Context, relayInfo *relaycommon.RelayInfo) } } relayInfo.IsStream = textRequest.Stream + relayInfo.UpstreamModelName = textRequest.Model return textRequest, nil } From 72194bda98bcb3ea3ca8fcd730a4d46042a73ef4 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 22:07:32 +0800 Subject: [PATCH 09/23] =?UTF-8?q?feat:=20=E5=8F=AF=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E5=8F=8A=E6=9C=80=E4=BD=8E=E8=B4=AD=E4=B9=B0=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/constants.go | 13 +++++++---- controller/misc.go | 1 + controller/topup.go | 16 ++++++------- model/option.go | 6 +++++ service/epay.go | 10 +++++++++ web/src/components/SystemSetting.js | 35 ++++++++++++++++++++++------- web/src/pages/TopUp/index.js | 17 ++++++++++++-- 7 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 service/epay.go diff --git a/common/constants.go b/common/constants.go index e11d7fe..8352d14 100644 --- a/common/constants.go +++ b/common/constants.go @@ -9,14 +9,19 @@ import ( "github.com/google/uuid" ) +// Pay Settings + +var PayAddress = "" +var CustomCallbackAddress = "" +var EpayId = "" +var EpayKey = "" +var Price = 7.3 +var MinTopUp = 1 + var StartTime = time.Now().Unix() // unit: second var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change var SystemName = "New API" var ServerAddress = "http://localhost:3000" -var PayAddress = "" -var EpayId = "" -var EpayKey = "" -var Price = 7.3 var Footer = "" var Logo = "" var TopUpLink = "" diff --git a/controller/misc.go b/controller/misc.go index 3ed3c5e..b992a0c 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -27,6 +27,7 @@ func GetStatus(c *gin.Context) { "wechat_login": common.WeChatAuthEnabled, "server_address": common.ServerAddress, "price": common.Price, + "min_topup": common.MinTopUp, "turnstile_check": common.TurnstileCheckEnabled, "turnstile_site_key": common.TurnstileSiteKey, "top_up_link": common.TopUpLink, diff --git a/controller/topup.go b/controller/topup.go index 961ffa2..3203eb8 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -9,6 +9,7 @@ import ( "net/url" "one-api/common" "one-api/model" + "one-api/service" "strconv" "time" ) @@ -55,14 +56,14 @@ func RequestEpay(c *gin.Context) { c.JSON(200, gin.H{"message": err.Error(), "data": 10}) return } - if req.Amount < 1 { - c.JSON(200, gin.H{"message": "充值金额不能小于1", "data": 10}) + if req.Amount < common.MinTopUp { + c.JSON(200, gin.H{"message": fmt.Sprintf("充值数量不能小于 %d", common.MinTopUp), "data": 10}) return } id := c.GetInt("id") user, _ := model.GetUserById(id, false) - amount := GetAmount(float64(req.Amount), *user) + payMoney := GetAmount(float64(req.Amount), *user) var payType epay.PurchaseType if req.PaymentMethod == "zfb" { @@ -72,11 +73,10 @@ func RequestEpay(c *gin.Context) { req.PaymentMethod = "wxpay" payType = epay.WechatPay } - + callBackAddress := service.GetCallbackAddress() returnUrl, _ := url.Parse(common.ServerAddress + "/log") - notifyUrl, _ := url.Parse(common.ServerAddress + "/api/user/epay/notify") + notifyUrl, _ := url.Parse(callBackAddress + "/api/user/epay/notify") tradeNo := strconv.FormatInt(time.Now().Unix(), 10) - payMoney := amount client := GetEpayClient() if client == nil { c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"}) @@ -169,8 +169,8 @@ func RequestAmount(c *gin.Context) { c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) return } - if req.Amount < 1 { - c.JSON(200, gin.H{"message": "error", "data": "充值金额不能小于1"}) + if req.Amount < common.MinTopUp { + c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", common.MinTopUp)}) return } id := c.GetInt("id") diff --git a/model/option.go b/model/option.go index 03c0230..c2aeecd 100644 --- a/model/option.go +++ b/model/option.go @@ -56,9 +56,11 @@ func InitOptionMap() { common.OptionMap["Logo"] = common.Logo common.OptionMap["ServerAddress"] = "" common.OptionMap["PayAddress"] = "" + common.OptionMap["CustomCallbackAddress"] = "" common.OptionMap["EpayId"] = "" common.OptionMap["EpayKey"] = "" common.OptionMap["Price"] = strconv.FormatFloat(common.Price, 'f', -1, 64) + common.OptionMap["MinTopUp"] = strconv.Itoa(common.MinTopUp) common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientSecret"] = "" @@ -194,12 +196,16 @@ func updateOptionMap(key string, value string) (err error) { common.ServerAddress = value case "PayAddress": common.PayAddress = value + case "CustomCallbackAddress": + common.CustomCallbackAddress = value case "EpayId": common.EpayId = value case "EpayKey": common.EpayKey = value case "Price": common.Price, _ = strconv.ParseFloat(value, 64) + case "MinTopUp": + common.MinTopUp, _ = strconv.Atoi(value) case "TopupGroupRatio": err = common.UpdateTopupGroupRatioByJSONString(value) case "GitHubClientId": diff --git a/service/epay.go b/service/epay.go new file mode 100644 index 0000000..7ce4aad --- /dev/null +++ b/service/epay.go @@ -0,0 +1,10 @@ +package service + +import "one-api/common" + +func GetCallbackAddress() string { + if common.CustomCallbackAddress == "" { + return common.ServerAddress + } + return common.CustomCallbackAddress +} diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index 197050e..e622574 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -20,8 +20,10 @@ const SystemSetting = () => { EpayId: '', EpayKey: '', Price: 7.3, + MinTopUp: 1, TopupGroupRatio: '', PayAddress: '', + CustomCallbackAddress: '', Footer: '', WeChatAuthEnabled: '', WeChatServerAddress: '', @@ -280,7 +282,7 @@ const SystemSetting = () => { 更新服务器地址 -
支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)
+
支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)
{ name='EpayKey' onChange={handleInputChange} /> - + + + + diff --git a/web/src/pages/TopUp/index.js b/web/src/pages/TopUp/index.js index 822ae24..92ae57e 100644 --- a/web/src/pages/TopUp/index.js +++ b/web/src/pages/TopUp/index.js @@ -10,6 +10,7 @@ const TopUp = () => { const [topUpCount, setTopUpCount] = useState(10); const [minTopupCount, setMinTopUpCount] = useState(1); const [amount, setAmount] = useState(0.0); + const [minTopUp, setMinTopUp] = useState(1); const [topUpLink, setTopUpLink] = useState(''); const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false); const [userQuota, setUserQuota] = useState(0); @@ -61,6 +62,10 @@ const TopUp = () => { if (amount === 0) { await getAmount(); } + if (topUpCount < minTopUp) { + showInfo('充值数量不能小于' + minTopUp); + return; + } setPayWay(payment) setOpen(true); } @@ -69,6 +74,10 @@ const TopUp = () => { if (amount === 0) { await getAmount(); } + if (topUpCount < minTopUp) { + showInfo('充值数量不能小于' + minTopUp); + return; + } setOpen(false); try { const res = await API.post('/api/user/pay', { @@ -132,6 +141,9 @@ const TopUp = () => { if (status.top_up_link) { setTopUpLink(status.top_up_link); } + if (status.min_topup) { + setMinTopUp(status.min_topup); + } if (status.enable_online_topup) { setEnableOnlineTopUp(status.enable_online_topup); } @@ -239,12 +251,13 @@ const TopUp = () => { disabled={!enableOnlineTopUp} field={'redemptionCount'} label={'实付金额:' + renderAmount()} - placeholder='充值数量' + placeholder={'充值数量,最低' + minTopUp + '$'} name='redemptionCount' type={'number'} value={topUpCount} suffix={'$'} - min={1} + min={minTopUp} + defaultValue={minTopUp} max={100000} onChange={async (value) => { if (value < 1) { From a16e949318689ade024f92e3252f7ec08ce4421e Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 22:08:59 +0800 Subject: [PATCH 10/23] feat: update SparkDesk model ratio --- common/model-ratio.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/model-ratio.go b/common/model-ratio.go index 30b87ee..846ec8a 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -80,7 +80,9 @@ var ModelRatio = map[string]float64{ "qwen-turbo": 0.8572, // ¥0.012 / 1k tokens "qwen-plus": 10, // ¥0.14 / 1k tokens "text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens - "SparkDesk": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v1.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v2.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens "embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens "embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens From 4c43012e6cfc21907cef44cb30909c6cbfbe359f Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 22:46:26 +0800 Subject: [PATCH 11/23] fix: Improve handling of small weights in channel selection logic --- model/cache.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/model/cache.go b/model/cache.go index b1199e2..dc85639 100644 --- a/model/cache.go +++ b/model/cache.go @@ -291,10 +291,13 @@ func CacheGetRandomSatisfiedChannel(group string, model string) (*Channel, error } } } + // 平滑系数 + smoothingFactor := 10 + // Calculate the total weight of all channels up to endIdx totalWeight := 0 for _, channel := range channels[:endIdx] { - totalWeight += channel.GetWeight() + totalWeight += channel.GetWeight() + smoothingFactor } if totalWeight == 0 { @@ -307,8 +310,8 @@ func CacheGetRandomSatisfiedChannel(group string, model string) (*Channel, error // Find a channel based on its weight for _, channel := range channels[:endIdx] { - randomWeight -= channel.GetWeight() - if randomWeight <= 0 { + randomWeight -= channel.GetWeight() + smoothingFactor + if randomWeight < 0 { return channel, nil } } From 06b746a740563169d53dbf9e35b1497016aff48f Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 2 Mar 2024 23:02:49 +0800 Subject: [PATCH 12/23] fix: remove useless code --- model/cache.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/model/cache.go b/model/cache.go index dc85639..8294e73 100644 --- a/model/cache.go +++ b/model/cache.go @@ -291,19 +291,19 @@ func CacheGetRandomSatisfiedChannel(group string, model string) (*Channel, error } } } + // 平滑系数 smoothingFactor := 10 - // Calculate the total weight of all channels up to endIdx totalWeight := 0 for _, channel := range channels[:endIdx] { totalWeight += channel.GetWeight() + smoothingFactor } - if totalWeight == 0 { - // If all weights are 0, select a channel randomly - return channels[rand.Intn(endIdx)], nil - } + //if totalWeight == 0 { + // // If all weights are 0, select a channel randomly + // return channels[rand.Intn(endIdx)], nil + //} // Generate a random value in the range [0, totalWeight) randomWeight := rand.Intn(totalWeight) From 54088bc6646f4e54b473ba852b5df8e731bc09ae Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sun, 3 Mar 2024 15:53:48 +0800 Subject: [PATCH 13/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=92=8C=E7=BC=96=E8=BE=91=E6=B8=A0=E9=81=93=E6=97=A0?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E6=8B=A9=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/model-ratio.go | 1 + controller/model.go | 7 +++++++ router/api-router.go | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/model-ratio.go b/common/model-ratio.go index 846ec8a..648f9fc 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -83,6 +83,7 @@ var ModelRatio = map[string]float64{ "SparkDesk-v1.1": 1.2858, // ¥0.018 / 1k tokens "SparkDesk-v2.1": 1.2858, // ¥0.018 / 1k tokens "SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens "embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens "embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens diff --git a/controller/model.go b/controller/model.go index 8909de4..38c6c46 100644 --- a/controller/model.go +++ b/controller/model.go @@ -129,6 +129,13 @@ func ListModels(c *gin.Context) { }) } +func ChannelListModels(c *gin.Context) { + c.JSON(200, gin.H{ + "object": "list", + "data": openAIModels, + }) +} + func RetrieveModel(c *gin.Context) { modelId := c.Param("model") if model, ok := openAIModelsMap[modelId]; ok { diff --git a/router/api-router.go b/router/api-router.go index 04d3490..48d62fa 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -73,7 +73,7 @@ func SetApiRouter(router *gin.Engine) { { channelRoute.GET("/", controller.GetAllChannels) channelRoute.GET("/search", controller.SearchChannels) - channelRoute.GET("/models", controller.ListModels) + channelRoute.GET("/models", controller.ChannelListModels) channelRoute.GET("/:id", controller.GetChannel) channelRoute.GET("/test", controller.TestAllChannels) channelRoute.GET("/test/:id", controller.TestChannel) From 699fe256d0d85b9b7bae6dd6483fd9a2e793a579 Mon Sep 17 00:00:00 2001 From: Ehco1996 Date: Sun, 3 Mar 2024 19:41:15 +0800 Subject: [PATCH 14/23] address comment --- makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/makefile b/makefile index 1e8ab1b..f846d30 100644 --- a/makefile +++ b/makefile @@ -3,7 +3,7 @@ BACKEND_DIR = . .PHONY: all build-frontend start-backend -all: start-frontend start-backend +all: build-frontend start-backend build-frontend: @echo "Building frontend..." @@ -12,4 +12,3 @@ build-frontend: start-backend: @echo "Starting backend dev server..." @cd $(BACKEND_DIR) && go run main.go & - From 37b307a784752ac4f7f065fa99bfb8b299abbc43 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sun, 3 Mar 2024 22:05:00 +0800 Subject: [PATCH 15/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=A2=84?= =?UTF-8?q?=E6=89=A3=E8=B4=B9=E5=88=A4=E5=AE=9A=E6=97=A0=E6=95=88=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=94=A8=E6=88=B7=E5=8F=AF=E6=97=A0=E9=99=90=E6=AC=A0?= =?UTF-8?q?=E8=B4=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- relay/relay-text.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relay/relay-text.go b/relay/relay-text.go index 95c29ec..c0d089a 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -115,7 +115,7 @@ func TextHelper(c *gin.Context) *dto.OpenAIErrorWithStatusCode { // pre-consume quota 预消耗配额 preConsumedQuota, userQuota, openaiErr := preConsumeQuota(c, preConsumedQuota, relayInfo) - if err != nil { + if openaiErr != nil { return openaiErr } @@ -183,7 +183,7 @@ func preConsumeQuota(c *gin.Context, preConsumedQuota int, relayInfo *relaycommo if err != nil { return 0, 0, service.OpenAIErrorWrapper(err, "get_user_quota_failed", http.StatusInternalServerError) } - if userQuota < 0 || userQuota-preConsumedQuota < 0 { + if userQuota <= 0 || userQuota-preConsumedQuota < 0 { return 0, 0, service.OpenAIErrorWrapper(errors.New("user quota is not enough"), "insufficient_user_quota", http.StatusForbidden) } err = model.CacheDecreaseUserQuota(relayInfo.UserId, preConsumedQuota) From d2de675564a73d42019bab1c0c73e0fcf4b34791 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sun, 3 Mar 2024 22:28:07 +0800 Subject: [PATCH 16/23] chore: update README.md --- README.md | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f852c0e..5eeed36 100644 --- a/README.md +++ b/README.md @@ -14,30 +14,41 @@ > 最新版Docker镜像 calciumion/new-api:latest > 更新指令 docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower -cR -## 此分叉版本的主要变更 +## 主要变更 +此分叉版本的主要变更如下: + 1. 全新的UI界面(部分界面还待更新) -2. 添加[Midjourney-Proxy](https://github.com/novicezk/midjourney-proxy)接口的支持: - + [x] /mj/submit/imagine - + [x] /mj/submit/change - + [x] /mj/submit/blend - + [x] /mj/submit/describe - + [x] /mj/image/{id} (通过此接口获取图片,**请必须在系统设置中填写服务器地址!!**) - + [x] /mj/task/{id}/fetch (此接口返回的图片地址为经过One API转发的地址) - + [x] /task/list-by-condition +2. 添加[Midjourney-Proxy](https://github.com/novicezk/midjourney-proxy)接口的支持 + + [x] /mj/submit/imagine + + [x] /mj/submit/change + + [x] /mj/submit/blend + + [x] /mj/submit/describe + + [x] /mj/image/{id} (通过此接口获取图片,**请必须在系统设置中填写服务器地址!!**) + + [x] /mj/task/{id}/fetch (此接口返回的图片地址为经过One API转发的地址) + + [x] /task/list-by-condition 3. 支持在线充值功能,可在系统设置中设置,当前支持的支付接口: - + [x] 易支付 + + [x] 易支付 4. 支持用key查询使用额度: - + 配合项目[neko-api-key-tool](https://github.com/Calcium-Ion/neko-api-key-tool)可实现用key查询使用情况,方便二次分销 + + 配合项目[neko-api-key-tool](https://github.com/Calcium-Ion/neko-api-key-tool)可实现用key查询使用 5. 渠道显示已使用额度,支持指定组织访问 6. 分页支持选择每页显示数量 -7. 支持 gpt-4-1106-vision-preview,dall-e-3,tts-1 -8. 支持第三方模型 **gps** (gpt-4-gizmo-*),在渠道中添加自定义模型gpt-4-gizmo-*即可 -9. 兼容原版One API的数据库,可直接使用原版数据库(one-api.db) -10. 支持模型按次数收费,可在 系统设置-运营设置 中设置 -11. 支持gemini-pro,gemini-pro-vision模型 -12. 支持渠道**加权随机** -13. 数据看板 -14. 可设置令牌能调用的模型 +7. 兼容原版One API的数据库,可直接使用原版数据库(one-api.db) +8. 支持模型按次数收费,可在 系统设置-运营设置 中设置 +9. 支持渠道**加权随机** +10. 数据看板 +11. 可设置令牌能调用的模型 + +## 模型支持 +此版本支持以下模型: +1. OpenAI基础模型(gpt-3.5, gpt-4, gpt-4-turbo系列) +2. dall-e-3 +3. tts-1 +4. 第三方模型 **gps** (gpt-4-gizmo-*) +5. gemini-pro, gemini-pro-vision +6. 智谱glm-4, glm-4v识图 + +您可以在渠道中添加自定义模型gpt-4-gizmo-*,此模型并非OpenAI官方模型,而是第三方模型,使用官方key无法调用。 + ## 部署 ### 基于 Docker 进行部署 From 1f0a48a8792eaaf59eca92c4cc16240dcf23a0fb Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sun, 3 Mar 2024 23:41:00 +0800 Subject: [PATCH 17/23] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BF=9D?= =?UTF-8?q?=E5=AD=98telegram=E8=AE=BE=E7=BD=AE=E5=90=8E=E4=BC=9A=E5=B0=86t?= =?UTF-8?q?elegram=E7=99=BB=E9=99=86=E7=A6=81=E7=94=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++-------- web/src/components/SystemSetting.js | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5eeed36..33fd7aa 100644 --- a/README.md +++ b/README.md @@ -37,19 +37,15 @@ 9. 支持渠道**加权随机** 10. 数据看板 11. 可设置令牌能调用的模型 +12. 支持Telegram授权登录 ## 模型支持 -此版本支持以下模型: -1. OpenAI基础模型(gpt-3.5, gpt-4, gpt-4-turbo系列) -2. dall-e-3 -3. tts-1 -4. 第三方模型 **gps** (gpt-4-gizmo-*) -5. gemini-pro, gemini-pro-vision -6. 智谱glm-4, glm-4v识图 +此版本额外支持以下模型: +1. 第三方模型 **gps** (gpt-4-gizmo-*) +2. 智谱glm-4v,glm-4v识图 您可以在渠道中添加自定义模型gpt-4-gizmo-*,此模型并非OpenAI官方模型,而是第三方模型,使用官方key无法调用。 - ## 部署 ### 基于 Docker 进行部署 ```shell diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index bddac31..e955afe 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -244,7 +244,7 @@ const SystemSetting = () => { }; const submitTelegramSettings = async () => { - await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); + // await updateOption('TelegramOAuthEnabled', inputs.TelegramOAuthEnabled); await updateOption('TelegramBotToken', inputs.TelegramBotToken); await updateOption('TelegramBotName', inputs.TelegramBotName); }; From 1ad1112351116c935d7f0afbe24b0276cb2fcfb4 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Mon, 4 Mar 2024 14:55:26 +0800 Subject: [PATCH 18/23] =?UTF-8?q?feat:=20=E8=A7=A3=E5=86=B3=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E5=AF=86=E9=92=A5=E9=95=BF=E5=BA=A6=E9=99=90=E5=88=B6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/channel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/channel.go b/model/channel.go index 7c7b0d9..b06f578 100644 --- a/model/channel.go +++ b/model/channel.go @@ -8,7 +8,7 @@ import ( type Channel struct { Id int `json:"id"` Type int `json:"type" gorm:"default:0"` - Key string `json:"key" gorm:"not null;index"` + Key string `json:"key" gorm:"not null"` OpenAIOrganization *string `json:"openai_organization"` Status int `json:"status" gorm:"default:1"` Name string `json:"name" gorm:"index"` From 912f46fcd2bb2ab6cbb3ef6a11b853f2eae50416 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Mon, 4 Mar 2024 14:58:30 +0800 Subject: [PATCH 19/23] =?UTF-8?q?feat:=20=E8=AE=B0=E5=BD=95=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84token=E7=94=A8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/log.go | 2 +- model/usedata.go | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/model/log.go b/model/log.go index 44bc5b7..2740c5a 100644 --- a/model/log.go +++ b/model/log.go @@ -85,7 +85,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke } if common.DataExportEnabled { common.SafeGoroutine(func() { - LogQuotaData(userId, username, modelName, quota, common.GetTimestamp()) + LogQuotaData(userId, username, modelName, quota, common.GetTimestamp(), promptTokens+completionTokens) }) } } diff --git a/model/usedata.go b/model/usedata.go index 9ca623d..b2f3025 100644 --- a/model/usedata.go +++ b/model/usedata.go @@ -15,6 +15,7 @@ type QuotaData struct { Username string `json:"username" gorm:"index:idx_qdt_model_user_name,priority:2;size:64;default:''"` ModelName string `json:"model_name" gorm:"index:idx_qdt_model_user_name,priority:1;size:64;default:''"` CreatedAt int64 `json:"created_at" gorm:"bigint;index:idx_qdt_created_at,priority:2"` + TokenUsed int `json:"token_used" gorm:"default:0"` Count int `json:"count" gorm:"default:0"` Quota int `json:"quota" gorm:"default:0"` } @@ -38,7 +39,7 @@ func UpdateQuotaData() { var CacheQuotaData = make(map[string]*QuotaData) var CacheQuotaDataLock = sync.Mutex{} -func logQuotaDataCache(userId int, username string, modelName string, quota int, createdAt int64) { +func logQuotaDataCache(userId int, username string, modelName string, quota int, createdAt int64, tokenUsed int) { key := fmt.Sprintf("%d-%s-%s-%d", userId, username, modelName, createdAt) quotaData, ok := CacheQuotaData[key] if ok { @@ -52,18 +53,19 @@ func logQuotaDataCache(userId int, username string, modelName string, quota int, CreatedAt: createdAt, Count: 1, Quota: quota, + TokenUsed: tokenUsed, } } CacheQuotaData[key] = quotaData } -func LogQuotaData(userId int, username string, modelName string, quota int, createdAt int64) { +func LogQuotaData(userId int, username string, modelName string, quota int, createdAt int64, tokenUsed int) { // 只精确到小时 createdAt = createdAt - (createdAt % 3600) CacheQuotaDataLock.Lock() defer CacheQuotaDataLock.Unlock() - logQuotaDataCache(userId, username, modelName, quota, createdAt) + logQuotaDataCache(userId, username, modelName, quota, createdAt, tokenUsed) } func SaveQuotaDataCache() { From fe7f42fc2e5204ce23ea71a33c52e9b1dc8b5c43 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Mon, 4 Mar 2024 19:32:59 +0800 Subject: [PATCH 20/23] feat: add "/api/status/test" --- controller/misc.go | 16 ++++++++++++++++ model/main.go | 32 ++++++++++++++++++++++++++++++++ router/api-router.go | 1 + 3 files changed, 49 insertions(+) diff --git a/controller/misc.go b/controller/misc.go index 0a4f1d8..0259426 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -11,6 +11,22 @@ import ( "github.com/gin-gonic/gin" ) +func TestStatus(c *gin.Context) { + err := model.PingDB() + if err != nil { + c.JSON(http.StatusServiceUnavailable, gin.H{ + "success": false, + "message": "数据库连接失败", + }) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "Server is running", + }) + return +} + func GetStatus(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, diff --git a/model/main.go b/model/main.go index ade3cc1..bb91255 100644 --- a/model/main.go +++ b/model/main.go @@ -5,9 +5,11 @@ import ( "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" + "log" "one-api/common" "os" "strings" + "sync" "time" ) @@ -148,3 +150,33 @@ func CloseDB() error { err = sqlDB.Close() return err } + +var ( + lastPingTime time.Time + pingMutex sync.Mutex +) + +func PingDB() error { + pingMutex.Lock() + defer pingMutex.Unlock() + + if time.Since(lastPingTime) < time.Second*10 { + return nil + } + + sqlDB, err := DB.DB() + if err != nil { + log.Printf("Error getting sql.DB from GORM: %v", err) + return err + } + + err = sqlDB.Ping() + if err != nil { + log.Printf("Error pinging DB: %v", err) + return err + } + + lastPingTime = time.Now() + common.SysLog("Database pinged successfully") + return nil +} diff --git a/router/api-router.go b/router/api-router.go index 1683a4f..592e8ed 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -14,6 +14,7 @@ func SetApiRouter(router *gin.Engine) { apiRouter.Use(middleware.GlobalAPIRateLimit()) { apiRouter.GET("/status", controller.GetStatus) + apiRouter.GET("/status/test", middleware.AdminAuth(), controller.TestStatus) apiRouter.GET("/notice", controller.GetNotice) apiRouter.GET("/about", controller.GetAbout) apiRouter.GET("/midjourney", controller.GetMidjourney) From d160736a4957c6e82cd20eb58c02d8b5b83ae2be Mon Sep 17 00:00:00 2001 From: QuentinHsu Date: Tue, 5 Mar 2024 12:15:56 +0800 Subject: [PATCH 21/23] fix: model names must not contain spaces at both ends --- web/src/pages/Channel/EditChannel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index f61c38e..4bff8e9 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -454,7 +454,7 @@ const EditChannel = (props) => { placeholder='输入自定义模型名称' value={customModel} onChange={(value) => { - setCustomModel(value); + setCustomModel(value.trim()); }} />
From 92c1ed7f1dfc7e2f31379f8b9e9191de5abf185e Mon Sep 17 00:00:00 2001 From: QuentinHsu Date: Tue, 5 Mar 2024 12:18:30 +0800 Subject: [PATCH 22/23] perf: prompt when the name of the custom model input already exists --- web/src/pages/Channel/EditChannel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index 4bff8e9..876015d 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -241,7 +241,7 @@ const EditChannel = (props) => { const addCustomModel = () => { if (customModel.trim() === '') return; - if (inputs.models.includes(customModel)) return; + if (inputs.models.includes(customModel)) return showError("该模型已存在!"); let localModels = [...inputs.models]; localModels.push(customModel); let localModelOptions = []; From fb39b6b30e7f7f69c55770059f71d65eaa0f5d2f Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Tue, 5 Mar 2024 23:04:57 +0800 Subject: [PATCH 23/23] fix: Fix the issue of 'unknown relay mode' when making an embedding request (close #93) --- relay/relay-text.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/relay/relay-text.go b/relay/relay-text.go index c0d089a..d38afaa 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -169,6 +169,8 @@ func getPromptTokens(textRequest *dto.GeneralOpenAIRequest, info *relaycommon.Re promptTokens, err = service.CountTokenInput(textRequest.Prompt, textRequest.Model), nil case relayconstant.RelayModeModerations: promptTokens, err = service.CountTokenInput(textRequest.Input, textRequest.Model), nil + case relayconstant.RelayModeEmbeddings: + promptTokens, err = service.CountTokenInput(textRequest.Input, textRequest.Model), nil default: err = errors.New("unknown relay mode") promptTokens = 0