@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
153 lines (152 loc) • 5.96 kB
JavaScript
"use strict";
/*
* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
This is meant to run on a multi-user system, but where the hub
runs as a single user and all projects also run as that same
user, but with there own HOME directories. There is thus no
security or isolation at all between projects. There is still
a notion of multiple cocalc projects and cocalc users.
This is useful for:
- development of cocalc from inside of a CoCalc project
- non-collaborative use of cocalc on your own
laptop, e.g., when you're on an airplane.
*/
const process_1 = require("process");
const util_1 = require("./util");
const base_1 = require("./base");
const logger_1 = __importDefault(require("@cocalc/backend/logger"));
const query_1 = require("@cocalc/database/postgres/query");
const database_1 = require("@cocalc/database");
const quota_1 = require("@cocalc/util/upgrades/quota");
const winston = (0, logger_1.default)("project-control:single-user");
// Usually should fully start in about 5 seconds, but we give it 20s.
const MAX_START_TIME_MS = 20000;
const MAX_STOP_TIME_MS = 10000;
class Project extends base_1.BaseProject {
constructor(project_id) {
super(project_id);
this.HOME = (0, util_1.homePath)(this.project_id);
}
async state() {
if (this.stateChanging != null) {
return this.stateChanging;
}
const state = await (0, util_1.getState)(this.HOME);
this.saveStateToDatabase(state);
return state;
}
async status() {
const status = await (0, util_1.getStatus)(this.HOME);
// TODO: don't include secret token in log message.
winston.debug(`got status of ${this.project_id} = ${JSON.stringify(status)}`);
await this.saveStatusToDatabase(status);
return status;
}
async start() {
winston.debug("start", this.project_id);
if (this.stateChanging != null)
return;
// Home directory
const HOME = this.HOME;
if (await (0, util_1.isProjectRunning)(HOME)) {
winston.debug("start -- already running");
await this.saveStateToDatabase({ state: "running" });
return;
}
try {
this.stateChanging = { state: "starting" };
await this.saveStateToDatabase(this.stateChanging);
await this.siteLicenseHook();
await this.setRunQuota();
await (0, util_1.mkdir)(HOME, { recursive: true });
await (0, util_1.ensureConfFilesExists)(HOME);
// this.get('env') = extra env vars for project (from synctable):
const env = await (0, util_1.getEnvironment)(this.project_id);
winston.debug(`start ${this.project_id}: env = ${JSON.stringify(env)}`);
// Setup files
await (0, util_1.setupDataPath)(HOME);
// Fork and launch project server
await (0, util_1.launchProjectDaemon)(env);
await this.wait({
until: async () => {
if (!(await (0, util_1.isProjectRunning)(this.HOME))) {
return false;
}
const status = await this.status();
return !!status.secret_token && !!status["hub-server.port"];
},
maxTime: MAX_START_TIME_MS,
});
}
finally {
this.stateChanging = undefined;
// ensure state valid in database
await this.state();
}
}
async stop() {
if (this.stateChanging != null)
return;
winston.debug("stop ", this.project_id);
if (!(await (0, util_1.isProjectRunning)(this.HOME))) {
await this.saveStateToDatabase({ state: "opened" });
return;
}
try {
this.stateChanging = { state: "stopping" };
await this.saveStateToDatabase(this.stateChanging);
try {
const pid = await (0, util_1.getProjectPID)(this.HOME);
(0, process_1.kill)(-pid);
}
catch (_err) {
// expected exception if no pid
}
await this.wait({
until: async () => !(await (0, util_1.isProjectRunning)(this.HOME)),
maxTime: MAX_STOP_TIME_MS,
});
}
finally {
this.stateChanging = undefined;
// ensure state valid.
await this.state();
}
}
async copyPath(opts) {
winston.debug("copyPath ", this.project_id, opts);
await (0, util_1.copyPath)(opts, this.project_id);
return "";
}
// despite not being used, this is useful for development and
// some day in the future the run_quota will be shown in the UI
async setRunQuota() {
const { settings, users, site_license } = await (0, query_1.query)({
db: (0, database_1.db)(),
select: ["site_license", "settings", "users"],
table: "projects",
where: { project_id: this.project_id },
one: true,
});
const run_quota = (0, quota_1.quota)(settings, users, site_license);
await (0, query_1.query)({
db: (0, database_1.db)(),
query: "UPDATE projects",
where: { project_id: this.project_id },
jsonb_set: { run_quota },
});
winston.debug("updated run_quota=", run_quota);
}
}
function get(project_id) {
return (0, base_1.getProject)(project_id) ?? new Project(project_id);
}
exports.default = get;
//# sourceMappingURL=single-user.js.map