diff --git a/document/content/docs/introduction/guide/plugins/meta.json b/document/content/docs/introduction/guide/plugins/meta.json index 9cda406e9656..774b87483212 100644 --- a/document/content/docs/introduction/guide/plugins/meta.json +++ b/document/content/docs/introduction/guide/plugins/meta.json @@ -1,5 +1,14 @@ { "title": "系统插件", "description": "介绍如何使用和提交系统插件,以及各插件的填写说明", - "pages": ["dev_system_tool","how_to_submit_system_plugin","searxng_plugin_guide","google_search_plugin_guide","bing_search_plugin","doc2x_plugin_guide"] -} \ No newline at end of file + "pages": [ + "dev_system_tool", + "how_to_submit_system_plugin", + "upload_system_tool", + "searxng_plugin_guide", + "google_search_plugin_guide", + "bing_search_plugin", + "doc2x_plugin_guide", + "deepseek_plugin_guide" + ] +} diff --git a/document/content/docs/introduction/guide/plugins/upload_system_tool.mdx b/document/content/docs/introduction/guide/plugins/upload_system_tool.mdx new file mode 100644 index 000000000000..b5dc821567d6 --- /dev/null +++ b/document/content/docs/introduction/guide/plugins/upload_system_tool.mdx @@ -0,0 +1,123 @@ +--- +title: 如何在线上传系统工具 +description: FastGPT 系统工具在线上传指南 +--- + +> 从 FastGPT 4.14.0 版本开始,系统管理员可以通过 Web 界面直接上传和更新系统工具,无需重新部署服务 + +## 权限要求 + +⚠️ **重要提示**:只有 **root 用户** 才能使用在线上传系统工具功能。 + +- 确保您已使用 `root` 账户登录 FastGPT +- 普通用户无法看到"导入/更新"按钮和删除功能 + +## 支持的文件格式 + +- **文件类型**:`.js` 文件 +- **文件大小**:最大 10MB +- **文件数量**:每次只能上传一个文件 + +## 上传步骤 + +### 1. 进入系统工具页面 + +1. 登录 FastGPT 管理后台 +2. 导航到:**工作台** → **系统工具** +3. 确认页面右上角显示"导入/更新"按钮(只有 root 用户可见) + +![](/imgs/plugins/entry.png) + +### 2. 准备工具文件 + +在上传之前,请确保您的 `.js` 文件是从 fastgpt-plugin 项目中通过 `bun run build` 命令打包后的 dist/tools/built-in 文件夹下得到的 + +![](/imgs/plugins/file.png) + +### 3. 执行上传 + +1. 点击 **"导入/更新"** 按钮 +2. 在弹出的对话框中,点击文件选择区域 +3. 选择您准备好的 `.js` 工具文件 +4. 确认文件信息无误后,点击 **"确认导入"** + +### 4. 上传过程 + +- 上传成功后会显示成功提示 +- 页面自动刷新,新工具会出现在工具列表中 + +## 功能特点 + +### 工具管理 + +- **查看工具**:所有用户都可以查看已安装的系统工具 +- **上传工具**:仅 root 用户可以上传新工具或更新现有工具 +- **删除工具**:仅 root 用户可以删除已上传的工具 + +### 工具类型识别 + +系统会根据工具的配置自动识别工具类型: + +- 🔧 **工具 (tools)** +- 🔍 **搜索 (search)** +- 🎨 **多模态 (multimodal)** +- 💬 **通讯 (communication)** +- 📦 **其他 (other)** + +## 常见问题 + +### Q: 上传失败,提示"文件内容存在错误" + +**可能原因:** +- fastgpt-plugin 项目不是最新的,导致打包的 `.js` 文件缺少正确的内容 +- 工具配置格式不正确 + +**解决方案:** +1. 拉取最新的 fastgpt-plugin 项目重新进行 `bun run build` 获得打包后的 `.js` 文件 +2. 检查本地插件运行是否成功 + +### Q: 无法看到"导入/更新"按钮 + +**原因:** 当前用户不是 root 用户 + +**解决方案:** 使用 root 账户重新登录 + +### Q: 文件上传超时 + +**可能原因:** +- 文件过大(超过 10MB) +- 网络连接不稳定 + +**解决方案:** +1. 确认文件大小在限制范围内 +2. 检查网络连接 +3. 尝试重新上传 + +## 最佳实践 + +### 上传前检查 + +1. **代码测试**:在本地环境测试工具功能 +2. **格式验证**:确保符合 FastGPT 工具规范 +3. **文件大小**:保持文件在合理大小范围内 + +### 版本管理 + +- 建议为工具添加版本号注释 +- 更新工具时,先备份原有版本 +- 记录更新日志和功能变更 + +### 安全考虑 + +- 仅上传来源可信的工具文件 +- 避免包含敏感信息或凭据 +- 定期审查已安装的工具 + +### 存储方式 + +- 工具文件存储在 MinIO 中 +- 工具元数据保存在 MongoDB 中 + +--- + +通过在线上传功能,您可以快速部署和管理系统工具,提高 FastGPT 的扩展性和灵活性。如遇到问题,请参考上述常见问题或联系技术支持。 diff --git a/document/content/docs/toc.mdx b/document/content/docs/toc.mdx index 48f073af6bec..65fb0c72d802 100644 --- a/document/content/docs/toc.mdx +++ b/document/content/docs/toc.mdx @@ -90,6 +90,7 @@ description: FastGPT 文档目录 - [/docs/introduction/guide/plugins/doc2x_plugin_guide](/docs/introduction/guide/plugins/doc2x_plugin_guide) - [/docs/introduction/guide/plugins/google_search_plugin_guide](/docs/introduction/guide/plugins/google_search_plugin_guide) - [/docs/introduction/guide/plugins/searxng_plugin_guide](/docs/introduction/guide/plugins/searxng_plugin_guide) +- [/docs/introduction/guide/plugins/upload_system_tool](/docs/introduction/guide/plugins/upload_system_tool) - [/docs/introduction/guide/team_permissions/invitation_link](/docs/introduction/guide/team_permissions/invitation_link) - [/docs/introduction/guide/team_permissions/team_roles_permissions](/docs/introduction/guide/team_permissions/team_roles_permissions) - [/docs/introduction/index](/docs/introduction/index) diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 19c0e8dda602..487e1bcfc1a6 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -87,6 +87,7 @@ "document/content/docs/introduction/guide/plugins/doc2x_plugin_guide.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/plugins/google_search_plugin_guide.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/plugins/searxng_plugin_guide.mdx": "2025-07-23T21:35:03+08:00", + "document/content/docs/introduction/guide/plugins/upload_system_tool.mdx": "2025-07-31T11:46:10+08:00", "document/content/docs/introduction/guide/team_permissions/invitation_link.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/team_permissions/team_roles_permissions.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/index.en.mdx": "2025-07-23T21:35:03+08:00", diff --git a/document/public/imgs/plugins/entry.png b/document/public/imgs/plugins/entry.png new file mode 100644 index 000000000000..aaadd6fad6cf Binary files /dev/null and b/document/public/imgs/plugins/entry.png differ diff --git a/document/public/imgs/plugins/file.png b/document/public/imgs/plugins/file.png new file mode 100644 index 000000000000..8843823d1c05 Binary files /dev/null and b/document/public/imgs/plugins/file.png differ diff --git a/packages/global/core/app/plugin/type.d.ts b/packages/global/core/app/plugin/type.d.ts index 807e15a4d22e..1849beef75c5 100644 --- a/packages/global/core/app/plugin/type.d.ts +++ b/packages/global/core/app/plugin/type.d.ts @@ -56,6 +56,9 @@ export type SystemPluginTemplateItemType = WorkflowTemplateType & { inputList?: FlowNodeInputItemType['inputList']; inputListVal?: Record; hasSystemSecret?: boolean; + + // Plugin source type + toolSource?: 'uploaded' | 'built-in'; }; export type SystemPluginTemplateListItemType = Omit< diff --git a/packages/global/core/workflow/type/node.d.ts b/packages/global/core/workflow/type/node.d.ts index 69a83353cd48..23e2617f620c 100644 --- a/packages/global/core/workflow/type/node.d.ts +++ b/packages/global/core/workflow/type/node.d.ts @@ -142,6 +142,7 @@ export type NodeTemplateListItemType = { instructions?: string; // 使用说明 courseUrl?: string; // 教程链接 sourceMember?: SourceMember; + toolSource?: 'uploaded' | 'built-in'; // Plugin source type }; export type NodeTemplateListType = { diff --git a/packages/global/package.json b/packages/global/package.json index 75e16b2f6e33..7aa6be14047a 100644 --- a/packages/global/package.json +++ b/packages/global/package.json @@ -2,7 +2,7 @@ "name": "@fastgpt/global", "version": "1.0.0", "dependencies": { - "@fastgpt-sdk/plugin": "^0.1.16", + "@fastgpt-sdk/plugin": "^0.1.18", "@apidevtools/swagger-parser": "^10.1.0", "@bany/curl-to-json": "^1.2.8", "axios": "^1.12.1", diff --git a/packages/service/common/cache/index.ts b/packages/service/common/cache/index.ts new file mode 100644 index 000000000000..3388caeac49c --- /dev/null +++ b/packages/service/common/cache/index.ts @@ -0,0 +1,37 @@ +import './init'; +import { FASTGPT_REDIS_PREFIX, getGlobalRedisConnection } from '../../common/redis'; +import type { SystemCacheKeyEnum } from './type'; +import { randomUUID } from 'node:crypto'; +import { initCache } from './init'; + +export const refreshSyncKey = async (key: `${SystemCacheKeyEnum}`) => { + if (!global.systemCache) initCache(); + const val = randomUUID(); + const redis = getGlobalRedisConnection(); + await redis.set(`${FASTGPT_REDIS_PREFIX}:SYNC_KEY:${key}`, val); +}; + +const getSynckey = async (key: `${SystemCacheKeyEnum}`) => { + if (!global.systemCache) initCache(); + const redis = getGlobalRedisConnection(); + const syncKey = `${FASTGPT_REDIS_PREFIX}:SYNC_KEY:${key}`; + const val = await redis.get(syncKey); + if (val) return val; + const newVal = randomUUID(); + await redis.set(syncKey, newVal); + return newVal; +}; + +export const getCachedData = async (key: `${SystemCacheKeyEnum}`) => { + if (!global.systemCache) initCache(); + const syncKey = await getSynckey(key); + const isDisableCache = process.env.DISABLE_CACHE === 'true'; + if (global.systemCache[key].syncKey === syncKey && !isDisableCache) { + return global.systemCache[key].data; + } + const refreshedData = await global.systemCache[key].refreshFunc(); + await refreshSyncKey(key); + global.systemCache[key].data = refreshedData; + global.systemCache[key].syncKey = syncKey; + return global.systemCache[key].data; +}; diff --git a/packages/service/common/cache/init.ts b/packages/service/common/cache/init.ts new file mode 100644 index 000000000000..3ad6a0bdcb31 --- /dev/null +++ b/packages/service/common/cache/init.ts @@ -0,0 +1,12 @@ +import { SystemCacheKeyEnum } from './type'; +import { refreshSystemTools } from '../../core/app/plugin/controller'; + +export const initCache = () => { + global.systemCache = { + [SystemCacheKeyEnum.systemTool]: { + syncKey: '', + data: [], + refreshFunc: refreshSystemTools + } + }; +}; diff --git a/packages/service/common/cache/type.ts b/packages/service/common/cache/type.ts new file mode 100644 index 000000000000..f45b9dffce8c --- /dev/null +++ b/packages/service/common/cache/type.ts @@ -0,0 +1,21 @@ +import type { SystemPluginTemplateItemType } from '@fastgpt/global/core/app/plugin/type'; + +export enum SystemCacheKeyEnum { + systemTool = 'systemTool' +} + +export type SystemCacheDataType = { + [SystemCacheKeyEnum.systemTool]: SystemPluginTemplateItemType[]; +}; + +type SystemCacheType = { + [K in SystemCacheKeyEnum]: { + syncKey: string; + data: SystemCacheDataType[K]; + refreshFunc: () => Promise; + }; +}; + +declare global { + var systemCache: SystemCacheType; +} diff --git a/packages/service/common/s3/config.ts b/packages/service/common/s3/config.ts new file mode 100644 index 000000000000..501d61be4b1a --- /dev/null +++ b/packages/service/common/s3/config.ts @@ -0,0 +1,10 @@ +import type { S3ServiceConfig } from './type'; + +export const defualtS3Config: Omit = { + endPoint: process.env.S3_ENDPOINT || 'localhost', + port: process.env.S3_PORT ? parseInt(process.env.S3_PORT) : 9000, + useSSL: process.env.S3_USE_SSL === 'true', + accessKey: process.env.S3_ACCESS_KEY || 'minioadmin', + secretKey: process.env.S3_SECRET_KEY || 'minioadmin', + externalBaseURL: process.env.S3_EXTERNAL_BASE_URL +}; diff --git a/packages/service/common/s3/const.ts b/packages/service/common/s3/const.ts new file mode 100644 index 000000000000..c91043dc1f96 --- /dev/null +++ b/packages/service/common/s3/const.ts @@ -0,0 +1,20 @@ +export const mimeMap: Record = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.pdf': 'application/pdf', + '.txt': 'text/plain', + '.json': 'application/json', + '.csv': 'text/csv', + '.zip': 'application/zip', + '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + '.doc': 'application/msword', + '.xls': 'application/vnd.ms-excel', + '.ppt': 'application/vnd.ms-powerpoint', + '.js': 'application/javascript' +}; diff --git a/packages/service/common/s3/controller.ts b/packages/service/common/s3/controller.ts new file mode 100644 index 000000000000..98f97896f0e6 --- /dev/null +++ b/packages/service/common/s3/controller.ts @@ -0,0 +1,170 @@ +import { Client } from 'minio'; +import { + type FileMetadataType, + type PresignedUrlInput as UploadPresignedURLProps, + type UploadPresignedURLResponse, + type S3ServiceConfig +} from './type'; +import { defualtS3Config } from './config'; +import { randomBytes } from 'crypto'; +import { HttpProxyAgent } from 'http-proxy-agent'; +import { HttpsProxyAgent } from 'https-proxy-agent'; +import { extname } from 'path'; +import { addLog } from '../../common/system/log'; +import { getErrText } from '@fastgpt/global/common/error/utils'; +import { mimeMap } from './const'; + +export class S3Service { + private client: Client; + private config: S3ServiceConfig; + private initialized: boolean = false; + initFunction?: () => Promise; + + constructor(config?: Partial) { + this.config = { ...defualtS3Config, ...config } as S3ServiceConfig; + + this.client = new Client({ + endPoint: this.config.endPoint, + port: this.config.port, + useSSL: this.config.useSSL, + accessKey: this.config.accessKey, + secretKey: this.config.secretKey, + transportAgent: process.env.HTTP_PROXY + ? new HttpProxyAgent(process.env.HTTP_PROXY) + : process.env.HTTPS_PROXY + ? new HttpsProxyAgent(process.env.HTTPS_PROXY) + : undefined + }); + + this.initFunction = config?.initFunction; + } + + public async init() { + if (!this.initialized) { + if (!(await this.client.bucketExists(this.config.bucket))) { + addLog.info(`Creating bucket: ${this.config.bucket}`); + await this.client.makeBucket(this.config.bucket); + } + + await this.initFunction?.(); + this.initialized = true; + } + } + + private generateFileId(): string { + return randomBytes(16).toString('hex'); + } + + private generateAccessUrl(filename: string): string { + const protocol = this.config.useSSL ? 'https' : 'http'; + const port = + this.config.port && this.config.port !== (this.config.useSSL ? 443 : 80) + ? `:${this.config.port}` + : ''; + + const externalBaseURL = this.config.externalBaseURL; + return externalBaseURL + ? `${externalBaseURL}/${encodeURIComponent(filename)}` + : `${protocol}://${this.config.endPoint}${port}/${this.config.bucket}/${encodeURIComponent(filename)}`; + } + + uploadFile = async (fileBuffer: Buffer, originalFilename: string): Promise => { + await this.init(); + const inferContentType = (filename: string) => { + const ext = extname(filename).toLowerCase(); + return mimeMap[ext] || 'application/octet-stream'; + }; + + if (this.config.maxFileSize && fileBuffer.length > this.config.maxFileSize) { + return Promise.reject( + `File size ${fileBuffer.length} exceeds limit ${this.config.maxFileSize}` + ); + } + + const fileId = this.generateFileId(); + const objectName = `${fileId}-${originalFilename}`; + const uploadTime = new Date(); + + const contentType = inferContentType(originalFilename); + await this.client.putObject(this.config.bucket, objectName, fileBuffer, fileBuffer.length, { + 'Content-Type': contentType, + 'Content-Disposition': `attachment; filename="${encodeURIComponent(originalFilename)}"`, + 'x-amz-meta-original-filename': encodeURIComponent(originalFilename), + 'x-amz-meta-upload-time': uploadTime.toISOString() + }); + + const metadata: FileMetadataType = { + fileId, + originalFilename, + contentType, + size: fileBuffer.length, + uploadTime, + accessUrl: this.generateAccessUrl(objectName) + }; + + return metadata; + }; + + generateUploadPresignedURL = async ({ + filepath, + contentType, + metadata, + filename + }: UploadPresignedURLProps): Promise => { + await this.init(); + const objectName = `${filepath}/${filename}`; + + try { + const policy = this.client.newPostPolicy(); + + policy.setBucket(this.config.bucket); + policy.setKey(objectName); + if (contentType) { + policy.setContentType(contentType); + } + if (this.config.maxFileSize) { + policy.setContentLengthRange(1, this.config.maxFileSize); + } + policy.setExpires(new Date(Date.now() + 10 * 60 * 1000)); // 10 mins + + policy.setUserMetaData({ + 'original-filename': encodeURIComponent(filename), + 'upload-time': new Date().toISOString(), + ...metadata + }); + + const { postURL, formData } = await this.client.presignedPostPolicy(policy); + + const response: UploadPresignedURLResponse = { + objectName, + uploadUrl: postURL, + formData + }; + + return response; + } catch (error) { + addLog.error('Failed to generate Upload Presigned URL', error); + return Promise.reject(`Failed to generate Upload Presigned URL: ${getErrText(error)}`); + } + }; + + generateDownloadUrl = (objectName: string): string => { + const pathParts = objectName.split('/'); + const encodedParts = pathParts.map((part) => encodeURIComponent(part)); + const encodedObjectName = encodedParts.join('/'); + return `${this.config.bucket}/${encodedObjectName}`; + }; + + getFile = async (objectName: string): Promise => { + try { + const stat = await this.client.statObject(this.config.bucket, objectName); + if (stat.size > 0) { + const accessUrl = this.generateDownloadUrl(objectName); + return accessUrl; + } + return Promise.reject(`File ${objectName} not found`); + } catch (error) { + return Promise.reject(`Failed to getFile: ${objectName}: ${getErrText(error)}`); + } + }; +} diff --git a/packages/service/common/s3/index.ts b/packages/service/common/s3/index.ts new file mode 100644 index 000000000000..3ecc91f0935e --- /dev/null +++ b/packages/service/common/s3/index.ts @@ -0,0 +1,15 @@ +import { S3Service } from './controller'; + +export const PluginS3Service = (() => { + if (!global.pluginS3Service) { + global.pluginS3Service = new S3Service({ + bucket: process.env.S3_PLUGIN_BUCKET, + maxFileSize: 10 * 1024 * 1024 // 10MB + }); + } + return global.pluginS3Service; +})(); + +declare global { + var pluginS3Service: S3Service; +} diff --git a/packages/service/common/s3/type.ts b/packages/service/common/s3/type.ts new file mode 100644 index 000000000000..a480530493e8 --- /dev/null +++ b/packages/service/common/s3/type.ts @@ -0,0 +1,49 @@ +import type { ClientOptions } from 'minio'; + +export type S3ServiceConfig = { + bucket: string; + externalBaseURL?: string; + /** + * Unit: Byte + */ + maxFileSize?: number; + /** + * for executing some init function for the s3 service + */ + initFunction?: () => Promise; +} & ClientOptions; + +export type FileMetadataType = { + fileId: string; + originalFilename: string; + contentType: string; + size: number; + uploadTime: Date; + accessUrl: string; +}; + +export type PresignedUrlInput = { + filepath: string; + filename: string; + contentType?: string; + metadata?: Record; +}; + +export type UploadPresignedURLResponse = { + objectName: string; + uploadUrl: string; + formData: Record; +}; + +export type FileUploadInput = { + buffer: Buffer; + filename: string; +}; + +export enum PluginTypeEnum { + tool = 'tool' +} + +export const PluginFilePath = { + [PluginTypeEnum.tool]: 'plugin/tools' +}; diff --git a/packages/service/common/vectorDB/controller.ts b/packages/service/common/vectorDB/controller.ts index a2658b97e139..dc59726dcf5b 100644 --- a/packages/service/common/vectorDB/controller.ts +++ b/packages/service/common/vectorDB/controller.ts @@ -2,12 +2,8 @@ import { PgVectorCtrl } from './pg'; import { ObVectorCtrl } from './oceanbase'; import { getVectorsByText } from '../../core/ai/embedding'; -import type { - EmbeddingRecallCtrlProps} from './controller.d'; -import { - type DelDatasetVectorCtrlProps, - type InsertVectorProps -} from './controller.d'; +import type { EmbeddingRecallCtrlProps } from './controller.d'; +import { type DelDatasetVectorCtrlProps, type InsertVectorProps } from './controller.d'; import { type EmbeddingModelItemType } from '@fastgpt/global/core/ai/model.d'; import { MILVUS_ADDRESS, PG_ADDRESS, OCEANBASE_ADDRESS } from './constants'; import { MilvusCtrl } from './milvus'; diff --git a/packages/service/core/app/plugin/controller.ts b/packages/service/core/app/plugin/controller.ts index 387985927fe6..af5fd7da7549 100644 --- a/packages/service/core/app/plugin/controller.ts +++ b/packages/service/core/app/plugin/controller.ts @@ -47,6 +47,8 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { getMCPChildren } from '../mcp'; import { cloneDeep } from 'lodash'; import { UserError } from '@fastgpt/global/common/error/utils'; +import { getCachedData } from '../../../common/cache'; +import { SystemCacheKeyEnum } from '../../../common/cache/type'; type ChildAppType = SystemPluginTemplateItemType & { teamId?: string; @@ -56,6 +58,8 @@ type ChildAppType = SystemPluginTemplateItemType & { isLatestVersion?: boolean; // Auto computed }; +export const getSystemTools = () => getCachedData(SystemCacheKeyEnum.systemTool); + export const getSystemPluginByIdAndVersionId = async ( pluginId: string, versionId?: string @@ -503,97 +507,63 @@ const dbPluginFormat = (item: SystemPluginConfigSchemaType): SystemPluginTemplat }; /* FastsGPT-Pluign api: */ -function getCachedSystemPlugins() { - if (!global.systemToolsCache) { - global.systemToolsCache = { - expires: 0, - data: [] as SystemPluginTemplateItemType[] - }; - } - return global.systemToolsCache; -} +export const refreshSystemTools = async (): Promise => { + const tools = await APIGetSystemToolList(); + // 从数据库里加载插件配置进行替换 + const systemToolsArray = await MongoSystemPlugin.find({}).lean(); + const systemTools = new Map(systemToolsArray.map((plugin) => [plugin.pluginId, plugin])); -const cleanSystemPluginCache = () => { - global.systemToolsCache = undefined; -}; + const formatTools = tools.map((item) => { + const dbPluginConfig = systemTools.get(item.id); + const isFolder = tools.some((tool) => tool.parentId === item.id); -export const refetchSystemPlugins = () => { - const changeStream = MongoSystemPlugin.watch(); + const versionList = (item.versionList as SystemPluginTemplateItemType['versionList']) || []; - changeStream.on('change', () => { - try { - cleanSystemPluginCache(); - } catch (error) {} + return { + id: item.id, + parentId: item.parentId, + isFolder, + name: item.name, + avatar: item.avatar, + intro: item.description, + toolDescription: item.toolDescription, + author: item.author, + courseUrl: item.courseUrl, + instructions: dbPluginConfig?.customConfig?.userGuide, + weight: item.weight, + toolSource: item.toolSource || 'built-in', + workflow: { + nodes: [], + edges: [] + }, + versionList, + templateType: item.templateType, + showStatus: true, + isActive: dbPluginConfig?.isActive ?? item.isActive ?? true, + inputList: item?.secretInputConfig, + hasSystemSecret: !!dbPluginConfig?.inputListVal, + + originCost: dbPluginConfig?.originCost ?? 0, + currentCost: dbPluginConfig?.currentCost ?? 0, + systemKeyCost: dbPluginConfig?.systemKeyCost ?? 0, + hasTokenFee: dbPluginConfig?.hasTokenFee ?? false, + pluginOrder: dbPluginConfig?.pluginOrder + }; }); -}; - -export const getSystemTools = async (): Promise => { - if (getCachedSystemPlugins().expires > Date.now() && isProduction) { - return getCachedSystemPlugins().data; - } else { - const tools = await APIGetSystemToolList(); - - // 从数据库里加载插件配置进行替换 - const systemToolsArray = await MongoSystemPlugin.find({}).lean(); - const systemTools = new Map(systemToolsArray.map((plugin) => [plugin.pluginId, plugin])); - - const formatTools = tools.map((item) => { - const dbPluginConfig = systemTools.get(item.id); - const isFolder = tools.some((tool) => tool.parentId === item.id); - - const versionList = (item.versionList as SystemPluginTemplateItemType['versionList']) || []; - - return { - id: item.id, - parentId: item.parentId, - isFolder, - name: item.name, - avatar: item.avatar, - intro: item.description, - toolDescription: item.toolDescription, - author: item.author, - courseUrl: item.courseUrl, - instructions: dbPluginConfig?.customConfig?.userGuide, - weight: item.weight, - workflow: { - nodes: [], - edges: [] - }, - versionList, - templateType: item.templateType, - showStatus: true, - isActive: dbPluginConfig?.isActive ?? item.isActive ?? true, - inputList: item?.secretInputConfig, - hasSystemSecret: !!dbPluginConfig?.inputListVal, - - originCost: dbPluginConfig?.originCost ?? 0, - currentCost: dbPluginConfig?.currentCost ?? 0, - systemKeyCost: dbPluginConfig?.systemKeyCost ?? 0, - hasTokenFee: dbPluginConfig?.hasTokenFee ?? false, - pluginOrder: dbPluginConfig?.pluginOrder - }; - }); - - // TODO: Check the app exists - const dbPlugins = systemToolsArray - .filter((item) => item.customConfig?.associatedPluginId) - .map((item) => dbPluginFormat(item)); - const concatTools = [...formatTools, ...dbPlugins]; - concatTools.sort((a, b) => (a.pluginOrder ?? 0) - (b.pluginOrder ?? 0)); + const dbPlugins = systemToolsArray + .filter((item) => item.customConfig?.associatedPluginId) + .map((item) => dbPluginFormat(item)); - global.systemToolsCache = { - expires: Date.now() + 30 * 60 * 1000, // 30 minutes - data: concatTools - }; + const concatTools = [...formatTools, ...dbPlugins]; + concatTools.sort((a, b) => (a.pluginOrder ?? 0) - (b.pluginOrder ?? 0)); - global.systemToolsTypeCache = {}; - concatTools.forEach((item) => { - global.systemToolsTypeCache[item.templateType] = 1; - }); + global.systemToolsTypeCache = {}; + concatTools.forEach((item) => { + global.systemToolsTypeCache[item.templateType] = 1; + }); - return concatTools; - } + return concatTools; }; export const getSystemToolById = async (id: string): Promise => { @@ -613,11 +583,5 @@ export const getSystemToolById = async (id: string): Promise; } diff --git a/packages/service/core/app/tool/api.ts b/packages/service/core/app/tool/api.ts index db76aa5f84bb..90780f3defcb 100644 --- a/packages/service/core/app/tool/api.ts +++ b/packages/service/core/app/tool/api.ts @@ -25,6 +25,26 @@ export async function APIGetSystemToolList() { return Promise.reject(res.body); } +export async function deleteSystemTool(toolId: string) { + const res = await pluginClient.tool.delete({ body: { toolId } }); + + if (res.status === 200) { + return res.body; + } + + return Promise.reject(res.body); +} + +export async function uploadSystemTool(objectName: string) { + const res = await pluginClient.tool.upload({ body: { objectName } }); + + if (res.status === 200) { + return res.body; + } + + return Promise.reject(res.body); +} + const runToolInstance = new RunToolWithStream({ baseUrl: BASE_URL, token: TOKEN diff --git a/packages/service/package.json b/packages/service/package.json index bb146d44b61a..eb367ff8ed38 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -27,6 +27,8 @@ "encoding": "^0.1.13", "file-type": "^19.0.0", "form-data": "^4.0.4", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "iconv-lite": "^0.6.3", "ioredis": "^5.6.0", "joplin-turndown-plugin-gfm": "^1.0.12", @@ -35,6 +37,7 @@ "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "mammoth": "^1.6.0", + "minio": "^8.0.5", "mongoose": "^8.10.1", "multer": "2.0.2", "mysql2": "^3.11.3", diff --git a/packages/web/components/common/DateRangePicker/index.tsx b/packages/web/components/common/DateRangePicker/index.tsx index f21a4fa5b320..335a76327211 100644 --- a/packages/web/components/common/DateRangePicker/index.tsx +++ b/packages/web/components/common/DateRangePicker/index.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useRef, useEffect } from 'react'; import type { BoxProps } from '@chakra-ui/react'; -import { Box, Card, Flex, useOutsideClick, Button } from '@chakra-ui/react'; +import { Box, Card, Flex, useTheme, useOutsideClick, Button } from '@chakra-ui/react'; import { addDays, format } from 'date-fns'; import { DayPicker } from 'react-day-picker'; import 'react-day-picker/dist/style.css'; diff --git a/packages/web/components/common/MySelect/MultipleSelect.tsx b/packages/web/components/common/MySelect/MultipleSelect.tsx index c95ee045c85c..4ba33c691fed 100644 --- a/packages/web/components/common/MySelect/MultipleSelect.tsx +++ b/packages/web/components/common/MySelect/MultipleSelect.tsx @@ -1,7 +1,6 @@ import type { FlexProps } from '@chakra-ui/react'; import { Box, - Button, type ButtonProps, Checkbox, Flex, diff --git a/packages/web/core/workflow/constants.ts b/packages/web/core/workflow/constants.ts index 59737bdee1c7..bc1227bef155 100644 --- a/packages/web/core/workflow/constants.ts +++ b/packages/web/core/workflow/constants.ts @@ -34,7 +34,7 @@ export const workflowSystemNodeTemplateList: { export const defaultGroup: SystemToolGroupSchemaType = { groupId: 'systemPlugin', groupAvatar: 'core/app/type/pluginLight', - groupName: i18nT('common:core.module.template.System Plugin'), + groupName: i18nT('app:core.module.template.System Tools'), groupOrder: 0, groupTypes: [] // from getPluginGroups }; diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 0b46ef4d0dfa..82d69ad7c54c 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -62,6 +62,7 @@ "copilot_confirm_message": "The original configuration has been received to understand the current code structure and input and output parameters. \nPlease explain your optimization requirements.", "copy_one_app": "Create Duplicate", "core.app.QG.Switch": "Enable guess what you want to ask", + "core.module.template.System Tools": "System Tools", "core.dataset.import.Custom prompt": "Custom Prompt", "core.workflow.Copilot": "AI Generation", "create_by_curl": "By CURL", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index abc817ca360c..ce0bbc1cb5ba 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -663,7 +663,6 @@ "core.module.template.AI support tool tip": "Models that support function calls can better use tool calls.", "core.module.template.Basic Node": "Basic", "core.module.template.Query extension": "Question Optimization", - "core.module.template.System Plugin": "System Plugin", "core.module.template.System input module": "System Input", "core.module.template.Team app": "Team", "core.module.template.UnKnow Module": "Unknown Module", @@ -1226,6 +1225,7 @@ "support.wallet.usage.Total points": "AI Points Consumption", "support.wallet.usage.Usage Detail": "Usage Details", "support.wallet.usage.Whisper": "Voice Input", + "sure_delete_tool_cannot_undo": "Are you sure to delete the tool? \nThis operation cannot be withdrawn", "sync_link": "Sync Link", "sync_success": "Synced Successfully", "system.Concat us": "Contact Us", diff --git a/packages/web/i18n/en/file.json b/packages/web/i18n/en/file.json index d53d0a88c0a5..172b7b0bdcdd 100644 --- a/packages/web/i18n/en/file.json +++ b/packages/web/i18n/en/file.json @@ -13,12 +13,14 @@ "Only_support_uploading_one_image": "Only support uploading one image", "Please select the image to upload": "Please select the image to upload", "Please wait for all files to upload": "Please wait for all files to be uploaded to complete", + "common.upload_system_tools": "Upload system tools", "bucket_chat": "Conversation Files", "bucket_file": "Dataset Documents", "bucket_image": "picture", "click_to_view_raw_source": "Click to View Original Source", "common.Some images failed to process": "Some images failed to process", "common.dataset_data_input_image_support_format": "Support .jpg, .jpeg, .png, .gif, .webp formats", + "common.import_update": "Import/Update", "count.core.dataset.collection.Create Success": "{{count}} picture successfully imported", "delete_image": "Delete pictures", "file_name": "Filename", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index 9e79f0dff08d..6f7ba13ac311 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -63,6 +63,7 @@ "copilot_confirm_message": "已接收到原始配置,了解当前代码结构和输入输出参数。请说明您的优化需求。", "copy_one_app": "创建副本", "core.app.QG.Switch": "启用猜你想问", + "core.module.template.System Tools": "系统工具", "core.dataset.import.Custom prompt": "自定义提示词", "core.workflow.Copilot": "AI 生成", "create_by_curl": "从 CURL 创建", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index e327c5dff03e..8e4397cbde14 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -663,7 +663,6 @@ "core.module.template.AI support tool tip": "支持函数调用的模型,可以更好的使用工具调用。", "core.module.template.Basic Node": "基础功能", "core.module.template.Query extension": "问题优化", - "core.module.template.System Plugin": "系统插件", "core.module.template.System input module": "系统输入", "core.module.template.Team app": "团队应用", "core.module.template.UnKnow Module": "未知模块", @@ -1228,6 +1227,7 @@ "support.wallet.usage.Total points": "AI 积分消耗", "support.wallet.usage.Usage Detail": "使用详情", "support.wallet.usage.Whisper": "语音输入", + "sure_delete_tool_cannot_undo": "是否确认删除该工具?该操作无法撤回", "sync_link": "同步链接", "sync_success": "同步成功", "system.Concat us": "联系我们", diff --git a/packages/web/i18n/zh-CN/file.json b/packages/web/i18n/zh-CN/file.json index bfd7df19c518..02b6f7dcff48 100644 --- a/packages/web/i18n/zh-CN/file.json +++ b/packages/web/i18n/zh-CN/file.json @@ -19,6 +19,8 @@ "click_to_view_raw_source": "点击查看来源", "common.Some images failed to process": "部分图片处理失败", "common.dataset_data_input_image_support_format": "支持 .jpg, .jpeg, .png, .gif, .webp 格式", + "common.import_update": "导入/更新", + "common.upload_system_tools": "上传系统工具", "count.core.dataset.collection.Create Success": "成功导入 {{count}} 张图片", "delete_image": "删除图片", "file_name": "文件名", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index 7ffb50fb271b..5f5ac30ab19d 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -63,6 +63,7 @@ "copy_one_app": "建立副本", "core.app.QG.Switch": "啟用猜你想問", "core.dataset.import.Custom prompt": "自訂提示詞", + "core.module.template.System Tools": "系統工具", "core.workflow.Copilot": "AI 生成", "create_by_curl": "從 CURL 建立", "create_by_template": "從範本建立", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index ed301a2b87dc..2edd3fe217be 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -662,7 +662,6 @@ "core.module.template.AI support tool tip": "支援函式呼叫的模型可以更好地使用工具呼叫。", "core.module.template.Basic Node": "基本功能", "core.module.template.Query extension": "問題最佳化", - "core.module.template.System Plugin": "系統外掛", "core.module.template.System input module": "系統輸入模組", "core.module.template.Team app": "團隊應用程式", "core.module.template.UnKnow Module": "未知模組", @@ -1225,6 +1224,7 @@ "support.wallet.usage.Total points": "AI 點數消耗", "support.wallet.usage.Usage Detail": "使用詳細資訊", "support.wallet.usage.Whisper": "語音輸入", + "sure_delete_tool_cannot_undo": "是否確認刪除該工具?\n該操作無法撤回", "sync_link": "同步連結", "sync_success": "同步成功", "system.Concat us": "聯絡我們", diff --git a/packages/web/i18n/zh-Hant/file.json b/packages/web/i18n/zh-Hant/file.json index 8445cf42e5de..577e454cb65e 100644 --- a/packages/web/i18n/zh-Hant/file.json +++ b/packages/web/i18n/zh-Hant/file.json @@ -13,12 +13,14 @@ "Only_support_uploading_one_image": "僅支持上傳一張圖片", "Please select the image to upload": "請選擇要上傳的圖片", "Please wait for all files to upload": "請等待所有文件上傳完成", + "common.upload_system_tools": "上傳系統工具", "bucket_chat": "對話檔案", "bucket_file": "知識庫檔案", "bucket_image": "圖片", "click_to_view_raw_source": "點選檢視原始來源", "common.Some images failed to process": "部分圖片處理失敗", "common.dataset_data_input_image_support_format": "支持 .jpg, .jpeg, .png, .gif, .webp 格式", + "common.import_update": "導入/更新", "count.core.dataset.collection.Create Success": "成功導入 {{count}} 張圖片", "delete_image": "刪除圖片", "file_name": "檔案名稱", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc3a31fa122b..4a1208b76987 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: version: 10.1.4(socks@2.8.4) next-i18next: specifier: 15.4.2 - version: 15.4.2(i18next@23.16.8)(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 15.4.2(i18next@23.16.8)(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) prettier: specifier: 3.2.4 version: 3.2.4 @@ -72,8 +72,8 @@ importers: specifier: ^1.2.8 version: 1.2.8 '@fastgpt-sdk/plugin': - specifier: ^0.1.16 - version: 0.1.16(@types/node@20.14.0) + specifier: ^0.1.18 + version: 0.1.18(@types/node@20.14.0) axios: specifier: ^1.12.1 version: 1.12.1 @@ -198,6 +198,12 @@ importers: form-data: specifier: ^4.0.4 version: 4.0.4 + http-proxy-agent: + specifier: ^7.0.2 + version: 7.0.2 + https-proxy-agent: + specifier: ^7.0.6 + version: 7.0.6 iconv-lite: specifier: ^0.6.3 version: 0.6.3 @@ -222,6 +228,9 @@ importers: mammoth: specifier: ^1.6.0 version: 1.9.0 + minio: + specifier: ^8.0.5 + version: 8.0.5 mongoose: specifier: ^8.10.1 version: 8.12.1(socks@2.8.4) @@ -318,7 +327,7 @@ importers: version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/next-js': specifier: 2.4.2 - version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1) + version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1) '@chakra-ui/react': specifier: 2.10.7 version: 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -396,7 +405,7 @@ importers: version: 14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next-i18next: specifier: 15.4.2 - version: 15.4.2(i18next@23.16.8)(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 15.4.2(i18next@23.16.8)(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) papaparse: specifier: ^5.4.1 version: 5.4.1 @@ -460,7 +469,7 @@ importers: version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1) '@chakra-ui/next-js': specifier: 2.4.2 - version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1) + version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1) '@chakra-ui/react': specifier: 2.10.7 version: 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -548,6 +557,9 @@ importers: mermaid: specifier: ^10.9.4 version: 10.9.4 + minio: + specifier: ^8.0.5 + version: 8.0.5 nanoid: specifier: ^5.1.3 version: 5.1.3 @@ -556,7 +568,7 @@ importers: version: 14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1) next-i18next: specifier: 15.4.2 - version: 15.4.2(i18next@23.16.8)(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 15.4.2(i18next@23.16.8)(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) nprogress: specifier: ^0.2.0 version: 0.2.0 @@ -1982,8 +1994,8 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@fastgpt-sdk/plugin@0.1.16': - resolution: {integrity: sha512-HGoq0jy3YrY8RAJvn8b0u13uqOjjNe5OE4w2dySQc4dgUjHpsmMl9hfGzAhi9bpuUrtptpGohtf0ealNeAAfDQ==} + '@fastgpt-sdk/plugin@0.1.18': + resolution: {integrity: sha512-w57dzES/Q2Oa/tA+lc+FO7dAZNH4M00+T0vS+LUTSj3+BSwjgKutVurxVI/161L5RkDhXZY+XgzSIGTZ1KS3qg==} '@fastify/accept-negotiator@1.1.0': resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} @@ -3847,6 +3859,9 @@ packages: '@zilliz/milvus2-sdk-node@2.4.10': resolution: {integrity: sha512-KeXRFePLGoAMFQRM2w+oyH0X+R1uaj+Pt1o0rAdgQfGTV9aGdEx2zOJAt3XPWKovbphvF6ANmCGw2bbk7alNxQ==} + '@zxing/text-encoding@0.9.0': + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4190,6 +4205,9 @@ packages: bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + block-stream2@2.1.0: + resolution: {integrity: sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==} + bluebird@3.4.7: resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} @@ -4218,6 +4236,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browser-or-node@2.1.1: + resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==} + browserslist@4.24.4: resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -4243,6 +4264,10 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -4930,6 +4955,10 @@ packages: decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -5532,6 +5561,10 @@ packages: fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-xml-parser@4.5.3: + resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} + hasBin: true + fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} @@ -5595,6 +5628,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + finalhandler@1.3.1: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} @@ -6108,6 +6145,10 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + is-absolute-url@4.0.1: resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6124,6 +6165,10 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -7250,6 +7295,10 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minio@8.0.5: + resolution: {integrity: sha512-/vAze1uyrK2R/DSkVutE4cjVoAowvIQ18RAwn7HrqnLecLlMazFnY0oNBqfuoAWvu7mZIGX75AzpuV05TJeoHg==} + engines: {node: ^16 || ^18 || >=20} + minipass-collect@2.0.1: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} engines: {node: '>=16 || 14 >=14.17'} @@ -8063,6 +8112,10 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -8524,6 +8577,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -8716,6 +8772,10 @@ packages: sparse-bitfield@3.0.3: resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -8771,6 +8831,12 @@ packages: resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-json@1.9.1: + resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -8778,6 +8844,10 @@ packages: streamx@2.22.0: resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==} + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -8868,6 +8938,9 @@ packages: strip-literal@2.1.1: resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} + strnum@1.1.2: + resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + strtok3@9.1.1: resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} engines: {node: '>=16'} @@ -8900,7 +8973,7 @@ packages: superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supertest@6.3.4: resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} @@ -9001,6 +9074,9 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -9453,6 +9529,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -9658,6 +9737,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -9782,10 +9864,18 @@ packages: engines: {node: '>=0.8'} hasBin: true + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + xmlbuilder@10.1.1: resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==} engines: {node: '>=4.0'} + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -9976,7 +10066,7 @@ snapshots: '@babel/traverse': 7.26.10 '@babel/types': 7.26.10 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10732,7 +10822,7 @@ snapshots: '@babel/parser': 7.26.10 '@babel/template': 7.26.9 '@babel/types': 7.26.10 - debug: 4.4.0 + debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -10795,7 +10885,7 @@ snapshots: '@chakra-ui/system': 2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)': + '@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)': dependencies: '@chakra-ui/react': 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@emotion/cache': 11.14.0 @@ -11220,7 +11310,7 @@ snapshots: '@eslint/js@8.57.1': {} - '@fastgpt-sdk/plugin@0.1.16(@types/node@20.14.0)': + '@fastgpt-sdk/plugin@0.1.18(@types/node@20.14.0)': dependencies: '@fortaine/fetch-event-source': 3.0.6 '@ts-rest/core': 3.52.1(@types/node@20.14.0)(zod@3.25.51) @@ -11301,7 +11391,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.0 + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -13425,6 +13515,9 @@ snapshots: protobufjs: 7.4.0 winston: 3.17.0 + '@zxing/text-encoding@0.9.0': + optional: true + abbrev@2.0.0: {} abort-controller@3.0.0: @@ -13825,6 +13918,10 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + block-stream2@2.1.0: + dependencies: + readable-stream: 3.6.2 + bluebird@3.4.7: {} body-parser@1.20.3: @@ -13848,7 +13945,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.0 + debug: 4.4.1 http-errors: 2.0.0 iconv-lite: 0.6.3 on-finished: 2.4.1 @@ -13884,6 +13981,8 @@ snapshots: dependencies: fill-range: 7.1.1 + browser-or-node@2.1.1: {} + browserslist@4.24.4: dependencies: caniuse-lite: 1.0.30001704 @@ -13910,6 +14009,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffer-crc32@1.0.0: {} + buffer-equal-constant-time@1.0.1: {} buffer-fill@1.0.0: {} @@ -14637,6 +14738,8 @@ snapshots: dependencies: character-entities: 2.0.2 + decode-uri-component@0.2.2: {} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -15140,7 +15243,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.31.0)(eslint@8.56.0))(eslint@8.56.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.56.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -15151,7 +15254,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -15173,7 +15276,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.31.0)(eslint@8.56.0))(eslint@8.56.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.56.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15202,7 +15305,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.0)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15630,6 +15733,10 @@ snapshots: fast-uri@3.0.6: {} + fast-xml-parser@4.5.3: + dependencies: + strnum: 1.1.2 + fastify-plugin@4.5.1: {} fastify@4.28.1: @@ -15721,6 +15828,8 @@ snapshots: dependencies: to-regex-range: 5.0.1 + filter-obj@1.1.0: {} + finalhandler@1.3.1: dependencies: debug: 2.6.9 @@ -15735,7 +15844,7 @@ snapshots: finalhandler@2.1.0: dependencies: - debug: 4.4.0 + debug: 4.4.1 encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -16202,7 +16311,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -16349,6 +16458,8 @@ snapshots: ipaddr.js@1.9.1: {} + ipaddr.js@2.2.0: {} + is-absolute-url@4.0.1: {} is-alphabetical@1.0.4: {} @@ -16365,6 +16476,11 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -17979,6 +18095,23 @@ snapshots: minimist@1.2.8: {} + minio@8.0.5: + dependencies: + async: 3.2.6 + block-stream2: 2.1.0 + browser-or-node: 2.1.1 + buffer-crc32: 1.0.0 + eventemitter3: 5.0.1 + fast-xml-parser: 4.5.3 + ipaddr.js: 2.2.0 + lodash: 4.17.21 + mime-types: 2.1.35 + query-string: 7.1.3 + stream-json: 1.9.1 + through2: 4.0.2 + web-encoding: 1.1.5 + xml2js: 0.6.2 + minipass-collect@2.0.1: dependencies: minipass: 7.1.2 @@ -18189,11 +18322,11 @@ snapshots: new-find-package-json@2.0.0: dependencies: - debug: 4.4.0 + debug: 4.4.1 transitivePeerDependencies: - supports-color - next-i18next@15.4.2(i18next@23.16.8)(next@14.2.32(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + next-i18next@15.4.2(i18next@23.16.8)(next@14.2.32(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.26.10 '@types/hoist-non-react-statics': 3.3.6 @@ -18879,6 +19012,13 @@ snapshots: dependencies: side-channel: 1.1.0 + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -19404,7 +19544,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.0 + debug: 4.4.1 depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -19473,6 +19613,8 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.1 + sax@1.4.1: {} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -19528,7 +19670,7 @@ snapshots: send@1.2.0: dependencies: - debug: 4.4.0 + debug: 4.4.1 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -19706,6 +19848,8 @@ snapshots: dependencies: memory-pager: 1.5.0 + split-on-first@1.1.0: {} + split2@4.2.0: {} sprintf-js@1.0.3: {} @@ -19744,6 +19888,12 @@ snapshots: dependencies: bl: 5.1.0 + stream-chain@2.2.5: {} + + stream-json@1.9.1: + dependencies: + stream-chain: 2.2.5 + streamsearch@1.1.0: {} streamx@2.22.0: @@ -19753,6 +19903,8 @@ snapshots: optionalDependencies: bare-events: 2.5.4 + strict-uri-encode@2.0.0: {} + string-argv@0.3.2: {} string-length@4.0.2: @@ -19869,6 +20021,8 @@ snapshots: dependencies: js-tokens: 9.0.1 + strnum@1.1.2: {} + strtok3@9.1.1: dependencies: '@tokenizer/token': 0.3.0 @@ -19897,7 +20051,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.0 + debug: 4.4.1 fast-safe-stringify: 2.1.1 form-data: 4.0.4 formidable: 2.1.2 @@ -20027,6 +20181,10 @@ snapshots: dependencies: real-require: 0.2.0 + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + through@2.3.8: {} tiktoken@1.0.17: {} @@ -20466,6 +20624,14 @@ snapshots: util-deprecate@1.0.2: {} + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.0 + is-typed-array: 1.1.15 + which-typed-array: 1.1.19 + utils-merge@1.0.1: {} uuid@8.3.2: {} @@ -20538,7 +20704,7 @@ snapshots: vite-node@1.6.1(@types/node@20.17.24)(sass@1.85.1)(terser@5.39.0): dependencies: cac: 6.7.14 - debug: 4.4.0 + debug: 4.4.1 pathe: 1.1.2 picocolors: 1.1.1 vite: 5.4.14(@types/node@20.17.24)(sass@1.85.1)(terser@5.39.0) @@ -20694,6 +20860,12 @@ snapshots: dependencies: defaults: 1.0.4 + web-encoding@1.1.5: + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + web-namespaces@2.0.1: {} web-streams-polyfill@4.0.0-beta.3: {} @@ -20866,8 +21038,15 @@ snapshots: xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz: {} + xml2js@0.6.2: + dependencies: + sax: 1.4.1 + xmlbuilder: 11.0.1 + xmlbuilder@10.1.1: {} + xmlbuilder@11.0.1: {} + xtend@4.0.2: {} y18n@4.0.3: {} diff --git a/projects/app/.env.template b/projects/app/.env.template index 5bc671fb275d..0e73de9ec0cd 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -101,3 +101,11 @@ CONFIG_JSON_PATH= # CHAT_LOG_SOURCE_ID_PREFIX=fastgpt- +# S3 Config +# S3_EXTERNAL_BASE_URL=https://s3.example.com +S3_ENDPOINT=localhost +S3_PORT=9000 +S3_USE_SSL=false +S3_ACCESS_KEY=minioadmin +S3_SECRET_KEY=minioadmin +S3_PLUGIN_BUCKET=fastgpt-plugins diff --git a/projects/app/package.json b/projects/app/package.json index 26fe8b21e826..6d582618363a 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -64,7 +64,8 @@ "request-ip": "^3.3.0", "sass": "^1.58.3", "use-context-selector": "^1.4.4", - "zod": "^3.24.2" + "zod": "^3.24.2", + "minio": "^8.0.5" }, "devDependencies": { "@svgr/webpack": "^6.5.1", diff --git a/projects/app/src/pageComponents/dataset/detail/components/FileSelector.tsx b/projects/app/src/components/Select/FileSelectorBox.tsx similarity index 100% rename from projects/app/src/pageComponents/dataset/detail/components/FileSelector.tsx rename to projects/app/src/components/Select/FileSelectorBox.tsx diff --git a/projects/app/src/pageComponents/app/plugin/UploadSystemToolModal.tsx b/projects/app/src/pageComponents/app/plugin/UploadSystemToolModal.tsx new file mode 100644 index 000000000000..d7e9050d5c4a --- /dev/null +++ b/projects/app/src/pageComponents/app/plugin/UploadSystemToolModal.tsx @@ -0,0 +1,132 @@ +import FileSelectorBox, { type SelectFileItemType } from '@/components/Select/FileSelectorBox'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { getDocPath } from '@/web/common/system/doc'; +import { Box, Button, Flex, HStack, Link, ModalBody, ModalFooter, VStack } from '@chakra-ui/react'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import { postS3UploadFile } from '@/web/common/file/api'; +import { getPluginUploadPresignedURL, postConfirmUpload } from '@/web/core/app/api/plugin'; +import MyIconButton from '@fastgpt/web/components/common/Icon/button'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useToast } from '@fastgpt/web/hooks/useToast'; + +function UploadSystemToolModal({ + onClose, + onSuccess +}: { + onClose: () => void; + onSuccess: () => void; +}) { + const { t } = useTranslation(); + const { toast } = useToast(); + const [selectFiles, setSelectFiles] = useState([]); + const { runAsync: handlePluginUpload, loading: uploadLoading } = useRequest2( + async () => { + const file = selectFiles[0]; + + const presignedData = await getPluginUploadPresignedURL({ + filename: file.name + }); + + const formData = new FormData(); + Object.entries(presignedData.formData).forEach(([key, value]) => { + formData.append(key, value); + }); + formData.append('file', file.file); + + await postS3UploadFile(presignedData.uploadUrl, formData); + + await postConfirmUpload({ + objectName: presignedData.objectName + }); + }, + { + manual: true, + onSuccess: async () => { + toast({ + title: t('common:import_success'), + status: 'success' + }); + + setSelectFiles([]); + onSuccess(); + onClose(); + }, + onError: (error) => { + toast({ + title: t('common:import_failed'), + description: error instanceof Error ? error.message : t('dataset:common.error.unKnow'), + status: 'error' + }); + } + } + ); + return ( + + + + + + {t('common:Instructions')} + + + + {/* File render */} + {selectFiles.length > 0 && ( + + {selectFiles.map((item, index) => ( + + + {item.name} + + {item.size} + + { + setSelectFiles(selectFiles.filter((_, i) => i !== index)); + }} + /> + + ))} + + )} + + + + + + + ); +} + +export default UploadSystemToolModal; diff --git a/projects/app/src/pageComponents/dashboard/SystemPlugin/ToolCard.tsx b/projects/app/src/pageComponents/dashboard/SystemPlugin/ToolCard.tsx index 5063ae5ef50c..7e56bdfe99f0 100644 --- a/projects/app/src/pageComponents/dashboard/SystemPlugin/ToolCard.tsx +++ b/projects/app/src/pageComponents/dashboard/SystemPlugin/ToolCard.tsx @@ -2,9 +2,11 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import { Box, Flex, HStack } from '@chakra-ui/react'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyBox from '@fastgpt/web/components/common/MyBox'; -import React from 'react'; +import React, { useState } from 'react'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; +import MyIconButton from '@fastgpt/web/components/common/Icon/button'; +import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { type NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node'; import { type SystemToolGroupSchemaType } from '@fastgpt/service/core/app/plugin/type'; import UseGuideModal from '@/components/common/Modal/UseGuideModal'; @@ -12,19 +14,39 @@ import { parseI18nString } from '@fastgpt/global/common/i18n/utils'; const PluginCard = ({ item, - groups + groups, + onDelete }: { item: NodeTemplateListItemType; groups: SystemToolGroupSchemaType[]; + onDelete?: (id: string) => Promise; }) => { const { t, i18n } = useTranslation(); const { feConfigs } = useSystemStore(); + const [isHovered, setIsHovered] = useState(false); + + const { openConfirm, ConfirmModal } = useConfirm({ + type: 'delete', + content: t('common:sure_delete_tool_cannot_undo') + }); const type = groups.reduce((acc, group) => { const foundType = group.groupTypes.find((type) => type.typeId === item.templateType); return foundType ? parseI18nString(foundType.typeName, i18n.language) : acc; }, undefined); + const isUploadedPlugin = item.toolSource === 'uploaded'; + + const handleDelete = async () => { + if (onDelete && item.id) { + await onDelete(item.id); + } + }; + + const handleDeleteClick = () => { + openConfirm(handleDelete)(); + }; + return ( setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} _hover={{ borderColor: 'primary.300', boxShadow: '1.5' }} > + {/* Delete button with centered confirmation modal */} + {isUploadedPlugin && ( + + )} + + + @@ -103,7 +145,10 @@ const PluginCard = ({ )} - {`by ${item.author || feConfigs.systemTitle}`} + {/* Hide author info when showing delete button but maintain space */} + + {`by ${item.author || feConfigs.systemTitle}`} + ); diff --git a/projects/app/src/pageComponents/dataset/detail/CollectionCard/BackupImportModal.tsx b/projects/app/src/pageComponents/dataset/detail/CollectionCard/BackupImportModal.tsx index a2b6c9c869e4..830a79ac565d 100644 --- a/projects/app/src/pageComponents/dataset/detail/CollectionCard/BackupImportModal.tsx +++ b/projects/app/src/pageComponents/dataset/detail/CollectionCard/BackupImportModal.tsx @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import { Box, Button, HStack, ModalBody, ModalFooter, VStack } from '@chakra-ui/react'; -import FileSelector, { type SelectFileItemType } from '../components/FileSelector'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import { postBackupDatasetCollection } from '@/web/core/dataset/api'; @@ -10,6 +9,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext'; import { useContextSelector } from 'use-context-selector'; import LightTip from '@fastgpt/web/components/common/LightTip'; +import FileSelectorBox, { type SelectFileItemType } from '@/components/Select/FileSelectorBox'; const BackupImportModal = ({ onFinish, @@ -46,7 +46,7 @@ const BackupImportModal = ({ - , + res: NextApiResponse +) { + const { objectName } = req.query; + + // Verify file upload and get access URL + await PluginS3Service.getFile(objectName); + await uploadSystemTool(objectName); + + return {}; +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/api/common/file/plugin/getUploadURL.ts b/projects/app/src/pages/api/common/file/plugin/getUploadURL.ts new file mode 100644 index 000000000000..dc0e69dd679a --- /dev/null +++ b/projects/app/src/pages/api/common/file/plugin/getUploadURL.ts @@ -0,0 +1,30 @@ +import type { NextApiResponse } from 'next'; +import { NextAPI } from '@/service/middleware/entry'; +import { PluginS3Service } from '@fastgpt/service/common/s3'; +import { mimeMap } from '@fastgpt/service/common/s3/const'; +import type { ApiRequestProps } from '@fastgpt/service/type/next'; +import type { UploadPresignedURLResponse } from '@fastgpt/service/common/s3/type'; +import { UploadToolsS3Path } from '@fastgpt/global/sdk/fastgpt-plugin'; + +export type getUploadURLQuery = { + filename: string; +}; + +async function handler( + req: ApiRequestProps<{}, getUploadURLQuery>, + res: NextApiResponse +) { + const { filename } = req.query; + + if (!filename) { + return Promise.reject('Filename is required'); + } + + return PluginS3Service.generateUploadPresignedURL({ + filepath: UploadToolsS3Path, + contentType: mimeMap['.js'], + filename + }); +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/app/plugin/getSystemPluginTemplates.ts b/projects/app/src/pages/api/core/app/plugin/getSystemPluginTemplates.ts index fcc8c387b137..b27207e313c7 100644 --- a/projects/app/src/pages/api/core/app/plugin/getSystemPluginTemplates.ts +++ b/projects/app/src/pages/api/core/app/plugin/getSystemPluginTemplates.ts @@ -13,7 +13,7 @@ import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constant export type GetSystemPluginTemplatesBody = { searchKey?: string; - parentId: ParentIdType; + parentId?: ParentIdType; }; async function handler( @@ -37,12 +37,13 @@ async function handler( name: parseI18nString(plugin.name, lang), intro: parseI18nString(plugin.intro ?? '', lang), instructions: parseI18nString(plugin.userGuide ?? '', lang), - toolDescription: plugin.toolDescription + toolDescription: plugin.toolDescription, + toolSource: plugin.toolSource })) .filter((item) => { if (searchKey) { - const regx = new RegExp(`${replaceRegChars(searchKey)}`, 'i'); - return regx.test(String(item.name)) || regx.test(String(item.intro || '')); + const regex = new RegExp(`${replaceRegChars(searchKey)}`, 'i'); + return regex.test(String(item.name)) || regex.test(String(item.intro || '')); } return item.parentId === formatParentId; }); diff --git a/projects/app/src/pages/api/plugin/delete.ts b/projects/app/src/pages/api/plugin/delete.ts new file mode 100644 index 000000000000..934699653a9b --- /dev/null +++ b/projects/app/src/pages/api/plugin/delete.ts @@ -0,0 +1,16 @@ +import type { NextApiResponse } from 'next'; +import { NextAPI } from '@/service/middleware/entry'; +import { deleteSystemTool } from '@fastgpt/service/core/app/tool/api'; +import type { ApiRequestProps } from '@fastgpt/service/type/next'; + +async function handler(req: ApiRequestProps<{}, { toolId: string }>, res: NextApiResponse) { + const { toolId } = req.query; + + if (!toolId) { + return Promise.reject('ToolId is required'); + } + + return deleteSystemTool(toolId.split('-').slice(1).join('-')); +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/dashboard/[pluginGroupId]/index.tsx b/projects/app/src/pages/dashboard/[pluginGroupId]/index.tsx index 6d1a65c16a0f..57fbff4aeec6 100644 --- a/projects/app/src/pages/dashboard/[pluginGroupId]/index.tsx +++ b/projects/app/src/pages/dashboard/[pluginGroupId]/index.tsx @@ -1,30 +1,73 @@ 'use client'; +import UploadSystemToolModal from '@/pageComponents/app/plugin/UploadSystemToolModal'; import DashboardContainer from '@/pageComponents/dashboard/Container'; - import PluginCard from '@/pageComponents/dashboard/SystemPlugin/ToolCard'; import { serviceSideProps } from '@/web/common/i18n/utils'; -import { getSystemPlugTemplates } from '@/web/core/app/api/plugin'; -import { Box, Flex, Grid } from '@chakra-ui/react'; +import { getSystemPlugTemplates, postDeletePlugin } from '@/web/core/app/api/plugin'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { Box, Button, Flex, Grid, useDisclosure } from '@chakra-ui/react'; +import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; +import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useSystem } from '@fastgpt/web/hooks/useSystem'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useMemo, useState } from 'react'; -import { useTranslation } from 'next-i18next'; -import MyBox from '@fastgpt/web/components/common/MyBox'; -import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; -import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; -import { useSystem } from '@fastgpt/web/hooks/useSystem'; const SystemTools = () => { const { t } = useTranslation(); + const { toast } = useToast(); const router = useRouter(); const { type, pluginGroupId } = router.query as { type?: string; pluginGroupId?: string }; const { isPc } = useSystem(); + const { userInfo } = useUserStore(); + + const isRoot = userInfo?.username === 'root'; const [searchKey, setSearchKey] = useState(''); + const [deletingPlugins, setDeletingPlugins] = useState>(new Set()); - const { data: plugins = [], loading: isLoading } = useRequest2(getSystemPlugTemplates, { + const { + data: plugins = [], + loading: isLoading, + runAsync: refreshPlugins + } = useRequest2(getSystemPlugTemplates, { manual: false }); + const { + isOpen: isOpenUploadPlugin, + onOpen: onOpenUploadPlugin, + onClose: onCloseUploadPlugin + } = useDisclosure(); + + const handlePluginDelete = async (pluginId: string) => { + setDeletingPlugins((prev) => new Set(prev).add(pluginId)); + + try { + await postDeletePlugin(pluginId); + toast({ + title: t('common:delete_success'), + status: 'success' + }); + + // null means all tools + await refreshPlugins({ parentId: null }); + } catch (error) { + Promise.reject(error); + toast({ + title: t('common:delete_failed'), + status: 'error' + }); + } finally { + setDeletingPlugins((prev) => { + const newSet = new Set(prev); + newSet.delete(pluginId); + return newSet; + }); + } + }; const currentPlugins = useMemo(() => { return plugins @@ -34,8 +77,8 @@ const SystemTools = () => { }) .filter((item) => { if (!searchKey) return true; - const regx = new RegExp(searchKey, 'i'); - return regx.test(`${item.name}${item.intro}${item.instructions}`); + const regex = new RegExp(searchKey, 'i'); + return regex.test(`${item.name}${item.intro}${item.instructions}`); }); }, [plugins, searchKey, type]); @@ -59,44 +102,61 @@ const SystemTools = () => { }); return ( - - - - {isPc ? ( - - {t('common:core.module.template.System Plugin')} - - ) : ( - MenuIcon - )} - - - setSearchKey(e.target.value)} - placeholder={t('common:search_tool')} - /> - - - - {filterPluginsByGroup.map((item) => ( - - ))} - - {filterPluginsByGroup.length === 0 && } - - + <> + + + + {isPc ? ( + + {t('app:core.module.template.System Tools')} + + ) : ( + MenuIcon + )} + + + setSearchKey(e.target.value)} + placeholder={t('common:plugin.Search plugin')} + /> + + {isRoot && ( + + )} + + + + {filterPluginsByGroup.map((item) => ( + + ))} + + {filterPluginsByGroup.length === 0 && } + + + {isOpenUploadPlugin && ( + refreshPlugins({ parentId: null })} + /> + )} + ); }} @@ -108,7 +168,7 @@ export default SystemTools; export async function getServerSideProps(content: any) { return { props: { - ...(await serviceSideProps(content, ['app'])) + ...(await serviceSideProps(content, ['app', 'file'])) } }; } diff --git a/projects/app/src/pages/dashboard/evaluation/create.tsx b/projects/app/src/pages/dashboard/evaluation/create.tsx index a956719eab8c..d89229f1734b 100644 --- a/projects/app/src/pages/dashboard/evaluation/create.tsx +++ b/projects/app/src/pages/dashboard/evaluation/create.tsx @@ -10,9 +10,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import AppSelect from '@/components/Select/AppSelect'; import MyIcon from '@fastgpt/web/components/common/Icon'; -import FileSelector, { - type SelectFileItemType -} from '@/pageComponents/dataset/detail/components/FileSelector'; +import FileSelector, { type SelectFileItemType } from '@/components/Select/FileSelectorBox'; import { Trans } from 'next-i18next'; import MyIconButton from '@fastgpt/web/components/common/Icon/button'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; diff --git a/projects/app/src/service/common/system/volumnMongoWatch.ts b/projects/app/src/service/common/system/volumnMongoWatch.ts index 08edaf921db9..729120ed7d12 100644 --- a/projects/app/src/service/common/system/volumnMongoWatch.ts +++ b/projects/app/src/service/common/system/volumnMongoWatch.ts @@ -6,14 +6,12 @@ import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSc import { getAppTemplatesAndLoadThem } from '@fastgpt/service/core/app/templates/register'; import { watchSystemModelUpdate } from '@fastgpt/service/core/ai/config/utils'; import { SystemConfigsTypeEnum } from '@fastgpt/global/common/system/config/constants'; -import { refetchSystemPlugins } from '@fastgpt/service/core/app/plugin/controller'; export const startMongoWatch = async () => { reloadConfigWatch(); createDatasetTrainingMongoWatch(); refetchAppTemplates(); watchSystemModelUpdate(); - refetchSystemPlugins(); }; const reloadConfigWatch = () => { diff --git a/projects/app/src/web/common/file/api.ts b/projects/app/src/web/common/file/api.ts index 796a8c91dbdf..c73dad821095 100644 --- a/projects/app/src/web/common/file/api.ts +++ b/projects/app/src/web/common/file/api.ts @@ -1,5 +1,6 @@ -import { GET, POST } from '@/web/common/api/request'; +import { DELETE, GET, POST } from '@/web/common/api/request'; import type { UploadImgProps } from '@fastgpt/global/common/file/api.d'; +import type { UploadPresignedURLResponse } from '@fastgpt/service/common/s3/type'; import { type AxiosProgressEvent } from 'axios'; export const postUploadImg = (e: UploadImgProps) => POST('/common/file/uploadImage', e); @@ -18,3 +19,16 @@ export const postUploadFiles = ( 'Content-Type': 'multipart/form-data; charset=utf-8' } }); + +export const postS3UploadFile = ( + postURL: string, + form: FormData, + onUploadProgress?: (progressEvent: AxiosProgressEvent) => void +) => + POST(postURL, form, { + timeout: 600000, + headers: { + 'Content-Type': 'multipart/form-data' + }, + onUploadProgress + }); diff --git a/projects/app/src/web/core/app/api/plugin.ts b/projects/app/src/web/core/app/api/plugin.ts index b8f987571c0b..a880d08794a0 100644 --- a/projects/app/src/web/core/app/api/plugin.ts +++ b/projects/app/src/web/core/app/api/plugin.ts @@ -1,4 +1,4 @@ -import { GET, POST } from '@/web/common/api/request'; +import { DELETE, GET, POST } from '@/web/common/api/request'; import type { createHttpPluginBody } from '@/pages/api/core/app/httpPlugin/create'; import type { UpdateHttpPluginBody } from '@/pages/api/core/app/httpPlugin/update'; import type { @@ -33,6 +33,7 @@ import type { McpGetChildrenmQuery, McpGetChildrenmResponse } from '@/pages/api/core/app/mcpTools/getChildren'; +import type { UploadPresignedURLResponse } from '@fastgpt/service/common/s3/type'; /* ============ team plugin ============== */ export const getTeamPlugTemplates = async (data?: { @@ -96,6 +97,17 @@ export const getPreviewPluginNode = (data: GetPreviewNodeQuery) => export const getToolVersionList = (data: getToolVersionListProps) => POST('/core/app/plugin/getVersionList', data); +export const getPluginUploadPresignedURL = (data: { filename: string }) => + GET('/common/file/plugin/getUploadURL', data); + +export const postConfirmUpload = (data: { objectName: string }) => + GET('/common/file/plugin/confirmUpload', data); + +export const postDeletePlugin = (toolId: string) => + DELETE('/plugin/delete', { + toolId + }); + /* ============ mcp tools ============== */ export const postCreateMCPTools = (data: createMCPToolsBody) => POST('/core/app/mcpTools/create', data); diff --git a/test/setup.ts b/test/setup.ts index b58b1e90e97a..16dd3df6f3b0 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -98,3 +98,5 @@ beforeEach(async () => { ]); }); }); + +delay(1000);