// packages/storage/src/index.ts —— 存储包统一出口:端口类型 + MinIO 适配器 + 错误 + // env 工厂。api(Task 18 presign/verify)与 worker(Task 20 variants put/get)经 // `createStorageFromEnv()` 构造实例,测试/本地指向 MinIO,生产指向 OSS。 // commonjs 包(消费方 api/worker 均为 CJS);@photo/contracts 为 ESM dist,运行时经 // Node require(esm) 消费(Node >= 22.12,见根 engines)。 import { MinioStorageAdapter, type MinioStorageConfig } from './minio.adapter'; export { MinioStorageAdapter } from './minio.adapter'; export type { MinioStorageConfig } from './minio.adapter'; export { StorageError, ErrorCodes } from './errors'; export type { StoragePort, UploadCredential } from './port'; export { OBJECT_KEY_ROOT, variantsObjectKey, watermarkVariantObjectKey, } from './keys'; export type { PhotoVariantName } from './keys'; /** * 从 S3_* 环境变量构造 MinIO/S3 兼容适配器(仓库根 .env 经 dotenv-cli 或 * test/load-env.ts 注入)。任一变量缺失即抛错,禁止静默回退默认值。 */ export function createStorageFromEnv(): MinioStorageAdapter { const config = { endpoint: process.env.S3_ENDPOINT, region: process.env.S3_REGION, accessKey: process.env.S3_ACCESS_KEY, secretKey: process.env.S3_SECRET_KEY, bucket: process.env.S3_BUCKET, // S3_FORCE_PATH_STYLE 可选:缺省 true(MinIO path-style);阿里云 OSS 等 // 云形态设 false(virtual-host + 基础 endpoint)——G1 实测定案。 forcePathStyle: process.env.S3_FORCE_PATH_STYLE ? process.env.S3_FORCE_PATH_STYLE === 'true' : true, }; // 只校验必填字符串字段(forcePathStyle 是布尔,false 不代表缺失)。 const required = ['endpoint', 'region', 'accessKey', 'secretKey', 'bucket'] as const; const missing = required.filter((key) => !config[key]).map((key) => `S3_${key.toUpperCase()}`); if (missing.length > 0) { throw new Error(`createStorageFromEnv: 缺少环境变量 ${missing.join(', ')}(S3_* 配置见仓库根 .env)`); } return new MinioStorageAdapter(config as MinioStorageConfig); }