mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 03:15:06 +03:00
feat/storage-versioning/007-archived-objects-data
1376
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
96a2aad7f2 |
fix(storage): drain the whole history when purging an archived file
The version list endpoint caps a page at 1000 rows. Purging an archived file deleted that one page plus the marker and called it done, so a longer history kept its older versions — and with the marker gone one of them became current again, putting the "permanently deleted" file back in the bucket. It now lists and deletes in rounds until nothing is left, the way the live object purge does. It also deletes versions by id, the current one included, so a restore between opening the dialog and confirming would hand it a live file to destroy. It now re-reads the marker immediately before the first delete and aborts if the path is no longer archived. That narrows the window rather than closing it; only Storage can make the check and the delete one operation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
0ef67903c5 |
fix(storage): report a truncated archived listing instead of guessing
Two problems with the page cap. It stopped at 20 pages without telling the caller the bucket had more, and because the listing is ordered by name the cut could land mid-path, leaving `toArchivedObjects` to classify a fragment: a lost delete marker reads as a live object, lost retained versions drop an archived one. The query now returns `isTruncated` and discards the path it stopped on. The archived purge also refetched its version list through the client's one-minute staleTime, so a version uploaded after that list was cached could survive the purge. It now bypasses the cache, like the live object purge does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
289c71c754 |
test(storage): type the archived objects fixture instead of casting to any
Same treatment as the object versions fixture: the row type comes from the function under test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
7d30fdbc5d |
feat(storage): carry an archived version's mime type through the query
The API returns it and the mapper dropped it, so nothing downstream could tell an image from a PDF. The archived preview needs it to render. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
613d9c08b9 |
refactor(storage): trim comments to one line where they earn their place
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
78f2df7e0a |
feat(storage): fetch and mutate archived objects against Storage
An archived object is one whose top row is a delete marker: gone from the
live listing, versions still retained. `list-v2` reports those rows once
`deleteMarkers` and `noncurrentVersions` are included, so the archived
list is a grouping of that listing rather than a dedicated endpoint.
The flat, undelimited listing is deliberate — the inline overlay
synthesizes folders from full paths, so it needs the whole bucket, not one
level at a time. Paging is bounded.
- Restoring deletes the delete marker, which promotes the version beneath
it back to current; nothing is copied
- Purging deletes every retained version and the marker, since leaving the
marker would keep the object listed as archived with nothing under it
- Deleting one retained version addresses it by `{ path, versionId }`
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
|
||
|
|
98a4065e33 |
feat(storage): add archived objects data layer
On a versioned bucket a delete is a soft delete, and the file preview panel already calls that action Archive and promises the versions stay recoverable. Nothing in the dashboard lets a user see or restore an archived file yet. This is the data layer for that, with no UI: query and mutation shapes written to the studio conventions, endpoints stubbed, returning empty. - `archived-objects-query.ts` — `ArchivedObject` / `ArchivedObjectVersion` types and `archivedObjectsQueryOptions` - `archived-object-restore-mutation.ts` — bring an archived object back - `archived-object-purge-mutation.ts` — delete it and every version, permanently - `archived-object-version-delete-mutation.ts` — remove one retained version - `archivedOverlay.utils.ts` — synthesizes the explorer rows for one folder from the archived list - `archivedVersions.utils.ts` — an archived object's history as one flat list Two prototype problems fixed rather than carried over: The prototype identified the "was current when archived" row by the sentinel `versionId === objectId`, which was load-bearing across three files and would break the moment real version ids arrived. `ArchivedObject` now carries a `currentVersion` record, so merging invents no fields and the distinction is an explicit `wasCurrentAtArchive` flag. The prototype's object had both `name` and `originalPath`, inconsistently — the path normalization existed mostly to strip a duplicated leaf folder that inconsistency produced. There is now a single `path`, and `getArchivedSegments` is the only function that interprets it, so a different API shape is a one-place change. Also drops `deletedBy` and `expiresAt`, which the prototype never rendered. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
c7c6b9af6b |
fix(storage): fall back to unsegmented Storage Size until retention data exists
The retention endpoint is still a stub. Returning zeroed totals made the breakdown assert "0 bytes" of retained data and the segmented chart render "No data in period", neither of which the platform has told us. The stub now returns null, and Storage Size stays unsegmented until real figures arrive. Also restores the platform and org-slug guards that the feature-flag override was clobbering, and rejects an empty slug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
04b6f64496 |
refactor(storage): trim comments to one line where they earn their place
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn |
||
|
|
3ad97080d5 | improve comments | ||
|
|
67b11976ef |
feat(storage): break down retained storage on the org usage page
Splits Storage Size into current versus noncurrent objects, and attributes the noncurrent portion to the buckets responsible, so the cost of object versioning sits with the metric it inflates. The split is flag-aware. With the preview off, Storage Size renders exactly the single real series it does today; `USAGE_CATEGORIES` takes an options argument and only emits the two stacked segments when the preview is on. Noncurrent is deliberately not split further into noncurrent versions and soft-deleted files: the platform can't reliably tell the two apart after the fact, and the user's lever for shrinking either is the same lifecycle policy. Using S3's own terms also reads better than Studio-only words like "live". - `storage-retention-usage-query.ts` — org-scoped (the prototype keyed this org-level data under `['projects', undefined, …]`), real `queryOptions` shape with the endpoint stubbed - `StorageRetention.constants.ts` — one definition of the two segments, used by both the chart attributes and the breakdown table, so labels, colors and key casing cannot drift - `StorageRetention.utils.ts` + tests — maps retention days onto chart points - `StorageRetentionBreakdown.tsx` — the segment table, retained-data warning, and per-bucket list, rendered through the existing `additionalInfo` hook - `Usage.colors.ts` — `COLOR_MAP` and `AttributeColor` moved out of `Usage.constants` so the breakdown can read a color token without a cycle back through the module that renders it Known limitation: with the preview on, the stacked chart and the breakdown read zeros until the retention endpoint exists. No mock data is shipped. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
690ef5e7f7 |
feat(studio): scoped oauth apps, admins can see an app grants (#50979)
## Problem Admins need to see the grants associated to an approved OAuth application that uses scoped tokens. ## Solution - Add a menu to the org settings oauth approved apps rows to see the grant list - Update the react query hook to use infinite query for the grant list <img width="1149" height="356" alt="image" src="https://github.com/user-attachments/assets/2e6cfc76-5584-4447-aa19-a0bf103e2218" /> <img width="429" height="395" alt="image" src="https://github.com/user-attachments/assets/dd811d33-2c0a-46df-b9e9-3cbf8ad2fa7f" /> <img width="434" height="267" alt="image" src="https://github.com/user-attachments/assets/108905ba-4dcb-4dca-abe2-1a4e1fc3dbd2" /> ## Review instructions In Org Settings/OAuth apps, you should see a menu button for each app that contains a _View grants_ item. Clicking this item should open a dialog with the grants --------- Co-authored-by: kemal <hello@kemal.earth> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
12ab771e86 |
feat(studio): authorized apps table in org settings (#50529)
<img width="1150" height="669" alt="image" src="https://github.com/user-attachments/assets/0b324577-bed5-4272-a7e0-f4444a0c1230" /> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com> |
||
|
|
af3e397e7f | feat(credit-codes): show that credit codes were reduced by partner deals (#51173) | ||
|
|
2d0bd7af69 |
feat(storage): add object versions data layer (#49207)
| # | Branch | Base | | - | ------ | ---- | | 1 | `feat/storage-versioning-private-alpha` — merged | `master` | | 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` | | 3 | `feat/storage-versioning/003-bucket-modals` | 2 | | 4 | `feat/storage-versioning/004-object-versions-data` ◀ | 3 | | 5 | `feat/storage-versioning/005-file-preview-versions` | 4 | | 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 | | 7 | `feat/storage-versioning/007-archived-objects-data` | 6 | | 8 | `feat/storage-versioning/008-archived-rows` | 7 | | 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 | | 10 | `feat/storage-versioning/010-replace-file` | 9 | ## [4/10] Storage object versioning: object versions data layer **Base:** `feat/storage-versioning/003-bucket-modals` (PR 3) ### This PR The query and mutation hooks for the version history UI, written to `queryOptions` using the real Storage endpoints. - `object-versions-query.ts` — the version list, plus `ObjectVersion` and `LifecyclePolicy` - `object-version-restore-mutation.ts` — promote a noncurrent version to current - `object-version-delete-mutation.ts` — remove one specific version - `object-purge-mutation.ts` — delete an object and every version, bypassing versioning - `VersionHistory.utils.ts` — `computeVersionFate`, the pure rule deciding what removal outlook each version row shows - `BroomSparklesIcon.tsx` — inline SVG for a glyph absent from lucide-react 0.436 Easier to test directly from next PR in the stack #49208 which wires the queries to the real file preview panel ui. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
6ef729cb5c |
feat(storage): versioning bucket modals (FE-4161) (#49205)
| # | Branch | Base | | - | ------ | ---- | | 1 | `feat/storage-versioning-private-alpha` — merged | `master` | | 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` | | 3 | `feat/storage-versioning/003-bucket-modals` ◀ | 2 | | 4 | `feat/storage-versioning/004-object-versions-data` | 3 | | 5 | `feat/storage-versioning/005-file-preview-versions` | 4 | | 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 | | 7 | `feat/storage-versioning/007-archived-objects-data` | 6 | | 8 | `feat/storage-versioning/008-archived-rows` | 7 | | 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 | | 10 | `feat/storage-versioning/010-replace-file` | 9 | ## [3/10] Storage object versioning: wire into the bucket modals **Base:** `feat/storage-versioning/002-bucket-form-fields` (PR 2) ### This PR Mounts the object-versioning form section in the create and edit bucket modals behind the feature preview, and saves it. - create and edit bucket modals spread `bucketVersioningFormFields` into their existing form schema - lifecycle defaults to 30 days / 10 versions - edit adds a confirmation before suspending an actively versioned bucket ## Enabling object versioning on a new bucket and setting lifecycle policies https://github.com/user-attachments/assets/194f8319-4929-432e-8a50-206f180a77a8 ## Edit and suspend object-versioning https://github.com/user-attachments/assets/f31e1d34-9840-4f5a-a269-6a911214742d ## To reproduce 1. Make sure storage versioning is enabled under feature previews > Storage Versioning 2. Open Storage Bucket File explorer 3. create new bucket and enable Object Versioning 4. set lifecycle policy - Noncurrent version expiration: can be either empty or >1 - Retained noncurrent versions: can be either empty or between 1 and 100 and can't exist without "Noncurrent version expiration" 5. Open new bucket with object versioning and test changing lifecycle policies 6. Disabling object-versioning shows proper warning and updates `versioning_status` to SUSPENDED (it can never go back to DISABLED once it has been enabled on a bucket) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> |
||
|
|
6c6b19c6c6 |
chore(studio): report empty-body GET 200s to Sentry with a no-store probe (#51123)
<!-- ccr-slack-attribution --> _Requested by **Ivan Vasilov** · [Slack thread](https://supabase.slack.com/archives/C063LNYJJKS/p1790710416069689?thread_ts=1790710416.069689&cid=C063LNYJJKS)_ **Before:** When a Studio API GET comes back as a 200 with an empty body, openapi-fetch hands the caller `{}` and we only see the downstream crash, with no record of the response that caused it. **After:** The first time this happens for an endpoint in a page session, Studio sends one Sentry warning, `Empty response body on successful API request`. It carries the response metadata, browser state, resource timing, and the result of a single `cache: 'no-store'` refetch. What the caller receives is unchanged. ## Problem Studio crashes trace back to GET requests that return 200 with an empty body, which openapi-fetch turns into `{}`. They are heavily skewed to Firefox and Safari. The leading hypothesis is browser cache revalidation (Express weak ETags, no `Cache-Control` on api.supabase.com), but nothing confirms it yet. The `no-store` probe tells the two cases apart: if the refetch has a body, the browser cache is the likely culprit; if it is also empty, the server or the edge is sending empty bodies. This data should show whether the fix belongs on the API side or the Cloudflare side. Context: #51041 (closed) tried to guard the crashing call sites instead. ## Needs API-side change to be fully useful Cross-origin, Studio can only read CORS-safelisted response headers, and resource timing sizes read as zero. If api.supabase.com sends `Access-Control-Expose-Headers: ETag, cf-ray, cf-cache-status, x-request-id` and `Timing-Allow-Origin: <studio origin>`, this event will also carry the ETag, cf-ray, and cache status, plus the real transfer and body sizes and the negotiated protocol. Until then, those fields read as `null` or `0`. ## Solution - `data/empty-body-diagnostics.ts` (new): `reportEmptyBodyResponse({ request, response, schemaPath })`. - Runs only for `GET` and only when `IS_PLATFORM`. Empty POST/201 bodies are legitimate. - Reports at most once per templated endpoint per page session (module-level `Set`). - Endpoint: openapi-fetch's `schemaPath` (e.g. `/platform/projects/{ref}/settings`), passed through `templateEndpointPath`. That function drops the query string and hash, replaces the segment after `projects`/`organizations`/`branches` with `{ref}`/`{slug}`/`{branch}`, and replaces UUIDs, numeric IDs, and 20+ character alphanumeric IDs with `{id}`. I used `schemaPath` rather than the request URL so user-chosen names (bucket names, function slugs) never end up in tags or fingerprints. - Probe: one plain `fetch(new Request(request, { cache: 'no-store', ... }))` with a fresh `X-Request-Id` and a 10s `AbortController` timeout. `AbortSignal.timeout` isn't available in older Safari. The probe bypasses the openapi-fetch middleware, so it can't recurse. Only the body's byte length is recorded, never its contents. - Event: `level: 'warning'`, `fingerprint: ['empty-body-response', endpoint]`, `tags: { endpoint, probe_has_body, empty_body_diagnostic: 'true' }`, where `probe_has_body` is `true` / `false` / `error`. `extra` holds: - the request: method, status, `response.type`, `redirected`, and the original `X-Request-Id` (for API log lookup) - response headers: `content-type`, `cache-control`, `last-modified`, `expires`, `content-length`, `etag`, `cf-ray`, `cf-cache-status`, `x-request-id` - browser state: `visibilityState`, `navigator.onLine`, the navigation type, ms since navigation start, and whether the page was restored from bfcache - the latest `PerformanceResourceTiming` for the URL (transfer, encoded, and decoded size, `nextHopProtocol`, `responseStatus`) - the probe: status, request ID, body length, `content-length`, `content-type`, or the error name - Fire-and-forget: everything is wrapped in a `try`/`catch`, and the caller does not await it. - `data/fetchers.ts`: the `onResponse` middleware passes `{ request, schemaPath }` to `normalizeEmptyBodyResponse`, which calls the reporter in its empty-body branch and also for a 200 that carries `Content-Length: 0`. openapi-fetch short-circuits that case to `{}` the same way, so it is the same symptom. The return value is unchanged in every branch. - `packages/common/sentry.ts`: `filterSentryEvent` normally keeps only 1% of events that aren't page crashes. It now sends events tagged `empty_body_diagnostic` unsampled, with `codeSampleRate: '1'`. A once-per-session warning would barely show up at 1%. Consent and platform gating and the third-party filter still apply. www and docs also use `filterSentryEvent`, but only Studio's reporter sets this tag, so sampling for them and for every other Studio event is unchanged. Sentry config: Studio's `beforeSend` doesn't otherwise drop this message. It has no exception values, so the no-stack-trace filter doesn't apply, and it matches no `ignoreErrors` entry. ## Review instructions 1. Check `normalizeEmptyBodyResponse` in `data/fetchers.ts`: the reporter is `void`-called and its return value is untouched. 2. Check `probe()` in `data/empty-body-diagnostics.ts`: only `byteLength` is read from the body. The probe reuses the original request's headers and credentials (same auth as the original GET). 3. Check `filterSentryEvent` in `packages/common/sentry.ts`: only the `empty_body_diagnostic` tag skips sampling. 4. Tests: `data/empty-body-diagnostics.test.ts` and `packages/common/sentry.test.ts`. ## Verification - Unit tests (`data/empty-body-diagnostics.test.ts`, new): - path templating cases - `probe_has_body` `true` / `false` / `error` - the secret body content never appears in the Sentry call - one report per endpoint - non-GET and non-platform requests are skipped - no throw when `fetch` or Sentry throws - end-to-end through `client.GET`: still resolves `{}` and reports the `schemaPath`, for both a missing `Content-Length` and `Content-Length: 0` - `packages/common/sentry.test.ts`: tagged diagnostics are sent unsampled, untagged or false-tagged ones are still sampled, and they're still dropped without consent. These tests, plus the existing `normalizeEmptyBodyResponse.test.ts`, `handleError.test.ts`, and the rest of `sentry.test.ts`, pass (67 tests) under vitest 5 + jsdom. I ran them in a minimal harness, not the full `pnpm install` workspace, because the local checkout is sparse. - I ran TypeScript 7.0.2 (`--strict`) on the five touched files against the real `api-types`, with stubbed `common`/Sentry types. No errors in the touched files. - Prettier `--check` with the repo config passes, with and without `SORT_IMPORTS=false`. - Not run locally: the full studio typecheck, `lint:ratchet`, and knip. CI covers them. The change adds no `any`, no default exports, and no deps. ## Checklist - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UefDak8XYLMi9aiEjDPXc5 --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
22886fd304 |
feat(studio): add flag-gated general region selection (#51274)
## Problem
We want to be able to show free-plan organizations a simplified region
selector that only lists general regions (Americas, Europe,
Asia-Pacific), controlled per organization through ConfigCat.
## Solution
- ConfigCat flags are now evaluated with `organization_slug` and
`organization_created_at` (Unix seconds) custom attributes, so flags can
target and bucket by organization.
- `organization_created_at` is read from `GET
/platform/organizations/{slug}`, fetched only for free-plan
organizations, since the organization list response doesn't include it.
- Two flags:
- `freeTierGeneralRegionEnrollment`: the organization is enrolled
(control or test).
- `freeTierGeneralRegionSelection`: the organization sees only general
regions.
- For enrolled free organizations, the region selector stays in its
loading state until flags have been evaluated with the organization's
creation time, so specific regions aren't shown and then removed.
- In the test variant, the selector hides specific regions and shows a
footer linking to the plan upgrade panel. High Availability keeps its
own region list.
- Telemetry: new `free_tier_general_region_experiment_exposed` and
`free_tier_general_region_upgrade_clicked` events, and
`freeTierGeneralRegionExperiment` / `regionSelectionType` properties on
`project_creation_simple_version_submitted`.
- `created_at` is added to `OrganizationSlugResponse` in the generated
platform types, matching the API.
## Review instructions
1. With both flags off, open `/new/[slug]` for a free organization and
confirm the region selector is unchanged.
2. Using the dev toolbar, set `freeTierGeneralRegionEnrollment` and
`freeTierGeneralRegionSelection` to `true`. Confirm only general regions
are listed and the footer links to the billing plan panel.
3. Set `freeTierGeneralRegionSelection` to `false` and confirm the full
selector is shown.
4. Repeat with a paid organization and confirm the full selector is
always shown.
|
||
|
|
16bd06dfb8 |
fix(studio): silence status page errors when incident.io isn't configured (#51315)
Running Studio locally without the incident.io env vars produces a 500 from `/api/status-page` on every fetch, plus a server-side log and a client `console.error`. Retries and the 5-minute refetch keep repeating them, so the dev console stays noisy. **Changed:** - `lib/api/status-page.ts` – when `INCIDENT_IO_WIDGET_URL` is unset outside production, return an empty (degraded, short-cached) status page instead of throwing. The missing API key / status page ID log is also limited to production. Production behavior is unchanged. - `data/platform/status-page-query.ts` – remove the client-side `console.error`, which repeated the error that React Query already exposes **Added:** - Tests for the missing widget URL in dev (empty payload, no error log) and in production (still throws) ## To test - Run Studio locally in platform mode without `INCIDENT_IO_*` set: no status page errors in the terminal or the browser console, and `/api/status-page` returns 200 with empty arrays - Support form and project creation still render normally (no status banner, status page link points to status.supabase.com) Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> |
||
|
|
454a232947 |
feat(studio): scoped oauth interstitial ui (#50090)
## 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? The scoped OAuth consent interstitial: scope display, org/project selection, consent screen assembly, success screen, and post-submit role-validation error state. Consolidates the former stacked PRs #49481, #49484, #49486, #49489, #49951 into one reviewable unit (each contained the last; no code was dropped). - Entirely behind `useFlag('oauthAppScopedGrants')` — no reachable UI with the flag off. - Runs on the mock data layer from #49476 (`USE_MOCKS`-gated); no real endpoints are called. - Covers PROD-626, PROD-627, PROD-628, PROD-629, PROD-653. Base is #49476 (data contracts + mocks); will retarget to master once it merges. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com> |
||
|
|
d2ffd76395 |
perf(logs): load pathname facet counts on demand DEBUG-230 (#51112)
## Problem Unified Logs included a pathname aggregation in every initial sidebar count query, even when the pathname filter was closed. This issue is tracked in [DEBUG-230](https://linear.app/supabase/issue/DEBUG-230/fetch-sidebar-counts-only-when-needed). ## Fix Remove pathname aggregation from the initial ClickHouse and BigQuery count queries while keeping the existing shared count scans. Fetch scoped pathname options through the existing facet query when the filter opens or its search changes. Order limited pathname results by count, validate response rows with Zod, and retain selected paths during loading and validation errors. Use live sidebar filters while their URL update is pending and URL filters after navigation. Cache results by project and filter scope, and wait for feature flags before requesting options. ## How to test - Open Unified Logs, then open Pathname and search. Confirm options load on demand. - Change the time range, another filter, or navigate through browser history. Confirm the options reflect the current scope. - Close and reopen Pathname without changing the scope. Confirm cached options return. - Run the pathname filter component tests and Studio typecheck. |
||
|
|
eb20a4674d |
getTableDefinitionSql to escape SQL identifiers (#51258)
## Context Similar to domain to https://github.com/supabase/supabase/pull/51256 - `getTableDefinitionSql` doesn't escape SQL identifiers, which generates invalid SQL on the dashboard's table editor for the "Copy table schema" CTA, or the table definition tab. Also fixes the "Copy table schema" CTA which was missing the `scoped` parameter when calling `getTableDefinition` Changes here addresses this issue, can test with a table named like `test"table` |
||
|
|
089133cc2c |
feat(storage): add bucket object versioning form fields (FE-4161) (#49203)
| # | Branch | Base | | - | ------ | ---- | | 1 | `feat/storage-versioning-private-alpha` — merged | `master` | | 2 | `feat/storage-versioning/002-bucket-form-fields` ◀ | `master` | | 3 | `feat/storage-versioning/003-bucket-modals` | 2 | | 4 | `feat/storage-versioning/004-object-versions-data` | 3 | | 5 | `feat/storage-versioning/005-file-preview-versions` | 4 | | 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 | | 7 | `feat/storage-versioning/007-archived-objects-data` | 6 | | 8 | `feat/storage-versioning/008-archived-rows` | 7 | | 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 | | 10 | `feat/storage-versioning/010-replace-file` | 9 | ## [2/10] Storage object versioning: bucket form fields The object versioning + lifecycle policy form section for the create and edit bucket modals. Mounted onto the ui in PR 3 #49205 - `BucketVersioningFields` — the versioning switch and the suspension / public-bucket / retention-tightening warnings - `LifecyclePolicySection` — the retention window and version cap inputs - `ExpirationModeToggle` — how the two conditions combine (and / or) - `BucketVersioningFields.schema.ts` — zod fields the parent modals spread into their own schema, plus `superRefineBucketVersioning` - `BucketVersioningFields.utils.ts` — retention-tightening detection - `StorageVersioning.constants.ts` — versioning state and expiration mode types, the prefill defaults, and `getBucketVersioningState` Note: a single s3 lifecycle policy expects both `version_expiry_days` and `max_noncurrent_versions` and always evaluate the two fields within the same policy with an AND logic. To enable both AND and OR/EITHER logic, we save two distinct s3 policies so we can enforce the OR logic. See demos and how to reproduce in #49205 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary * **New Features** * Eligible projects with the preview enabled can configure object versioning for storage buckets, including version expiration, retained-version limits, and “and/or” lifecycle conditions. * Settings default to 30 days and 10 retained versions, with validation for retention values and requirements for setting a version limit. * Notices highlight public buckets, missing lifecycle conditions, suspending existing versioning, and changes that tighten retention limits. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> |
||
|
|
7d04c43082 |
fix(logs): default unified logs to otel DEBUG-228 (#51089)
## Problem Unified Logs could start legacy BigQuery requests while ConfigCat loaded, then switch to OTEL when the flag resolved. ## Fix Default Unified Logs to OTEL unless otelUnifiedLogs is explicitly false. Use that selection for list, count, chart, facet, detail, download, and manual refresh requests. Keep BigQuery as an explicit opt-out until the legacy backend is removed. ## Validation - Studio typecheck passed locally. - Existing Unified Logs utility tests passed (21 tests). - The focused Unified Logs query test file was removed as requested; backend-selection and manual-refresh regressions are no longer covered by that suite. - CI checks are running on the current head. Tracks [DEBUG-228](https://linear.app/supabase/issue/DEBUG-228/prevent-bq-queries-before-the-feature-flag-loads). |
||
|
|
0c2257fb3d |
feat(studio): scoped oauth data layer (#49476)
## 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? ~~This is the first part (and ultimately the final part of the stacked PR). PR 1 introduces mock data for us while we build the requirements of the scoped oauth interstitial, all following PR's will be stacked on top of this one.~~ This is the first part, the data layer side. We began with mock data, but as backend support arrived we've used this PR to help us shape the UI as well as the frontend data layer. This now acts as the frontend data layer. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added OAuth app authorization request handling. * Added visibility into application details, requested scopes, and existing authorizations. * Added organization and project selection during authorization. * Added organization roles and project access details. * Added approval and denial options with secure redirect handling. * Added validation for required authorization details and project selections. * Added support for role validation feedback during approval. * Added representative authorization scenarios for approved, denied, and re-consent flows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com> Co-authored-by: Samir Ketema <6003000+samirketema@users.noreply.github.com> |
||
|
|
77e3b4382f |
feat(role): Allow eligible organizations to invite users as 'No-access' base role (#50922)
## Problem As the API has allow inviting users into `None / No-access` role for team, enterprise, and platform tier organization, we need to update the documentation and descriptions for this new role on the invitation form. ## Solution 1. Updated `apps/docs/content/guides/platform/access-control.mdx` to include the role 2. Added the role description on `apps/studio/components/interfaces/Organization/TeamSettings/Roles.constants.tsx` 3. Add the roles into the proper sorting order at `apps/studio/data/organization-members/organization-roles-query.ts` 4. Add logic to invitation components to disable the role when inviting user into project(s), as the backend does not allow it. ## Testing and verification steps The UI: https://studio-staging-aa8is1m07-supabase.vercel.app/dashboard/org Documentation: https://docs-kht98bi78-supabase.vercel.app/docs/guides/platform/access-control <!-- ## Preview links If relevant, include links to changed pages for easy review access. Copy the preview base URL from the Vercel bot comment on this PR. Use the following table as an example template. | Site | Live | Preview | Search for | | -------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------- | | WWW | [/blog/your-post](https://supabase.com/blog/your-post) | [/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post) | unique phrase from the change | | Docs | [/docs/guides/your-page](https://supabase.com/docs/guides/your-page) | [/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page) | unique phrase from the change | | Studio | [/dashboard](https://supabase.com/dashboard) | [/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard) | unique phrase from the change | | Design system | [/design-system](https://supabase.com/design-system) | [/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system) | unique phrase from the change | | UI library | [/library](https://supabase.com/library) | [/library](https://ui-library-git-branch-name-supabase.vercel.app/library) | unique phrase from the change | | Knowledge base | [/kb/guides/your-page](https://supabase.com/kb/guides/your-page) | [/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page) | unique phrase from the change | --> <!-- ## Additional context Optionally add any other context or screenshots. --> ## Checklist Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which references [WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md) and the docs [CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary * **Updates** * The **None** role is labeled **No-access** and describes the lack of organization and project resource access. * **None** is included after **Read-only** in the role list. When inviting a member with project-only access, **None** is disabled with an explanation. * **Documentation** * Clarified plan coverage for **Read-Only** and **No access**, and added guidance on assigning **No access** at the organization level before granting project-specific roles. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
838fcaaa89 |
Joshenlim/fe 4509 fdw general UI consolidation and refactor (#51079)
## Context Stacks on top of https://github.com/supabase/supabase/pull/51074 PR's just mainly refactoring, no visual differences: - `CreateWrapperSheet` + `EditWrapperSheet` use the same UI components for the foreign tables section - Can be consolidated into one reusable component - `WrapperTableEditor` is still using `SidePanel` component - Can be swapped to use new `Sheet` component - Refactor `WrapperTableEditor`'s layout a little - added separators for clarity between sections <img width="400" alt="image" src="https://github.com/user-attachments/assets/b1983bf2-cff5-43eb-8b31-40a7abb65038" /> - Update `getCreateFDWSql` to just use the Foreign Data Wrapper's name from `wrapperMeta` since its now standardized <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a shared foreign-table selector for wrapper setup and editing, with options to view columns, add or edit table definitions, and remove tables. * Updated the table editor to use a sheet layout with a fixed footer. * **Bug Fixes** * Wrapper creation now uses the wrapper’s configured name when creating the server. * Foreign-table targets display the table name when other target details are unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
521881a899 |
Joshenlim/fe 4480 fdw create wrapper to only init fdw once users to name the (#51074)
## Context PR here refactors the way we manage Foreign Data Wrappers in the dashboard (Under Project -> Integrations), as there's some DX problems with the current behaviour. Currently whenever a user creates a new wrapper, the dashboard is creating both the Foreign Data Wrapper (`create foreign data wrapper...`) + server (`create server ...`). The former is **_redundant_** to create multiples of given that it just handles the `handler` and `validator`, whereas what matters more is the server which holds the connection credentials. Hence standard practice is usually one Foreign Data Wrapper with multiple servers. (The former just needs to be created once if not done yet) This also led to some problems as well when users created their own wrappers via SQL and tried to manage them through the dashboard GUI, leading to us having to add some guard rails to prevent managing wrappers sharing the same Foreign Data Wrapper ([ref](https://github.com/supabase/supabase/pull/50785)) ## Changes involved - When creating a wrapper, if the Foreign Data Wrapper has yet to be set up for the wrapper type, the dashboard will initialize one and subsequently use that same Foreign Data Wrapper for any new wrappers - When creating / editing a wrapper, users will name the **server** instead of the **wrapper** <img width="500" alt="image" src="https://github.com/user-attachments/assets/b3e61204-0e16-4599-84ac-af2aab5b93c2" /> - When deleting a wrapper, the clean up for vault secrets are now deterministic by referencing the wrapper's server options - RE backwards compatibility: Existing wrappers will _not_ be affected by the changes here - they can be edited / deleted as per normal ## Unrelated fixes + UI refactors added - Fix Iceberg Wrapper not showing the right form when adding new wrapper - Adjust form layouts in side panel to be horizontal instead of vertical (Follows Database -> Pipelines) - Clean up to use newer UI components like `ButtonTooltip` - Opt to hide Docs + Create CTA under `WrappersTab` if marketplace feature preview is enabled (Since these actions are already in the header, will be duplicates) - Consolidate foreign tables configuration for create + edit wrapper sheet into one component `ForeignTablesSelector` ## To test - [ ] Verify that existing wrappers with their own Foreign Data Wrapper can be edited correctly - [ ] Verify that existing wrappers with their own Foreign Data Wrapper can be deleted - [ ] Verify that existing wrappers with shared Foreign Data Wrapper can be edited correctly - [ ] Verify that existing wrappers with shared Foreign Data Wrapper can be deleted - [ ] Verify that new wrappers can be created - [ ] Verify that newly created wrappers can be edited correctly - [ ] Verify that newly created wrappers can be deleted |
||
|
|
7353782724 |
fix(studio): show compute waitlist notice when project is not enrolled (#51224)
Resolves FUNC-941 ## What When a project isn't enrolled in Compute, show a short notice with a link to the waitlist (https://supabase.com/compute) instead of a generic error. ## Why The compute API returns its 404 as `{ "error": { "code", "message" } }`. `handleError` only reads a top-level `message`, so it dropped the status code, `isComputeUnavailable` never matched, and users saw "API error happened while trying to communicate with the server." The query now checks `response.status === 404` directly. ## Testing - [ ] Project not enrolled in Compute: the waitlist notice shows - [ ] Enrolled project: the instance list loads as before |
||
|
|
4b2d163a1d |
fix(orioledb): use alpha and beta conditionally based on AMI version (#51162)
## Problem - Older orioledb projects show "Public Beta" instead of "Public Alpha" in UI. - List of backups in the "Scheduled backups" tab hangs. ## Solution - show in the UI "Public Alpha" for projects older than 17.11.0.001-orioledb - show in the UI "Public Beta" for new projects - enable scheduled-backup query for "Public Beta" <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Backup availability messages now reflect OrioleDB’s current release stage rather than always describing it as public beta. * AWS backup queries are no longer disabled for every OrioleDB project; they remain disabled during the alpha stage. * **New Features** * Added an informational notice and documentation link for scheduled backups on AWS OrioleDB projects in alpha. * Added release-stage details to the PITR availability notice. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> |
||
|
|
123c768de1 |
Warn when authenticator role overrides exposed schemas (FE-4472) (#50982)
## What Adds a warning in Project Settings > API when the `authenticator` role's `pgrst.db_schemas` setting overrides the Dashboard's "Exposed schemas" configuration, plus an inline "Reset override" button to fix it in one click. ## Why `ALTER ROLE authenticator SET pgrst.db_schemas = ...` silently overrides what PostgREST actually exposes, regardless of what's selected in the Dashboard. Users hit a confusing PGRST106 error with no indication that a role-level override is the cause. ## How - New query (`authenticatorRoleConfigQueryOptions`) reads `pg_roles.rolconfig` for the `authenticator` role and parses out any `pgrst.db_schemas` value. Configured to always refetch on mount and window focus, since the fix is often applied outside the Dashboard (SQL editor, another client) with no cache-invalidation event for the app to react to. - `PostgrestConfig.tsx` compares that value against the currently selected schemas and shows an `Admonition` warning naming the actual overriding schemas, with a link to the PGRST106 troubleshooting guide, when they differ. - The warning includes a "Reset override" button that runs `alter role authenticator reset pgrst.db_schemas` after a confirmation step (showing the exact SQL that will run, with a copy button), then refetches so the warning clears immediately without a page reload. ## Testing 1. In the SQL Editor of a test project, run: ```sql alter role authenticator set pgrst.db_schemas = 'public'; ``` 2. Go to Project Settings > API, and select a schema other than (or in addition to) `public` in "Exposed schemas" (e.g. add `api`). 3. The new warning should appear, naming `public` as the schema actually in effect, with a link to the PGRST106 troubleshooting guide. 4. Click "Reset override" in the warning, confirm in the modal, and check that the warning clears immediately without a page reload. 5. Alternatively, clear the override manually from the SQL editor: ```sql alter role authenticator reset pgrst.db_schemas; ``` then navigate away from the API settings page and back (or refocus the browser tab) — the warning should clear without a hard refresh. Fixes [FE-4472](https://linear.app/supabase/issue/FE-4472/warn-when-authenticator-role-overrides-exposed-schemas) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * The API settings page now warns when the authenticator role’s exposed schemas differ from the saved Dashboard configuration. * You can reset the override to restore the saved schema configuration. The reset requires permission and provides success or error feedback. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cd305313e4 |
fix(roles): Show only document-defined roles (#51087)
## Problem As part of having `None / No Access` available to customers, we need to start providing this role entry in `/platform/organization/:ref/roles` endpoint. However, when making it available, the role will show up prematurely on all the components that relies on `useOrganizationRolesV2Query` function. ## Solution This change is to allow us to test the behavior of the new role without having to turn the API on/off. The UI will show only the "predefined" entries and ignore the "extras" sent by API. After this is merged, we will do the following 1. Unhide the None role from the API https://github.com/supabase/platform/pull/39137 -- this will not have any effect on the frontend as we already ignore it in this PR 2. Work and continue testing on https://github.com/supabase/supabase/pull/50922 -- which will be easier to verify as we no longer need to change the API side ## Review instructions 1. Modify the items in the `FIXED_ROLE_ORDER` list, remove some roles from there 2. You will see that the role will disappear from the components like the invitation form or managed access form. ## Checklist Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs [style guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **Bug Fixes** * Organization role lists now show only supported roles, in the expected order. Roles outside the supported set are no longer displayed. This keeps the list consistent and focused on recognized roles, making available organization roles easier to review. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c5b4fbfa11 |
fix: Handle NO_PROJECT_MARKER for projectRef (#51083)
Skip `NO_PROJECT_MARKER` values for projectRef in API validation. |
||
|
|
38b74af3f1 |
fix(studio): fall back for framework icons without an asset (#51065)
I made the connected-project framework icons fall back when no shipped SVG exists for a framework. The three icon sites built `/img/icons/frameworks/<framework>.svg` straight from the integration's framework preset, which is an open-ended string. They only fell back when the value was empty, so any preset without an asset (`express`, `hono`, `fastapi`, `tanstack-start` and others) showed a broken image and logged a 404. **Changed:** - **Broken framework icons**: `getFrameworkIconUrl` returns the asset URL only for slugs in a set that mirrors `public/img/icons/frameworks/`. The integration connection row, the org project linker and the marketplace project picker now show their existing fallback icon for any other slug. A test keeps the set equal to the directory listing. - **Framework type**: I deleted the hand-kept `VercelFramework` union. It listed exactly the shipped icon slugs, while the API types the field as `string | null`, and that mismatch is what made the old empty-only check look safe. **Note:** I rejected an `onError` fallback because the browser still sends the 404 request. Adding logos for common presets is left for design. ## To test Tested on Vercel preview (staging): no real connection there uses these presets, so I rewrote the org integrations response in the browser to give one integration four connections. - [x] Open an org's Integrations page with connections whose framework has no shipped icon (`express`, `eve`, `tanstack-start-lovable`). Expect the fallback badge and no request under `/dashboard/img/icons/frameworks/` for those slugs. Observed: all three rows showed the badge and the network log had no request for their SVGs. - [x] Same page with a `nextjs` connection. Expect its framework logo. Observed: `nextjs.svg` loaded with a 200. - [x] Same page with the real, unmodified response (one connection with `framework: null`). Expect the badge, no frameworks requests, and no new console errors. Observed: as expected. ## Linear - fixes GROWTH-1309 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Vercel integration and project views now display framework icons when available and fall back to the Vercel icon when no matching icon exists. * Framework metadata now supports values beyond a fixed list, while unsupported frameworks continue to use the fallback icon. * **Tests** * Added coverage for supported and unsupported framework icons, including base-path handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d22702e907 |
refactor(studio): extract user project regions + emergency override hooks (#51033)
## Summary - Extracts `useUserProjectRegions` (org + project region aggregation, fail-open on fetch errors) and `useEmergencyIncidentOverride` (the `ongoingIncident` flag / env var check) out of `useStatusPageBannerVisibility`, so the upcoming incident.io status-page banner can reuse both without duplicating the org/project fan-out logic. - No behavior change for the legacy banner except one intentional fix: `StatusPageBanner.utils.ts` compared the incident's affected regions (lowercased) against the user's regions (not normalized), so a mixed-case region on either side could silently fail to match. Both sides now go through the same `normalizeRegion` helper. - Part of the Linear FE-4057 stack (PR 5a of 6). Base branch is PR 4 (`charis/fe-4057-pr4-project-creation-status-admonition`), not master. Linear: FE-4057 ## Test plan - [x] `pnpm --filter studio run typecheck` - [x] `pnpm --filter studio run lint:ratchet` - [x] `pnpm knip --workspace apps/studio` - [x] `pnpm test:prettier` - [x] `pnpm --filter studio exec vitest run` on the touched test files and the `hooks/misc/` and `components/layouts/AppLayout/` directories (all passing, no regressions) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved status banner targeting by matching incidents against normalized regions across the user’s projects. * Updated banner visibility checks to handle incomplete project or region data, including when project information fails to load. * Improved loading behavior so the banner can be evaluated based on user-region data rather than waiting for separate organization and project requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4a941518e3 |
feat(studio): track Explorer runs and saves (#51004)
I added outcome events for Explorer query runs and successful manual notebook saves. Existing page visits and preview toggles do not show whether users complete queries or persist notebooks. **Changed:** - **Query usage:** Accepted runs from query tabs and notebook cells emit submitted and terminal outcome events with a shared run ID. Canceled confirmations emit no run events. - **Notebook adoption:** Successful manual saves emit created or updated events. Recreated notebooks count as creations. Unsaved drafts and failed saves emit neither. - **Event metadata:** Explorer action events use `Explorer` as their page title. **Note:** Assistant-generated saves are outside this PR. Custom properties omit SQL and notebook content. Page visits still carry the browser title, which can include a notebook name. ## To test Tested on the staging preview: - [x] Run valid and invalid SQL from an Explorer query tab. Each run emits one submitted event and one matching completed or failed event with the same run ID. - [x] Run database and Logs notebook query cells, then add a markdown cell. The query cells emit matching event pairs; the markdown cell emits no query event. - [x] Save a new notebook, then edit and save it again. The successful saves emit created and updated events. - [x] Cancel a guarded query. It emits no query run event. - [ ] Recreate a notebook deleted on the server after local edits. A successful save emits created, not updated. - [x] Inspect an Explorer action event request. Its page title is `Explorer`; page visits still use the browser title. - [ ] Force a notebook save failure. It should emit no save event. This case was not tested manually. ## Linear - fixes GROWTH-1298 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Analytics** * Explorer query runs are tracked for database and log queries, including whether they complete or fail. * Query activity is associated with its location in Explorer, such as a query tab or notebook cell. * Successful notebook saves are tracked as creations or updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
995f6f65c7 |
[FE-4483] fix(studio): disable network bans for v3 projects (#50997)
Disables network bans for v3 (`AWS_K8S`) projects and shows a specific unsupported notice. The shared banned-IP query waits for project details and skips unsupported projects, covering both Database Settings and Advisor for v3 and High Availability projects. The hook returns the standard query result and uses `skipToken` to prevent unsupported requests, including manual refetches. Database Settings handles project-detail errors at the call site. Open unban confirmations are cleared when the section becomes disabled, and submission checks eligibility. Addresses [FE-4483](https://linear.app/supabase/issue/FE-4483/disable-network-bans-for-v3-aws-k8s-projects). ## To test - Open Database Settings on a v3 project. Check that Network bans shows the v3 notice, hides the IP list and unban controls, and makes no network-bans retrieval request on initial load or reload, including while Advisor is mounted. - Check that an HA project still shows its existing notice and makes no network-bans retrieval request on initial load or reload. - Navigate from a supported project to a v3 or HA project and check that no banned-IP request is sent for the unsupported project and no banned-IP signals from the previous project appear in Advisor. - If project details fail without cached data, check that Network bans shows an error after retries finish and does not retrieve bans. A successful retry should restore normal behavior. - Open an unban confirmation on a supported project, then navigate to a v3 or HA project. Check that the dialog closes without sending an unban request and stays closed when returning. A newly opened confirmation should still work. - On a supported project, check the empty state and banned IP list. Confirm that users with permission can unban an IP and users without permission see a disabled button with the permissions tooltip. Validation: 17 focused tests passed, covering automatic and manual request suppression, project-detail error display and recovery, and navigation between supported and unsupported projects. Changed-file ESLint, Prettier, and full Studio typecheck (without the incremental cache) passed. Earlier local browser checks on `9912c6c` confirmed no retrieval requests for an HA project on AWS_K8S across reloads and Advisor, and a successful empty state on a supported project. The local failed-project case redirected to the organization after retries, so the inline error remains verified by the component test only. The latest preview, standalone v3 notice, populated bans/unban, and no-permission tooltip still need browser verification. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Banned IP settings now show an unsupported-project notice for AWS Kubernetes projects and hide ban lists and unban actions for AWS Kubernetes and High Availability projects. * Banned IP data loads only after project details are available and only for supported projects; unsupported projects do not display cached ban data. * Project-detail errors are shown separately from ban-list errors. * Unban confirmations close when a project becomes unsupported or an unban succeeds. Unbanning is unavailable when you lack update permission or the project is unsupported. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> |
||
|
|
6a0518dd86 |
fix(studio): forward HA flag to project-creation pre-flight checks (#50902)
## Summary - Fixes MUL-1678: toggling High Availability on in the project-creation wizard 400'd for orgs that are HA-entitled but not enrolled in the "V3 rollout ramp" feature flag on the backend, because two pre-flight endpoints Studio calls before project creation didn't know about HA and hit the ramp check the real create-project call already bypasses. - Threads `highAvailability` through `useOrganizationAvailableRegionsQuery` (`GET /platform/projects/available-regions`, sent as `high_availability=true|false` on the query string) and `useProjectCreationPostgresVersionsQuery` / `useAvailableOrioleImageVersion` (`POST /platform/organizations/:slug/available-versions`, sent as `high_availability` in the body), including their query-key cache keys so HA and non-HA responses for the same org/provider/size don't collide. - Wires the (already form-tracked) `highAvailability` value into every call site: `ProjectCreationForm.tsx`, `RegionSelector.tsx`, and a new `highAvailability` prop on `PostgresVersionSelector.tsx` passed from `InternalOnlyConfiguration.tsx`. ## Problem The project-creation wizard's HA toggle is wired into the actual project-create request, but two pre-flight calls Studio makes before that (available regions, available Postgres versions) had no way to signal HA, so the backend's ramp-flag gate rejected them for HA-entitled orgs outside the ramp. ## Solution Add an optional `highAvailability` field end-to-end on the Studio side: query hook variables, cache keys, request serialization, and the components that already track the toggle via `useWatch`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Available regions and database versions in project creation now reflect the selected high-availability setting. * The Create button remains disabled while available regions are loading. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: GuptaManan100 <guptamanan100@gmail.com> Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
8b8569bd1a |
feat(studio): status page client data layer + support form (#50984)
## Summary * Adds the client query layer for `/api/status-page` (`statusPageQueryOptions` in `data/platform/status-page-query.ts`) and a shared normalization module (`lib/status-page/status-page.utils.ts`) that maps the endpoint response into region- and project-creation-aware `StatusItem`s — later PRs in this stack (assistant tool, project-creation admonition, global banner) build on this same module. * Wires the support form's status pill and incident admonition to the new data behind the `incidentIoStatusPage` ConfigCat flag. `useSupportStatus` branches between the new endpoint and the existing `useIncidentStatusQuery`/`processIncidentData` path, both mapped to the same `SupportStatus` shape. * `IncidentAdmonition` becomes purely presentational; the status link now points at the `pageUrl` returned by the endpoint instead of a hardcoded URL. Part of [FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured) — see Linear for full design context. ## Test plan - [X] `pnpm --filter studio run typecheck` - [X] `pnpm --filter studio run lint:ratchet` - [X] `pnpm knip --workspace apps/studio` - [X] `pnpm test:prettier` - [X] `pnpm --filter studio exec vitest run` (touched files) — 70 tests passing, including a flag-on case for `SupportFormPage` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Support pages now display current incident and maintenance information, including relevant status descriptions and links to the status page. * Status details can reflect items affecting a user’s region or project-creation services. Upcoming maintenance is excluded from active alerts. * **Bug Fixes** * Status labels and alerts now account for loading, errors, incidents, and maintenance consistently across support pages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e3fec137fe |
Joshenlim/fe 4466 audit logs update organization logs as well (#50935)
## Context Follows up from [this PR](https://github.com/supabase/supabase/pull/50799) - updates the Audit Logs UI for organization audit logs. Just differs from Account audit logs slightly with added "Actor" + "Target" column Reuses the same UI components from account audit logs so quite a bit of code clean up 🙂 <img width="1451" height="955" alt="image" src="https://github.com/user-attachments/assets/a1002cee-917f-4d9a-b977-3fab8ecd2d13" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Organization audit logs now use a sortable table with row selection and a resizable details panel. * Actor details show a member’s username when available, otherwise the actor’s email; a dash appears when neither is available. * Logs can be filtered by users and projects, with separate messages for no logs and no matching results. * A refresh control and loading indicators help show audit-log updates. * **Bug Fixes** * Organization project lists stop loading when pagination totals are unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f2ff4ec0e9 |
fix(realtime): display banner when realtime has been suspended by admin (#50497)
## 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? If Realtime's admin_suspended_at is set we display a banner. ## What is the current behavior? REAL-1095 ## What is the new behavior? <img width="1160" height="442" alt="Screenshot 2026-09-17 at 12 06 30 pm" src="https://github.com/user-attachments/assets/f5abe900-a163-48c9-ab55-945fc62df0d1" /> ## Additional context Depends on https://github.com/supabase/platform/pull/38476 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Added a notice to Realtime settings when the service is suspended, with instructions to contact support. - **Bug Fixes** - Improved invitation resending and role updates for roles without a linked base role. - **Tests** - Added coverage to verify the suspension notice appears only when applicable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
509afd0bf0 | fix(o11y): support partial metric loading (#50867) | ||
|
|
f0952fdef8 |
fix(studio): delete only the selected foreign server FE-4462 (#50785)
## Problem The dashboard lists one row per foreign server, but deleting a row dropped its foreign data wrapper with CASCADE. When multiple servers shared a wrapper, deleting one removed all of them. ## Fix Drop the selected server and its foreign tables. Remove the underlying wrapper and Vault secret only when no servers still use it. Edits to a shared wrapper now stop before making changes because the existing edit flow recreates the underlying wrapper. ## How to test 1. Configure two BigQuery foreign servers that use the same foreign data wrapper. Delete one from the dashboard. 2. Confirm the other server and its foreign tables still exist and work. 3. Delete the remaining server. Confirm the foreign data wrapper and its Vault secret are removed. 4. Attempt to edit one of two servers sharing a wrapper. Confirm the edit fails without removing either server. Focused pg-meta tests and typecheck pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Shared connections are identified in the integrations list, with guidance for editing them in the SQL Editor. Editing is disabled when a wrapper is shared, with an explanation shown. * Deleting a connection removes its foreign tables and removes the wrapper and Vault secret only when no other connection uses them. * **Bug Fixes** * Connection deletion verifies that the selected server still belongs to the wrapper and reports failures using connection-focused wording. * Attempts to edit a wrapper used by another connection are blocked with a clear explanation. * Connection deletion and confirmation messages now consistently refer to deleting a connection. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
12894ddd2a |
feat(studio): migrate storage infinite-query hook to list-v2 (#50730)
## Problem Storage's `list` (v1) endpoint is being deprecated in favor of `list-v2`, which uses cursor pagination instead of numeric offset (which degrades on large buckets) and fixes folders that differ only by case not both being listable. This is PR 1 of the migration (parent: FE-4423); it covers the shared infinite-query hook and its two consumers. ## Solution Added `listBucketObjectsV2` alongside the existing v1 `listBucketObjects` (still used elsewhere, migrated in a later PR), and replaced the `useBucketObjectsInfiniteQuery` hook with `bucketObjectsInfiniteQueryOptions` built on `infiniteQueryOptions`, following the repo's preferred data-fetching pattern. Pagination now uses `hasNext`/`nextCursor` instead of an offset multiplier, and `queryFn` rejects a response that claims `hasNext` without advancing the cursor so a misbehaving backend can't send `fetchNextPage` into an infinite loop. v2 splits results into separate `folders`/`objects` arrays and has no `search` field, so the two consumers (`BucketFilePickerColumn`, `MoveItemsFolderPicker`) merge/sort those arrays themselves, and search is folded into a `prefix` match instead. Also removed "Time last accessed" from the picker's sort dropdown since v2's `sortBy.column` doesn't support it, with a defensive fallback to `name` in case the shared sort preference (still used by the v1 main file explorer) carries that value over. Added the missing self-hosted `list-v2` API proxy route (`pages/api` + the TanStack `routes/api` wrapper) using storage-js's `listV2()`, since self-hosted Studio only had a v1 route and every v2 request was 404ing there. ## Review instructions 1. Open the bucket file picker (e.g. via an OAuth app logo upload), confirm folders and files both render and paginate correctly, and that searching still filters as expected. 2. Open the "Move items" modal's folder picker, confirm you can navigate into and back out of subfolders, and that folder search still works. 3. Run `pnpm test:studio -- MoveItemsModal`. ## Checklist - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [ ] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which references [WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md) and the docs [CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Storage browsing loads large bucket listings in pages, helping keep navigation responsive. * Folder pickers display folders and files together across paginated results. * Moving items between folders uses the same paginated browsing experience. * Search remains available in the final folder level, and folder navigation shows the correct contents. * **Updates** * “Time last accessed” is no longer available as a sorting option in storage pickers. Sorting is available by name, creation time, or update time. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
ad5c4bb879 |
fix: gracefully handle 403s on org invites for project-scoped members (#50876)
## Problem Gracefully handles 403s when getting org invitations as a project-scoped org member. The backend currently returns an empty list with a 200 status, but that will be changing soon - so this PR aims to fix the issue in advance. ## Solution Pretty self explanatory - but here is a before/after (with backend changes) ### Before <img width="2480" height="688" alt="CleanShot 2026-09-24 at 11 02 34 AM@2x" src="https://github.com/user-attachments/assets/13c6ed9e-7580-4962-9920-f49ede9c4592" /> ### After <img width="2466" height="820" alt="CleanShot 2026-09-24 at 11 07 13 AM@2x" src="https://github.com/user-attachments/assets/248f7bb5-1743-41c9-823f-f776a608dd17" /> <!-- ## Preview links If relevant, include links to changed pages for easy review access. Copy the preview base URL from the Vercel bot comment on this PR. Use the following table as an example template. | Site | Live | Preview | Search for | | -------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------- | | WWW | [/blog/your-post](https://supabase.com/blog/your-post) | [/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post) | unique phrase from the change | | Docs | [/docs/guides/your-page](https://supabase.com/docs/guides/your-page) | [/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page) | unique phrase from the change | | Studio | [/dashboard](https://supabase.com/dashboard) | [/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard) | unique phrase from the change | | Design system | [/design-system](https://supabase.com/design-system) | [/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system) | unique phrase from the change | | UI library | [/library](https://supabase.com/library) | [/library](https://ui-library-git-branch-name-supabase.vercel.app/library) | unique phrase from the change | | Knowledge base | [/kb/guides/your-page](https://supabase.com/kb/guides/your-page) | [/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page) | unique phrase from the change | --> <!-- ## Additional context Optionally add any other context or screenshots. --> ## Review instructions 1. Open the preview 2. Navigate to the `Team` tab in `Organization` 3. Invite a project-scoped member 4. Accept the project-scoped member invite in another browser 5. Navigate to the same `Team` tab as that project-scoped member. ## Checklist Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which references [WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md) and the docs [CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Organization member lists now load even when invitation details aren’t accessible. * Missing invitation data no longer prevents member lists from loading. * Invitation errors other than access-denied errors, and errors loading members, continue to be reported. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
881c124301 |
fix(studio): prevent API Gateway report tab crash (#50847)
## Problem The API Gateway report initializes its request range with the Logs Explorer helper, which leaves `iso_timestamp_end` empty. When an OTEL chart returns sparse microsecond timestamps, the time series filler can interpret them as milliseconds while deriving the end of the range and attempt an unbounded fill, freezing or crashing the browser tab. ## Fix Initialize the report with its date range helper so both request bounds are present. Normalize microsecond timestamps before deriving fill bounds, including the single point case. Add regression tests for sparse OTEL data with both empty and explicit end dates. ## How to test - Open Observability → API Gateway on a project with requests spanning two hourly buckets. The page should render without a tab crash, and the analytics requests should include a nonempty `iso_timestamp_end`. - Run `pnpm --filter studio exec vitest run tests/features/logs/Logs.utils.test.ts` and confirm the microsecond timestamp cases pass. - Run the Studio typecheck and lint checks. The Vitest, typecheck, and lint commands could not run in this worktree because Studio dependencies are not installed. The source diff passed `git diff --check` and received a focused code review. |
||
|
|
d51ed9f451 |
Update Studio disk IO burst copy (#50809)
## Problem Studio copy ties disk IO burst behavior to compute size thresholds and says the IO budget "resets". Burst eligibility isn't a single size cutoff EBS burst credits refill continuously while disk usage runs below baseline Docs already use this framing (#50016) Fixes PROD-665 ## Solution Three copy changes: - `UnavailableChartBlock.tsx`: the burst balance chart placeholder no longer names a size. It now describes sustained IO with no burst credit pool - `database-charts.ts`: the Disk IO Burst Balance tooltip describes the EBS burst credit pool without referencing instance size - `ResourceExhaustionWarningBanner.constants.ts`: the warning and critical banners say the budget refills whenever disk usage runs below baseline, instead of "resets" ## Review instructions 1. Read the diff. Copy changes only. 2. Optional: on a project with burstable disk IO, open Reports > Database and hover the Disk IO Burst Balance chart title to see the new tooltip. ## Checklist Check all before review: - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [x] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill, which references [WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md) and the docs [CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md) guide <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Documentation * Updated disk I/O chart messaging to explain that some compute types sustain disk throughput without a burst credit pool to track. * Clarified that disk I/O burst budgets refill when demand is at or below baseline, and that throughput remains at baseline until the budget refills. * Updated the burst-balance chart tooltip to describe how compute uses the EBS burst credit pool. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3063679f1b |
feat(auth): restore key last-used timestamps FE-2462 FE-4315 (#50732)
## Problem Studio expected aliased fields from the last-used API-key endpoint, but the live endpoint returns OTEL attribute names. This kept legacy API-key activity unavailable and prevented Studio from showing activity for new JWT signing keys. Tracks FE-2462 and FE-4315. ## Fix Normalize the endpoint response at the data boundary, keep the `showApiKeysLastUsed` feature flag, and show activity from the past 24 hours for new JWT signing keys. Legacy HS256 signing keys remain blank because the analytics response does not provide a stable signing-key record ID for them. The request remains hosted-only, permission-gated, and non-blocking, and the existing last-rotated column remains intact. ## How to test - Make a request with a legacy anon or service-role API key, then open Project Settings > API Keys and verify its last request appears. - Make an Auth request signed by a new JWT signing key, then open JWT Keys and verify the matching key shows a Last used timestamp. - Verify a new key without activity shows No requests in the past 24 hours. - Verify the legacy HS256 signing-key row leaves Last used blank. - Expected result: legacy API keys and new JWT signing keys display activity from the shared endpoint without changing self-hosted Studio. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a **Last used** column for JWT signing keys on supported platforms. * Displays usage timestamps, loading and error states, or when a key has had no requests in the past 24 hours. * Usage tracking now includes both API keys and JWT signing keys. * **Bug Fixes** * Improved handling of usage records for legacy and current keys. * Usage details appear only on supported platforms and for users with the required permissions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
3ec2dfca44 | fix(stripe-atlas): guard stripe-atlas page in self-hosted mode (#50780) | ||
|
|
ec574f3a51 |
fix(studio): pass Authorization header to assistant list_policies tool (#50756)
<!-- ccr-slack-attribution --> _Requested by **Saxon Fletcher** · [Slack thread](https://supabase.slack.com/archives/C051L8U2EJF/p1789995309253479?thread_ts=1789995309.253479&cid=C051L8U2EJF)_ Resolves AI-1246 ## Problem **Before:** The Assistant's `list_policies` tool fails in about 70% of traces. It only "succeeds" when the org has AI opt-in disabled, because then it returns the privacy stub and never makes a request. When opt-in is enabled, it runs the pg-meta query server-side with no `Authorization` header, so the request is unauthenticated and fails. The Assistant then falls back to `execute_sql`. **After:** `list_policies` sends the caller's `Authorization` header, the same way `execute_sql` in `studio-tools.ts` already does, so it returns the project's RLS policies. ## Solution `getTools` already receives `authorization` but didn't pass it to `getSchemaTools`. This PR passes it through. `list_policies` builds `{ Authorization }` from it, and `getDatabasePolicies` gets an optional `headersInit` argument that it forwards to `executeSql`, the same pattern `getDatabaseFunctions` uses. Existing client-side callers of `getDatabasePolicies` don't change. Files: `lib/ai/tools/index.ts`, `lib/ai/tools/schema-tools.ts`, `data/database-policies/database-policies-query.ts`, plus tests in `lib/ai/tools/schema-tools.test.ts` (new) and `lib/ai/tools/index.test.ts`. ## Review instructions 1. Read `schema-tools.ts` and compare it with the `authHeaders` handling in `studio-tools.ts` (`execute_sql`). 2. On the preview, use an org with AI opt-in set to at least "schema" and ask the Assistant to list the RLS policies on `public`. `list_policies` should return the policies without falling back to `execute_sql`. Local gates (all passed): - `pnpm typecheck` in `apps/studio` (next typegen + `tsc --noEmit`) - `npx eslint` on touched files: 0 errors. The 2 warnings are on lines this PR doesn't change. - `npx vitest run lib/ai/tools/schema-tools.test.ts lib/ai/tools/index.test.ts lib/ai/tool-filter.test.ts`: 24/24 passed. I also ran the new header test against the old `schema-tools.ts` and it failed, as expected. - `SORT_IMPORTS=false npx prettier --config prettier.config.mjs --check` on touched files Follow-up, not in this PR: `getRlsKnowledge` in `fallback-tools.ts` (self-hosted path) also calls `getDatabasePolicies` without headers, even though a `headers` object is already in scope there. ## AI disclosure Claude Code (agent) wrote this PR from the Slack request. @SaxonF (Saxon Fletcher) is the accountable human owner. A human needs to review it before merge. ## Checklist - [x] I have read [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) - [ ] If I wrote a new docs topic or edited an existing topic, I used the `/write-the-docs` or `/edit-the-docs` skill (N/A, no docs changes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0171Mk7SiKYLDDoAQvbDYfeK --- _Generated by [Claude Code](https://claude.ai/code/session_0171Mk7SiKYLDDoAQvbDYfeK)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |