mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)}`;
|
||||
}
|
||||
37
tests/unit/provider-onboarding-href.test.ts
Normal file
37
tests/unit/provider-onboarding-href.test.ts
Normal 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");
|
||||
});
|
||||
Reference in New Issue
Block a user