Files
AeroStart/src/utils/suggestions.ts
ZyphrZero 7a1815069a 🐛 fix(api): add Vercel serverless function for Bilibili search suggestions
- Create api/bilibili.ts to handle Bilibili API requests in production
  - Proxy requests to https://s.search.bilibili.com/main/suggest
  - Add proper headers (User-Agent, Referer) to avoid blocking
  - Set CORS headers and cache control (60s)
  - Handle errors gracefully with fallback response
- Update src/utils/suggestions.ts to detect environment
  - Use /bilibili (Vite proxy) in development
  - Use /api/bilibili (Vercel Function) in production
  - Check import.meta.env.DEV to determine environment

This fixes the 404 error in production where Vite proxy is not available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 10:47:35 +08:00

85 lines
2.2 KiB
TypeScript

import {
getSearchEngineConfig,
buildJsonpUrl,
type SearchEngineType
} from '@/config/searchEngines';
let callbackCount = 0;
/**
* Fetch search suggestions (supports hybrid JSONP and Fetch mode)
*/
export const fetchSuggestions = (engine: string, query: string): Promise<string[]> => {
return new Promise((resolve) => {
if (!query || !query.trim()) {
resolve([]);
return;
}
const config = getSearchEngineConfig(engine as SearchEngineType);
if (!config) {
resolve([]);
return;
}
// Bilibili uses fetch (via Vite proxy in dev, Vercel Function in production)
if (engine === 'Bilibili') {
// In production (Vercel), use /api/bilibili; in development, use /bilibili (Vite proxy)
const isDev = import.meta.env.DEV;
const url = isDev
? `/bilibili?term=${encodeURIComponent(query)}`
: `/api/bilibili?term=${encodeURIComponent(query)}`;
fetch(url)
.then(response => response.json())
.then(data => {
try {
const suggestions = config.parseResponse(data);
resolve(suggestions);
} catch (e) {
resolve([]);
}
})
.catch(() => {
resolve([]);
});
return;
}
// Other engines use JSONP
const callbackName = `jsonp_cb_${Date.now()}_${callbackCount++}`;
const script = document.createElement('script');
let timeoutId: any;
const cleanup = () => {
if ((window as any)[callbackName]) delete (window as any)[callbackName];
if (document.body.contains(script)) document.body.removeChild(script);
if (timeoutId) clearTimeout(timeoutId);
};
// 3 seconds timeout
timeoutId = setTimeout(() => {
cleanup();
resolve([]);
}, 3000);
// Set global callback function
(window as any)[callbackName] = (data: any) => {
cleanup();
try {
const suggestions = config.parseResponse(data);
resolve(suggestions);
} catch (e) {
resolve([]);
}
};
// Build URL and make request
script.src = buildJsonpUrl(config, query, callbackName);
script.onerror = () => {
cleanup();
resolve([]);
};
document.body.appendChild(script);
});
};