Commit fc6953fc by Archer Committed by GitHub

Fix secret (#6738)

* fix: nosql inject

* fix: nosql

* fix: ts

* doc

* fix: update feedbacksession
parent 6173a745
...@@ -142,7 +142,6 @@ FastGPT 项目特定的代码规范和约定: ...@@ -142,7 +142,6 @@ FastGPT 项目特定的代码规范和约定:
- **TypeScript 问题**: any 类型滥用、类型定义不完整、不安全断言 - **TypeScript 问题**: any 类型滥用、类型定义不完整、不安全断言
- **异步错误处理**: 未处理 Promise、错误信息丢失、静默失败 - **异步错误处理**: 未处理 Promise、错误信息丢失、静默失败
- **React 性能**: 不必要的重渲染、渲染中创建对象、缺少 memoization - **React 性能**: 不必要的重渲染、渲染中创建对象、缺少 memoization
- **工作流节点**: isEntry 未重置、交互历史未清理、白名单遗漏
- **安全漏洞**: 注入攻击、XSS、文件上传漏洞 - **安全漏洞**: 注入攻击、XSS、文件上传漏洞
📖 **详细清单**: [common-issues-checklist.md](./common-issues-checklist.md) 📖 **详细清单**: [common-issues-checklist.md](./common-issues-checklist.md)
......
...@@ -14,3 +14,4 @@ description: 'FastGPT V4.14.11 更新说明' ...@@ -14,3 +14,4 @@ description: 'FastGPT V4.14.11 更新说明'
1. 对话 Agent 模式,模型存在刷新后被重置问题。 1. 对话 Agent 模式,模型存在刷新后被重置问题。
2. 部分接口未正确进行权限校验。 2. 部分接口未正确进行权限校验。
3. 修复部分接口 nosql 注入分析。
\ No newline at end of file
...@@ -222,7 +222,7 @@ ...@@ -222,7 +222,7 @@
"document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/41410.en.mdx": "2026-03-31T23:15:29+08:00", "document/content/docs/self-host/upgrading/4-14/41410.en.mdx": "2026-03-31T23:15:29+08:00",
"document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-04-08T16:15:25+08:00", "document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-04-08T16:15:25+08:00",
"document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-07T21:48:43+08:00", "document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-09T15:12:39+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
......
...@@ -21,8 +21,10 @@ import type { ...@@ -21,8 +21,10 @@ import type {
YuqueServer YuqueServer
} from './apiDataset/type'; } from './apiDataset/type';
import type { SourceMemberType } from '../../support/user/type'; import type { SourceMemberType } from '../../support/user/type';
import type { DatasetDataIndexTypeEnum } from './data/constants'; import { DatasetDataIndexTypeEnum } from './data/constants';
import type { ParentIdType } from '../../common/parentFolder/type'; import type { ParentIdType } from '../../common/parentFolder/type';
import z from 'zod';
import { ObjectIdSchema } from '../../common/type/mongo';
export type ChunkSettingsType = { export type ChunkSettingsType = {
trainingType?: DatasetCollectionDataProcessModeEnum; trainingType?: DatasetCollectionDataProcessModeEnum;
...@@ -146,35 +148,42 @@ export type DatasetCollectionTagsSchemaType = { ...@@ -146,35 +148,42 @@ export type DatasetCollectionTagsSchemaType = {
tag: string; tag: string;
}; };
export type DatasetDataIndexItemType = { export const DatasetDataIndexItemSchema = z.object({
type: `${DatasetDataIndexTypeEnum}`; type: z.enum(DatasetDataIndexTypeEnum).meta({ description: '索引类型' }),
dataId: string; // pg data id dataId: z.string().meta({ description: 'vectorDB ID' }),
text: string; text: z.string().meta({ description: '索引文本' })
}; });
export type DatasetDataIndexItemType = z.infer<typeof DatasetDataIndexItemSchema>;
export type DatasetDataFieldType = {
q: string; // large chunks or question export const DatasetDataFieldSchema = z.object({
a?: string; // answer or custom content q: z.string().meta({ description: '问题/主文本' }),
imageId?: string; a: z.string().optional().meta({ description: '回答/补充文本' }),
}; imageId: z.string().optional().meta({ description: '图片 ID' })
export type DatasetDataSchemaType = DatasetDataFieldType & { });
_id: string; export type DatasetDataFieldType = z.infer<typeof DatasetDataFieldSchema>;
userId: string;
teamId: string; export const DatasetDataHistorySchema = DatasetDataFieldSchema.extend({
tmbId: string; updateTime: z.date().meta({ description: '更新时间' })
datasetId: string; });
collectionId: string; export type DatasetDataHistoryType = z.infer<typeof DatasetDataHistorySchema>;
chunkIndex: number;
updateTime: Date; export const DatasetDataSchema = DatasetDataFieldSchema.extend({
history?: (DatasetDataFieldType & { _id: ObjectIdSchema.meta({ description: '数据 ID' }),
updateTime: Date; userId: ObjectIdSchema.meta({ description: '用户 ID' }),
})[]; teamId: ObjectIdSchema.meta({ description: '团队 ID' }),
forbid?: boolean; tmbId: ObjectIdSchema.meta({ description: '团队成员 ID' }),
fullTextToken: string; datasetId: ObjectIdSchema.meta({ description: '数据集 ID' }),
indexes: DatasetDataIndexItemType[]; collectionId: ObjectIdSchema.meta({ description: '集合 ID' }),
rebuilding?: boolean; chunkIndex: z.int().min(0).meta({ description: '块索引' }),
imageDescMap?: Record<string, string>; updateTime: z.date().meta({ description: '更新时间' }),
}; history: z.array(DatasetDataHistorySchema).optional().meta({ description: '历史版本' }),
forbid: z.boolean().optional().meta({ description: '是否禁用' }),
fullTextToken: z.string().meta({ description: '全文 token' }),
indexes: z.array(DatasetDataIndexItemSchema).meta({ description: '向量索引' }),
rebuilding: z.boolean().optional().meta({ description: '重建中' }),
imageDescMap: z.record(z.string(), z.string()).optional().meta({ description: '图片描述映射' })
});
export type DatasetDataSchemaType = z.infer<typeof DatasetDataSchema>;
export type DatasetDataTextSchemaType = { export type DatasetDataTextSchemaType = {
_id: string; _id: string;
...@@ -308,13 +317,18 @@ export type SearchDataResponseItemType = Omit< ...@@ -308,13 +317,18 @@ export type SearchDataResponseItemType = Omit<
// score: number; // score: number;
}; };
export type DatasetCiteItemType = { export const DatasetCiteItemSchema = z
_id: string; .object({
q: string; _id: ObjectIdSchema.meta({ description: '数据 ID' }),
a?: string; q: z.string().meta({ description: '问题/主文本' }),
imagePreivewUrl?: string; a: z.string().optional().meta({ description: '回答/补充文本' }),
history?: DatasetDataSchemaType['history']; imagePreivewUrl: z.string().optional().meta({ description: '图片预览 URL' }),
updateTime: DatasetDataSchemaType['updateTime']; history: DatasetDataSchema.shape.history.optional(),
index: DatasetDataSchemaType['chunkIndex']; updateTime: DatasetDataSchema.shape.updateTime,
updated?: boolean; index: DatasetDataSchema.shape.chunkIndex,
}; updated: z.boolean().optional().meta({ description: '是否已更新' })
})
.meta({
description: '知识库引用数据列表'
});
export type DatasetCiteItemType = z.infer<typeof DatasetCiteItemSchema>;
import { z } from 'zod';
import { OutLinkChatAuthSchema } from '../../../../support/permission/chat';
import { ChatMessageSchema } from '../api';
/* ============================================================================
* API: 创建问题引导
* Route: POST /api/core/ai/agent/createQuestionGuide
* Method: POST
* Description: 根据对话历史生成推荐的引导问题列表
* Tags: ['AI', 'Agent', 'Read']
* ============================================================================ */
export const CreateQuestionGuideBodySchema = OutLinkChatAuthSchema.extend({
messages: z.array(ChatMessageSchema).meta({
description: '对话历史消息列表'
})
});
export type CreateQuestionGuideBodyType = z.infer<typeof CreateQuestionGuideBodySchema>;
export const CreateQuestionGuideResponseSchema = z.array(z.string()).meta({
example: ['你能帮我做什么?', '如何使用这个功能?'],
description: '推荐的引导问题列表'
});
export type CreateQuestionGuideResponseType = z.infer<typeof CreateQuestionGuideResponseSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import { CreateQuestionGuideBodySchema, CreateQuestionGuideResponseSchema } from './api';
export const AgentPath: OpenAPIPath = {
'/core/ai/agent/createQuestionGuide': {
post: {
summary: '创建问题引导',
description: '根据对话历史生成推荐的引导问题列表',
tags: [TagsMap.aiCommon],
requestBody: {
content: {
'application/json': {
schema: CreateQuestionGuideBodySchema
}
}
},
responses: {
200: {
description: '成功返回推荐的引导问题列表',
content: {
'application/json': {
schema: CreateQuestionGuideResponseSchema
}
}
}
}
}
}
};
...@@ -31,3 +31,24 @@ export const LLMRequestRecordSchema = z.object({ ...@@ -31,3 +31,24 @@ export const LLMRequestRecordSchema = z.object({
}); });
export type LLMRequestRecordSchemaType = z.infer<typeof LLMRequestRecordSchema>; export type LLMRequestRecordSchemaType = z.infer<typeof LLMRequestRecordSchema>;
/* ============================================================================
* 共享 Schema
* ============================================================================ */
export const ChatMessageSchema = z.object({
role: z.enum(['user', 'assistant', 'system', 'tool', 'function']).meta({
example: 'user',
description: '消息角色'
}),
content: z
.union([z.string(), z.array(z.object())])
.optional()
.meta({
example: '你好',
description: '消息内容'
}),
name: z.string().optional().meta({ description: '发送者名称' }),
tool_calls: z.array(z.object()).optional().meta({ description: '工具调用' }),
tool_call_id: z.string().optional().meta({ description: '工具调用 ID' })
});
...@@ -2,9 +2,11 @@ import type { OpenAPIPath } from '../../type'; ...@@ -2,9 +2,11 @@ import type { OpenAPIPath } from '../../type';
import { TagsMap } from '../../tag'; import { TagsMap } from '../../tag';
import { GetLLMRequestRecordParamsSchema, LLMRequestRecordSchema } from './api'; import { GetLLMRequestRecordParamsSchema, LLMRequestRecordSchema } from './api';
import { SandboxPath } from './sandbox'; import { SandboxPath } from './sandbox';
import { AgentPath } from './agent';
export const AIPath: OpenAPIPath = { export const AIPath: OpenAPIPath = {
...SandboxPath, ...SandboxPath,
...AgentPath,
'/core/ai/record/getRecord': { '/core/ai/record/getRecord': {
get: { get: {
......
...@@ -7,6 +7,8 @@ import { GetRecentlyUsedAppsResponseSchema } from './api'; ...@@ -7,6 +7,8 @@ import { GetRecentlyUsedAppsResponseSchema } from './api';
import { TagsMap } from '../../tag'; import { TagsMap } from '../../tag';
import { ChatControllerPath } from './controler'; import { ChatControllerPath } from './controler';
import { HelperBotPath } from './helperBot'; import { HelperBotPath } from './helperBot';
import { ChatQuotePath } from './quote/index';
import { ChatInputGuidePath } from './inputGuide/index';
export const ChatPath: OpenAPIPath = { export const ChatPath: OpenAPIPath = {
...ChatSettingPath, ...ChatSettingPath,
...@@ -15,6 +17,8 @@ export const ChatPath: OpenAPIPath = { ...@@ -15,6 +17,8 @@ export const ChatPath: OpenAPIPath = {
...ChatHistoryPath, ...ChatHistoryPath,
...ChatControllerPath, ...ChatControllerPath,
...HelperBotPath, ...HelperBotPath,
...ChatQuotePath,
...ChatInputGuidePath,
'/core/chat/recentlyUsed': { '/core/chat/recentlyUsed': {
get: { get: {
......
import { z } from 'zod';
import { PaginationSchema } from '../../../api';
import { ObjectIdSchema } from '../../../../common/type/mongo';
/* ============================================================================
* API: 获取对话输入引导列表
* Route: POST /api/core/chat/inputGuide/list
* Method: POST
* Description: 获取指定应用的对话输入引导列表,支持关键词搜索
* Tags: ['Chat', 'InputGuide', 'Read']
* ============================================================================ */
export const ChatInputGuideListBodySchema = PaginationSchema.extend({
appId: ObjectIdSchema.describe('应用 ID'),
searchKey: z.string().max(200).optional().default('').meta({
example: '如何使用',
description: '搜索关键词,用于模糊匹配引导文本'
})
});
export type ChatInputGuideListBodyType = z.infer<typeof ChatInputGuideListBodySchema>;
export const ChatInputGuideItemSchema = z.object({
_id: z.coerce.string().meta({ example: '68ad85a7463006c963799a05', description: '引导 ID' }),
appId: z.coerce.string().meta({ example: '68ad85a7463006c963799a06', description: '应用 ID' }),
text: z.string().meta({ example: '如何开始使用?', description: '引导文本' })
});
export const ChatInputGuideListResponseSchema = z.object({
list: z.array(ChatInputGuideItemSchema).meta({ description: '引导列表' }),
total: z.number().meta({ example: 10, description: '总数' })
});
export type ChatInputGuideListResponseType = z.infer<typeof ChatInputGuideListResponseSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import { ChatInputGuideListBodySchema, ChatInputGuideListResponseSchema } from './api';
export const ChatInputGuidePath: OpenAPIPath = {
'/core/chat/inputGuide/list': {
post: {
summary: '获取对话输入引导列表',
description: '获取指定应用的对话输入引导列表,支持关键词模糊搜索和分页',
tags: [TagsMap.chatInputGuide],
requestBody: {
content: {
'application/json': {
schema: ChatInputGuideListBodySchema
}
}
},
responses: {
200: {
description: '成功返回输入引导列表',
content: {
'application/json': {
schema: ChatInputGuideListResponseSchema
}
}
}
}
}
}
};
import { z } from 'zod';
import { ObjectIdSchema } from '../../../../common/type/mongo';
import { OutLinkChatAuthSchema } from '../../../../support/permission/chat';
import { DatasetCiteItemSchema } from '../../../../core/dataset/type';
/* ============================================================================
* API: 获取对话引用数据
* Route: POST /api/core/chat/quote/getQuote
* Method: POST
* Description: 获取指定对话消息的数据集引用列表
* Tags: ['Chat', 'Quote', 'Read']
* ============================================================================ */
export const GetQuoteBodySchema = OutLinkChatAuthSchema.extend({
appId: ObjectIdSchema.describe('应用 ID'),
chatId: z.string().min(1).max(256).meta({
example: 'chat_abc123',
description: '对话 ID'
}),
chatItemDataId: z.string().min(1).max(256).meta({
example: 'item_abc123',
description: '对话消息 dataId'
}),
datasetDataIdList: z
.array(z.string().min(1).max(256))
.max(200)
.meta({
example: ['68ad85a7463006c963799a05'],
description: '数据集数据 ID 列表'
}),
collectionIdList: z
.array(z.string().min(1).max(256))
.max(200)
.meta({
example: ['68ad85a7463006c963799a06'],
description: '集合 ID 列表'
})
});
export type GetQuoteBodyType = z.infer<typeof GetQuoteBodySchema>;
export const GetQuoteResponseSchema = z.array(DatasetCiteItemSchema);
export type GetQuoteResponseType = z.infer<typeof GetQuoteResponseSchema>;
/* ============================================================================
* API: 获取集合分页引用数据
* Route: POST /api/core/chat/quote/getCollectionQuote
* Method: POST
* Description: 以链式分页方式获取指定集合的引用数据,支持前后翻页
* Tags: ['Chat', 'Quote', 'Read']
* ============================================================================ */
export const GetCollectionQuoteBodySchema = OutLinkChatAuthSchema.extend({
appId: ObjectIdSchema.describe('应用 ID'),
chatId: z.string().min(1).max(256).describe('对话 ID'),
chatItemDataId: z.string().min(1).max(256).describe('对话消息 dataId'),
collectionId: ObjectIdSchema.describe('集合 ID'),
pageSize: z.number().int().min(1).max(30).default(15).describe('每页条数,范围 [1, 30]'),
anchor: z.number().optional().describe('当前锚点 chunkIndex'),
initialId: z.string().optional().describe('初始定位数据 ID'),
nextId: z.string().optional().describe('向后翻页的游标 ID'),
prevId: z.string().optional().describe('向前翻页的游标 ID')
});
export type GetCollectionQuoteBodyType = z.infer<typeof GetCollectionQuoteBodySchema>;
export const GetCollectionQuoteResSchema = z.object({
list: z.array(
DatasetCiteItemSchema.extend({
id: z.string().describe('数据 ID(alias _id)'),
anchor: z.number().optional().describe('chunk 序号(alias index)')
})
),
hasMorePrev: z.boolean().describe('是否还有更多前置数据'),
hasMoreNext: z.boolean().describe('是否还有更多后置数据')
});
export type GetCollectionQuoteResType = z.infer<typeof GetCollectionQuoteResSchema>;
import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag';
import {
GetQuoteBodySchema,
GetQuoteResponseSchema,
GetCollectionQuoteBodySchema,
GetCollectionQuoteResSchema
} from './api';
export const ChatQuotePath: OpenAPIPath = {
'/core/chat/quote/getQuote': {
post: {
summary: '获取对话引用数据',
description: '获取指定对话消息的数据集引用列表,需要对话访问权限',
tags: [TagsMap.chatPage],
requestBody: {
content: {
'application/json': {
schema: GetQuoteBodySchema
}
}
},
responses: {
200: {
description: '成功返回引用数据列表',
content: {
'application/json': {
schema: GetQuoteResponseSchema
}
}
}
}
}
},
'/core/chat/quote/getCollectionQuote': {
post: {
summary: '获取集合分页引用数据',
description: '以链式分页方式获取指定集合的引用数据,支持前后翻页,需要对话访问权限',
tags: [TagsMap.chatPage],
requestBody: {
content: {
'application/json': {
schema: GetCollectionQuoteBodySchema
}
}
},
responses: {
200: {
description: '成功返回分页引用数据',
content: {
'application/json': {
schema: GetCollectionQuoteResSchema
}
}
}
}
}
}
};
...@@ -23,6 +23,7 @@ export const TagsMap = { ...@@ -23,6 +23,7 @@ export const TagsMap = {
chatController: '对话操作', chatController: '对话操作',
chatFeedback: '对话反馈', chatFeedback: '对话反馈',
chatSetting: '门户页配置', chatSetting: '门户页配置',
chatInputGuide: '对话输入引导',
// Dataset // Dataset
datasetCollection: '集合', datasetCollection: '集合',
......
import { Box, Flex, HStack } from '@chakra-ui/react'; import { Box, Flex, HStack } from '@chakra-ui/react';
import { import { type SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
type DatasetCiteItemType,
type SearchDataResponseItemType
} from '@fastgpt/global/core/dataset/type';
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils'; import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -110,7 +107,7 @@ const CollectionReader = ({ ...@@ -110,7 +107,7 @@ const CollectionReader = ({
const formatedDataList = useMemo( const formatedDataList = useMemo(
() => () =>
datasetDataList.map((item: DatasetCiteItemType) => { datasetDataList.map((item) => {
const isCurrentSelected = currentQuoteItem?.id === item._id; const isCurrentSelected = currentQuoteItem?.id === item._id;
const quoteIndex = filterResults.findIndex((res) => res.id === item._id); const quoteIndex = filterResults.findIndex((res) => res.id === item._id);
......
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push'; import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide'; import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { type ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { type AuthModeType } from '@fastgpt/service/support/permission/type'; import { type AuthModeType } from '@fastgpt/service/support/permission/type';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant'; import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink'; import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink';
...@@ -17,17 +14,19 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; ...@@ -17,17 +14,19 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getDefaultLLMModel } from '@fastgpt/service/core/ai/model'; import { getDefaultLLMModel } from '@fastgpt/service/core/ai/model';
import {
CreateQuestionGuideBodySchema,
CreateQuestionGuideResponseSchema,
type CreateQuestionGuideResponseType
} from '@fastgpt/global/openapi/core/ai/agent/api';
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { type ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
async function handler( async function handler(
req: ApiRequestProps< req: ApiRequestProps,
OutLinkChatAuthProps & { _res: NextApiResponse
messages: ChatCompletionMessageParam[]; ): Promise<CreateQuestionGuideResponseType> {
} const { messages } = CreateQuestionGuideBodySchema.parse(req.body);
>,
res: NextApiResponse<any>
) {
try {
const { messages } = req.body;
const { tmbId, teamId } = await authChatCert({ const { tmbId, teamId } = await authChatCert({
req, req,
...@@ -38,14 +37,10 @@ async function handler( ...@@ -38,14 +37,10 @@ async function handler(
const qgModel = getDefaultLLMModel(); const qgModel = getDefaultLLMModel();
const { result, inputTokens, outputTokens } = await createQuestionGuide({ const { result, inputTokens, outputTokens } = await createQuestionGuide({
messages, messages: messages as ChatCompletionMessageParam[],
model: qgModel.model model: qgModel.model
}); });
jsonRes(res, {
data: result
});
pushQuestionGuideUsage({ pushQuestionGuideUsage({
model: qgModel.model, model: qgModel.model,
inputTokens, inputTokens,
...@@ -53,12 +48,8 @@ async function handler( ...@@ -53,12 +48,8 @@ async function handler(
teamId, teamId,
tmbId tmbId
}); });
} catch (err) {
jsonRes(res, { return CreateQuestionGuideResponseSchema.parse(result);
code: 500,
error: err
});
}
} }
export default NextAPI(handler); export default NextAPI(handler);
......
...@@ -89,8 +89,7 @@ async function handler( ...@@ -89,8 +89,7 @@ async function handler(
} }
}, },
{ {
sort: { createTime: -1 }, sort: { createTime: -1 }
session
} }
); );
} }
......
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import type { import {
GetHelperBotChatRecordsParamsType, GetHelperBotChatRecordsParamsSchema,
GetHelperBotChatRecordsResponseType type GetHelperBotChatRecordsParamsType,
type GetHelperBotChatRecordsResponseType
} from '@fastgpt/global/openapi/core/chat/helperBot/api'; } from '@fastgpt/global/openapi/core/chat/helperBot/api';
import { authHelperBotChatCrud } from '@/service/support/permission/auth/chat'; import { authHelperBotChatCrud } from '@/service/support/permission/auth/chat';
import { MongoHelperBotChatItem } from '../../../../../../../../packages/service/core/chat/HelperBot/chatItemSchema'; import { MongoHelperBotChatItem } from '../../../../../../../../packages/service/core/chat/HelperBot/chatItemSchema';
...@@ -18,15 +19,15 @@ async function handler( ...@@ -18,15 +19,15 @@ async function handler(
req: ApiRequestProps<getRecordsBody, getRecordsQuery>, req: ApiRequestProps<getRecordsBody, getRecordsQuery>,
res: ApiResponseType<any> res: ApiResponseType<any>
): Promise<getRecordsResponse> { ): Promise<getRecordsResponse> {
const { type, chatId } = req.query; const { type, chatId } = GetHelperBotChatRecordsParamsSchema.parse(req.query);
const { chat, userId } = await authHelperBotChatCrud({ const { userId } = await authHelperBotChatCrud({
type, type,
chatId, chatId,
req, req,
authToken: true authToken: true
}); });
const { offset, pageSize } = parsePaginationRequest(req); const { offset } = parsePaginationRequest(req);
const [histories, total] = await Promise.all([ const [histories, total] = await Promise.all([
MongoHelperBotChatItem.find({ userId, chatId }).sort({ _id: -1 }).skip(offset).limit(20).lean(), MongoHelperBotChatItem.find({ userId, chatId }).sort({ _id: -1 }).skip(offset).limit(20).lean(),
......
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema'; import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
import { type PaginationProps, type PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { type ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
export type ChatInputGuideProps = PaginationProps<{ import {
appId: string; ChatInputGuideListBodySchema,
searchKey: string; ChatInputGuideListResponseSchema,
}>; type ChatInputGuideListResponseType
export type ChatInputGuideResponse = PaginationResponse<ChatInputGuideSchemaType>; } from '@fastgpt/global/openapi/core/chat/inputGuide/api';
async function handler( async function handler(
req: ApiRequestProps<ChatInputGuideProps>, req: ApiRequestProps,
res: NextApiResponse<any> res: NextApiResponse<any>
): Promise<ChatInputGuideResponse> { ): Promise<ChatInputGuideListResponseType> {
const { appId, searchKey } = req.body; const { appId, searchKey } = ChatInputGuideListBodySchema.parse(req.body);
const { offset, pageSize } = parsePaginationRequest(req); const { offset, pageSize } = parsePaginationRequest(req);
await authApp({ req, appId, authToken: true, per: ReadPermissionVal }); await authApp({ req, appId, authToken: true, per: ReadPermissionVal });
const params = { const params = {
appId, appId,
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } }) ...(searchKey && { text: { $regex: replaceRegChars(searchKey), $options: 'i' } })
}; };
const [result, total] = await Promise.all([ const [result, total] = await Promise.all([
...@@ -33,10 +31,10 @@ async function handler( ...@@ -33,10 +31,10 @@ async function handler(
MongoChatInputGuide.countDocuments(params) MongoChatInputGuide.countDocuments(params)
]); ]);
return { return ChatInputGuideListResponseSchema.parse({
list: result, list: result,
total total
}; });
} }
export default NextAPI(handler); export default NextAPI(handler);
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat'; import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
import { import { type DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type';
type DatasetCiteItemType,
type DatasetDataSchemaType
} from '@fastgpt/global/core/dataset/type';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/service/type/next';
import {
type LinkedListResponse,
type LinkedPaginationProps
} from '@fastgpt/web/common/fetch/type';
import { type FilterQuery, Types } from 'mongoose'; import { type FilterQuery, Types } from 'mongoose';
import { quoteDataFieldSelector } from '@/service/core/chat/constants'; import { quoteDataFieldSelector } from '@/service/core/chat/constants';
import { processChatTimeFilter } from '@/service/core/chat/utils'; import { processChatTimeFilter } from '@/service/core/chat/utils';
...@@ -17,27 +10,14 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; ...@@ -17,27 +10,14 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller'; import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller'; import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import {
export type GetCollectionQuoteProps = LinkedPaginationProps<{}, number> & { GetCollectionQuoteBodySchema,
chatId: string; type GetCollectionQuoteResType
chatItemDataId: string; } from '@fastgpt/global/openapi/core/chat/quote/api';
collectionId: string;
appId: string;
shareId?: string;
outLinkUid?: string;
teamId?: string;
teamToken?: string;
};
export type GetCollectionQuoteRes = LinkedListResponse<DatasetCiteItemType, number>;
type BaseMatchType = FilterQuery<DatasetDataSchemaType>; type BaseMatchType = FilterQuery<DatasetDataSchemaType>;
async function handler( async function handler(req: ApiRequestProps): Promise<GetCollectionQuoteResType> {
req: ApiRequestProps<GetCollectionQuoteProps>
): Promise<GetCollectionQuoteRes> {
const { const {
initialId, initialId,
prevId, prevId,
...@@ -52,10 +32,10 @@ async function handler( ...@@ -52,10 +32,10 @@ async function handler(
outLinkUid, outLinkUid,
teamId, teamId,
teamToken, teamToken,
pageSize = 15 pageSize
} = req.body; } = GetCollectionQuoteBodySchema.parse(req.body);
const limitedPageSize = Math.min(pageSize, 30); const limitedPageSize = pageSize;
const [collection, { chat, showFullText }, chatItem] = await Promise.all([ const [collection, { chat, showFullText }, chatItem] = await Promise.all([
getCollectionWithDataset(collectionId), getCollectionWithDataset(collectionId),
...@@ -125,7 +105,7 @@ async function handleInitialLoad({ ...@@ -125,7 +105,7 @@ async function handleInitialLoad({
pageSize: number; pageSize: number;
chatTime: Date; chatTime: Date;
baseMatch: BaseMatchType; baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> { }): Promise<GetCollectionQuoteResType> {
const centerNode = await MongoDatasetData.findOne( const centerNode = await MongoDatasetData.findOne(
{ {
_id: new Types.ObjectId(initialId) _id: new Types.ObjectId(initialId)
...@@ -192,7 +172,7 @@ async function handlePaginatedLoad({ ...@@ -192,7 +172,7 @@ async function handlePaginatedLoad({
pageSize: number; pageSize: number;
chatTime: Date; chatTime: Date;
baseMatch: BaseMatchType; baseMatch: BaseMatchType;
}): Promise<GetCollectionQuoteRes> { }): Promise<GetCollectionQuoteResType> {
const { list, hasMore } = prevId const { list, hasMore } = prevId
? await getPrevNodes(prevId, nextAnchor, pageSize, baseMatch) ? await getPrevNodes(prevId, nextAnchor, pageSize, baseMatch)
: await getNextNodes(nextId!, nextAnchor, pageSize, baseMatch); : await getNextNodes(nextId!, nextAnchor, pageSize, baseMatch);
......
...@@ -6,38 +6,25 @@ import { quoteDataFieldSelector } from '@/service/core/chat/constants'; ...@@ -6,38 +6,25 @@ import { quoteDataFieldSelector } from '@/service/core/chat/constants';
import { processChatTimeFilter } from '@/service/core/chat/utils'; import { processChatTimeFilter } from '@/service/core/chat/utils';
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller'; import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
import type { DatasetCiteItemType } from '@fastgpt/global/core/dataset/type';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import {
GetQuoteBodySchema,
GetQuoteResponseSchema,
type GetQuoteResponseType
} from '@fastgpt/global/openapi/core/chat/quote/api';
export type GetQuoteProps = { async function handler(req: ApiRequestProps): Promise<GetQuoteResponseType> {
datasetDataIdList: string[];
collectionIdList: string[];
chatId: string;
chatItemDataId: string;
appId: string;
shareId?: string;
outLinkUid?: string;
teamId?: string;
teamToken?: string;
};
export type GetQuotesRes = DatasetCiteItemType[];
async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRes> {
const { const {
appId, appId,
chatId, chatId,
chatItemDataId, chatItemDataId,
shareId, shareId,
outLinkUid, outLinkUid,
teamId, teamId,
teamToken, teamToken,
collectionIdList, collectionIdList,
datasetDataIdList datasetDataIdList
} = req.body; } = GetQuoteBodySchema.parse(req.body);
const [{ chat, showCite }, chatItem] = await Promise.all([ const [{ chat, showCite }, chatItem] = await Promise.all([
authChatCrud({ authChatCrud({
...@@ -60,12 +47,10 @@ async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRe ...@@ -60,12 +47,10 @@ async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRe
quoteDataFieldSelector quoteDataFieldSelector
).lean(); ).lean();
// Get image preview url
const formatPreviewUrlList = getFormatDatasetCiteList(list); const formatPreviewUrlList = getFormatDatasetCiteList(list);
const quoteList = processChatTimeFilter(formatPreviewUrlList, chatItem.time); const quoteList = processChatTimeFilter(formatPreviewUrlList, chatItem.time);
return quoteList; return GetQuoteResponseSchema.parse(quoteList);
} }
export default NextAPI(handler); export default NextAPI(handler);
...@@ -6,12 +6,6 @@ import { NextAPI } from '@/service/middleware/entry'; ...@@ -6,12 +6,6 @@ import { NextAPI } from '@/service/middleware/entry';
import { type OutLinkSchema } from '@fastgpt/global/support/outLink/type'; import { type OutLinkSchema } from '@fastgpt/global/support/outLink/type';
import type { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant'; import type { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
export const ApiMetadata = {
name: '获取应用内所有 Outlink',
author: 'Finley',
version: '0.1.0'
};
export type OutLinkListQuery = { export type OutLinkListQuery = {
appId: string; // 应用 ID appId: string; // 应用 ID
type: `${PublishChannelEnum}`; type: `${PublishChannelEnum}`;
......
...@@ -39,7 +39,7 @@ const formatIndexes = async ({ ...@@ -39,7 +39,7 @@ const formatIndexes = async ({
indexPrefix?: string; indexPrefix?: string;
}): Promise< }): Promise<
{ {
type: `${DatasetDataIndexTypeEnum}`; type: DatasetDataIndexTypeEnum;
text: string; text: string;
dataId?: string; dataId?: string;
}[] }[]
......
...@@ -12,13 +12,16 @@ import type { ...@@ -12,13 +12,16 @@ import type {
getChatRecordsBody, getChatRecordsBody,
getChatRecordsResponse getChatRecordsResponse
} from '@/pages/api/core/chat/record/getRecords_v2'; } from '@/pages/api/core/chat/record/getRecords_v2';
import type { GetQuoteProps, GetQuotesRes } from '@/pages/api/core/chat/quote/getQuote';
import type { import type {
GetCollectionQuoteProps, GetQuoteBodyType,
GetCollectionQuoteRes GetQuoteResponseType
} from '@/pages/api/core/chat/quote/getCollectionQuote'; } from '@fastgpt/global/openapi/core/chat/quote/api';
import type { ChatSettingModelType, ChatSettingType } from '@fastgpt/global/core/chat/setting/type'; import type { ChatSettingModelType, ChatSettingType } from '@fastgpt/global/core/chat/setting/type';
import type { import type {
GetCollectionQuoteBodyType,
GetCollectionQuoteResType
} from '@fastgpt/global/openapi/core/chat/quote/api';
import type {
GetChatFavouriteListParamsType, GetChatFavouriteListParamsType,
UpdateFavouriteAppParamsType UpdateFavouriteAppParamsType
} from '@fastgpt/global/openapi/core/chat/favourite/api'; } from '@fastgpt/global/openapi/core/chat/favourite/api';
...@@ -57,11 +60,11 @@ export const getChatRecords = (data: getChatRecordsBody) => ...@@ -57,11 +60,11 @@ export const getChatRecords = (data: getChatRecordsBody) =>
export const delChatRecordById = (data: DeleteChatItemProps) => export const delChatRecordById = (data: DeleteChatItemProps) =>
POST(`/core/chat/item/delete`, data); POST(`/core/chat/item/delete`, data);
export const getQuoteDataList = (data: GetQuoteProps) => export const getQuoteDataList = (data: GetQuoteBodyType) =>
POST<GetQuotesRes>(`/core/chat/quote/getQuote`, data); POST<GetQuoteResponseType>(`/core/chat/quote/getQuote`, data);
export const getCollectionQuote = (data: GetCollectionQuoteProps) => export const getCollectionQuote = (data: GetCollectionQuoteBodyType) =>
POST<GetCollectionQuoteRes>(`/core/chat/quote/getCollectionQuote`, data); POST<GetCollectionQuoteResType>(`/core/chat/quote/getCollectionQuote`, data);
/*---------- chat setting ------------*/ /*---------- chat setting ------------*/
export const getChatSetting = () => GET<ChatSettingType>('/proApi/core/chat/setting/detail'); export const getChatSetting = () => GET<ChatSettingType>('/proApi/core/chat/setting/detail');
......
import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
import type { import type {
ChatInputGuideProps, ChatInputGuideListBodyType,
ChatInputGuideResponse ChatInputGuideListResponseType
} from '@/pages/api/core/chat/inputGuide/list'; } from '@fastgpt/global/openapi/core/chat/inputGuide/api';
import type { import type {
countChatInputGuideTotalQuery, countChatInputGuideTotalQuery,
countChatInputGuideTotalResponse countChatInputGuideTotalResponse
...@@ -24,8 +24,8 @@ export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery) ...@@ -24,8 +24,8 @@ export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery)
/** /**
* Get chat input guide list * Get chat input guide list
*/ */
export const getChatInputGuideList = (data: ChatInputGuideProps) => export const getChatInputGuideList = (data: ChatInputGuideListBodyType) =>
POST<ChatInputGuideResponse>(`/core/chat/inputGuide/list`, data); POST<ChatInputGuideListResponseType>(`/core/chat/inputGuide/list`, data);
export const queryChatInputGuideList = (data: QueryChatInputGuideBody, url?: string) => { export const queryChatInputGuideList = (data: QueryChatInputGuideBody, url?: string) => {
if (url) { if (url) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment