153 lines
4.0 KiB
TypeScript
153 lines
4.0 KiB
TypeScript
import {
|
|
S3Client,
|
|
PutObjectCommand,
|
|
DeleteObjectCommand,
|
|
CreateMultipartUploadCommand,
|
|
UploadPartCommand,
|
|
CompleteMultipartUploadCommand,
|
|
AbortMultipartUploadCommand,
|
|
} from "@aws-sdk/client-s3";
|
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
|
|
const RUSTFS_ENDPOINT = process.env.RUSTFS_ENDPOINT || "http://localhost:9000";
|
|
const RUSTFS_ACCESS_KEY = process.env.RUSTFS_ACCESS_KEY || "minioadmin";
|
|
const RUSTFS_SECRET_KEY = process.env.RUSTFS_SECRET_KEY || "minioadmin";
|
|
const RUSTFS_BUCKET = process.env.RUSTFS_BUCKET || "surf";
|
|
const RUSTFS_PUBLIC = (process.env.RUSTFS_PUBLIC || "true") === "true";
|
|
|
|
let client: S3Client | null = null;
|
|
|
|
function getRustFsClient(): S3Client {
|
|
if (!client) {
|
|
client = new S3Client({
|
|
endpoint: RUSTFS_ENDPOINT,
|
|
region: "us-east-1",
|
|
credentials: {
|
|
accessKeyId: RUSTFS_ACCESS_KEY,
|
|
secretAccessKey: RUSTFS_SECRET_KEY,
|
|
},
|
|
forcePathStyle: true,
|
|
});
|
|
}
|
|
return client;
|
|
}
|
|
|
|
function getPublicUrl(key: string): string {
|
|
if (RUSTFS_PUBLIC) {
|
|
return `${RUSTFS_ENDPOINT}/${RUSTFS_BUCKET}/${key}`;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
export function getObjectUrl(key: string): string {
|
|
const publicUrl = getPublicUrl(key);
|
|
if (publicUrl) return publicUrl;
|
|
return `/api/videos/file/${encodeURIComponent(key)}`;
|
|
}
|
|
|
|
export async function uploadObject(
|
|
key: string,
|
|
body: Buffer,
|
|
contentType: string,
|
|
): Promise<void> {
|
|
const s3 = getRustFsClient();
|
|
await s3.send(
|
|
new PutObjectCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
Body: body,
|
|
ContentType: contentType,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function deleteObject(key: string): Promise<void> {
|
|
const s3 = getRustFsClient();
|
|
await s3.send(
|
|
new DeleteObjectCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function getPresignedUploadUrl(
|
|
key: string,
|
|
contentType: string,
|
|
expiresInSeconds = 3600,
|
|
): Promise<string> {
|
|
const s3 = getRustFsClient();
|
|
const command = new PutObjectCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
ContentType: contentType,
|
|
});
|
|
return getSignedUrl(s3, command, { expiresIn: expiresInSeconds });
|
|
}
|
|
|
|
export async function createMultipartUpload(
|
|
key: string,
|
|
contentType: string,
|
|
): Promise<{ uploadId: string }> {
|
|
const s3 = getRustFsClient();
|
|
const result = await s3.send(
|
|
new CreateMultipartUploadCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
ContentType: contentType,
|
|
}),
|
|
);
|
|
if (!result.UploadId) {
|
|
throw new Error("Failed to initiate multipart upload");
|
|
}
|
|
return { uploadId: result.UploadId };
|
|
}
|
|
|
|
export async function getPresignedUploadPartUrl(
|
|
key: string,
|
|
uploadId: string,
|
|
partNumber: number,
|
|
expiresInSeconds = 3600,
|
|
): Promise<string> {
|
|
const s3 = getRustFsClient();
|
|
const command = new UploadPartCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
UploadId: uploadId,
|
|
PartNumber: partNumber,
|
|
});
|
|
return getSignedUrl(s3, command, { expiresIn: expiresInSeconds });
|
|
}
|
|
|
|
export async function completeMultipartUpload(
|
|
key: string,
|
|
uploadId: string,
|
|
parts: { PartNumber: number; ETag: string }[],
|
|
): Promise<void> {
|
|
const s3 = getRustFsClient();
|
|
await s3.send(
|
|
new CompleteMultipartUploadCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
UploadId: uploadId,
|
|
MultipartUpload: {
|
|
Parts: parts.sort((a, b) => a.PartNumber - b.PartNumber),
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function abortMultipartUpload(
|
|
key: string,
|
|
uploadId: string,
|
|
): Promise<void> {
|
|
const s3 = getRustFsClient();
|
|
await s3.send(
|
|
new AbortMultipartUploadCommand({
|
|
Bucket: RUSTFS_BUCKET,
|
|
Key: key,
|
|
UploadId: uploadId,
|
|
}),
|
|
);
|
|
}
|