Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/client/users.ts
1503 views
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 { User } from "../frame-editors/generic/client";
7
import { isChatBot, chatBotName } from "@cocalc/frontend/account/chatbot";
8
import TTL from "@isaacs/ttlcache";
9
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
10
import type { WebappClient } from "./client";
11
12
const nameCache = new TTL({ ttl: 60 * 1000 });
13
14
export class UsersClient {
15
private client: WebappClient;
16
17
constructor(client) {
18
this.client = client;
19
}
20
21
/*
22
There are two possible item types in the query list: email addresses
23
and strings that are not email addresses. An email query item will return
24
account id, first name, last name, and email address for the unique
25
account with that email address, if there is one. A string query item
26
will return account id, first name, and last name for all matching
27
accounts.
28
29
We do not reveal email addresses of users queried by name to non admins.
30
31
String query matches first and last names that start with the given string.
32
If a string query item consists of two strings separated by space,
33
the search will return accounts in which the first name begins with one
34
of the two strings and the last name begins with the other.
35
String and email queries may be mixed in the list for a single
36
user_search call. Searches are case-insensitive.
37
38
Note: there is a hard limit of 50 returned items in the results, except for
39
admins that can search for more.
40
*/
41
user_search = reuseInFlight(
42
async ({
43
query,
44
limit = 20,
45
admin,
46
only_email,
47
}: {
48
query: string;
49
limit?: number;
50
admin?: boolean; // admins can do an admin version of the query, which also does substring searches on email address (not just name)
51
only_email?: boolean; // search only via email address
52
}): Promise<User[]> => {
53
return await this.client.conat_client.hub.system.userSearch({
54
query,
55
limit,
56
admin,
57
only_email,
58
});
59
},
60
);
61
62
// Gets username with given account_id. We use caching and aggregate to
63
// makes it so this never calls to the backend more than once at a time
64
// (per minute) for a given account_id.
65
get_username = reuseInFlight(
66
async (
67
account_id: string,
68
): Promise<{ first_name: string; last_name: string }> => {
69
if (isChatBot(account_id)) {
70
return { first_name: chatBotName(account_id), last_name: "" };
71
}
72
const v = await this.getNames([account_id]);
73
const u = v[account_id];
74
if (u == null) {
75
throw Error(`no user with account_id ${account_id}`);
76
}
77
return u;
78
},
79
);
80
81
// get map from account_id to first_name, last_name (or undefined if no account); cached
82
// for about a minute client side.
83
getNames = reuseInFlight(async (account_ids: string[]) => {
84
const x: {
85
[account_id: string]:
86
| {
87
first_name: string;
88
last_name: string;
89
profile?: { color?: string; image?: string };
90
}
91
| undefined;
92
} = {};
93
const v: string[] = [];
94
for (const account_id of account_ids) {
95
if (nameCache.has(account_id)) {
96
x[account_id] = nameCache.get(account_id);
97
} else {
98
v.push(account_id);
99
}
100
}
101
if (v.length > 0) {
102
const names = await this.client.conat_client.hub.system.getNames(v);
103
for (const account_id of v) {
104
// iterate over v to record accounts that don't exist too
105
x[account_id] = names[account_id];
106
nameCache.set(account_id, names[account_id]);
107
}
108
}
109
return x;
110
});
111
}
112
113