mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-05-22 13:40:16 +09:00
feat: Cloudflare R2 对象存储支持
This commit is contained in:
parent
6d8150a1b0
commit
2947274ce6
36
app/api/file/[...path]/route.ts
Normal file
36
app/api/file/[...path]/route.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { auth } from "../../auth";
|
||||||
|
import S3FileStorage from "../../../utils/r2_file_storage";
|
||||||
|
|
||||||
|
async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { path: string[] } },
|
||||||
|
) {
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
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(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "image/png",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return new Response("not found", {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET = handle;
|
||||||
|
|
||||||
|
export const runtime = "edge";
|
@ -1,9 +1,6 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
import { Tool } from "langchain/tools";
|
import { Tool } from "langchain/tools";
|
||||||
import OpenAI from "openai";
|
import OpenAI from "openai";
|
||||||
|
import S3FileStorage from "../../utils/r2_file_storage";
|
||||||
|
|
||||||
export class DallEAPIWrapper extends Tool {
|
export class DallEAPIWrapper extends Tool {
|
||||||
name = "dalle_image_generator";
|
name = "dalle_image_generator";
|
||||||
@ -22,20 +19,10 @@ export class DallEAPIWrapper extends Tool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveImageFromUrl(url: string) {
|
async saveImageFromUrl(url: string) {
|
||||||
const response = await axios.get(url, { responseType: "arraybuffer" });
|
const response = await fetch(url);
|
||||||
|
const content = await response.arrayBuffer();
|
||||||
const uploadsDir = "public/uploads";
|
const buffer = Buffer.from(content);
|
||||||
console.log("[fileUpload]", { uploadsDir });
|
return await S3FileStorage.put(`${Date.now()}.png`, buffer);
|
||||||
if (!fs.existsSync(uploadsDir)) {
|
|
||||||
fs.mkdirSync(uploadsDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename = `${Date.now()}.png`;
|
|
||||||
const filePath = path.join(uploadsDir, filename);
|
|
||||||
|
|
||||||
fs.writeFileSync(filePath, Buffer.from(response.data, "binary"));
|
|
||||||
|
|
||||||
return `uploads/${filename}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ignore */
|
/** @ignore */
|
||||||
@ -61,5 +48,5 @@ export class DallEAPIWrapper extends Tool {
|
|||||||
description = `openai's dall-e image generator.
|
description = `openai's dall-e image generator.
|
||||||
input must be a english prompt.
|
input must be a english prompt.
|
||||||
output will be the image link url.
|
output will be the image link url.
|
||||||
use markdown to display images.`;
|
use markdown to display images. like: `;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,8 @@ export const CN_PLUGINS: BuiltinPlugin[] = [
|
|||||||
name: "DALL·E",
|
name: "DALL·E",
|
||||||
toolName: "dalle_image_generator",
|
toolName: "dalle_image_generator",
|
||||||
lang: "cn",
|
lang: "cn",
|
||||||
description: "DALL·E 可以根据自然语言的描述创建逼真的图像和艺术。",
|
description:
|
||||||
|
"DALL·E 可以根据自然语言的描述创建逼真的图像和艺术。使用本插件需要配置 Cloudflare R2 对象存储服务。",
|
||||||
builtin: true,
|
builtin: true,
|
||||||
createdAt: 1694703673000,
|
createdAt: 1694703673000,
|
||||||
enable: false,
|
enable: false,
|
||||||
|
@ -45,7 +45,7 @@ export const EN_PLUGINS: BuiltinPlugin[] = [
|
|||||||
toolName: "dalle_image_generator",
|
toolName: "dalle_image_generator",
|
||||||
lang: "en",
|
lang: "en",
|
||||||
description:
|
description:
|
||||||
"DALL·E 2 is an AI system that can create realistic images and art from a description in natural language.",
|
"DALL·E 2 is an AI system that can create realistic images and art from a description in natural language. Using this plugin requires configuring Cloudflare R2 object storage service.",
|
||||||
builtin: true,
|
builtin: true,
|
||||||
createdAt: 1694703673000,
|
createdAt: 1694703673000,
|
||||||
enable: false,
|
enable: false,
|
||||||
|
61
app/utils/r2_file_storage.ts
Normal file
61
app/utils/r2_file_storage.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
S3Client,
|
||||||
|
ListBucketsCommand,
|
||||||
|
ListObjectsV2Command,
|
||||||
|
GetObjectCommand,
|
||||||
|
PutObjectCommand,
|
||||||
|
} from "@aws-sdk/client-s3";
|
||||||
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
|
|
||||||
|
const R2_ACCOUNT_ID = process.env.R2_ACCOUNT_ID;
|
||||||
|
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 getR2Client = () => {
|
||||||
|
return new S3Client({
|
||||||
|
region: "auto",
|
||||||
|
endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: R2_ACCESS_KEY_ID!,
|
||||||
|
secretAccessKey: R2_SECRET_ACCESS_KEY!,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class S3FileStorage {
|
||||||
|
static async get(fileName: string) {
|
||||||
|
const file = await getR2Client().send(
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: R2_BUCKET,
|
||||||
|
Key: fileName,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
throw new Error("not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async put(fileName: string, data: Buffer) {
|
||||||
|
const signedUrl = await getSignedUrl(
|
||||||
|
getR2Client(),
|
||||||
|
new PutObjectCommand({
|
||||||
|
Bucket: R2_BUCKET,
|
||||||
|
Key: fileName,
|
||||||
|
}),
|
||||||
|
{ expiresIn: 60 },
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(signedUrl);
|
||||||
|
|
||||||
|
await fetch(signedUrl, {
|
||||||
|
method: "PUT",
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
return `/api/file/${fileName}`;
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
|
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.414.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.414.0",
|
||||||
"@fortaine/fetch-event-source": "^3.0.6",
|
"@fortaine/fetch-event-source": "^3.0.6",
|
||||||
"@hello-pangea/dnd": "^16.3.0",
|
"@hello-pangea/dnd": "^16.3.0",
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
|
Loading…
Reference in New Issue
Block a user