-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
149 lines (128 loc) · 4.84 KB
/
index.js
File metadata and controls
149 lines (128 loc) · 4.84 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
137
138
139
140
141
142
143
144
145
146
147
148
149
'use strict';
const AWS = require('aws-sdk');
const S3 = new AWS.S3({
signatureVersion: 'v4',
});
const sharp = require('sharp');
const util = require('util');
//Get env variables
const MASTER_BUCKET = process.env.MASTER_BUCKET;
const OPTIMIZED_BUCKET = process.env.OPTIMIZED_BUCKET;
const OPTIMIZED_URL = process.env.OPTIMIZED_URL;
const MASTER_URL = process.env.MASTER_URL;
// env.ALLOWED_RESOLUTIONS is comma separated values (can be empty, and can have spaces around commas) e.g. "1920x1080, 1280x720"
const ALLOWED_RESOLUTIONS = process.env.ALLOWED_RESOLUTIONS ? new Set(process.env.ALLOWED_RESOLUTIONS.split(/\s*,\s*/)) : new Set([]);
const DEFAULT_RESOLUTION = process.env.DEFAULT_RESOLUTION;
const ALLOWED_IMAGE_TYPES = ['image/jpg', 'image/jpeg', 'image/png'];
exports.handler = async function(event, context, callback) {
// Normalise S3 bucket key to get it in the form "1280x720/test/test0.jpg"
const keyNormalisationResult = checkAndNormaliseKey(event.queryStringParameters.key);
if (keyNormalisationResult === null) {
// This may happen if the requested resolution is not allowed
callback(null, {
statusCode: '403',
headers: {},
body: 'Forbidden key. The resolution may not be allowed',
});
return "(debugging return) Invalid key";
}
const optimizedKey = keyNormalisationResult.optimizedKey;
// Check if optimized image already exists at the given resolution in the optimized bucket
try {
let test = await S3.headObject({Bucket: OPTIMIZED_BUCKET, Key: optimizedKey}).promise()
callback(null, {
statusCode: '301',
headers: {'location': `${OPTIMIZED_URL}/${optimizedKey}`},
body: 'Image already optimized',
});
return "(debugging return) Optimized file found";
} catch (err) { /* The file is not in the optimized bucket yet */ }
const keyParts = keyNormalisationResult.keyParts; // keyParts is the differnt sections of key (formed by regex)
const width = parseInt(keyParts[2], 10);
const height = parseInt(keyParts[3], 10);
const masterKey = keyParts[4]; // Key without the resolution. Example: "wind/surfing/test.jpg"
try {
// Check if the file is an image
const objectContentType = getObjectType(masterKey);
if (!ALLOWED_IMAGE_TYPES.includes(objectContentType)) {
callback(null, {
statusCode: '301',
headers: {'location': `${MASTER_URL}/${masterKey}`},
body: 'File is not an image. Redirecting to original',
});
return "(debugging return) Original file is not an image";
} else {
//The original file is an image. Optimize it...
const s3Object = await S3.getObject({Bucket: MASTER_BUCKET, Key: masterKey}).promise();
const optimizedObject = await optimize(s3Object, width, height, event.headers);
// Save the image to the optimized bucket
let response = await S3.putObject({
Body: optimizedObject.buffer,
Bucket: OPTIMIZED_BUCKET,
ContentType: optimizedObject.contentType,
Key: optimizedKey,
}).promise();
callback(null, {
statusCode: '301',
headers: {'location': `${OPTIMIZED_URL}/${optimizedKey}`},
body: 'Optimized image. Redirecting to optimized bucket',
});
return "(debugging return) Image optimized";
}
} catch (error) {
callback(null, {
statusCode: '301',
headers: {'location': `${MASTER_URL}/${masterKey}`},
body: 'Something went wrong. Redirecting to original',
});
return "(debugging return) ERROR";
}
}
function checkAndNormaliseKey(key) {
let match = getMatch(key); // E.g. "600x400/wind.jpg" or "1920x1080/Activities/Surfing.jpg"
if (!match) {
// There may be no resolution in the key
key = DEFAULT_RESOLUTION + '/' + key;
match = getMatch(key);
}
//Check if requested resolution is forbidden
if( (ALLOWED_RESOLUTIONS.size > 0) && (!ALLOWED_RESOLUTIONS.has(match[1])) ) {
return null;
}
return { optimizedKey: key, keyParts: match };
}
function getMatch(query) {
return query.match(/((\d+)x(\d+))\/(.*)/);
}
function getObjectType(key) {
const fileExtension = key.split('.').pop().toLowerCase();
const contentTypeMap = {
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg'
};
return contentTypeMap[fileExtension] || null;
}
async function optimize (s3Object, width, height, headers) {
let newContentType = null;
const sharpImage = await sharp(s3Object.Body);
await sharpImage.resize({
fit: sharp.fit.inside,
width: width,
height: height,
withoutEnlargement: true
})
// If webp is supported
if (headers['accept'] && headers['accept'].includes('image/webp')) {
await sharpImage.webp();
newContentType = 'image/webp';
}
return {
buffer: await sharpImage.toBuffer(),
contentType: newContentType
};
}
if (require.main === module) {
console.log("THIS IS A TEST");
exports.handler();
}