overleaf/services/clsi/app/js/ResourceStateManager.js

131 lines
4.1 KiB
JavaScript
Raw Normal View History

/* 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:
* DS201: Simplify complex destructure assignments
* 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) {
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')
2020-12-17 07:01:14 -05:00
fs.unlink(stateFile, function (err) {
2020-12-17 07:05:17 -05:00
if (err && err.code !== 'ENOENT') {
return callback(err)
} else {
return callback()
}
})
} else {
logger.log({ state, basePath }, 'writing sync state')
2020-12-17 06:49:02 -05:00
const resourceList = resources.map((resource) => resource.path)
2020-12-17 07:01:14 -05:00
fs.writeFile(
stateFile,
2020-12-17 06:49:02 -05:00
[...resourceList, `stateHash:${state}`].join('\n'),
callback
)
}
},
checkProjectStateMatches(state, basePath, callback) {
const stateFile = Path.join(basePath, this.SYNC_STATE_FILE)
const size = this.SYNC_STATE_MAX_SIZE
2020-12-17 07:01:14 -05:00
SafeReader.readFile(stateFile, size, 'utf8', function (
err,
result,
bytesRead
) {
2020-12-17 07:05:17 -05:00
if (err) {
return callback(err)
}
if (bytesRead === size) {
logger.error(
{ file: stateFile, size, bytesRead },
'project state file truncated'
)
}
2020-12-17 06:59:15 -05:00
const array = result.toString().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 {
2020-12-17 06:49:02 -05:00
const resources = resourceList.map((path) => ({ path }))
2020-12-17 07:01:14 -05:00
callback(null, resources)
}
})
},
checkResourceFiles(resources, allFiles, basePath, callback) {
// check the paths are all relative to current directory
let file
2020-12-18 09:51:46 -05:00
const containsRelativePath = (resource) => {
const dirs = resource.path.split('/')
return dirs.indexOf('..') !== -1
}
if (resources.some(containsRelativePath)) {
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 = {}
2020-12-17 06:49:02 -05:00
for (file of allFiles) {
seenFile[file] = true
}
2020-12-17 06:49:02 -05:00
const missingFiles = resources
2020-08-10 12:01:11 -04:00
.filter((resource) => !seenFile[resource.path])
.map((resource) => resource.path)
2020-12-17 07:05:17 -05:00
if (missingFiles && missingFiles.length > 0) {
logger.err(
{ missingFiles, basePath, allFiles, resources },
'missing input files for project'
)
return callback(
new Errors.FilesOutOfSyncError(
'resource files missing in incremental update'
)
)
} else {
2020-12-17 07:01:14 -05:00
callback()
}
2020-12-17 06:49:02 -05:00
},
}