Просмотр исходного кода

Merge pull request #4303 from overleaf/sk-tag-saas-migrations

Migrations: tag migrations with relevant environment
GitOrigin-RevId: ad6c3bea19d3c21a1fdae58e09c861a3173c792b
Shane Kilkelly 5 лет назад
Родитель
Сommit
4b9aa97ea1
56 измененных файлов с 2139 добавлено и 12 удалено
  1. 0 10
      services/web/bin/east
  2. 25 0
      services/web/migrations/20190720165251_create_migrations.js
  3. 22 0
      services/web/migrations/20190730093801_script_example.js
  4. 31 0
      services/web/migrations/20190912145001_create_contacts_indexes.js
  5. 36 0
      services/web/migrations/20190912145002_create_deletedProjects_indexes.js
  6. 37 0
      services/web/migrations/20190912145003_create_deletedSubscriptions_indexes.js
  7. 30 0
      services/web/migrations/20190912145004_create_docHistoryIndex_indexes.js
  8. 58 0
      services/web/migrations/20190912145005_create_docHistory_indexes.js
  9. 31 0
      services/web/migrations/20190912145006_create_docOps_indexes.js
  10. 37 0
      services/web/migrations/20190912145007_create_docSnapshots_indexes.js
  11. 30 0
      services/web/migrations/20190912145008_create_docs_indexes.js
  12. 41 0
      services/web/migrations/20190912145009_create_githubSyncEntityVersions_indexes.js
  13. 36 0
      services/web/migrations/20190912145010_create_githubSyncProjectStates_indexes.js
  14. 33 0
      services/web/migrations/20190912145011_create_githubSyncUserCredentials_indexes.js
  15. 31 0
      services/web/migrations/20190912145012_create_institutions_indexes.js
  16. 31 0
      services/web/migrations/20190912145013_create_messages_indexes.js
  17. 43 0
      services/web/migrations/20190912145014_create_notifications_indexes.js
  18. 38 0
      services/web/migrations/20190912145015_create_oauthAccessTokens_indexes.js
  19. 31 0
      services/web/migrations/20190912145016_create_oauthApplications_indexes.js
  20. 31 0
      services/web/migrations/20190912145017_create_oauthAuthorizationCodes_indexes.js
  21. 30 0
      services/web/migrations/20190912145018_create_projectHistoryFailures_indexes.js
  22. 36 0
      services/web/migrations/20190912145019_create_projectHistoryLabels_indexes.js
  23. 30 0
      services/web/migrations/20190912145020_create_projectHistoryMetaData_indexes.js
  24. 37 0
      services/web/migrations/20190912145021_create_projectHistorySyncState_indexes.js
  25. 30 0
      services/web/migrations/20190912145022_create_projectImportFailures_indexes.js
  26. 37 0
      services/web/migrations/20190912145023_create_projectInvites_indexes.js
  27. 140 0
      services/web/migrations/20190912145024_create_projects_indexes.js
  28. 31 0
      services/web/migrations/20190912145025_create_publishers_indexes.js
  29. 30 0
      services/web/migrations/20190912145026_create_rooms_indexes.js
  30. 30 0
      services/web/migrations/20190912145027_create_spellingPreferences_indexes.js
  31. 85 0
      services/web/migrations/20190912145028_create_subscriptions_indexes.js
  32. 38 0
      services/web/migrations/20190912145029_create_tags_indexes.js
  33. 43 0
      services/web/migrations/20190912145030_create_templates_indexes.js
  34. 32 0
      services/web/migrations/20190912145031_create_tokens_indexes.js
  35. 165 0
      services/web/migrations/20190912145032_create_users_indexes.js
  36. 30 0
      services/web/migrations/20190912145033_create_userstubs_indexes.js
  37. 44 0
      services/web/migrations/20191106102104_saml-log-indexes.js
  38. 32 0
      services/web/migrations/20191107191318_saml-indentifiers-index.js
  39. 24 0
      services/web/migrations/20200110183327_brandVarationIdIndex.js
  40. 36 0
      services/web/migrations/20200120163346_atlas_recommended_indexes.js
  41. 42 0
      services/web/migrations/20200210084301_remove-duplicate-deleted-things.js
  42. 70 0
      services/web/migrations/20200210121103_uniqueify-deletedthings-indexes.js
  43. 25 0
      services/web/migrations/20200302143624_users_affiliationUnchecked_index.js
  44. 12 0
      services/web/migrations/20200522145727_dropProjectImportFailures.js
  45. 12 0
      services/web/migrations/20200522145741_dropProjectImportBatchRecords.js
  46. 38 0
      services/web/migrations/20200608213302_saml-cache-indexes.js
  47. 44 0
      services/web/migrations/20200729120824_update_subscriptions_manager_ids_index.js
  48. 21 0
      services/web/migrations/20201106094956_active-projects-index-with-id.js
  49. 22 0
      services/web/migrations/20210310111225_create_deletedFiles_projectId_index.js
  50. 36 0
      services/web/migrations/20210407085118_token-expiry-with-ttl-index.js
  51. 24 0
      services/web/migrations/20210408123210_create_docs_project_id_deleted_deletedAt_index.js
  52. 69 0
      services/web/migrations/README.md
  53. 56 0
      services/web/migrations/lib/adapter.js
  54. 26 0
      services/web/migrations/lib/helpers.js
  55. 15 0
      services/web/migrations/lib/template.js
  56. 15 2
      services/web/test/acceptance/src/helpers/MongoHelper.js

