mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 12:26:43 +02:00
feat(storage): add deleteByPrefix, copy, getMetadata and prod lifecycle rules
- Add deleteByPrefix(prefix) for bulk user data deletion (account cleanup) - Add copy(sourceKey, destKey) via CopyObjectCommand for file duplication - Add getMetadata(key) via HeadObjectCommand for content-type/size/metadata - Add FileMetadata type for structured metadata responses - Add minio-init container to docker-compose.macmini.yml with bucket creation, public access policies, and lifecycle rules (matching dev compose) - 96 tests passing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b0e5a9c5ff
commit
152fa5fe08
5 changed files with 191 additions and 0 deletions
|
|
@ -6,6 +6,7 @@ import {
|
|||
DeleteObjectsCommand,
|
||||
ListObjectsV2Command,
|
||||
HeadObjectCommand,
|
||||
CopyObjectCommand,
|
||||
CreateMultipartUploadCommand,
|
||||
UploadPartCommand,
|
||||
CompleteMultipartUploadCommand,
|
||||
|
|
@ -24,6 +25,7 @@ import type {
|
|||
MultipartUploadPart,
|
||||
UploadResult,
|
||||
FileInfo,
|
||||
FileMetadata,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
|
|
@ -370,6 +372,59 @@ export class StorageClient {
|
|||
return this.bucket.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all files matching a prefix.
|
||||
* Useful for account deletion (e.g., deleteByPrefix('users/user-123/'))
|
||||
*/
|
||||
async deleteByPrefix(prefix: string): Promise<number> {
|
||||
const files = await this.list(prefix);
|
||||
if (files.length === 0) return 0;
|
||||
|
||||
const keys = files.map((f) => f.key);
|
||||
await this.deleteMany(keys);
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file within the same bucket.
|
||||
* For move operations, call copy() then delete() the source.
|
||||
*/
|
||||
async copy(sourceKey: string, destKey: string): Promise<UploadResult> {
|
||||
const command = new CopyObjectCommand({
|
||||
Bucket: this.bucket.name,
|
||||
CopySource: `${this.bucket.name}/${sourceKey}`,
|
||||
Key: destKey,
|
||||
});
|
||||
|
||||
const result = await this.client.send(command);
|
||||
|
||||
return {
|
||||
key: destKey,
|
||||
url: this.getPublicUrl(destKey),
|
||||
etag: result.CopyObjectResult?.ETag,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file metadata without downloading the file.
|
||||
*/
|
||||
async getMetadata(key: string): Promise<FileMetadata> {
|
||||
const command = new HeadObjectCommand({
|
||||
Bucket: this.bucket.name,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
const response = await this.client.send(command);
|
||||
|
||||
return {
|
||||
contentType: response.ContentType,
|
||||
size: response.ContentLength ?? 0,
|
||||
lastModified: response.LastModified,
|
||||
etag: response.ETag,
|
||||
metadata: response.Metadata,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Presigned Multipart Upload (browser direct-upload) ──────────────
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue