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.
This commit is contained in:
KooshaPari
2026-07-05 01:37:42 -07:00
committed by GitHub
parent 7e2b839935
commit 0e0ca7e26b
4 changed files with 86 additions and 8 deletions

View File

@@ -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

View File

@@ -22,6 +22,7 @@ import {
getWizardOAuthProviderOptions,
type WizardProviderOption,
} from "./providerOnboardingCatalog";
import { buildProviderDetailsHref } from "./providerOnboardingHref";
import {
createCompatibleProviderNode,
createOnboardingConnection,
@@ -260,14 +261,19 @@ function ResultSummary({
)}
<div className="flex flex-wrap gap-2">
{connection?.id && (
<Link
href={`/dashboard/providers/${connection.id}`}
className="inline-flex items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary/90"
>
{providerText(t, "onboardingOpenProviderDetails", "Open provider details")}
</Link>
)}
{(() => {
const detailsHref = buildProviderDetailsHref(connection);
return (
detailsHref && (
<Link
href={detailsHref}
className="inline-flex items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary/90"
>
{providerText(t, "onboardingOpenProviderDetails", "Open provider details")}
</Link>
)
);
})()}
<Link
href="/dashboard/providers"
className="inline-flex items-center justify-center rounded-lg border border-border bg-bg-subtle px-4 py-2 text-sm font-medium text-text-main transition-colors hover:bg-bg-card"

View File

@@ -0,0 +1,33 @@
import type { OnboardingConnection } from "./providerOnboardingApi";
export interface OnboardingSummaryAction {
href: string | null;
label: string;
}
/**
* Minimum surface area required to compute the dashboard details href.
* Accepts either a full {@link OnboardingConnection} or any partial that
* exposes the server-assigned UUID under `id`.
*/
export type HrefConnection = Pick<OnboardingConnection, "id"> & {
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)}`;
}

View File

@@ -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");
});