Spaces:
Sleeping
Sleeping
import { PreviewServer, ViteDevServer } from "vite"; | |
import { RateLimiterMemory } from "rate-limiter-flexible"; | |
import { argon2Verify } from "hash-wasm"; | |
import { incrementSearchesSinceLastRestart } from "./searchesSinceLastRestart"; | |
import { rankSearchResults } from "./rankSearchResults"; | |
import { getSearchToken } from "./searchToken"; | |
import { fetchSearXNG } from "./fetchSearXNG"; | |
import { isVerifiedToken, addVerifiedToken } from "./verifiedTokens"; | |
export function searchEndpointServerHook< | |
T extends ViteDevServer | PreviewServer, | |
>(server: T) { | |
const rateLimiter = new RateLimiterMemory({ | |
points: 2, | |
duration: 10, | |
}); | |
server.middlewares.use(async (request, response, next) => { | |
if (!request.url.startsWith("/search")) return next(); | |
const { searchParams } = new URL( | |
request.url, | |
`http://${request.headers.host}`, | |
); | |
const limitParam = searchParams.get("limit"); | |
const limit = | |
limitParam && Number(limitParam) > 0 ? Number(limitParam) : undefined; | |
const query = searchParams.get("q"); | |
if (!query) { | |
response.statusCode = 400; | |
response.end("Missing the query parameter."); | |
return; | |
} | |
const token = searchParams.get("token"); | |
if (!isVerifiedToken(token)) { | |
let isValidToken = false; | |
try { | |
isValidToken = await argon2Verify({ | |
password: getSearchToken(), | |
hash: token, | |
}); | |
} catch (error) { | |
void error; | |
} | |
if (isValidToken) { | |
addVerifiedToken(token); | |
} else { | |
response.statusCode = 401; | |
response.end("Unauthorized."); | |
return; | |
} | |
} | |
try { | |
await rateLimiter.consume(token); | |
} catch { | |
response.statusCode = 429; | |
response.end("Too many requests."); | |
return; | |
} | |
const { textResults, imageResults } = await fetchSearXNG(query, limit); | |
incrementSearchesSinceLastRestart(); | |
if (textResults.length === 0 && imageResults.length === 0) { | |
response.setHeader("Content-Type", "application/json"); | |
response.end(JSON.stringify({ textResults: [], imageResults: [] })); | |
return; | |
} | |
try { | |
const rankedTextResults = await rankSearchResults(query, textResults); | |
response.setHeader("Content-Type", "application/json"); | |
response.end( | |
JSON.stringify({ textResults: rankedTextResults, imageResults }), | |
); | |
} catch (error) { | |
const errorMessage = | |
error instanceof Error ? error.message : String(error); | |
console.error(`Error ranking search results: ${errorMessage}`); | |
response.setHeader("Content-Type", "application/json"); | |
response.end(JSON.stringify({ textResults, imageResults })); | |
} | |
}); | |
} | |