Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ then the runtime will return a 404 error.

<Admonition type="note">

If the tests return a 404 but don't match the criteria below, the error is coming from your app logic, not the JWT check.
If the tests return a 404 but do not match the criteria below, the error is coming from your app's logic.

</Admonition>

Expand Down Expand Up @@ -61,18 +61,22 @@ Always configure an appropriate time frame when using the log explorer

</Admonition>

One cannot inspect the function dashboard to find platform 404 errors, instead, you must run the below query in the [log explorer](/dashboard/project/_/logs/explorer?q=SELECT+DISTINCT%0A++++req.pathname+AS+function_name%2C%0A++++res.status_code%0AFROM+function_edge_logs%0ACROSS+JOIN+UNNEST%28metadata%29+AS+metadata+%0ACROSS+JOIN+UNNEST%28metadata.request%29+AS+req+%0ACROSS+JOIN+UNNEST%28metadata.response%29+AS+res+%0AWHERE+%0A++++status_code+%3D+404+%0A++++++++AND+%0A++++metadata.execution_id+IS+NULL%0ALIMIT+10%3B). Results will show all requests that reached Supabase but were rejected as unrecognizable.
You cannot inspect the function dashboard to find platform 404 errors, instead, run the below query in the [log explorer](</dashboard/project/_/logs/explorer?its=&ite=&s=select+distinct%0A++req.pathname+as+function_name,%0A++res.status_code,%0A++CASE+%0A++++WHEN+metadata.execution_id+is+null+THEN+%27FUNCTION_NOT_FOUND%27%0A++++ELSE+%27FUNCTION+RECOGNIZED:+custom+404+message+in+app+logic%27%0A++END+AS+type_of_404%0Afrom%0A++function_edge_logs%0A++cross+join+UNNEST(metadata)+as+metadata%0A++cross+join+UNNEST(metadata.request)+as+req%0A++cross+join+UNNEST(metadata.response)+as+res%0Awhere+status_code+=+404%0Alimit+10;>). The results show all requests that reached Supabase but were rejected as unrecognizable.

```sql
select distinct
req.pathname as function_name,
res.status_code
res.status_code,
case
when metadata.execution_id is null then 'FUNCTION_NOT_FOUND'
else 'FUNCTION RECOGNIZED: custom 404 message in app logic'
end as type_of_404
from
function_edge_logs
cross join UNNEST(metadata) as metadata
cross join UNNEST(metadata.request) as req
cross join UNNEST(metadata.response) as res
where status_code = 404 and metadata.execution_id is null
where status_code = 404
limit 10;
```

Expand Down
1 change: 1 addition & 0 deletions apps/docs/features/docs/Reference.api.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface IApiEndPoint {
tags?: Array<string>
security?: Array<ISecurityOption>
'x-oauth-scope'?: string
'x-allowed-plans'?: string[]
}

export type ISchema =
Expand Down
14 changes: 14 additions & 0 deletions apps/docs/features/docs/Reference.sections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,20 @@ async function ApiEndpointSection({ link, section, servicePath }: ApiEndpointSec
</ul>
</section>
)}
{endpointDetails['x-allowed-plans'] && (
<section>
<h3 className="mb-3 text-base text-foreground">
This endpoint is only available on the following plans:
</h3>
<ul>
{endpointDetails['x-allowed-plans'].map((plan) => (
<li key={plan} className="list-['-'] ml-2 pl-2">
<span className="font-mono text-sm font-medium text-foreground">{plan}</span>
</li>
))}
</ul>
</section>
)}
{endpointFgaPermissionGroups.length > 0 && (
<section>
<h3 className="mb-3 text-base text-foreground">
Expand Down
55 changes: 43 additions & 12 deletions apps/docs/internals/generate-guides-markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import fs from 'node:fs'
import path from 'node:path'
import { globby } from 'globby'
import matter from 'gray-matter'
import { create as createTar } from 'tar'

const PARTIALS_DIR = path.join(process.cwd(), 'content', '_partials')

Expand Down Expand Up @@ -63,6 +62,44 @@ function convertStepHike(content: string): string {
})
}

