Skip to content

Commit 6457193

Browse files
committed
fix: encrypted image cdn if possible for ipx and/or use allowed remote url list
1 parent 9d5d1a2 commit 6457193

File tree

7 files changed

+570
-97
lines changed

7 files changed

+570
-97
lines changed

plugin/src/helpers/cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { NetlifyPluginOptions } from '@netlify/build'
55

66
import { getGatsbyRoot } from './config'
77

8-
function getCacheDirs(publish) {
8+
export function getCacheDirs(publish) {
99
return [publish, normalizedCacheDir(publish)]
1010
}
1111

plugin/src/helpers/config.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -409,18 +409,4 @@ export function shouldSkip(publishDir: string): boolean {
409409

410410
return shouldSkipResult
411411
}
412-
413-
export function checkNetlifyImageCdn({
414-
netlifyConfig,
415-
}: {
416-
netlifyConfig: NetlifyConfig
417-
}): void {
418-
/* eslint-disable no-param-reassign */
419-
const { NETLIFY_IMAGE_CDN } = netlifyConfig.build.environment
420-
421-
if (NETLIFY_IMAGE_CDN === 'true') {
422-
netlifyConfig.build.environment.GATSBY_CLOUD_IMAGE_CDN = 'true'
423-
}
424-
/* eslint-enable no-param-reassign */
425-
}
426412
/* eslint-enable max-lines */

plugin/src/helpers/functions.ts

Lines changed: 133 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
/* eslint-disable max-lines */
12
import { NetlifyConfig, NetlifyPluginConstants } from '@netlify/build'
23
import { copy, copyFile, ensureDir, existsSync, rm, writeFile } from 'fs-extra'
34
import { resolve, join, relative } from 'pathe'
45

56
import { makeApiHandler, makeHandler } from '../templates/handlers'
67

78
import { getGatsbyRoot } from './config'
9+
import {
10+
ImageCdnImplementation,
11+
ImageCdnUrlSyntax,
12+
type PrepareImageCdnResult,
13+
} from './image_cdn'
814

915
export type FunctionList = Array<'API' | 'SSR' | 'DSG'>
1016

@@ -37,16 +43,20 @@ export const writeFunctions = async ({
3743
constants,
3844
netlifyConfig,
3945
neededFunctions,
46+
prepareImageCdnResult,
4047
}: {
4148
constants: NetlifyPluginConstants
4249
netlifyConfig: NetlifyConfig
4350
neededFunctions: FunctionList
51+
prepareImageCdnResult?: PrepareImageCdnResult
4452
}): Promise<void> => {
4553
const { PUBLISH_DIR, INTERNAL_FUNCTIONS_SRC } = constants
4654
const siteRoot = getGatsbyRoot(PUBLISH_DIR)
4755
const functionDir = resolve(INTERNAL_FUNCTIONS_SRC, '__api')
4856
const appDir = relative(functionDir, siteRoot)
4957

58+
await ensureDir(INTERNAL_FUNCTIONS_SRC)
59+
5060
if (neededFunctions.includes('SSR')) {
5161
await writeFunction({
5262
renderMode: 'SSR',
@@ -65,97 +75,158 @@ export const writeFunctions = async ({
6575
})
6676
}
6777

68-
await setupImageCdn({ constants, netlifyConfig })
78+
if (
79+
prepareImageCdnResult &&
80+
prepareImageCdnResult.imageCDNImplementation !== 'NONE'
81+
) {
82+
await setupImageCdn({ constants, netlifyConfig, prepareImageCdnResult })
83+
}
6984

7085
if (neededFunctions.includes('API')) {
7186
await writeApiFunction({ appDir, functionDir })
7287
}
7388
}
7489

90+
// eslint-disable-next-line max-lines-per-function, complexity
7591
export const setupImageCdn = async ({
7692
constants,
7793
netlifyConfig,
94+
prepareImageCdnResult,
7895
}: {
7996
constants: NetlifyPluginConstants
8097
netlifyConfig: NetlifyConfig
98+
prepareImageCdnResult: PrepareImageCdnResult
8199
}) => {
82-
const { GATSBY_CLOUD_IMAGE_CDN, NETLIFY_IMAGE_CDN } =
83-
netlifyConfig.build.environment
84-
85-
if (
86-
NETLIFY_IMAGE_CDN !== `true` &&
87-
GATSBY_CLOUD_IMAGE_CDN !== '1' &&
88-
GATSBY_CLOUD_IMAGE_CDN !== 'true'
89-
) {
90-
return
91-
}
92-
93-
await ensureDir(constants.INTERNAL_FUNCTIONS_SRC)
94-
95-
await copyFile(
100+
await ensureDir(join(constants.INTERNAL_FUNCTIONS_SRC, '_ipx'))
101+
await copy(
96102
join(__dirname, '..', '..', 'src', 'templates', 'ipx.ts'),
97-
join(constants.INTERNAL_FUNCTIONS_SRC, '_ipx.ts'),
103+
join(constants.INTERNAL_FUNCTIONS_SRC, '_ipx', '_ipx.ts'),
104+
{ overwrite: true },
98105
)
99106

100-
if (NETLIFY_IMAGE_CDN === `true`) {
107+
await writeFile(
108+
join(constants.INTERNAL_FUNCTIONS_SRC, '_ipx', 'config.ts'),
109+
`exports.config = ${JSON.stringify(prepareImageCdnResult.imageCDNConfig)}`,
110+
)
111+
112+
if (
113+
prepareImageCdnResult.imageCDNImplementation ===
114+
ImageCdnImplementation.NETLIFY_IMAGE_CDN
115+
) {
101116
await copyFile(
102117
join(__dirname, '..', '..', 'src', 'templates', 'image.ts'),
103118
join(constants.INTERNAL_FUNCTIONS_SRC, '__image.ts'),
104119
)
105120

106121
netlifyConfig.redirects.push(
107-
{
108-
from: '/_gatsby/image/:unused/:unused2/:filename',
109-
// eslint-disable-next-line id-length
110-
query: { u: ':url', a: ':args', cd: ':cd' },
111-
to: '/.netlify/functions/__image/image_query_compat?url=:url&args=:args&cd=:cd',
112-
status: 301,
113-
force: true,
114-
},
115-
{
116-
from: '/_gatsby/image/*',
117-
to: '/.netlify/functions/__image',
118-
status: 200,
119-
force: true,
120-
},
122+
...[
123+
// QUERY_WITH_ENCRYPTED_URL is forcefully disable when using Netlify Image CDN
124+
// so we don't need to handle it here
125+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
126+
ImageCdnUrlSyntax.QUERY,
127+
)
128+
? {
129+
from: '/_gatsby/image/:unused/:unused2/:filename',
130+
// eslint-disable-next-line id-length
131+
query: { u: ':url', a: ':args', cd: ':cd' },
132+
to: '/.netlify/functions/__image/image_query_compat?url=:url&args=:args&cd=:cd',
133+
status: 301,
134+
force: true,
135+
}
136+
: undefined,
137+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
138+
ImageCdnUrlSyntax.BASE64,
139+
)
140+
? {
141+
from: '/_gatsby/image/*',
142+
to: '/.netlify/functions/__image',
143+
status: 200,
144+
force: true,
145+
}
146+
: undefined,
147+
].filter(Boolean),
121148
)
122149
} else if (
123-
GATSBY_CLOUD_IMAGE_CDN === '1' ||
124-
GATSBY_CLOUD_IMAGE_CDN === 'true'
150+
prepareImageCdnResult.imageCDNImplementation === ImageCdnImplementation.IPX
125151
) {
126152
netlifyConfig.redirects.push(
127-
{
128-
from: `/_gatsby/image/:unused/:unused2/:filename`,
129-
// eslint-disable-next-line id-length
130-
query: { u: ':url', a: ':args' },
131-
to: `/.netlify/builders/_ipx/image_query_compat/:args/:url/:filename`,
132-
status: 301,
133-
force: true,
134-
},
135-
{
136-
from: '/_gatsby/image/*',
137-
to: '/.netlify/builders/_ipx',
138-
status: 200,
139-
force: true,
140-
},
153+
// eslint-disable-next-line no-sparse-arrays
154+
...[
155+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
156+
ImageCdnUrlSyntax.QUERY_WITH_ENCRYPTED_URL,
157+
)
158+
? {
159+
from: `/_gatsby/image/:unused/:unused2/:filename`,
160+
// eslint-disable-next-line id-length
161+
query: { eu: ':encrypted_url', a: ':args' },
162+
to: `/.netlify/builders/_ipx/image_query_compat_eu/:args/:encrypted_url/:filename`,
163+
status: 301,
164+
force: true,
165+
}
166+
: undefined,
167+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
168+
ImageCdnUrlSyntax.QUERY,
169+
)
170+
? {
171+
from: `/_gatsby/image/:unused/:unused2/:filename`,
172+
// eslint-disable-next-line id-length
173+
query: { u: ':url', a: ':args' },
174+
to: `/.netlify/builders/_ipx/image_query_compat/:args/:url/:filename`,
175+
status: 301,
176+
force: true,
177+
}
178+
: undefined,
179+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
180+
ImageCdnUrlSyntax.BASE64,
181+
)
182+
? {
183+
from: '/_gatsby/image/*',
184+
to: '/.netlify/builders/_ipx',
185+
status: 200,
186+
force: true,
187+
}
188+
: undefined,
189+
,
190+
].filter(Boolean),
141191
)
142192
}
143193

144194
netlifyConfig.redirects.push(
145-
{
146-
from: `/_gatsby/file/:unused/:filename`,
147-
// eslint-disable-next-line id-length
148-
query: { u: ':url' },
149-
to: `/.netlify/functions/_ipx/file_query_compat/:url/:filename`,
150-
status: 301,
151-
force: true,
152-
},
153-
{
154-
from: '/_gatsby/file/*',
155-
to: '/.netlify/functions/_ipx',
156-
status: 200,
157-
force: true,
158-
},
195+
...[
196+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
197+
ImageCdnUrlSyntax.QUERY_WITH_ENCRYPTED_URL,
198+
)
199+
? {
200+
from: `/_gatsby/file/:unused/:filename`,
201+
query: { eu: ':encrypted_url' },
202+
to: `/.netlify/functions/_ipx/file_query_compat_eu/:encrypted_url/:filename`,
203+
status: 301,
204+
force: true,
205+
}
206+
: undefined,
207+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
208+
ImageCdnUrlSyntax.QUERY,
209+
)
210+
? {
211+
from: `/_gatsby/file/:unused/:filename`,
212+
// eslint-disable-next-line id-length
213+
query: { u: ':url' },
214+
to: `/.netlify/functions/_ipx/file_query_compat/:url/:filename`,
215+
status: 301,
216+
force: true,
217+
}
218+
: undefined,
219+
prepareImageCdnResult.imageCDNConfig.enabledUrlPatterns.includes(
220+
ImageCdnUrlSyntax.BASE64,
221+
)
222+
? {
223+
from: '/_gatsby/file/*',
224+
to: '/.netlify/functions/_ipx',
225+
status: 200,
226+
force: true,
227+
}
228+
: undefined,
229+
].filter(Boolean),
159230
)
160231
}
161232

@@ -169,3 +240,4 @@ export const deleteFunctions = async ({
169240
}
170241
}
171242
}
243+
/* eslint-enable max-lines */

0 commit comments

Comments
 (0)