diff --git a/util/archive/old storage/Provider.js b/util/archive/old storage/Provider.js new file mode 100644 index 0000000..0e96ed1 --- /dev/null +++ b/util/archive/old storage/Provider.js @@ -0,0 +1,22 @@ +class Provider { + + constructor(manager, opts) { + + if(!opts.config) throw new Error('No config file provided!'); + this.config = opts.config[opts.name]; + if(this.config && (!this.config.database || !this.config.host)) throw new Error('Invalid config file provided!' + JSON.stringify(this.config)); + + this.manager = manager; + + this.name = opts.name; + + this.connection = null; + this.db = null; + + this._initialized = false; + + } + +} + +module.exports = Provider; \ No newline at end of file diff --git a/util/archive/old storage/StorageManager.js b/util/archive/old storage/StorageManager.js new file mode 100644 index 0000000..a3487be --- /dev/null +++ b/util/archive/old storage/StorageManager.js @@ -0,0 +1,91 @@ +const { Collection } = require('../util/'); +const path = require('path'); +const fs = require('fs'); + +class StorageManager { + + constructor(manager, options = {}) { + + this.manager = manager; + + this.providers = new Collection(); + this.options = options; + + } + + async initialize() { + + this.manager.logger.write('debug', "Initializing storage providers."); + + const _providers = path.join(process.cwd(), 'storage', 'providers'); + const providers = fs.readdirSync(_providers); + + for(const _provider of providers) { + + let provider = require(path.join(_providers, _provider)); + provider = new provider(this.manager, this.options); + + await provider.init(); + + this.providers.set(provider.name, provider); + + } + + return this; + + } + + async _handleStorageRequest(shard, message) { + + // vv TO BE GENERATED ON CLIENT vv + // Transaction ID concept + //`${shard.id}-${new Date().getTime().toString(36)}` + + //NULL RESPONSE IF COLLECTION DOES NOT EXIST!! + //EMPTY OBJECT IF NOTHING MATCHED!! + + /** Message structure + * { + * provider: string, -- mongodb or mariadb + * transactionID: string, + * request: { + * type: string, -- The function to use from this class, ex. findOne + * collection: string, -- Which collection to query from + * query: { }, -- Actual search query + * data: { } -- If the operation is to update or insert something, this is what to insert + * } + * } + */ + + if(!message.provider) return this._send(shard, { error: true, message: "Provider not provided", transactionID: message.transactionID }); + if(!this.providers.has(message.provider)) return this._send(shard, { error: true, message: `Invalid provider, expected 'mongodb' or 'mariadb', got '${message.provider}'`, transactionID: message.transactionID }); + if(!message.request) return this._send(shard, { error: true, message: `Missing request.`, transactionID: message.transactionID }); + + const response = await this.providers.get(message.provider)._query(message.request) + .catch((error) => { + this.manager.logger.write('error', `Provider ${message.provider} errored: ${error}`, shard); + }); + + if(response && response.error) { + this.manager.logger.write('error', `Provider ${message.provider} errored: ${response.message}`, shard); + return this._send(shard, response.error); + } + + this._send(shard, { transactionID: message.transactionID, result: response }); + + } + + _send(shard, message) { + shard.send({ + _storage: true, + transactionID: message.transactionID, + id: message.id, + error: message.error, + message: message.message, + result: message.result + }); + } + +} + +module.exports = StorageManager; \ No newline at end of file diff --git a/util/archive/old storage/providers/Mariadb.js b/util/archive/old storage/providers/Mariadb.js new file mode 100644 index 0000000..47e9da8 --- /dev/null +++ b/util/archive/old storage/providers/Mariadb.js @@ -0,0 +1,76 @@ +const Provider = require('../Provider.js'); +const MySQL = require('mysql'); + +class MariaDBProvider extends Provider { + + constructor(manager, config) { + + super(manager, { + name: 'mariadb', + config + }); + + } + + async init() { + + try { + + this.db = MySQL.createPool(this.config); + + this.db.on('connection', async (connection) => { + + // this.manager.logger.log('MariaDB connected.'); + + connection.on('error', (err) => { + // this.manager.logger.error('MariaDB errored.', err); + }); + + connection.on('close', (data) => { + // this.manager.logger.log('MariaDB connection closed.', data); + }); + + }); + + this.loaded = true; + + } catch(err) { + + // this.manager.logger.error('MariaDB connection failed.', err); + + } + + return this; + + } + + close() { + this.db.end(); + } + + /** + * Query using SQL to MariaDB + * + * @param {string} query SQL query string. + * @param {array} values Array of values to replace ? with in the query string + * @returns {object} Returns an object containing the query result + * @memberof MariaDBProvider + */ + query(query, values) { + + if(!this.loaded) throw new Error('MariaDB not connected'); + + return new Promise((resolve, reject) => { + + this.db.query(query, values, (err, result) => { + if(err) reject(err); + resolve(result); + }); + + }); + + } + +} + +module.exports = MariaDBProvider; \ No newline at end of file diff --git a/util/archive/old storage/providers/Mongodb.js b/util/archive/old storage/providers/Mongodb.js new file mode 100644 index 0000000..ffd8db8 --- /dev/null +++ b/util/archive/old storage/providers/Mongodb.js @@ -0,0 +1,369 @@ +const Provider = require('../Provider.js'); +const { MongoClient, ObjectId } = require('mongodb'); + +class MongoDBProvider extends Provider { + + constructor(manager, config) { + + super(manager, { + name: 'mongodb', + config + }); + + } + + async init() { + + try { + this.connection = await MongoClient.connect(this.config.host+this.config.database, { useUnifiedTopology: true }); + this.manager.db = await this.connection.db(this.config.database); + this.db = this.manager.db; + this.manager.logger.write('info', `Provider ${this.name} connected.`); + this._initialized = true; + } catch(err) { + this.manager.logger.write('error', `Provider ${this.name} failed to connect: ${err}`); + } + + } + + _query(request) { + + /* Query object structure + { + type: '', -- The function to use from this class, ex. findOne + collection: '', -- Which collection to query from + query: { }, -- Actual search query + data: { }, -- If the operation is to update or insert something, this is what to insert + upsert: bool, -- If true and no matching documents are found, insert a new one + options: { }, + projection: { } -- The fields that should be returned + } + */ + + + if (!this._initialized) return { error: true, message: 'MongoDB not connected' }; + if (!this[request.type]) return { error: true, message: `Invalid request type, got '${request.type}'` }; + if (!request.collection && request.type !== 'stats') return { error: true, message: 'You must specify a collection to query!' }; + + if (request.query?._id) { + if (typeof request.query?._id === 'string') request.query._id = ObjectId(request.query._id); + else if (typeof request.query?._id === 'object') request.query._id = { + $in: Object.values(request.query._id)[0].map((id) => { + return ObjectId(id); + }) + }; + } + + return this[request.type](request); + + } + + /** + * Fetches basic statistics about the database or collection + * + * @param {String} collection The collection to query, optional + * @param {Object} options Optional options for the stats method + * @returns + * @memberof MongoDBProvider + */ + stats({ collection, options = { } }) { + + return new Promise((resolve, reject) => { + if (!collection) { + this.db.stats(options, (err, stats) => { + if (err) return reject(err); + + const { db, collections, objects, avgObjSize, dataSize } = stats; + return resolve({ db, collections, objects, averageSize: avgObjSize, dataSize }); + }); + } else { + this.db.collection(collection).stats(options, (err, stats) => { + if (err) return reject(err); + + const { ns, size, count, avgObjSize } = stats; + return resolve({ index: ns, size, count, averageSize: avgObjSize }); + }); + } + }); + + } + + /** + * Count the objects in + * + * @param {String} collection The collection to query + * @param {Object} query Only documents matching this will be counted, optional + * @param {Object} options Optional options, see mongo docs for these + * @returns + * @memberof MongoDBProvider + */ + count({ collection, query, options }) { + + return new Promise((resolve, reject) => { + + this.db.collection(collection).countDocuments(query, options, (error, result) => { + + if (error) return reject(error); + return resolve(result); + + }); + + }); + + } + + + /** + * Remove a document from a collection + * + * @param {String} collection The collection to remove from + * @param {Object} query Any documents matching this will be removed + * @returns + * @memberof MongoDBProvider + */ + delete({ collection, query }) { + + return new Promise((resolve, reject) => { + + this.db.collection(collection).deleteOne(query, (err, result) => { + + if (err) return reject(err); + return resolve(result); + + }); + + }); + + } + + /** + * Remove multiple documents from a collection + * + * @param {String} collection The collection to remove from + * @param {Object} query Any documents matching this will be removed + * @returns + * @memberof MongoDBProvider + */ + deleteMany({ collection, query }) { + + return new Promise((resolve, reject) => { + + this.db.collection(collection).deleteMany(query, (err, result) => { + + if (err) return reject(err); + return resolve(result); + + }); + + }); + + } + + /** + * Insert one document to the collection + * + * @param {String} collection The collection to insert into + * @param {Object} data The document to insert + * @returns + * @memberof MongoDBProvider + */ + insertOne({ collection, data }) { + + return new Promise((resolve, reject) => { + + if(data.buffer) data.buffer = Buffer.from(data.buffer); + + this.db.collection(collection).insertOne(data, (err, result) => { + + if(err) reject(err); + resolve(result); + + }); + + }); + + } + + /** + * Find and return the first match + * + * @param {String} collection The collection to find from + * @param {Object} query Documents matching this will be returned + * @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional + * @memberof Database + */ + find({ collection, query, projection }) { + + //if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`); + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + + this.db.collection(collection).find(query, { projection }, async (error, cursor) => { + + if(error) return reject(error); + return resolve(await cursor.toArray()); + + }); + + }); + + } + + /** + * Find and return the first match + * + * @param {String} collection The collection in which the data is to be updated + * @param {Object} query The filter that is used to find the data + * @param {Object} projection Defines which fields to return, { 'key': 1, 'key2': 0 } -- key will return, key2 won't, optional + * @returns {Object} An object containing the queried data + * @memberof Database + */ + findOne({ collection, query, projection, sort }) { + + //if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`); + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + + this.db.collection(collection).findOne(query, { projection, sort }, async (error, item) => { + + if(error) return reject(error); + return resolve(item); + + }); + + }); + + } + + /** + * Update the first filter match. + * + * @param {String} collection The collection in which the data is to be updated + * @param {Object} query The filter that is used to find the data + * @param {Object} data The updated data + * @param {Boolean} [upsert=true] Create a new entry if no match is found + * @returns {WriteResult} Object containing the followint counts: Matched, Upserted, Modified + * @memberof Database + */ + updateOne({ collection, query, data, upsert = true }) { + + //if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`); + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + + this.db.collection(collection).updateOne(query, { $set: data }, { upsert }, async (error, result) => { + + if(error) return reject(error); + + //return resolve(result) + const { matchedCount, upsertedCount, modifiedCount } = result; + return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount }); + + }); + + }); + + } + + /** + * Remove a document property + * + * @param {String} collection The collection to query + * @param {Object} query The filter that is used to find the data + * @param {Array} data Array of fields to remove + * @returns + * @memberof MongoDBProvider + */ + removeProperty({ collection, query, data }) { + return new Promise((resolve, reject) => { + + const unset = {}; + for (const field of data) unset[field] = ''; + this.db.collection(collection).updateOne(query, { $unset: unset }, async (error, result) => { + + if(error) return reject(error); + + const { matchedCount, modifiedCount } = result; + return resolve({ matched: matchedCount, modified: modifiedCount }); + + }); + + }); + + } + + /** + * Push data to an array + * + * @param {string} collection The collection to query + * @param {object} query The filter to find the document to update + * @param {object} data The data to be pushed + * @param {boolean} [upsert=true] Create a new entry if no match is found + * @returns + * @memberof Database + */ + push({ collection, query, data, upsert = true }) { + + //if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`); + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + + this.db.collection(collection).updateOne(query, { $push: data }, { upsert }, async (error, result) => { + + if(error) return reject(error); + return resolve(result); + + }); + + }); + + } + + /** + * Find a random element from a database + * + * @param {string} collection The collection to query + * @param {object} [query={}] The filtering object to narrow down the sample pool + * @param {number} [amount=1] Amount of items to return + * @returns {object} + * @memberof Database + */ + random({ collection, query = {}, amount = 1 }) { + + //if(this.manager.debug) this.manager.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`); + if(amount > 100) amount = 100; + + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + + this.db.collection(collection).aggregate([{ $match: query }, { $sample: { size: amount } }], (err, item) => { + if(err) return reject(err); + return resolve(item); + }); + + }); + + } + + aggregate({ collection, query }) { + + return new Promise((resolve, reject) => { + + if(!this._initialized) reject(new Error('MongoDB not connected')); + this.db.collection(collection).aggregate(query, (err, item) => { + if(err) return reject(err); + return resolve(item.toArray()); + }); + + }); + + } + +} + +module.exports = MongoDBProvider; \ No newline at end of file