11import { db } from '@sim/db'
2- import { member, subscription, user, userStats } from '@sim/db/schema'
2+ import { member, subscription, user } from '@sim/db/schema'
33import { createLogger } from '@sim/logger'
44import { and, eq, inArray, sql } from 'drizzle-orm'
55import { getEffectiveBillingStatus, isOrganizationBillingBlocked } from '@/lib/billing/core/access'
66import { getHighestPrioritySubscription } from '@/lib/billing/core/plan'
7- import { getUserUsageLimit } from '@/lib/billing/core/usage'
87import {
98 getPlanTierCredits,
10- isOrgPlan,
119 isPro as isPlanPro,
1210 isTeam as isPlanTeam,
1311} from '@/lib/billing/plan-helpers'
@@ -16,12 +14,9 @@ import {
1614 checkProPlan,
1715 checkTeamPlan,
1816 ENTITLED_SUBSCRIPTION_STATUSES,
19- getFreeTierLimit,
20- getPerUserMinimumLimit,
2117 hasUsableSubscriptionAccess,
2218 USABLE_SUBSCRIPTION_STATUSES,
2319} from '@/lib/billing/subscriptions/utils'
24- import type { UserSubscriptionState } from '@/lib/billing/types'
2520import {
2621 isAccessControlEnabled,
2722 isBillingEnabled,
@@ -485,145 +480,6 @@ export async function hasLiveSyncAccess(userId: string): Promise<boolean> {
485480 }
486481}
487482
488- /**
489- * Check if user has exceeded their cost limit based on current period usage
490- */
491- export async function hasExceededCostLimit(userId: string): Promise<boolean> {
492- try {
493- if (!isBillingEnabled) {
494- return false
495- }
496-
497- const subscription = await getHighestPrioritySubscription(userId)
498-
499- let limit = getFreeTierLimit() // Default free tier limit
500-
501- if (subscription) {
502- // Team/Enterprise: Use organization limit
503- if (isOrgPlan(subscription.plan)) {
504- limit = await getUserUsageLimit(userId)
505- logger.info('Using organization limit', {
506- userId,
507- plan: subscription.plan,
508- limit,
509- })
510- } else {
511- // Pro/Free: Use individual limit
512- limit = getPerUserMinimumLimit(subscription)
513- logger.info('Using subscription-based limit', {
514- userId,
515- plan: subscription.plan,
516- limit,
517- })
518- }
519- } else {
520- logger.info('Using free tier limit', { userId, limit })
521- }
522-
523- // Get user stats to check current period usage
524- const statsRecords = await db.select().from(userStats).where(eq(userStats.userId, userId))
525-
526- if (statsRecords.length === 0) {
527- return false
528- }
529-
530- // Use current period cost instead of total cost for accurate billing period tracking
531- const currentCost = Number.parseFloat(
532- statsRecords[0].currentPeriodCost?.toString() || statsRecords[0].totalCost.toString()
533- )
534-
535- logger.info('Checking cost limit', { userId, currentCost, limit })
536-
537- return currentCost >= limit
538- } catch (error) {
539- logger.error('Error checking cost limit', { error, userId })
540- return false // Be conservative in case of error
541- }
542- }
543-
544- /**
545- * Check if sharing features are enabled for user
546- */
547- // Removed unused feature flag helpers: isSharingEnabled, isMultiplayerEnabled, isWorkspaceCollaborationEnabled
548-
549- /**
550- * Get comprehensive subscription state for a user
551- * Single function to get all subscription information
552- */
553- export async function getUserSubscriptionState(userId: string): Promise<UserSubscriptionState> {
554- try {
555- // Get subscription and user stats in parallel to minimize DB calls
556- const [subscription, statsRecords] = await Promise.all([
557- getHighestPrioritySubscription(userId),
558- db.select().from(userStats).where(eq(userStats.userId, userId)).limit(1),
559- ])
560-
561- // Determine plan types based on subscription (avoid redundant DB calls)
562- const isPro =
563- !isBillingEnabled ||
564- !!(
565- subscription &&
566- (checkProPlan(subscription) ||
567- checkTeamPlan(subscription) ||
568- checkEnterprisePlan(subscription))
569- )
570- const isTeam =
571- !isBillingEnabled ||
572- !!(subscription && (checkTeamPlan(subscription) || checkEnterprisePlan(subscription)))
573- const isEnterprise = !isBillingEnabled || !!(subscription && checkEnterprisePlan(subscription))
574- const isFree = !isPro && !isTeam && !isEnterprise
575-
576- // Determine plan name
577- let planName = 'free'
578- if (isEnterprise) planName = 'enterprise'
579- else if (isTeam) planName = 'team'
580- else if (isPro) planName = 'pro'
581-
582- // Check cost limit using already-fetched user stats
583- let hasExceededLimit = false
584- if (isBillingEnabled && statsRecords.length > 0) {
585- let limit = getFreeTierLimit() // Default free tier limit
586- if (subscription) {
587- // Team/Enterprise: Use organization limit
588- if (isOrgPlan(subscription.plan)) {
589- limit = await getUserUsageLimit(userId)
590- } else {
591- // Pro/Free: Use individual limit
592- limit = getPerUserMinimumLimit(subscription)
593- }
594- }
595-
596- const currentCost = Number.parseFloat(
597- statsRecords[0].currentPeriodCost?.toString() || statsRecords[0].totalCost.toString()
598- )
599- hasExceededLimit = currentCost >= limit
600- }
601-
602- return {
603- isPro,
604- isTeam,
605- isEnterprise,
606- isFree,
607- highestPrioritySubscription: subscription,
608- hasExceededLimit,
609- planName,
610- }
611- } catch (error) {
612- logger.error('Error getting user subscription state', { error, userId })
613-
614- // Return safe defaults in case of error
615- return {
616- isPro: false,
617- isTeam: false,
618- isEnterprise: false,
619- isFree: true,
620- highestPrioritySubscription: null,
621- hasExceededLimit: false,
622- planName: 'free',
623- }
624- }
625- }
626-
627483/**
628484 * Send welcome email for Pro and Team plan subscriptions
629485 */
0 commit comments