/* 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); }); } });