FilestoreApp.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. const logger = require('@overleaf/logger')
  2. const Settings = require('@overleaf/settings')
  3. const fs = require('fs')
  4. const Path = require('path')
  5. const { promisify } = require('util')
  6. const disrequire = require('disrequire')
  7. const AWS = require('aws-sdk')
  8. logger.logger.level('info')
  9. const fsReaddir = promisify(fs.readdir)
  10. const sleep = promisify(setTimeout)
  11. class FilestoreApp {
  12. constructor() {
  13. this.running = false
  14. this.initing = false
  15. }
  16. async runServer() {
  17. if (this.running) {
  18. return
  19. }
  20. if (this.initing) {
  21. return await this.waitForInit()
  22. }
  23. this.initing = true
  24. this.app = await FilestoreApp.requireApp()
  25. await new Promise((resolve, reject) => {
  26. this.server = this.app.listen(
  27. Settings.internal.filestore.port,
  28. '127.0.0.1',
  29. err => {
  30. if (err) {
  31. return reject(err)
  32. }
  33. resolve()
  34. }
  35. )
  36. })
  37. if (Settings.filestore.backend === 's3') {
  38. try {
  39. await FilestoreApp.waitForS3()
  40. } catch (err) {
  41. await this.stop()
  42. throw err
  43. }
  44. }
  45. this.initing = false
  46. this.persistor = require('../../../app/js/PersistorManager')
  47. }
  48. async waitForInit() {
  49. while (this.initing) {
  50. await sleep(1000)
  51. }
  52. }
  53. async stop() {
  54. if (!this.server) return
  55. const closeServer = promisify(this.server.close).bind(this.server)
  56. try {
  57. await closeServer()
  58. } finally {
  59. delete this.server
  60. }
  61. }
  62. static async waitForS3() {
  63. let tries = 0
  64. if (!Settings.filestore.s3.endpoint) {
  65. return
  66. }
  67. const s3 = new AWS.S3({
  68. accessKeyId: Settings.filestore.s3.key,
  69. secretAccessKey: Settings.filestore.s3.secret,
  70. endpoint: Settings.filestore.s3.endpoint,
  71. s3ForcePathStyle: true,
  72. signatureVersion: 'v4',
  73. })
  74. while (true) {
  75. try {
  76. return await s3
  77. .putObject({
  78. Key: 'startup',
  79. Body: '42',
  80. Bucket: Settings.filestore.stores.user_files,
  81. })
  82. .promise()
  83. } catch (err) {
  84. // swallow errors, as we may experience them until fake-s3 is running
  85. if (tries === 9) {
  86. // throw just before hitting the 10s test timeout
  87. throw err
  88. }
  89. tries++
  90. await sleep(1000)
  91. }
  92. }
  93. }
  94. static async requireApp() {
  95. // unload the app, as we may be doing this on multiple runs with
  96. // different settings, which affect startup in some cases
  97. const files = await fsReaddir(Path.resolve(__dirname, '../../../app/js'))
  98. files.forEach(file => {
  99. disrequire(Path.resolve(__dirname, '../../../app/js', file))
  100. })
  101. disrequire(Path.resolve(__dirname, '../../../app'))
  102. disrequire('@overleaf/object-persistor')
  103. return require('../../../app')
  104. }
  105. }
  106. module.exports = FilestoreApp