Add share link hook (#351)

This commit is contained in:
Archer
2023-09-25 23:12:42 +08:00
committed by GitHub
parent 9136c9306a
commit 63cd379682
29 changed files with 430 additions and 98 deletions

View File

@@ -40,7 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase();
/* user auth */
const { userId, user } = await authUser({ req, authBalance: true });
const { userId, user } = await authUser({ req, authToken: true, authBalance: true });
if (!user) {
throw new Error('user not found');

View File

@@ -9,7 +9,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
try {
const { name } = req.body as CreateTrainingBillType;
const { userId } = await authUser({ req, authToken: true });
const { userId } = await authUser({ req, authToken: true, authApiKey: true });
await connectToDatabase();

View File

@@ -16,7 +16,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
await PgClient.delete(PgDatasetTableName, {
where: [['user_id', userId], 'AND', ['id', dataId]]

View File

@@ -21,7 +21,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await connectToDatabase();
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
jsonRes(res, {
data: await getVectorAndInsertDataset({

View File

@@ -36,7 +36,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await connectToDatabase();
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true, authApiKey: true });
jsonRes<PushDataResponse>(res, {
data: await pushDataToKb({

View File

@@ -20,7 +20,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// auth user and get kb
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
authUser({ req, authToken: true }),
KB.findById(kbId, 'vectorModel')
]);

View File

@@ -18,7 +18,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// 凭证校验
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
authUser({ req, authToken: true, authApiKey: true }),
KB.findById(kbId, 'vectorModel')
]);

View File

@@ -17,7 +17,7 @@ type Response = {
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
let { input, model } = req.query as Props;
if (!Array.isArray(input)) {

View File

@@ -34,7 +34,7 @@ import requestIp from 'request-ip';
import { replaceVariable } from '@/utils/common/tools/text';
import { ModuleDispatchProps } from '@/types/core/modules';
import { selectShareResponse } from '@/utils/service/core/chat';
import { updateOutLinkUsage } from '@/service/support/outLink';
import { pushResult2Remote, updateOutLinkUsage } from '@/service/support/outLink';
import { updateApiKeyUsage } from '@/service/support/openapi';
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
@@ -44,6 +44,7 @@ type FastGptWebChatProps = {
};
type FastGptShareChatProps = {
shareId?: string;
authToken?: string;
};
export type Props = CreateChatCompletionRequest &
FastGptWebChatProps &
@@ -71,6 +72,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
chatId,
appId,
shareId,
authToken,
stream = false,
detail = false,
messages = [],
@@ -111,10 +113,15 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (shareId) {
return authOutLinkChat({
shareId,
ip: requestIp.getClientIp(req)
ip: requestIp.getClientIp(req),
authToken,
question:
(messages[messages.length - 2]?.role === 'user'
? messages[messages.length - 2].content
: messages[messages.length - 1]?.content) || ''
});
}
return authUser({ req, authBalance: true });
return authUser({ req, authToken: true, authApiKey: true, authBalance: true });
})();
if (!user) {
@@ -260,11 +267,13 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
response: responseData
});
!!shareId &&
if (shareId) {
pushResult2Remote({ authToken, shareId, responseData });
updateOutLinkUsage({
shareId,
total
});
}
!!apikey &&
updateApiKeyUsage({
apikey,

View File

@@ -16,7 +16,7 @@ export type Response = { history: ChatItemType[] };
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
const { chatId, limit } = req.body as Props;
jsonRes<Response>(res, {

View File

@@ -18,7 +18,7 @@ const fetchContent = async (req: NextApiRequest, res: NextApiResponse) => {
throw new Error('urlList is empty');
}
await authUser({ req });
await authUser({ req, authToken: true });
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));

View File

@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
const gridFs = new GridFSStorage('dataset', userId);

View File

@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
// auth file
const gridFs = new GridFSStorage('dataset', userId);

View File

@@ -5,12 +5,14 @@ import type { InitShareChatResponse } from '@/api/response/chat';
import { authApp } from '@/service/utils/auth';
import { HUMAN_ICON } from '@/constants/chat';
import { getChatModelNameList, getSpecialModule } from '@/components/ChatBox/utils';
import { authShareChatInit } from '@/service/support/outLink/auth';
/* init share chat window */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
let { shareId } = req.query as {
let { shareId, authToken } = req.query as {
shareId: string;
authToken?: string;
};
if (!shareId) {
@@ -36,7 +38,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
userId: String(shareChat.userId),
authOwner: false
}),
User.findById(shareChat.userId, 'avatar')
User.findById(shareChat.userId, 'avatar'),
authShareChatInit(authToken, shareChat.limit?.hookUrl)
]);
jsonRes<InitShareChatResponse>(res, {

View File

@@ -17,7 +17,8 @@ import {
Menu,
MenuButton,
MenuList,
MenuItem
MenuItem,
Link
} from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyIcon from '@/components/Icon';
@@ -58,7 +59,7 @@ const Share = ({ appId }: { appId: string }) => {
} = useQuery(['initShareChatList', appId], () => getShareChatList(appId));
return (
<Box position={'relative'} pt={[3, 5, 8]} px={[5, 8]} minH={'50vh'}>
<Box position={'relative'} pt={[3, 5, 8]} px={[2, 8]} minH={'50vh'}>
<Flex justifyContent={'space-between'}>
<Box fontWeight={'bold'}>
@@ -85,7 +86,7 @@ const Share = ({ appId }: { appId: string }) => {
</Button>
</Flex>
<TableContainer mt={3}>
<Table variant={'simple'} w={'100%'} overflowX={'auto'}>
<Table variant={'simple'} w={'100%'} overflowX={'auto'} fontSize={'sm'}>
<Thead>
<Tr>
<Th></Th>
@@ -96,6 +97,7 @@ const Share = ({ appId }: { appId: string }) => {
<Th>()</Th>
<Th>IP限流/</Th>
<Th></Th>
<Th>token校验</Th>
</>
)}
<Th>使</Th>
@@ -113,12 +115,13 @@ const Share = ({ appId }: { appId: string }) => {
<Td>
{item.limit && item.limit.credit > -1 ? `${item.limit.credit}` : '无限制'}
</Td>
<Td>{item.limit?.QPM || '-'}</Td>
<Td>{item?.limit?.QPM || '-'}</Td>
<Td>
{item.limit?.expiredTime
{item?.limit?.expiredTime
? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
: '-'}
</Td>
<Th>{item?.limit?.hookUrl ? '✔' : '✖'}</Th>
</>
)}
<Td>{item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'}</Td>
@@ -267,7 +270,6 @@ function EditLinkModal({
});
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
mutationFn: (e: OutLinkEditType) => {
console.log(e);
return putShareChat(e);
},
errorToast: '更新链接异常',
@@ -338,6 +340,26 @@ function EditLinkModal({
}}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'}>
{t('outlink.token auth')}
<MyTooltip label={t('outlink.token auth Tips') || ''}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Input
placeholder={t('outlink.token auth Tips') || ''}
{...register('limit.hookUrl')}
/>
</Flex>
<Link
href="https://doc.fastgpt.run/docs/development/openapi/#分享链接中增加额外-query"
target={'_blank'}
fontSize={'sm'}
color={'myGray.500'}
>
{t('outlink.token auth use cases')}
</Link>
</>
)}

View File

@@ -21,11 +21,20 @@ import ChatHeader from './components/ChatHeader';
import ChatHistorySlider from './components/ChatHistorySlider';
import { serviceSideProps } from '@/utils/web/i18n';
const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
const OutLink = ({
shareId,
chatId,
authToken
}: {
shareId: string;
chatId: string;
authToken?: string;
}) => {
const router = useRouter();
const { toast } = useToast();
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
const { isPc } = useGlobalStore();
const forbidRefresh = useRef(false);
const ChatBoxRef = useRef<ComponentRef>(null);
@@ -53,7 +62,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
messages: prompts,
variables,
shareId,
chatId: completionChatId
chatId: completionChatId,
authToken
},
onMessage: generatingMessage,
abortSignal: controller
@@ -75,10 +85,12 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
});
if (completionChatId !== chatId && controller.signal.reason !== 'leave') {
forbidRefresh.current = true;
router.replace({
query: {
shareId,
chatId: completionChatId
chatId: completionChatId,
authToken
}
});
}
@@ -96,11 +108,11 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
return { responseText, responseData };
},
[chatId, router, saveChatResponse, shareId]
[authToken, chatId, router, saveChatResponse, shareId]
);
const loadAppInfo = useCallback(
async (shareId: string, chatId: string) => {
async (shareId: string, chatId: string, authToken?: string) => {
if (!shareId) return null;
const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
@@ -111,7 +123,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
const chatData = await (async () => {
if (shareChatData.app.name === '') {
return initShareChatInfo({
shareId
shareId,
authToken
});
}
return shareChatData;
@@ -142,8 +155,12 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
[delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast]
);
useQuery(['init', shareId, chatId], () => {
return loadAppInfo(shareId, chatId);
useQuery(['init', shareId, chatId, authToken], () => {
if (forbidRefresh.current) {
forbidRefresh.current = false;
return null;
}
return loadAppInfo(shareId, chatId, authToken);
});
return (
@@ -185,7 +202,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
router.replace({
query: {
chatId: chatId || '',
shareId
shareId,
authToken
}
});
if (!isPc) {
@@ -197,7 +215,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
delManyShareChatHistoryByShareId(shareId);
router.replace({
query: {
shareId
shareId,
authToken
}
});
}}
@@ -222,6 +241,7 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
{/* chat box */}
<Box flex={1}>
<ChatBox
active={!!shareChatData.app.name}
ref={ChatBoxRef}
appAvatar={shareChatData.app.avatar}
userAvatar={shareChatData.userAvatar}
@@ -252,9 +272,10 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
export async function getServerSideProps(context: any) {
const shareId = context?.query?.shareId || '';
const chatId = context?.query?.chatId || '';
const authToken = context?.query?.authToken || '';
return {
props: { shareId, chatId, ...(await serviceSideProps(context)) }
props: { shareId, chatId, authToken, ...(await serviceSideProps(context)) }
};
}