[pull] master from supabase:master - #1207
Merged
Merged
Conversation
…tester (#49650) <!-- ccr-slack-attribution --> _Requested by **Kalleby Santos** · [Slack thread](https://supabase.slack.com/archives/C0AQ3UHCCKW/p1787840441551609?thread_ts=1787840441.551609&cid=C0AQ3UHCCKW)_ **Before:** you deploy the editor's default template ("Deploy a new function" → "Via Editor"), which wraps its handler in `withSupabase({ auth: ["publishable", "secret"] })`. You click **Test** and get `401 {"message":"Invalid credentials","code":"INVALID_CREDENTIALS"}` — from the function's own middleware, with an empty Headers section. Studio was quietly setting `Authorization` to a legacy `service_role` JWT (and, before that, to your dashboard session token), routed through a private `x-test-authorization` header that the proxy route renamed to `Authorization`. A legacy JWT is neither a publishable nor a secret key, so the middleware rejected it. Pasting your own `Authorization` row did not help: the route overwrote it unconditionally. On a project with legacy keys disabled there was no `service_role` key at all and the literal string `Bearer undefined` went out. **After:** the tester sends your publishable key on the `apikey` header, where new-format keys belong, and never generates an `Authorization` header. `Authorization` only ever comes from your own header rows — typed by hand, or prefilled for you by the role selector. The editor's default template works on the first click, a header you paste is actually sent, and an **Add secret key** action in the "Add header" dropdown gives you one-click access to a secret key, the same affordance the database webhooks and cron job screens already have. **How:** header construction moves into `buildEdgeFunctionTestHeaders` (`EdgeFunctionTesterSheet.utils.ts`), which sets `Content-Type` and `apikey` and then applies the user's rows last. The `x-test-authorization` hop is gone from both the component and `pages/api/edge-functions/test.ts`; the route now forwards the supplied headers as given. Both sides merge on the lowercased header name, so a row typed `authorization` or `apikey` replaces the generated one instead of sitting beside it and being comma-joined by `fetch`. The Headers and Query Parameters sections now use the shared `KeyValueFieldArray`, which is what makes `buildEdgeFunctionHeaderAddActions` reusable here. ## 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? Fixes #42755. - `EdgeFunctionTesterSheet.tsx` sent the legacy `service_role` JWT (or a role-impersonation JWT) as the value of `x-test-authorization` on every request, plus the dashboard session access token as `Authorization`. - `pages/api/edge-functions/test.ts` then overwrote `Authorization` with `x-test-authorization` whenever that header was present, discarding any `Authorization` the user had entered. - No `apikey` header was ever sent, so `withSupabase` in `publishable` or `secret` auth mode — the modes used by the editor's own templates — could never succeed. - Header merging was case-sensitive on both sides of the proxy, so a row typed in the conventional lowercase form produced two entries that `fetch` comma-joined into one malformed value. - The API keys query did not pass `reveal: true`, unlike the webhooks and cron job UIs. ## What is the new behavior? - `apikey` carries the publishable key, falling back to the legacy `anon` key. This mirrors the example snippets on the function details page, which already prefer `publishableKey ?? anonKey`. Defaulting to the least-privileged key means a secret key is only ever sent when the user explicitly adds it. - `Authorization` is never generated. The `useSessionAccessTokenQuery` call is removed from this component entirely — the dashboard user's own session token has no business being forwarded to a project's function. - `x-test-authorization` is removed from both files. The proxy route stays, because it is what reads the raw upstream response for the response panel (`redirect: 'manual'`, full status/header/body capture), keeps the request off the browser's CORS path, and holds the `isValidEdgeFunctionURL` guard and the local-dev URL rewrite. Only the header rewriting is gone. - Role impersonation keeps working, but as a visible, editable `Authorization` row rather than a hidden injected header, so what is sent is always what is displayed. Two details worth reviewing: the selector tracks the value it last wrote, so clearing the role removes only that row and leaves an `Authorization` row you typed by hand alone; and an incrementing request id discards a JWT that resolves after a newer role has already been picked. - Headers merge case-insensitively, user rows winning. - `reveal: true` is passed on the API keys query, matching `Database/Hooks/HTTPHeaders.tsx`. ## Additional context **Relationship to #47159.** #47159 identified the same root cause independently and got the important part right: the key belongs on `apikey`, and neither the legacy service-role JWT nor the dashboard session token should be forwarded. Its extraction of a testable header builder is a good shape, and this PR keeps it — including the spirit of its test suite. The differences are in scope rather than direction. This PR also removes the `x-test-authorization` hop and the route's unconditional `Authorization` overwrite (#47159 leaves the route untouched); drops the remaining legacy service-role fallback rather than keeping it for projects without a publishable key; adds `reveal: true`, secret-key support and the shared "Add secret key" affordance; and normalizes header casing for every header rather than only `x-test-authorization`. Whether to land that PR first and layer this on top, or take this one, is the maintainers' call — either way the credit for spotting it belongs there too. **Overlap with #48143.** That open PR fixes the same case-sensitivity defect for `Content-Type` in these two files. It is not addressed separately here, but the case-insensitive merge in this PR covers `Content-Type` as a side effect, so the two will conflict textually. Happy to rebase on whichever lands first. **A note on `verify_jwt`.** The gateway creates a temporary token when `apikey` is present, so `verify_jwt` does not affect this path and a request with `apikey` and no `Authorization` reaches the function normally. No deploy defaults are changed here. **Compatibility.** One behaviour gets worse and is worth an explicit decision: a function that expects a legacy JWT on `Authorization` used to "just work" in the tester because Studio injected the service-role key. It now needs an `Authorization` row, which the **Add secret key** action produces in one click — the shared helper already emits an `Authorization: Bearer` row for legacy-format keys. Projects with legacy keys disabled strictly improve: they used to receive `Bearer undefined`. Functions using `auth: "user"` are unchanged — the tester never had a real end-user JWT, only the impersonation token. ## Testing `apps/studio` dependencies could not be installed in the environment this was written in (`pnpm install` fails on a 403 from `npm.jsr.io`), so `vitest`, `tsc --noEmit` and `eslint` were not run. What was run instead: - Prettier with the repo's config, including `@ianvs/prettier-plugin-sort-imports`: clean on all five files. - `tsc` parse of the changed files: no syntax or type errors beyond pre-existing unresolved-module noise. - Both new test suites transpiled and executed as plain Node assertions: 7/7 for `buildEdgeFunctionTestHeaders`, 4/4 driving the API route handler with a stubbed `fetch`. Please run the real suites in CI. `pnpm --filter studio exec vitest --run tests/components/Functions/EdgeFunctionTesterSheet.utils.test.ts tests/pages/api/edge-functions/test.test.ts` covers the added tests. A component-level test of the impersonation prefill is not included and would be a reasonable follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Kalleby Santos <105971119+kallebysantos@users.noreply.github.com>
…49781) Hides the "Export Metrics to your dashboards. Get started for free!" banner (`ObservabilityLink`) for High Availability (Multigres) projects — the Metrics API it links to is not available for them. The check lives inside the shared component, so it applies to every observability sub-page that renders the banner; non-HA projects are unchanged. Addresses [MUL-1346](https://linear.app/supabase/issue/MUL-1346/database-observability-dashboard-remove-text-for-unsupported-feature). ## To test - On an HA project: open Observability → Database (and any other observability sub-page, e.g. Auth) — the "Export Metrics to your dashboards" banner at the bottom of the page should be gone - On a non-HA project: same pages — the banner still shows, with "Get started for free!" linking to the metrics docs <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Metrics export links are now hidden for High Availability projects, where the Metrics API isn’t available. * Existing metrics export functionality remains unchanged for supported projects. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Adds "Multigres" to the "Which services are affected?" multi-select on the Contact Support form, so Alpha customers can tag tickets for the Multigres Front inbox. Placed alphabetically between Edge Functions and Realtime; the later option ids are renumbered, which is safe — they're only used as React list keys, and the submit payload sends the lowercase value tokens (`affectedServices: "multigres;..."`, verified with a live submission). Addresses [FE-4273](https://linear.app/supabase/issue/FE-4273/add-multigres-to-support-form-services). Front-inbox routing itself is Platform-side (SUPPORT-421) and should match on the token `multigres`. ## To test - Open /support/new → "Which services are affected?" → Multigres appears between Edge Functions and Realtime, selectable alongside other services, and the combobox search finds it - Submit a ticket with it selected → the POST to `/platform/feedback/send` carries `affectedServices` containing `multigres` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Multigres as a selectable service option in the support interface. * Updated service ordering to accommodate the new option. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…g boot (#49786) While an HA (Multigres) project is provisioning, the `/ha-admin` topology endpoints fail or return an empty topology as a matter of course — the cluster topology diagram rendered that as a hard "Failed to retrieve cluster topology" error (or the "Cluster topology unavailable" contact-support state). The diagram now checks the project status and, while the project is building (`COMING_UP`/`UNKNOWN`, same pair `ProjectLayout` treats as booting), shows a "Setting up project" empty state instead. Both the project-detail query (self-polls while booting) and the ha-admin queries (30s interval) keep refetching, so the diagram appears on its own once boot completes. Once the project is past provisioning, genuine errors and the empty-topology state surface exactly as before. <img width="1491" height="769" alt="mul1475-setting-up-state-wide" src="https://github.com/user-attachments/assets/3f095634-9939-41cd-88c3-0849cbad7374" /> **Added:** - Component tests for the four states: booting + error, booting + empty (→ setup state), running + error (→ error alert), running + empty (→ unavailable state) Addresses [MUL-1475](https://linear.app/supabase/issue/MUL-1475/polish-infra-diagram). ## To test - On an HA project mid-provisioning (or simulate: dev toolbar project-status override → `COMING_UP`, with `/ha-admin` requests failing), open Settings → Infrastructure — the topology panel shows "Setting up project" with a spinner, not the error alert - On a healthy HA project, the topology diagram renders as before; if `/ha-admin` genuinely fails there, the error alert still shows - `pnpm --filter studio exec vitest run components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/HaInstanceConfiguration.test.tsx` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a “Setting up project” state while projects are provisioning or their status is unavailable. * Prevents premature topology errors or unavailable messages during project setup. * Added accessible status announcements for loading and setup-state transitions. * **Bug Fixes** * Active projects now correctly display topology errors when cluster health data cannot be retrieved. * Healthy responses with no topology data display an appropriate unavailable state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…rod (#49787) `getHighAvailabilityRegionCode` had `staging` and `local` cases but no `prod` case, so it returned `undefined` in production and the High Availability region filtering never applied — the creation flow offered every AWS region while HA Alpha is only live in `us-east-1`. Adds the `prod` case returning `us-east-1`, matching staging. The region filter and the "High Availability projects are currently limited to…" banner both key off this value, so no other changes are needed. This keeps the accepted hardcoded-per-environment pattern for Alpha; the API/entitlement-driven enabled-regions design stays open on the ticket for when the rollout expands. **Changed:** - `ProjectCreation.utils.ts` — `prod` → `us-east-1` - `ProjectCreation.utils.test.ts` — the two env tables previously pinned prod as unrestricted; now expect `us-east-1` Addresses [FE-3716](https://linear.app/supabase/issue/FE-3716/show-enabled-regions) (and the folded-in MUL-1337). ## To test - `pnpm --filter studio exec vitest run components/interfaces/ProjectCreation/ProjectCreation.utils.test.ts` - In prod after deploy: new project → toggle High Availability → region select offers only East US (North Virginia) and shows the "limited to" notice; staging/local behavior unchanged (only the `prod` env branch changed) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - High-availability project creation now correctly uses the `us-east-1` region for production environments. - Region selection is now properly restricted to `us-east-1` when creating production projects. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…rives (#49792) In the Realtime Inspector, the messages view — which holds every "Broadcast a message" entry point — only rendered once at least one message had been received. With only Broadcast enabled and no inbound traffic, the page stayed on the "Create realtime experiences" onboarding forever, so there was no way to send a broadcast at all. The render gate now keys off a channel being set rather than `logData.length`: once you join a channel, `MessagesTable` renders and its existing empty states provide the send entry points ("Listening • No message found yet…" toolbar + the "No Realtime messages found" panel). The onboarding remains the pre-channel state. Addresses [FE-4278](https://linear.app/supabase/issue/FE-4278/realtime-inspector-cant-send-broadcast-without-incoming-messages). ## To test - Realtime → Inspector, before joining a channel: the "Create realtime experiences" onboarding still shows - Join a channel (with Presence off / Broadcast only so nothing arrives): the listening view renders immediately with "No message found yet…" and "Broadcast a message" in both the toolbar and the empty-state panel — previously this was stuck on the onboarding - Click "Broadcast a message" and send with defaults: the broadcast appears in the grid - Stop listening: no crash, messages retained <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * The Realtime Inspector now displays the messages view as soon as a channel is selected. * Empty-state guidance, including the option to broadcast a message, is now available before any messages arrive. * Pre-channel onboarding remains visible until a channel is configured. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
## Problem Workers Secrets was merged in #49589 into the stacked jordi/workers-detail branch. The parent Workers PR reached master without that child merge, leaving the page absent from staging. ## Fix Cherry-pick the missing Workers Secrets route, menu item, shared-secret copy, and generated route tree onto current master. The page uses the existing workers flag and permission gates. ## How to test - Enable the workers flag for a project with Workers access. - Open Workers, then select Secrets. - Expected result: the shared project secrets page renders at /project/:ref/workers/secrets and is not treated as a worker named secrets. - Add, edit, or delete a secret, then confirm the same value appears under Edge Functions, Secrets. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a **Secrets** page to the Workers section. * Added navigation to Worker secrets from the Workers menu. * Displayed default secrets and deployment-specific guidance where applicable. * Clarified that platform secrets are shared between Edge Functions and Workers. * Updated deletion warnings to reflect shared secret usage. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…#48044) ## Summary Studio's Advisor UI reads lint metadata from a fixed `lintInfoMap`, not from the API response. A lint name missing from that map shows a blank title, no icon, no filter checkbox, and no remediation link. This PR adds a `pitr_archiving_stale` entry to `lintInfoMap`, copied from the existing `pitr_not_enabled` entry, so the new lint renders correctly in the Advisor UI. ## Dependencies > [!WARNING] > [supabase/platform#35862](supabase/platform#35862) defines the `pitr_archiving_stale` lint. Until it merges, the API never sends this lint name, so the Advisor grid and the public `/v1/projects/{ref}/advisors/security` response never show the new row -- but the Security Rules page (`/project/<ref>/advisors/rules/security`) renders one row per `lintInfoMap` entry regardless of the API, so this PR's new row appears there immediately, before the backend lint exists. See Details for what that means in the gap between merges. --- <details> <summary>Details</summary> - A lint name missing from `lintInfoMap` has these effects: - The grid row shows a blank title and no icon. There is no fallback to the API's own `title`. - The row has no filter checkbox. Filter options come from `lintInfoMap`, not from the API. - The row has no lint-specific remediation link. The "Learn more" link falls back to the generic database-linter page. - The row does not appear in the Advisor Rules enable/disable list. - The new `pitr_archiving_stale` entry copies the existing `pitr_not_enabled` entry's `link`, `docsLink`, and `category`, and uses a new `title` matching [supabase/platform#35862](supabase/platform#35862 lint definition verbatim. Its `name` also matches that lint definition exactly. - **Known gap, until the backend PR merges:** `AdvisorRules` (`components/interfaces/Advisors/AdvisorRules.tsx`) filters `lintInfoMap` by `category` alone, with no dependency on the API returning the lint -- so this entry makes a "PITR archiving may be broken" row appear on the Security Rules page for every project right away, ahead of the backend lint actually existing. From that row, a user can open `CreateRuleSheet` and submit a disable rule, which `POST`s `lint_name: 'pitr_archiving_stale'` to the notification-exceptions endpoint. That name is not yet in the generated `CreateNotificationExceptionsBody` enum (`packages/api-types/types/platform.d.ts`), so the request either errors or stores an exception keyed to a lint name nothing will ever match, until api-types regenerates after the backend PR ships. This window closes on its own once [supabase/platform#35862](supabase/platform#35862) merges; accepted as a short-lived tradeoff rather than gating this PR on merge order or adding code to hide the row until then. - The docs anchor (`#point-in-time-recovery`) explains what PITR and WAL-G archiving are. It does not explain how to fix a stale or broken archive. That content does not exist yet in either pull request. INDATA-1149 tracks this as a follow-up. - `packages/api-types/types/platform.d.ts` is a generated file. This repo's own CLAUDE.md says never to hand-edit it. The file does not list `pitr_archiving_stale` yet, because it regenerates only after the backend lint ships and `pnpm api:codegen` runs. Until then, `LintInfo['name']` stays a plain `string`. If someone misspells the new entry's `name`, the code still compiles and the tests still pass. At runtime, the icon and docs link fall back silently instead of failing a build. Once [supabase/platform#35862](supabase/platform#35862) merges and api-types regenerates, `LintInfo['name']` must tighten to the generated `LINT_TYPES` union. This closes the gap for every lint entry, not only this one. </details> --- <details> <summary>Testing</summary> - `pnpm --filter=studio test Linter.utils.test.tsx` (17 passed, including a test that asserts the `pitr_archiving_stale` entry's shape) - `pnpm typecheck --filter=studio` (clean) - `pnpm exec eslint` on the touched files (clean; the `pnpm lint --filter=studio` turbo wrapper itself errors on this machine with an unrelated JSON-parse failure -- a tool-invocation issue, not a lint finding) - `prettier --check` on the touched files - The `docsLink` assertion (`toContain('/guides/platform/backups#point-in-time-recovery')`) is domain-agnostic by construction, so it holds regardless of which `NEXT_PUBLIC_DOCS_URL` value is set -- no test in this file overrides that variable, this is a property of the assertion's own shape, not a scenario the suite exercises </details> --- <details> <summary>Misc</summary> - Part of INDATA-979 - Changelog: [supabase/changelog#192](supabase/changelog#192) </details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )