64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
|
|
import { Client } from 'minio';
|
||
|
|
|
||
|
|
import { env } from '@/lib/env';
|
||
|
|
import { logger } from '@/lib/logger';
|
||
|
|
|
||
|
|
export const minioClient = new Client({
|
||
|
|
endPoint: env.MINIO_ENDPOINT,
|
||
|
|
port: env.MINIO_PORT,
|
||
|
|
useSSL: env.MINIO_USE_SSL,
|
||
|
|
accessKey: env.MINIO_ACCESS_KEY,
|
||
|
|
secretKey: env.MINIO_SECRET_KEY,
|
||
|
|
});
|
||
|
|
|
||
|
|
const BUCKET = env.MINIO_BUCKET;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Ensures the configured bucket exists, creating it if not.
|
||
|
|
* Should be called once at application startup.
|
||
|
|
*/
|
||
|
|
export async function ensureBucket(): Promise<void> {
|
||
|
|
try {
|
||
|
|
const exists = await minioClient.bucketExists(BUCKET);
|
||
|
|
if (!exists) {
|
||
|
|
await minioClient.makeBucket(BUCKET);
|
||
|
|
logger.info({ bucket: BUCKET }, 'MinIO bucket created');
|
||
|
|
} else {
|
||
|
|
logger.debug({ bucket: BUCKET }, 'MinIO bucket exists');
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
logger.error({ err, bucket: BUCKET }, 'Failed to ensure MinIO bucket');
|
||
|
|
throw err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generates a pre-signed GET URL for an object.
|
||
|
|
*
|
||
|
|
* Default expiry is 15 minutes (900 seconds) per SECURITY-GUIDELINES.md §7.1.
|
||
|
|
*/
|
||
|
|
export async function getPresignedUrl(
|
||
|
|
objectKey: string,
|
||
|
|
expirySeconds = 900,
|
||
|
|
): Promise<string> {
|
||
|
|
return minioClient.presignedGetObject(BUCKET, objectKey, expirySeconds);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Constructs a storage path from typed components.
|
||
|
|
*
|
||
|
|
* Format: `{portSlug}/{entity}/{entityId}/{fileId}.{extension}`
|
||
|
|
*
|
||
|
|
* No user-supplied input should ever be used as path components — only UUIDs
|
||
|
|
* and controlled slugs (SECURITY-GUIDELINES.md §3.4, §7.1).
|
||
|
|
*/
|
||
|
|
export function buildStoragePath(
|
||
|
|
portSlug: string,
|
||
|
|
entity: string,
|
||
|
|
entityId: string,
|
||
|
|
fileId: string,
|
||
|
|
extension: string,
|
||
|
|
): string {
|
||
|
|
return `${portSlug}/${entity}/${entityId}/${fileId}.${extension}`;
|
||
|
|
}
|