+ 0 - 10
services/web/bin/east

@@ -1,10 +0,0 @@
-#!/bin/sh
-
-SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
-
-if [ -f /.dockerenv ]; then
-  # I'm running inside docker
-  npm run migrations -- "$@"
-else
-  ${SCRIPTPATH}/run web bin/east "$@"
-fi

+ 25 - 0
services/web/migrations/20190720165251_create_migrations.js

@@ -0,0 +1,25 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.addIndexesToCollection(db.migrations, [
+    {
+      key: { name: 1 },
+      unique: true,
+    },
+  ])
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.migrations, [{ name: 1 }])
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 22 - 0
services/web/migrations/20190730093801_script_example.js

@@ -0,0 +1,22 @@
+/* eslint-disable no-unused-vars */
+
+/*
+ * Example migration for a script:
+ *
+ * This migration demonstrates how to run a script. In this case, the example
+ * script will print "hello world" if there are no users in the users collection
+ * or "hello <name>", when User.findOne() finds something.
+ */
+
+const runScript = require('../scripts/example/script_for_migration.js')
+
+exports.tags = []
+
+exports.migrate = async client => {
+  const { db } = client
+  await runScript()
+}
+
+exports.rollback = async client => {
+  const { db } = client
+}

+ 31 - 0
services/web/migrations/20190912145001_create_contacts_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.contacts, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.contacts, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 36 - 0
services/web/migrations/20190912145002_create_deletedProjects_indexes.js

@@ -0,0 +1,36 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      'deleterData.deletedAt': 1,
+    },
+    name: 'deleterData.deletedAt_1',
+  },
+  {
+    key: {
+      'deleterData.deletedProjectId': 1,
+    },
+    name: 'deleterData.deletedProjectId_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.deletedProjects, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.deletedProjects, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 37 - 0
services/web/migrations/20190912145003_create_deletedSubscriptions_indexes.js

@@ -0,0 +1,37 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      'subscription._id': 1,
+    },
+    name: 'subscription._id_1',
+  },
+  {
+    key: {
+      'subscription.admin_id': 1,
+    },
+    name: 'subscription.admin_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.deletedSubscriptions, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.deletedSubscriptions, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145004_create_docHistoryIndex_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.docHistoryIndex, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.docHistoryIndex, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 58 - 0
services/web/migrations/20190912145005_create_docHistory_indexes.js

@@ -0,0 +1,58 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      doc_id: 1,
+      v: 1,
+    },
+    name: 'doc_id_1_v_1',
+  },
+  {
+    key: {
+      project_id: 1,
+      'meta.end_ts': 1,
+    },
+    name: 'project_id_1_meta.end_ts_1',
+  },
+  {
+    key: {
+      doc_id: 1,
+      project_id: 1,
+    },
+    name: 'doc_id_1_project_id_1',
+  },
+  {
+    key: {
+      expiresAt: 1,
+    },
+    name: 'expiresAt_1',
+    expireAfterSeconds: 0,
+  },
+  {
+    key: {
+      last_checked: 1,
+    },
+    name: 'last_checked_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.docHistory, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.docHistory, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145006_create_docOps_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      doc_id: 1,
+    },
+    name: 'doc_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.docOps, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.docOps, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 37 - 0
services/web/migrations/20190912145007_create_docSnapshots_indexes.js

