From 6bbe1b19a1c9db5676cc5c1ef7fb0ca189a806f9 Mon Sep 17 00:00:00 2001 From: Pedro Soares <32311264+pedromtec@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:43:31 -0300 Subject: [PATCH] Sfs 1755 generate middleware in build time (#2540) ## What's the purpose of this pull request? This task will allow enabling the redirects middleware within **@faststore/core** during the build process based on a flag. The idea is to enable this functionality only for projects that have the flag `ENABLE_REDIRECTS_MIDDLEWARE` saved in the env vars and which are hosted by homebrew. In the future, we intend to have control of this feature through webops. Next steps: - Set this flag in a pipeline env var. - Implements dynamoDB client (Reference: https://github.com/vtex/faststore/pull/2517) ## How it works? The key is the name of the file: **middleware__DISABLED.ts** This middleware is disabled by default. Only stores that are in **homebrew** and want this functionality will be able to enable it via a feature flag. When the flag is active, the CLI at build time will check whether the **ENABLE_REDIRECTS_MIDDLEWARE** flag is set or not, if so, the file name will be changed to **middleware.ts** and nextjs will know how to automatically deal with it. ## How to test it? I created a [PR](https://github.com/vtex-sites/starter.store/pull/599) with the flag enabled, clone the branch and then: - Run `yarn build` - check if the **middleware.ts** was created inside **.faststore/src** ### References https://docs.google.com/document/d/1iC1fLwJbypnvDyp_JilKBfQfaHjFDHYrjS1kC722sB4/edit?tab=t.0#heading=h.tglo77yl0lf5 --- packages/cli/src/utils/generate.ts | 35 ++++++++++++ packages/core/src/middleware__DISABLED.ts | 65 +++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 packages/core/src/middleware__DISABLED.ts diff --git a/packages/cli/src/utils/generate.ts b/packages/cli/src/utils/generate.ts index 4ac1f496c4..342a7e859f 100644 --- a/packages/cli/src/utils/generate.ts +++ b/packages/cli/src/utils/generate.ts @@ -437,6 +437,40 @@ function validateAndInstallMissingDependencies(basePath: string) { }) } +// TODO: Read the value from an environment variable +const ENABLE_REDIRECTS_MIDDLEWARE = false + +// Enable redirects middleware by renaming the file from middleware__DISABLED.ts to middleware.tsß +function enableRedirectsMiddleware(basePath: string) { + if (!ENABLE_REDIRECTS_MIDDLEWARE) { + return + } + + try { + const { tmpDir } = withBasePath(basePath) + + const disabledMiddlewarePath = path.join( + tmpDir, + 'src', + 'middleware__DISABLED.ts' + ) + + /* Rename the file to enable middleware functionality and then remove the disabled middleware file */ + if (existsSync(disabledMiddlewarePath)) { + const enabledMiddlewarePath = path.join(tmpDir, 'src', 'middleware.ts') + copyFileSync(disabledMiddlewarePath, enabledMiddlewarePath) + removeSync(disabledMiddlewarePath) + + logger.log( + `${chalk.green('success')} Redirects middleware has been enabled` + ) + } + } catch (error) { + logger.error(error) + throw error + } +} + export async function generate(options: GenerateOptions) { const { basePath, setup = false } = options @@ -461,5 +495,6 @@ export async function generate(options: GenerateOptions) { copyTheme(basePath), createCmsWebhookUrlsJsonFile(basePath), updateNextConfig(basePath), + enableRedirectsMiddleware(basePath), ]) } diff --git a/packages/core/src/middleware__DISABLED.ts b/packages/core/src/middleware__DISABLED.ts new file mode 100644 index 0000000000..2cf3f02eb2 --- /dev/null +++ b/packages/core/src/middleware__DISABLED.ts @@ -0,0 +1,65 @@ +/* + * This middleware is disabled by default. Only stores that are in + * homebrew and want this functionality will be able to enable it via + * a feature flag. When the flag is active, the CLI at build time will + * check whether the ENABLE_REDIRECTS_MIDDLEWARE flag is set or not, + * if so, the file name will be changed to middleware.ts and nextjs + * will know how to automatically deal with it. + */ + +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' +import storeConfig from 'discovery.config' + +type Redirect = { + from: string + to: string + type: 'permanent' | 'temporary' +} +interface RedirectsClient { + get(from: string): Promise +} + +class DynamoRedirectsClient implements RedirectsClient { + async get(from: string): Promise { + // TODO: Implement DynamoDB client. Ensure that the cluster has access to DynamoDB first. + return null + } +} + +const redirectsClient = new DynamoRedirectsClient() + +export async function middleware(request: NextRequest) { + const { pathname } = request.nextUrl + + const redirect = await redirectsClient.get(pathname) + + if (redirect) { + const redirectUrl = new URL(redirect.to, storeConfig.storeUrl) + const redirectStatusCode = redirect.type === 'permanent' ? 301 : 302 + + const response = NextResponse.redirect(redirectUrl, redirectStatusCode) + + response.headers.set( + 'Cache-Control', + 'public, max-age=300, stale-while-revalidate=31536000' + ) + + return response + } + + return NextResponse.next() +} + +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - api (API routes) + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + */ + '/((?!api|_next/static|_next/image|favicon.ico).*)', + ], +}