From 65976cb36391b75a0fc45ff064b865600596bbb0 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Tue, 28 Feb 2023 08:26:18 -0500 Subject: [PATCH] Merge pull request #11869 from overleaf/em-upgrade-mongoose-web Upgrade Mongoose and the Mongo driver in web GitOrigin-RevId: 2cad1aabe57eae424a9e4c68b2e0062f0e78ffaf --- package-lock.json | 205 ++++++++--- .../Authentication/AuthenticationManager.js | 2 +- .../Collaborators/CollaboratorsHandler.js | 2 +- .../Features/Project/ProjectHistoryHandler.js | 8 +- .../web/app/src/infrastructure/Mongoose.js | 16 +- services/web/app/src/models/DeletedFile.js | 2 +- services/web/app/src/models/DeletedProject.js | 2 +- .../web/app/src/models/DeletedSubscription.js | 2 +- services/web/app/src/models/DeletedUser.js | 2 +- services/web/app/src/models/Doc.js | 9 +- services/web/app/src/models/DocSnapshot.js | 2 +- services/web/app/src/models/Feedback.js | 27 +- services/web/app/src/models/File.js | 33 +- services/web/app/src/models/Folder.js | 9 +- services/web/app/src/models/Institution.js | 17 +- .../web/app/src/models/OauthAccessToken.js | 1 + .../web/app/src/models/OauthApplication.js | 1 + .../app/src/models/OauthAuthorizationCode.js | 1 + services/web/app/src/models/Project.js | 159 ++++----- .../app/src/models/ProjectAuditLogEntry.js | 1 + .../app/src/models/ProjectHistoryFailure.js | 2 +- services/web/app/src/models/ProjectInvite.js | 1 + services/web/app/src/models/Publisher.js | 11 +- services/web/app/src/models/SamlCache.js | 1 + services/web/app/src/models/SamlLog.js | 1 + services/web/app/src/models/SplitTest.js | 103 +++--- services/web/app/src/models/Subscription.js | 87 ++--- services/web/app/src/models/Survey.js | 1 + services/web/app/src/models/SystemMessage.js | 9 +- services/web/app/src/models/Tag.js | 13 +- services/web/app/src/models/TeamInvite.js | 15 +- services/web/app/src/models/User.js | 327 +++++++++--------- .../web/app/src/models/UserAuditLogEntry.js | 1 + services/web/config/settings.defaults.js | 4 +- services/web/package.json | 4 +- services/web/scripts/force_doc_flush.js | 8 +- services/web/scripts/set_tex_live_image.js | 4 +- services/web/test/unit/bootstrap.js | 11 +- .../AuthenticationManagerTests.js | 10 +- .../CollaboratorsHandlerTests.js | 8 +- .../src/Project/ProjectHistoryHandlerTests.js | 2 +- .../unit/src/User/UserAuditLogHandlerTests.js | 2 +- .../test/unit/src/User/UserCreatorTests.js | 8 +- 43 files changed, 660 insertions(+), 474 deletions(-) diff --git a/package-lock.json b/package-lock.json index c16d16ce08..cdf899c8cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34519,8 +34519,8 @@ "minimist": "^1.2.7", "mmmagic": "^0.5.3", "moment": "^2.29.4", - "mongodb": "~3.6.0", - "mongoose": "^5.13.11", + "mongodb": "^4.13.0", + "mongoose": "^6.9.1", "multer": "overleaf/multer#e1df247fbf8e7590520d20ae3601eaef9f3d2e9e", "nocache": "^2.1.0", "nock": "^13.1.3", @@ -34910,6 +34910,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "services/web/node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "services/web/node_modules/bson/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "services/web/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -35934,6 +35968,14 @@ "graceful-fs": "^4.1.6" } }, + "services/web/node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, "services/web/node_modules/karma-webpack": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz", @@ -36094,41 +36136,60 @@ } }, "services/web/node_modules/mongodb": { - "version": "3.6.12", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.12.tgz", - "integrity": "sha512-ErHpF4P4disEIQB8Nns2twIMVXcvmlwjpKqfVnyB/hhd/L5We48LfoBYjBjuUSiSqL6ffmcygPTgjvpy2LETRQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.13.0.tgz", + "integrity": "sha512-+taZ/bV8d1pYuHL4U+gSwkhmDrwkWbH1l4aah4YpmpscMwgFBkufIKxgP/G7m87/NUuQzc2Z75ZTI7ZOyqZLbw==", "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.0.3", - "safe-buffer": "^5.1.2" + "bson": "^4.7.0", + "mongodb-connection-string-url": "^2.5.4", + "socks": "^2.7.1" }, "engines": { - "node": ">=4" + "node": ">=12.9.0" }, "optionalDependencies": { - "saslprep": "^1.0.0" + "@aws-sdk/credential-providers": "^3.186.0", + "saslprep": "^1.0.3" + } + }, + "services/web/node_modules/mongoose": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.9.1.tgz", + "integrity": "sha512-hOz1ZWV0w6WEVLrj89Wpk7PXDYtDDF6k7/NX79lY5iKqeFtZsceBXW8xW59YFNcW5O3cH32hQ8IbDlhgyBsDMA==", + "dependencies": { + "bson": "^4.7.0", + "kareem": "2.5.1", + "mongodb": "4.13.0", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" }, - "peerDependenciesMeta": { - "aws4": { - "optional": true - }, - "bson-ext": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "mongodb-extjson": { - "optional": true - }, - "snappy": { - "optional": true - } + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "services/web/node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "services/web/node_modules/mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=12.0.0" } }, "services/web/node_modules/multer": { @@ -36814,6 +36875,11 @@ "stack-trace": "0.0.10" } }, + "services/web/node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, "services/web/node_modules/sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", @@ -43734,8 +43800,8 @@ "mocha-each": "^2.0.1", "mock-fs": "^5.1.2", "moment": "^2.29.4", - "mongodb": "~3.6.0", - "mongoose": "^5.13.11", + "mongodb": "^4.13.0", + "mongoose": "^6.9.1", "multer": "overleaf/multer#e1df247fbf8e7590520d20ae3601eaef9f3d2e9e", "nocache": "^2.1.0", "nock": "^13.1.1", @@ -43993,6 +44059,25 @@ "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true }, + "bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "requires": { + "buffer": "^5.6.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -44710,6 +44795,11 @@ "graceful-fs": "^4.1.6" } }, + "kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" + }, "karma-webpack": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz", @@ -44829,16 +44919,42 @@ } }, "mongodb": { - "version": "3.6.12", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.12.tgz", - "integrity": "sha512-ErHpF4P4disEIQB8Nns2twIMVXcvmlwjpKqfVnyB/hhd/L5We48LfoBYjBjuUSiSqL6ffmcygPTgjvpy2LETRQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.13.0.tgz", + "integrity": "sha512-+taZ/bV8d1pYuHL4U+gSwkhmDrwkWbH1l4aah4YpmpscMwgFBkufIKxgP/G7m87/NUuQzc2Z75ZTI7ZOyqZLbw==", "requires": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.0.3", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" + "@aws-sdk/credential-providers": "^3.186.0", + "bson": "^4.7.0", + "mongodb-connection-string-url": "^2.5.4", + "saslprep": "^1.0.3", + "socks": "^2.7.1" + } + }, + "mongoose": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.9.1.tgz", + "integrity": "sha512-hOz1ZWV0w6WEVLrj89Wpk7PXDYtDDF6k7/NX79lY5iKqeFtZsceBXW8xW59YFNcW5O3cH32hQ8IbDlhgyBsDMA==", + "requires": { + "bson": "^4.7.0", + "kareem": "2.5.1", + "mongodb": "4.13.0", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" + } + }, + "mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" + }, + "mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "requires": { + "debug": "4.x" } }, "multer": { @@ -45289,6 +45405,11 @@ "stack-trace": "0.0.10" } }, + "sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, "sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", diff --git a/services/web/app/src/Features/Authentication/AuthenticationManager.js b/services/web/app/src/Features/Authentication/AuthenticationManager.js index 5ffdc6f054..bf5dff47df 100644 --- a/services/web/app/src/Features/Authentication/AuthenticationManager.js +++ b/services/web/app/src/Features/Authentication/AuthenticationManager.js @@ -109,7 +109,7 @@ const AuthenticationManager = { if (err) { return callback(err) } - if (result.nModified !== 1) { + if (result.modifiedCount !== 1) { return callback(new ParallelLoginError()) } if (!match) { diff --git a/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js b/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js index 57f0459b96..886de8b9c1 100644 --- a/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js +++ b/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js @@ -234,7 +234,7 @@ async function setCollaboratorPrivilegeLevel( } } const mongoResponse = await Project.updateOne(query, update).exec() - if (mongoResponse.n === 0) { + if (mongoResponse.matchedCount === 0) { throw new Errors.NotFoundError('project or collaborator not found') } } diff --git a/services/web/app/src/Features/Project/ProjectHistoryHandler.js b/services/web/app/src/Features/Project/ProjectHistoryHandler.js index 26a9a9771b..939d87ba07 100644 --- a/services/web/app/src/Features/Project/ProjectHistoryHandler.js +++ b/services/web/app/src/Features/Project/ProjectHistoryHandler.js @@ -18,7 +18,7 @@ const ProjectHistoryHandler = { if (err) { return callback(err) } - if (result.n === 0) { + if (result.matchedCount === 0) { return callback(new Error('history exists')) } callback() @@ -57,7 +57,7 @@ const ProjectHistoryHandler = { return callback(err) } // return an error if overleaf.history.id wasn't present - if (result.n === 0) { + if (result.matchedCount === 0) { return callback(new Error('history not upgraded')) } callback() @@ -76,7 +76,7 @@ const ProjectHistoryHandler = { if (err) { return callback(err) } - if (result.n === 0) { + if (result.matchedCount === 0) { return callback(new Error('history not downgraded')) } callback() @@ -94,7 +94,7 @@ const ProjectHistoryHandler = { if (err) { return callback(err) } - if (result.n === 0) { + if (result.matchedCount === 0) { return callback(new Error('migration flag not set')) } callback() diff --git a/services/web/app/src/infrastructure/Mongoose.js b/services/web/app/src/infrastructure/Mongoose.js index 528d72fd99..18053ffd67 100644 --- a/services/web/app/src/infrastructure/Mongoose.js +++ b/services/web/app/src/infrastructure/Mongoose.js @@ -12,20 +12,14 @@ if ( ) } +mongoose.set('autoIndex', false) +mongoose.set('strictQuery', false) + const connectionPromise = mongoose.connect( Settings.mongo.url, - Object.assign( - { - // mongoose specific config - config: { autoIndex: false }, - // mongoose defaults to false, native driver defaults to true - useNewUrlParser: true, - // use the equivalent `findOneAndUpdate` methods of the native driver - useFindAndModify: false, - }, - Settings.mongo.options - ) + Settings.mongo.options ) + addConnectionDrainer('mongoose', async () => { await connectionPromise await mongoose.disconnect() diff --git a/services/web/app/src/models/DeletedFile.js b/services/web/app/src/models/DeletedFile.js index b7f56bee05..45d30d8099 100644 --- a/services/web/app/src/models/DeletedFile.js +++ b/services/web/app/src/models/DeletedFile.js @@ -14,7 +14,7 @@ const DeletedFileSchema = new Schema( }, deletedAt: { type: Date }, }, - { collection: 'deletedFiles' } + { collection: 'deletedFiles', minimize: false } ) exports.DeletedFile = mongoose.model('DeletedFile', DeletedFileSchema) diff --git a/services/web/app/src/models/DeletedProject.js b/services/web/app/src/models/DeletedProject.js index 1f240f7494..d69ff5c7d3 100644 --- a/services/web/app/src/models/DeletedProject.js +++ b/services/web/app/src/models/DeletedProject.js @@ -26,7 +26,7 @@ const DeletedProjectSchema = new Schema( deleterData: DeleterDataSchema, project: ProjectSchema, }, - { collection: 'deletedProjects' } + { collection: 'deletedProjects', minimize: false } ) exports.DeletedProject = mongoose.model('DeletedProject', DeletedProjectSchema) diff --git a/services/web/app/src/models/DeletedSubscription.js b/services/web/app/src/models/DeletedSubscription.js index 0aa387255b..10b649d9be 100644 --- a/services/web/app/src/models/DeletedSubscription.js +++ b/services/web/app/src/models/DeletedSubscription.js @@ -23,7 +23,7 @@ const DeletedSubscriptionSchema = new Schema( deleterData: DeleterDataSchema, subscription: SubscriptionSchema, }, - { collection: 'deletedSubscriptions' } + { collection: 'deletedSubscriptions', minimize: false } ) exports.DeletedSubscription = mongoose.model( diff --git a/services/web/app/src/models/DeletedUser.js b/services/web/app/src/models/DeletedUser.js index 579862819f..5d20f9bb40 100644 --- a/services/web/app/src/models/DeletedUser.js +++ b/services/web/app/src/models/DeletedUser.js @@ -23,7 +23,7 @@ const DeletedUserSchema = new Schema( deleterData: DeleterDataSchema, user: UserSchema, }, - { collection: 'deletedUsers' } + { collection: 'deletedUsers', minimize: false } ) exports.DeletedUser = mongoose.model('DeletedUser', DeletedUserSchema) diff --git a/services/web/app/src/models/Doc.js b/services/web/app/src/models/Doc.js index 0653d410a8..9f38ac0975 100644 --- a/services/web/app/src/models/Doc.js +++ b/services/web/app/src/models/Doc.js @@ -2,9 +2,12 @@ const mongoose = require('../infrastructure/Mongoose') const { Schema } = mongoose -const DocSchema = new Schema({ - name: { type: String, default: 'new doc' }, -}) +const DocSchema = new Schema( + { + name: { type: String, default: 'new doc' }, + }, + { minimize: false } +) exports.Doc = mongoose.model('Doc', DocSchema) diff --git a/services/web/app/src/models/DocSnapshot.js b/services/web/app/src/models/DocSnapshot.js index 0c25a367df..b851d4a94f 100644 --- a/services/web/app/src/models/DocSnapshot.js +++ b/services/web/app/src/models/DocSnapshot.js @@ -12,7 +12,7 @@ const DocSnapshotSchema = new Schema( ranges: Schema.Types.Mixed, ts: Date, }, - { collection: 'docSnapshots' } + { collection: 'docSnapshots', minimize: false } ) exports.DocSnapshot = mongoose.model('DocSnapshot', DocSnapshotSchema) diff --git a/services/web/app/src/models/Feedback.js b/services/web/app/src/models/Feedback.js index 0a1dff0657..8b6382114f 100644 --- a/services/web/app/src/models/Feedback.js +++ b/services/web/app/src/models/Feedback.js @@ -2,20 +2,23 @@ const mongoose = require('../infrastructure/Mongoose') const { Schema } = mongoose const { ObjectId } = Schema -const FeedbackSchema = new Schema({ - userId: { - type: ObjectId, - ref: 'User', - }, - source: String, - createdAt: { - type: Date, - default() { - return new Date() +const FeedbackSchema = new Schema( + { + userId: { + type: ObjectId, + ref: 'User', }, + source: String, + createdAt: { + type: Date, + default() { + return new Date() + }, + }, + data: {}, }, - data: {}, -}) + { minimize: false } +) exports.Feedback = mongoose.model('Feedback', FeedbackSchema) exports.FeedbackSchema = FeedbackSchema diff --git a/services/web/app/src/models/File.js b/services/web/app/src/models/File.js index 5bc5d523fe..5a7c772e3e 100644 --- a/services/web/app/src/models/File.js +++ b/services/web/app/src/models/File.js @@ -2,23 +2,26 @@ const mongoose = require('../infrastructure/Mongoose') const { Schema } = mongoose -const FileSchema = new Schema({ - name: { - type: String, - default: '', - }, - created: { - type: Date, - default() { - return new Date() +const FileSchema = new Schema( + { + name: { + type: String, + default: '', + }, + created: { + type: Date, + default() { + return new Date() + }, + }, + rev: { type: Number, default: 0 }, + linkedFileData: { type: Schema.Types.Mixed }, + hash: { + type: String, }, }, - rev: { type: Number, default: 0 }, - linkedFileData: { type: Schema.Types.Mixed }, - hash: { - type: String, - }, -}) + { minimize: false } +) exports.File = mongoose.model('File', FileSchema) exports.FileSchema = FileSchema diff --git a/services/web/app/src/models/Folder.js b/services/web/app/src/models/Folder.js index 989f3fde5e..52312e90d8 100644 --- a/services/web/app/src/models/Folder.js +++ b/services/web/app/src/models/Folder.js @@ -4,9 +4,12 @@ const { FileSchema } = require('./File') const { Schema } = mongoose -const FolderSchema = new Schema({ - name: { type: String, default: 'new folder' }, -}) +const FolderSchema = new Schema( + { + name: { type: String, default: 'new folder' }, + }, + { minimize: false } +) FolderSchema.add({ docs: [DocSchema], diff --git a/services/web/app/src/models/Institution.js b/services/web/app/src/models/Institution.js index bf3f8877bb..0128f294e9 100644 --- a/services/web/app/src/models/Institution.js +++ b/services/web/app/src/models/Institution.js @@ -5,14 +5,17 @@ const settings = require('@overleaf/settings') const logger = require('@overleaf/logger') const request = require('request') -const InstitutionSchema = new Schema({ - v1Id: { type: Number, required: true }, - managerIds: [{ type: ObjectId, ref: 'User' }], - metricsEmail: { - optedOutUserIds: [{ type: ObjectId, ref: 'User' }], - lastSent: { type: Date }, +const InstitutionSchema = new Schema( + { + v1Id: { type: Number, required: true }, + managerIds: [{ type: ObjectId, ref: 'User' }], + metricsEmail: { + optedOutUserIds: [{ type: ObjectId, ref: 'User' }], + lastSent: { type: Date }, + }, }, -}) + { minimize: false } +) // fetch institution's data from v1 API. Errors are ignored InstitutionSchema.method('fetchV1Data', function (callback) { diff --git a/services/web/app/src/models/OauthAccessToken.js b/services/web/app/src/models/OauthAccessToken.js index 6caa4d1c17..2ebe10cfcc 100644 --- a/services/web/app/src/models/OauthAccessToken.js +++ b/services/web/app/src/models/OauthAccessToken.js @@ -15,6 +15,7 @@ const OauthAccessTokenSchema = new Schema( }, { collection: 'oauthAccessTokens', + minimize: false, } ) diff --git a/services/web/app/src/models/OauthApplication.js b/services/web/app/src/models/OauthApplication.js index cd7e4dccdb..f02a9850db 100644 --- a/services/web/app/src/models/OauthApplication.js +++ b/services/web/app/src/models/OauthApplication.js @@ -13,6 +13,7 @@ const OauthApplicationSchema = new Schema( }, { collection: 'oauthApplications', + minimize: false, } ) diff --git a/services/web/app/src/models/OauthAuthorizationCode.js b/services/web/app/src/models/OauthAuthorizationCode.js index d67b920f2c..2cf0204c4f 100644 --- a/services/web/app/src/models/OauthAuthorizationCode.js +++ b/services/web/app/src/models/OauthAuthorizationCode.js @@ -14,6 +14,7 @@ const OauthAuthorizationCodeSchema = new Schema( }, { collection: 'oauthAuthorizationCodes', + minimize: false, } ) diff --git a/services/web/app/src/models/Project.js b/services/web/app/src/models/Project.js index 55bda02268..14e2f9d9f1 100644 --- a/services/web/app/src/models/Project.js +++ b/services/web/app/src/models/Project.js @@ -24,92 +24,95 @@ const DeletedFileSchema = new Schema({ deletedAt: { type: Date }, }) -const ProjectSchema = new Schema({ - name: { type: String, default: 'new project' }, - lastUpdated: { - type: Date, - default() { - return new Date() - }, - }, - lastUpdatedBy: { type: ObjectId, ref: 'User' }, - lastOpened: { type: Date }, - active: { type: Boolean, default: true }, - owner_ref: { type: ObjectId, ref: 'User' }, - collaberator_refs: [{ type: ObjectId, ref: 'User' }], - readOnly_refs: [{ type: ObjectId, ref: 'User' }], - rootDoc_id: { type: ObjectId }, - rootFolder: [FolderSchema], - version: { type: Number }, // incremented for every change in the project structure (folders and filenames) - publicAccesLevel: { type: String, default: 'private' }, - compiler: { type: String, default: 'pdflatex' }, - spellCheckLanguage: { type: String, default: 'en' }, - deletedByExternalDataSource: { type: Boolean, default: false }, - description: { type: String, default: '' }, - archived: { type: Schema.Types.Mixed }, - trashed: [{ type: ObjectId, ref: 'User' }], - deletedDocs: [DeletedDocSchema], - deletedFiles: [DeletedFileSchema], - imageName: { type: String }, - brandVariationId: { type: String }, - track_changes: { type: Object }, - tokens: { - readOnly: { - type: String, - index: { - unique: true, - partialFilterExpression: { 'tokens.readOnly': { $exists: true } }, +const ProjectSchema = new Schema( + { + name: { type: String, default: 'new project' }, + lastUpdated: { + type: Date, + default() { + return new Date() }, }, - readAndWrite: { - type: String, - index: { - unique: true, - partialFilterExpression: { 'tokens.readAndWrite': { $exists: true } }, + lastUpdatedBy: { type: ObjectId, ref: 'User' }, + lastOpened: { type: Date }, + active: { type: Boolean, default: true }, + owner_ref: { type: ObjectId, ref: 'User' }, + collaberator_refs: [{ type: ObjectId, ref: 'User' }], + readOnly_refs: [{ type: ObjectId, ref: 'User' }], + rootDoc_id: { type: ObjectId }, + rootFolder: [FolderSchema], + version: { type: Number }, // incremented for every change in the project structure (folders and filenames) + publicAccesLevel: { type: String, default: 'private' }, + compiler: { type: String, default: 'pdflatex' }, + spellCheckLanguage: { type: String, default: 'en' }, + deletedByExternalDataSource: { type: Boolean, default: false }, + description: { type: String, default: '' }, + archived: { type: Schema.Types.Mixed }, + trashed: [{ type: ObjectId, ref: 'User' }], + deletedDocs: [DeletedDocSchema], + deletedFiles: [DeletedFileSchema], + imageName: { type: String }, + brandVariationId: { type: String }, + track_changes: { type: Object }, + tokens: { + readOnly: { + type: String, + index: { + unique: true, + partialFilterExpression: { 'tokens.readOnly': { $exists: true } }, + }, }, - }, - readAndWritePrefix: { - type: String, - index: { - unique: true, - partialFilterExpression: { - 'tokens.readAndWritePrefix': { $exists: true }, + readAndWrite: { + type: String, + index: { + unique: true, + partialFilterExpression: { 'tokens.readAndWrite': { $exists: true } }, + }, + }, + readAndWritePrefix: { + type: String, + index: { + unique: true, + partialFilterExpression: { + 'tokens.readAndWritePrefix': { $exists: true }, + }, }, }, }, - }, - tokenAccessReadOnly_refs: [{ type: ObjectId, ref: 'User' }], - tokenAccessReadAndWrite_refs: [{ type: ObjectId, ref: 'User' }], - fromV1TemplateId: { type: Number }, - fromV1TemplateVersionId: { type: Number }, - overleaf: { - id: { type: Number }, - imported_at_ver_id: { type: Number }, - token: { type: String }, - read_token: { type: String }, - history: { - id: { type: Schema.Types.Mixed }, - display: { type: Boolean }, - upgradedAt: { type: Date }, - allowDowngrade: { type: Boolean }, - zipFileArchivedInProject: { type: Boolean }, - }, - }, - collabratecUsers: [ - { - user_id: { type: ObjectId, ref: 'User' }, - collabratec_document_id: { type: String }, - collabratec_privategroup_id: { type: String }, - added_at: { - type: Date, - default() { - return new Date() - }, + tokenAccessReadOnly_refs: [{ type: ObjectId, ref: 'User' }], + tokenAccessReadAndWrite_refs: [{ type: ObjectId, ref: 'User' }], + fromV1TemplateId: { type: Number }, + fromV1TemplateVersionId: { type: Number }, + overleaf: { + id: { type: Number }, + imported_at_ver_id: { type: Number }, + token: { type: String }, + read_token: { type: String }, + history: { + id: { type: Schema.Types.Mixed }, + display: { type: Boolean }, + upgradedAt: { type: Date }, + allowDowngrade: { type: Boolean }, + zipFileArchivedInProject: { type: Boolean }, }, }, - ], - deferredTpdsFlushCounter: { type: Number }, -}) + collabratecUsers: [ + { + user_id: { type: ObjectId, ref: 'User' }, + collabratec_document_id: { type: String }, + collabratec_privategroup_id: { type: String }, + added_at: { + type: Date, + default() { + return new Date() + }, + }, + }, + ], + deferredTpdsFlushCounter: { type: Number }, + }, + { minimize: false } +) ProjectSchema.statics.getProject = function (projectOrId, fields, callback) { if (projectOrId._id != null) { diff --git a/services/web/app/src/models/ProjectAuditLogEntry.js b/services/web/app/src/models/ProjectAuditLogEntry.js index bb0e851fc1..3b9b3370be 100644 --- a/services/web/app/src/models/ProjectAuditLogEntry.js +++ b/services/web/app/src/models/ProjectAuditLogEntry.js @@ -11,6 +11,7 @@ const ProjectAuditLogEntrySchema = new Schema( }, { collection: 'projectAuditLogEntries', + minimize: false, } ) diff --git a/services/web/app/src/models/ProjectHistoryFailure.js b/services/web/app/src/models/ProjectHistoryFailure.js index b28fb07c87..e8979fd5b8 100644 --- a/services/web/app/src/models/ProjectHistoryFailure.js +++ b/services/web/app/src/models/ProjectHistoryFailure.js @@ -15,7 +15,7 @@ const ProjectHistoryFailureSchema = new Schema( resyncAttempts: Number, requestCount: Number, }, - { collection: 'projectHistoryFailures' } + { collection: 'projectHistoryFailures', minimize: false } ) exports.ProjectHistoryFailure = mongoose.model( diff --git a/services/web/app/src/models/ProjectInvite.js b/services/web/app/src/models/ProjectInvite.js index 3dc792848c..33edd60cec 100644 --- a/services/web/app/src/models/ProjectInvite.js +++ b/services/web/app/src/models/ProjectInvite.js @@ -27,6 +27,7 @@ const ProjectInviteSchema = new Schema( }, { collection: 'projectInvites', + minimize: false, } ) diff --git a/services/web/app/src/models/Publisher.js b/services/web/app/src/models/Publisher.js index 8c8a0a6aaa..e9e10e49c0 100644 --- a/services/web/app/src/models/Publisher.js +++ b/services/web/app/src/models/Publisher.js @@ -5,10 +5,13 @@ const settings = require('@overleaf/settings') const logger = require('@overleaf/logger') const request = require('request') -const PublisherSchema = new Schema({ - slug: { type: String, required: true }, - managerIds: [{ type: ObjectId, ref: 'User' }], -}) +const PublisherSchema = new Schema( + { + slug: { type: String, required: true }, + managerIds: [{ type: ObjectId, ref: 'User' }], + }, + { minimize: false } +) // fetch publisher's (brand on v1) data from v1 API. Errors are ignored PublisherSchema.method('fetchV1Data', function (callback) { diff --git a/services/web/app/src/models/SamlCache.js b/services/web/app/src/models/SamlCache.js index 9c3bf642c7..185fe61422 100644 --- a/services/web/app/src/models/SamlCache.js +++ b/services/web/app/src/models/SamlCache.js @@ -8,6 +8,7 @@ const SamlCacheSchema = new Schema( }, { collection: 'samlCache', + minimize: false, } ) diff --git a/services/web/app/src/models/SamlLog.js b/services/web/app/src/models/SamlLog.js index dedf076a9d..52889770f0 100644 --- a/services/web/app/src/models/SamlLog.js +++ b/services/web/app/src/models/SamlLog.js @@ -14,6 +14,7 @@ const SamlLogSchema = new Schema( }, { collection: 'samlLogs', + minimize: false, } ) diff --git a/services/web/app/src/models/SplitTest.js b/services/web/app/src/models/SplitTest.js index 426aa8392b..25b3a86a39 100644 --- a/services/web/app/src/models/SplitTest.js +++ b/services/web/app/src/models/SplitTest.js @@ -96,59 +96,62 @@ const VersionSchema = new Schema( { _id: false } ) -const SplitTestSchema = new Schema({ - name: { - type: String, - minLength: MIN_NAME_LENGTH, - maxlength: MAX_NAME_LENGTH, - required: true, - unique: true, - validate: { - validator: function (input) { - return input !== null && NAME_REGEX.test(input) +const SplitTestSchema = new Schema( + { + name: { + type: String, + minLength: MIN_NAME_LENGTH, + maxlength: MAX_NAME_LENGTH, + required: true, + unique: true, + validate: { + validator: function (input) { + return input !== null && NAME_REGEX.test(input) + }, + message: `invalid, must match: ${NAME_REGEX}`, }, - message: `invalid, must match: ${NAME_REGEX}`, + }, + versions: [VersionSchema], + forbidReleasePhase: { + type: Boolean, + required: false, + }, + description: { + type: String, + required: false, + }, + expectedEndDate: { + type: Date, + required: false, + }, + ticketUrl: { + type: String, + required: false, + }, + reportsUrls: { + type: [String], + required: false, + default: [], + }, + winningVariant: { + type: String, + required: false, + }, + archived: { + type: Boolean, + required: false, + }, + archivedAt: { + type: Date, + required: false, + }, + badgeInfo: { + type: BadgeInfoSchema, + required: false, }, }, - versions: [VersionSchema], - forbidReleasePhase: { - type: Boolean, - required: false, - }, - description: { - type: String, - required: false, - }, - expectedEndDate: { - type: Date, - required: false, - }, - ticketUrl: { - type: String, - required: false, - }, - reportsUrls: { - type: [String], - required: false, - default: [], - }, - winningVariant: { - type: String, - required: false, - }, - archived: { - type: Boolean, - required: false, - }, - archivedAt: { - type: Date, - required: false, - }, - badgeInfo: { - type: BadgeInfoSchema, - required: false, - }, -}) + { minimize: false } +) module.exports = { SplitTest: mongoose.model('SplitTest', SplitTestSchema), diff --git a/services/web/app/src/models/Subscription.js b/services/web/app/src/models/Subscription.js index a525c5d938..0720d5be27 100644 --- a/services/web/app/src/models/Subscription.js +++ b/services/web/app/src/models/Subscription.js @@ -4,52 +4,55 @@ const { TeamInviteSchema } = require('./TeamInvite') const { Schema } = mongoose const { ObjectId } = Schema -const SubscriptionSchema = new Schema({ - admin_id: { - type: ObjectId, - ref: 'User', - index: { unique: true, dropDups: true }, - }, - manager_ids: { - type: [ObjectId], - ref: 'User', - required: true, - validate: function (managers) { - // require at least one manager - return !!managers.length +const SubscriptionSchema = new Schema( + { + admin_id: { + type: ObjectId, + ref: 'User', + index: { unique: true, dropDups: true }, }, - }, - member_ids: [{ type: ObjectId, ref: 'User' }], - invited_emails: [String], - teamInvites: [TeamInviteSchema], - recurlySubscription_id: String, - teamName: { type: String }, - teamNotice: { type: String }, - planCode: { type: String }, - groupPlan: { type: Boolean, default: false }, - membersLimit: { type: Number, default: 0 }, - customAccount: Boolean, - overleaf: { - id: { - type: Number, - index: { - unique: true, - partialFilterExpression: { 'overleaf.id': { $exists: true } }, + manager_ids: { + type: [ObjectId], + ref: 'User', + required: true, + validate: function (managers) { + // require at least one manager + return !!managers.length + }, + }, + member_ids: [{ type: ObjectId, ref: 'User' }], + invited_emails: [String], + teamInvites: [TeamInviteSchema], + recurlySubscription_id: String, + teamName: { type: String }, + teamNotice: { type: String }, + planCode: { type: String }, + groupPlan: { type: Boolean, default: false }, + membersLimit: { type: Number, default: 0 }, + customAccount: Boolean, + overleaf: { + id: { + type: Number, + index: { + unique: true, + partialFilterExpression: { 'overleaf.id': { $exists: true } }, + }, + }, + }, + recurlyStatus: { + state: { + type: String, + }, + trialStartedAt: { + type: Date, + }, + trialEndsAt: { + type: Date, }, }, }, - recurlyStatus: { - state: { - type: String, - }, - trialStartedAt: { - type: Date, - }, - trialEndsAt: { - type: Date, - }, - }, -}) + { minimize: false } +) // Subscriptions have no v1 data to fetch SubscriptionSchema.method('fetchV1Data', function (callback) { diff --git a/services/web/app/src/models/Survey.js b/services/web/app/src/models/Survey.js index 8a5027f665..2898d94107 100644 --- a/services/web/app/src/models/Survey.js +++ b/services/web/app/src/models/Survey.js @@ -40,6 +40,7 @@ const SurveySchema = new Schema( }, { collection: 'surveys', + minimize: false, } ) diff --git a/services/web/app/src/models/SystemMessage.js b/services/web/app/src/models/SystemMessage.js index af263fafc8..c3e37d00c6 100644 --- a/services/web/app/src/models/SystemMessage.js +++ b/services/web/app/src/models/SystemMessage.js @@ -2,8 +2,11 @@ const mongoose = require('../infrastructure/Mongoose') const { Schema } = mongoose -const SystemMessageSchema = new Schema({ - content: { type: String, default: '' }, -}) +const SystemMessageSchema = new Schema( + { + content: { type: String, default: '' }, + }, + { minimize: false } +) exports.SystemMessage = mongoose.model('SystemMessage', SystemMessageSchema) diff --git a/services/web/app/src/models/Tag.js b/services/web/app/src/models/Tag.js index 0f4b23db30..83eeb156bc 100644 --- a/services/web/app/src/models/Tag.js +++ b/services/web/app/src/models/Tag.js @@ -4,11 +4,14 @@ const { Schema } = mongoose // Note that for legacy reasons, user_id and project_ids are plain strings, // not ObjectIds. -const TagSchema = new Schema({ - user_id: { type: String, required: true }, - name: { type: String, required: true }, - project_ids: [String], -}) +const TagSchema = new Schema( + { + user_id: { type: String, required: true }, + name: { type: String, required: true }, + project_ids: [String], + }, + { minimize: false } +) exports.Tag = mongoose.model('Tag', TagSchema) exports.TagSchema = TagSchema diff --git a/services/web/app/src/models/TeamInvite.js b/services/web/app/src/models/TeamInvite.js index 9600e2054f..b115d27261 100644 --- a/services/web/app/src/models/TeamInvite.js +++ b/services/web/app/src/models/TeamInvite.js @@ -2,12 +2,15 @@ const mongoose = require('../infrastructure/Mongoose') const { Schema } = mongoose -const TeamInviteSchema = new Schema({ - email: { type: String, required: true }, - token: { type: String }, - inviterName: { type: String }, - sentAt: { type: Date }, -}) +const TeamInviteSchema = new Schema( + { + email: { type: String, required: true }, + token: { type: String }, + inviterName: { type: String }, + sentAt: { type: Date }, + }, + { minimize: false } +) exports.TeamInvite = mongoose.model('TeamInvite', TeamInviteSchema) exports.TeamInviteSchema = TeamInviteSchema diff --git a/services/web/app/src/models/User.js b/services/web/app/src/models/User.js index 732c1a8e30..a0da92672b 100644 --- a/services/web/app/src/models/User.js +++ b/services/web/app/src/models/User.js @@ -7,173 +7,182 @@ const { ObjectId } = Schema // See https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address/574698#574698 const MAX_EMAIL_LENGTH = 254 -const UserSchema = new Schema({ - email: { type: String, default: '', maxlength: MAX_EMAIL_LENGTH }, - emails: [ - { - email: { type: String, default: '', maxlength: MAX_EMAIL_LENGTH }, - reversedHostname: { type: String, default: '' }, - createdAt: { - type: Date, - default() { - return new Date() +const UserSchema = new Schema( + { + email: { type: String, default: '', maxlength: MAX_EMAIL_LENGTH }, + emails: [ + { + email: { type: String, default: '', maxlength: MAX_EMAIL_LENGTH }, + reversedHostname: { type: String, default: '' }, + createdAt: { + type: Date, + default() { + return new Date() + }, + }, + confirmedAt: { type: Date }, + samlProviderId: { type: String }, + affiliationUnchecked: { type: Boolean }, + reconfirmedAt: { type: Date }, + }, + ], + first_name: { type: String, default: '' }, + last_name: { type: String, default: '' }, + role: { type: String, default: '' }, + institution: { type: String, default: '' }, + hashedPassword: String, + isAdmin: { type: Boolean, default: false }, + staffAccess: { + publisherMetrics: { type: Boolean, default: false }, + publisherManagement: { type: Boolean, default: false }, + institutionMetrics: { type: Boolean, default: false }, + institutionManagement: { type: Boolean, default: false }, + groupMetrics: { type: Boolean, default: false }, + groupManagement: { type: Boolean, default: false }, + adminMetrics: { type: Boolean, default: false }, + splitTestMetrics: { type: Boolean, default: false }, + splitTestManagement: { type: Boolean, default: false }, + }, + signUpDate: { + type: Date, + default() { + return new Date() + }, + }, + loginEpoch: { type: Number }, + lastActive: { type: Date }, + lastFailedLogin: { type: Date }, + lastLoggedIn: { type: Date }, + lastLoginIp: { type: String, default: '' }, + lastPrimaryEmailCheck: { type: Date }, + loginCount: { type: Number, default: 0 }, + holdingAccount: { type: Boolean, default: false }, + ace: { + mode: { type: String, default: 'none' }, + theme: { type: String, default: 'textmate' }, + overallTheme: { type: String, default: '' }, + fontSize: { type: Number, default: '12' }, + autoComplete: { type: Boolean, default: true }, + autoPairDelimiters: { type: Boolean, default: true }, + spellCheckLanguage: { type: String, default: 'en' }, + pdfViewer: { type: String, default: 'pdfjs' }, + syntaxValidation: { type: Boolean }, + fontFamily: { type: String }, + lineHeight: { type: String }, + }, + features: { + collaborators: { + type: Number, + default: Settings.defaultFeatures.collaborators, + }, + versioning: { + type: Boolean, + default: Settings.defaultFeatures.versioning, + }, + dropbox: { type: Boolean, default: Settings.defaultFeatures.dropbox }, + github: { type: Boolean, default: Settings.defaultFeatures.github }, + gitBridge: { type: Boolean, default: Settings.defaultFeatures.gitBridge }, + compileTimeout: { + type: Number, + default: Settings.defaultFeatures.compileTimeout, + }, + compileGroup: { + type: String, + default: Settings.defaultFeatures.compileGroup, + }, + templates: { type: Boolean, default: Settings.defaultFeatures.templates }, + references: { + type: Boolean, + default: Settings.defaultFeatures.references, + }, + trackChanges: { + type: Boolean, + default: Settings.defaultFeatures.trackChanges, + }, + mendeley: { type: Boolean, default: Settings.defaultFeatures.mendeley }, + zotero: { type: Boolean, default: Settings.defaultFeatures.zotero }, + referencesSearch: { + type: Boolean, + default: Settings.defaultFeatures.referencesSearch, + }, + symbolPalette: { + type: Boolean, + default: Settings.defaultFeatures.symbolPalette, + }, + }, + featuresOverrides: [ + { + createdAt: { + type: Date, + default() { + return new Date() + }, + }, + expiresAt: { type: Date }, + note: { type: String }, + features: { + collaborators: { type: Number }, + versioning: { type: Boolean }, + dropbox: { type: Boolean }, + github: { type: Boolean }, + gitBridge: { type: Boolean }, + compileTimeout: { type: Number }, + compileGroup: { type: String }, + templates: { type: Boolean }, + trackChanges: { type: Boolean }, + mendeley: { type: Boolean }, + zotero: { type: Boolean }, + referencesSearch: { type: Boolean }, + symbolPalette: { type: Boolean }, }, }, - confirmedAt: { type: Date }, - samlProviderId: { type: String }, - affiliationUnchecked: { type: Boolean }, - reconfirmedAt: { type: Date }, - }, - ], - first_name: { type: String, default: '' }, - last_name: { type: String, default: '' }, - role: { type: String, default: '' }, - institution: { type: String, default: '' }, - hashedPassword: String, - isAdmin: { type: Boolean, default: false }, - staffAccess: { - publisherMetrics: { type: Boolean, default: false }, - publisherManagement: { type: Boolean, default: false }, - institutionMetrics: { type: Boolean, default: false }, - institutionManagement: { type: Boolean, default: false }, - groupMetrics: { type: Boolean, default: false }, - groupManagement: { type: Boolean, default: false }, - adminMetrics: { type: Boolean, default: false }, - splitTestMetrics: { type: Boolean, default: false }, - splitTestManagement: { type: Boolean, default: false }, - }, - signUpDate: { - type: Date, - default() { - return new Date() - }, - }, - loginEpoch: { type: Number }, - lastActive: { type: Date }, - lastFailedLogin: { type: Date }, - lastLoggedIn: { type: Date }, - lastLoginIp: { type: String, default: '' }, - lastPrimaryEmailCheck: { type: Date }, - loginCount: { type: Number, default: 0 }, - holdingAccount: { type: Boolean, default: false }, - ace: { - mode: { type: String, default: 'none' }, - theme: { type: String, default: 'textmate' }, - overallTheme: { type: String, default: '' }, - fontSize: { type: Number, default: '12' }, - autoComplete: { type: Boolean, default: true }, - autoPairDelimiters: { type: Boolean, default: true }, - spellCheckLanguage: { type: String, default: 'en' }, - pdfViewer: { type: String, default: 'pdfjs' }, - syntaxValidation: { type: Boolean }, - fontFamily: { type: String }, - lineHeight: { type: String }, - }, - features: { - collaborators: { - type: Number, - default: Settings.defaultFeatures.collaborators, - }, - versioning: { type: Boolean, default: Settings.defaultFeatures.versioning }, - dropbox: { type: Boolean, default: Settings.defaultFeatures.dropbox }, - github: { type: Boolean, default: Settings.defaultFeatures.github }, - gitBridge: { type: Boolean, default: Settings.defaultFeatures.gitBridge }, - compileTimeout: { - type: Number, - default: Settings.defaultFeatures.compileTimeout, - }, - compileGroup: { + ], + featuresUpdatedAt: { type: Date }, + featuresEpoch: { type: String, - default: Settings.defaultFeatures.compileGroup, }, - templates: { type: Boolean, default: Settings.defaultFeatures.templates }, - references: { type: Boolean, default: Settings.defaultFeatures.references }, - trackChanges: { - type: Boolean, - default: Settings.defaultFeatures.trackChanges, - }, - mendeley: { type: Boolean, default: Settings.defaultFeatures.mendeley }, - zotero: { type: Boolean, default: Settings.defaultFeatures.zotero }, - referencesSearch: { - type: Boolean, - default: Settings.defaultFeatures.referencesSearch, - }, - symbolPalette: { - type: Boolean, - default: Settings.defaultFeatures.symbolPalette, - }, - }, - featuresOverrides: [ - { - createdAt: { - type: Date, - default() { - return new Date() - }, - }, - expiresAt: { type: Date }, - note: { type: String }, - features: { - collaborators: { type: Number }, - versioning: { type: Boolean }, - dropbox: { type: Boolean }, - github: { type: Boolean }, - gitBridge: { type: Boolean }, - compileTimeout: { type: Number }, - compileGroup: { type: String }, - templates: { type: Boolean }, - trackChanges: { type: Boolean }, - mendeley: { type: Boolean }, - zotero: { type: Boolean }, - referencesSearch: { type: Boolean }, - symbolPalette: { type: Boolean }, + // when auto-merged from SL and must-reconfirm is set, we may end up using + // `sharelatexHashedPassword` to recover accounts... + sharelatexHashedPassword: String, + must_reconfirm: { type: Boolean, default: false }, + referal_id: { + type: String, + default() { + return TokenGenerator.generateReferralId() }, }, - ], - featuresUpdatedAt: { type: Date }, - featuresEpoch: { - type: String, - }, - // when auto-merged from SL and must-reconfirm is set, we may end up using - // `sharelatexHashedPassword` to recover accounts... - sharelatexHashedPassword: String, - must_reconfirm: { type: Boolean, default: false }, - referal_id: { - type: String, - default() { - return TokenGenerator.generateReferralId() + refered_users: [{ type: ObjectId, ref: 'User' }], + refered_user_count: { type: Number, default: 0 }, + refProviders: { + // The actual values are managed by third-party-references. + mendeley: Schema.Types.Mixed, + zotero: Schema.Types.Mixed, }, + alphaProgram: { type: Boolean, default: false }, // experimental features + betaProgram: { type: Boolean, default: false }, + labsProgram: { type: Boolean, default: false }, + labsProgramGalileo: { type: Boolean, default: false }, + overleaf: { + id: { type: Number }, + accessToken: { type: String }, + refreshToken: { type: String }, + }, + awareOfV2: { type: Boolean, default: false }, + samlIdentifiers: { type: Array, default: [] }, + thirdPartyIdentifiers: { type: Array, default: [] }, + migratedAt: { type: Date }, + twoFactorAuthentication: { + createdAt: { type: Date }, + enrolledAt: { type: Date }, + secretEncrypted: { type: String }, + }, + onboardingEmailSentAt: { type: Date }, + splitTests: Schema.Types.Mixed, + analyticsId: { type: String }, + surveyResponses: Schema.Types.Mixed, }, - refered_users: [{ type: ObjectId, ref: 'User' }], - refered_user_count: { type: Number, default: 0 }, - refProviders: { - // The actual values are managed by third-party-references. - mendeley: Schema.Types.Mixed, - zotero: Schema.Types.Mixed, - }, - alphaProgram: { type: Boolean, default: false }, // experimental features - betaProgram: { type: Boolean, default: false }, - labsProgram: { type: Boolean, default: false }, - labsProgramGalileo: { type: Boolean, default: false }, - overleaf: { - id: { type: Number }, - accessToken: { type: String }, - refreshToken: { type: String }, - }, - awareOfV2: { type: Boolean, default: false }, - samlIdentifiers: { type: Array, default: [] }, - thirdPartyIdentifiers: { type: Array, default: [] }, - migratedAt: { type: Date }, - twoFactorAuthentication: { - createdAt: { type: Date }, - enrolledAt: { type: Date }, - secretEncrypted: { type: String }, - }, - onboardingEmailSentAt: { type: Date }, - splitTests: Schema.Types.Mixed, - analyticsId: { type: String }, - surveyResponses: Schema.Types.Mixed, -}) + { minimize: false } +) function formatSplitTestsSchema(next) { if (this.splitTests) { diff --git a/services/web/app/src/models/UserAuditLogEntry.js b/services/web/app/src/models/UserAuditLogEntry.js index 6821241efc..c4355d26a5 100644 --- a/services/web/app/src/models/UserAuditLogEntry.js +++ b/services/web/app/src/models/UserAuditLogEntry.js @@ -12,6 +12,7 @@ const UserAuditLogEntrySchema = new Schema( }, { collection: 'userAuditLogEntries', + minimize: false, } ) diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index 61e2bedf9a..52a49b2870 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -88,9 +88,7 @@ module.exports = { mongo: { options: { appname: 'web', - useUnifiedTopology: - (process.env.MONGO_USE_UNIFIED_TOPOLOGY || 'true') === 'true', - poolSize: parseInt(process.env.MONGO_POOL_SIZE, 10) || 10, + maxPoolSize: parseInt(process.env.MONGO_POOL_SIZE, 10) || 100, serverSelectionTimeoutMS: parseInt(process.env.MONGO_SERVER_SELECTION_TIMEOUT, 10) || 60000, socketTimeoutMS: parseInt(process.env.MONGO_SOCKET_TIMEOUT, 10) || 60000, diff --git a/services/web/package.json b/services/web/package.json index a89f95657c..fcbc25347c 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -189,8 +189,8 @@ "minimist": "^1.2.7", "mmmagic": "^0.5.3", "moment": "^2.29.4", - "mongodb": "~3.6.0", - "mongoose": "^5.13.11", + "mongodb": "^4.13.0", + "mongoose": "^6.9.1", "multer": "overleaf/multer#e1df247fbf8e7590520d20ae3601eaef9f3d2e9e", "nocache": "^2.1.0", "nock": "^13.1.3", diff --git a/services/web/scripts/force_doc_flush.js b/services/web/scripts/force_doc_flush.js index 27be7d32e4..7727f622ff 100644 --- a/services/web/scripts/force_doc_flush.js +++ b/services/web/scripts/force_doc_flush.js @@ -27,7 +27,7 @@ async function main() { }) if (!DRY_RUN) { console.log(`updating doc ${DOC_ID} in mongo for project ${PROJECT_ID}`) - const { result } = await db.docs.updateOne( + const result = await db.docs.updateOne( { _id: ObjectId(DOC_ID), project_id: ObjectId(PROJECT_ID) }, { $set: { lines, version, ranges }, @@ -38,7 +38,11 @@ async function main() { } ) console.log('mongo result', result) - if (result.n !== 1 || result.nModified !== 1 || result.ok !== 1) { + if ( + result.matchedCount !== 1 || + result.modifiedCount !== 1 || + !result.acknowledged + ) { throw new Error('unexpected result from mongo update') } console.log(`deleting doc ${DOC_ID} from redis for project ${PROJECT_ID}`) diff --git a/services/web/scripts/set_tex_live_image.js b/services/web/scripts/set_tex_live_image.js index 5e873cc358..a9a35e0b04 100644 --- a/services/web/scripts/set_tex_live_image.js +++ b/services/web/scripts/set_tex_live_image.js @@ -54,8 +54,8 @@ async function updateImage(image, projectIds) { { _id: { $in: projectIds.map(ObjectId) } }, { $set: { imageName: `quay.io/sharelatex/${image}` } } ).exec() - console.log(`Found ${res.n} out of ${projectIds.length} projects`) - console.log(`Modified ${res.nModified} projects`) + console.log(`Found ${res.matchedCount} out of ${projectIds.length} projects`) + console.log(`Modified ${res.modifiedCount} projects`) } main() diff --git a/services/web/test/unit/bootstrap.js b/services/web/test/unit/bootstrap.js index 276552f33c..5a9a5eabd0 100644 --- a/services/web/test/unit/bootstrap.js +++ b/services/web/test/unit/bootstrap.js @@ -46,7 +46,16 @@ const SandboxedModule = require('sandboxed-module') SandboxedModule.configure({ ignoreMissing: true, requires: getSandboxedModuleRequires(), - globals: { AbortSignal, Buffer, Promise, console, process, URL }, + globals: { + AbortSignal, + Buffer, + Promise, + console, + process, + URL, + TextEncoder, + TextDecoder, + }, }) function getSandboxedModuleRequires() { diff --git a/services/web/test/unit/src/Authentication/AuthenticationManagerTests.js b/services/web/test/unit/src/Authentication/AuthenticationManagerTests.js index 7b0a55c506..dd56d493f9 100644 --- a/services/web/test/unit/src/Authentication/AuthenticationManagerTests.js +++ b/services/web/test/unit/src/Authentication/AuthenticationManagerTests.js @@ -17,7 +17,7 @@ describe('AuthenticationManager', function () { requires: { '../../models/User': { User: (this.User = { - updateOne: sinon.stub().callsArgWith(3, null, { nModified: 1 }), + updateOne: sinon.stub().callsArgWith(3, null, { modifiedCount: 1 }), }), }, '../../infrastructure/mongodb': { @@ -99,7 +99,7 @@ describe('AuthenticationManager', function () { }) it('should return the user', function () { - this.callback.calledWith(null, this.user).should.equal(true) + this.callback.should.have.been.calledWith(null, this.user) }) it('should send metrics', function () { @@ -147,7 +147,7 @@ describe('AuthenticationManager', function () { beforeEach(function () { this.User.updateOne = sinon .stub() - .callsArgWith(3, null, { nModified: 0 }) + .callsArgWith(3, null, { modifiedCount: 0 }) }) describe('correct password', function () { @@ -171,7 +171,9 @@ describe('AuthenticationManager', function () { describe('bad password', function () { beforeEach(function (done) { - this.User.updateOne = sinon.stub().yields(null, { nModified: 0 }) + this.User.updateOne = sinon + .stub() + .yields(null, { modifiedCount: 0 }) this.AuthenticationManager.authenticate( { email: this.email }, 'notthecorrectpassword', diff --git a/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js b/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js index b3cad303d0..2e6791dfc6 100644 --- a/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js +++ b/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js @@ -506,7 +506,7 @@ describe('CollaboratorsHandler', function () { } ) .chain('exec') - .resolves({ n: 1 }) + .resolves({ matchedCount: 1 }) await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel( this.projectId, this.userId, @@ -530,7 +530,7 @@ describe('CollaboratorsHandler', function () { } ) .chain('exec') - .resolves({ n: 1 }) + .resolves({ matchedCount: 1 }) await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel( this.projectId, this.userId, @@ -539,7 +539,9 @@ describe('CollaboratorsHandler', function () { }) it('throws a NotFoundError if the project or collaborator does not exist', async function () { - this.ProjectMock.expects('updateOne').chain('exec').resolves({ n: 0 }) + this.ProjectMock.expects('updateOne') + .chain('exec') + .resolves({ matchedCount: 0 }) await expect( this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel( this.projectId, diff --git a/services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js b/services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js index e594aa1d84..148c6e1272 100644 --- a/services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js +++ b/services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js @@ -75,7 +75,7 @@ describe('ProjectHistoryHandler', function () { .callsArgWith(1, null, this.project) this.ProjectModel.updateOne = sinon .stub() - .callsArgWith(2, null, { n: 1 }) + .callsArgWith(2, null, { matchedCount: 1 }) return this.ProjectHistoryHandler.ensureHistoryExistsForProject( project_id, this.callback diff --git a/services/web/test/unit/src/User/UserAuditLogHandlerTests.js b/services/web/test/unit/src/User/UserAuditLogHandlerTests.js index c11353971b..4f39a1fd71 100644 --- a/services/web/test/unit/src/User/UserAuditLogHandlerTests.js +++ b/services/web/test/unit/src/User/UserAuditLogHandlerTests.js @@ -40,7 +40,7 @@ describe('UserAuditLogHandler', function () { beforeEach(function () { this.dbUpdate = this.UserAuditLogEntryMock.expects('create') .chain('exec') - .resolves({ nModified: 1 }) + .resolves({ modifiedCount: 1 }) }) it('writes a log', async function () { await this.UserAuditLogHandler.promises.addEntry( diff --git a/services/web/test/unit/src/User/UserCreatorTests.js b/services/web/test/unit/src/User/UserCreatorTests.js index c32c7077dc..d11df91500 100644 --- a/services/web/test/unit/src/User/UserCreatorTests.js +++ b/services/web/test/unit/src/User/UserCreatorTests.js @@ -35,9 +35,11 @@ describe('UserCreator', function () { }), './UserUpdater': (this.UserUpdater = { promises: { - addAffiliationForNewUser: sinon - .stub() - .resolves({ n: 1, nModified: 1, ok: 1 }), + addAffiliationForNewUser: sinon.stub().resolves({ + matchedCount: 1, + modifiedCount: 1, + acknowledged: true, + }), updateUser: sinon.stub().resolves(), }, }),