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