11import { db } from '@sim/db'
22import { userStats } from '@sim/db/schema'
33import { createLogger } from '@sim/logger'
4- import { toError } from '@sim/utils/errors'
4+ import { getErrorMessage , toError } from '@sim/utils/errors'
55import { eq } from 'drizzle-orm'
66import { USAGE_UNAVAILABLE_MESSAGE } from '@/lib/billing/constants'
77import { isOrganizationBillingBlocked } from '@/lib/billing/core/access'
@@ -306,6 +306,38 @@ export async function checkBillingEntityBlocked(
306306 * @param userId The ID of the user to check
307307 * @returns An object containing the exceeded status and usage details
308308 */
309+ /**
310+ * A blocked account's ledger usage, for display only: `/api/users/me/usage-limits` exposes it as
311+ * `currentPeriodCost`. The account is blocked either way, so a failed read reports zero rather
312+ * than turning the blocked refusal into an unreadable-usage one.
313+ */
314+ async function readBlockedAccountUsage (
315+ userId : string ,
316+ preloadedSubscription : UsageLimitSubscription | null | undefined ,
317+ preloadedBillingContext : BillingContext | undefined
318+ ) : Promise < number > {
319+ try {
320+ const sub =
321+ preloadedSubscription !== undefined
322+ ? preloadedSubscription
323+ : await getHighestPrioritySubscription ( userId )
324+ const subIsOrgScoped = isOrgScopedSubscription ( sub , userId )
325+ const billingEntity : BillingEntity =
326+ subIsOrgScoped && sub
327+ ? { type : 'organization' , id : sub . referenceId }
328+ : { type : 'user' , id : userId }
329+ const billingPeriod = preloadedBillingContext ?. billingPeriod ??
330+ resolveSubscriptionUsagePeriod ( sub ) ?? { ...defaultBillingPeriod ( ) , source : 'default' }
331+ return await getBillingPeriodUsageCost ( billingEntity , billingPeriod )
332+ } catch ( error ) {
333+ logger . warn ( 'Blocked account usage could not be read' , {
334+ userId,
335+ error : getErrorMessage ( error ) ,
336+ } )
337+ return 0
338+ }
339+ }
340+
309341export async function checkServerSideUsageLimits (
310342 userId : string ,
311343 preloadedSubscription ?: UsageLimitSubscription | null ,
@@ -331,20 +363,11 @@ export async function checkServerSideUsageLimits(
331363
332364 const blocked = await checkBillingBlocked ( userId )
333365 if ( blocked . blocked ) {
334- // Enforcement stays blocked, but surfaced usage must be the real ledger
335- // value — `/api/users/me/usage-limits` exposes it as `currentPeriodCost`.
336- const sub =
337- preloadedSubscription !== undefined
338- ? preloadedSubscription
339- : await getHighestPrioritySubscription ( userId )
340- const subIsOrgScoped = isOrgScopedSubscription ( sub , userId )
341- const billingEntity : BillingEntity =
342- subIsOrgScoped && sub
343- ? { type : 'organization' , id : sub . referenceId }
344- : { type : 'user' , id : userId }
345- const billingPeriod = preloadedBillingContext ?. billingPeriod ??
346- resolveSubscriptionUsagePeriod ( sub ) ?? { ...defaultBillingPeriod ( ) , source : 'default' }
347- const currentUsage = await getBillingPeriodUsageCost ( billingEntity , billingPeriod )
366+ const currentUsage = await readBlockedAccountUsage (
367+ userId ,
368+ preloadedSubscription ,
369+ preloadedBillingContext
370+ )
348371 return {
349372 isExceeded : true ,
350373 currentUsage,
0 commit comments