mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 12:22:34 +03:00
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
This commit is contained in:
committed by
GitHub
parent
8939ccbae3
commit
47032e7769
@@ -0,0 +1 @@
|
||||
- **fix(providers):** correct Magnific API key validation, which reported every valid key as invalid due to a GET probe against a POST-only endpoint (#12927) — thanks @hubo1989
|
||||
@@ -30,9 +30,13 @@ const IMAGE_PROVIDER_VALIDATION_ENDPOINTS: Record<
|
||||
path: "/account/v1/credits/balance",
|
||||
},
|
||||
magnific: {
|
||||
// GET /v1/ai/mystic lists tasks and does not start a paid generation.
|
||||
// GET /v1/ai/mystic is POST-only (task submission); once a key authenticates,
|
||||
// routing to that unhandled GET 404s, reporting every valid key as invalid
|
||||
// (#12927). GET /v1/ai/flows is a genuine read-only route that returns 200 for
|
||||
// valid keys (team AND personal accounts) and 401 for invalid/missing keys,
|
||||
// verified against a real Premium+ personal account by the issue reporter.
|
||||
baseUrl: "https://api.magnific.com",
|
||||
path: "/v1/ai/mystic",
|
||||
path: "/v1/ai/flows",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
80
tests/unit/issue-12927-magnific-validation-probe.test.ts
Normal file
80
tests/unit/issue-12927-magnific-validation-probe.test.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
// Repro for GitHub issue #12927: the Magnific image-provider key validation probe
|
||||
// sends GET /v1/ai/mystic, which is a POST-only task-submission route. Once
|
||||
// authentication passes, the API has no GET handler for that path and returns 404,
|
||||
// so a VALID key is reported as invalid ("Validation failed: 404").
|
||||
//
|
||||
// Run: DATA_DIR=$(mktemp -d) node --import tsx/esm --test --test-force-exit \
|
||||
// tests/unit/issue-12927-magnific-validation-probe.test.ts
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
|
||||
// Simulates the real Magnific API auth-before-routing behavior described by the
|
||||
// reporter: a bad key gets 401 on every route; a valid key gets routed and the
|
||||
// POST-only /v1/ai/mystic path answers 404 to a GET, while /v1/ai/flows answers 200.
|
||||
function mockMagnificFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||
const url = String(input instanceof URL ? input.toString() : input);
|
||||
const method = String(init?.method || "GET").toUpperCase();
|
||||
const headers = new Headers(init?.headers);
|
||||
const apiKey = headers.get("x-magnific-api-key");
|
||||
|
||||
if (apiKey !== "valid-magnific-key") {
|
||||
return Promise.resolve(
|
||||
new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 })
|
||||
);
|
||||
}
|
||||
|
||||
if (url === "https://api.magnific.com/v1/ai/mystic" && method === "GET") {
|
||||
// Valid key, but this route is POST-only -> 404 once routed past auth.
|
||||
return Promise.resolve(new Response(JSON.stringify({ error: "Not found" }), { status: 404 }));
|
||||
}
|
||||
|
||||
if (url === "https://api.magnific.com/v1/ai/flows" && method === "GET") {
|
||||
// The reporter's verified working read-only probe for personal accounts.
|
||||
return Promise.resolve(new Response(JSON.stringify({ flows: [] }), { status: 200 }));
|
||||
}
|
||||
|
||||
return Promise.resolve(new Response(JSON.stringify({ error: "unexpected" }), { status: 500 }));
|
||||
}
|
||||
|
||||
// NOTE: `open-sse/utils/proxyFetch.ts` unconditionally does
|
||||
// `globalThis.fetch = patchedFetch` as a module-level side effect the first time it is
|
||||
// imported (directly or transitively). imageValidation.ts pulls it in via
|
||||
// safeOutboundFetch -> proxyFetch, so assigning our mock to globalThis.fetch BEFORE that
|
||||
// first import gets silently clobbered. Import first, THEN install the mock so it is what
|
||||
// `fetchWithTimeout` reads (`fetchFn || globalThis.fetch`, read at call time, not captured).
|
||||
const { validateImageProviderApiKey } = await import("../../src/lib/providers/imageValidation.ts");
|
||||
(globalThis as unknown as { fetch: typeof mockMagnificFetch }).fetch = mockMagnificFetch;
|
||||
|
||||
test("issue #12927: a genuinely valid Magnific key must validate as valid", async () => {
|
||||
const result = await validateImageProviderApiKey({
|
||||
provider: "magnific",
|
||||
apiKey: "valid-magnific-key",
|
||||
providerSpecificData: {},
|
||||
});
|
||||
|
||||
// EXPECTED (post-fix): a valid key validates successfully.
|
||||
// Pre-fix, IMAGE_PROVIDER_VALIDATION_ENDPOINTS.magnific pointed GET at
|
||||
// /v1/ai/mystic, a POST-only task-submission route. Auth passed but routing 404s,
|
||||
// so validateImageProviderApiKey() reported `{ valid: false, error: "Validation failed: 404" }`
|
||||
// for a key that is genuinely valid — the false negative from the issue.
|
||||
assert.equal(
|
||||
result.valid,
|
||||
true,
|
||||
`expected a valid key to validate as valid, got: ${JSON.stringify(result)}`
|
||||
);
|
||||
});
|
||||
|
||||
test("control: invalid Magnific key correctly fails with 401 -> Invalid API key", async () => {
|
||||
const { validateImageProviderApiKey } =
|
||||
await import("../../src/lib/providers/imageValidation.ts");
|
||||
|
||||
const result = await validateImageProviderApiKey({
|
||||
provider: "magnific",
|
||||
apiKey: "totally-wrong-key",
|
||||
providerSpecificData: {},
|
||||
});
|
||||
|
||||
assert.equal(result.valid, false);
|
||||
assert.equal(result.error, "Invalid API key");
|
||||
});
|
||||
@@ -36,7 +36,7 @@ const imageOnlyProviders = {
|
||||
value: "topaz-key",
|
||||
},
|
||||
magnific: {
|
||||
url: "https://api.magnific.com/v1/ai/mystic",
|
||||
url: "https://api.magnific.com/v1/ai/flows",
|
||||
header: "x-magnific-api-key",
|
||||
value: "magnific-key",
|
||||
},
|
||||
@@ -101,11 +101,11 @@ for (const provider of Object.keys(imageOnlyProviders)) {
|
||||
}
|
||||
}
|
||||
|
||||
test("freepik alias validates through the Magnific Mystic endpoint", async () => {
|
||||
test("freepik alias validates through the Magnific Flows endpoint", async () => {
|
||||
let fetchCalled = false;
|
||||
globalThis.fetch = async (url, init = {}) => {
|
||||
fetchCalled = true;
|
||||
assert.equal(String(url), "https://api.magnific.com/v1/ai/mystic");
|
||||
assert.equal(String(url), "https://api.magnific.com/v1/ai/flows");
|
||||
assert.equal((init.headers as Record<string, string>)["x-magnific-api-key"], "legacy-key");
|
||||
return new Response(JSON.stringify({ data: [] }), { status: 200 });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user