fix(missing-user-stats): missing user stats rows covered via migration' (#1409)

This commit is contained in:
Vikhyath Mondreti
2025-09-22 10:50:04 -07:00
committed by GitHub
parent d83865c635
commit 16f5819941
2 changed files with 69 additions and 33 deletions

View File

@@ -403,54 +403,43 @@ export class ExecutionLogger implements IExecutionLoggerService {
// Apply cost multiplier only to model costs, not base execution charge
const costToStore = costSummary.baseExecutionCharge + costSummary.modelCost * costMultiplier
// Upsert user stats record - insert if doesn't exist, update if it does
const { getFreeTierLimit } = await import('@/lib/billing/subscriptions/utils')
const defaultLimit = getFreeTierLimit()
const existing = await db.select().from(userStats).where(eq(userStats.userId, userId))
if (existing.length === 0) {
logger.error('User stats record not found - should be created during onboarding', {
userId,
trigger,
})
return
}
const updateFields: any = {
totalTokensUsed: sql`total_tokens_used + ${costSummary.totalTokens}`,
totalCost: sql`total_cost + ${costToStore}`,
currentPeriodCost: sql`current_period_cost + ${costToStore}`,
lastActive: new Date(),
}
const triggerIncrements: any = {}
switch (trigger) {
case 'manual':
triggerIncrements.totalManualExecutions = sql`total_manual_executions + 1`
updateFields.totalManualExecutions = sql`total_manual_executions + 1`
break
case 'api':
triggerIncrements.totalApiCalls = sql`total_api_calls + 1`
updateFields.totalApiCalls = sql`total_api_calls + 1`
break
case 'webhook':
triggerIncrements.totalWebhookTriggers = sql`total_webhook_triggers + 1`
updateFields.totalWebhookTriggers = sql`total_webhook_triggers + 1`
break
case 'schedule':
triggerIncrements.totalScheduledExecutions = sql`total_scheduled_executions + 1`
updateFields.totalScheduledExecutions = sql`total_scheduled_executions + 1`
break
case 'chat':
triggerIncrements.totalChatExecutions = sql`total_chat_executions + 1`
updateFields.totalChatExecutions = sql`total_chat_executions + 1`
break
}
await db
.insert(userStats)
.values({
id: uuidv4(),
userId: userId,
currentUsageLimit: defaultLimit.toString(),
usageLimitUpdatedAt: new Date(),
totalTokensUsed: costSummary.totalTokens,
totalCost: costToStore,
currentPeriodCost: costToStore,
lastActive: new Date(),
...triggerIncrements,
})
.onConflictDoUpdate({
target: userStats.userId,
set: {
totalTokensUsed: sql`total_tokens_used + ${costSummary.totalTokens}`,
totalCost: sql`total_cost + ${costToStore}`,
currentPeriodCost: sql`current_period_cost + ${costToStore}`,
lastActive: new Date(),
...triggerIncrements,
},
})
await db.update(userStats).set(updateFields).where(eq(userStats.userId, userId))
logger.debug('Upserted user stats record with cost data', {
logger.debug('Updated user stats record with cost data', {
userId,
trigger,
addedCost: costToStore,

View File

@@ -0,0 +1,47 @@
-- Backfill user_stats for any users missing a stats row
-- Uses defaults from schema for limits and counters
INSERT INTO "user_stats" (
"id",
"user_id",
"current_usage_limit",
"usage_limit_updated_at",
"total_manual_executions",
"total_api_calls",
"total_webhook_triggers",
"total_scheduled_executions",
"total_chat_executions",
"total_tokens_used",
"total_cost",
"current_period_cost",
"last_period_cost",
"total_copilot_cost",
"total_copilot_tokens",
"total_copilot_calls",
"last_active",
"billing_blocked"
)
SELECT
u."id" AS id,
u."id" AS user_id,
NULL::decimal AS current_usage_limit,
NOW() AS usage_limit_updated_at,
0 AS total_manual_executions,
0 AS total_api_calls,
0 AS total_webhook_triggers,
0 AS total_scheduled_executions,
0 AS total_chat_executions,
0 AS total_tokens_used,
'0'::decimal AS total_cost,
'0'::decimal AS current_period_cost,
'0'::decimal AS last_period_cost,
'0'::decimal AS total_copilot_cost,
0 AS total_copilot_tokens,
0 AS total_copilot_calls,
NOW() AS last_active,
FALSE AS billing_blocked
FROM "user" u
LEFT JOIN "user_stats" s ON s."user_id" = u."id"
WHERE s."user_id" IS NULL;