1
1
const crypto = require ( 'crypto' ) ;
2
2
const path = require ( 'path' ) ;
3
3
const fetch = require ( 'node-fetch' ) ;
4
+ const { JSDOM } = require ( 'jsdom' ) ;
4
5
const cloudinary = require ( 'cloudinary' ) . v2 ;
5
6
6
7
const { isRemoteUrl, determineRemoteUrl } = require ( './util' ) ;
@@ -15,28 +16,62 @@ function getCloudinary() {
15
16
16
17
module . exports . getCloudinary = getCloudinary ;
17
18
19
+ /**
20
+ * createPublicId
21
+ */
22
+
23
+ async function createPublicId ( { path : filePath } = { } ) {
24
+ let hash = crypto . createHash ( 'md5' ) ;
25
+
26
+ const { name : imgName } = path . parse ( filePath ) ;
27
+
28
+ if ( ! isRemoteUrl ( filePath ) ) {
29
+ hash . update ( filePath ) ;
30
+ } else {
31
+ const response = await fetch ( filePath ) ;
32
+ const buffer = await response . buffer ( ) ;
33
+ hash . update ( buffer ) ;
34
+ }
35
+
36
+ hash = hash . digest ( 'hex' ) ;
37
+
38
+ return `${ imgName } -${ hash } `
39
+ }
40
+
41
+ module . exports . createPublicId = createPublicId ;
42
+
18
43
/**
19
44
* getCloudinaryUrl
20
45
*/
21
46
22
47
async function getCloudinaryUrl ( options = { } ) {
23
48
const {
24
- apiKey,
25
- apiSecret,
26
49
deliveryType,
27
50
folder,
28
51
path : filePath ,
29
- publishDir,
52
+ localDir,
53
+ remoteHost,
30
54
uploadPreset,
31
55
} = options ;
32
56
57
+ const { cloud_name : cloudName , api_key : apiKey , api_secret : apiSecret } = cloudinary . config ( ) ;
58
+ const canSignUpload = apiKey && apiSecret ;
59
+
60
+ if ( ! cloudName ) {
61
+ throw new Error ( 'Cloudinary Cloud Name required.' ) ;
62
+ }
63
+
64
+ if ( deliveryType === 'upload' && ! canSignUpload && ! uploadPreset ) {
65
+ throw new Error ( `To use deliveryType ${ deliveryType } , please use an uploadPreset for unsigned requests or an API Key and Secret for signed requests.` ) ;
66
+ }
67
+
33
68
let fileLocation ;
34
69
35
70
if ( deliveryType === 'fetch' ) {
36
71
// fetch allows us to pass in a remote URL to the Cloudinary API
37
72
// which it will cache and serve from the CDN, but not store
38
73
39
- fileLocation = determineRemoteUrl ( filePath ) ;
74
+ fileLocation = determineRemoteUrl ( filePath , remoteHost ) ;
40
75
} else if ( deliveryType === 'upload' ) {
41
76
// upload will actually store the image in the Cloudinary account
42
77
// and subsequently serve that stored image
@@ -48,7 +83,7 @@ async function getCloudinaryUrl(options = {}) {
48
83
let fullPath = filePath ;
49
84
50
85
if ( ! isRemoteUrl ( fullPath ) ) {
51
- fullPath = path . join ( publishDir , fullPath ) ;
86
+ fullPath = path . join ( localDir , fullPath ) ;
52
87
}
53
88
54
89
const id = await createPublicId ( {
@@ -57,28 +92,29 @@ async function getCloudinaryUrl(options = {}) {
57
92
58
93
const uploadOptions = {
59
94
folder,
60
- public_id : id
95
+ public_id : id ,
96
+ overwrite : false
97
+ }
98
+
99
+ if ( uploadPreset ) {
100
+ uploadOptions . upload_preset = uploadPreset ;
61
101
}
62
102
63
103
let results ;
64
104
65
- if ( apiKey && apiSecret ) {
105
+ if ( canSignUpload ) {
66
106
// We need an API Key and Secret to use signed uploading
67
107
68
108
results = await cloudinary . uploader . upload ( fullPath , {
69
- ...uploadOptions ,
70
- overwrite : false
109
+ ...uploadOptions
71
110
} ) ;
72
- } else if ( uploadPreset ) {
111
+ } else {
73
112
// If we want to avoid signing our uploads, we don't need our API Key and Secret,
74
113
// however, we need to provide an uploadPreset
75
114
76
115
results = await cloudinary . uploader . unsigned_upload ( fullPath , uploadPreset , {
77
116
...uploadOptions
78
- // Unsigned uploads default to overwrite: false
79
117
} ) ;
80
- } else {
81
- throw new Error ( `To use deliveryType ${ deliveryType } , please use an uploadPreset for unsigned requests or an API Key and Secret for signed requests.` ) ;
82
118
}
83
119
84
120
// Finally use the stored public ID to grab the image URL
@@ -104,25 +140,55 @@ async function getCloudinaryUrl(options = {}) {
104
140
module . exports . getCloudinaryUrl = getCloudinaryUrl ;
105
141
106
142
/**
107
- * createPublicId
143
+ * updateHtmlImagesToCloudinary
108
144
*/
109
145
110
- async function createPublicId ( { path : filePath } = { } ) {
111
- let hash = crypto . createHash ( 'md5' ) ;
146
+ async function updateHtmlImagesToCloudinary ( html , options = { } ) {
147
+ const {
148
+ deliveryType,
149
+ uploadPreset,
150
+ folder,
151
+ localDir,
152
+ remoteHost
153
+ } = options ;
112
154
113
- const { name : imgName } = path . parse ( filePath ) ;
155
+ const errors = [ ] ;
156
+ const dom = new JSDOM ( html ) ;
114
157
115
- if ( ! isRemoteUrl ( filePath ) ) {
116
- hash . update ( filePath ) ;
117
- } else {
118
- const response = await fetch ( filePath ) ;
119
- const buffer = await response . buffer ( ) ;
120
- hash . update ( buffer ) ;
121
- }
158
+ // Loop through all images found in the DOM and swap the source with
159
+ // a Cloudinary URL
122
160
123
- hash = hash . digest ( 'hex' ) ;
161
+ const images = Array . from ( dom . window . document . querySelectorAll ( 'img' ) ) ;
124
162
125
- return `${ imgName } -${ hash } `
163
+ for ( const $img of images ) {
164
+ let imgSrc = $img . getAttribute ( 'src' ) ;
165
+
166
+ try {
167
+ const cloudinarySrc = await getCloudinaryUrl ( {
168
+ deliveryType,
169
+ folder,
170
+ path : imgSrc ,
171
+ localDir,
172
+ uploadPreset,
173
+ remoteHost
174
+ } ) ;
175
+
176
+ $img . setAttribute ( 'src' , cloudinarySrc )
177
+ } catch ( e ) {
178
+ const { error } = e ;
179
+ errors . push ( {
180
+ imgSrc,
181
+ message : e . message || error . message
182
+ } ) ;
183
+ continue ;
184
+ }
185
+
186
+ }
187
+
188
+ return {
189
+ html : dom . serialize ( ) ,
190
+ errors
191
+ }
126
192
}
127
193
128
- module . exports . createPublicId = createPublicId ;
194
+ module . exports . updateHtmlImagesToCloudinary = updateHtmlImagesToCloudinary ;
0 commit comments