/**
* For getting-started.mdx: replaces `{[ ...objects ].map(...)}` resource-card
* grids with a markdown bullet list of `[title](href), description`. Without
* this, the rendered output leaves raw JS code in the markdown since stripping
* JSX components doesn't touch JS expressions wrapped in `{...}`.
*/
function convertResourceLists(content: string): string {
return content.replace(/\{\s*\[\s*\{[\s\S]*?\},?\s*\][\s\S]*?\}\)\}/g, (block) => {
const arrMatch = block.match(/\[([\s\S]+?)\]\s*\.(?:filter|map)\b/)
if (!arrMatch) return block

// Collect top-level { ... } object literals from the array body.
const arr = arrMatch[1]
const objs: string[] = []
let depth = 0
let start = -1
for (let i = 0; i < arr.length; i++) {
if (arr[i] === '{') {
if (depth === 0) start = i
depth++
} else if (arr[i] === '}' && --depth === 0 && start !== -1) {
objs.push(arr.slice(start, i + 1))
start = -1
}
}

return objs
.map((o) => {
const title = o.match(/title:\s*['"`]([^'"`]+)['"`]/)?.[1]
const href = o.match(/href:\s*['"`]([^'"`]+)['"`]/)?.[1]
const desc = o.match(/description:\s*[`'"]([^`'"]+)[`'"]/)?.[1]
return title && href ? `- [${title}](${href})${desc ? `. ${desc}` : ''}` : ''
})
.filter(Boolean)
.join('\n')
})
}

/**
* Strips JSX component tags (capitalized names, dot-notation, or $-prefixed)
* while keeping their inner content. Also strips wrapper div and a elements.
Expand Down Expand Up @@ -128,7 +165,11 @@ async function generate() {

const withPartials = await inlinePartials(rawContent)
const withSteps = convertStepHike(withPartials)
const processed = stripJsxTags(withSteps)
const withLists =
filePath === 'content/guides/getting-started.mdx'
? convertResourceLists(withSteps)
: withSteps
const processed = stripJsxTags(withLists)

const header = [
data.title ? `# ${data.title}` : '',
Expand Down Expand Up @@ -161,16 +202,6 @@ async function generate() {

const summary = warnings ? ` (${warnings} with warnings)` : ''
console.log(`Generated ${files.length} markdown files under public/docs/guides/${summary}`)

// Create a tar.gz archive of the generated docs, served at /docs/docs.tar.gz.
// Sorted entries, portable headers, and a fixed mtime keep the output deterministic.
const archivePath = 'public/docs.tar.gz'
const entries = (await globby(['**'], { cwd: 'public/docs' })).sort()
await createTar(
{ gzip: true, file: archivePath, cwd: 'public/docs', portable: true, mtime: new Date() },
entries
)
console.log(`Created archive at ${archivePath}`)
}

generate()
16 changes: 16 additions & 0 deletions apps/docs/internals/generate-gz-archive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { globby } from 'globby'
import { create as createTar } from 'tar'

async function generate() {
// Create a tar.gz archive of the generated docs, served at /docs/docs.tar.gz.
// Sorted entries, portable headers, and a fixed mtime keep the output deterministic.
const archivePath = 'public/docs.tar.gz'
const entries = (await globby(['**'], { cwd: 'public/docs' })).sort()
await createTar(
{ gzip: true, file: archivePath, cwd: 'public/docs', portable: true, mtime: new Date() },
entries
)
console.log(`Created archive at ${archivePath}`)
}

generate()
3 changes: 2 additions & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"build:analyze": "ANALYZE=true next build",
"build:llms": "tsx --conditions=react-server ./scripts/llms.ts",
"build:guides-markdown": "tsx ./internals/generate-guides-markdown.ts",
"build:gz-archive": "tsx ./internals/generate-gz-archive.ts",
"build:sitemap": "tsx ./internals/generate-sitemap.ts",
"clean": "rimraf .next .turbo node_modules features/docs/generated examples __generated__",
"codegen:examples": "shx cp -r ../../examples ./examples",
Expand All @@ -26,7 +27,7 @@
"lint": "eslint .",
"lint:mdx": "supa-mdx-lint content --config ../../supa-mdx-lint.config.toml",
"postbuild": "pnpm run build:sitemap && pnpm run build:llms && ./../../scripts/upload-static-assets.sh",
"prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples && pnpm run build:guides-markdown",
"prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples && pnpm run build:guides-markdown && pnpm run build:gz-archive",
"predev": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples",
"preembeddings": "pnpm run codegen:references",
"preinstall": "npx only-allow pnpm",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 64 additions & 47 deletions apps/studio/components/interfaces/Auth/RedirectUrls/AddNewURLModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ import { useParams } from 'common'
import { useEffect } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { Button, cn, DialogSectionSeparator, Form, Modal, ScrollArea } from 'ui'
import {
Button,
cn,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogSection,
DialogSectionSeparator,
DialogTitle,
Form,
ScrollArea,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { SingleValueFieldArray } from 'ui-patterns/form/SingleValueFieldArray/SingleValueFieldArray'
import * as z from 'zod'
Expand Down Expand Up @@ -82,55 +95,59 @@ export const AddNewURLModal = ({ visible, allowList, onClose }: AddNewURLModalPr
}, [visible])

return (
<Modal
hideFooter
size="medium"
className="max-w-[440px]!"
visible={visible}
onCancel={() => {
<Dialog
open={visible}
onOpenChange={() => {
form.reset(initialValues)
onClose()
}}
header="Add new redirect URLs"
description="This will add a URL to a list of allowed URLs that can interact with your Authentication services for this project."
>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<Modal.Content className="flex flex-col gap-y-2 px-0">
<Label className="px-5">URL</Label>
<ScrollArea className={cn(urls.length > 4 ? 'h-[220px]' : '')}>
<div className="px-5 py-1">
<FormItemLayout className="[&>div>div]:mt-0">
<SingleValueFieldArray
control={form.control}
name="urls"
valueFieldName="value"
createEmptyRow={() => ({ value: '' })}
placeholder="https://mydomain.com"
addLabel="Add URL"
removeLabel="Remove URL"
minimumRows={1}
rowsClassName="space-y-2"
addButtonClassName="w-min"
/>
</FormItemLayout>
</div>
</ScrollArea>
</Modal.Content>
<DialogSectionSeparator />
<Modal.Content>
<Button
block
htmlType="submit"
size="small"
disabled={isUpdatingConfig}
loading={isUpdatingConfig}
>
Save URLs
</Button>
</Modal.Content>
</form>
</Form>
</Modal>
<DialogContent size="medium" className="max-w-[440px]!">
<DialogHeader>
<DialogTitle>Add new redirect URLs</DialogTitle>
<DialogDescription>
This will add a URL to a list of allowed URLs that can interact with your Authentication
services for this project.
</DialogDescription>
</DialogHeader>
<DialogSectionSeparator />
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<DialogSection className="flex flex-col gap-y-2 px-0">
<Label className="px-5">URL</Label>
<ScrollArea className={cn(urls.length > 4 ? 'h-[220px]' : '')}>
<div className="px-5 py-1">
<FormItemLayout className="[&>div>div]:mt-0">
<SingleValueFieldArray
control={form.control}
name="urls"
valueFieldName="value"
createEmptyRow={() => ({ value: '' })}
placeholder="https://mydomain.com"
addLabel="Add URL"
removeLabel="Remove URL"
minimumRows={1}
rowsClassName="space-y-2"
addButtonClassName="w-min"
/>
</FormItemLayout>
</div>
</ScrollArea>
</DialogSection>
<DialogFooter>
<Button
block
htmlType="submit"
size="small"
disabled={isUpdatingConfig}
loading={isUpdatingConfig}
>
Save URLs
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
)
}
Loading
Loading