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/proxy/get-public-path.ts
Views: 687
1
/*
2
Supported proxy schema should be mostly consistent with (and an extension of) nbviewer.org:
3
4
`url/example.com/thingy...` or `gist/user/id` or `github/owner/repo...`, etc.
5
6
Three options handled right now; anything else is an error. The path must uniquely determine
7
what is publicly shared (at some unit), and it's nice if it is useful (e.g., for url's and gists).
8
9
- github/cocalc/sagemathinc/... --> 'github/cocalc/sagemathinc'; i.e., the github repo maps 1:1 to cocalc public path.
10
- gist/darribas/4121857 --> 'gist/4121857/guardian_gaza.ipynb'; here guardian_gaza.ipynb is the first filename hosted
11
in the gist, and requires api call to github to get it. This uniquely determines the gist *and* tells us what type of file
12
it contains so we can render it.
13
- url/wstein.org/Tables/modjac/curves.txt --> 'url/wstein.org/Tables/modjac/curves.txt'; path equals the url, since this is
14
completely generic and there is nothing further we could do.
15
16
*/
17
18
import getProxyProjectId from "lib/share/proxy/project";
19
import getPool from "@cocalc/database/pool";
20
import * as sha1 from "sha1";
21
import { fileInGist } from "./api";
22
23
export function shouldUseProxy(owner: string): boolean {
24
return owner == "github" || owner == "gist";
25
}
26
27
const QUERY = `SELECT id, project_id, path, url, description, counter::INT, last_edited,
28
(SELECT COUNT(*)::INT FROM public_path_stars WHERE public_path_id=id) AS stars
29
FROM public_paths WHERE `;
30
31
export default async function getProxyPublicPath({
32
id,
33
project_id,
34
path,
35
url,
36
description,
37
}: {
38
id?: string;
39
url?: string;
40
project_id?: string;
41
path?: string;
42
description?: string;
43
}): Promise<{
44
id: string;
45
project_id: string;
46
path: string;
47
url: string;
48
counter?: number;
49
stars: number;
50
last_edited: Date;
51
description?: string;
52
}> {
53
const pool = getPool("short");
54
if (id != null) {
55
// id is given, so return public_path with that id, if there is one.
56
const { rows } = await pool.query(QUERY + " id = $1", [id]);
57
if (rows.length > 0) {
58
return rows[0];
59
}
60
}
61
if (url != null) {
62
// url is given, so return public_path with that url, if already known.
63
const { rows } = await pool.query(QUERY + " url = $1", [url]);
64
if (rows.length > 0) {
65
return rows[0];
66
}
67
}
68
if (project_id == null) {
69
// this is the unique project used for all url proxying functionality; if not given,
70
// we just look it up for convenience.
71
project_id = await getProxyProjectId();
72
}
73
if (id == null && path != null) {
74
// if id not given but path is, then we can compute the id from project_id and path, since it's derived from them.
75
id = sha1(project_id + path);
76
// try based on the id, which we now know:
77
const { rows } = await pool.query(QUERY + " id = $1", [id]);
78
if (rows.length > 0) {
79
return rows[0];
80
}
81
}
82
83
// We need to create this public_path and return that.
84
if (!url) {
85
// There is no possible way to create a public_path associated to
86
// proxying a URL without knowing the URL.
87
throw Error("url must be specified in order to create public_path");
88
}
89
// We can assume url is known.
90
if (path == null) {
91
path = await getPath(url);
92
}
93
94
if (id == null) {
95
id = sha1(project_id + path);
96
}
97
if (id == null) throw Error("bug"); // not possible.
98
99
// It could still be that the path with this id is in the database.
100
// Example is gist/4121857 and gist/darribas/4121857 resolve to same record,
101
// but initial search for 'gist/4121857' does not find anything.
102
// id is given, so return public_path with that id, if there is one.
103
const { rows } = await pool.query(QUERY + " id = $1", [id]);
104
if (rows.length > 0) {
105
return rows[0];
106
}
107
108
let publicPathUrl;
109
if (url.startsWith("github/")) {
110
// URL to the repository, not the exact path being requested.
111
publicPathUrl = url.split("/").slice(0, 3).join("/");
112
} else {
113
publicPathUrl = url;
114
}
115
116
const now = new Date();
117
await pool.query(
118
"INSERT INTO public_paths (id, url, project_id, path, description, last_edited, last_saved, created) VALUES($1, $2, $3, $4, $5, $6, $7, $8)",
119
[id, publicPathUrl, project_id, path, description, now, now, now]
120
);
121
return {
122
id,
123
url,
124
project_id,
125
path,
126
description,
127
last_edited: now,
128
counter: 0,
129
stars: 0,
130
};
131
}
132
133
async function getPath(url: string) {
134
if (url.startsWith("github/")) {
135
const v = url.split("/");
136
if (v.length < 3) {
137
throw Error(`invalid url - ${url} - must at least specify repo`);
138
}
139
return v.slice(0, 3).join("/");
140
}
141
if (url.startsWith("url/")) {
142
return url;
143
}
144
if (url.startsWith("gist/")) {
145
const v = url.split("/");
146
return await fileInGist(v.length >= 3 ? v[2] : v[1]);
147
}
148
throw Error(`unknown proxy url schema -- ${url}`);
149
}
150
151