mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 12:55:06 +03:00
Merge branch 'master' into feat/mermaid-blog
This commit is contained in:
18 files changed
+120
-17
No files matched your search
@@ -8,7 +8,7 @@ A [JSON Web Token](https://jwt.io/introduction) is a type of data structure, rep
|
||||
|
||||
Supabase Auth continuously issues a new JWT for each user session, for as long as the user remains signed in. Check the comprehensive guide on [Sessions](/docs/guides/auth/sessions) to find out how you can tailor this process for your needs.
|
||||
|
||||
JWTs provide the foundation for [Row Level Security](/docs/guides/database/row-level-security). Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.
|
||||
JWTs provide the foundation for [Row Level Security](/docs/guides/database/postgres/row-level-security). Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.
|
||||
|
||||
Supabase provides a comprehensive system of managing [JWT Signing Keys](/docs/guides/auth/signing-keys) used to create and verify JSON Web Tokens.
|
||||
|
||||
|
||||
@@ -572,7 +572,7 @@ npx expo start --tunnel
|
||||
|
||||
And add it to the **Redirect URLs** field in [your Supabase dashboard Authentication configuration](/dashboard/project/_/auth/url-configuration).
|
||||
|
||||
For more information, follow the [Supabase Login with Apple](docs/guides/auth/social-login/auth-apple) guide.
|
||||
For more information, follow the [Supabase Login with Apple](/docs/guides/auth/social-login/auth-apple) guide.
|
||||
|
||||
</Admonition>
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ async function signInWithFigma() {
|
||||
</TabPanel>
|
||||
<TabPanel id="flutter" label="Flutter">
|
||||
|
||||
When your user signs in, call [`signInWithOAuth()`](/docs/reference/flutter/auth-signinwithoauth) with `figma` as the `provider`:
|
||||
When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `figma` as the `provider`:
|
||||
|
||||
```dart
|
||||
Future<void> signInWithFigma() async {
|
||||
|
||||
@@ -571,7 +571,7 @@ Future<void> _nativeGoogleSignIn() async {
|
||||
|
||||
<TabPanel id="flutter-other" label="Flutter (web, macOS, Windows, Linux)">
|
||||
|
||||
Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [`signInWithOAuth`](docs/reference/dart/auth-signinwithoauth) method.
|
||||
Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [`signInWithOAuth`](/docs/reference/dart/auth-signinwithoauth) method.
|
||||
|
||||
This method of signing in is web based, and will open a browser window to perform the sign in. For non-web platforms, the user is brought back to the app via [deep linking](/docs/guides/auth/native-mobile-deep-linking?platform=flutter).
|
||||
|
||||
@@ -702,7 +702,7 @@ When using [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multipla
|
||||
|
||||
**Initialize the Supabase Client**
|
||||
|
||||
**Note:** You have to create OAuth credentials for both a Web and Android application. [Learn more](https://developers.google.com/identity/one-tap/android/get-started#api-console)
|
||||
**Note:** You have to create OAuth credentials for both a Web and Android application. [Learn more](https://developer.android.com/identity/legacy/one-tap/legacy-get-started)
|
||||
|
||||
```kotlin
|
||||
val supabaseClient = createSupabaseClient(
|
||||
|
||||
@@ -93,4 +93,4 @@ create table contacts (
|
||||
## Resources
|
||||
|
||||
- [Choosing a Postgres Primary Key](/blog/choosing-a-postgres-primary-key)
|
||||
- [The Basics Of Postgres `UUID` Data Type](https://www.postgresqltutorial.com/postgresql-uuid/)
|
||||
- [The Basics Of Postgres `UUID` Data Type](https://www.pgtutorial.com/postgresql-tutorial/postgresql-uuid/)
|
||||
@@ -244,4 +244,4 @@ Use the value from the `BRANCH PROJECT ID` column as your `project_id` in the re
|
||||
|
||||
- Explore [branching integrations](/docs/guides/deployment/branching/integrations)
|
||||
- Learn about [troubleshooting branches](/docs/guides/deployment/branching/troubleshooting)
|
||||
- Review [branching pricing](/docs/guides/deployment/branching/pricing)
|
||||
- Review [branching pricing](/docs/guides/platform/manage-your-usage/branching#pricing)
|
||||
@@ -55,7 +55,7 @@ After developing your project and deciding it's Production Ready, you should run
|
||||
- You can restore paused projects from the Supabase dashboard.
|
||||
- Upgrade to Pro to guarantee that your project will not be paused for inactivity.
|
||||
- Database backups are not available for download on the Free Plan.
|
||||
- You can set up your own backup systems using tools like [pg_dump](https://www.postgresqltutorial.com/postgresql-backup-database/) or [wal-g](https://github.com/wal-g/wal-g).
|
||||
- You can set up your own backup systems using tools like [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html) or [wal-g](https://github.com/wal-g/wal-g).
|
||||
- Nightly backups for Pro Plan projects are available on the Supabase dashboard for up to 7 days.
|
||||
- Point in Time Recovery (PITR) allows a project to be backed up at much shorter intervals. This provides users an option to restore to any chosen point of up to seconds in granularity. In terms of Recovery Point Objective (RPO), Daily Backups would be suitable for projects willing to lose up to 24 hours worth of data. If a lower RPO is required, enable PITR.
|
||||
- Supabase Projects use disks that offer 99.8-99.9% durability by default.
|
||||
|
||||
@@ -92,7 +92,7 @@ You can use Supabase to store and process Protected Health Information (PHI). Yo
|
||||
- Signing a Business Associate Agreement (BAA) with Supabase. Submit a [HIPAA add-on request](https://forms.supabase.com/hipaa2) to get started. You will need to be at least on the [Team Plan](/pricing) to sign a BAA with us.
|
||||
- [Marking specific projects as HIPAA projects](/docs/guides/platform/hipaa-projects) and addressing security issues raised by the advisor.
|
||||
- Ensuring [MFA is enabled](/docs/guides/platform/multi-factor-authentication) on all Supabase accounts.
|
||||
- [Enforce MFA](/docs/guides/platform/org-mfa-enforcement) as a requirement to access the organization
|
||||
- [Enforce MFA](/docs/guides/platform/mfa/org-mfa-enforcement) as a requirement to access the organization
|
||||
- Enabling [Point in Time Recovery](/docs/guides/platform/backups#point-in-time-recovery) which requires at least a [small compute add-on](/docs/guides/platform/compute-add-ons).
|
||||
- Turning on [SSL Enforcement](/docs/guides/platform/ssl-enforcement).
|
||||
- Enabling [Network Restrictions](/docs/guides/platform/network-restrictions).
|
||||
|
||||
@@ -36,7 +36,7 @@ For security reasons, we will not be able to restore access to your account if y
|
||||
|
||||
Once you've enabled MFA for your Supabase user account, you will be prompted to enter your second factor challenge code as seen in your preferred TOTP app.
|
||||
|
||||
If you are an organization owner and on the Pro, Team or Enterprise plan, you can enforce that all organization members [must have MFA enabled](/docs/guides/platform/org-mfa-enforcement).
|
||||
If you are an organization owner and on the Pro, Team or Enterprise plan, you can enforce that all organization members [must have MFA enabled](/docs/guides/platform/mfa/org-mfa-enforcement).
|
||||
|
||||
## Disable MFA
|
||||
|
||||
|
||||
@@ -10,6 +10,25 @@ Let's explore how to implement Realtime Presence to track state between multiple
|
||||
|
||||
You can use the Supabase client libraries to track Presence state between users.
|
||||
|
||||
### How Presence works
|
||||
|
||||
Presence lets each connected client publish a small piece of state—called a “presence payload”—to a shared channel. Supabase stores each client’s payload under a unique presence key and keeps a merged view of all connected clients.
|
||||
|
||||
When any client subscribes, disconnects, or updates their presence payload, Supabase triggers one of three events:
|
||||
|
||||
- **`sync`** — the full presence state has been updated
|
||||
- **`join`** — a new client has started tracking presence
|
||||
- **`leave`** — a client has stopped tracking presence
|
||||
|
||||
The complete presence state returned by `presenceState()` looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"client_key_1": [{ "userId": 1, "typing": false }],
|
||||
"client_key_2": [{ "userId": 2, "typing": true }]
|
||||
}
|
||||
```
|
||||
|
||||
### Initialize the client
|
||||
|
||||
Go to your Supabase project's [API Settings](/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
|
||||
|
||||
@@ -80,7 +80,7 @@ Here's a list of the most common error codes and their potential resolutions:
|
||||
Indicates that the resource is not found or you don't have the correct permission to access it
|
||||
**Resolution:**
|
||||
|
||||
- Add a RLS policy to grant permission to the resource. See our [Access Control docs](/docs/guides/storage/uploads/access-control) for more information.
|
||||
- Add a RLS policy to grant permission to the resource. See our [Access Control docs](/docs/guides/storage/security/access-control) for more information.
|
||||
- Ensure you include the user `Authorization` header
|
||||
- Verify the object exists
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
|
||||
|
||||
function onDeleteRow(p: RowContextMenuItemProps) {
|
||||
const rowIdx = p.props?.rowIdx
|
||||
if (!rowIdx) return
|
||||
if (rowIdx === undefined || rowIdx === null) return
|
||||
|
||||
const row = rows[rowIdx]
|
||||
if (row) tableEditorSnap.onDeleteRows([row])
|
||||
@@ -30,7 +30,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
|
||||
|
||||
function onEditRowClick(p: RowContextMenuItemProps) {
|
||||
const rowIdx = p.props?.rowIdx
|
||||
if (!rowIdx) return
|
||||
if (rowIdx === undefined || rowIdx === null) return
|
||||
|
||||
const row = rows[rowIdx]
|
||||
tableEditorSnap.onEditRow(row)
|
||||
@@ -39,7 +39,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
|
||||
const onCopyCellContent = useCallback(
|
||||
(p: RowContextMenuItemProps) => {
|
||||
const rowIdx = p.props?.rowIdx
|
||||
if (!snap.selectedCellPosition || !rowIdx) return
|
||||
if (!snap.selectedCellPosition || rowIdx === undefined || rowIdx === null) return
|
||||
|
||||
const row = rows[rowIdx]
|
||||
const columnKey = snap.gridColumns[snap.selectedCellPosition.idx as number].key
|
||||
@@ -56,7 +56,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
|
||||
const onCopyRowContent = useCallback(
|
||||
(p: RowContextMenuItemProps) => {
|
||||
const rowIdx = p.props?.rowIdx
|
||||
if (!rowIdx) return
|
||||
if (rowIdx === undefined || rowIdx === null) return
|
||||
|
||||
const row = rows[rowIdx]
|
||||
copyToClipboard(JSON.stringify(row))
|
||||
|
||||
@@ -48,7 +48,7 @@ test('renders dates in local time', async () => {
|
||||
)
|
||||
// renders time locally
|
||||
await userEvent.click(await screen.findByText(RegExp(from.format('DD MMM'))))
|
||||
await screen.findByText(RegExp(from.format('MMMM YYYY')))
|
||||
await screen.findByText(RegExp(from.format('DD MMM')))
|
||||
})
|
||||
|
||||
test('renders datepicker selected dates in local time', async () => {
|
||||
|
||||
@@ -20,6 +20,19 @@ description: Real-time chat component for collaborative applications
|
||||
|
||||
The Realtime Chat component provides a complete chat interface that enables users to exchange messages in real-time within a shared room.
|
||||
|
||||
## How it works under the hood
|
||||
|
||||
This chat component uses **Supabase Realtime Broadcast** to send and receive messages between connected clients.
|
||||
|
||||
Messages sent through Broadcast are:
|
||||
|
||||
- delivered in real time to other connected clients
|
||||
- **not stored** unless you handle persistence yourself
|
||||
- **not guaranteed** to arrive if the client disconnects
|
||||
- scoped to a specific `roomName`, which corresponds to a broadcast channel
|
||||
|
||||
This design keeps latency extremely low, but it means you should use the `onMessage` callback if you want to store messages permanently or show chat history on page load.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Image } from 'ui'
|
||||
import dynamic from 'next/dynamic'
|
||||
import MainProducts from './MainProducts'
|
||||
import { PRODUCT_SHORTNAMES } from 'shared-data/products'
|
||||
import { SITE_ORIGIN } from '~/lib/constants'
|
||||
|
||||
const AuthVisual = dynamic(() => import('components/Products/AuthVisual'))
|
||||
const FunctionsVisual = dynamic(() => import('components/Products/FunctionsVisual'))
|
||||
@@ -53,6 +54,7 @@ const data = {
|
||||
metaTitle: 'Supabase at AWS re:Invent 2025 | December 1-4, Las Vegas',
|
||||
metaDescription:
|
||||
'Meet Supabase at AWS re:Invent 2025. Visit us at Booth #1857, join our exclusive events, and learn how to build in a weekend and scale to millions.',
|
||||
ogImage: `${SITE_ORIGIN}/images/og/re_invent.png`,
|
||||
},
|
||||
heroSection: {
|
||||
id: 'hero',
|
||||
|
||||
@@ -21,7 +21,14 @@ const AWSReInvent2025: NextPage = () => {
|
||||
openGraph={{
|
||||
title: data.metadata.metaTitle,
|
||||
description: data.metadata.metaDescription,
|
||||
url: `https://supabase.com/aws-reinvent-2025`,
|
||||
images: [
|
||||
{
|
||||
url: data.metadata.ogImage,
|
||||
width: 1280,
|
||||
height: 685,
|
||||
alt: 'Supabase AWS Re:Invent 2025',
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Layout className="overflow-visible">
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -717,4 +717,66 @@ test.describe('table editor', () => {
|
||||
|
||||
await deleteTable(page, ref, tableNameDataActions)
|
||||
})
|
||||
|
||||
test('copying cell values from first and second row works', async ({ page, ref }) => {
|
||||
const tableName = 'pw_table_copy_rows'
|
||||
const colName = 'pw_column'
|
||||
|
||||
// Ensure we're on editor
|
||||
if (!page.url().includes('/editor')) {
|
||||
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
|
||||
await waitForTableToLoad(page, ref)
|
||||
}
|
||||
|
||||
// Create table and add two rows
|
||||
await createTable(page, ref, tableName)
|
||||
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
|
||||
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
|
||||
|
||||
// Insert first row with value 'first_row_value'
|
||||
await page.getByTestId('table-editor-insert-new-row').click()
|
||||
await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
|
||||
await page.getByTestId(`${colName}-input`).fill('first_row_value')
|
||||
await page.getByTestId('action-bar-save-row').click()
|
||||
await waitForApiResponse(page, 'pg-meta', ref, 'query?key=', { method: 'POST' })
|
||||
|
||||
// Insert second row with value 'second_row_value'
|
||||
await page.getByTestId('table-editor-insert-new-row').click()
|
||||
await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
|
||||
await page.getByTestId(`${colName}-input`).fill('second_row_value')
|
||||
await page.getByTestId('action-bar-save-row').click()
|
||||
await waitForApiResponse(page, 'pg-meta', ref, 'query?key=', { method: 'POST' })
|
||||
|
||||
// Wait for grid to be visible
|
||||
await expect(page.getByRole('grid')).toBeVisible()
|
||||
|
||||
// Right-click on the first row's cell to open context menu
|
||||
const firstRowCell = page.getByRole('gridcell', { name: 'first_row_value' })
|
||||
await expect(firstRowCell).toBeVisible()
|
||||
await firstRowCell.click({ button: 'right' })
|
||||
|
||||
// Click "Copy cell" from context menu
|
||||
await page.getByRole('menuitem', { name: 'Copy cell' }).click()
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
// Verify first row value was copied
|
||||
const firstCopiedValue = await page.evaluate(() => navigator.clipboard.readText())
|
||||
expect(firstCopiedValue).toBe('first_row_value')
|
||||
|
||||
// Right-click on the second row's cell to open context menu
|
||||
const secondRowCell = page.getByRole('gridcell', { name: 'second_row_value' })
|
||||
await expect(secondRowCell).toBeVisible()
|
||||
await secondRowCell.click({ button: 'right' })
|
||||
|
||||
// Click "Copy cell" from context menu
|
||||
await page.getByRole('menuitem', { name: 'Copy cell' }).click()
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
// Verify second row value was copied
|
||||
const secondCopiedValue = await page.evaluate(() => navigator.clipboard.readText())
|
||||
expect(secondCopiedValue).toBe('second_row_value')
|
||||
|
||||
// Cleanup
|
||||
await deleteTable(page, ref, tableName)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user