@@ -0,0 +1,37 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+  {
+    key: {
+      ts: 1,
+    },
+    name: 'ts_1',
+    expireAfterSeconds: 2592000,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.docSnapshots, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.docSnapshots, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145008_create_docs_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.docs, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.docs, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 41 - 0
services/web/migrations/20190912145009_create_githubSyncEntityVersions_indexes.js

@@ -0,0 +1,41 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      pid: 1,
+      eid: 1,
+    },
+    name: 'pid_1_eid_1',
+  },
+  {
+    key: {
+      c: 1,
+    },
+    name: 'c_1',
+    expireAfterSeconds: 2592000,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.githubSyncEntityVersions, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(
+      db.githubSyncEntityVersions,
+      indexes
+    )
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 36 - 0
services/web/migrations/20190912145010_create_githubSyncProjectStates_indexes.js

@@ -0,0 +1,36 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+  {
+    key: {
+      owner_id: 1,
+    },
+    name: 'owner_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.githubSyncProjectStates, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.githubSyncProjectStates, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 33 - 0
services/web/migrations/20190912145011_create_githubSyncUserCredentials_indexes.js

@@ -0,0 +1,33 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.githubSyncUserCredentials, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(
+      db.githubSyncUserCredentials,
+      indexes
+    )
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145012_create_institutions_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      v1Id: 1,
+    },
+    name: 'v1Id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.institutions, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.institutions, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145013_create_messages_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      room_id: 1,
+      timestamp: -1,
+    },
+    name: 'room_id_1_timestamp_-1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.messages, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.messages, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 43 - 0
services/web/migrations/20190912145014_create_notifications_indexes.js

@@ -0,0 +1,43 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      expires: 1,
+    },
+    name: 'expires_1',
+    expireAfterSeconds: 10,
+  },
+  {
+    key: {
+      key: 1,
+    },
+    name: 'key_1',
+  },
+  {
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.notifications, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.notifications, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 38 - 0
services/web/migrations/20190912145015_create_oauthAccessTokens_indexes.js

@@ -0,0 +1,38 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      accessToken: 1,
+    },
+    name: 'accessToken_1',
+  },
+  {
+    unique: true,
+    key: {
+      refreshToken: 1,
+    },
+    name: 'refreshToken_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.oauthAccessTokens, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.oauthAccessTokens, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145016_create_oauthApplications_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      id: 1,
+    },
+    name: 'id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.oauthApplications, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.oauthApplications, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145017_create_oauthAuthorizationCodes_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      authorizationCode: 1,
+    },
+    name: 'authorizationCode_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.oauthAuthorizationCodes, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.oauthAuthorizationCodes, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145018_create_projectHistoryFailures_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectHistoryFailures, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectHistoryFailures, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 36 - 0
services/web/migrations/20190912145019_create_projectHistoryLabels_indexes.js

