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/store/compute-cost.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { AVG_MONTH_DAYS } from "@cocalc/util/consts/billing";
7
import {
8
compute_cost,
9
compute_cost_dedicated,
10
} from "@cocalc/util/licenses/purchase/compute-cost";
11
import type {
12
CostInputPeriod,
13
PurchaseInfo,
14
} from "@cocalc/util/licenses/purchase/types";
15
import { fixRange } from "@cocalc/util/licenses/purchase/purchase-info";
16
import { getDays } from "@cocalc/util/stripe/timecalcs";
17
import { PRICES } from "@cocalc/util/upgrades/dedicated";
18
import type { ComputeCostProps } from "@cocalc/util/upgrades/shopping";
19
import { CURRENT_VERSION } from "@cocalc/util/licenses/purchase/consts";
20
21
function computeDedicatedDiskCost(
22
props: ComputeCostProps,
23
): CostInputPeriod | undefined {
24
if (props.type !== "disk") {
25
throw new Error("compute cost for disk only");
26
}
27
if (props.dedicated_disk == null)
28
throw new Error("missing props.dedicated_disk");
29
const { dedicated_disk } = props;
30
if (props.period != "monthly") throw new Error("period must be monthly");
31
if (dedicated_disk === false) throw new Error(`should not happen`);
32
33
try {
34
return {
35
input: { ...props, subscription: props.period },
36
...compute_cost_dedicated({
37
dedicated_disk,
38
subscription: props.period,
39
}),
40
};
41
} catch (err) {
42
console.log(`problem calculating dedicated price: ${err}`);
43
}
44
}
45
46
function computeDedicatedVMCost(
47
props: ComputeCostProps,
48
): CostInputPeriod | undefined {
49
if (props.type !== "vm") {
50
throw new Error("compute cost for VM only");
51
}
52
if (props.dedicated_vm == null) {
53
throw new Error("missing props.dedicated_vm");
54
}
55
const { range, dedicated_vm } = props;
56
const machine = dedicated_vm.machine;
57
if (range == null || range[0] == null || range[1] == null) return;
58
const price_day = PRICES.vms[machine]?.price_day;
59
if (price_day == null) return;
60
const days = getDays({ start: range[0], end: range[1] });
61
const price = days * price_day;
62
return {
63
cost: price,
64
cost_per_unit: price,
65
cost_per_project_per_month: AVG_MONTH_DAYS * price_day,
66
cost_sub_month: AVG_MONTH_DAYS * price_day,
67
cost_sub_year: 12 * AVG_MONTH_DAYS * price_day,
68
input: {
69
...props,
70
subscription: "no",
71
start: range[0] ?? new Date(),
72
end: range?.[1],
73
},
74
period: "range",
75
quantity: 1,
76
};
77
}
78
79
function computeCashVoucherPrice(props: ComputeCostProps) {
80
if (props.type != "cash-voucher") {
81
throw Error("BUG");
82
}
83
const cost = props.amount;
84
return {
85
// a lot of this is mainly for typescript.
86
cost,
87
cost_per_unit: cost,
88
input: {
89
...props,
90
subscription: "no",
91
},
92
period: "range",
93
cost_per_project_per_month: 0,
94
cost_sub_month: 0,
95
cost_sub_year: 0,
96
quantity: 1,
97
} as const;
98
}
99
100
export function computeCost(
101
props: ComputeCostProps,
102
): CostInputPeriod | undefined {
103
const type = props.type ?? "quota";
104
switch (type) {
105
case "cash-voucher":
106
return computeCashVoucherPrice(props);
107
108
case "disk":
109
return computeDedicatedDiskCost(props);
110
111
case "vm":
112
return computeDedicatedVMCost(props);
113
114
case "quota":
115
default:
116
if (
117
props.type == "disk" ||
118
props.type == "vm" ||
119
props.type == "cash-voucher"
120
) {
121
throw Error("must be a quota upgrade license");
122
}
123
const {
124
user,
125
run_limit,
126
period,
127
range,
128
ram,
129
cpu,
130
disk,
131
always_running,
132
member,
133
uptime,
134
boost = false, // if true, allow "all zero" values and start at 0 USD
135
} = props;
136
137
if (period == "range" && range?.[1] == null) {
138
return undefined;
139
}
140
141
const input: PurchaseInfo = {
142
version: CURRENT_VERSION,
143
type: "quota",
144
user,
145
upgrade: "custom" as "custom",
146
quantity: run_limit,
147
subscription: (period == "range" ? "no" : period) as
148
| "no"
149
| "monthly"
150
| "yearly",
151
custom_ram: ram,
152
custom_dedicated_ram: 0,
153
custom_cpu: cpu,
154
custom_dedicated_cpu: 0,
155
custom_disk: disk,
156
custom_always_running: always_running,
157
custom_member: member,
158
custom_uptime: uptime,
159
boost,
160
...fixRange(range, period),
161
};
162
return {
163
...compute_cost(input),
164
input,
165
period,
166
};
167
}
168
}
169
170