diff --git a/build-libs/__tests__/redirects.test.ts b/build-libs/__tests__/redirects.test.ts index 1b40b40771..6417b88531 100644 --- a/build-libs/__tests__/redirects.test.ts +++ b/build-libs/__tests__/redirects.test.ts @@ -6,7 +6,6 @@ import { splitRedirectsByType, groupSimpleRedirects, - addHostCondition, filterInvalidRedirects, } from '../redirects' @@ -165,62 +164,6 @@ describe('groupSimpleRedirects', () => { }) }) -describe('addHostCondition', () => { - test('adds developer host condition for GA products in production', () => { - const redirect = { - source: '/vault/docs/foo', - destination: '/vault/docs/bar', - permanent: true, - } - - let result - - withHashiEnv('production', () => { - result = addHostCondition([redirect], 'vault') - }) - - expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "destination": "/vault/docs/bar", - "has": Array [ - Object { - "type": "host", - "value": "developer.hashicorp.com", - }, - ], - "permanent": true, - "source": "/vault/docs/foo", - }, - ] - `) - }) - - test('does not add developer host condition for GA products in non-production', () => { - const redirect = { - source: '/vault/docs/foo', - destination: '/vault/docs/bar', - permanent: true, - } - - let result - - withHashiEnv('preview', () => { - result = addHostCondition([redirect], 'vault') - }) - - expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "destination": "/vault/docs/bar", - "permanent": true, - "source": "/vault/docs/foo", - }, - ] - `) - }) -}) - describe('filterInvalidRedirects', () => { it('filters out redirects that are not prefixed with the product slug', () => { // Spy on and suppress console.warn for this test, we expect a warning diff --git a/build-libs/load-proxied-site-redirects.js b/build-libs/load-proxied-site-redirects.js deleted file mode 100644 index 59dc858e96..0000000000 --- a/build-libs/load-proxied-site-redirects.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -const fs = require('fs') -const path = require('path') -const { isPreview } = require('../src/lib/env-checks') - -/** - * Loads redirects from the `product-redirects` folder and automatically applies the necessary host - * condition so that the redirects apply to the correct domain. The domain is derived from the filename: - * - * `www.waypointproject.io.redirects.js` will define redirects for `www.waypointproject.io` - */ -module.exports = async function loadProxiedSiteRedirects() { - const redirectFiles = await fs.promises.readdir( - path.join(process.cwd(), 'proxied-redirects') - ) - - let redirects = [] - - for (const redirectFile of redirectFiles) { - // Don't apply the test redirect in production, it's an unnecessary definition and only used for testing in lower environments. - if ( - process.env.HASHI_ENV === 'production' && - redirectFile === 'www.test-domain.io.redirects.js' - ) { - continue - } - - const domain = redirectFile.replace('.redirects.js', '') - - console.log(`[redirects] loading redirects for ${domain}`) - let redirectsConfig = [] - try { - redirectsConfig = require(`../proxied-redirects/${redirectFile}`) - } catch (err) { - console.log(`[redirects] unable to load redirects for ${domain}: `, err) - } - - const redirectsWithHostCondition = redirectsConfig.map((redirect) => - addHostConditionToProxiedSiteRedirect(domain, redirect) - ) - - redirects = redirects.concat(redirectsWithHostCondition) - } - - return redirects -} - -/** - * This is a helper util to add cookie & host matching conditions - * @see https://nextjs.org/docs/api-reference/next.config.js/redirects#header-cookie-and-query-matching - */ -function addHostConditionToProxiedSiteRedirect(domain, redirect) { - const hasCondition = isPreview() - ? [ - { - type: 'cookie', - key: 'hc_dd_proxied_site', - value: domain, - }, - ] - : [ - { - type: 'host', - value: domain, - }, - ] - - if (redirect.has) { - redirect.has = [...redirect.has, ...hasCondition] - } else { - redirect.has = [...hasCondition] - } - - return redirect -} diff --git a/build-libs/redirects.js b/build-libs/redirects.js index 2fb7e68b61..e09e74b6cc 100644 --- a/build-libs/redirects.js +++ b/build-libs/redirects.js @@ -7,18 +7,9 @@ const fs = require('fs') const path = require('path') -/** - * TODO: clean this up, proxySettings was previously a non-empty import - * Task: https://app.asana.com/0/1204759533834554/1206183781878379/f - */ -const proxySettings = {} -const { - getProxiedProductSlug, - isPreview, - isDeployPreview, -} = require('../src/lib/env-checks') + +const { isDeployPreview } = require('../src/lib/env-checks') const fetchGithubFile = require('./fetch-github-file') -const loadProxiedSiteRedirects = require('./load-proxied-site-redirects') const { getTutorialRedirects } = require('./tutorial-redirects') const { getDocsDotHashiCorpRedirects, @@ -32,122 +23,12 @@ require('isomorphic-unfetch') /** @typedef { import("next/dist/lib/load-custom-routes").Redirect } Redirect */ -const PROXIED_PRODUCT = getProxiedProductSlug() - // copied from src/constants/hostname-map.ts so it's usable at build-time in the next config const HOSTNAME_MAP = { 'docs.hashicorp.com': 'sentinel', 'test-st.hashi-mktg.com': 'sentinel', } -// Redirect all proxied product pages -// to the appropriate product domain -// -// Note: we do this for ALL domains, as we never want visitors to -// see the original "proxied" routes, no matter what domain they're on. -const productsToProxy = Object.keys(proxySettings) -// In preview environments, it's actually nice to NOT have these redirects, -// as they prevent us from seeing the content we build for the preview URL -/** @type {Redirect[]} */ -const devPortalToDotIoRedirects = isPreview() - ? [] - : productsToProxy.reduce((acc, slug) => { - const routesToProxy = proxySettings[slug].routesToProxy - // If we're trying to test this product's redirects in dev, - // then we'll set the domain to an empty string for absolute URLs - const domain = slug == PROXIED_PRODUCT ? '' : proxySettings[slug].domain - const toDotIoRedirects = routesToProxy - .filter(({ skipRedirect }) => !skipRedirect) - .map(({ proxiedRoute, localRoute }) => { - return { - source: localRoute, - destination: domain + proxiedRoute, - permanent: false, - } - }) - return acc.concat(toDotIoRedirects) - }, []) - -/** - * - * @param {Redirect[]} redirects - * @param {string} productSlug - * @returns {Redirect[]} - */ -function addHostCondition(redirects, productSlug) { - const isProxiedProduct = proxySettings[productSlug] !== undefined - const host = proxySettings[productSlug]?.host - return redirects.map((redirect) => { - /** - * If we've explicitly set the DEV_IO env variable, or meet a specific - * (but rarely used) commit message trigger (see `getProxiedProductSlug`), - * then we know we're trying to preview the proxied domain, even without - * any kind of host or other condition. In this case, we return the redirect - * with no additional conditions. - */ - if (productSlug == PROXIED_PRODUCT) { - return redirect - } - - /** - * If the productSlug is NOT a beta product, ie has been migrated to Dev Dot - * and does not have its docs hosted on a proxied domain, handle it as such. - * Note we'll have validated these author-controlled redirects - * in `filterInvalidRedirects`, to ensure they start with `/`. - */ - if (!isProxiedProduct) { - // The redirect should always apply in lower environments - if (process.env.HASHI_ENV !== 'production') { - return redirect - } - - /** - * For production, only apply the redirect for the developer domain, as - * other proxied domains still exist, and we don't want to affect those. - * TODO: remove this once Sentinel is migrated, as we'll no longer - * have proxied domains. Asana task: - * https://app.asana.com/0/1203706321379360/1205838575366383/f - */ - return { - ...redirect, - has: [ - { - type: 'host', - value: 'developer.hashicorp.com', - }, - ], - } - } else if (isPreview()) { - // To enable previewing of .io sites, we accept an hc_dd_proxied_site - // cookie which must have a value matching a product slug. - return { - ...redirect, - has: [ - { - type: 'cookie', - key: 'hc_dd_proxied_site', - value: host, - }, - ], - } - } else { - /** - * This default case captures !isPreview() && isProxiedProduct, - * that is, this is a redirect for a proxied product in production. - */ - return { - ...redirect, - has: [ - { - type: 'host', - value: host, - }, - ], - } - } - }) -} - /** * Fetch the latest ref from the content API to ensure the redirects are accurate. * @@ -222,15 +103,11 @@ async function getRedirectsFromContentRepo( /** * Evaluate the redirects file string, filter invalid redirects, and add * a host condition for any sites that have `proxySettings` defined. - * - * TODO(zachshilton): can remove `addHostCondition` once Sentinel is migrated - * (once `docs.hashicorp.com/sentinel` redirects to `developer.hashicorp.com`) - * Task: https://app.asana.com/0/1203706321379360/1205838575366383/f */ /** @type {Redirect[]} */ const parsedRedirects = eval(redirectsFileString) ?? [] const validRedirects = filterInvalidRedirects(parsedRedirects, repoName) - return addHostCondition(validRedirects, repoName) + return validRedirects } async function buildProductRedirects() { @@ -257,7 +134,7 @@ async function buildProductRedirects() { ]) ).flat() - return [...devPortalToDotIoRedirects, ...productRedirects] + return productRedirects } /** @@ -388,17 +265,10 @@ function filterInvalidRedirects(redirects, repoSlug) { * Filter out any redirects not prefixed with the `product` slug. */ const validRedirects = redirects.filter((entry) => { - /** - * Proxied product redirects are assumed to be valid. - * TODO: we can remove this once Sentinel is migrated, as it's the - * last proxied product. Asana task: - * https://app.asana.com/0/1203706321379360/1205838575366383/f - */ - const isProxiedProduct = Boolean(proxySettings[repoSlug]) // Redirects for non-proxied must be prefixed with the product slug. const isPrefixed = entry.source.startsWith(`/${productSlug}`) // Keep track of invalid redirects, we want to warn about these - const isValidRedirect = isProxiedProduct || isPrefixed + const isValidRedirect = isPrefixed if (!isValidRedirect) { invalidRedirects.push(entry) } @@ -429,15 +299,6 @@ function filterInvalidRedirects(redirects, repoSlug) { * @param {Redirect[]} redirects */ function groupSimpleRedirects(redirects) { - /** @type {Record} */ - const hostMatching = Object.entries(proxySettings).reduce( - (acc, [productSlug, productProxySettings]) => { - acc[productProxySettings.host] = productSlug - return acc - }, - {} - ) - /** @type {Record>} */ const groupedRedirects = {} redirects.forEach((redirect) => { @@ -447,7 +308,7 @@ function groupSimpleRedirects(redirects) { const hasHostValue = redirect.has[0].value // this handles the scenario where redirects are built through our proxy config and have the host value matching what is defined in build-libs/proxy-config.js - product = hostMatching[hasHostValue] ?? HOSTNAME_MAP[hasHostValue] + product = HOSTNAME_MAP[hasHostValue] } else { // this handles the `hc_dd_proxied_site` cookie product = HOSTNAME_MAP[redirect.has[0].value] @@ -491,12 +352,10 @@ function groupSimpleRedirects(redirects) { async function redirectsConfig() { const productRedirects = await buildProductRedirects() const devPortalRedirects = await buildDevPortalRedirects() - const proxiedSiteRedirects = await loadProxiedSiteRedirects() const tutorialRedirects = await getTutorialRedirects() const docsDotHashiCorpRedirects = getDocsDotHashiCorpRedirects() const { simpleRedirects, complexRedirects } = splitRedirectsByType([ - ...proxiedSiteRedirects, ...productRedirects, ...devPortalRedirects, ...tutorialRedirects, @@ -523,6 +382,5 @@ module.exports = { redirectsConfig, splitRedirectsByType, groupSimpleRedirects, - addHostCondition, filterInvalidRedirects, } diff --git a/build-libs/rewrites.js b/build-libs/rewrites.js index 4d7649f49e..8d6baf20d0 100644 --- a/build-libs/rewrites.js +++ b/build-libs/rewrites.js @@ -10,12 +10,10 @@ * Task: https://app.asana.com/0/1204759533834554/1206183781878379/f */ const proxySettings = {} -const { getProxiedProductSlug, isPreview } = require('../src/lib/env-checks') +const { isPreview } = require('../src/lib/env-checks') /** @typedef { import("next/dist/lib/load-custom-routes").Redirect } Redirect */ -const PROXIED_PRODUCT = getProxiedProductSlug() - /** * # Some notes on rewrites * @@ -60,14 +58,13 @@ const dotIoRewrites = productsToProxy.reduce((acc, slug) => { source: proxiedRoute, destination: localRoute, } - if (slug !== PROXIED_PRODUCT) { - rewrite.has = [ - { - type: 'host', - value: proxySettings[slug].host, - }, - ] - } + + rewrite.has = [ + { + type: 'host', + value: proxySettings[slug].host, + }, + ] // To enable previewing of .io sites, we accept an hc_dd_proxied_site cookie which must have a value matching a product slug if (isPreview()) { diff --git a/src/__tests__/e2e/proxied-redirects.spec.ts b/src/__tests__/e2e/proxied-redirects.spec.ts deleted file mode 100644 index f5d14905e6..0000000000 --- a/src/__tests__/e2e/proxied-redirects.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import { test, expect } from '@playwright/test' - -// see redirect fixture at proxied-redirects/www.test-domain.io.redirects.js -test('should respect redirects specified for proxied domains', async ({ - page, - context, - baseURL, -}) => { - await context.addCookies([ - { - name: 'hc_dd_proxied_site', - value: 'www.test-domain.io', - url: baseURL, - }, - ]) - - await page.goto('/source') - const { pathname } = new URL(page.url()) - expect(pathname).toBe('/') -}) diff --git a/src/lib/env-checks.js b/src/lib/env-checks.js index eef6c53b52..119424ed2c 100644 --- a/src/lib/env-checks.js +++ b/src/lib/env-checks.js @@ -5,86 +5,10 @@ //@ts-check -/** - * TODO: clean this up, proxyConfig was previously a non-empty import - * Task: https://app.asana.com/0/1204759533834554/1206183781878379/f - */ -const proxyConfig = {} - -// NOTE: this module uses CommonJS exports, -// as it must be required() into redirects and rewrites config, -// neither of which are transpiled. - -const PROXIED_PRODUCTS = Object.keys(proxyConfig) - function isPreview() { return process.env.HASHI_ENV == 'preview' } -/** - * - * @param {string=} hostname - * @returns {string | boolean} - */ -function getProxiedProductSlug(hostname) { - const proxiedProductSlug = PROXIED_PRODUCTS.reduce((acc, slug) => { - if (!acc && isProxiedProduct(slug, hostname)) { - return slug - } - return acc - }, false) - return proxiedProductSlug -} - -/** - * - * @param {string=} hostname - * @returns {string | boolean | undefined} - */ -function getMatchedDomain(hostname) { - if (!hostname) { - return - } - const domainProductSlug = PROXIED_PRODUCTS.reduce((acc, slug) => { - const productHost = proxyConfig[slug].host - if (!acc && hostname.match(new RegExp(productHost))) { - return slug - } - return acc - }, false) - return domainProductSlug -} - -/** - * - * @param {string} productSlug - * @param {string=} hostname - * @returns {boolean} - */ -function isProxiedProduct(productSlug, hostname) { - const isDevEnvSet = process.env.DEV_IO == productSlug - // Allow commit messages to trigger specific proxy settings, - // but NOT if we're deploying off the main branch. - const commitMsg = - process.env.VERCEL_GIT_COMMIT_MESSAGE || - process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE || - '' - const commitFirstLine = commitMsg.split('\n')[0] - const isCommitMatch = commitFirstLine.indexOf(`(${productSlug})`) !== -1 - // ... but only if NOT in production - const commitRef = - process.env.VERCEL_GIT_COMMIT_REF || - process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF - const isOnMain = commitRef == 'main' - // When deploying to specific proxied domains, - // this function should accurately reflect the proxied product - const isDomainMatch = productSlug == getMatchedDomain(hostname) - // Combine local and deployed settings - const isLocalMatch = isDevEnvSet - const isDeployedMatch = isDomainMatch || (isCommitMatch && !isOnMain) - return isLocalMatch || isDeployedMatch -} - function isDeployPreview(productSlug) { const isProductSlugMatching = !productSlug || productSlug === process.env.PREVIEW_FROM_REPO @@ -105,7 +29,6 @@ function isVersionedDocsEnabled(productSlug) { } module.exports = { - getProxiedProductSlug, isPreview, isDeployPreview, isVersionedDocsEnabled,