-
Notifications
You must be signed in to change notification settings - Fork 220
/
Copy pathaccount-queries.ts
155 lines (139 loc) · 4.56 KB
/
account-queries.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: MS-RSL – see LICENSE.md for details
*/
// Various functions involving the database and accounts.
import { callback2 } from "@cocalc/util/async-utils";
import {
assert_valid_account_id,
assert_valid_email_address,
len,
} from "@cocalc/util/misc";
import { is_a_site_license_manager } from "./site-license/search";
import { PostgreSQL, SetAccountFields } from "./types";
//import getLogger from "@cocalc/backend/logger";
//const L = getLogger("db:pg:account-queries");
/* For now we define "paying customer" to mean they have a subscription.
It's OK if it expired. They at least bought one once.
This is mainly used for anti-abuse purposes...
TODO: modernize this or don't use this at all...
*/
export async function is_paying_customer(
db: PostgreSQL,
account_id: string,
): Promise<boolean> {
let x;
try {
x = await callback2(db.get_account, {
account_id,
columns: ["stripe_customer"],
});
} catch (_err) {
// error probably means there is no such account or account_id is badly formatted.
return false;
}
if (!!x.stripe_customer?.subscriptions?.total_count) {
// they have at least one subscription of some form -- so that's enough to count.
return true;
}
// If they manage any licenses then they also count:
return await is_a_site_license_manager(db, account_id);
}
// this is like set_account_info_if_different, but only sets the fields if they're not set
export async function set_account_info_if_not_set(
opts: SetAccountFields,
): Promise<{ email_changed: boolean }> {
return await set_account_info_if_different(opts, false);
}
// This sets the given fields of an account, if it is different from the current value – except for the email address, which we only set but not change
export async function set_account_info_if_different(
opts: SetAccountFields,
overwrite = true,
): Promise<{ email_changed: boolean }> {
const columns = ["email_address", "first_name", "last_name"];
// this could throw an error for "no such account"
const account = await get_account<{
email_address: string;
first_name: string;
last_name: string;
}>(opts.db, opts.account_id, columns);
const do_set: { [field: string]: string } = {};
let do_email: string | undefined = undefined;
for (const field of columns) {
if (typeof opts[field] !== "string") continue;
if (!overwrite && account[field] != null) continue;
if (account[field] != opts[field]) {
if (field === "email_address") {
do_email = opts[field];
} else {
do_set[field] = opts[field];
}
}
}
if (len(do_set) > 0) {
await set_account(opts.db, opts.account_id, do_set);
}
if (do_email) {
if (account["email_address"] != null) {
// if it changes, we have to call the change_email_address function
await callback2(opts.db.change_email_address.bind(opts.db), {
account_id: opts.account_id,
email_address: do_email,
});
}
// Just changed email address - might be added to a project...
await callback2(opts.db.do_account_creation_actions.bind(opts.db), {
email_address: do_email,
account_id: opts.account_id,
});
}
return { email_changed: !!do_email };
}
export async function set_account(
db: PostgreSQL,
account_id: string,
set: { [field: string]: any },
): Promise<void> {
await db.async_query({
query: "UPDATE accounts",
where: { "account_id = $::UUID": account_id },
set,
});
}
// TODO typing: pick the column fields from the actual account type stored in the database
export async function get_account<T>(
db: PostgreSQL,
account_id: string,
columns: string[],
): Promise<T> {
return await callback2(db.get_account.bind(db), {
account_id,
columns,
});
}
interface SetEmailAddressVerifiedOpts {
db: PostgreSQL;
account_id: string;
email_address: string;
}
export async function set_email_address_verified(
opts: SetEmailAddressVerifiedOpts,
): Promise<void> {
const { db, account_id, email_address } = opts;
assert_valid_account_id(account_id);
assert_valid_email_address(email_address);
await db.async_query({
query: "UPDATE accounts",
jsonb_set: { email_address_verified: { [email_address]: new Date() } },
where: { "account_id = $::UUID": account_id },
});
}
export async function is_admin(
db: PostgreSQL,
account_id: string,
): Promise<boolean> {
const { groups } = await get_account<{ groups?: string[] }>(db, account_id, [
"groups",
]);
return Array.isArray(groups) && groups.includes("admin");
}