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-public-path-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 from "@cocalc/database/pool";
7
import getContents, { getSizeLimit } from "./get-contents";
8
import getProjectInfo from "./get-project";
9
import { join } from "path";
10
import basePath from "lib/base-path";
11
import isCollaborator from "@cocalc/server/projects/is-collaborator";
12
import getAccountId from "lib/account/get-account";
13
import { isStarred as getIsStarred } from "@cocalc/server/public-paths/star";
14
import getProxiedPublicPathInfo from "lib/share/proxy/get-proxied-public-path-info";
15
16
export default async function getPublicPathInfo({
17
id,
18
relativePath,
19
public_path,
20
req,
21
}: {
22
id: string;
23
relativePath?: string;
24
public_path?: string[];
25
req; // use to get account_id if necessary
26
}) {
27
if (typeof id != "string" || id.length != 40) {
28
throw Error("invalid id");
29
}
30
31
// TODO: currently not using any caching because when editing and saving, we want info to update.
32
// However, we should implement this by using a query param to prevent using cache?
33
const pool = getPool();
34
35
// Get the database entry that describes the public path
36
const { rows } = await pool.query(
37
`SELECT project_id, path, description, compute_image, license, disabled, unlisted,
38
authenticated, url, jupyter_api, redirect, created, last_edited,
39
counter::INT,
40
(SELECT COUNT(*)::INT FROM public_path_stars WHERE public_path_id=id) AS stars,
41
CASE WHEN site_license_id <> '' THEN TRUE ELSE FALSE END AS has_site_license
42
FROM public_paths WHERE vhost IS NULL AND id=$1`,
43
[id],
44
);
45
if (rows.length == 0 || rows[0].project_id == null || rows[0].path == null) {
46
throw Error("not found");
47
}
48
49
if (relativePath == null) {
50
if (public_path?.[1] == "files") {
51
// only files/ implemented right now; we might add other things like edit/ later?
52
relativePath = public_path.slice(2).join("/");
53
} else {
54
relativePath = "";
55
}
56
}
57
58
if (relativePath.includes("..") || relativePath.startsWith("/")) {
59
// a security check
60
throw Error("invalid relativePath");
61
}
62
63
const { disabled, authenticated } = rows[0];
64
const account_id = await getAccountId(req);
65
66
if (disabled) {
67
// Share is disabled, so account_id must be a collaborator on the project.
68
if (
69
!account_id ||
70
!(await isCollaborator({
71
account_id,
72
project_id: rows[0].project_id,
73
}))
74
) {
75
throw Error("not found");
76
}
77
}
78
79
if (authenticated) {
80
// Only authenticated users are allowed to access
81
if (account_id == null) {
82
throw Error("not found");
83
}
84
}
85
86
// if user is signed in, whether or not they stared this.
87
const isStarred = account_id ? await getIsStarred(id, account_id) : null;
88
89
try {
90
let details;
91
if (rows[0].url) {
92
// only proxied public paths have url attribute
93
details = await getProxiedPublicPathInfo(rows[0].url, public_path ?? []);
94
if (details.contents != null) {
95
const limit = getSizeLimit(
96
public_path
97
? (public_path[public_path.length - 1] ?? "")
98
: rows[0].url,
99
);
100
if (details.contents.size > limit) {
101
// it would be nice to do this *BEFORE* pulling it from github, etc., but
102
// life is short.
103
details.contents.content =
104
"File too big to be displayed; download it instead.";
105
details.contents.size = details.contents.content.length;
106
}
107
}
108
} else {
109
const { title, avatar_image_full } = await getProjectInfo(
110
rows[0].project_id,
111
["title", "avatar_image_full"],
112
"medium",
113
);
114
details = {
115
contents: await getContents(
116
rows[0].project_id,
117
join(rows[0].path, relativePath),
118
),
119
projectTitle: title,
120
projectAvatarImage: avatar_image_full,
121
};
122
}
123
return {
124
id,
125
...rows[0],
126
relativePath,
127
basePath,
128
isStarred,
129
...details,
130
created: rows[0].created?.toISOString() ?? null,
131
last_edited: rows[0].last_edited?.toISOString() ?? null,
132
};
133
} catch (error) {
134
return {
135
id,
136
...rows[0],
137
relativePath,
138
isStarred,
139
created: rows[0].created?.toISOString() ?? null,
140
last_edited: rows[0].last_edited?.toISOString() ?? null,
141
error: error.toString(),
142
};
143
}
144
}
145
146