@@ -0,0 +1,36 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+  {
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectHistoryLabels, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectHistoryLabels, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145020_create_projectHistoryMetaData_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectHistoryMetaData, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectHistoryMetaData, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 37 - 0
services/web/migrations/20190912145021_create_projectHistorySyncState_indexes.js

@@ -0,0 +1,37 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+  {
+    key: {
+      expiresAt: 1,
+    },
+    name: 'expiresAt_1',
+    expireAfterSeconds: 0,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectHistorySyncState, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectHistorySyncState, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145022_create_projectImportFailures_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      v1_project_id: 1,
+    },
+    name: 'v1_project_id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectImportFailures, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectImportFailures, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 37 - 0
services/web/migrations/20190912145023_create_projectInvites_indexes.js

@@ -0,0 +1,37 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      expires: 1,
+    },
+    name: 'expires_1',
+    expireAfterSeconds: 10,
+  },
+  {
+    key: {
+      projectId: 1,
+    },
+    name: 'projectId_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projectInvites, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projectInvites, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 140 - 0
services/web/migrations/20190912145024_create_projects_indexes.js

@@ -0,0 +1,140 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      active: 1,
+      lastOpened: 1,
+    },
+    name: 'active_1_lastOpened_1',
+  },
+  {
+    unique: true,
+    key: {
+      'tokens.readOnly': 1,
+    },
+    name: 'tokens.readOnly_1',
+    partialFilterExpression: {
+      'tokens.readOnly': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      'overleaf.history.id': 1,
+    },
+    name: 'overleaf.history.id_1',
+    partialFilterExpression: {
+      'overleaf.history.id': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      'tokens.readAndWritePrefix': 1,
+    },
+    name: 'tokens.readAndWritePrefix_1',
+    partialFilterExpression: {
+      'tokens.readAndWritePrefix': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    key: {
+      publicAccesLevel: 1,
+    },
+    name: 'publicAccesLevel_1',
+  },
+  {
+    key: {
+      owner_ref: 1,
+    },
+    name: 'owner_ref_1',
+  },
+  {
+    key: {
+      tokenAccessReadAndWrite_refs: 1,
+    },
+    name: 'tokenAccessReadAndWrite_refs_1',
+  },
+  {
+    key: {
+      readOnly_refs: 1,
+    },
+    name: 'readOnly_refs_1',
+  },
+  {
+    key: {
+      tokenAccessReadOnly_refs: 1,
+    },
+    name: 'tokenAccessReadOnly_refs_1',
+  },
+  {
+    unique: true,
+    key: {
+      'overleaf.id': 1,
+    },
+    name: 'overleaf.id_1',
+    partialFilterExpression: {
+      'overleaf.id': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    key: {
+      collaberator_refs: 1,
+    },
+    name: 'collaberator_refs_1',
+  },
+  {
+    key: {
+      name: 1,
+    },
+    name: 'name_1',
+  },
+  {
+    unique: true,
+    key: {
+      'tokens.readAndWrite': 1,
+    },
+    name: 'tokens.readAndWrite_1',
+    partialFilterExpression: {
+      'tokens.readAndWrite': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    key: {
+      'collabratecUsers.user_id': 1,
+    },
+    name: 'collabratecUsers.user_id_1',
+    sparse: true,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.projects, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.projects, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 31 - 0
services/web/migrations/20190912145025_create_publishers_indexes.js

@@ -0,0 +1,31 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      slug: 1,
+    },
+    name: 'slug_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.publishers, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.publishers, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145026_create_rooms_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.rooms, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.rooms, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145027_create_spellingPreferences_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      token: 1,
+    },
+    name: 'token',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.spellingPreferences, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.spellingPreferences, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 85 - 0
services/web/migrations/20190912145028_create_subscriptions_indexes.js

@@ -0,0 +1,85 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      'teamInvites.token': 1,
+    },
+    name: 'teamInvites.token_1',
+    partialFilterExpression: {
+      'teamInvites.token': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      manager_ids: 1,
+    },
+    name: 'manager_ids_1',
+    partialFilterExpression: {
+      manager_ids: {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      admin_id: 1,
+    },
+    name: 'admin_id_1',
+  },
+  {
+    key: {
+      'freeTrial.downgraded': 1,
+      'freeTrial.expiresAt': 1,
+    },
+    name: 'free_trial',
+  },
+  {
+    key: {
+      member_ids: 1,
+    },
+    name: 'member_ids_1',
+  },
+  {
+    key: {
+      invited_emails: 1,
+    },
+    name: 'invited_emails_1',
+  },
+  {
+    unique: true,
+    key: {
+      'overleaf.id': 1,
+    },
+    name: 'overleaf.id_1',
+    partialFilterExpression: {
+      'overleaf.id': {
+        $exists: true,
+      },
+    },
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.subscriptions, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.subscriptions, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 38 - 0
services/web/migrations/20190912145029_create_tags_indexes.js

@@ -0,0 +1,38 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+  {
+    unique: true,
+    key: {
+      user_id: 1,
+      name: 1,
+    },
+    name: 'user_id_1_name_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.tags, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.tags, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 43 - 0
services/web/migrations/20190912145030_create_templates_indexes.js

@@ -0,0 +1,43 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      project_id: 1,
+    },
+    name: 'project_id_1',
+  },
+  {
+    key: {
+      user_id: 1,
+    },
+    name: 'user_id_1',
+  },
+  {
+    key: {
+      name: 1,
+    },
+    name: 'name_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.templates, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.templates, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 32 - 0
services/web/migrations/20190912145031_create_tokens_indexes.js

@@ -0,0 +1,32 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      token: 1,
+      use: 1,
+    },
+    name: 'token_1_use_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.tokens, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.tokens, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 165 - 0
services/web/migrations/20190912145032_create_users_indexes.js

@@ -0,0 +1,165 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    unique: true,
+    key: {
+      email: 1,
+    },
+    name: 'email_case_insensitive',
+    collation: {
+      locale: 'en',
+      caseLevel: false,
+      caseFirst: 'off',
+      strength: 2,
+      numericOrdering: false,
+      alternate: 'non-ignorable',
+      maxVariable: 'punct',
+      normalization: false,
+      backwards: false,
+      version: '57.1',
+    },
+  },
+  {
+    key: {
+      'dropbox.access_token.oauth_token_secret': 1,
+    },
+    name: 'has dropbox',
+  },
+  {
+    unique: true,
+    key: {
+      'overleaf.id': 1,
+    },
+    name: 'overleaf.id_1',
+    partialFilterExpression: {
+      'overleaf.id': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      'thirdPartyIdentifiers.externalUserId': 1,
+      'thirdPartyIdentifiers.providerId': 1,
+    },
+    name:
+      'thirdPartyIdentifiers.externalUserId_1_thirdPartyIdentifiers.providerId_1',
+    sparse: true,
+  },
+  {
+    key: {
+      'subscription.freeTrialDowngraded': 1,
+    },
+    name: 'subscription.freeTrialDowngraded_1',
+  },
+  {
+    key: {
+      signUpDate: 1,
+    },
+    name: 'signUpDate',
+  },
+  {
+    unique: true,
+    key: {
+      'emails.email': 1,
+    },
+    name: 'emails_email_1',
+    partialFilterExpression: {
+      'emails.email': {
+        $exists: true,
+      },
+    },
+  },
+  {
+    unique: true,
+    key: {
+      'emails.email': 1,
+    },
+    name: 'emails_email_case_insensitive',
+    partialFilterExpression: {
+      'emails.email': {
+        $exists: true,
+      },
+    },
+    collation: {
+      locale: 'en',
+      caseLevel: false,
+      caseFirst: 'off',
+      strength: 2,
+      numericOrdering: false,
+      alternate: 'non-ignorable',
+      maxVariable: 'punct',
+      normalization: false,
+      backwards: false,
+      version: '57.1',
+    },
+  },
+  {
+    unique: true,
+    key: {
+      'dropbox.access_token.uid': 1,
+    },
+    name: 'dropbox.access_token.uid_unique',
+    sparse: true,
+  },
+  {
+    key: {
+      password: 1,
+      email: 1,
+    },
+    name: 'password_and_email',
+  },
+  {
+    key: {
+      referal_id: 1,
+    },
+    name: 'referal_id',
+  },
+  {
+    key: {
+      'subscription.freeTrialExpiresAt': 1,
+    },
+    name: 'subscription.freeTrialExpiresAt_1',
+  },
+  {
+    key: {
+      auth_token: 1,
+    },
+    name: 'auth_token_1',
+  },
+  {
+    unique: true,
+    key: {
+      email: 1,
+    },
+    name: 'email_1',
+  },
+  {
+    key: {
+      'emails.reversedHostname': 1,
+    },
+    name: 'emails.reversedHostname_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.users, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.users, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 30 - 0
services/web/migrations/20190912145033_create_userstubs_indexes.js

@@ -0,0 +1,30 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      'overleaf.id': 1,
+    },
+    name: 'overleaf.id_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.userstubs, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.userstubs, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 44 - 0
services/web/migrations/20191106102104_saml-log-indexes.js

