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/named-servers/list.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
This file defines all of the named servers we support.
8
9
To add another one, define a new entry in SPEC:
10
11
- The key is the name of the server.
12
- The value is a string this a function of (ip, port, basePath). The string
13
is a bash shell command that when run starts the server. It might optionally
14
use process.env so that the env can influence command line options.
15
*/
16
17
import { exec } from "node:child_process";
18
import { mkdir, writeFile } from "node:fs/promises";
19
import { join } from "node:path";
20
21
import { NamedServerName } from "@cocalc/util/types/servers";
22
23
type CommandFunction = (
24
ip: string,
25
port: number,
26
basePath: string,
27
) => Promise<string>;
28
29
// Disables JupyterLab RTC since it is still very buggy, unfortunately.
30
/*
31
Reported:
32
1. The steps I’ve taken:
33
* Start a JupyterLabs Notebook server from my project settings
34
* In the server, open & edit a Jupyter Notebook w/ Python 3 system-wide kernel
35
* (Optional) Shutdown project/close browser tab
36
* Walk away, return 30+ minutes later
37
* (Optional) Restart project/server
38
* Edit already open notebook, try to save/export/download
39
40
2. What happened:
41
Editing the notebook behaves as usual (code runs), I can access the file system, interact with a terminal, but any changes I make to this already-open notebook won’t save.
42
43
I also saw almost exactly this happen in the JupyterLab weekly meeting
44
with the latest beta in early November (that was even worse, since refreshing
45
maybe didn't even work).
46
*/
47
48
// If you want to enable it, set the environment variable in Project Settings {"COCALC_JUPYTERLAB_RTC": "true"}
49
const JUPYTERLAB_RTC = process.env.COCALC_JUPYTERLAB_RTC === "true";
50
51
// iopub params for jupyter notebook
52
const JUPYTERNB_DATA =
53
process.env.COCALC_JUPYTER_NOTEBOOK_iopub_data_rate_limit ?? 2000000;
54
const JUPYTERNB_MSGS =
55
process.env.COCALC_JUPYTER_NOTEBOOK_iopub_msg_rate_limit ?? 50;
56
57
// iopub params for jupyter lab
58
const JUPYTERLAB_DATA =
59
process.env.COCALC_JUPYTER_LAB_iopub_data_rate_limit ?? 2000000;
60
const JUPYTERLAB_MSGS =
61
process.env.COCALC_JUPYTER_LAB_iopub_msg_rate_limit ?? 50;
62
63
async function rserver(_ip: string, port: number, basePath: string) {
64
// tmp: this is used to write a small config file and then use it
65
const tmp = join(process.env.TMP ?? "/tmp", "rserver");
66
await mkdir(tmp, { recursive: true });
67
const home = process.env.HOME ?? "/home/user";
68
// ATTN: by trial and error I learned this must be in the home dir (not tmp) – otherwise silent crash
69
// Also, that dir name has a length limit (unknown), does not work for nested dev-in-project
70
const data = join(home, ".config", "rserver");
71
const data_db = join(data, "db");
72
// This creates the tmp dir, and the data dir, and the data/db dir
73
await mkdir(data_db, { recursive: true });
74
const db_conf = join(tmp, "db.conf");
75
await writeFile(db_conf, `provider=sqlite\ndirectory=${data_db}`);
76
77
// ATTN: it's tempting to add --www-address=${ip} \
78
// to tell it where to listen to, but for some reason that doesn't work. Hence $ip is ignored.
79
// The default is 0.0.0.0, which works (and it's fine, because we proxy it anyway).
80
81
// Check, if the user $USER exists in /etc/passwd using grep. If not, call the user "user".
82
// Just process.env.USER does not work in development, i.e. when the "random id" user does not exist.
83
const user = await new Promise<string>((resolve) => {
84
const name = process.env.USER ?? "user";
85
exec(`grep "^${name}:" /etc/passwd`, (err) => {
86
resolve(err ? "user" : name);
87
});
88
});
89
90
// watch out, this will be prefixed with #!/bin/sh and piped into stdout/stderr files
91
// part from that, rserver must be in the $PATH
92
// see note at project/configuration::get_rserver
93
return [
94
`rserver`,
95
`--server-daemonize=0`,
96
`--auth-none=1`,
97
`--auth-encrypt-password=0`,
98
`--server-user=${user}`,
99
`--database-config-file="${db_conf}"`,
100
`--server-data-dir="${data}"`,
101
`--server-working-dir="${process.env.HOME}"`,
102
`--www-port=${port}`,
103
`--www-root-path="${basePath}/"`, // www-root-path needs the trailing slash and it must be "server", not "port"
104
`--server-pid-file="${join(tmp, "rserver.pid")}"`,
105
].join(" ");
106
}
107
108
const SPEC: { [name in NamedServerName]: CommandFunction } = {
109
code: async (ip: string, port: number) =>
110
`code-server --bind-addr=${ip}:${port} --auth=none`,
111
jupyter: async (ip: string, port: number, basePath: string) =>
112
[
113
`jupyter notebook`,
114
`--port-retries=0`,
115
`--no-browser`,
116
`--NotebookApp.iopub_data_rate_limit=${JUPYTERNB_DATA}`,
117
`--NotebookApp.iopub_msg_rate_limit=${JUPYTERNB_MSGS}`,
118
// we run Jupyter NB without authentication, because everything is proxied through CoCalc anyway
119
`--NotebookApp.token='' --NotebookApp.password=''`,
120
`--NotebookApp.allow_remote_access=True`,
121
`--NotebookApp.mathjax_url=/cdn/mathjax/MathJax.js`,
122
`--NotebookApp.base_url=${basePath} --ip=${ip} --port=${port}`,
123
].join(" "),
124
jupyterlab: async (ip: string, port: number, basePath: string) =>
125
[
126
"jupyter lab",
127
`--port-retries=0`, // don't try another port, only the one we specified will work
128
`--no-browser`, // don't open a browser – the UI does this if appliable
129
`--NotebookApp.iopub_data_rate_limit=${JUPYTERLAB_DATA}`,
130
`--NotebookApp.iopub_msg_rate_limit=${JUPYTERLAB_MSGS}`,
131
// we run Jupyter Lab without authentication, because everything is proxied through CoCalc anyway
132
`--NotebookApp.token='' --NotebookApp.password=''`,
133
// additionally to the above, and since several Jupyter Lab across projects might interfere with each other, we disable XSRF protection
134
// see https://github.com/sagemathinc/cocalc/issues/6492
135
`--ServerApp.disable_check_xsrf=True`, // Ref: https://jupyter-server.readthedocs.io/en/latest/other/full-config.html
136
`--NotebookApp.allow_remote_access=True`,
137
`--NotebookApp.mathjax_url=/cdn/mathjax/MathJax.js`,
138
`--NotebookApp.base_url=${basePath}`,
139
`--ip=${ip}`,
140
`--port=${port}`,
141
`${JUPYTERLAB_RTC ? "--collaborative" : ""}`,
142
].join(" "),
143
pluto: async (ip: string, port: number) =>
144
`echo 'import Pluto; Pluto.run(launch_browser=false, require_secret_for_access=false, host="${ip}", port=${port})' | julia`,
145
rserver,
146
} as const;
147
148
export default function getSpec(name: NamedServerName): CommandFunction {
149
const spec = SPEC[name];
150
if (spec == null) {
151
throw Error(`unknown named server: "${name}"`);
152
}
153
return spec;
154
}
155
156