96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
import patterns from '../../lib/patterns.js'
|
|
import { URL } from 'url'
|
|
import languages, { pathLanguagePrefixed } from '../../lib/languages.js'
|
|
import { cacheControlFactory } from '../cache-control.js'
|
|
|
|
const cacheControl = cacheControlFactory(60 * 60 * 24) // one day
|
|
const noCacheControl = cacheControlFactory(0)
|
|
|
|
export default function handleRedirects(req, res, next) {
|
|
// never redirect assets
|
|
if (patterns.assetPaths.test(req.path)) return next()
|
|
|
|
// blanket redirects for languageless homepage
|
|
if (req.path === '/') {
|
|
let language = 'en'
|
|
|
|
// if set, redirect to user's preferred language translation or else English
|
|
if (
|
|
req.context.userLanguage &&
|
|
languages[req.context.userLanguage] &&
|
|
!languages[req.context.userLanguage].wip
|
|
) {
|
|
language = req.context.userLanguage
|
|
}
|
|
|
|
// Undo the cookie setting that CSRF sets.
|
|
res.removeHeader('set-cookie')
|
|
|
|
noCacheControl(res)
|
|
|
|
return res.redirect(302, `/${language}`)
|
|
}
|
|
|
|
// begin redirect handling
|
|
let redirect = req.path
|
|
let queryParams = req._parsedUrl.query
|
|
|
|
// update old-style query params (#9467)
|
|
if ('q' in req.query) {
|
|
const newQueryParams = new URLSearchParams(queryParams)
|
|
newQueryParams.set('query', newQueryParams.get('q'))
|
|
newQueryParams.delete('q')
|
|
return res.redirect(301, `${req.path}?${newQueryParams.toString()}`)
|
|
}
|
|
|
|
// have to do this now because searchPath replacement changes the path as well as the query params
|
|
if (queryParams) {
|
|
queryParams = '?' + queryParams
|
|
redirect = (redirect + queryParams).replace(patterns.searchPath, '$1')
|
|
}
|
|
|
|
// remove query params temporarily so we can find the path in the redirects object
|
|
let redirectWithoutQueryParams = removeQueryParams(redirect)
|
|
|
|
// look for a redirect in the global object
|
|
// for example, given an incoming path /v3/activity/event_types
|
|
// find /en/developers/webhooks-and-events/github-event-types
|
|
redirectWithoutQueryParams =
|
|
req.context.redirects[redirectWithoutQueryParams] || redirectWithoutQueryParams
|
|
|
|
// add query params back in
|
|
redirect = queryParams ? redirectWithoutQueryParams + queryParams : redirectWithoutQueryParams
|
|
|
|
// do not redirect a path to itself
|
|
// req._parsedUrl.path includes query params whereas req.path does not
|
|
if (redirect === req._parsedUrl.path) return next()
|
|
|
|
// do not redirect if the redirected page can't be found
|
|
if (!req.context.pages[removeQueryParams(redirect)]) {
|
|
// display error on the page in development, but not in production
|
|
// include final full redirect path in the message
|
|
if (process.env.NODE_ENV !== 'production' && req.context) {
|
|
req.context.redirectNotFound = redirect
|
|
}
|
|
return next()
|
|
}
|
|
|
|
// Undo the cookie setting that CSRF sets.
|
|
res.removeHeader('set-cookie')
|
|
|
|
// do the redirect if the from-URL already had a language in it
|
|
if (pathLanguagePrefixed(req.path)) {
|
|
cacheControl(res)
|
|
}
|
|
|
|
// If the redirect involved injecting a language prefix, then don't
|
|
// permanently redirect because that could overly cache in users'
|
|
// browsers if we some day want to make the language redirect
|
|
// depend on a cookie or 'Accept-Language' header.
|
|
return res.redirect(pathLanguagePrefixed(req.path) ? 301 : 302, redirect)
|
|
}
|
|
|
|
function removeQueryParams(redirect) {
|
|
return new URL(redirect, 'https://docs.github.com').pathname
|
|
}
|