@@ -0,0 +1,44 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      providerId: 1,
+    },
+    name: 'providerId_1',
+  },
+  {
+    key: {
+      sessionId: 1,
+    },
+    name: 'sessionId_1',
+  },
+  {
+    // expire after 30 days
+    expireAfterSeconds: 60 * 60 * 24 * 30,
+    key: {
+      createdAt: 1,
+    },
+    name: 'createdAt_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.samllog, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.samllog, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 32 - 0
services/web/migrations/20191107191318_saml-indentifiers-index.js

@@ -0,0 +1,32 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      'samlIdentifiers.externalUserId': 1,
+      'samlIdentifiers.providerId': 1,
+    },
+    name: 'samlIdentifiers.externalUserId_1_samlIdentifiers.providerId_1',
+    sparse: true,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.user, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.user, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 24 - 0
services/web/migrations/20200110183327_brandVarationIdIndex.js

@@ -0,0 +1,24 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      brandVariationId: 1,
+    },
+    name: 'brandVariationId_1',
+  },
+]
+
+exports.migrate = async ({ db }) => {
+  await Helpers.addIndexesToCollection(db.projects, indexes)
+}
+
+exports.rollback = async ({ db }) => {
+  try {
+    await Helpers.dropIndexesFromCollection(db.projects, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 36 - 0
services/web/migrations/20200120163346_atlas_recommended_indexes.js

@@ -0,0 +1,36 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = {
+  docSnapshots: [{ key: { project_id: 1, ts: 1 }, name: 'project_id_1_ts_1' }],
+  deletedProjects: [
+    {
+      key: { 'deleterData.deletedProjectOwnerId': 1 },
+      name: 'deleterdata_deletedProjectOwnerId_1',
+    },
+  ],
+  docs: [{ key: { project_id: 1, inS3: 1 }, name: 'project_id_1_inS3_1' }],
+}
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Promise.all(
+    Object.keys(indexes).map(key =>
+      Helpers.addIndexesToCollection(db[key], indexes[key])
+    )
+  )
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  await Promise.all(
+    Object.keys(indexes).map(key =>
+      Helpers.dropIndexesFromCollection(db[key], indexes[key])
+    )
+  )
+}

+ 42 - 0
services/web/migrations/20200210084301_remove-duplicate-deleted-things.js

@@ -0,0 +1,42 @@
+/* eslint-disable no-unused-vars */
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+async function removeDuplicates(collection, field) {
+  const duplicates = await collection.aggregate(
+    [
+      {
+        $group: {
+          _id: { projectId: `$deleterData.${field}` },
+          dups: { $addToSet: '$_id' },
+          count: { $sum: 1 },
+        },
+      },
+      { $match: { count: { $gt: 1 } } },
+    ],
+    { allowDiskUse: true }
+  )
+  let duplicate
+  while ((duplicate = await duplicates.next())) {
+    // find duplicate items, ignore the most recent and delete the rest
+    const items = await collection
+      .find(
+        { _id: { $in: duplicate.dups } },
+        { projection: { _id: 1 }, sort: { 'deleterData.deletedAt': -1 } }
+      )
+      .toArray()
+    items.pop()
+    const ids = items.map(item => item._id)
+    await collection.deleteMany({ _id: { $in: ids } })
+  }
+}
+
+exports.migrate = async client => {
+  const { db } = client
+  await removeDuplicates(db.deletedProjects, 'deletedProjectId')
+  await removeDuplicates(db.deletedUsers, 'deletedUserId')
+}
+
+exports.rollback = async client => {
+  // can't really do anything here
+}

+ 70 - 0
services/web/migrations/20200210121103_uniqueify-deletedthings-indexes.js

@@ -0,0 +1,70 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+exports.migrate = async client => {
+  const { db } = client
+  // deletedUsers did not have an index before
+  await Helpers.dropIndexesFromCollection(db.deletedProjects, [
+    {
+      key: {
+        'deleterData.deletedProjectId': 1,
+      },
+      name: 'deleterData.deletedProjectId_1',
+    },
+  ])
+  // deletedUsers did not have an index before
+
+  await Helpers.addIndexesToCollection(db.deletedProjects, [
+    {
+      key: {
+        'deleterData.deletedProjectId': 1,
+      },
+      unique: true,
+      name: 'deleterData.deletedProjectId_1',
+    },
+  ])
+  await Helpers.addIndexesToCollection(db.deletedUsers, [
+    {
+      key: {
+        'deleterData.deletedUserId': 1,
+      },
+      unique: true,
+      name: 'deleterData.deleteUserId_1',
+    },
+  ])
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  await Helpers.dropIndexesFromCollection(db.deletedProjects, [
+    {
+      key: {
+        'deleterData.deletedProjectId': 1,
+      },
+      unique: true,
+      name: 'deleterData.deletedProjectId_1',
+    },
+  ])
+  await Helpers.dropIndexesFromCollection(db.deletedUsers, [
+    {
+      key: {
+        'deleterData.deletedUserId': 1,
+      },
+      unique: true,
+      name: 'deleterData.deleteUserId_1',
+    },
+  ])
+
+  await Helpers.addIndexesToCollection(db.deletedProjects, [
+    {
+      key: {
+        'deleterData.deletedProjectId': 1,
+      },
+      name: 'deleterData.deletedProjectId_1',
+    },
+  ])
+  // deletedUsers did not have an index before
+}

+ 25 - 0
services/web/migrations/20200302143624_users_affiliationUnchecked_index.js

@@ -0,0 +1,25 @@
+/* eslint-disable no-unused-vars */
+
+exports.tags = ['saas']
+
+const Helpers = require('./lib/helpers')
+
+const indexes = [
+  {
+    key: {
+      'emails.affiliationUnchecked': 1,
+    },
+    name: 'affiliationUnchecked_1',
+    sparse: true,
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.addIndexesToCollection(db.users, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  await Helpers.dropIndexesFromCollection(db.users, indexes)
+}

+ 12 - 0
services/web/migrations/20200522145727_dropProjectImportFailures.js

@@ -0,0 +1,12 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.dropCollection(db, 'projectImportFailures')
+}
+
+exports.rollback = async client => {
+  // can't really do anything here
+}

+ 12 - 0
services/web/migrations/20200522145741_dropProjectImportBatchRecords.js

@@ -0,0 +1,12 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.dropCollection(db, 'projectImportBatchRecords')
+}
+
+exports.rollback = async client => {
+  // can't really do anything here
+}

+ 38 - 0
services/web/migrations/20200608213302_saml-cache-indexes.js

@@ -0,0 +1,38 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const indexes = [
+  {
+    key: {
+      requestId: 1,
+    },
+    name: 'requestId_1',
+  },
+  {
+    // expire after 24 hours
+    expireAfterSeconds: 60 * 60 * 24,
+    key: {
+      createdAt: 1,
+    },
+    name: 'createdAt_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.addIndexesToCollection(db.samlCache, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.samlCache, indexes)
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 44 - 0
services/web/migrations/20200729120824_update_subscriptions_manager_ids_index.js

@@ -0,0 +1,44 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['saas']
+
+const oldIndex = {
+  unique: true,
+  key: {
+    manager_ids: 1,
+  },
+  name: 'manager_ids_1',
+  partialFilterExpression: {
+    manager_ids: {
+      $exists: true,
+    },
+  },
+}
+
+const newIndex = {
+  key: {
+    manager_ids: 1,
+  },
+  name: 'manager_ids_1',
+  sparse: true,
+}
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Helpers.dropIndexesFromCollection(db.subscriptions, [oldIndex])
+  await Helpers.addIndexesToCollection(db.subscriptions, [newIndex])
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  try {
+    await Helpers.dropIndexesFromCollection(db.subscriptions, [newIndex])
+    await Helpers.addIndexesToCollection(db.subscriptions, [oldIndex])
+  } catch (err) {
+    console.error('Something went wrong rolling back the migrations', err)
+  }
+}

+ 21 - 0
services/web/migrations/20201106094956_active-projects-index-with-id.js

@@ -0,0 +1,21 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const index = {
+  key: { _id: 1, lastOpened: 1, active: 1 },
+  name: '_id_1_lastOpened_1_active_1',
+  partialFilterExpression: {
+    active: true,
+  },
+}
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.addIndexesToCollection(db.projects, [index])
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  await Helpers.dropIndexesFromCollection(db.projects, [index])
+}

+ 22 - 0
services/web/migrations/20210310111225_create_deletedFiles_projectId_index.js

@@ -0,0 +1,22 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      projectId: 1,
+    },
+    name: 'projectId_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.addIndexesToCollection(db.deletedFiles, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  await Helpers.dropIndexesFromCollection(db.deletedFiles, indexes)
+}

