diff --git a/packages/service/core/app/controller.ts b/packages/service/core/app/controller.ts index 1facc88e2..a7e6e8f0f 100644 --- a/packages/service/core/app/controller.ts +++ b/packages/service/core/app/controller.ts @@ -27,6 +27,8 @@ import { getS3ChatSource } from '../../common/s3/sources/chat'; import { MongoAppChatLog } from './logs/chatLogsSchema'; import { MongoAppRegistration } from '../../support/appRegistration/schema'; import { MongoMcpKey } from '../../support/mcp/schema'; +import { type ClientSession } from '../../common/mongo'; +import { MongoAppRecord } from './record/schema'; export const beforeUpdateAppFormat = ({ nodes }: { nodes?: StoreNodeItemType[] }) => { if (!nodes) return; @@ -181,6 +183,8 @@ export const deleteAppDataProcessor = async ({ await MongoAppRegistration.deleteMany({ appId }); // 删除应用从MCP key apps数组中移除 await MongoMcpKey.updateMany({ teamId, 'apps.appId': appId }, { $pull: { apps: { appId } } }); + // 删除应用使用记录 + await MongoAppRecord.deleteMany({ appId }); // 删除应用本身 await MongoApp.deleteOne({ _id: appId }); @@ -214,13 +218,10 @@ export async function updateParentFoldersUpdateTime({ }): Promise { if (!parentId) return; - const parentApp = await MongoApp.findById(parentId).lean(); + const parentApp = await MongoApp.findById(parentId, 'parentId'); if (!parentApp) return; - // Only update if parent is a folder - if (AppFolderTypeList.includes(parentApp.type)) { - await MongoApp.findByIdAndUpdate(parentId, { updateTime: new Date() }, { session }); - } + await MongoApp.findByIdAndUpdate(parentId, { updateTime: new Date() }, { session }); // Recursively update parent folders await updateParentFoldersUpdateTime({ diff --git a/packages/service/core/app/record/schema.ts b/packages/service/core/app/record/schema.ts new file mode 100644 index 000000000..1a04a5236 --- /dev/null +++ b/packages/service/core/app/record/schema.ts @@ -0,0 +1,45 @@ +import { + TeamCollectionName, + TeamMemberCollectionName +} from '@fastgpt/global/support/user/team/constant'; +import { getMongoModel, Schema } from '../../../common/mongo'; +import { AppCollectionName } from '../schema'; +import type { AppRecordType } from './type'; + +export const AppRecordCollectionName = 'app_records'; + +const AppRecordSchema = 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: AppCollectionName, + required: true + }, + lastUsedTime: { + type: Date, + default: () => new Date() + } + }, + { + timestamps: false + } +); + +AppRecordSchema.index({ tmbId: 1, lastUsedTime: -1 }); // 查询用户最近使用的应用 +AppRecordSchema.index({ tmbId: 1, appId: 1 }, { unique: true }); // 防止重复记录 +AppRecordSchema.index({ teamId: 1, appId: 1 }); // 用于清理权限失效的记录 + +export const MongoAppRecord = getMongoModel( + AppRecordCollectionName, + AppRecordSchema +); diff --git a/packages/service/core/app/record/type.ts b/packages/service/core/app/record/type.ts new file mode 100644 index 000000000..85303b130 --- /dev/null +++ b/packages/service/core/app/record/type.ts @@ -0,0 +1,69 @@ +import { z } from 'zod'; + +// Zod schemas +export const AppRecordSchemaZod = z.object({ + _id: z.string().optional(), + tmbId: z.string(), + teamId: z.string(), + appId: z.string(), + lastUsedTime: z.date() +}); + +// 创建应用记录时的 schema(不包含 _id) +export const CreateAppRecordSchemaZod = AppRecordSchemaZod.omit({ + _id: true +}); + +// 更新应用记录时的 schema(部分字段可选) +export const UpdateAppRecordSchemaZod = AppRecordSchemaZod.partial().omit({ + _id: true, + tmbId: true, + teamId: true, + appId: true +}); + +// 查询参数的 schema +export const AppRecordQuerySchemaZod = z.object({ + tmbId: z.string().optional(), + teamId: z.string().optional(), + appId: z.string().optional(), + lastUsedTime: z + .union([ + z.date(), + z.object({ + gte: z.date().optional(), + lte: z.date().optional(), + gt: z.date().optional(), + lt: z.date().optional() + }) + ]) + .optional() +}); + +// TypeScript types inferred from Zod schemas +export type AppRecordType = z.infer; +export type CreateAppRecordType = z.infer; +export type UpdateAppRecordType = z.infer; +export type AppRecordQueryType = z.infer; + +// 兼容旧版本的类型定义(保持向后兼容) +export type { + AppRecordType as AppRecordSchemaType, + CreateAppRecordType as AppRecordCreateType, + UpdateAppRecordType as AppRecordUpdateType +}; + +// 应用记录统计类型 +export const AppRecordStatsSchemaZod = z.object({ + totalRecords: z.number().min(0), + uniqueApps: z.number().min(0), + mostUsedApp: z + .object({ + appId: z.string(), + usageCount: z.number().min(0) + }) + .optional(), + lastUsedTime: z.date().optional() +}); + +export type AppRecordStatsType = z.infer; diff --git a/packages/service/core/app/usage/utils.ts b/packages/service/core/app/record/utils.ts similarity index 61% rename from packages/service/core/app/usage/utils.ts rename to packages/service/core/app/record/utils.ts index 49dd87805..f82d1ba3e 100644 --- a/packages/service/core/app/usage/utils.ts +++ b/packages/service/core/app/record/utils.ts @@ -1,5 +1,5 @@ import { mongoSessionRun } from '../../../common/mongo/sessionRun'; -import { MongoAppUsage } from './schema'; +import { MongoAppRecord } from './schema'; export const recordAppUsage = async ({ appId, @@ -11,7 +11,7 @@ export const recordAppUsage = async ({ teamId: string; }) => { await mongoSessionRun(async (session) => { - await MongoAppUsage.findOneAndUpdate( + await MongoAppRecord.findOneAndUpdate( { tmbId, appId }, { $set: { @@ -26,24 +26,24 @@ export const recordAppUsage = async ({ } ); - // 保留最新的50条记录,删除超出限制的旧记录 - const threshold = await MongoAppUsage.findOne( + const records = await MongoAppRecord.find( { tmbId }, - { lastUsedTime: 1 }, + { _id: 1 }, { session, sort: { lastUsedTime: -1 }, - skip: 49, lean: true } ); - if (threshold) { - await MongoAppUsage.deleteMany( + if (records.length > 50) { + const toDeleteRecords = records.slice(50); + const toDeleteIds = toDeleteRecords.map((record) => record._id); + + await MongoAppRecord.deleteMany( { tmbId, - _id: { $ne: threshold._id }, - lastUsedTime: { $lte: threshold.lastUsedTime } + _id: { $in: toDeleteIds } }, { session } ); diff --git a/packages/service/core/app/usage/schema.ts b/packages/service/core/app/usage/schema.ts deleted file mode 100644 index 10ffca1e2..000000000 --- a/packages/service/core/app/usage/schema.ts +++ /dev/null @@ -1,49 +0,0 @@ -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(AppUsageCollectionName, AppUsageSchema); - -export type AppUsageType = { - _id?: string; - tmbId: string; - teamId: string; - appId: string; - lastUsedTime: Date; -}; diff --git a/projects/app/src/pageComponents/chat/ChatWindow/AppChatWindow.tsx b/projects/app/src/pageComponents/chat/ChatWindow/AppChatWindow.tsx index e0251ab60..2522ec4c2 100644 --- a/projects/app/src/pageComponents/chat/ChatWindow/AppChatWindow.tsx +++ b/projects/app/src/pageComponents/chat/ChatWindow/AppChatWindow.tsx @@ -32,9 +32,10 @@ const CustomPluginRunBox = dynamic(() => import('@/pageComponents/chat/CustomPlu type Props = { myApps: AppListItemType[]; + refreshRecentlyUsed?: () => void; }; -const AppChatWindow = ({ myApps }: Props) => { +const AppChatWindow = ({ myApps, refreshRecentlyUsed }: Props) => { const { userInfo } = useUserStore(); const { chatId, appId, outLinkAuthData } = useChatStore(); @@ -122,9 +123,19 @@ const AppChatWindow = ({ myApps }: Props) => { title: newTitle })); + refreshRecentlyUsed?.(); + return { responseText, isNewChat: forbidLoadChat.current }; }, - [appId, chatId, onUpdateHistoryTitle, setChatBoxData, forbidLoadChat, isShowCite] + [ + appId, + chatId, + onUpdateHistoryTitle, + setChatBoxData, + forbidLoadChat, + isShowCite, + refreshRecentlyUsed + ] ); return ( diff --git a/projects/app/src/pageComponents/chat/ChatWindow/HomeChatWindow.tsx b/projects/app/src/pageComponents/chat/ChatWindow/HomeChatWindow.tsx index 062b176a8..7b8fb59f5 100644 --- a/projects/app/src/pageComponents/chat/ChatWindow/HomeChatWindow.tsx +++ b/projects/app/src/pageComponents/chat/ChatWindow/HomeChatWindow.tsx @@ -51,6 +51,7 @@ import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; type Props = { myApps: AppListItemType[]; + refreshRecentlyUsed?: () => void; }; const defaultFileSelectConfig: AppFileSelectConfigType = { @@ -68,7 +69,7 @@ const defaultWhisperConfig: AppWhisperConfigType = { autoTTSResponse: false }; -const HomeChatWindow = ({ myApps }: Props) => { +const HomeChatWindow = ({ myApps, refreshRecentlyUsed }: Props) => { const { t } = useTranslation(); const { isPc } = useSystem(); @@ -232,6 +233,8 @@ const HomeChatWindow = ({ myApps }: Props) => { title: newTitle })); + refreshRecentlyUsed?.(); + return { responseText, isNewChat: forbidLoadChat.current }; } @@ -281,6 +284,8 @@ const HomeChatWindow = ({ myApps }: Props) => { title: newTitle })); + refreshRecentlyUsed?.(); + return { responseText, isNewChat: forbidLoadChat.current }; } ); diff --git a/projects/app/src/pageComponents/chat/useChat.ts b/projects/app/src/pageComponents/chat/useChat.ts index c470f498a..88afe1b23 100644 --- a/projects/app/src/pageComponents/chat/useChat.ts +++ b/projects/app/src/pageComponents/chat/useChat.ts @@ -12,7 +12,7 @@ export const useChat = (appId: string) => { const [isInitedUser, setIsInitedUser] = useState(false); // get app list - const { data: myApps = [] } = useRequest2(() => getRecentlyUsedApps(), { + const { data: myApps = [], refresh } = useRequest2(() => getRecentlyUsedApps(), { manual: false, errorToast: '', refreshDeps: [userInfo], @@ -43,6 +43,7 @@ export const useChat = (appId: string) => { return { isInitedUser, userInfo, - myApps + myApps, + refreshRecentlyUsed: refresh }; }; diff --git a/projects/app/src/pages/api/core/app/recentlyUsed.ts b/projects/app/src/pages/api/core/app/recentlyUsed.ts index 214911997..a47d4d064 100644 --- a/projects/app/src/pages/api/core/app/recentlyUsed.ts +++ b/projects/app/src/pages/api/core/app/recentlyUsed.ts @@ -1,11 +1,11 @@ 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 { MongoAppRecord } from '@fastgpt/service/core/app/record/schema'; +import { MongoApp } from '@fastgpt/service/core/app/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'; +import { AppPermission } from '@fastgpt/global/support/permission/app/controller'; export type GetRecentlyUsedAppsResponse = AppListItemType[]; @@ -13,57 +13,37 @@ async function handler( req: ApiRequestProps<{}, {}>, _res: ApiResponseType ): Promise { - const { tmbId, teamId } = await authUserPer({ + const { tmbId } = await authUserPer({ req, authToken: true, authApiKey: true }); - const recentUsages = await MongoAppUsage.find( + const recentRecords = await MongoAppRecord.find( { tmbId }, { appId: 1 }, { sort: { lastUsedTime: -1 }, limit: 20 } ).lean(); - if (!recentUsages.length) return []; + if (!recentRecords.length) return []; - const appIds = recentUsages.map((usage) => usage.appId); + const appIds = recentRecords.map((record) => record.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 apps = await MongoApp.find( + { + _id: { $in: appIds }, + deleteTime: null + }, + '_id parentId tmbId name avatar intro type updateTime pluginData inheritPermission' + ).lean(); - const validApps: { - appId: string; - app: NonNullable>['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) - ); - } + const appMap = new Map(apps.map((app) => [String(app._id), app])); + const sortedApps = recentRecords + .map((record) => appMap.get(String(record.appId))) + .filter((app) => app != null); return addSourceMember({ - list: validApps.map(({ app }) => ({ + list: sortedApps.map((app) => ({ _id: app._id, parentId: app.parentId, tmbId: app.tmbId, @@ -73,7 +53,10 @@ async function handler( type: app.type, updateTime: app.updateTime, pluginData: app.pluginData, - permission: app.permission, + permission: new AppPermission({ + role: 0, + isOwner: String(app.tmbId) === String(tmbId) + }), inheritPermission: app.inheritPermission })) }); diff --git a/projects/app/src/pages/api/core/app/version/publish.ts b/projects/app/src/pages/api/core/app/version/publish.ts index ce3dc632d..d7a1e3873 100644 --- a/projects/app/src/pages/api/core/app/version/publish.ts +++ b/projects/app/src/pages/api/core/app/version/publish.ts @@ -30,6 +30,10 @@ async function handler(req: ApiRequestProps, res: NextApiRe nodes }); + await updateParentFoldersUpdateTime({ + parentId: app.parentId + }); + if (autoSave) { await mongoSessionRun(async (session) => { await MongoAppVersion.updateOne( @@ -62,11 +66,6 @@ async function handler(req: ApiRequestProps, res: NextApiRe session } ); - - await updateParentFoldersUpdateTime({ - parentId: app.parentId, - session - }); }); addAuditLog({ @@ -128,11 +127,6 @@ async function handler(req: ApiRequestProps, res: NextApiRe session } ); - - await updateParentFoldersUpdateTime({ - parentId: app.parentId, - session - }); }); (async () => { diff --git a/projects/app/src/pages/api/core/chat/init.ts b/projects/app/src/pages/api/core/chat/init.ts index efd1fc3cf..840786906 100644 --- a/projects/app/src/pages/api/core/chat/init.ts +++ b/projects/app/src/pages/api/core/chat/init.ts @@ -1,6 +1,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { authApp } from '@fastgpt/service/support/permission/app/auth'; +import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils'; import { getChatModelNameListByModules } from '@/service/core/app/workflow'; import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d'; @@ -11,6 +12,8 @@ import { NextAPI } from '@/service/middleware/entry'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { presignVariablesFileUrls } from '@fastgpt/service/core/chat/utils'; +import { MongoAppRecord } from '@fastgpt/service/core/app/record/schema'; +import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; async function handler( req: NextApiRequest, @@ -25,57 +28,75 @@ async function handler( }); } - // auth app permission - const [{ app, tmbId }, chat] = await Promise.all([ - authApp({ - req, - authToken: true, - authApiKey: true, - appId, - per: ReadPermissionVal - }), - chatId ? MongoChat.findOne({ appId, chatId }) : undefined - ]); - - // auth chat permission - if (chat && !app.permission.hasReadChatLogPer && String(tmbId) !== String(chat?.tmbId)) { - return Promise.reject(ChatErrEnum.unAuthChat); - } - - // get app and history - const { nodes, chatConfig } = await getAppLatestVersion(app._id, app); - const pluginInputs = - chat?.pluginInputs ?? - nodes?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs ?? - []; - - const variables = await presignVariablesFileUrls({ - variables: chat?.variables, - variableConfig: chat?.variableList - }); - - return { - chatId, - appId, - title: chat?.title, - userAvatar: undefined, - variables, - app: { - chatConfig: getAppChatConfig({ - chatConfig, - systemConfigNode: getGuideModule(nodes), - storeVariables: chat?.variableList, - storeWelcomeText: chat?.welcomeText, - isPublicFetch: false + try { + // auth app permission + const [{ app, tmbId }, chat] = await Promise.all([ + authApp({ + req, + authToken: true, + authApiKey: true, + appId, + per: ReadPermissionVal }), - chatModels: getChatModelNameListByModules(nodes), - name: app.name, - avatar: app.avatar, - intro: app.intro, - type: app.type, - pluginInputs + chatId ? MongoChat.findOne({ appId, chatId }) : undefined + ]); + + // auth chat permission + if (chat && !app.permission.hasReadChatLogPer && String(tmbId) !== String(chat?.tmbId)) { + return Promise.reject(ChatErrEnum.unAuthChat); } - }; + + // get app and history + const { nodes, chatConfig } = await getAppLatestVersion(app._id, app); + const pluginInputs = + chat?.pluginInputs ?? + nodes?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs ?? + []; + + const variables = await presignVariablesFileUrls({ + variables: chat?.variables, + variableConfig: chat?.variableList + }); + + return { + chatId, + appId, + title: chat?.title, + userAvatar: undefined, + variables, + app: { + chatConfig: getAppChatConfig({ + chatConfig, + systemConfigNode: getGuideModule(nodes), + storeVariables: chat?.variableList, + storeWelcomeText: chat?.welcomeText, + isPublicFetch: false + }), + chatModels: getChatModelNameListByModules(nodes), + name: app.name, + avatar: app.avatar, + intro: app.intro, + type: app.type, + pluginInputs + } + }; + } catch (error: any) { + if (error === AppErrEnum.unAuthApp) { + const { tmbId, teamId } = await authUserPer({ + req, + authToken: true, + authApiKey: true + }); + + await MongoAppRecord.deleteMany({ + tmbId, + teamId, + appId + }); + } + + return Promise.reject(error); + } } export default NextAPI(handler); diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 64680ed55..a42ea345b 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -27,7 +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 { recordAppUsage } from '@fastgpt/service/core/app/record/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'; diff --git a/projects/app/src/pages/api/v2/chat/completions.ts b/projects/app/src/pages/api/v2/chat/completions.ts index c251a500e..3a36e7a8b 100644 --- a/projects/app/src/pages/api/v2/chat/completions.ts +++ b/projects/app/src/pages/api/v2/chat/completions.ts @@ -27,7 +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 { recordAppUsage } from '@fastgpt/service/core/app/record/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'; diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index 536b4d19d..d634e3f6c 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -33,7 +33,13 @@ import { MongoOutLink } from '@fastgpt/service/support/outLink/schema'; import { addLog } from '@fastgpt/service/common/system/log'; import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant'; -const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { +const Chat = ({ + myApps, + refreshRecentlyUsed +}: { + myApps: AppListItemType[]; + refreshRecentlyUsed: () => void; +}) => { const { isPc } = useSystem(); const { appId } = useChatStore(); @@ -62,7 +68,9 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { {(!datasetCiteData || isPc) && ( {/* home chat window */} - {pane === ChatSidebarPaneEnum.HOME && } + {pane === ChatSidebarPaneEnum.HOME && ( + + )} {/* favourite apps */} {pane === ChatSidebarPaneEnum.FAVORITE_APPS && } @@ -71,7 +79,9 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { {pane === ChatSidebarPaneEnum.TEAM_APPS && } {/* recently used apps chat window */} - {pane === ChatSidebarPaneEnum.RECENTLY_USED_APPS && } + {pane === ChatSidebarPaneEnum.RECENTLY_USED_APPS && ( + + )} {/* setting */} {pane === ChatSidebarPaneEnum.SETTING && } @@ -103,7 +113,7 @@ const Render = (props: { const { chatId } = useChatStore(); const { setUserInfo } = useUserStore(); const { feConfigs } = useSystemStore(); - const { isInitedUser, userInfo, myApps } = useChat(appId); + const { isInitedUser, userInfo, myApps, refreshRecentlyUsed } = useChat(appId); const chatHistoryProviderParams = useMemo( () => ({ appId, source: ChatSourceEnum.online }), @@ -154,7 +164,7 @@ const Render = (props: { isShowFullText={props.showFullText} > - +