improvement(consts): removed redundant default consts in favor of envvar defaults for storage & usage limits (#1737)

* improvement(consts): removed redundant default consts in favor of envvar defaults for storage & usage limits

* remove unnecessary tests
This commit is contained in:
Waleed
2025-10-26 21:43:00 -07:00
committed by GitHub
parent fb3d6d4c88
commit 659b46fa2f
11 changed files with 33 additions and 99 deletions

View File

@@ -2,9 +2,7 @@ import { Building2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Progress } from '@/components/ui/progress'
import { Skeleton } from '@/components/ui/skeleton'
import { DEFAULT_TEAM_TIER_COST_LIMIT } from '@/lib/billing/constants'
import { checkEnterprisePlan } from '@/lib/billing/subscriptions/utils'
import { env } from '@/lib/env'
import { checkEnterprisePlan, getTeamTierLimitPerSeat } from '@/lib/billing/subscriptions/utils'
type Subscription = {
id: string
@@ -102,7 +100,7 @@ export function TeamSeatsOverview({
<span className='font-medium text-sm'>Seats</span>
{!checkEnterprisePlan(subscriptionData) ? (
<span className='text-muted-foreground text-xs'>
(${env.TEAM_TIER_COST_LIMIT ?? DEFAULT_TEAM_TIER_COST_LIMIT}/month each)
(${getTeamTierLimitPerSeat()}/month each)
</span>
) : null}
</div>

View File

@@ -17,8 +17,7 @@ import {
SelectValue,
} from '@/components/ui/select'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { DEFAULT_TEAM_TIER_COST_LIMIT } from '@/lib/billing/constants'
import { env } from '@/lib/env'
import { getTeamTierLimitPerSeat } from '@/lib/billing/subscriptions/utils'
interface TeamSeatsProps {
open: boolean
@@ -55,7 +54,7 @@ export function TeamSeats({
}
}, [open, initialSeats])
const costPerSeat = env.TEAM_TIER_COST_LIMIT ?? DEFAULT_TEAM_TIER_COST_LIMIT
const costPerSeat = getTeamTierLimitPerSeat()
const totalMonthlyCost = selectedSeats * costPerSeat
const costChange = currentSeats ? (selectedSeats - currentSeats) * costPerSeat : 0

View File

@@ -1,9 +1,7 @@
import { useCallback, useEffect, useState } from 'react'
import { Alert, AlertDescription, AlertTitle, Skeleton } from '@/components/ui'
import { useSession } from '@/lib/auth-client'
import { DEFAULT_TEAM_TIER_COST_LIMIT } from '@/lib/billing/constants'
import { checkEnterprisePlan } from '@/lib/billing/subscriptions/utils'
import { env } from '@/lib/env'
import { checkEnterprisePlan, getTeamTierLimitPerSeat } from '@/lib/billing/subscriptions/utils'
import { createLogger } from '@/lib/logs/console/logger'
import {
MemberInvitationCard,
@@ -295,8 +293,7 @@ export function TeamManagement() {
<ul className='ml-4 list-disc space-y-2 text-muted-foreground text-xs'>
<li>
Your team is billed a minimum of $
{(subscriptionData?.seats || 0) *
(env.TEAM_TIER_COST_LIMIT ?? DEFAULT_TEAM_TIER_COST_LIMIT)}
{(subscriptionData?.seats || 0) * getTeamTierLimitPerSeat()}
/month for {subscriptionData?.seats || 0} licensed seats
</li>
<li>All team member usage is pooled together from a shared limit</li>
@@ -414,7 +411,7 @@ export function TeamManagement() {
open={isAddSeatDialogOpen}
onOpenChange={setIsAddSeatDialogOpen}
title='Add Team Seats'
description={`Each seat costs $${env.TEAM_TIER_COST_LIMIT ?? DEFAULT_TEAM_TIER_COST_LIMIT}/month and provides $${env.TEAM_TIER_COST_LIMIT ?? DEFAULT_TEAM_TIER_COST_LIMIT} in monthly inference credits. Adjust the number of licensed seats for your team.`}
description={`Each seat costs $${getTeamTierLimitPerSeat()}/month and provides $${getTeamTierLimitPerSeat()} in monthly inference credits. Adjust the number of licensed seats for your team.`}
currentSeats={subscriptionData?.seats || 1}
initialSeats={newSeatCount}
isLoading={isUpdatingSeats}

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react'
import { DEFAULT_FREE_CREDITS } from '@/lib/billing/constants'
import { getFreeTierLimit } from '@/lib/billing/subscriptions/utils'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('useSubscriptionState')
@@ -82,7 +82,7 @@ export function useSubscriptionState() {
usage: {
current: data?.usage?.current ?? 0,
limit: data?.usage?.limit ?? DEFAULT_FREE_CREDITS,
limit: data?.usage?.limit ?? getFreeTierLimit(),
percentUsed: data?.usage?.percentUsed ?? 0,
isWarning: data?.usage?.isWarning ?? false,
isExceeded: data?.usage?.isExceeded ?? false,
@@ -203,9 +203,9 @@ export function useUsageLimit() {
}
return {
currentLimit: data?.currentLimit ?? DEFAULT_FREE_CREDITS,
currentLimit: data?.currentLimit ?? getFreeTierLimit(),
canEdit: data?.canEdit ?? false,
minimumLimit: data?.minimumLimit ?? DEFAULT_FREE_CREDITS,
minimumLimit: data?.minimumLimit ?? getFreeTierLimit(),
plan: data?.plan ?? 'free',
setBy: data?.setBy,
updatedAt: data?.updatedAt ? new Date(data.updatedAt) : null,

View File

@@ -1,27 +1,5 @@
/**
* Billing and cost constants shared between client and server code
*/
/**
* Fallback free credits (in dollars) when env var is not set
*/
export const DEFAULT_FREE_CREDITS = 10
/**
* Default per-user minimum limits (in dollars) for paid plans when env vars are absent
*/
export const DEFAULT_PRO_TIER_COST_LIMIT = 20
export const DEFAULT_TEAM_TIER_COST_LIMIT = 40
export const DEFAULT_ENTERPRISE_TIER_COST_LIMIT = 200
/**
* Base charge applied to every workflow execution
* This charge is applied regardless of whether the workflow uses AI models
*/
export const BASE_EXECUTION_CHARGE = 0.001
/**
* Default threshold (in dollars) for incremental overage billing
* When unbilled overage reaches this amount, an invoice item is created
*/
export const DEFAULT_OVERAGE_THRESHOLD = 50

View File

@@ -4,15 +4,9 @@
*/
import { db } from '@sim/db'
import {
DEFAULT_ENTERPRISE_STORAGE_LIMIT_GB,
DEFAULT_FREE_STORAGE_LIMIT_GB,
DEFAULT_PRO_STORAGE_LIMIT_GB,
DEFAULT_TEAM_STORAGE_LIMIT_GB,
} from '@sim/db/consts'
import { organization, subscription, userStats } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { getEnv } from '@/lib/env'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('StorageLimits')
@@ -25,25 +19,16 @@ function gbToBytes(gb: number): number {
}
/**
* Get storage limits from environment variables with fallback to constants
* Get storage limits from environment variables
* Returns limits in bytes
* Defaults are defined in env.ts and will be applied automatically
*/
export function getStorageLimits() {
return {
free: gbToBytes(
Number.parseInt(getEnv('FREE_STORAGE_LIMIT_GB') || String(DEFAULT_FREE_STORAGE_LIMIT_GB))
),
pro: gbToBytes(
Number.parseInt(getEnv('PRO_STORAGE_LIMIT_GB') || String(DEFAULT_PRO_STORAGE_LIMIT_GB))
),
team: gbToBytes(
Number.parseInt(getEnv('TEAM_STORAGE_LIMIT_GB') || String(DEFAULT_TEAM_STORAGE_LIMIT_GB))
),
enterpriseDefault: gbToBytes(
Number.parseInt(
getEnv('ENTERPRISE_STORAGE_LIMIT_GB') || String(DEFAULT_ENTERPRISE_STORAGE_LIMIT_GB)
)
),
free: gbToBytes(env.FREE_STORAGE_LIMIT_GB),
pro: gbToBytes(env.PRO_STORAGE_LIMIT_GB),
team: gbToBytes(env.TEAM_STORAGE_LIMIT_GB),
enterpriseDefault: gbToBytes(env.ENTERPRISE_STORAGE_LIMIT_GB),
}
}
@@ -78,7 +63,6 @@ export function getStorageLimitForPlan(plan: string, metadata?: any): number {
*/
export async function getUserStorageLimit(userId: string): Promise<number> {
try {
// Check if user is in a team/enterprise org
const { getHighestPrioritySubscription } = await import('@/lib/billing/core/subscription')
const sub = await getHighestPrioritySubscription(userId)
@@ -92,9 +76,7 @@ export async function getUserStorageLimit(userId: string): Promise<number> {
return limits.pro
}
// Team/Enterprise: Use organization limit
if (sub.plan === 'team' || sub.plan === 'enterprise') {
// Get organization storage limit
const orgRecord = await db
.select({ metadata: subscription.metadata })
.from(subscription)
@@ -108,7 +90,6 @@ export async function getUserStorageLimit(userId: string): Promise<number> {
}
}
// Default for team/enterprise
return sub.plan === 'enterprise' ? limits.enterpriseDefault : limits.team
}
@@ -125,12 +106,10 @@ export async function getUserStorageLimit(userId: string): Promise<number> {
*/
export async function getUserStorageUsage(userId: string): Promise<number> {
try {
// Check if user is in a team/enterprise org
const { getHighestPrioritySubscription } = await import('@/lib/billing/core/subscription')
const sub = await getHighestPrioritySubscription(userId)
if (sub && (sub.plan === 'team' || sub.plan === 'enterprise')) {
// Use organization storage
const orgRecord = await db
.select({ storageUsedBytes: organization.storageUsedBytes })
.from(organization)
@@ -140,7 +119,6 @@ export async function getUserStorageUsage(userId: string): Promise<number> {
return orgRecord.length > 0 ? orgRecord[0].storageUsedBytes || 0 : 0
}
// Free/Pro: Use user stats
const stats = await db
.select({ storageUsedBytes: userStats.storageUsedBytes })
.from(userStats)

View File

@@ -1,37 +1,31 @@
import {
DEFAULT_ENTERPRISE_TIER_COST_LIMIT,
DEFAULT_FREE_CREDITS,
DEFAULT_PRO_TIER_COST_LIMIT,
DEFAULT_TEAM_TIER_COST_LIMIT,
} from '@/lib/billing/constants'
import { env } from '@/lib/env'
/**
* Get the free tier limit from env or fallback to default
* Get the free tier limit
*/
export function getFreeTierLimit(): number {
return env.FREE_TIER_COST_LIMIT || DEFAULT_FREE_CREDITS
return env.FREE_TIER_COST_LIMIT
}
/**
* Get the pro tier limit from env or fallback to default
* Get the pro tier limit
*/
export function getProTierLimit(): number {
return env.PRO_TIER_COST_LIMIT || DEFAULT_PRO_TIER_COST_LIMIT
return env.PRO_TIER_COST_LIMIT
}
/**
* Get the team tier limit per seat from env or fallback to default
* Get the team tier limit per seat
*/
export function getTeamTierLimitPerSeat(): number {
return env.TEAM_TIER_COST_LIMIT || DEFAULT_TEAM_TIER_COST_LIMIT
return env.TEAM_TIER_COST_LIMIT
}
/**
* Get the enterprise tier limit per seat from env or fallback to default
* Get the enterprise tier limit per seat
*/
export function getEnterpriseTierLimitPerSeat(): number {
return env.ENTERPRISE_TIER_COST_LIMIT || DEFAULT_ENTERPRISE_TIER_COST_LIMIT
return env.ENTERPRISE_TIER_COST_LIMIT
}
export function checkEnterprisePlan(subscription: any): boolean {

View File

@@ -2,7 +2,6 @@ import { db } from '@sim/db'
import { member, subscription, userStats } from '@sim/db/schema'
import { and, eq, inArray, sql } from 'drizzle-orm'
import type Stripe from 'stripe'
import { DEFAULT_OVERAGE_THRESHOLD } from '@/lib/billing/constants'
import { calculateSubscriptionOverage, getPlanPricing } from '@/lib/billing/core/billing'
import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription'
import { requireStripeClient } from '@/lib/billing/stripe-client'
@@ -11,7 +10,7 @@ import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('ThresholdBilling')
const OVERAGE_THRESHOLD = env.OVERAGE_THRESHOLD_DOLLARS || DEFAULT_OVERAGE_THRESHOLD
const OVERAGE_THRESHOLD = env.OVERAGE_THRESHOLD_DOLLARS
function parseDecimal(value: string | number | null | undefined): number {
if (value === null || value === undefined) return 0

View File

@@ -41,16 +41,16 @@ export const env = createEnv({
STRIPE_SECRET_KEY: z.string().min(1).optional(), // Stripe secret key for payment processing
STRIPE_WEBHOOK_SECRET: z.string().min(1).optional(), // General Stripe webhook secret
STRIPE_FREE_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for free tier
FREE_TIER_COST_LIMIT: z.number().optional(), // Cost limit for free tier users
FREE_TIER_COST_LIMIT: z.number().optional().default(10), // Cost limit for free tier users (in dollars)
FREE_STORAGE_LIMIT_GB: z.number().optional().default(5), // Storage limit in GB for free tier users
STRIPE_PRO_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for pro tier
PRO_TIER_COST_LIMIT: z.number().optional(), // Cost limit for pro tier users
PRO_TIER_COST_LIMIT: z.number().optional().default(20), // Cost limit for pro tier users (in dollars)
PRO_STORAGE_LIMIT_GB: z.number().optional().default(50), // Storage limit in GB for pro tier users
STRIPE_TEAM_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for team tier
TEAM_TIER_COST_LIMIT: z.number().optional(), // Cost limit for team tier users
TEAM_TIER_COST_LIMIT: z.number().optional().default(40), // Cost limit per seat for team tier (in dollars)
TEAM_STORAGE_LIMIT_GB: z.number().optional().default(500), // Storage limit in GB for team tier organizations (pooled)
STRIPE_ENTERPRISE_PRICE_ID: z.string().min(1).optional(), // Stripe price ID for enterprise tier
ENTERPRISE_TIER_COST_LIMIT: z.number().optional(), // Cost limit for enterprise tier users
ENTERPRISE_TIER_COST_LIMIT: z.number().optional().default(200), // Cost limit per seat for enterprise tier (in dollars)
ENTERPRISE_STORAGE_LIMIT_GB: z.number().optional().default(500), // Default storage limit in GB for enterprise tier (can be overridden per org)
BILLING_ENABLED: z.boolean().optional(), // Enable billing enforcement and usage tracking
OVERAGE_THRESHOLD_DOLLARS: z.number().optional().default(50), // Dollar threshold for incremental overage billing (default: $50)

View File

@@ -1,6 +1,6 @@
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import { DEFAULT_FREE_CREDITS } from '@/lib/billing/constants'
import { getFreeTierLimit } from '@/lib/billing/subscriptions/utils'
import { createLogger } from '@/lib/logs/console/logger'
import type {
BillingStatus,
@@ -16,7 +16,7 @@ const CACHE_DURATION = 30 * 1000
const defaultUsage: UsageData = {
current: 0,
limit: DEFAULT_FREE_CREDITS,
limit: getFreeTierLimit(),
percentUsed: 0,
isWarning: false,
isExceeded: false,

View File

@@ -8,15 +8,6 @@
*/
export const DEFAULT_FREE_CREDITS = 10
/**
* Storage limit constants (in GB)
* Can be overridden via environment variables
*/
export const DEFAULT_FREE_STORAGE_LIMIT_GB = 5
export const DEFAULT_PRO_STORAGE_LIMIT_GB = 50
export const DEFAULT_TEAM_STORAGE_LIMIT_GB = 500
export const DEFAULT_ENTERPRISE_STORAGE_LIMIT_GB = 500
/**
* Tag slots available for knowledge base documents and embeddings
*/