UNPKG

@cocalc/project

Version:
151 lines 5.11 kB
"use strict"; /* * 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