refactor: remove deprecated storage functions and streamline storage management

- Deleted the builder-storage.mdc file, which contained outdated documentation for the storage abstraction layer.
- Removed deprecated functions related to S3 operations from the storage adapters, encouraging the use of the new StorageManager API.
- Updated various components to utilize the StorageManager for file retrieval and URL generation, enhancing consistency and maintainability across the codebase.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-06-23 12:04:19 +08:00
parent 3b0c52ed82
commit 90e712373b
11 changed files with 19 additions and 254 deletions

View File

@@ -1,128 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# 存储抽象层
本模块使用适配器模式提供了统一的存储接口,支持多种存储提供商。目前已实现 S3 存储提供商,后续可以方便地扩展其他存储服务。
## 架构设计
- **`interfaces.ts`**: 定义通用的存储接口和数据结构
- **`providers/`**: 具体的存储提供商实现
- **`s3-provider.ts`**: S3 存储提供商实现
- **`factory.ts`**: 存储提供商工厂,负责创建具体的存储实例
- **`manager.ts`**: 存储管理器,提供统一的存储操作接口
- **`adapters.ts`**: 适配器函数,保持与原有 API 的兼容性
## 使用方式
### 推荐方式:使用 StorageManager
```typescript
import { defaultStorageManager, StorageManager } from '@/core/storage'
// 使用默认存储管理器(基于环境变量配置)
const buffer = await defaultStorageManager.getFile('path/to/image.jpg')
const images = await defaultStorageManager.listImages()
const allFiles = await defaultStorageManager.listAllFiles()
const publicUrl = defaultStorageManager.generatePublicUrl('path/to/image.jpg')
const livePhotos = await defaultStorageManager.detectLivePhotos()
// 或者创建自定义配置的存储管理器
const customManager = new StorageManager({
provider: 's3',
bucket: 'my-bucket',
region: 'us-east-1',
endpoint: 'https://s3.amazonaws.com',
prefix: 'photos/',
customDomain: 'https://cdn.example.com'
})
```
### 兼容性方式:使用原有 API
```typescript
// 这些函数仍然可用,但标记为 deprecated
import {
getImageFromS3,
listImagesFromS3,
listAllFilesFromS3,
detectLivePhotos,
generateS3Url
} from '@/core/s3/operations'
const buffer = await getImageFromS3('path/to/image.jpg')
const images = await listImagesFromS3()
```
### 直接使用存储提供商
```typescript
import { S3StorageProvider } from '@/core/storage'
const s3Provider = new S3StorageProvider({
provider: 's3',
bucket: 'my-bucket',
region: 'us-east-1',
// ... 其他配置
})
const buffer = await s3Provider.getFile('path/to/image.jpg')
```
## 扩展新的存储提供商
1. 实现 `StorageProvider` 接口:
```typescript
import { StorageProvider, StorageObject } from '@/core/storage/interfaces'
export class MyStorageProvider implements StorageProvider {
async getFile(key: string, logger?: Logger['s3']): Promise<Buffer | null> {
// 实现文件获取逻辑
}
async listImages(): Promise<StorageObject[]> {
// 实现图片列表逻辑
}
// ... 实现其他接口方法
}
```
2. 在工厂类中注册新的提供商:
```typescript
// 在 factory.ts 中添加新的 case
case 'my-storage':
return new MyStorageProvider(config)
```
3. 更新 `StorageConfig` 接口的 `provider` 类型。
## 优势
1. **解耦**: 业务逻辑与具体存储实现分离
2. **可扩展**: 轻松添加新的存储提供商
3. **统一接口**: 所有存储操作使用相同的 API
4. **向后兼容**: 保持原有 API 的兼容性
5. **类型安全**: 完整的 TypeScript 类型支持
6. **配置灵活**: 支持多种配置方式
## 迁移指南
现有代码可以继续使用原有的 S3 函数,无需立即修改。建议在新代码中使用 `StorageManager` API并逐步迁移现有代码。
旧代码:
```typescript
import { getImageFromS3 } from '@/core/s3/operations'
const buffer = await getImageFromS3('key')
```
新代码:
```typescript
import { defaultStorageManager } from '@/core/storage'
const buffer = await defaultStorageManager.getFile('key')
```

View File

