Skip to content

Commit 896b72f

Browse files
committed
Block guy who keeps disputing charges
1 parent ecead95 commit 896b72f

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

web/src/app/api/v1/chat/completions/__tests__/completions.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ describe('/api/v1/chat/completions POST endpoint', () => {
2828
'test-api-key-no-credits': {
2929
id: 'user-no-credits',
3030
},
31+
'test-api-key-blocked': {
32+
id: '5e5aa538-92c8-4051-b0ec-5f75dbd69767',
33+
},
3134
}
3235

3336
const mockGetUserInfoFromApiKey: GetUserInfoFromApiKeyFn = async ({
@@ -348,6 +351,41 @@ describe('/api/v1/chat/completions POST endpoint', () => {
348351
})
349352
})
350353

354+
describe('Blocked users', () => {
355+
it('returns 503 with cryptic error for blocked user IDs', async () => {
356+
const req = new NextRequest(
357+
'http://localhost:3000/api/v1/chat/completions',
358+
{
359+
method: 'POST',
360+
headers: { Authorization: 'Bearer test-api-key-blocked' },
361+
body: JSON.stringify({
362+
stream: true,
363+
codebuff_metadata: { run_id: 'run-123' },
364+
}),
365+
},
366+
)
367+
368+
const response = await postChatCompletions({
369+
req,
370+
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
371+
logger: mockLogger,
372+
trackEvent: mockTrackEvent,
373+
getUserUsageData: mockGetUserUsageData,
374+
getAgentRunFromId: mockGetAgentRunFromId,
375+
fetch: mockFetch,
376+
insertMessageBigquery: mockInsertMessageBigquery,
377+
loggerWithContext: mockLoggerWithContext,
378+
})
379+
380+
expect(response.status).toBe(503)
381+
const body = await response.json()
382+
expect(body).toEqual({
383+
error: 'upstream_timeout',
384+
message: 'Overloaded. Request could not be processed',
385+
})
386+
})
387+
})
388+
351389
describe('Credit validation', () => {
352390
it('returns 402 when user has insufficient credits', async () => {
353391
const req = new NextRequest(

web/src/app/api/v1/chat/completions/_post.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ import {
1616
} from '@/llm-api/openrouter'
1717
import { extractApiKeyFromHeader } from '@/util/auth'
1818

19+
/**
20+
* User IDs that are blocked from using the chat completions API.
21+
* Returns a cryptic error to avoid revealing the block.
22+
*/
23+
const BLOCKED_USER_IDS: string[] = [
24+
'5e5aa538-92c8-4051-b0ec-5f75dbd69767',
25+
]
26+
1927
import type { TrackEventFn } from '@codebuff/common/types/contracts/analytics'
2028
import type { InsertMessageBigqueryFn } from '@codebuff/common/types/contracts/bigquery'
2129
import type { GetUserUsageDataFn } from '@codebuff/common/types/contracts/billing'
@@ -149,6 +157,14 @@ export async function postChatCompletions(params: {
149157

150158
const userId = userInfo.id
151159

160+
// Check if user is blocked. Return fake overloaded error to avoid revealing the block.
161+
if (BLOCKED_USER_IDS.includes(userId)) {
162+
return NextResponse.json(
163+
{ error: 'upstream_timeout', message: 'Overloaded. Request could not be processed' },
164+
{ status: 503 },
165+
)
166+
}
167+
152168
// Track API request
153169
trackEvent({
154170
event: AnalyticsEvent.CHAT_COMPLETIONS_REQUEST,

0 commit comments

Comments
 (0)