fix(search): fall back to duckduckgo-free when no search provider is configured (#11097)

Validated on the combined batch board over tip 8a42aeeb: static gates clean (changelog, file-size 159 frozen, complexity 2621<=2774, cognitive 1181<=1223, dead-code 408<=416), typecheck:core clean, 107 focused tests green.

Zero-credential /v1/search now actually reaches the fallback-only providers (duckduckgo-free/searxng) instead of erroring before the last-resort block could run — the dead-code path is live. Also carries a one-line suppressHydrationWarning on the logo. Thank you @Egorich-print!
This commit is contained in:
3g0r1ch
2026-08-23 10:01:11 +00:00
committed by GitHub
parent 0fe1bb2390
commit 0b41259f39
2 changed files with 31 additions and 2 deletions

View File

@@ -56,7 +56,7 @@ export async function OPTIONS() {
* GET /v1/search — list available search providers
*/
export async function GET() {
const settings = await getSettings().catch(() => ({} as any));
const settings = await getSettings().catch(() => ({}) as any);
const blockedProviders = settings?.blockedProviders || [];
const providers = getAllSearchProviders(blockedProviders);
const timestamp = Math.floor(Date.now() / 1000);
@@ -139,7 +139,7 @@ async function postHandler(request: Request, context: unknown) {
const policy = await enforceApiKeyPolicy(request, "search");
if (policy.rejection) return policy.rejection;
const settings = await getSettings().catch(() => ({} as any));
const settings = await getSettings().catch(() => ({}) as any);
const blockedProviders = settings?.blockedProviders || [];
// Resolve provider and credentials
@@ -247,6 +247,34 @@ async function postHandler(request: Request, context: unknown) {
}
}
// Last resort before failing: promote a fallback-only free provider (e.g.
// duckduckgo-free) to the primary pick so out-of-the-box search works when
// no credentialed provider is configured at all.
if (!credentials) {
const fallbackProviders = Object.values(SEARCH_PROVIDERS)
.filter(
(provider) =>
provider.fallbackOnly &&
supportsSearchType(provider, body.search_type) &&
!isProviderBlockedByIdOrAlias(provider.id, blockedProviders)
)
.sort((a, b) => a.costPerQuery - b.costPerQuery);
for (const fallbackProvider of fallbackProviders) {
providerConfig = fallbackProvider;
if (fallbackProvider.id === "duckduckgo-free") {
credentials = {};
break;
}
const fallbackCreds = await resolveSearchCredentials(fallbackProvider.id);
if (isAllRateLimitedCredentials(fallbackCreds)) continue;
if (fallbackCreds) {
credentials = fallbackCreds;
break;
}
}
}
if (!credentials) {
if (firstRateLimitedCredentials) {
return rateLimitedProviderResponse(

View File

@@ -16,6 +16,7 @@ export default function OmniRouteLogo({ size = 20, className = "" }: OmniRouteLo
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
suppressHydrationWarning
>
{/* Central node */}
<circle cx="16" cy="16" r="3" fill="currentColor" />