Skip to content

Commit e8f967a

Browse files
committed
refactor(s3): replace AWS SDK S3 types with custom S3ObjectLike interface
- Removed dependencies on AWS SDK's S3 types in favor of a custom S3ObjectLike interface for better type management. - Updated various services and components to utilize the new S3ObjectLike type, enhancing code clarity and maintainability. - Introduced a SimpleS3Client interface to streamline S3 client interactions and improve fetch handling. - Added fast-xml-parser for XML response parsing in S3 operations, enhancing object listing functionality. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 9f436c6 commit e8f967a

File tree

13 files changed

+404
-275
lines changed

13 files changed

+404
-275
lines changed

be/apps/core/src/modules/content/photo/builder/photo-builder.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
PhotoManifestItem,
55
PhotoProcessingContext,
66
PhotoProcessorOptions,
7+
S3ObjectLike,
78
StorageConfig,
89
StorageObject,
910
} from '@afilmory/builder'
@@ -19,7 +20,6 @@ import {
1920
createStorageKeyNormalizer,
2021
runWithPhotoExecutionContext,
2122
} from '@afilmory/builder/photo/index.js'
22-
import type { _Object } from '@aws-sdk/client-s3'
2323
import { logger as coreLogger } from 'core/helpers/logger.helper'
2424
import { injectable } from 'tsyringe'
2525

@@ -126,7 +126,7 @@ export class PhotoBuilderService {
126126
}
127127
}
128128

129-
private toLegacyObject(object: StorageObject): _Object {
129+
private toLegacyObject(object: StorageObject): S3ObjectLike {
130130
return {
131131
Key: object.key,
132132
Size: object.size,
@@ -135,12 +135,12 @@ export class PhotoBuilderService {
135135
}
136136
}
137137

138-
private toLegacyLivePhotoMap(livePhotoMap?: Map<string, StorageObject>): Map<string, _Object> {
138+
private toLegacyLivePhotoMap(livePhotoMap?: Map<string, StorageObject>): Map<string, S3ObjectLike> {
139139
if (!livePhotoMap) {
140140
return new Map()
141141
}
142142

143-
const result = new Map<string, _Object>()
143+
const result = new Map<string, S3ObjectLike>()
144144

145145
for (const [key, value] of livePhotoMap) {
146146
result.set(key, this.toLegacyObject(value))

be/apps/dashboard/src/modules/photos/components/sync/PhotoSyncResultPanel.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,14 @@ export function PhotoSyncResultPanel({
342342
type="button"
343343
size="xs"
344344
variant={selectedActionType === filter.type ? 'primary' : 'ghost'}
345-
className="gap-1"
345+
className="gap-1 group"
346+
data-selected={selectedActionType === filter.type}
346347
onClick={() => handleSelectActionType(filter.type)}
347348
>
348349
<span>{filter.label}</span>
349-
<span className="text-text-tertiary text-[11px]">{filter.count}</span>
350+
<span className="text-text-tertiary text-[11px] group-data-[selected=true]:text-text">
351+
{filter.count}
352+
</span>
350353
</Button>
351354
))}
352355
</div>

packages/builder/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@
1212
"cli": "tsx src/cli.ts"
1313
},
1414
"dependencies": {
15-
"@aws-sdk/client-s3": "3.929.0",
16-
"@aws-sdk/node-http-handler": "3.374.0",
17-
"@aws-sdk/s3-request-presigner": "3.929.0",
1815
"@vingle/bmp-js": "^0.2.5",
1916
"blurhash": "2.0.5",
2017
"c12": "^3.3.2",
2118
"dotenv-expand": "catalog:",
2219
"execa": "9.6.0",
2320
"exiftool-vendored": "31.1.0",
21+
"fast-xml-parser": "4.4.1",
2422
"heic-convert": "2.1.0",
2523
"heic-to": "1.3.0",
2624
"sharp": "0.34.5",
@@ -44,4 +42,4 @@
4442
}
4543
}
4644
}
47-
}
45+
}

packages/builder/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ export { StorageFactory, StorageManager } from './storage/index.js'
3939
export type { BuilderConfig, BuilderConfigInput } from './types/config.js'
4040
export type { AfilmoryManifest, CameraInfo, LensInfo } from './types/manifest.js'
4141
export type { FujiRecipe, PhotoManifestItem, PickedExif, ToneAnalysis } from './types/photo.js'
42+
export type { S3ObjectLike } from './types/s3.js'

