Spaces:
Configuration error
Configuration error
fix: search rate limit now properly applies stricter limits for semantic search
Browse files- src/app/api/search/route.ts +17 -6
src/app/api/search/route.ts
CHANGED
|
@@ -24,16 +24,28 @@ export async function GET(request: NextRequest) {
|
|
| 24 |
)
|
| 25 |
}
|
| 26 |
|
| 27 |
-
// Rate limit — semantic search is expensive, stricter
|
| 28 |
const authUser = await getAuthUser()
|
| 29 |
const identifier = getClientIdentifier(request, authUser?.id ?? undefined)
|
| 30 |
-
const
|
| 31 |
-
const
|
| 32 |
-
|
| 33 |
if (!rateLimit.success) {
|
| 34 |
return NextResponse.json({ error: rateLimit.error }, { status: 429 })
|
| 35 |
}
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
// Build base query
|
| 38 |
const whereClause: Record<string, unknown> = {
|
| 39 |
visibility: "public",
|
|
@@ -81,7 +93,7 @@ Return ONLY a comma-separated list of terms, nothing else.`,
|
|
| 81 |
}
|
| 82 |
}
|
| 83 |
|
| 84 |
-
// Build OR clause for search
|
| 85 |
const orClause = searchTerms.flatMap(term => [
|
| 86 |
{ title: { contains: term, mode: "insensitive" as const } },
|
| 87 |
{ description: { contains: term, mode: "insensitive" as const } },
|
|
@@ -175,7 +187,6 @@ Return ONLY a comma-separated list of terms, nothing else.`,
|
|
| 175 |
hasMore: offset + prompts.length < total,
|
| 176 |
}, {
|
| 177 |
headers: {
|
| 178 |
-
// Short cache for search results
|
| 179 |
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
|
| 180 |
},
|
| 181 |
})
|
|
|
|
| 24 |
)
|
| 25 |
}
|
| 26 |
|
| 27 |
+
// Rate limit — semantic search is expensive, use a stricter key prefix
|
| 28 |
const authUser = await getAuthUser()
|
| 29 |
const identifier = getClientIdentifier(request, authUser?.id ?? undefined)
|
| 30 |
+
const rateLimitKey = semantic ? `search-semantic:${identifier}` : `search:${identifier}`
|
| 31 |
+
const rateLimit = await checkRateLimit(rateLimitKey, !!authUser, false)
|
| 32 |
+
|
| 33 |
if (!rateLimit.success) {
|
| 34 |
return NextResponse.json({ error: rateLimit.error }, { status: 429 })
|
| 35 |
}
|
| 36 |
|
| 37 |
+
// For semantic search, apply a secondary per-minute burst limit
|
| 38 |
+
if (semantic) {
|
| 39 |
+
const burstKey = `search-semantic-burst:${identifier}`
|
| 40 |
+
const burstLimit = await checkRateLimit(burstKey, !!authUser, false)
|
| 41 |
+
if (!burstLimit.success) {
|
| 42 |
+
return NextResponse.json(
|
| 43 |
+
{ error: "Semantic search rate limit exceeded. Please wait a moment." },
|
| 44 |
+
{ status: 429 }
|
| 45 |
+
)
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
// Build base query
|
| 50 |
const whereClause: Record<string, unknown> = {
|
| 51 |
visibility: "public",
|
|
|
|
| 93 |
}
|
| 94 |
}
|
| 95 |
|
| 96 |
+
// Build OR clause for search
|
| 97 |
const orClause = searchTerms.flatMap(term => [
|
| 98 |
{ title: { contains: term, mode: "insensitive" as const } },
|
| 99 |
{ description: { contains: term, mode: "insensitive" as const } },
|
|
|
|
| 187 |
hasMore: offset + prompts.length < total,
|
| 188 |
}, {
|
| 189 |
headers: {
|
|
|
|
| 190 |
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
|
| 191 |
},
|
| 192 |
})
|