mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
fix: failing type errors in build (#38761)
* fix(www): types for awaited params * fix(docs): no extraneous exports from route page * fix(studio): api handler types, no non-handlers allowed in pages
This commit is contained in:
1 parent
872a372119
commit
283b125369
16 files changed
+141
-117
No files matched your search
@@ -6,7 +6,7 @@ import { headers } from 'next/headers'
|
||||
import { NextRequest } from 'next/server'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
|
||||
|
||||
import { _handleRevalidateRequest } from './route'
|
||||
import { _handleRevalidateRequest } from './route.utils'
|
||||
|
||||
// Mock Next.js modules
|
||||
vi.mock('next/cache', () => ({
|
||||
|
||||
@@ -1,109 +1,9 @@
|
||||
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)),
|
||||
})
|
||||
import { _handleRevalidateRequest } from './route.utils'
|
||||
|
||||
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 {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
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 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',
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { ResponseError, ResponseFailure } from 'types'
|
||||
import { IS_PLATFORM } from '../constants'
|
||||
@@ -26,9 +26,9 @@ export function isResponseOk<T>(response: T | ResponseFailure | undefined): resp
|
||||
export default async function apiWrapper(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
handler: NextApiHandler,
|
||||
handler: (req: NextApiRequest, res: NextApiResponse) => Promise<Response | void>,
|
||||
options?: { withAuth: boolean }
|
||||
) {
|
||||
): Promise<Response | void> {
|
||||
try {
|
||||
const { withAuth } = options || {}
|
||||
|
||||
|
||||
File renamed without changes.
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { DEFAULT_PROJECT } from '../../constants'
|
||||
import { DEFAULT_PROJECT } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { PROJECT_ANALYTICS_URL } from 'pages/api/constants'
|
||||
import { PROJECT_ANALYTICS_URL } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { paths } from 'api-types'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { PROJECT_REST_URL } from 'pages/api/constants'
|
||||
import { PROJECT_REST_URL } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { DEFAULT_PROJECT, PROJECT_REST_URL } from 'pages/api/constants'
|
||||
import { DEFAULT_PROJECT, PROJECT_REST_URL } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { components } from 'api-types'
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { PROJECT_ENDPOINT, PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants'
|
||||
import { PROJECT_ENDPOINT, PROJECT_ENDPOINT_PROTOCOL } from 'lib/constants/api'
|
||||
|
||||
type ProjectAppConfig = components['schemas']['ProjectSettingsResponse']['app_config'] & {
|
||||
protocol?: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { DEFAULT_PROJECT } from '../../constants'
|
||||
import { DEFAULT_PROJECT } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
PROJECT_ENDPOINT,
|
||||
PROJECT_ENDPOINT_PROTOCOL,
|
||||
PROJECT_REST_URL,
|
||||
} from 'pages/api/constants'
|
||||
} from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import apiWrapper from 'lib/api/apiWrapper'
|
||||
import { DEFAULT_PROJECT } from 'pages/api/constants'
|
||||
import { DEFAULT_PROJECT } from 'lib/constants/api'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
|
||||
|
||||
|
||||
@@ -18,7 +18,13 @@ export async function generateStaticParams() {
|
||||
export const revalidate = 30
|
||||
export const dynamic = 'force-static'
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {
|
||||
export async function generateMetadata({
|
||||
params: paramsPromise,
|
||||
}: {
|
||||
params: Promise<Params>
|
||||
}): Promise<Metadata> {
|
||||
const params = await paramsPromise
|
||||
|
||||
const capitalizedCategory = capitalize(params?.category.replaceAll('-', ' '))
|
||||
return {
|
||||
title: `Blog | ${capitalizedCategory}`,
|
||||
@@ -26,7 +32,13 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
export default async function CategoriesPage({ params }: { params: Params }) {
|
||||
export default async function CategoriesPage({
|
||||
params: paramsPromise,
|
||||
}: {
|
||||
params: Promise<Params>
|
||||
}) {
|
||||
const params = await paramsPromise
|
||||
|
||||
const staticPosts = getSortedPosts({
|
||||
directory: '_blog',
|
||||
limit: 0,
|
||||
|
||||
@@ -18,7 +18,13 @@ export async function generateStaticParams() {
|
||||
export const revalidate = 30
|
||||
export const dynamic = 'force-static'
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {
|
||||
export async function generateMetadata({
|
||||
params: paramsPromise,
|
||||
}: {
|
||||
params: Promise<Params>
|
||||
}): Promise<Metadata> {
|
||||
const params = await paramsPromise
|
||||
|
||||
const capitalizedTag = capitalize(params?.tag.replaceAll('-', ' '))
|
||||
return {
|
||||
title: `Blog | ${capitalizedTag}`,
|
||||
@@ -26,7 +32,9 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
export default async function TagPage({ params }: { params: Params }) {
|
||||
export default async function TagPage({ params: paramsPromise }: { params: Promise<Params> }) {
|
||||
const params = await paramsPromise
|
||||
|
||||
const staticPosts = getSortedPosts({ directory: '_blog', limit: 0, tags: [params.tag] })
|
||||
const cmsPosts = await getAllCMSPosts({ tags: [params.tag] })
|
||||
const blogs = [...(staticPosts as any[]), ...(cmsPosts as any[])] as unknown as PostTypes[]
|
||||
|
||||
Reference in new issue
Block a user