@cocalc/project
Version:
CoCalc: project daemon
151 lines • 5.11 kB
JavaScript
;
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
Object.defineProperty(exports, "__esModule", { value: true });
/*
Monitoring of public paths in a running project.
*/
const UPDATE_INTERVAL_S = 20;
//const UPDATE_INTERVAL_S: number = 3; // for testing
const fs_1 = require("fs");
const child_process_1 = require("child_process");
const awaiting_1 = require("awaiting");
let monitor = undefined;
function init() {
if (monitor !== undefined)
return;
monitor = new MonitorPublicPaths();
}
exports.default = init;
class MonitorPublicPaths {
constructor() {
this.client = require("./client").client;
if (this.client == null) {
throw Error("client must have been initialized first");
}
if (process.env.COCALC_EPHEMERAL_STATE === "yes") {
// nothing to do -- can't do anything with public paths if can't write to db.
return;
}
this.init();
}
dbg(f) {
return this.client.dbg(`MonitorPublicPaths.${f}`);
}
init() {
const dbg = this.dbg("_init");
dbg("initializing public_paths table");
const pattern = {
id: null,
project_id: this.client.client_id(),
path: null,
last_edited: null,
disabled: null,
};
this.table = this.client.sync_table({ public_paths: [pattern] });
this.update_loop(); // do not await!
}
async update_loop() {
const dbg = this.dbg("update_loop");
dbg(`run update every ${UPDATE_INTERVAL_S} seconds`);
while (this.table != null) {
try {
await this.update();
dbg("successful update");
}
catch (err) {
dbg("error doing update", err);
}
await (0, awaiting_1.delay)(UPDATE_INTERVAL_S * 1000);
}
dbg("this.table is null, so stopping update loop");
}
close() {
const d = this.dbg("close");
if (this.table == null) {
d("already closed");
return;
}
d("closing...");
this.table.close();
delete this.table;
}
async update() {
if (this.table == null || this.table.get_state() !== "connected") {
return;
}
// const d = this.dbg("update");
const work = [];
this.table.get().forEach((info, id) => {
if (!info.get("disabled")) {
let last_edited = info.get("last_edited", 0);
if (last_edited) {
last_edited = last_edited.valueOf();
}
work.push({
id,
path: info.get("path"),
last_edited,
});
}
});
for (const w of work) {
await this.update_path(w);
}
}
async update_path(opts) {
const { id, path, last_edited } = opts;
//const d = this.dbg(`update_path('${path}')`);
const d = function (..._args) { }; // too verbose...
// If any file in the given path was modified after last_edited,
// update last_edited to when the path was modified.
let changed = false; // don't know yet
let stats;
d("lstat");
stats = await (0, awaiting_1.callback)(fs_1.lstat, path);
if (stats.mtime.valueOf() > last_edited) {
d("clearly modified, since path changed");
changed = true;
}
if (!changed && stats.isDirectory()) {
// Is a directory, and directory mtime hasn't changed; still possible
// that there is a file in some subdir has changed, so have to do
// a full scan.
const days = (new Date().valueOf() - last_edited) / (1000 * 60 * 60 * 24);
// This input to find will give return code 1 if and only if it finds a FILE
// modified since last_edited (since we know the path exists).
const args = [
process.env.HOME + "/" + path,
"-type",
"f",
"-mtime",
`-${days}`,
"-exec",
"false",
"{}",
"+",
];
try {
await (0, awaiting_1.callback)(child_process_1.execFile, "find", args);
}
catch (err) {
if (err.code) {
d("some files changed");
changed = true;
}
else {
d("nothing changed");
}
}
}
if (changed) {
d("change -- update database table");
const last_edited = new Date();
this.table.set({ id, last_edited }, "shallow");
await this.table.save(); // and also cause change to get saved to database.
}
}
}
//# sourceMappingURL=public-paths.js.map