Initial commit
This commit is contained in:
97
packages/minio/src/index.ts
Normal file
97
packages/minio/src/index.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import "server-only";
|
||||
|
||||
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||
import { z } from "zod";
|
||||
|
||||
const envSchema = z.object({
|
||||
MINIO_INTERNAL_ENDPOINT: z.string().url().optional(),
|
||||
MINIO_PUBLIC_ENDPOINT_TS: z.string().url().optional(),
|
||||
MINIO_ACCESS_KEY_ID: z.string().min(1),
|
||||
MINIO_SECRET_ACCESS_KEY: z.string().min(1),
|
||||
MINIO_REGION: z.string().min(1).default("us-east-1"),
|
||||
MINIO_BUCKET: z.string().min(1).default("media"),
|
||||
MINIO_PRESIGN_EXPIRES_SECONDS: z.coerce.number().int().positive().default(900)
|
||||
});
|
||||
|
||||
type MinioEnv = z.infer<typeof envSchema>;
|
||||
|
||||
let cachedEnv: MinioEnv | undefined;
|
||||
let cachedInternal: S3Client | undefined;
|
||||
let cachedPublic: S3Client | undefined;
|
||||
|
||||
export function getMinioEnv(): MinioEnv {
|
||||
if (cachedEnv) return cachedEnv;
|
||||
const parsed = envSchema.safeParse(process.env);
|
||||
if (!parsed.success) {
|
||||
throw new Error(`Invalid MinIO env: ${parsed.error.message}`);
|
||||
}
|
||||
cachedEnv = parsed.data;
|
||||
return cachedEnv;
|
||||
}
|
||||
|
||||
export function getMinioBucket() {
|
||||
return getMinioEnv().MINIO_BUCKET;
|
||||
}
|
||||
|
||||
export function getMinioInternalClient(): S3Client {
|
||||
if (cachedInternal) return cachedInternal;
|
||||
const env = getMinioEnv();
|
||||
if (!env.MINIO_INTERNAL_ENDPOINT) {
|
||||
throw new Error("MINIO_INTERNAL_ENDPOINT is required for internal MinIO client");
|
||||
}
|
||||
|
||||
cachedInternal = new S3Client({
|
||||
region: env.MINIO_REGION,
|
||||
endpoint: env.MINIO_INTERNAL_ENDPOINT,
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: env.MINIO_ACCESS_KEY_ID,
|
||||
secretAccessKey: env.MINIO_SECRET_ACCESS_KEY
|
||||
}
|
||||
});
|
||||
|
||||
return cachedInternal;
|
||||
}
|
||||
|
||||
export function getMinioPublicSigningClient(): S3Client {
|
||||
if (cachedPublic) return cachedPublic;
|
||||
const env = getMinioEnv();
|
||||
if (!env.MINIO_PUBLIC_ENDPOINT_TS) {
|
||||
throw new Error("MINIO_PUBLIC_ENDPOINT_TS is required for presigned URL generation");
|
||||
}
|
||||
|
||||
cachedPublic = new S3Client({
|
||||
region: env.MINIO_REGION,
|
||||
endpoint: env.MINIO_PUBLIC_ENDPOINT_TS,
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: env.MINIO_ACCESS_KEY_ID,
|
||||
secretAccessKey: env.MINIO_SECRET_ACCESS_KEY
|
||||
}
|
||||
});
|
||||
|
||||
return cachedPublic;
|
||||
}
|
||||
|
||||
export async function presignGetObjectUrl(input: {
|
||||
bucket: string;
|
||||
key: string;
|
||||
expiresSeconds?: number;
|
||||
responseContentType?: string;
|
||||
responseContentDisposition?: string;
|
||||
}) {
|
||||
const env = getMinioEnv();
|
||||
const s3 = getMinioPublicSigningClient();
|
||||
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: input.bucket,
|
||||
Key: input.key,
|
||||
ResponseContentType: input.responseContentType,
|
||||
ResponseContentDisposition: input.responseContentDisposition,
|
||||
});
|
||||
|
||||
const expiresIn = input.expiresSeconds ?? env.MINIO_PRESIGN_EXPIRES_SECONDS;
|
||||
const url = await getSignedUrl(s3, command, { expiresIn });
|
||||
return { url, expiresSeconds: expiresIn };
|
||||
}
|
||||
Reference in New Issue
Block a user