Merge pull request #66 from overleaf/spd-decaf-cleanup-3

Clean up and promisify health-check controller and KeyBuilder
This commit is contained in:
Simon Detheridge 2020-01-07 15:44:23 +00:00 committed by GitHub
commit cca5b66313
4 changed files with 145 additions and 144 deletions

View file

@ -89,82 +89,82 @@ Metrics.injectMetricsRoute(app)
app.head(
'/project/:project_id/file/:file_id',
keyBuilder.userFileKey,
keyBuilder.userFileKeyMiddleware,
fileController.getFileHead
)
app.get(
'/project/:project_id/file/:file_id',
keyBuilder.userFileKey,
keyBuilder.userFileKeyMiddleware,
fileController.getFile
)
app.post(
'/project/:project_id/file/:file_id',
keyBuilder.userFileKey,
keyBuilder.userFileKeyMiddleware,
fileController.insertFile
)
app.put(
'/project/:project_id/file/:file_id',
keyBuilder.userFileKey,
keyBuilder.userFileKeyMiddleware,
bodyParser.json(),
fileController.copyFile
)
app.del(
'/project/:project_id/file/:file_id',
keyBuilder.userFileKey,
keyBuilder.userFileKeyMiddleware,
fileController.deleteFile
)
app.head(
'/template/:template_id/v/:version/:format',
keyBuilder.templateFileKey,
keyBuilder.templateFileKeyMiddleware,
fileController.getFileHead
)
app.get(
'/template/:template_id/v/:version/:format',
keyBuilder.templateFileKey,
keyBuilder.templateFileKeyMiddleware,
fileController.getFile
)
app.get(
'/template/:template_id/v/:version/:format/:sub_type',
keyBuilder.templateFileKey,
keyBuilder.templateFileKeyMiddleware,
fileController.getFile
)
app.post(
'/template/:template_id/v/:version/:format',
keyBuilder.templateFileKey,
keyBuilder.templateFileKeyMiddleware,
fileController.insertFile
)
app.head(
'/project/:project_id/public/:public_file_id',
keyBuilder.publicFileKey,
keyBuilder.publicFileKeyMiddleware,
fileController.getFileHead
)
app.get(
'/project/:project_id/public/:public_file_id',
keyBuilder.publicFileKey,
keyBuilder.publicFileKeyMiddleware,
fileController.getFile
)
app.post(
'/project/:project_id/public/:public_file_id',
keyBuilder.publicFileKey,
keyBuilder.publicFileKeyMiddleware,
fileController.insertFile
)
app.put(
'/project/:project_id/public/:public_file_id',
keyBuilder.publicFileKey,
keyBuilder.publicFileKeyMiddleware,
bodyParser.json(),
fileController.copyFile
)
app.del(
'/project/:project_id/public/:public_file_id',
keyBuilder.publicFileKey,
keyBuilder.publicFileKeyMiddleware,
fileController.deleteFile
)
app.get(
'/project/:project_id/size',
keyBuilder.publicProjectKey,
keyBuilder.publicProjectKeyMiddleware,
fileController.directorySize
)

View file

