mirror of
https://github.com/supabase/supabase.git
synced 2026-10-13 13:19:09 +03:00
Merge remote-tracking branch 'origin/master' into feat/query-perf-labels-columns-update
This commit is contained in:
commit
032ef93721
22 files changed
+133
-103
No files matched your search
@@ -47,10 +47,6 @@ const nextConfig = {
|
||||
// We are already running linting via GH action, this will skip linting during production build on Vercel
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
experimental: {
|
||||
// Ensure compatibility with Turbopack and Sharp
|
||||
serverComponentsExternalPackages: ['sharp'],
|
||||
},
|
||||
// Configure Sharp as an external package for server-side rendering
|
||||
serverExternalPackages: ['sharp'],
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
|
||||
"migrate": "cross-env NODE_OPTIONS=--no-deprecation tsx scripts/migrate.ts",
|
||||
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
|
||||
"start": "cross-env NODE_OPTIONS=--no-deprecation next start --port 3030",
|
||||
"start": "cross-env NODE_OPTIONS=--no-deprecation next start",
|
||||
"vercel-build": "pnpm migrate && pnpm build",
|
||||
"typecheck_IGNORED": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import config from '@payload-config'
|
||||
import '@payloadcms/next/css'
|
||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
|
||||
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
||||
@@ -1,8 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import config from '@payload-config'
|
||||
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
|
||||
|
||||
export const POST = GRAPHQL_POST(config)
|
||||
|
||||
export const OPTIONS = REST_OPTIONS(config)
|
||||
@@ -102,7 +102,7 @@ export default buildConfig({
|
||||
// Global configuration for better performance
|
||||
globals: [],
|
||||
graphQL: {
|
||||
disable: process.env.NODE_ENV !== 'development', // Disable GraphQL in production for better performance
|
||||
disable: true,
|
||||
},
|
||||
// Reduce payload init overhead
|
||||
telemetry: false,
|
||||
|
||||
@@ -20,7 +20,7 @@ You can read about Unified egress, included quota, and how to check the egress u
|
||||
While pointing out the exact cause for egress may not be straightforward, there are various steps you can take to determine the source of these issues:
|
||||
|
||||
- Picking the "Top Paths" from the [log explorer](https://app.supabase.com/project/_/logs/explorer/templates) will help you identify heavily queried paths
|
||||
- By finding the most requested queries from Query performance report: https://app.supabase.com/project/_/reports/query-performance
|
||||
- By finding the most requested queries from Query performance report: https://app.supabase.com/project/_/advisors/query-performance
|
||||
- Supavisor Egress is independent of client. There is no direct relation between a single query and Supavisor egress, it is harder to debug and identify. But you can make use of frequent queries from the link in above step that also displays average number of rows which will help to identify queries with a large number of rows returned. While this does not display Supavisor queries specifically, it will give an overview of queries with lots of rows that can help.
|
||||
- For Storage Egress, all outgoing traffic for storage-related requests to download/view your Storage items are considered as Storage egress. We have a "Storage Egress Requests" template in logs explorer that you can use to get the number of requests for each Storage object
|
||||
- If you pull 1mb of data out of the database using the Supavisor connection in your edge function, but only sends 100kb back to the user, you will have the Egress from the Supavisor to your Edge function plus from the edge function to your user
|
||||
|
||||
+1
-1
@@ -13,6 +13,6 @@ You can run this query to check the current settings set for your roles: `SELECT
|
||||
|
||||
To increase the `statement_timeout` for a specific role, you may follow the instructions [here](/docs/guides/database/timeouts#changing-the-default-timeout). Note that it may require a quick reboot for the changes to take effect.
|
||||
|
||||
Additionally, to check how long a query is taking, you can check the Query Performance report which can give you more information on the query's performance: https://app.supabase.com/project/_/reports/query-performance. You can use the [query plan analyzer](https://www.postgresql.org/docs/current/sql-explain.html) on any expensive queries that you have identified: `explain analyze <query-statement-here>;`. For supabase-js/ PostgREST queries you can use `.explain()`.
|
||||
Additionally, to check how long a query is taking, you can check the Query Performance report which can give you more information on the query's performance: https://app.supabase.com/project/_/advisors/query-performance. You can use the [query plan analyzer](https://www.postgresql.org/docs/current/sql-explain.html) on any expensive queries that you have identified: `explain analyze <query-statement-here>;`. For supabase-js/ PostgREST queries you can use `.explain()`.
|
||||
|
||||
You can also make use of Postgres logs that will give you useful information like when the query was executed: https://app.supabase.com/project/_/logs/postgres-logs.
|
||||
+1
-1
@@ -10,7 +10,7 @@ database_id = "1da315be-38ac-487a-b178-f865a1a73c09"
|
||||
message = "insufficient privilege"
|
||||
---
|
||||
|
||||
If you see the error "insufficient privilege" when accessing [pg_stat_statements](/docs/guides/platform/performance#postgres-cumulative-statistics-system) or when accessing [Query Performance Report](/dashboard/project/_/reports/query-performance), it means that the Postgres role does not have required permissions.
|
||||
If you see the error "insufficient privilege" when accessing [pg_stat_statements](/docs/guides/platform/performance#postgres-cumulative-statistics-system) or when accessing [Query Performance Report](/dashboard/project/_/advisors/query-performance), it means that the Postgres role does not have required permissions.
|
||||
|
||||
In this case, you can run the below command to allow the Postgres role to read all statistics from the system:
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useReportsGotoCommands(options?: CommandOptions) {
|
||||
{
|
||||
id: 'nav-reports-query-performance',
|
||||
name: 'Query Performance Reports',
|
||||
route: `/project/${ref}/reports/query-performance`,
|
||||
route: `/project/${ref}/advisors/query-performance`,
|
||||
defaultHidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -304,7 +304,7 @@ const nextConfig = {
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/project/:ref/reports/query-performance',
|
||||
source: '/project/:ref/query-performance',
|
||||
destination: '/project/:ref/advisors/query-performance',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,6 +6,12 @@ import { generateReadingTime } from '~/lib/helpers'
|
||||
// Lightweight runtime for better performance
|
||||
export const runtime = 'edge'
|
||||
|
||||
// CORS headers for cross-origin requests
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||
}
|
||||
|
||||
// Lightweight TOC generation for edge runtime
|
||||
type TocItem = { content: string; slug: string; lvl: number }
|
||||
|
||||
@@ -115,6 +121,14 @@ function convertRichTextToMarkdown(content: any): string {
|
||||
.join('\n\n')
|
||||
}
|
||||
|
||||
// Handle preflight requests
|
||||
export async function OPTIONS() {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: corsHeaders,
|
||||
})
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
@@ -212,12 +226,15 @@ export async function GET(request: NextRequest) {
|
||||
_status: latestVersion._status,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,12 +306,15 @@ export async function GET(request: NextRequest) {
|
||||
_status: post._status,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,12 +384,15 @@ export async function GET(request: NextRequest) {
|
||||
_status: post._status,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
isDraft: shouldFetchDraft,
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,16 +475,21 @@ export async function GET(request: NextRequest) {
|
||||
toc_depth: post.toc_depth || 3,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
source: 'versions-api',
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
source: 'versions-api',
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('[cms-posts] Versions API failed, response:', await versionsResponse.text())
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log('[cms-posts] Versions API failed, response:', await versionsResponse.text())
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: If versions API didn't work, try finding the parent post first, then get its latest published version
|
||||
@@ -556,12 +584,15 @@ export async function GET(request: NextRequest) {
|
||||
richContent: mode === 'full' ? post.content : undefined,
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
source: 'versions-by-parent-api',
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: processedPost,
|
||||
mode,
|
||||
source: 'versions-by-parent-api',
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -590,6 +621,12 @@ export async function GET(request: NextRequest) {
|
||||
// For individual post requests, don't cache to ensure fresh data
|
||||
cache: slug ? 'no-store' : 'default',
|
||||
next: slug ? undefined : { revalidate: 300 },
|
||||
// Add SSL configuration for production
|
||||
...(process.env.NODE_ENV === 'production' && {
|
||||
// Allow self-signed certificates in development, but use proper SSL in production
|
||||
// This helps with Vercel's internal networking
|
||||
agent: false,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -600,7 +637,7 @@ export async function GET(request: NextRequest) {
|
||||
error: 'Failed to fetch posts from CMS',
|
||||
status: response.status,
|
||||
},
|
||||
{ status: response.status }
|
||||
{ status: response.status, headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -613,7 +650,7 @@ export async function GET(request: NextRequest) {
|
||||
error: 'CMS returned non-JSON response',
|
||||
contentType,
|
||||
},
|
||||
{ status: 502 }
|
||||
{ status: 502, headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -695,20 +732,26 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// For single post requests, return the post directly
|
||||
if (slug && posts.length > 0) {
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
post: posts[0],
|
||||
mode,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
post: posts[0],
|
||||
mode,
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
posts,
|
||||
total: posts.length,
|
||||
mode,
|
||||
cached: true,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
posts,
|
||||
total: posts.length,
|
||||
mode,
|
||||
cached: true,
|
||||
},
|
||||
{ headers: corsHeaders }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('[cms-posts] Error:', error)
|
||||
return NextResponse.json(
|
||||
@@ -717,7 +760,7 @@ export async function GET(request: NextRequest) {
|
||||
error: 'Internal server error',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
{ status: 500, headers: corsHeaders }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,32 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Check for secret to confirm this is a valid request
|
||||
if (req.query.secret !== process.env.CMS_PREVIEW_SECRET) {
|
||||
return res.status(401).json({ message: 'Invalid token' })
|
||||
}
|
||||
|
||||
const { path } = req.query
|
||||
|
||||
if (!path) {
|
||||
return res.status(400).json({ message: 'Missing path parameter' })
|
||||
}
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// This will revalidate the specific page
|
||||
await res.revalidate(String(path))
|
||||
const body = await request.json()
|
||||
const { secret, path } = body
|
||||
|
||||
return res.json({ revalidated: true, path })
|
||||
// Check for secret to confirm this is a valid request
|
||||
if (secret !== process.env.CMS_PREVIEW_SECRET) {
|
||||
return NextResponse.json({ message: 'Invalid token' }, { status: 401 })
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
return NextResponse.json({ message: 'Missing path parameter' }, { status: 400 })
|
||||
}
|
||||
|
||||
// This will revalidate the specific page
|
||||
revalidatePath(path)
|
||||
|
||||
return NextResponse.json({ revalidated: true, path })
|
||||
} catch (error) {
|
||||
console.error('[Revalidate API] Error during revalidation:', error)
|
||||
return res.status(500).json({
|
||||
message: 'Error revalidating',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: 'Error revalidating',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ const FONT_URLS = {
|
||||
const LW_TABLE = 'tickets'
|
||||
const LW_MATERIALIZED_VIEW = 'tickets_view'
|
||||
|
||||
export async function GET(req: Request, res: Response) {
|
||||
export async function GET(req: Request) {
|
||||
const url = new URL(req.url)
|
||||
|
||||
// Just here to silence snyk false positives
|
||||
|
||||
@@ -28,7 +28,7 @@ async function getCMSPostFromAPI(
|
||||
const fetchOptions = isDraft
|
||||
? {
|
||||
// For draft mode: always fresh data, no caching
|
||||
cache: 'no-store' as const,
|
||||
// cache: 'no-store' as const,
|
||||
next: { revalidate: 0 },
|
||||
}
|
||||
: {
|
||||
|
||||
@@ -15,6 +15,7 @@ export async function generateStaticParams() {
|
||||
return categories.map((category: string) => ({ category }))
|
||||
}
|
||||
|
||||
export const revalidate = 30
|
||||
export const dynamic = 'force-static'
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {
|
||||
|
||||
@@ -15,6 +15,7 @@ export async function generateStaticParams() {
|
||||
return tags.map((tag: string) => ({ tag }))
|
||||
}
|
||||
|
||||
export const revalidate = 30
|
||||
export const dynamic = 'force-static'
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {
|
||||
|
||||
@@ -14,10 +14,8 @@ import ComparisonsData from 'data/Comparisons'
|
||||
import CustomersData from 'data/CustomerStories'
|
||||
import MainProductsData from 'data/MainProducts'
|
||||
import ProductModulesData from 'data/ProductModules'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const ProductDropdown = () => {
|
||||
const { basePath } = useRouter()
|
||||
const isTablet = useBreakpoint(1279)
|
||||
|
||||
return (
|
||||
|
||||
@@ -34,8 +34,8 @@ export const SITE_ORIGIN =
|
||||
|
||||
export const CMS_SITE_ORIGIN =
|
||||
process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'
|
||||
? // In production, require env or fall back to localhost to avoid hitting supabase.com
|
||||
'http://localhost:3030'
|
||||
? // In production, use the actual CMS domain
|
||||
process.env.CMS_SITE_ORIGIN || 'https://cms.supabase.com'
|
||||
: process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL &&
|
||||
typeof process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL === 'string'
|
||||
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL.replace('zone-www-dot-com-git-', 'cms-git-')}`
|
||||
|
||||
@@ -288,7 +288,7 @@ export async function getCMSPostBySlug(slug: string, preview = false) {
|
||||
'Content-Type': 'application/json',
|
||||
...(CMS_API_KEY && { Authorization: `Bearer ${CMS_API_KEY}` }),
|
||||
},
|
||||
cache: 'no-store',
|
||||
// cache: 'no-store',
|
||||
next: { revalidate: 0 },
|
||||
})
|
||||
|
||||
@@ -300,7 +300,7 @@ export async function getCMSPostBySlug(slug: string, preview = false) {
|
||||
'Content-Type': 'application/json',
|
||||
...(CMS_API_KEY && { Authorization: `Bearer ${CMS_API_KEY}` }),
|
||||
},
|
||||
cache: 'no-store',
|
||||
// cache: 'no-store',
|
||||
next: { revalidate: 0 },
|
||||
})
|
||||
}
|
||||
@@ -490,8 +490,8 @@ export async function getAllCMSPosts({
|
||||
'Content-Type': 'application/json',
|
||||
...(CMS_API_KEY && { Authorization: `Bearer ${CMS_API_KEY}` }),
|
||||
},
|
||||
cache: 'no-store', // Ensure we always get fresh data
|
||||
next: { revalidate: 0 }, // Disable caching for this fetch
|
||||
// cache: 'no-store', // Ensure we always get fresh data
|
||||
next: { revalidate: 30 }, // Disable caching for this fetch
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Accordion, Badge } from 'ui'
|
||||
import { SITE_ORIGIN } from '~/lib/constants'
|
||||
import { WeekDayProps } from './types6'
|
||||
import type { WeekDayProps } from '~/types/launch-week-6'
|
||||
|
||||
import styles from './styles/launchWeek6.module.css'
|
||||
import styleUtils from './styles/utils6.module.css'
|
||||
|
||||
File renamed without changes.
@@ -116,6 +116,7 @@
|
||||
"ANALYZE",
|
||||
"CMS_API_KEY",
|
||||
"CMS_PREVIEW_SECRET",
|
||||
"CMS_SITE_ORIGIN",
|
||||
"NEXT_PUBLIC_MISC_USE_URL",
|
||||
"NEXT_PUBLIC_MISC_USE_ANON_KEY",
|
||||
"NEXT_PUBLIC_STUDIO_URL",
|
||||
|
||||
Reference in new issue
Block a user