v4.6.9-alpha (#918)

Co-authored-by: Mufei <327958099@qq.com>
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-03-04 00:05:25 +08:00
committed by GitHub
parent f9f0b4bffd
commit 42a8184ea0
153 changed files with 4906 additions and 4307 deletions

View File

@@ -18,31 +18,28 @@ import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink'
import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
import requestIp from 'request-ip';
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
import { authTeamShareChatStart } from '@/service/support/permission/auth/teamChat';
import { selectShareResponse } from '@/utils/service/core/chat';
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
import { selectSimpleChatResponse } from '@/utils/service/core/chat';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { connectToDatabase } from '@/service/mongo';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { autChatCrud } from '@/service/support/permission/auth/chat';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { AppSchema } from '@fastgpt/global/core/app/type';
import { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
type FastGptWebChatProps = {
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
appId?: string;
};
type FastGptShareChatProps = {
shareId?: string;
outLinkUid?: string;
};
type FastGptTeamShareChatProps = {
shareTeamId?: string;
outLinkUid?: string;
};
export type Props = ChatCompletionCreateParams &
FastGptWebChatProps &
FastGptShareChatProps &
FastGptTeamShareChatProps & {
OutLinkChatAuthProps & {
messages: ChatMessageItemType[];
stream?: boolean;
detail?: boolean;
@@ -53,6 +50,18 @@ export type ChatResponseType = {
quoteLen?: number;
};
type AuthResponseType = {
teamId: string;
tmbId: string;
user: UserModelSchema;
app: AppSchema;
responseDetail?: boolean;
authType: `${AuthUserTypeEnum}`;
apikey?: string;
canWrite: boolean;
outLinkUserId?: string;
};
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('close', () => {
res.end();
@@ -65,9 +74,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
const {
chatId,
appId,
shareTeamId,
// share chat
shareId,
outLinkUid,
// team chat
teamId: spaceTeamId,
teamToken,
stream = false,
detail = false,
messages = [],
@@ -100,136 +112,44 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (!question) {
throw new Error('Question is empty');
}
/* auth app permission */
/*
1. auth app permission
2. auth balance
3. get app
4. parse outLink token
*/
const { teamId, tmbId, user, app, responseDetail, authType, apikey, canWrite, outLinkUserId } =
await (async () => {
// share chat
if (shareId && outLinkUid) {
const { teamId, tmbId, user, appId, authType, responseDetail, uid } =
await authOutLinkChatStart({
shareId,
ip: originIp,
outLinkUid,
question: question.value
});
const app = await MongoApp.findById(appId);
if (!app) {
return Promise.reject('app is empty');
}
return {
teamId,
tmbId,
user,
app,
responseDetail,
apikey: '',
authType,
canWrite: false,
outLinkUserId: uid
};
}
// team Apps share
if (shareTeamId && appId && outLinkUid) {
const { user, uid, tmbId } = await authTeamShareChatStart({
teamId: shareTeamId,
ip: originIp,
return authShareChat({
shareId,
outLinkUid,
chatId,
ip: originIp,
question: question.value
});
const app = await MongoApp.findById(appId);
if (!app) {
return Promise.reject('app is empty');
}
return {
teamId: shareTeamId,
tmbId,
user,
app,
responseDetail: detail,
authType: AuthUserTypeEnum.token,
apikey: '',
canWrite: false,
outLinkUserId: uid
};
}
// team space chat
if (spaceTeamId && appId && teamToken) {
return authTeamSpaceChat({
teamId: spaceTeamId,
teamToken,
appId,
chatId
});
}
const {
appId: apiKeyAppId,
teamId,
tmbId,
authType,
apikey
} = await authCert({
/* parse req: api or token */
return authHeaderRequest({
req,
authToken: true,
authApiKey: true
});
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
// openapi key
if (authType === AuthUserTypeEnum.apikey) {
if (!apiKeyAppId) {
return Promise.reject(
'Key is error. You need to use the app key rather than the account key.'
);
}
const app = await MongoApp.findById(apiKeyAppId);
if (!app) {
return Promise.reject('app is empty');
}
return {
teamId,
tmbId,
user,
app,
responseDetail: detail,
apikey,
authType,
canWrite: true
};
}
// token auth
if (!appId) {
return Promise.reject('appId is empty');
}
const { app, canWrite } = await authApp({
req,
authToken: true,
appId,
per: 'r'
chatId,
detail
});
return {
teamId,
tmbId,
user,
app,
responseDetail: detail,
apikey,
authType,
canWrite: canWrite || false
};
})();
// auth chat permission
await autChatCrud({
req,
authToken: true,
authApiKey: true,
appId: app._id,
chatId,
shareId,
shareTeamId,
outLinkUid,
per: 'w'
});
// get and concat history
const { history } = await getChatItems({
appId: app._id,
@@ -237,7 +157,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
limit: 30,
field: `dataId obj value`
});
const concatHistories = history.concat(chatMessages);
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
@@ -263,13 +182,14 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// save chat
if (chatId) {
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
await saveChat({
chatId,
appId: app._id,
teamId,
tmbId: tmbId,
variables,
updateUseTime: !shareId && String(tmbId) === String(app.tmbId), // owner update use time
updateUseTime: isOwnerUse, // owner update use time
shareId,
outLinkUid: outLinkUserId,
source: (() => {
@@ -279,6 +199,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (authType === 'apikey') {
return ChatSourceEnum.api;
}
if (spaceTeamId) {
return ChatSourceEnum.team;
}
return ChatSourceEnum.online;
})(),
content: [
@@ -299,7 +222,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
/* select fe response field */
const feResponseData = canWrite ? responseData : selectShareResponse({ responseData });
const feResponseData = canWrite ? responseData : selectSimpleChatResponse({ responseData });
if (stream) {
responseWrite({
@@ -382,3 +305,162 @@ export const config = {
responseLimit: '20mb'
}
};
const authShareChat = async ({
chatId,
...data
}: AuthOutLinkChatProps & {
shareId: string;
chatId?: string;
}): Promise<AuthResponseType> => {
const { teamId, tmbId, user, appId, authType, responseDetail, uid } =
await authOutLinkChatStart(data);
const app = await MongoApp.findById(appId).lean();
if (!app) {
return Promise.reject('app is empty');
}
// get chat
const chat = await MongoChat.findOne({ appId, chatId }).lean();
if (chat && (chat.shareId !== data.shareId || chat.outLinkUid !== uid)) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
return {
teamId,
tmbId,
user,
app,
responseDetail,
apikey: '',
authType,
canWrite: false,
outLinkUserId: uid
};
};
const authTeamSpaceChat = async ({
appId,
teamId,
teamToken,
chatId
}: {
appId: string;
teamId: string;
teamToken: string;
chatId?: string;
}): Promise<AuthResponseType> => {
const { uid } = await authTeamSpaceToken({
teamId,
teamToken
});
const app = await MongoApp.findById(appId).lean();
if (!app) {
return Promise.reject('app is empty');
}
const [chat, { user }] = await Promise.all([
MongoChat.findOne({ appId, chatId }).lean(),
getUserChatInfoAndAuthTeamPoints(app.tmbId)
]);
if (chat && (String(chat.teamId) !== teamId || chat.outLinkUid !== uid)) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
return {
teamId,
tmbId: app.tmbId,
user,
app,
responseDetail: true,
authType: AuthUserTypeEnum.outLink,
apikey: '',
canWrite: false,
outLinkUserId: uid
};
};
const authHeaderRequest = async ({
req,
appId,
chatId,
detail
}: {
req: NextApiRequest;
appId?: string;
chatId?: string;
detail?: boolean;
}): Promise<AuthResponseType> => {
const {
appId: apiKeyAppId,
teamId,
tmbId,
authType,
apikey,
canWrite: apiKeyCanWrite
} = await authCert({
req,
authToken: true,
authApiKey: true
});
const { app, canWrite } = await (async () => {
if (authType === AuthUserTypeEnum.apikey) {
if (!apiKeyAppId) {
return Promise.reject(
'Key is error. You need to use the app key rather than the account key.'
);
}
const app = await MongoApp.findById(apiKeyAppId);
if (!app) {
return Promise.reject('app is empty');
}
appId = String(app._id);
return {
app,
canWrite: apiKeyCanWrite
};
} else {
// token auth
if (!appId) {
return Promise.reject('appId is empty');
}
const { app, canWrite } = await authApp({
req,
authToken: true,
appId,
per: 'r'
});
return {
app,
canWrite: canWrite
};
}
})();
const [{ user }, chat] = await Promise.all([
getUserChatInfoAndAuthTeamPoints(tmbId),
MongoChat.findOne({ appId, chatId }).lean()
]);
if (chat && (String(chat.teamId) !== teamId || String(chat.tmbId) !== tmbId)) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
return {
teamId,
tmbId,
user,
app,
responseDetail: detail,
apikey,
authType,
canWrite
};
};