refactor(ui): point invitation links at the dedicated /onboarding route - #30857
Conversation
Invitation and reset-password links were built as /ui?invitation_id=..., which lands on the dashboard index and renders the onboarding form inline. They now point at the standalone /ui/onboarding route, so the index no longer has to special-case invitations. Old links keep working unchanged; the index still renders onboarding inline for ?invitation_id until the migration closeout removes that branch. Updates the three generators (the enterprise email builder, bulk user create, and the invitation/reset-password modal) and extracts the modal's URL building into a pure, unit-tested buildOnboardingUrl Refs LIT-3687
Greptile SummaryThis PR updates all three invitation/reset-password link generators to point at the dedicated
Confidence Score: 5/5Safe to merge — all three link generators are updated consistently, old links remain functional, and the new URL construction logic is verified by dedicated unit tests. The change is narrow and mechanical: a path segment is replaced in three places, an existing inline function is extracted and made testable, and every backend assertion that pinned the old format is updated in lock-step. The backward-compatibility guarantee (index still renders the onboarding form for old links) is preserved by design. No auth paths, data-handling logic, or critical request paths are touched. No files require special attention.
|
| Filename | Overview |
|---|---|
| enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py | Single-line URL change: /ui?invitation_id= → /ui/onboarding?invitation_id= in _construct_invitation_link; straightforward and correct. |
| tests/test_litellm/enterprise/enterprise_callbacks/send_emails/test_base_email.py | Seven assertion strings updated from the old /ui?invitation_id= format to the new /ui/onboarding?invitation_id= format; one mock return value updated in parallel — all changes correctly track the new intended behavior without weakening coverage. |
| ui/litellm-dashboard/src/components/bulk_create_users_button.tsx | One-line URL path update from /ui?invitation_id= to /ui/onboarding?invitation_id= in the bulk-create invitation URL builder. |
| ui/litellm-dashboard/src/components/onboarding_link.tsx | Extracts the URL-building logic into an exported buildOnboardingUrl pure function (with guards for missing baseUrl and undefined invitationId) and delegates getInvitationUrl to it; the routing path changes from /ui to /ui/onboarding for non-SSO users. |
| ui/litellm-dashboard/src/components/onboarding_link.test.tsx | New test file covering all key branches of buildOnboardingUrl: basic case, server_root_path prefix, reset_password action, SSO redirect, empty baseUrl, and undefined invitationId guard. |
Reviews (2): Last reviewed commit: "refactor(ui): guard buildOnboardingUrl a..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Return "" instead of emitting an invitation_id=undefined link when the id is not yet available, matching the existing empty-baseUrl guard. Placed after the SSO branch so the SSO link, which does not use the id, is unaffected Refs LIT-3687
|
@greptileai re review |
febb276
into
litellm_internal_staging
…te (BerriAI#30857) * refactor(ui): point invitation links at the dedicated /onboarding route Invitation and reset-password links were built as /ui?invitation_id=..., which lands on the dashboard index and renders the onboarding form inline. They now point at the standalone /ui/onboarding route, so the index no longer has to special-case invitations. Old links keep working unchanged; the index still renders onboarding inline for ?invitation_id until the migration closeout removes that branch. Updates the three generators (the enterprise email builder, bulk user create, and the invitation/reset-password modal) and extracts the modal's URL building into a pure, unit-tested buildOnboardingUrl Refs LIT-3687 * refactor(ui): guard buildOnboardingUrl against a missing invitation id Return "" instead of emitting an invitation_id=undefined link when the id is not yet available, matching the existing empty-baseUrl guard. Placed after the SSO branch so the SSO link, which does not use the id, is unaffected Refs LIT-3687
Relevant issues
Linear ticket
Refs LIT-3687
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
make test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Screenshots / Proof of Fix
This is a UI routing change with no LLM calls, so the proof is the onboarding flow itself against a live proxy. Run the proxy, then walk the suite below. Steps that need a raw
invitation_idcan mint one with curl; the rest are UI clicks. Use an Incognito window for every "open the link" step, because an authenticated session masks the login gate that the invitation flow is supposed to bypass.Setup:
Mint a user + invitation when a step needs a raw id:
A. New invitation link uses the dedicated route (UI)
.../ui/onboarding?invitation_id=...and no longer.../ui?invitation_id=...invitation_linkin the results table is also the/ui/onboardingformB. New link works end to end (Incognito)
3. Open the
new formatURL printed above. Expect: the set-password form renders, no sidebar/navbar, and no redirect to/ui/login4. Set a password and submit. Expect a redirect to
.../ui/?login=success5. Log in as
invitee-local@example.comwith that password; you should land in the dashboardC. Reset-password variant
6. For an existing user, generate a reset-password link (Internal Users -> the user -> reset password). Confirm the link is
.../ui/onboarding?invitation_id=...&action=reset_password7. Open it in Incognito; the form header should read "Reset Password" (the reset variant), not "Sign Up"
D. Backward compatibility for already-sent emails
8. Open the
old formatURL (/ui/?invitation_id=...) in Incognito. It must still render the onboarding form inline with no login bounce, so links already delivered to users keep workingE. SSO and server_root_path (optional)
9. With an SSO-enabled user, the invitation modal link should point at
/ui(the SSO login entry), not/ui/onboarding10. If you run the proxy under
SERVER_ROOT_PATH=/litellm, the modal link should preserve the prefix:.../litellm/ui/onboarding?invitation_id=...Negative control: open
http://localhost:4000/ui/in Incognito with noinvitation_idand no cookie; it should bounce to login, confirming the gate only opens for invitations.Type
🧹 Refactoring
Changes
Invitation and reset-password links were generated as
/ui?invitation_id=..., which lands on the dashboard index ((dashboard)/page.tsx) and renders the onboarding form inline throughUserDashboard. This points all three generators at the standalone/ui/onboardingroute that already serves that same form, so the index no longer has to special-caseinvitation_id. It is the first step of the App Router migration closeout (decoupling onboarding from the index); the index keeps rendering onboarding inline for old?invitation_idlinks, so nothing already delivered to a user breaks. Removing that inline branch and adding a forwarding redirect is a later closeout step.Generators updated: the enterprise email builder (
base_email.py:_construct_invitation_link), bulk user create (bulk_create_users_button.tsx), and the invitation/reset-password modal (onboarding_link.tsx). The modal's URL building is extracted into a purebuildOnboardingUrlso the server_root_path prefix, thereset_passwordaction, and the SSO branch are covered by a focused unit test. The seven backend assertions that pinned the old/ui?invitation_id=string are updated to the new format, locking it in as a regression anchor.