V4.8.17 feature (#3485)

* feat: add third party account config (#3443)

* temp

* editor workflow variable style

* add team to dispatch

* i18n

* delete console

* change openai account position

* fix

* fix

* fix

* fix

* fix

* 4.8.17 test (#3461)

* perf: external provider config

* perf: ui

* feat: add template config (#3434)

* change template position

* template config

* delete console

* delete

* fix

* fix

* perf: Mongo visutal field (#3464)

* remve invalid code

* perf: team member visutal code

* perf: virtual search; perf: search test data

* fix: ts

* fix: image response headers

* perf: template code

* perf: auth layout;perf: auto save (#3472)

* perf: auth layout

* perf: auto save

* perf: auto save

* fix: template guide display & http input support external variables (#3475)

* fix: template guide display

* http editor support external workflow variables

* perf: auto save;fix: ifelse checker line break; (#3478)

* perf: auto save

* perf: auto save

* fix: ifelse checker line break

* perf: doc

* perf: doc

* fix: update var type error

* 4.8.17 test (#3479)

* perf: auto save

* perf: auto save

* perf: template code

* 4.8.17 test (#3480)

* perf: auto save

* perf: auto save

* perf: model price model

* feat: add react memo

* perf: model provider filter

* fix: ts (#3481)

* perf: auto save

* perf: auto save

* fix: ts

* simple app tool select (#3473)

* workflow plugin userguide & simple tool ui

* simple tool filter

* reuse component

* change component to hook

* fix

* perf: too selector modal (#3484)

* perf: auto save

* perf: auto save

* perf: markdown render

* perf: too selector

* fix: app version require tmbId

* perf: templates refresh

* perf: templates refresh

* hide auto save error tip

* perf: toolkit guide

---------

Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
Archer
2024-12-27 20:05:12 +08:00
committed by GitHub
parent a209856d48
commit b520988c64
207 changed files with 2943 additions and 1378 deletions

View File

@@ -83,6 +83,13 @@ const OutLinkSchema = new Schema({
}
});
OutLinkSchema.virtual('associatedApp', {
ref: AppCollectionName,
localField: 'appId',
foreignField: '_id',
justOne: true
});
try {
OutLinkSchema.index({ shareId: -1 });
} catch (error) {

View File

@@ -1,18 +1,32 @@
import { TeamMemberWithUserSchema } from '@fastgpt/global/support/user/team/type';
import { MongoTeamMember } from '../../user/team/teamMemberSchema';
import { checkTeamAIPoints } from '../teamLimit';
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { TeamSchema } from '@fastgpt/global/support/user/team/type';
export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
const tmb = (await MongoTeamMember.findById(tmbId, 'teamId userId').populate(
'userId',
'timezone openaiAccount'
)) as TeamMemberWithUserSchema;
const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId')
.populate<{ user: UserModelSchema; team: TeamSchema }>([
{
path: 'user',
select: 'timezone'
},
{
path: 'team',
select: 'openaiAccount externalWorkflowVariables'
}
])
.lean();
if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
await checkTeamAIPoints(tmb.teamId);
await checkTeamAIPoints(tmb.team._id);
return {
user: tmb.userId
timezone: tmb.user.timezone,
externalProvider: {
openaiAccount: tmb.team.openaiAccount,
externalWorkflowVariables: tmb.team.externalWorkflowVariables
}
};
}

View File

@@ -10,17 +10,17 @@ import { MongoResourcePermission } from './schema';
import { ClientSession } from 'mongoose';
import {
PermissionValueType,
ResourcePermissionType,
ResourcePerWithGroup,
ResourcePerWithTmbWithUser
ResourcePermissionType
} from '@fastgpt/global/support/permission/type';
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
import { addMinutes } from 'date-fns';
import { getGroupsByTmbId } from './memberGroup/controllers';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
/** get resource permission for a team member
* If there is no permission for the team member, it will return undefined
@@ -160,32 +160,33 @@ export const getClbsAndGroupsWithInfo = async ({
teamId: string;
}) =>
Promise.all([
(await MongoResourcePermission.find({
MongoResourcePermission.find({
teamId,
resourceId,
resourceType,
tmbId: {
$exists: true
}
}).populate({
path: 'tmbId',
select: 'name userId',
populate: {
path: 'userId',
select: 'avatar'
}
})) as ResourcePerWithTmbWithUser[],
(await MongoResourcePermission.find({
})
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
path: 'tmb',
select: 'name userId',
populate: {
path: 'user',
select: 'avatar'
}
})
.lean(),
MongoResourcePermission.find({
teamId,
resourceId,
resourceType,
groupId: {
$exists: true
}
}).populate({
path: 'groupId',
select: 'name avatar'
})) as ResourcePerWithGroup[]
})
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
.lean()
]);
export const delResourcePermissionById = (id: string) => {

View File

@@ -3,7 +3,6 @@ import { getResourcePermission, parseHeaderCert } from '../controller';
import {
CollectionWithDatasetType,
DatasetDataItemType,
DatasetFileSchema,
DatasetSchemaType
} from '@fastgpt/global/core/dataset/type';
import { getTmbInfoByTmbId } from '../../user/team/controller';
@@ -12,10 +11,6 @@ import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/per
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { getCollectionWithDataset } from '../../../core/dataset/controller';
import { MongoDatasetCollection } from '../../../core/dataset/collection/schema';
import { getFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MongoDatasetData } from '../../../core/dataset/data/schema';
import { AuthModeType, AuthResponseType } from '../type';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
@@ -181,7 +176,7 @@ export async function authDatasetCollection({
const { dataset } = await authDatasetByTmbId({
tmbId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
per,
isRoot: isRootFromHeader
});

View File

@@ -66,48 +66,19 @@ export const getGroupsByTmbId = async ({
},
...(role ? { role: { $in: role } } : {})
})
.populate('groupId')
.populate<{ group: MemberGroupSchemaType }>('group')
.lean()
).map((item) => {
return {
...(item.groupId as any as MemberGroupSchemaType)
};
}),
).map((item) => item.group),
role ? [] : getTeamDefaultGroup({ teamId })
])
).flat();
export const getTmbByGroupId = async (groupId: string) => {
return (
await MongoGroupMemberModel.find({
groupId
})
.populate('tmbId')
.lean()
).map((item) => {
return {
...(item.tmbId as any as MemberGroupSchemaType)
};
});
};
export const getGroupMembersByGroupId = async (groupId: string) => {
return await MongoGroupMemberModel.find({
groupId
}).lean();
};
export const getGroupMembersWithInfoByGroupId = async (groupId: string) => {
return (
await MongoGroupMemberModel.find({
groupId
})
.populate('tmbId')
.lean()
).map((item) => item.tmbId) as any as TeamMemberSchema[]; // HACK: type casting
};
/**
* Get tmb's group permission: the maximum permission of the group
* @param tmbId

View File

@@ -26,6 +26,13 @@ export const GroupMemberSchema = new Schema({
}
});
GroupMemberSchema.virtual('group', {
ref: MemberGroupCollectionName,
localField: 'groupId',
foreignField: '_id',
justOne: true
});
try {
GroupMemberSchema.index({
groupId: 1

View File

@@ -39,6 +39,19 @@ export const ResourcePermissionSchema = new Schema({
}
});
ResourcePermissionSchema.virtual('tmb', {
ref: TeamMemberCollectionName,
localField: 'tmbId',
foreignField: '_id',
justOne: true
});
ResourcePermissionSchema.virtual('group', {
ref: MemberGroupCollectionName,
localField: 'groupId',
foreignField: '_id',
justOne: true
});
try {
ResourcePermissionSchema.index(
{

View File

@@ -44,7 +44,6 @@ export async function getUserDetail({
avatar: user.avatar,
timezone: user.timezone,
promotionRate: user.promotionRate,
openaiAccount: user.openaiAccount,
team: tmb,
notificationAccount: tmb.notificationAccount,
permission: tmb.permission

View File

@@ -1,4 +1,4 @@
import { TeamTmbItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
import { TeamSchema, TeamTmbItemType } from '@fastgpt/global/support/user/team/type';
import { ClientSession, Types } from '../../../common/mongo';
import {
TeamMemberRoleEnum,
@@ -15,37 +15,41 @@ import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/use
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema;
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
if (!tmb) {
return Promise.reject('member not exist');
}
const Per = await getResourcePermission({
resourceType: PerResourceTypeEnum.team,
teamId: tmb.teamId._id,
teamId: tmb.teamId,
tmbId: tmb._id
});
return {
userId: String(tmb.userId),
teamId: String(tmb.teamId._id),
teamName: tmb.teamId.name,
teamId: String(tmb.teamId),
teamName: tmb.team.name,
memberName: tmb.name,
avatar: tmb.teamId.avatar,
balance: tmb.teamId.balance,
avatar: tmb.team.avatar,
balance: tmb.team.balance,
tmbId: String(tmb._id),
teamDomain: tmb.teamId?.teamDomain,
teamDomain: tmb.team?.teamDomain,
role: tmb.role,
status: tmb.status,
defaultTeam: tmb.defaultTeam,
lafAccount: tmb.teamId.lafAccount,
permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal,
isOwner: tmb.role === TeamMemberRoleEnum.owner
}),
notificationAccount: tmb.teamId.notificationAccount
notificationAccount: tmb.team.notificationAccount,
lafAccount: tmb.team.lafAccount,
openaiAccount: tmb.team.openaiAccount,
externalWorkflowVariables: tmb.team.externalWorkflowVariables
};
}
@@ -145,21 +149,87 @@ export async function updateTeam({
name,
avatar,
teamDomain,
lafAccount
lafAccount,
openaiAccount,
externalWorkflowVariable
}: UpdateTeamProps & { teamId: string }) {
// auth openai key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
openaiAccount.baseUrl = baseUrl;
const ai = getAIApi({
userKey: openaiAccount
});
const response = await ai.chat.completions.create({
model: 'gpt-4o-mini',
max_tokens: 1,
messages: [{ role: 'user', content: 'hi' }]
});
if (response?.choices?.[0]?.message?.content === undefined) {
return Promise.reject('Key response is empty');
}
}
return mongoSessionRun(async (session) => {
const unsetObj = (() => {
const obj: Record<string, 1> = {};
if (lafAccount?.pat === '') {
obj.lafAccount = 1;
}
if (openaiAccount?.key === '') {
obj.openaiAccount = 1;
}
if (externalWorkflowVariable) {
if (externalWorkflowVariable.value === '') {
obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] = 1;
}
}
if (Object.keys(obj).length === 0) {
return undefined;
}
return {
$unset: obj
};
})();
const setObj = (() => {
const obj: Record<string, any> = {};
if (lafAccount?.pat && lafAccount?.appid) {
obj.lafAccount = lafAccount;
}
if (openaiAccount?.key && openaiAccount?.baseUrl) {
obj.openaiAccount = openaiAccount;
}
if (externalWorkflowVariable) {
if (externalWorkflowVariable.value !== '') {
obj[`externalWorkflowVariables.${externalWorkflowVariable.key}`] =
externalWorkflowVariable.value;
}
}
if (Object.keys(obj).length === 0) {
return undefined;
}
return obj;
})();
await MongoTeam.findByIdAndUpdate(
teamId,
{
name,
avatar,
teamDomain,
lafAccount
$set: {
...(name ? { name } : {}),
...(avatar ? { avatar } : {}),
...(teamDomain ? { teamDomain } : {}),
...setObj
},
...unsetObj
},
{ session }
);
// update default group
// Update member group avatar
if (avatar) {
await MongoMemberGroupModel.updateOne(
{

View File

@@ -3,7 +3,6 @@ const { Schema } = connectionMongo;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
import {
TeamMemberRoleMap,
TeamMemberStatusMap,
TeamMemberCollectionName,
TeamCollectionName
@@ -42,6 +41,19 @@ const TeamMemberSchema = new Schema({
}
});
TeamMemberSchema.virtual('team', {
ref: TeamCollectionName,
localField: 'teamId',
foreignField: '_id',
justOne: true
});
TeamMemberSchema.virtual('user', {
ref: userCollectionName,
localField: 'userId',
foreignField: '_id',
justOne: true
});
try {
TeamMemberSchema.index({ teamId: 1 }, { background: true });
TeamMemberSchema.index({ userId: 1 }, { background: true });

View File

@@ -47,6 +47,16 @@ const TeamSchema = new Schema({
type: String
}
},
openaiAccount: {
type: {
key: String,
baseUrl: String
}
},
externalWorkflowVariables: {
type: Object,
default: {}
},
notificationAccount: {
type: String,
required: false