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).*)', + ], +}