mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
* Bump all versions of postcss to 8.5.3. * Run next/codemod on the docs app. * Move two experimental flags into stable. Add next-mdx-remote as a transpiled package. * Add extra folders to the clean command in docs. * Fix type errors in docs test. * Run prettier on the new files. * remove turbopack, fix fetch revalidation, fix metadata awaits Couple of minor fixes: - Turbopack doesn't work in dev because of known MDX loader limitations (cannot load functions in MDX plugin config) - Fetches not cached by default anymore in Next 15 so need to manually cache the ones we need - Missing a few awaits for metadata generation with page params * Bump the graphiql version because headlessui/react is not building with Next 15. --------- Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>
118 lines
3.5 KiB
TypeScript
118 lines
3.5 KiB
TypeScript
import { createClient } from '@supabase/supabase-js'
|
|
import { type Database } from 'common'
|
|
import { revalidateTag } from 'next/cache'
|
|
import { headers } from 'next/headers'
|
|
import { type NextRequest } from 'next/server'
|
|
import { z } from 'zod'
|
|
import { VALID_REVALIDATION_TAGS } from '~/features/helpers.fetch'
|
|
|
|
enum AuthorizationLevel {
|
|
Unauthorized,
|
|
Basic,
|
|
Override,
|
|
}
|
|
|
|
const requestBodySchema = z.object({
|
|
tags: z.array(z.enum(VALID_REVALIDATION_TAGS)),
|
|
})
|
|
|
|
export const POST = handleError(_handleRevalidateRequest)
|
|
|
|
export async function _handleRevalidateRequest(request: NextRequest) {
|
|
const requestHeaders = await headers()
|
|
const authorization = requestHeaders.get('Authorization')
|
|
if (!authorization) {
|
|
return new Response('Missing Authorization header', { status: 401 })
|
|
}
|
|
|
|
const basicKeys = process.env.DOCS_REVALIDATION_KEYS?.split(/\s*,\s*/) ?? []
|
|
const overrideKeys = process.env.DOCS_REVALIDATION_OVERRIDE_KEYS?.split(/\s*,\s*/) ?? []
|
|
if (basicKeys.length === 0 && overrideKeys.length === 0) {
|
|
console.error('No keys configured for revalidation')
|
|
return new Response('Internal server error', {
|
|
status: 500,
|
|
})
|
|
}
|
|
|
|
let authorizationLevel = AuthorizationLevel.Unauthorized
|
|
const token = authorization.replace(/^Bearer /, '')
|
|
if (overrideKeys.includes(token)) {
|
|
authorizationLevel = AuthorizationLevel.Override
|
|
} else if (basicKeys.includes(token)) {
|
|
authorizationLevel = AuthorizationLevel.Basic
|
|
}
|
|
if (authorizationLevel === AuthorizationLevel.Unauthorized) {
|
|
return new Response('Invalid Authorization header', { status: 401 })
|
|
}
|
|
|
|
let result: z.infer<typeof requestBodySchema>
|
|
try {
|
|
result = requestBodySchema.parse(await request.json())
|
|
} catch (error) {
|
|
console.error(error)
|
|
return new Response(
|
|
'Malformed request body: should be a JSON object with a "tags" array of strings.',
|
|
{ status: 400 }
|
|
)
|
|
}
|
|
|
|
const supabaseAdmin = createClient<Database>(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.SUPABASE_SECRET_KEY!
|
|
)
|
|
|
|
if (authorizationLevel === AuthorizationLevel.Basic) {
|
|
const { data: lastRevalidation, error } = await supabaseAdmin.rpc(
|
|
'get_last_revalidation_for_tags',
|
|
{
|
|
tags: result.tags,
|
|
}
|
|
)
|
|
if (error) {
|
|
console.error(error)
|
|
return new Response('Internal server error', { status: 500 })
|
|
}
|
|
|
|
const sixHoursAgo = new Date()
|
|
sixHoursAgo.setHours(sixHoursAgo.getHours() - 6)
|
|
if (lastRevalidation?.some((revalidation) => new Date(revalidation.created_at) > sixHoursAgo)) {
|
|
return new Response(
|
|
'Your request includes a tag that has been revalidated within the last 6 hours. You can override this limit by authenticating with Override permissions.',
|
|
{
|
|
status: 429,
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
const { error } = await supabaseAdmin
|
|
.from('validation_history')
|
|
.insert(result.tags.map((tag) => ({ tag })))
|
|
if (error) {
|
|
console.error('Failed to update revalidation table: %o', error)
|
|
}
|
|
|
|
result.tags.forEach((tag) => {
|
|
revalidateTag(tag)
|
|
})
|
|
|
|
return new Response(null, {
|
|
status: 204,
|
|
headers: {
|
|
'Cache-Control': 'no-cache',
|
|
},
|
|
})
|
|
}
|
|
|
|
function handleError(handleRequest: (request: NextRequest) => Promise<Response>) {
|
|
return async function (request: NextRequest) {
|
|
try {
|
|
const response = await handleRequest(request)
|
|
return response
|
|
} catch (error) {
|
|
console.error(error)
|
|
return new Response('Internal server error', { status: 500 })
|
|
}
|
|
}
|
|
}
|