mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
f05ab582da90efd5f4fa64e2dc314d7ae6b92556
126
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
205ab69061 |
feat(studio): move CLI login to connect interstitial (#45814)
## What kind of change does this PR introduce? Feature / UI refactor ## What is the current behaviour? The CLI browser login route still uses the older API authorisation layout and redirects missing or failed sign-in session states to generic 404/500 pages. ## What is the new behaviour? Moves `/cli/login` onto the shared connect interstitial layout as the next small stacked slice after the organisation invite work. This keeps the real CLI login contract intact while updating the surface: - creates the CLI login session from `session_id`, `public_key`, and optional `token_name` - redirects to the generated `device_code` - renders missing-parameter and session-creation failures in-card instead of redirecting away - keeps the 8-character verification code selectable and copyable as a single string - uses a full-width primary `Copy code` action This also adds the small shared interstitial helpers needed by this surface and adjusts `CopyButton` so the copied check icon inherits the primary button colour instead of turning green. This also removes the CLI version admonition: > Browser login flow requires Supabase CLI version 1.219.0 and above. I checked with our stats and the CLI team. The vast majority of users are on a newer version. | Before | After | | --- | --- | | <img width="1024" height="759" alt="Authorize API access Supabase-D1E3CF26-BD59-4BB2-B457-B552EE47E3DA" src="https://github.com/user-attachments/assets/c89b8b13-fa98-41b7-8093-e59d15b2aa9e" /> | <img width="1024" height="759" alt="Authorize CLI Supabase-C9977F21-88B8-441B-8A2C-09A9515935B0" src="https://github.com/user-attachments/assets/ca13b65a-3875-425c-b73b-8f2101c1e406" /> | | <img width="1024" height="759" alt="Supabase-F42FBEAF-F74D-4920-8A51-7C25004F66D5" src="https://github.com/user-attachments/assets/51adb1e6-a2fb-41fb-b36f-0ae466fe60e2" /> | <img width="1024" height="759" alt="Authorize CLI Supabase-8159A1B1-2594-4183-AC35-FEF1EFD4EA37" src="https://github.com/user-attachments/assets/6f143218-795d-41c9-a8e1-52e529a6b988" /> | <img width="1024" height="759" alt="Supabase-2506E468-9F42-44B9-A5B7-BC4D3777F552" src="https://github.com/user-attachments/assets/a304fca5-cf26-4ae7-abe9-77cdbc21fba5" /> | <img width="1024" height="759" alt="Authorize CLI Supabase-A0EE1239-A345-427C-9CF7-997037A8FC0E" src="https://github.com/user-attachments/assets/33118777-35f3-49d6-bc1e-30e7124b3677" /> | | <img width="1024" height="759" alt="Authorize API access Supabase-A7B84CA6-D230-4C3E-9227-DE21CE35375C" src="https://github.com/user-attachments/assets/78eb6296-035a-4201-b254-b97eda44443c" /> | <img width="1024" height="759" alt="Authorize CLI Supabase-F55E26B2-609B-449C-9C64-08AA90AE3D1E" src="https://github.com/user-attachments/assets/ff7b3b4e-729c-4681-844d-2d5d94bfc084" /> | ## Testing instructions Use the Vercel preview URL for this PR once it is available. The examples below use `<preview-origin>` as a placeholder, for example `https://studio-git-dnywh-feat-cli-login-interstitial-supabase.vercel.app`. You need to be signed in to Studio to see these states because `/cli/login` is still behind `withAuth`. Ready state: - Open `<preview-origin>/cli/login?device_code=ABCD1234` - Check the page title is `Authorize CLI | Supabase` - Check the card title is `Authorize Supabase CLI` - Check the code fills the width, uses the normal sans font, and can be selected - Drag-select the code and copy it; the clipboard should contain `ABCD1234`, not one character per line - Click `Copy code`; the button should show the usual copied success state without a green check icon on the primary button Missing parameters state: - Open `<preview-origin>/cli/login` - Check the card says `Missing sign-in parameters` and names the missing `session_id` and `public_key` parameters - Open `<preview-origin>/cli/login?session_id=session-test` - Check it still stays in-card and names the missing `public_key` parameter instead of redirecting to `/404` Creation error state: - Open `<preview-origin>/cli/login?session_id=not-real&public_key=not-real&token_name=local-dev` - Check it stays in-card with `Unable to create CLI sign-in` instead of redirecting to `/500` - The exact error detail can vary by environment; the important bit is that the failure is shown inside the interstitial card Loading state: - This is transient because there are no production mocks in this slice - To inspect it manually, throttle the browser network before opening a session-creation URL such as `<preview-origin>/cli/login?session_id=not-real&public_key=not-real` Real CLI flow: - Run the browser login flow from Supabase CLI as usual - When the CLI opens a Studio URL, keep the path and query string but replace the origin with the PR preview origin - The page should create the login session and then route to `/cli/login?device_code=<8 character code>` - Enter that 8-character code back in the CLI prompt <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Redesigned CLI login flow with clearer state-driven screens and improved verification UI. * Added a small paired-logo component for centered logo pairs with a connector icon. * **Improvements** * Copy button behavior and styling refined for consistent visual feedback across variants. * **Tests** * New unit tests covering copy-button behavior and multiple CLI login UI flows. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45814) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9660b0075c |
refine organisation invite state helpers (#45813)
## What kind of change does this PR introduce? Code cleanup. Follow-up to #45774. ## What is the current behavior? The organisation invite interstitial derives invite states, titles, and descriptions from nested conditional logic in the component. That makes the component harder to scan and pushes too much state coverage into render tests. ## What is the new behavior? See #45774 for screenshots of the general UI before-and-after (which this one builds upon). That PR also contains testing instructions. Extracts the invite status and content decisions into small pure helpers, then covers those helpers with focused unit tests. The component keeps the user-facing render and interaction coverage, including the invalid lookup regression where a 404 should render the invalid invite state instead of raw backend copy. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved organization invite flow with enhanced error state handling for expired, invalid, and wrong-account scenarios. * Better consistency in error messages and user guidance throughout the invite process. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45813) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
791fc74412 |
feat(studio): shared connect layout for organisation invites (#45774)
## What kind of change does this PR introduce? Feature. Part of DEPR-279. ## What is the current behavior? The organization invite page has its own bespoke centered card and page-level Supabase logo. ## What is the new behavior? Introduces a minimal shared interstitial layout and migrates `/join` onto it as the first small connect-surface slice. The invite API and accept-invite mutation paths are unchanged. | Before | After | | --- | --- | | <img width="1024" height="794" alt="Supabase-F2325C57-D5DE-445D-8083-12EF8A1EE0CA" src="https://github.com/user-attachments/assets/b23dcc7a-c649-4b59-9393-9232d74f0c6b" /> | <img width="1024" height="794" alt="Join Organization Supabase-66CDA329-0531-4B12-AC32-A7E21931F876" src="https://github.com/user-attachments/assets/454917ce-1a96-4e50-b003-6c16a541b39a" /> | | <img width="1060" height="822" alt="CleanShot 2026-03-13 at 11 04 43@2x-2616AECB-8203-4439-A1CD-45AB18FC4CA8 1-584A0600-CCE0-4F16-9111-9BEB94BE85EC" src="https://github.com/user-attachments/assets/871c7dcb-120e-40cd-afc8-2cec95e4b7ae" /> | <img width="1024" height="794" alt="Join Organization Supabase-26AD978E-4CF9-4600-9885-082084349E94" src="https://github.com/user-attachments/assets/ee9bfaff-dde4-4366-abae-77dc8a95c4ef" /> | | <img width="1024" height="794" alt="Supabase-4993D74C-D62B-43B7-9681-826BE1591AC4" src="https://github.com/user-attachments/assets/1c411ae0-90e7-481d-a4cc-3eac26267291" /> | <img width="1024" height="794" alt="Join Organization Supabase-C84D4E4C-24F5-463D-B1D6-D11D3256596F" src="https://github.com/user-attachments/assets/688387a4-3c49-41db-b89c-7c5531e91aed" /> | | <img width="1024" height="794" alt="Supabase-D9BD2601-98A4-489D-A51D-CEB73F51FA6F" src="https://github.com/user-attachments/assets/6d1da65f-d655-4047-9f6a-db65f8c0a729" /> | <img width="1024" height="794" alt="Join Organization Supabase-50065F40-179A-4BD6-8F1D-6106FFD8A15C" src="https://github.com/user-attachments/assets/e61809f9-dcec-4e51-ba94-91b04010ec50" /> | ## Testing notes Staging invite emails are generated with the fixed staging dashboard origin, for example: ```text https://supabase.green/dashboard/join?token=...&slug=... ``` To test this PR preview with a real invite token, keep the path and query string from the email but replace the origin with the Vercel preview origin, for example: ```text https://studio-staging-git-dnywh-featconnect-interstitial-join-supabase.vercel.app/dashboard/join?token=...&slug=... ``` ### Manual state checks - **Signed out:** open the swapped invite URL in an incognito window or a browser signed out of Studio. Expected: `View invitation`, sign-in/create-account actions, and no loading skeleton hang. - **Wrong account:** sign in to the PR preview as an account that is not the invite recipient, then open the swapped invite URL. Expected: `Wrong account`, warning callout, and `Sign out`. - **Happy path:** sign in as the invited email address, then open the swapped invite URL. Expected: `Join {Organization}`, signed-in account row, `Accept invite`, and `Decline`. Accepting should join the organization. - **Invalid token:** alter one character in the token in the swapped invite URL. Expected: invalid invite state. - **No longer valid:** accept the invite once, then open the same swapped invite URL again. Expected: no-longer-valid/already-used state, depending on the backend response. ### Test-covered states Expired invites, generic backend error, loading, and create-account-disabled states are harder to force manually in staging. They are covered by `tests/components/OrganizationInvite.test.tsx`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Redesigned the organization invitation experience with an interstitial layout, clearer early-return flows for signed-out, loading, expired/invalid, wrong-account, and accepted-invite states; primary CTA now reads “Accept invite”. * Streamlined error and sign-out flows with clearer, focused messaging. * **New Features** * Added a reusable interstitial layout and compact account row for invitation screens. * **Tests** * Added comprehensive tests covering invite states, accept/decline actions, and error handling. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45774) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d676c832f3 |
fix(studio): pre-empt React 19 regressions in tests + Support form (#45784)
Four React-19-sensitive patterns that pass on React 18 today but break
under React 19 (verified on the in-flight TanStack Start branch).
Landing on master now so the eventual React 19 upgrade is a no-op for
tests, instead of a separate cleanup pass under upgrade pressure.
Each fix is a strict superset / less-fragile equivalent of the existing
pattern, so master (React 18) stays green.
**Changed:**
- `hooks/misc/useStateTransition.ts` — fire on entry into `newTest` from
any state other than `newTest`, instead of requiring exactly `prevTest →
newTest`. React 18+ auto-batches dispatches across awaits (e.g.
`dispatch SUBMIT` in the handler, `dispatch ERROR` in `onError`),
collapsing `editing → submitting → error` into a single render where the
intermediate `submitting` tick is never observed. Strict superset of the
old check for our reducers — `success`/`error` are only reachable from
`submitting`.
- `Support/CategoryAndSeverityInfo.tsx` — guard `onValueChange` against
Radix Select's spurious `''` emission. When the controlled value
transitions from `undefined` to a defined value whose `SelectItem` isn't
mounted yet (dropdown closed → items haven't registered), Radix's hidden
`BubbleSelect` fires `onValueChange('')` and clobbers the field. No
`SelectItem` can have `value=""` (Radix throws), so any `''` is
guaranteed spurious — drop it before calling `field.onChange`.
([radix-ui/primitives#3381](https://github.com/radix-ui/primitives/issues/3381))
- `EditSecretModal.test.tsx` — `getByLabelText` → `findByLabelText`.
Under React 19's scheduling, the decrypted-value query resolves on a
separate render tick, so form fields appear one tick after the skeleton.
- `LogsPreviewer.test.tsx` — `addEventListener('click', spy)` instead of
`loadOlder.onclick = vi.fn()`. React 19 reassigns `.onclick` on managed
elements as part of its event wiring, clobbering the direct-property
spy.
## To test
### Unit tests
- `pnpm --filter studio test` — all unit tests pass on master (React 18)
### Support form URL prefill (Radix Select guard)
- `/support/new?category=Problem` → category dropdown reads "APIs and
client libraries" on first paint
- `/support/new?category=dashboard_bug` → "Dashboard bug"
(case-insensitive match)
- `/support/new?category=invalid_garbage` → falls back to "Select an
issue" placeholder, no crash
- `/support/new?subject=My%20issue&message=Details%20here` → subject and
message inputs are prefilled
- `/support/new?projectRef=<your-ref>&category=Problem` → both project
selector and category set, library selector appears
- With a prefilled URL, click the category dropdown and pick a different
option — the new value sticks (this is the path that surfaced the Radix
bug, want to confirm we didn't break user selection)
- DevTools console on first load should be clean — no React hydration
mismatch warning
### Support form submit (`useStateTransition` success + error branches)
- Submit a valid support form → green toast "Support request sent"
appears **once**, view swaps to the success screen, one `POST
/platform/feedback/send` in the network panel
- Block `POST /platform/feedback/send` in DevTools → submit → red error
toast appears **once** (not twice — if you see two toasts the relaxed
transition is firing more than it should), form stays editable with all
inputs preserved
- Unblock and submit again → success path runs cleanly
### Sidebar support form (same reducer + `useStateTransition`, separate
component)
- Open the support widget in the side nav (`SupportSidebarForm`)
- Repeat the success and error paths — should behave identically
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed category selector to prevent selected values from being
unexpectedly cleared during form interactions.
* **Tests**
* Improved test reliability for modal field rendering and event handling
assertions.
* **Chores**
* Clarified internal comments for form initialization logic.
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45784)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
|
||
|
|
d8bb0ade65 |
feat(studio): add timezone picker to user dropdown (#45517)
## Problem The dashboard renders all timestamps in the browser's local timezone. When debugging app issues, users often want to see logs and timestamps in a different timezone (e.g. their app's deployment region) without changing their OS clock. ## Fix - New Timezone submenu in the user-avatar dropdown, sitting next to the existing Theme picker. Search-as-you-type combobox over the full IANA catalog plus an Auto detect option. - Selection persists in localStorage (`supabase-ui-timezone`) and survives `clearLocalStorage()`. No backend schema change. - New `lib/datetime.tsx` exposes pure timezone-aware formatters (`formatDateTime`, `formatDate`, `formatTime`, `formatFromNow`, `toTimezone`) plus a `TimezoneProvider` and matching React hooks (`useTimezone`, `useFormatDateTime`, ...). The pure functions take `tz` explicitly so they're easy to unit test (17 vitest cases covering DST transitions, multi-tz formatting, unix-micro/Date inputs, invalid-tz fallback). - The selected timezone propagates to every existing `<TimestampInfo>` in Studio via a new `TimestampInfoProvider` context exported from `ui-patterns`. No per-callsite changes needed for those ~20+ surfaces. - The `UnifiedLogs` date column migrates off `date-fns` to the new `useFormatDateTime` hook (the rest of the date-fns callers stay as-is, since they're either internal range math or non-display). - `ALL_TIMEZONES` (~600 entries) moves out of `PITR.constants.ts` into a shared `lib/constants/timezones.ts`. PITR keeps a re-export shim so its callers don't move. New `TIMEZONES_BY_IANA` dedupes the catalog by primary IANA name (the original list contains both PDT and PST rows for `America/Los_Angeles`, etc.) and `findTimezoneByIana` provides reverse lookup. - Telemetry: `timezone_picker_clicked` PostHog event with `previousTimezone`, `nextTimezone`, `isAutoDetected` properties. Notes for reviewers: - Bare `dayjs(x).format(...)` calls (~157 files) intentionally still render in browser-local time. Surfaces opt in by switching to the new wrappers, so this PR is the abstraction plus logs adoption; broader migration is a follow-up. - Two `// prettier-ignore` lines (`apps/studio/pages/_app.tsx`, `apps/studio/components/interfaces/UnifiedLogs/UnifiedLogs.fields.tsx`) work around a pre-existing local-tooling issue where `prettier-plugin-sql-cst` strips angle-bracket type arguments under certain conditions. Project's pinned prettier (3.8.1) does not strip; the issue surfaces with a globally-installed prettier. Worth tracking separately. - Hydration: `guessLocalTimezone()` and `useLocalStorageQuery` are client-only. Studio is mostly CSR via the Pages Router, but any SSR'd `<TimestampInfo>` may briefly render in the server's tz before client hydration. Existing behavior already had this mismatch with `.local()`; this PR does not regress it. - Backend timestamps round-tripped through query params and mutations stay UTC. The picker is display-only. ## How to test - Run `pnpm dev:studio`, sign in. - Open the user avatar dropdown (top right). Hover Timezone. - Search for "tokyo", pick `(UTC+09:00) Osaka, Sapporo, Tokyo`. - Open any project, navigate to Logs (e.g. `Project > Logs > Edge Functions`). Hover a log row's timestamp; the popover should show UTC, the chosen tz (`Asia/Tokyo`), and the relative time. Visible cell text should be in JST. - Visit any page that uses `<TimestampInfo>` (Database > Backups, Project Pause state, Edge Function details). Same tooltip should reflect Asia/Tokyo. - Refresh the page; timezone is still Asia/Tokyo. - Reopen the picker, choose Auto detect; timestamps revert to browser local. - Run `pnpm --filter studio test lib/datetime.test.ts`. 17 tests should pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Timezone selector added to the user menu with auto-detect and manual override * App-wide timezone provider and hooks plus a shared timezone catalog for consistent timezone-aware display * Timestamp components accept an optional timezone prop and respect user preference (persisted) * **Bug Fixes / Improvements** * Logs and timestamp displays now use the new timezone formatting hooks * **Tests** * Added comprehensive datetime and timezone catalog tests * **Telemetry** * Telemetry event added for timezone picker interactions <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
153f2619bc |
feat(studio): show expand affordance for large SQL result cells (#45589)
## Summary
- Adds a hover-revealed expand button to SQL result cells whose value is
unlikely to fit on one line (objects, arrays, strings >60 chars, or
strings with newlines). Clicking opens the existing `CellDetailPanel`
for that cell.
- Switches the expand state from a boolean tied to the selected cell to
a direct `{ column, value }` reference, so the context menu and the new
button both target the right-clicked / clicked cell.
- Extracts the per-cell renderer into its own `ResultCell` component to
keep `Results.tsx` digestible and the new affordance isolated.
- Covers the new logic with exhaustive `isLargeValue` unit tests and a
`ResultCell` component test (visibility, click, right-click).
Linear: [FE-3130](https://linear.app/supabase/issue/FE-3130)
## Test plan
- [x] Run a SQL query that returns mixed cell types (short strings, long
strings, JSON objects, arrays, nulls) and confirm the expand button
appears only on cells where content is likely truncated.
- [x] Hover a large cell and click the expand button — `CellDetailPanel`
opens with the correct column + value.
- [x] Right-click a large cell and choose "View cell content" — same
panel opens with the right cell.
- [x] Right-click a small cell and "Copy cell content" — clipboard
contains the raw value.
- [x] Resize a column wider than its content and confirm the button
still positions correctly.
- [x] `pnpm vitest` for `Results.utils.test.ts`, `Results.test.tsx`,
`ResultCell.test.tsx` — all green.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enhanced SQL result cells with automatic detection and expansion
functionality for large values (exceeding 60 characters or containing
line breaks)
* Added expand button to view full cell content directly in results
* Integrated right-click context menu for cell content options
* Improved display of null values in query results
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
ee5d4a9314 |
chore: remove format param from audit log query (#45466)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Cleanup after shipping https://github.com/supabase/supabase/pull/45389, the backend is now defaulting to the new v2 `format`, and made `format` param optional. So this: - removes references to `v2` naming, as this is the only format - removes the `format` query param from the audit logs API calls ## What is the current behavior? Same audit log functionality shown in https://github.com/supabase/supabase/pull/45389 ## What is the new behavior? Functionally the same behavior for audit logs. - [x] Manual test in staging ## Additional context ⚠️ Will leave the `do-not-merge` tag on until: - [ ] backend `format` optional PR lands in production. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated audit log type definitions and updated internal API request formatting for audit endpoints across Account and Organization audit log components. No changes to user-facing functionality or audit log display. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e540f9089f |
fix(studio): restore Safari table editor cell copy and context menu (#45353)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Bug fix. ## What is the current behavior? - Safari Table Editor cells fail to copy from a focused cell with `⌘C`. - Safari right-click can show the browser menu instead of the custom cell menu. - Copy can leave RDG's copied-cell fill behind. ## What is the new behavior? - Reuses the existing shared `copyToClipboard(value, onSuccess)` pattern, with the Safari clipboard fix inside that util. - Handles selected-cell `⌘C` in the RDG keydown path, preventing browser/RDG defaults and showing the success toast only after copy. - Replaces the row-level synthetic context-menu shim with RDG's `onCellContextMenu`, so we prevent Safari's browser menu at the source and select/focus the target cell. - Keeps the selected-cell outline while the controlled menu is open. ## Additional context - `RowRenderer` was only supporting the old context-menu shim; removing it is part of moving to RDG's cell event path. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Context menu now provides feedback with toast notifications when copying cells or rows. * Selected cells retain their visual styling when context menu is open. * **Bug Fixes** * Improved keyboard shortcut handling for copy functionality. * Enhanced clipboard error handling with user-friendly error messages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com> |
||
|
|
d666b950ff |
feat: update audit log schema & UI (#45389)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? - Update to the audit log schema (changes were already applied in staging) - Updates the org & project audit log page to reflect the changes to the schema - The schema should be agnostic to whether logs were emitted to logflare with the old & new schema format - the backend adjusts old logs to the new format. ## What is the current behavior? Currently, the frontend is parsing the legacy schema as the backend returns this by default. It also doesn't show some of these new fields yet. ## What is the new behavior? ### Org Audit Logs - Table View <img width="1810" height="1310" alt="CleanShot 2026-04-29 at 18 27 22" src="https://github.com/user-attachments/assets/47fec068-1ffa-4e52-bc46-3bffdef55adb" /> ### Org Audit Logs - Single log View <img width="1842" height="1494" alt="CleanShot 2026-04-29 at 18 27 37" src="https://github.com/user-attachments/assets/3cff3bdf-4a6a-4981-acaa-7f95bb3ae9cf" /> Note that the `Target` field is no longer there. We just show the `metadata` JSON. <img width="1842" height="1494" alt="CleanShot 2026-04-29 at 18 27 40" src="https://github.com/user-attachments/assets/d2e681f0-41a6-4bc7-a3d7-ec7e8101616c" /> ### Account (Profile) Audit Logs - Table View <img width="1810" height="1310" alt="CleanShot 2026-04-29 at 18 25 20" src="https://github.com/user-attachments/assets/c72e19df-9b82-4611-8889-7af463769550" /> ### Account (Profile) Audit Logs - Single log View <img width="1810" height="1310" alt="CleanShot 2026-04-29 at 18 25 32" src="https://github.com/user-attachments/assets/46f8d3b6-4f2f-4944-b891-431a93e5f3c3" /> ## Additional context ⚠️ currently leaving the `do not merge` tag on, until: - [x] I have verified it works in staging - [x] We've deployed the new schema to production <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Audit logs now use the v2 format with microsecond-accurate timestamps, improved ordering, and a revamped details panel showing clearer actor/action/request/project/org fields and fallback labels. * Page/header layout updated so audit logs render at top level with adjusted spacing. * **Refactor** * Shared sorting and filtering utilities added for consistent user/project filtering and non-mutating log sorting. * **Tests** * Added tests for timestamp conversion, sorting, filtering, and date-range formatting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7f5865872a |
Enforce noUnusedLocals and noUnusedParameters in tsconfig.json + fix all related issues (#45264)
## Context Enforce `noUnusedLocals` and `noUnusedParameters` in tsconfig.json + fix all related issues |
||
|
|
8454ec241d |
feat: add batch email org invites (#44832)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature ## What is the current behavior? When sending organization invites to multiple emails at once, the invitations API is called once for each email passed, passing a single email address in the `email` field. ## What is the new behavior? A single request is used when sending multiple organization invites at once, by using the new `emails` field. ## Additional context This builds further on https://github.com/supabase/supabase/pull/42637 ⚠️ Note: I'd like to merge this after getting the API changes in first: https://github.com/supabase/platform/pull/31561 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Bulk invite: paste comma-separated emails (parsed, trimmed, deduplicated, lowercased) and send as a single batched request; inputs are categorized into new, already-invited, and existing members. * SSO and project scope options included in invite payloads. * **Bug Fixes / API** * Invitation endpoint now accepts multiple emails; resend uses multi-email format. Invalid addresses are blocked, existing members are skipped with error toasts, and overall success is reported with the dialog closing after invite. * **Tests** * Added unit and UI tests covering parsing, categorization, payload building, validation limits, and invite flows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> |
||
|
|
1ab7251060 |
chore(studio): clarify partnered orgs and projects (#44328)
## What kind of change does this PR introduce? UI improvements. - Resolves DEPR-401 - Resolves DEPR-424 - Resolves DEPR-425 ## What is the current behaviour? Studio currently blurs two different concepts together: - `billing_partner` / `billing_via_partner`, which represent real billing ownership for marketplace-managed organisations such as AWS and Vercel - Stripe connection state, which is not actually partner billing in the same sense, but was previously being mocked through the same UI paths That made the Stripe work harder to reason about and left some local behaviour dependent on temporary overrides rather than the API shape we want to ship. ## What is the new behaviour? This PR separates those concerns while keeping the existing AWS and Vercel marketplace experience intact. - AWS and Vercel continue to use `billing_partner` / `billing_via_partner` for billing ownership, lockouts, and manage CTAs - Stripe display state now comes from `integration_source`, which lets Studio show Stripe-specific badges and alerts without treating Stripe as a billing partner - organisation-level partner UI is unified across AWS, Vercel, and Stripe, including the org banner, navbar icon treatment, and organisation cards - project-level Stripe UI now appears only when the project itself is marked as Stripe-connected, including the project switcher, project list surfaces, and a project-level banner - Stripe-connected organisations are no longer incorrectly blocked behind the AWS/Vercel-style billing management alerts for invoices, billing address, payment methods, or plan changes - banner dismissal is scoped to the relevant org/project and partner/integration state ## Review order Most of the diff size here is regression tests and generated types. The behavioural changes are concentrated in a smaller set of files. Recommended review order: 1. `integration_source` vs `billing_partner` data-model split and org/project query mapping 2. org-level UI: partner icon, org banner, org dropdown/card treatment 3. billing gating updates for Stripe vs AWS/Vercel 4. project-level Stripe UI: dropdown, list surfaces, banner 5. tests and generated types | Stripe Org(s) | | --- | | <img width="1024" height="759" alt="Organizations Supabase" src="https://github.com/user-attachments/assets/d0ef338c-3b41-4c6d-b3bd-f21a2c182840" /> | | Vercel Org(s) | | --- | | <img width="1024" height="759" alt="Organizations Supabase" src="https://github.com/user-attachments/assets/1dc57770-3f24-45ac-840f-34680555cde8" /> | | AWS Org(s) | | --- | | <img width="1024" height="759" alt="Organizations Supabase" src="https://github.com/user-attachments/assets/7847dad0-ee30-4a65-ab0b-b3b16af0d34f" /> | | Stripe Org, Non-Stripe Project | | --- | | <img width="1152" height="885" alt="Mallet Toolshed Supabase-1673E019-792C-462C-B6F8-C5DDB810B331" src="https://github.com/user-attachments/assets/556fbea3-b5ae-4f2f-96b9-6f66c6654e4a" /> | | Stripe Org, Stripe Project | | --- | | <img width="1152" height="885" alt="Hammer Toolshed Supabase-7E86C17C-561F-4221-BD16-EAFF7D41AAE0" src="https://github.com/user-attachments/assets/94f8daf6-0320-413e-8d56-59f9acaaea15" /> | | Vercel Org | | --- | | <img width="1024" height="759" alt="Projects Toolshed Supabase-A7891653-9366-4B99-89DD-789D70CD52E3" src="https://github.com/user-attachments/assets/c87ee6e8-4451-4866-a905-23a38b2593e3" /> | | AWS Org | | --- | | <img width="1024" height="759" alt="Projects Toolshed Supabase-58A43ECE-569E-4541-9463-346A90B02CFF" src="https://github.com/user-attachments/assets/9350a180-4d58-42a1-ad1a-95893c2e8b12" /> | This also removes the old Stripe mock override path in Studio so the frontend matches the intended API model more closely. ## ~~Dependencies~~ (merged!) This work depends on the private platform change that exposes `integration_source` on the relevant organisation and project payloads: - https://github.com/supabase/platform/pull/31874 _Update: now merged._ ## Local testing ### Stripe If you have the private `platform` repo checked out locally, make sure your local API returns `integration_source: 'stripe_projects'` consistently for the Stripe-linked org/project you are testing. Important responses: - `/platform/organizations` - `/platform/organizations/:slug/projects` - `/platform/projects/:ref` Verify: - org banner and org icon show the Stripe connected state - unopened and opened project switcher both show Stripe only for Stripe-linked projects - project cards / table rows show the Stripe chip only for Stripe-linked projects - the project-level Stripe banner appears across project surfaces - billing address, tax ID, invoices, payment methods, and plan changes remain editable in Studio for Stripe orgs ### Vercel Use a Vercel Marketplace org with real `billing_partner` / `billing_via_partner` values. Important org-level endpoints for local mocking in `platform`: - `/platform/organizations` - `/platform/organizations/:slug` - `/platform/organizations/:slug/billing/subscription` Project-level Vercel indicators still come from `/platform/integrations/:slug`, not `integration_source`. ### AWS Use an AWS Marketplace org with real `billing_partner` / `billing_via_partner` values. Important org-level endpoints for local mocking in `platform`: - `/platform/organizations` - `/platform/organizations/:slug` - `/platform/organizations/:slug/billing/subscription` AWS does not currently have a Stripe-like project-level indicator in these org/project payloads. ## Notes - `billing_partner` is no longer the right abstraction for the Stripe-connected case in this PR. It remains the source of truth for marketplace billing ownership, while Stripe currently uses `integration_source` as a connection/display signal. - I re-ran `pnpm api:codegen` while tightening this PR and kept only the generated type changes this branch actually depends on, to avoid unrelated API drift in the review. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Stripe Projects integration added for billing and project flows * Partner icons/badges shown across org and project lists, dropdowns, and rows * Dismissible, partner-specific marketplace/integration banners with contextual CTA behavior * Improved partner-billing detection to drive billing UI and invoice/plan availability * **Tests** * Extensive new test coverage for billing UI, partner-managed fallbacks, banners, icons, and related flows <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> |
||
|
|
e8df67d5d5 |
chore: migrate shortcuts to new hooks API (#44955)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Cleanup shortcuts with new hooks <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Centralized keyboard shortcut system for consistent shortcut behavior across the app and moved preference toggles to a unified registry. * **New Features** * Added explicit shortcuts for Command Menu, AI Assistant, Inline Editor, and result copy/download actions. * Hotkey preferences UI now renders dynamically from the centralized shortcut list. * **Tests** * Test helpers updated to include the command menu provider for accurate shortcut behavior in tests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1b1d05ff96 |
chore: upgrade vite to v8 and vitest to v4 (#44833)
Upgrade vite and vitest to their latest major versions across the
monorepo, along with related packages.
**Changed:**
- `vite` catalog: `^7.3.2` → `^8.0.8` (Rolldown replaces esbuild/Rollup)
- `vitest` catalog: `^3.2.0` → `^4.1.4`
- `@vitejs/plugin-react`: `^4.3.4` → `^6.0.1`
- `@vitest/coverage-v8`: `^3.2.0` → `^4.1.4`
- `@vitest/ui`: `^3.2.0` → `^4.1.4`
- `vite-tsconfig-paths`: `^4.3.2` / `^5.1.4` → `^6.1.1`
**Pinned to vite 7:**
- `apps/lite-studio` — `@react-router/dev` hasn't declared vite 8
support yet
- `blocks/vue` — Nuxt plugins (`vite-plugin-inspect`, `vite-dev-rpc`,
`vite-hot-client`, `vite-plugin-vue-tracer`) haven't declared vite 8
support yet
**Test fixes for vitest 4 breaking changes:**
- **`apps/studio/lib/api/snippets.utils.test.ts`** — Replaced
`vi.mock('fs/promises')` automock with an explicit factory. Vitest 4's
automocking doesn't create mock functions for getter-based exports on
Node built-ins, so `mockedFS.access.mockResolvedValue` etc. were
`undefined`.
- **`apps/studio/lib/api/self-hosted/functions/index.test.ts`** —
Changed `mockReturnValue` to `mockImplementation(function() { ... })`
for a constructor mock. Vitest 4 no longer allows `mockReturnValue` when
the mock is called with `new`.
- **`apps/studio/tests/pages/api/mcp/index.test.ts`** — Changed arrow
function to regular `function` in `mockImplementation` for
`StreamableHTTPServerTransport`. Arrow functions can't be constructors,
and vitest 4 now enforces this.
- **`packages/ui-patterns/vitest.setup.ts`** — Changed `ResizeObserver`
mock from arrow function to regular `function` for the same constructor
enforcement reason. This was crashing Radix popover rendering in jsdom.
## To test
- `pnpm test:studio` — all 226 test files should pass
- `pnpm --filter ui-patterns vitest run` — all 183 tests should pass
- `pnpm --filter www test -- --run` — all 19 tests should pass
- `pnpm --filter ui vitest run` — all tests should pass
- `pnpm --filter dev-tools vitest run` — all tests should pass
- `pnpm --filter ai-commands vitest run` — all tests should pass
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Standardized and updated development tooling versions and version
sources for consistent installs across the repo (Vite, Vitest,
vite-tsconfig-paths and related plugins/catalog entries).
* **Tests**
* Improved test mocks and typings (updated mock
factories/implementations and tightened spy/type assertions) to increase
test reliability and compatibility with updated tooling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
|
||
|
|
21584fe512 |
feat(studio): add backup cli instructions (#44621)
## Problem When a project is paused, in a failed state, or about to be deleted, users have no obvious way to take a logical backup of their data before proceeding. This is particularly risky at deletion time — once deleted, data is gone. ## Solution Introduce a new `LogicalBackupCliInstructions` component that surfaces ready-to-run `supabase db dump` commands pre-filled with the project's direct connection details. ### Where it appears | State | How | |---|---| | Project paused (restorable) | Inline in `ProjectPausedState` with a note to resume first | | Pause failed | Dialog via "Download backup" button when no backup is available | | Restore failed | Dialog via "Download backup" button when no backup is available | | Delete project modal | Inline in `DeleteProjectModal` for all plans | Not shown in `PauseDisabledState` (project paused 90+ days, compute stopped — `pg_dump` would fail anyway). ### What the component does - Fetches the project's direct connection settings via `useProjectSettingsV2Query` - Builds a connection URI with a `[YOUR-PASSWORD]` placeholder (password is never stored or displayed) - Shows three shell commands to dump roles, schema, and data separately — mirroring the [logical backup docs](https://supabase.com/docs/guides/platform/backups) - Optionally shows a **Reset database password** button (gated on `UPDATE projects` permission); shown in the paused state, hidden elsewhere via `showResetPassword={false}` - Includes inline guidance to percent-encode special characters in the password ### Shell safety The generated `--db-url` values are wrapped in single quotes to prevent shell metacharacter expansion when users paste and run the commands. `npx supabase login` is intentionally omitted — the `--db-url` flag authenticates directly against Postgres and does not require a Supabase account. ### Backup button behaviour in failed states The "Download backup" button in `PauseFailedState` and `RestoreFailedState` now always stays enabled: - **Backup available** — downloads immediately (unchanged) - **No backup / physical backups** — opens a dialog with CLI instructions instead of silently failing ## How to test **Delete project flow** 1. Open any project → Settings → General → Delete project 2. Verify the CLI backup section appears with the project's host, port, user, and db name pre-filled 3. Verify no Reset database password button is shown **Paused project** 1. Open a paused project (`ProjectPausedState`) — verify CLI instructions appear with the "Your project must be resumed before running these commands." note 2. Open a project paused for 90+ days (`PauseDisabledState`) — verify CLI instructions do not appear **Failed states** 1. Simulate a pause-failed or restore-failed state 2. If a downloadable backup exists — "Download backup" downloads it directly 3. Block the backup API or use a project with physical backups — "Download backup" should open the CLI instructions dialog **Error state** 1. Block the project settings API call (DevTools → Network → block request) 2. Verify an error message appears with a link to Database settings 3. Verify a loading skeleton shows while the request is in flight --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
cfce3c7efc |
refactor: save tax ID via customer update endpoint (#44728)
## Summary
- Consolidate tax ID saving into the `PUT
/organizations/{slug}/customer` endpoint instead of using a
separate `/tax-ids` endpoint
## Test plan
### Updating billing information
From the billing dashboard`/org/_/billing`:
- [ ] Manually test updating billing address + tax ID from org billing
settings - single API call should save both
- [ ] Clear the Tax ID in the org, save and assert that it has been
deleted
- [ ] Modify all the fields in the address form, including TaxID. Click
"Cancel" and assert that everything has returned to the previous values.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Billing address and tax ID updates are now sent in a single update
operation.
* Error messaging for billing updates consolidated into a single failure
notification.
* Tax ID handling clarified: you can clear tax ID explicitly or leave it
unchanged; omitting address fields no longer overwrites existing
address.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|
|
205cbe7d26 | chore(studio}: enforce import order, remove bare import specifiers (#44585) | ||
|
|
fbb0770b47 |
fix(studio): AI assistant results make page unresponsive (#44578)
## Summary Fixes https://linear.app/supabase/issue/FE-2941 - Replaced per-cell `ContextMenu_Shadcn_` in SQL result tables with a single shared instance - Each Radix ContextMenu registers a `keydown` listener on `document` via `useEffect`. With 1000+ row result sets, this created thousands of document-level listeners. Chrome trace showed listeners growing from 21k to 347k, with every keystroke taking ~250ms (70k+ function calls per long task) - Fixed DataGrid not rendering in Firefox by using flex layout instead of `height: 100%` for sizing - Fixed double scrollbar in QueryBlock results by making the container a flex column and removing `overflow-auto` - Moved format utils into `Results.utils.ts` ## Test plan The `Results` component and `QueryBlock` are used in several places. Each should be verified: - [x] **AI Assistant** — Have the assistant run a query returning 1000+ rows. Verify the page stays responsive while typing and there is only a single scrollbar on the results table - [x] **AI Assistant (Firefox)** — Same as above but in Firefox. Verify the results table actually renders - [x] **SQL Editor results panel** — Run a query in the SQL Editor. Verify results render correctly with a single scrollbar in the utility panel - [x] **SQL Editor explain tab** — Run an EXPLAIN query. Verify explain results render - [x] **Editor Panel** (used in Table Editor SQL preview) — Verify results display correctly - [x] **Context menu** — Right-click a cell in any of the above and verify "Copy cell content" and "View cell content" still work ## Traces <img width="891" height="599" alt="image" src="https://github.com/user-attachments/assets/6e1b710b-ca9e-4748-9369-d03457d80206" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed context menu behavior so it opens at the cursor and is instantiated once. * Improved clipboard copy formatting for cell values; NULLs copy as empty and cells render as "NULL". * **Style** * Adjusted table layout and scrolling behavior to improve results container sizing. * **Tests** * Added tests for results rendering, context-menu behavior, and cell/clipboard formatting utilities. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
33c890f564 |
fix(studio): allow selecting columns for empty-cell NULL conversion on CSV import (#44515)
Bug fix / enhancement to an existing Studio import flow. ## What is the current behavior? Studio currently supports converting empty CSV cells to `NULL`, but the behavior applies across all imported columns. That makes it impossible to preserve empty strings in some columns while still treating empty cells as `NULL` in others. This follows up on the earlier CSV import fix discussed in #43281, which addressed the underlying issue reported in #43258. ## What is the new behavior? This PR updates the CSV import flow so users can choose exactly which imported columns should convert empty cells to `NULL`. The import flow now: - keeps all imported columns selected by default for backwards-compatible behavior - lets users narrow that selection down to specific columns - keeps preview behavior aligned with the actual inserted data ## Additional context I manually verified the full create-table-from-CSV flow locally in Studio, including saving the imported data and confirming that only the selected columns were persisted as `NULL` while other empty cells remained empty values. Formatting was checked with Prettier. A direct production build run was started locally, but the Next.js build process remained running without producing a final result during this session, so I am not claiming a completed local build verification here. ## Related issues - Fix follow-up for #43281 - Original issue: #43258 ## Summary by CodeRabbit ## Release Notes * **New Features** * Added a "Set empty cells as NULL" selector during spreadsheet import, allowing users to specify which columns should treat empty strings as NULL values rather than converting all nullable columns automatically. * Enhanced import preview to display NULL representation for selected empty-string columns. * **Tests** * Added test coverage for selective empty-string-to-NULL conversion behavior. --------- Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com> |
||
|
|
6563dc7998 |
refactor: authorize page (#44522)
Refactor authorize page to move Next.js dependencies into the page shell and have Next.js-agnostic code for the core logic. Add unit tests for authorize screen. ## Summary by CodeRabbit * **New Features** * New end-to-end API authorization UI: loading, error, invalid, approved, and main approval screens. * Organization selector with preselection, create-organization link, MCP warning, expiration handling, and approve/decline actions. * Improved page title handling via layout/head provider. * **Tests** * Added comprehensive component tests covering loading, error, approval flows, organization states, validation, and side effects. |
||
|
|
6be596ea34 |
feat: add user preference to enable queue operations (#44366)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? - Remove queue operations from feature preview into settings - Refactor dashboard settings - Resolves DEPR-434 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Dashboard settings panel in Account preferences with toggles for Inline Editor and Queue Operations; “Dashboard” added to project Configuration. * **Removed** * Old Inline Editor settings UI and the Queue Operations feature-preview UI removed. * **Refactor** * Consolidated dashboard preferences into a single settings surface; banners and actions now navigate to preferences; account/preferences layouts and back-navigation behavior adjusted for platform vs self-hosted. * **Tests** * Added tests for settings UI, menu generation, redirects, and local-storage. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> |
||
|
|
2335906d08 |
feat(read-replicas): bump up max read replicas for small instances (#44027)
INDATA-193 BACKEND: <https://github.com/supabase/platform/pull/30575> <img width="1199" height="1005" alt="Screenshot 2026-04-03 at 11 54 29" src="https://github.com/user-attachments/assets/38e9d676-449f-45c0-9e07-f273312a812f" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated read replica limit configuration to provide more consistent behavior across different compute tiers. * **Tests** * Added comprehensive test coverage for read replica eligibility checks and replica limit calculations based on compute tier. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
be3737d9d0 |
feat(billing): use Stripe AddressElement for billing address form (#44105)
### Summary This PR refactors the billing address form to use Stripe AddressElement on both the org billing page and the missing-address modal, adding Google Maps autocomplete and country-aware state/province inputs. ### Testing - Address element renders on org billing page, autocomplete works, and state/province options update per country. - Existing billing address data loads correctly into the form. - Need to verify this with invalid country/state as well. - Save enables only when the form is dirty after editing address or tax ID fields. - Cancel restores both address and tax ID to the original values. - Submitting with missing required address fields shows a validation toast. - Tax ID options are filtered by the selected billing country. - Tax-ID-only edits submit successfully without interacting with the address form. - Without billing write permission, address fields are read-only. - Update billing address modal behaves the same as the org billing page. - Address element appearance looks correct in both light and dark mode. - Payment method form remains unchanged and still uses floating labels. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Billing form now uses Stripe Address Element via Stripe Elements with an embedded custom font and theme-aware appearance; tax-ID entry remains separate. * **Bug Fixes** * Stronger address validation and submit gating, improved dirty-state tracking, explicit save confirmation and reset behavior, and clearer error toasts on update failures. * **Tests** * Expanded tests covering hook behavior, address element validation, submit gating, payloads, reset, and permission scenarios. * **Style** * Adjusted billing email form spacing/layout. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
55e0b34a18 |
fix(studio): add option to treat empty CSV cells as NULL on import (#43281)
When importing a CSV file, empty cells were always imported as empty strings with no way to import NULL values instead. This adds a "Treat empty cells as NULL" checkbox to the import configuration panel. When enabled, PapaParse's transform option converts empty strings to null before the data is parsed and previewed, so the imported rows correctly contain NULL rather than empty strings. Fixes #43258. ## What kind of change does this PR introduce? Bug fix ## What is the current behavior? Empty cells in a CSV file are always imported as empty strings with no way to import NULL values instead. Fixes #43258. ## What is the new behavior? "Treat empty cells as NULL" checkbox is added to the import configuration panel. When enabled, empty cells are imported as NULL instead of empty strings. Toggling the checkbox instantly re-parses the preview. ## Additional context The fix uses PapaParse's transform option to convert empty strings to null before parsing. Applies to both file uploads and pasted text. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com> |
||
|
|
c55908bbf0 |
chore(billing): make update billing address modal non-dismissable (#44127)
### Summary This PR removes the visible close button from the billing address required modal to make it non dismissable, since the majority of users on paid orgs are still not filling their billing address. The modal already prevents dismissal via Esc and outside/backdrop interaction, and this change preserves that behavior. The existing submit flow is unchanged, so the modal still closes after a successful billing address update. ### Manual testing: 1. Open Studio in a state where the billing address required modal appears. 2. Confirm the top-right close button is no longer shown. 3. Press Esc and verify the modal remains open. 4. Click outside the modal / on the backdrop and verify the modal remains open. 5. Submit a valid billing address and verify the modal closes after a successful save. For testing whether an update successfully closes the modal, you can: - You can create a free org without a billing address - You will need to tweak this logic (on the frontend) ``` const shouldShow = Boolean( IS_PLATFORM && // showMissingAddressModal && org && // org.plan.id !== 'free' && org.organization_missing_address && !org.billing_partner && permissionsLoaded && canBillingWrite ) ``` - Tweak the backend logic which computes `organization_missing_address` to exclude free orgs --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
98b1b79909 |
Chore/shift manual queries into pg meta 04 (#43956)
## Context Shifts all remaining dashboard queries into pg-meta so that we centralize all manually written queries in one place Having them in packages/pg-meta also allows us to write tests for them ## To test Just needs a smoke test on - Role Impersonation - Lints - Data API - Database - Enumerated Types - Integrations - Foreign Data Wrappers - Vault |
||
|
|
5d942e2a46 |
refactor(billing): allow GB VAT tax IDs for Isle of Man customers (#43640)
### Summary This PR adds support for Isle of Man customers to use GB VAT tax IDs. Isle of Man uses the UK VAT system, so Orb requires country: 'GB' for validation - but the billing address country is IM. - Adds an IM VAT entry to `TAX_IDS` with a new `taxCountryIso2` override field, so the UI shows "IM VAT" while sending country: 'GB' to Orb - Introduces `getEffectiveTaxCountry()` to resolve the correct country code for Orb - Introduces `resolveStoredTaxId()` to map stored Orb data back to the correct UI entry, using billing address country to disambiguate IM vs GB - Updates all three billing forms to use these utilities: Billing Settings page, Add Payment Method modal and Update Billing Address modal ### Testing #### Automated tests I've added unit tests in this PR #### Manual tests You can use the following country/city/postal code combinations to test: - Isle of Man, Douglas, IM1 2LE - United Kingdom, Crumlin, BT29 4AA **Billing Settings page** - Select Isle of Man as billing country → tax ID dropdown shows "Isle of Man - IM VAT" - Select United Kingdom as billing country → tax ID dropdown shows "United Kingdom - UK VAT" / "United Kingdom - GB VAT" (not "IM VAT") - Select IM VAT, enter a valid GB VAT number, save → check network tab, Orb receives country: 'GB' - Reload page for an IM customer with existing tax ID → "IM VAT" is pre-selected (not "UK VAT") - Reload page for a GB customer with existing tax ID → "UK VAT" is pre-selected (no regression) - Change country from IM to GB → available tax IDs update, IM VAT no longer shown **Add Payment Method modal** - Open modal for an IM customer with existing tax ID → "IM VAT" is pre-selected - Check "I'm purchasing as a business", select Isle of Man in Stripe address → tax ID dropdown shows "IM VAT" - Check "I'm purchasing as a business", select United Kingdom in Stripe address → tax ID dropdown shows "UK VAT" / "GB VAT" - Submit with IM VAT → check network tab, tax ID is saved with country: 'GB' - Open modal for a GB customer → no regression, "UK VAT" shown as before **Update Billing Address modal** Appears for paid orgs missing a billing address - Open modal for an IM customer with existing tax ID → "IM VAT" is pre-selected - Open modal for a GB customer with existing tax ID → "UK VAT" is pre-selected - Fill in IM address + IM VAT and save → saves correctly, modal closes --------- Co-authored-by: Ignacio Dobronich <ignacio@dobronich.com> |
||
|
|
62426253c3 |
fix: pass exposedSchemas to getLints in MCP advisor operations (#43790)
## Summary - MCP `getSecurityAdvisors` and `getPerformanceAdvisors` now pass `exposedSchemas` to `getLints`, fixing empty advisor results in local/self-hosted environments - Extracts `DEFAULT_EXPOSED_SCHEMAS` constant shared between the MCP handler and the `run-lints` API route (cc @joshenlim related https://github.com/supabase/supabase/pull/40043) - Adds unit tests for `enrichLintsQuery` and the MCP advisor operations ## The bug The MCP advisor tools (`get_advisors`) return empty arrays (`[]`) for **all** scenarios when running locally via `supabase start`. No security or performance advisors are surfaced, even when the database has clear issues (e.g., tables with no RLS). ### Root cause In `lib/api/self-hosted/mcp.ts`, both `getSecurityAdvisors` and `getPerformanceAdvisors` call `getLints({ headers })` **without passing `exposedSchemas`**: ```typescript // Before (mcp.ts:131) const { data, error } = await getLints({ headers }) ``` When `exposedSchemas` is `undefined`, `enrichLintsQuery` in `lints.ts` skips the `SET LOCAL pgrst.db_schemas = '...'` SQL statement: ```typescript // lints.ts:23 ${!!exposedSchemas ? `set local pgrst.db_schemas = '${exposedSchemas}';` : ''} ``` Without this GUC being set, the splinter SQL queries filter results using `current_setting('pgrst.db_schemas', 't')` — which returns an empty string in local environments. Every schema-filtered lint matches no schemas and returns zero rows. ### Why this only affects local/self-hosted environments In **hosted Supabase**, PostgREST sets the `pgrst.db_schemas` GUC on its own database connections based on the project's API configuration. The Studio MCP server in production reads the same project configuration, so the GUC is already available. **Locally**, PostgREST runs in a separate Docker container and only sets this GUC on _its own_ connections. Studio connects directly to PostgreSQL (bypassing PostgREST), so `current_setting('pgrst.db_schemas', 't')` returns `''`. The HTTP API endpoint (`/api/platform/.../run-lints`) already worked because `run-lints.ts` passes `exposedSchemas: 'public, storage'` — this parameter was simply never added to the MCP code path. ## How we verified the fix ### 1. Tests written to fail against the previous code We wrote two test files that target the exact bug: **`tests/unit/lints/enrichLintsQuery.test.ts`** — validates the SQL generation: - Confirms `SET LOCAL pgrst.db_schemas` is included when `exposedSchemas` is provided - Confirms it's omitted when `undefined` or empty (documenting current behavior) **`tests/unit/lints/mcp-advisors.test.ts`** — validates the MCP operations: - Asserts `getSecurityAdvisors` passes `exposedSchemas` to `getLints` - Asserts `getPerformanceAdvisors` passes `exposedSchemas` to `getLints` - Asserts the value matches `DEFAULT_EXPOSED_SCHEMAS` - Verifies SECURITY/PERFORMANCE category filtering still works Before the fix, the two `exposedSchemas` assertions failed: ``` FAIL getSecurityAdvisors should pass exposedSchemas to getLints → expected { Object (headers) } to have property "exposedSchemas" FAIL getPerformanceAdvisors should pass exposedSchemas to getLints → expected { Object (headers) } to have property "exposedSchemas" ``` ### 2. Fix applied, all tests pass After adding `exposedSchemas: DEFAULT_EXPOSED_SCHEMAS` to both MCP operations, all 14 tests pass (9 new + 5 existing MCP tests). ## Test plan run `supabase start`, create a table without RLS, call `get_advisors` via MCP — should return `rls_disabled_in_public` lint --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
83592b1932 |
feat(billing): show billing address modal for paid orgs with missing address (#43480)
### Summary This PR adds a dismissable modal that prompts paid org users to fill in their billing address. We need billing addresses for tax compliance and the existing banner hasn't been as effective (~10k orgs still missing). The modal shows on page load when all of these are true: - ConfigCat flag `enableBillingAddressModal` is on - Org is on a paid plan (not free) - Org does not have billing address set - Org is not partner-managed - User has BILLING_WRITE permission on stripe.customer The modal can be dismissed via the close button, but re-opens on every page navigation. Once the user saves a valid address, the organizations query is invalidated and organization_missing_address flips to false - the modal stops appearing permanently. <img width="1256" height="703" alt="Screenshot 2026-03-05 at 4 22 11 PM" src="https://github.com/user-attachments/assets/57e7d1b8-b3b6-4366-ad23-60910a0defe8" /> ### Testing #### Automated Tests I've added unit tests in this PR #### Manual testing Manual test cases: - Free org - modal does not appear - Partner-managed org - modal does not appear - User with "developer role" modal does not appear - Configcat Flag disabled - modal does not appear - Dismiss modal, navigate to another page - modal re-opens - Fill form and save - modal closes and does not reappear - API failure loading billing data - shows error state with Retry button --------- Co-authored-by: Ignacio Dobronich <ignacio@dobronich.com> |
||
|
|
173b725c93 |
fix: add old homepage to self hosted w/ unit tests (#43705)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? - Bring back the old home page for self hosted, because the new one does not work for self hosted users - Unit tests to catch this in the future |
||
|
|
b997b7fb13 |
feat(studio): org project branch mobile selector (#43238)
- add new [OrgSelector](https://github.com/supabase/supabase/pull/43238/changes#diff-214b339101a9c06864ea2755ac7246eb4c971ce74c5d3169b1385a28ee1d4227) and [ProjectBranchSelector](https://github.com/supabase/supabase/pull/43238/changes#diff-82d25c128c306b61bea7481026f58f670a229874f23ca3a1a1d78ddeabde21e0) components to the mobile navigation, replacing the previous which took up a lot of horizontal space - org/project/branch dropdowns now open up in the bottom MobileSheetNav for a better mobile ux - desktop nav remains unchanged |
||
|
|
2fc591887e |
fix: missing org id lint error (#43642)
Fixes the `organization_missing_tax_id` missing in type error |
||
|
|
4fdab665a8 |
chore: Fix vulnerable packages (#43634)
Fix bunch of vulnerable dependencies: - https://github.com/supabase/supabase/security/dependabot/3027 - https://github.com/supabase/supabase/security/dependabot/3038 - https://github.com/supabase/supabase/security/dependabot/3058 - https://github.com/supabase/supabase/security/dependabot/3064 - https://github.com/supabase/supabase/security/dependabot/3065 - https://github.com/supabase/supabase/security/dependabot/3085 - https://github.com/supabase/supabase/security/dependabot/3095 |
||
|
|
4b994fcb54 |
feat: copy multiple logs (#43218)
Allows users to select logs and copy their contents for debugging, or pass them on to assistant with one click. ## To test - go to logs or log explorer - select some logs - try copying as json, markdown, or sending it to assistant. |
||
|
|
6a873c67fa |
chore: logs entitlements badges (#43443)
### Changes - Replace hardcoded plan-to-days mapping (`getAvailableInForDays`) with entitlement-driven checks. - Show a lock icon on date picker helpers that exceed the user's entitled log retention, instead of a plan name badge. - Remove availableIn from DatetimeHelper type — the entitlement API is now the single source of truth. ### Testing - Head to `/project/_/logs` on a free plan project and verify lock icons appear on helpers beyond 1 day - Verify clicking a locked helper still triggers the upgrade prompt <img width="511" height="483" alt="image" src="https://github.com/user-attachments/assets/41dce8c0-205a-4d82-9c07-12f4bb6b0f4d" /> |
||
|
|
785625c92f |
fix: add compact formatting to Y axis numbers in db charts (#43170)
In the db charts, numbers in the Y axis are not formatted, sometimes these get too big and get cut-off. ## before (in this example it is not cutoff because the number is not big enough) <img width="1020" height="634" alt="CleanShot 2026-02-25 at 11 20 35@2x" src="https://github.com/user-attachments/assets/bc29edcc-331f-44d1-934f-1553b895c338" /> ## after <img width="936" height="676" alt="CleanShot 2026-02-25 at 11 21 07@2x" src="https://github.com/user-attachments/assets/eae4ea4c-b8cd-4fe4-a361-9b029955217b" /> |
||
|
|
9c8a8d25d1 |
fix: universal link redirects to general (#43131)
Currently `/org/_` redirects to `/general` (org settings) after picking an org, this makes it redirect to the project list ## to test - go to /dashboard/org/_ - select org - redirects to project picker ## test 2 - go to /dashboard/org/_/general - select org - redirects to /org/whatever/general (settings) |
||
|
|
637b6fb792 |
chore: update react-resizable-panels to v4 to support pixel width (#42990)
This pull request standardizes the usage of props and value types for the `ResizablePanelGroup` and `ResizablePanel` components across multiple files in the codebase. Specifically, it replaces the deprecated `direction` prop with `orientation`, and updates numeric prop values (such as `defaultSize`, `minSize`, and `maxSize`) to be passed as strings. This ensures consistency with the updated component API and improves type safety. **Component API Updates:** * Replaced the `direction` prop with `orientation` for all usages of `ResizablePanelGroup` * Updated all `ResizablePanel` props (`defaultSize`, `minSize`, `maxSize`) to be passed as strings instead of numbers, ensuring compatibility with the latest API requirements. * Removed deprecated or unnecessary props such as `order` from `ResizablePanel` components, and ensured all size-related props are consistently formatted as strings. |
||
|
|
a36c50ad88 |
add logarithmic option to custom reports (#43001)
https://linear.app/supabase/issue/FE-2595/dashboard-feedback-logarithmic-scale-option-for-custom-report-charts#comment-19c2f27a - adds logarithmic view option to charts <img width="1308" height="866" alt="CleanShot 2026-02-18 at 21 15 56@2x" src="https://github.com/user-attachments/assets/2ed95d0e-ccd0-4cd1-9a3b-ac9ae5628995" /> |
||
|
|
a54fd1e93c |
Fix resizeable panel error in LogTable (#42851)
## Context There's an error from the `LogTable` component `Previous layout not found` that's caused by trying to set `defaultSize` as a dynamic value for the `ResizablePanel` component. Theres 2 ways to address this really Option 1: Set `id` param on `ResizablePanel` to be a dynamic: this came from a suggestion by the library's author [here](https://github.com/bvaughn/react-resizable-panels/issues/401#issuecomment-2351010630) Option 2: Avoid dynamic values entirely Opting for option 2 as the cleaner way to fix this. Just note as well that there's a couple of console warnings from the library about `Invalid layout total size` - this one's safe to ignore and is seemingly an issue with the current version of `react-resizable-panels` that we're using (v2, there's currently v4 already) ## To test Tbh I couldn't reproduce this locally actually, but I happened to come across this same bug while debugging the `Invalid layout total size` warning in `DefaultLayout` by trying to set a dynamic value for `defaultSize` which made me realise the dynamic value was the one causing that error <img width="534" height="89" alt="image" src="https://github.com/user-attachments/assets/3b696c16-ea66-481e-a698-d59841c7400d" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Improved logs viewer layout with resizable panels for better content management and log selection display. * Reorganized internal component structure and module exports for improved maintainability. * **Chores** * Updated test imports to reflect internal module restructuring. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f0fbcbd2a3 |
Add preflight EXPLAIN check to table editor rows (#42321)
## Context Part of an investigation to see how we can make the dashboard more resilient for large databases by ensuring that the dashboard never becomes the reason for taking down the database accidentally. Am proposing that for interfaces that rely heavily on queries to the database for data to render, we add preflight checks to ensure that we never run queries that exceed a certain cost threshold (and also have UI handlers to communicate this) - this can be done by running an EXPLAIN query before running the actual query, and if the cost from the EXPLAIN exceeds a specified threshold, the UI throws an error then and skips calling the actual query. ## Demo Am piloting this with the Table Editor, and got an example here in which my table has 500K+ rows, and I'm trying to sort on an unindexed column: https://github.com/user-attachments/assets/ccad2ea9-d62c-4106-8295-2a6df5941474 With this UX, the pros are that - It's relatively seamless and not too invasive, most users won't notice this unless they run into this specific scenario - We can incrementally apply this to other parts of the dashboard, next will probably be Auth Users for example However there are some considerations: - The additional EXPLAIN query adds a bit more latency to the query since its a separate API request to the query endpoint - ^ On a similar note, it will hammer the API a bit more, which may result in higher probability of 429s - However, I reckon that the preflight checks are meant to be used sparingly and only for certain parts of the dashboard that we believe may cause high load. - e.g for the Table Editor, reckon we only need this for fetching rows? The count query is largely optimized already (although we could just add a preflight check there too) - It's just meant to be a safeguard to prevent running heavy queries on the database <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Query preflight with cost checks and a user-facing high-cost dialog showing cost details and remediation suggestions. * Grid exposes an explicit error flag and surfaces richer error metadata. * **Bug Fixes** * Standardized error handling and more consistent error displays across the app. * Explain analysis now reports an additional max-cost metric for queries. * **UI** * Tweaked empty-state interaction/layout and slightly wider header delete control. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com> |
||
|
|
d21098ada4 |
chore(studio): upgrade @supabase/mcp-server-supabase to v0.6.2 (#42210)
* chore(mcp): upgrade @supabase/mcp-server-supabase to v0.6.2 * fix: mocks for Node.js / Web Standard HTTP APIs |
||
|
|
eb85515acb |
minor percentage formatting fixes (#41923)
* support total percentage normalization and show idle time in cpu usage chart * pretier * improve test * clarify in comment why new prop is necessary * fix type error int est * refactor to use max value instead of idle property, add more tests * cleanup composed chart * prettier * fix tests * react improvements * remove unused stacking properties * remove rest property references as it wont be used * fix comment and max cpu desc * test the resolvehighlightedvalue function * undo unnecessary composedcharthandler change * remove unnecessary comment * minor db charts percentage formatting fixes * move format check * fix issue with conflict resolution |
||
|
|
f4409d4ac5 |
fix: updated explain to not crash on non text formats (#42076)
updated explain to not crash on non text formats |
||
|
|
d786c1bdd4 |
add dynamic datepicker (#41924)
* add dynamic datepicker * tests * handle singulars |
||
|
|
aec9be147a |
chart: handle values below 0.01 (#41742)
* fix * fix file |
||
|
|
5b3cb3e8ba | fix Log Explorer run query inconsistency (#41634) | ||
|
|
89e0fe4f28 |
feature: Explain tab in SQL editor that shows output of explain analyze (#41569)
* wip: explain tab in results editor * updated to add sql explain * updated to default back to results * updated explain function * updated case with multiple statements * updated to reset explain query results * added tests for semi colon comments * feature: add explain w/ AI on pretty-explain tab (#41588) * wip: added explain with AI * wip: updated header with new buttons * updated prompt * remove any types * removed unused flag * updated header * formatted code |
||
|
|
000c79e22b |
fix(studio): rework global storage size validation (#41378)
The global storage size validation depends on an unpaginated buckets query to determine whether it is lower than any individual bucket's cutoff. This causes a problem for users with tens of thousands of buckets. There's a bit of a UX/performance problem here, because in order to determine whether any bucket's `file_size_limit` exceeds the global setting, we need to get the max `file_size_limit` of `storage.buckets` -- however, that column is not indexed. My workaround is: - Below a certain threshold (10,000) buckets, the query for max `file_size_limit` is automatically run on form submit. - Above that threshold, the user must confirm whether they want to run the query. They're still allowed to change the storage config without running it -- this does open a loophole where they can have a global storage setting lower than an individual bucket's file size limit, but though this is a potentially confusing situation, it's not strictly an error. --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
3a41846044 |
Feature: Pretty explain in SQL Editor (#41289)
* added cleaner explanation * update UI * refactored components * updated logic * removed steps * updated UI * updated read * updated node render styles * added tests * updated file naming * updated utility method * updated description * updated spacing * updated to include header * updated logic * added flag to hide pretty explain * updated cost width * added aria label * move index scan down * updated to catch schema and qoutes * remove node * forgot the missed node * addressed PR feedback * updated bad slop * removing tests * updated node to be undefined, null remove it makes the logic cleaner * updated function name * updated row indicator to be separate components * updated code rabbit feedback * added divide by zero check * updated test description |