@h-ai/storage 0.1.0-alpha.19 → 0.1.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node.js +1 -4
- package/dist/node.js.map +1 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -825,9 +825,6 @@ function createS3Provider() {
|
|
|
825
825
|
|
|
826
826
|
// src/storage-main.ts
|
|
827
827
|
var logger3 = core.logger.child({ module: "storage", scope: "main" });
|
|
828
|
-
function sanitizeStorageConfig(config) {
|
|
829
|
-
return core.sanitize.sanitizeSensitiveFields(config);
|
|
830
|
-
}
|
|
831
828
|
var currentProvider = null;
|
|
832
829
|
var currentConfig = null;
|
|
833
830
|
var initInProgress = false;
|
|
@@ -932,7 +929,7 @@ var storage = {
|
|
|
932
929
|
},
|
|
933
930
|
/** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */
|
|
934
931
|
get config() {
|
|
935
|
-
return currentConfig ?
|
|
932
|
+
return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null;
|
|
936
933
|
},
|
|
937
934
|
/** 是否已完成初始化 */
|
|
938
935
|
get isInitialized() {
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/storage-provider-local.ts","../src/providers/storage-provider-s3.ts","../src/storage-main.ts"],"names":["getMimeType","stat","dir","resolve","logger","core","toStorageError","Buffer","ok","err"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * @h-ai/storage — Local Provider 实现\n *\n * 基于本地文件系统的存储 Provider。\n * @module storage-provider-local\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { LocalConfig, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\n\nimport * as path from 'node:path'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport { MIME_TYPE_DEFAULT, MIME_TYPES } from '../storage-mime.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-local' })\nconst PATH_SEGMENT_SPLIT_REGEX = /[\\\\/]+/\n\n// ─── 辅助函数 ───\n\n/**\n * 将 Node.js 文件系统错误转换为 StorageError\n *\n * 映射规则:\n * - ENOENT → NOT_FOUND\n * - EACCES / EPERM → PERMISSION_DENIED\n * - ENOSPC → QUOTA_EXCEEDED\n * - EISDIR → INVALID_PATH\n * - 其他 → IO_ERROR\n *\n * @param error - 原始错误对象\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as NodeJS.ErrnoException\n\n if (e.code === 'ENOENT') {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EACCES' || e.code === 'EPERM') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'ENOSPC') {\n return {\n ...HaiStorageError.QUOTA_EXCEEDED,\n message: storageM('storage_diskSpaceInsufficient'),\n cause: error,\n }\n }\n\n if (e.code === 'EISDIR') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: storageM('storage_pathIsDir', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EINVAL') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: e.message || storageM('storage_pathTraversal'),\n cause: error,\n }\n }\n\n return {\n code: HaiStorageError.IO_ERROR.code,\n message: storageM('storage_ioError', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 根据文件扩展名获取 MIME 类型\n *\n * 使用共享 MIME_TYPES 映射表,未匹配时返回 'application/octet-stream'。\n *\n * @param filePath - 文件路径或文件名\n * @returns MIME 类型字符串\n */\nfunction getMimeType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase().replace('.', '')\n return MIME_TYPES[ext] || MIME_TYPE_DEFAULT\n}\n\n/**\n * 根据文件 stat 信息计算 ETag\n *\n * 使用 \"size-mtime\" 格式,与 head 操作保持一致,无需读取文件内容。\n *\n * @param stat - 文件的 fs.Stats 对象\n * @returns 带双引号的 ETag 字符串,如 '\"a-18f7ec3b4c0\"'\n */\nfunction calculateEtag(stat: fs.Stats): string {\n return `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n}\n\n/**\n * 检查 key 是否指向内部元数据文件(.meta.json)\n *\n * 拦截外部对 .meta.json 文件的直接访问,防止元数据泄漏。\n */\nfunction isMetaFile(key: string): boolean {\n return key.endsWith('.meta.json')\n}\n\n/**\n * 创建非法路径错误(用于统一映射为 INVALID_PATH)\n */\nfunction createInvalidPathError(): NodeJS.ErrnoException {\n const error = new Error(storageM('storage_pathTraversal')) as NodeJS.ErrnoException\n error.code = 'EINVAL'\n return error\n}\n\n/**\n * 判断路径片段中是否包含路径穿越标记(..)\n */\nfunction hasPathTraversalSegment(key: string): boolean {\n const segments = key.split(PATH_SEGMENT_SPLIT_REGEX).filter(Boolean)\n return segments.includes('..')\n}\n\n/**\n * 安全的路径解析,防止路径穿越攻击\n *\n * 处理流程:\n * 1. 拒绝绝对路径、空字节、`..` 路径穿越片段\n * 2. resolve(root, key) 得到完整路径\n * 3. 校验完整路径必须位于 root 内部(或等于 root)\n *\n * @param root - 存储根目录绝对路径\n * @param key - 用户提供的文件键\n * @returns 安全的完整文件路径\n * @throws 当解析后路径超出 root 范围时抛出异常\n */\nfunction safePath(root: string, key: string): string {\n // 拒绝绝对路径输入,防止 Windows 盘符绕过(如 D:\\etc\\passwd)\n if (path.isAbsolute(key)) {\n throw createInvalidPathError()\n }\n\n // 拒绝空字节与路径穿越片段\n if (key.includes('\\0') || hasPathTraversalSegment(key)) {\n throw createInvalidPathError()\n }\n\n const normalized = path.normalize(key)\n const fullPath = path.resolve(root, normalized)\n\n // 确保路径在 root 目录下\n const realRoot = path.resolve(root)\n const inRoot = fullPath === realRoot || fullPath.startsWith(`${realRoot}${path.sep}`)\n\n if (!inRoot) {\n throw createInvalidPathError()\n }\n\n return fullPath\n}\n\n// ─── Local Provider 实现 ───\n\n/**\n * 创建本地存储 Provider\n *\n * 基于本地文件系统实现的 StorageProvider。\n * connect 时会自动创建根目录;文件操作会自动创建中间目录。\n *\n * 已知限制:\n * - list 不支持 continuationToken 分页(仅支持 maxKeys 截断)\n * - presign 返回虚拟 `local://` URL,应用层需自行处理\n * - publicUrl 始终返回 null\n *\n * @returns Local Provider 实例\n */\nexport function createLocalProvider(): StorageProvider {\n let config: LocalConfig | null = null\n let connected = false\n\n // -------------------------------------------------------------------------\n // 辅助方法\n // -------------------------------------------------------------------------\n\n /** 获取当前 Local 配置,未初始化时抛异常 */\n function getConfig(): LocalConfig {\n if (!config) {\n throw new Error(storageM('storage_localNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 转为安全的绝对路径(经 safePath 防穿越处理) */\n function fullPath(key: string): string {\n return safePath(getConfig().root, key)\n }\n\n /**\n * 将输入数据统一转为 Buffer\n *\n * @param data - 字符串、Buffer 或 Uint8Array\n * @returns Buffer 实例\n */\n function toBuffer(data: Buffer | Uint8Array | string): Buffer {\n if (typeof data === 'string') {\n return Buffer.from(data)\n }\n if (data instanceof Buffer) {\n return data\n }\n return Buffer.from(data)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n const buffer = toBuffer(data)\n logger.debug('Putting file', { key, size: buffer.length })\n\n // 确保目录存在\n const dir = path.dirname(filePath)\n await fsp.mkdir(dir, { recursive: true, mode: getConfig().directoryMode })\n\n // 写入文件\n await fsp.writeFile(filePath, buffer, { mode: getConfig().fileMode })\n\n // 获取元数据\n const stat = await fsp.stat(filePath)\n const metadata: FileMetadata = {\n key,\n size: stat.size,\n contentType: options.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据(使用扩展属性或元数据文件)\n if (options.metadata) {\n const metaPath = `${filePath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n logger.warn('Put file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n\n // 检查范围请求\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const stat = await fsp.stat(filePath)\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : stat.size - 1\n\n // 使用流读取指定范围\n return new Promise((resolve) => {\n const chunks: Buffer[] = []\n const stream = fs.createReadStream(filePath, { start, end })\n\n stream.on('data', (chunk: Buffer) => chunks.push(chunk))\n stream.on('end', () => resolve(ok(Buffer.concat(chunks))))\n stream.on('error', error => resolve(err(toStorageError(error, key))))\n })\n }\n\n const data = await fsp.readFile(filePath)\n return ok(data)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n const stat = await fsp.stat(filePath)\n\n if (stat.isDirectory()) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_pathIsDir', { params: { key } }),\n )\n }\n\n // 尝试读取元数据文件\n let customMetadata: { contentType?: string, metadata?: Record<string, string> } = {}\n try {\n const metaPath = `${filePath}.meta.json`\n const metaContent = await fsp.readFile(metaPath, 'utf-8')\n customMetadata = JSON.parse(metaContent)\n }\n catch {\n // 元数据文件不存在,忽略\n }\n\n // 基于文件大小和修改时间生成 ETag(无需读取整个文件)\n const etag = `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n\n return ok({\n key,\n size: stat.size,\n contentType: customMetadata.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag,\n metadata: customMetadata.metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n await fsp.access(filePath, fs.constants.F_OK)\n return ok(true)\n }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(false)\n }\n return err(toStorageError(error, key))\n }\n },\n\n async delete(key): Promise<HaiResult<void>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logger.debug('Deleting file', { key })\n await fsp.unlink(filePath)\n\n // 尝试删除元数据文件\n try {\n await fsp.unlink(`${filePath}.meta.json`)\n }\n catch {\n // 忽略\n }\n\n return ok(undefined)\n }\n catch (error) {\n // 如果文件不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n logger.warn('Delete file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n // 并行删除,避免 await-in-loop(N+1)\n const results = await Promise.allSettled(keys.map(key => file.delete(key)))\n\n const errors: HaiError[] = []\n for (const result of results) {\n if (result.status === 'fulfilled' && !result.value.success) {\n errors.push(result.value.error)\n }\n }\n\n if (errors.length > 0) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_deleteManyFailed', { params: { count: errors.length } }),\n errors,\n )\n }\n\n return ok(undefined)\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(sourceKey) || isMetaFile(destKey)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key: sourceKey } }),\n sourceKey,\n )\n }\n try {\n const sourcePath = fullPath(sourceKey)\n const destPath = fullPath(destKey)\n logger.debug('Copying file', { sourceKey, destKey })\n\n // 确保目标目录存在\n await fsp.mkdir(path.dirname(destPath), { recursive: true, mode: getConfig().directoryMode })\n\n // 复制文件\n await fsp.copyFile(sourcePath, destPath)\n\n // 获取新文件的元数据(无需读取文件内容,使用 stat 计算 ETag)\n const stat = await fsp.stat(destPath)\n\n const metadata: FileMetadata = {\n key: destKey,\n size: stat.size,\n contentType: options.contentType || getMimeType(destKey),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据\n if (options.contentType || options.metadata) {\n const metaPath = `${destPath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const cfg = getConfig()\n const prefix = options.prefix || ''\n const delimiter = options.delimiter || ''\n const maxKeys = options.maxKeys || 1000\n\n const basePath = safePath(cfg.root, prefix)\n const files: FileMetadata[] = []\n const commonPrefixes = new Set<string>()\n\n // 递归读取目录\n async function readDir(dirPath: string): Promise<void> {\n let entries: fs.Dirent[]\n try {\n entries = await fsp.readdir(dirPath, { withFileTypes: true })\n }\n catch {\n return // 目录不存在,返回空\n }\n\n for (const entry of entries) {\n if (files.length >= maxKeys)\n return\n\n const fullEntryPath = path.join(dirPath, entry.name)\n const key = path.relative(cfg.root, fullEntryPath).replace(/\\\\/g, '/')\n\n // 跳过元数据文件\n if (entry.name.endsWith('.meta.json'))\n continue\n\n if (entry.isDirectory()) {\n if (delimiter) {\n // 有分隔符时,目录作为公共前缀\n commonPrefixes.add(`${key}/`)\n }\n else {\n // 无分隔符时,递归遍历\n await readDir(fullEntryPath)\n }\n }\n else if (entry.isFile()) {\n const stat = await fsp.stat(fullEntryPath)\n files.push({\n key,\n size: stat.size,\n contentType: getMimeType(key),\n lastModified: stat.mtime,\n })\n }\n }\n }\n\n await readDir(basePath)\n\n return ok({\n files,\n commonPrefixes: Array.from(commonPrefixes),\n isTruncated: files.length >= maxKeys,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n const dirPath = fullPath(prefix)\n\n // 递归删除目录\n await fsp.rm(dirPath, { recursive: true, force: true })\n\n return ok(undefined)\n }\n catch (error) {\n // 如果目录不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n return err(toStorageError(error, prefix))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n // 本地存储不支持真正的签名 URL\n // 返回一个带签名参数的虚拟 URL,应用层需要自行处理\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?expires=${expires}&signature=${signature}`)\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n // 同上,返回虚拟 URL\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`put:${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?action=put&expires=${expires}&signature=${signature}`)\n },\n\n publicUrl(_key): string | null {\n // 本地存储不支持公开 URL\n return null\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 'local',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 'local') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_localConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting local provider', { root: cfg.root })\n\n // 确保根目录存在\n await fsp.mkdir(cfg.root, { recursive: true, mode: cfg.directoryMode })\n\n connected = true\n logger.info('Local provider connected', { root: cfg.root })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Local provider connect failed', { error })\n config = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n logger.info('Local provider disconnected')\n config = null\n connected = false\n },\n\n isConnected(): boolean {\n return connected\n },\n }\n}\n","/**\n * @h-ai/storage — S3 Provider 实现\n *\n * 基于 AWS SDK v3 实现的 S3 协议存储 Provider。\n * 支持 AWS S3、MinIO、阿里云 OSS 等兼容 S3 协议的存储服务。\n * @module storage-provider-s3\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { S3Config, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport {\n CopyObjectCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-s3' })\nconst TRAILING_SLASHES_REGEX = /\\/+$/\nconst LEADING_SLASHES_REGEX = /^\\/+/\n\n/**\n * 脱敏 S3 endpoint,避免日志暴露认证信息\n */\nfunction sanitizeEndpointUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (parsed.username)\n parsed.username = '***'\n if (parsed.password)\n parsed.password = '***'\n return parsed.toString()\n }\n catch {\n return '(invalid url)'\n }\n}\n\n// ─── 辅助函数 ───\n\n/**\n * 将 S3 SDK 错误转换为 StorageError\n *\n * 映射规则:\n * - NoSuchKey / NotFound / 404 → NOT_FOUND\n * - AccessDenied → PERMISSION_DENIED\n * - NoSuchBucket → CONFIG_ERROR\n * - NetworkError / ECONNREFUSED → NETWORK_ERROR\n * - 其他 → OPERATION_FAILED\n *\n * @param error - AWS SDK 抛出的原始错误\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as { name?: string, message?: string, Code?: string }\n\n // S3 特定错误处理\n if (e.name === 'NoSuchKey' || e.Code === 'NoSuchKey'\n || e.name === 'NotFound' || e.Code === 'NotFound'\n || e.name === '404' || (e as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode === 404) {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'AccessDenied' || e.Code === 'AccessDenied') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'NoSuchBucket' || e.Code === 'NoSuchBucket') {\n return {\n ...HaiStorageError.CONFIG_ERROR,\n message: storageM('storage_bucketNotExist'),\n cause: error,\n }\n }\n\n // 网络相关错误\n if (e.name === 'NetworkError' || e.name?.includes('ECONNREFUSED')) {\n return {\n ...HaiStorageError.NETWORK_ERROR,\n message: storageM('storage_networkError'),\n cause: error,\n }\n }\n\n return {\n ...HaiStorageError.OPERATION_FAILED,\n message: storageM('storage_operationFailed', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 为 key 添加配置中的路径前缀\n *\n * @param key - 用户提供的文件键\n * @param prefix - 配置中的前缀(可能为空)\n * @returns 拼接后的完整 S3 key\n */\nfunction withPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n return `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/${key.replace(LEADING_SLASHES_REGEX, '')}`\n}\n\n/**\n * 从 S3 key 中移除配置前缀,还原为用户视角的文件键\n *\n * @param key - 包含前缀的完整 S3 key\n * @param prefix - 配置中的前缀\n * @returns 去除前缀后的文件键\n */\nfunction withoutPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n const normalizedPrefix = `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/`\n if (key.startsWith(normalizedPrefix)) {\n return key.slice(normalizedPrefix.length)\n }\n return key\n}\n\n// ─── S3 Provider 实现 ───\n\n/**\n * 创建 S3 存储 Provider\n *\n * 基于 AWS SDK v3 实现的 StorageProvider。\n * connect 时会创建 S3Client 并通过 ListObjectsV2 验证连接。\n * 支持配置 prefix 重定向所有 key,对用户透明。\n *\n * 实现说明:\n * - copy 时若指定了 contentType 或 metadata,使用 MetadataDirective='REPLACE'\n * - dir.delete 通过先 list 再批量删除实现\n * - presign 使用 @aws-sdk/s3-request-presigner 签名\n *\n * @returns S3 Provider 实例\n */\nexport function createS3Provider(): StorageProvider {\n let client: S3Client | null = null\n let config: S3Config | null = null\n\n // -------------------------------------------------------------------------\n // 内部辅助\n // -------------------------------------------------------------------------\n\n /** 获取当前 S3Client,未初始化时抛异常 */\n function getClient(): S3Client {\n if (!client) {\n throw new Error(storageM('storage_s3ClientNotInitialized'))\n }\n return client\n }\n\n /** 获取当前 S3 配置,未初始化时抛异常 */\n function getConfig(): S3Config {\n if (!config) {\n throw new Error(storageM('storage_s3ConfigNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 拼接配置前缀,得到完整 S3 对象键 */\n function fullKey(key: string): string {\n return withPrefix(key, getConfig().prefix)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const body = typeof data === 'string' ? Buffer.from(data) : data\n\n await s3Client.send(new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Body: body,\n ContentType: options.contentType,\n Metadata: options.metadata,\n CacheControl: options.cacheControl,\n ContentDisposition: options.contentDisposition,\n }))\n\n // 获取上传后的元数据\n const headResult = await file.head(key)\n if (headResult.success) {\n return headResult\n }\n\n // 如果获取元数据失败,返回基本信息\n return ok({\n key,\n size: body.length,\n contentType: options.contentType || 'application/octet-stream',\n lastModified: new Date(),\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n let Range: string | undefined\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : ''\n Range = `bytes=${start}-${end}`\n }\n\n const response = await s3Client.send(new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Range,\n }))\n\n if (!response.Body) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_responseBodyEmpty'),\n )\n }\n\n // 将流转换为 Buffer\n const chunks: Uint8Array[] = []\n const stream = response.Body as AsyncIterable<Uint8Array>\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n return ok(Buffer.concat(chunks))\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const response = await s3Client.send(new HeadObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok({\n key,\n size: response.ContentLength || 0,\n contentType: response.ContentType || 'application/octet-stream',\n lastModified: response.LastModified || new Date(),\n etag: response.ETag,\n metadata: response.Metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n const result = await file.head(key)\n if (result.success) {\n return ok(true)\n }\n if (result.error.code === HaiStorageError.NOT_FOUND.code) {\n return ok(false)\n }\n return err(result.error)\n },\n\n async delete(key): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n await s3Client.send(new DeleteObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n if (keys.length === 0) {\n return ok(undefined)\n }\n\n await s3Client.send(new DeleteObjectsCommand({\n Bucket: s3Config.bucket,\n Delete: {\n Objects: keys.map(key => ({ Key: fullKey(key) })),\n },\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const sourceFullPath = fullKey(sourceKey)\n const destFullPath = fullKey(destKey)\n\n await s3Client.send(new CopyObjectCommand({\n Bucket: s3Config.bucket,\n CopySource: `${s3Config.bucket}/${sourceFullPath}`,\n Key: destFullPath,\n ContentType: options.contentType,\n Metadata: options.metadata,\n MetadataDirective: (options.metadata || options.contentType) ? 'REPLACE' : 'COPY',\n }))\n\n return file.head(destKey)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n // 构建完整前缀\n let prefix = s3Config.prefix || ''\n if (options.prefix) {\n prefix = withPrefix(options.prefix, s3Config.prefix)\n }\n\n const response = await s3Client.send(new ListObjectsV2Command({\n Bucket: s3Config.bucket,\n Prefix: prefix || undefined,\n Delimiter: options.delimiter,\n MaxKeys: options.maxKeys,\n ContinuationToken: options.continuationToken,\n }))\n\n const files: FileMetadata[] = (response.Contents || []).map(item => ({\n key: withoutPrefix(item.Key || '', s3Config.prefix),\n size: item.Size || 0,\n contentType: 'application/octet-stream', // ListObjects 不返回 ContentType\n lastModified: item.LastModified || new Date(),\n etag: item.ETag,\n }))\n\n const commonPrefixes = (response.CommonPrefixes || [])\n .map(p => withoutPrefix(p.Prefix || '', s3Config.prefix))\n .filter(p => p.length > 0)\n\n return ok({\n files,\n commonPrefixes,\n nextContinuationToken: response.NextContinuationToken,\n isTruncated: response.IsTruncated || false,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n // 分页列出并逐批删除,每批最多 1000 个对象\n let continuationToken: string | undefined\n\n do {\n const listResult = await dir.list({\n prefix,\n continuationToken,\n maxKeys: 1000,\n })\n\n if (!listResult.success) {\n return err(listResult.error)\n }\n\n const keys = listResult.data.files.map(f => f.key)\n if (keys.length > 0) {\n const deleteResult = await file.deleteMany(keys)\n if (!deleteResult.success) {\n return deleteResult\n }\n }\n\n continuationToken = listResult.data.nextContinuationToken\n } while (continuationToken)\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const command = new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ResponseContentType: options?.responseContentType,\n ResponseContentDisposition: options?.responseContentDisposition,\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUrlFailed'),\n error,\n )\n }\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const command = new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ContentType: options?.contentType || 'application/octet-stream',\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUploadUrlFailed'),\n error,\n )\n }\n },\n\n publicUrl(key): string | null {\n const s3Config = getConfig()\n if (!s3Config.publicUrl) {\n return null\n }\n const fullPath = fullKey(key)\n return `${s3Config.publicUrl.replace(TRAILING_SLASHES_REGEX, '')}/${fullPath}`\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 's3',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 's3') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_s3ConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting S3 provider', {\n bucket: cfg.bucket,\n region: cfg.region,\n endpoint: cfg.endpoint ? sanitizeEndpointUrl(cfg.endpoint) : undefined,\n })\n\n client = new S3Client({\n region: cfg.region,\n endpoint: cfg.endpoint,\n forcePathStyle: cfg.forcePathStyle,\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n })\n\n // 测试连接:尝试列出一个对象\n await client.send(new ListObjectsV2Command({\n Bucket: cfg.bucket,\n MaxKeys: 1,\n }))\n\n logger.info('S3 provider connected', { bucket: cfg.bucket })\n return ok(undefined)\n }\n catch (error) {\n logger.error('S3 provider connect failed', { error })\n config = null\n client = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n if (client) {\n client.destroy()\n client = null\n config = null\n logger.info('S3 provider disconnected')\n }\n },\n\n isConnected(): boolean {\n return client !== null && config !== null\n },\n }\n}\n","/**\n * @h-ai/storage — 存储服务主入口\n *\n * 提供统一的 `storage` 对象,管理运行时状态与生命周期。\n * @module storage-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StorageConfig, StorageConfigInput } from './storage-config.js'\nimport type { DirOperations, FileOperations, PresignOperations, StorageFunctions, StorageProvider } from './storage-types.js'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { createLocalProvider } from './providers/storage-provider-local.js'\nimport { createS3Provider } from './providers/storage-provider-s3.js'\nimport { StorageConfigSchema } from './storage-config.js'\nimport { storageM } from './storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from './storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'main' })\n\n/**\n * 返回对外可见的脱敏配置快照\n *\n * @param config - 当前解析后的内部配置\n */\nfunction sanitizeStorageConfig(config: StorageConfig): StorageConfig {\n return core.sanitize.sanitizeSensitiveFields(config)\n}\n\n// ─── 内部状态 ───\n\n/** 当前使用的存储 Provider 实例(init 后非空,close 后置空) */\nlet currentProvider: StorageProvider | null = null\n\n/** 当前解析后的存储配置(init 后非空,close 后置空) */\nlet currentConfig: StorageConfig | null = null\n\n/** 并发初始化防护标志;防止多次 init() 同时执行导致资源泄漏 */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置类型创建对应的存储 Provider\n *\n * @param config - 经 Schema 校验后的存储配置\n * @returns 对应类型的 Provider 实例(尚未连接)\n * @throws 理论上仅在出现未覆盖分支时抛出异常(正常情况下由 Schema 保证不会发生)\n */\nfunction createProvider(config: StorageConfig): StorageProvider {\n switch (config.type) {\n case 's3': return createS3Provider()\n case 'local': return createLocalProvider()\n default:\n throw new Error(storageM('storage_unsupportedType', { params: { type: (config as StorageConfig).type } }))\n }\n}\n\n// ─── 未初始化占位 ───\n\n/**\n * 创建未初始化状态的错误工具包\n *\n * 当 storage 未调用 init() 就直接使用 file/dir/presign 操作时,\n * 所有方法调用都会返回 NOT_INITIALIZED 错误。\n */\nconst notInitialized = core.module.createNotInitializedKit(\n HaiStorageError.NOT_INITIALIZED,\n () => storageM('storage_notInitialized'),\n)\n\n/** 未初始化时的文件操作占位代理 */\nconst notInitializedFile = notInitialized.proxy<FileOperations>()\n\n/** 未初始化时的目录操作占位代理 */\nconst notInitializedDir = notInitialized.proxy<DirOperations>()\n\n/**\n * 未初始化时的签名 URL 操作占位代理\n *\n * publicUrl 始终返回 null;其余方法返回 NOT_INITIALIZED 错误。\n */\nconst notInitializedPresignBase = notInitialized.proxy<PresignOperations>()\nconst notInitializedPresign = new Proxy(\n notInitializedPresignBase,\n {\n get: (target, prop, receiver) => prop === 'publicUrl' ? () => null : Reflect.get(target, prop, receiver),\n },\n)\n\n// ─── 存储服务对象 ───\n\n/**\n * 存储服务单例\n *\n * 使用前必须先调用 `storage.init()` 初始化,传入 S3 或本地存储配置。\n * 初始化后通过 `storage.file`、`storage.dir`、`storage.presign` 访问各操作接口。\n *\n * @example\n * ```ts\n * import { storage } from '@h-ai/storage'\n *\n * // 初始化\n * await storage.init({ type: 'local', root: '/data/uploads' })\n *\n * // 上传文件\n * await storage.file.put('hello.txt', 'Hello World')\n *\n * // 关闭连接\n * await storage.close()\n * ```\n */\nexport const storage: StorageFunctions = {\n /**\n * 初始化存储连接。\n *\n * 如果当前已有活跃连接,会先自动 close 再重新初始化。\n * 配置会通过 Zod Schema 校验;校验失败或连接异常会返回 `CONNECTION_FAILED`。\n *\n * @param config 存储配置(S3 或本地),支持省略带默认值的字段。\n * @returns 成功时返回 ok(undefined);失败时返回包含错误码和消息的 err。\n */\n async init(config: StorageConfigInput): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致 Provider 泄漏\n if (initInProgress) {\n logger.warn('Storage init already in progress, skipping concurrent call')\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_operationFailed', { params: { error: 'Concurrent initialization detected' } }),\n )\n }\n initInProgress = true\n\n try {\n if (currentProvider) {\n logger.warn('Storage module is already initialized, reinitializing')\n const closeResult = await storage.close()\n if (!closeResult.success) {\n return closeResult\n }\n }\n\n logger.info('Initializing storage module')\n\n const parseResult = StorageConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Storage config validation failed', { error: parseResult.error.message })\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n\n const provider = createProvider(parsed)\n const connectResult = await provider.connect(parsed)\n if (!connectResult.success) {\n logger.error('Storage module initialization failed', {\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n return connectResult\n }\n currentProvider = provider\n currentConfig = parsed\n logger.info('Storage module initialized')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module initialization failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n /** 文件操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get file(): FileOperations { return currentProvider?.file ?? notInitializedFile },\n\n /** 目录操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get dir(): DirOperations { return currentProvider?.dir ?? notInitializedDir },\n\n /** 签名 URL 操作接口。未初始化时 publicUrl 返回 null,其余返回错误 */\n get presign(): PresignOperations { return currentProvider?.presign ?? notInitializedPresign },\n\n /** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */\n get config() {\n return currentConfig ? sanitizeStorageConfig(currentConfig) : null\n },\n\n /** 是否已完成初始化 */\n get isInitialized() { return currentProvider !== null },\n\n /**\n * 关闭存储连接并释放资源\n *\n * 关闭后 file/dir/presign 操作将返回 NOT_INITIALIZED 错误。\n * 重复调用不会报错。\n */\n async close(): Promise<HaiResult<void>> {\n if (!currentProvider) {\n currentConfig = null\n logger.info('Storage module already closed, skipping')\n return ok(undefined)\n }\n\n logger.info('Closing storage module')\n\n try {\n await currentProvider.close()\n logger.info('Storage module closed')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module close failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n currentProvider = null\n currentConfig = null\n }\n },\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/providers/storage-provider-local.ts","../src/providers/storage-provider-s3.ts","../src/storage-main.ts"],"names":["getMimeType","stat","dir","resolve","logger","core","toStorageError","Buffer","ok","err"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * @h-ai/storage — Local Provider 实现\n *\n * 基于本地文件系统的存储 Provider。\n * @module storage-provider-local\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { LocalConfig, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as fsp from 'node:fs/promises'\n\nimport * as path from 'node:path'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport { MIME_TYPE_DEFAULT, MIME_TYPES } from '../storage-mime.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-local' })\nconst PATH_SEGMENT_SPLIT_REGEX = /[\\\\/]+/\n\n// ─── 辅助函数 ───\n\n/**\n * 将 Node.js 文件系统错误转换为 StorageError\n *\n * 映射规则:\n * - ENOENT → NOT_FOUND\n * - EACCES / EPERM → PERMISSION_DENIED\n * - ENOSPC → QUOTA_EXCEEDED\n * - EISDIR → INVALID_PATH\n * - 其他 → IO_ERROR\n *\n * @param error - 原始错误对象\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as NodeJS.ErrnoException\n\n if (e.code === 'ENOENT') {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EACCES' || e.code === 'EPERM') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'ENOSPC') {\n return {\n ...HaiStorageError.QUOTA_EXCEEDED,\n message: storageM('storage_diskSpaceInsufficient'),\n cause: error,\n }\n }\n\n if (e.code === 'EISDIR') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: storageM('storage_pathIsDir', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.code === 'EINVAL') {\n return {\n ...HaiStorageError.INVALID_PATH,\n message: e.message || storageM('storage_pathTraversal'),\n cause: error,\n }\n }\n\n return {\n code: HaiStorageError.IO_ERROR.code,\n message: storageM('storage_ioError', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 根据文件扩展名获取 MIME 类型\n *\n * 使用共享 MIME_TYPES 映射表,未匹配时返回 'application/octet-stream'。\n *\n * @param filePath - 文件路径或文件名\n * @returns MIME 类型字符串\n */\nfunction getMimeType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase().replace('.', '')\n return MIME_TYPES[ext] || MIME_TYPE_DEFAULT\n}\n\n/**\n * 根据文件 stat 信息计算 ETag\n *\n * 使用 \"size-mtime\" 格式,与 head 操作保持一致,无需读取文件内容。\n *\n * @param stat - 文件的 fs.Stats 对象\n * @returns 带双引号的 ETag 字符串,如 '\"a-18f7ec3b4c0\"'\n */\nfunction calculateEtag(stat: fs.Stats): string {\n return `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n}\n\n/**\n * 检查 key 是否指向内部元数据文件(.meta.json)\n *\n * 拦截外部对 .meta.json 文件的直接访问,防止元数据泄漏。\n */\nfunction isMetaFile(key: string): boolean {\n return key.endsWith('.meta.json')\n}\n\n/**\n * 创建非法路径错误(用于统一映射为 INVALID_PATH)\n */\nfunction createInvalidPathError(): NodeJS.ErrnoException {\n const error = new Error(storageM('storage_pathTraversal')) as NodeJS.ErrnoException\n error.code = 'EINVAL'\n return error\n}\n\n/**\n * 判断路径片段中是否包含路径穿越标记(..)\n */\nfunction hasPathTraversalSegment(key: string): boolean {\n const segments = key.split(PATH_SEGMENT_SPLIT_REGEX).filter(Boolean)\n return segments.includes('..')\n}\n\n/**\n * 安全的路径解析,防止路径穿越攻击\n *\n * 处理流程:\n * 1. 拒绝绝对路径、空字节、`..` 路径穿越片段\n * 2. resolve(root, key) 得到完整路径\n * 3. 校验完整路径必须位于 root 内部(或等于 root)\n *\n * @param root - 存储根目录绝对路径\n * @param key - 用户提供的文件键\n * @returns 安全的完整文件路径\n * @throws 当解析后路径超出 root 范围时抛出异常\n */\nfunction safePath(root: string, key: string): string {\n // 拒绝绝对路径输入,防止 Windows 盘符绕过(如 D:\\etc\\passwd)\n if (path.isAbsolute(key)) {\n throw createInvalidPathError()\n }\n\n // 拒绝空字节与路径穿越片段\n if (key.includes('\\0') || hasPathTraversalSegment(key)) {\n throw createInvalidPathError()\n }\n\n const normalized = path.normalize(key)\n const fullPath = path.resolve(root, normalized)\n\n // 确保路径在 root 目录下\n const realRoot = path.resolve(root)\n const inRoot = fullPath === realRoot || fullPath.startsWith(`${realRoot}${path.sep}`)\n\n if (!inRoot) {\n throw createInvalidPathError()\n }\n\n return fullPath\n}\n\n// ─── Local Provider 实现 ───\n\n/**\n * 创建本地存储 Provider\n *\n * 基于本地文件系统实现的 StorageProvider。\n * connect 时会自动创建根目录;文件操作会自动创建中间目录。\n *\n * 已知限制:\n * - list 不支持 continuationToken 分页(仅支持 maxKeys 截断)\n * - presign 返回虚拟 `local://` URL,应用层需自行处理\n * - publicUrl 始终返回 null\n *\n * @returns Local Provider 实例\n */\nexport function createLocalProvider(): StorageProvider {\n let config: LocalConfig | null = null\n let connected = false\n\n // -------------------------------------------------------------------------\n // 辅助方法\n // -------------------------------------------------------------------------\n\n /** 获取当前 Local 配置,未初始化时抛异常 */\n function getConfig(): LocalConfig {\n if (!config) {\n throw new Error(storageM('storage_localNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 转为安全的绝对路径(经 safePath 防穿越处理) */\n function fullPath(key: string): string {\n return safePath(getConfig().root, key)\n }\n\n /**\n * 将输入数据统一转为 Buffer\n *\n * @param data - 字符串、Buffer 或 Uint8Array\n * @returns Buffer 实例\n */\n function toBuffer(data: Buffer | Uint8Array | string): Buffer {\n if (typeof data === 'string') {\n return Buffer.from(data)\n }\n if (data instanceof Buffer) {\n return data\n }\n return Buffer.from(data)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n const buffer = toBuffer(data)\n logger.debug('Putting file', { key, size: buffer.length })\n\n // 确保目录存在\n const dir = path.dirname(filePath)\n await fsp.mkdir(dir, { recursive: true, mode: getConfig().directoryMode })\n\n // 写入文件\n await fsp.writeFile(filePath, buffer, { mode: getConfig().fileMode })\n\n // 获取元数据\n const stat = await fsp.stat(filePath)\n const metadata: FileMetadata = {\n key,\n size: stat.size,\n contentType: options.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据(使用扩展属性或元数据文件)\n if (options.metadata) {\n const metaPath = `${filePath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n logger.warn('Put file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n\n // 检查范围请求\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const stat = await fsp.stat(filePath)\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : stat.size - 1\n\n // 使用流读取指定范围\n return new Promise((resolve) => {\n const chunks: Buffer[] = []\n const stream = fs.createReadStream(filePath, { start, end })\n\n stream.on('data', (chunk: Buffer) => chunks.push(chunk))\n stream.on('end', () => resolve(ok(Buffer.concat(chunks))))\n stream.on('error', error => resolve(err(toStorageError(error, key))))\n })\n }\n\n const data = await fsp.readFile(filePath)\n return ok(data)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n const stat = await fsp.stat(filePath)\n\n if (stat.isDirectory()) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_pathIsDir', { params: { key } }),\n )\n }\n\n // 尝试读取元数据文件\n let customMetadata: { contentType?: string, metadata?: Record<string, string> } = {}\n try {\n const metaPath = `${filePath}.meta.json`\n const metaContent = await fsp.readFile(metaPath, 'utf-8')\n customMetadata = JSON.parse(metaContent)\n }\n catch {\n // 元数据文件不存在,忽略\n }\n\n // 基于文件大小和修改时间生成 ETag(无需读取整个文件)\n const etag = `\"${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}\"`\n\n return ok({\n key,\n size: stat.size,\n contentType: customMetadata.contentType || getMimeType(key),\n lastModified: stat.mtime,\n etag,\n metadata: customMetadata.metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n await fsp.access(filePath, fs.constants.F_OK)\n return ok(true)\n }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(false)\n }\n return err(toStorageError(error, key))\n }\n },\n\n async delete(key): Promise<HaiResult<void>> {\n if (isMetaFile(key)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key } }),\n )\n }\n try {\n const filePath = fullPath(key)\n logger.debug('Deleting file', { key })\n await fsp.unlink(filePath)\n\n // 尝试删除元数据文件\n try {\n await fsp.unlink(`${filePath}.meta.json`)\n }\n catch {\n // 忽略\n }\n\n return ok(undefined)\n }\n catch (error) {\n // 如果文件不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n logger.warn('Delete file failed', { key, error })\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n // 并行删除,避免 await-in-loop(N+1)\n const results = await Promise.allSettled(keys.map(key => file.delete(key)))\n\n const errors: HaiError[] = []\n for (const result of results) {\n if (result.status === 'fulfilled' && !result.value.success) {\n errors.push(result.value.error)\n }\n }\n\n if (errors.length > 0) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_deleteManyFailed', { params: { count: errors.length } }),\n errors,\n )\n }\n\n return ok(undefined)\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n if (isMetaFile(sourceKey) || isMetaFile(destKey)) {\n return err(\n HaiStorageError.INVALID_PATH,\n storageM('storage_metaFileAccess', { params: { key: sourceKey } }),\n sourceKey,\n )\n }\n try {\n const sourcePath = fullPath(sourceKey)\n const destPath = fullPath(destKey)\n logger.debug('Copying file', { sourceKey, destKey })\n\n // 确保目标目录存在\n await fsp.mkdir(path.dirname(destPath), { recursive: true, mode: getConfig().directoryMode })\n\n // 复制文件\n await fsp.copyFile(sourcePath, destPath)\n\n // 获取新文件的元数据(无需读取文件内容,使用 stat 计算 ETag)\n const stat = await fsp.stat(destPath)\n\n const metadata: FileMetadata = {\n key: destKey,\n size: stat.size,\n contentType: options.contentType || getMimeType(destKey),\n lastModified: stat.mtime,\n etag: calculateEtag(stat),\n metadata: options.metadata,\n }\n\n // 存储自定义元数据\n if (options.contentType || options.metadata) {\n const metaPath = `${destPath}.meta.json`\n await fsp.writeFile(metaPath, JSON.stringify({\n contentType: options.contentType,\n metadata: options.metadata,\n }))\n }\n\n return ok(metadata)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const cfg = getConfig()\n const prefix = options.prefix || ''\n const delimiter = options.delimiter || ''\n const maxKeys = options.maxKeys || 1000\n\n const basePath = safePath(cfg.root, prefix)\n const files: FileMetadata[] = []\n const commonPrefixes = new Set<string>()\n\n // 递归读取目录\n async function readDir(dirPath: string): Promise<void> {\n let entries: fs.Dirent[]\n try {\n entries = await fsp.readdir(dirPath, { withFileTypes: true })\n }\n catch {\n return // 目录不存在,返回空\n }\n\n for (const entry of entries) {\n if (files.length >= maxKeys)\n return\n\n const fullEntryPath = path.join(dirPath, entry.name)\n const key = path.relative(cfg.root, fullEntryPath).replace(/\\\\/g, '/')\n\n // 跳过元数据文件\n if (entry.name.endsWith('.meta.json'))\n continue\n\n if (entry.isDirectory()) {\n if (delimiter) {\n // 有分隔符时,目录作为公共前缀\n commonPrefixes.add(`${key}/`)\n }\n else {\n // 无分隔符时,递归遍历\n await readDir(fullEntryPath)\n }\n }\n else if (entry.isFile()) {\n const stat = await fsp.stat(fullEntryPath)\n files.push({\n key,\n size: stat.size,\n contentType: getMimeType(key),\n lastModified: stat.mtime,\n })\n }\n }\n }\n\n await readDir(basePath)\n\n return ok({\n files,\n commonPrefixes: Array.from(commonPrefixes),\n isTruncated: files.length >= maxKeys,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n const dirPath = fullPath(prefix)\n\n // 递归删除目录\n await fsp.rm(dirPath, { recursive: true, force: true })\n\n return ok(undefined)\n }\n catch (error) {\n // 如果目录不存在,视为删除成功\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return ok(undefined)\n }\n return err(toStorageError(error, prefix))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n // 本地存储不支持真正的签名 URL\n // 返回一个带签名参数的虚拟 URL,应用层需要自行处理\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?expires=${expires}&signature=${signature}`)\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n // 同上,返回虚拟 URL\n const expiresIn = options?.expiresIn || 3600\n const expires = Math.floor(Date.now() / 1000) + expiresIn\n const signature = crypto\n .createHash('sha256')\n .update(`put:${key}:${expires}`)\n .digest('hex')\n .slice(0, 16)\n\n return ok(`local://${key}?action=put&expires=${expires}&signature=${signature}`)\n },\n\n publicUrl(_key): string | null {\n // 本地存储不支持公开 URL\n return null\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 'local',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 'local') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_localConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting local provider', { root: cfg.root })\n\n // 确保根目录存在\n await fsp.mkdir(cfg.root, { recursive: true, mode: cfg.directoryMode })\n\n connected = true\n logger.info('Local provider connected', { root: cfg.root })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Local provider connect failed', { error })\n config = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n logger.info('Local provider disconnected')\n config = null\n connected = false\n },\n\n isConnected(): boolean {\n return connected\n },\n }\n}\n","/**\n * @h-ai/storage — S3 Provider 实现\n *\n * 基于 AWS SDK v3 实现的 S3 协议存储 Provider。\n * 支持 AWS S3、MinIO、阿里云 OSS 等兼容 S3 协议的存储服务。\n * @module storage-provider-s3\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { S3Config, StorageConfig } from '../storage-config.js'\nimport type { DirOperations, FileMetadata, FileOperations, ListResult, PresignOperations, StorageProvider } from '../storage-types.js'\nimport { Buffer } from 'node:buffer'\nimport {\n CopyObjectCommand,\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n} from '@aws-sdk/client-s3'\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { storageM } from '../storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from '../storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'provider-s3' })\nconst TRAILING_SLASHES_REGEX = /\\/+$/\nconst LEADING_SLASHES_REGEX = /^\\/+/\n\n/**\n * 脱敏 S3 endpoint,避免日志暴露认证信息\n */\nfunction sanitizeEndpointUrl(url: string): string {\n try {\n const parsed = new URL(url)\n if (parsed.username)\n parsed.username = '***'\n if (parsed.password)\n parsed.password = '***'\n return parsed.toString()\n }\n catch {\n return '(invalid url)'\n }\n}\n\n// ─── 辅助函数 ───\n\n/**\n * 将 S3 SDK 错误转换为 StorageError\n *\n * 映射规则:\n * - NoSuchKey / NotFound / 404 → NOT_FOUND\n * - AccessDenied → PERMISSION_DENIED\n * - NoSuchBucket → CONFIG_ERROR\n * - NetworkError / ECONNREFUSED → NETWORK_ERROR\n * - 其他 → OPERATION_FAILED\n *\n * @param error - AWS SDK 抛出的原始错误\n * @param key - 相关文件键(可选,用于错误消息)\n * @returns 统一的 StorageError\n */\nfunction toStorageError(error: unknown, key?: string): HaiError {\n const e = error as { name?: string, message?: string, Code?: string }\n\n // S3 特定错误处理\n if (e.name === 'NoSuchKey' || e.Code === 'NoSuchKey'\n || e.name === 'NotFound' || e.Code === 'NotFound'\n || e.name === '404' || (e as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode === 404) {\n return {\n ...HaiStorageError.NOT_FOUND,\n message: storageM('storage_fileNotFound', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'AccessDenied' || e.Code === 'AccessDenied') {\n return {\n ...HaiStorageError.PERMISSION_DENIED,\n message: storageM('storage_permissionDenied', { params: { key: key ?? '' } }),\n cause: error,\n }\n }\n\n if (e.name === 'NoSuchBucket' || e.Code === 'NoSuchBucket') {\n return {\n ...HaiStorageError.CONFIG_ERROR,\n message: storageM('storage_bucketNotExist'),\n cause: error,\n }\n }\n\n // 网络相关错误\n if (e.name === 'NetworkError' || e.name?.includes('ECONNREFUSED')) {\n return {\n ...HaiStorageError.NETWORK_ERROR,\n message: storageM('storage_networkError'),\n cause: error,\n }\n }\n\n return {\n ...HaiStorageError.OPERATION_FAILED,\n message: storageM('storage_operationFailed', { params: { error: e.message ?? '' } }),\n cause: error,\n }\n}\n\n/**\n * 为 key 添加配置中的路径前缀\n *\n * @param key - 用户提供的文件键\n * @param prefix - 配置中的前缀(可能为空)\n * @returns 拼接后的完整 S3 key\n */\nfunction withPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n return `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/${key.replace(LEADING_SLASHES_REGEX, '')}`\n}\n\n/**\n * 从 S3 key 中移除配置前缀,还原为用户视角的文件键\n *\n * @param key - 包含前缀的完整 S3 key\n * @param prefix - 配置中的前缀\n * @returns 去除前缀后的文件键\n */\nfunction withoutPrefix(key: string, prefix: string): string {\n if (!prefix)\n return key\n const normalizedPrefix = `${prefix.replace(TRAILING_SLASHES_REGEX, '')}/`\n if (key.startsWith(normalizedPrefix)) {\n return key.slice(normalizedPrefix.length)\n }\n return key\n}\n\n// ─── S3 Provider 实现 ───\n\n/**\n * 创建 S3 存储 Provider\n *\n * 基于 AWS SDK v3 实现的 StorageProvider。\n * connect 时会创建 S3Client 并通过 ListObjectsV2 验证连接。\n * 支持配置 prefix 重定向所有 key,对用户透明。\n *\n * 实现说明:\n * - copy 时若指定了 contentType 或 metadata,使用 MetadataDirective='REPLACE'\n * - dir.delete 通过先 list 再批量删除实现\n * - presign 使用 @aws-sdk/s3-request-presigner 签名\n *\n * @returns S3 Provider 实例\n */\nexport function createS3Provider(): StorageProvider {\n let client: S3Client | null = null\n let config: S3Config | null = null\n\n // -------------------------------------------------------------------------\n // 内部辅助\n // -------------------------------------------------------------------------\n\n /** 获取当前 S3Client,未初始化时抛异常 */\n function getClient(): S3Client {\n if (!client) {\n throw new Error(storageM('storage_s3ClientNotInitialized'))\n }\n return client\n }\n\n /** 获取当前 S3 配置,未初始化时抛异常 */\n function getConfig(): S3Config {\n if (!config) {\n throw new Error(storageM('storage_s3ConfigNotInitialized'))\n }\n return config\n }\n\n /** 将用户 key 拼接配置前缀,得到完整 S3 对象键 */\n function fullKey(key: string): string {\n return withPrefix(key, getConfig().prefix)\n }\n\n // -------------------------------------------------------------------------\n // 文件操作实现\n // -------------------------------------------------------------------------\n\n const file: FileOperations = {\n async put(key, data, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const body = typeof data === 'string' ? Buffer.from(data) : data\n\n await s3Client.send(new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Body: body,\n ContentType: options.contentType,\n Metadata: options.metadata,\n CacheControl: options.cacheControl,\n ContentDisposition: options.contentDisposition,\n }))\n\n // 获取上传后的元数据\n const headResult = await file.head(key)\n if (headResult.success) {\n return headResult\n }\n\n // 如果获取元数据失败,返回基本信息\n return ok({\n key,\n size: body.length,\n contentType: options.contentType || 'application/octet-stream',\n lastModified: new Date(),\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async get(key, options = {}): Promise<HaiResult<Buffer>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n let Range: string | undefined\n if (options.rangeStart !== undefined || options.rangeEnd !== undefined) {\n const start = options.rangeStart ?? 0\n const end = options.rangeEnd !== undefined ? options.rangeEnd : ''\n Range = `bytes=${start}-${end}`\n }\n\n const response = await s3Client.send(new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n Range,\n }))\n\n if (!response.Body) {\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_responseBodyEmpty'),\n )\n }\n\n // 将流转换为 Buffer\n const chunks: Uint8Array[] = []\n const stream = response.Body as AsyncIterable<Uint8Array>\n for await (const chunk of stream) {\n chunks.push(chunk)\n }\n\n return ok(Buffer.concat(chunks))\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async head(key): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const response = await s3Client.send(new HeadObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok({\n key,\n size: response.ContentLength || 0,\n contentType: response.ContentType || 'application/octet-stream',\n lastModified: response.LastModified || new Date(),\n etag: response.ETag,\n metadata: response.Metadata,\n })\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async exists(key): Promise<HaiResult<boolean>> {\n const result = await file.head(key)\n if (result.success) {\n return ok(true)\n }\n if (result.error.code === HaiStorageError.NOT_FOUND.code) {\n return ok(false)\n }\n return err(result.error)\n },\n\n async delete(key): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n await s3Client.send(new DeleteObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error, key))\n }\n },\n\n async deleteMany(keys): Promise<HaiResult<void>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n if (keys.length === 0) {\n return ok(undefined)\n }\n\n await s3Client.send(new DeleteObjectsCommand({\n Bucket: s3Config.bucket,\n Delete: {\n Objects: keys.map(key => ({ Key: fullKey(key) })),\n },\n }))\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async copy(sourceKey, destKey, options = {}): Promise<HaiResult<FileMetadata>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const sourceFullPath = fullKey(sourceKey)\n const destFullPath = fullKey(destKey)\n\n await s3Client.send(new CopyObjectCommand({\n Bucket: s3Config.bucket,\n CopySource: `${s3Config.bucket}/${sourceFullPath}`,\n Key: destFullPath,\n ContentType: options.contentType,\n Metadata: options.metadata,\n MetadataDirective: (options.metadata || options.contentType) ? 'REPLACE' : 'COPY',\n }))\n\n return file.head(destKey)\n }\n catch (error) {\n return err(toStorageError(error, sourceKey))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 目录操作实现\n // -------------------------------------------------------------------------\n\n const dir: DirOperations = {\n async list(options = {}): Promise<HaiResult<ListResult>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n\n // 构建完整前缀\n let prefix = s3Config.prefix || ''\n if (options.prefix) {\n prefix = withPrefix(options.prefix, s3Config.prefix)\n }\n\n const response = await s3Client.send(new ListObjectsV2Command({\n Bucket: s3Config.bucket,\n Prefix: prefix || undefined,\n Delimiter: options.delimiter,\n MaxKeys: options.maxKeys,\n ContinuationToken: options.continuationToken,\n }))\n\n const files: FileMetadata[] = (response.Contents || []).map(item => ({\n key: withoutPrefix(item.Key || '', s3Config.prefix),\n size: item.Size || 0,\n contentType: 'application/octet-stream', // ListObjects 不返回 ContentType\n lastModified: item.LastModified || new Date(),\n etag: item.ETag,\n }))\n\n const commonPrefixes = (response.CommonPrefixes || [])\n .map(p => withoutPrefix(p.Prefix || '', s3Config.prefix))\n .filter(p => p.length > 0)\n\n return ok({\n files,\n commonPrefixes,\n nextContinuationToken: response.NextContinuationToken,\n isTruncated: response.IsTruncated || false,\n })\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n\n async delete(prefix): Promise<HaiResult<void>> {\n try {\n // 分页列出并逐批删除,每批最多 1000 个对象\n let continuationToken: string | undefined\n\n do {\n const listResult = await dir.list({\n prefix,\n continuationToken,\n maxKeys: 1000,\n })\n\n if (!listResult.success) {\n return err(listResult.error)\n }\n\n const keys = listResult.data.files.map(f => f.key)\n if (keys.length > 0) {\n const deleteResult = await file.deleteMany(keys)\n if (!deleteResult.success) {\n return deleteResult\n }\n }\n\n continuationToken = listResult.data.nextContinuationToken\n } while (continuationToken)\n\n return ok(undefined)\n }\n catch (error) {\n return err(toStorageError(error))\n }\n },\n }\n\n // -------------------------------------------------------------------------\n // 签名 URL 操作实现\n // -------------------------------------------------------------------------\n\n const presign: PresignOperations = {\n async getUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const command = new GetObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ResponseContentType: options?.responseContentType,\n ResponseContentDisposition: options?.responseContentDisposition,\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUrlFailed'),\n error,\n )\n }\n },\n\n async putUrl(key, options?): Promise<HaiResult<string>> {\n try {\n const s3Client = getClient()\n const s3Config = getConfig()\n const fullPath = fullKey(key)\n\n const command = new PutObjectCommand({\n Bucket: s3Config.bucket,\n Key: fullPath,\n ContentType: options?.contentType || 'application/octet-stream',\n })\n\n const url = await getSignedUrl(s3Client, command, {\n expiresIn: options?.expiresIn || 3600,\n })\n\n return ok(url)\n }\n catch (error) {\n return err(\n HaiStorageError.PRESIGN_FAILED,\n storageM('storage_presignUploadUrlFailed'),\n error,\n )\n }\n },\n\n publicUrl(key): string | null {\n const s3Config = getConfig()\n if (!s3Config.publicUrl) {\n return null\n }\n const fullPath = fullKey(key)\n return `${s3Config.publicUrl.replace(TRAILING_SLASHES_REGEX, '')}/${fullPath}`\n },\n }\n\n // -------------------------------------------------------------------------\n // Provider 接口实现\n // -------------------------------------------------------------------------\n\n return {\n name: 's3',\n\n file,\n dir,\n presign,\n\n async connect(cfg: StorageConfig): Promise<HaiResult<void>> {\n if (cfg.type !== 's3') {\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_s3ConfigTypeError'),\n )\n }\n\n try {\n config = cfg\n logger.info('Connecting S3 provider', {\n bucket: cfg.bucket,\n region: cfg.region,\n endpoint: cfg.endpoint ? sanitizeEndpointUrl(cfg.endpoint) : undefined,\n })\n\n client = new S3Client({\n region: cfg.region,\n endpoint: cfg.endpoint,\n forcePathStyle: cfg.forcePathStyle,\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n })\n\n // 测试连接:尝试列出一个对象\n await client.send(new ListObjectsV2Command({\n Bucket: cfg.bucket,\n MaxKeys: 1,\n }))\n\n logger.info('S3 provider connected', { bucket: cfg.bucket })\n return ok(undefined)\n }\n catch (error) {\n logger.error('S3 provider connect failed', { error })\n config = null\n client = null\n return err(toStorageError(error))\n }\n },\n\n async close(): Promise<void> {\n if (client) {\n client.destroy()\n client = null\n config = null\n logger.info('S3 provider disconnected')\n }\n },\n\n isConnected(): boolean {\n return client !== null && config !== null\n },\n }\n}\n","/**\n * @h-ai/storage — 存储服务主入口\n *\n * 提供统一的 `storage` 对象,管理运行时状态与生命周期。\n * @module storage-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StorageConfig, StorageConfigInput } from './storage-config.js'\nimport type { DirOperations, FileOperations, PresignOperations, StorageFunctions, StorageProvider } from './storage-types.js'\n\nimport { core, err, ok } from '@h-ai/core'\n\nimport { createLocalProvider } from './providers/storage-provider-local.js'\nimport { createS3Provider } from './providers/storage-provider-s3.js'\nimport { StorageConfigSchema } from './storage-config.js'\nimport { storageM } from './storage-i18n.js'\nimport {\n\n HaiStorageError,\n\n} from './storage-types.js'\n\nconst logger = core.logger.child({ module: 'storage', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 当前使用的存储 Provider 实例(init 后非空,close 后置空) */\nlet currentProvider: StorageProvider | null = null\n\n/** 当前解析后的存储配置(init 后非空,close 后置空) */\nlet currentConfig: StorageConfig | null = null\n\n/** 并发初始化防护标志;防止多次 init() 同时执行导致资源泄漏 */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置类型创建对应的存储 Provider\n *\n * @param config - 经 Schema 校验后的存储配置\n * @returns 对应类型的 Provider 实例(尚未连接)\n * @throws 理论上仅在出现未覆盖分支时抛出异常(正常情况下由 Schema 保证不会发生)\n */\nfunction createProvider(config: StorageConfig): StorageProvider {\n switch (config.type) {\n case 's3': return createS3Provider()\n case 'local': return createLocalProvider()\n default:\n throw new Error(storageM('storage_unsupportedType', { params: { type: (config as StorageConfig).type } }))\n }\n}\n\n// ─── 未初始化占位 ───\n\n/**\n * 创建未初始化状态的错误工具包\n *\n * 当 storage 未调用 init() 就直接使用 file/dir/presign 操作时,\n * 所有方法调用都会返回 NOT_INITIALIZED 错误。\n */\nconst notInitialized = core.module.createNotInitializedKit(\n HaiStorageError.NOT_INITIALIZED,\n () => storageM('storage_notInitialized'),\n)\n\n/** 未初始化时的文件操作占位代理 */\nconst notInitializedFile = notInitialized.proxy<FileOperations>()\n\n/** 未初始化时的目录操作占位代理 */\nconst notInitializedDir = notInitialized.proxy<DirOperations>()\n\n/**\n * 未初始化时的签名 URL 操作占位代理\n *\n * publicUrl 始终返回 null;其余方法返回 NOT_INITIALIZED 错误。\n */\nconst notInitializedPresignBase = notInitialized.proxy<PresignOperations>()\nconst notInitializedPresign = new Proxy(\n notInitializedPresignBase,\n {\n get: (target, prop, receiver) => prop === 'publicUrl' ? () => null : Reflect.get(target, prop, receiver),\n },\n)\n\n// ─── 存储服务对象 ───\n\n/**\n * 存储服务单例\n *\n * 使用前必须先调用 `storage.init()` 初始化,传入 S3 或本地存储配置。\n * 初始化后通过 `storage.file`、`storage.dir`、`storage.presign` 访问各操作接口。\n *\n * @example\n * ```ts\n * import { storage } from '@h-ai/storage'\n *\n * // 初始化\n * await storage.init({ type: 'local', root: '/data/uploads' })\n *\n * // 上传文件\n * await storage.file.put('hello.txt', 'Hello World')\n *\n * // 关闭连接\n * await storage.close()\n * ```\n */\nexport const storage: StorageFunctions = {\n /**\n * 初始化存储连接。\n *\n * 如果当前已有活跃连接,会先自动 close 再重新初始化。\n * 配置会通过 Zod Schema 校验;校验失败或连接异常会返回 `CONNECTION_FAILED`。\n *\n * @param config 存储配置(S3 或本地),支持省略带默认值的字段。\n * @returns 成功时返回 ok(undefined);失败时返回包含错误码和消息的 err。\n */\n async init(config: StorageConfigInput): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致 Provider 泄漏\n if (initInProgress) {\n logger.warn('Storage init already in progress, skipping concurrent call')\n return err(\n HaiStorageError.OPERATION_FAILED,\n storageM('storage_operationFailed', { params: { error: 'Concurrent initialization detected' } }),\n )\n }\n initInProgress = true\n\n try {\n if (currentProvider) {\n logger.warn('Storage module is already initialized, reinitializing')\n const closeResult = await storage.close()\n if (!closeResult.success) {\n return closeResult\n }\n }\n\n logger.info('Initializing storage module')\n\n const parseResult = StorageConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Storage config validation failed', { error: parseResult.error.message })\n return err(\n HaiStorageError.CONFIG_ERROR,\n storageM('storage_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n\n const provider = createProvider(parsed)\n const connectResult = await provider.connect(parsed)\n if (!connectResult.success) {\n logger.error('Storage module initialization failed', {\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n return connectResult\n }\n currentProvider = provider\n currentConfig = parsed\n logger.info('Storage module initialized')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module initialization failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n /** 文件操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get file(): FileOperations { return currentProvider?.file ?? notInitializedFile },\n\n /** 目录操作接口。未初始化时所有方法返回 NOT_INITIALIZED 错误 */\n get dir(): DirOperations { return currentProvider?.dir ?? notInitializedDir },\n\n /** 签名 URL 操作接口。未初始化时 publicUrl 返回 null,其余返回错误 */\n get presign(): PresignOperations { return currentProvider?.presign ?? notInitializedPresign },\n\n /** 当前脱敏后的存储配置快照;未初始化或已关闭时为 null */\n get config() {\n return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null\n },\n\n /** 是否已完成初始化 */\n get isInitialized() { return currentProvider !== null },\n\n /**\n * 关闭存储连接并释放资源\n *\n * 关闭后 file/dir/presign 操作将返回 NOT_INITIALIZED 错误。\n * 重复调用不会报错。\n */\n async close(): Promise<HaiResult<void>> {\n if (!currentProvider) {\n currentConfig = null\n logger.info('Storage module already closed, skipping')\n return ok(undefined)\n }\n\n logger.info('Closing storage module')\n\n try {\n await currentProvider.close()\n logger.info('Storage module closed')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Storage module close failed', { error })\n return err(\n HaiStorageError.CONNECTION_FAILED,\n storageM('storage_operationFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n finally {\n currentProvider = null\n currentConfig = null\n }\n },\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/storage",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.21",
|
|
5
5
|
"description": "Hai Framework object storage module for S3-compatible and local backends.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"exports": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@aws-sdk/client-s3": "^3.723.0",
|
|
30
30
|
"@aws-sdk/s3-request-presigner": "^3.723.0",
|
|
31
31
|
"zod": "^4.4.3",
|
|
32
|
-
"@h-ai/core": "0.1.0-alpha.
|
|
32
|
+
"@h-ai/core": "0.1.0-alpha.21"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^25.9.1",
|