Skip to content

Commit ef4bf8d

Browse files
committed
setting up redirects and a function to deliver cloudinary images
1 parent 0d05925 commit ef4bf8d

File tree

6 files changed

+190
-12
lines changed

6 files changed

+190
-12
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "netlify-plugin-cloudinary",
3-
"version": "0.0.9",
3+
"version": "0.0.10",
44
"description": "",
55
"main": "src/index.js",
66
"scripts": {
@@ -14,6 +14,7 @@
1414
"license": "MIT",
1515
"dependencies": {
1616
"cloudinary": "^1.27.1",
17+
"fs-extra": "^10.0.0",
1718
"glob": "^7.2.0",
1819
"jsdom": "^18.1.1",
1920
"node-fetch": "2"

src/index.js

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,81 @@
1-
const fs = require('fs').promises;
1+
2+
const fs = require('fs-extra')
3+
const path = require('path');
24
const glob = require('glob');
35

4-
const { getCloudinary, updateHtmlImagesToCloudinary } = require('./lib/cloudinary');
6+
const { getCloudinary, updateHtmlImagesToCloudinary, getCloudinaryUrl } = require('./lib/cloudinary');
7+
8+
const CLOUDINARY_ASSET_PATH = "/cloudinary-assets";
9+
const CLOUDINARY_IMAGES_PATH = `${CLOUDINARY_ASSET_PATH}/images`;
10+
11+
const CLOUDINARY_MEDIA_FUNCTIONS = ['images'];
512

613
/**
714
* TODO
815
* - Handle srcset
9-
* - Delivery type for redirect via Netlify redirects
1016
*/
1117

1218
module.exports = {
1319

20+
async onBuild({ netlifyConfig, constants, inputs }) {
21+
const { FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC } = constants;
22+
const { uploadPreset, deliveryType } = inputs;
23+
24+
const cloudName = process.env.CLOUDINARY_CLOUD_NAME || inputs.cloudName;
25+
26+
if ( !cloudName ) {
27+
throw new Error('Cloudinary Cloud Name required. Please set cloudName input or use environment variable CLOUDINARY_CLOUD_NAME');
28+
}
29+
30+
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
31+
32+
// Copy all of the templates over including the functions to deploy
33+
34+
try {
35+
await fs.copy(path.join(__dirname, 'templates'), functionsPath);
36+
} catch(e) {
37+
console.log('Failed to copy templates:', e);
38+
throw e;
39+
}
40+
41+
// Configure reference parameters for Cloudinary delivery to attach to redirect
42+
43+
const params = {
44+
uploadPreset,
45+
deliveryType: 'fetch',
46+
cloudName
47+
}
48+
49+
const paramsString = Object.keys(params)
50+
.filter(key => typeof params[key] !== 'undefined')
51+
.map(key => `${key}=${encodeURIComponent(params[key])}`)
52+
.join('&');
53+
54+
// Redirect any requests that hits /[media type]/* to a serverless function
55+
56+
CLOUDINARY_MEDIA_FUNCTIONS.forEach(mediaName => {
57+
const functionName = `cld_${mediaName}`;
58+
59+
netlifyConfig.redirects.push({
60+
from: `/${mediaName}/*`,
61+
to: `${process.env.DEPLOY_PRIME_URL}/.netlify/functions/${functionName}/:splat?${paramsString}`,
62+
status: 302,
63+
force: true,
64+
});
65+
66+
netlifyConfig.redirects.push({
67+
from: `/cld-assets/${mediaName}/*`,
68+
to: `/${mediaName}/:splat`,
69+
status: 200,
70+
force: true
71+
});
72+
});
73+
74+
},
75+
76+
// Post build looks through all of the output HTML and rewrites any src attributes to use a cloudinary URL
77+
// This only solves on-page references until any JS refreshes the DOM
78+
1479
async onPostBuild({ constants, inputs }) {
1580
const { PUBLISH_DIR } = constants;
1681
const {
@@ -27,12 +92,10 @@ module.exports = {
2792
throw new Error('Cloudinary Cloud Name required. Please use environment variable CLOUDINARY_CLOUD_NAME');
2893
}
2994

30-
const cloudinary = getCloudinary();
31-
32-
cloudinary.config({
33-
cloud_name: cloudName,
34-
api_key: apiKey,
35-
api_secret: apiSecret
95+
const cloudinary = getCloudinary({
96+
cloudName,
97+
apiKey,
98+
apiSecret
3699
});
37100

38101
// Find all HTML source files in the publish directory

src/lib/cloudinary.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ const { isRemoteUrl, determineRemoteUrl } = require('./util');
1010
* getCloudinary
1111
*/
1212

13-
function getCloudinary() {
13+
function getCloudinary(config = {}) {
14+
cloudinary.config({
15+
cloud_name: config.cloudName,
16+
api_key: config.apiKey,
17+
api_secret: config.apiSecret
18+
});
19+
1420
return cloudinary;
1521
}
1622

src/lib/util.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,27 @@ function determineRemoteUrl(url, host) {
2424
return url;
2525
}
2626

27-
module.exports.determineRemoteUrl = determineRemoteUrl;
27+
module.exports.determineRemoteUrl = determineRemoteUrl;
28+
29+
/**
30+
* getQueryParams
31+
*/
32+
33+
function getQueryParams(url) {
34+
if ( typeof url !== 'string') {
35+
throw new Error('Can not getQueryParams. Invalid URL');
36+
}
37+
38+
const params = {};
39+
40+
const urlSegments = url.split('?');
41+
42+
urlSegments[1] && urlSegments[1].split('&').forEach(segment => {
43+
const [key, value] = segment.split('=');
44+
params[key] = value;
45+
});
46+
47+
return params;
48+
}
49+
50+
module.exports.getQueryParams = getQueryParams;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
exports.handler = async function (event, context) {
2+
const { rawUrl } = event;
3+
4+
const rawUrlSegments = rawUrl.split('.netlify/functions/cld_images');
5+
const endpoint = rawUrlSegments[0].replace(/\/$/, '');
6+
const pathSegments = rawUrlSegments[1].split('?');
7+
const imagePath = `/cld-assets/images${pathSegments[0]}`;
8+
9+
const { deliveryType, uploadPreset } = getQueryParams(rawUrl);
10+
11+
const cloudName = process.env.CLOUDINARY_CLOUD_NAME || queryParams.cloudName;
12+
13+
const remoteUrl = encodeURIComponent(`${endpoint}${imagePath}`);
14+
15+
const cloudinaryUrl = `https://res.cloudinary.com/colbydemo/image/fetch/f_auto,q_auto/${remoteUrl}`
16+
17+
console.log({
18+
rawUrl,
19+
pathSegments,
20+
imagePath,
21+
cloudName,
22+
endpoint,
23+
imagePath,
24+
remoteUrl,
25+
cloudinaryUrl
26+
})
27+
28+
return {
29+
statusCode: 302,
30+
headers: {
31+
Location: cloudinaryUrl
32+
}
33+
};
34+
};
35+
36+
/**
37+
* getQueryParams
38+
*/
39+
40+
function getQueryParams(url) {
41+
if ( typeof url !== 'string') {
42+
throw new Error('Can not getQueryParams. Invalid URL');
43+
}
44+
45+
const params = {};
46+
47+
const urlSegments = url.split('?');
48+
49+
urlSegments[1] && urlSegments[1].split('&').forEach(segment => {
50+
const [key, value] = segment.split('=');
51+
params[key] = value;
52+
});
53+
54+
return params;
55+
}
56+
57+
module.exports.getQueryParams = getQueryParams;

yarn.lock

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,15 @@ form-data@^4.0.0:
11821182
combined-stream "^1.0.8"
11831183
mime-types "^2.1.12"
11841184

1185+
fs-extra@^10.0.0:
1186+
version "10.0.0"
1187+
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
1188+
integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==
1189+
dependencies:
1190+
graceful-fs "^4.2.0"
1191+
jsonfile "^6.0.1"
1192+
universalify "^2.0.0"
1193+
11851194
fs.realpath@^1.0.0:
11861195
version "1.0.0"
11871196
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1234,6 +1243,11 @@ globals@^11.1.0:
12341243
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
12351244
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
12361245

1246+
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
1247+
version "4.2.9"
1248+
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
1249+
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
1250+
12371251
graceful-fs@^4.2.4:
12381252
version "4.2.8"
12391253
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
@@ -1942,6 +1956,15 @@ json5@^2.1.2:
19421956
dependencies:
19431957
minimist "^1.2.5"
19441958

1959+
jsonfile@^6.0.1:
1960+
version "6.1.0"
1961+
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
1962+
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
1963+
dependencies:
1964+
universalify "^2.0.0"
1965+
optionalDependencies:
1966+
graceful-fs "^4.1.6"
1967+
19451968
kleur@^3.0.3:
19461969
version "3.0.3"
19471970
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@@ -2513,6 +2536,11 @@ universalify@^0.1.2:
25132536
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
25142537
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
25152538

2539+
universalify@^2.0.0:
2540+
version "2.0.0"
2541+
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
2542+
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
2543+
25162544
v8-to-istanbul@^8.1.0:
25172545
version "8.1.0"
25182546
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz#0aeb763894f1a0a1676adf8a8b7612a38902446c"

0 commit comments

Comments
 (0)