@ -20,6 +20,7 @@ class BackwardCompatibleError extends OError {
class NotFoundError extends BackwardCompatibleError {}
class WriteError extends BackwardCompatibleError {}
class ReadError extends BackwardCompatibleError {}
class HealthCheckError extends BackwardCompatibleError {}
class ConversionsDisabledError extends BackwardCompatibleError {}
class ConversionError extends BackwardCompatibleError {}
@ -44,5 +45,6 @@ module.exports = {
ConversionsDisabledError,
WriteError,
ReadError,
ConversionError
ConversionError,
HealthCheckError
}

View file

@ -1,80 +1,72 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const fs = require('fs-extra')
const path = require('path')
const async = require('async')
const fileConverter = require('./FileConverter')
const keyBuilder = require('./KeyBuilder')
const fileController = require('./FileController')
const logger = require('logger-sharelatex')
const settings = require('settings-sharelatex')
const Settings = require('settings-sharelatex')
const streamBuffers = require('stream-buffers')
const _ = require('underscore')
const { promisify } = require('util')
const Stream = require('stream')
const checkCanStoreFiles = function(callback) {
callback = _.once(callback)
const req = { params: {}, query: {}, headers: {} }
req.params.project_id = settings.health_check.project_id
req.params.file_id = settings.health_check.file_id
const myWritableStreamBuffer = new streamBuffers.WritableStreamBuffer({
const pipeline = promisify(Stream.pipeline)
const fsCopy = promisify(fs.copy)
const fsUnlink = promisify(fs.unlink)
const { HealthCheckError } = require('./Errors')
const FileConverter = require('./FileConverter').promises
const FileHandler = require('./FileHandler').promises
async function checkCanGetFiles() {
if (!Settings.health_check) {
return
}
const projectId = Settings.health_check.project_id
const fileId = Settings.health_check.file_id
const key = `${projectId}/${fileId}`
const bucket = Settings.filestore.stores.user_files
const buffer = new streamBuffers.WritableStreamBuffer({
initialSize: 100
})
const res = {
send(code) {
if (code !== 200) {
return callback(new Error(`non-200 code from getFile: ${code}`))
}
}
const sourceStream = await FileHandler.getFile(bucket, key, {})
try {
await pipeline(sourceStream, buffer)
} catch (err) {
throw new HealthCheckError('failed to get health-check file').withCause(err)
}
if (!buffer.size()) {
throw new HealthCheckError('no bytes written to download stream')
}
myWritableStreamBuffer.send = res.send
return keyBuilder.userFileKey(req, res, function() {
fileController.getFile(req, myWritableStreamBuffer)
return myWritableStreamBuffer.on('close', function() {
if (myWritableStreamBuffer.size() > 0) {
return callback()
} else {
const err = 'no data in write stream buffer for health check'
logger.err({ err }, 'error performing health check')
return callback(err)
}
})
})
}
const checkFileConvert = function(callback) {
if (!settings.enableConversions) {
return callback()
async function checkFileConvert() {
if (!Settings.enableConversions) {
return
}
const imgPath = path.join(Settings.path.uploadFolder, '/tiny.pdf')
let resultPath
try {
await fsCopy('./tiny.pdf', imgPath)
resultPath = await FileConverter.thumbnail(imgPath)
} finally {
if (resultPath) {
await fsUnlink(resultPath)
}
await fsUnlink(imgPath)
}
const imgPath = path.join(settings.path.uploadFolder, '/tiny.pdf')
return async.waterfall(
[
cb => fs.copy('./tiny.pdf', imgPath, cb),
cb => fileConverter.thumbnail(imgPath, cb),
(resultPath, cb) => fs.unlink(resultPath, cb),
cb => fs.unlink(imgPath, cb)
],
callback
)
}
module.exports = {
check(req, res) {
logger.log({}, 'performing health check')
return async.parallel([checkFileConvert, checkCanStoreFiles], function(
err
) {
if (err != null) {
Promise.all([checkCanGetFiles(), checkFileConvert()])
.then(() => res.send(200))
.catch(err => {
logger.err({ err }, 'Health check: error running')
return res.send(500)
} else {
return res.send(200)
}
})
res.send(500)
})
}
}

View file

@ -1,71 +1,78 @@
/* eslint-disable
camelcase,
no-return-assign,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const settings = require('settings-sharelatex')
module.exports = {
getConvertedFolderKey(key) {
return (key = `${key}-converted-cache/`)
},
addCachingToKey(key, opts) {
key = this.getConvertedFolderKey(key)
if (opts.format != null && opts.style == null) {
key = `${key}format-${opts.format}`
}
if (opts.style != null && opts.format == null) {
key = `${key}style-${opts.style}`
}
if (opts.style != null && opts.format != null) {
key = `${key}format-${opts.format}-style-${opts.style}`
}
return key
},
userFileKey(req, res, next) {
const { project_id, file_id } = req.params
req.key = `${project_id}/${file_id}`
req.bucket = settings.filestore.stores.user_files
return next()
},
publicFileKey(req, res, next) {
const { project_id, public_file_id } = req.params
if (settings.filestore.stores.public_files == null) {
return res.status(501).send('public files not available')
} else {
req.key = `${project_id}/${public_file_id}`
req.bucket = settings.filestore.stores.public_files
return next()
}
},
templateFileKey(req, res, next) {
const { template_id, format, version, sub_type } = req.params
req.key = `${template_id}/v/${version}/${format}`
if (sub_type != null) {
req.key = `${req.key}/${sub_type}`
}
req.bucket = settings.filestore.stores.template_files
req.version = version
const opts = req.query
return next()
},
publicProjectKey(req, res, next) {
const { project_id } = req.params
req.project_id = project_id
req.bucket = settings.filestore.stores.user_files
return next()
}
getConvertedFolderKey,
addCachingToKey,
userFileKeyMiddleware,
publicFileKeyMiddleware,
publicProjectKeyMiddleware,
templateFileKeyMiddleware
}
function getConvertedFolderKey(key) {
return `${key}-converted-cache/`
}
function addCachingToKey(key, opts) {
key = this.getConvertedFolderKey(key)
if (opts.format && !opts.style) {
key = `${key}format-${opts.format}`
}
if (opts.style && !opts.format) {
key = `${key}style-${opts.style}`
}
if (opts.style && opts.format) {
key = `${key}format-${opts.format}-style-${opts.style}`
}
return key
}
function userFileKeyMiddleware(req, res, next) {
const { project_id: projectId, file_id: fileId } = req.params
req.key = `${projectId}/${fileId}`
req.bucket = settings.filestore.stores.user_files
next()
}
function publicFileKeyMiddleware(req, res, next) {
if (settings.filestore.stores.public_files == null) {
return res.status(501).send('public files not available')
}
const { project_id: projectId, public_file_id: publicFileId } = req.params
req.key = `${projectId}/${publicFileId}`
req.bucket = settings.filestore.stores.public_files
next()
}
function templateFileKeyMiddleware(req, res, next) {
const {
template_id: templateId,
format,
version,
sub_type: subType
} = req.params
req.key = `${templateId}/v/${version}/${format}`
if (subType) {
req.key = `${req.key}/${subType}`
}
req.bucket = settings.filestore.stores.template_files
req.version = version
next()
}
function publicProjectKeyMiddleware(req, res, next) {
const { project_id: projectId } = req.params
req.project_id = projectId
req.bucket = settings.filestore.stores.user_files
next()
}