mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Pollinations image requests with no configured apiKey/accessToken (the common free case) were sent with no Authorization header AND no fingerprint headers, so Pollinations' own upstream legitimately rejected them with a real 401 even for a valid OmniRoute key. The chat path already has an anonymous fingerprint-pool fallback (PollinationsExecutor.execute()'s isAnonymous branch); the image path never reused it. Adds open-sse/handlers/imageGeneration/pollinationsAnonAuth.ts, mirroring the chat executor's anonymous session-pool fallback for handleOpenAIImageGeneration, and fixes the pre-existing bug where Authorization was set to the literal string "Bearer undefined" when no token was configured (now correctly gated by if (token)). Regression test: tests/unit/pollinations-image-anon-fallback-8085.test.ts
65 lines
2.6 KiB
TypeScript
65 lines
2.6 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { mkdtempSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
// Isolated DATA_DIR so this test never touches the real ~/.omniroute DB
|
|
// (handleImageGeneration's call-log path opens the shared DB singleton).
|
|
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-images-anon-8085-"));
|
|
|
|
// #8085 — Pollinations image-generation requests with NO configured API key
|
|
// (the common free/keyless case) must reuse the same anonymous fingerprint
|
|
// session-pool fallback that PollinationsExecutor.execute() already applies
|
|
// to the chat path (open-sse/executors/pollinations.ts:52-71). Without it,
|
|
// the outbound request to gen.pollinations.ai carries no Authorization AND
|
|
// no browser fingerprint, so Pollinations legitimately rejects it with a
|
|
// real upstream 401 — even though the caller supplied a perfectly valid
|
|
// OmniRoute API key.
|
|
const { handleImageGeneration } = await import("../../open-sse/handlers/imageGeneration.ts");
|
|
|
|
test("#8085 keyless Pollinations image request includes anonymous fingerprint headers (User-Agent) instead of going out bare", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (url, options = {}) => {
|
|
captured = {
|
|
url: String(url),
|
|
headers: options.headers || {},
|
|
};
|
|
|
|
return new Response(
|
|
JSON.stringify({ created: 123, data: [{ url: "https://cdn.example.com/image.png" }] }),
|
|
{ status: 200, headers: { "content-type": "application/json" } }
|
|
);
|
|
};
|
|
|
|
try {
|
|
const result = await handleImageGeneration({
|
|
body: {
|
|
model: "pollinations/flux",
|
|
prompt: "a cat",
|
|
},
|
|
// No apiKey/accessToken — the common keyless/free Pollinations case.
|
|
credentials: {},
|
|
log: null,
|
|
});
|
|
|
|
assert.equal(result.success, true);
|
|
assert.ok(captured, "fetch should have been called");
|
|
assert.equal(captured.url, "https://gen.pollinations.ai/v1/images/generations");
|
|
|
|
// No key was supplied, so no Authorization header is expected — but the
|
|
// anonymous fallback must inject fingerprint headers (mirroring the chat
|
|
// executor's isAnonymous branch) so the upstream doesn't see a bare,
|
|
// headerless request and reject it with a real 401.
|
|
assert.equal(captured.headers.Authorization, undefined);
|
|
assert.ok(
|
|
captured.headers["User-Agent"],
|
|
"expected anonymous session-pool fingerprint headers (User-Agent) on the outbound Pollinations image request"
|
|
);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|