Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283// Shamelessly stolen from dub.co
import type { WorkspacePlan } from "@openstatus/db/src/schema";import { allPlans } from "@openstatus/db/src/schema/plan/config";import type { Addons, BillingInterval, Limits,} from "@openstatus/db/src/schema/plan/schema";import { getAddonMaxQuantity, getAddonPackSize, getLimits, updateAddonInLimits,} from "@openstatus/db/src/schema/plan/utils";import type Stripe from "stripe";
/** * Rebuild a workspace's limits from the full set of subscription line items. * The plan item sets the baseline; each addon item then re-applies its flag or * quantity on top, so purchased addons survive subscription updates instead of * being reset to the plan default. Returns null when no plan item is present. * Throws on a line item whose price is neither a known plan nor a known addon, * so misconfigured prices surface instead of silently drifting from billing. */export function buildLimitsFromSubscription( subscription: Stripe.Subscription,): { plan: WorkspacePlan; limits: Limits } | null { const detectedPlan = subscription.items.data .map((item) => getPlanFromPriceId(item.price.id)) .find((plan) => plan !== undefined);
if (!detectedPlan) return null;
let limits: Limits = getLimits(detectedPlan.plan);
for (const item of subscription.items.data) { if (getPlanFromPriceId(item.price.id)) continue; const feature = getFeatureFromPriceId(item.price.id); if (!feature) { throw new Error( `Unsupported Stripe price on subscription: ${item.price.id}`, ); } // Accumulate onto the running value so repeated addon items add up; boolean // addons just flip on. One unit of a pack addon grants `packSize` units. const current = limits[feature.feature]; const value = typeof current === "number" ? current + (item.quantity ?? 1) * getAddonPackSize(feature.feature) : true; limits = updateAddonInLimits(limits, feature.feature, value); }
return { plan: detectedPlan.plan, limits };}
/** * Resolve a requested pack count into the Stripe subscription quantity and the * workspace limit it produces. Throws when the request is not a whole number of * packs or exceeds the self-serve ceiling. */export function resolveAddonQuantity(args: { addon: keyof Addons; plan: WorkspacePlan; packs: number;}): { quantity: number; newLimit: number; packSize: number; maxPacks: number | null;} { const { addon, plan, packs } = args; const packSize = getAddonPackSize(addon); const maxPacks = getAddonMaxQuantity(addon);
if (!Number.isInteger(packs) || packs < 0) { throw new Error( `Quantity must be a whole number of packs, received ${packs}`, ); }
if (maxPacks !== null && packs > maxPacks) { throw new Error( `You can add up to ${maxPacks * packSize} with this add-on. Contact us for more.`, ); }
const planDefault = allPlans[plan].limits[addon]; if (typeof planDefault !== "number") { throw new Error(`${addon} is not a quantity add-on`); }
return { quantity: packs, newLimit: planDefault + packs * packSize, packSize, maxPacks, };}
export const getPlanFromPriceId = (priceId: string) => { const env = process.env.NEXT_PUBLIC_VERCEL_ENV === "production" ? "production" : "test"; return PLANS.find( (plan) => plan.price.monthly.priceIds[env] === priceId || plan.price.yearly.priceIds[env] === priceId, );};
export const getFeatureFromPriceId = (priceId: string) => { const env = process.env.NEXT_PUBLIC_VERCEL_ENV === "production" ? "production" : "test"; return FEATURES.find((feature) => Object.values(feature.price).some((p) => p.priceIds[env] === priceId), );};
export const getPriceIdForPlan = ( plan: WorkspacePlan, interval: BillingInterval = "monthly",) => { const env = process.env.NEXT_PUBLIC_VERCEL_ENV === "production" ? "production" : "test"; return PLANS.find((p) => p.plan === plan)?.price[interval].priceIds[env];};
export const getPriceIdForFeature = (feature: keyof Addons) => { const env = process.env.NEXT_PUBLIC_VERCEL_ENV === "production" ? "production" : "test"; return FEATURES.find((f) => f.feature === feature)?.price.monthly.priceIds[ env ];};
export const PLANS = [ { plan: "team", price: { monthly: { priceIds: { test: "price_1OVHQDBXJcTfzsyJjfiXl10Y", production: "price_1RxsLNBXJcTfzsyJ7La5Jn5y", }, }, yearly: { priceIds: { test: "XXX", production: "price_1TDlHxBXJcTfzsyJygJw92nU", }, }, }, }, { plan: "starter", price: { monthly: { priceIds: { test: "price_1OVHPlBXJcTfzsyJvPlB1kNb", production: "price_1RxsJzBXJcTfzsyJBOztaKlR", }, }, yearly: { priceIds: { test: "XXX", production: "price_1TDlGSBXJcTfzsyJMsDV4DRQ", }, }, }, }, { plan: "scale", price: { monthly: { priceIds: { test: "XXXX", production: "price_1Te9BLBXJcTfzsyJlXLsuyFP", }, }, yearly: { priceIds: { test: "XXXX", production: "price_1Te9BLBXJcTfzsyJliabl2ou", }, }, }, },] satisfies Array<{ plan: WorkspacePlan; price: { monthly: { priceIds: { test: string; production: string } }; yearly: { priceIds: { test: string; production: string } }; };}>;
export const FEATURES = [ { feature: "email-domain-protection", price: { monthly: { priceIds: { test: "price_1Sl4xqBXJcTfzsyJlzpD1DDm", production: "price_1Sl6oqBXJcTfzsyJCxtzDIx5", }, }, }, }, { feature: "ip-restriction", price: { monthly: { priceIds: { test: "price_1TMpxlBXJcTfzsyJ1woQtafW", production: "price_1TMq0GBXJcTfzsyJrIVx9KPL", }, }, }, }, { feature: "white-label", price: { monthly: { priceIds: { test: "price_1SlbQsBXJcTfzsyJ1awtpOno", production: "price_1SlbSdBXJcTfzsyJahJiFE8D", }, }, }, }, { feature: "custom-theme", price: { monthly: { priceIds: { test: "price_1TwKPqBXJcTfzsyJqAu4Wqwh", production: "price_1Tvv0zBXJcTfzsyJseLIjNnz", }, }, }, }, { feature: "status-pages", price: { monthly: { priceIds: { test: "price_1Slrk8BXJcTfzsyJXQxshFU4", production: "price_1SlrkHBXJcTfzsyJIxHeKUYe", }, }, }, }, { feature: "monitors", price: { monthly: { priceIds: { // TODO: create the "Monitor Pack" product and its monthly // multi-currency price (USD 15 / EUR 15) in Stripe, then paste the ids. test: "price_1UChsCBXJcTfzsyJgomhUtYY", production: "price_1UChsCBXJcTfzsyJgomhUtYY", }, }, }, }, { feature: "sso", price: { monthly: { priceIds: { test: "price_1TySGYBXJcTfzsyJQFVFJi6N", production: "price_1TySGYBXJcTfzsyJQFVFJi6N", }, }, }, },] satisfies Array<{ feature: keyof Addons; price: { monthly: { priceIds: { test: string; production: string } }; };}>;