-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathhandler.ts
154 lines (132 loc) · 3.43 KB
/
handler.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
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import * as AWS from 'aws-sdk'
import { getConfig, parseParams, Params } from './parser'
import { callbackRuntime } from 'lambda-helpers'
import { APIGatewayEvent, ProxyResult } from 'aws-lambda'
import { GraphQLClient } from 'graphql-request'
const sharp = require('sharp')
import 'source-map-support/register'
const s3 = new AWS.S3()
export default callbackRuntime(async (event: APIGatewayEvent) => {
// NOTE currently needed for backward compatibility
if (event.path.split('/')[1] !== 'v1') {
try {
// log projectId of projects using the old API version
const client = new GraphQLClient(process.env['GRAPHCOOL_ENDPOINT']!, {
headers: {
Authorization: `Bearer ${process.env['GRAPHCOOL_PAT']}`,
},
})
await client.request(`mutation {
createApiUser(projectId: "${event.path.split('/')[1]}") { id }
}`)
} catch (e) {
if (e.response.errors[0].code !== 3010) {
throw e
}
}
return {
statusCode: 301,
body: '',
headers: {
Location: `https://images.graph.cool/v1${event.path}`,
},
}
}
const [paramsErr, params] = parseParams(event.path)
if (paramsErr) {
return {
statusCode: 400,
body: paramsErr.toString(),
}
}
const { projectId, fileSecret, crop, resize } = params!
const options = {
Bucket: process.env['BUCKET_NAME']!,
Key: `${projectId}/${fileSecret}`,
}
const {
ContentLength,
ContentType,
ContentDisposition,
ETag,
LastModified
} = await s3.headObject(options).promise()
if (ContentLength! > 25 * 1024 * 1024) {
return {
statusCode: 400,
body: 'File too big',
}
}
if (!ContentType!.includes('image')) {
return {
statusCode: 400,
body: 'File not an image',
}
}
// return original for gifs, svgs or no params
if (
ContentType === 'image/gif' ||
ContentType === 'image/svg+xml' ||
(resize === undefined && crop === undefined)
) {
const obj = await s3.getObject(options).promise()
const body = (obj.Body as Buffer).toString('base64')
return base64Response(body, ContentType!, ContentDisposition!, ETag!, LastModified!)
}
const s3Resp = await s3.getObject(options).promise()
const stream = sharp(s3Resp.Body)
try {
const config = getConfig({ resize, crop })
stream.limitInputPixels(false)
if (config.crop) {
stream.extract({
left: config.crop.x,
top: config.crop.y,
width: config.crop.width,
height: config.crop.height,
})
}
if (config.resize) {
stream.rotate()
stream.resize(config.resize.width, config.resize.height)
if (config.resize.force) {
stream.ignoreAspectRatio()
} else {
stream.max()
}
}
} catch (err) {
return {
statusCode: 400,
body: err.toString(),
}
}
const buf = await stream.withMetadata().toBuffer()
return base64Response(
buf.toString('base64'),
ContentType!,
ContentDisposition!,
ETag!,
LastModified!
)
})
function base64Response(
body: string,
ContentType: string,
ContentDisposition: string,
ETag: string,
LastModified: any
) {
return {
statusCode: 200,
headers: {
'Content-Type': ContentType,
'Content-Disposition': ContentDisposition,
'Cache-Control': 'max-age=31536000',
'ETag': ETag,
'Last-Modified': LastModified
},
body,
isBase64Encoded: true,
}
}