Files
podcastdistributiona/lib/usage/limits.ts
T

52 lines
1.5 KiB
TypeScript
Raw Normal View History

import { getEffectivePlan } from "@/lib/billing/subscription";
import { getUsage } from "./meter";
import { PLANS, UNLIMITED, withinLimit, type PlanKey, type UsageMetric } from "@/lib/billing/plans";
export interface LimitCheck {
allowed: boolean;
used: number;
limit: number; // UNLIMITED (-1) when uncapped
plan: PlanKey;
metric: UsageMetric;
}
/** Thrown by enforceLimit when a metric's monthly cap is reached. */
export class LimitExceededError extends Error {
constructor(public check: LimitCheck) {
super(`Monthly ${check.metric} limit reached on the ${check.plan} plan`);
this.name = "LimitExceededError";
}
}
/** Read-only check of a metric against the subject's plan + current usage. */
export async function checkLimit(
userId: string,
metric: UsageMetric,
activeOrgId?: string | null
): Promise<LimitCheck> {
const { key, subjectId } = await getEffectivePlan(userId, activeOrgId);
const used = await getUsage(subjectId, metric);
return {
allowed: withinLimit(key, metric, used),
used,
limit: PLANS[key].limits[metric],
plan: key,
metric,
};
}
/** Throw LimitExceededError if the metric is over its cap. */
export async function enforceLimit(
userId: string,
metric: UsageMetric,
activeOrgId?: string | null
): Promise<LimitCheck> {
const check = await checkLimit(userId, metric, activeOrgId);
if (!check.allowed) throw new LimitExceededError(check);
return check;
}
export function isUnlimited(limit: number): boolean {
return limit === UNLIMITED;
}