Path: blob/master/src/packages/util/licenses/purchase/purchase-info.ts
1450 views
import type {1Period,2SiteLicenseDescriptionDB,3} from "@cocalc/util/upgrades/shopping";4import type { PurchaseInfo, StartEndDates, Subscription } from "./types";5import { CURRENT_VERSION } from "./consts";6import type { Date0 } from "@cocalc/util/types/store";7import dayjs from "dayjs";89// this ALWAYS returns purchaseInfo that is the *current* version.10export default function getPurchaseInfo(11conf: SiteLicenseDescriptionDB,12): PurchaseInfo {13conf.type = conf.type ?? "quota"; // backwards compatibility1415const { title, description } = conf;1617switch (conf.type) {18case "quota":19const {20type,21user,22run_limit,23period,24ram,25cpu,26disk,27member,28uptime,29boost = false,30} = conf;31return {32version: CURRENT_VERSION,33type, // "quota"34user,35upgrade: "custom" as "custom",36quantity: run_limit,37subscription: (period == "range" ? "no" : period) as Subscription,38...fixRange(conf.range, conf.period),39custom_ram: ram,40custom_dedicated_ram: 0,41custom_cpu: cpu,42custom_dedicated_cpu: 0,43custom_disk: disk,44custom_member: member,45custom_uptime: uptime,46boost,47title,48description,49};5051case "vm":52return {53version: CURRENT_VERSION,54type: "vm",55quantity: 1,56dedicated_vm: conf.dedicated_vm,57subscription: "no",58...fixRange(conf.range, conf.period),59title,60description,61};6263case "disk":64return {65version: CURRENT_VERSION,66type: "disk",67quantity: 1,68dedicated_disk: conf.dedicated_disk,69subscription: conf.period,70title,71description,72...fixRange(null, conf.period),73};74}75}7677// Make sure both start and end dates defined as Date. For all licenses we78// always require that both are defined. For a subscription, the end date must79// be defined, but it will get periodically moved forward as the subscription80// is updated.81// Later, when actually saving the range to the database, we will maybe append82// a portion of the start which is in the past.83export function fixRange(84rangeOrig: readonly [Date0 | string, Date0 | string] | undefined | null,85period: Period,86noRangeShift?: boolean,87): StartEndDates {88if (period != "range" && !noRangeShift) {89// ATTN! -- we messed up and didn't deal with this case before, and a user90// could in theory:91// 1. set the period to 'range', and put in a week period via start and end92// 2. set the period to 'yearly'.93// Then rangeOrig is still a week, so they pay for one week instead of one year!94// Instead, in whenever the period is 'monthly' or 'yearly' (anything but 'range',95// we unset rangeOrig here so we use start=now and end=now + a year (say) for96// the price computation.97rangeOrig = null;98}99const now = new Date();100if (rangeOrig == null) {101if (period == "range") {102throw Error(103"if period is 'range', then start and end dates must be explicitly given",104);105}106// we expand the dates to be as inclusive as possible for subscriptions, since107// that doesn't result in any more charge to the user.108return {109start: dayjs(now).startOf("day").toDate(),110end: dayjs(addPeriod(now, period)).endOf("day").toDate(),111};112}113114return {115start: rangeOrig?.[0] ? new Date(rangeOrig?.[0]) : now,116end: rangeOrig?.[1] ? new Date(rangeOrig?.[1]) : addPeriod(now, period),117};118}119120function addPeriod(date: Date, period: Period): Date {121if (period == "range") {122throw Error("period must not be range");123} else if (period == "monthly") {124return dayjs(date).add(1, "month").toDate();125} else if (period == "yearly") {126return dayjs(date).add(1, "year").toDate();127} else {128throw Error(`unsupported period ${period}`);129}130}131132133