diff --git a/backend/db.js b/backend/db.js index 85fc84e..79be921 100644 --- a/backend/db.js +++ b/backend/db.js @@ -138,6 +138,14 @@ function sanitizeMongoUpdateSetObject(update_obj) { return sanitized; } +function isMongoWriteAck(result) { + if (!result) return false; + if (typeof result.acknowledged === 'boolean') return result.acknowledged; + if (typeof result.ok === 'number') return result.ok === 1; + if (result.result && typeof result.result.ok === 'number') return result.result.ok === 1; + return true; +} + function setDB(input_db, input_users_db) { db = input_db; users_db = input_users_db; exports.db = input_db; @@ -193,10 +201,7 @@ exports.connectToDB = async (retries = 5, no_fallback = false, custom_connection exports._connectToDB = async (custom_connection_string = null) => { const uri = !custom_connection_string ? config_api.getConfigItem('ytdl_mongodb_connection_string') : custom_connection_string; // "mongodb://127.0.0.1:27017/?compressors=zlib&gssapiServiceName=mongodb"; - const client = new MongoClient(uri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); + const client = new MongoClient(uri); try { await client.connect(); @@ -336,12 +341,12 @@ exports.insertRecordIntoTable = async (table, doc, replaceFilter = null) => { } ]); logger.debug(`Inserted doc into ${table} with filter: ${JSON.stringify(replaceFilter)}`); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } const output = await database.collection(table).insertOne(doc); logger.debug(`Inserted doc into ${table}`); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.insertRecordsIntoTable = async (table, docs, ignore_errors = false) => { @@ -360,7 +365,7 @@ exports.insertRecordsIntoTable = async (table, docs, ignore_errors = false) => { } const output = await database.collection(table).insertMany(docs, {ordered: !ignore_errors}); logger.debug(`Inserted ${output.insertedCount} docs into ${table}`); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.bulkInsertRecordsIntoTable = async (table, docs) => { @@ -368,18 +373,18 @@ exports.bulkInsertRecordsIntoTable = async (table, docs) => { if (using_local_db) { return await exports.insertRecordsIntoTable(table, docs); } + if (!docs || docs.length === 0) return true; // not a necessary function as insertRecords does the same thing but gives us more control on batch size if needed - const table_collection = database.collection(table); - - let bulk = table_collection.initializeOrderedBulkOp(); // Initialize the Ordered Batch - - for (let i = 0; i < docs.length; i++) { - bulk.insert(docs[i]); - } - - const output = await bulk.execute(); - return !!(output['result']['ok']); + const output = await database.collection(table).bulkWrite( + docs.map(doc => ({ + insertOne: { + document: doc + } + })), + { ordered: true } + ); + return isMongoWriteAck(output); } @@ -407,7 +412,12 @@ exports.getRecords = async (table, filter_obj = null, return_count = false, sort return !return_count ? cursor : cursor.length; } - const cursor = filter_obj ? database.collection(table).find(filter_obj) : database.collection(table).find(); + const collection = database.collection(table); + if (return_count) { + return await collection.countDocuments(filter_obj || {}); + } + + const cursor = filter_obj ? collection.find(filter_obj) : collection.find(); if (sort) { cursor.sort({[sort['by']]: sort['order']}); } @@ -415,7 +425,7 @@ exports.getRecords = async (table, filter_obj = null, return_count = false, sort cursor.skip(range[0]).limit(range[1] - range[0]); } - return !return_count ? await cursor.toArray() : await cursor.count(); + return await cursor.toArray(); } // Update @@ -455,7 +465,7 @@ exports.updateRecord = async (table, filter_obj, update_obj, nested_mode = false } const output = await database.collection(table).updateOne(sanitized_filter_obj, {$set: sanitized_update_obj}); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.updateRecords = async (table, filter_obj, update_obj) => { @@ -494,7 +504,7 @@ exports.updateRecords = async (table, filter_obj, update_obj) => { } const output = await database.collection(table).updateMany(sanitized_filter_obj, {$set: sanitized_update_obj}); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.removePropertyFromRecord = async (table, filter_obj, remove_obj) => { @@ -506,7 +516,7 @@ exports.removePropertyFromRecord = async (table, filter_obj, remove_obj) => { } const output = await database.collection(table).updateOne(filter_obj, {$unset: remove_obj}); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.bulkUpdateRecordsByKey = async (table, key_label, update_obj) => { @@ -526,21 +536,19 @@ exports.bulkUpdateRecordsByKey = async (table, key_label, update_obj) => { return true; } - const table_collection = database.collection(table); - - let bulk = table_collection.initializeOrderedBulkOp(); // Initialize the Ordered Batch - const item_ids_to_update = Object.keys(update_obj); + if (item_ids_to_update.length === 0) return true; - for (let i = 0; i < item_ids_to_update.length; i++) { - const item_id_to_update = item_ids_to_update[i]; - bulk.find({[key_label]: item_id_to_update }).updateOne({ - "$set": update_obj[item_id_to_update] - }); - } - - const output = await bulk.execute(); - return !!(output['result']['ok']); + const output = await database.collection(table).bulkWrite( + item_ids_to_update.map(item_id_to_update => ({ + updateOne: { + filter: {[key_label]: item_id_to_update}, + update: { "$set": update_obj[item_id_to_update] } + } + })), + { ordered: true } + ); + return isMongoWriteAck(output); } exports.pushToRecordsArray = async (table, filter_obj, key, value) => { @@ -551,7 +559,7 @@ exports.pushToRecordsArray = async (table, filter_obj, key, value) => { } const output = await database.collection(table).updateOne(filter_obj, {$push: {[key]: value}}); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } exports.pullFromRecordsArray = async (table, filter_obj, key, value) => { @@ -562,7 +570,7 @@ exports.pullFromRecordsArray = async (table, filter_obj, key, value) => { } const output = await database.collection(table).updateOne(filter_obj, {$pull: {[key]: value}}); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } // Delete @@ -575,7 +583,7 @@ exports.removeRecord = async (table, filter_obj) => { } const output = await database.collection(table).deleteOne(filter_obj); - return !!(output['result']['ok']); + return isMongoWriteAck(output); } // exports.removeRecordsByUIDBulk = async (table, uids) => { @@ -658,7 +666,7 @@ exports.removeAllRecords = async (table = null, filter_obj = null) => { const output = await database.collection(table_to_remove).deleteMany(filter_obj ? filter_obj : {}); logger.debug(`Successfully removed records from ${table_to_remove}`); - success &= !!(output['result']['ok']); + success &= isMongoWriteAck(output); } return success; } @@ -682,8 +690,7 @@ const getDBTableStats = async (table) => { if (using_local_db) { table_stats['records_count'] = local_db.get(table).value().length; } else { - const stats = await database.collection(table).stats(); - table_stats['records_count'] = stats.count; + table_stats['records_count'] = await database.collection(table).countDocuments({}); } return table_stats; } diff --git a/backend/package-lock.json b/backend/package-lock.json index 6e3cddb..3f6ea35 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -34,7 +34,7 @@ "md5": "^2.2.1", "mocha": "^11.7.5", "moment": "^2.29.4", - "mongodb": "^3.6.9", + "mongodb": "^6.21.0", "multer": "^2.0.2", "node-fetch": "^2.7.0", "node-id3": "^0.2.9", @@ -250,6 +250,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz", + "integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@oozcitak/dom": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", @@ -401,6 +410,21 @@ "@types/node": "*" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.4", "license": "MIT", @@ -772,10 +796,12 @@ "license": "ISC" }, "node_modules/bson": { - "version": "1.1.6", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", "engines": { - "node": ">=0.6.19" + "node": ">=16.20.1" } }, "node_modules/buffer": { @@ -1360,13 +1386,6 @@ "node": ">=0.4.0" } }, - "node_modules/denque": { - "version": "1.5.1", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", "license": "MIT", @@ -2660,8 +2679,9 @@ }, "node_modules/memory-pager": { "version": "1.5.0", - "license": "MIT", - "optional": true + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" }, "node_modules/merge-descriptors": { "version": "1.0.3", @@ -2864,26 +2884,35 @@ } }, "node_modules/mongodb": { - "version": "3.7.3", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", + "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==", "license": "Apache-2.0", "dependencies": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "optional-require": "^1.1.8", - "safe-buffer": "^5.1.2" + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=16.20.1" }, - "optionalDependencies": { - "saslprep": "^1.0.0" + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" }, "peerDependenciesMeta": { - "aws4": { + "@aws-sdk/credential-providers": { "optional": true }, - "bson-ext": { + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { "optional": true }, "kerberos": { @@ -2892,33 +2921,56 @@ "mongodb-client-encryption": { "optional": true }, - "mongodb-extjson": { + "snappy": { "optional": true }, - "snappy": { + "socks": { "optional": true } } }, - "node_modules/mongodb/node_modules/bl": { - "version": "2.2.1", + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "license": "MIT", "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" } }, - "node_modules/mongodb/node_modules/readable-stream": { - "version": "2.3.7", + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/ms": { @@ -3130,16 +3182,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optional-require": { - "version": "1.1.8", - "license": "Apache-2.0", - "dependencies": { - "require-at": "^1.0.6" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/p-cancelable": { "version": "2.1.1", "license": "MIT", @@ -3372,6 +3414,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.14.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", @@ -3504,13 +3555,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/require-at": { - "version": "1.0.6", - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3562,17 +3606,6 @@ "version": "2.1.2", "license": "MIT" }, - "node_modules/saslprep": { - "version": "1.0.3", - "license": "MIT", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/sax": { "version": "1.2.4", "license": "ISC" @@ -3765,8 +3798,9 @@ }, "node_modules/sparse-bitfield": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "license": "MIT", - "optional": true, "dependencies": { "memory-pager": "^1.0.2" } diff --git a/backend/package.json b/backend/package.json index 3fadd86..efff4fc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -48,7 +48,7 @@ "md5": "^2.2.1", "mocha": "^11.7.5", "moment": "^2.29.4", - "mongodb": "^3.6.9", + "mongodb": "^6.21.0", "multer": "^2.0.2", "node-fetch": "^2.7.0", "node-id3": "^0.2.9",