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/next/lib/share/get-account-info.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 getPool, { timeInSeconds } from "@cocalc/database/pool";
7
import { isUUID } from "./util";
8
import { PublicPath } from "./types";
9
import getAccountId from "lib/account/get-account";
10
11
interface AccountInfo {
12
account_id: string;
13
first_name: string;
14
last_name: string;
15
name: string;
16
publicPaths: PublicPath[];
17
}
18
19
export default async function getAccountInfo(
20
account_id: string,
21
req
22
): Promise<AccountInfo> {
23
return {
24
account_id,
25
...(await getName(account_id)),
26
publicPaths: await getPublicPaths(account_id, req),
27
};
28
}
29
30
export async function getName(account_id: string): Promise<{
31
first_name: string;
32
last_name: string;
33
name: string;
34
email_address: string;
35
is_anonymous: boolean;
36
}> {
37
if (!isUUID(account_id)) {
38
throw Error("invalid UUID");
39
}
40
const pool = getPool("long");
41
42
// Get the database entry
43
const { rows } = await pool.query(
44
"SELECT name, first_name, last_name, email_address, passports FROM accounts WHERE account_id=$1",
45
[account_id]
46
);
47
if (rows.length == 0) {
48
throw Error("no such user");
49
}
50
const is_anonymous = !rows[0].email_address && !rows[0].passports;
51
return {
52
first_name: rows[0].first_name,
53
last_name: rows[0].last_name,
54
name: rows[0].name,
55
email_address: rows[0].email_address,
56
is_anonymous,
57
};
58
}
59
60
async function getPublicPaths(
61
account_id: string,
62
req // used to get account_id of requester to see if we should include unlisted and disabled public_paths
63
): Promise<PublicPath[]> {
64
if (!isUUID(account_id)) {
65
// VERY important to check this because we substitute the account_id
66
// into the query string directly, and this is input directly from the user!
67
throw Error("invalid UUID");
68
}
69
const pool = getPool("medium");
70
71
// Returns public paths for which account_id is a collaborator on the project and they have
72
// actively used the project at some point.
73
// We sort from most recently edited.
74
const query = `SELECT public_paths.id as id, public_paths.path as path, public_paths.description as description,
75
public_paths.disabled as disabled, public_paths.unlisted as unlisted, public_paths.vhost as vhost, public_paths.authenticated as authenticated,
76
(SELECT COUNT(*)::INT FROM public_path_stars WHERE public_path_id=public_paths.id) AS stars,
77
public_paths.counter::INT AS counter,
78
${timeInSeconds(
79
"public_paths.last_edited",
80
"last_edited"
81
)} FROM public_paths, projects WHERE public_paths.project_id = projects.project_id AND projects.last_active ? '${account_id}' AND projects.users ? '${account_id}' ORDER BY stars DESC, public_paths.last_edited DESC`;
82
const { rows } = await pool.query(query);
83
// If there are any disabled or unlisted public_paths, we also get the id of the requestor so we can filter them out.
84
return await filterNonPublicAndNotAuthenticated(rows, account_id, req);
85
}
86
87
async function filterNonPublicAndNotAuthenticated(
88
rows: PublicPath[],
89
account_id: string,
90
req
91
): Promise<PublicPath[]> {
92
const v: any[] = [];
93
let client_id: string | undefined = undefined;
94
for (const row of rows) {
95
if (!row.disabled && !row.unlisted && !row.authenticated) {
96
v.push(row);
97
continue;
98
}
99
if (client_id == null) {
100
client_id = (await getAccountId(req)) ?? "";
101
}
102
if (client_id == account_id) {
103
v.push(row);
104
} else if (row.authenticated === true && client_id !== "") {
105
v.push(row);
106
}
107
}
108
return v;
109
}
110
111