+ 36 - 0
services/web/migrations/20210407085118_token-expiry-with-ttl-index.js

@@ -0,0 +1,36 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = {
+  tokens: [
+    {
+      // expire all tokens 90 days after they are created
+      expireAfterSeconds: 90 * 24 * 60 * 60,
+      key: {
+        createdAt: 1,
+      },
+      name: 'createdAt_1',
+    },
+  ],
+}
+
+exports.migrate = async client => {
+  const { db } = client
+
+  await Promise.all(
+    Object.keys(indexes).map(key =>
+      Helpers.addIndexesToCollection(db[key], indexes[key])
+    )
+  )
+}
+
+exports.rollback = async client => {
+  const { db } = client
+
+  await Promise.all(
+    Object.keys(indexes).map(key =>
+      Helpers.dropIndexesFromCollection(db[key], indexes[key])
+    )
+  )
+}

+ 24 - 0
services/web/migrations/20210408123210_create_docs_project_id_deleted_deletedAt_index.js

@@ -0,0 +1,24 @@
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+const indexes = [
+  {
+    key: {
+      project_id: 1,
+      deleted: 1,
+      deletedAt: -1,
+    },
+    name: 'project_id_deleted_deletedAt_1',
+  },
+]
+
+exports.migrate = async client => {
+  const { db } = client
+  await Helpers.addIndexesToCollection(db.docs, indexes)
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  await Helpers.dropIndexesFromCollection(db.docs, indexes)
+}

