Initial commit

This commit is contained in:
OpenCode Test
2025-12-24 10:50:10 -08:00
commit e1a64aa092
70 changed files with 5827 additions and 0 deletions

View 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 };
}