CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/licenses/purchase/purchase-info.ts
Views: 687
1
import type {
2
Period,
3
SiteLicenseDescriptionDB,
4
} from "@cocalc/util/upgrades/shopping";
5
import type { PurchaseInfo, StartEndDates, Subscription } from "./types";
6
import { CURRENT_VERSION } from "./consts";
7
import type { Date0 } from "@cocalc/util/types/store";
8
import dayjs from "dayjs";
9
10
// this ALWAYS returns purchaseInfo that is the *current* version.
11
export default function getPurchaseInfo(
12
conf: SiteLicenseDescriptionDB,
13
): PurchaseInfo {
14
conf.type = conf.type ?? "quota"; // backwards compatibility
15
16
const { title, description } = conf;
17
18
switch (conf.type) {
19
case "quota":
20
const {
21
type,
22
user,
23
run_limit,
24
period,
25
ram,
26
cpu,
27
disk,
28
member,
29
uptime,
30
boost = false,
31
} = conf;
32
return {
33
version: CURRENT_VERSION,
34
type, // "quota"
35
user,
36
upgrade: "custom" as "custom",
37
quantity: run_limit,
38
subscription: (period == "range" ? "no" : period) as Subscription,
39
...fixRange(conf.range, conf.period),
40
custom_ram: ram,
41
custom_dedicated_ram: 0,
42
custom_cpu: cpu,
43
custom_dedicated_cpu: 0,
44
custom_disk: disk,
45
custom_member: member,
46
custom_uptime: uptime,
47
boost,
48
title,
49
description,
50
};
51
52
case "vm":
53
return {
54
version: CURRENT_VERSION,
55
type: "vm",
56
quantity: 1,
57
dedicated_vm: conf.dedicated_vm,
58
subscription: "no",
59
...fixRange(conf.range, conf.period),
60
title,
61
description,
62
};
63
64
case "disk":
65
return {
66
version: CURRENT_VERSION,
67
type: "disk",
68
quantity: 1,
69
dedicated_disk: conf.dedicated_disk,
70
subscription: conf.period,
71
title,
72
description,
73
...fixRange(null, conf.period),
74
};
75
}
76
}
77
78
// Make sure both start and end dates defined as Date. For all licenses we
79
// always require that both are defined. For a subscription, the end date must
80
// be defined, but it will get periodically moved forward as the subscription
81
// is updated.
82
// Later, when actually saving the range to the database, we will maybe append
83
// a portion of the start which is in the past.
84
export function fixRange(
85
rangeOrig: readonly [Date0 | string, Date0 | string] | undefined | null,
86
period: Period,
87
): StartEndDates {
88
if (period != "range") {
89
// ATTN! -- we messed up and didn't deal with this case before, and a user
90
// could in theory:
91
// 1. set the period to 'range', and put in a week period via start and end
92
// 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) for
96
// the price computation.
97
rangeOrig = null;
98
}
99
const now = new Date();
100
if (rangeOrig == null) {
101
if (period == "range") {
102
throw Error(
103
"if period is 'range', then start and end dates must be explicitly given",
104
);
105
}
106
return { start: now, end: addPeriod(now, period) };
107
}
108
109
return {
110
start: rangeOrig?.[0] ? new Date(rangeOrig?.[0]) : now,
111
end: rangeOrig?.[1] ? new Date(rangeOrig?.[1]) : addPeriod(now, period),
112
};
113
}
114
115
function addPeriod(date: Date, period: Period): Date {
116
if (period == "range") {
117
throw Error("period must not be range");
118
} else if (period == "monthly") {
119
return dayjs(date).add(1, "month").toDate();
120
} else if (period == "yearly") {
121
return dayjs(date).add(1, "year").toDate();
122
} else {
123
throw Error(`unsupported period ${period}`);
124
}
125
}
126
127