From b84da5e1206233c48d2626d24229813347b3301c Mon Sep 17 00:00:00 2001 From: Hk-Gosuto Date: Sun, 10 Dec 2023 21:02:49 +0800 Subject: [PATCH] feat: support custom s3 service --- README.md | 22 ++---- app/api/file/[...path]/route.ts | 11 +-- .../langchain-tools/dalle_image_generator.ts | 2 +- .../stable_diffusion_image_generator.ts | 2 +- ...{r2_file_storage.ts => s3_file_storage.ts} | 18 +++-- docs/s3-oss.md | 68 +++++++++++++++++++ 6 files changed, 88 insertions(+), 35 deletions(-) rename app/utils/{r2_file_storage.ts => s3_file_storage.ts} (69%) create mode 100644 docs/s3-oss.md diff --git a/README.md b/README.md index 561238d32..e33e8cb4e 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ - 除插件工具外,与原项目保持一致 [ChatGPT-Next-Web 主要功能](https://github.com/Yidadaa/ChatGPT-Next-Web#主要功能) - 支持 GPT-4V(视觉) 模型 + - 需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置 + - 基于 [LangChain](https://github.com/hwchase17/langchainjs) 实现的插件功能,目前支持以下插件,未来会添加更多 - 搜索 - [SerpAPI](https://js.langchain.com/docs/api/tools/classes/SerpAPI) @@ -51,11 +53,11 @@ - 其它 - [Wiki](https://js.langchain.com/docs/api/tools/classes/WikipediaQueryRun) - DALL-E 3 - - DALL-E 3 插件需要配置 R2 存储,请参考 [Cloudflare R2 服务配置指南](./docs/cloudflare-r2-cn.md) 配置 + - DALL-E 3 插件需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置 - StableDiffusion - 本插件目前为测试版本,后续可能会有较大的变更,请谨慎使用 - 使用本插件需要一定的专业知识,Stable Diffusion 本身的相关问题不在本项目的解答范围内,如果您确定要使用本插件请参考 [Stable Diffusion 插件配置指南](./docs/stable-diffusion-plugin-cn.md) 文档进行配置 - - StableDiffusion 插件需要配置 R2 存储,请参考 [Cloudflare R2 服务配置指南](./docs/cloudflare-r2-cn.md) 配置 + - StableDiffusion 插件需要配置对象存储服务,请参考 [对象存储服务配置指南](./docs/s3-oss.md) 配置 - Arxiv ## 开发计划 @@ -181,22 +183,6 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 如果你不想让用户查询余额,将此环境变量设置为 1 即可。 -### `R2_ACCOUNT_ID` (可选) - -Cloudflare R2 帐户 ID,使用 `DALL-E` 插件时需要配置。 - -### `R2_ACCESS_KEY_ID` (可选) - -Cloudflare R2 访问密钥 ID,使用 `DALL-E` 插件时需要配置。 - -### `R2_SECRET_ACCESS_KEY` (可选) - -Cloudflare R2 机密访问密钥,使用 `DALL-E` 插件时需要配置。 - -### `R2_BUCKET` (可选) - -Cloudflare R2 Bucket 名称,使用 `DALL-E` 插件时需要配置。 - ## 部署 ### 容器部署 (推荐) diff --git a/app/api/file/[...path]/route.ts b/app/api/file/[...path]/route.ts index f35c7770e..8b4f9547a 100644 --- a/app/api/file/[...path]/route.ts +++ b/app/api/file/[...path]/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; -import { auth } from "../../auth"; -import S3FileStorage from "../../../utils/r2_file_storage"; +import S3FileStorage from "../../../utils/s3_file_storage"; async function handle( req: NextRequest, @@ -10,13 +9,6 @@ async function handle( return NextResponse.json({ body: "OK" }, { status: 200 }); } - // const authResult = auth(req); - // if (authResult.error) { - // return NextResponse.json(authResult, { - // status: 401, - // }); - // } - try { var file = await S3FileStorage.get(params.path[0]); return new Response(file?.transformToWebStream(), { @@ -34,3 +26,4 @@ async function handle( export const GET = handle; export const runtime = "edge"; +export const revalidate = 0; diff --git a/app/api/langchain-tools/dalle_image_generator.ts b/app/api/langchain-tools/dalle_image_generator.ts index c13a801d5..a63d68dda 100644 --- a/app/api/langchain-tools/dalle_image_generator.ts +++ b/app/api/langchain-tools/dalle_image_generator.ts @@ -1,6 +1,6 @@ import { StructuredTool } from "langchain/tools"; import { z } from "zod"; -import S3FileStorage from "../../utils/r2_file_storage"; +import S3FileStorage from "../../utils/s3_file_storage"; export class DallEAPIWrapper extends StructuredTool { name = "dalle_image_generator"; diff --git a/app/api/langchain-tools/stable_diffusion_image_generator.ts b/app/api/langchain-tools/stable_diffusion_image_generator.ts index 86eee3fb4..f64025454 100644 --- a/app/api/langchain-tools/stable_diffusion_image_generator.ts +++ b/app/api/langchain-tools/stable_diffusion_image_generator.ts @@ -1,5 +1,5 @@ import { Tool } from "langchain/tools"; -import S3FileStorage from "../../utils/r2_file_storage"; +import S3FileStorage from "../../utils/s3_file_storage"; export class StableDiffusionWrapper extends Tool { name = "stable_diffusion_image_generator"; diff --git a/app/utils/r2_file_storage.ts b/app/utils/s3_file_storage.ts similarity index 69% rename from app/utils/r2_file_storage.ts rename to app/utils/s3_file_storage.ts index 80d71d671..139e145fd 100644 --- a/app/utils/r2_file_storage.ts +++ b/app/utils/s3_file_storage.ts @@ -12,13 +12,19 @@ const R2_ACCESS_KEY_ID = process.env.R2_ACCESS_KEY_ID; const R2_SECRET_ACCESS_KEY = process.env.R2_SECRET_ACCESS_KEY; const R2_BUCKET = process.env.R2_BUCKET; +const S3_ENDPOINT = process.env.S3_ENDPOINT; +const S3_ACCESS_KEY_ID = process.env.S3_ACCESS_KEY_ID; +const S3_SECRET_ACCESS_KEY = process.env.S3_SECRET_ACCESS_KEY; +const S3_BUCKET = process.env.S3_BUCKET; + const getR2Client = () => { return new S3Client({ region: "auto", - endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, + endpoint: + S3_ENDPOINT ?? `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { - accessKeyId: R2_ACCESS_KEY_ID!, - secretAccessKey: R2_SECRET_ACCESS_KEY!, + accessKeyId: S3_ACCESS_KEY_ID ?? R2_ACCESS_KEY_ID!, + secretAccessKey: S3_SECRET_ACCESS_KEY ?? R2_SECRET_ACCESS_KEY!, }, }); }; @@ -27,7 +33,7 @@ export default class S3FileStorage { static async get(fileName: string) { const file = await getR2Client().send( new GetObjectCommand({ - Bucket: R2_BUCKET, + Bucket: S3_BUCKET ?? R2_BUCKET, Key: fileName, }), ); @@ -43,7 +49,7 @@ export default class S3FileStorage { const signedUrl = await getSignedUrl( getR2Client(), new PutObjectCommand({ - Bucket: R2_BUCKET, + Bucket: S3_BUCKET ?? R2_BUCKET, Key: fileName, }), { expiresIn: 60 }, @@ -59,7 +65,7 @@ export default class S3FileStorage { return `/api/file/${fileName}`; } catch (e) { - console.error("[R2]", e); + console.error("[S3]", e); throw e; } } diff --git a/docs/s3-oss.md b/docs/s3-oss.md new file mode 100644 index 000000000..68e89970f --- /dev/null +++ b/docs/s3-oss.md @@ -0,0 +1,68 @@ +# 对象存储服务配置指南 + +请根据自身网络情况,选择 S3 或 R2 来作为对象存储服务,两个服务配置其一即可。 + +由于国内网络使用 R2 经常抽风,这边推荐选择一家支持 S3 协议的对象存储服务提供商。 + +## 配置 S3 对象存储服务 + +这边以 `又拍云` 做为演示,其它运营商请查询对应文档。 + +1. 登录 [又拍云 - 加速在线业务 - CDN加速 - 云存储 (upyun.com)](https://www.upyun.com/) +2. 注册账户 +3. 进入”云存储“控制台[又拍云控制台 (upyun.com)](https://console.upyun.com/services/file/) +4. 创建一个服务,记录你的服务名 +5. 进入"用户管理","操作员"创建一个"操作员"并赋予相应权限 +6. 编辑"操作员"复制"AccessKey"和"SecretAccessKey" +7. 如果读写权限未勾选则选中后确定 +8. 回到 ChatGPT-Next-Web-LangChain 项目修改环境变量。按照以下信息填写: + - `S3_ENDPOINT=http://s3.api.upyun.com` + - `S3_ACCESS_KEY_ID=AccessKey` + - `S3_SECRET_ACCESS_KEY=SecretAccessKey` + - `S3_BUCKET=服务名` +9. Enjoy. + +## 配置 R2 服务 +登录到 dash.cloudflare.com 并在左侧菜单进入 R2。 + +1. 复制右侧 "账户ID" + +2. 点击 "创建存储桶"。 + +3. 自定义配置一个存储桶名称,记录下来用于后面配置环境变量,点击 "创建存储桶"。 + +4. 进入 "设置",点击 "添加 CORS 策略",将下面内容粘贴上去并点击 "保存"。 + + ```json + [ + { + "AllowedOrigins": [ + "*" + ], + "AllowedMethods": [ + "PUT", + "DELETE", + "GET" + ], + "AllowedHeaders": [ + "content-type" + ], + "MaxAgeSeconds": 3000 + } + ] + ``` + +5. 回到 R2 主菜单,点击右侧 "管理 R2 API 令牌"。 + +6. 点击 "创建 API 令牌",权限选择为 "管理员读和写",TTL 选择为 "永久",点击 "创建 API 令牌"。 + +7. 复制 "访问密钥 ID" 和 "机密访问密钥",点击 "完成"。 + +8. 回到 ChatGPT-Next-Web-LangChain 项目修改环境变量。按照以下信息填写: + + - `R2_ACCOUNT_ID=账户ID` + - `R2_ACCESS_KEY_ID=访问密钥 ID` + - `R2_SECRET_ACCESS_KEY=机密访问密钥` + - `R2_BUCKET=存储桶名称` + +9. Enjoy. \ No newline at end of file