| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- /* eslint-disable
- camelcase,
- handle-callback-err,
- no-useless-escape,
- */
- // 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
- */
- let DocArchive
- const MongoManager = require('./MongoManager')
- const Errors = require('./Errors')
- const logger = require('logger-sharelatex')
- const _ = require('underscore')
- const async = require('async')
- const settings = require('settings-sharelatex')
- const request = require('request')
- const crypto = require('crypto')
- const RangeManager = require('./RangeManager')
- const thirtySeconds = 30 * 1000
- module.exports = DocArchive = {
- archiveAllDocs(project_id, callback) {
- if (callback == null) {
- callback = function (err, docs) {}
- }
- return MongoManager.getProjectsDocs(
- project_id,
- { include_deleted: true },
- { lines: true, ranges: true, rev: true, inS3: true },
- function (err, docs) {
- if (err != null) {
- return callback(err)
- } else if (docs == null) {
- return callback(
- new Errors.NotFoundError(`No docs for project ${project_id}`)
- )
- }
- docs = _.filter(docs, (doc) => doc.inS3 !== true)
- const jobs = _.map(docs, (doc) => (cb) =>
- DocArchive.archiveDoc(project_id, doc, cb)
- )
- return async.parallelLimit(jobs, 5, callback)
- }
- )
- },
- archiveDoc(project_id, doc, callback) {
- let options
- logger.log({ project_id, doc_id: doc._id }, 'sending doc to s3')
- try {
- options = DocArchive.buildS3Options(project_id + '/' + doc._id)
- } catch (e) {
- return callback(e)
- }
- return DocArchive._mongoDocToS3Doc(doc, function (error, json_doc) {
- if (error != null) {
- return callback(error)
- }
- options.body = json_doc
- options.headers = { 'Content-Type': 'application/json' }
- return request.put(options, function (err, res) {
- if (err != null || res.statusCode !== 200) {
- logger.err(
- {
- err,
- res,
- project_id,
- doc_id: doc._id,
- statusCode: res != null ? res.statusCode : undefined
- },
- 'something went wrong archiving doc in aws'
- )
- return callback(new Error('Error in S3 request'))
- }
- const md5lines = crypto
- .createHash('md5')
- .update(json_doc, 'utf8')
- .digest('hex')
- const md5response = res.headers.etag.toString().replace(/\"/g, '')
- if (md5lines !== md5response) {
- logger.err(
- {
- responseMD5: md5response,
- linesMD5: md5lines,
- project_id,
- doc_id: doc != null ? doc._id : undefined
- },
- 'err in response md5 from s3'
- )
- return callback(new Error('Error in S3 md5 response'))
- }
- return MongoManager.markDocAsArchived(doc._id, doc.rev, function (err) {
- if (err != null) {
- return callback(err)
- }
- return callback()
- })
- })
- })
- },
- unArchiveAllDocs(project_id, callback) {
- if (callback == null) {
- callback = function (err) {}
- }
- return MongoManager.getArchivedProjectDocs(project_id, function (
- err,
- docs
- ) {
- if (err != null) {
- logger.err({ err, project_id }, 'error unarchiving all docs')
- return callback(err)
- } else if (docs == null) {
- return callback(
- new Errors.NotFoundError(`No docs for project ${project_id}`)
- )
- }
- const jobs = _.map(
- docs,
- (doc) =>
- function (cb) {
- if (doc.inS3 == null) {
- return cb()
- } else {
- return DocArchive.unarchiveDoc(project_id, doc._id, cb)
- }
- }
- )
- return async.parallelLimit(jobs, 5, callback)
- })
- },
- unarchiveDoc(project_id, doc_id, callback) {
- let options
- logger.log({ project_id, doc_id }, 'getting doc from s3')
- try {
- options = DocArchive.buildS3Options(project_id + '/' + doc_id)
- } catch (e) {
- return callback(e)
- }
- options.json = true
- return request.get(options, function (err, res, doc) {
- if (err != null || res.statusCode !== 200) {
- logger.err(
- { err, res, project_id, doc_id },
- 'something went wrong unarchiving doc from aws'
- )
- return callback(new Errors.NotFoundError('Error in S3 request'))
- }
- return DocArchive._s3DocToMongoDoc(doc, function (error, mongo_doc) {
- if (error != null) {
- return callback(error)
- }
- return MongoManager.upsertIntoDocCollection(
- project_id,
- doc_id.toString(),
- mongo_doc,
- function (err) {
- if (err != null) {
- return callback(err)
- }
- logger.log({ project_id, doc_id }, 'deleting doc from s3')
- return DocArchive._deleteDocFromS3(project_id, doc_id, callback)
- }
- )
- })
- })
- },
- destroyAllDocs(project_id, callback) {
- if (callback == null) {
- callback = function (err) {}
- }
- return MongoManager.getProjectsDocs(
- project_id,
- { include_deleted: true },
- { _id: 1 },
- function (err, docs) {
- if (err != null) {
- logger.err({ err, project_id }, "error getting project's docs")
- return callback(err)
- } else if (docs == null) {
- return callback()
- }
- const jobs = _.map(docs, (doc) => (cb) =>
- DocArchive.destroyDoc(project_id, doc._id, cb)
- )
- return async.parallelLimit(jobs, 5, callback)
- }
- )
- },
- destroyDoc(project_id, doc_id, callback) {
- logger.log({ project_id, doc_id }, 'removing doc from mongo and s3')
- return MongoManager.findDoc(project_id, doc_id, { inS3: 1 }, function (
- error,
- doc
- ) {
- if (error != null) {
- return callback(error)
- }
- if (doc == null) {
- return callback(new Errors.NotFoundError('Doc not found in Mongo'))
- }
- if (doc.inS3 === true) {
- return DocArchive._deleteDocFromS3(project_id, doc_id, function (err) {
- if (err != null) {
- return err
- }
- return MongoManager.destroyDoc(doc_id, callback)
- })
- } else {
- return MongoManager.destroyDoc(doc_id, callback)
- }
- })
- },
- _deleteDocFromS3(project_id, doc_id, callback) {
- let options
- try {
- options = DocArchive.buildS3Options(project_id + '/' + doc_id)
- } catch (e) {
- return callback(e)
- }
- options.json = true
- return request.del(options, function (err, res, body) {
- if (err != null || res.statusCode !== 204) {
- logger.err(
- { err, res, project_id, doc_id },
- 'something went wrong deleting doc from aws'
- )
- return callback(new Error('Error in S3 request'))
- }
- return callback()
- })
- },
- _s3DocToMongoDoc(doc, callback) {
- if (callback == null) {
- callback = function (error, mongo_doc) {}
- }
- const mongo_doc = {}
- if (doc.schema_v === 1 && doc.lines != null) {
- mongo_doc.lines = doc.lines
- if (doc.ranges != null) {
- mongo_doc.ranges = RangeManager.jsonRangesToMongo(doc.ranges)
- }
- } else if (doc instanceof Array) {
- mongo_doc.lines = doc
- } else {
- return callback(new Error("I don't understand the doc format in s3"))
- }
- return callback(null, mongo_doc)
- },
- _mongoDocToS3Doc(doc, callback) {
- if (callback == null) {
- callback = function (error, s3_doc) {}
- }
- if (doc.lines == null) {
- return callback(new Error('doc has no lines'))
- }
- const json = JSON.stringify({
- lines: doc.lines,
- ranges: doc.ranges,
- schema_v: 1
- })
- if (json.indexOf('\u0000') !== -1) {
- const error = new Error('null bytes detected')
- logger.err({ err: error, doc, json }, error.message)
- return callback(error)
- }
- return callback(null, json)
- },
- buildS3Options(key) {
- if (settings.docstore.s3 == null) {
- throw new Error('S3 settings are not configured')
- }
- return {
- aws: {
- key: settings.docstore.s3.key,
- secret: settings.docstore.s3.secret,
- bucket: settings.docstore.s3.bucket
- },
- timeout: thirtySeconds,
- uri: `https://${settings.docstore.s3.bucket}.s3.amazonaws.com/${key}`
- }
- }
- }
|