-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paths3.ts
More file actions
137 lines (112 loc) · 3.42 KB
/
s3.ts
File metadata and controls
137 lines (112 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
DeleteObjectCommand,
CopyObjectCommand,
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
// MinIO configuration
const s3Client = new S3Client({
region: process.env.AWS_REGION || 'us-east-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
// Support MinIO endpoint (for local dev or MinIO deployment)
...(process.env.AWS_ENDPOINT && {
endpoint: process.env.AWS_ENDPOINT,
forcePathStyle: true, // Required for MinIO
}),
});
const QUARANTINE_BUCKET = process.env.S3_BUCKET_QUARANTINE!;
const PUBLIC_BUCKET = process.env.S3_BUCKET_PUBLIC!;
const MULTIPART_CHUNK_SIZE = 100 * 1024 * 1024; // 100 MB
export async function createMultipartUpload(
filename: string,
fileSize: number
): Promise<{ uploadId: string; presignedUrls: string[] }> {
const key = `quarantine/${Date.now()}-${filename}`;
// Initiate multipart upload
const createCommand = new CreateMultipartUploadCommand({
Bucket: QUARANTINE_BUCKET,
Key: key,
ContentType: 'application/octet-stream',
});
const { UploadId } = await (s3Client as any).send(createCommand);
if (!UploadId) {
throw new Error('Failed to create multipart upload');
}
// Calculate number of parts
const numParts = Math.ceil(fileSize / MULTIPART_CHUNK_SIZE);
const presignedUrls: string[] = [];
// Generate presigned URLs for each part
for (let partNumber = 1; partNumber <= numParts; partNumber++) {
const uploadPartCommand = new UploadPartCommand({
Bucket: QUARANTINE_BUCKET,
Key: key,
UploadId,
PartNumber: partNumber,
});
const presignedUrl = await getSignedUrl(s3Client, uploadPartCommand, {
expiresIn: 3600, // 1 hour
});
presignedUrls.push(presignedUrl);
}
return {
uploadId: UploadId,
presignedUrls,
};
}
export async function completeMultipartUpload(
key: string,
uploadId: string,
parts: { ETag: string; PartNumber: number }[]
): Promise<void> {
const command = new CompleteMultipartUploadCommand({
Bucket: QUARANTINE_BUCKET,
Key: key,
UploadId: uploadId,
MultipartUpload: {
Parts: parts,
},
});
await (s3Client as any).send(command);
}
export async function moveToPublicBucket(quarantineKey: string): Promise<string> {
const publicKey = quarantineKey.replace('quarantine/', 'public/');
// Copy to public bucket
const copyCommand = new CopyObjectCommand({
CopySource: `${QUARANTINE_BUCKET}/${quarantineKey}`,
Bucket: PUBLIC_BUCKET,
Key: publicKey,
});
await (s3Client as any).send(copyCommand);
// Delete from quarantine
const deleteCommand = new DeleteObjectCommand({
Bucket: QUARANTINE_BUCKET,
Key: quarantineKey,
});
await (s3Client as any).send(deleteCommand);
return publicKey;
}
export async function deleteFromQuarantine(key: string): Promise<void> {
const command = new DeleteObjectCommand({
Bucket: QUARANTINE_BUCKET,
Key: key,
});
await (s3Client as any).send(command);
}
export async function getPresignedDownloadUrl(key: string): Promise<string> {
const command = new GetObjectCommand({
Bucket: PUBLIC_BUCKET,
Key: key,
});
return getSignedUrl(s3Client, command, {
expiresIn: 3600, // 1 hour
});
}
export { s3Client };