( userId: string, bytes: number, workspaceId?: string )
| 123 | * after a shrink. Best-effort; never blocks the caller. |
| 124 | */ |
| 125 | export async function decrementStorageUsage( |
| 126 | userId: string, |
| 127 | bytes: number, |
| 128 | workspaceId?: string |
| 129 | ): Promise<void> { |
| 130 | if (!isBillingEnabled) { |
| 131 | logger.debug('Billing disabled, skipping storage decrement') |
| 132 | return |
| 133 | } |
| 134 | |
| 135 | let sub: HighestPrioritySubscription | null = null |
| 136 | try { |
| 137 | const { getHighestPrioritySubscription } = await import('@/lib/billing/core/subscription') |
| 138 | sub = await getHighestPrioritySubscription(userId) |
| 139 | |
| 140 | if (isOrgScopedSubscription(sub, userId) && sub) { |
| 141 | await db |
| 142 | .update(organization) |
| 143 | .set({ |
| 144 | storageUsedBytes: sql`GREATEST(0, ${organization.storageUsedBytes} - ${bytes})`, |
| 145 | }) |
| 146 | .where(eq(organization.id, sub.referenceId)) |
| 147 | |
| 148 | logger.info(`Decremented org storage: ${bytes} bytes for org ${sub.referenceId}`) |
| 149 | } else { |
| 150 | await db |
| 151 | .update(userStats) |
| 152 | .set({ |
| 153 | storageUsedBytes: sql`GREATEST(0, ${userStats.storageUsedBytes} - ${bytes})`, |
| 154 | }) |
| 155 | .where(eq(userStats.userId, userId)) |
| 156 | |
| 157 | logger.info(`Decremented user storage: ${bytes} bytes for user ${userId}`) |
| 158 | } |
| 159 | } catch (error) { |
| 160 | logger.error('Error decrementing storage usage:', error) |
| 161 | throw error |
| 162 | } |
| 163 | |
| 164 | if (workspaceId) { |
| 165 | void maybeNotifyStorageLimit(userId, workspaceId, sub, true) |
| 166 | } |
| 167 | } |
no test coverage detected