Skip to content

Commit

Permalink
fix: add withMakeswift middleware for draft mode (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewvolk authored Jun 21, 2024
1 parent 679d0b1 commit 889b9ec
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 2 deletions.
27 changes: 27 additions & 0 deletions app/api/makeswift/draft-mode/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { draftMode } from 'next/headers';
import { NextRequest } from 'next/server';

export const GET = (request: NextRequest) => {
/**
* This route is called by the withMakeswift middleware to enable draft mode.
*
* First, we check if the request has the Makeswift API key attached to it.
* This is to prevent unauthorized access to the draft mode endpoint.
*/
if (request.headers.get('x-makeswift-api-key') === process.env.MAKESWIFT_SITE_API_KEY) {
/**
* Then, we enable draft mode using `draftMode().enable()`.
*
* According to the Next.js docs on Draft Mode, `draftMode().enable()` does two things:
* 1. Adds a 'Set-Cookie' header containing the `__prerender_bypass` cookie on the response
* 2. If you request a page which has the cookie set, then data will be fetched at request
* time (instead of at build time).
*/
draftMode().enable();
}

/**
* Finally, we return a 200 response with an empty body
*/
return new Response(null);
};
3 changes: 2 additions & 1 deletion middleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { composeMiddlewares } from './middlewares/compose-middlewares';
import { withAuth } from './middlewares/with-auth';
import { withMakeswift } from './middlewares/with-makeswift';
import { withRoutes } from './middlewares/with-routes';

export const middleware = composeMiddlewares(withAuth, withRoutes);
export const middleware = composeMiddlewares(withAuth, withMakeswift, withRoutes);

export const config = {
matcher: [
Expand Down
66 changes: 66 additions & 0 deletions middlewares/with-makeswift.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { NextRequest } from 'next/server';
import { parse as parseSetCookie } from 'set-cookie-parser';

import { MiddlewareFactory } from './compose-middlewares';

export const withMakeswift: MiddlewareFactory = (middleware) => {
return async (request, event) => {
/**
* Check if the incoming request URL has a secret attached to it
*
* @todo either by query param _or_ URL?
*/
const apiKey = request.nextUrl.searchParams.get('x-makeswift-draft-mode');

/**
* Check that the secret matches the given Makeswift API key
*/
if (apiKey === process.env.MAKESWIFT_SITE_API_KEY) {
/**
* If it does, initiate a fetch request to an API endpoint. This endpoint
* simply enables draft mode and then returns the response. We can also
* attach other data to this response via cookies/headers as well
*/
const response = await fetch(new URL('/api/makeswift/draft-mode', request.nextUrl.origin), {
headers: {
'x-makeswift-api-key': apiKey,
},
});

/**
* If the request to enable draft mode is successful, it will have the
* `__prerender_bypass` value in its `Set-Cookie` header. We can extract
* this value and attach it to the incoming request.
*/
const cookies = parseSetCookie(response.headers.get('set-cookie') || '');
const prerenderBypassValue = cookies.find((c) => c.name === '__prerender_bypass')?.value;

/**
* If the `__prerender_bypass` cookie is not found, continue to the next
* middleware without modifying the request
*/
if (!prerenderBypassValue) {
return middleware(request, event);
}

const proxiedRequest = new NextRequest(request);

proxiedRequest.cookies.set('__prerender_bypass', prerenderBypassValue);
proxiedRequest.cookies.set(
'x-makeswift-draft-data',
JSON.stringify({ makeswift: true, siteVersion: 'Working' }),
);

/**
* Continue to the next middleware with the modified request
*/
return middleware(proxiedRequest, event);
}

/**
* If incoming request URL does not have a secret attached to it,
* continue to the next middleware without modifying the request
*/
return middleware(request, event);
};
};
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const createWithMakeswift = require('@makeswift/runtime/next/plugin');
const createNextIntlPlugin = require('next-intl/plugin');

const withMakeswift = createWithMakeswift();
const withMakeswift = createWithMakeswift({ previewMode: false });
const withNextIntl = createNextIntlPlugin();

// @todo relax csp for makeswift embedding
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"react-hot-toast": "^2.4.1",
"schema-dts": "^1.1.2",
"server-only": "^0.0.1",
"set-cookie-parser": "^2.6.0",
"sharp": "^0.33.4",
"tailwind-merge": "^2.3.0",
"tailwindcss-radix": "^3.0.3",
Expand All @@ -64,6 +65,7 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-google-recaptcha": "^2.1.9",
"@types/set-cookie-parser": "^2.4.9",
"autoprefixer": "^10.4.19",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.4.2",
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 889b9ec

Please sign in to comment.