packages/builder/src/manifest/manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import fs from 'node:fs/promises'
22
import path, { basename } from 'node:path'
33

44
import { workdir } from '@afilmory/builder/path.js'
5-
import type { _Object } from '@aws-sdk/client-s3'
65

76
import { logger } from '../logger/index.js'
87
import type { AfilmoryManifest, CameraInfo, LensInfo } from '../types/manifest.js'
98
import type { PhotoManifestItem } from '../types/photo.js'
9+
import type { S3ObjectLike } from '../types/s3.js'
1010
import { migrateManifestFileIfNeeded } from './migrate.js'
1111
import { CURRENT_MANIFEST_VERSION } from './version.js'
1212

@@ -45,7 +45,7 @@ export async function loadExistingManifest(): Promise<AfilmoryManifest> {
4545
}
4646

4747
// 检查照片是否需要更新(基于最后修改时间)
48-
export function needsUpdate(existingItem: PhotoManifestItem | undefined, s3Object: _Object): boolean {
48+
export function needsUpdate(existingItem: PhotoManifestItem | undefined, s3Object: S3ObjectLike): boolean {
4949
if (!existingItem) return true
5050
if (!s3Object.LastModified) return true
5151

packages/builder/src/photo/image-pipeline.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import crypto from 'node:crypto'
22
import path from 'node:path'
33

44
import { compressUint8Array } from '@afilmory/utils'
5-
import type { _Object } from '@aws-sdk/client-s3'
65
import sharp from 'sharp'
76

87
import type { BuilderOptions } from '../builder/builder.js'
@@ -15,6 +14,7 @@ import {
1514
import type { PluginRunState } from '../plugins/manager.js'
1615
import { THUMBNAIL_PLUGIN_DATA_KEY } from '../plugins/thumbnail-storage/shared.js'
1716
import type { PhotoManifestItem, ProcessPhotoResult } from '../types/photo.js'
17+
import type { S3ObjectLike } from '../types/s3.js'
1818
import { shouldProcessPhoto } from './cache-manager.js'
1919
import { processExifData, processThumbnailAndBlurhash, processToneAnalysis } from './data-processors.js'
2020
import { getPhotoExecutionContext } from './execution-context.js'
@@ -32,9 +32,9 @@ export interface ProcessedImageData {
3232

3333
export interface PhotoProcessingContext {
3434
photoKey: string
35-
obj: _Object
35+
obj: S3ObjectLike
3636
existingItem: PhotoManifestItem | undefined
37-
livePhotoMap: Map<string, _Object>
37+
livePhotoMap: Map<string, S3ObjectLike>
3838
options: PhotoProcessorOptions
3939
pluginData: Record<string, unknown>
4040
}

packages/builder/src/photo/live-photo-handler.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type { _Object } from '@aws-sdk/client-s3'
2-
31
import type { StorageManager } from '../storage/index.js'
42
import type { StorageObject } from '../storage/interfaces.js'
3+
import type { S3ObjectLike } from '../types/s3.js'
54
import { getGlobalLoggers } from './logger-adapter.js'
65

76
export interface LivePhotoResult {
@@ -19,7 +18,7 @@ export interface LivePhotoResult {
1918
*/
2019
export async function processLivePhoto(
2120
photoKey: string,
22-
livePhotoMap: Map<string, _Object | StorageObject>,
21+
livePhotoMap: Map<string, S3ObjectLike | StorageObject>,
2322
storageManager: StorageManager,
2423
): Promise<LivePhotoResult> {
2524
const loggers = getGlobalLoggers()
@@ -32,10 +31,10 @@ export async function processLivePhoto(
3231

3332
// 处理不同类型的视频对象
3433
let videoKey: string
35-
if ('Key' in livePhotoVideo && livePhotoVideo.Key) {
34+
if ('Key' in livePhotoVideo && typeof livePhotoVideo.Key === 'string') {
3635
// _Object 类型
3736
videoKey = livePhotoVideo.Key
38-
} else if ('key' in livePhotoVideo && livePhotoVideo.key) {
37+
} else if ('key' in livePhotoVideo && typeof livePhotoVideo.key === 'string') {
3938
// StorageObject 类型
4039
videoKey = livePhotoVideo.key
4140
} else {
@@ -59,7 +58,7 @@ export async function processLivePhoto(
5958
* @param objects S3 对象列表
6059
* @returns Live Photo 映射表
6160
*/
62-
export function createLivePhotoMap(objects: _Object[]): Map<string, _Object>
61+
export function createLivePhotoMap(objects: S3ObjectLike[]): Map<string, S3ObjectLike>
6362

6463
/**
6564
* 创建 Live Photo 映射表 (兼容 StorageObject 类型)
@@ -69,16 +68,18 @@ export function createLivePhotoMap(objects: _Object[]): Map<string, _Object>
6968
*/
7069
export function createLivePhotoMap(objects: StorageObject[]): Map<string, StorageObject>
7170

72-
export function createLivePhotoMap(objects: _Object[] | StorageObject[]): Map<string, _Object | StorageObject> {
73-
const livePhotoMap = new Map<string, _Object | StorageObject>()
71+
export function createLivePhotoMap(
72+
objects: S3ObjectLike[] | StorageObject[],
73+
): Map<string, S3ObjectLike | StorageObject> {
74+
const livePhotoMap = new Map<string, S3ObjectLike | StorageObject>()
7475

7576
// 分离照片和视频文件
76-
const photos: (_Object | StorageObject)[] = []
77-
const videos: (_Object | StorageObject)[] = []
77+
const photos: (S3ObjectLike | StorageObject)[] = []
78+
const videos: (S3ObjectLike | StorageObject)[] = []
7879

7980
for (const obj of objects) {
8081
// 获取 key,兼容两种类型
81-
const key = 'Key' in obj ? obj.Key : (obj as StorageObject).key
82+
const key = 'Key' in obj ? (typeof obj.Key === 'string' ? obj.Key : undefined) : (obj as StorageObject).key
8283
if (!key) continue
8384

8485
const ext = key.toLowerCase().split('.').pop()
@@ -91,14 +92,16 @@ export function createLivePhotoMap(objects: _Object[] | StorageObject[]): Map<st
9192

9293
// 匹配 Live Photo
9394
for (const photo of photos) {
94-
const photoKey = 'Key' in photo ? photo.Key : (photo as StorageObject).key
95+
const photoKey =
96+
'Key' in photo ? (typeof photo.Key === 'string' ? photo.Key : undefined) : (photo as StorageObject).key
9597
if (!photoKey) continue
9698

9799
const photoBaseName = photoKey.replace(/\.[^/.]+$/, '')
98100

99101
// 查找对应的视频文件
100102
const matchingVideo = videos.find((video) => {
101-
const videoKey = 'Key' in video ? video.Key : (video as StorageObject).key
103+
const videoKey =
104+
'Key' in video ? (typeof video.Key === 'string' ? video.Key : undefined) : (video as StorageObject).key
102105
if (!videoKey) return false
103106
const videoBaseName = videoKey.replace(/\.[^/.]+$/, '')
104107
return videoBaseName === photoBaseName

packages/builder/src/photo/processor.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import type { _Object } from '@aws-sdk/client-s3'
2-
31
import type { AfilmoryBuilder, BuilderOptions } from '../builder/builder.js'
42
import { logger } from '../logger/index.js'
53
import type { PluginRunState } from '../plugins/manager.js'
64
import type { PhotoManifestItem, ProcessPhotoResult } from '../types/photo.js'
5+
import type { S3ObjectLike } from '../types/s3.js'
76
import { createStorageKeyNormalizer, runWithPhotoExecutionContext } from './execution-context.js'
87
import type { PhotoProcessingContext } from './image-pipeline.js'
98
import { processPhotoWithPipeline } from './image-pipeline.js'
@@ -17,12 +16,12 @@ export interface PhotoProcessorOptions {
1716

1817
// 处理单张照片
1918
export async function processPhoto(
20-
obj: _Object,
19+
obj: S3ObjectLike,
2120
index: number,
2221
workerId: number,
2322
totalImages: number,
2423
existingManifestMap: Map<string, PhotoManifestItem>,
25-
livePhotoMap: Map<string, _Object>,
24+
livePhotoMap: Map<string, S3ObjectLike>,
2625
options: PhotoProcessorOptions,
2726
builder: AfilmoryBuilder,
2827
pluginRuntime: {

0 commit comments

Comments
 (0)