-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathstaticHandler.ts
95 lines (78 loc) · 2.83 KB
/
staticHandler.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
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
import type { ContainerClient } from '@azure/storage-blob'
import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'
import type { CollectionConfig } from 'payload'
import { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities'
import path from 'path'
import { getRangeFromHeader } from './utils/getRangeFromHeader.js'
interface Args {
collection: CollectionConfig
getStorageClient: () => ContainerClient
}
export const getHandler = ({ collection, getStorageClient }: Args): StaticHandler => {
return async (req, { headers: incomingHeaders, params: { clientUploadContext, filename } }) => {
try {
const prefix = await getFilePrefix({ clientUploadContext, collection, filename, req })
const blockBlobClient = getStorageClient().getBlockBlobClient(
path.posix.join(prefix, filename),
)
const { end, start } = await getRangeFromHeader(
blockBlobClient,
String(req.headers.get('range')),
)
const blob = await blockBlobClient.download(start, end)
const response = blob._response
const rawHeaders = { ...response.headers.rawHeaders() }
let initHeaders: HeadersInit = {
...rawHeaders,
}
// Typescript is difficult here with merging these types from Azure
if (incomingHeaders) {
initHeaders = {
...initHeaders,
...incomingHeaders,
}
}
let headers = new Headers(initHeaders)
const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')
const objectEtag = response.headers.get('etag')
if (
collection.upload &&
typeof collection.upload === 'object' &&
typeof collection.upload.modifyResponseHeaders === 'function'
) {
headers = collection.upload.modifyResponseHeaders({ headers }) || headers
}
if (etagFromHeaders && etagFromHeaders === objectEtag) {
return new Response(null, {
headers,
status: 304,
})
}
// Manually create a ReadableStream for the web from a Node.js stream.
const readableStream = new ReadableStream({
start(controller) {
const nodeStream = blob.readableStreamBody
if (!nodeStream) {
throw new Error('No readable stream body')
}
nodeStream.on('data', (chunk) => {
controller.enqueue(new Uint8Array(chunk))
})
nodeStream.on('end', () => {
controller.close()
})
nodeStream.on('error', (err) => {
controller.error(err)
})
},
})
return new Response(readableStream, {
headers,
status: response.status,
})
} catch (err: unknown) {
req.payload.logger.error(err)
return new Response('Internal Server Error', { status: 500 })
}
}
}