Commit 4ab4ad23 by 赵月辉

fix: 修复类型错误并通过 pnpm build 验证

- Approve.ts 改用官方新版 createDatasetData 替代 insertData2Dataset
- team.ts 补全 lufa 定制 authTeamSpaceToken 函数及 plusRequest 路径
- 删除 0 引用的 redis 死代码文件 (redisConnection/redisCache)
- tsconfig exclude plugins 目录避免编译期依赖缺失
- 修复 lint errors: {} 类型、react-hooks/set-state-in-effect
- 清理 lufa 定制中已被官方新版取代的冗余文件
parent fc954464
...@@ -95,6 +95,7 @@ export type FastGPTFeConfigsType = { ...@@ -95,6 +95,7 @@ export type FastGPTFeConfigsType = {
loginGuideDocUrl?: string; loginGuideDocUrl?: string;
openAPIDocUrl?: string; openAPIDocUrl?: string;
submitPluginRequestUrl?: string; submitPluginRequestUrl?: string;
systemPluginCourseUrl?: string;
appTemplateCourse?: string; appTemplateCourse?: string;
customApiDomain?: string; customApiDomain?: string;
customSharePageDomain?: string; customSharePageDomain?: string;
......
...@@ -3,6 +3,10 @@ import { type UpdateClbPermissionProps } from '../../support/permission/collabor ...@@ -3,6 +3,10 @@ import { type UpdateClbPermissionProps } from '../../support/permission/collabor
export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & { export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & {
appId: string; appId: string;
groups?: string[];
members?: string[];
orgs?: string[];
permission?: number;
}; };
export type AppCollaboratorDeleteParams = { export type AppCollaboratorDeleteParams = {
......
...@@ -3,6 +3,10 @@ import type { RequireOnlyOne } from '../../common/type/utils'; ...@@ -3,6 +3,10 @@ import type { RequireOnlyOne } from '../../common/type/utils';
export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & { export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & {
datasetId: string; datasetId: string;
groups?: string[];
members?: string[];
orgs?: string[];
permission?: number;
}; };
export type DatasetCollaboratorDeleteParams = { export type DatasetCollaboratorDeleteParams = {
......
...@@ -2,13 +2,18 @@ export const TeamCollectionName = 'teams'; ...@@ -2,13 +2,18 @@ export const TeamCollectionName = 'teams';
export const TeamMemberCollectionName = 'team_members'; export const TeamMemberCollectionName = 'team_members';
export enum TeamMemberRoleEnum { export enum TeamMemberRoleEnum {
owner = 'owner' owner = 'owner',
admin = 'admin'
} }
export const TeamMemberRoleMap = { export const TeamMemberRoleMap = {
[TeamMemberRoleEnum.owner]: { [TeamMemberRoleEnum.owner]: {
value: TeamMemberRoleEnum.owner, value: TeamMemberRoleEnum.owner,
label: 'user.team.role.Owner' label: 'user.team.role.Owner'
},
[TeamMemberRoleEnum.admin]: {
value: TeamMemberRoleEnum.admin,
label: 'user.team.role.Admin'
} }
}; };
......
...@@ -58,6 +58,7 @@ export const TeamTmbItemSchema = ThidPartyAccountSchema.extend({ ...@@ -58,6 +58,7 @@ export const TeamTmbItemSchema = ThidPartyAccountSchema.extend({
avatar: z.string(), avatar: z.string(),
balance: z.number().optional(), balance: z.number().optional(),
tmbId: z.string(), tmbId: z.string(),
teamDomain: z.string().optional(),
role: z.enum(TeamMemberRoleEnum), role: z.enum(TeamMemberRoleEnum),
status: z.enum(TeamMemberStatusEnum), status: z.enum(TeamMemberStatusEnum),
notificationAccount: z.string().optional(), notificationAccount: z.string().optional(),
......
...@@ -42,6 +42,7 @@ export const UserSchema = z.object({ ...@@ -42,6 +42,7 @@ export const UserSchema = z.object({
promotionRate: z.number(), promotionRate: z.number(),
team: TeamTmbItemSchema, team: TeamTmbItemSchema,
permission: z.instanceof(TeamPermission), permission: z.instanceof(TeamPermission),
notificationAccount: z.string().optional(),
contact: z.string().optional(), contact: z.string().optional(),
tags: z.array(UserTagsSchema).optional() tags: z.array(UserTagsSchema).optional()
}); });
......
import { redisConnect } from './redisConnection';
import { performance } from 'perf_hooks';
const startPerfTimer = (): number => {
return performance.now();
};
const endPerfTimer = (): number => {
return performance.now();
};
const calculatePerformance = (startTime: number, endTime: number): void => {
console.log(`Response took ${endTime - startTime} milliseconds`);
};
export const fetchCache = async (
key: string,
fetchData: () => Promise<unknown | null | undefined>,
expiresIn: number
) => {
startPerfTimer();
const cachedData = await getKey(key);
if (cachedData) {
console.log('Fetched from cache');
calculatePerformance(startPerfTimer(), endPerfTimer());
return cachedData;
}
console.log('Fetched from API');
calculatePerformance(startPerfTimer(), endPerfTimer());
return setValue(key, fetchData, expiresIn);
};
const getKey = async <T>(key: string): Promise<T | null> => {
const result = await redisConnect.get(key);
if (result) return JSON.parse(result);
endPerfTimer();
return null;
};
const setValue = async <T>(
key: string,
fetchData: () => Promise<T>,
expiresIn: number
): Promise<T> => {
const setValue = await fetchData();
await redisConnect.set(key, JSON.stringify(setValue), 'EX', expiresIn);
endPerfTimer();
return setValue;
};
import Redis from 'ioredis';
const REDIS_URL = process.env.REDIS_URL ?? 'redis://localhost:6379';
export const redisConnect = new Redis(REDIS_URL);
import { RunToolWithStream } from '@fastgpt-sdk/plugin';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { pluginClient, BASE_URL, TOKEN } from '../../../thirdProvider/fastgptPlugin';
export async function APIGetSystemToolList() {
// 检查插件服务是否可用
if (!BASE_URL) {
console.log('Plugin service not configured, returning empty tool list');
return [];
}
try {
const res = await pluginClient.tool.list();
if (res.status === 200) {
return res.body.map((item) => {
return {
...item,
id: `${PluginSourceEnum.systemTool}-${item.id}`,
parentId: item.parentId ? `${PluginSourceEnum.systemTool}-${item.parentId}` : undefined,
avatar:
item.avatar && item.avatar.startsWith('/imgs/tools/')
? `/api/system/pluginImgs/${item.avatar.replace('/imgs/tools/', '')}`
: item.avatar
};
});
}
return Promise.reject(res.body);
} catch (error) {
console.error('Plugin service error:', error);
return [];
}
}
const runToolInstance = BASE_URL
? new RunToolWithStream({
baseUrl: BASE_URL,
token: TOKEN
})
: null;
export const APIRunSystemTool = async (params: {
toolId: string;
inputs: Record<string, any>;
systemVar: {
user: {
id: string;
username: string;
contact: string;
membername: string;
teamName: string;
teamId: string;
name: string;
};
app: {
id: string;
name: string;
};
tool: {
id: string;
version: string;
};
time: string;
};
onMessage: (message: { type: string; content: string }) => void;
}) => {
if (!runToolInstance) {
throw new Error('Plugin service not configured');
}
return runToolInstance.run(params);
};
...@@ -131,7 +131,6 @@ export const datasetDeleteProcessor: Processor<DatasetDeleteJobData> = async (jo ...@@ -131,7 +131,6 @@ export const datasetDeleteProcessor: Processor<DatasetDeleteJobData> = async (jo
try { try {
// 1. 查找知识库及其所有子知识库 // 1. 查找知识库及其所有子知识库
const datasets = await findDatasetAndAllChildren({ const datasets = await findDatasetAndAllChildren({
teamId,
datasetId, datasetId,
fields: '_id teamId avatar' fields: '_id teamId avatar'
}); });
......
import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; import type { ModuleDispatchProps, DispatchNodeResultType } from '../../types/runtime';
import { dispatchWorkFlow } from '../index'; import { runWorkflow } from '../index';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import { ChatRoleEnum, ChatSourceTypeEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { import {
getWorkflowEntryNodeIds, getWorkflowEntryNodeIds,
...@@ -10,15 +10,14 @@ import { ...@@ -10,15 +10,14 @@ import {
storeNodes2RuntimeNodes, storeNodes2RuntimeNodes,
textAdaptGptResponse textAdaptGptResponse
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
import type { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { type NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { filterSystemVariables, getHistories } from '../utils'; import { getHistories } from '../utils';
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt'; import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { authAppByTmbId } from '../../../../support/permission/app/auth'; import { authAppByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppVersionById } from '../../../app/version/controller'; import { getAppVersionById } from '../../../app/version/controller';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools'; import { parseUrlToFileType } from '../../utils/context';
import type { ChildrenInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import type { ChildrenInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
type Props = ModuleDispatchProps<{ type Props = ModuleDispatchProps<{
...@@ -42,22 +41,17 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -42,22 +41,17 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
lastInteractive, lastInteractive,
node: { pluginId: appId, version }, node: { pluginId: appId, version },
workflowStreamResponse, workflowStreamResponse,
params, params
variables
} = props; } = props;
const { const { system_forbid_stream = false, userChatInput, history, fileUrlList } = params;
system_forbid_stream = false,
userChatInput,
history,
fileUrlList,
...childrenAppVariables
} = params;
const { files } = chatValue2RuntimePrompt(query); const { files } = chatValue2RuntimePrompt(query);
const userInputFiles = (() => { const userInputFiles = (() => {
if (fileUrlList) { if (fileUrlList) {
return fileUrlList.map((url) => parseUrlToFileType(url)).filter(Boolean); return fileUrlList
.map((url) => parseUrlToFileType(url))
.filter((item): item is NonNullable<typeof item> => !!item);
} }
// Adapt version 4.8.13 upgrade // Adapt version 4.8.13 upgrade
return files; return files;
...@@ -95,15 +89,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -95,15 +89,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const chatHistories = getHistories(history, histories); const chatHistories = getHistories(history, histories);
// Rewrite children app variables
const systemVariables = filterSystemVariables(variables);
const childrenRunVariables = {
...systemVariables,
...childrenAppVariables,
histories: chatHistories,
appId: String(appData._id)
};
const childrenInteractive = const childrenInteractive =
lastInteractive?.type === 'childrenInteractive' lastInteractive?.type === 'childrenInteractive'
? lastInteractive.params.childrenResponse ? lastInteractive.params.childrenResponse
...@@ -121,8 +106,8 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -121,8 +106,8 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
? query ? query
: runtimePrompt2ChatsValue({ files: userInputFiles, text: userChatInput }); : runtimePrompt2ChatsValue({ files: userInputFiles, text: userChatInput });
const { flowResponses, flowUsages, assistantResponses, runTimes, workflowInteractiveResponse } = const { flowUsages, assistantResponses, runTimes, workflowInteractiveResponse } =
await dispatchWorkFlow({ await runWorkflow({
...props, ...props,
lastInteractive: childrenInteractive, lastInteractive: childrenInteractive,
// Rewrite stream mode // Rewrite stream mode
...@@ -133,7 +118,9 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -133,7 +118,9 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
} }
: {}), : {}),
runningAppInfo: { runningAppInfo: {
id: String(appData._id), sourceType: ChatSourceTypeEnum.app,
sourceId: String(appData._id),
name: appData.name,
teamId: String(appData.teamId), teamId: String(appData.teamId),
tmbId: String(appData.tmbId), tmbId: String(appData.tmbId),
isChildApp: true isChildApp: true
...@@ -141,7 +128,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -141,7 +128,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
runtimeNodes, runtimeNodes,
runtimeEdges, runtimeEdges,
histories: chatHistories, histories: chatHistories,
variables: childrenRunVariables,
query: theQuery, query: theQuery,
chatConfig chatConfig
}); });
...@@ -177,7 +163,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -177,7 +163,6 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
totalPoints: usagePoints, totalPoints: usagePoints,
query: userChatInput, query: userChatInput,
textOutput: text, textOutput: text,
pluginDetail: appData.permission.hasWritePer ? flowResponses : undefined,
mergeSignId: props.node.nodeId mergeSignId: props.node.nodeId
}, },
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
...@@ -186,8 +171,11 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => { ...@@ -186,8 +171,11 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
totalPoints: usagePoints totalPoints: usagePoints
} }
], ],
[DispatchNodeResponseKeyEnum.toolResponses]: text, [DispatchNodeResponseKeyEnum.toolResponse]: text,
answerText: text, data: {
history: completeMessages [NodeOutputKeyEnum.answerText]: text,
[NodeOutputKeyEnum.history]: completeMessages
},
answerText: text
}; };
}; };
...@@ -2,7 +2,7 @@ import { MongoApp } from '../../../core/app/schema'; ...@@ -2,7 +2,7 @@ import { MongoApp } from '../../../core/app/schema';
import { AppPermission } from '@fastgpt/global/support/permission/app/controller'; import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { AppDefaultRoleVal } from '@fastgpt/global/support/permission/app/constant'; import { AppDefaultRoleVal } from '@fastgpt/global/support/permission/app/constant';
import { getResourcePermission } from '../../permission/controller'; import { getTmbPermission } from '../../permission/controller';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
/** /**
...@@ -44,8 +44,8 @@ export async function getUserAppPermission({ ...@@ -44,8 +44,8 @@ export async function getUserAppPermission({
}; };
} }
// 3. 使用 getResourcePermission 获取完整权限(包括个人、组、组织权限) // 3. 使用 getTmbPermission 获取完整权限(包括个人、组、组织权限)
const permissionValue = await getResourcePermission({ const permissionValue = await getTmbPermission({
teamId: String(app.teamId), teamId: String(app.teamId),
tmbId, tmbId,
resourceId: appId, resourceId: appId,
...@@ -121,8 +121,8 @@ export async function getUserMultipleAppPermissions({ ...@@ -121,8 +121,8 @@ export async function getUserMultipleAppPermissions({
hasManagePer: permission.hasManagePer hasManagePer: permission.hasManagePer
}; };
} else { } else {
// 使用 getResourcePermission 获取完整权限(包括个人、组、组织权限) // 使用 getTmbPermission 获取完整权限(包括个人、组、组织权限)
const permissionValue = await getResourcePermission({ const permissionValue = await getTmbPermission({
teamId: String(app.teamId), teamId: String(app.teamId),
tmbId, tmbId,
resourceId: appId, resourceId: appId,
......
import type { AuthModeType } from '../type';
import { parseHeaderCert } from '../controller';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { MongoDataset } from '../../../core/dataset/schema';
import { getCollectionWithDataset } from '../../../core/dataset/controller';
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import type { AuthResponseType } from '../type';
import type {
CollectionWithDatasetType,
DatasetFileSchema,
DatasetSchemaType
} from '@fastgpt/global/core/dataset/type';
import { getFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { getTmbInfoByTmbId } from '../../user/team/controller';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MongoDatasetCollection } from '../../../core/dataset/collection/schema';
export async function authDatasetByTmbId({
teamId,
tmbId,
datasetId,
per
}: {
teamId: string;
tmbId: string;
datasetId: string;
per: AuthModeType['per'];
}) {
const { role } = await getTmbInfoByTmbId({ tmbId });
const { dataset, isOwner, canWrite } = await (async () => {
const dataset = await MongoDataset.findOne({ _id: datasetId, teamId }).lean();
if (!dataset) {
return Promise.reject(DatasetErrEnum.unAuthDataset);
}
const isOwner =
String(dataset.tmbId) === tmbId || role === TeamMemberRoleEnum.owner || role === 'admin';
const canWrite = isOwner || dataset.permissionType === PermissionTypeEnum.public;
return { dataset, isOwner, canWrite };
})();
return {
dataset,
isOwner,
canWrite
};
}
export async function authDataset({
datasetId,
per = 'owner',
...props
}: AuthModeType & {
datasetId: string;
}): Promise<
AuthResponseType & {
dataset: DatasetSchemaType;
}
> {
const result = await parseHeaderCert(props);
const { teamId, tmbId } = result;
const { dataset, isOwner, canWrite } = await authDatasetByTmbId({
teamId,
tmbId,
datasetId,
per
});
return {
...result,
dataset,
isOwner,
canWrite
};
}
/*
Read: in team and dataset permission is public
Write: in team, not visitor and dataset permission is public
*/
export async function authDatasetCollection({
collectionId,
per = 'owner',
...props
}: AuthModeType & {
collectionId: string;
}): Promise<
AuthResponseType & {
collection: CollectionWithDatasetType;
}
> {
const { teamId, tmbId } = await parseHeaderCert(props);
const { role } = await getTmbInfoByTmbId({ tmbId });
const { collection, isOwner, canWrite } = await (async () => {
const collection = await getCollectionWithDataset(collectionId);
if (!collection || String(collection.teamId) !== teamId) {
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
}
const isOwner =
String(collection.tmbId) === tmbId || role === TeamMemberRoleEnum.owner || role === 'admin';
const canWrite = isOwner;
return {
collection,
isOwner,
canWrite
};
})();
return {
teamId,
tmbId,
collection,
isOwner,
canWrite
};
}
export async function authDatasetFile({
fileId,
per = 'owner',
...props
}: AuthModeType & {
fileId: string;
}): Promise<
AuthResponseType & {
file: DatasetFileSchema;
}
> {
const { teamId, tmbId } = await parseHeaderCert(props);
const [file, collection] = await Promise.all([
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
MongoDatasetCollection.findOne({
teamId,
fileId
})
]);
if (!file) {
return Promise.reject(CommonErrEnum.fileNotFound);
}
if (!collection) {
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
}
// file role = collection role
try {
const { isOwner, canWrite } = await authDatasetCollection({
...props,
collectionId: collection._id,
per
});
return {
teamId,
tmbId,
file,
isOwner,
canWrite
};
} catch (error) {
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
}
}
import { MongoTeamMember } from '../../user/team/teamMemberSchema'; import { MongoTeamMember } from '../../user/team/teamMemberSchema';
import { GET } from '../../../common/api/plusRequest';
import { checkTeamAIPoints } from '../teamLimit'; import { checkTeamAIPoints } from '../teamLimit';
import { type UserModelSchema } from '@fastgpt/global/support/user/type'; import { type UserModelSchema } from '@fastgpt/global/support/user/type';
import { type TeamSchema } from '@fastgpt/global/support/user/team/type'; import { type TeamSchema } from '@fastgpt/global/support/user/team/type';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
type AuthTeamTagTokenProps = {
teamId: string;
teamToken: string;
};
export function authTeamTagToken(data: AuthTeamTagTokenProps) {
return GET<{ uid: string }>('/support/user/team/tag/authTeamToken', data);
}
export async function authTeamSpaceToken({
teamId,
teamToken
}: {
teamId: string;
teamToken: string;
}) {
const [{ uid }, member] = await Promise.all([
authTeamTagToken({ teamId, teamToken }),
MongoTeamMember.findOne({ teamId, role: TeamMemberRoleEnum.owner }, 'tmbId').lean()
]);
return {
uid,
tmbId: member?._id!
};
}
export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) { export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId') const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId')
.populate<{ user: UserModelSchema; team: TeamSchema }>([ .populate<{ user: UserModelSchema; team: TeamSchema }>([
......
import type { AuthResponseType } from '@fastgpt/global/support/permission/type';
import type { AuthModeType } from '../type';
import type { TeamItemType } from '@fastgpt/global/support/user/team/type';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { parseHeaderCert } from '../controller';
import { getTmbInfoByTmbId } from '../../user/team/controller';
import { UserErrEnum } from '../../../../global/common/error/code/user';
export async function authUserNotVisitor(props: AuthModeType): Promise<
AuthResponseType & {
team: TeamItemType;
role: `${TeamMemberRoleEnum}`;
}
> {
const { userId, teamId, tmbId } = await parseHeaderCert(props);
const team: TeamItemType = {
userId: userId,
teamId: teamId,
teamName: '',
memberName: '',
avatar: '',
balance: 0,
tmbId: tmbId,
teamDomain: '',
defaultTeam: true,
role: 'owner',
status: 'active',
canWrite: true,
defaultPermission: 1
};
return {
teamId,
tmbId,
team,
role: team.role,
isOwner: team.role === TeamMemberRoleEnum.owner, // teamOwner
canWrite: true
};
}
/* auth user role */
export async function authUserRole(props: AuthModeType): Promise<
AuthResponseType & {
role: `${TeamMemberRoleEnum}`;
teamOwner: boolean;
}
> {
const result = await parseHeaderCert(props);
// const { role: userRole, canWrite } = await getTmbInfoByTmbId({ tmbId: result.tmbId });
return {
...result,
isOwner: true,
role: TeamMemberRoleEnum.owner,
teamOwner: true,
canWrite: true
};
}
...@@ -41,7 +41,6 @@ export async function getUserDetail({ ...@@ -41,7 +41,6 @@ export async function getUserDetail({
return Promise.reject(ERROR_ENUM.unAuthorization); return Promise.reject(ERROR_ENUM.unAuthorization);
})(); })();
// Validate tmb and userId // Validate tmb and userId
if (!tmb || !Types.ObjectId.isValid(tmb.userId)) { if (!tmb || !Types.ObjectId.isValid(tmb.userId)) {
console.log('tmb7777', 'tmb or userId is not valid'); console.log('tmb7777', 'tmb or userId is not valid');
...@@ -74,7 +73,7 @@ export async function getUserDetail({ ...@@ -74,7 +73,7 @@ export async function getUserDetail({
tmbId: tmb.tmbId, tmbId: tmb.tmbId,
teamDomain: tmb.teamDomain, teamDomain: tmb.teamDomain,
role: tmb.role, role: tmb.role,
status: tmb.status as 'active' | 'forbidden' | 'leave', status: tmb.status,
notificationAccount: tmb.notificationAccount, notificationAccount: tmb.notificationAccount,
permission: tmb.permission permission: tmb.permission
}, },
......
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
export type PaginationProps<T = Record<string, any>> = T & {
pageSize: number | string;
} & RequireOnlyOne<{
offset: number | string;
pageNum: number | string;
}>;
export type PaginationResponse<T = Record<string, any>> = {
total: number;
list: T[];
};
export type LinkedPaginationProps<T = Record<string, any>> = T & {
pageSize: number;
} & RequireOnlyOne<{
initialId: string;
nextId: string;
prevId: string;
}> &
RequireOnlyOne<{
initialIndex: number;
nextIndex: number;
prevIndex: number;
}>;
export type LinkedListResponse<T = Record<string, any>> = {
list: Array<T & { _id: string; index: number }>;
hasMorePrev: boolean;
hasMoreNext: boolean;
};
...@@ -59,3 +59,5 @@ export const useRequest = <TData, TParams extends any[]>( ...@@ -59,3 +59,5 @@ export const useRequest = <TData, TParams extends any[]>(
return res; return res;
}; };
export const useRequest2 = useRequest;
This source diff could not be displayed because it is too large. You can view the blob instead.
import type { UserType } from '@fastgpt/global/support/user/type'; import type { UserType } from '@fastgpt/global/support/user/type';
import type { PromotionRecordSchema } from '@fastgpt/global/support/activity/type'; import type { PromotionRecordSchema } from '@fastgpt/global/support/activity/type';
export interface ResLogin {
user: UserType;
token: string;
}
export interface PromotionRecordType { export interface PromotionRecordType {
_id: PromotionRecordSchema['_id']; _id: PromotionRecordSchema['_id'];
type: PromotionRecordSchema['type']; type: PromotionRecordSchema['type'];
......
...@@ -28,6 +28,7 @@ import { ...@@ -28,6 +28,7 @@ import {
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant'; import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type'; import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type';
import type { OutLinkUpdateBodyType } from '@fastgpt/global/openapi/support/outLink/api';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
...@@ -158,15 +159,17 @@ const Share = ({ appId }: { appId: string; type: PublishChannelEnum }) => { ...@@ -158,15 +159,17 @@ const Share = ({ appId }: { appId: string; type: PublishChannelEnum }) => {
icon: 'delete', icon: 'delete',
type: 'danger', type: 'danger',
onClick: () => onClick: () =>
openConfirm(async () => { openConfirm({
setIsLoading(true); onConfirm: async () => {
try { setIsLoading(true);
await delShareChatById(item._id); try {
refetchShareChatList(); await delShareChatById(item._id);
} catch (error) { refetchShareChatList();
console.log(error); } catch (error) {
console.log(error);
}
setIsLoading(false);
} }
setIsLoading(false);
})() })()
} }
] ]
...@@ -256,7 +259,7 @@ function EditLinkModal({ ...@@ -256,7 +259,7 @@ function EditLinkModal({
}); });
const { mutate: onclickUpdate, isLoading: updating } = useRequest({ const { mutate: onclickUpdate, isLoading: updating } = useRequest({
mutationFn: (e: OutLinkEditType) => { mutationFn: (e: OutLinkEditType) => {
return putShareChat(e); return putShareChat(e as unknown as OutLinkUpdateBodyType);
}, },
errorToast: t('common:common.Update Failed'), errorToast: t('common:common.Update Failed'),
onSuccess: onEdit onSuccess: onEdit
......
import React, { useState } from 'react';
import {
Box,
Flex,
Button,
IconButton,
HStack,
ModalBody,
Checkbox,
ModalFooter
} from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { type AppSchema, type AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { useTranslation } from 'next-i18next';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import TagsEditModal from '../TagsEditModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { AppContext } from '@/pageComponents/app/detail/context';
import { useContextSelector } from 'use-context-selector';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { postTransition2Workflow } from '@/web/core/app/api/app';
import { form2AppWorkflow } from '@/web/core/app/utils';
import { type SimpleAppSnapshotType } from './useSnapshots';
import ExportConfigPopover from '@/pageComponents/app/detail/ExportConfigPopover';
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
const AppCard = ({
appForm,
setPast
}: {
appForm: AppSimpleEditFormType;
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => void;
}) => {
const router = useRouter();
const { t } = useTranslation();
const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp);
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
const onOpenInfoEdit = useContextSelector(AppContext, (v) => v.onOpenInfoEdit);
const onDelApp = useContextSelector(AppContext, (v) => v.onDelApp);
const appId = appDetail._id;
const { feConfigs } = useSystemStore();
const [TeamTagsSet, setTeamTagsSet] = useState<AppSchema>();
// transition to workflow
const [transitionCreateNew, setTransitionCreateNew] = useState<boolean>();
const { runAsync: onTransition, loading: transiting } = useRequest2(
async () => {
const { nodes, edges } = form2AppWorkflow(appForm, t);
await onSaveApp({
nodes,
edges,
chatConfig: appForm.chatConfig,
isPublish: false,
versionName: t('app:transition_to_workflow')
});
return postTransition2Workflow({ appId, createNew: transitionCreateNew });
},
{
onSuccess: ({ id }) => {
if (id) {
router.replace({
query: {
appId: id
}
});
} else {
setPast([]);
router.reload();
}
},
successToast: t('common:Success')
}
);
return (
<>
{/* basic info */}
<Box px={[4, 6]} py={4} position={'relative'}>
<Flex alignItems={'center'}>
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
<Box ml={3} fontWeight={'bold'} fontSize={'md'} flex={'1 0 0'} color={'myGray.900'}>
{appDetail.name}
</Box>
</Flex>
<Box
flex={1}
mt={3}
mb={4}
className={'textEllipsis3'}
wordBreak={'break-all'}
color={'myGray.600'}
fontSize={'xs'}
minH={'46px'}
>
应用ID: {appDetail._id}
</Box>
<Box
flex={1}
mt={3}
mb={4}
className={'textEllipsis3'}
wordBreak={'break-all'}
color={'myGray.600'}
fontSize={'xs'}
minH={'46px'}
>
{appDetail.intro || t('common:core.app.tip.Add a intro to app')}
</Box>
<HStack alignItems={'center'}>
<Button
size={['sm', 'md']}
variant={'whitePrimary'}
leftIcon={<MyIcon name={'core/chat/chatLight'} w={'16px'} />}
onClick={() =>
router.push(`/chat?appId=${appId}&pane=${ChatSidebarPaneEnum.RECENTLY_USED_APPS}`)
}
>
{t('common:core.Chat')}
</Button>
{appDetail.permission.hasManagePer && (
<Button
size={['sm', 'md']}
variant={'whitePrimary'}
leftIcon={<MyIcon name={'common/settingLight'} w={'16px'} />}
onClick={onOpenInfoEdit}
>
{t('common:Setting')}
</Button>
)}
{appDetail.permission.isOwner && (
<MyMenu
size={'xs'}
Button={
<IconButton
variant={'whitePrimary'}
size={['smSquare', 'mdSquare']}
icon={<MyIcon name={'more'} w={'1rem'} />}
aria-label={''}
/>
}
menuList={[
{
children: [
{
label: (
<Flex>
<ExportConfigPopover
appName={appDetail.name}
appForm={appForm}
chatConfig={appDetail.chatConfig}
/>
</Flex>
)
},
{
icon: 'core/app/type/workflow',
label: t('app:transition_to_workflow'),
onClick: () => setTransitionCreateNew(true)
},
...(appDetail.permission.hasWritePer && feConfigs?.show_team_chat
? [
{
icon: 'core/chat/fileSelect',
label: t('app:team_tags_set'),
onClick: () => setTeamTagsSet(appDetail)
}
]
: [])
]
},
{
children: [
{
icon: 'delete',
type: 'danger',
label: t('common:Delete'),
onClick: onDelApp
}
]
}
]}
/>
)}
<Box flex={1} />
{/* {isPc && ( */}
{/* <MyTag */}
{/* type="borderFill" */}
{/* colorSchema="gray" */}
{/* onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} */}
{/* > */}
{/* <PermissionIconText defaultPermission={appDetail.defaultPermission} /> */}
{/* </MyTag> */}
{/* )} */}
</HStack>
</Box>
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
{transitionCreateNew !== undefined && (
<MyModal isOpen title={t('app:transition_to_workflow')} iconSrc="core/app/type/workflow">
<ModalBody>
<Box mb={3}>{t('app:transition_to_workflow_create_new_tip')}</Box>
<HStack cursor={'pointer'} onClick={() => setTransitionCreateNew((state) => !state)}>
<Checkbox
isChecked={transitionCreateNew}
icon={<MyIcon name={'common/check'} w={'12px'} />}
/>
<Box>{t('app:transition_to_workflow_create_new_placeholder')}</Box>
</HStack>
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} onClick={() => setTransitionCreateNew(undefined)} mr={3}>
{t('common:Close')}
</Button>
<Button variant={'dangerFill'} isLoading={transiting} onClick={() => onTransition()}>
{t('common:Confirm')}
</Button>
</ModalFooter>
</MyModal>
)}
</>
);
};
export default React.memo(AppCard);
...@@ -63,7 +63,6 @@ import { ...@@ -63,7 +63,6 @@ import {
import { AppToolSourceEnum } from '@fastgpt/global/core/app/tool/constants'; import { AppToolSourceEnum } from '@fastgpt/global/core/app/tool/constants';
import { getAppPermission } from '@/web/core/app/api'; import { getAppPermission } from '@/web/core/app/api';
import { ObjectIdSchema } from '@fastgpt/global/common/type/mongo'; import { ObjectIdSchema } from '@fastgpt/global/common/type/mongo';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { SystemToolVersionType } from '@fastgpt/global/core/app/tool/systemTool/type/base'; import type { SystemToolVersionType } from '@fastgpt/global/core/app/tool/systemTool/type/base';
import DebugToolTag from '@fastgpt/web/components/core/plugin/tool/DebugToolTag'; import DebugToolTag from '@fastgpt/web/components/core/plugin/tool/DebugToolTag';
import type { WorkflowCheckIssue } from '@fastgpt/global/core/workflow/type/node'; import type { WorkflowCheckIssue } from '@fastgpt/global/core/workflow/type/node';
......
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, HStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyBox from '@fastgpt/web/components/common/MyBox';
import React from 'react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { type NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node';
import { type PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
const PluginCard = ({
item,
groups
}: {
item: NodeTemplateListItemType;
groups: PluginGroupSchemaType[];
}) => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const type = groups.reduce<string | undefined>((acc, group) => {
const foundType = group.groupTypes.find((type) => type.typeId === item.templateType);
return foundType ? foundType.typeName : acc;
}, undefined);
return (
<MyBox
key={item.id}
lineHeight={1.5}
h="100%"
pt={4}
pb={3}
px={4}
border={'base'}
boxShadow={'2'}
bg={'white'}
borderRadius={'10px'}
position={'relative'}
display={'flex'}
flexDirection={'column'}
_hover={{
borderColor: 'primary.300',
boxShadow: '1.5'
}}
>
<HStack>
<Avatar src={item.avatar} borderRadius={'sm'} w={'1.5rem'} h={'1.5rem'} />
<Box flex={'1 0 0'} color={'myGray.900'} fontWeight={500}>
{item.name}
</Box>
<Box mr={'-1rem'}>
<Flex
bg={'myGray.100'}
color={'myGray.600'}
py={0.5}
pl={2}
pr={3}
borderLeftRadius={'sm'}
whiteSpace={'nowrap'}
>
<Box ml={1} fontSize={'mini'}>
{t(type as any)}
</Box>
</Flex>
</Box>
</HStack>
<Box
flex={['1 0 48px', '1 0 56px']}
mt={3}
pr={1}
textAlign={'justify'}
wordBreak={'break-all'}
fontSize={'xs'}
color={'myGray.500'}
>
<Box className={'textEllipsis2'}>{item.intro || t('app:templateMarket.no_intro')}</Box>
</Box>
<Flex w={'full'} fontSize={'mini'}>
<Flex flex={1}>
{(item.instructions || item.courseUrl) && (
<UseGuideModal
title={item.name}
iconSrc={item.avatar}
text={item.instructions}
link={item.courseUrl}
>
{({ onClick }) => (
<Flex
color={'primary.700'}
alignItems={'center'}
gap={1}
cursor={'pointer'}
onClick={onClick}
_hover={{ bg: 'myGray.100' }}
>
<MyIcon name={'book'} w={'14px'} />
{t('app:plugin.Instructions')}
</Flex>
)}
</UseGuideModal>
)}
</Flex>
<Box color={'myGray.500'}>{`by ${feConfigs.systemTitle}`}</Box>
</Flex>
</MyBox>
);
};
export default React.memo(PluginCard);
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
type ParentIdType, type ParentIdType,
type ParentTreePathItemType type ParentTreePathItemType
} from '@fastgpt/global/common/parentFolder/type'; } from '@fastgpt/global/common/parentFolder/type';
import { type AppUpdateParams } from '@/global/core/app/api'; import type { UpdateAppBodyType } from '@fastgpt/global/openapi/core/app/common/api';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
...@@ -25,7 +25,7 @@ type AppListContextType = { ...@@ -25,7 +25,7 @@ type AppListContextType = {
isFetchingApps: boolean; isFetchingApps: boolean;
folderDetail: AppDetailType | undefined | null; folderDetail: AppDetailType | undefined | null;
paths: ParentTreePathItemType[]; paths: ParentTreePathItemType[];
onUpdateApp: (id: string, data: AppUpdateParams) => Promise<any>; onUpdateApp: (id: string, data: UpdateAppBodyType) => Promise<any>;
setMoveAppId: React.Dispatch<React.SetStateAction<string | undefined>>; setMoveAppId: React.Dispatch<React.SetStateAction<string | undefined>>;
refetchFolderDetail: () => Promise<AppDetailType | null>; refetchFolderDetail: () => Promise<AppDetailType | null>;
searchKey: string; searchKey: string;
...@@ -41,7 +41,7 @@ export const AppListContext = createContext<AppListContextType>({ ...@@ -41,7 +41,7 @@ export const AppListContext = createContext<AppListContextType>({
isFetchingApps: false, isFetchingApps: false,
folderDetail: undefined, folderDetail: undefined,
paths: [], paths: [],
onUpdateApp: function (id: string, data: AppUpdateParams): Promise<any> { onUpdateApp: function (id: string, data: UpdateAppBodyType): Promise<any> {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
setMoveAppId: function (value: React.SetStateAction<string | undefined>): void { setMoveAppId: function (value: React.SetStateAction<string | undefined>): void {
...@@ -74,8 +74,8 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => { ...@@ -74,8 +74,8 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
() => { () => {
const formatType = (() => { const formatType = (() => {
if (!type || type === 'all') return undefined; if (!type || type === 'all') return undefined;
if (type === AppTypeEnum.plugin) if (type === AppTypeEnum.workflowTool)
return [AppTypeEnum.folder, AppTypeEnum.plugin, AppTypeEnum.httpPlugin]; return [AppTypeEnum.folder, AppTypeEnum.workflowTool, AppTypeEnum.httpPlugin];
return [AppTypeEnum.folder, type]; return [AppTypeEnum.folder, type];
})(); })();
...@@ -108,7 +108,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => { ...@@ -108,7 +108,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
} }
); );
const { runAsync: onUpdateApp } = useRequest2((id: string, data: AppUpdateParams) => const { runAsync: onUpdateApp } = useRequest2((id: string, data: UpdateAppBodyType) =>
putAppById(id, data).then(async (res) => { putAppById(id, data).then(async (res) => {
await Promise.all([refetchFolderDetail(), refetchPaths(), loadMyApps()]); await Promise.all([refetchFolderDetail(), refetchPaths(), loadMyApps()]);
return res; return res;
...@@ -145,6 +145,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => { ...@@ -145,6 +145,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
// Clear search key when parentId changes // Clear search key when parentId changes
useEffect(() => { useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setSearchKey(''); setSearchKey('');
}, [parentId]); }, [parentId]);
......
...@@ -24,7 +24,6 @@ import { ...@@ -24,7 +24,6 @@ import {
getCollaboratorList, getCollaboratorList,
postUpdateDatasetCollaborators postUpdateDatasetCollaborators
} from '@/web/core/dataset/api/collaborator'; } from '@/web/core/dataset/api/collaborator';
import { getModelProvider } from '@fastgpt/global/core/ai/provider';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import UserBox from '@fastgpt/web/components/common/UserBox'; import UserBox from '@fastgpt/web/components/common/UserBox';
......
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { uploadFile } from '@fastgpt/service/common/file/gridfs/controller';
import { getUploadModel } from '@fastgpt/service/common/file/multer';
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
import { NextAPI } from '@/service/middleware/entry';
import { createFileToken } from '@fastgpt/service/support/permission/controller';
import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
import { addLog } from '@fastgpt/service/common/system/log';
import { authFrequencyLimit } from '@/service/common/frequencyLimit/api';
import { addSeconds } from 'date-fns';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
export type UploadChatFileProps = {
appId: string;
} & OutLinkChatAuthProps;
export type UploadDatasetFileProps = {
datasetId: string;
};
const authUploadLimit = (tmbId: string) => {
if (!global.feConfigs.uploadFileMaxAmount) return;
return authFrequencyLimit({
eventId: `${tmbId}-uploadfile`,
maxAmount: global.feConfigs.uploadFileMaxAmount * 2,
expiredTime: addSeconds(new Date(), 30) // 30s
});
};
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const filePaths: string[] = [];
try {
const start = Date.now();
/* Creates the multer uploader */
const upload = getUploadModel({
maxSize: global.feConfigs?.uploadFileMaxSize
});
const { file, bucketName, metadata, data } = await upload.getUploadFile<
UploadChatFileProps | UploadDatasetFileProps
>(req, res);
filePaths.push(file.path);
const { teamId, uid } = await (async () => {
if (bucketName === 'chat') {
const chatData = data as UploadChatFileProps;
const authData = await authChatCrud({
req,
authToken: true,
authApiKey: true,
...chatData
});
return {
teamId: authData.teamId,
uid: authData.uid
};
}
if (bucketName === 'dataset') {
const chatData = data as UploadDatasetFileProps;
const authData = await authDataset({
datasetId: chatData.datasetId,
per: WritePermissionVal,
req,
authToken: true,
authApiKey: true
});
return {
teamId: authData.teamId,
uid: authData.tmbId
};
}
return Promise.reject('bucketName is empty');
})();
//await authUploadLimit(uid);
addLog.info(`Upload file success ${file.originalname}, cost ${Date.now() - start}ms`);
if (!bucketName) {
throw new Error('bucketName is empty');
}
const fileId = await uploadFile({
teamId,
uid,
bucketName,
path: file.path,
filename: file.originalname,
contentType: file.mimetype,
metadata: metadata
});
jsonRes(res, {
data: {
fileId,
previewUrl: `${ReadFileBaseUrl}/${file.originalname}?token=${await createFileToken({
bucketName,
teamId,
uid,
fileId
})}`
}
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
removeFilesByPaths(filePaths);
}
export default NextAPI(handler);
export const config = {
api: {
bodyParser: false
}
};
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { type ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { countGptMessagesTokens } from '@fastgpt/service/common/string/tiktoken';
export type tokenQuery = {};
export type tokenBody = {
messages: ChatCompletionMessageParam[];
};
export type tokenResponse = {};
async function handler(
req: ApiRequestProps<tokenBody, tokenQuery>,
res: ApiResponseType<any>
): Promise<tokenResponse> {
const start = Date.now();
await authCert({ req, authRoot: true });
const tokens = await countGptMessagesTokens(req.body.messages);
return {
tokens,
time: Date.now() - start,
memory: process.memoryUsage()
};
}
export default NextAPI(handler);
export const config = {
api: {
bodyParser: {
sizeLimit: '200mb'
},
responseLimit: '200mb'
}
};
import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import type { GetChatSpeechProps } from '@/global/core/chat/api';
import { text2Speech } from '@fastgpt/service/core/ai/audio/speech';
import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import { authType2UsageSource } from '@/service/support/wallet/usage/utils';
import { getTTSModel } from '@fastgpt/service/core/ai/model';
import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
/*
1. get tts from chatItem store
2. get tts from ai
4. push bill
*/
async function handler(req: ApiRequestProps<GetChatSpeechProps>, res: NextApiResponse) {
try {
const { ttsConfig, input } = req.body;
if (!ttsConfig.model || !ttsConfig.voice) {
throw new Error('model or voice not found');
}
const { teamId, tmbId, authType } = await authChatCrud({
req,
authToken: true,
authApiKey: true,
...req.body
});
// 获取团队的openaiAccount信息
const team = await MongoTeam.findById(teamId, 'openaiAccount').lean();
const userKey = team?.openaiAccount;
const ttsModel = getTTSModel(ttsConfig.model);
const voiceData = ttsModel.voices?.find((item) => item.value === ttsConfig.voice);
if (!voiceData) {
throw new Error('voice not found');
}
const bufferId = `${ttsModel.model}-${ttsConfig.voice}`;
/* get audio from buffer */
const ttsBuffer = await MongoTTSBuffer.findOne(
{
bufferId,
text: JSON.stringify({ text: input, speed: ttsConfig.speed })
},
'buffer'
);
if (ttsBuffer?.buffer) {
return res.end(new Uint8Array(ttsBuffer.buffer.buffer));
}
/* request audio */
await text2Speech({
res,
input,
model: ttsConfig.model,
voice: ttsConfig.voice,
speed: ttsConfig.speed,
userKey,
onSuccess: async ({ model, buffer }) => {
try {
/* bill */
pushAudioSpeechUsage({
model: model,
charsLength: input.length,
tmbId,
teamId,
source: authType2UsageSource({ authType })
});
/* create buffer */
await MongoTTSBuffer.create(
{
bufferId,
text: JSON.stringify({ text: input, speed: ttsConfig.speed }),
buffer
},
ttsModel.requestUrl && ttsModel.requestAuth
? {
path: ttsModel.requestUrl,
headers: {
Authorization: `Bearer ${ttsModel.requestAuth}`
}
}
: {}
);
} catch (error) {}
},
onError: (err) => {
jsonRes(res, {
code: 500,
error: err
});
}
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}
// 不能使用 NextApiResponse
export default handler;
import { NextAPI } from '@/service/middleware/entry';
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
import {
type DatasetCiteItemType,
type DatasetDataSchemaType
} from '@fastgpt/global/core/dataset/type';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import {
type LinkedListResponse,
type LinkedPaginationProps
} from '@fastgpt/web/common/fetch/type';
import { type FilterQuery, Types } from 'mongoose';
import { quoteDataFieldSelector } from '@/service/core/chat/constants';
import { processChatTimeFilter } from '@/service/core/chat/utils';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
export type GetCollectionQuoteProps = LinkedPaginationProps & {
chatId: string;
chatItemDataId: string;
collectionId: string;
appId: string;
shareId?: string;
outLinkUid?: string;
teamId?: string;
teamToken?: string;
};
export type GetCollectionQuoteRes = LinkedListResponse<DatasetCiteItemType>;
type BaseMatchType = FilterQuery<DatasetDataSchemaType>;
async function handler(
req: ApiRequestProps<GetCollectionQuoteProps>
): Promise<GetCollectionQuoteRes> {
const {
initialId,
initialIndex,
prevId,
prevIndex,
nextId,
nextIndex,
collectionId,
chatItemDataId,
appId,
chatId,
shareId,
outLinkUid,
teamId,
teamToken,
pageSize = 15
} = req.body;
const limitedPageSize = Math.min(pageSize, 30);
const [collection, { chat, showRawSource }, { chatItem }] = await Promise.all([
getCollectionWithDataset(collectionId),
authChatCrud({
req,
authToken: true,
appId,
chatId,
shareId,
outLinkUid,
teamId,
teamToken
}),
authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
]);
if (!showRawSource) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
if (!chat) return Promise.reject(ChatErrEnum.unAuthChat);
const baseMatch: BaseMatchType = {
teamId: collection.teamId,
datasetId: collection.datasetId,
collectionId,
$or: [
{ updateTime: { $lt: new Date(chatItem.time) } },
{ history: { $elemMatch: { updateTime: { $lt: new Date(chatItem.time) } } } }
]
};
if (initialId && initialIndex !== undefined) {
return await handleInitialLoad({
initialId,
initialIndex,
pageSize: limitedPageSize,
chatTime: chatItem.time,
baseMatch
});
}
if ((prevId && prevIndex !== undefined) || (nextId && nextIndex !== undefined)) {
return await handlePaginatedLoad({
prevId,
prevIndex,
nextId,
nextIndex,
pageSize: limitedPageSize,
chatTime: chatItem.time,
baseMatch
});
}
return { list: [], hasMorePrev: false, hasMoreNext: false };
}
export default NextAPI(handler);
async function handleInitialLoad({
initialId,
initialIndex,
pageSize,
chatTime,
baseMatch
}: {
initialId: string;
initialIndex: number;
pageSize: number;
chatTime: Date;
baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> {
const centerNode = await MongoDatasetData.findOne(
{
_id: new Types.ObjectId(initialId)
},
quoteDataFieldSelector
).lean();
if (!centerNode) {
const list = await MongoDatasetData.find(baseMatch, quoteDataFieldSelector)
.sort({ chunkIndex: 1, _id: 1 })
.limit(pageSize)
.lean();
const hasMoreNext = list.length === pageSize;
return {
list: processChatTimeFilter(getFormatDatasetCiteList(list), chatTime),
hasMorePrev: false,
hasMoreNext
};
}
const prevHalfSize = Math.floor(pageSize / 2);
const nextHalfSize = pageSize - prevHalfSize - 1;
const { list: prevList, hasMore: hasMorePrev } = await getPrevNodes(
initialId,
initialIndex,
prevHalfSize,
baseMatch
);
const { list: nextList, hasMore: hasMoreNext } = await getNextNodes(
initialId,
initialIndex,
nextHalfSize,
baseMatch
);
const resultList = [...prevList, centerNode, ...nextList];
return {
list: processChatTimeFilter(getFormatDatasetCiteList(resultList), chatTime),
hasMorePrev,
hasMoreNext
};
}
async function handlePaginatedLoad({
prevId,
prevIndex,
nextId,
nextIndex,
pageSize,
chatTime,
baseMatch
}: {
prevId: string | undefined;
prevIndex: number | undefined;
nextId: string | undefined;
nextIndex: number | undefined;
pageSize: number;
chatTime: Date;
baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> {
const { list, hasMore } =
prevId && prevIndex !== undefined
? await getPrevNodes(prevId, prevIndex, pageSize, baseMatch)
: await getNextNodes(nextId!, nextIndex!, pageSize, baseMatch);
const processedList = processChatTimeFilter(getFormatDatasetCiteList(list), chatTime);
return {
list: processedList,
hasMorePrev: !!prevId && hasMore,
hasMoreNext: !!nextId && hasMore
};
}
async function getPrevNodes(
initialId: string,
initialIndex: number,
limit: number,
baseMatch: BaseMatchType
): Promise<{
list: DatasetDataSchemaType[];
hasMore: boolean;
}> {
const match: BaseMatchType = {
...baseMatch,
$or: [
{ chunkIndex: { $lt: initialIndex } },
{ chunkIndex: initialIndex, _id: { $lt: new Types.ObjectId(initialId) } }
]
};
const list = await MongoDatasetData.find(match, quoteDataFieldSelector)
.sort({ chunkIndex: -1, _id: -1 })
.limit(limit)
.lean();
return {
list: list.filter((item) => String(item._id) !== initialId).reverse(),
hasMore: list.length === limit
};
}
async function getNextNodes(
initialId: string,
initialIndex: number,
limit: number,
baseMatch: BaseMatchType
): Promise<{
list: DatasetDataSchemaType[];
hasMore: boolean;
}> {
const match: BaseMatchType = {
...baseMatch,
$or: [
{ chunkIndex: { $gt: initialIndex } },
{ chunkIndex: initialIndex, _id: { $gt: new Types.ObjectId(initialId) } }
]
};
const list = await MongoDatasetData.find(match, quoteDataFieldSelector)
.sort({ chunkIndex: 1, _id: 1 })
.limit(limit)
.lean();
return {
list: list.filter((item) => String(item._id) !== initialId),
hasMore: list.length === limit
};
}
import type { InitChatResponse, InitTeamChatProps } from '@/global/core/chat/api'; import type { InitChatResponse, InitTeamChatProps } from '@/global/core/chat/api';
import { getChatModelNameListByModules } from '@/service/core/app/workflow'; import { getChatModelNameListByModules } from '@/service/core/app/workflow';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/next/type';
import { authTeamSpaceToken } from '@/service/support/permission/auth/team'; import { authTeamSpaceToken } from '@fastgpt/service/support/permission/auth/team';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
...@@ -15,7 +15,7 @@ import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema'; ...@@ -15,7 +15,7 @@ import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
async function handler(req: ApiRequestProps<InitTeamChatProps>, res: NextApiResponse) { async function handler(req: ApiRequestProps<InitTeamChatProps>, res: NextApiResponse) {
let { teamId, appId, chatId, teamToken } = req.query; const { teamId, appId, chatId, teamToken } = req.query;
if (!teamId || !appId || !teamToken) { if (!teamId || !appId || !teamToken) {
return Promise.reject('teamId, appId, teamToken are required'); return Promise.reject('teamId, appId, teamToken are required');
......
...@@ -26,7 +26,6 @@ async function handler(req: ApiRequestProps) { ...@@ -26,7 +26,6 @@ async function handler(req: ApiRequestProps) {
}); });
const deleteDatasets = await findDatasetAndAllChildren({ const deleteDatasets = await findDatasetAndAllChildren({
teamId,
datasetId, datasetId,
fields: '_id' fields: '_id'
}); });
......
...@@ -35,12 +35,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) { ...@@ -35,12 +35,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { datasetId } = ExportDatasetQuerySchema.parse(req.query); const { datasetId } = ExportDatasetQuerySchema.parse(req.query);
// 凭证校验 // 凭证校验
// const { teamId } = await authDataset({ const { teamId } = await authDataset({
// req, req,
// authToken: true, authToken: true,
// datasetId, datasetId,
// per: WritePermissionVal per: WritePermissionVal
// }); });
// await checkExportDatasetLimit({ // await checkExportDatasetLimit({
// teamId, // teamId,
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoChatFeedbackLog } from '@fastgpt/service/core/chat/chatfeedbacklogSchema'; import { MongoChatFeedbackLog } from '@fastgpt/service/core/chat/chatfeedbacklogSchema';
import { MongoChatFeedbackLogRelation } from '@fastgpt/service/core/chat/chatfeedbacklogrelationSchema'; import { MongoChatFeedbackLogRelation } from '@fastgpt/service/core/chat/chatfeedbacklogrelationSchema';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller'; import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { ChatFeedbackStatusEnum } from '@fastgpt/global/core/chat/constants'; import { ChatFeedbackStatusEnum } from '@fastgpt/global/core/chat/constants';
import { insertData2Dataset } from '@/service/core/dataset/data/controller'; import { createDatasetData } from '@/service/core/dataset/data/data';
import { simpleText } from '@fastgpt/global/common/string/tools'; import { simpleText } from '@fastgpt/global/common/string/tools';
import { connectionMongo } from '@fastgpt/service/common/mongo'; import { connectionMongo } from '@fastgpt/service/common/mongo';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
...@@ -41,7 +41,7 @@ async function getDataset(datasetId: string) { ...@@ -41,7 +41,7 @@ async function getDataset(datasetId: string) {
// 获取或创建数据集集合 // 获取或创建数据集集合
async function getOrCreateCollection(datasetId: string, feedbackLog: any) { async function getOrCreateCollection(datasetId: string, feedbackLog: any) {
const relation = await MongoChatFeedbackLogRelation.findOne({ datasetId }).lean(); const relation = await MongoChatFeedbackLogRelation.findOne({ datasetId }).lean();
let collection = relation const collection = relation
? await MongoDatasetCollection.findById(relation.collectionId).lean() ? await MongoDatasetCollection.findById(relation.collectionId).lean()
: null; : null;
let collectionId = collection?._id || ''; let collectionId = collection?._id || '';
...@@ -79,7 +79,7 @@ async function handler(req: ApiRequestProps<ApproveBody>) { ...@@ -79,7 +79,7 @@ async function handler(req: ApiRequestProps<ApproveBody>) {
const formatQ = simpleText(q); const formatQ = simpleText(q);
const formatA = simpleText(a); const formatA = simpleText(a);
try { try {
await insertData2Dataset({ await createDatasetData({
teamId: feedbackLog.teamId, teamId: feedbackLog.teamId,
tmbId: feedbackLog.tmbId, tmbId: feedbackLog.tmbId,
datasetId, datasetId,
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { MongoChatFeedbackLog } from '@fastgpt/service/core/chat/chatfeedbacklogSchema'; import { MongoChatFeedbackLog } from '@fastgpt/service/core/chat/chatfeedbacklogSchema';
import { MongoChatFeedbackLogRelation } from '@fastgpt/service/core/chat/chatfeedbacklogrelationSchema'; import { MongoChatFeedbackLogRelation } from '@fastgpt/service/core/chat/chatfeedbacklogrelationSchema';
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller'; import { parseHeaderCert } from '@fastgpt/service/support/permission/auth/common';
export type CollaboratorQuery = { export type CollaboratorQuery = {
tmbId: string; tmbId: string;
appId: string; appId: string;
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import type { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator'; import type { CollaboratorItemDetailType } from '@fastgpt/global/support/permission/collaborator';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
...@@ -44,7 +44,7 @@ async function handler(req: ApiRequestProps<any, CollaboratorBody>) { ...@@ -44,7 +44,7 @@ async function handler(req: ApiRequestProps<any, CollaboratorBody>) {
} }
])) as CollaboratorType[]; ])) as CollaboratorType[];
const list: CollaboratorItemType[] = collaborators.map((item) => ({ const list: CollaboratorItemDetailType[] = collaborators.map((item) => ({
...item, ...item,
permission: new Permission({ role: item.permission }) permission: new Permission({ role: item.permission })
})); }));
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import type { UpdateAppCollaboratorBody } from '@fastgpt/global/core/app/collaborator'; import type { UpdateAppCollaboratorBody } from '@fastgpt/global/core/app/collaborator';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller'; import { parseHeaderCert } from '@fastgpt/service/support/permission/auth/common';
async function handler(req: ApiRequestProps<UpdateAppCollaboratorBody>) { async function handler(req: ApiRequestProps<UpdateAppCollaboratorBody>) {
const { appId, groups, members, orgs, permission } = req.body; const { appId, groups, members, orgs, permission } = req.body;
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { getUserAppPermission } from '@fastgpt/service/support/permission/app/getUserAppPermission'; import { getUserAppPermission } from '@fastgpt/service/support/permission/app/getUserAppPermission';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import type { DatasetCollaboratorDeleteParams } from '@fastgpt/global/core/dataset/collaborator'; import type { DatasetCollaboratorDeleteParams } from '@fastgpt/global/core/dataset/collaborator';
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller'; import { parseHeaderCert } from '@fastgpt/service/support/permission/auth/common';
async function handler(req: ApiRequestProps<any, DatasetCollaboratorDeleteParams>) { async function handler(req: ApiRequestProps<any, DatasetCollaboratorDeleteParams>) {
const { tmbId, datasetId } = req.query; const { tmbId, datasetId } = req.query;
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import type { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator'; import type { CollaboratorItemDetailType } from '@fastgpt/global/support/permission/collaborator';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
...@@ -44,7 +44,7 @@ async function handler(req: ApiRequestProps<any, CollaboratorQuery>) { ...@@ -44,7 +44,7 @@ async function handler(req: ApiRequestProps<any, CollaboratorQuery>) {
} }
])) as CollaboratorType[]; ])) as CollaboratorType[];
const list: CollaboratorItemType[] = collaborators.map((item) => ({ const list: CollaboratorItemDetailType[] = collaborators.map((item) => ({
...item, ...item,
permission: new Permission({ role: item.permission }) permission: new Permission({ role: item.permission })
})); }));
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { UpdateDatasetCollaboratorBody } from '@fastgpt/global/core/dataset/collaborator'; import type { UpdateDatasetCollaboratorBody } from '@fastgpt/global/core/dataset/collaborator';
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller'; import { parseHeaderCert } from '@fastgpt/service/support/permission/auth/common';
async function handler(req: ApiRequestProps<UpdateDatasetCollaboratorBody>) { async function handler(req: ApiRequestProps<UpdateDatasetCollaboratorBody>) {
const { datasetId, groups, members, orgs, permission } = req.body; const { datasetId, groups, members, orgs, permission } = req.body;
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { getUserDatasetCollaboratorPermission } from '@fastgpt/service/support/permission/dataset/getDatasetPermissions'; import { getUserDatasetCollaboratorPermission } from '@fastgpt/service/support/permission/dataset/getDatasetPermissions';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
export type GroupBody = { export type GroupBody = {
searchKey?: string; searchKey?: string;
}; };
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import type { TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; import type { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import type { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import type { TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
...@@ -60,7 +60,7 @@ async function handler( ...@@ -60,7 +60,7 @@ async function handler(
avatar: item.avatar, avatar: item.avatar,
createTime: item.createTime, createTime: item.createTime,
memberName: item.name, memberName: item.name,
orgs: [] as String[], orgs: [] as string[],
role: item.role, role: item.role,
status: item.status, status: item.status,
teamId: item.teamId, teamId: item.teamId,
......
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/type/next'; import type { ApiRequestProps } from '@fastgpt/next/type';
export type OrgBody = { export type OrgBody = {
searchKey?: string; searchKey?: string;
}; };
......
import React, { useCallback, useEffect } from 'react';
import { useRouter } from 'next/router';
import { postFastLogin } from '@/web/support/user/api';
import { clearToken, setToken } from '@/web/support/user/auth';
import { useUserStore } from '@/web/support/user/useUserStore';
import { serviceSideProps } from '@/web/common/i18n/utils';
import { getErrText } from '@fastgpt/global/common/error/utils';
import Loading from '@fastgpt/web/components/common/MyLoading';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useTranslation } from 'next-i18next';
import { validateRedirectUrl } from '@/web/common/utils/uri';
import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api';
import { useLoginRedirectAfterLogin } from '@/web/support/user/loginRedirect';
import type { LangEnum } from '@fastgpt/global/common/i18n/type';
import { getFastGPTSem, onFastGPTLoginSuccess } from '@/web/support/marketing/utils';
const FastLogin = ({
code,
token,
callbackUrl,
lastTmbId
}: {
code: string;
token: string;
callbackUrl: string;
lastTmbId?: string;
}) => {
const { setUserInfo } = useUserStore();
const router = useRouter();
const { toast } = useToast();
const { t, i18n } = useTranslation();
const resolveLoginRedirect = useLoginRedirectAfterLogin();
const loginSuccess = useCallback(
async (res: LoginSuccessResponseType) => {
const safeCallbackUrl = validateRedirectUrl(callbackUrl);
const targetRoute = await resolveLoginRedirect({
user: res.user,
fallbackRoute: safeCallbackUrl,
lastTmbId
});
setUserInfo(res.user);
if (targetRoute) {
setTimeout(() => {
router.push(targetRoute);
}, 100);
}
},
[callbackUrl, lastTmbId, resolveLoginRedirect, router, setUserInfo]
);
const authCode = useCallback(
async (code: string, token: string) => {
try {
const res = await postFastLogin({
code,
token,
fastgpt_sem: getFastGPTSem(),
language: i18n.language as LangEnum
});
if (!res) {
toast({
status: 'warning',
title: t('common:support.user.login.error')
});
return setTimeout(() => {
router.replace('/login');
}, 1000);
}
await onFastGPTLoginSuccess(loginSuccess, res);
} catch (error) {
toast({
status: 'warning',
title: getErrText(error, t('common:support.user.login.error'))
});
setTimeout(() => {
router.replace('/login');
}, 1000);
}
},
[i18n.language, loginSuccess, router, t, toast]
);
useEffect(() => {
clearToken();
router.prefetch(callbackUrl);
setToken(token);
setTimeout(() => {
router.push(decodeURIComponent(callbackUrl + '?token=' + token));
}, 100);
// authCode(code, token);
}, [callbackUrl, code, router, token]);
return <Loading />;
};
export async function getServerSideProps(content: any) {
return {
props: {
code: content?.query?.code || '',
token: content?.query?.token || '',
callbackUrl: content?.query?.callbackUrl || '/dashboard/agent',
lastTmbId: content?.query?.lastTmbId || '',
...(await serviceSideProps(content, ['login']))
}
};
}
export default FastLogin;
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { GET } from '@fastgpt/service/common/api/plusRequest';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
type AuthTeamTagTokenProps = {
teamId: string;
teamToken: string;
};
export function authTeamTagToken(data: AuthTeamTagTokenProps) {
return GET<{ uid: string }>('/support/user/team/tag/authTeamToken', data);
}
export async function authTeamSpaceToken({
teamId,
teamToken
}: {
teamId: string;
teamToken: string;
}) {
// get outLink and app
const [{ uid }, member] = await Promise.all([
authTeamTagToken({ teamId, teamToken }),
MongoTeamMember.findOne({ teamId, role: TeamMemberRoleEnum.owner }, 'tmbId').lean()
]);
return {
uid,
tmbId: member?._id!
};
}
import { GET, POST } from '@/web/common/api/request';
import type { createHttpPluginBody } from '@/pages/api/core/app/httpPlugin/create';
import type { UpdateHttpPluginBody } from '@/pages/api/core/app/httpPlugin/update';
import type {
FlowNodeTemplateType,
NodeTemplateListItemType
} from '@fastgpt/global/core/workflow/type/node';
import { getAppDetailById, getMyApps } from '../api';
import type { ListAppBody } from '@/pages/api/core/app/list';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
import type { GetPreviewNodeQuery } from '@/pages/api/core/app/plugin/getPreviewNode';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import type {
GetPathProps,
ParentIdType,
ParentTreePathItemType
} from '@fastgpt/global/common/parentFolder/type';
import type { GetSystemPluginTemplatesBody } from '@/pages/api/core/app/plugin/getSystemPluginTemplates';
import type { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { defaultGroup } from '@fastgpt/web/core/workflow/constants';
import type { createMCPToolsBody } from '@/pages/api/core/app/mcpTools/create';
import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
import type { updateMCPToolsBody } from '@/pages/api/core/app/mcpTools/update';
import type { RunMCPToolBody } from '@/pages/api/support/mcp/client/runTool';
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
import type {
getToolVersionListProps,
getToolVersionResponse
} from '@/pages/api/core/app/plugin/getVersionList';
import type { McpGetChildrenmResponse } from '@/pages/api/core/app/mcpTools/getChildren';
/* ============ team plugin ============== */
export const getTeamPlugTemplates = async (data?: {
parentId?: ParentIdType;
searchKey?: string;
}) => {
if (data?.parentId) {
// handle get mcptools
const app = await getAppDetailById(data.parentId);
if (app.type === AppTypeEnum.toolSet) {
const children = await getMcpChildren(data.parentId);
return children.map((item) => ({
...item,
flowNodeType: FlowNodeTypeEnum.tool,
templateType: FlowNodeTemplateTypeEnum.teamApp
}));
}
}
return getMyApps(data).then((res) =>
res.map((app) => ({
tmbId: app.tmbId,
id: app._id,
pluginId: app._id,
isFolder:
app.type === AppTypeEnum.folder ||
app.type === AppTypeEnum.httpPlugin ||
app.type === AppTypeEnum.toolSet,
templateType: FlowNodeTemplateTypeEnum.teamApp,
flowNodeType:
app.type === AppTypeEnum.workflow
? FlowNodeTypeEnum.appModule
: app.type === AppTypeEnum.toolSet
? FlowNodeTypeEnum.toolSet
: FlowNodeTypeEnum.pluginModule,
avatar: app.avatar,
name: app.name,
intro: app.intro,
showStatus: false,
version: app.pluginData?.nodeVersion,
isTool: true,
sourceMember: app.sourceMember
}))
);
};
/* ============ system plugin ============== */
export const getSystemPlugTemplates = (data: GetSystemPluginTemplatesBody) =>
POST<NodeTemplateListItemType[]>('/core/app/plugin/getSystemPluginTemplates', data);
export const getPluginGroups = () => {
return Promise.resolve([defaultGroup]);
};
export const getSystemPluginPaths = (data: GetPathProps) => {
if (!data.sourceId) return Promise.resolve<ParentTreePathItemType[]>([]);
return GET<ParentTreePathItemType[]>('/core/app/plugin/path', data);
};
export const getPreviewPluginNode = (data: GetPreviewNodeQuery) =>
GET<FlowNodeTemplateType>('/core/app/plugin/getPreviewNode', data);
export const getToolVersionList = (data: getToolVersionListProps) =>
POST<getToolVersionResponse>('/core/app/plugin/getVersionList', data);
/* ============ mcp tools ============== */
export const postCreateMCPTools = (data: createMCPToolsBody) =>
POST('/core/app/mcpTools/create', data);
export const postUpdateMCPTools = (data: updateMCPToolsBody) =>
POST('/core/app/mcpTools/update', data);
export const getMCPTools = (data: getMCPToolsBody) =>
POST<McpToolConfigType[]>('/support/mcp/client/getTools', data);
export const postRunMCPTool = (data: RunMCPToolBody) =>
POST('/support/mcp/client/runTool', data, { timeout: 300000 });
export const getMcpChildren = (id: string) =>
GET<McpGetChildrenmResponse>('/core/app/mcpTools/getChildren', { id });
/* ============ http plugin ============== */
export const postCreateHttpPlugin = (data: createHttpPluginBody) =>
POST('/core/app/httpPlugin/create', data);
export const putUpdateHttpPlugin = (body: UpdateHttpPluginBody) =>
POST('/core/app/httpPlugin/update', body);
export const getApiSchemaByUrl = (url: string) =>
POST<Object>(
'/core/app/httpPlugin/getApiSchemaByUrl',
{ url },
{
timeout: 30000
}
);
import { useSystemStore } from '@/web/common/system/useSystemStore';
import {
ChatSidebarPaneEnum,
defaultCollapseStatus,
type CollapseStatusType
} from '@/pageComponents/chat/constants';
import { getChatSetting } from '@/web/core/chat/api';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import type { ChatSettingSchema } from '@fastgpt/global/core/chat/setting/type';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useRouter } from 'next/router';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { createContext } from 'use-context-selector';
type ChatSettingReturnType = ChatSettingSchema | undefined;
export type ChatSettingContextValue = {
pane: ChatSidebarPaneEnum;
handlePaneChange: (pane: ChatSidebarPaneEnum, _id?: string) => void;
collapse: CollapseStatusType;
onTriggerCollapse: () => void;
chatSettings: ChatSettingSchema | undefined;
refreshChatSetting: () => Promise<ChatSettingReturnType>;
logos: Pick<ChatSettingSchema, 'wideLogoUrl' | 'squareLogoUrl'>;
};
export const ChatSettingContext = createContext<ChatSettingContextValue>({
pane: ChatSidebarPaneEnum.HOME,
handlePaneChange: () => {},
collapse: defaultCollapseStatus,
onTriggerCollapse: () => {},
chatSettings: undefined,
refreshChatSetting: function (): Promise<ChatSettingReturnType> {
throw new Error('Function not implemented.');
},
logos: {
wideLogoUrl: '',
squareLogoUrl: ''
}
});
export const ChatSettingContextProvider = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
const { feConfigs } = useSystemStore();
const { appId, setLastPane, setLastChatAppId, lastPane } = useChatStore();
const { pane = lastPane || ChatSidebarPaneEnum.HOME } = router.query as {
pane: ChatSidebarPaneEnum;
};
const [collapse, setCollapse] = useState<CollapseStatusType>(defaultCollapseStatus);
const { data: chatSettings, runAsync: refreshChatSetting } = useRequest2(
async () => {
if (!feConfigs.isPlus) return;
return await getChatSetting();
},
{
manual: false,
refreshDeps: [feConfigs.isPlus],
onSuccess(data) {
if (!data) return;
// Reset home page appId
if (pane === ChatSidebarPaneEnum.HOME && appId !== data.appId) {
handlePaneChange(ChatSidebarPaneEnum.HOME, data.appId);
}
}
}
);
const handlePaneChange = useCallback(
async (newPane: ChatSidebarPaneEnum, id?: string) => {
if (newPane === pane && !id) return;
const _id = (() => {
if (id) return id;
const hiddenAppId = chatSettings?.appId;
if (newPane === ChatSidebarPaneEnum.HOME && hiddenAppId) {
return hiddenAppId;
}
return '';
})();
await router.replace({
query: {
appId: _id,
pane: newPane
}
});
setLastPane(newPane);
setLastChatAppId(_id);
},
[pane, router, setLastPane, setLastChatAppId, chatSettings?.appId]
);
useEffect(() => {
if (!Object.values(ChatSidebarPaneEnum).includes(pane)) {
handlePaneChange(ChatSidebarPaneEnum.HOME);
}
}, [pane]);
const logos: Pick<ChatSettingSchema, 'wideLogoUrl' | 'squareLogoUrl'> = useMemo(
() => ({
wideLogoUrl: chatSettings?.wideLogoUrl,
squareLogoUrl: chatSettings?.squareLogoUrl
}),
[chatSettings?.squareLogoUrl, chatSettings?.wideLogoUrl]
);
const value: ChatSettingContextValue = useMemo(
() => ({
pane,
handlePaneChange,
collapse,
onTriggerCollapse: () => setCollapse(collapse === 0 ? 1 : 0),
chatSettings,
refreshChatSetting,
logos
}),
[pane, handlePaneChange, collapse, chatSettings, refreshChatSetting, logos]
);
return <ChatSettingContext.Provider value={value}>{children}</ChatSettingContext.Provider>;
};
...@@ -29,7 +29,7 @@ export type DatasetPermissionState = { ...@@ -29,7 +29,7 @@ export type DatasetPermissionState = {
export const useDatasetPermission = (datasetId: string, autoCheck = true) => { export const useDatasetPermission = (datasetId: string, autoCheck = true) => {
const { data, loading, error, run } = useRequest2(() => checkDatasetPermission({ datasetId }), { const { data, loading, error, run } = useRequest2(() => checkDatasetPermission({ datasetId }), {
manual: !autoCheck, manual: !autoCheck,
onError: (err) => { onError: (err: any) => {
console.error('Failed to fetch dataset permission:', err); console.error('Failed to fetch dataset permission:', err);
} }
}); });
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"**/*.test.tsx", "**/*.test.tsx",
"../../packages/**/vitest*.config.ts", "../../packages/**/vitest*.config.ts",
"../../packages/**/test/**", "../../packages/**/test/**",
"../../packages/plugins/**",
".next", ".next",
"dist", "dist",
"coverage" "coverage"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment