Эх сурвалжийг харах

Merge pull request #4315 from overleaf/jpa-import-ce-scripts

[scripts] import scripts from server-ce and add tests

GitOrigin-RevId: 07e7e15aa86030ccd3eca40ed40e5492622dc2d1
Jakob Ackermann 5 жил өмнө
parent
commit
8742a29d80

+ 1 - 0
services/web/Dockerfile

@@ -50,6 +50,7 @@ RUN chmod 0755 ./install_deps.sh && ./install_deps.sh
 FROM webpack as app
 
 RUN find /app/public -name '*.js.map' -delete
+RUN rm /app/modules/server-ce-scripts -rf
 USER node
 
 CMD ["node", "--expose-gc", "app.js"]

+ 1 - 1
services/web/config/settings.defaults.js

@@ -702,7 +702,7 @@ module.exports = {
     tprLinkedFileRefreshError: [],
   },
 
-  moduleImportSequence: ['launchpad', 'user-activate'],
+  moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
 
   csp: {
     percentage: parseFloat(process.env.CSP_PERCENTAGE) || 0,

+ 1 - 0
services/web/modules/server-ce-scripts/index.js

@@ -0,0 +1 @@
+module.exports = {}

+ 12 - 0
services/web/modules/server-ce-scripts/scripts/check-mongodb.js

@@ -0,0 +1,12 @@
+const { waitForDb } = require('../../../app/src/infrastructure/mongodb')
+
+waitForDb()
+  .then(() => {
+    console.error('Mongodb is up.')
+    process.exit(0)
+  })
+  .catch(err => {
+    console.error('Cannot connect to mongodb.')
+    console.error(err)
+    process.exit(1)
+  })

+ 18 - 0
services/web/modules/server-ce-scripts/scripts/check-redis.js

@@ -0,0 +1,18 @@
+const RedisWrapper = require('../../../app/src/infrastructure/RedisWrapper')
+const rclient = RedisWrapper.client('health_check')
+rclient.on('error', err => {
+  console.error('Cannot connect to redis.')
+  console.error(err)
+  process.exit(1)
+})
+
+rclient.healthCheck(err => {
+  if (err) {
+    console.error('Cannot connect to redis.')
+    console.error(err)
+    process.exit(1)
+  } else {
+    console.error('Redis is up.')
+    process.exit(0)
+  }
+})

+ 72 - 0
services/web/modules/server-ce-scripts/scripts/create-admin.js

@@ -0,0 +1,72 @@
+const Settings = require('@overleaf/settings')
+const { db, waitForDb } = require('../../../app/src/infrastructure/mongodb')
+const UserRegistrationHandler = require('../../../app/src/Features/User/UserRegistrationHandler')
+const OneTimeTokenHandler = require('../../../app/src/Features/Security/OneTimeTokenHandler')
+
+async function main() {
+  await waitForDb()
+
+  const email = (process.argv.slice(2).pop() || '').replace(/^--email=/, '')
+  if (!email) {
+    console.error(`Usage: node ${__filename} --email=joe@example.com`)
+    process.exit(1)
+  }
+
+  await new Promise((resolve, reject) => {
+    UserRegistrationHandler.registerNewUser(
+      {
+        email,
+        password: require('crypto').randomBytes(32).toString('hex'),
+      },
+      (error, user) => {
+        if (error && error.message !== 'EmailAlreadyRegistered') {
+          return reject(error)
+        }
+        db.users.updateOne(
+          { _id: user._id },
+          { $set: { isAdmin: true } },
+          error => {
+            if (error) {
+              return reject(error)
+            }
+            const ONE_WEEK = 7 * 24 * 60 * 60 // seconds
+            OneTimeTokenHandler.getNewToken(
+              'password',
+              {
+                expiresIn: ONE_WEEK,
+                email: user.email,
+                user_id: user._id.toString(),
+              },
+              (err, token) => {
+                if (err) {
+                  return reject(err)
+                }
+
+                console.log('')
+                console.log(`\
+Successfully created ${email} as an admin user.
+
+Please visit the following URL to set a password for ${email} and log in:
+
+${Settings.siteUrl}/user/password/set?passwordResetToken=${token}
+\
+`)
+                resolve()
+              }
+            )
+          }
+        )
+      }
+    )
+  })
+}
+
+main()
+  .then(() => {
+    console.error('Done.')
+    process.exit(0)
+  })
+  .catch(err => {
+    console.error(err)
+    process.exit(1)
+  })

+ 43 - 0
services/web/modules/server-ce-scripts/scripts/delete-user.js

@@ -0,0 +1,43 @@
+const { waitForDb } = require('../../../app/src/infrastructure/mongodb')
+const UserGetter = require('../../../app/src/Features/User/UserGetter')
+const UserDeleter = require('../../../app/src/Features/User/UserDeleter')
+
+async function main() {
+  await waitForDb()
+
+  const email = (process.argv.slice(2).pop() || '').replace(/^--email=/, '')
+  if (!email) {
+    console.error(`Usage: node ${__filename} --email=joe@example.com`)
+    process.exit(1)
+  }
+
+  await new Promise((resolve, reject) => {
+    UserGetter.getUser({ email }, function (error, user) {
+      if (error) {
+        return reject(error)
+      }
+      if (!user) {
+        console.log(
+          `user ${email} not in database, potentially already deleted`
+        )
+        return resolve()
+      }
+      UserDeleter.deleteUser(user._id, function (err) {
+        if (err) {
+          return reject(err)
+        }
+        resolve()
+      })
+    })
+  })
+}
+
+main()
+  .then(() => {
+    console.error('Done.')
+    process.exit(0)
+  })
+  .catch(err => {
+    console.error(err)
+    process.exit(1)
+  })

+ 5 - 0
services/web/modules/server-ce-scripts/test/acceptance/config/settings.test.js

@@ -0,0 +1,5 @@
+module.exports = {
+  test: {
+    counterInit: 160000,
+  },
+}

+ 1 - 0
services/web/modules/server-ce-scripts/test/acceptance/src/Init.js

@@ -0,0 +1 @@
+require(`../../../../../test/acceptance/src/helpers/InitApp`)

+ 110 - 0
services/web/modules/server-ce-scripts/test/acceptance/src/ServerCEScriptsTests.js

@@ -0,0 +1,110 @@
+const { execSync } = require('child_process')
+const { expect } = require('chai')
+const User = require('../../../../../test/acceptance/src/helpers/User').promises
+
+/**
+ * @param {string} cmd
+ * @return {string}
+ */
+function run(cmd) {
+  // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_child_process_execsync_command_options
+  // > stderr by default will be output to the parent process' stderr
+  // > unless stdio is specified.
+  // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_options_stdio
+  // Pipe stdin from /dev/null, store stdout, pipe stderr to /dev/null.
+  return execSync(cmd, {
+    stdio: ['ignore', 'pipe', 'ignore'],
+    cwd: 'modules/server-ce-scripts/scripts',
+  }).toString()
+}
+
+describe('ServerCEScripts', function () {
+  describe('check-mongodb', function () {
+    it('should exit with code 0 on success', function () {
+      run('node check-mongodb')
+    })
+
+    it('should exit with code 1 on error', function () {
+      try {
+        run(
+          'MONGO_SERVER_SELECTION_TIMEOUT=1' +
+            'MONGO_CONNECTION_STRING=mongodb://localhost:4242 ' +
+            'node check-mongodb'
+        )
+      } catch (e) {
+        expect(e.status).to.equal(1)
+        return
+      }
+      expect.fail('command should have failed')
+    })
+  })
+
+  describe('check-redis', function () {
+    it('should exit with code 0 on success', function () {
+      run('node check-redis')
+    })
+
+    it('should exit with code 1 on error', function () {
+      try {
+        run('REDIS_HOST=localhost node check-redis')
+      } catch (e) {
+        expect(e.status).to.equal(1)
+        return
+      }
+      expect.fail('command should have failed')
+    })
+  })
+
+  describe('create-admin', function () {
+    it('should exit with code 0 on success', function () {
+      const out = run('node create-admin --email=foo@bar.com')
+      expect(out).to.include('/user/password/set?passwordResetToken=')
+    })
+
+    it('should exit with code 1 on missing email', function () {
+      try {
+        run('node create-admin')
+      } catch (e) {
+        expect(e.status).to.equal(1)
+        return
+      }
+      expect.fail('command should have failed')
+    })
+  })
+
+  describe('delete-user', function () {
+    let user
+    beforeEach(async function () {
+      user = new User()
+      await user.login()
+    })
+
+    it('should log missing user', function () {
+      const email = 'does-not-exist@example.com'
+      const out = run('node delete-user --email=' + email)
+      expect(out).to.include('not in database, potentially already deleted')
+    })
+
+    it('should exit with code 0 on success', function () {
+      const email = user.email
+      run('node delete-user --email=' + email)
+    })
+
+    it('should have deleted the user on success', async function () {
+      const email = user.email
+      run('node delete-user --email=' + email)
+      const dbEntry = await user.get()
+      expect(dbEntry).to.not.exist
+    })
+
+    it('should exit with code 1 on missing email', function () {
+      try {
+        run('node delete-user')
+      } catch (e) {
+        expect(e.status).to.equal(1)
+        return
+      }
+      expect.fail('command should have failed')
+    })
+  })
+})