mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-26 04:32:50 +00:00
Merge d8d17031ae into 32affb0df0
This commit is contained in:
commit
209336b472
|
|
@ -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;
|
||||
|
|
@ -203,4 +205,29 @@ export const deleteAppsImmediate = async ({
|
|||
'_id'
|
||||
).lean();
|
||||
await Promise.all(evalJobs.map((evalJob) => removeEvaluationJob(evalJob._id)));
|
||||
|
||||
// Remove app record
|
||||
await MongoAppRecord.deleteMany({ teamId, appId: { $in: appIds } });
|
||||
};
|
||||
|
||||
export async function updateParentFoldersUpdateTime({
|
||||
parentId,
|
||||
session
|
||||
}: {
|
||||
parentId?: string | null;
|
||||
session?: ClientSession;
|
||||
}): Promise<void> {
|
||||
if (!parentId) return;
|
||||
|
||||
const parentApp = await MongoApp.findById(parentId, 'parentId updateTime');
|
||||
if (!parentApp) return;
|
||||
|
||||
parentApp.updateTime = new Date();
|
||||
await parentApp.save({ session });
|
||||
|
||||
// Recursively update parent folders
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: parentApp.parentId,
|
||||
session
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<AppRecordType>(
|
||||
AppRecordCollectionName,
|
||||
AppRecordSchema
|
||||
);
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const AppRecordSchemaZod = z.object({
|
||||
_id: z.string(),
|
||||
tmbId: z.string(),
|
||||
teamId: z.string(),
|
||||
appId: z.string(),
|
||||
lastUsedTime: z.date()
|
||||
});
|
||||
|
||||
// TypeScript types inferred from Zod schemas
|
||||
export type AppRecordType = z.infer<typeof AppRecordSchemaZod>;
|
||||
|
||||
export const GetRecentlyUsedAppsResponseSchema = z.array(
|
||||
z.object({
|
||||
_id: z.string(),
|
||||
name: z.string(),
|
||||
avatar: z.string()
|
||||
})
|
||||
);
|
||||
export type GetRecentlyUsedAppsResponseType = z.infer<typeof GetRecentlyUsedAppsResponseSchema>;
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
import { MongoAppRecord } from './schema';
|
||||
|
||||
export const recordAppUsage = async ({
|
||||
appId,
|
||||
tmbId,
|
||||
teamId
|
||||
}: {
|
||||
appId: string;
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
}) => {
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoAppRecord.updateOne(
|
||||
{ tmbId, appId },
|
||||
{
|
||||
$set: {
|
||||
teamId,
|
||||
lastUsedTime: new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
session
|
||||
}
|
||||
);
|
||||
|
||||
// 检查是否超过50条,如果超过则删除最旧的一条
|
||||
const count = await MongoAppRecord.countDocuments({ tmbId }, { session });
|
||||
|
||||
if (count > 50) {
|
||||
await MongoAppRecord.deleteOne(
|
||||
{ tmbId },
|
||||
{
|
||||
session,
|
||||
sort: { lastUsedTime: 1 }
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -35,7 +35,6 @@ export type Props = {
|
|||
nodes: StoreNodeItemType[];
|
||||
appChatConfig?: AppChatConfigType;
|
||||
variables?: Record<string, any>;
|
||||
isUpdateUseTime: boolean;
|
||||
newTitle: string;
|
||||
source: `${ChatSourceEnum}`;
|
||||
sourceName?: string;
|
||||
|
|
@ -219,7 +218,6 @@ export async function saveChat(props: Props) {
|
|||
nodes,
|
||||
appChatConfig,
|
||||
variables,
|
||||
isUpdateUseTime,
|
||||
newTitle,
|
||||
source,
|
||||
sourceName,
|
||||
|
|
@ -393,18 +391,6 @@ export async function saveChat(props: Props) {
|
|||
} catch (error) {
|
||||
addLog.error('Push chat log error', error);
|
||||
}
|
||||
|
||||
if (isUpdateUseTime) {
|
||||
await MongoApp.updateOne(
|
||||
{ _id: appId },
|
||||
{
|
||||
updateTime: new Date()
|
||||
},
|
||||
{
|
||||
...writePrimary
|
||||
}
|
||||
).catch();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog.error(`update chat history error`, error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { useMemo } from 'react';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { ChatSettingTabOptionEnum, ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
|
||||
|
|
@ -41,11 +41,11 @@ const ChatFavouriteApp = () => {
|
|||
|
||||
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
|
||||
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const wideLogoUrl = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.wideLogoUrl);
|
||||
const homeTabTitle = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.homeTabTitle);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
const wideLogoUrl = useContextSelector(ChatPageContext, (v) => v.chatSettings?.wideLogoUrl);
|
||||
const homeTabTitle = useContextSelector(ChatPageContext, (v) => v.chatSettings?.homeTabTitle);
|
||||
|
||||
const tags = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.favouriteTags || []);
|
||||
const tags = useContextSelector(ChatPageContext, (v) => v.chatSettings?.favouriteTags || []);
|
||||
const tagCache = useMemo(() => {
|
||||
return tags.reduce(
|
||||
(acc, tag) => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constan
|
|||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import { useRouter } from 'next/router';
|
||||
import { type AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import {
|
||||
type GetResourceFolderListProps,
|
||||
type GetResourceListItemResponse
|
||||
|
|
@ -24,7 +23,7 @@ import SelectOneResource from '@/components/common/folder/SelectOneResource';
|
|||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import VariablePopover from '@/components/core/chat/ChatContainer/components/VariablePopover';
|
||||
import { useCopyData } from '@fastgpt/web/hooks/useCopyData';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import {
|
||||
ChatSidebarPaneEnum,
|
||||
DEFAULT_LOGO_BANNER_COLLAPSED_URL
|
||||
|
|
@ -38,7 +37,6 @@ import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/const
|
|||
const ChatHeader = ({
|
||||
history,
|
||||
showHistory,
|
||||
apps,
|
||||
totalRecordsCount,
|
||||
|
||||
pane,
|
||||
|
|
@ -50,18 +48,15 @@ const ChatHeader = ({
|
|||
|
||||
history: ChatItemType[];
|
||||
showHistory?: boolean;
|
||||
apps?: AppListItemType[];
|
||||
totalRecordsCount: number;
|
||||
reserveSpace?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
const pathname = usePathname();
|
||||
const { source } = useChatStore();
|
||||
|
||||
const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
|
||||
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
|
||||
|
||||
const isPlugin = chatData.app.type === AppTypeEnum.workflowTool;
|
||||
const isShare = source === 'share';
|
||||
const chatType = isShare ? ChatTypeEnum.share : ChatTypeEnum.chat;
|
||||
|
|
@ -87,7 +82,6 @@ const ChatHeader = ({
|
|||
</>
|
||||
) : (
|
||||
<MobileHeader
|
||||
apps={apps}
|
||||
appId={chatData.appId}
|
||||
name={
|
||||
pane === ChatSidebarPaneEnum.HOME && !isShare
|
||||
|
|
@ -113,15 +107,7 @@ const ChatHeader = ({
|
|||
);
|
||||
};
|
||||
|
||||
const MobileDrawer = ({
|
||||
onCloseDrawer,
|
||||
appId,
|
||||
apps
|
||||
}: {
|
||||
onCloseDrawer: () => void;
|
||||
appId: string;
|
||||
apps?: AppListItemType[];
|
||||
}) => {
|
||||
const MobileDrawer = ({ onCloseDrawer, appId }: { onCloseDrawer: () => void; appId: string }) => {
|
||||
enum TabEnum {
|
||||
recently = 'recently',
|
||||
app = 'app'
|
||||
|
|
@ -129,6 +115,7 @@ const MobileDrawer = ({
|
|||
const { t } = useTranslation();
|
||||
|
||||
const { setChatId } = useChatStore();
|
||||
const myApps = useContextSelector(ChatPageContext, (v) => v.myApps);
|
||||
|
||||
const [currentTab, setCurrentTab] = useState<TabEnum>(TabEnum.recently);
|
||||
|
||||
|
|
@ -143,7 +130,7 @@ const MobileDrawer = ({
|
|||
);
|
||||
}, []);
|
||||
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
const onclickApp = (id: string) => {
|
||||
handlePaneChange(ChatSidebarPaneEnum.RECENTLY_USED_APPS, id);
|
||||
|
|
@ -201,8 +188,8 @@ const MobileDrawer = ({
|
|||
{/* history */}
|
||||
{currentTab === TabEnum.recently && (
|
||||
<Box px={3} overflow={'auto'} h={'100%'}>
|
||||
{Array.isArray(apps) &&
|
||||
apps.map((item) => (
|
||||
{Array.isArray(myApps) &&
|
||||
myApps.map((item) => (
|
||||
<Flex justify={'center'} key={item._id}>
|
||||
<Flex
|
||||
py={2.5}
|
||||
|
|
@ -247,13 +234,11 @@ const MobileHeader = ({
|
|||
showHistory,
|
||||
name,
|
||||
avatar,
|
||||
appId,
|
||||
apps
|
||||
appId
|
||||
}: {
|
||||
showHistory?: boolean;
|
||||
avatar: string;
|
||||
name: string;
|
||||
apps?: AppListItemType[];
|
||||
appId: string;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
|
@ -290,9 +275,7 @@ const MobileHeader = ({
|
|||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{isOpenDrawer && !isShareChat && (
|
||||
<MobileDrawer apps={apps} appId={appId} onCloseDrawer={onCloseDrawer} />
|
||||
)}
|
||||
{isOpenDrawer && !isShareChat && <MobileDrawer appId={appId} onCloseDrawer={onCloseDrawer} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import LogChart from '@/pageComponents/app/detail/Logs/LogChart';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import type { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
|
||||
|
|
@ -13,7 +13,7 @@ type Props = {
|
|||
};
|
||||
|
||||
const LogDetails = ({ Header }: Props) => {
|
||||
const appId = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.appId || '');
|
||||
const appId = useContextSelector(ChatPageContext, (v) => v.chatSettings?.appId || '');
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRangeType>({
|
||||
from: new Date(addDays(new Date(), -6).setHours(0, 0, 0, 0)),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
Box,
|
||||
|
|
@ -377,10 +377,10 @@ type Props = {
|
|||
const TagManageModal = ({ onClose, onRefresh }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const refreshChatSetting = useContextSelector(ChatSettingContext, (v) => v.refreshChatSetting);
|
||||
const refreshChatSetting = useContextSelector(ChatPageContext, (v) => v.refreshChatSetting);
|
||||
|
||||
// get tags from db
|
||||
const tags = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.favouriteTags || []);
|
||||
const tags = useContextSelector(ChatPageContext, (v) => v.chatSettings?.favouriteTags || []);
|
||||
// local editable tags list
|
||||
const [localTags, setLocalTags] = useState<ChatFavouriteTagType[]>(tags);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Flex,
|
||||
HStack,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
|
|
@ -68,7 +60,7 @@ const FavouriteAppSetting = ({ Header }: Props) => {
|
|||
|
||||
const searchAppTagValue = watchSearchValue('tag');
|
||||
// apps' tags options
|
||||
const tagOptions = useContextSelector(ChatSettingContext, (v) => {
|
||||
const tagOptions = useContextSelector(ChatPageContext, (v) => {
|
||||
const tags = v.chatSettings?.favouriteTags || [];
|
||||
return [
|
||||
{ label: t('chat:setting.favourite.category_all'), value: '' },
|
||||
|
|
@ -76,7 +68,7 @@ const FavouriteAppSetting = ({ Header }: Props) => {
|
|||
];
|
||||
});
|
||||
// app's tags cache map
|
||||
const tagMap = useContextSelector(ChatSettingContext, (v) =>
|
||||
const tagMap = useContextSelector(ChatPageContext, (v) =>
|
||||
(v.chatSettings?.favouriteTags || []).reduce<Record<string, ChatFavouriteTagType>>(
|
||||
(acc, tag) => {
|
||||
acc[tag.id] = { ...tag };
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
|||
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useMount } from 'ahooks';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import {
|
||||
DEFAULT_LOGO_BANNER_COLLAPSED_URL,
|
||||
DEFAULT_LOGO_BANNER_URL
|
||||
|
|
@ -46,8 +46,8 @@ const HomepageSetting = ({ Header, onDiagramShow }: Props) => {
|
|||
const { t } = useTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
|
||||
const refreshChatSetting = useContextSelector(ChatSettingContext, (v) => v.refreshChatSetting);
|
||||
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
|
||||
const refreshChatSetting = useContextSelector(ChatPageContext, (v) => v.refreshChatSetting);
|
||||
|
||||
const chatSettings2Form = useCallback(
|
||||
(data?: ChatSettingType) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import LogTable from '@/pageComponents/app/detail/Logs/LogTable';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import type { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
|
||||
|
|
@ -16,7 +16,7 @@ type Props = {
|
|||
const chatSourceValues = Object.values(ChatSourceEnum);
|
||||
|
||||
const LogDetails = ({ Header }: Props) => {
|
||||
const appId = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.appId || '');
|
||||
const appId = useContextSelector(ChatPageContext, (v) => v.chatSettings?.appId || '');
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRangeType>({
|
||||
from: new Date(addDays(new Date(), -6).setHours(0, 0, 0, 0)),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useContextSelector } from 'use-context-selector';
|
|||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import ChatSliderMobileDrawer from '@/pageComponents/chat/slider/ChatSliderMobileDrawer';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMount } from 'ahooks';
|
||||
|
|
@ -43,8 +43,8 @@ const ChatSetting = () => {
|
|||
);
|
||||
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
|
||||
|
||||
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
const handleTabChange = useCallback(
|
||||
(tab: ChatSettingTabOptionEnum) => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import AppTypeTag from '@/pageComponents/chat/ChatTeamApp/TypeTag';
|
|||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import UserBox from '@fastgpt/web/components/common/UserBox';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
|
||||
|
||||
const List = ({ appType }: { appType: AppTypeEnum | 'all' }) => {
|
||||
|
|
@ -33,7 +33,7 @@ const List = ({ appType }: { appType: AppTypeEnum | 'all' }) => {
|
|||
].includes(app.type)
|
||||
)
|
||||
);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
|||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import ChatSliderMobileDrawer from '@/pageComponents/chat/slider/ChatSliderMobileDrawer';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||
|
|
@ -29,7 +29,7 @@ const MyApps = () => {
|
|||
(v) => v
|
||||
);
|
||||
|
||||
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
|
||||
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
|
||||
|
||||
const onOpenSlider = useContextSelector(ChatContext, (v) => v.onOpenSlider);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import SideBar from '@/components/SideBar';
|
|||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
|
||||
import { type AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import { ChatTypeEnum } from '@/components/core/chat/ChatContainer/ChatBox/constants';
|
||||
import { useCallback } from 'react';
|
||||
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
|
||||
|
|
@ -20,7 +19,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { ChatSidebarPaneEnum } from '../constants';
|
||||
import ChatHistorySidebar from '@/pageComponents/chat/slider/ChatSliderSidebar';
|
||||
import ChatSliderMobileDrawer from '@/pageComponents/chat/slider/ChatSliderMobileDrawer';
|
||||
|
|
@ -30,11 +29,7 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
|||
|
||||
const CustomPluginRunBox = dynamic(() => import('@/pageComponents/chat/CustomPluginRunBox'));
|
||||
|
||||
type Props = {
|
||||
myApps: AppListItemType[];
|
||||
};
|
||||
|
||||
const AppChatWindow = ({ myApps }: Props) => {
|
||||
const AppChatWindow = () => {
|
||||
const { userInfo } = useUserStore();
|
||||
const { chatId, appId, outLinkAuthData } = useChatStore();
|
||||
|
||||
|
|
@ -55,9 +50,10 @@ const AppChatWindow = ({ myApps }: Props) => {
|
|||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed);
|
||||
|
||||
const { loading } = useRequest2(
|
||||
async () => {
|
||||
|
|
@ -122,9 +118,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 (
|
||||
|
|
@ -158,7 +164,6 @@ const AppChatWindow = ({ myApps }: Props) => {
|
|||
pane={pane}
|
||||
chatSettings={chatSettings}
|
||||
showHistory
|
||||
apps={myApps}
|
||||
history={chatRecords}
|
||||
totalRecordsCount={totalRecordsCount}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -36,12 +36,8 @@ import { getDefaultAppForm } from '@fastgpt/global/core/app/utils';
|
|||
import { getToolPreviewNode } from '@/web/core/app/api/tool';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import type {
|
||||
AppFileSelectConfigType,
|
||||
AppListItemType,
|
||||
AppWhisperConfigType
|
||||
} from '@fastgpt/global/core/app/type';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import type { AppFileSelectConfigType, AppWhisperConfigType } from '@fastgpt/global/core/app/type';
|
||||
import ChatHeader from '@/pageComponents/chat/ChatHeader';
|
||||
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
|
||||
import { ChatSidebarPaneEnum } from '../constants';
|
||||
|
|
@ -49,10 +45,6 @@ import ChatHistorySidebar from '@/pageComponents/chat/slider/ChatSliderSidebar';
|
|||
import ChatSliderMobileDrawer from '@/pageComponents/chat/slider/ChatSliderMobileDrawer';
|
||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||
|
||||
type Props = {
|
||||
myApps: AppListItemType[];
|
||||
};
|
||||
|
||||
const defaultFileSelectConfig: AppFileSelectConfigType = {
|
||||
maxFiles: 20,
|
||||
canSelectFile: true,
|
||||
|
|
@ -68,7 +60,7 @@ const defaultWhisperConfig: AppWhisperConfigType = {
|
|||
autoTTSResponse: false
|
||||
};
|
||||
|
||||
const HomeChatWindow = ({ myApps }: Props) => {
|
||||
const HomeChatWindow = () => {
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
|
|
@ -86,10 +78,11 @@ const HomeChatWindow = ({ myApps }: Props) => {
|
|||
const resetVariables = useContextSelector(ChatItemContext, (v) => v.resetVariables);
|
||||
const isShowCite = useContextSelector(ChatItemContext, (v) => v.isShowCite);
|
||||
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const chatSettings = useContextSelector(ChatSettingContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const homeAppId = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.appId || '');
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
const chatSettings = useContextSelector(ChatPageContext, (v) => v.chatSettings);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
const homeAppId = useContextSelector(ChatPageContext, (v) => v.chatSettings?.appId || '');
|
||||
const refreshRecentlyUsed = useContextSelector(ChatPageContext, (v) => v.refreshRecentlyUsed);
|
||||
|
||||
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
|
||||
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
|
||||
|
|
@ -232,6 +225,8 @@ const HomeChatWindow = ({ myApps }: Props) => {
|
|||
title: newTitle
|
||||
}));
|
||||
|
||||
refreshRecentlyUsed();
|
||||
|
||||
return { responseText, isNewChat: forbidLoadChat.current };
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +276,8 @@ const HomeChatWindow = ({ myApps }: Props) => {
|
|||
title: newTitle
|
||||
}));
|
||||
|
||||
refreshRecentlyUsed();
|
||||
|
||||
return { responseText, isNewChat: forbidLoadChat.current };
|
||||
}
|
||||
);
|
||||
|
|
@ -449,7 +446,6 @@ const HomeChatWindow = ({ myApps }: Props) => {
|
|||
pane={pane}
|
||||
chatSettings={chatSettings}
|
||||
showHistory
|
||||
apps={myApps}
|
||||
history={chatRecords}
|
||||
totalRecordsCount={totalRecordsCount}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
|
|
@ -15,8 +15,8 @@ const ChatSliderFooter = () => {
|
|||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const onCloseSlider = useContextSelector(ChatContext, (v) => v.onCloseSlider);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
|
||||
const isAdmin = !!userInfo?.team.permission.hasManagePer;
|
||||
const isSettingPane = pane === ChatSidebarPaneEnum.SETTING;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { GridItem, Grid } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
|
|
@ -24,9 +24,9 @@ const ChatSliderHeader = ({ title, banner }: Props) => {
|
|||
const { isPc } = useSystem();
|
||||
const { setChatId } = useChatStore();
|
||||
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const enableHome = useContextSelector(ChatSettingContext, (v) => v.chatSettings?.enableHome);
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
const enableHome = useContextSelector(ChatPageContext, (v) => v.chatSettings?.enableHome);
|
||||
|
||||
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
|
||||
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ import {
|
|||
} from '@/pageComponents/chat/constants';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { ChatSettingContext } from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext } from '@/web/core/chat/context/chatPageContext';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import type { GetRecentlyUsedAppsResponseType } from '@fastgpt/service/core/app/record/type';
|
||||
|
||||
type Props = {
|
||||
activeAppId: string;
|
||||
apps: AppListItemType[];
|
||||
};
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
|
@ -148,13 +148,13 @@ const AnimatedText: React.FC<AnimatedTextProps> = ({ show, children, className,
|
|||
);
|
||||
|
||||
const LogoSection = () => {
|
||||
const isCollapsed = useContextSelector(ChatSettingContext, (v) => v.collapse === 1);
|
||||
const logos = useContextSelector(ChatSettingContext, (v) => v.logos);
|
||||
const isCollapsed = useContextSelector(ChatPageContext, (v) => v.collapse === 1);
|
||||
const logos = useContextSelector(ChatPageContext, (v) => v.logos);
|
||||
const isHomeActive = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.pane === ChatSidebarPaneEnum.HOME
|
||||
);
|
||||
const onTriggerCollapse = useContextSelector(ChatSettingContext, (v) => v.onTriggerCollapse);
|
||||
const onTriggerCollapse = useContextSelector(ChatPageContext, (v) => v.onTriggerCollapse);
|
||||
const wideLogoSrc = logos.wideLogoUrl;
|
||||
const squareLogoSrc = logos.squareLogoUrl;
|
||||
|
||||
|
|
@ -256,24 +256,24 @@ const NavigationSection = () => {
|
|||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const isEnableHome = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.chatSettings?.enableHome ?? true
|
||||
);
|
||||
const isCollapsed = useContextSelector(ChatSettingContext, (v) => v.collapse === 1);
|
||||
const onTriggerCollapse = useContextSelector(ChatSettingContext, (v) => v.onTriggerCollapse);
|
||||
const isCollapsed = useContextSelector(ChatPageContext, (v) => v.collapse === 1);
|
||||
const onTriggerCollapse = useContextSelector(ChatPageContext, (v) => v.onTriggerCollapse);
|
||||
const isHomeActive = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.pane === ChatSidebarPaneEnum.HOME
|
||||
);
|
||||
const isTeamAppsActive = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.pane === ChatSidebarPaneEnum.TEAM_APPS
|
||||
);
|
||||
const isFavouriteAppsActive = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.pane === ChatSidebarPaneEnum.FAVORITE_APPS
|
||||
);
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
return (
|
||||
<Flex mt={4} flexDirection={'column'} gap={1} px={4}>
|
||||
|
|
@ -365,12 +365,12 @@ const BottomSection = () => {
|
|||
const isAdmin = !!userInfo?.team.permission.hasManagePer;
|
||||
const isShare = pathname === '/chat/share';
|
||||
|
||||
const isCollapsed = useContextSelector(ChatSettingContext, (v) => v.collapse === 1);
|
||||
const isCollapsed = useContextSelector(ChatPageContext, (v) => v.collapse === 1);
|
||||
const isSettingActive = useContextSelector(
|
||||
ChatSettingContext,
|
||||
ChatPageContext,
|
||||
(v) => v.pane === ChatSidebarPaneEnum.SETTING
|
||||
);
|
||||
const onSettingClick = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const onSettingClick = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
return (
|
||||
<MotionBox mt={'auto'} px={3} py={4} layout={false}>
|
||||
|
|
@ -485,13 +485,14 @@ const BottomSection = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const ChatSlider = ({ apps, activeAppId }: Props) => {
|
||||
const ChatSlider = ({ activeAppId }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isCollapsed = useContextSelector(ChatSettingContext, (v) => v.collapse === 1);
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const isCollapsed = useContextSelector(ChatPageContext, (v) => v.collapse === 1);
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
const myApps = useContextSelector(ChatPageContext, (v) => v.myApps);
|
||||
|
||||
const handlePaneChange = useContextSelector(ChatSettingContext, (v) => v.handlePaneChange);
|
||||
const handlePaneChange = useContextSelector(ChatPageContext, (v) => v.handlePaneChange);
|
||||
|
||||
return (
|
||||
<MotionFlex
|
||||
|
|
@ -531,7 +532,7 @@ const ChatSlider = ({ apps, activeAppId }: Props) => {
|
|||
</HStack>
|
||||
|
||||
<MyBox flex={'1 0 0'} h={0} overflow={'overlay'} px={4} position={'relative'}>
|
||||
{apps.map((item) => (
|
||||
{myApps.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
py={2}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
import { getRecentlyUsedApps } from '@/web/core/app/api';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useMount } from 'ahooks';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export const useChat = (appId: string) => {
|
||||
const { setSource, setAppId } = useChatStore();
|
||||
const { userInfo, initUserInfo } = useUserStore();
|
||||
|
||||
const [isInitedUser, setIsInitedUser] = useState(false);
|
||||
|
||||
// get app list
|
||||
const { data: myApps = [] } = useRequest2(() => getRecentlyUsedApps({ getRecentlyChat: true }), {
|
||||
manual: false,
|
||||
errorToast: '',
|
||||
refreshDeps: [userInfo],
|
||||
pollingInterval: 30000
|
||||
});
|
||||
|
||||
// initialize user info
|
||||
useMount(async () => {
|
||||
// ensure store has current appId before setting source (avoids fallback to lastChatAppId)
|
||||
if (appId) setAppId(appId);
|
||||
try {
|
||||
await initUserInfo();
|
||||
} catch (error) {
|
||||
console.log('User not logged in:', error);
|
||||
} finally {
|
||||
setSource('online');
|
||||
setIsInitedUser(true);
|
||||
}
|
||||
});
|
||||
|
||||
// sync appId to store as soon as route/appId changes
|
||||
useEffect(() => {
|
||||
if (appId) {
|
||||
setAppId(appId);
|
||||
}
|
||||
}, [appId, setAppId, userInfo]);
|
||||
|
||||
return {
|
||||
isInitedUser,
|
||||
userInfo,
|
||||
myApps
|
||||
};
|
||||
};
|
||||
|
|
@ -33,6 +33,7 @@ import { isS3ObjectKey } from '@fastgpt/service/common/s3/utils';
|
|||
import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSchema';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import path from 'node:path';
|
||||
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export type CreateAppBody = {
|
||||
parentId?: ParentIdType;
|
||||
|
|
@ -243,6 +244,11 @@ export const onCreateApp = async ({
|
|||
|
||||
await getS3AvatarSource().refreshAvatar(_avatar, undefined, session);
|
||||
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId,
|
||||
session
|
||||
});
|
||||
|
||||
(async () => {
|
||||
addAuditLog({
|
||||
tmbId,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { MongoApp } from '@fastgpt/service/core/app/schema';
|
|||
import type { StoreSecretValueType } from '@fastgpt/global/common/secret/type';
|
||||
import { storeSecretValue } from '@fastgpt/service/common/secret/utils';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export type UpdateHttpPluginBody = {
|
||||
appId: string;
|
||||
|
|
@ -50,6 +51,12 @@ async function handler(req: ApiRequestProps<UpdateHttpPluginBody>, res: NextApiR
|
|||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: app.parentId,
|
||||
session
|
||||
});
|
||||
|
||||
await MongoAppVersion.updateOne(
|
||||
{ appId },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { getMCPToolSetRuntimeNode } from '@fastgpt/global/core/app/tool/mcpTool/
|
|||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { type StoreSecretValueType } from '@fastgpt/global/common/secret/type';
|
||||
import { storeSecretValue } from '@fastgpt/service/common/secret/utils';
|
||||
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export type updateMCPToolsQuery = {};
|
||||
|
||||
|
|
@ -51,6 +52,12 @@ async function handler(
|
|||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: app.parentId,
|
||||
session
|
||||
});
|
||||
|
||||
await MongoAppVersion.updateOne(
|
||||
{ appId },
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
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 { MongoAppRecord } from '@fastgpt/service/core/app/record/schema';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import type { GetRecentlyUsedAppsResponseType } from '@fastgpt/service/core/app/record/type';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<{}, {}>,
|
||||
_res: ApiResponseType<GetRecentlyUsedAppsResponseType>
|
||||
) {
|
||||
const { tmbId } = await authUserPer({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
const recentRecords = await MongoAppRecord.find(
|
||||
{ tmbId },
|
||||
{ appId: 1 },
|
||||
{ sort: { lastUsedTime: -1 }, limit: 20 }
|
||||
).lean();
|
||||
|
||||
if (!recentRecords.length) return [];
|
||||
|
||||
const apps = await MongoApp.find(
|
||||
{ _id: { $in: recentRecords.map((record) => record.appId) } },
|
||||
'_id name avatar'
|
||||
).lean();
|
||||
|
||||
const appMap = new Map(apps.map((app) => [String(app._id), app]));
|
||||
|
||||
return recentRecords
|
||||
.map((record) => appMap.get(String(record.appId)))
|
||||
.filter((app) => app != null)
|
||||
.map((app) => ({
|
||||
_id: String(app._id),
|
||||
name: app.name,
|
||||
avatar: app.avatar
|
||||
}));
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
|
@ -27,6 +27,7 @@ import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
|
|||
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
import { getS3AvatarSource } from '@fastgpt/service/common/s3/sources/avatar';
|
||||
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
export type AppUpdateQuery = {
|
||||
appId: string;
|
||||
|
|
@ -117,7 +118,7 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
|||
|
||||
await getS3AvatarSource().refreshAvatar(avatar, app.avatar, session);
|
||||
|
||||
return MongoApp.findByIdAndUpdate(
|
||||
const result = await MongoApp.findByIdAndUpdate(
|
||||
appId,
|
||||
{
|
||||
...parseParentIdInMongo(parentId),
|
||||
|
|
@ -137,6 +138,26 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
|||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
if (isMove) {
|
||||
// Update both old and new parent folders
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: app.parentId,
|
||||
session
|
||||
});
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId,
|
||||
session
|
||||
});
|
||||
} else {
|
||||
// Update current parent folder
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: parentId || app.parentId,
|
||||
session
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Move
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
|
|||
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
|
||||
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
import { updateParentFoldersUpdateTime } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiResponse<any>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
|
@ -29,6 +30,10 @@ async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiRe
|
|||
nodes
|
||||
});
|
||||
|
||||
await updateParentFoldersUpdateTime({
|
||||
parentId: app.parentId
|
||||
});
|
||||
|
||||
if (autoSave) {
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoAppVersion.updateOne(
|
||||
|
|
|
|||
|
|
@ -248,7 +248,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.test,
|
||||
userContent: userQuestion,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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/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';
|
||||
|
|
@ -326,7 +327,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
const source = (() => {
|
||||
if (shareId) {
|
||||
return ChatSourceEnum.share;
|
||||
|
|
@ -363,7 +363,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
newTitle,
|
||||
shareId,
|
||||
outLinkUid: outLinkUserId,
|
||||
|
|
@ -383,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/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';
|
||||
|
|
@ -328,7 +329,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
})();
|
||||
|
||||
// save chat
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
const source = (() => {
|
||||
if (shareId) {
|
||||
return ChatSourceEnum.share;
|
||||
|
|
@ -365,7 +365,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
newTitle,
|
||||
shareId,
|
||||
outLinkUid: outLinkUserId,
|
||||
|
|
@ -385,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 */
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { serviceSideProps } from '@/web/common/i18n/utils';
|
|||
import { ChatSidebarPaneEnum } from '@/pageComponents/chat/constants';
|
||||
import { GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import ChatContextProvider from '@/web/core/chat/context/chatContext';
|
||||
import { type AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
|
@ -18,13 +17,9 @@ import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
|
|||
import LoginModal from '@/pageComponents/login/LoginModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import ChatSetting from '@/pageComponents/chat/ChatSetting';
|
||||
import { useChat } from '@/pageComponents/chat/useChat';
|
||||
import AppChatWindow from '@/pageComponents/chat/ChatWindow/AppChatWindow';
|
||||
import HomeChatWindow from '@/pageComponents/chat/ChatWindow/HomeChatWindow';
|
||||
import {
|
||||
ChatSettingContext,
|
||||
ChatSettingContextProvider
|
||||
} from '@/web/core/chat/context/chatSettingContext';
|
||||
import { ChatPageContext, ChatPageContextProvider } from '@/web/core/chat/context/chatPageContext';
|
||||
import ChatTeamApp from '@/pageComponents/chat/ChatTeamApp';
|
||||
import ChatFavouriteApp from '@/pageComponents/chat/ChatFavouriteApp';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
|
|
@ -33,7 +28,7 @@ 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 = () => {
|
||||
const { isPc } = useSystem();
|
||||
|
||||
const { appId } = useChatStore();
|
||||
|
|
@ -41,8 +36,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||
const datasetCiteData = useContextSelector(ChatItemContext, (v) => v.datasetCiteData);
|
||||
const setCiteModalData = useContextSelector(ChatItemContext, (v) => v.setCiteModalData);
|
||||
|
||||
const collapse = useContextSelector(ChatSettingContext, (v) => v.collapse);
|
||||
const pane = useContextSelector(ChatSettingContext, (v) => v.pane);
|
||||
const collapse = useContextSelector(ChatPageContext, (v) => v.collapse);
|
||||
const pane = useContextSelector(ChatPageContext, (v) => v.pane);
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
|
|
@ -55,14 +50,14 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||
overflow={'hidden'}
|
||||
transition={'width 0.1s ease-in-out'}
|
||||
>
|
||||
<ChatSlider apps={myApps} activeAppId={appId} />
|
||||
<ChatSlider activeAppId={appId} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{(!datasetCiteData || isPc) && (
|
||||
<PageContainer flex="1 0 0" w={0} position="relative">
|
||||
{/* home chat window */}
|
||||
{pane === ChatSidebarPaneEnum.HOME && <HomeChatWindow myApps={myApps} />}
|
||||
{pane === ChatSidebarPaneEnum.HOME && <HomeChatWindow />}
|
||||
|
||||
{/* favourite apps */}
|
||||
{pane === ChatSidebarPaneEnum.FAVORITE_APPS && <ChatFavouriteApp />}
|
||||
|
|
@ -71,7 +66,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||
{pane === ChatSidebarPaneEnum.TEAM_APPS && <ChatTeamApp />}
|
||||
|
||||
{/* recently used apps chat window */}
|
||||
{pane === ChatSidebarPaneEnum.RECENTLY_USED_APPS && <AppChatWindow myApps={myApps} />}
|
||||
{pane === ChatSidebarPaneEnum.RECENTLY_USED_APPS && <AppChatWindow />}
|
||||
|
||||
{/* setting */}
|
||||
{pane === ChatSidebarPaneEnum.SETTING && <ChatSetting />}
|
||||
|
|
@ -91,19 +86,23 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const Render = (props: {
|
||||
type ChatPageProps = {
|
||||
appId: string;
|
||||
isStandalone?: string;
|
||||
showRunningStatus: boolean;
|
||||
showCite: boolean;
|
||||
showFullText: boolean;
|
||||
canDownloadSource: boolean;
|
||||
}) => {
|
||||
};
|
||||
|
||||
const ChatContent = (props: ChatPageProps) => {
|
||||
const { appId, isStandalone } = props;
|
||||
const { chatId } = useChatStore();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { isInitedUser, userInfo, myApps } = useChat(appId);
|
||||
|
||||
const isInitedUser = useContextSelector(ChatPageContext, (v) => v.isInitedUser);
|
||||
const userInfo = useContextSelector(ChatPageContext, (v) => v.userInfo);
|
||||
|
||||
const chatHistoryProviderParams = useMemo(
|
||||
() => ({ appId, source: ChatSourceEnum.online }),
|
||||
|
|
@ -144,21 +143,27 @@ const Render = (props: {
|
|||
|
||||
// show main chat interface
|
||||
return (
|
||||
<ChatSettingContextProvider>
|
||||
<ChatContextProvider params={chatHistoryProviderParams}>
|
||||
<ChatItemContextProvider
|
||||
showRouteToDatasetDetail={isStandalone !== '1'}
|
||||
showRunningStatus={props.showRunningStatus}
|
||||
canDownloadSource={props.canDownloadSource}
|
||||
isShowCite={props.showCite}
|
||||
isShowFullText={props.showFullText}
|
||||
>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<Chat myApps={myApps} />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
</ChatContextProvider>
|
||||
</ChatSettingContextProvider>
|
||||
<ChatContextProvider params={chatHistoryProviderParams}>
|
||||
<ChatItemContextProvider
|
||||
showRouteToDatasetDetail={isStandalone !== '1'}
|
||||
showRunningStatus={props.showRunningStatus}
|
||||
canDownloadSource={props.canDownloadSource}
|
||||
isShowCite={props.showCite}
|
||||
isShowFullText={props.showFullText}
|
||||
>
|
||||
<ChatRecordContextProvider params={chatRecordProviderParams}>
|
||||
<Chat />
|
||||
</ChatRecordContextProvider>
|
||||
</ChatItemContextProvider>
|
||||
</ChatContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const Render = (props: ChatPageProps) => {
|
||||
return (
|
||||
<ChatPageContextProvider appId={props.appId}>
|
||||
<ChatContent {...props} />
|
||||
</ChatPageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ export const getScheduleTriggerApp = async () => {
|
|||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: {},
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle: 'Cron Job',
|
||||
source: ChatSourceEnum.cronJob,
|
||||
userContent: {
|
||||
|
|
|
|||
|
|
@ -257,7 +257,6 @@ export const callMcpServerTool = async ({ key, toolName, inputs }: toolCallProps
|
|||
nodes,
|
||||
appChatConfig: chatConfig,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: false, // owner update use time
|
||||
newTitle,
|
||||
source: ChatSourceEnum.mcp,
|
||||
userContent: userQuestion,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type { CreateAppBody } from '@/pages/api/core/app/create';
|
|||
import type { ListAppBody } from '@/pages/api/core/app/list';
|
||||
|
||||
import type { getBasicInfoResponse } from '@/pages/api/core/app/getBasicInfo';
|
||||
import type { GetRecentlyUsedAppsResponseType } from '@fastgpt/service/core/app/record/type';
|
||||
|
||||
/**
|
||||
* 获取应用列表
|
||||
|
|
@ -14,10 +15,8 @@ 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<GetRecentlyUsedAppsResponseType>('/core/app/recentlyUsed');
|
||||
|
||||
/**
|
||||
* 创建一个应用
|
||||
|
|
|
|||
|
|
@ -13,8 +13,14 @@ import { useRouter } from 'next/router';
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
|
||||
import { getRecentlyUsedApps } from '@/web/core/app/api';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useMount } from 'ahooks';
|
||||
import type { GetRecentlyUsedAppsResponseType } from '@fastgpt/service/core/app/record/type';
|
||||
import type { UserType } from '@fastgpt/global/support/user/type';
|
||||
|
||||
export type ChatSettingContextValue = {
|
||||
export type ChatPageContextValue = {
|
||||
// Pane & collapse
|
||||
pane: ChatSidebarPaneEnum;
|
||||
handlePaneChange: (
|
||||
pane: ChatSidebarPaneEnum,
|
||||
|
|
@ -23,12 +29,18 @@ export type ChatSettingContextValue = {
|
|||
) => void;
|
||||
collapse: CollapseStatusType;
|
||||
onTriggerCollapse: () => void;
|
||||
// Chat settings
|
||||
chatSettings: ChatSettingType | undefined;
|
||||
refreshChatSetting: () => Promise<ChatSettingType | undefined>;
|
||||
logos: { wideLogoUrl?: string; squareLogoUrl?: string };
|
||||
// User & apps
|
||||
isInitedUser: boolean;
|
||||
userInfo: UserType | null;
|
||||
myApps: GetRecentlyUsedAppsResponseType;
|
||||
refreshRecentlyUsed: () => void;
|
||||
};
|
||||
|
||||
export const ChatSettingContext = createContext<ChatSettingContextValue>({
|
||||
export const ChatPageContext = createContext<ChatPageContextValue>({
|
||||
pane: ChatSidebarPaneEnum.HOME,
|
||||
handlePaneChange: () => {},
|
||||
collapse: defaultCollapseStatus,
|
||||
|
|
@ -37,19 +49,62 @@ export const ChatSettingContext = createContext<ChatSettingContextValue>({
|
|||
logos: { wideLogoUrl: '', squareLogoUrl: '' },
|
||||
refreshChatSetting: function (): Promise<ChatSettingType | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
},
|
||||
isInitedUser: false,
|
||||
userInfo: null,
|
||||
myApps: [],
|
||||
refreshRecentlyUsed: () => {}
|
||||
});
|
||||
|
||||
export const ChatSettingContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
export const ChatPageContextProvider = ({
|
||||
appId: routeAppId,
|
||||
children
|
||||
}: {
|
||||
appId: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { appId, setLastPane, setLastChatAppId, lastPane } = useChatStore();
|
||||
const { setSource, setAppId, setLastPane, setLastChatAppId, lastPane } = useChatStore();
|
||||
const { userInfo, initUserInfo } = useUserStore();
|
||||
|
||||
const { pane = lastPane || ChatSidebarPaneEnum.HOME } = router.query as {
|
||||
pane: ChatSidebarPaneEnum;
|
||||
};
|
||||
|
||||
const [collapse, setCollapse] = useState<CollapseStatusType>(defaultCollapseStatus);
|
||||
const [isInitedUser, setIsInitedUser] = useState(false);
|
||||
|
||||
// Get recently used apps
|
||||
const { data: myApps = [], refresh: refreshRecentlyUsed } = useRequest2(
|
||||
() => getRecentlyUsedApps(),
|
||||
{
|
||||
manual: false,
|
||||
errorToast: '',
|
||||
refreshDeps: [userInfo],
|
||||
pollingInterval: 30000
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize user info
|
||||
useMount(async () => {
|
||||
if (routeAppId) setAppId(routeAppId);
|
||||
try {
|
||||
await initUserInfo();
|
||||
} catch (error) {
|
||||
console.log('User not logged in:', error);
|
||||
} finally {
|
||||
setSource('online');
|
||||
setIsInitedUser(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Sync appId to store as route/appId changes
|
||||
useEffect(() => {
|
||||
if (routeAppId) {
|
||||
setAppId(routeAppId);
|
||||
}
|
||||
}, [routeAppId, setAppId, userInfo]);
|
||||
|
||||
const { data: chatSettings, runAsync: refreshChatSetting } = useRequest2(
|
||||
async () => {
|
||||
|
|
@ -69,8 +124,8 @@ export const ChatSettingContextProvider = ({ children }: { children: React.React
|
|||
|
||||
if (
|
||||
pane === ChatSidebarPaneEnum.HOME &&
|
||||
appId !== data.appId &&
|
||||
data.quickAppList.every((q) => q._id !== appId)
|
||||
routeAppId !== data.appId &&
|
||||
data.quickAppList.every((q) => q._id !== routeAppId)
|
||||
) {
|
||||
handlePaneChange(ChatSidebarPaneEnum.HOME, data.appId);
|
||||
}
|
||||
|
|
@ -126,7 +181,7 @@ export const ChatSettingContextProvider = ({ children }: { children: React.React
|
|||
setCollapse(collapse === 0 ? 1 : 0);
|
||||
}, [collapse]);
|
||||
|
||||
const value: ChatSettingContextValue = useMemoEnhance(
|
||||
const value: ChatPageContextValue = useMemoEnhance(
|
||||
() => ({
|
||||
pane,
|
||||
handlePaneChange,
|
||||
|
|
@ -134,10 +189,26 @@ export const ChatSettingContextProvider = ({ children }: { children: React.React
|
|||
onTriggerCollapse,
|
||||
chatSettings,
|
||||
refreshChatSetting,
|
||||
logos
|
||||
logos,
|
||||
isInitedUser,
|
||||
userInfo,
|
||||
myApps,
|
||||
refreshRecentlyUsed
|
||||
}),
|
||||
[pane, handlePaneChange, collapse, chatSettings, refreshChatSetting, onTriggerCollapse, logos]
|
||||
[
|
||||
pane,
|
||||
handlePaneChange,
|
||||
collapse,
|
||||
onTriggerCollapse,
|
||||
chatSettings,
|
||||
refreshChatSetting,
|
||||
logos,
|
||||
isInitedUser,
|
||||
userInfo,
|
||||
myApps,
|
||||
refreshRecentlyUsed
|
||||
]
|
||||
);
|
||||
|
||||
return <ChatSettingContext.Provider value={value}>{children}</ChatSettingContext.Provider>;
|
||||
return <ChatPageContext.Provider value={value}>{children}</ChatPageContext.Provider>;
|
||||
};
|
||||
|
|
@ -31,7 +31,6 @@ const createMockProps = (
|
|||
outputs: []
|
||||
}
|
||||
],
|
||||
isUpdateUseTime: true,
|
||||
newTitle: 'Test Chat',
|
||||
source: 'online' as any,
|
||||
userContent: {
|
||||
|
|
@ -228,7 +227,7 @@ describe('saveChat', () => {
|
|||
collectionId: 'collection-1',
|
||||
sourceId: 'source-1',
|
||||
sourceName: 'doc.pdf',
|
||||
score: [{ type: 'embedding', value: 0.95, index: 0 }],
|
||||
score: [{ type: 'embedding' as const, value: 0.95, index: 0 }],
|
||||
q: 'What is AI?',
|
||||
a: 'AI stands for Artificial Intelligence...',
|
||||
updateTime: new Date()
|
||||
|
|
@ -283,36 +282,6 @@ describe('saveChat', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should update app use time when isUpdateUseTime is true', async () => {
|
||||
const beforeTime = new Date();
|
||||
|
||||
const props = createMockProps(
|
||||
{ isUpdateUseTime: true },
|
||||
{ appId: testAppId, teamId: testTeamId, tmbId: testTmbId }
|
||||
);
|
||||
|
||||
await saveChat(props);
|
||||
|
||||
const app = await MongoApp.findById(testAppId);
|
||||
expect(app?.updateTime).toBeDefined();
|
||||
expect(app!.updateTime.getTime()).toBeGreaterThanOrEqual(beforeTime.getTime());
|
||||
});
|
||||
|
||||
it('should not update app use time when isUpdateUseTime is false', async () => {
|
||||
const app = await MongoApp.findById(testAppId);
|
||||
const originalUpdateTime = app!.updateTime;
|
||||
|
||||
const props = createMockProps(
|
||||
{ isUpdateUseTime: false },
|
||||
{ appId: testAppId, teamId: testTeamId, tmbId: testTmbId }
|
||||
);
|
||||
|
||||
await saveChat(props);
|
||||
|
||||
const updatedApp = await MongoApp.findById(testAppId);
|
||||
expect(updatedApp!.updateTime.getTime()).toBe(originalUpdateTime.getTime());
|
||||
});
|
||||
|
||||
it('should create chat data log with error count when response has error', async () => {
|
||||
const props = createMockProps(
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue