4.6.4-alpha (#569)

This commit is contained in:
Archer
2023-12-07 13:43:08 +08:00
committed by GitHub
parent 71afe71192
commit e01c38efe0
80 changed files with 1401 additions and 1109 deletions

View File

@@ -4,7 +4,9 @@ import { ErrType } from '../errorCode';
export enum OutLinkErrEnum {
unExist = 'unExist',
unAuthLink = 'unAuthLink',
linkUnInvalid = 'linkUnInvalid'
linkUnInvalid = 'linkUnInvalid',
unAuthUser = 'unAuthUser'
}
const errList = [
{
@@ -19,6 +21,10 @@ const errList = [
code: 501,
statusText: OutLinkErrEnum.linkUnInvalid,
message: '分享链接无效'
},
{
statusText: OutLinkErrEnum.unAuthUser,
message: '身份校验失败'
}
];
export default errList.reduce((acc, cur, index) => {

View File

@@ -1,33 +0,0 @@
import { ModuleItemType } from '../module/type';
import { AdminFbkType, ChatItemType, moduleDispatchResType } from './type';
export type UpdateHistoryProps = {
chatId: string;
customTitle?: string;
top?: boolean;
};
export type AdminUpdateFeedbackParams = AdminFbkType & {
chatItemId: string;
};
export type InitChatResponse = {
chatId: string;
appId: string;
app: {
userGuideModule?: ModuleItemType;
chatModels?: string[];
name: string;
avatar: string;
intro: string;
canUse?: boolean;
};
title: string;
variables: Record<string, any>;
history: ChatItemType[];
};
export type ChatHistoryItemResType = moduleDispatchResType & {
moduleType: `${FlowNodeTypeEnum}`;
moduleName: string;
};

View File

@@ -44,6 +44,12 @@ export const ChatSourceMap = {
}
};
export enum ChatStatusEnum {
loading = 'loading',
running = 'running',
finish = 'finish'
}
export const HUMAN_ICON = `/icon/human.svg`;
export const LOGO_ICON = `/icon/logo.svg`;

View File

@@ -1,6 +1,6 @@
import { ClassifyQuestionAgentItemType } from '../module/type';
import { SearchDataResponseItemType } from '../dataset/type';
import { ChatRoleEnum, ChatSourceEnum } from './constants';
import { ChatRoleEnum, ChatSourceEnum, ChatStatusEnum } from './constants';
import { FlowNodeTypeEnum } from '../module/node/constant';
import { ModuleOutputKeyEnum } from '../module/constants';
import { AppSchema } from '../app/type';
@@ -20,7 +20,7 @@ export type ChatSchema = {
variables: Record<string, any>;
source: `${ChatSourceEnum}`;
shareId?: string;
isInit: boolean;
outLinkUid?: string;
content: ChatItemType[];
};
@@ -51,6 +51,7 @@ export type AdminFbkType = {
a?: string;
};
/* --------- chat item ---------- */
export type ChatItemType = {
dataId?: string;
obj: ChatItemSchema['obj'];
@@ -61,11 +62,12 @@ export type ChatItemType = {
};
export type ChatSiteItemType = ChatItemType & {
status: 'loading' | 'running' | 'finish';
status: `${ChatStatusEnum}`;
moduleName?: string;
ttsBuffer?: Uint8Array;
};
/* ---------- history ------------- */
export type HistoryItemType = {
chatId: string;
updateTime: Date;
@@ -77,10 +79,10 @@ export type ChatHistoryItemType = HistoryItemType & {
top: boolean;
};
// response data
/* ------- response data ------------ */
export type moduleDispatchResType = {
moduleLogo?: string;
price: number;
price?: number;
runningTime?: number;
tokens?: number;
model?: string;
@@ -112,3 +114,8 @@ export type moduleDispatchResType = {
// plugin output
pluginOutput?: Record<string, any>;
};
export type ChatHistoryItemResType = moduleDispatchResType & {
moduleType: `${FlowNodeTypeEnum}`;
moduleName: string;
};

View File

@@ -6,9 +6,9 @@ import type { LLMModelItemType } from '../ai/model.d';
export type DatasetUpdateBody = {
id: string;
parentId?: string;
tags?: string[];
name?: string;
avatar?: string;
intro?: string;
permission?: DatasetSchemaType['permission'];
agentModel?: LLMModelItemType;
websiteConfig?: DatasetSchemaType['websiteConfig'];

View File

@@ -1,27 +1,12 @@
import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d';
import type { InitChatResponse } from '../../core/chat/api.d';
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
export type InitShareChatResponse = {
userAvatar: string;
app: InitChatResponse['app'];
};
/* one page type */
export type ShareChatType = InitShareChatResponse & {
history: ShareChatHistoryItemType;
};
/* history list item type */
export type ShareChatHistoryItemType = HistoryItemType & {
shareId: string;
variables?: Record<string, any>;
chats: ChatSiteItemType[];
};
export type AuthLinkChatProps = { ip?: string | null; authToken?: string; question: string };
export type AuthLinkLimitProps = AuthLinkChatProps & { outLink: OutLinkSchema };
export type AuthShareChatInitProps = {
authToken?: string;
export type AuthOutLinkInitProps = {
outLinkUid: string;
tokenUrl?: string;
};
export type AuthOutLinkChatProps = { ip?: string | null; outLinkUid: string; question: string };
export type AuthOutLinkLimitProps = AuthOutLinkChatProps & { outLink: OutLinkSchema };
export type AuthOutLinkResponse = {
uid: string;
};

View File

@@ -3,7 +3,6 @@ import { OutLinkTypeEnum } from './constant';
export type OutLinkSchema = {
_id: string;
shareId: string;
userId: string;
teamId: string;
tmbId: string;
appId: string;

View File

@@ -45,4 +45,4 @@ export const TeamMemberStatusMap = {
color: 'red.600'
}
};
export const leaveStatus = { $ne: TeamMemberStatusEnum.leave };
export const notLeaveStatus = { $ne: TeamMemberStatusEnum.leave };

View File

@@ -1,5 +1,5 @@
import { UserModelSchema } from '../type';
import { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
import type { UserModelSchema } from '../type';
import type { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
export type TeamSchema = {
_id: string;
@@ -22,6 +22,16 @@ export type TeamMemberSchema = {
defaultTeam: boolean;
};
export type TeamMemberWithUserSchema = TeamMemberSchema & {
userId: UserModelSchema;
};
export type TeamMemberWithTeamSchema = TeamMemberSchema & {
teamId: TeamSchema;
};
export type TeamMemberWithTeamAndUserSchema = TeamMemberWithTeamSchema & {
userId: UserModelSchema;
};
export type TeamItemType = {
userId: string;
teamId: string;

View File

@@ -26,9 +26,13 @@ export async function connectMongo({
bufferCommands: true,
maxConnecting: Number(process.env.DB_MAX_LINK || 5),
maxPoolSize: Number(process.env.DB_MAX_LINK || 5),
minPoolSize: Number(process.env.DB_MAX_LINK || 10) * 0.5,
minPoolSize: Math.min(10, Number(process.env.DB_MAX_LINK || 10)),
connectTimeoutMS: 60000,
waitQueueTimeoutMS: 60000
waitQueueTimeoutMS: 60000,
socketTimeoutMS: 60000,
maxIdleTimeMS: 300000,
retryWrites: true,
retryReads: true
});
console.log('mongo connected');

View File

@@ -50,10 +50,6 @@ const ChatSchema = new Schema({
top: {
type: Boolean
},
variables: {
type: Object,
default: {}
},
source: {
type: String,
enum: Object.keys(ChatSourceMap),
@@ -62,9 +58,17 @@ const ChatSchema = new Schema({
shareId: {
type: String
},
isInit: {
type: Boolean,
default: false
outLinkUid: {
type: String
},
variables: {
type: Object,
default: {}
},
metadata: {
//For special storage
type: Object,
default: {}
},
content: {
type: [
@@ -89,9 +93,10 @@ const ChatSchema = new Schema({
});
try {
ChatSchema.index({ tmbId: 1 });
ChatSchema.index({ updateTime: -1 });
ChatSchema.index({ appId: 1 });
ChatSchema.index({ tmbId: 1 });
ChatSchema.index({ shareId: 1 });
ChatSchema.index({ updateTime: -1 });
} catch (error) {
console.log(error);
}

View File

@@ -0,0 +1,22 @@
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { MongoChatItem } from './chatItemSchema';
export async function getChatItems({
chatId,
limit = 30,
field
}: {
chatId?: string;
limit?: number;
field: string;
}): Promise<{ history: ChatItemType[] }> {
if (!chatId) {
return { history: [] };
}
const history = await MongoChatItem.find({ chatId }, field).sort({ _id: -1 }).limit(limit);
history.reverse();
return { history };
}

View File

@@ -12,10 +12,6 @@ const OutLinkSchema = new Schema({
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'user'
},
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName,

View File

@@ -22,15 +22,15 @@ export const updateOutLinkUsage = async ({
};
export const pushResult2Remote = async ({
authToken,
outLinkUid,
shareId,
responseData
}: {
authToken?: string;
outLinkUid?: string; // raw id, not parse
shareId?: string;
responseData?: any[];
}) => {
if (!shareId || !authToken || !global.systemEnv.pluginBaseUrl) return;
if (!shareId || !outLinkUid || !global.systemEnv.pluginBaseUrl) return;
try {
const outLink = await MongoOutLink.findOne({
shareId
@@ -42,7 +42,7 @@ export const pushResult2Remote = async ({
baseURL: outLink.limit.hookUrl,
url: '/shareAuth/finish',
data: {
token: authToken,
token: outLinkUid,
responseData
}
});

View File

@@ -19,6 +19,7 @@ export async function authApp({
AuthResponseType & {
teamOwner: boolean;
app: AppDetailType;
role: `${TeamMemberRoleEnum}`;
}
> {
const result = await parseHeaderCert(props);
@@ -65,6 +66,7 @@ export async function authApp({
return {
...result,
app,
role,
isOwner,
canWrite,
teamOwner: role === TeamMemberRoleEnum.owner

View File

@@ -1,67 +0,0 @@
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
import { AuthModeType } from '../type';
import type { ChatWithAppSchema } from '@fastgpt/global/core/chat/type';
import { parseHeaderCert } from '../controller';
import { MongoChat } from '../../../core/chat/chatSchema';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getTeamInfoByTmbId } from '../../user/team/controller';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
export async function authChat({
chatId,
per = 'owner',
...props
}: AuthModeType & {
chatId: string;
}): Promise<
AuthResponseType & {
chat: ChatWithAppSchema;
}
> {
const { userId, teamId, tmbId } = await parseHeaderCert(props);
const { role } = await getTeamInfoByTmbId({ tmbId });
const { chat, isOwner, canWrite } = await (async () => {
// get chat
const chat = (await MongoChat.findOne({ chatId, teamId })
.populate('appId')
.lean()) as ChatWithAppSchema;
if (!chat) {
return Promise.reject('Chat is not exists');
}
const isOwner = role === TeamMemberRoleEnum.owner || String(chat.tmbId) === tmbId;
const canWrite = isOwner;
if (
per === 'r' &&
role !== TeamMemberRoleEnum.owner &&
chat.appId.permission !== PermissionTypeEnum.public
) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
if (per === 'w' && !canWrite) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
if (per === 'owner' && !isOwner) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
return {
chat,
isOwner,
canWrite
};
})();
return {
userId,
teamId,
tmbId,
chat,
isOwner,
canWrite
};
}

View File

@@ -20,11 +20,11 @@ export async function authCertAndShareId({
return authCert(props);
}
const { app } = await authOutLinkValid({ shareId });
const { shareChat } = await authOutLinkValid({ shareId });
return {
teamId: String(app.teamId),
tmbId: String(app.tmbId),
teamId: String(shareChat.teamId),
tmbId: String(shareChat.tmbId),
authType: AuthUserTypeEnum.outLink,
apikey: '',
isOwner: false,

View File

@@ -78,21 +78,19 @@ export async function authOutLinkCrud({
};
}
/* outLink exist and it app exist */
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
if (!shareId) {
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
}
const shareChat = await MongoOutLink.findOne({ shareId });
if (!shareChat) {
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
}
const app = await MongoApp.findById(shareChat.appId);
if (!app) {
return Promise.reject(AppErrEnum.unExist);
}
return {
app,
appId: shareChat.appId,
shareChat
};
}

View File

@@ -1,4 +1,8 @@
import { UserType } from '@fastgpt/global/support/user/type';
import { MongoUser } from './schema';
import { getTeamInfoByTmbId, getUserDefaultTeam } from './team/controller';
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
export async function authUserExist({ userId, username }: { userId?: string; username?: string }) {
if (userId) {
@@ -9,3 +13,56 @@ export async function authUserExist({ userId, username }: { userId?: string; use
}
return null;
}
export async function getUserDetail({
tmbId,
userId
}: {
tmbId?: string;
userId?: string;
}): Promise<UserType> {
const team = await (async () => {
if (tmbId) {
return getTeamInfoByTmbId({ tmbId });
}
if (userId) {
return getUserDefaultTeam({ userId });
}
return Promise.reject(ERROR_ENUM.unAuthorization);
})();
const user = await MongoUser.findById(team.userId);
if (!user) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return {
_id: user._id,
username: user.username,
avatar: user.avatar,
balance: user.balance,
timezone: user.timezone,
promotionRate: user.promotionRate,
openaiAccount: user.openaiAccount,
team
};
}
export async function getUserAndAuthBalance({
tmbId,
minBalance
}: {
tmbId: string;
minBalance?: number;
}) {
const user = await getUserDetail({ tmbId });
if (!user) {
return Promise.reject(UserErrEnum.unAuthUser);
}
if (minBalance !== undefined && user.team.balance < minBalance) {
return Promise.reject(UserErrEnum.balanceNotEnough);
}
return user;
}

View File

@@ -1,35 +1,15 @@
import { TeamItemType } from '@fastgpt/global/support/user/team/type';
import { connectionMongo, Types } from '../../../common/mongo';
import { TeamItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
import { Types } from '../../../common/mongo';
import {
TeamMemberRoleEnum,
TeamMemberStatusEnum,
TeamCollectionName,
TeamMemberCollectionName,
leaveStatus
notLeaveStatus
} from '@fastgpt/global/support/user/team/constant';
import { MongoTeamMember } from './teamMemberSchema';
import { MongoTeam } from './teamSchema';
async function getTeam(match: Record<string, any>): Promise<TeamItemType> {
const db = connectionMongo?.connection?.db;
const TeamMember = db.collection(TeamMemberCollectionName);
const results = await TeamMember.aggregate([
{
$match: match
},
{
$lookup: {
from: TeamCollectionName,
localField: 'teamId',
foreignField: '_id',
as: 'team'
}
},
{
$unwind: '$team'
}
]).toArray();
const tmb = results[0];
const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema;
if (!tmb) {
return Promise.reject('member not exist');
@@ -37,17 +17,17 @@ async function getTeam(match: Record<string, any>): Promise<TeamItemType> {
return {
userId: String(tmb.userId),
teamId: String(tmb.teamId),
teamName: tmb.team.name,
teamId: String(tmb.teamId._id),
teamName: tmb.teamId.name,
memberName: tmb.name,
avatar: tmb.team.avatar,
balance: tmb.team.balance,
avatar: tmb.teamId.avatar,
balance: tmb.teamId.balance,
tmbId: String(tmb._id),
role: tmb.role,
status: tmb.status,
defaultTeam: tmb.defaultTeam,
canWrite: tmb.role !== TeamMemberRoleEnum.visitor,
maxSize: tmb.team.maxSize
maxSize: tmb.teamId.maxSize
};
}
@@ -57,7 +37,7 @@ export async function getTeamInfoByTmbId({ tmbId }: { tmbId: string }) {
}
return getTeam({
_id: new Types.ObjectId(tmbId),
status: leaveStatus
status: notLeaveStatus
});
}
@@ -83,12 +63,8 @@ export async function createDefaultTeam({
balance?: number;
maxSize?: number;
}) {
const db = connectionMongo.connection.db;
const Team = db.collection(TeamCollectionName);
const TeamMember = db.collection(TeamMemberCollectionName);
// auth default team
const tmb = await TeamMember.findOne({
const tmb = await MongoTeamMember.findOne({
userId: new Types.ObjectId(userId),
defaultTeam: true
});
@@ -97,7 +73,7 @@ export async function createDefaultTeam({
console.log('create default team', userId);
// create
const { insertedId } = await Team.insertOne({
const { _id: insertedId } = await MongoTeam.create({
ownerId: userId,
name: teamName,
avatar,
@@ -105,7 +81,7 @@ export async function createDefaultTeam({
maxSize,
createTime: new Date()
});
await TeamMember.insertOne({
await MongoTeamMember.create({
teamId: insertedId,
userId,
name: 'Owner',
@@ -116,16 +92,11 @@ export async function createDefaultTeam({
});
} else {
console.log('default team exist', userId);
await Team.updateOne(
{
_id: new Types.ObjectId(tmb.teamId)
},
{
$set: {
...(balance !== undefined && { balance }),
maxSize
}
await MongoTeam.findByIdAndUpdate(tmb.teamId, {
$set: {
...(balance !== undefined && { balance }),
maxSize
}
);
});
}
}

View File

@@ -0,0 +1,51 @@
import { connectionMongo, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
import {
TeamMemberRoleMap,
TeamMemberStatusMap,
TeamMemberCollectionName,
TeamCollectionName
} from '@fastgpt/global/support/user/team/constant';
const TeamMemberSchema = new Schema({
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: userCollectionName,
required: true
},
name: {
type: String,
default: 'Member'
},
role: {
type: String,
enum: Object.keys(TeamMemberRoleMap)
},
status: {
type: String,
enum: Object.keys(TeamMemberStatusMap)
},
createTime: {
type: Date,
default: () => new Date()
},
defaultTeam: {
type: Boolean,
default: false
}
});
try {
} catch (error) {
console.log(error);
}
export const MongoTeamMember: Model<TeamMemberType> =
models[TeamMemberCollectionName] || model(TeamMemberCollectionName, TeamMemberSchema);

View File

@@ -0,0 +1,41 @@
import { connectionMongo, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
const TeamSchema = new Schema({
name: {
type: String,
required: true
},
ownerId: {
type: Schema.Types.ObjectId,
ref: userCollectionName
},
avatar: {
type: String,
default: '/icon/logo.svg'
},
createTime: {
type: Date,
default: () => Date.now()
},
balance: {
type: Number,
default: 2 * PRICE_SCALE
},
maxSize: {
type: Number,
default: 5
}
});
try {
} catch (error) {
console.log(error);
}
export const MongoTeam: Model<TeamType> =
models[TeamCollectionName] || model(TeamCollectionName, TeamSchema);