diff --git a/README.md b/README.md index 341733e90..aa795be8a 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ 您可以在渠道中添加自定义模型gpt-4-gizmo-*,此模型并非OpenAI官方模型,而是第三方模型,使用官方key无法调用。 ## 比原版One API多出的配置 +- `GENERATE_DEFAULT_TOKEN`:是否为新注册用户生成初始令牌,默认为 `false`。 - `STREAMING_TIMEOUT`:设置流式一次回复的超时时间,默认为 30 秒。 - `DIFY_DEBUG`:设置 Dify 渠道是否输出工作流和节点信息到客户端,默认为 `true`。 - `FORCE_STREAM_OPTION`:是否覆盖客户端stream_options参数,请求上游返回流模式usage,默认为 `true`,建议开启,不影响客户端传入stream_options参数返回结果。 diff --git a/constant/env.go b/constant/env.go index c5d498d17..6bab4bea8 100644 --- a/constant/env.go +++ b/constant/env.go @@ -46,3 +46,6 @@ func InitEnv() { } } } + +// 是否生成初始令牌,默认关闭。 +var GenerateDefaultToken = common.GetEnvOrDefaultBool("GENERATE_DEFAULT_TOKEN", false) diff --git a/controller/user.go b/controller/user.go index 6faec2b70..e69a61313 100644 --- a/controller/user.go +++ b/controller/user.go @@ -11,6 +11,7 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "one-api/constant" ) type LoginRequest struct { @@ -186,6 +187,39 @@ func Register(c *gin.Context) { }) return } + + // 获取插入后的用户ID + var insertedUser model.User + if err := model.DB.Where("username = ?", cleanUser.Username).First(&insertedUser).Error; err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "用户注册失败或用户ID获取失败", + }) + return + } + // 生成默认令牌 + if constant.GenerateDefaultToken { + // 生成默认令牌 + token := model.Token{ + UserId: insertedUser.Id, // 使用插入后的用户ID + Name: cleanUser.Username + "的初始令牌", + Key: common.GenerateKey(), + CreatedTime: common.GetTimestamp(), + AccessedTime: common.GetTimestamp(), + ExpiredTime: -1, // 永不过期 + RemainQuota: 500000, // 示例额度 + UnlimitedQuota: true, + ModelLimitsEnabled: false, + } + if err := token.Insert(); err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "创建默认令牌失败", + }) + return + } + } + c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", diff --git a/web/src/App.js b/web/src/App.js index 06d582023..c56dd095c 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -20,12 +20,11 @@ import Redemption from './pages/Redemption'; import TopUp from './pages/TopUp'; import Log from './pages/Log'; import Chat from './pages/Chat'; +import Chat2Link from './pages/Chat2Link'; import { Layout } from '@douyinfe/semi-ui'; import Midjourney from './pages/Midjourney'; import Pricing from './pages/Pricing/index.js'; import Task from "./pages/Task/index.js"; -import FooterBar from './components/Footer.js'; -// import Detail from './pages/Detail'; const Home = lazy(() => import('./pages/Home')); const Detail = lazy(() => import('./pages/Detail')); @@ -255,9 +254,20 @@ function App() { } /> - } /> - - + {/* 方便使用chat2link直接跳转聊天... */} + + }> + + + + } + /> + } /> + + ); } diff --git a/web/src/components/fetchTokenKeys.js b/web/src/components/fetchTokenKeys.js new file mode 100644 index 000000000..7c70c65f4 --- /dev/null +++ b/web/src/components/fetchTokenKeys.js @@ -0,0 +1,70 @@ +// src/hooks/useTokenKeys.js +import { useEffect, useState } from 'react'; +import { API, showError } from '../helpers'; + +async function fetchTokenKeys() { + try { + const response = await API.get('/api/token/?p=0&size=999'); + const { success, data } = response.data; + if (success) { + const activeTokens = data.filter((token) => token.status === 1); + return activeTokens.map((token) => token.key); + } else { + throw new Error('Failed to fetch token keys'); + } + } catch (error) { + console.error("Error fetching token keys:", error); + return []; + } +} + +function getServerAddress() { + let status = localStorage.getItem('status'); + let serverAddress = ''; + + if (status) { + try { + status = JSON.parse(status); + serverAddress = status.server_address || ''; + } catch (error) { + console.error("Failed to parse status from localStorage:", error); + } + } + + if (!serverAddress) { + serverAddress = window.location.origin; + } + + return serverAddress; +} + +export function useTokenKeys() { + const [keys, setKeys] = useState([]); + const [chatLink, setChatLink] = useState(''); + const [serverAddress, setServerAddress] = useState(''); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const loadAllData = async () => { + const fetchedKeys = await fetchTokenKeys(); + if (fetchedKeys.length === 0) { + showError('当前没有可用的启用令牌,请确认是否有令牌处于启用状态!'); + setTimeout(() => { + window.location.href = '/token'; + }, 1500); // 延迟 1.5 秒后跳转 + } + setKeys(fetchedKeys); + setIsLoading(false); + + const link = localStorage.getItem('chat_link'); + setChatLink(link); + + const address = getServerAddress(); + setServerAddress(address); + }; + + loadAllData(); + }, []); + + return { keys, chatLink, serverAddress, isLoading }; +} \ No newline at end of file diff --git a/web/src/pages/Chat/index.js b/web/src/pages/Chat/index.js index 86f2fd692..b2dd414fd 100644 --- a/web/src/pages/Chat/index.js +++ b/web/src/pages/Chat/index.js @@ -1,14 +1,35 @@ import React from 'react'; +import { useTokenKeys } from '../../components/fetchTokenKeys'; +import { Layout } from '@douyinfe/semi-ui'; -const Chat = () => { - const chatLink = localStorage.getItem('chat_link'); +const ChatPage = () => { + const { keys, chatLink, serverAddress, isLoading } = useTokenKeys(); - return ( + const comLink = (key) => { + if (!chatLink || !serverAddress || !key) return ''; + return `${chatLink}/#/?settings={"key":"sk-${key}","url":"${encodeURIComponent(serverAddress)}"}`; + }; + + const iframeSrc = keys.length > 0 ? comLink(keys[0]) : ''; + + return !isLoading && iframeSrc ? (