Chore/assistant in self hosted (#33422)

* Add layoutheader to self-hosted to allow showing assistant

* Fix layout

* Remove debug checks

* Check for key

* Dont load tables

* Fix assistant error handling

* Yeet

* Update turbo.json

* Another one

* god

* Fix

* Add fallback

* last attempt to fix

* Clean up

* take the wheel

* Tiny fix

* Skip enrich query for local, to match prod

---------

Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Terry Sutton
2025-02-18 06:07:53 -03:30
committed by GitHub
parent 1f7c7389b4
commit dff6c817ae
11 changed files with 187 additions and 172 deletions

View File

@@ -21,7 +21,6 @@ import BreadcrumbsView from './BreadcrumbsView'
import { FeedbackDropdown } from './FeedbackDropdown'
import HelpPopover from './HelpPopover'
import NotificationsPopoverV2 from './NotificationsPopoverV2/NotificationsPopover'
const LayoutHeaderDivider = () => (
<span className="text-border-stronger">
<svg
@@ -102,26 +101,27 @@ const LayoutHeader = ({
<div className="relative flex flex-1 overflow-hidden">
<div className="flex w-full items-center justify-between py-2 pl-1 pr-3 md:px-3 flex-nowrap overflow-x-auto no-scrollbar">
<div className="flex items-center text-sm">
{projectRef && (
<Link
href={IS_PLATFORM ? '/projects' : `/project/${projectRef}`}
className="mx-1 hidden md:flex items-center w-[40px] h-[40px]"
>
<img
alt="Supabase"
src={`${router.basePath}/img/supabase-logo.svg`}
className="absolute h-[40px] w-6 cursor-pointer rounded"
/>
</Link>
<Link
href={IS_PLATFORM ? '/projects' : `/project/default`}
className="mx-1 hidden md:flex items-center w-[40px] h-[40px]"
>
<img
alt="Supabase"
src={`${router.basePath}/img/supabase-logo.svg`}
className="absolute h-[40px] w-6 cursor-pointer rounded"
/>
</Link>
{!IS_PLATFORM && (
<div className="ml-3 text-xs text-foreground-light">Default project</div>
)}
{projectRef && (
{projectRef && IS_PLATFORM && (
<>
<div className="flex items-center">
<OrganizationDropdown />
<LayoutHeaderDivider />
<ProjectDropdown />
{exceedingLimits && (
<div className="ml-2">
<Link href={`/org/${selectedOrganization?.slug}/usage`}>
@@ -129,7 +129,6 @@ const LayoutHeader = ({
</Link>
</div>
)}
{selectedProject && isBranchingEnabled && (
<>
<LayoutHeaderDivider />
@@ -144,7 +143,6 @@ const LayoutHeader = ({
</div>
</>
)}
{/* Additional breadcrumbs are supplied */}
<BreadcrumbsView defaultValue={breadcrumbs} />
</div>

View File

@@ -24,7 +24,6 @@ import { ProjectPausedState } from './PausedState/ProjectPausedState'
import PauseFailedState from './PauseFailedState'
import PausingState from './PausingState'
import ProductMenuBar from './ProductMenuBar'
import { ProjectContextProvider } from './ProjectContext'
import { ResizingState } from './ResizingState'
import RestartingState from './RestartingState'
import RestoreFailedState from './RestoreFailedState'
@@ -62,8 +61,6 @@ export interface ProjectLayoutProps {
isBlocking?: boolean
product?: string
productMenu?: ReactNode
hideHeader?: boolean
hideIconBar?: boolean
selectedTable?: string
resizableSidebar?: boolean
}
@@ -77,8 +74,6 @@ const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<ProjectLayout
product = '',
productMenu,
children,
hideHeader = false,
hideIconBar = false,
selectedTable,
resizableSidebar = false,
},
@@ -86,7 +81,6 @@ const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<ProjectLayout
) => {
const router = useRouter()
const [isClient, setIsClient] = useState(false)
const { ref: projectRef } = useParams()
const selectedOrganization = useSelectedOrganization()
const selectedProject = useSelectedProject()
const { aiAssistantPanel, setAiAssistantPanel, mobileMenuOpen, setMobileMenuOpen } =

View File

@@ -96,7 +96,7 @@ export const AIAssistant = ({
const [lastSentMessage, setLastSentMessage] = useState<MessageType>()
const [isConfirmOptInModalOpen, setIsConfirmOptInModalOpen] = useState(false)
const { data: check } = useCheckOpenAIKeyQuery()
const { data: check, isSuccess } = useCheckOpenAIKeyQuery()
const isApiKeySet = IS_PLATFORM || !!check?.hasKey
const isInSQLEditor = router.pathname.includes('/sql/[id]')
@@ -106,11 +106,14 @@ export const AIAssistant = ({
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug })
const hasHipaaAddon = subscriptionHasHipaaAddon(subscription)
const { data: tables, isLoading: isLoadingTables } = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
schema: 'public',
})
const { data: tables, isLoading: isLoadingTables } = useTablesQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
schema: 'public',
},
{ enabled: isApiKeySet }
)
const currentTable = tables?.find((t) => t.id.toString() === entityId)
const currentSchema = searchParams?.get('schema') ?? 'public'
@@ -137,6 +140,10 @@ export const AIAssistant = ({
table: currentTable?.name,
},
onFinish: (message) => saveLatestMessage(message),
onError: (error) => {
const errorMessage = JSON.parse(error.message).message
toast.error(errorMessage)
},
})
const canUpdateOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
@@ -370,14 +377,14 @@ export const AIAssistant = ({
))}
</div>
</div>
) : isLoadingTables ? (
) : isLoadingTables && isApiKeySet ? (
<div className="w-full h-full flex-1 flex flex-col justify-end items-start p-5">
{/* [Joshen] We could try play around with a custom loader for the assistant here */}
<GenericSkeletonLoader className="w-4/5" />
</div>
) : (tables ?? [])?.length > 0 ? (
<AIOnboarding setMessages={setMessages} onSendMessage={sendMessageToAssistant} />
) : (
) : isApiKeySet ? (
<div className="w-full flex flex-col justify-end flex-1 h-full p-5">
<h2 className="text-base mb-2">Welcome to Supabase!</h2>
<p className="text-sm text-foreground-lighter mb-6">
@@ -428,7 +435,7 @@ export const AIAssistant = ({
))}
</div>
</div>
)}
) : null}
</div>
<AnimatePresence>
@@ -495,7 +502,7 @@ export const AIAssistant = ({
/>
)}
{!isApiKeySet && (
{isSuccess && !isApiKeySet && (
<Admonition
type="default"
title="OpenAI API key not set"

View File

@@ -1,6 +1,7 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import { IS_PLATFORM } from 'lib/constants'
import type { ResponseError } from 'types'
import { subscriptionKeys } from './keys'
@@ -34,7 +35,7 @@ export const useProjectAddonsQuery = <TData = ProjectAddonsData>(
subscriptionKeys.addons(projectRef),
({ signal }) => getProjectAddons({ projectRef }, signal),
{
enabled: enabled && typeof projectRef !== 'undefined',
enabled: enabled && IS_PLATFORM && typeof projectRef !== 'undefined',
...options,
}
)

View File

@@ -1,5 +1,5 @@
import { snakeCase } from 'lodash'
import { IS_PLATFORM } from 'lib/constants'
import { snakeCase } from 'lodash'
/**
* Construct headers for api request.
@@ -13,6 +13,7 @@ export function constructHeaders(headers: { [prop: string]: any }) {
const cleansedHeaders = {
Accept: headers.Accept,
Authorization: headers.Authorization,
cookie: headers.cookie,
'Content-Type': headers['Content-Type'],
'x-connection-encrypted': headers['x-connection-encrypted'],
} as any

View File

@@ -5,8 +5,18 @@ export * from './infrastructure'
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
export const DEFAULT_HOME = IS_PLATFORM ? '/projects' : '/project/default'
// TODO: Replace PG_META_URL with STUDIO_PG_META_URL and remove all references to PLATFORM_PG_META_URL
export const API_URL = IS_PLATFORM ? process.env.NEXT_PUBLIC_API_URL! : '/api'
export const API_URL = (() => {
// If running in platform, use API_URL from the env var
if (IS_PLATFORM) return process.env.NEXT_PUBLIC_API_URL!
// If running in browser, let it add the host
if (typeof window !== 'undefined') return '/api'
// If running self-hosted Vercel preview, use VERCEL_URL
if (!!process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}/api`
// If running on self-hosted, use NEXT_PUBLIC_SITE_URL
if (!!process.env.NEXT_PUBLIC_SITE_URL) return `${process.env.NEXT_PUBLIC_SITE_URL}/api`
return '/api'
})()
export const PG_META_URL = IS_PLATFORM
? process.env.PLATFORM_PG_META_URL
: process.env.STUDIO_PG_META_URL

View File

@@ -37,117 +37,129 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
})
}
const cookie = req.headers.cookie
const authorization = req.headers.authorization
const { result: schemas } = includeSchemaMetadata
? await executeSql(
{
projectRef,
connectionString,
sql: pgMetaSchemasList.sql,
},
undefined,
{
'Content-Type': 'application/json',
...(authorization && { Authorization: authorization }),
}
)
: { result: [] }
try {
const { result: schemas } = includeSchemaMetadata
? await executeSql(
{
projectRef,
connectionString,
sql: pgMetaSchemasList.sql,
},
undefined,
{
'Content-Type': 'application/json',
...(cookie && { cookie }),
...(authorization && { Authorization: authorization }),
}
)
: { result: [] }
const result = await streamText({
model: openai('gpt-4o-mini'),
maxSteps: 5,
system: `
You are a Supabase Postgres expert who can do the following things.
# You generate and debug SQL
The generated SQL (must be valid SQL), and must adhere to the following:
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
- Always use semicolons
- Output as markdown
- Always include code snippets if available
- If a code snippet is SQL, the first line of the snippet should always be -- props: {"title": "Query title", "runQuery": "false", "isChart": "true", "xAxis": "columnOrAlias", "yAxis": "columnOrAlias"}
- Only include one line of comment props per markdown snippet, even if the snippet has multiple queries
- Only set chart to true if the query makes sense as a chart. xAxis and yAxis need to be columns or aliases returned by the query.
- Only set runQuery to true if the query has no risk of writing data and is not a debugging request. Set it to false if there are any values that need to be replaced with real data.
- Explain what the snippet does in a sentence or two before showing it
- Use vector(384) data type for any embedding/vector related query
- When debugging, retrieve sql schema details to ensure sql is correct
- In Supabase, the auth schema already has a users table which is used to store users. It is common practice to create a profiles table in the public schema that links to auth.users to store user information instead. You don't need to create a new users table.
- Never suggest creating a view to retrieve information from the users table of the auth schema. This is against our best practices.
When generating tables, do the following:
- Ensure that all tables always have a primary key
- Ensure that all tables have RLS enabled. Inform the user that they will need to create RLS policies before being able to read or write to the table over Supabase APIs.
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
- Prefer creating foreign key references in the create statement
- Prefer 'text' over 'varchar'
- Prefer 'timestamp with time zone' over 'date'
- If a foreign key is created, include a statement to create a database index on the columns involved in the foreign key. This is a best practice as it is common to use foreign key columns in join conditions when querying the database, hence the index will improve the performance of those joins and reduce database resource consumption.
- If the table is a foreign table, create it in a new schema called private. If the private schema doesnt exist, create it.
- Explain to the user that we create it outside of the public schema as it can present a security risk as foreign tables views cannot be configured to respect RLS policies. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api
When generating views, do the following:
- All views should include 'with (security_invoker=on)' clause in the SQL statement for creating views (only views though - do not do this for tables)
- Place the 'with (security_invoker=on)' immediately after the CREATE VIEW statement, before AS
- If the view is a materialized view, create it in a new schema called private. If the private schema doesnt exist, create it.
- Explain to the user that we create it outside of the public schema as it can present a security risk as materialized views cannot be configured to respect RLS policies of the underlying tables they are built upon, nor can they be secured with RLS directly. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api
When installing database extensions, do the following:
- Never install extensions in the public schema
- Extensions should be installed in the extensions schema, or a dedicated schema
Feel free to suggest corrections for suspected typos.
# You write row level security policies.
Your purpose is to generate a policy with the constraints given by the user.
- First, use getSchema to retrieve more information about a schema or schemas that will contain policies, usually the public schema.
- Then retrieve existing RLS policies and guidelines on how to write policies using the getRlsKnowledge tool .
- Then write new policies or update existing policies based on the prompt
- When asked to suggest policies, either alter existing policies or add new ones to the public schema.
- When writing policies that use a function from the auth schema, ensure that the calls are wrapped with parentheses e.g select auth.uid() should be written as (select auth.uid()) instead
# You write database functions
Your purpose is to generate a database function with the constraints given by the user. The output may also include a database trigger
if the function returns a type of trigger. When generating functions, do the following:
- If the function returns a trigger type, ensure that it uses security definer, otherwise default to security invoker. Include this in the create functions SQL statement.
- Ensure to set the search_path configuration parameter as '', include this in the create functions SQL statement.
- Default to create or replace whenever possible for updating an existing function, otherwise use the alter function statement
Please make sure that all queries are valid Postgres SQL queries
# You write edge functions
Your purpose is to generate entire edge functions with the constraints given by the user.
- First, always use the getEdgeFunctionKnowledge tool to get knowledge about how to write edge functions for Supabase
- When writing edge functions, always ensure that they are written in TypeScript and Deno JavaScript runtime.
- When writing edge functions, write complete code so the user doesn't need to replace any placeholders.
- When writing edge functions, always ensure that they are written in a way that is compatible with the database schema.
- When suggesting edge functions, follow the guidelines in getEdgeFunctionKnowledge tool. Always create personalised edge functions based on the database schema
- When outputting edge functions, always include a props comment in the first line of the code block:
-- props: {"name": "function-name", "title": "Human readable title"}
- The function name in the props must be URL-friendly (use hyphens instead of spaces or underscores)
- Always wrap the edge function code in a markdown code block with the language set to 'edge'
- The props comment must be the first line inside the code block, followed by the actual function code
# You convert sql to supabase-js client code
Use the convertSqlToSupabaseJs tool to convert select sql to supabase-js client code. Only provide js code snippets if explicitly asked. If conversion isn't supported, build a postgres function instead and suggest using supabase-js to call it via "const { data, error } = await supabase.rpc('echo', { say: '👋'})"
# For all your abilities, follow these instructions:
- First look at the list of provided schemas and if needed, get more information about a schema. You will almost always need to retrieve information about the public schema before answering a question.
- If the question is about users or involves creating a users table, also retrieve the auth schema.
- If it a query is a destructive query e.g. table drop, ask for confirmation before writing the query. The user will still have to run the query once you create it
const result = await streamText({
model: openai('gpt-4o-mini'),
maxSteps: 5,
system: `
You are a Supabase Postgres expert who can do the following things.
# You generate and debug SQL
The generated SQL (must be valid SQL), and must adhere to the following:
- Always use double apostrophe in SQL strings (eg. 'Night''s watch')
- Always use semicolons
- Output as markdown
- Always include code snippets if available
- If a code snippet is SQL, the first line of the snippet should always be -- props: {"title": "Query title", "runQuery": "false", "isChart": "true", "xAxis": "columnOrAlias", "yAxis": "columnOrAlias"}
- Only include one line of comment props per markdown snippet, even if the snippet has multiple queries
- Only set chart to true if the query makes sense as a chart. xAxis and yAxis need to be columns or aliases returned by the query.
- Only set runQuery to true if the query has no risk of writing data and is not a debugging request. Set it to false if there are any values that need to be replaced with real data.
- Explain what the snippet does in a sentence or two before showing it
- Use vector(384) data type for any embedding/vector related query
- When debugging, retrieve sql schema details to ensure sql is correct
- In Supabase, the auth schema already has a users table which is used to store users. It is common practice to create a profiles table in the public schema that links to auth.users to store user information instead. You don't need to create a new users table.
- Never suggest creating a view to retrieve information from the users table of the auth schema. This is against our best practices.
When generating tables, do the following:
- Ensure that all tables always have a primary key
- Ensure that all tables have RLS enabled. Inform the user that they will need to create RLS policies before being able to read or write to the table over Supabase APIs.
- For primary keys, always use "id bigint primary key generated always as identity" (not serial)
- Prefer creating foreign key references in the create statement
- Prefer 'text' over 'varchar'
- Prefer 'timestamp with time zone' over 'date'
- If a foreign key is created, include a statement to create a database index on the columns involved in the foreign key. This is a best practice as it is common to use foreign key columns in join conditions when querying the database, hence the index will improve the performance of those joins and reduce database resource consumption.
- If the table is a foreign table, create it in a new schema called private. If the private schema doesnt exist, create it.
- Explain to the user that we create it outside of the public schema as it can present a security risk as foreign tables views cannot be configured to respect RLS policies. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api
When generating views, do the following:
- All views should include 'with (security_invoker=on)' clause in the SQL statement for creating views (only views though - do not do this for tables)
- Place the 'with (security_invoker=on)' immediately after the CREATE VIEW statement, before AS
- If the view is a materialized view, create it in a new schema called private. If the private schema doesnt exist, create it.
- Explain to the user that we create it outside of the public schema as it can present a security risk as materialized views cannot be configured to respect RLS policies of the underlying tables they are built upon, nor can they be secured with RLS directly. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api
When installing database extensions, do the following:
- Never install extensions in the public schema
- Extensions should be installed in the extensions schema, or a dedicated schema
Feel free to suggest corrections for suspected typos.
# You write row level security policies.
Your purpose is to generate a policy with the constraints given by the user.
- First, use getSchema to retrieve more information about a schema or schemas that will contain policies, usually the public schema.
- Then retrieve existing RLS policies and guidelines on how to write policies using the getRlsKnowledge tool .
- Then write new policies or update existing policies based on the prompt
- When asked to suggest policies, either alter existing policies or add new ones to the public schema.
- When writing policies that use a function from the auth schema, ensure that the calls are wrapped with parentheses e.g select auth.uid() should be written as (select auth.uid()) instead
# You write database functions
Your purpose is to generate a database function with the constraints given by the user. The output may also include a database trigger
if the function returns a type of trigger. When generating functions, do the following:
- If the function returns a trigger type, ensure that it uses security definer, otherwise default to security invoker. Include this in the create functions SQL statement.
- Ensure to set the search_path configuration parameter as '', include this in the create functions SQL statement.
- Default to create or replace whenever possible for updating an existing function, otherwise use the alter function statement
Please make sure that all queries are valid Postgres SQL queries
# You write edge functions
Your purpose is to generate entire edge functions with the constraints given by the user.
- First, always use the getEdgeFunctionKnowledge tool to get knowledge about how to write edge functions for Supabase
- When writing edge functions, always ensure that they are written in TypeScript and Deno JavaScript runtime.
- When writing edge functions, write complete code so the user doesn't need to replace any placeholders.
- When writing edge functions, always ensure that they are written in a way that is compatible with the database schema.
- When suggesting edge functions, follow the guidelines in getEdgeFunctionKnowledge tool. Always create personalised edge functions based on the database schema
- When outputting edge functions, always include a props comment in the first line of the code block:
-- props: {"name": "function-name", "title": "Human readable title"}
- The function name in the props must be URL-friendly (use hyphens instead of spaces or underscores)
- Always wrap the edge function code in a markdown code block with the language set to 'edge'
- The props comment must be the first line inside the code block, followed by the actual function code
# You convert sql to supabase-js client code
Use the convertSqlToSupabaseJs tool to convert select sql to supabase-js client code. Only provide js code snippets if explicitly asked. If conversion isn't supported, build a postgres function instead and suggest using supabase-js to call it via "const { data, error } = await supabase.rpc('echo', { say: '👋'})"
# For all your abilities, follow these instructions:
- First look at the list of provided schemas and if needed, get more information about a schema. You will almost always need to retrieve information about the public schema before answering a question.
- If the question is about users or involves creating a users table, also retrieve the auth schema.
- If it a query is a destructive query e.g. table drop, ask for confirmation before writing the query. The user will still have to run the query once you create it
Here are the existing database schema names you can retrieve: ${schemas}
${schema !== undefined && includeSchemaMetadata ? `The user is currently looking at the ${schema} schema.` : ''}
${table !== undefined && includeSchemaMetadata ? `The user is currently looking at the ${table} table.` : ''}
`,
messages,
tools: getTools({
projectRef,
connectionString,
cookie,
authorization,
includeSchemaMetadata,
}),
})
Here are the existing database schema names you can retrieve: ${schemas}
${schema !== undefined && includeSchemaMetadata ? `The user is currently looking at the ${schema} schema.` : ''}
${table !== undefined && includeSchemaMetadata ? `The user is currently looking at the ${table} table.` : ''}
`,
messages,
tools: getTools({ projectRef, connectionString, authorization, includeSchemaMetadata }),
})
// write the data stream to the response
// Note: this is sent as a single response, not a stream
result.pipeDataStreamToResponse(res)
// write the data stream to the response
// Note: this is sent as a single response, not a stream
result.pipeDataStreamToResponse(res)
} catch (error: any) {
return res.status(500).json({ message: error.message })
}
}

View File

@@ -2,23 +2,31 @@ import { tool } from 'ai'
import { stripIndent } from 'common-tags'
import { z } from 'zod'
import { processSql, renderSupabaseJs } from '@supabase/sql-to-rest'
import { getDatabaseFunctions } from 'data/database-functions/database-functions-query'
import { getDatabasePolicies } from 'data/database-policies/database-policies-query'
import { getEntityDefinitionsSql } from 'data/database/entity-definitions-query'
import { executeSql } from 'data/sql/execute-sql-query'
import { processSql, renderSupabaseJs } from '@supabase/sql-to-rest'
import { getDatabaseFunctions } from 'data/database-functions/database-functions-query'
export const getTools = ({
projectRef,
connectionString,
cookie,
authorization,
includeSchemaMetadata,
}: {
projectRef: string
connectionString: string
cookie?: string
authorization?: string
includeSchemaMetadata: boolean
}) => {
const headers = {
'Content-Type': 'application/json',
...(cookie && { cookie }),
...(authorization && { Authorization: authorization }),
}
return {
getSchema: tool({
description: 'Get more information about one or more schemas',
@@ -35,10 +43,7 @@ export const getTools = ({
sql: getEntityDefinitionsSql({ schemas }),
},
undefined,
{
'Content-Type': 'application/json',
...(authorization && { Authorization: authorization }),
}
headers
)
: { result: [] }
@@ -82,10 +87,7 @@ export const getTools = ({
connectionString,
},
undefined,
{
'Content-Type': 'application/json',
...(authorization && { Authorization: authorization }),
}
headers
)
: []
@@ -357,10 +359,7 @@ export const getTools = ({
connectionString,
},
undefined,
{
'Content-Type': 'application/json',
...(authorization && { Authorization: authorization }),
}
headers
)
: []

View File

@@ -1,8 +1,8 @@
import { NextApiRequest, NextApiResponse } from 'next'
import apiWrapper from 'lib/api/apiWrapper'
import { constructHeaders } from 'lib/api/apiHelpers'
import { PG_META_URL } from 'lib/constants'
import apiWrapper from 'lib/api/apiWrapper'
import { post } from 'lib/common/fetch'
import { PG_META_URL } from 'lib/constants'
import { NextApiRequest, NextApiResponse } from 'next'
export default (req: NextApiRequest, res: NextApiResponse) =>
apiWrapper(req, res, handler, { withAuth: true })
@@ -22,7 +22,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
const { query } = req.body
const headers = constructHeaders(req.headers)
const response = await post(`${PG_META_URL}/query`, { query: enrichQuery(query) }, { headers })
const response = await post(`${PG_META_URL}/query`, { query }, { headers })
if (response.error) {
return res.status(400).json(response.error)
@@ -30,11 +30,3 @@ const handlePost = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).json(response)
}
}
const enrichQuery = (query: string) => `
-- source: dashboard
-- user: ${'self host'}
-- date: ${new Date().toISOString()}
${query}
`

View File

@@ -21,7 +21,7 @@ const defaultEnv = {
SENTRY_IGNORE_API_RESOLUTION_ERROR: '1',
LOGFLARE_URL: 'http://localhost:54329',
LOGFLARE_API_KEY: 'api-key',
NEXT_PUBLIC_SITE_URL: 'http://localhost:3000',
NEXT_PUBLIC_SITE_URL: 'http://localhost:8082',
NEXT_PUBLIC_GOTRUE_URL: '$SUPABASE_PUBLIC_URL/auth/v1',
NEXT_PUBLIC_HCAPTCHA_SITE_KEY: '10000000-ffff-ffff-ffff-000000000001',
NEXT_PUBLIC_NODE_ENV: 'test',

View File

@@ -70,7 +70,8 @@
"AWS_SECRET_ACCESS_KEY",
"FORCE_ASSET_CDN",
"ASSET_CDN_S3_ENDPOINT",
"SITE_NAME"
"SITE_NAME",
"VERCEL_URL"
],
"outputs": [".next/**", "!.next/cache/**"]
},