-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add redirects/rewrite self-serve config (#358)
- Loading branch information
Showing
7 changed files
with
2,584 additions
and
2,608 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
{ | ||
"redirects": [ | ||
{ | ||
"name": "Redirect /design/accessibility", | ||
"match": "(design)/(accessibility)(.*)", | ||
"destination": "/design/guides/accessibility{R:3}" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/packages/ruby", | ||
"match": "octicons/packages/ruby", | ||
"destination": "https://github.com/primer/octicons/tree/main/lib/octicons_gem#readme" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/packages/jekyll", | ||
"match": "octicons/packages/jekyll", | ||
"destination": "https://github.com/primer/octicons/tree/main/lib/octicons_jekyll#readme" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/packages/javascript", | ||
"match": "octicons/packages/javascript", | ||
"destination": "https://github.com/primer/octicons/tree/main/lib/octicons_node#readme" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/packages/react", | ||
"match": "octicons/packages/react", | ||
"destination": "https://github.com/primer/octicons/tree/main/lib/octicons_react#readme" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/packages/styled-system", | ||
"match": "octicons/packages/styled-system", | ||
"destination": "https://github.com/primer/octicons/tree/main/lib/octicons_styled#readme" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/guidelines/design", | ||
"match": "octicons/guidelines/design", | ||
"destination": "/design/foundations/icons/design-guidelines" | ||
}, | ||
{ | ||
"name": "Redirect /octicons/*", | ||
"match": "(octicons)(.*)", | ||
"destination": "/design/foundations/icons{R:2}" | ||
} | ||
], | ||
"rewrites": [ | ||
{ | ||
"name": "ViewComponents Preview assets proxy", | ||
"match": "^assets/(.*)", | ||
"destination": "https://view-components-storybook.eastus.cloudapp.azure.com/assets/{R:1}" | ||
}, | ||
{ | ||
"name": "ViewComponents Lookbook assets proxy", | ||
"match": "^lookbook-assets/(.*)", | ||
"destination": "https://view-components-storybook.eastus.cloudapp.azure.com/lookbook-assets/{R:1}" | ||
}, | ||
{ | ||
"name": "ViewComponents Lookbook proxy", | ||
"match": "^view-components/lookbook/(.*)", | ||
"destination": "https://view-components-storybook.eastus.cloudapp.azure.com/view-components/lookbook/{R:1}" | ||
}, | ||
{ | ||
"name": "ViewComponents Rails App proxy", | ||
"match": "^view-components/rails-app/(.*)", | ||
"destination": "https://view-components-storybook.eastus.cloudapp.azure.com/view-components/rails-app/{R:1}" | ||
}, | ||
{ | ||
"name": "ViewComponents proxy", | ||
"match": "^view-components/(.*)", | ||
"destination": "https://primer.github.io/view_components/{R:1}" | ||
}, | ||
{ | ||
"name": "Design proxy", | ||
"match": "^design/(.*)", | ||
"destination": "https://primer.github.io/design/{R:1}" | ||
}, | ||
{ | ||
"name": "Presentations proxy", | ||
"match": "^presentations/(.*)", | ||
"destination": "https://primer.github.io/presentations/{R:1}" | ||
}, | ||
{ | ||
"name": "Doctocat", | ||
"match": "^doctocat/(.*)", | ||
"destination": "https://primer.github.io/doctocat/{R:1}" | ||
}, | ||
{ | ||
"name": "CLI proxy", | ||
"match": "^cli/(.*)", | ||
"destination": "https://primer.github.io/cli/{R:1}" | ||
}, | ||
{ | ||
"name": "Primitives proxy", | ||
"match": "^primitives/(.*)", | ||
"destination": "https://primer.github.io/primitives/{R:1}" | ||
}, | ||
{ | ||
"name": "Mobile proxy", | ||
"match": "^mobile/(.*)", | ||
"destination": "https://primer.github.io/mobile/{R:1}" | ||
}, | ||
{ | ||
"name": "Brand proxy", | ||
"match": "^brand/(.*)", | ||
"destination": "https://primer.github.io/brand/{R:1}" | ||
}, | ||
{ | ||
"name": "Desktop proxy", | ||
"match": "^desktop/(.*)", | ||
"destination": "https://primer.github.io/desktop/{R:1}" | ||
}, | ||
{ | ||
"name": "Contribute proxy", | ||
"match": "^contribute/(.*)", | ||
"destination": "https://primer.github.io/contribute/{R:1}" | ||
}, | ||
{ | ||
"name": "Prism proxy", | ||
"match": "^prism/(.*)", | ||
"destination": "https://primer.github.io/prism/{R:1}" | ||
}, | ||
{ | ||
"name": "React reverse-proxy", | ||
"match": "^react/(.*)", | ||
"destination": "https://primer.github.io/react/{R:1}" | ||
}, | ||
{ | ||
"name": "CSS reverse-proxy", | ||
"match": "^css/(.*)", | ||
"destination": "https://primer.github.io/css/{R:1}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
const fs = require('fs') | ||
const path = require('path') | ||
|
||
function validator(input) { | ||
if (!input || typeof input !== "object") { | ||
return { valid: false, error: "Input must be an object" }; | ||
} | ||
|
||
if (!Array.isArray(input.redirects) || !Array.isArray(input.rewrites)) { | ||
return { valid: false, error: "Input must have 'redirects' and 'rewrites' arrays" }; | ||
} | ||
|
||
for (const redirect of input.redirects) { | ||
if ( | ||
typeof redirect !== "object" || | ||
typeof redirect.name !== "string" || | ||
typeof redirect.match !== "string" || | ||
typeof redirect.destination !== "string" | ||
) { | ||
const invalidKey = Object.keys(redirect).find( | ||
(key) => | ||
typeof redirect[key] !== "string" || | ||
(key !== "name" && key !== "match" && key !== "destination") | ||
) || ""; | ||
return { valid: false, error: `'${invalidKey}' redirect has invalid value ` }; | ||
} | ||
|
||
try { | ||
new RegExp(redirect.match); | ||
} catch (error) { | ||
return { valid: false, error: `Redirect has invalid regex in 'match' property` }; | ||
} | ||
} | ||
|
||
for (const rewrite of input.rewrites) { | ||
if ( | ||
typeof rewrite !== "object" || | ||
typeof rewrite.name !== "string" || | ||
typeof rewrite.match !== "string" || | ||
typeof rewrite.destination !== "string" | ||
) { | ||
const invalidKey = Object.keys(rewrite).find( | ||
(key) => | ||
typeof rewrite[key] !== "string" || | ||
(key !== "name" && key !== "match" && key !== "destination") | ||
) || ""; | ||
return { valid: false, error: `'${invalidKey}' rewrite has an invalid value ` }; | ||
} | ||
|
||
try { | ||
new RegExp(rewrite.match); | ||
} catch (error) { | ||
return { valid: false, error: `Rewrite has invalid regex in 'match' property` }; | ||
} | ||
} | ||
|
||
return { valid: true }; | ||
} | ||
|
||
function buildRedirects() { | ||
const inputFile = path.join(__dirname, '../redirects.json') | ||
const outputFile = path.join(__dirname, '../web.config') | ||
|
||
const input = JSON.parse(fs.readFileSync(inputFile)) | ||
|
||
const {valid, error} = validator(input) | ||
|
||
|
||
if (!valid) { | ||
throw new Error(`Invalid redirects.json file. ${error}`) | ||
} | ||
|
||
console.info(`redirects.json was validated successfully` ) | ||
console.info('...') | ||
|
||
const redirects = input.redirects | ||
.map( | ||
({name, match, destination}) => `<rule name="${name}" stopProcessing="true"> | ||
<match url="${match}" /> | ||
<action type="Redirect" redirectType="Permanent" url="${destination}" /> | ||
</rule> | ||
` | ||
) | ||
.join('') | ||
|
||
const rewrites = input.rewrites | ||
.map( | ||
({name, match, destination}) => `<rule name="${name}" stopProcessing="true"> | ||
<match url="${match}" ignoreCase="true" /> | ||
<conditions> | ||
<add input="{HTTP_HOST}" pattern="^(?:www.)?(.*)$" /> | ||
</conditions> | ||
<action type="Rewrite" url="${destination}" /> | ||
<serverVariables> | ||
<set name="HTTP_X_UNPROXIED_URL" value="${destination}" /> | ||
<set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" /> | ||
<set name="HTTP_X_ORIGINAL_HOST" value="{HTTP_HOST}" /> | ||
<set name="HTTP_ACCEPT_ENCODING" value="" /> | ||
</serverVariables> | ||
</rule> | ||
` | ||
) | ||
.join('') | ||
|
||
const config = `<?xml version="1.0" encoding="UTF-8"?> | ||
<configuration> | ||
<system.webServer> | ||
<staticContent> | ||
<mimeMap fileExtension=".json" mimeType="application/json" /> | ||
<mimeMap fileExtension=".webmanifest" mimeType="application/manifest+json" /> | ||
<remove fileExtension=".woff2" /> | ||
<mimeMap fileExtension=".woff2" mimeType="font/woff2" /> | ||
</staticContent> | ||
<httpErrors errorMode="Custom" existingResponse="Auto" defaultResponseMode="ExecuteURL" > | ||
<remove statusCode="404"/> | ||
<error statusCode="404" responseMode="ExecuteURL" path="/404/index.html" /> | ||
</httpErrors> | ||
<rewrite> | ||
<rules> | ||
<!--BEGIN SSL--> | ||
<rule name="ForceSSL" stopProcessing="true"> | ||
<match url="(.*)" /> | ||
<conditions> | ||
<add input="{HTTPS}" pattern="^OFF$" ignoreCase="true" /> | ||
</conditions> | ||
<action type="Redirect" url="https://{C:2}/{R:1}" redirectType="Permanent" /> | ||
</rule> | ||
<!--END SSL--> | ||
<!--BEGIN Trailing slash enforcement--> | ||
<rule name="Add trailing slash" stopProcessing="true"> | ||
<match url="(.*[^/])$" /> | ||
<conditions> | ||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> | ||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> | ||
<add input="{REQUEST_FILENAME}" pattern="(.*?)\\.[a-zA-Z]{1,4}$" negate="true" /> | ||
<add input="{URL}" negate="true" pattern="\\.woff2$" /> | ||
<add input="{URL}" negate="true" pattern="\\.webmanifest$" /> | ||
</conditions> | ||
<action type="Redirect" redirectType="Temporary" url="{R:1}/" /> | ||
</rule> | ||
<!--END Trailing slash enforcement--> | ||
<!--BEGIN 301 redirects. Goes before URL rewrites --> | ||
${redirects} | ||
<!--END 301 redirects --> | ||
${rewrites} | ||
</rules> | ||
<outboundRules> | ||
<preConditions> | ||
<preCondition name="CheckContentType"> | ||
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^(text/html|text/plain|text/xml|application/rss\\+xml)" /> | ||
</preCondition> | ||
</preConditions> | ||
</outboundRules> | ||
</rewrite> | ||
</system.webServer> | ||
</configuration>` | ||
|
||
|
||
fs.writeFileSync(outputFile, config) | ||
console.info(`Redirects built successfully and written to ${outputFile}`) | ||
} | ||
|
||
try { | ||
console.info('Building redirects...') | ||
console.info('...') | ||
buildRedirects() | ||
} catch (error) { | ||
console.error(error) | ||
} |
Oops, something went wrong.