From 6d438aafdf945681bd746bf96e78ddd69da863d0 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Wed, 13 Sep 2023 08:49:22 +0800 Subject: [PATCH] google login and power share link (#292) --- client/data/config.json | 4 +- client/public/locales/en/common.json | 23 + client/public/locales/zh/common.json | 23 + client/src/api/chat.ts | 30 +- client/src/api/plugins/kb.ts | 11 +- client/src/api/request/kb.d.ts | 5 + client/src/api/service/request.ts | 1 - client/src/api/support/outLink.ts | 34 ++ client/src/api/user.ts | 15 +- .../src/components/Icon/icons/fill/google.svg | 1 + client/src/components/Icon/index.tsx | 1 + client/src/components/Layout/navbar.tsx | 4 +- client/src/constants/model.ts | 11 +- client/src/constants/user.ts | 4 + client/src/pages/account/components/Info.tsx | 4 +- .../pages/api/openapi/v1/chat/completions.ts | 18 +- .../api/plugins/kb/data/exportModelData.ts | 4 +- .../api/plugins/kb/file/deleteEmptyFiles.ts | 2 +- client/src/pages/api/plugins/kb/file/list.ts | 35 +- .../shareChat => support/outLink}/create.ts | 8 +- .../shareChat => support/outLink}/delete.ts | 4 +- .../shareChat => support/outLink}/init.ts | 2 +- .../shareChat => support/outLink}/list.ts | 14 +- .../src/pages/api/support/outLink/update.ts | 25 ++ client/src/pages/api/system/getInitData.ts | 4 +- .../pages/app/detail/components/OutLink.tsx | 406 ++++++++++++------ client/src/pages/chat/share.tsx | 2 +- .../pages/kb/detail/components/DataCard.tsx | 39 +- .../pages/kb/detail/components/FileCard.tsx | 91 +++- .../pages/kb/detail/components/Import/QA.tsx | 6 +- .../src/pages/login/components/LoginForm.tsx | 67 ++- client/src/pages/login/provider.tsx | 63 +-- client/src/service/common/ipLimit/schema.ts | 24 ++ client/src/service/mongo.ts | 2 +- client/src/service/support/outLink/auth.ts | 79 ++++ .../outLink.ts => support/outLink/schema.ts} | 22 +- client/src/service/utils/auth.ts | 21 - client/src/store/global.ts | 3 +- client/src/types/app.d.ts | 4 - client/src/types/common/ipLimit.d.ts | 7 + client/src/types/index.d.ts | 9 +- client/src/types/mongoSchema.d.ts | 13 +- client/src/types/support/outLink.d.ts | 25 ++ .../content/docs/development/configuration.md | 2 - files/deploy/fastgpt/config.json | 1 - 45 files changed, 813 insertions(+), 360 deletions(-) create mode 100644 client/src/api/support/outLink.ts create mode 100644 client/src/components/Icon/icons/fill/google.svg rename client/src/pages/api/{chat/shareChat => support/outLink}/create.ts (84%) rename client/src/pages/api/{chat/shareChat => support/outLink}/delete.ts (100%) rename client/src/pages/api/{chat/shareChat => support/outLink}/init.ts (96%) rename client/src/pages/api/{chat/shareChat => support/outLink}/list.ts (78%) create mode 100644 client/src/pages/api/support/outLink/update.ts create mode 100644 client/src/service/common/ipLimit/schema.ts create mode 100644 client/src/service/support/outLink/auth.ts rename client/src/service/{models/outLink.ts => support/outLink/schema.ts} (57%) create mode 100644 client/src/types/common/ipLimit.d.ts create mode 100644 client/src/types/support/outLink.d.ts diff --git a/client/data/config.json b/client/data/config.json index 8ebc01f4e..c8f8073e6 100644 --- a/client/data/config.json +++ b/client/data/config.json @@ -9,7 +9,9 @@ "show_doc": true, "systemTitle": "FastGPT", "authorText": "Made by FastGPT Team.", - "exportLimitMinutes": 0, + "limit": { + "exportLimitMinutes": 0 + }, "scripts": [] }, "SystemParams": { diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 3e143fdce..bce86cd63 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -83,14 +83,18 @@ "Delete Failed": "Delete Failed", "Delete Success": "Delete Successful", "Delete Warning": "Warning", + "Edit": "Edit", + "Expired Time": "Expired", "Filed is repeat": "Filed is repeated", "Filed is repeated": "", "Input": "Input", + "Name is empty": "Name is empty", "Output": "Output", "Password inconsistency": "Password inconsistency", "Rename": "Rename", "Search": "Search", "Status": "Status", + "Update Successful": "Update Successful", "export": "" }, "dataset": { @@ -192,6 +196,25 @@ "Store": "Store", "Tools": "Tools" }, + "outlink": { + "Copy Iframe": "Copy Iframe", + "Copy Link": "Copy", + "Create Ifrme Window": "Create Iframe Link", + "Create Share Window": "Create Share Window", + "Delete Link": "Delete", + "Edit Ifrme Link": "Edit Iframe Link", + "Edit Link": "Edit", + "Edit Share Window": "Edit Share Window", + "Link Name": "Link Name", + "Link is empty": "", + "Max credit": "Credit", + "Max credit tips": "What is the maximum amount of money that can be consumed by the link? If the link is exceeded, it will be banned. -1 indicates no limit.", + "QPM": "QPM", + "QPM Tips": "The maximum number of queries per IP address per minute", + "QPM is empty": "QPM is empty", + "Response Detail": "Detail", + "Response Detail tips": "Whether detailed data such as references and full context need to be returned" + }, "user": { "Account": "Account", "Amount of earnings": "Earnings", diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json index b12dafebb..733003569 100644 --- a/client/public/locales/zh/common.json +++ b/client/public/locales/zh/common.json @@ -83,14 +83,18 @@ "Delete Failed": "删除失败", "Delete Success": "删除成功", "Delete Warning": "删除警告", + "Edit": "编辑", + "Expired Time": "过期时间", "Filed is repeat": "", "Filed is repeated": "字段重复了", "Input": "输入", + "Name is empty": "名称不能为空", "Output": "输出", "Password inconsistency": "两次密码不一致", "Rename": "重命名", "Search": "搜索", "Status": "状态", + "Update Successful": "更新成功", "export": "" }, "dataset": { @@ -192,6 +196,25 @@ "Store": "应用市场", "Tools": "工具" }, + "outlink": { + "Copy Iframe": "复制嵌入", + "Copy Link": "复制", + "Create Ifrme Window": "创建嵌入链接", + "Create Share Window": "创建免登录窗口", + "Delete Link": "删除链接", + "Edit Ifrme Link": "更新嵌入链接", + "Edit Link": "编辑", + "Edit Share Window": "更新分享窗口", + "Link Name": "分享链接的名字", + "Link is empty": "", + "Max credit": "最大金额", + "Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。", + "QPM": "", + "QPM Tips": "每个 IP 每分钟最多提问多少次", + "QPM is empty": "QPM 不能为空", + "Response Detail": "返回详情", + "Response Detail tips": "是否需要返回引用、完整上下文等详细数据" + }, "user": { "Account": "账号", "Amount of earnings": "收益(¥)", diff --git a/client/src/api/chat.ts b/client/src/api/chat.ts index c7fa4447b..dd72c6a26 100644 --- a/client/src/api/chat.ts +++ b/client/src/api/chat.ts @@ -1,9 +1,7 @@ import { GET, POST, DELETE, PUT } from './request'; import type { ChatHistoryItemType } from '@/types/chat'; -import type { InitChatResponse, InitShareChatResponse } from './response/chat'; +import type { InitChatResponse } from './response/chat'; import { RequestPaging } from '../types/index'; -import type { OutLinkSchema } from '@/types/mongoSchema'; -import type { ShareChatEditType } from '@/types/app'; import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory'; import { AdminUpdateFeedbackParams } from './request/chat'; @@ -40,32 +38,6 @@ export const delChatRecordById = (data: { chatId: string; contentId: string }) = export const putChatHistory = (data: UpdateHistoryProps) => PUT('/chat/history/updateChatHistory', data); -/** - * 初始化分享聊天 - */ -export const initShareChatInfo = (data: { shareId: string }) => - GET(`/chat/shareChat/init`, data); - -/** - * create a shareChat - */ -export const createShareChat = ( - data: ShareChatEditType & { - appId: string; - } -) => POST(`/chat/shareChat/create`, data); - -/** - * get shareChat - */ -export const getShareChatList = (appId: string) => - GET(`/chat/shareChat/list`, { appId }); - -/** - * delete a shareChat - */ -export const delShareChatById = (id: string) => DELETE(`/chat/shareChat/delete?id=${id}`); - export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) => POST('/chat/feedback/userUpdate', data); diff --git a/client/src/api/plugins/kb.ts b/client/src/api/plugins/kb.ts index 1836b5213..c1c44f7f9 100644 --- a/client/src/api/plugins/kb.ts +++ b/client/src/api/plugins/kb.ts @@ -17,7 +17,12 @@ import { Response as SearchTestResponse } from '@/pages/api/openapi/kb/searchTest'; import { Props as UpdateDataProps } from '@/pages/api/openapi/kb/updateData'; -import type { KbUpdateParams, CreateKbParams, GetKbDataListProps } from '../request/kb'; +import type { + KbUpdateParams, + CreateKbParams, + GetKbDataListProps, + GetFileListProps +} from '../request/kb'; import { QuoteItemType } from '@/types/chat'; import { KbTypeEnum } from '@/constants/kb'; @@ -38,8 +43,8 @@ export const putKbById = (data: KbUpdateParams) => PUT(`/plugins/kb/update`, dat export const delKbById = (id: string) => DELETE(`/plugins/kb/delete?id=${id}`); /* kb file */ -export const getKbFiles = (data: { kbId: string; searchText: string }) => - GET(`/plugins/kb/file/list`, data); +export const getKbFiles = (data: GetFileListProps) => + POST(`/plugins/kb/file/list`, data); export const deleteKbFileById = (params: { fileId: string; kbId: string }) => DELETE(`/plugins/kb/file/delFileByFileId`, params); export const getFileInfoById = (fileId: string) => diff --git a/client/src/api/request/kb.d.ts b/client/src/api/request/kb.d.ts index b382a0e49..3d93407c6 100644 --- a/client/src/api/request/kb.d.ts +++ b/client/src/api/request/kb.d.ts @@ -17,6 +17,11 @@ export type CreateKbParams = { type: `${KbTypeEnum}`; }; +export type GetFileListProps = RequestPaging & { + kbId: string; + searchText: string; +}; + export type GetKbDataListProps = RequestPaging & { kbId: string; searchText: string; diff --git a/client/src/api/service/request.ts b/client/src/api/service/request.ts index a4e196418..a1e47e539 100644 --- a/client/src/api/service/request.ts +++ b/client/src/api/service/request.ts @@ -1,5 +1,4 @@ import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; -import { baseUrl } from '../../service/lib/openai'; interface ConfigType { headers?: { [key: string]: string }; diff --git a/client/src/api/support/outLink.ts b/client/src/api/support/outLink.ts new file mode 100644 index 000000000..c94640ba4 --- /dev/null +++ b/client/src/api/support/outLink.ts @@ -0,0 +1,34 @@ +import { GET, POST, DELETE } from '../request'; +import type { InitShareChatResponse } from '../response/chat'; +import type { OutLinkEditType } from '@/types/support/outLink'; +import type { OutLinkSchema } from '@/types/support/outLink'; + +/** + * 初始化分享聊天 + */ +export const initShareChatInfo = (data: { shareId: string }) => + GET(`/support/outLink/init`, data); + +/** + * create a shareChat + */ +export const createShareChat = ( + data: OutLinkEditType & { + appId: string; + type: OutLinkSchema['type']; + } +) => POST(`/support/outLink/create`, data); + +export const putShareChat = (data: OutLinkEditType) => + POST(`/support/outLink/update`, data); + +/** + * get shareChat + */ +export const getShareChatList = (appId: string) => + GET(`/support/outLink/list`, { appId }); + +/** + * delete a shareChat + */ +export const delShareChatById = (id: string) => DELETE(`/support/outLink/delete?id=${id}`); diff --git a/client/src/api/user.ts b/client/src/api/user.ts index 4da704c27..a8d6010e9 100644 --- a/client/src/api/user.ts +++ b/client/src/api/user.ts @@ -5,16 +5,21 @@ import { UserAuthTypeEnum } from '@/constants/common'; import { UserBillType, UserType, UserUpdateParams } from '@/types/user'; import type { PagingData, RequestPaging } from '@/types'; import { informSchema, PaySchema } from '@/types/mongoSchema'; +import { OAuthEnum } from '@/constants/user'; export const sendAuthCode = (data: { username: string; type: `${UserAuthTypeEnum}`; googleToken: string; -}) => POST(`/plusApi/user/account/sendCode`, data); +}) => POST(`/plusApi/user/inform/sendAuthCode`, data); export const getTokenLogin = () => GET('/user/account/tokenLogin'); -export const gitLogin = (params: { code: string; inviterId?: string }) => - GET('/plusApi/user/account/gitLogin', params); +export const oauthLogin = (params: { + type: `${OAuthEnum}`; + code: string; + callbackUrl: string; + inviterId?: string; +}) => POST('/plusApi/user/account/login/oauth', params); export const postRegister = ({ username, @@ -27,7 +32,7 @@ export const postRegister = ({ password: string; inviterId?: string; }) => - POST(`/plusApi/user/account/register`, { + POST(`/plusApi/user/account/register/emailAndPhone`, { username, code, inviterId, @@ -43,7 +48,7 @@ export const postFindPassword = ({ code: string; password: string; }) => - POST(`/plusApi/user/account/updatePasswordByCode`, { + POST(`/plusApi/user/account/password/updateByCode`, { username, code, password: createHashPassword(password) diff --git a/client/src/components/Icon/icons/fill/google.svg b/client/src/components/Icon/icons/fill/google.svg new file mode 100644 index 000000000..68e64f8c8 --- /dev/null +++ b/client/src/components/Icon/icons/fill/google.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx index b6c9a893f..b9522f01c 100644 --- a/client/src/components/Icon/index.tsx +++ b/client/src/components/Icon/index.tsx @@ -24,6 +24,7 @@ const map = { out: require('./icons/out.svg').default, git: require('./icons/git.svg').default, gitFill: require('./icons/fill/git.svg').default, + googleFill: require('./icons/fill/google.svg').default, menu: require('./icons/menu.svg').default, edit: require('./icons/edit.svg').default, inform: require('./icons/inform.svg').default, diff --git a/client/src/components/Layout/navbar.tsx b/client/src/components/Layout/navbar.tsx index 50c2819cf..6a0f5d435 100644 --- a/client/src/components/Layout/navbar.tsx +++ b/client/src/components/Layout/navbar.tsx @@ -109,7 +109,7 @@ const Navbar = ({ unread }: { unread: number }) => { @@ -157,7 +157,7 @@ const Navbar = ({ unread }: { unread: number }) => { diff --git a/client/src/constants/model.ts b/client/src/constants/model.ts index db06a9a86..7dc7a5c69 100644 --- a/client/src/constants/model.ts +++ b/client/src/constants/model.ts @@ -1,5 +1,5 @@ -import type { ShareChatEditType } from '@/types/app'; import type { AppSchema } from '@/types/mongoSchema'; +import type { OutLinkEditType } from '@/types/support/outLink'; export const defaultApp: AppSchema = { _id: '', @@ -17,6 +17,11 @@ export const defaultApp: AppSchema = { modules: [] }; -export const defaultShareChat: ShareChatEditType = { - name: '' +export const defaultOutLinkForm: OutLinkEditType = { + name: '', + responseDetail: false, + limit: { + QPM: 100, + credit: -1 + } }; diff --git a/client/src/constants/user.ts b/client/src/constants/user.ts index 8bc212581..1eb5ba797 100644 --- a/client/src/constants/user.ts +++ b/client/src/constants/user.ts @@ -1,3 +1,7 @@ +export enum OAuthEnum { + github = 'github', + google = 'google' +} export enum BillSourceEnum { fastgpt = 'fastgpt', api = 'api', diff --git a/client/src/pages/account/components/Info.tsx b/client/src/pages/account/components/Info.tsx index 7ec75d942..52301c3ee 100644 --- a/client/src/pages/account/components/Info.tsx +++ b/client/src/pages/account/components/Info.tsx @@ -124,10 +124,12 @@ const UserInfo = () => { h={['44px', '54px']} borderRadius={'50%'} border={theme.borders.base} + overflow={'hidden'} + p={'2px'} boxShadow={'0 0 5px rgba(0,0,0,0.1)'} mb={2} > - + diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 1362f12be..238631884 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { connectToDatabase } from '@/service/mongo'; -import { authUser, authApp, authShareChat, AuthUserTypeEnum } from '@/service/utils/auth'; +import { authUser, authApp } from '@/service/utils/auth'; import { sseErrRes, jsonRes } from '@/service/response'; import { addLog, withNextCors } from '@/service/utils/tools'; import { ChatRoleEnum, ChatSourceEnum, sseResponseEventEnum } from '@/constants/chat'; @@ -29,6 +29,8 @@ import { ChatHistoryItemResType } from '@/types/chat'; import { UserModelSchema } from '@/types/mongoSchema'; import { SystemInputEnum } from '@/constants/app'; import { getSystemTime } from '@/utils/user'; +import { authOutLinkChat } from '@/service/support/outLink/auth'; +import requestIp from 'request-ip'; export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string }; type FastGptWebChatProps = { @@ -82,14 +84,17 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex let startTime = Date.now(); /* user auth */ - const { + let { + // @ts-ignore + responseDetail, user, userId, appId: authAppid, authType } = await (shareId - ? authShareChat({ - shareId + ? authOutLinkChat({ + shareId, + ip: requestIp.getClientIp(req) }) : authUser({ req, authBalance: true })); @@ -112,6 +117,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex ]); const isOwner = !shareId && userId === String(app.userId); + responseDetail = isOwner || responseDetail; const prompts = history.concat(gptMessage2ChatType(messages)); if (prompts[prompts.length - 1].obj === 'AI') { @@ -157,7 +163,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex appId, userId, variables, - isOwner, + isOwner, // owner update use time shareId, source: (() => { if (shareId) { @@ -197,7 +203,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex data: '[DONE]' }); - if (isOwner && detail) { + if (responseDetail && detail) { sseResponse({ res, event: sseResponseEventEnum.appStreamResponse, diff --git a/client/src/pages/api/plugins/kb/data/exportModelData.ts b/client/src/pages/api/plugins/kb/data/exportModelData.ts index dd49ea2c2..fc1e042e8 100644 --- a/client/src/pages/api/plugins/kb/data/exportModelData.ts +++ b/client/src/pages/api/plugins/kb/data/exportModelData.ts @@ -23,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const { userId } = await authUser({ req, authToken: true }); const thirtyMinutesAgo = new Date( - Date.now() - (global.feConfigs?.exportLimitMinutes || 0) * 60 * 1000 + Date.now() - (global.feConfigs?.limit?.exportLimitMinutes || 0) * 60 * 1000 ); // auth export times @@ -39,7 +39,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ); if (!authTimes) { - const minutes = `${global.feConfigs?.exportLimitMinutes || 0} 分钟`; + const minutes = `${global.feConfigs?.limit?.exportLimitMinutes || 0} 分钟`; throw new Error(`上次导出未到 ${minutes},每 ${minutes}仅可导出一次。`); } diff --git a/client/src/pages/api/plugins/kb/file/deleteEmptyFiles.ts b/client/src/pages/api/plugins/kb/file/deleteEmptyFiles.ts index 1cb1c979e..abac5af8f 100644 --- a/client/src/pages/api/plugins/kb/file/deleteEmptyFiles.ts +++ b/client/src/pages/api/plugins/kb/file/deleteEmptyFiles.ts @@ -21,7 +21,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const files = await bucket // 1 hours expired .find({ - uploadDate: { $lte: new Date(Date.now() - 60 * 1000) }, + uploadDate: { $lte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }, ['metadata.kbId']: kbId, ['metadata.userId']: userId }) diff --git a/client/src/pages/api/plugins/kb/file/list.ts b/client/src/pages/api/plugins/kb/file/list.ts index 2577213e4..722704a20 100644 --- a/client/src/pages/api/plugins/kb/file/list.ts +++ b/client/src/pages/api/plugins/kb/file/list.ts @@ -5,14 +5,19 @@ import { authUser } from '@/service/utils/auth'; import { GridFSStorage } from '@/service/lib/gridfs'; import { PgClient } from '@/service/pg'; import { PgTrainingTableName } from '@/constants/plugin'; -import { KbFileItemType } from '@/types/plugin'; import { FileStatusEnum, OtherFileId } from '@/constants/kb'; +import mongoose from 'mongoose'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - let { kbId, searchText } = req.query as { kbId: string; searchText: string }; + let { + pageNum = 1, + pageSize = 10, + kbId, + searchText + } = req.body as { pageNum: number; pageSize: number; kbId: string; searchText: string }; searchText = searchText.replace(/'/g, ''); // 凭证校验 @@ -21,10 +26,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const gridFs = new GridFSStorage('dataset', userId); const bucket = gridFs.GridFSBucket(); - const files = await bucket - .find({ ['metadata.kbId']: kbId, ...(searchText && { filename: { $regex: searchText } }) }) - .sort({ _id: -1 }) - .toArray(); + const mongoWhere = { + ['metadata.kbId']: kbId, + ...(searchText && { filename: { $regex: searchText } }) + }; + const [files, total] = await Promise.all([ + bucket + .find(mongoWhere) + .sort({ _id: -1 }) + .skip((pageNum - 1) * pageSize) + .limit(pageSize) + .toArray(), + mongoose.connection.db.collection('dataset.files').countDocuments(mongoWhere) + ]); async function GetOtherData() { return { @@ -72,8 +86,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }) ]); - jsonRes(res, { - data: data.flat().filter((item) => item.chunkLength > 0) + jsonRes(res, { + data: { + pageNum, + pageSize, + data: data.flat().filter((item) => item.chunkLength > 0), + total + } }); } catch (err) { jsonRes(res, { diff --git a/client/src/pages/api/chat/shareChat/create.ts b/client/src/pages/api/support/outLink/create.ts similarity index 84% rename from client/src/pages/api/chat/shareChat/create.ts rename to client/src/pages/api/support/outLink/create.ts index 51476e0bd..e8656ab77 100644 --- a/client/src/pages/api/chat/shareChat/create.ts +++ b/client/src/pages/api/support/outLink/create.ts @@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase, OutLink } from '@/service/mongo'; import { authApp, authUser } from '@/service/utils/auth'; -import type { ShareChatEditType } from '@/types/app'; +import type { OutLinkEditType } from '@/types/support/outLink'; import { customAlphabet } from 'nanoid'; import { OutLinkTypeEnum } from '@/constants/chat'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); @@ -10,8 +10,9 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); /* create a shareChat */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { appId, name } = req.body as ShareChatEditType & { + const { appId, ...props } = req.body as OutLinkEditType & { appId: string; + type: `${OutLinkTypeEnum}`; }; await connectToDatabase(); @@ -28,8 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) shareId, userId, appId, - name, - type: OutLinkTypeEnum.share + ...props }); jsonRes(res, { diff --git a/client/src/pages/api/chat/shareChat/delete.ts b/client/src/pages/api/support/outLink/delete.ts similarity index 100% rename from client/src/pages/api/chat/shareChat/delete.ts rename to client/src/pages/api/support/outLink/delete.ts index 081795850..d732ee3bb 100644 --- a/client/src/pages/api/chat/shareChat/delete.ts +++ b/client/src/pages/api/support/outLink/delete.ts @@ -6,12 +6,12 @@ import { authUser } from '@/service/utils/auth'; /* delete a shareChat by shareChatId */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { + await connectToDatabase(); + const { id } = req.query as { id: string; }; - await connectToDatabase(); - const { userId } = await authUser({ req, authToken: true }); await OutLink.findOneAndRemove({ diff --git a/client/src/pages/api/chat/shareChat/init.ts b/client/src/pages/api/support/outLink/init.ts similarity index 96% rename from client/src/pages/api/chat/shareChat/init.ts rename to client/src/pages/api/support/outLink/init.ts index d4867fc3e..01e364099 100644 --- a/client/src/pages/api/chat/shareChat/init.ts +++ b/client/src/pages/api/support/outLink/init.ts @@ -6,7 +6,7 @@ import { authApp } from '@/service/utils/auth'; import { HUMAN_ICON } from '@/constants/chat'; import { getChatModelNameList, getSpecialModule } from '@/components/ChatBox/utils'; -/* 初始化我的聊天框,需要身份验证 */ +/* init share chat window */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { let { shareId } = req.query as { diff --git a/client/src/pages/api/chat/shareChat/list.ts b/client/src/pages/api/support/outLink/list.ts similarity index 78% rename from client/src/pages/api/chat/shareChat/list.ts rename to client/src/pages/api/support/outLink/list.ts index b080ea8b4..81f232ab4 100644 --- a/client/src/pages/api/chat/shareChat/list.ts +++ b/client/src/pages/api/support/outLink/list.ts @@ -7,12 +7,12 @@ import { hashPassword } from '@/service/utils/tools'; /* get shareChat list by appId */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { + await connectToDatabase(); + const { appId } = req.query as { appId: string; }; - await connectToDatabase(); - const { userId } = await authUser({ req, authToken: true }); const data = await OutLink.find({ @@ -22,15 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) _id: -1 }); - jsonRes(res, { - data: data.map((item) => ({ - _id: item._id, - shareId: item.shareId, - name: item.name, - total: item.total, - lastTime: item.lastTime - })) - }); + jsonRes(res, { data }); } catch (err) { jsonRes(res, { code: 500, diff --git a/client/src/pages/api/support/outLink/update.ts b/client/src/pages/api/support/outLink/update.ts new file mode 100644 index 000000000..53a6a43b8 --- /dev/null +++ b/client/src/pages/api/support/outLink/update.ts @@ -0,0 +1,25 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@/service/response'; +import { connectToDatabase, OutLink } from '@/service/mongo'; +import type { OutLinkEditType } from '@/types/support/outLink'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + + const { _id, name, responseDetail, limit } = req.body as OutLinkEditType & {}; + + await OutLink.findByIdAndUpdate(_id, { + name, + responseDetail, + limit + }); + + jsonRes(res); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/client/src/pages/api/system/getInitData.ts b/client/src/pages/api/system/getInitData.ts index ca3f584bc..4a69eafa4 100644 --- a/client/src/pages/api/system/getInitData.ts +++ b/client/src/pages/api/system/getInitData.ts @@ -44,7 +44,9 @@ const defaultFeConfigs: FeConfigsType = { show_doc: true, systemTitle: 'FastGPT', authorText: 'Made by FastGPT Team.', - exportLimitMinutes: 0, + limit: { + exportLimitMinutes: 0 + }, scripts: [] }; const defaultChatModels = [ diff --git a/client/src/pages/app/detail/components/OutLink.tsx b/client/src/pages/app/detail/components/OutLink.tsx index e167af8f9..cb90ec9cc 100644 --- a/client/src/pages/app/detail/components/OutLink.tsx +++ b/client/src/pages/app/detail/components/OutLink.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import { Flex, Box, @@ -10,45 +10,46 @@ import { Th, Td, Tbody, - useDisclosure, ModalFooter, ModalBody, - FormControl, Input, - useTheme + useTheme, + Switch, + Menu, + MenuButton, + MenuList, + MenuItem } from '@chakra-ui/react'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; import MyIcon from '@/components/Icon'; import { useLoading } from '@/hooks/useLoading'; import { useQuery } from '@tanstack/react-query'; -import { getShareChatList, delShareChatById, createShareChat } from '@/api/chat'; +import { + getShareChatList, + delShareChatById, + createShareChat, + putShareChat +} from '@/api/support/outLink'; import { formatTimeToChatTime, useCopyData } from '@/utils/tools'; import { useForm } from 'react-hook-form'; -import { defaultShareChat } from '@/constants/model'; -import type { ShareChatEditType } from '@/types/app'; +import { defaultOutLinkForm } from '@/constants/model'; +import type { OutLinkEditType } from '@/types/support/outLink'; import { useRequest } from '@/hooks/useRequest'; import { formatPrice } from '@/utils/user'; +import { OutLinkTypeEnum } from '@/constants/chat'; +import { useTranslation } from 'react-i18next'; +import { useToast } from '@/hooks/useToast'; import MyTooltip from '@/components/MyTooltip'; import MyModal from '@/components/MyModal'; import MyRadio from '@/components/Radio'; +import dayjs from 'dayjs'; const Share = ({ appId }: { appId: string }) => { + const { t } = useTranslation(); const { Loading, setIsLoading } = useLoading(); const { copyData } = useCopyData(); - const { - isOpen: isOpenCreateShareChat, - onOpen: onOpenCreateShareChat, - onClose: onCloseCreateShareChat - } = useDisclosure(); - const { - register: registerShareChat, - getValues: getShareChatValues, - setValue: setShareChatValues, - handleSubmit: submitShareChat, - reset: resetShareChat - } = useForm({ - defaultValues: defaultShareChat - }); + const [editLinkData, setEditLinkData] = useState(); + const { toast } = useToast(); const { isFetching, @@ -56,22 +57,6 @@ const Share = ({ appId }: { appId: string }) => { refetch: refetchShareChatList } = useQuery(['initShareChatList', appId], () => getShareChatList(appId)); - const { mutate: onclickCreateShareChat, isLoading: creating } = useRequest({ - mutationFn: async (e: ShareChatEditType) => - createShareChat({ - ...e, - appId - }), - errorToast: '创建分享链接异常', - onSuccess(id) { - onCloseCreateShareChat(); - refetchShareChatList(); - const url = `${location.origin}/chat/share?shareId=${id}`; - copyData(url, '创建成功。已复制分享地址,可直接分享使用'); - resetShareChat(defaultShareChat); - } - }); - return ( @@ -79,7 +64,7 @@ const Share = ({ appId }: { appId: string }) => { 免登录窗口 @@ -94,7 +79,7 @@ const Share = ({ appId }: { appId: string }) => { title: '最多创建10组' } : {})} - onClick={onOpenCreateShareChat} + onClick={() => setEditLinkData(defaultOutLinkForm)} > 创建新链接 @@ -105,8 +90,14 @@ const Share = ({ appId }: { appId: string }) => { 名称 金额消耗 + <> + 金额限制 + IP限流(人/分钟) + + 返回详情 + 过期时间 最后使用时间 - 操作 + @@ -114,60 +105,90 @@ const Share = ({ appId }: { appId: string }) => { {item.name} {formatPrice(item.total)}元 + {item.limit && ( + <> + {item.limit?.credit > -1 ? `${item.limit?.credit}元` : '无限制'} + {item.limit?.QPM} + + )} + {item.responseDetail ? '✔' : '✖'} + + {item.limit?.expiredTime + ? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm') + : '-'} + {item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'} - - + { - const url = `${location.origin}/chat/share?shareId=${item.shareId}`; - const src = `${location.origin}/js/iframe.js`; - const script = ``; - copyData(script, '已复制嵌入 Script,可在应用 HTML 底部嵌入', 3000); - }} - /> - - - { - const url = `${location.origin}/chat/share?shareId=${item.shareId}`; - copyData(url, '已复制分享链接,可直接分享使用'); - }} - /> - - - { - setIsLoading(true); - try { - await delShareChatById(item._id); - refetchShareChatList(); - } catch (error) { - console.log(error); + borderRadius={'md'} + > + + + + + setEditLinkData({ + _id: item._id, + name: item.name, + responseDetail: item.responseDetail, + limit: item.limit + }) } - setIsLoading(false); - }} - /> - + py={[2, 3]} + > + + {t('common.Edit')} + + { + const url = `${location.origin}/chat/share?shareId=${item.shareId}`; + copyData(url, '已复制分享链接,可直接分享使用'); + }} + py={[2, 3]} + > + + {t('common.Copy')} + + { + const url = `${location.origin}/chat/share?shareId=${item.shareId}`; + const src = `${location.origin}/js/iframe.js`; + const script = ``; + copyData(script, '已复制嵌入 Script,可在应用 HTML 底部嵌入', 3000); + }} + py={[2, 3]} + > + + {t('outlink.Copy Iframe')} + + { + setIsLoading(true); + try { + await delShareChatById(item._id); + refetchShareChatList(); + } catch (error) { + console.log(error); + } + setIsLoading(false); + }} + py={[2, 3]} + > + + {t('common.Delete')} + + + ))} + {shareChatList.length === 0 && !isFetching && ( @@ -176,56 +197,185 @@ const Share = ({ appId }: { appId: string }) => { )} - {/* create shareChat modal */} - - - - - - 名称: - - - - - - - - - - - - + {!!editLinkData && ( + { + const url = `${location.origin}/chat/share?shareId=${id}`; + copyData(url, '创建成功。已复制分享地址,可直接分享使用'); + refetchShareChatList(); + setEditLinkData(undefined); + }} + onEdit={() => { + toast({ + status: 'success', + title: t('common.Update Successful') + }); + refetchShareChatList(); + setEditLinkData(undefined); + }} + onClose={() => setEditLinkData(undefined)} + /> + )} ); }; -enum LinkTypeEnum { - share = 'share', - iframe = 'iframe' +// edit link modal +export function EditLinkModal({ + appId, + type, + defaultData, + onClose, + onCreate, + onEdit +}: { + appId: string; + type: `${OutLinkTypeEnum}`; + defaultData: OutLinkEditType; + onClose: () => void; + onCreate: (id: string) => void; + onEdit: () => void; +}) { + const { t } = useTranslation(); + const isEdit = useMemo(() => !!defaultData._id, [defaultData]); + const titleMap = useRef({ + create: { + [OutLinkTypeEnum.share]: t('outlink.Create Share Window'), + [OutLinkTypeEnum.iframe]: t('outlink.Create Ifrme Window') + }, + edit: { + [OutLinkTypeEnum.share]: t('outlink.Edit Share Window'), + [OutLinkTypeEnum.iframe]: t('outlink.Edit Ifrme Link') + } + }); + const { + register, + setValue, + handleSubmit: submitShareChat + } = useForm({ + defaultValues: defaultData + }); + + const { mutate: onclickCreate, isLoading: creating } = useRequest({ + mutationFn: async (e: OutLinkEditType) => + createShareChat({ + ...e, + appId, + type + }), + errorToast: '创建链接异常', + onSuccess: onCreate + }); + const { mutate: onclickUpdate, isLoading: updating } = useRequest({ + mutationFn: (e: OutLinkEditType) => { + console.log(e); + return putShareChat(e); + }, + errorToast: '更新链接异常', + onSuccess: onEdit + }); + + return ( + {}} + title={isEdit ? titleMap.current.edit[type] : titleMap.current.create[type]} + > + + + {t('Name')}: + + + + + QPM: + + + + + + + + + {t('outlink.Max credit')}: + + + + + + + + + {t('common.Expired Time')}: + + { + setValue('limit.expiredTime', new Date(e.target.value)); + }} + /> + + + + {t('outlink.Response Detail')}: + + + + + + + + + + + + + + + ); } const OutLink = ({ appId }: { appId: string }) => { const theme = useTheme(); - const [linkType, setLinkType] = useState<`${LinkTypeEnum}`>(LinkTypeEnum.share); + const [linkType, setLinkType] = useState<`${OutLinkTypeEnum}`>(OutLinkTypeEnum.share); return ( @@ -241,21 +391,21 @@ const OutLink = ({ appId }: { appId: string }) => { icon: 'outlink_share', title: '免登录窗口', desc: '分享链接给其他用户,无需登录即可直接进行使用', - value: LinkTypeEnum.share + value: OutLinkTypeEnum.share } // { // icon: 'outlink_iframe', // title: '网页嵌入', // desc: '嵌入到已有网页中,右下角会生成对话按键', - // value: LinkTypeEnum.iframe + // value: OutLinkTypeEnum.iframe // } ]} value={linkType} - onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)} + onChange={(e) => setLinkType(e as `${OutLinkTypeEnum}`)} /> - {linkType === LinkTypeEnum.share && } + {linkType === OutLinkTypeEnum.share && } ); }; diff --git a/client/src/pages/chat/share.tsx b/client/src/pages/chat/share.tsx index d1d5c2bd8..e7f489b15 100644 --- a/client/src/pages/chat/share.tsx +++ b/client/src/pages/chat/share.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useRef } from 'react'; import Head from 'next/head'; import { useRouter } from 'next/router'; -import { initShareChatInfo } from '@/api/chat'; +import { initShareChatInfo } from '@/api/support/outLink'; import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react'; import { useToast } from '@/hooks/useToast'; import { useGlobalStore } from '@/store/global'; diff --git a/client/src/pages/kb/detail/components/DataCard.tsx b/client/src/pages/kb/detail/components/DataCard.tsx index 465b80656..3e23a1f1d 100644 --- a/client/src/pages/kb/detail/components/DataCard.tsx +++ b/client/src/pages/kb/detail/components/DataCard.tsx @@ -65,22 +65,6 @@ const DataCard = ({ kbId }: { kbId: string }) => { const [editInputData, setEditInputData] = useState(); - const { data: { qaListLen = 0, vectorListLen = 0 } = {}, refetch: refetchTrainingData } = - useQuery(['getModelSplitDataList', kbId], () => getTrainingData({ kbId, init: false }), { - onError(err) { - console.log(err); - } - }); - - const refetchData = useCallback( - (num = pageNum) => { - getData(num); - refetchTrainingData(); - return null; - }, - [getData, pageNum, refetchTrainingData] - ); - // get first page data const getFirstData = useCallback( debounce(() => { @@ -90,12 +74,6 @@ const DataCard = ({ kbId }: { kbId: string }) => { [] ); - // interval get data - useQuery(['refetchData'], () => refetchData(1), { - refetchInterval: 5000, - enabled: qaListLen > 0 || vectorListLen > 0 - }); - // get file info const { data: fileInfo } = useQuery(['getFileInfo', fileId], () => getFileInfoById(fileId)); const fileIcon = useMemo( @@ -122,7 +100,7 @@ const DataCard = ({ kbId }: { kbId: string }) => { filename }); }, - successToast: `导出成功,下次导出需要 ${feConfigs.exportLimitMinutes} 分钟后`, + successToast: `导出成功,下次导出需要 ${feConfigs?.limit?.exportLimitMinutes} 分钟后`, errorToast: '导出异常' }); @@ -136,7 +114,7 @@ const DataCard = ({ kbId }: { kbId: string }) => { fontSize={['sm', 'md']} alignItems={'center'} > - {''} + {''} {t(fileInfo?.filename || 'Filename')} {feConfigs?.show_register && ( <> - - + + {oAuthList.map((item) => ( + { + setLoginStore({ + provider: item.provider, + lastRoute, + state: state.current + }); + router.replace(item.redirectUrl, '_self'); + }} + /> + ))} )} diff --git a/client/src/pages/login/provider.tsx b/client/src/pages/login/provider.tsx index cd3ad706a..6f17518d8 100644 --- a/client/src/pages/login/provider.tsx +++ b/client/src/pages/login/provider.tsx @@ -5,14 +5,14 @@ import { ResLogin } from '@/api/response/user'; import { useChatStore } from '@/store/chat'; import { useUserStore } from '@/store/user'; import { setToken } from '@/utils/user'; -import { gitLogin } from '@/api/user'; +import { oauthLogin } from '@/api/user'; import { useToast } from '@/hooks/useToast'; import Loading from '@/components/Loading'; import { serviceSideProps } from '@/utils/i18n'; import { useQuery } from '@tanstack/react-query'; import { getErrText } from '@/utils/tools'; -const provider = ({ code }: { code: string }) => { +const provider = ({ code, state }: { code: string; state: string }) => { const { loginStore } = useGlobalStore(); const { setLastChatId, setLastChatAppId } = useChatStore(); const { setUserInfo } = useUserStore(); @@ -36,45 +36,55 @@ const provider = ({ code }: { code: string }) => { [setLastChatId, setLastChatAppId, setUserInfo, router, loginStore?.lastRoute] ); - const authCode = useCallback(async () => { - if (!code) return; - if (!loginStore) { - router.replace('/login'); - return; - } - try { - const res = await (async () => { - if (loginStore.provider === 'git') { - return gitLogin({ - code, - inviterId: localStorage.getItem('inviterId') || undefined + const authCode = useCallback( + async (code: string) => { + if (!loginStore) { + router.replace('/login'); + return; + } + try { + const res = await oauthLogin({ + type: loginStore?.provider, + code, + callbackUrl: `${location.origin}/login/provider`, + inviterId: localStorage.getItem('inviterId') || undefined + }); + if (!res) { + toast({ + status: 'warning', + title: '登录异常' }); + return setTimeout(() => { + router.replace('/login'); + }, 1000); } - return null; - })(); - if (!res) { + loginSuccess(res); + } catch (error) { toast({ status: 'warning', - title: '登录异常' + title: getErrText(error, '登录异常') }); - return setTimeout(() => { + setTimeout(() => { router.replace('/login'); }, 1000); } - loginSuccess(res); - } catch (error) { + }, + [loginStore, loginSuccess, router, toast] + ); + + useQuery(['init', code], () => { + if (!code) return; + if (state !== loginStore?.state) { toast({ status: 'warning', - title: getErrText(error, '登录异常') + title: '安全校验失败' }); setTimeout(() => { router.replace('/login'); }, 1000); + return; } - }, [code, loginStore, loginSuccess]); - - useQuery(['init', code], () => { - authCode(); + authCode(code); return null; }); @@ -85,6 +95,7 @@ export async function getServerSideProps(content: any) { return { props: { code: content?.query?.code, + state: content?.query?.state, ...(await serviceSideProps(content)) } }; diff --git a/client/src/service/common/ipLimit/schema.ts b/client/src/service/common/ipLimit/schema.ts new file mode 100644 index 000000000..730c0df68 --- /dev/null +++ b/client/src/service/common/ipLimit/schema.ts @@ -0,0 +1,24 @@ +import { Schema, model, models, Model } from 'mongoose'; +import type { IpLimitSchemaType } from '@/types/common/ipLimit'; + +const IpLimitSchema = new Schema({ + eventId: { + type: String, + required: true + }, + ip: { + type: String, + required: true + }, + account: { + type: Number, + default: 0 + }, + lastMinute: { + type: Date, + default: () => new Date() + } +}); + +export const IpLimit: Model = + models['ip_limit'] || model('ip_limit', IpLimitSchema); diff --git a/client/src/service/mongo.ts b/client/src/service/mongo.ts index f623d6208..c1f11c407 100644 --- a/client/src/service/mongo.ts +++ b/client/src/service/mongo.ts @@ -133,7 +133,7 @@ export * from './models/trainingData'; export * from './models/openapi'; export * from './models/promotionRecord'; export * from './models/collection'; -export * from './models/outLink'; export * from './models/kb'; export * from './models/inform'; export * from './models/image'; +export * from './support/outLink/schema'; diff --git a/client/src/service/support/outLink/auth.ts b/client/src/service/support/outLink/auth.ts new file mode 100644 index 000000000..6867af1bd --- /dev/null +++ b/client/src/service/support/outLink/auth.ts @@ -0,0 +1,79 @@ +import { PRICE_SCALE } from '@/constants/common'; +import { IpLimit } from '@/service/common/ipLimit/schema'; +import { authBalanceByUid, AuthUserTypeEnum } from '@/service/utils/auth'; +import { OutLinkSchema } from '@/types/support/outLink'; +import { OutLink } from './schema'; + +export async function authOutLinkChat({ shareId, ip }: { shareId: string; ip?: string | null }) { + // get outLink + const outLink = await OutLink.findOne({ + shareId + }); + + if (!outLink) { + return Promise.reject('分享链接无效'); + } + + const uid = String(outLink.userId); + + // authBalance + const user = await authBalanceByUid(uid); + + // limit auth + await authOutLinkLimit({ outLink, ip }); + + return { + user, + userId: String(outLink.userId), + appId: String(outLink.appId), + authType: AuthUserTypeEnum.token, + responseDetail: outLink.responseDetail + }; +} + +export async function authOutLinkLimit({ + outLink, + ip +}: { + outLink: OutLinkSchema; + ip?: string | null; +}) { + if (!ip || !outLink.limit) { + return; + } + + if (outLink.limit.expiredTime && outLink.limit.expiredTime.getTime() < Date.now()) { + return Promise.reject('分享链接已过期'); + } + + if (outLink.limit.credit > -1 && outLink.total > outLink.limit.credit * PRICE_SCALE) { + return Promise.reject('链接超出使用限制'); + } + + const ipLimit = await IpLimit.findOne({ ip, eventId: outLink._id }); + + try { + if (!ipLimit) { + await IpLimit.create({ + eventId: outLink._id, + ip, + account: outLink.limit.QPM - 1 + }); + return; + } + // over one minute + const diffTime = Date.now() - ipLimit.lastMinute.getTime(); + if (diffTime >= 60 * 1000) { + ipLimit.account = outLink.limit.QPM - 1; + ipLimit.lastMinute = new Date(); + return await ipLimit.save(); + } + if (ipLimit.account <= 0) { + return Promise.reject( + `每分钟仅能请求 ${outLink.limit.QPM} 次, ${60 - Math.round(diffTime / 1000)}s 后重试~` + ); + } + ipLimit.account = ipLimit.account - 1; + await ipLimit.save(); + } catch (error) {} +} diff --git a/client/src/service/models/outLink.ts b/client/src/service/support/outLink/schema.ts similarity index 57% rename from client/src/service/models/outLink.ts rename to client/src/service/support/outLink/schema.ts index ba46ac7d4..ce8f8837a 100644 --- a/client/src/service/models/outLink.ts +++ b/client/src/service/support/outLink/schema.ts @@ -1,5 +1,5 @@ import { Schema, model, models, Model } from 'mongoose'; -import { OutLinkSchema as SchmaType } from '@/types/mongoSchema'; +import { OutLinkSchema as SchemaType } from '@/types/support/outLink'; import { OutLinkTypeEnum } from '@/constants/chat'; const OutLinkSchema = new Schema({ @@ -26,12 +26,30 @@ const OutLinkSchema = new Schema({ required: true }, total: { + // total amount type: Number, default: 0 }, lastTime: { type: Date + }, + responseDetail: { + type: Boolean, + default: false + }, + limit: { + expiredTime: { + type: Date + }, + QPM: { + type: Number, + default: 1000 + }, + credit: { + type: Number, + default: -1 + } } }); -export const OutLink: Model = models['outlinks'] || model('outlinks', OutLinkSchema); +export const OutLink: Model = models['outlinks'] || model('outlinks', OutLinkSchema); diff --git a/client/src/service/utils/auth.ts b/client/src/service/utils/auth.ts index 8c08486d4..64870952a 100644 --- a/client/src/service/utils/auth.ts +++ b/client/src/service/utils/auth.ts @@ -208,24 +208,3 @@ export const authKb = async ({ kbId, userId }: { kbId: string; userId: string }) } return Promise.reject(ERROR_ENUM.unAuthKb); }; - -export const authShareChat = async ({ shareId }: { shareId: string }) => { - // get shareChat - const shareChat = await OutLink.findOne({ shareId }); - - if (!shareChat) { - return Promise.reject('分享链接已失效'); - } - - const uid = String(shareChat.userId); - - // authBalance - const user = await authBalanceByUid(uid); - - return { - user, - userId: String(shareChat.userId), - appId: String(shareChat.appId), - authType: AuthUserTypeEnum.token - }; -}; diff --git a/client/src/store/global.ts b/client/src/store/global.ts index 964abeaab..ebb0493c9 100644 --- a/client/src/store/global.ts +++ b/client/src/store/global.ts @@ -2,8 +2,9 @@ import { create } from 'zustand'; import { devtools, persist } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; import axios from 'axios'; +import { OAuthEnum } from '@/constants/user'; -type LoginStoreType = { provider: 'git'; lastRoute: string }; +type LoginStoreType = { provider: `${OAuthEnum}`; lastRoute: string; state: string }; type State = { lastRoute: string; diff --git a/client/src/types/app.d.ts b/client/src/types/app.d.ts index 916a84005..6c5b87c4b 100644 --- a/client/src/types/app.d.ts +++ b/client/src/types/app.d.ts @@ -38,10 +38,6 @@ export interface ShareAppItem { isCollection: boolean; } -export type ShareChatEditType = { - name: string; -}; - /* agent */ /* question classify */ export type ClassifyQuestionAgentItemType = { diff --git a/client/src/types/common/ipLimit.d.ts b/client/src/types/common/ipLimit.d.ts new file mode 100644 index 000000000..95b09ee62 --- /dev/null +++ b/client/src/types/common/ipLimit.d.ts @@ -0,0 +1,7 @@ +export type IpLimitSchemaType = { + _id: string; + eventId: string; + ip: string; + account: number; + lastMinute: Date; +}; diff --git a/client/src/types/index.d.ts b/client/src/types/index.d.ts index ede3ae52a..6becaed3a 100644 --- a/client/src/types/index.d.ts +++ b/client/src/types/index.d.ts @@ -27,8 +27,13 @@ export type FeConfigsType = { authorText?: string; beianText?: string; googleClientVerKey?: string; - gitLoginKey?: string; - exportLimitMinutes?: number; + oauth?: { + github?: string; + google?: string; + }; + limit?: { + exportLimitMinutes?: number; + }; scripts?: { [key: string]: string }[]; }; export type SystemEnvType = { diff --git a/client/src/types/mongoSchema.d.ts b/client/src/types/mongoSchema.d.ts index 38840e043..08be6ca68 100644 --- a/client/src/types/mongoSchema.d.ts +++ b/client/src/types/mongoSchema.d.ts @@ -4,7 +4,7 @@ import type { DataType } from './data'; import { BillSourceEnum, InformTypeEnum } from '@/constants/user'; import { TrainingModeEnum } from '@/constants/plugin'; import type { AppModuleItemType } from './app'; -import { ChatSourceEnum, OutLinkTypeEnum } from '@/constants/chat'; +import { ChatSourceEnum } from '@/constants/chat'; import { AppTypeEnum } from '@/constants/app'; import { KbTypeEnum } from '@/constants/kb'; @@ -156,17 +156,6 @@ export interface PromotionRecordSchema { amount: number; } -export interface OutLinkSchema { - _id: string; - shareId: string; - userId: string; - appId: string; - name: string; - total: number; - lastTime: Date; - type: `${OutLinkTypeEnum}`; -} - export type kbSchema = { _id: string; userId: string; diff --git a/client/src/types/support/outLink.d.ts b/client/src/types/support/outLink.d.ts new file mode 100644 index 000000000..4617da531 --- /dev/null +++ b/client/src/types/support/outLink.d.ts @@ -0,0 +1,25 @@ +import { OutLinkTypeEnum } from '@/constants/chat'; + +export interface OutLinkSchema { + _id: string; + shareId: string; + userId: string; + appId: string; + name: string; + total: number; + lastTime: Date; + type: `${OutLinkTypeEnum}`; + responseDetail: boolean; + limit?: { + expiredTime?: Date; + QPM: number; + credit: number; + }; +} + +export type OutLinkEditType = { + _id?: string; + name: string; + responseDetail: OutLinkSchema['responseDetail']; + limit: OutLinkSchema['limit']; +}; diff --git a/docSite/content/docs/development/configuration.md b/docSite/content/docs/development/configuration.md index 3243ed9d7..fc88e6cb1 100644 --- a/docSite/content/docs/development/configuration.md +++ b/docSite/content/docs/development/configuration.md @@ -31,7 +31,6 @@ weight: 520 "show_git": true, // 是否展示 Git "systemTitle": "FastGPT", // 系统的 title "authorText": "Made by FastGPT Team.", // 签名 - "gitLoginKey": "" // Git 登录凭证 }, ... ... @@ -56,7 +55,6 @@ weight: 520 "show_git": true, "systemTitle": "FastGPT", "authorText": "Made by FastGPT Team.", - "gitLoginKey": "", "scripts": [] }, "SystemParams": { diff --git a/files/deploy/fastgpt/config.json b/files/deploy/fastgpt/config.json index 2b6b5cd6e..49330988c 100644 --- a/files/deploy/fastgpt/config.json +++ b/files/deploy/fastgpt/config.json @@ -9,7 +9,6 @@ "show_doc": true, "systemTitle": "FastGPT", "authorText": "Made by FastGPT Team.", - "gitLoginKey": "", "scripts": [] }, "SystemParams": {