Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/util/licenses/purchase/purchase-info.ts
Views: 687
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,86): StartEndDates {87if (period != "range") {88// ATTN! -- we messed up and didn't deal with this case before, and a user89// could in theory:90// 1. set the period to 'range', and put in a week period via start and end91// 2. set the period to 'yearly'.92// Then rangeOrig is still a week, so they pay for one week instead of one year!93// Instead, in whenever the period is 'monthly' or 'yearly' (anything but 'range',94// we unset rangeOrig here so we use start=now and end=now + a year (say) for95// the price computation.96rangeOrig = null;97}98const now = new Date();99if (rangeOrig == null) {100if (period == "range") {101throw Error(102"if period is 'range', then start and end dates must be explicitly given",103);104}105return { start: now, end: addPeriod(now, period) };106}107108return {109start: rangeOrig?.[0] ? new Date(rangeOrig?.[0]) : now,110end: rangeOrig?.[1] ? new Date(rangeOrig?.[1]) : addPeriod(now, period),111};112}113114function addPeriod(date: Date, period: Period): Date {115if (period == "range") {116throw Error("period must not be range");117} else if (period == "monthly") {118return dayjs(date).add(1, "month").toDate();119} else if (period == "yearly") {120return dayjs(date).add(1, "year").toDate();121} else {122throw Error(`unsupported period ${period}`);123}124}125126127