mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
600a0d6fcf
879 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
03e7b61308 | chore: update package versions | ||
|
|
bf8d0ece28 | chore: update package versions | ||
|
|
4ff24dea9b | chore: update package versions | ||
|
|
e42ec65c88
|
Payments app design fixes (#1375)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> ## Summary This PR brings the Payments dashboard surfaces in line with the shared design system: product creation, product-line / included-item dialogs, auth-method toggles, payments empty states, and related layout polish. Dialogs migrate from raw shadcn `Dialog` to `DesignDialog` with consistent headers, footers, inputs, and selector dropdowns. **Base:** `dev` → **Head:** `Payments-app-design-fixes` **Scope:** 31 files, ~+1.4k / −1.3k lines **Captured on:** local dev server (`internal` project), signed in as `admin@example.com` ## Screenshots Captured from `http://localhost:8101` (viewport: **1920×1200** standard, **2560×1440** widescreen). Assets hosted in [this gist](https://gist.github.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf). > Red outlines on the **after** shots mark the new or changed UI introduced by this PR. ### Create Product — payments form redesign | | Before | After | | --- | --- | --- | | Light |  |  | | Dark |  |  | Widescreen: | | Before | After | | --- | --- | --- | | Light |  |  | | Dark |  |  | ### Product Lines onboarding — vertical centering fix | | Before | After | | --- | --- | --- | | Light |  |  | | Dark |  |  | ### Create Product Line dialog — `DesignDialog` migration | | Before | After | | --- | --- | --- | | Light | *(legacy shadcn dialog on `dev` — open via Product Line → Create new)* |  | | Dark | |  | ### Auth Methods — toggle row accessibility | | Before | After | | --- | --- | --- | | Light |  |  | | Dark |  |  | ### Other migrated surfaces (after only) | Page | Light | Dark | | --- | --- | --- | | Payments settings |  |  | | Sign-up rules |  |  | | Projects list (Create Project button) |  |  | | Playground / DesignDialog |  |  | | Included Item dialog |  |  | ### Scroll behaviour — Sign-up Rules | | Light | Dark | | --- | --- | --- | | Scroll |  |  | ## What's new - **`DesignDialog`** extended with `customHeader`, `noBodyPadding`, and section `className` hooks; Playground updated to showcase them. - **Payments dialogs** (`CreateProductLineDialog`, `IncludedItemDialog`, price edit, item dialog) migrated to design-system components. - **Create Product** page uses `DesignButton`, `DesignInput`, `DesignSelectorDropdown`, and refreshed header actions. - **Auth Methods** toggle rows use semantic `<Label htmlFor>` instead of click-capture divs. - **Payments layout** empty-state card centers correctly; product-lines onboarding slideshow vertically centers. - **Backend** seed invariant for Growth product price; removed unused import in product switch route. ## Notes for reviewers - Dialog migrations preserve validation + async error handling (`runAsynchronouslyWithAlert` where applicable). - Included-item dialog uses a sentinel value for “Create new item” to avoid colliding with real item IDs. - `packages/stack` / `packages/js` are untouched; template + dashboard-ui-components carry SDK-facing dialog changes. ## Test plan - [x] Visual capture on `internal` project (`admin@example.com`) — light/dark, standard + widescreen - [ ] Create product flow: customer type → product line dropdown → create line dialog - [ ] Add included item dialog from create/edit product - [ ] Auth Methods toggles (label click + switch) - [ ] Payments product-lines onboarding slideshow at varied viewport heights - [ ] `pnpm typecheck` / `pnpm lint` / targeted E2E if API surface changed --------- Co-authored-by: nams1570 <amanganapathy@gmail.com> Co-authored-by: mantrakp04 <mantrakp@gmail.com> Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com> |
||
|
|
be2ad595ad
|
fix: checkout flow for 0 dollar subscription (#1465)
### Context There was a small bug via dashboard checkout flow where it would fail on trying to create a checkout flow for a free product subscription because no client secret is generated for a 0 dollar subscription. ### Summary of Changes The flow should be fine now. There's special carve out logic for it. That being said, users attempting to mimic a free plan grant are encouraged to follow the `ensureFreePlan` pattern. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Free subscription selections now bypass Stripe payment processing, streamlining checkout for zero-cost offerings. * Purchase return flow now properly recognizes and activates free subscriptions without requiring payment confirmation. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1465?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3b2e991c78
|
Fix browser compatibility: guard requestIdleCallback and startViewTransition (#1464) | ||
|
|
b8fc04bdbd
|
feat: link Stack Auth projects to GitHub and push config from the dashboard (#1450)
End-to-end flow for managing Stack Auth config via GitHub: link a repo
during onboarding, edit settings in the dashboard, and have the change
committed to your repo + synced back via a GitHub Actions workflow.

## What this adds
- **CLI** — `stack config push --source github --source-repo
--source-path --source-workflow-path`. Records the source on the config
row so the dashboard knows where the file lives. Reads `GITHUB_SHA` /
`GITHUB_REF_NAME` for commit + branch.
- **Onboarding "Link existing project"** — searchable repo/branch
comboboxes, auto-detects candidate `stack.config.{ts,js}` paths, writes
`STACK_AUTH_PROJECT_ID` + `STACK_AUTH_SECRET_SERVER_KEY` secrets, and
commits a generated workflow YAML that re-runs `stack config push` on
every change to the config file.
- **Dashboard "Push to GitHub" dialog** — replaces the prior TODO
buttons. Pre-flights `repo`+`workflow` scopes on the user's GitHub
connection; if missing, the button flips to "Reconnect with GitHub". On
push, commits the dashboard's edit straight to the linked repo/branch
via the Contents API (with `cache: "no-store"` to dodge GitHub's 60s GET
cache so consecutive pushes don't 409). Suspense boundary scoped to the
dialog body so opening it doesn't blank the dashboard.
- **Project settings** — surface the linked workflow file as a clickable
GitHub link when the source carries `workflow_path`.
## Test plan
- `pnpm lint` (29/29) ✓
- `pnpm typecheck` (29/29) ✓
- `pnpm --filter @stackframe/stack-cli test` (111/111) ✓
- Dashboard vitest on the three relevant files
(`link-existing-onboarding-workflow`, `github-api`,
`github-config-push`) — 37/37 ✓
- Live end-to-end: `BilalG1/lex-lookup` linked to a local dev project;
passkey toggled, push committed `0bb958bd`
([commit](
|
||
|
|
002692e519
|
Compress oversized images client-side in AI chat (#1456) | ||
|
|
0e85b05c3d
|
[Fix]: Payments App Sundry Fixes (#1455)
### Summary of Changes
You can now edit items on a product view.
The "Make free" button is less obtuse, and it clearly tells you what
it's going to do.
Additionally, we found out while working on this PR that you cannot
create a `paymentIntent` on stripe that is < 0.5$. So, you can't create
an OTP for a "free" product. We add safeguards to protect against that.
Also, 0 dollar subscriptions don't create a subscription invoice.
Additionally, the old code relied on being able to fetch the stripe
client secret, which would be null for a 0 dollar subscription so we
create a carve out.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Better free-product checkout handling: $0 subscriptions return an
empty success response without a payment client secret; non-free
subscriptions include client secret when needed.
* UI: “Make free” flow, “Free · {amount}” with price ID, per-price
checkout error indicators/tooltips, and an alert for products with
invalid prices.
* Client- and server-side Stripe one-time minimum checks.
* **Bug Fixes**
* Included-item dialog now resets form state when opened to avoid stale
values.
* **Documentation**
* OpenAPI: clarified client_secret may be omitted when no customer
confirmation is required.
* **Tests**
* Added end-to-end tests covering $0 purchase-session flows.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1455?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
01aacd2dd4
|
fix(dashboard): repair and polish the GitHub link-existing project flow (#1441)
Rework of the **new-project → Link Existing Config** flow on the dashboard, plus the published `stack-cli` it depends on. The starting point on `dev` had the link-existing flow effectively broken end-to-end (the generated GitHub workflow could never authenticate, and the GitHub-account selection UI dead-ended in several states). This PR fixes the blockers, polishes the local-CLI path, and adds a searchable repo/branch picker. --- ## What was broken | Severity | Issue | Fixed in | |---|---|---| | 🔴 | Generated workflow omitted the required `--cloud-project-id` flag → every run failed at Commander before the action ran. | `d0e6ad15f`, `55ff7e319` | | 🔴 | Workflow exported `STACK_PROJECT_ID` env var the CLI never read. | `55ff7e319` (CLI now reads it; workflow drops the explicit flag) | | 🔴 | `pnpx` isn't on `ubuntu-latest` → step failed with `command not found`. | `65789a1ac` | | 🔴 | "No connected GitHub account found" alert with **no Connect button**. | `d0e6ad15f` | | 🟠 | "Connect new" used `getOrLinkConnectedAccount` (get-or-link) → silently returned the existing account instead of starting a fresh OAuth flow. | `d0e6ad15f` | | 🟠 | `workflow_dispatch` 404s on non-default branches; threw before advancing to the logs step even though the push-triggered run worked. | `d0e6ad15f` | | 🟠 | Config-path suggestions prepended `./`, which breaks GitHub's `on.push.paths` filter — ongoing config edits never re-triggered the workflow. | `d0e6ad15f` | | 🟡 | Account selector briefly showed the numeric `providerAccountId` before the GitHub `/user` fetch populated the username. | `de9ec1923` | | 🟡 | Repository / branch dropdowns capped at 100 entries with no search. | `7550eaacb` | ## What changed ### Dashboard — Link Existing Config flow - **Local CLI step rebuild** (`ed25eabf9`, `ebb090e5b`): split into separate "Sign in" and "Push config" code blocks using the shared `CodeBlock` component (copy button built-in), added a `npx / pnpx / bunx` runner pill toggle (default `npx`), moved `--config-file <path>` to the end of the push command so users can copy everything up to the placeholder, trimmed redundant helper text. - **GitHub OAuth states** (`d0e6ad15f`, `de9ec1923`): empty-state "Connect GitHub account" button; "Connect new" now uses `linkConnectedAccount` so it actually starts OAuth; loading row instead of `providerAccountId` flash. - **Searchable repo + branch combobox** (`7550eaacb`, `5ce1b6bd9`): new `RemoteSearchCombobox` (Popover + cmdk, same pattern as `data-table/faceted-filter`), debounced GitHub `/search/repositories` and `/git/matching-refs/heads/{prefix}` calls so users with > 100 repos/branches can find any of them. Branch "Refresh" button removed — branches auto-load on repo select. - **Workflow generator** (`d0e6ad15f`, `65789a1ac`): config paths normalised (strip leading `./`); workflow uses `actions/setup-node@v4` + `npx --yes`; `workflow_dispatch` failure is now best-effort (the workflow-file commit's push event triggers the run on any branch). ### Stack CLI - `STACK_PROJECT_ID` env-var fallback for `--cloud-project-id` (`55ff7e319`). Both `config push` and `config pull` are affected; explicit flag still wins. New `resolveProjectId` helper in `lib/auth.ts` with 5 unit tests (`auth.test.ts`). ### Misc - `2faffb662` drops an unused `useTransition` wrapper around a `setProjectStatuses` Map insert in the new-project flow. --- ## Release ordering note The generated workflow's `run:` line **no longer passes `--cloud-project-id`** — the CLI reads `STACK_PROJECT_ID` from env instead. This means a workflow generated by this branch only works against a `@stackframe/stack-cli` published with the env-var fallback from `55ff7e319`. The CLI and dashboard ship from the same monorepo so this should be a non-issue in the normal release cadence, but worth confirming the CLI publishes alongside the dashboard deploy. Existing workflows already committed in user repos still have the explicit flag and continue to work unchanged. ## Validation - `pnpm --filter @stackframe/dashboard run typecheck` ✅ - `pnpm --filter @stackframe/dashboard run lint` ✅ - `pnpm --filter @stackframe/stack-cli run typecheck` ✅ - `pnpm --filter @stackframe/stack-cli run lint` ✅ - `pnpm --filter @stackframe/stack-cli test` ✅ (14 tests; 5 new for `resolveProjectId`) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Searchable repository and branch selection UI for GitHub onboarding * New remote search combobox component for selecting repos/branches * Selectable CLI package runner and dynamic command display during onboarding * **Improvements** * CLI accepts STACK_PROJECT_ID env var; cloud project flag is optional * Workflow generation normalizes/validates config paths, sets up Node.js v20, and uses npx; onboarding dispatch is non-fatal * Hardened repository loading to avoid stale async updates * **Tests** * Added tests covering project ID resolution logic <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1441?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
20b7921b93
|
Fix theme toggle in browsers without View Transitions (#1453)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/hexclave/stack-auth/blob/dev/CONTRIBUTING.md --> Fixes the dashboard theme toggle in browsers that do not support `document.startViewTransition` by falling back to an immediate theme change. Link to Devin session: https://app.devin.ai/sessions/c1f1deed2f1c4d42979df2ee949cf74d Requested by: @madster456 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
055304d3fd
|
Onboarding app redesign (#1370)
# Onboarding app redesign Rolls out a unified dashboard visual language centered on `DesignCard` groupings, a new canonical `DesignDialog`, and an inline live-preview pattern. Touches the project listing, project overview, auth methods, design language, onboarding, and sign-up rules surfaces. Reusable primitives (`DesignCard`, `DesignDialog`, `MethodToggleRow`) replace one-off layouts, and the project card now leads with **total users + 30-day signups** instead of a weekly-users tile. **Base:** `dev` → **Head:** `onboarding-app-redesign` > Red outlines on the "after" shots highlight the UI that changed in this PR. Empty outlines = layout/chrome change with no data delta. --- ## Flagship: Project listing (`/projects`) Project cards swap the weekly-users widget for a `ProjectUsersMetric` (total user count + 30-day signups sparkline). Hover lifts the card; the metrics row is now part of the card body instead of a footer strip. | | Light | Dark | |--------|-------|------| | Before |  |  | | After |  |  | ## Flagship: Auth methods (`/projects/[id]/auth-methods`) Full restructure: the horizontal `SettingCard` strips are replaced by stacked `DesignCard` sections (Sign-in methods · Sign-up policies · User deletion), with a sticky **live sign-in preview** column on the right. Provider rows become `MethodToggleRow`s with inline configure actions. | | Light | Dark | |--------|-------|------| | Before |  |  | | After |  |  | ## Flagship: Project overview (`/projects/[id]`) Line + donut charts migrate to the shared `AnalyticsChart` component. Referrers list gains a max-height + scroll affordance so it no longer pushes neighbouring tiles off-screen. | | Light | Dark | |--------|-------|------| | Before |  |  | | After |  |  | ## Other migrated surfaces | Surface | Before (dark) | After (dark) | What changed | |---------|---------------|--------------|--------------| | `/projects/[id]/onboarding` |  |  | Email-verification toggle adopts the new `MethodToggleRow` + confirmation `DesignDialog` variant | | `/projects/[id]/sign-up-rules` |  |  | Rule builder rewrapped in `DesignCard`/`DesignAlert`/`DesignButton` primitives | | `/projects/[id]/design-language` |  |  | Adds a `DesignDialog` showcase section so consumers can see the canonical modal styling | | `/playground` |  |  | New `dialog` playground entry exercising the size/variant/icon-chip permutations | Light-mode counterparts for the long-tail surfaces are in the [companion gist](https://gist.github.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7). --- ## What's new - **`DesignDialog`** (`packages/dashboard-ui-components/src/components/dialog.tsx`) — canonical modal with configurable size/variant, optional icon chip, and split header/body/footer regions. Replaces ad-hoc `Dialog` + `DialogContent` usage across the dashboard. - **`MethodToggleRow`** — shared row primitive used by auth-methods and onboarding for "thing with a toggle and an inline configure CTA". - **`ProjectUsersMetric`** — total users + 30-day signups sparkline; powers the new project card metric and reuses the `projects-weekly-users` backend route renamed to `projects-metrics`. - **`action-dialog`** gains `keepOpenOnOutsideInteraction` and `contentClassName` props so variant chrome can ride along through the existing helper. - Backend: new internal `projects-metrics` route + test; `seed-dummy-data.ts` updated to populate the new metric. ## Notes for reviewers - Reusable primitives (`DesignCard`, `DesignDialog`, `MethodToggleRow`) live in `packages/dashboard-ui-components` — please flag any inline duplications you spot. - The auth-methods live-preview only renders at `lg+`. Below that breakpoint the page falls back to the stacked card layout. - The OAuth provider config dialogs adopt the new pill toggle for **Shared keys / Custom OAuth credentials**; the underlying form fields are unchanged. ## Test plan - [ ] `/projects` — verify the metric tile renders both empty-state and populated (Demo Project has 584 users seeded) - [ ] `/projects/[id]/auth-methods` — toggle each method on/off, confirm live preview updates in real time - [ ] `/projects/[id]/auth-methods` — open a provider dialog, switch between Shared / Custom keys, verify form state preserved - [ ] `/projects/[id]/onboarding` — toggle email verification, confirm the confirmation dialog variant - [ ] `/projects/[id]/sign-up-rules` — verify rule builder still saves correctly under the new chrome - [ ] Mobile/`md` breakpoint — auth-methods falls back to stacked layout, no overflow - [ ] Dark mode parity on every flagship surface <sub>Visuals captured via local dev server (`localhost:8101`) on `admin@example.com` seeded account. Red outlines mark new/changed UI on the "after" pass.</sub> --------- Co-authored-by: mantrakp04 <mantrakp@gmail.com> Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com> |
||
|
|
20b029fd81 | Fix build | ||
|
|
90421431ee | chore: update package versions | ||
|
|
6a35289aa7 | Revert upgrades | ||
|
|
2c620aa208
|
Show enabled alpha apps in sidebar and app store (#1449) | ||
|
|
512099ed23
|
Speed up dummy-project seeding (preview create-project ~15s → ~1.3s) (#1437)
## Summary The internal `preview/create-project` endpoint was taking ~15s because `seedDummyProject` created its dummy users one at a time through the full `usersCrudHandlers.adminCreate` CRUD pipeline (one DB transaction + config render per user, ~86 users). This reworks the seeding path to use bulk inserts. End-to-end, the endpoint's server-side handler time drops from **~15,100ms → ~1,300ms** (~11× faster). ## Seeding changes (`seed-dummy-data.ts`) - **`seedDummyUsers` — bulk insert.** Build every row (`ProjectUser`, `ContactChannel`, `AuthMethod`, `ProjectUserOAuthAccount`, `OAuthAuthMethod`, default permissions) up front with pre-generated UUIDs, then insert via one `createMany` per table inside a single transaction — replacing ~86 sequential `adminCreate` transactions. Named-user team memberships are bulk-inserted the same way (`TeamMember` + `TeamMemberDirectPermission`). Idempotency is preserved with a single up-front email lookup, so re-runs against an existing project still skip existing users. - **Native `randomUUID`.** The seed paths now use `node:crypto`'s `randomUUID()` instead of stack-shared's `generateUuid()`. The browser-safe polyfill calls `crypto.getRandomValues` ~31× per UUID (once per template char, each with a fresh `Uint8Array(1)`); generating thousands of seed UUIDs made that ~800ms of pure CPU in the activity-event build alone. - **`seedBulkSignupsAndActivity`.** Skip the redundant back-date `UPDATE` for freshly-inserted users (`createMany` already writes correct `createdAt`/`signedUpAt`), and flush ClickHouse events in larger, parallel batches. - **`seedDummyProject`.** Run `seedBulkSignupsAndActivity` concurrently with the lighter remaining steps, and fold `seedDummyTransactions` into the emails/activity/replays `Promise.all`. - Removed the now-unused `syncSeedUserOauthProviders` helper. The bulk path produces the same rows as the CRUD-handler path (verified row-count equality during development). Webhooks / soft-limit checks are intentionally not fired for seed data, consistent with the rest of the seed. ## Also in this PR — preview-mode 404 fix (`preview-project-redirect.tsx`) While testing the above, the dashboard 404'd right after a preview project was created. In preview mode the `/projects` page renders `PreviewProjectRedirect`, which `POST`s `/internal/preview/create-project` and then `router.push()`es to `/projects/<new-id>` — but it never refreshed the client-side owned-projects cache, so the `[projectId]` route's `useAdminApp()` read a stale list, failed to find the just-created project, and called `notFound()`. Fixed by refreshing the owned-projects cache before navigating, matching what the normal create-project flow in `page-client.tsx` already does. (Pre-existing bug, not caused by the seeding change — but it surfaces the seeding path, so it's bundled here.) ## Testing `pnpm typecheck` and `pnpm lint` pass for both backend and dashboard. The preview endpoint was exercised repeatedly during development (HTTP 200, projects created and populated correctly). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance** * Much faster bulk user and event seeding via larger, parallelized batches and optimized backfilling. * **Refactor** * Dummy data seeding redesigned to be idempotent, deterministic, and bulk-oriented; seeding tasks now overlap where safe. * **Bug Fixes** * Preview project flow validates client capabilities and refreshes the local project list to avoid stale navigation. * Auto-login guarded to run only once to prevent duplicate sign-ins. * **UI/UX** * Walkthrough steps and sidebar behavior improved; walkthrough labels and search keywords updated. * **Chore** * CLI identity command now resolves session authentication more reliably. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1437?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ffbd09dc57
|
Fix flaky tests and preexisting CI failures (#1443) | ||
|
|
97f86a116b
|
Fix globe drag not ending when pointer released outside element (#1447) | ||
|
|
5dbfb1ebab
|
Auth app redesign (#1367)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a reusable DesignDialog modal system (sizes, variants, header/footer/headerContent, trigger/close controls). * Added a documented "roids" skill and pinned it in the skills registry. * **Documentation** * Expanded design guide with comprehensive dialog usage patterns, examples, and props. * **Improvements** * Playground now previews and generates dialog code interactively. * Auth methods and sign-up rules UIs migrated to the new design system. * Action dialogs can opt to ignore outside interactions and accept custom content classes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- ## Summary Two things bundled together: 1. **New `DesignDialog` primitive** in `@stackframe/dashboard-ui-components` — the canonical glassmorphic dashboard modal shell. Exposes configurable sizes (`sm`→`7xl`/`full`), `glassmorphic` vs `plain` variant, optional icon / title / description / footer / custom header slots, and a `DesignDialogClose` companion. Replaces the ad-hoc dialog wrappers scattered across the dashboard. 2. **Auth-app pages migrated onto the design-components system** — `auth-methods` and `sign-up-rules` are rebuilt on `DesignCard` / `DesignAlert` / `DesignButton` / `DesignBadge` / `DesignInput` / `DesignMenu` / `DesignSelectorDropdown` / `DesignDialog`. Live OAuth-page preview frame, glassmorphic confirmation dialogs, and a redesigned rule-builder all live behind these new shells. The design-language catalog page and the `/playground` component explorer were both extended with full dialog showcases so the new primitive has a single discoverable home. **Base:** `dev` → **Head:** `auth-app-redesign` **Scope:** 11 files changed · +2553 / −1151 lines --- ## Screenshots — before and after > Captured locally against `http://localhost:8101` at 1440×900 with a fresh project (`Demo Project`) created via the sign-up + new-project flow. Dev-only overlays (outdated-version banner, console toasts) are hidden via injected CSS for clarity. ### Auth methods — `/projects/<id>/auth-methods` The big page-client rewrite. Before was a flat list of toggleable rows with a live preview pinned to the right. After is a sectioned layout — `SIGN-IN METHODS` and `SSO PROVIDERS` get uppercase subheaders, each method gets a `DesignBadge` icon + description ("Classic email + password credentials.", "One-time codes delivered by email.", "Phishing-resistant device-bound credentials."), and empty states (e.g. SSO with no providers configured) become real call-outs instead of plain rows. | Before (`dev`) | After (this PR) | | --- | --- | |  |  | |  |  | ### Sign-up rules — `/projects/<id>/sign-up-rules` Full rule-builder rewrite (CEL ↔ visual tree round-trip kept intact, just dressed in the new design system). Before's empty state was a flat alert + plain "Default action" row. After uses `DesignCard` variants — `NO RULES YET` with an inline "Add your first rule" CTA, an "If no rules match → Allow sign-up" surface, and a dedicated `TEST RULES` card linking the simulator. | Before (`dev`) | After (this PR) | | --- | --- | |  |  | |  |  | ### Component playground — `/playground` A new **Dialog** entry was added to the component selector. The before shots show `dev` — the selector only listed Button (and a handful of other primitives) and had no Dialog playground at all. The after shots show the new entry: a props panel for `shape` / `size` / `variant` / `title` / `description` / `headerIcon` / `footer` / `topRightClose`, plus an "Open confirmation" button that mounts the live `DesignDialog`. #### Closed (props panel + code preview) | Before (`dev` — no Dialog entry) | After (this PR) | | --- | --- | |  |  | |  |  | > The "before" shots default to the Button playground because the Dialog entry doesn't exist on `dev` — that's the change. #### Open (glassmorphic surface in action) The dialog itself — only available after this PR, so no `dev` equivalent. | Light | Dark | | --- | --- | |  |  | ## What changed - **New** `packages/dashboard-ui-components/src/components/dialog.tsx` — the `DesignDialog` primitive. Props shape: `size` × `variant` × optional `icon` / `title` / `description` / `headerContent` / `customHeader` / `footer` slots, plus `trigger`, `noBodyPadding`, `hideTopCloseButton`, and per-section `*ClassName` escape hatches. Exports `DesignDialog`, `DesignDialogClose`, plus the `DesignDialogSize` / `DesignDialogVariant` / `DesignDialogProps` types. - **Exports** wired through `packages/dashboard-ui-components/src/index.ts` so consumers import from `@stackframe/dashboard-ui-components` or, by extension, the dashboard's local `@/components/design-components` barrel. - **Auth methods page** (`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx`) — full migration. Sign-in methods, OAuth provider list, dot-menu actions, "Add disabled providers" search dialog, two confirmation dialogs, sign-up policy block, user-deletion block. Old `Card` / `Input` / `Button` / `SettingCard` imports replaced with their design-component counterparts. `providers.tsx` follows the same migration for the per-provider config dialogs. - **Sign-up rules page** (`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx`) — the big 1830-line rewrite. Rule builder, empty state, conditional-group editor, and tester sheet all rebuilt on the new primitives. CEL ↔ visual-tree conversion (`parseCelToVisualTree` / `visualTreeToCel`) is unchanged. - **Design-language catalog** (`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/design-language/page-client.tsx`) — adds the Dialog section so the catalog reflects the new primitive. - **Playground** (`apps/dashboard/src/app/(main)/(outside-dashboard)/playground/page-client.tsx`) — adds the Dialog entry with `Shape` / `Size` / `Variant` / `Title` / `Description` / `Header Icon` / `Footer` / `Top-right close` controls and live JSX generation. - **Design guide** (`apps/dashboard/DESIGN-GUIDE.md`) — new "Dialogs" section documenting when to reach for `DesignDialog` (default), `DesignDrawer`, `ActionDialog`, or the raw `<Dialog>` primitives, plus the canonical usage snippet. - **Action dialog shim** (`apps/dashboard/src/components/ui/action-dialog.tsx`) — small follow-on edits so existing `ActionDialog` callers stay consistent with the new look. ## Notes for reviewers - **Start with** `packages/dashboard-ui-components/src/components/dialog.tsx` — it's the load-bearing piece. The two state machines worth eyeballing are the `dialogSurfaceClasses` map (glassmorphic vs plain shells, including the dark-mode ring/backdrop tweaks) and the header/body/footer composition inside the main `DesignDialog` function. - **Then** `sign-up-rules/page-client.tsx`. 1830 lines, but the diff is mostly mechanical (Card→DesignCard, Button→DesignButton, etc.). The interesting bits are the rule-row layout, the conditional-group editor, and the simulator drawer — those received structural tweaks, not just visual ones. The CEL serialization (`parseCelToVisualTree` / `visualTreeToCel`) was deliberately left alone. - **OAuth provider migration to non-pushable config** — a `// OAuth client ID/secret are environment-level (not pushable)` comment was removed from a couple of call-sites. Behaviour-equivalent (the call already passes `pushable: false`), just trimmed because the new code is cleaner. Flag if you want it kept. - **Catalog routes are dashboard-internal** (`/projects/<id>/design-language`, `/playground`) — exposed only in dev/staging, not customer-facing. They exist so design changes have a discoverable demo surface. - **Live-preview frame on `auth-methods`** uses a real `<AuthPage>` inside `BrowserFrame`, fed by the in-progress config. Verify your changes still render correctly there if you touch `<AuthPage>` props. ## Test plan - [ ] `/projects/<id>/auth-methods` — toggle each sign-in method; live preview reflects the change; "Save changes" inline action works; "Add SSO providers" dialog filters via the search input - [ ] OAuth provider dot-menu — open the provider config dialog (now `DesignDialog` glassmorphic), confirm the per-provider switches/inputs save through the `useUpdateConfig` hook - [ ] Sign-up confirmation dialogs — toggling "Allow new user sign-ups" off and back on shows the new warning `DesignAlert`s inside the dialog - [ ] `/projects/<id>/sign-up-rules` — add a rule, add a condition group, run the tester sheet; CEL output unchanged vs `dev` - [ ] `/projects/<id>/design-language` — Dialog showcase renders all sizes/variants without overflow - [ ] `/playground` → select **Dialog** — all prop combinations render; generated code snippet matches the rendered component; "Open confirmation" launches the glassmorphic shell - [ ] Light + dark mode visual sanity across all four pages (screenshots above are the canonical reference) --------- Co-authored-by: Aadesh Kheria <kheriaaadesh@gmail.com> |
||
|
|
2aa4affa54
|
Fix build and lint failures on dev (#1445) | ||
|
|
6e769c3be3
|
Upgrade Next.js | ||
|
|
a62702354b | Don't show alpha apps during onboarding | ||
|
|
bb901068cb | Fix React error | ||
|
|
48acb8c640 | chore: update package versions | ||
|
|
29cea48beb
|
Remote dev envs (#1435) | ||
|
|
d0202eeef9
|
payments: rework refund flow to three-knob API (#1429)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary
- Replaces per-entry refund schema with a flat `{ amount_usd,
revoke_product, end_subscription? }` shape; refund state is now derived
from bulldozer ledger rows (`refund:<sourceTxnId>:<uuid>`) instead of
the legacy `refundedAt` column, enabling multiple partial refunds up to
the remaining cap.
- Adds `invoice_id` for refunding any subscription invoice (start or
renewal), Stripe idempotency keys derived from `(tenancyId, sourceTxnId,
amount, prior_refunded)` so retries dedupe but intentional partials
don't collide, and a legacy backstop that rejects pre-rework
`refundedAt` purchases.
- Dashboard refund dialog rebuilt around the three toggles (revoke→end
coupling cascades into the UI); refund rows surface in the listing as
`type: "refund"` with `adjusted_by` linkage handling both new and legacy
formats.
## Implements
[STA2-52 — Build in refund logic for
payments](https://linear.app/stack-auth/issue/STA2-52/build-in-refund-logic-for-payments)
## Documented limitations (planned follow-up work)
These are called out in code comments and intentionally deferred to a
follow-up PR:
- **Cap-check race under concurrent refunds.** Bulldozer's embedded
`BEGIN/COMMIT` prevents an outer Prisma tx from scoping the writes, so
two concurrent refunds can both pass the cap check. Needs a
bulldozer-aware mutex or pending-refund-intent pattern. In practice
refunds are admin-only and rare, so the race window is small.
- **Stripe + DB non-atomicity on the DB-success → response-loss path.**
The Stripe idempotency key is keyed on `(tenancyId, sourceTxnId, amount,
priorRefunded)`, so a retry after Stripe-success → DB-fail self-heals
(Stripe dedupes; the next attempt writes the bulldozer row). The hole is
the reverse direction: if the bulldozer row commits but the response is
lost, a retry sees a higher `priorRefunded` and generates a fresh key —
Stripe would issue a second real refund. No out-of-band reconciliation
today.
- **Dashboard can't reach the `invoice_id` path.** Refund actions are
only enabled on `purchase` rows and the submit call never passes
`invoice_id`, so admins refunding a renewal must use the API directly.
Follow-up: enable the action on `subscription-renewal` rows and thread
`invoice_id` through.
## Architectural note
`active-subscription-end` and `item-quantity-expire` entries are **not**
emitted on the refund row itself. They're produced by the derived
sub-end transaction (`transactions.ts:158-228`) once Prisma
`subscription.endedAt` is updated, keeping the `expiresWhen` /
`when-repeated` semantics in one place. This is the main structural
divergence from the ticket's literal entry recipe.
## Review follow-ups addressed in this PR
**First-pass review:**
- **KnownError back-compat preserved**: `SubscriptionAlreadyRefunded` /
`OneTimePurchaseAlreadyRefunded` are once again thrown by the
legacy-`refundedAt` backstop, and `TestModePurchaseNonRefundable` is
thrown when an admin sends `amount_usd > 0` against a test-mode
purchase. Callers catching by error code keep working through the
rework.
- **Idempotency-key comment corrected**: now accurately describes the
`(tenancyId, sourceTxnId, amount, priorRefunded)` key and its
self-healing behaviour on the Stripe-success → DB-fail retry path (see
Documented limitations above for the remaining hole).
- **Renewal-invoice e2e coverage added**: new test sets up a live-mode
subscription via Stripe webhooks (`subscription_create` +
`subscription_cycle` invoices), refunds the renewal invoice via
`invoice_id`, and asserts the resulting `refund_transaction_id` starts
with `refund:sub-renewal:` and is linked back via `adjusted_by` on the
*renewal* row (not the start row). Plus negative cases:
cross-subscription `invoice_id` → 404, `invoice_id` on a one-time
purchase → SchemaError.
**Second-pass review:**
- **Idempotent sub-cancel error-code string fix**: the Stripe code for
re-cancelling an already-canceled sub is
`subscription_already_canceled`, not `subscription_canceled` — the
previous catch would have re-thrown.
- **End-only sub refund replay rejected**: when `amount=0, revoke=false,
end=true` and the sub is already `cancelAtPeriodEnd` or `endedAt`, throw
SchemaError. Otherwise `readPriorRefundSummary` doesn't see end-only
events and the call would be a forever-no-op accumulating empty refund
rows.
- **`revoke_product=true` with renewal `invoice_id` rejected**: the
product grant lives on the sub-start txn, not on renewal txns — a
renewal-scoped revocation would write a back-reference to a non-existent
entry. Forces admin to revoke against the start invoice (or the default
no-`invoice_id` call).
- **Refund row `id` matches the linkage**: the listing route now returns
the full refund txnId as `id` for `type: "refund"` rows so it matches
`adjusted_by.transaction_id` — the dashboard can join source rows to
their refund rows.
- **+2 e2e tests** for the above (end-only replay rejection,
revoke+renewal rejection).
**Third-pass review:**
- **Dashboard refund dialog seeds state on open**: previously the reset
block lived in `ActionDialog`'s `onOpenChange`, which doesn't fire on
the open transition for a controlled dialog. As a result the dialog
opened with the initial `useState` defaults (`amountUsd = '0'`), and an
admin submitting unchanged on a paid purchase would revoke/end at $0
instead of refunding the charged amount. The seed now runs in the menu
`onClick` before `setIsDialogOpen(true)`.
- **`SUBSCRIPTION_START_PRODUCT_GRANT_ENTRY_INDEX` corrected from 1 →
0**: the constant is persisted as `adjustedEntryIndex` on
product-revocation entries and copied through verbatim by
`mapLedgerEntry`. That mapper drops the hidden
`active-subscription-start` entry, so the public-API layout puts the
product grant at index 0. The prior value of `1` pointed at the
money-transfer entry (or out of range on test-mode subs) through the
public listing.
- **`amountTotal` cap gated behind a USD pre-flight**:
`SubscriptionInvoice` doesn't persist invoice currency, and the previous
code took `invoice.amountTotal` as USD cents directly. Now
`getTotalUsdStripeUnits` (which throws on non-USD pricing) is always
called first; `amountTotal` is only preferred as the actual cap after
that pre-flight succeeds.
## Test plan
- [x] `pnpm typecheck` — 28/28 pass
- [x] `pnpm lint` — 28/28 pass
- [x] `pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts`
— **19/19 pass** (was 14/14 on the original PR; +3 for `invoice_id`
path: renewal refund happy path, unrelated `invoice_id` rejection,
`invoice_id` on OTP rejection; +2 for second-pass: end-only replay
rejection, revoke+renewal rejection)
- [x] curl smoke against
`/api/latest/internal/payments/transactions/refund` — unknown purchase →
404, no-op → 400, negative → 400, sub-revoke-without-end → 400
- [x] **Dashboard UI end-to-end re-run pending** — the original
agent-browser pass ran before the third-pass dialog-seed fix, so any
"money + revoke" submissions may have actually sent `amount_usd = "0"`.
Re-test before un-drafting: open the refund dialog from the menu,
confirm the amount field pre-fills with the charged amount, exercise
validation (negative / exceeds-cap / no-op), and submit both an
end-subscription-only sub refund and a money+revoke OTP refund; verify
bulldozer rows and Prisma `cancelAtPeriodEnd` updates.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Ledger-driven refund flow with stable refund IDs, invoice-aware
refunds, OTP/product-revocation support, tri-state end_action (now /
at-period-end / none), and API responses that include
refund_transaction_id.
* **Bug Fixes / Improvements**
* Deterministic Stripe idempotency, stronger replay protection,
refundable-amount caps, test-mode constraints, and transactions listing
updated to surface refunds.
* **Tests**
* Expanded unit and E2E coverage for new request shape, invoice paths,
money-unit conversion, and edge cases.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1429)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
b526e3b367
|
Project transfer page redesign (#1309)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Reusable transfer confirmation UI with clear loading, success, and error states. * Neon-specific transfer flow added, guiding sign-in, account switching, or accepting transfers. * Custom integration transfer flow with streamlined confirm/check behavior. * Improved transfer sign-up redirect so users return to the correct page after auth. * **Bug Fixes** * Consistent messaging for missing/invalid/expired transfer codes. * Safer widget “Reload” handling when reset may be unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- ## Summary Redesigns the **custom integration** project-transfer confirmation page (`/integrations/custom/projects/transfer/confirm`) onto the new design-components system (`DesignCard` + `DesignAlert` + `DesignButton` + `DesignInput`). The presentational shell is extracted into a reusable `ProjectTransferConfirmView` so the route file only handles state + API calls. The legacy Neon transfer page is split out unchanged into its own client component to keep the existing Neon × Stack co-branded UI intact. --- ## Screenshots — before and after > Captured against `http://localhost:8101` at 1280×900. Dev-only overlays (outdated-version banner, console toast, DEV badge) are hidden via injected CSS for clarity. ### Custom integration — missing transfer code Visiting `/integrations/custom/projects/transfer/confirm` with no `?code=…` query param. | Before (`dev`) | After (this PR) | | --- | --- | |  |  | |  |  | Before was a raw `"Error: No transfer code provided."` line. After is a dedicated `DesignAlert` with an explanation and recovery instructions. ### Custom integration — invalid / expired code (check endpoint fails) | Before (`dev`) | After (this PR) | | --- | --- | |  |  | |  |  | Before showed the raw backend error string (`Request validation failed on POST …`). After uses a `DesignCard` with the `ArrowsLeftRightIcon`, a friendlier "This transfer can't continue" copy in an inline `DesignAlert`, the Stack Auth logomark in the actions slot, and an explicit **Close** button to dismiss. ### Neon integration — legacy UI preserved The Neon page (`/integrations/neon/projects/transfer/confirm`) was deliberately **not** redesigned — it still uses the Neon × Stack co-branded card so partner-facing copy/branding stay identical. It's now its own client component (`neon-transfer-confirm-page.tsx`) instead of sharing the redesigned one. | Before (`dev`) | After (this PR) | | --- | --- | |  |  | |  |  | Same shell on both sides — copy was tightened slightly ("Return to your Neon dashboard and start the transfer again") and the raw API error string is gone. --- ## What changed - **New** `apps/dashboard/src/components/project-transfer-confirm-view.tsx` — purely presentational `ProjectTransferConfirmView`. Owns the design-components shell, the loading spinner, the signed-in vs signed-out branches of the success state (with `DesignInput` + "Use a different account" button), and the error / missing-code alerts. - **New** `apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx` — extraction of the legacy Neon UI (Neon logo, Stack logo, "Project transfer" header, Card / CardContent / CardFooter). Behaviour and copy match the previous `transfer-confirm-page` exactly when `type === "neon"`. - **Rewritten** `apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx` — now hard-coded to the `custom` integration (no more `type` prop), defers UI to `ProjectTransferConfirmView`, and exports a `TransferConfirmMissingCodeView` used by the route when `code` is absent from the URL. - **Route plumbing** - `app/(main)/integrations/custom/projects/transfer/confirm/page.tsx` — renders the redesigned flow, falls back to `TransferConfirmMissingCodeView` when `code` is missing. - `app/(main)/integrations/neon/projects/transfer/confirm/page.tsx` — points at the new dedicated Neon client component. - **New** `apps/dashboard/src/lib/stack-app-internals.ts` — consolidates the symbol-keyed `getStackAppInternals(app)` helper (and `stackAppInternalsSymbol`) into one module with a JSDoc explainer + runtime type guard, replacing scattered `as any` casts. - **New** `apps/dashboard/src/lib/transfer-utils.ts` — `buildTransferSignUpUrl()` helper so the route file + the view stay in sync on the `/handler/signup?after_auth_return_to=…` query construction. --- ## Bot review follow-ups addressed in this PR - **Fail-loud assertions for unset handlers** in the success state of `ProjectTransferConfirmView` (`StackAssertionError` instead of silent no-op). - **SSR safety:** moved every `window.location` read into client-only handlers / `useEffect`s — the page was previously evaluating it at module load. - **Friendly error fallback** when the backend `/check` endpoint throws — replaces the raw `KnownError<…>` message with "This transfer link is invalid, has expired, or has already been used. Open the original link from the partner or integrations dashboard, or start the transfer again." - **`runAsynchronouslyWithAlert`** around every async `onClick` (Transfer, Sign in, Switch account, Close) so unhandled rejections surface to the user. - **JSX entity bug fix:** `'` was a string-attribute literal, not a JSX expression — converted to a JSX expression so it renders as `'`. - **`window.close()` removal** in error state — replaced with a Close button that resets local state, so users on a fresh tab (no opener) aren't stuck. - **`getStackAppInternals` consolidated** — previously three independent copies (here + two in `projects/page-client.tsx`). Now one helper with a runtime type guard instead of `as any`, plus a comment explaining the symbol-keyed SDK escape hatch. - **Widget-playground reset:** the original change here turned out to duplicate a deliberate prior fix on `dev` (N2D4, `e68015909d "Fix lint"`). Reverted in `fe92689eb` so we don't fight that fix. --- ## Notes for reviewers - **Start with** `components/project-transfer-confirm-view.tsx`. Everything reviewer-interesting is in the props shape (`ProjectTransferConfirmUiState` union, `onPrimary` / `onCancel` / `onSwitchAccount` callbacks). The route file just wires those to the `getStackAppInternals(app).sendRequest(...)` calls. - **The Neon page was intentionally not migrated.** Partner-facing co-branding (Neon logo × Stack logo, "Neon would like to transfer…" copy) is unchanged — flag it if you think it should be brought onto design-components too, but the goal of this PR was only the custom flow. - **API surface is unchanged** — same `/integrations/custom/projects/transfer/confirm/check` and `/integrations/custom/projects/transfer/confirm` endpoints, same request bodies, same redirect to `/projects/{project_id}` on success. - **Success state isn't in the screenshots** because reproducing it locally needs a real transfer code (the `/check` endpoint validates the code against the DB). It uses the same `DesignCard` shell with either a `DesignInput` showing the receiving account + a "Use a different account" outline button (signed-in branch), or a `DesignAlert variant="info"` prompting sign-in (signed-out branch). Worth manually testing on a real transfer before merging. ## Test plan - [ ] Visit `/integrations/custom/projects/transfer/confirm` with no `code` → renders the "transfer link is incomplete" alert (screenshots above) - [ ] Visit `/integrations/custom/projects/transfer/confirm?code=invalid` → renders the redesigned card with the friendly error inside a `DesignAlert variant="error"` and a working Close button - [ ] Trigger a real custom-integration transfer end to end → loading spinner, success state, "Accept transfer" works while signed in, "Sign in" deep-links to `/handler/signup?after_auth_return_to=…` while signed out - [ ] Visit `/integrations/neon/projects/transfer/confirm?code=…` → unchanged legacy Neon × Stack co-branded card - [ ] Light + dark mode visual sanity (screenshots above are the canonical reference) --------- Co-authored-by: Aadesh Kheria <kheriaaadesh@gmail.com> Co-authored-by: aadesh18 <110230993+aadesh18@users.noreply.github.com> |
||
|
|
5cb9240bc3
|
refactor(dashboard): unify AI chat surfaces on assistant-ui Thread (#1427)
## Summary - Replace the bespoke `ai-chat-shared` chat UI (used by ask-ai, the stack companion widget, vibe coding chat, and the create-dashboard preview) with the shared `assistant-ui` `Thread` component. - Extract streaming request/format helpers into a new `components/assistant-ui/chat-stream.ts` module so each surface only owns its `ChatModelAdapter`. - Add a reusable `ToolFallback` for tool-call rendering and delete the now-unused `ai-chat-shared.tsx` (-1386 / +747 lines net). Stacked on top of `refactor/data-grid-and-dashboard-surfaces`. Base: `refactor/data-grid-and-dashboard-surfaces` → Head: `refactor/assistant-ui-chat-surfaces` · 18 files changed > Red outlines on the **after** shots mark the unified `assistant-ui` `Thread` surface in each location. ## Screenshots ### Analytics → Tables — AI Query dialog | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-dark.png" width="480" /> | ### Stack Companion — chat widget | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-dark.png" width="480" /> | ### Ask-AI command palette (⌘K → Ask AI) | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-dark.png" width="480" /> | ### Email editor — embedded chat panel | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-dark.png" width="480" /> | ## Notes for reviewers The four surfaces above all previously shared `components/commands/ai-chat-shared.tsx` (516 lines, deleted). After this PR they each own a thin `ChatModelAdapter` and render through `components/assistant-ui/thread.tsx` + the new `chat-stream.ts` helpers. Visual differences between **before** and **after** are intentional — the `assistant-ui` `Thread` brings its own message bubbles, scroll-to-bottom behaviour, composer, and `ToolFallback` rendering. The email editor's chat panel is the surface where the behaviour change is most visible (tool-call rendering now consistent with the rest of the app). Heaviest changes (lines): - `components/stack-companion/ai-chat-widget.tsx` (571) - `components/commands/ai-chat-shared.tsx` (516, deleted) - `analytics/tables/ai-query-dialog.tsx` (429) - `components/vibe-coding/chat-adapters.ts` (400) - `components/assistant-ui/chat-stream.ts` (284, new) - `components/commands/ask-ai.tsx` (274) - `components/assistant-ui/thread.tsx` (115) - `components/assistant-ui/tool-fallback.tsx` (113) ## Test plan - [ ] `pnpm lint` - [ ] `pnpm typecheck` - [ ] Manually exercise each affected surface: command-center Ask AI, stack-companion widget, vibe-coding chat, analytics tables AI query, create-dashboard preview, email editor chat. - [ ] Verify tool-call chips render consistently across all four surfaces (uses the new `ToolFallback`). - [ ] Verify streaming + cancel works on each adapter (`chat-stream.ts` is shared). |
||
|
|
c808e23b7d
|
Data-grid overhaul + session-replays / team-payments dashboard surfaces (#1424)
## Summary Refactors the dashboard data-grid into a smaller, URL-state-aware primitive and lands several new dashboard surfaces around it: per-user session replays, team-level analytics and payments, and pagination for permission definitions. Also moves session replays out from under `/analytics` to a top-level surface and adds a `project_user.last_active_at` index that the new weekly-active metrics depend on. **Base:** `dev` → **Head:** `refactor/data-grid-and-dashboard-surfaces` **Scope:** 91 files, +5,644 / −1,858. Assets in [this gist](https://gist.github.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7). ## Screenshots Captured from a local dev server (dashboard at `:8101`, dummy project seeded with 26 users). Standard viewport **1920×1200**, widescreen **2560×1440**. ### Users list — data-grid overhaul in context | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### User detail — new session-replays card + weekly metrics | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Session replays — moved out of `/analytics` | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Project permissions — new pagination | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Other migrated surfaces | Page | Light | Dark | | --- | --- | --- | | Project picker |  |  | | Overview / setup |  |  | | Teams list |  |  | | Team permissions |  |  | | API keys |  |  | ### Scroll behaviour — new data-grid on the users list | Light | Dark | | --- | --- | |  |  | ## What's new - **`packages/dashboard-ui-components/src/components/data-grid`** — rewritten. Trimmed `data-grid.tsx` from ~1.7k LOC, split sizing logic into `data-grid-sizing.ts`, added `use-url-state.ts` for URL-synced state, and added `data-grid.test.tsx`. - **Session replays** moved from `…/analytics/replays` to `…/session-replays` (top-level surface). New `user-session-replays.tsx` card on the user detail page; new internal `route.tsx` to feed it. - **Teams** detail page gains `team-analytics.tsx` and `team-payments.tsx`. - **Permissions** — new shared `permission-definitions-pagination.ts` consumed by both project and team permission CRUD routes. - **Backend** — Prisma migration `add_project_user_last_active_at_idx` + a `lastActiveAt` index that backs the new weekly-active metrics. - **Polish** — `editable-input`, `inline-save-discard`, `settings.tsx`, walkthrough steps, and several data-table components touched in line with the data-grid rewrite. ## Notes for reviewers - The data-grid rewrite changes the *shape* of state (now URL-synced), not just internals. Consumers in `apps/dashboard/src/components/data-table/*` were updated to match — please scan those for any missed knobs. - The `analytics/replays` → `session-replays` rename is git-tracked as renames; diffs should be small in those files. - New SDK surface in `packages/template/src/lib/stack-app/session-replays/index.ts` and additions in `admin-app-impl.ts` / `server-app-impl.ts` mean OpenAPI specs (`docs-mintlify/openapi/{admin,client}.json`) regenerate; the diff is mostly mechanical. ## Test plan - [ ] `pnpm typecheck` clean - [ ] `pnpm lint` clean - [ ] Data-grid unit tests pass (`packages/dashboard-ui-components`) - [ ] Manual: users list — column resize, sort, filter, paginate; URL state reflects each change and survives reload - [ ] Manual: user detail — session-replays card lists replays; weekly-metrics card renders without `lastActiveAt` index migration applied (i.e. on a fresh DB) and after applying it - [ ] Manual: project + team permissions — pagination cursor advances and stays consistent under search - [ ] Manual: session-replays top-level page loads; old `/analytics/replays/...` URL path is no longer expected to be linked anywhere <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Session Replays app (embedded mode, search, sorting, share links) * Tabbed Team pages with Team Analytics and Team Payments dashboards * Server-backed cursor pagination, debounced search, and infinite-scroll for teams/users/permissions * **UX** * Permission and member tables refresh after edits; permission creation triggers table refresh * Users list supports sorting by last-active * **Performance** * Index added to speed ProjectUser last-active queries * **Documentation** * API/SDK docs updated for pagination and new query params * Contributor guidance: explicit git-safety rules added (no destructive git ops without consent) * **Tests** * Added e2e tests for pagination and filtering on list endpoints <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a9623d976a
|
[Refactor] [Fix] Remove default prod creation (#1350)
With the new bulldozer rework we dont support default products anymore. Users are encouraged to currently manually handle granting products to their end users. We block api requests and new product creations that attempt to set no price, and we remove any options to set include-by-default. We also migrate users' existing product snapshots in `Subscriptions`, `OneTimePurchases`, and `ProductVersions` to have no price set if it's an include-by-default product. This will make it so that next time a user goes onto their products page, they will be informed that the pricing is invalid and it is no longer delivered by default. Note, however, that these products will still be providing items and the like to the users who have them. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Migrated legacy product snapshots so missing included-items no longer break readers. * Removed deprecated "include-by-default" pricing sentinel; pricing now requires explicit price entries and write validation rejects the old sentinel. * **Chores** * Simplified dashboard pricing flows: create/edit/save now use explicit prices and surface an alert when a formerly implicit free plan needs an explicit $0 price. * Config overrides and stored data are auto-normalized to explicit price objects. * **Tests** * Updated and added tests covering migration, validation, and switching behavior for explicit prices. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: mantrakp04 <mantrakp@gmail.com> Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com> |
||
|
|
15faf709f3
|
stack-cli: explicit --cloud-project-id / --config-file across exec, config, project (#1422)
## Summary Reworks the `stack` CLI surface so the cloud-vs-local choice is **explicit at every invocation**, removing the global `--project-id` / `STACK_PROJECT_ID` env var and the local-default `exec` behavior introduced earlier in this branch. ### `stack exec` - Removes `--cloud`, `STACK_EXEC_DEFAULT_TARGET`, and the implicit local default. The CLI now requires **exactly one** of: - `--cloud-project-id <id>` — run against the Stack Auth cloud API - `--config-file <path>` — run against the local emulator project mapped to that absolute config-file path - The `--config-file` branch resolves the project id by calling the existing `GET /api/latest/internal/local-emulator/project` endpoint and matching `absolute_file_path` client-side. No new backend endpoint introduced. ### `stack config pull` / `stack config push` - Both now take `--cloud-project-id <id>` per-command instead of the global flag / `STACK_PROJECT_ID` env. - `config pull --config-file` is **optional**: when omitted, the CLI uses `./stack.config.ts` from the current directory. If neither flag nor cwd file is present, it exits with a clear hint to pass `--config-file` or `cd` into a directory containing `stack.config.ts`. ### `stack project list` - Default (no flags) lists both **cloud and local emulator** projects. Each entry carries a `target: "cloud" | "dev"` field (text format: `<id>\t<displayName>\t[<target>]`). - `--cloud` / `--dev` filter to a single source (mutually exclusive — passing both errors). - On the default code path, an unreachable local emulator emits a single stderr warning (`warning: skipping dev projects — local emulator not reachable …`) and the command still succeeds with cloud results. With `--dev` explicit, the unreachable case hard-errors. ### `stack project create` - Now requires `--cloud` to make the cloud-vs-local choice explicit. There is no local alternative today; the flag exists to surface the decision so a future local-project create doesn't silently change behavior. ### Backend - Bumps the `LIMIT` on `GET /api/latest/internal/local-emulator/project` from 20 → 100 so `project list --dev` doesn't silently truncate. ### Refactors (from earlier in this branch, unchanged here) - Local-emulator paths/ports/PCK polling live in `packages/stack-cli/src/lib/emulator-paths.ts`. - Shared local-emulator admin credentials live in `packages/stack-shared/src/local-emulator.ts`. - `resolveAuth` / `resolveLocalEmulatorAuth` take an explicit `projectId: string` (no more `Flags` parameter). - New `packages/stack-cli/src/lib/local-emulator-client.ts` encapsulates the GET-and-match flow used by both `exec --config-file` and `project list --dev`. ## Breaking changes **Scripts that relied on any of the following must be updated:** | Removed | Replacement | | --- | --- | | Global `--project-id <id>` flag | Per-command `--cloud-project-id <id>` | | `STACK_PROJECT_ID` env var | Per-command `--cloud-project-id <id>` | | `stack exec --cloud` | `stack exec --cloud-project-id <id>` | | `STACK_EXEC_DEFAULT_TARGET=cloud\|local` | `--cloud-project-id <id>` or `--config-file <path>` | | `stack exec` defaulting to local emulator | Explicit `--config-file <path>` required | | `stack project create` without a flag | `stack project create --cloud …` required | ## Test plan - [x] `pnpm lint` (stack-cli, backend, e2e) — clean - [x] `pnpm --filter @stackframe/stack-cli typecheck` — clean - [x] `pnpm --filter @stackframe/stack-cli exec vitest run` — **72/72 passing** (new unit tests: `parseExecTarget`, `resolveConfigFilePathForPull`, `resolveProjectListSources`, `formatProjectList`) - [x] `pnpm test run apps/e2e/tests/general/cli.test.ts` — **73 passing, 4 skipped, 0 failing**. New e2e cases cover: - `exec` with neither flag → errors with "Specify a target" - `exec` with both flags → errors with "not both" - `exec --config-file` with missing file / missing PCK / unreachable API - `exec --config-file` happy path against a real local-emulator backend (gated on `NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR=true`) - `config pull` cwd fallback to `./stack.config.ts` - `config pull` with no `--config-file` and no cwd `stack.config.ts` → errors with `Pass --config-file …` - `project list --cloud --dev` together → errors - `project list` default with unreachable emulator → cloud results + single stderr warning - `project create` without `--cloud` → errors - All previously-`--cloud` exec cases ported to `--cloud-project-id` - [x] Manual smoke: `stack exec --help`, `stack project list --cloud --dev`, `stack project create` all emit the expected friendly errors / help text. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * CLI `exec`, `config`, and `project` commands now require explicit targeting via `--cloud-project-id` (cloud) or `--config-file` (local emulator). * `project list` now supports `--cloud` and `--dev` flags to display projects from both sources with target indicators. * Enhanced environment variable validation for emulator service ports with proper fallback handling. * **Bug Fixes** * `project list` now gracefully handles unreachable emulator with warning fallback instead of failure. * **Tests** * Expanded test coverage for project targeting, config file resolution, and emulator connectivity scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2cf0f6f981
|
[Apps] Adding support app alpha and dogfooding (#1368)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Support app: inbox UI to create, view, reply, and manage conversations (status, priority, assignee, tags, internal notes). * Dashboard pages: Conversations and Support Settings; feedback can create managed conversations. * Public/internal APIs for listing, creating, updating, and fetching conversation details; client-side helpers. * **SLA** * Configurable first/next response targets, urgency classification, and timing logic. * **Data** * New conversation persistence (conversations, entry points, messages) and migration tests; preserves conversations on user/team deletion and anonymizes sender data. * **Tests** * Unit, migration, and end-to-end tests added. * **Documentation** * Updated docs describing conversation model and workflow rules. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d2030e826b | Unhandled promise rejections no longer kill the whole server if not in development | ||
|
|
76023af9d6
|
Custom Dashboards Versioning fix (#1418)
This PR fixes the versioning error that we ran into for custom dashboards. Now if the latest version of the packages does not work, we fall back to the version that is one patch below the latest version. We log this into sentry. If the fall back doesn't work either, we log that into sentry as well and show the user an error message. Apart from that, I also made changes to ensure dashboards with older versions of the dashboard-ui-component package would still work. Each dashboard now stores the version it was created with, as a comment at the top of its source code, and we use that version when loading the dashboard. When a dashboard gets edited via the AI chat, we re-stamp it with the latest version of the package so it stays up to date. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved error handling and reporting for dashboard load failures; host surfaces structured dependency errors for faster diagnostics. * Added automatic fallback loading for missing resources to reduce load failures. * Fixed page height calculation so pages align correctly with the viewport. * **New Features** * Generated and editor-provided dashboard code is now stamped with the app version for clearer provenance. * **UI/UX Improvements** * Clearer, more informative error messages when custom dashboard loading encounters issues. [](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1418) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
efa2153d47 | Improve project overview weekly users | ||
|
|
9ff2c13f8d | Add functionality to restrict or unrestrict users | ||
|
|
80a26ca15d | chore: update package versions | ||
|
|
227dac6567
|
feat(dashboard): add weekly users metrics for projects (#1412)
- Introduced a new API endpoint to fetch weekly and daily user metrics for managed projects. - Updated the dashboard to utilize this new endpoint, replacing the previous daily active users data. - Created a new component to visualize weekly users metrics in the project cards. - Refactored existing components to accommodate the new data structure and ensure proper rendering of user activity charts. This change enhances the analytics capabilities of the dashboard, providing better insights into user engagement over time. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * New internal endpoint providing per-project weekly user totals and 7-day daily activity series. * **Updates** * Dashboard and project cards switched from DAU to weekly user metrics; main metric shows weekly users and label reads "users/wk". * Charts now display weekly-user-aware sparklines alongside daily activity. * **Tests** * Added unit tests covering weekly aggregation and daily-series merging. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
68ae6d1f1c
|
[codex] Add TanStack Start SDK integration (#1399)
## Summary - Adds the generated `@stackframe/tanstack-start` workspace package registration. - Adds TanStack Start platform macros/dependencies to the SDK template and generator. - Adds TanStack Start cookie/token-store support plus the handler SSR guard needed by Start. ## Scope This intentionally excludes Dashboard V2 routes, hooks, components, app shell logic, and dashboard API type additions. Those stay in the existing dashboard PR/branch. ## Validation - `pnpm install --lockfile-only --ignore-scripts` - `pnpm install --ignore-scripts` - `pnpm -C packages/template lint src/components-page/stack-handler-client.tsx src/lib/cookie.ts src/lib/stack-app/apps/implementations/client-app-impl.ts` Package typecheck was attempted with `pnpm -C packages/template typecheck`, but the clean worktree lacks generated package declaration outputs for workspace dependencies such as `@stackframe/stack-shared` and `@stackframe/stack-ui`. Per repo instructions, package builds/codegen are not run by agents. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * TanStack Start integration: published SDK package, example demo app, dashboard onboarding flow, framework-aware CTAs/docs, and a TanStack-specific provider for client-only auth routes. * Improved client/server auth: safer runtime guards and consistent cookie/token-store behavior across SSR and client. * **Documentation** * New Integrations guide and expanded getting-started/setup docs with TanStack Start examples and env/key guidance. * **Chores** * Template, build, tooling, and demo config updates to support the new platform. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
acc646cb0b
|
stack-cli: cloud/local init flow, auto-create on empty projects, post-setup next-steps (#1383)
### Summary Reworks `stack init` UX, adds Sentry error reporting to the CLI, polishes the emulator start flow, and overhauls the local-emulator dashboard's "Open config file" dialog. #### `stack init` flow - **New top-level flow.** Drops the old "link existing vs. create new local" fork. `init` now asks *where* to create the project — "Stack Auth Cloud" or "Local". Adds a new `create-cloud` mode that logs the user in, creates a cloud project, mints keys, and writes `.env` — no round-trip through the dashboard. - **Conditional emulator-install warning.** The "Local" choice label only shows "(requires local emulator installation, ~1.3gb storage required)" when the QEMU image isn't already on disk; otherwise it shows "(emulator already installed)". Driven by a new `isEmulatorImageInstalled()` helper in `commands/emulator.ts`. - **Auto-create on zero-projects.** When the link-from-cloud path hits an empty project list, the CLI now prompts *"You don't have any Stack Auth projects yet. Would you like to create one?"* and, on yes, runs the same flow as `stack project create`. Skips the pointless "select a project" prompt when we just created one. - **MCP-server notice.** Before invoking the coding agent, the CLI announces that it's also registering the Stack Auth MCP server (`mcp.stack-auth.com`) so the agent can answer Stack-specific questions going forward. - **Local-emulator env header.** When `writeProjectKeysToEnv` runs in `local` mode it writes a 3-line comment header above the keys explaining they're emulator-only and only valid while the emulator is running. - **"What's next" footer.** After setup finishes, prints a short orientation block: where the sign-up/sign-in routes live (`/handler/sign-up`, `/handler/sign-in`), how to start the local emulator (for `create` mode), a dashboard deep link for cloud projects (respects `STACK_DASHBOARD_URL`), and a docs link. #### Sentry error reporting (`lib/sentry.ts`, `index.ts`, `tsdown.config.ts`) - New `lib/sentry.ts` initializes `@sentry/node` with PII scrubbing (Stack key prefixes, JWTs, home-dir paths, sensitive field names like `token`/`secret`/`password`/`dsn`). - DSN is baked at build time via a tsdown `define` sentinel (`__STACK_CLI_SENTRY_DSN__`) — no DSN in source, no runtime env-var dependency for installed users. CI sets `STACK_CLI_SENTRY_DSN_BUILD` before `pnpm build`. - Disabled when `NODE_ENV=development` or `CI`. No user opt-out. - Wired into `main()`'s catch (only for unexpected errors — `CliError`/`AuthError` still print and exit cleanly) plus `uncaughtException` and `unhandledRejection` handlers via a `handleFatal` helper. #### `stack emulator start` welcome - After a fresh start (not when reusing a running VM, not when `--config-file` keeps stdout JSON-only), prints a short "Emulator is up" block with service URLs (dashboard / backend / inbucket) and common commands (`status`, `stop`, `reset`, `run`). #### Local-emulator dashboard "Open config file" dialog The dialog at `http://localhost:26700` (when no project is loaded) used to be a single text input asking for an absolute path, with no explanation of where that path comes from. **Backend** (`apps/backend/src/app/api/latest/internal/local-emulator/project/route.tsx`): - POST is now tolerant of directory paths or paths that don't end in `.ts`/`.js`/`.mjs` — it appends `stack.config.ts` and creates the file if missing (`writeConfigToFile` mkdir's parents). Lets users paste a project folder instead of hunting for the config file. - New GET endpoint returns up to 20 most-recent `LocalEmulatorProject` rows joined with their display names, sorted by `updatedAt` desc. Same `isLocalEmulatorEnabled()` + client-auth gating as POST. **Dashboard** (`apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx`): - Title changed to "Open your Stack Auth project". Description now explicitly ties the file to `stack init`: *"Point the local dashboard at the `stack.config.ts` in your project. If you just ran `stack init`, it was created at the root of that project."* - Added: *"Don't have one yet? Paste your project folder path instead and we'll create stack.config.ts for you."* - Recent-projects list (clickable rows that prefill the input) fetched from the new GET endpoint when the dialog opens. - OS-specific copy-path tip below the input (macOS ⌥-Copy as Pathname, Windows Shift+RC Copy as path, Linux `realpath`). - "Open project" button is disabled when the input is empty. - All error paths (empty input, non-absolute path, server errors, exceptions) surface via destructive toasts instead of throwing. Why no native file picker: browsers do not expose absolute filesystem paths from `<input type="file">`, drag-and-drop, or the File System Access API. The backend requires an absolute path, so a Finder-style picker isn't possible from a web page. The recent list + OS tips are the workaround. ### Goal The previous `init` flow dead-ended new users: if you had no project you got an error telling you to go create one in the dashboard and come back. The happy path also forced a choice between "link existing" and "create local emulator" — not the question most users are trying to answer. The emulator dashboard's open-project dialog had similar friction: an unexplained path field with no recall of previously-opened projects. And the CLI silently swallowed unexpected errors with no telemetry. This branch makes the first-run path work end-to-end from the terminal, gives the emulator dashboard a usable open-project surface, and turns CLI crashes into actionable bug reports. ### How to review - Start with `packages/stack-cli/src/commands/init.ts` — the whole user-facing flow lives in `runInit`. Mode dispatch at the top, `handleCreateCloud` is the new cloud branch, `printNextSteps` is the footer, the MCP notice prints right before `runClaudeAgent`. - `packages/stack-cli/src/lib/sentry.ts` is small and self-contained; the sentinel-replacement contract is in `tsdown.config.ts`'s `define` block. Confirm `dist/index.js` contains zero `__STACK_CLI_SENTRY_DSN__` occurrences after a build with the env var unset, and the actual DSN host after a build with it set. - `packages/stack-cli/src/commands/emulator.ts` — `printEmulatorWelcome()` is the welcome block; `isEmulatorImageInstalled()` is the new exported helper used by `init.ts`. - `apps/backend/src/app/api/latest/internal/local-emulator/project/route.tsx` — the directory-tolerance branch is in the POST handler around the `looksLikeConfigFile` check; the GET handler is appended at the bottom. - `apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx` — dialog markup, recent-list fetch effect, `pathCopyTip` memo, and the toast-based error handling in `handleOpenConfigFile`. - Non-interactive (CI) paths stay strict: empty-project list still errors with a pointer to `stack project create --display-name`. No surprise project creation in CI. - No tests. The CLI has no harness for the interactive flow; verification is manual. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Recent local emulator projects listed in the config dialog for quick selection. * New CLI create-cloud mode and --display-name flag; interactive cloud project creation and clearer next steps. * Emulator start shows a welcome banner with service URLs when a new instance starts. * **Improvements** * Config dialog UX, validation, error-toasting, and platform-aware copy refined; “Open project” disabled for empty/invalid paths. * CLI: centralized interactive project creation and improved fatal error handling. * **Chores** * Sentry added and initialized for CLI error reporting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Bilal Godil <bg2002@gmail.com> |
||
|
|
647883c7ac
|
Move MCP server into a standalone apps/mcp app (#1405)
## Summary Splits the Stack Auth MCP server out of `apps/backend` and into a dedicated Next.js app at `apps/mcp/`, served on port `:42` (suffixed via `NEXT_PUBLIC_STACK_PORT_PREFIX`) and exposed in production at `https://mcp.stack-auth.com/mcp`. The backend no longer carries the MCP transport route; clients now point at the new host. Base: `dev` → Head: `chore/move-mcp-to-a-sep-app` Scope: 34 files, +1425 / −353 ## What changed - **New app** `apps/mcp/` — standalone Next.js + `@vercel/mcp-adapter`, with: - `src/app/api/internal/[transport]/route.ts` — MCP transport handler (moved from backend) - `src/app/mcp/route.ts`, `src/app/route.ts` — public landing + setup page - `src/app/health/route.ts` — health check - `src/mcp-handler.ts`, `src/setup-page.ts`, `src/analytics.ts` - **Backend** drops `apps/backend/src/app/api/internal/[transport]/route.ts` (−105) — MCP code is gone from the backend image. - **Dashboard** install hint updated to point at `https://mcp.stack-auth.com/mcp` (was `/`). - **Dev launchpad** gets an MCP tile so the new service shows up alongside the rest of the local stack. - **CI** workflows (`db-migration-backwards-compatibility`, `e2e-api-tests*`) start the MCP service in the background before running tests. - **Docs** (`docs-mintlify`, `docs/`) and `init-stack` / `init-prompt` updated to reference the new URL. - **E2E** `apps/e2e/tests/backend/endpoints/api/v1/internal/mcp.test.ts` reworked to hit the new host; `helpers.ts` and env files gain an MCP base-URL var. ## Visuals ### New `apps/mcp` setup page (`https://mcp.stack-auth.com/`) The standalone app's root now serves a self-contained MCP setup guide with per-client instructions (Cursor, VS Code, Codex, Claude Code, Claude Desktop, Windsurf, ChatGPT, Gemini CLI):  ### Dev launchpad now lists the MCP service New tile at port suffix `:42`, importance 2, alongside Backend / Dashboard / Demo app:  ## Notes for reviewers - The MCP transport endpoint moved path: it was mounted under `/api/internal/[transport]` in the backend; in the new app it's at the same path but on the dedicated host. The public-facing URL is `https://mcp.stack-auth.com/mcp`. - `apps/mcp` ships its own PostHog analytics client (`src/analytics.ts`) so the backend doesn't have to proxy events for it anymore. - Port allocation: `${PORT_PREFIX}42` (default `8142` in dev). Picked to fit the existing dev-launchpad importance-2 row. - No DB migrations. ## Test plan - [x] `apps/mcp` builds and `pnpm dev` serves on `:8142` - [x] Dev launchpad renders the new MCP tile (screenshot above) - [x] MCP setup page renders client tabs (screenshot above) - [x] E2E `mcp.test.ts` updated to hit the new host - [ ] CI green on `e2e-api-tests*` and `db-migration-backwards-compatibility` workflows (they were touched to start the MCP service) - [ ] `init-stack` / `mcp.ts` install flow lands users on the new URL <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Standalone MCP app added with a public /mcp endpoint and health check. * MCP appears in the dev-launchpad apps list. * **Documentation** * MCP endpoint updated to https://mcp.stack-auth.com/mcp in all setup guides and installer snippets. * Setup page enhanced with detailed client install tabs and instructions. * **Chores** * MCP service integrated into CI/e2e workflows and local env configs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
616d805443
|
layout fix (#1408)
This PR fixes a layout bug <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Updated dashboard top-panel sizing to use viewport-aware height for a more consistent fit across screen sizes. * Improved dark-mode spacing to prevent clipping and ensure content remains fully visible without extra scrolling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5ccd8dfd38 | Update GitHub URL | ||
|
|
765b0f4e29
|
New setup (#1413) | ||
|
|
440c18c894 | chore: update package versions | ||
|
|
185bddec9e
|
[Dashboard] Redefine the user page with tabs and updated UI (#1351)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Tabbed user profile with Activity (30-day analytics, KPIs, daily chart, top lists, recent events), Payments (transactions, subscriptions, product/item balances) and an activity heatmap sidebar. * New internal user-activity API and admin-facing activity hook; admin API client can fetch per-user activity. * **UI/UX Improvements** * Unified menus, cards and tables; inline editable user details with accept/revert; metadata editor validates JSON; country-code input has draft editing; tabs support optional icons. * **API** * Transactions endpoint and admin transaction queries now support optional customer-scoped filtering. * **Tests** * End-to-end coverage for the user-activity endpoint. <!-- end of auto-generated comment: release notes by coderabbit.ai --> <img width="1326" height="752" alt="image" src="https://github.com/user-attachments/assets/97c04dca-db59-4357-98b1-8eae5a7a3673" /> <img width="1142" height="251" alt="image" src="https://github.com/user-attachments/assets/e1aa44fc-0d7e-436d-90a5-c7cb15155e24" /> <img width="1170" height="1125" alt="image" src="https://github.com/user-attachments/assets/bf6659fd-a9b5-4ae6-a13d-dab9956ad650" /> |
||
|
|
7a54e82865 | Revert globe coloring to old algorithm | ||
|
|
c01c052ac9
|
[Refactor][Feat] Implement Plan Limits for Hard-and-Soft Item Caps (#1215)
### Suggested Review Areas Please see `plans.ts` and `seed.ts` to verify whether the item caps are where they should be. Outside of that, each commit should be atomic so stepping through the commits should give you an idea of how I implemented each limit. ### Discussion Something to discuss: when a user cancels team/growth we regrant free fine, but any extra-seats they had just keeps billing. So they end up paying ~$29/mo per extra-seat on top of free's 1 seat, which is strictly worse than just staying on team. This surfaced while manually testing this PR, we only enforce the add-on base requirement at purchase time, nothing cascades on cancel. Should we cascade cancel add ons? ### Context Now that we have a stable suite of products for stack-auth, we want to limit the items under each product a customer has access to based on their plan. So for example, a free plan user has a certain amount of emails they can send out each month, and so on. We try to implement limits in this PR. ### Summary of Changes Implemented hard limits for dashboard admins, analytics per-query timeouts, sent email monthly capacity, events, and session replays. Implemented a soft cap for auth users (where if there's a signup beyond the limit, we log it to sentry so we can manually choose to email that user/team). For auth users, we do not block new user sign ups once plan limit has been hit. We also don't degrade or impact the customer experience. It logs to sentry and it is up to us to take manual action to email the user to upgrade the plan. Also, implementation wise, we count all the users across all the projects for this team and compare it to their plan item limit, rather than debiting items like we do for other approaches. As a soft cap, this should be fine plus this is a better source of truth. For email capacity, we operate a monthly limit of emails. Once this is hit, no more emails can be sent until the next month/ a plan upgrade. These emails will be treated as a send error, so they can be manually resent once the capacity is reset. With respect to the `email-queue` state engine, they go from `SENDING`->`SERVER_ERROR`, hooking into the existing state engine flow, with an external error that shows it's because of the rate limit. This is cleaner than inventing a new state that is identical for all intents and purposes to `SERVER_ERROR`. We check in processSingleEmail since that maps to the sending state. For analytics query timeouts, the backend route accepts a timeout parameter with the request. The way we implement the timeout for each query is by taking the `min(request_timeout,plan_timeout)` and using that. This determines how long a query can run for. For analytics events, there are server-side events (like refresh token refreshes or sign up rule triggers) and client side events (like page views or clicks). When these events occur, they are written to the events table in clickhouse. We choose to implement a hard cap for the total events, not just server side or client side. Once the cap is hit, we stop storing the events and display a banner on the analytics page. A different banner renders when we are at >=80% of total plan capacity. For session replays, we stop creating new session replays when the limit is hit. Old replays can still have chunks appended to them. The source of truth here is the session replay table- a new replay corresponds to a new row in the table. We have similar banners as to the events. Dashboard admins should be 4 for both team and unlimited. #### Implementation Caveats For debiting items across these limits, we now use `tryDecreaseQuantity` at the beginning. This means we debit first if possible before conducting the action (like writing events to clickhouse). In practice, this means that if clickhouse fails, then the user is debited for something that doesn't happen. However trying to build a refund workaround would be very clunky, and also, clickhouse is reliable. For debits that are very small in the order of things (say, 200 items on a 100k plan), it doesn't mean much. For emails, we don't debit items if it's a retry. This prevents the user for being charged multiple times for effectively one email. ### UI Changes The only UI changes in this PR are having certain banners render in analytics when a customer is approaching/ is at their monthly limit of session replays or events. ### Out of Scope for this PR We do not have metered pricing yet, so events/session replays/ email use beyond the limits cannot be charged yet. This is why for this implementation, we rely on hard and soft caps. We do not implement payment per-transaction pricing yet. That is deferred to a followup PR. The UI for the onboarding call will be set up as part of the overall onboarding flow which doesn't exist yet, so it has been deferred. Since the UI for the dashboard home page and project/account settings is currently being reworked, finding a better spot for plan upgrades is not handled in this PR. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Session replays added as a monthly included entitlement; onboarding calls added to Team/Growth plans. Dashboard banners warn about analytics-event and session-replay limits. Projects page adds extra-seat flow and improved invitation error handling. * **Behavior Changes** * Monthly renewal semantics for emails-per-month and analytics-events; analytics query timeouts now respect plan limits and are clamped. Email sends, analytics events, and new session creation are blocked when quotas are exhausted. Growth plan seats set to 4. * **Tests** * E2E and unit tests added to verify quota enforcement and free-plan regranting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com> |
||
|
|
9f79bfbe5c
|
fix(dashboard): collapsed email editor height; sandbox email-preview iframes (#1406)
## Summary Two small dashboard fixes bundled together. ### 1. Email editor renders with zero height The email template/theme/draft pages render `VibeCodeLayout`, whose mobile and desktop root wrappers used `h-full`. The dashboard shell's `<main>` (`sidebar-layout.tsx:750`) has no explicit height — its flex parent uses `items-start`, so `<main>` shrinks to its content rather than stretching. With no definite height up the chain, every `h-full` along the way (sidebar-layout's inner div, the `data-full-bleed` wrapper, `VibeCodeLayout`'s own root) resolves to `auto`, and since the editor's content lives inside absolutely-positioned `ResizablePanel`s, the wrapper collapses to ~0. **Fix:** anchor `VibeCodeLayout`'s root wrappers to viewport-minus-header instead of `h-full`. The values match what `sidebar-layout.tsx:738` already uses for the sticky sidebar (`3.5rem` light / `6rem` dark for the floating header card). With a definite height at the top, the existing `flex-1` chains inside `VibeCodeLayout` resolve correctly without any layout/architecture refactor in the surrounding dashboard shell. ```diff -<div className="flex flex-col h-full w-full overflow-hidden md:hidden"> +<div className="flex flex-col h-[calc(100dvh-3.5rem)] w-full overflow-hidden md:hidden"> -<div className="hidden md:flex flex-col h-full w-full overflow-hidden"> +<div className="hidden md:flex flex-col h-[calc(100vh-3.5rem)] dark:h-[calc(100vh-6rem)] w-full overflow-hidden"> ``` Trade-off: the editor knows the dashboard header is `3.5rem` (`6rem` dark). The same numbers are already hardcoded in `sidebar-layout.tsx`, so this isn't a new coupling. ### 2. Sandbox the email-preview iframes `EmailPreviewContent` and `EmailPreviewEditableContent` rendered user-authored template HTML in iframes with no `sandbox` at all. With `srcDoc`-rendered iframes treated as same-origin by default, that meant any `<script>` (or `onerror=`, `javascript:` URL, etc.) inside a template could read the dashboard's cookies/localStorage and call the API as the viewing admin. Set `sandbox="allow-scripts"` on both iframes: - Iframe is forced into a unique opaque origin → no access to parent cookies, `localStorage`, `sessionStorage`, or DOM. - No `allow-same-origin`, so credentialed fetches to the dashboard API don't carry the user's session (cookies aren't sent to a third-party opaque origin under default `SameSite=Lax`; cross-origin responses also unreadable due to CORS). - No `allow-top-navigation` / `allow-forms` / `allow-popups` → template can't redirect the parent tab, submit forms, or open windows. - `allow-scripts` is required so the inline scripts we inject (link-click prevention; the WYSIWYG editor that drives the `postMessage` flow at `email-preview.tsx:413-435` and `:625-672`) can actually run. Without it, the editor itself was broken and links navigated freely. Note: `allow-scripts allow-same-origin` together would be equivalent to no sandbox at all (the iframe could rewrite its own `sandbox` attribute and escape), so we deliberately omit `allow-same-origin`. **Residual risk (not addressed in this PR):** a malicious template script can still `postMessage` a fake `stack_edit_commit` to the parent — the parent's `e.source === iframeWindow` check passes because the script *is* running in that iframe. The viewing admin would silently apply attacker-chosen source-code edits on save. That's a cross-admin UI-redress concern, not token exfiltration, and is best fixed with a CSP nonce on the injected script (so user template `<script>` tags can't run at all). Tracking as a follow-up. ## Test plan - [ ] Open an email template editor — verify the preview, code panel, and chat panel are all visible at full height (light + dark mode). - [ ] Same for an email theme editor and an email draft editor in the `draft` stage. - [ ] Resize the window vertically — editor should fill the viewport below the header without overflowing past the bottom. - [ ] Click a link inside the rendered preview — should not navigate (link-click prevention script works under `allow-scripts`). - [ ] In edit mode, hover an editable text region, click to edit, type a change, hit ✓ — change should round-trip through `postMessage` and update the source. - [ ] Sanity check: paste `<script>document.title='pwned'</script>` (or `<img onerror=...>`) into a template, render preview — parent tab title/cookies/etc. should be untouched (script runs in opaque origin, can't reach parent). |