@@ -9,4 +9,5 @@ i18n 编写规范。
2. 使用扁平键。使用 `.` 方式分割。不能使用 object 形式嵌套。
3. 对单复数敏感的语言应该区分,使用 `_one` 和 `_other` 的形式。
4. 在 build 阶段,扁平化的点分隔键(如 'exif.custom.rendered.custom')会自动转换为嵌套的 object 对象,因此可能引发冲突。例如,'exif.custom.rendered.custom' 会与 'exif.custom.rendered' 发生冲突。请避免使用此类点分隔的扁平键。
5. @locales 位于根目录,需要同时处理已有的全部的语言
5. @locales 位于根目录,需要同时处理已有的全部的语言
6. 你需要修改 [en.json](mdc:locales/app/en.json) 然后再修改 [zh-CN.json](mdc:locales/app/zh-CN.json) 和其他语言。因为 eslint 会自动删除其他 json 中的 en 中没有的 key。

View File

@@ -26,7 +26,7 @@ export interface BuilderOptions {
concurrencyLimit?: number // 可选,如果未提供则使用配置文件中的默认值
}
export class PhotoGalleryBuilder {
class PhotoGalleryBuilder {
private storageManager: StorageManager
private config: BuilderConfig

View File

@@ -1,2 +1,2 @@
export type { BuilderOptions } from './builder.js'
export { defaultBuilder, PhotoGalleryBuilder } from './builder.js'
export { defaultBuilder } from './builder.js'

View File

@@ -1,9 +1,5 @@
// 主要构建器
export {
type BuilderOptions,
defaultBuilder,
PhotoGalleryBuilder,
} from './builder/index.js'
export { type BuilderOptions, defaultBuilder } from './builder/index.js'
export type { StorageConfig } from './storage/interfaces.js'
// 日志系统
export { type Logger, logger, type WorkerLogger } from './logger/index.js'
@@ -33,11 +29,6 @@ export {
thumbnailExists,
} from './image/thumbnail.js'
export { s3Client } from './s3/client.js'
export {
generateS3Url,
getImageFromS3,
listImagesFromS3,
} from './s3/operations.js'
// 照片处理
export { extractPhotoInfo } from './photo/info-extractor.js'

View File

@@ -1,13 +1,13 @@
import type { _Object } from '@aws-sdk/client-s3'
import sharp from 'sharp'
import { defaultBuilder } from '../builder/builder.js'
import {
convertBmpToJpegSharpInstance,
getImageMetadataWithSharp,
isBitmap,
preprocessImageBuffer,
} from '../image/processor.js'
import { defaultStorageManager } from '../storage/manager.js'
import type { PhotoManifestItem } from '../types/photo.js'
import {
processExifData,
@@ -46,7 +46,9 @@ export async function preprocessImage(
try {
// 获取图片数据
const rawImageBuffer = await defaultStorageManager.getFile(photoKey)
const rawImageBuffer = await defaultBuilder
.getStorageManager()
.getFile(photoKey)
if (!rawImageBuffer) {
loggers.image.error(`无法获取图片数据:${photoKey}`)
return null
@@ -199,7 +201,9 @@ export async function executePhotoProcessingPipeline(
dateTaken: photoInfo.dateTaken,
views: photoInfo.views,
tags: photoInfo.tags,
originalUrl: defaultStorageManager.generatePublicUrl(photoKey),
originalUrl: defaultBuilder
.getStorageManager()
.generatePublicUrl(photoKey),
thumbnailUrl: thumbnailResult.thumbnailUrl,
blurhash: thumbnailResult.blurhash,
width: metadata.width,

View File

@@ -1,7 +1,7 @@
import type { _Object } from '@aws-sdk/client-s3'
import { defaultBuilder } from '../builder/builder.js'
import type { StorageObject } from '../storage/interfaces.js'
import { defaultStorageManager } from '../storage/manager.js'
import { getGlobalLoggers } from './logger-adapter.js'
export interface LivePhotoResult {
@@ -40,7 +40,9 @@ export function processLivePhoto(
return { isLivePhoto: false }
}
const livePhotoVideoUrl = defaultStorageManager.generatePublicUrl(videoKey)
const livePhotoVideoUrl = defaultBuilder
.getStorageManager()
.generatePublicUrl(videoKey)
loggers.image.info(`📱 检测到 Live Photo${photoKey} -> ${videoKey}`)

View File

@@ -1,79 +0,0 @@
import type { _Object } from '@aws-sdk/client-s3'
import type { Logger } from '../logger/index.js'
import type { StorageObject } from './interfaces.js'
import { defaultStorageManager } from './manager.js'
// 将 StorageObject 转换为 _Object 以保持兼容性
function convertStorageObjectToS3Object(storageObject: StorageObject): _Object {
return {
Key: storageObject.key,
Size: storageObject.size,
LastModified: storageObject.lastModified,
ETag: storageObject.etag,
}
}
/**
* 从 S3 获取图片(兼容性函数)
* @deprecated 推荐使用 defaultStorageManager.getFile()
*/
export async function getImageFromS3(
key: string,
s3Logger?: Logger['s3'],
): Promise<Buffer | null> {
return defaultStorageManager.getFile(key, s3Logger)
}
/**
* 列出 S3 中的所有图片文件(兼容性函数)
* @deprecated 推荐使用 defaultStorageManager.listImages()
*/
export async function listImagesFromS3(): Promise<_Object[]> {
const storageObjects = await defaultStorageManager.listImages()
return storageObjects.map((obj) => convertStorageObjectToS3Object(obj))
}
/**
* 列出 S3 中的所有文件(兼容性函数)
* @deprecated 推荐使用 defaultStorageManager.listAllFiles()
*/
export async function listAllFilesFromS3(): Promise<_Object[]> {
const storageObjects = await defaultStorageManager.listAllFiles()
return storageObjects.map((obj) => convertStorageObjectToS3Object(obj))
}
/**
* 检测 live photo 配对(兼容性函数)
* @deprecated 推荐使用 defaultStorageManager.detectLivePhotos()
*/
export function detectLivePhotos(allObjects: _Object[]): Map<string, _Object> {
// 转换 _Object 数组为 StorageObject 数组
const storageObjects: StorageObject[] = allObjects.map((obj) => ({
key: obj.Key || '',
size: obj.Size,
lastModified: obj.LastModified,
etag: obj.ETag,
}))
// 使用存储管理器检测 Live Photos
const livePhotoMap = defaultStorageManager
.getProvider()
.detectLivePhotos(storageObjects)
// 转换回 _Object 格式
const result = new Map<string, _Object>()
for (const [key, storageObject] of livePhotoMap) {
result.set(key, convertStorageObjectToS3Object(storageObject))
}
return result
}
/**
* 生成 S3 公共 URL兼容性函数
* @deprecated 推荐使用 defaultStorageManager.generatePublicUrl()
*/
export function generateS3Url(key: string): string {
return defaultStorageManager.generatePublicUrl(key)
}

View File

@@ -1,5 +1,3 @@
import { env } from '@env'
import type { StorageConfig, StorageProvider } from './interfaces'
import { GitHubStorageProvider } from './providers/github-provider.js'
import { S3StorageProvider } from './providers/s3-provider.js'
@@ -20,23 +18,4 @@ export class StorageFactory {
}
}
}
/**
* 基于环境变量创建默认的存储提供商
* @returns 默认存储提供商实例
*/
static createDefaultProvider(): StorageProvider {
const config: StorageConfig = {
provider: 's3',
bucket: env.S3_BUCKET_NAME,
region: env.S3_REGION,
endpoint: env.S3_ENDPOINT,
accessKeyId: env.S3_ACCESS_KEY_ID,
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
prefix: env.S3_PREFIX,
customDomain: env.S3_CUSTOM_DOMAIN,
}
return this.createProvider(config)
}
}

View File

@@ -9,7 +9,7 @@ export type {
export { StorageFactory } from './factory.js'
// 导出管理器
export { defaultStorageManager, StorageManager } from './manager.js'
export { StorageManager } from './manager.js'
// 导出具体提供商(如果需要直接使用)
export { GitHubStorageProvider } from './providers/github-provider.js'

View File

@@ -9,10 +9,8 @@ import type {
export class StorageManager {
private provider: StorageProvider
constructor(config?: StorageConfig) {
this.provider = config
? StorageFactory.createProvider(config)
: StorageFactory.createDefaultProvider()
constructor(config: StorageConfig) {
this.provider = StorageFactory.createProvider(config)
}
/**
@@ -78,6 +76,3 @@ export class StorageManager {
this.provider = StorageFactory.createProvider(config)
}
}
// 导出默认的存储管理器实例
export const defaultStorageManager = new StorageManager()