(params: {
userId: string
logger: Logger
})
| 441 | } |
| 442 | |
| 443 | export async function triggerMonthlyResetAndGrant(params: { |
| 444 | userId: string |
| 445 | logger: Logger |
| 446 | }): Promise<MonthlyResetResult> { |
| 447 | const { userId, logger } = params |
| 448 | |
| 449 | const { result } = await withAdvisoryLockTransaction({ |
| 450 | callback: async (tx) => { |
| 451 | const now = new Date() |
| 452 | |
| 453 | // Get user's current reset date and auto top-up status |
| 454 | const user = await tx.query.user.findFirst({ |
| 455 | where: eq(schema.user.id, userId), |
| 456 | columns: { |
| 457 | next_quota_reset: true, |
| 458 | auto_topup_enabled: true, |
| 459 | }, |
| 460 | }) |
| 461 | |
| 462 | if (!user) { |
| 463 | throw new Error(`User ${userId} not found`) |
| 464 | } |
| 465 | |
| 466 | const autoTopupEnabled = user.auto_topup_enabled ?? false |
| 467 | const currentResetDate = user.next_quota_reset |
| 468 | |
| 469 | // If reset date is in the future, no action needed |
| 470 | if (currentResetDate && currentResetDate > now) { |
| 471 | return { quotaResetDate: currentResetDate, autoTopupEnabled } |
| 472 | } |
| 473 | |
| 474 | // Calculate new reset date |
| 475 | const newResetDate = getNextQuotaReset(currentResetDate) |
| 476 | |
| 477 | const [freeGrantAmount, referralBonus] = await Promise.all([ |
| 478 | getGrandfatheredFreeGrantAmount(params), |
| 479 | calculateTotalLegacyReferralBonus(params), |
| 480 | ]) |
| 481 | |
| 482 | // Generate a deterministic operation ID based on userId and reset date to minute precision |
| 483 | const timestamp = generateOperationIdTimestamp(newResetDate) |
| 484 | const freeOperationId = `free-${userId}-${timestamp}` |
| 485 | const referralOperationId = `referral-${userId}-${timestamp}` |
| 486 | |
| 487 | // Update the user's next reset date |
| 488 | await tx |
| 489 | .update(schema.user) |
| 490 | .set({ next_quota_reset: newResetDate }) |
| 491 | .where(eq(schema.user.id, userId)) |
| 492 | |
| 493 | if (freeGrantAmount > 0) { |
| 494 | await executeGrantCreditOperation({ |
| 495 | ...params, |
| 496 | amount: freeGrantAmount, |
| 497 | type: 'free', |
| 498 | description: 'Monthly free credits (grandfathered)', |
| 499 | expiresAt: newResetDate, |
| 500 | operationId: freeOperationId, |
no test coverage detected