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