/* eslint-disable handle-callback-err, no-unused-vars, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * DS103: Rewrite code to no longer use __guard__ * DS201: Simplify complex destructure assignments * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let ResourceStateManager; const Path = require("path"); const fs = require("fs"); const logger = require("logger-sharelatex"); const settings = require("settings-sharelatex"); const Errors = require("./Errors"); const SafeReader = require("./SafeReader"); module.exports = (ResourceStateManager = { // The sync state is an identifier which must match for an // incremental update to be allowed. // // The initial value is passed in and stored on a full // compile, along with the list of resources.. // // Subsequent incremental compiles must come with the same value - if // not they will be rejected with a 409 Conflict response. The // previous list of resources is returned. // // An incremental compile can only update existing files with new // content. The sync state identifier must change if any docs or // files are moved, added, deleted or renamed. SYNC_STATE_FILE: ".project-sync-state", SYNC_STATE_MAX_SIZE: 128*1024, saveProjectState(state, resources, basePath, callback) { if (callback == null) { callback = function(error) {}; } const stateFile = Path.join(basePath, this.SYNC_STATE_FILE); if ((state == null)) { // remove the file if no state passed in logger.log({state, basePath}, "clearing sync state"); return fs.unlink(stateFile, function(err) { if ((err != null) && (err.code !== 'ENOENT')) { return callback(err); } else { return callback(); } }); } else { logger.log({state, basePath}, "writing sync state"); const resourceList = (Array.from(resources).map((resource) => resource.path)); return fs.writeFile(stateFile, [...Array.from(resourceList), `stateHash:${state}`].join("\n"), callback); } }, checkProjectStateMatches(state, basePath, callback) { if (callback == null) { callback = function(error, resources) {}; } const stateFile = Path.join(basePath, this.SYNC_STATE_FILE); const size = this.SYNC_STATE_MAX_SIZE; return SafeReader.readFile(stateFile, size, 'utf8', function(err, result, bytesRead) { if (err != null) { return callback(err); } if (bytesRead === size) { logger.error({file:stateFile, size, bytesRead}, "project state file truncated"); } const array = __guard__(result != null ? result.toString() : undefined, x => x.split("\n")) || []; const adjustedLength = Math.max(array.length, 1); const resourceList = array.slice(0, adjustedLength - 1); const oldState = array[adjustedLength - 1]; const newState = `stateHash:${state}`; logger.log({state, oldState, basePath, stateMatches: (newState === oldState)}, "checking sync state"); if (newState !== oldState) { return callback(new Errors.FilesOutOfSyncError("invalid state for incremental update")); } else { const resources = (Array.from(resourceList).map((path) => ({path}))); return callback(null, resources); } }); }, checkResourceFiles(resources, allFiles, basePath, callback) { // check the paths are all relative to current directory let file; if (callback == null) { callback = function(error) {}; } for (file of Array.from(resources || [])) { for (const dir of Array.from(__guard__(file != null ? file.path : undefined, x => x.split('/')))) { if (dir === '..') { return callback(new Error("relative path in resource file list")); } } } // check if any of the input files are not present in list of files const seenFile = {}; for (file of Array.from(allFiles)) { seenFile[file] = true; } const missingFiles = (Array.from(resources).filter((resource) => !seenFile[resource.path]).map((resource) => resource.path)); if ((missingFiles != null ? missingFiles.length : undefined) > 0) { logger.err({missingFiles, basePath, allFiles, resources}, "missing input files for project"); return callback(new Errors.FilesOutOfSyncError("resource files missing in incremental update")); } else { return callback(); } } }); function __guard__(value, transform) { return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; }