From ca274feb2ef63d577c976a2471f2eda070c249e2 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 21 Oct 2025 11:18:53 +0800 Subject: [PATCH] perf: init s3 (#5795) * fix: variables refresh * fix: workflow start check * perf: init s3 --- document/content/docs/upgrading/4-13/4132.mdx | 32 +++++++++++++++++-- document/data/doc-last-modified.json | 2 +- packages/service/common/s3/buckets/base.ts | 30 +++++++++-------- projects/app/src/pages/api/admin/initv4132.ts | 19 +++++++++++ 4 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 projects/app/src/pages/api/admin/initv4132.ts diff --git a/document/content/docs/upgrading/4-13/4132.mdx b/document/content/docs/upgrading/4-13/4132.mdx index 3ccf63943..ac31fd3e1 100644 --- a/document/content/docs/upgrading/4-13/4132.mdx +++ b/document/content/docs/upgrading/4-13/4132.mdx @@ -3,14 +3,40 @@ title: 'V4.13.2(进行中)' description: 'FastGPT V4.13.2 更新说明' --- -# 更新指南 +## 更新指南 -## 增加 FastGPT/FastGPT-pro 环境变量 +### 1. 更新镜像: + +- 更新 FastGPT 镜像tag: v4.13.2 +- 更新 FastGPT 商业版镜像tag: v4.13.2 +- 更新 fastgpt-plugin 镜像 tag: v0.2.4 +- mcp_server 无需更新 +- Sandbox 无需更新 +- AIProxy 无需更新 + +### 2. 增加 FastGPT/FastGPT-pro 环境变量 ``` -S3_PUBLIC_BUCKET=S3公开桶名称(公开读私有写) +S3_PUBLIC_BUCKET=fastgpt_public #(公开读公开桶名称,对应原来 plugin 项目的S3_TOOL_BUCKET) +S3_PRIVATE_BUCKET=fastgpt_private #(私有读私有写桶名称,对应原来 plugin 项目的S3_PLUGIN_BUCKET) ``` +### 3. 修复 fastgpt-plugin 环境变量 + +- S3_TOOL_BUCKET 改名成 S3_PUBLIC_BUCKET +- S3_PLUGIN_BUCKET 改名成 S3_PRIVATE_BUCKET + +### 4. 执行升级脚本 + +从任意终端,发起 1 个 HTTP 请求。其中 `{{rootkey}}` 替换成环境变量里的 `rootkey`;`{{host}}` 替换成**FastGPT 域名**。 + +```bash +curl --location --request POST 'https://{{host}}/api/admin/initv4132' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` + +会删除原先 S3 的 circleLife 策略。如果使用的是外部 S3,可能会因为不支持 circleLife 操作导 ## 🚀 新增内容 1. HTTP 工具集支持手动创建模式。 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index ff6dd5cfa..d5921a7a7 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -113,7 +113,7 @@ "document/content/docs/upgrading/4-12/4124.mdx": "2025-09-17T22:29:56+08:00", "document/content/docs/upgrading/4-13/4130.mdx": "2025-09-30T16:00:10+08:00", "document/content/docs/upgrading/4-13/4131.mdx": "2025-09-30T15:47:06+08:00", - "document/content/docs/upgrading/4-13/4132.mdx": "2025-10-17T21:40:12+08:00", + "document/content/docs/upgrading/4-13/4132.mdx": "2025-10-20T19:08:21+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/service/common/s3/buckets/base.ts b/packages/service/common/s3/buckets/base.ts index 4aa07e321..330abceed 100644 --- a/packages/service/common/s3/buckets/base.ts +++ b/packages/service/common/s3/buckets/base.ts @@ -1,6 +1,5 @@ -import { Client, type RemoveOptions, type CopyConditions, type LifecycleConfig } from 'minio'; +import { Client, type RemoveOptions, type CopyConditions } from 'minio'; import { - type ExtensionType, type CreatePostPresignedUrlOptions, type CreatePostPresignedUrlParams, type CreatePostPresignedUrlResult, @@ -9,9 +8,9 @@ import { import { defaultS3Options, Mimes } from '../constants'; import path from 'node:path'; import { MongoS3TTL } from '../schema'; -import { UserError } from '@fastgpt/global/common/error/utils'; import { getNanoid } from '@fastgpt/global/common/string/tools'; import { addHours } from 'date-fns'; +import { addLog } from '../../system/log'; export class S3BaseBucket { private _client: Client; @@ -56,6 +55,7 @@ export class S3BaseBucket { await this.client.makeBucket(this.bucketName); } await this.options.afterInit?.(); + console.log(`S3 init success: ${this.name}`); }; init(); } @@ -63,8 +63,10 @@ export class S3BaseBucket { get name(): string { return this.bucketName; } - - protected get client(): Client { + get client(): Client { + return this._client; + } + get externalClient(): Client { return this._externalClient ?? this._client; } @@ -93,9 +95,9 @@ export class S3BaseBucket { try { const { expiredHours } = options; const filename = params.filename; - const ext = path.extname(filename).toLowerCase() as ExtensionType; - const contentType = Mimes[ext] ?? 'application/octet-stream'; - const maxFileSize = this.options.maxFileSize as number; + const ext = path.extname(filename).toLowerCase(); + const contentType = Mimes[ext as keyof typeof Mimes] ?? 'application/octet-stream'; + const maxFileSize = this.options.maxFileSize; const key = (() => { if ('rawKey' in params) return params.rawKey; @@ -103,20 +105,21 @@ export class S3BaseBucket { return `${params.source}/${params.teamId}/${getNanoid(6)}-${filename}`; })(); - const policy = this.client.newPostPolicy(); + const policy = this.externalClient.newPostPolicy(); policy.setKey(key); policy.setBucket(this.name); policy.setContentType(contentType); - policy.setContentLengthRange(1, maxFileSize); + if (maxFileSize) { + policy.setContentLengthRange(1, maxFileSize); + } policy.setExpires(new Date(Date.now() + 10 * 60 * 1000)); policy.setUserMetaData({ - 'content-type': contentType, 'content-disposition': `attachment; filename="${encodeURIComponent(filename)}"`, 'origin-filename': encodeURIComponent(filename), 'upload-time': new Date().toISOString() }); - const { formData, postURL } = await this.client.presignedPostPolicy(policy); + const { formData, postURL } = await this.externalClient.presignedPostPolicy(policy); if (expiredHours) { await MongoS3TTL.create({ @@ -131,7 +134,8 @@ export class S3BaseBucket { fields: formData }; } catch (error) { - return Promise.reject(error); + addLog.error('Failed to create post presigned url', error); + return Promise.reject('Failed to create post presigned url'); } } } diff --git a/projects/app/src/pages/api/admin/initv4132.ts b/projects/app/src/pages/api/admin/initv4132.ts new file mode 100644 index 000000000..995887f3e --- /dev/null +++ b/projects/app/src/pages/api/admin/initv4132.ts @@ -0,0 +1,19 @@ +import { NextAPI } from '@/service/middleware/entry'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { type NextApiRequest, type NextApiResponse } from 'next'; +import { S3Buckets } from '@fastgpt/service/common/s3/constants'; + +// 将 S3 原先的 circleLife 策略全部去掉 +async function handler(req: NextApiRequest, _res: NextApiResponse) { + await authCert({ req, authRoot: true }); + + if (!global.s3BucketMap[S3Buckets.public]) { + return Promise.reject('S3 not initialized'); + } + + await global.s3BucketMap[S3Buckets.public].client.removeBucketLifecycle(S3Buckets.public); + await global.s3BucketMap[S3Buckets.private].client.removeBucketLifecycle(S3Buckets.private); + return {}; +} + +export default NextAPI(handler);