MongoAWS.coffee 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. settings = require "settings-sharelatex"
  2. logger = require "logger-sharelatex"
  3. AWS = require 'aws-sdk'
  4. S3S = require 's3-streams'
  5. {db, ObjectId} = require "./mongojs"
  6. JSONStream = require "JSONStream"
  7. ReadlineStream = require "byline"
  8. zlib = require "zlib"
  9. Metrics = require "metrics-sharelatex"
  10. DAYS = 24 * 3600 * 1000 # one day in milliseconds
  11. createStream = (streamConstructor, project_id, doc_id, pack_id) ->
  12. AWS_CONFIG =
  13. accessKeyId: settings.trackchanges.s3.key
  14. secretAccessKey: settings.trackchanges.s3.secret
  15. return streamConstructor new AWS.S3(AWS_CONFIG), {
  16. "Bucket": settings.trackchanges.stores.doc_history,
  17. "Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id
  18. }
  19. module.exports = MongoAWS =
  20. archivePack: (project_id, doc_id, pack_id, _callback = (error) ->) ->
  21. callback = (args...) ->
  22. _callback(args...)
  23. _callback = () ->
  24. query = {
  25. _id: ObjectId(pack_id)
  26. doc_id: ObjectId(doc_id)
  27. }
  28. return callback new Error("invalid project id") if not project_id?
  29. return callback new Error("invalid doc id") if not doc_id?
  30. return callback new Error("invalid pack id") if not pack_id?
  31. logger.log {project_id, doc_id, pack_id}, "uploading data to s3"
  32. upload = createStream S3S.WriteStream, project_id, doc_id, pack_id
  33. db.docHistory.findOne query, (err, result) ->
  34. return callback(err) if err?
  35. return callback new Error("cannot find pack to send to s3") if not result?
  36. return callback new Error("refusing to send pack with TTL to s3") if result.expiresAt?
  37. uncompressedData = JSON.stringify(result)
  38. if uncompressedData.indexOf("\u0000") != -1
  39. error = new Error("null bytes found in upload")
  40. logger.error err: error, project_id: project_id, doc_id: doc_id, pack_id: pack_id, error.message
  41. return callback(error)
  42. zlib.gzip uncompressedData, (err, buf) ->
  43. logger.log {project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack"
  44. return callback(err) if err?
  45. upload.on 'error', (err) ->
  46. callback(err)
  47. upload.on 'finish', () ->
  48. Metrics.inc("archive-pack")
  49. logger.log {project_id, doc_id, pack_id}, "upload to s3 completed"
  50. callback(null)
  51. upload.write buf
  52. upload.end()
  53. readArchivedPack: (project_id, doc_id, pack_id, _callback = (error, result) ->) ->
  54. callback = (args...) ->
  55. _callback(args...)
  56. _callback = () ->
  57. return callback new Error("invalid project id") if not project_id?
  58. return callback new Error("invalid doc id") if not doc_id?
  59. return callback new Error("invalid pack id") if not pack_id?
  60. logger.log {project_id, doc_id, pack_id}, "downloading data from s3"
  61. download = createStream S3S.ReadStream, project_id, doc_id, pack_id
  62. inputStream = download
  63. .on 'open', (obj) ->
  64. return 1
  65. .on 'error', (err) ->
  66. callback(err)
  67. gunzip = zlib.createGunzip()
  68. gunzip.setEncoding('utf8')
  69. gunzip.on 'error', (err) ->
  70. logger.log {project_id, doc_id, pack_id, err}, "error uncompressing gzip stream"
  71. callback(err)
  72. outputStream = inputStream.pipe gunzip
  73. parts = []
  74. outputStream.on 'error', (err) ->
  75. return callback(err)
  76. outputStream.on 'end', () ->
  77. logger.log {project_id, doc_id, pack_id}, "download from s3 completed"
  78. try
  79. object = JSON.parse parts.join('')
  80. catch e
  81. return callback(e)
  82. object._id = ObjectId(object._id)
  83. object.doc_id = ObjectId(object.doc_id)
  84. object.project_id = ObjectId(object.project_id)
  85. for op in object.pack
  86. op._id = ObjectId(op._id) if op._id?
  87. callback null, object
  88. outputStream.on 'data', (data) ->
  89. parts.push data
  90. unArchivePack: (project_id, doc_id, pack_id, callback = (error) ->) ->
  91. MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, object) ->
  92. return callback(err) if err?
  93. Metrics.inc("unarchive-pack")
  94. # allow the object to expire, we can always retrieve it again
  95. object.expiresAt = new Date(Date.now() + 7 * DAYS)
  96. logger.log {project_id, doc_id, pack_id}, "inserting object from s3"
  97. db.docHistory.insert object, callback