mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
## Summary
Fixes two dashboard UI bugs surfaced while auditing the project area for
large user-visible issues:
1. **Webhook detail page completely broken** — the page shows a blank
screen because the SvixProvider token was being set to the string
`"[object Object]"`.
2. **Editing a trusted domain with an `http://` base URL silently
upgrades it to `https://`** — saving the edit dialog without changing
anything changes the protocol, breaking callbacks to the original host.
Both are corrected with minimal, targeted changes in the dashboard app.
No API, schema, or shared package changes are required.
---
## Bug 1 — Webhook detail page crashes because `svixToken + ''` yields
`"[object Object]"`
### Where
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/[endpointId]/page-client.tsx`
### Root cause
`stackAdminApp.useSvixToken()` returns an object of shape `{ token:
string, url: string | null }` (see
`packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts`).
The page was doing:
```ts
const svixToken = stackAdminApp.useSvixToken();
const [updateCounter, setUpdateCounter] = useState(0);
// This is a hack to make sure svix hooks update when content changes
const svixTokenUpdated = useMemo(() => {
return svixToken + '';
}, [svixToken, updateCounter]);
// …
<SvixProvider token={svixTokenUpdated} …>
```
`svixToken + ''` coerces the object to the string `"[object Object]"`,
which is then passed to `<SvixProvider>` as the auth token. Every nested
Svix hook (`useEndpoint`, `useEndpointSecret`,
`useEndpointMessageAttempts`) authenticates with that bogus token, gets
a `401 {"code":"authentication_failed","detail":"Invalid token"}` from
Svix, and `getSvixResult`
(`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/utils.tsx`)
throws, crashing the page.
Additional notes while in there:
- `setUpdateCounter` was declared but never called anywhere, so the
surrounding `useMemo`/`useState` was dead weight as well as broken.
Removing it removes the dead code too.
- The neighbouring list page (`webhooks/page-client.tsx`) already uses
the correct shape (`svixToken.token`, `svixToken.url`), which is why the
list page rendered correctly while the detail page didn't.
### Fix
Pass `svixToken.token` directly to `<SvixProvider>` and drop the unused
counter/memo.
```ts
export default function PageClient(props: { endpointId: string }) {
const stackAdminApp = useAdminApp();
const svixToken = stackAdminApp.useSvixToken();
return (
<AppEnabledGuard appId="webhooks">
<SvixProvider
token={svixToken.token}
appId={stackAdminApp.projectId}
options={{ serverUrl: getPublicEnvVar('NEXT_PUBLIC_STACK_SVIX_SERVER_URL') }}
>
<PageInner endpointId={props.endpointId} />
</SvixProvider>
</AppEnabledGuard>
);
}
```
### Reproduction (before fix)
1. Enable the Webhooks app on a project.
2. Create an endpoint with any URL.
3. Open the row's action menu and click **View Details**.
4. The page renders blank (Svix hooks throw 401 Invalid token; the error
boundary unmounts the detail tree). URL, Description, Verification
Secret, and Events History never appear.
### Before / After
| Before | After |
| --- | --- |
| 
| 
|
---
## Bug 2 — Editing an `http://` trusted domain silently upgrades it to
`https://`
### Where
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/domains/page-client.tsx`
### Root cause
In `EditDialog`, the form's `defaultValues` always set `insecureHttp:
false`, regardless of the protocol of the domain being edited:
```ts
defaultValues={{
addWww: props.type === 'create',
domain: props.type === 'update' ? props.defaultDomain.replace(/^https?:\/\//, "") : undefined,
handlerPath: props.type === 'update' ? props.defaultHandlerPath : "/handler",
insecureHttp: false, // ← ignores the existing protocol
}}
```
The `domain` field strips `http(s)://` for display but the protocol
itself is only tracked through the `insecureHttp` switch, which lives
inside the collapsed-by-default **Advanced** accordion. On submit:
```ts
const protocol = values.insecureHttp ? 'http://' : 'https://';
const baseUrl = protocol + values.domain;
```
So an `http://myapp.test` entry reopens with `insecureHttp: false`, the
Advanced section stays collapsed, the user sees nothing wrong, and
hitting **Save** (even with zero visible changes) writes
`https://myapp.test` back to config. Existing redirects from SSO / email
verification flows that depend on the original `http://` host stop
working.
### Fix
Derive `insecureHttp` from the existing `defaultDomain` when editing:
```ts
insecureHttp: props.type === 'update' ? props.defaultDomain.startsWith('http://') : false,
```
This makes the switch in the Advanced panel pre-check itself correctly
and the submit path emits the preserved protocol.
### Reproduction (before fix)
1. Go to **Project Settings → Trusted Domains**.
2. Add a new domain, expand **Advanced**, toggle **Use HTTP instead of
HTTPS** on, enter `myapp.test`, click **Create**. The list now shows
`http://myapp.test`.
3. Click the row's **⋯ → Edit**, then **Save** without changing
anything.
4. Observe the list now shows `https://myapp.test`.
### Before / After
**Domain list after an edit+save:**
| Before (http silently became https) | After (http preserved) |
| --- | --- |
| 
| 
|
In the "before" screenshot, `http://myapp.test` was edited with no
changes and silently became `https://myapp.test`.
`http://www.myapp.test` (not edited) stayed `http://`, confirming the
bug is triggered only through the edit-save path.
**Edit dialog (Advanced expanded):**
| Before (HTTP switch always off) | After (reflects stored protocol) |
| --- | --- |
| 
| 
|
The "after" dialog also shows the protocol prefix label flip from
`https://` to `http://` next to the input — a second visual cue that the
user is editing an HTTP domain.
---
## Scope / out of scope
In scope here:
- The two fixes above, plus a small amount of dead-code cleanup adjacent
to the first fix (the unused `updateCounter` / `useMemo` hack).
Intentionally **not** included (tracked separately from the same audit —
see internal notes):
- Cursor pagination cache wipe across Users/Teams/Transactions tables
(`data-table/common/cursor-pagination.tsx`)
- Email Outbox "Scheduled At" input being reset on every keystroke and
rendered in the wrong timezone (`email-outbox/page-client.tsx`)
- Latent empty-group handling in the sign-up rule builder (validator +
CEL emitter), which is real in code but not currently reachable through
the editor UI
These are broader and deserve their own PRs.
## Test plan
- [ ] **Bug 1 (webhook detail):** Enable Webhooks on a project, create
an endpoint, open **View Details**. Confirm URL, Description,
Verification Secret, and Events History render (no 401s in the console,
no blank page). Confirm the Copy button on the verification secret still
copies the key.
- [ ] **Bug 2 (domain edit preserves http):** Add an `http://` trusted
domain. Edit it and save with no changes — list should still show
`http://`. Edit again, flip the Advanced switch to HTTPS, save — list
should show `https://`. Repeat with the inverse direction (start https,
flip to http).
- [ ] **Regression sweep:** Webhooks list page, create/delete endpoint,
copy signing secret; Trusted Domains add/delete; auth-methods callbacks
against an `http://localhost` domain continue to work.
- [ ] `pnpm typecheck` passes locally. (`pnpm lint` was also run against
the dashboard app and is clean.)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Domain editing now correctly initializes and preserves the protocol
type (HTTP or HTTPS) based on the existing domain configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| .env | ||
| .env.development | ||
| .eslintrc.cjs | ||
| .gitignore | ||
| .npmrc | ||
| components.json | ||
| DESIGN-GUIDE.md | ||
| instrumentation-client.ts | ||
| LICENSE | ||
| next.config.mjs | ||
| package.json | ||
| postcss.config.js | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vitest.config.ts | ||