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/project/browser-websocket/api.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
/*
7
* License
8
*/
9
10
// Websocket based request/response api.
11
//
12
// All functionality here is of the form:
13
//
14
// -- one request
15
// -- one response
16
17
import { getClient } from "@cocalc/project/client";
18
import { get_configuration } from "../configuration";
19
import { run_formatter, run_formatter_string } from "../formatters";
20
import { nbconvert as jupyter_nbconvert } from "../jupyter/convert";
21
import { lean, lean_channel } from "../lean/server";
22
import { jupyter_strip_notebook } from "@cocalc/jupyter/nbgrader/jupyter-parse";
23
import { jupyter_run_notebook } from "@cocalc/jupyter/nbgrader/jupyter-run";
24
import { synctable_channel } from "../sync/server";
25
import { syncdoc_call } from "../sync/sync-doc";
26
import { terminal } from "@cocalc/terminal";
27
import { x11_channel } from "../x11/server";
28
import { canonical_paths } from "./canonical-path";
29
import { delete_files } from "@cocalc/backend/files/delete-files";
30
import { eval_code } from "./eval-code";
31
import computeFilesystemCache from "./compute-filesystem-cache";
32
import { move_files } from "@cocalc/backend/files/move-files";
33
import { rename_file } from "@cocalc/backend/files/rename-file";
34
import { realpath } from "./realpath";
35
import { project_info_ws } from "../project-info";
36
import query from "./query";
37
import { browser_symmetric_channel } from "./symmetric_channel";
38
import type { Mesg } from "@cocalc/comm/websocket/types";
39
import handleSyncFsApiCall, {
40
handleSyncFsRequestCall,
41
handleComputeServerSyncRegister,
42
handleCopy,
43
handleSyncFsGetListing,
44
handleComputeServerDeleteFiles,
45
handleComputeServerMoveFiles,
46
handleComputeServerRenameFile,
47
handleComputeServerComputeRegister,
48
} from "@cocalc/sync-fs/lib/handle-api-call";
49
import { version } from "@cocalc/util/smc-version";
50
import { getLogger } from "@cocalc/project/logger";
51
import execCode from "./exec-code";
52
53
const log = getLogger("websocket-api");
54
55
let primus: any = undefined;
56
export function init_websocket_api(_primus: any): void {
57
primus = _primus;
58
59
primus.on("connection", function (spark) {
60
// Now handle the connection, which can be either from a web browser, or
61
// from a compute server.
62
log.debug(`new connection from ${spark.address.ip} -- ${spark.id}`);
63
64
spark.on("request", async (data, done) => {
65
log.debug("primus-api", "request", data, "REQUEST");
66
const t0 = Date.now();
67
try {
68
const resp = await handleApiCall(data, spark);
69
//log.debug("primus-api", "response", resp);
70
done(resp);
71
} catch (err) {
72
// put this in for debugging...
73
// It's normal to sometimes get errors, e.g., when a Jupyter kernel
74
// isn't yet available.
75
// console.trace(); log.debug("primus-api error stacktrack", err.stack, err);
76
done({ error: err.toString(), status: "error" });
77
}
78
log.debug(
79
"primus-api",
80
"request",
81
data,
82
`FINISHED: time=${Date.now() - t0}ms`,
83
);
84
});
85
});
86
87
primus.on("disconnection", function (spark) {
88
log.debug(
89
"primus-api",
90
`end connection from ${spark.address.ip} -- ${spark.id}`,
91
);
92
});
93
}
94
95
async function handleApiCall(data: Mesg, spark): Promise<any> {
96
const client = getClient();
97
switch (data.cmd) {
98
case "version":
99
return version;
100
case "listing":
101
return await listing(data.path, data.hidden, data.compute_server_id);
102
case "delete_files":
103
const { compute_server_id, paths } = data;
104
if (compute_server_id) {
105
return await handleComputeServerDeleteFiles({
106
paths,
107
compute_server_id,
108
});
109
} else {
110
return await delete_files(data.paths);
111
}
112
case "move_files":
113
if (data.compute_server_id) {
114
return await handleComputeServerMoveFiles(data);
115
} else {
116
return await move_files(data.paths, data.dest, (path) =>
117
client.set_deleted(path),
118
);
119
}
120
case "rename_file":
121
if (data.compute_server_id) {
122
return await handleComputeServerRenameFile(data);
123
} else {
124
return await rename_file(data.src, data.dest, (path) =>
125
client.set_deleted(path),
126
);
127
}
128
case "canonical_paths":
129
return await canonical_paths(data.paths);
130
case "configuration":
131
return await get_configuration(data.aspect, data.no_cache);
132
case "prettier": // deprecated
133
case "formatter":
134
return await run_formatter(client, data.path, data.options, log);
135
case "prettier_string": // deprecated
136
case "formatter_string":
137
return await run_formatter_string(data.path, data.str, data.options, log);
138
case "exec":
139
if (data.opts == null) {
140
throw Error("opts must not be null");
141
}
142
return await execCode(data.opts);
143
case "query":
144
return await query(client, data.opts);
145
case "eval_code":
146
return await eval_code(data.code);
147
case "terminal":
148
return await terminal(primus, data.path, data.options);
149
case "lean":
150
return await lean(client, primus, log, data.opts);
151
case "jupyter_strip_notebook":
152
return await jupyter_strip_notebook(data.ipynb_path);
153
case "jupyter_nbconvert":
154
return await jupyter_nbconvert(data.opts);
155
case "jupyter_run_notebook":
156
return await jupyter_run_notebook(log, data.opts);
157
case "lean_channel":
158
return await lean_channel(client, primus, log, data.path);
159
case "x11_channel":
160
return await x11_channel(client, primus, log, data.path, data.display);
161
case "synctable_channel":
162
return await synctable_channel(
163
client,
164
primus,
165
log,
166
data.query,
167
data.options,
168
);
169
case "syncdoc_call":
170
return await syncdoc_call(data.path, data.mesg);
171
case "symmetric_channel":
172
return await browser_symmetric_channel(client, primus, log, data.name);
173
case "realpath":
174
return realpath(data.path);
175
case "project_info":
176
return await project_info_ws(primus, log);
177
case "compute_filesystem_cache":
178
return await computeFilesystemCache(data.opts);
179
case "sync_fs":
180
return await handleSyncFsApiCall(data.opts);
181
case "compute_server_sync_register":
182
// register filesystem container
183
return await handleComputeServerSyncRegister(data.opts, spark);
184
case "compute_server_compute_register":
185
// register compute container
186
return await handleComputeServerComputeRegister(data.opts, spark);
187
case "compute_server_sync_request":
188
return await handleSyncFsRequestCall(data.opts);
189
case "copy_from_project_to_compute_server":
190
case "copy_from_compute_server_to_project":
191
return await handleCopy({ event: data.cmd, ...data.opts });
192
default:
193
throw Error(
194
`command "${
195
(data as any).cmd
196
}" not implemented -- restart your project (in Project --> Settings)`,
197
);
198
}
199
}
200
/* implementation of the api calls */
201
202
import { DirectoryListingEntry } from "@cocalc/util/types";
203
import getListing from "@cocalc/backend/get-listing";
204
async function listing(
205
path: string,
206
hidden: boolean,
207
compute_server_id?: number,
208
): Promise<DirectoryListingEntry[]> {
209
if (!compute_server_id) {
210
return await getListing(path, hidden);
211
} else {
212
return await handleSyncFsGetListing({ path, hidden, compute_server_id });
213
}
214
}
215
216