@@ -13,6 +13,7 @@ import { and, desc, eq, gt, isNull, lte, or, sql } from 'drizzle-orm'
1313import { generateOperationIdTimestamp } from './utils'
1414
1515import type { GrantType } from '@codebuff/common/db/schema'
16+ import type { Logger } from '@codebuff/types/logger'
1617
1718type CreditGrantSelect = typeof schema . creditLedger . $inferSelect
1819type DbTransaction = Parameters < typeof db . transaction > [ 0 ] extends (
@@ -30,9 +31,12 @@ type DbTransaction = Parameters<typeof db.transaction>[0] extends (
3031 * @param userId The ID of the user.
3132 * @returns The amount of the last expired free grant (capped at 2000) or the default.
3233 */
33- export async function getPreviousFreeGrantAmount (
34- userId : string ,
35- ) : Promise < number > {
34+ export async function getPreviousFreeGrantAmount ( params : {
35+ userId : string
36+ logger : Logger
37+ } ) : Promise < number > {
38+ const { userId, logger } = params
39+
3640 const now = new Date ( )
3741 const lastExpiredFreeGrant = await db
3842 . select ( {
@@ -72,9 +76,12 @@ export async function getPreviousFreeGrantAmount(
7276 * @param userId The ID of the user.
7377 * @returns The total referral bonus credits earned.
7478 */
75- export async function calculateTotalReferralBonus (
76- userId : string ,
77- ) : Promise < number > {
79+ export async function calculateTotalReferralBonus ( params : {
80+ userId : string
81+ logger : Logger
82+ } ) : Promise < number > {
83+ const { userId, logger } = params
84+
7885 try {
7986 const result = await db
8087 . select ( {
@@ -103,15 +110,27 @@ export async function calculateTotalReferralBonus(
103110/**
104111 * Core grant operation that can be part of a larger transaction.
105112 */
106- export async function grantCreditOperation (
107- userId : string ,
108- amount : number ,
109- type : GrantType ,
110- description : string ,
111- expiresAt : Date | null ,
112- operationId : string ,
113- tx ?: DbTransaction ,
114- ) {
113+ export async function grantCreditOperation ( params : {
114+ userId : string
115+ amount : number
116+ type : GrantType
117+ description : string
118+ expiresAt : Date | null
119+ operationId : string
120+ tx ?: DbTransaction
121+ logger : Logger
122+ } ) {
123+ const {
124+ userId,
125+ amount,
126+ type,
127+ description,
128+ expiresAt,
129+ operationId,
130+ tx,
131+ logger,
132+ } = params
133+
115134 const dbClient = tx || db
116135
117136 const now = new Date ( )
@@ -228,36 +247,28 @@ export async function grantCreditOperation(
228247 * Processes a credit grant request with retries and failure logging.
229248 * Used for standalone credit grants that need retry logic and failure tracking.
230249 */
231- export async function processAndGrantCredit (
232- userId : string ,
233- amount : number ,
234- type : GrantType ,
235- description : string ,
236- expiresAt : Date | null ,
237- operationId : string ,
238- ) : Promise < void > {
250+ export async function processAndGrantCredit ( params : {
251+ userId : string
252+ amount : number
253+ type : GrantType
254+ description : string
255+ expiresAt : Date | null
256+ operationId : string
257+ logger : Logger
258+ } ) : Promise < void > {
259+ const { operationId, logger } = params
260+
239261 try {
240- await withRetry (
241- ( ) =>
242- grantCreditOperation (
243- userId ,
244- amount ,
245- type ,
246- description ,
247- expiresAt ,
248- operationId ,
249- ) ,
250- {
251- maxRetries : 3 ,
252- retryIf : ( ) => true ,
253- onRetry : ( error , attempt ) => {
254- logger . warn (
255- { operationId, attempt, error } ,
256- `processAndGrantCredit retry ${ attempt } ` ,
257- )
258- } ,
262+ await withRetry ( ( ) => grantCreditOperation ( params ) , {
263+ maxRetries : 3 ,
264+ retryIf : ( ) => true ,
265+ onRetry : ( error , attempt ) => {
266+ logger . warn (
267+ { operationId, attempt, error } ,
268+ `processAndGrantCredit retry ${ attempt } ` ,
269+ )
259270 } ,
260- )
271+ } )
261272 } catch ( error : any ) {
262273 await logSyncFailure ( {
263274 id : operationId ,
@@ -336,9 +347,12 @@ export async function revokeGrantByOperationId(
336347 * @param userId The ID of the user
337348 * @returns The effective quota reset date (either existing or new)
338349 */
339- export async function triggerMonthlyResetAndGrant (
340- userId : string ,
341- ) : Promise < Date > {
350+ export async function triggerMonthlyResetAndGrant ( params : {
351+ userId : string
352+ logger : Logger
353+ } ) : Promise < Date > {
354+ const { userId, logger } = params
355+
342356 return await db . transaction ( async ( tx ) => {
343357 const now = new Date ( )
344358
@@ -366,8 +380,8 @@ export async function triggerMonthlyResetAndGrant(
366380
367381 // Calculate grant amounts separately
368382 const [ freeGrantAmount , referralBonus ] = await Promise . all ( [
369- getPreviousFreeGrantAmount ( userId ) ,
370- calculateTotalReferralBonus ( userId ) ,
383+ getPreviousFreeGrantAmount ( params ) ,
384+ calculateTotalReferralBonus ( params ) ,
371385 ] )
372386
373387 // Generate a deterministic operation ID based on userId and reset date to minute precision
@@ -382,25 +396,25 @@ export async function triggerMonthlyResetAndGrant(
382396 . where ( eq ( schema . user . id , userId ) )
383397
384398 // Always grant free credits
385- await processAndGrantCredit (
386- userId ,
387- freeGrantAmount ,
388- 'free' ,
389- 'Monthly free credits' ,
390- newResetDate , // Free credits expire at next reset
391- freeOperationId ,
392- )
399+ await processAndGrantCredit ( {
400+ ... params ,
401+ amount : freeGrantAmount ,
402+ type : 'free' ,
403+ description : 'Monthly free credits' ,
404+ expiresAt : newResetDate , // Free credits expire at next reset
405+ operationId : freeOperationId ,
406+ } )
393407
394408 // Only grant referral credits if there are any
395409 if ( referralBonus > 0 ) {
396- await processAndGrantCredit (
397- userId ,
398- referralBonus ,
399- 'referral' ,
400- 'Monthly referral bonus' ,
401- newResetDate , // Referral credits expire at next reset
402- referralOperationId ,
403- )
410+ await processAndGrantCredit ( {
411+ ... params ,
412+ amount : referralBonus ,
413+ type : 'referral' ,
414+ description : 'Monthly referral bonus' ,
415+ expiresAt : newResetDate , // Referral credits expire at next reset
416+ operationId : referralOperationId ,
417+ } )
404418 }
405419
406420 logger . info (
0 commit comments