/* eslint-disable camelcase, 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 * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let TrackChangesClient const async = require('async') const zlib = require('zlib') const request = require('request') const Settings = require('settings-sharelatex') const rclient = require('redis-sharelatex').createClient(Settings.redis.history) // Only works locally for now const Keys = Settings.redis.history.key_schema const { db, ObjectId } = require('../../../../app/js/mongojs') const aws = require('aws-sdk') const s3 = new aws.S3({ accessKeyId: Settings.trackchanges.s3.key, secretAccessKey: Settings.trackchanges.s3.secret, endpoint: Settings.trackchanges.s3.endpoint, s3ForcePathStyle: Settings.trackchanges.s3.pathStyle }) const S3_BUCKET = Settings.trackchanges.stores.doc_history module.exports = TrackChangesClient = { flushAndGetCompressedUpdates(project_id, doc_id, callback) { if (callback == null) { callback = function (error, updates) {} } return TrackChangesClient.flushDoc(project_id, doc_id, (error) => { if (error != null) { return callback(error) } return TrackChangesClient.getCompressedUpdates(doc_id, callback) }) }, flushDoc(project_id, doc_id, callback) { if (callback == null) { callback = function (error) {} } return request.post( { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush` }, (error, response, body) => { response.statusCode.should.equal(204) return callback(error) } ) }, flushProject(project_id, callback) { if (callback == null) { callback = function (error) {} } return request.post( { url: `http://localhost:3015/project/${project_id}/flush` }, (error, response, body) => { response.statusCode.should.equal(204) return callback(error) } ) }, getCompressedUpdates(doc_id, callback) { if (callback == null) { callback = function (error, updates) {} } return db.docHistory .find({ doc_id: ObjectId(doc_id) }) .sort({ 'meta.end_ts': 1 }) .toArray(callback) }, getProjectMetaData(project_id, callback) { if (callback == null) { callback = function (error, updates) {} } return db.projectHistoryMetaData.find( { project_id: ObjectId(project_id) }, (error, projects) => callback(error, projects[0]) ) }, setPreserveHistoryForProject(project_id, callback) { if (callback == null) { callback = function (error) {} } return db.projectHistoryMetaData.update( { project_id: ObjectId(project_id) }, { $set: { preserveHistory: true } }, { upsert: true }, callback ) }, pushRawUpdates(project_id, doc_id, updates, callback) { if (callback == null) { callback = function (error) {} } return rclient.sadd( Keys.docsWithHistoryOps({ project_id }), doc_id, (error) => { if (error != null) { return callback(error) } return rclient.rpush( Keys.uncompressedHistoryOps({ doc_id }), ...Array.from(Array.from(updates).map((u) => JSON.stringify(u))), callback ) } ) }, getDiff(project_id, doc_id, from, to, callback) { if (callback == null) { callback = function (error, diff) {} } return request.get( { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}` }, (error, response, body) => { response.statusCode.should.equal(200) return callback(null, JSON.parse(body)) } ) }, getUpdates(project_id, options, callback) { if (callback == null) { callback = function (error, body) {} } return request.get( { url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}` }, (error, response, body) => { response.statusCode.should.equal(200) return callback(null, JSON.parse(body)) } ) }, restoreDoc(project_id, doc_id, version, user_id, callback) { if (callback == null) { callback = function (error) {} } return request.post( { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/version/${version}/restore`, headers: { 'X-User-Id': user_id } }, (error, response, body) => { response.statusCode.should.equal(204) return callback(null) } ) }, pushDocHistory(project_id, doc_id, callback) { if (callback == null) { callback = function (error) {} } return request.post( { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push` }, (error, response, body) => { response.statusCode.should.equal(204) return callback(error) } ) }, pullDocHistory(project_id, doc_id, callback) { if (callback == null) { callback = function (error) {} } return request.post( { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull` }, (error, response, body) => { response.statusCode.should.equal(204) return callback(error) } ) }, waitForS3(done, retries) { if (retries == null) { retries = 42 } if (!Settings.trackchanges.s3.endpoint) { return done() } return request.get(`${Settings.trackchanges.s3.endpoint}/`, (err, res) => { if (res && res.statusCode < 500) { return done() } if (retries === 0) { return done(err || new Error(`s3 returned ${res.statusCode}`)) } return setTimeout( () => TrackChangesClient.waitForS3(done, --retries), 1000 ) }) }, getS3Doc(project_id, doc_id, pack_id, callback) { if (callback == null) { callback = function (error, body) {} } const params = { Bucket: S3_BUCKET, Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` } return s3.getObject(params, (error, data) => { if (error != null) { return callback(error) } const body = data.Body if (body == null) { return callback(new Error('empty response from s3')) } return zlib.gunzip(body, (err, result) => { if (err != null) { return callback(err) } return callback(null, JSON.parse(result.toString())) }) }) }, removeS3Doc(project_id, doc_id, callback) { if (callback == null) { callback = function (error, res, body) {} } let params = { Bucket: S3_BUCKET, Prefix: `${project_id}/changes-${doc_id}` } return s3.listObjects(params, (error, data) => { if (error != null) { return callback(error) } params = { Bucket: S3_BUCKET, Delete: { Objects: data.Contents.map((s3object) => ({ Key: s3object.Key })) } } return s3.deleteObjects(params, callback) }) } }