ソースを参照

Merge pull request #439 from guoruqiang/main

改进了聊天页面,增加了初始令牌,方便用户注册后即可使用聊天功能。
Calcium-Ion 1 年間 前
コミット
fff7609f06

+ 1 - 0
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参数返回结果。

+ 3 - 0
constant/env.go

@@ -46,3 +46,6 @@ func InitEnv() {
 		}
 	}
 }
+
+// 是否生成初始令牌,默认关闭。
+var GenerateDefaultToken = common.GetEnvOrDefaultBool("GENERATE_DEFAULT_TOKEN", false)

+ 34 - 0
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": "",

+ 15 - 5
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() {
             </Suspense>
           }
         />
-        <Route path='*' element={<NotFound />} />
-      </Routes>
-    </>
+        {/* 方便使用chat2link直接跳转聊天... */}
+          <Route
+            path='/chat2link'
+            element={
+              <PrivateRoute>
+                <Suspense fallback={<Loading></Loading>}>
+                    <Chat2Link />
+                </Suspense>
+              </PrivateRoute>
+            }
+          />
+          <Route path='*' element={<NotFound />} />
+        </Routes>
+      </>
   );
 }
 

+ 70 - 0
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 };
+}

+ 26 - 5
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 ? (
     <iframe
-      src={chatLink}
+      src={iframeSrc}
       style={{ width: '100%', height: '85vh', border: 'none' }}
+      title="Token Frame"
     />
+  ) : (
+    <div>
+      <Layout>
+        <Layout.Header>
+          <h3 style={{ color: 'red'}}>
+            当前没有可用的已启用令牌,请确认是否有令牌处于启用状态!<br />
+            正在跳转......
+          </h3>
+        </Layout.Header>
+      </Layout>
+    </div>
   );
 };
 
-export default Chat;
+export default ChatPage;

+ 26 - 0
web/src/pages/Chat2Link/index.js

@@ -0,0 +1,26 @@
+import React from 'react';
+import { useTokenKeys } from '../../components/fetchTokenKeys';
+
+const chat2page = () => {
+  const { keys, chatLink, serverAddress, isLoading } = useTokenKeys();
+
+  const comLink = (key) => {
+    if (!chatLink || !serverAddress || !key) return '';
+    return `${chatLink}/#/?settings={"key":"sk-${key}","url":"${encodeURIComponent(serverAddress)}"}`;
+  };
+
+  if (keys.length > 0) {
+    const redirectLink = comLink(keys[0]);
+    if (redirectLink) {
+      window.location.href = redirectLink;
+    }
+  }
+
+  return (
+    <div>
+        <h3>正在加载,请稍候...</h3>
+    </div>
+  );
+};
+
+export default chat2page;