+ 69 - 0
services/web/migrations/README.md

@@ -0,0 +1,69 @@
+# Migrations
+
+Migrations for the app environment live in this folder, and use the [East](https://github.com/okv/east) migration
+framework.
+
+We have an npm script which wraps east: `npm run migrations -- ...`
+
+For example:
+
+``` sh
+npm run migrations -- list -t 'saas'
+```
+
+### Environments and Tags
+
+Overleaf is deployed in three different environments:
+
+- `server-ce`: community edition installations (the base system)
+- `server-pro`: server pro installations
+- `saas`: the production overleaf site
+
+All migrations are tagged with the environments they should run in.
+For example, a migration that should run in every environment would be tagged with `['server-ce', 'server-pro', 'saas']`.
+
+When invoking east, we specify the relevant tags with the `-t` or `--tags` flag.
+Our adapter will refuse to run if this flag is not set.
+
+
+### Creating new migrations
+
+To create a new migration, run:
+
+```
+npm run migrations -- create <migration name>
+```
+
+This command will create a new migration file in the migrations folder, based on a template. The template provides
+`migrate` and `rollback` methods, which are run by the `east` binary when running the migrations. `rollback` should
+undo the changes made in `migrate`.
+
+#### Running scripts as a migration
+
+To run a script in a migration file, look at `migrations/20190730093801_script_example.js`, which runs the script
+`scripts/example/script_for_migration.js`. This uses a method where the script can be run standalone via `node`, or
+through the migrations mechanism.
+
+### Running migrations
+
+To run all migrations in a server-ce environment:
+``` sh
+npm run migrations -- migrate -t 'server-ce'
+```
+
+To run all migrations in a saas environment:
+``` sh
+npm run migrations -- migrate -t 'saas'
+```
+
+The `-t` flag also works with other `east` commands like `rollback`, and `list`.
+
+For other options, or for information on how to roll migrations back, take a look at the
+[East](https://github.com/okv/east) documentation.
+
+### Tips
+
+Try to use Mongo directly via the `db` object instead of using Mongoose models. Migrations will need to run in the
+future, and model files can change. It's unwise to make the migrations depend on code which might change.
+
+**Note:** Running `east rollback` without any arguments rolls back *all* migrations, which you may well not want.

+ 56 - 0
services/web/migrations/lib/adapter.js

@@ -0,0 +1,56 @@
+const Path = require('path')
+const {
+  addCollection,
+  waitForDb,
+  db,
+} = require('../../app/src/infrastructure/mongodb')
+
+class Adapter {
+  constructor(params) {
+    if (!(process.argv.includes('-t') || process.argv.includes('--tags'))) {
+      console.error("ERROR: must pass tags using '-t' or '--tags', exiting")
+      process.exit(1)
+    }
+    this.params = params || {}
+  }
+
+  getTemplatePath() {
+    return Path.resolve(__dirname, 'template.js')
+  }
+
+  async connect() {
+    await waitForDb()
+
+    await addCollection('projectImportFailures')
+    await addCollection('samllog')
+    await addCollection('user')
+
+    return { db }
+  }
+
+  disconnect() {
+    return Promise.resolve()
+  }
+
+  async getExecutedMigrationNames() {
+    const migrations = await db.migrations
+      .find({}, { sort: { migratedAt: 1 }, projection: { name: 1 } })
+      .toArray()
+    return migrations.map(migration => migration.name)
+  }
+
+  async markExecuted(name) {
+    return db.migrations.insertOne({
+      name: name,
+      migratedAt: new Date(),
+    })
+  }
+
+  async unmarkExecuted(name) {
+    return db.migrations.deleteOne({
+      name: name,
+    })
+  }
+}
+
+module.exports = Adapter

+ 26 - 0
services/web/migrations/lib/helpers.js

@@ -0,0 +1,26 @@
+const { getCollectionNames } = require('../../app/src/infrastructure/mongodb')
+
+async function addIndexesToCollection(collection, indexes) {
+  return Promise.all(
+    indexes.map(index => {
+      index.background = true
+      return collection.createIndex(index.key, index)
+    })
+  )
+}
+
+async function dropIndexesFromCollection(collection, indexes) {
+  return Promise.all(indexes.map(index => collection.dropIndex(index.name)))
+}
+
+async function dropCollection(db, collectionName) {
+  const allCollections = await getCollectionNames()
+  if (!allCollections.includes(collectionName)) return
+  return db[collectionName].drop()
+}
+
+module.exports = {
+  addIndexesToCollection,
+  dropIndexesFromCollection,
+  dropCollection,
+}

+ 15 - 0
services/web/migrations/lib/template.js

@@ -0,0 +1,15 @@
+/* eslint-disable no-unused-vars */
+
+const Helpers = require('./lib/helpers')
+
+exports.tags = ['server-ce', 'server-pro', 'saas']
+
+exports.migrate = async client => {
+  const { db } = client
+  // await Helpers.addIndexesToCollection(db.wombats, [{ name: 1 }])
+}
+
+exports.rollback = async client => {
+  const { db } = client
+  // Helpers.dropIndexesFromCollection(db.wombats, [{ name: 1 }])
+}

+ 15 - 2
services/web/test/acceptance/src/helpers/MongoHelper.js

@@ -1,12 +1,25 @@
-const { exec } = require('child_process')
+const { execFile } = require('child_process')
 const { waitForDb, db } = require('../../../../app/src/infrastructure/mongodb')
+const Settings = require('@overleaf/settings')
+
+const DEFAULT_ENV = 'saas'
 
 module.exports = {
   initialize() {
     before(waitForDb)
 
     before(function (done) {
-      exec('bin/east migrate', (error, stdout, stderr) => {
+      const args = [
+        'run',
+        'migrations',
+        '--',
+        'migrate',
+        '-t',
+        Settings.env || DEFAULT_ENV,
+      ]
+      execFile('npm', args, (error, stdout, stderr) => {
+        console.log(stdout)
+        console.error(stderr)
         if (error) {
           throw error
         }