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/frontend/client/account.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { callback } from "awaiting";
7
declare const $: any; // jQuery
8
import * as message from "@cocalc/util/message";
9
import { AsyncCall, WebappClient } from "./client";
10
import type { ApiKey } from "@cocalc/util/db-schema/api-keys";
11
import api from "./api";
12
13
export class AccountClient {
14
private async_call: AsyncCall;
15
private client: WebappClient;
16
private create_account_lock: boolean = false;
17
18
constructor(client: WebappClient) {
19
this.client = client;
20
this.async_call = client.async_call;
21
}
22
23
private async call(message): Promise<any> {
24
return await this.async_call({
25
message,
26
allow_post: false, // never works or safe for account related functionality
27
timeout: 30, // 30s for all account stuff.
28
});
29
}
30
31
public async create_account(opts: {
32
first_name?: string;
33
last_name?: string;
34
email_address?: string;
35
password?: string;
36
agreed_to_terms?: boolean;
37
usage_intent?: string;
38
get_api_key?: boolean; // if given, will create/get api token in response message
39
token?: string; // only required if an admin set the account creation token.
40
}): Promise<any> {
41
if (this.create_account_lock) {
42
// don't allow more than one create_account message at once -- see https://github.com/sagemathinc/cocalc/issues/1187
43
return message.account_creation_failed({
44
reason: {
45
account_creation_failed:
46
"You are submitting too many requests to create an account; please wait a second.",
47
},
48
});
49
}
50
51
try {
52
this.create_account_lock = true;
53
return await this.call(message.create_account(opts));
54
} finally {
55
setTimeout(() => (this.create_account_lock = false), 1500);
56
}
57
}
58
59
public async delete_account(account_id: string): Promise<any> {
60
return await this.call(message.delete_account({ account_id }));
61
}
62
63
public async sign_in(opts: {
64
email_address: string;
65
password: string;
66
remember_me?: boolean;
67
get_api_key?: boolean; // if given, will create/get api token in response message
68
}): Promise<any> {
69
return await this.async_call({
70
message: message.sign_in(opts),
71
error_event: false,
72
});
73
}
74
75
public async cookies(mesg): Promise<void> {
76
const f = (cb) => {
77
const j = $.ajax({
78
url: mesg.url,
79
data: { id: mesg.id, set: mesg.set, get: mesg.get, value: mesg.value },
80
});
81
j.done(() => cb());
82
j.fail(() => cb("failed"));
83
};
84
await callback(f);
85
}
86
87
public async sign_out(everywhere: boolean = false): Promise<void> {
88
await api("/accounts/sign-out", { all: everywhere });
89
delete this.client.account_id;
90
await this.call(message.sign_out({ everywhere }));
91
this.client.emit("signed_out");
92
}
93
94
public async change_password(
95
currentPassword: string,
96
newPassword: string = "",
97
): Promise<void> {
98
await api("/accounts/set-password", { currentPassword, newPassword });
99
}
100
101
public async change_email(
102
new_email_address: string,
103
password: string = "",
104
): Promise<void> {
105
if (this.client.account_id == null) {
106
throw Error("must be logged in");
107
}
108
const x = await this.call(
109
message.change_email_address({
110
account_id: this.client.account_id,
111
new_email_address,
112
password,
113
}),
114
);
115
if (x.error) {
116
throw Error(x.error);
117
}
118
}
119
120
public async send_verification_email(
121
only_verify: boolean = true,
122
): Promise<void> {
123
const account_id = this.client.account_id;
124
if (!account_id) {
125
throw Error("must be signed in to an account");
126
}
127
const x = await this.call(
128
message.send_verification_email({
129
account_id,
130
only_verify,
131
}),
132
);
133
if (x.error) {
134
throw Error(x.error);
135
}
136
}
137
138
// forgot password -- send forgot password request to server
139
public async forgot_password(email_address: string): Promise<void> {
140
const x = await this.call(
141
message.forgot_password({
142
email_address,
143
}),
144
);
145
if (x.error) {
146
throw Error(x.error);
147
}
148
}
149
150
// forgot password -- send forgot password request to server
151
public async reset_forgot_password(
152
reset_code: string,
153
new_password: string,
154
): Promise<void> {
155
const resp = await this.call(
156
message.reset_forgot_password({
157
reset_code,
158
new_password,
159
}),
160
);
161
if (resp.error) {
162
throw Error(resp.error);
163
}
164
}
165
166
// forget about a given passport authentication strategy for this user
167
public async unlink_passport(strategy: string, id: string): Promise<any> {
168
return await this.call(
169
message.unlink_passport({
170
strategy,
171
id,
172
}),
173
);
174
}
175
176
// legacy api: getting, setting, deleting, etc., the api key for this account
177
public async api_key(
178
action: "get" | "delete" | "regenerate",
179
password: string,
180
): Promise<string> {
181
if (this.client.account_id == null) {
182
throw Error("must be logged in");
183
}
184
return (
185
await this.call(
186
message.api_key({
187
action,
188
password,
189
}),
190
)
191
).api_key;
192
}
193
194
// new interface: getting, setting, editing, deleting, etc., the api keys for a project
195
public async api_keys(opts: {
196
action: "get" | "delete" | "create" | "edit";
197
password?: string;
198
name?: string;
199
id?: number;
200
expire?: Date;
201
}): Promise<ApiKey[] | undefined> {
202
if (this.client.account_id == null) {
203
throw Error("must be logged in");
204
}
205
// because message always uses id, so we have to use something else!
206
const opts2: any = { ...opts };
207
delete opts2.id;
208
opts2.key_id = opts.id;
209
return (await this.call(message.api_keys(opts2))).response;
210
}
211
}
212
213