/
createUpload.ts
59 lines (47 loc) · 1.55 KB
/
createUpload.ts
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
import { APIGatewayProxyHandlerV2, APIGatewayProxyResultV2 } from 'aws-lambda'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { v4 as uuid } from 'uuid'
import { z } from 'zod'
import { jsonResponse } from './util'
const schema = z
.object({
contentType: z.string(),
filename: z.string(),
size: z.number(),
})
.strict()
type CreateUploadInput = z.infer<typeof schema>
type CreateUploadResponse = {
contentType: string
id: string
url: string
}
type Response = APIGatewayProxyResultV2<{ error: string } | CreateUploadResponse>
const bucketName = process.env.BUCKET_NAME!
const s3 = new S3Client({ region: 'us-east-1' })
export const handler: APIGatewayProxyHandlerV2<Response> = async (event): Promise<Response> => {
if (!event.headers['content-type']?.match(/^application\/json/) || !event.body) {
return jsonResponse({ error: 'Not JSON' }, 400)
}
const payload = JSON.parse(event.body)
let input: CreateUploadInput
try {
input = schema.parse(payload)
} catch (err) {
if (err instanceof z.ZodError) {
return jsonResponse({ error: err.issues.map((i) => `${i.path[0]}: ${i.message}`).join('; ') }, 400)
} else {
throw err
}
}
const id = uuid()
const { contentType } = input
const command = new PutObjectCommand({
Bucket: bucketName,
Key: `uploads/${id}/content`,
ContentType: contentType,
})
const url = await getSignedUrl(s3, command, { expiresIn: 900 })
return jsonResponse({ id, contentType, url })
}