import path from 'path' import got from 'got' import patterns from '../lib/patterns.js' import isArchivedVersion from '../lib/is-archived-version.js' import { cacheControlFactory } from './cache-control.js' const cacheControl = cacheControlFactory(60 * 60 * 24) // This module handles requests for the CSS and JS assets for // deprecated GitHub Enterprise versions by routing them to static content in // help-docs-archived-enterprise-versions // // See also ./archived-enterprise-versions.js for non-CSS/JS paths export default async function archivedEnterpriseVersionsAssets(req, res, next) { // Only match asset paths // This can be true on /enterprise/2.22/_next/static/foo.css // or /_next/static/foo.css if (!patterns.assetPaths.test(req.path)) return next() // We now know the URL is either /enterprise/2.22/_next/static/foo.css // or the regular /_next/static/foo.css. But we're only going to // bother looking it up on https://github.github.com/help-docs-archived-enterprise-versions // if the URL has the enterprise bit in it, or if the path was // /_next/static/foo.css *and* its Referrer had the enterprise // bit in it. if ( !( patterns.getEnterpriseVersionNumber.test(req.path) || patterns.getEnterpriseServerNumber.test(req.path) || patterns.getEnterpriseVersionNumber.test(req.get('referrer')) || patterns.getEnterpriseServerNumber.test(req.get('referrer')) ) ) { return next() } // Now we know the URL is definitely not /_next/static/foo.css // So it's probably /enterprise/2.22/_next/static/foo.css and we // should see if we might find this in the proxied backend. // But `isArchivedVersion()` will only return truthy if the // Referrer header also indicates that the request for this static // asset came from a page const { isArchived, requestedVersion } = isArchivedVersion(req) if (!isArchived) return next() const assetPath = req.path.replace(`/enterprise/${requestedVersion}`, '') const proxyPath = path.join('/', requestedVersion, assetPath) try { const r = await got( `https://github.github.com/help-docs-archived-enterprise-versions${proxyPath}` ) res.set('accept-ranges', 'bytes') res.set('content-type', r.headers['content-type']) res.set('content-length', r.headers['content-length']) res.set('x-is-archived', 'true') res.set('x-robots-tag', 'noindex') // Allow the browser and Fastly to cache these cacheControl(res) return res.send(r.body) } catch (err) { // Primarly for the developers working on tests that mock // requests. If you don't set up `nock` correctly, you might // not realize that and think it failed for other reasons. if (err.toString().includes('Nock: No match for request')) { throw err } // It's important that we don't give up on this by returning a 404 // here. It's better to let this through in case the asset exists // beyond the realm of archived enterprise versions. // For example, image you load // /enterprise-server@2.21/en/DOES/NOT/EXIST in your browser. // Quickly, we discover that the proxying is failing because // it didn't find a page called `/en/DOES/NOT/EXIST` over there. // So, we proceed to render *our* 404 HTML page. // Now, on that 404 page, it will reference static assets too. // E.g. // These will thus be requested, with a Referrer header that // forces us to give it a chance, but it'll find it can't find it // but we mustn't return a 404 yet, because that // /_next/static/styles.css will probably still succeed because the 404 // page is not that of the archived enterprise version. return next() } }