import { readFileSync } from 'node:fs'; import { Agent as HttpsAgent } from 'node:https'; import { Client } from 'minio'; type MinioTarget = { endPoint: string; port: number; useSSL: boolean; origin: string; }; const parseBoolean = (value: string | undefined, fallback: boolean): boolean => { if (value == null || value.trim() === '') { return fallback; } return value.toLowerCase() === 'true'; }; const resolveMinioTarget = ({ origin, endpoint, port, useSSL, }: { origin?: string; endpoint?: string; port?: string | number; useSSL: boolean; }): MinioTarget => { const rawOrigin = origin?.trim(); if (rawOrigin) { const url = new URL(rawOrigin); const targetUseSSL = url.protocol === 'https:'; const targetPort = Number(url.port || (targetUseSSL ? 443 : 80)); return { endPoint: url.hostname, port: targetPort, useSSL: targetUseSSL, origin: url.origin, }; } const rawEndpoint = endpoint?.trim() || 'localhost'; if (rawEndpoint.startsWith('http://') || rawEndpoint.startsWith('https://')) { const url = new URL(rawEndpoint); const targetUseSSL = url.protocol === 'https:'; const targetPort = Number(url.port || (targetUseSSL ? 443 : 80)); return { endPoint: url.hostname, port: targetPort, useSSL: targetUseSSL, origin: url.origin, }; } const targetPort = Number(port ?? (useSSL ? 443 : 80)); const includePort = !(useSSL && targetPort === 443) && !(!useSSL && targetPort === 80); return { endPoint: rawEndpoint, port: targetPort, useSSL, origin: `${useSSL ? 'https' : 'http'}://${rawEndpoint}${includePort ? `:${targetPort}` : ''}`, }; }; const accessKey = process.env.MINIO_ACCESS_KEY; const secretKey = process.env.MINIO_SECRET_KEY; const insecureSkipTlsVerify = parseBoolean(process.env.MINIO_INSECURE_SKIP_TLS_VERIFY, false); const tlsRejectUnauthorized = parseBoolean(process.env.MINIO_TLS_REJECT_UNAUTHORIZED, true); const minioCaCertPath = process.env.MINIO_CA_CERT_PATH?.trim(); const internalUseSSL = parseBoolean(process.env.MINIO_USE_SSL, false); const internalTarget = resolveMinioTarget({ endpoint: process.env.MINIO_ENDPOINT ?? 'localhost', port: process.env.MINIO_PORT ?? 9000, useSSL: internalUseSSL, }); const publicTarget = resolveMinioTarget({ origin: process.env.MINIO_PUBLIC_ORIGIN, endpoint: process.env.MINIO_PUBLIC_ENDPOINT ?? internalTarget.endPoint, port: process.env.MINIO_PUBLIC_PORT ?? internalTarget.port, useSSL: parseBoolean(process.env.MINIO_PUBLIC_USE_SSL, internalTarget.useSSL), }); if (!accessKey || !secretKey) { throw new Error('MINIO_ACCESS_KEY and MINIO_SECRET_KEY must be set'); } export const minioBucket = process.env.MINIO_BUCKET ?? 'videos'; export const minioPresignedExpirySeconds = Number(process.env.MINIO_PRESIGNED_EXPIRY_SECONDS ?? 60 * 10); export const minioPublicOrigin = publicTarget.origin; const customCa = minioCaCertPath ? readFileSync(minioCaCertPath) : undefined; const transportAgent = internalTarget.useSSL ? new HttpsAgent({ keepAlive: true, ca: customCa, rejectUnauthorized: insecureSkipTlsVerify ? false : tlsRejectUnauthorized, }) : undefined; export const minioClient = new Client({ endPoint: internalTarget.endPoint, port: internalTarget.port, useSSL: internalTarget.useSSL, accessKey, secretKey, region: process.env.MINIO_REGION ?? 'us-east-1', transportAgent, }); export const minioPresignClient = new Client({ endPoint: publicTarget.endPoint, port: publicTarget.port, useSSL: publicTarget.useSSL, accessKey, secretKey, region: process.env.MINIO_REGION ?? 'us-east-1', }); let ensureBucketPromise: Promise | null = null; export const ensureMinioBucket = async (): Promise => { if (ensureBucketPromise) { return ensureBucketPromise; } ensureBucketPromise = (async () => { const exists = await minioClient.bucketExists(minioBucket); if (!exists) { await minioClient.makeBucket(minioBucket, process.env.MINIO_REGION ?? 'us-east-1'); } })(); try { await ensureBucketPromise; console.log("Minio Up") } catch (error) { ensureBucketPromise = null; throw error; } };