mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
recent app
This commit is contained in:
parent
284ca367aa
commit
afd9e6c439
|
|
@ -0,0 +1,49 @@
|
|||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { getMongoModel, Schema } from '../../../common/mongo';
|
||||
|
||||
export const AppUsageCollectionName = 'app_usages';
|
||||
|
||||
const AppUsageSchema = new Schema(
|
||||
{
|
||||
tmbId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamMemberCollectionName,
|
||||
required: true
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'apps',
|
||||
required: true
|
||||
},
|
||||
lastUsedTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
minimize: false,
|
||||
timestamps: false
|
||||
}
|
||||
);
|
||||
|
||||
AppUsageSchema.index({ tmbId: 1, lastUsedTime: -1 }); // 查询用户最近使用的应用
|
||||
AppUsageSchema.index({ tmbId: 1, appId: 1 }, { unique: true }); // 防止重复记录
|
||||
AppUsageSchema.index({ teamId: 1, appId: 1 }); // 用于清理权限失效的记录
|
||||
|
||||
export const MongoAppUsage = getMongoModel<AppUsageType>(AppUsageCollectionName, AppUsageSchema);
|
||||
|
||||
export type AppUsageType = {
|
||||
_id?: string;
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
appId: string;
|
||||
lastUsedTime: Date;
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
import { MongoAppUsage } from './schema';
|
||||
|
||||
export const recordAppUsage = async ({
|
||||
appId,
|
||||
tmbId,
|
||||
teamId
|
||||
}: {
|
||||
appId: string;
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
}) => {
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoAppUsage.findOneAndUpdate(
|
||||
{ tmbId, appId },
|
||||
{
|
||||
$set: {
|
||||
teamId,
|
||||
lastUsedTime: new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
new: true,
|
||||
session
|
||||
}
|
||||
);
|
||||
|
||||
// 保留最新的50条记录,删除超出限制的旧记录
|
||||
const threshold = await MongoAppUsage.findOne(
|
||||
{ tmbId },
|
||||
{ lastUsedTime: 1 },
|
||||
{
|
||||
session,
|
||||
sort: { lastUsedTime: -1 },
|
||||
skip: 49,
|
||||
lean: true
|
||||
}
|
||||
);
|
||||
|
||||
if (threshold) {
|
||||
await MongoAppUsage.deleteMany(
|
||||
{
|
||||
tmbId,
|
||||
_id: { $ne: threshold._id },
|
||||
lastUsedTime: { $lte: threshold.lastUsedTime }
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -12,7 +12,7 @@ export const useChat = (appId: string) => {
|
|||
const [isInitedUser, setIsInitedUser] = useState(false);
|
||||
|
||||
// get app list
|
||||
const { data: myApps = [] } = useRequest2(() => getRecentlyUsedApps({ getRecentlyChat: true }), {
|
||||
const { data: myApps = [] } = useRequest2(() => getRecentlyUsedApps(), {
|
||||
manual: false,
|
||||
errorToast: '',
|
||||
refreshDeps: [userInfo],
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import { sumPer } from '@fastgpt/global/support/permission/utils';
|
|||
export type ListAppBody = {
|
||||
parentId?: ParentIdType;
|
||||
type?: AppTypeEnum | AppTypeEnum[];
|
||||
getRecentlyChat?: boolean;
|
||||
searchKey?: string;
|
||||
};
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ export type ListAppBody = {
|
|||
*/
|
||||
|
||||
async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemType[]> {
|
||||
const { parentId, type, getRecentlyChat, searchKey } = req.body;
|
||||
const { parentId, type, searchKey } = req.body;
|
||||
|
||||
// Auth user permission
|
||||
const [{ tmbId, teamId, permission: teamPer }] = await Promise.all([
|
||||
|
|
@ -94,14 +93,6 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||
);
|
||||
|
||||
const findAppsQuery = (() => {
|
||||
if (getRecentlyChat) {
|
||||
return {
|
||||
// get all chat app, excluding hidden apps and deleted apps
|
||||
teamId,
|
||||
type: { $in: [AppTypeEnum.workflow, AppTypeEnum.simple, AppTypeEnum.workflowTool] }
|
||||
};
|
||||
}
|
||||
|
||||
// Filter apps by permission, if not owner, only get apps that I have permission to access
|
||||
const idList = { _id: { $in: myPerList.map((item) => item.resourceId) } };
|
||||
const appPerQuery = teamPer.isOwner
|
||||
|
|
@ -153,7 +144,6 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||
};
|
||||
})();
|
||||
const limit = (() => {
|
||||
if (getRecentlyChat) return 15;
|
||||
if (searchKey) return 50;
|
||||
return;
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoAppUsage } from '@fastgpt/service/core/app/usage/schema';
|
||||
import { addSourceMember } from '@fastgpt/service/support/user/utils';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import type { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
export type GetRecentlyUsedAppsResponse = AppListItemType[];
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<{}, {}>,
|
||||
_res: ApiResponseType<GetRecentlyUsedAppsResponse>
|
||||
): Promise<GetRecentlyUsedAppsResponse> {
|
||||
const { tmbId, teamId } = await authUserPer({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
const recentUsages = await MongoAppUsage.find(
|
||||
{ tmbId },
|
||||
{ appId: 1 },
|
||||
{ sort: { lastUsedTime: -1 }, limit: 20 }
|
||||
).lean();
|
||||
|
||||
if (!recentUsages.length) return [];
|
||||
|
||||
const appIds = recentUsages.map((usage) => usage.appId);
|
||||
|
||||
// 并发检查权限
|
||||
const results = await Promise.allSettled(
|
||||
appIds.map((appId) =>
|
||||
authApp({ req, authToken: true, authApiKey: true, appId, per: ReadPermissionVal })
|
||||
.then(({ app }) => ({ appId, app }))
|
||||
.catch(() => ({ appId, app: null }))
|
||||
)
|
||||
);
|
||||
|
||||
const validApps: {
|
||||
appId: string;
|
||||
app: NonNullable<Awaited<ReturnType<typeof authApp>>['app']>;
|
||||
}[] = [];
|
||||
const invalidAppIds: string[] = [];
|
||||
|
||||
for (const result of results) {
|
||||
if (result.status === 'fulfilled') {
|
||||
const { appId, app } = result.value;
|
||||
if (app) {
|
||||
validApps.push({ appId, app });
|
||||
} else {
|
||||
invalidAppIds.push(appId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 异步清理无效记录
|
||||
if (invalidAppIds.length) {
|
||||
MongoAppUsage.deleteMany({ tmbId, teamId, appId: { $in: invalidAppIds } }).catch((err) =>
|
||||
console.error('Failed to clean invalid app usage records:', err)
|
||||
);
|
||||
}
|
||||
|
||||
return addSourceMember({
|
||||
list: validApps.map(({ app }) => ({
|
||||
_id: app._id,
|
||||
parentId: app.parentId,
|
||||
tmbId: app.tmbId,
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro,
|
||||
type: app.type,
|
||||
updateTime: app.updateTime,
|
||||
pluginData: app.pluginData,
|
||||
permission: app.permission,
|
||||
inheritPermission: app.inheritPermission
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from '@fastgpt/service/core/chat/saveChat';
|
||||
import { responseWrite } from '@fastgpt/service/common/response';
|
||||
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
|
||||
import { recordAppUsage } from '@fastgpt/service/core/app/usage/utils';
|
||||
import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
|
|
@ -381,6 +382,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
await saveChat(params);
|
||||
}
|
||||
|
||||
setImmediate(async () => {
|
||||
await recordAppUsage({
|
||||
appId: String(app._id),
|
||||
tmbId: String(tmbId),
|
||||
teamId: String(teamId)
|
||||
});
|
||||
});
|
||||
|
||||
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
|
||||
|
||||
/* select fe response field */
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from '@fastgpt/service/core/chat/saveChat';
|
||||
import { responseWrite } from '@fastgpt/service/common/response';
|
||||
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
|
||||
import { recordAppUsage } from '@fastgpt/service/core/app/usage/utils';
|
||||
import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
|
|
@ -383,6 +384,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
await saveChat(params);
|
||||
}
|
||||
|
||||
setImmediate(async () => {
|
||||
await recordAppUsage({
|
||||
appId: String(app._id),
|
||||
tmbId: String(tmbId),
|
||||
teamId: String(teamId)
|
||||
});
|
||||
});
|
||||
|
||||
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
|
||||
|
||||
/* select fe response field */
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ export const getMyApps = (data?: ListAppBody) =>
|
|||
maxQuantity: 1
|
||||
});
|
||||
|
||||
export const getRecentlyUsedApps = (data?: ListAppBody) =>
|
||||
POST<AppListItemType[]>('/core/app/list?t=0', data, {
|
||||
maxQuantity: 1
|
||||
});
|
||||
export const getRecentlyUsedApps = () => GET<AppListItemType[]>('/core/app/recentlyUsed');
|
||||
|
||||
/**
|
||||
* 创建一个应用
|
||||
|
|
|
|||
Loading…
Reference in New Issue