Gwk-Cultural-Park/pages/api/preview.ts
2024-09-07 08:40:25 +07:00

99 lines
3.5 KiB
TypeScript

// API: Preview
import {
apiVersion,
dataset,
previewSecretId,
projectId,
useCdn,
} from 'lib/sanity.api'
import { pageBySlugQuery, postBySlugQuery } from 'lib/sanity.queries'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { PageConfig } from 'next/types'
import { createClient } from 'next-sanity'
import { getSecret } from 'plugins/productionUrl/utils'
// res.setPreviewData only exists in the nodejs runtime, setting the config here allows changing the global runtime
// option in next.config.mjs without breaking preview mode
export const config: PageConfig = { runtime: 'nodejs' }
function redirectToPreview(
res: NextApiResponse<string | void>,
previewData: { token?: string },
Location: '/' | `/posts/${string}`
): void {
// Enable Preview Mode by setting the cookies
res.setPreviewData(previewData)
// Redirect to a preview capable route
res.writeHead(307, { Location })
res.end()
}
const _client = createClient({ projectId, dataset, apiVersion, useCdn })
export default async function preview(
req: NextApiRequest,
res: NextApiResponse<string | void>
) {
const previewData: { token?: string } = {}
// If you want to require preview mode sessions to be started from the Studio, set the SANITY_REQUIRE_PREVIEW_SECRET
// environment variable to 'true'. The benefit of doing this that unauthorized users attempting to brute force into your
// preview mode won't make it past the secret check, and only legitimate users are able to bypass the statically generated pages and load up
// the serverless-powered preview mode.
if (
process.env.SANITY_REQUIRE_PREVIEW_SECRET === 'true' &&
!req.query.secret
) {
return res.status(401).send('Invalid secret')
}
// If a secret is present in the URL, verify it and if valid we upgrade to token based preview mode, which works in Safari and Incognito mode
if (req.query.secret) {
const token = process.env.SANITY_API_READ_TOKEN
if (!token) {
throw new Error(
'A secret is provided but there is no `SANITY_API_READ_TOKEN` environment variable setup.'
)
}
const client = _client.withConfig({ useCdn: false, token })
const secret = await getSecret(client, previewSecretId)
if (req.query.secret !== secret) {
return res.status(401).send('Invalid secret')
}
previewData.token = token
}
// If no slug is provided open preview mode on the frontpage
if (!req.query.slug) {
return redirectToPreview(res, previewData, '/')
}
// Check if the post with the given `slug` exists
const client = _client.withConfig({
// Fallback to using the WRITE token until https://www.sanity.io/docs/vercel-integration starts shipping a READ token.
// As this client only exists on the server and the token is never shared with the browser, we don't risk escalating permissions to untrustworthy users
token:
process.env.SANITY_API_READ_TOKEN || process.env.SANITY_API_WRITE_TOKEN,
})
const post = await client.fetch(postBySlugQuery, {
slug: req.query.slug,
})
const page = await client.fetch(pageBySlugQuery, {
slug: req.query.slug,
})
// If the slug doesn't exist prevent preview mode from being enabled
if (!post) {
return res.status(401).send('Invalid slug')
}
if (!page) {
return res.status(401).send('Invalid slug')
}
// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
redirectToPreview(res, previewData, `/posts/${post.slug}`)
}