1- import { Database , eq , and , sql , inArray } from "@opencode-ai/console-core/drizzle/index.js"
2- import { IpRateLimitTable } from "@opencode-ai/console-core/schema/ip.sql.js"
31import { FreeUsageLimitError } from "./error"
42import { logger } from "./logger"
53import { buildRateLimitKey , getRedis } from "./redis"
@@ -22,7 +20,6 @@ export function createRateLimiter(modelId: string, rateLimit: number | undefined
2220
2321 const ip = ! rawIp . length ? "unknown" : rawIp
2422 const now = Date . now ( )
25- const lifetimeInterval = ""
2623 const dailyInterval = rateLimit ? `${ buildYYYYMMDD ( now ) } ${ modelId . substring ( 0 , 2 ) } ` : buildYYYYMMDD ( now )
2724 const retryAfter = getRetryAfterDay ( now )
2825 const redis = getRedis ( )
@@ -32,33 +29,12 @@ export function createRateLimiter(modelId: string, rateLimit: number | undefined
3229
3330 return {
3431 check : async ( ) => {
35- const [ counts , rows ] = await Promise . all ( [
36- redis . mget < ( string | number | null ) [ ] > ( isDefaultModel ? [ lifetimeKey , dailyKey ] : [ dailyKey ] ) . catch ( ( ) => [ ] ) ,
37- Database . use ( ( tx ) =>
38- tx
39- . select ( { interval : IpRateLimitTable . interval , count : IpRateLimitTable . count } )
40- . from ( IpRateLimitTable )
41- . where (
42- and (
43- eq ( IpRateLimitTable . ip , ip ) ,
44- isDefaultModel
45- ? inArray ( IpRateLimitTable . interval , [ lifetimeInterval , dailyInterval ] )
46- : inArray ( IpRateLimitTable . interval , [ dailyInterval ] ) ,
47- ) ,
48- ) ,
49- ) ,
50- ] )
51- const redisLifetimeCount = isDefaultModel ? Number ( counts [ 0 ] ?? 0 ) : 0
52- const redisDailyCount = Number ( counts [ isDefaultModel ? 1 : 0 ] ?? 0 )
53- const databaseLifetimeCount = rows . find ( ( r ) => r . interval === lifetimeInterval ) ?. count ?? 0
54- const databaseDailyCount = rows . find ( ( r ) => r . interval === dailyInterval ) ?. count ?? 0
55- const lifetimeCount = Math . max ( redisLifetimeCount , databaseLifetimeCount )
56- const dailyCount = Math . max ( redisDailyCount , databaseDailyCount )
32+ const counts = await redis . mget < ( string | number | null ) [ ] > ( isDefaultModel ? [ lifetimeKey , dailyKey ] : [ dailyKey ] )
33+ const lifetimeCount = isDefaultModel ? Number ( counts [ 0 ] ?? 0 ) : 0
34+ const dailyCount = Number ( counts [ isDefaultModel ? 1 : 0 ] ?? 0 )
5735 logger . debug ( `rate limit lifetime: ${ lifetimeCount } , daily: ${ dailyCount } ` )
5836
5937 isNew = isDefaultModel && lifetimeCount < dailyLimit * 7
60- if ( isDefaultModel && databaseLifetimeCount > redisLifetimeCount )
61- await redis . set ( lifetimeKey , databaseLifetimeCount ) . catch ( ( ) => { } )
6238
6339 if ( ( isNew && dailyCount >= dailyLimit * 2 ) || ( ! isNew && dailyCount >= dailyLimit ) )
6440 throw new FreeUsageLimitError ( dict [ "zen.api.error.rateLimitExceeded" ] , retryAfter )
@@ -68,18 +44,7 @@ export function createRateLimiter(modelId: string, rateLimit: number | undefined
6844 pipeline . incr ( dailyKey )
6945 pipeline . expire ( dailyKey , retryAfter )
7046 if ( isNew ) pipeline . incr ( lifetimeKey )
71- await Promise . all ( [
72- pipeline . exec ( ) . catch ( ( ) => { } ) ,
73- Database . use ( ( tx ) =>
74- tx
75- . insert ( IpRateLimitTable )
76- . values ( [
77- { ip, interval : dailyInterval , count : 1 } ,
78- ...( isNew ? [ { ip, interval : lifetimeInterval , count : 1 } ] : [ ] ) ,
79- ] )
80- . onDuplicateKeyUpdate ( { set : { count : sql `${ IpRateLimitTable . count } + 1` } } ) ,
81- ) ,
82- ] )
47+ await pipeline . exec ( )
8348 } ,
8449 }
8550}
0 commit comments