diff --git a/apps/docs/components/GuidesTableOfContents.tsx b/apps/docs/components/GuidesTableOfContents.tsx index af1b92852ec34..4b9a8f378e28f 100644 --- a/apps/docs/components/GuidesTableOfContents.tsx +++ b/apps/docs/components/GuidesTableOfContents.tsx @@ -32,6 +32,10 @@ const formatTOCHeader = (content: string) => { begin = false res.push(``) } + } else if (x === '<') { + res.push('<') + } else if (x === '>') { + res.push('>') } else { res.push(x) } diff --git a/apps/docs/spec/cli_v1_config.yaml b/apps/docs/spec/cli_v1_config.yaml index 3188928d9e7e8..2cd7a2e2e8664 100644 --- a/apps/docs/spec/cli_v1_config.yaml +++ b/apps/docs/spec/cli_v1_config.yaml @@ -586,12 +586,53 @@ parameters: tags: ['storage'] required: false default: '"50MiB"' + description: | + The maximum file size allowed for all buckets in the project. + links: + - name: 'Storage server configuration' + link: 'https://supabase.com/docs/guides/self-hosting/storage/config' + + - id: 'storage.buckets.bucket_name.public' + title: 'storage.buckets..public' + tags: ['storage'] + required: false + default: 'false' + description: | + Enable public access to the bucket. + links: + - name: 'Storage server configuration' + link: 'https://supabase.com/docs/guides/self-hosting/storage/config' + + - id: 'storage.buckets.bucket_name.file_size_limit' + title: 'storage.buckets..file_size_limit' + tags: ['storage'] + required: false description: | The maximum file size allowed (e.g. "5MB", "500KB"). links: - name: 'Storage server configuration' link: 'https://supabase.com/docs/guides/self-hosting/storage/config' + - id: 'storage.buckets.bucket_name.allowed_mime_types' + title: 'storage.buckets..allowed_mime_types' + tags: ['storage'] + required: false + description: | + The list of allowed MIME types for objects in the bucket. + links: + - name: 'Storage server configuration' + link: 'https://supabase.com/docs/guides/self-hosting/storage/config' + + - id: 'storage.buckets.bucket_name.objects_path' + title: 'storage.buckets..objects_path' + tags: ['storage'] + required: false + description: | + The local directory to upload objects to the bucket. + links: + - name: 'Storage server configuration' + link: 'https://supabase.com/docs/guides/self-hosting/storage/config' + - id: 'auth.enabled' title: 'auth.enabled' tags: ['auth'] @@ -1176,6 +1217,7 @@ parameters: required: false description: | Specify the Deno import map file to use for the Function. + When not specified, defaults to `supabase/functions//deno.json`. Note that the `--import-map` flag overrides this configuration. links: diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx index 5d4580608f3ef..cb541d25750b7 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx @@ -63,7 +63,19 @@ const edgeFunctionSchema = z.object({ edgeFunctionName: z.string().trim().min(1, 'Please select one of the listed Edge Functions'), timeoutMs: z.coerce.number().int().gte(1000).lte(5000).default(1000), httpHeaders: z.array(z.object({ name: z.string(), value: z.string() })), - httpBody: z.string().trim(), + httpBody: z + .string() + .trim() + .optional() + .refine((value) => { + if (!value) return true + try { + JSON.parse(value) + return true + } catch { + return false + } + }, 'Input must be valid JSON'), }) const httpRequestSchema = z.object({ @@ -77,7 +89,19 @@ const httpRequestSchema = z.object({ .refine((value) => value.startsWith('http'), 'Please include HTTP/HTTPs to your URL'), timeoutMs: z.coerce.number().int().gte(1000).lte(5000).default(1000), httpHeaders: z.array(z.object({ name: z.string(), value: z.string() })), - httpBody: z.string().trim(), + httpBody: z + .string() + .trim() + .optional() + .refine((value) => { + if (!value) return true + try { + JSON.parse(value) + return true + } catch { + return false + } + }, 'Input must be valid JSON'), }) const sqlFunctionSchema = z.object({ diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx index e1a071142d597..f670e48bc4687 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx @@ -69,9 +69,7 @@ export const CronJobCard = ({ job, onEditCronJob, onDeleteCronJob }: CronJobCard onCheckedChange={() => showToggleConfirmationModal(true)} /> diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobs.utils.ts b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobs.utils.ts index 45398aa5e9ebd..b62ce3febdbd1 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobs.utils.ts +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobs.utils.ts @@ -9,7 +9,7 @@ export const buildHttpRequestCommand = ( method: 'GET' | 'POST', url: string, headers: HTTPHeader[], - body: string, + body: string | undefined, timeout: number ) => { return `$$ @@ -20,7 +20,7 @@ export const buildHttpRequestCommand = ( .filter((v) => v.name && v.value) .map((v) => `'${v.name}', '${v.value}'`) .join(', ')}), - ${method === 'POST' ? `body:='${body}',` : ''} + ${method === 'POST' && body ? `body:='${body}',` : ''} timeout_milliseconds:=${timeout} ); $$` @@ -117,7 +117,12 @@ export function calculateDuration(start: string, end: string): string { const startTime = new Date(start).getTime() const endTime = new Date(end).getTime() const duration = endTime - startTime - return isNaN(duration) ? 'Invalid Date' : `${duration} ms` + + if (isNaN(duration)) return 'Invalid Date' + + if (duration < 1000) return `${duration}ms` + if (duration < 60000) return `${(duration / 1000).toFixed(1)}s` + return `${(duration / 60000).toFixed(1)}m` } export function formatDate(dateString: string): string { diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx index 82798c330a23c..efda8511f72bd 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/HttpBodyFieldSection.tsx @@ -32,9 +32,6 @@ export const HttpBodyFieldSection = ({ form }: HttpBodyFieldSectionProps) => { onChange={field.onChange} /> - - The content should match the content-type header. - )} diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/PreviousRunsTab.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/PreviousRunsTab.tsx index 8070ed705b99e..d5a39b263bbdd 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/PreviousRunsTab.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/PreviousRunsTab.tsx @@ -1,5 +1,5 @@ import { toString as CronToString } from 'cronstrue' -import { List } from 'lucide-react' +import { CircleCheck, CircleX, List, Loader } from 'lucide-react' import Link from 'next/link' import { UIEvent, useCallback, useEffect, useMemo } from 'react' import DataGrid, { Column, Row } from 'react-data-grid' @@ -12,7 +12,6 @@ import { useCronJobRunsInfiniteQuery, } from 'data/database-cron-jobs/database-cron-jobs-runs-infinite-query' import { - Badge, Button, cn, LoadingLine, @@ -66,9 +65,7 @@ const cronJobColumns = [ id: 'status', name: 'Status', minWidth: 75, - value: (row: CronJobRun) => ( - {row.status} - ), + value: (row: CronJobRun) => , }, { id: 'start_time', @@ -80,7 +77,9 @@ const cronJobColumns = [ id: 'end_time', name: 'End Time', minWidth: 120, - value: (row: CronJobRun) =>
{formatDate(row.end_time)}
, + value: (row: CronJobRun) => ( +
{row.status === 'succeeded' ? formatDate(row.end_time) : '-'}
+ ), }, { @@ -88,7 +87,9 @@ const cronJobColumns = [ name: 'Duration', minWidth: 100, value: (row: CronJobRun) => ( -
{calculateDuration(row.start_time, row.end_time)}
+ + {row.status === 'succeeded' ? calculateDuration(row.start_time, row.end_time) : ''} + ), }, ] @@ -215,7 +216,7 @@ export const PreviousRunsTab = () => { /> -
+
{isLoadingCronJobs ? ( ) : ( @@ -291,3 +292,35 @@ export const PreviousRunsTab = () => {
) } + +interface StatusBadgeProps { + status: string +} + +function StatusBadge({ status }: StatusBadgeProps) { + if (status === 'succeeded') { + return ( + + Succeeded + + ) + } + + if (status === 'failed') { + return ( + + Failed + + ) + } + + if (['running', 'starting', 'sending', 'connecting'].includes(status)) { + return ( + + Running + + ) + } + + return null +} diff --git a/apps/studio/components/layouts/Integrations/tabs.tsx b/apps/studio/components/layouts/Integrations/tabs.tsx index bc9c475d4a95a..dbc25a2c3cfa1 100644 --- a/apps/studio/components/layouts/Integrations/tabs.tsx +++ b/apps/studio/components/layouts/Integrations/tabs.tsx @@ -96,9 +96,7 @@ export const IntegrationTabs = ({ scroll, isSticky }: IntegrationTabsProps) => { > {tab.childIcon} - - {childId} - + {childId} diff --git a/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts b/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts index d52865cf607d5..ce4e4f4bc2906 100644 --- a/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts +++ b/apps/studio/data/database-cron-jobs/database-cron-jobs-runs-infinite-query.ts @@ -18,7 +18,8 @@ export type CronJobRun = { database: string username: string command: string - status: 'succeeded' | 'failed' + // statuses https://github.com/citusdata/pg_cron/blob/f5d111117ddc0f4d83a1bad34d61b857681b6720/include/job_metadata.h#L20 + status: 'starting' | 'running' | 'sending' | 'connecting' | 'succeeded' | 'failed' return_message: string start_time: string end_time: string @@ -35,9 +36,9 @@ export async function getDatabaseCronJobRuns({ if (!projectRef) throw new Error('Project ref is required') let query = ` - SELECT * FROM cron.job_run_details - WHERE - jobid = '${jobId}' + SELECT * FROM cron.job_run_details + WHERE + jobid = '${jobId}' ${afterTimestamp ? `AND start_time < '${afterTimestamp}'` : ''} ORDER BY start_time DESC LIMIT ${CRON_JOB_RUNS_PAGE_SIZE}` diff --git a/apps/studio/next.config.js b/apps/studio/next.config.js index 2deaf3ead2e90..6eca251889ea1 100644 --- a/apps/studio/next.config.js +++ b/apps/studio/next.config.js @@ -411,8 +411,8 @@ const nextConfig = { }, { permanent: true, - source: '/project/:ref/.....', - destination: '/project/:ref/integrations/cron-jobs', + source: '/project/:ref/database/cron-jobs', + destination: '/project/:ref/integrations/cron', }, { permanent: true, diff --git a/apps/www/_blog/2024-12-01-orioledb-launch.mdx b/apps/www/_blog/2024-12-01-orioledb-launch.mdx index 54d0b26f736c0..fde932ad13f72 100644 --- a/apps/www/_blog/2024-12-01-orioledb-launch.mdx +++ b/apps/www/_blog/2024-12-01-orioledb-launch.mdx @@ -10,6 +10,7 @@ tags: - supabase-engineering date: '2024-12-01' toc_depth: 3 +launchweek: '13' --- # OrioleDB Public Alpha diff --git a/apps/www/_blog/2024-12-02-supabase-ai-assistant-v2.mdx b/apps/www/_blog/2024-12-02-supabase-ai-assistant-v2.mdx index 52258b76c352a..ad3c5c6a5977d 100644 --- a/apps/www/_blog/2024-12-02-supabase-ai-assistant-v2.mdx +++ b/apps/www/_blog/2024-12-02-supabase-ai-assistant-v2.mdx @@ -12,6 +12,7 @@ tags: - postgres date: '2024-12-02' toc_depth: 3 +launchweek: '13' --- Today we are releasing Supabase Assistant v2 in the Dashboard - a global assistant with several new abilities: diff --git a/apps/www/_blog/2024-12-03-edge-functions-background-tasks-websockets.mdx b/apps/www/_blog/2024-12-03-edge-functions-background-tasks-websockets.mdx new file mode 100644 index 0000000000000..744914b033ead --- /dev/null +++ b/apps/www/_blog/2024-12-03-edge-functions-background-tasks-websockets.mdx @@ -0,0 +1,276 @@ +--- +title: 'Supabase Edge Functions: Introducing Background Tasks, Ephemeral Storage, and WebSockets' +description: Edge functions can be used for workloads outside the request-response lifecycle +author: laktek,nyannyacha +image: launch-week-13/day-2-functions-background-tasks/og.jpg +thumb: launch-week-13/day-2-functions-background-tasks/thumb.jpg +categories: + - product +tags: + - functions + - launch-week +date: '2024-12-03' +toc_depth: 3 +launchweek: '13' +--- + +We are excited to announce three long-awaited features: Background Tasks, Ephemeral File Storage, and WebSockets. + +Starting today, you can use these features in any project. Let's explore what exciting things you can build with them. + +
+