mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
Compare commits
3 Commits
refactor/e
...
fix/10096-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb340e2a47 | ||
|
|
8179ed630e | ||
|
|
ff6d465140 |
1
changelog.d/fixes/10096-kimi-coding-apikey-save.md
Normal file
1
changelog.d/fixes/10096-kimi-coding-apikey-save.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
- fix(dashboard): remap unified Kimi Code card API-key save to the admitted `kimi-coding-apikey` connection id, fixing 400 "Invalid provider" on Save (#10096)
|
||||||
@@ -435,7 +435,7 @@ export default function AddApiKeyModal({
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
provider,
|
provider: provider === "kimi-coding" ? "kimi-coding-apikey" : provider,
|
||||||
entries: parsed.entries.map((e) => ({
|
entries: parsed.entries.map((e) => ({
|
||||||
name: e.name,
|
name: e.name,
|
||||||
apiKey: e.apiKey,
|
apiKey: e.apiKey,
|
||||||
|
|||||||
@@ -241,6 +241,49 @@ describe("conn-modals (Phase 1c extraction)", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("AddApiKeyModal remaps Kimi Code bulk API-key additions to the admitted provider id", async () => {
|
||||||
|
const fetchMock = vi.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({ success: 1, failed: 0, total: 1, errors: [] }),
|
||||||
|
text: async () => "",
|
||||||
|
} as Response)
|
||||||
|
);
|
||||||
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
const c = renderModal(
|
||||||
|
<AddApiKeyModal
|
||||||
|
isOpen={true}
|
||||||
|
provider="kimi-coding"
|
||||||
|
providerName="Kimi Code"
|
||||||
|
isCompatible={false}
|
||||||
|
onSave={vi.fn().mockResolvedValue(undefined)}
|
||||||
|
onClose={vi.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const bulkTab = Array.from(c.querySelectorAll("button")).find(
|
||||||
|
(button) => button.textContent === "providers.bulkTabBulkAdd"
|
||||||
|
);
|
||||||
|
act(() => bulkTab!.click());
|
||||||
|
const bulkInput = c.querySelector<HTMLTextAreaElement>("textarea");
|
||||||
|
setTextareaValue(bulkInput!, "main|sk-kimi-test");
|
||||||
|
const submitButton = Array.from(c.querySelectorAll("button")).find(
|
||||||
|
(button) => button.textContent === "providers.bulkAddAllKeys"
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
submitButton!.click();
|
||||||
|
await Promise.resolve();
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith(
|
||||||
|
"/api/providers/bulk",
|
||||||
|
expect.objectContaining({
|
||||||
|
body: expect.stringContaining('"provider":"kimi-coding-apikey"'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("AddApiKeyModal does not infer a regional provider selection", () => {
|
it("AddApiKeyModal does not infer a regional provider selection", () => {
|
||||||
const c = renderModal(
|
const c = renderModal(
|
||||||
<AddApiKeyModal
|
<AddApiKeyModal
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ type UseApiKeySaveParams = {
|
|||||||
t: ProviderMessageTranslator;
|
t: ProviderMessageTranslator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Issue #10096: the unified Kimi Code dashboard card shares one page/providerId
|
||||||
|
// ("kimi-coding") between OAuth and API-key auth. "kimi-coding" is an
|
||||||
|
// OAuth-primary managed id and is NOT an admitted API-key/dual-auth connection
|
||||||
|
// id (see isManagedProviderConnectionId in src/lib/providers/catalog.ts), so
|
||||||
|
// posting it here 400s with "Invalid provider". The dedicated managed
|
||||||
|
// API-key id "kimi-coding-apikey" IS admitted — remap only the POST payload
|
||||||
|
// so the saved connection lands under the correct managed id. The OAuth flow
|
||||||
|
// (handleOAuthSuccess in ProviderDetailPageClient.tsx) does not go through
|
||||||
|
// this hook, so it keeps posting "kimi-coding" unchanged.
|
||||||
|
export function resolveApiKeySaveProviderId(providerId: string): string {
|
||||||
|
return providerId === "kimi-coding" ? "kimi-coding-apikey" : providerId;
|
||||||
|
}
|
||||||
|
|
||||||
export function useApiKeySave({
|
export function useApiKeySave({
|
||||||
providerId,
|
providerId,
|
||||||
fetchConnections,
|
fetchConnections,
|
||||||
@@ -48,7 +61,10 @@ export function useApiKeySave({
|
|||||||
const res = await fetch("/api/providers", {
|
const res = await fetch("/api/providers", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ provider: providerId, ...formData }),
|
body: JSON.stringify({
|
||||||
|
provider: resolveApiKeySaveProviderId(providerId),
|
||||||
|
...formData,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const connectionData = await res.json();
|
const connectionData = await res.json();
|
||||||
|
|||||||
37
tests/unit/bug-10096-kimi-coding-apikey-save.test.ts
Normal file
37
tests/unit/bug-10096-kimi-coding-apikey-save.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
// Issue #10096: Kimi Code API key validates OK but Save returns 400 "Invalid provider".
|
||||||
|
//
|
||||||
|
// Root cause: the unified Kimi Code dashboard card's API-key branch posted
|
||||||
|
// provider: "kimi-coding" (an OAuth-primary managed id, NOT an admitted
|
||||||
|
// API-key connection id) to POST /api/providers, which the backend rejects.
|
||||||
|
// The dedicated managed API-key id "kimi-coding-apikey" IS admitted.
|
||||||
|
//
|
||||||
|
// Fix: resolveApiKeySaveProviderId() in useApiKeySave.ts remaps the posted
|
||||||
|
// provider id to "kimi-coding-apikey" for the API-key save flow only, while
|
||||||
|
// the OAuth flow (which never calls this hook) keeps posting "kimi-coding".
|
||||||
|
|
||||||
|
const { isManagedProviderConnectionId } = await import("../../src/lib/providers/catalog.ts");
|
||||||
|
const { resolveApiKeySaveProviderId } = await import(
|
||||||
|
"../../src/app/(dashboard)/dashboard/providers/[id]/hooks/useApiKeySave.ts"
|
||||||
|
);
|
||||||
|
|
||||||
|
test("Kimi Code API-key save flow remaps to the admitted managed API-key id", () => {
|
||||||
|
assert.equal(
|
||||||
|
resolveApiKeySaveProviderId("kimi-coding"),
|
||||||
|
"kimi-coding-apikey",
|
||||||
|
"the unified Kimi Code card's API-key save flow must post kimi-coding-apikey, not kimi-coding"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
isManagedProviderConnectionId(resolveApiKeySaveProviderId("kimi-coding")),
|
||||||
|
true,
|
||||||
|
"the remapped id must be an admitted managed provider connection id (POST /api/providers accepts it)"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("resolveApiKeySaveProviderId leaves every other provider id untouched", () => {
|
||||||
|
assert.equal(resolveApiKeySaveProviderId("openai"), "openai");
|
||||||
|
assert.equal(resolveApiKeySaveProviderId("kimi-coding-apikey"), "kimi-coding-apikey");
|
||||||
|
assert.equal(resolveApiKeySaveProviderId("qoder"), "qoder");
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user