Skip to content

Commit 2fccaf1

Browse files
hrishikesh-kpieh
andauthored
fix: generate default allowedHosts with available variables instead of skipping all due to a single missing one (#441)
* update deps, apply prettier, change to ESM, update tests * update ci tests to use Node 22 * coderabbit 1 * remove exclude * update lockfiles * lint fixes * fix prettier config * fix npm ci * coderabbit 2 * downgrade and pin angular * update engine version range to match Angular * fix version and readme * update readme to suggest latest CLI * add allowedHosts and trustedProxyHeaders * regenrate lockfiles * fix type for getContext * update readme for deploy instructions * prevent missing env vars from failing build * fix for loop bug * update swapped file templates * fix context potentially being undefined * consolidate app-engine exports in a single file * conditionally update Angular config * revert pathname to request * fix typo * fix merge conflicts * match version of angular/ssr instead of core * Update README.md Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com> * seperate version detection for angular/core and ssr * update tests * update failBuild message and remove dead code * update readme * allow specifying own hosts * don't inject defaults when allowedHosts includes `*` * allow trusting all proxy headers * fix set logic * fix type definition * prettify * remove unnecessary config options --------- Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>
1 parent 234bc30 commit 2fccaf1

1 file changed

Lines changed: 120 additions & 32 deletions

File tree

src/app-engine.js

Lines changed: 120 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,142 @@
11
import { env } from 'node:process'
22

3+
/**
4+
* Generate `allowedHosts` config for `AngularAppEngine` from `@angular/ssr`
5+
* @returns {string[]}
6+
*/
37
export function getAllowedHosts() {
4-
const allowedHosts = []
8+
const defaultAllowedHosts = []
9+
10+
let deployId
11+
let deployPrimeUrlHostname
12+
let siteId
13+
let siteName
14+
515
const environmentVariables = ['DEPLOY_ID', 'DEPLOY_PRIME_URL', 'DEPLOY_URL', 'SITE_ID', 'SITE_NAME', 'URL']
616

717
for (const environmentVariable of environmentVariables) {
8-
if (!env[environmentVariable] || env[environmentVariable] === 'undefined') {
9-
console.warn(
10-
`Missing Netlify-specific environment variable ${environmentVariable}. \`allowedHosts\` config might be incomplete.`,
11-
)
12-
return allowedHosts
18+
switch (environmentVariable) {
19+
case 'DEPLOY_ID':
20+
// not setting this directly above so that validation
21+
// and warnings remain centralized in the helper
22+
deployId = getEnvironmentVariable(environmentVariable)
23+
break
24+
case 'DEPLOY_PRIME_URL':
25+
// DEPLOY_PRIME_URL:
26+
// <branch-name>--<subdomain>.netlify.app or
27+
// <dp-#>--<subdomain>.netlify.app (supports ADS)
28+
deployPrimeUrlHostname = getHostnameFromEnvironmentVariable(environmentVariable)
29+
if (deployPrimeUrlHostname) {
30+
defaultAllowedHosts.push(deployPrimeUrlHostname)
31+
}
32+
break
33+
case 'DEPLOY_URL':
34+
case 'URL': {
35+
// DEPLOY_URL: <deploy-id>--<subdomain>.netlify.app
36+
// URL: <subdomain>.netlify.app OR <custom-domain>
37+
// www handling is not required as Netlify auto-redirects
38+
const hostname = getHostnameFromEnvironmentVariable(environmentVariable)
39+
if (hostname) {
40+
defaultAllowedHosts.push(hostname)
41+
}
42+
break
43+
}
44+
case 'SITE_ID':
45+
// SITE_ID: <site_id>
46+
// this makes it <site_id>.netlify.app
47+
// separate handling because we need it later
48+
siteId = getEnvironmentVariable(environmentVariable)
49+
if (siteId) {
50+
defaultAllowedHosts.push(`${siteId}.netlify.app`)
51+
}
52+
break
53+
case 'SITE_NAME':
54+
// SITE_NAME: <subdomain>
55+
// this makes it <subdomain>.netlify.app
56+
// this may duplicate URL for sites without a custom domain
57+
// but duplicates are removed later and this is still useful
58+
// if a custom domain is removed after deployment
59+
siteName = getEnvironmentVariable(environmentVariable)
60+
if (siteName) {
61+
defaultAllowedHosts.push(`${siteName}.netlify.app`)
62+
}
63+
break
64+
default:
65+
break
1366
}
1467
}
1568

16-
const deployPrimeUrlHostname = new URL(env.DEPLOY_PRIME_URL).hostname
17-
18-
// <subdomain>.netlify.app OR <custom-domain>
19-
// www handling is not required as Netlify will auto-redirect
20-
allowedHosts.push(new URL(env.URL).hostname)
21-
// <deploy-id>--<subdomain>.netlify.app
22-
allowedHosts.push(new URL(env.DEPLOY_URL).hostname)
23-
// <branch-name>--<subdomain>.netlify.app or <dp-#>--<subdomain>.netlify.app (supports ADS)
24-
allowedHosts.push(deployPrimeUrlHostname)
25-
// <subdomain>.netlify.app
26-
// this will be duplicated for sites without custom domain
27-
// but it's important to have in case a site's custom domain is removed after a deploy
28-
allowedHosts.push(`${env.SITE_NAME}.netlify.app`)
29-
// <site-id>.netlify.app
30-
allowedHosts.push(`${env.SITE_ID}.netlify.app`)
31-
// <deploy-id>--<site-id>.netlify.app
32-
allowedHosts.push(`${env.DEPLOY_ID}--${env.SITE_ID}.netlify.app`)
33-
34-
// we need to extract the branch name or the deploy preview number
35-
// so we can add the subdomain as well as site-id specific URLs
36-
// this would be required for sites using ADS so that
37-
// we can add netlify.app URLs as well
38-
if (deployPrimeUrlHostname.includes('--')) {
69+
// extract the branch name or deploy preview number
70+
// so we can generate ADS-compatible URLs
71+
if (deployPrimeUrlHostname?.includes('--') && siteId && siteName) {
3972
const [branchNameOrDpNumber] = deployPrimeUrlHostname.split('--')
40-
allowedHosts.push(`${branchNameOrDpNumber}--${env.SITE_NAME}.netlify.app`)
41-
allowedHosts.push(`${branchNameOrDpNumber}--${env.SITE_ID}.netlify.app`)
73+
defaultAllowedHosts.push(`${branchNameOrDpNumber}--${siteName}.netlify.app`)
74+
defaultAllowedHosts.push(`${branchNameOrDpNumber}--${siteId}.netlify.app`)
75+
}
76+
77+
if (deployId && siteId) {
78+
// <deploy-id>--<site-id>.netlify.app
79+
defaultAllowedHosts.push(`${deployId}--${siteId}.netlify.app`)
4280
}
4381

44-
return allowedHosts
82+
return [...new Set(defaultAllowedHosts)]
4583
}
4684

85+
/**
86+
* Return Netlify-specific context
87+
* @returns {import('@netlify/edge-functions').Context | undefined}
88+
*/
4789
export function getContext() {
4890
// eslint-disable-next-line no-undef
4991
return typeof Netlify !== 'undefined' ? Netlify?.context : undefined
5092
}
5193

94+
/**
95+
* Return the value of an environment variable
96+
* @param {string} environmentVariable
97+
*
98+
* @returns {string | undefined}
99+
*/
100+
function getEnvironmentVariable(environmentVariable) {
101+
const value = env[environmentVariable]
102+
103+
// we inject the literal string "undefined" for variables that don't have a value
104+
if (value == null || value === '' || value === 'undefined') {
105+
console.warn(
106+
`Missing Netlify-specific environment variable ${environmentVariable}. ` +
107+
'`allowedHosts` config might be incomplete.',
108+
)
109+
110+
return
111+
}
112+
113+
return value
114+
}
115+
116+
/**
117+
* Return hostname from the value of an environment variable
118+
* @param {string} environmentVariable
119+
*
120+
* @returns {string | undefined}
121+
*/
122+
function getHostnameFromEnvironmentVariable(environmentVariable) {
123+
const value = getEnvironmentVariable(environmentVariable)
124+
125+
if (value == null) {
126+
return
127+
}
128+
129+
try {
130+
return new URL(value).hostname
131+
} catch {
132+
console.warn(`Netlify-specific environment variable ${environmentVariable} does not contain a valid URL`)
133+
}
134+
}
135+
136+
/**
137+
* Generate `trustProxyHeaders` config for `AngularAppEngine` from `@angular/ssr`
138+
* @returns {string[]}
139+
*/
52140
export function getTrustProxyHeaders() {
53141
return ['x-forwarded-for']
54142
}

0 commit comments

Comments
 (0)