From 0e0ca7e26be3eab7a786a483d71a5740d533897a Mon Sep 17 00:00:00 2001 From: KooshaPari <42529354+KooshaPari@users.noreply.github.com> Date: Sun, 5 Jul 2026 01:37:42 -0700 Subject: [PATCH] fix(dashboard): use connection.id (UUID) not connection.provider (category) in onboarding wizard href (issue #6144) (#6166) refactor(dashboard): extract tested buildProviderDetailsHref helper for onboarding wizard (#6166). Behavioral #6144 fix already on tip via #6145; this lands the tested-helper hardening. Thanks @KooshaPari. Integrated into release/v3.8.45. --- CHANGELOG.md | 2 + .../onboarding/ProviderOnboardingWizard.tsx | 22 +++++++---- .../onboarding/providerOnboardingHref.ts | 33 +++++++++++++++++ tests/unit/provider-onboarding-href.test.ts | 37 +++++++++++++++++++ 4 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingHref.ts create mode 100644 tests/unit/provider-onboarding-href.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc1e627c2..bfa214eb1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ - **fix(security):** the mutable cloud-agent routes (`/api/cloud/credentials/update`, `/api/cloud/models/alias`) now require management auth instead of being treated as public. They were classified as public API routes, so a request without management credentials could update stored cloud-agent credentials and model aliases. They are removed from the public-route set, classified as management routes in the authz pipeline, and gated by `requireManagementAuth`; cloud **read**/auth routes stay public. Regression guards: `tests/unit/cloud-write-auth.test.ts`, `tests/unit/authz/classify.test.ts`, `tests/unit/public-api-routes.test.ts`. ([#6233](https://github.com/diegosouzapw/OmniRoute/pull/6233) — thanks @vittoroliveira-dev) +- **refactor(dashboard):** extract the onboarding-wizard "Open provider details" link target into a pure, unit-tested `buildProviderDetailsHref(connection)` helper. The wizard already routes by `connection.id` (the node UUID) rather than the provider category slug (#6144/#6145); this hardens that behavior behind a tested helper that guards a missing id/connection. Regression guard: `tests/unit/provider-onboarding-href.test.ts`. ([#6166](https://github.com/diegosouzapw/OmniRoute/pull/6166) — thanks @KooshaPari) + --- ## [3.8.43] — 2026-07-02 diff --git a/src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx b/src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx index 65510ef0ce..4a44da1a89 100644 --- a/src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx +++ b/src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx @@ -22,6 +22,7 @@ import { getWizardOAuthProviderOptions, type WizardProviderOption, } from "./providerOnboardingCatalog"; +import { buildProviderDetailsHref } from "./providerOnboardingHref"; import { createCompatibleProviderNode, createOnboardingConnection, @@ -260,14 +261,19 @@ function ResultSummary({ )}
- {connection?.id && ( - - {providerText(t, "onboardingOpenProviderDetails", "Open provider details")} - - )} + {(() => { + const detailsHref = buildProviderDetailsHref(connection); + return ( + detailsHref && ( + + {providerText(t, "onboardingOpenProviderDetails", "Open provider details")} + + ) + ); + })()} & { + provider?: string; +}; + +/** + * Build the "open provider details" link target for the onboarding wizard + * success card. The dashboard detail route is keyed by the connection's + * server-assigned UUID (`OnboardingConnection.id`), not by the provider + * category (`connection.provider` is e.g. "openai-compatible" and is shared + * across many connections). + * + * Returns `null` if no usable id is present so callers can hide the action + * entirely rather than linking to a 404. + */ +export function buildProviderDetailsHref( + connection: HrefConnection | null | undefined +): string | null { + const id = connection?.id?.trim(); + if (!id) return null; + return `/dashboard/providers/${encodeURIComponent(id)}`; +} \ No newline at end of file diff --git a/tests/unit/provider-onboarding-href.test.ts b/tests/unit/provider-onboarding-href.test.ts new file mode 100644 index 0000000000..95548d0cab --- /dev/null +++ b/tests/unit/provider-onboarding-href.test.ts @@ -0,0 +1,37 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { buildProviderDetailsHref } from "../../src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingHref"; + +test("buildProviderDetailsHref uses the server-assigned connection UUID", () => { + const href = buildProviderDetailsHref({ + id: "9f3c1a4d-1c2b-4a3c-8def-0123456789ab", + }); + assert.equal( + href, + "/dashboard/providers/9f3c1a4d-1c2b-4a3c-8def-0123456789ab" + ); +}); + +test("buildProviderDetailsHref does not leak the provider category into the URL", () => { + // Regression for issue #6144: previously the wizard used + // `connection.provider` ("openai-compatible") as the URL slug, which 404'd + // on /dashboard/providers/[id] because that route is keyed by UUID. + const href = buildProviderDetailsHref({ + id: "9f3c1a4d-1c2b-4a3c-8def-0123456789ab", + provider: "openai-compatible", + }); + assert.notEqual(href, "/dashboard/providers/openai-compatible"); + assert.match(href ?? "", /\/dashboard\/providers\/[0-9a-f-]+$/); +}); + +test("buildProviderDetailsHref returns null when no id is available", () => { + assert.equal(buildProviderDetailsHref(null), null); + assert.equal(buildProviderDetailsHref(undefined), null); + assert.equal(buildProviderDetailsHref({ id: "" }), null); + assert.equal(buildProviderDetailsHref({ id: " " }), null); +}); + +test("buildProviderDetailsHref percent-encodes unusual ids", () => { + const href = buildProviderDetailsHref({ id: "abc/123 def" }); + assert.equal(href, "/dashboard/providers/abc%2F123%20def"); +}); \ No newline at end of file