| import type { NextFunction, Response } from 'express' |
|
|
| import patterns from '@/frame/lib/patterns' |
| import { pathLanguagePrefixed } from '@/languages/lib/languages-server' |
| import { deprecatedWithFunctionalRedirects } from '@/versions/lib/enterprise-server-releases' |
| import getRedirect from '../lib/get-redirect' |
| import { defaultCacheControl, languageCacheControl } from '@/frame/middleware/cache-control' |
| import { ExtendedRequest, URLSearchParamsTypes } from '@/types' |
|
|
| export default function handleRedirects(req: ExtendedRequest, res: Response, next: NextFunction) { |
| if (!req.context) throw new Error('Request not contextualized') |
|
|
| |
| |
| |
| if (req.path.includes('//')) { |
| return res.redirect(301, req.path.replace(/\/+/g, '/')) |
| } |
|
|
| |
| if (patterns.assetPaths.test(req.path)) return next() |
|
|
| |
| |
| if (req.path.startsWith('/api/')) return next() |
|
|
| |
| if (req.path === '/') { |
| const language = getLanguage(req) |
| languageCacheControl(res) |
| |
| let queryParams = new URLSearchParams((req?.query as any) || '').toString() |
| if (queryParams) { |
| queryParams = `?${queryParams}` |
| } |
| return res.redirect(302, `/${language}${queryParams}`) |
| } |
|
|
| |
| let redirect = req.path |
| let queryParams = req.originalUrl.includes('?') ? req.originalUrl.split('?')[1] : null |
|
|
| |
| |
| |
| |
| |
| |
| const onSearch = req.path.endsWith('/search') || req.path.startsWith('/api/search') |
| const hasQ = 'q' in req.query |
| const hasQuery = 'query' in req.query |
| if ((hasQ && !hasQuery) || (hasQuery && !onSearch)) { |
| const language = getLanguage(req) |
| const sp = new URLSearchParams(req.query as URLSearchParamsTypes) |
| if (sp.has('q') && !sp.has('query')) { |
| sp.set('query', sp.get('q')!) |
| sp.delete('q') |
| } |
|
|
| let redirectTo = `/${language}` |
| const { currentVersion } = req.context |
| if (currentVersion !== 'free-pro-team@latest') { |
| redirectTo += `/${currentVersion}` |
| |
| |
| |
| redirectTo = getRedirect(redirectTo, req.context) || redirectTo |
| } |
|
|
| redirectTo += `/search?${sp.toString()}` |
| return res.redirect(301, redirectTo) |
| } |
|
|
| |
| if (queryParams) { |
| queryParams = `?${queryParams}` |
| } |
|
|
| |
| let redirectWithoutQueryParams = removeQueryParams(redirect) |
|
|
| const redirectTo = getRedirect(redirectWithoutQueryParams, req.context) |
|
|
| redirectWithoutQueryParams = redirectTo || redirectWithoutQueryParams |
|
|
| redirect = queryParams ? redirectWithoutQueryParams + queryParams : redirectWithoutQueryParams |
|
|
| if (!redirectTo && !pathLanguagePrefixed(req.path)) { |
| |
| |
| |
| |
| |
| |
| |
| |
| const possibleRedirectTo = `/en${req.path}` |
| if (!req.context.pages) throw new Error('req.context.pages not yet set') |
| if (possibleRedirectTo in req.context.pages || isDeprecatedVersion(req.path)) { |
| const language = getLanguage(req) |
|
|
| |
| |
| |
| redirect = `/${language}${req.url}` |
| } |
| } |
|
|
| |
| if (redirect === req.originalUrl) { |
| return next() |
| } |
|
|
| if (!req.context.pages) throw new Error('req.context.pages not yet set') |
|
|
| |
| if ( |
| !(req.context.pages[removeQueryParams(redirect)] || isDeprecatedVersion(req.path)) && |
| !redirect.includes('://') |
| ) { |
| |
| |
| if (process.env.NODE_ENV !== 'production' && req.context) { |
| req.context.redirectNotFound = redirect |
| } |
| return next() |
| } |
|
|
| |
| if (pathLanguagePrefixed(req.path) || redirect.includes('://')) { |
| defaultCacheControl(res) |
| } else { |
| languageCacheControl(res) |
| } |
|
|
| const permanent = redirect.includes('://') || usePermanentRedirect(req) |
| return res.redirect(permanent ? 301 : 302, redirect) |
| } |
|
|
| function getLanguage(req: ExtendedRequest, default_ = 'en') { |
| |
| |
| return req.context!.userLanguage || default_ |
| } |
|
|
| function usePermanentRedirect(req: ExtendedRequest) { |
| |
| |
| |
| |
| |
| |
| if (req.path.includes('/enterprise-server@latest')) return false |
|
|
| |
| |
| |
| |
| if (pathLanguagePrefixed(req.path)) return true |
|
|
| |
| return false |
| } |
|
|
| function removeQueryParams(redirect: string) { |
| return new URL(redirect, 'https://docs.github.com').pathname |
| } |
|
|
| function isDeprecatedVersion(path: string) { |
| |
| |
| |
| |
| |
| |
| const split = path.split('/') |
| for (const version of deprecatedWithFunctionalRedirects) { |
| if (split.includes(`enterprise-server@${version}`)) { |
| return true |
| } |
| } |
| return false |
| } |
|
|