mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 04:32:31 +03:00
fix(dashboard): hide disabled provider connections from combo builder
The combos page's fetchData() only filtered available connections by
testStatus ("active"/"success"), so a connection the user had
explicitly disabled (isActive: false) could still show up in the
combo builder if it carried a stale testStatus from before it was
disabled.
Add filterActiveConnections() in src/shared/utils/connectionStatus.ts
and apply it ahead of the existing testStatus filter.
Co-authored-by: itolstov <attid0@gmail.com>
Inspired-by: https://github.com/decolua/9router/pull/2526
This commit is contained in:
@@ -13,6 +13,7 @@ import Modal from "@/shared/components/Modal";
|
||||
import Toggle from "@/shared/components/Toggle";
|
||||
import Tooltip from "@/shared/components/Tooltip";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
import { filterActiveConnections } from "@/shared/utils/connectionStatus";
|
||||
import { FieldLabelWithHelp, WeightTotalBar } from "./parts";
|
||||
import { ResponseValidationEditor, type ResponseValidationValue } from "./ResponseValidationEditor";
|
||||
import ReasoningTokenBufferToggle from "./ReasoningTokenBufferToggle";
|
||||
@@ -768,7 +769,10 @@ export default function CombosPage() {
|
||||
|
||||
if (combosRes.ok) setCombos((combosData.combos || []).filter((c) => !c.isHidden));
|
||||
if (providersRes.ok) {
|
||||
const active = (providersData.connections || []).filter(
|
||||
// Exclude connections the user has explicitly disabled (isActive === false)
|
||||
// before applying the test-status filter — a disabled connection can still
|
||||
// carry a stale "active"/"success" testStatus from before it was disabled.
|
||||
const active = filterActiveConnections(providersData.connections || []).filter(
|
||||
(c) => c.testStatus === "active" || c.testStatus === "success"
|
||||
);
|
||||
setActiveProviders(active);
|
||||
|
||||
26
src/shared/utils/connectionStatus.ts
Normal file
26
src/shared/utils/connectionStatus.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Shared helpers for filtering/classifying provider connections by their
|
||||
* active/disabled state, independent of their last test result.
|
||||
*
|
||||
* A connection can have `isActive: false` (explicitly disabled by the user)
|
||||
* while still carrying a stale `testStatus` of "active"/"success" from
|
||||
* before it was disabled — callers that only filter on `testStatus` will
|
||||
* incorrectly keep serving disabled connections.
|
||||
*/
|
||||
|
||||
export interface ConnectionActiveFlag {
|
||||
isActive?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out connections that have been explicitly disabled
|
||||
* (`isActive === false`). Connections without an `isActive` field are
|
||||
* treated as active for backward compatibility.
|
||||
*/
|
||||
export function filterActiveConnections<T extends ConnectionActiveFlag>(
|
||||
connections: T[] | null | undefined
|
||||
): T[] {
|
||||
if (!Array.isArray(connections)) return [];
|
||||
return connections.filter((connection) => connection?.isActive !== false);
|
||||
}
|
||||
47
tests/unit/connection-status-filter-active-2526.test.ts
Normal file
47
tests/unit/connection-status-filter-active-2526.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { filterActiveConnections } from "@/shared/utils/connectionStatus";
|
||||
|
||||
// Ported from decolua/9router#2526 — the combos builder listed provider
|
||||
// connections the user had explicitly disabled, because the page only
|
||||
// filtered on the connection's last `testStatus` and ignored `isActive`.
|
||||
// A disabled connection can still carry a stale "active"/"success"
|
||||
// testStatus from before it was disabled.
|
||||
|
||||
test("filterActiveConnections excludes explicitly disabled connections", () => {
|
||||
const active = { id: "active", isActive: true };
|
||||
const legacyActive = { id: "legacy" }; // no isActive field -> treated as active
|
||||
const disabled = { id: "disabled", isActive: false };
|
||||
|
||||
assert.deepEqual(filterActiveConnections([active, disabled, legacyActive]), [
|
||||
active,
|
||||
legacyActive,
|
||||
]);
|
||||
});
|
||||
|
||||
test("filterActiveConnections returns an empty list for invalid input", () => {
|
||||
assert.deepEqual(filterActiveConnections(undefined), []);
|
||||
assert.deepEqual(filterActiveConnections(null), []);
|
||||
});
|
||||
|
||||
test("combos page fetchData filter mirrors filterActiveConnections + testStatus gate", () => {
|
||||
// Regression for the exact bug: a disabled connection with a stale
|
||||
// "active" testStatus must NOT survive the combined filter used in
|
||||
// src/app/(dashboard)/dashboard/combos/page.tsx fetchData().
|
||||
const connections = [
|
||||
{ id: "healthy", isActive: true, testStatus: "active" },
|
||||
{ id: "disabled-but-stale-status", isActive: false, testStatus: "active" },
|
||||
{ id: "disabled-success-status", isActive: false, testStatus: "success" },
|
||||
{ id: "enabled-not-tested", isActive: true, testStatus: "untested" },
|
||||
];
|
||||
|
||||
const result = filterActiveConnections(connections).filter(
|
||||
(c) => c.testStatus === "active" || c.testStatus === "success"
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
result.map((c) => c.id),
|
||||
["healthy"]
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user